/// <summary> /// Ensures that the the Git Repository is opened and updates the <see cref="Worlds"/>. /// Returns true on success, false on error. /// </summary> /// <param name="m">The monitor to use.</param> /// <param name="force">True to refresh the <see cref="Worlds"/> even if <see cref="IsOpen"/> is already true.</param> /// <returns>Whether this repository has been successfully opened.</returns> internal bool Refresh(IActivityMonitor m, bool force = true) { bool isOpened = false; if (!IsOpen) { _git = GitRepository.Create(m, this, Root, Root.LastPart, false, BranchName, checkOutBranchName: true); if (_git == null) { return(false); } isOpened = true; } if (force || isOpened) { if (_git.Pull(m, MergeFileFavor.Theirs).ReloadNeeded || isOpened) { var worldNames = Directory.GetFiles(Root, "*.World.xml") .Select(p => LocalWorldName.TryParse(p, _store.WorldLocalMapping)) .Where(w => w != null) .ToDictionary(w => w.FullName); var invalidParallels = worldNames.Values.Where(p => p.ParallelName != null && !worldNames.ContainsKey(p.Name)).ToList(); foreach (var orphan in invalidParallels) { m.Warn($"Invalid Parallel World '{orphan.FullName}': unable to find the default stack definition '{orphan.Name}' in the repository. It is ignored."); worldNames.Remove(orphan.FullName); } foreach (var exists in _worlds) { if (!worldNames.Remove(exists.WorldName.FullName)) { if (exists.WorldName.HasDefinitionFile) { m.Warn($"Unable to find World definition file for '{exists.WorldName}'. File '{exists.WorldName.XmlDescriptionFilePath}' not found."); exists.WorldName.HasDefinitionFile = false; } } else { if (!exists.WorldName.HasDefinitionFile) { m.Trace($"Found World definition file for '{exists.WorldName}'."); exists.WorldName.HasDefinitionFile = true; } } } foreach (var newWorld in worldNames.Values) { m.Info($"Found a new World definition: creating '{newWorld.FullName}' entry."); newWorld.HasDefinitionFile = true; _worlds.Add(new WorldInfo(this, newWorld)); } } } return(IsOpen); }
internal WorldInfo(StackRepo r, XElement e) { Repo = r; var n = (string)e.AttributeRequired(nameof(WorldName.FullName)); _name = LocalWorldName.TryParse(r.Root.AppendPart(n + ".World.xml"), r.Store.WorldLocalMapping); _name.HasDefinitionFile = (bool?)e.Attribute(nameof(WorldName.HasDefinitionFile)) ?? false; _name.IsHidden = (bool?)e.Attribute(nameof(WorldName.IsHidden)) ?? false; }
void OnWorkingFolderChanged(IActivityMonitor m, LocalWorldName local) { var repo = _repos.FirstOrDefault(r => local.XmlDescriptionFilePath.StartsWith(r.Root)); if (repo == null) { m.Warn($"Unable to find the local repository for {local.FullName}."); } else if (!repo.IsOpen) { m.Warn($"Local repository {local.FullName} ({repo.Root}) is not opened."); } else { repo.PushChanges(m); } }
protected override LocalWorldName DoCreateNew(IActivityMonitor m, string name, string parallelName, XDocument content) { Debug.Assert(!String.IsNullOrWhiteSpace(name)); Debug.Assert(content != null); Debug.Assert(parallelName == null || !String.IsNullOrWhiteSpace(parallelName)); string wName = name + (parallelName != null ? '[' + parallelName + ']' : String.Empty); if (ReadWorlds(m).Any(w => w.FullName == wName)) { m.Error($"World '{wName}' already exists."); return(null); } int idx = _stacks.IndexOf(d => d.StackName == name); if (idx < 0) { m.Error("A repository must be created first for a new Stack."); return(null); } var home = FindRepo(m, name); if (home == null) { return(null); } var path = home.Root.AppendPart(wName + ".World.xml"); if (!File.Exists(path)) { var w = new LocalWorldName(path, name, parallelName, WorldLocalMapping); if (!WriteWorldDescription(m, w, content)) { m.Error($"Unable to create {wName} world."); return(null); } return(w); } m.Error($"World file {path} already exists."); return(null); }
internal void ReadWorlds(IActivityMonitor m, StackInitializeOption option, Action <LocalWorldName> addWorld) { if (option == StackInitializeOption.OpenRepository) { EnsureOpen(m); } else if (option == StackInitializeOption.OpenAndPullRepository) { Pull(m); } if (!IsOpen) { m.Warn($"Repository '{OriginUrl}' for stacks '{_stacks.Select( s => s.StackName ).Concatenate("', '")}' is not opened. Skipping Worlds reading from them."); return; } var worldNames = Directory.GetFiles(Root, "*.World.xml") .Select(p => LocalWorldName.TryParse(m, p, _store.WorldLocalMapping)) .Where(w => w != null) .ToList(); var missing = _stacks .Where(s => !worldNames.Any(w => w.FullName.Equals(s.StackName, StringComparison.OrdinalIgnoreCase))); foreach (var s in missing) { m.Warn($"Unable to find xml file definition for '{s.StackName}'."); } for (int i = 0; i < worldNames.Count; ++i) { var w = worldNames[i]; if (w.ParallelName == null && !_stacks.Any(s => s.StackName.Equals(w.FullName, StringComparison.OrdinalIgnoreCase))) { m.Warn($"Unexpected '{w.FullName}' stack found. It is ignored."); worldNames.RemoveAt(i--); } else { addWorld(w); } } }
protected override LocalWorldName DoCreateNewParallel(IActivityMonitor m, IRootedWorldName source, string parallelName, XDocument content) { Debug.Assert(source != null); Debug.Assert(content != null); Debug.Assert(!String.IsNullOrWhiteSpace(parallelName)); StackRepo repo = null; if (source is LocalWorldName src && (repo = _stackRepos.FirstOrDefault(r => src.XmlDescriptionFilePath.StartsWith(r.Root))) == null) { m.Error($"Unable to find source World."); return(null); } string wName = source.Name + (parallelName != null ? '[' + parallelName + ']' : String.Empty); var world = _stackRepos.SelectMany(r => r.Worlds).FirstOrDefault(w => w.WorldName.FullName == wName); if (world != null) { m.Error($"World '{wName}' already exists."); return(null); } var path = repo.Root.AppendPart(wName + ".World.xml"); if (!File.Exists(path)) { var w = new LocalWorldName(path, source.Name, parallelName, WorldLocalMapping); if (!WriteWorldDescription(m, w, content)) { m.Error($"Unable to create {wName} world."); return(null); } return(w); } m.Error($"World file '{path}' already exists."); return(null); }
/// <summary> /// Destroys this WorldInfo: this deletes the Local file state (in the <see cref="IRootedWorldName.Root"/>), the shared file state /// and the definition file itself. /// Once done we remove this object from the <see cref="StackRepo.Worlds"/>. /// </summary> public bool Destroy(IActivityMonitor m) { if (_name != null) { var p = Repo.Store.ToLocalStateFilePath(WorldName); try { File.Delete(p); p = Repo.Store.ToSharedStateFilePath(WorldName).Item2; File.Delete(p); p = WorldName.XmlDescriptionFilePath; File.Delete(p); Repo.OnDestroy(this); _name.HasDefinitionFile = false; _name = null; } catch (Exception ex) { m.Error($"Unable to delete file '{p}'.", ex); return(false); } } return(true); }
internal WorldInfo(StackRepo repo, LocalWorldName name) { Debug.Assert(repo != null && name != null); Repo = repo; _name = name; }