// Install a package public void InstallPackage(IPackage package, Action onComplete = null) { downloadThreads.Add(RunThread(() => { if (!isSafeToProcess()) { return; } Info("Preparing Installation"); OnProcessing(); IsDownloading = true; try { store.InstallPackage(package.Id, package.Version); } catch (Exception) { ShowErrorDialog("Could not retrieve package!"); } IsDownloading = false; SdkVersionManager.synchronizeData(); OnSDKVersionsUpdated(); Info(""); OnIdle(); if (onComplete != null) { onComplete(); } })); }
private static async Task UpdateLauncherFiles(IDispatcherService dispatcher, IDialogService dialogService, NugetStore store, CancellationToken cancellationToken) { var version = new PackageVersion(Version); var productAttribute = (typeof(SelfUpdater).Assembly).GetCustomAttribute <AssemblyProductAttribute>(); var packageId = productAttribute.Product; var packages = (await store.GetUpdates(new PackageName(packageId, version), true, true, cancellationToken)).OrderBy(x => x.Version); try { // First, check if there is a package forcing us to download new installer const string ReinstallUrlPattern = @"force-reinstall:\s*(\S+)\s*(\S+)"; var reinstallPackage = packages.LastOrDefault(x => x.Version > version && Regex.IsMatch(x.Description, ReinstallUrlPattern)); if (reinstallPackage != null) { var regexMatch = Regex.Match(reinstallPackage.Description, ReinstallUrlPattern); var minimumVersion = PackageVersion.Parse(regexMatch.Groups[1].Value); if (version < minimumVersion) { var installerDownloadUrl = regexMatch.Groups[2].Value; await DownloadAndInstallNewVersion(dispatcher, dialogService, installerDownloadUrl); return; } } } catch (Exception e) { await dialogService.MessageBox(string.Format(Strings.NewVersionDownloadError, e.Message), MessageBoxButton.OK, MessageBoxImage.Error); } // If there is a mandatory intermediate upgrade, take it, otherwise update straight to latest version var package = (packages.FirstOrDefault(x => x.Version > version && x.Version.SpecialVersion == "req") ?? packages.LastOrDefault()); // Check to see if an update is needed if (package != null && version < new PackageVersion(package.Version.Version, package.Version.SpecialVersion)) { var windowCreated = new TaskCompletionSource <SelfUpdateWindow>(); var mainWindow = dispatcher.Invoke(() => Application.Current.MainWindow as LauncherWindow); if (mainWindow == null) { throw new ApplicationException("Update requested without a Launcher Window. Cannot continue!"); } dispatcher.InvokeAsync(() => { selfUpdateWindow = new SelfUpdateWindow { Owner = mainWindow }; windowCreated.SetResult(selfUpdateWindow); selfUpdateWindow.ShowDialog(); }).Forget(); var movedFiles = new List <string>(); // Download package var installedPackage = await store.InstallPackage(package.Id, package.Version, null); // Copy files from tools\ to the current directory var inputFiles = installedPackage.GetFiles(); var window = windowCreated.Task.Result; dispatcher.Invoke(window.LockWindow); // TODO: We should get list of previous files from nuspec (store it as a resource and open it with NuGet API maybe?) // TODO: For now, we deal only with the App.config file since we won't be able to fix it afterward. var exeLocation = Launcher.GetExecutablePath(); var exeDirectory = Path.GetDirectoryName(exeLocation); const string directoryRoot = "tools/"; // Important!: this is matching where files are store in the nuspec try { if (File.Exists(exeLocation)) { Move(exeLocation, exeLocation + ".old"); movedFiles.Add(exeLocation); } var configLocation = exeLocation + ".config"; if (File.Exists(configLocation)) { Move(configLocation, configLocation + ".old"); movedFiles.Add(configLocation); } foreach (var file in inputFiles.Where(file => file.Path.StartsWith(directoryRoot) && !file.Path.EndsWith("/"))) { var fileName = Path.Combine(exeDirectory, file.Path.Substring(directoryRoot.Length)); // Move previous files to .old if (File.Exists(fileName)) { Move(fileName, fileName + ".old"); movedFiles.Add(fileName); } // Update the file UpdateFile(fileName, file); } } catch (Exception) { // Revert all olds files if a file didn't work well foreach (var oldFile in movedFiles) { Move(oldFile + ".old", oldFile); } throw; } // Remove .old files foreach (var oldFile in movedFiles) { try { var renamedPath = oldFile + ".old"; if (File.Exists(renamedPath)) { File.Delete(renamedPath); } } catch (Exception) { // All the files have been replaced, we let it go even if we cannot remove all the old files. } } // Clean cache from files obtain via package.GetFiles above. store.PurgeCache(); dispatcher.Invoke(RestartApplication); } }
public int Run(string[] args) { // Start self update downloadThreads.Add(RunThread(SelfUpdate)); DebugStep("SelfUpdate launched"); // Find locally installed package var installedPackage = FindLatestInstalledPackage(store.Manager.LocalRepository); DebugStep("Find installed package"); // Do we have a package in the cache? var cachePackage = FindLatestInstalledPackage(MachineCache.Default); DebugStep("Find cache package"); // If a package is installed if (installedPackage != null) { if (cachePackage != null && cachePackage.Version > installedPackage.Version) { var processCount = GetProcessCount(); bool isSafeToUpdate = processCount <= 1; // If we are safe to update, install the new package if (isSafeToUpdate) { IsDownloading = true; Info("Preparing installer for new {0} version {1}", mainPackage, cachePackage.Version); PackageUpdate(installedPackage, false); IsDownloading = false; DebugStep("Update package"); } else { ShowInformationDialog( string.Format("Cannot update {0} as there are [{1}] instances currently running.\n\nClose all your applications and restart", mainPackage, processCount)); } } else { DebugStep("Start download package in cache"); var localPackage = installedPackage; downloadThreads.Add(RunThread(() => PackageUpdate(localPackage, true))); } } else { if (store.CheckSource()) { //store.Manager.InstallPackage(mainPackage); IsDownloading = true; Info("Preparing installer for {0}", mainPackage); store.InstallPackage(mainPackage, null); IsDownloading = false; DebugStep("Package installed"); } else { ShowErrorDialog("Download server not available. Please try again later"); return(1); } } installedPackage = FindLatestInstalledPackage(store.Manager.LocalRepository); if (installedPackage == null) { ShowErrorDialog("No package installed"); return(1); } // Load the assembly and call the default entry point: var fullExePath = GetMainExecutable(installedPackage); Environment.CurrentDirectory = Path.GetDirectoryName(fullExePath); var appDomainSetup = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(fullExePath) }; var newAppDomain = AppDomain.CreateDomain("LauncherAppDomain", null, appDomainSetup); DebugStep("Run executable"); OnLoading(new LoadingEventArgs(installedPackage.Id, installedPackage.Version.ToString())); var newArgList = new List <string> { "/LauncherWindowHandle", MainWindowHandle.ToInt64().ToString(CultureInfo.InvariantCulture) }; newArgList.AddRange(args); try { return(newAppDomain.ExecuteAssembly(fullExePath, newArgList.ToArray())); } finally { // Important!: Force back current directory to this application as previous ExecuteAssembly is changing it Environment.CurrentDirectory = Path.GetDirectoryName(typeof(LauncherApp).Assembly.Location); // Note: The AppDomain.Unload method can block indefinitely when a crash occurs in the UI //try //{ // AppDomain.Unload(newAppDomain); //} //catch (Exception) //{ //} } }
public async Task CheckDeveloperTargetRedirects(string id, PackageVersion version, string realPath) { var nupkgFile = Path.Combine(Environment.ExpandEnvironmentVariables(store.DevSource), $"{id}.{version}.nupkg"); var isRedirectToCurrentPath = false; var installedPackage = store.GetLocalPackages(id).FirstOrDefault(x => x.Version == version); if (installedPackage != null) { var redirectFile = Path.Combine(installedPackage.Path, $@"{id}.redirect"); if (File.Exists(redirectFile) && String.Compare(File.ReadAllText(redirectFile), realPath, StringComparison.OrdinalIgnoreCase) == 0) { isRedirectToCurrentPath = true; } } // Note: later, we could do better and check existing package contents and scan for differences? // We could also delete older packages using same path if (File.Exists(nupkgFile) && isRedirectToCurrentPath) { return; } Directory.CreateDirectory(Path.GetDirectoryName(nupkgFile)); var builder = new NugetPackageBuilder(); var meta = new PackageMeta { Name = id, Version = version, Authors = { $"{id} developers" }, Description = $"{id} developer package using {realPath}", }; var nugetMeta = new ManifestMetadata(); ToNugetManifest(meta, nugetMeta); builder.Populate(nugetMeta); // Note: path must exist (created in NugetStore ctor) using (var tempDirectory = new TemporaryDirectory()) { // Generate fake files Directory.CreateDirectory(Path.Combine(tempDirectory.DirectoryPath, "build")); var files = new List <ManifestFile>(); foreach (var buildFileExtension in new[] { "targets", "props" }) { var source = Path.Combine(tempDirectory.DirectoryPath, $@"build\{id}.{buildFileExtension}"); File.WriteAllText(source, $@"<?xml version=""1.0"" encoding=""utf-8"" ?> <Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" > <Import Project=""{realPath}\Targets\Xenko.{buildFileExtension}"" /> </Project> "); files.Add(new ManifestFile { Source = source, Target = $@"build\{id}.{buildFileExtension}" }); } var redirectFile = Path.Combine(tempDirectory.DirectoryPath, $"{id}.redirect"); File.WriteAllText(redirectFile, realPath); files.Add(new ManifestFile { Source = redirectFile, Target = $@"{id}.redirect" }); builder.PopulateFiles(".", files); using (var nupkgStream = File.OpenWrite(nupkgFile)) { builder.Save(nupkgStream); } } // If package is already installed in cache so that it will force reinstallation. if (installedPackage != null) { await store.UninstallPackage(installedPackage, null); } await store.InstallPackage(id, version, null); }