using System; using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Invocation; using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Security.Cryptography; using UpToolLib; using UpToolLib.DataStructures; using UpToolLib.Tool; using Process = System.Diagnostics.Process; namespace UpToolCLI { public static class Program { private static readonly UtLibFunctions Functions = new UtLibFunctions(); public static int Main(string[] args) { MutexLock.Lock(); try { XmlTool.FixXml(); ExternalFunctionalityManager.Init(Functions); RootCommand rootCommand = new RootCommand(); rootCommand.AddCommand(new Command("update", "Updates the cache") { Handler = CommandHandler.Create(Update) }); Command install = new Command("install", "Install a package") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app or the file name"), new Option(new[] {"--force", "-f"}, "Overwrites older files") }; install.Handler = CommandHandler.Create(Install); rootCommand.AddCommand(install); Command upgrade = new Command("upgrade", "Upgrade a package") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app"), new Option(new[] {"--force", "-f"}, "Overwrites older files") }; upgrade.Handler = CommandHandler.Create(Upgrade); rootCommand.AddCommand(upgrade); Command command = new Command("upgrade-self", "Upgrades UpToolCLI") { new Option(new[] {"--force", "-f"}, "Overwrites older files") }; command.Handler = CommandHandler.Create(UpgradeSelf); rootCommand.AddCommand(command); Command reinstall = new Command("reinstall", "Reinstall a package") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app"), new Option(new[] {"--force", "-f"}, "Overwrites older files") }; reinstall.Handler = CommandHandler.Create(Reinstall); rootCommand.AddCommand(reinstall); Command remove = new Command("remove", "Remove a package") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app") }; remove.Handler = CommandHandler.Create(Remove); rootCommand.AddCommand(remove); Command purge = new Command("purge", "Completely remove a package") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app") }; purge.Handler = CommandHandler.Create(Purge); rootCommand.AddCommand(purge); rootCommand.AddCommand(new Command("list", "Lists installed packages") { Handler = CommandHandler.Create(List) }); rootCommand.AddCommand(new Command("dist-upgrade", "Upgrades all packages") { Handler = CommandHandler.Create(DistUpgrade) }); Command search = new Command("search", "Search for packages") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app") }; search.Handler = CommandHandler.Create(Search); rootCommand.AddCommand(search); Command show = new Command("show", "Shows package info") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app") }; show.Handler = CommandHandler.Create(Show); rootCommand.AddCommand(show); Command start = new Command("start", "Starts an app") { new Option(new[] {"--identifier", "-i"}, "Something to identify the app"), new Option(new[] {"--waitForExit", "-wait"}, "Waits until the program quits") }; start.Handler = CommandHandler.Create(Start); rootCommand.AddCommand(start); return rootCommand.InvokeAsync(args).Result; } finally { MutexLock.Unlock(); } } private static void UpgradeSelf(bool force) { #if DEBUG Console.WriteLine("Not enabled in debug builds"); #else if (!force && Assembly.GetExecutingAssembly().GetName().Version >= UpdateCheck.OnlineVersion) Console.WriteLine("Already up-to-date"); else { Console.WriteLine("Downloading latest"); (bool success, byte[] dl) = Functions.Download(UpdateCheck.Installer); if (!success) throw new Exception("Failed to update"); Console.WriteLine("Verifying"); using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) { string pkgHash = BitConverter.ToString(sha256.ComputeHash(dl)).Replace("-", string.Empty).ToUpper(); if (pkgHash != UpdateCheck.InstallerHash) throw new Exception($@"The hash is not equal to the one stored in the repo: Package: {pkgHash} Online: {UpdateCheck.InstallerHash}"); } Console.WriteLine("Installing"); if (Directory.Exists(PathTool.GetRelative("Install", "tmp"))) Directory.Delete(PathTool.GetRelative("Install", "tmp"), true); Directory.CreateDirectory(PathTool.GetRelative("Install", "tmp")); using (MemoryStream ms = new MemoryStream(dl)) { using ZipArchive ar = new ZipArchive(ms); ar.ExtractToDirectory(PathTool.GetRelative("Install", "tmp"), true); } string file = PathTool.GetRelative("Install", "tmp", "Installer.exe"); Console.WriteLine($"Starting {file}"); Process.Start(new ProcessStartInfo { FileName = file, Arguments = "-i", WorkingDirectory = PathTool.GetRelative("Install"), UseShellExecute = false }); } #endif } private static void Update() { Console.WriteLine("Fetching Repos..."); RepoManagement.FetchRepos(); RepoManagement.GetReposFromDisk(); Console.WriteLine(); IEnumerable tmp = GlobalVariables.Apps.Where(s => (s.Value.Status & Status.Updatable) == Status.Updatable).Select(s => s.Value); IEnumerable apps = tmp as App[] ?? tmp.ToArray(); int updatableCount = apps.Count(); Console.WriteLine(updatableCount == 0 ? "All up-to-date" : $@"Found {updatableCount} Updates: {string.Join(Environment.NewLine, apps.Select(s => $"- {s.Name} ({s.Version})"))}"); #if !DEBUG Version vLocal = Assembly.GetExecutingAssembly().GetName().Version; Version vOnline = UpdateCheck.OnlineVersion; if (vLocal < vOnline) Console.WriteLine($"uptool is outdated ({vLocal} vs {vOnline}), update using \"uptool upgrade-self\""); #endif } private static void List() { RepoManagement.GetReposFromDisk(); foreach (KeyValuePair app in GlobalVariables.Apps.Where(s => (s.Value.Status & Status.Installed) == Status.Installed)) { Console.BackgroundColor = (app.Value.Status & Status.Local) == Status.Local ? ConsoleColor.DarkRed : (app.Value.Status & Status.Updatable) == Status.Updatable ? ConsoleColor.DarkGreen : ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.White; Console.WriteLine($"{app.Value.Name} ({app.Key})"); } Console.ResetColor(); } private static void DistUpgrade() { RepoManagement.GetReposFromDisk(); foreach (KeyValuePair app in GlobalVariables.Apps.Where(s => (s.Value.Status & Status.Updatable) == Status.Updatable)) { Console.WriteLine($"Updating {app.Value.Name}"); AppExtras.Update(app.Value, false); } #if !DEBUG if (Assembly.GetExecutingAssembly().GetName().Version < UpdateCheck.OnlineVersion) { Console.WriteLine("Updating self"); UpgradeSelf(false); } #endif Console.WriteLine("Done!"); } private static void Show(string identifier) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); if (apps.Length == 0) Console.WriteLine("Package not found."); else Console.WriteLine(apps.First()); } private static void Search(string identifier) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); Console.WriteLine($"Found {apps.Length} app(s)"); for (int i = 0; i < apps.Length; i++) Console.WriteLine($"{apps[i].Name} ({apps[i].Id})"); } private static void Upgrade(string identifier, bool force) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); if (apps.Length == 0) Console.WriteLine("Package not found."); else { App tmp = apps.First(); if ((tmp.Status & Status.Updatable) == Status.Updatable) { Console.WriteLine($"Upgrading {tmp.Name}"); AppExtras.Update(tmp, force); } else Console.WriteLine("Package is up-to-date"); } Console.WriteLine("Done!"); } private static void Reinstall(string identifier, bool force) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); if (apps.Length == 0) Console.WriteLine("Package not found."); else { App tmp = apps.First(); Console.WriteLine($"Reinstalling {tmp.Name}"); AppExtras.Update(tmp, force); } Console.WriteLine("Done!"); } private static void Remove(string identifier) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); if (apps.Length == 0) Console.WriteLine("Package not found."); else { App tmp = apps.First(); if ((tmp.Status & Status.Installed) == Status.Installed) { Console.WriteLine($"Removing {tmp.Name}"); AppExtras.Remove(tmp, false); } else Console.WriteLine("Package is not installed"); } Console.WriteLine("Done!"); } private static void Purge(string identifier) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); if (apps.Length == 0) Console.WriteLine("Package not found."); else { App tmp = apps.First(); if ((tmp.Status & Status.Installed) == Status.Installed) { Console.WriteLine($"Purgeing {tmp.Name}"); AppExtras.Remove(tmp, true); } else Console.WriteLine("Package is not installed"); } Console.WriteLine("Done!"); } private static void Install(string identifier, bool force) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); if (apps.Length == 0) { if (File.Exists(identifier)) { Console.WriteLine("Name:"); string name = Console.ReadLine(); AppInstall.InstallZip(identifier, new App(name, "Locally installed package, removal only", GlobalVariables.MinimumVer, "", true, "", Guid.NewGuid(), Color.Red, "", false, ""), force); Console.WriteLine($"Successfully installed \"{name}\""); } else { Console.WriteLine("Package not found."); Console.WriteLine(identifier); } } else { App tmp = apps.First(); if ((tmp.Status & Status.Installed) == Status.Installed) Console.WriteLine("Package is already installed"); else { Console.WriteLine($"Installing {tmp.Name}"); AppInstall.Install(tmp, true); } } Console.WriteLine("Done!"); } private static void Start(string identifier, bool waitForExit) { RepoManagement.GetReposFromDisk(); App[] apps = AppExtras.FindApps(identifier); if (apps.Length == 0) Console.WriteLine("Package not found."); else { App tmp = apps.First(); if (tmp.Runnable) { Console.WriteLine($"Starting {tmp.Name}"); Process tmp1 = AppExtras.RunApp(tmp); if (waitForExit) tmp1.WaitForExit(); } else Console.WriteLine($"{tmp.Name} is not runnable"); } Console.WriteLine("Done!"); } } }