private static async Task <Tuple <List <Plugin>, List <Plugin> > > StageDefinition(SDK.Core.Plugins.Plugin definition, IDictionary <Name, Tuple <SDK.Core.Plugins.VersionRange, Plugin> > loaded = null) { var top = new List <Plugin>(); var nested = new List <Plugin>(); foreach (var dependency in definition.Dependencies ?? new Dictionary <Name, SDK.Core.Plugins.VersionRange>()) { var repo = definition.Repositories?.FirstOrDefault(r => r.Name.ToString() == dependency.Key.ToString()); var adapter = new AdapterBuilder(dependency.Key, repo).Adapter(); var versions = await adapter.GetVersions(); var versionMatch = versions.LastOrDefault(version => dependency.Value.IsSatisfied(version)); if (versionMatch == null) { throw new Exception("No matching version found"); } if (loaded == null) { loaded = new Dictionary <Name, Tuple <SDK.Core.Plugins.VersionRange, Plugin> >(); } if (loaded.ContainsKey(dependency.Key)) { if (dependency.Value.Value != "*" && loaded[dependency.Key].Item1.Value != "*" && !dependency.Value.IsSatisfied(loaded[dependency.Key].Item2.Version)) { throw new Exception($"{dependency.Key} was found"); } } var localPath = await adapter.Cache(versionMatch); var plugin = Plugin.Load(Path.Combine(localPath, ConfigurationManager.DefinitionFile)); if (loaded.ContainsKey(dependency.Key)) { loaded.Add(dependency.Key, new Tuple <SDK.Core.Plugins.VersionRange, Plugin>(dependency.Value, plugin)); } top.Add(plugin); // TODO: What should be validated? //if (plugin.Name != dependency.Key) throw new Exception("Downloaded package does not match requested."); //if (plugin.Version != versionMatch) throw new Exception("Downloaded package does not match requested."); nested.AddRange((await StageDefinition(plugin, loaded)).Item1); } return(new Tuple <List <Plugin>, List <Plugin> >(top, nested)); }
/// <inheritdoc /> /// <summary> /// Gets the valid release versions. /// </summary> /// <exception cref="T:System.IO.FileNotFoundException">Unable to find definition file</exception> public async Task <IEnumerable <Version> > GetVersions() { var path = Path.Combine(Path.GetFullPath(this.repo.Path), ConfigurationManager.DefinitionFile); if (!File.Exists(path)) { throw new FileNotFoundException("Unable to find definition file", path); } var definition = Plugin.Load(path); if (definition.Version == null) { return new List <Version> { new Version("*") } } ; return(await Task.FromResult(new List <Version> { new Version(definition.Version.ToString()) })); }
/// <inheritdoc /> /// <summary> /// Downloads and unpacks the specified plugin version. /// </summary> /// <param name="version">The version to download.</param> public async Task Download(Version version) { var src = Path.Combine(Path.GetFullPath(this.repo.Path), this.repo.Path); var dst = Path.Combine(Environment.CurrentDirectory, ConfigurationManager.PluginPath, this.name.Vendor, this.name.Project); var path = Path.Combine(Path.GetFullPath(this.repo.Path), ConfigurationManager.DefinitionFile); if (!File.Exists(path)) { throw new FileNotFoundException("Unable to find definition file", path); } var definition = Plugin.Load(path); var stockFiles = new List <string> { "nfive.yml", "nfive.lock", "README.md", "README", "LICENSE.md", "LICENSE" }; var files = new List <string>(); if (definition.Client != null) { if (definition.Client.Main != null && definition.Client.Main.Any()) { files.AddRange(definition.Client.Main.Select(f => $"{f}.net.dll")); } if (definition.Client.Include != null && definition.Client.Include.Any()) { files.AddRange(definition.Client.Include.Select(f => $"{f}.net.dll")); } if (definition.Client.Files != null && definition.Client.Files.Any()) { files.AddRange(definition.Client.Files); } if (definition.Client.Overlays != null && definition.Client.Overlays.Any()) { files.AddRange(definition.Client.Overlays); } } if (definition.Server != null) { if (definition.Server.Main != null && definition.Server.Main.Any()) { files.AddRange(definition.Server.Main.Select(f => $"{f}.net.dll")); } if (definition.Server.Include != null && definition.Server.Include.Any()) { files.AddRange(definition.Server.Include.Select(f => $"{f}.net.dll")); } } files = files.Distinct().ToList(); foreach (var file in stockFiles) { if (!File.Exists(Path.Combine(src, file))) { continue; } File.Copy(Path.Combine(src, file), Path.Combine(dst, file), true); } foreach (var file in files) { Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(dst, file).Replace(Path.DirectorySeparatorChar, '/'))); File.Copy(Path.Combine(src, file).Replace(Path.DirectorySeparatorChar, '/'), Path.Combine(dst, file).Replace(Path.DirectorySeparatorChar, '/'), true); } await Task.FromResult(0); }
public override async Task <int> Main() { var definition = LoadDefinition(); var graph = LoadGraph(); // New plugins if (this.Plugins.Any()) { foreach (var plugin in this.Plugins) { var input = plugin; // Local install if (Directory.Exists(plugin) && File.Exists(Path.Combine(plugin, ConfigurationManager.DefinitionFile))) { var path = Path.GetFullPath(plugin); var pluginDefinition = Plugin.Load(Path.Combine(path, ConfigurationManager.DefinitionFile)); if (definition.Repositories == null) { definition.Repositories = new List <Repository>(); } definition.Repositories.RemoveAll(r => r.Name == pluginDefinition.Name); definition.Repositories.Add(new Repository { Name = pluginDefinition.Name, Type = "local", Path = path }); input = pluginDefinition.Name; } var parts = input.Split(new[] { '@' }, 2); var name = new Name(parts[0].Trim()); var versionInput = parts.Length == 2 ? parts[1].Trim() : "*"; Models.VersionRange range = null; Version version = null; PartialVersion partial = null; try { partial = new PartialVersion(versionInput); } catch (Exception) { // ignored } var isSpecific = partial?.Major != null && partial.Minor.HasValue && partial.Patch.HasValue; try { range = new Models.VersionRange(versionInput); } catch (Exception) { // ignored } try { version = new Version(versionInput); } catch (Exception) { // ignored } List <Version> versions; try { var adapter = new AdapterBuilder(name, definition).Adapter(); versions = (await adapter.GetVersions()).ToList(); } catch (WebException ex) when((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { Console.WriteLine("Error ".DarkRed(), $"{name}".Red(), " not found.".DarkRed()); return(1); } var versionMatch = range.MaxSatisfying(versions); if (versionMatch == null) { Console.WriteLine("Error ".DarkRed(), $"{name}@{range}".Red(), " not found, available versions: ".DarkRed(), string.Join(" ", versions.Select(v => v.ToString())).Red()); return(1); } if (definition.Dependencies == null) { definition.Dependencies = new Dictionary <Name, SDK.Core.Plugins.VersionRange>(); } definition.Dependencies[name] = new Models.VersionRange("^" + (isSpecific ? partial.ToZeroVersion() : version ?? versionMatch)); Console.WriteLine("+ ", $"{name}@{definition.Dependencies[name]}".White()); } graph = new DefinitionGraph(); await graph.Apply(definition); definition.Save(ConfigurationManager.DefinitionFile); graph.Save(); } else { if (graph != null) { await graph.Apply(); } else { graph = new DefinitionGraph(); await graph.Apply(definition); graph.Save(); } } if (PathManager.IsResource()) { ResourceGenerator.Serialize(graph); } return(0); }
public override async Task <int> Main() { var results = new List <ColorToken[]> { new [] { "NAME".White(), "CURRENT".White(), "WANTED".White(), "LATEST".White() } }; var definition = LoadDefinition(); foreach (var dependency in definition?.Dependencies ?? new Dictionary <Name, VersionRange>()) { var repo = definition?.Repositories?.FirstOrDefault(r => r.Name.ToString() == dependency.Key.ToString()); var adapter = new AdapterBuilder(dependency.Key, repo).Adapter(); var versions = (await adapter.GetVersions()).ToList(); var versionMatch = versions.LastOrDefault(version => dependency.Value.IsSatisfied(version)); if (versionMatch == null) { throw new Exception("No matching version found"); } var current = "MISSING".Red(); ColorToken wanted = versionMatch.ToString(); ColorToken latest = versions.Last().ToString(); var pluginDefinition = new FileInfo(Path.Combine(Environment.CurrentDirectory, ConfigurationManager.PluginPath, dependency.Key.Vendor, dependency.Key.Project, ConfigurationManager.DefinitionFile)); if (pluginDefinition.Exists) { var plugin = Plugin.Load(pluginDefinition.FullName); current = plugin.Version.ToString(); current = current.Text != wanted.Text ? current.Red() : current.Green(); wanted = wanted.Text != latest.Text ? wanted.Red() : wanted.Green(); if (!this.All && current.Text == wanted.Text && wanted.Text == latest.Text) { continue; } } results.Add(new[] { dependency.Key.ToString(), current, wanted, latest }); } if (results.Count < 2) { return(0); } var nameLength = Math.Max(Math.Min(50, results.Max(d => d[0].Text.Length)), 10); var currentLength = Math.Max(Math.Min(20, results.Max(d => d[1].Text.ToString().Length)), 7); var wantedLength = Math.Max(Math.Min(20, results.Max(d => d[2].Text.ToString().Length)), 7); var latestLength = Math.Max(Math.Min(20, results.Max(d => d[3].Text.ToString().Length)), 7); foreach (var result in results) { result[0].Text = result[0].Text.Truncate(nameLength).PadRight(nameLength); result[1].Text = result[1].Text.Truncate(currentLength).PadLeft(currentLength); result[2].Text = result[2].Text.Truncate(wantedLength).PadLeft(wantedLength); result[3].Text = result[3].Text.Truncate(latestLength).PadLeft(latestLength); Console.WriteLine(result[0], " | ", result[1], " | ", result[2], " | ", result[3]); } return(await Task.FromResult(0)); }
public async Task Apply(Plugin baseDefinition = null) { if (baseDefinition != null) { var results = await StageDefinition(baseDefinition); var top = results.Item1; var all = top.Concat(results.Item2).Distinct().ToList(); foreach (var plugin in top.Where(d => d.Dependencies != null)) { foreach (var dependency in plugin.Dependencies) { var dependencyPlugin = all.FirstOrDefault(p => p.Name == dependency.Key); if (dependencyPlugin == null) { throw new Exception($"Unable to find dependency {dependency.Key}@{dependency.Value} required by {plugin.Name}@{plugin.Version}"); // TODO: DependencyException } if (!dependency.Value.IsSatisfied(dependencyPlugin.Version)) { throw new Exception($"{plugin.Name}@{plugin.Version} requires {dependencyPlugin.Name}@{dependency.Value} but {dependencyPlugin.Name}@{dependencyPlugin.Version} was found"); } if (plugin.DependencyNodes == null) { plugin.DependencyNodes = new List <Plugin>(); } plugin.DependencyNodes.Add(dependencyPlugin); } } this.Plugins = top; } this.Plugins = Sort(this.Plugins); // TODO: Don't store nested dependencies but still load them foreach (var definition in this.Plugins) { var path = Path.Combine(Environment.CurrentDirectory, ConfigurationManager.PluginPath, definition.Name.Vendor, definition.Name.Project, ConfigurationManager.DefinitionFile); if (File.Exists(path)) { var loadedDefinition = Plugin.Load(path); // TODO: IEquality if (loadedDefinition.Name.ToString() == definition.Name.ToString() && loadedDefinition.Version.ToString() == definition.Version.ToString() && baseDefinition?.Repositories?.FirstOrDefault(r => r.Name == definition.Name && r.Type == "local") == null) { continue; } } // TODO: Remove extra plugin folders/files // Missing or outdated var dir = Path.Combine(Environment.CurrentDirectory, ConfigurationManager.PluginPath, definition.Name.Vendor, definition.Name.Project); if (Directory.Exists(dir)) { DeleteDirectory(dir); } Directory.CreateDirectory(dir); var repo = baseDefinition?.Repositories?.FirstOrDefault(r => r.Name == definition.Name); var adapter = new AdapterBuilder(definition.Name, repo).Adapter(); await adapter.Download(new Version(definition.Version.ToString())); var dependencyDefinition = Plugin.Load(Path.Combine(dir, ConfigurationManager.DefinitionFile)); if (dependencyDefinition.Name != definition.Name) { throw new Exception("Downloaded package does not match requested."); } if (dependencyDefinition.Version.ToString() != definition.Version.ToString()) { throw new Exception("Downloaded package does not match requested."); } var configSrc = Path.Combine(dir, ConfigurationManager.ConfigurationPath); var configDst = Path.Combine(Environment.CurrentDirectory, ConfigurationManager.ConfigurationPath, definition.Name.Vendor, definition.Name.Project); if (!Directory.Exists(configSrc)) { continue; } if (!Directory.Exists(configDst)) { Directory.CreateDirectory(configDst); } // TODO: More files? // TODO: Ask user to replace foreach (var yml in Directory.EnumerateFiles(configSrc, "*.yml", SearchOption.TopDirectoryOnly)) { try { File.Copy(yml, Path.Combine(configDst, Path.GetFileName(yml)), false); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } }