public async Task FullUninstall() { var currentRelease = getReleases().MaxBy(x => x.Name.ToVersion()).FirstOrDefault(); this.Log().Info("Starting full uninstall"); if (currentRelease.Exists) { var version = currentRelease.Name.ToVersion(); try { var squirrelAwareApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(currentRelease.FullName); if (squirrelAwareApps.Count > 0) { await squirrelAwareApps.ForEachAsync(exe => Utility.InvokeProcessAsync(exe, String.Format("--squirrel-uninstall {0}", version)), 1); } else { var allApps = currentRelease.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .ToList(); allApps.ForEach(x => RemoveShortcutsForExecutable(x.Name, ShortcutLocation.StartMenu | ShortcutLocation.Desktop)); } } catch (Exception ex) { this.Log().WarnException("Failed to run pre-uninstall hooks, uninstalling anyways", ex); } } await this.ErrorIfThrows(() => Utility.DeleteDirectoryWithFallbackToNextReboot(rootAppDirectory), "Failed to delete app directory: " + rootAppDirectory); }
async Task invokePostInstall(SemanticVersion currentVersion, bool isInitialInstall, bool firstRunOnly, bool silentInstall) { var targetDir = getDirectoryForRelease(currentVersion); var args = isInitialInstall ? String.Format("--squirrel-install {0}", currentVersion) : String.Format("--squirrel-updated {0}", currentVersion); var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(targetDir.FullName); this.Log().Info("Squirrel Enabled Apps: [{0}]", String.Join(",", squirrelApps)); // For each app, run the install command in-order and wait if (!firstRunOnly) { await squirrelApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(45 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { this.Log().ErrorException("Couldn't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); } // If this is the first run, we run the apps with first-run and // *don't* wait for them, since they're probably the main EXE if (squirrelApps.Count == 0) { this.Log().Warn("No apps are marked as Squirrel-aware! Going to run them all"); squirrelApps = targetDir.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase)) .Select(x => x.FullName) .ToList(); // Create shortcuts for apps automatically if they didn't // create any Squirrel-aware apps squirrelApps.ForEach(x => CreateShortcutsForExecutable(Path.GetFileName(x), ShortcutLocations.Defaults, isInitialInstall == false, null, null)); } if (!isInitialInstall || silentInstall) { return; } var firstRunParam = isInitialInstall ? "--squirrel-firstrun" : ""; squirrelApps .Select(exe => new ProcessStartInfo(exe, firstRunParam) { WorkingDirectory = Path.GetDirectoryName(exe) }) .ForEach(info => { var p = Process.Start(info); this.Log().Info("ran {0}, pid {1}", info.FileName, p.Id); }); }
public async Task FullUninstall() { var currentRelease = getReleases().MaxBy(x => x.Name.ToVersion()).FirstOrDefault(); this.Log().Info("Starting full uninstall"); if (currentRelease.Exists) { var version = currentRelease.Name.ToVersion(); try { var squirrelAwareApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(currentRelease.FullName); if (isAppFolderDead(currentRelease.FullName)) { throw new Exception("App folder is dead, but we're trying to uninstall it?"); } if (squirrelAwareApps.Count > 0) { await squirrelAwareApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(10 * 1000); try { await Utility.InvokeProcessAsync(exe, String.Format("--squirrel-uninstall {0}", version), cts.Token); } catch (Exception ex) { this.Log().ErrorException("Failed to run cleanup hook, continuing: " + exe, ex); } } }, 1 /*at a time*/); } else { var allApps = currentRelease.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase)) .ToList(); allApps.ForEach(x => RemoveShortcutsForExecutable(x.Name, ShortcutLocation.StartMenu | ShortcutLocation.Desktop)); } } catch (Exception ex) { this.Log().WarnException("Failed to run pre-uninstall hooks, uninstalling anyways", ex); } } fixPinnedExecutables(new Version(255, 255, 255, 255)); await this.ErrorIfThrows(() => Utility.DeleteDirectoryWithFallbackToNextReboot(rootAppDirectory), "Failed to delete app directory: " + rootAppDirectory); // NB: We drop this file here so that --checkInstall will ignore // this folder - if we don't do this, users who "accidentally" run as // administrator will find the app reinstalling itself on every // reboot File.WriteAllText(Path.Combine(rootAppDirectory, ".dead"), " "); }
async Task invokePostInstall(Version currentVersion, bool isInitialInstall, bool firstRunOnly, bool silentInstall) { var targetDir = getDirectoryForRelease(currentVersion); var args = isInitialInstall ? String.Format("--squirrel-install {0}", currentVersion) : String.Format("--squirrel-updated {0}", currentVersion); var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(targetDir.FullName); this.Log().Info("Squirrel Enabled Apps: [{0}]", String.Join(",", squirrelApps)); // For each app, run the install command in-order and wait if (!firstRunOnly) { await squirrelApps.ForEachAsync(exe => Utility.InvokeProcessAsync(exe, args), 1 /* at a time */); } // If this is the first run, we run the apps with first-run and // *don't* wait for them, since they're probably the main EXE if (squirrelApps.Count == 0) { this.Log().Warn("No apps are marked as Squirrel-aware! Going to run them all"); squirrelApps = targetDir.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase)) .Select(x => x.FullName) .ToList(); // Create shortcuts for apps automatically if they didn't // create any Squirrel-aware apps squirrelApps.ForEach(x => CreateShortcutsForExecutable(Path.GetFileName(x), ShortcutLocation.Desktop | ShortcutLocation.StartMenu, isInitialInstall == false)); } if (!isInitialInstall || silentInstall) { return; } var firstRunParam = isInitialInstall ? "--squirrel-firstrun" : ""; squirrelApps .Select(exe => new ProcessStartInfo(exe, firstRunParam) { WorkingDirectory = Path.GetDirectoryName(exe) }) .ForEach(info => Process.Start(info)); }
public async Task FullUninstall() { var currentRelease = getReleases().MaxBy(x => x.Name.ToVersion()).FirstOrDefault(); this.Log().Info("Starting full uninstall"); if (currentRelease.Exists) { var version = currentRelease.Name.ToVersion(); try { var squirrelAwareApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(currentRelease.FullName); if (isAppFolderDead(currentRelease.FullName)) { throw new Exception("App folder is dead, but we're trying to uninstall it?"); } if (squirrelAwareApps.Count > 0) { await squirrelAwareApps.ForEachAsync(async exe => { var cts = new CancellationTokenSource(); cts.CancelAfter(10 * 1000); try { await Utility.InvokeProcessAsync(exe, String.Format("--squirrel-uninstall {0}", version), cts.Token); } catch (Exception ex) { this.Log().ErrorException("Failed to run cleanup hook, continuing: " + exe, ex); } }, 1 /*at a time*/); } else { var allApps = currentRelease.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase)) .ToList(); allApps.ForEach(x => RemoveShortcutsForExecutable(x.Name, ShortcutLocation.StartMenu | ShortcutLocation.Desktop)); } } catch (Exception ex) { this.Log().WarnException("Failed to run pre-uninstall hooks, uninstalling anyways", ex); } } await this.ErrorIfThrows(() => Utility.DeleteDirectoryWithFallbackToNextReboot(rootAppDirectory), "Failed to delete app directory: " + rootAppDirectory); }
async Task invokePostInstall(SemanticVersion currentVersion, bool isInitialInstall, bool firstRunOnly, bool silentInstall) { var targetDir = getDirectoryForRelease(currentVersion); var args = isInitialInstall ? String.Format("--squirrel-install {0}", currentVersion) : String.Format("--squirrel-updated {0}", currentVersion); var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(targetDir.FullName); this.Log().Info("Squirrel Enabled Apps: [{0}]", String.Join(",", squirrelApps)); // For each app, run the install command in-order and wait if (!firstRunOnly) { await squirrelApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(15 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { this.Log().ErrorException("Couldn't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); } squirrelApps.ForEach(x => CreateShortcutsForExecutable(Path.GetFileName(x), ShortcutLocation.StartMenu, isInitialInstall == false, null, null)); if (!isInitialInstall || silentInstall) { return; } squirrelApps .Select(exe => new ProcessStartInfo(exe) { WorkingDirectory = Path.GetDirectoryName(exe), Arguments = "install-to-path" }) .ForEach(info => Process.Start(info).WaitForExit()); var fuseExe = squirrelApps.First(); var fuseShim = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(fuseExe)), "Bin", "Fuse.exe"); Process.Start(fuseShim); }
public async Task FullUninstall() { var currentRelease = getReleases().MaxBy(x => x.Name.ToVersion()).FirstOrDefault(); this.Log().Info("Starting full uninstall"); if (currentRelease.Exists) { var version = currentRelease.Name.ToVersion(); try { await SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(currentRelease.FullName) .ForEachAsync(exe => Utility.InvokeProcessAsync(exe, String.Format("--squirrel-uninstall {0}", version)), 1); } catch (Exception ex) { this.Log().WarnException("Failed to run pre-uninstall hooks, uninstalling anyways", ex); } } await this.ErrorIfThrows(() => Utility.DeleteDirectoryWithFallbackToNextReboot(rootAppDirectory), "Failed to delete app directory: " + rootAppDirectory); }
async Task invokePostInstall(SemanticVersion currentVersion, bool isInitialInstall, bool firstRunOnly, bool silentInstall) { var targetDir = getDirectoryForRelease(currentVersion); var args = isInitialInstall ? String.Format("--squirrel-install {0}", currentVersion) : String.Format("--squirrel-updated {0}", currentVersion); var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(targetDir.FullName); this.Log().Info("Squirrel Enabled Apps: [{0}]", String.Join(",", squirrelApps)); // For each app, run the install command in-order and wait if (!firstRunOnly) { await squirrelApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(15 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { this.Log().ErrorException("Couldn't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); } if (!isInitialInstall || silentInstall) { return; } var firstRunParam = isInitialInstall ? "--squirrel-firstrun" : ""; squirrelApps .Select(exe => new ProcessStartInfo(exe, firstRunParam) { WorkingDirectory = Path.GetDirectoryName(exe) }) .ForEach(info => Process.Start(info)); }
async Task invokePostInstall(Version currentVersion, bool isInitialInstall, bool firstRunOnly) { var targetDir = getDirectoryForRelease(currentVersion); var args = isInitialInstall ? String.Format("--squirrel-install {0}", currentVersion) : String.Format("--squirrel-updated {0}", currentVersion); var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(targetDir.FullName); this.Log().Info("Squirrel Enabled Apps: [{0}]", String.Join(",", squirrelApps)); // For each app, run the install command in-order and wait if (!firstRunOnly) { await squirrelApps.ForEachAsync(exe => Utility.InvokeProcessAsync(exe, args), 1 /* at a time */); } if (!isInitialInstall) { return; } // If this is the first run, we run the apps with first-run and // *don't* wait for them, since they're probably the main EXE if (squirrelApps.Count == 0) { this.Log().Warn("No apps are marked as Squirrel-aware! Going to run them all"); squirrelApps = targetDir.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Select(x => x.FullName) .ToList(); } squirrelApps.ForEach(exe => Process.Start(exe, "--squirrel-firstrun")); }
// NB: Once we uninstall the old version of the app, we try to schedule // it to be deleted at next reboot. Unfortunately, depending on whether // the user has admin permissions, this can fail. So as a failsafe, // before we try to apply any update, we assume previous versions in the // directory are "dead" (i.e. already uninstalled, but not deleted), and // we blow them away. This is to make sure that we don't attempt to run // an uninstaller on an already-uninstalled version. async Task cleanDeadVersions(SemanticVersion originalVersion, SemanticVersion currentVersion, bool forceUninstall = false) { if (currentVersion == null) { return; } var di = new DirectoryInfo(rootAppDirectory); if (!di.Exists) { return; } this.Log().Info("cleanDeadVersions: for version {0}", currentVersion); string originalVersionFolder = null; if (originalVersion != null) { originalVersionFolder = getDirectoryForRelease(originalVersion).Name; this.Log().Info("cleanDeadVersions: exclude folder {0}", originalVersionFolder); } string currentVersionFolder = null; if (currentVersion != null) { currentVersionFolder = getDirectoryForRelease(currentVersion).Name; this.Log().Info("cleanDeadVersions: exclude folder {0}", currentVersionFolder); } // NB: If we try to access a directory that has already been // scheduled for deletion by MoveFileEx it throws what seems like // NT's only error code, ERROR_ACCESS_DENIED. Squelch errors that // come from here. var toCleanup = di.GetDirectories() .Where(x => x.Name.ToLowerInvariant().Contains("app-")) .Where(x => x.Name != currentVersionFolder && x.Name != originalVersionFolder) .Where(x => !isAppFolderDead(x.FullName)); if (forceUninstall == false) { await toCleanup.ForEachAsync(async x => { var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(x.FullName); var args = String.Format("--squirrel-obsolete {0}", x.Name.Replace("app-", "")); if (squirrelApps.Count > 0) { // For each app, run the install command in-order and wait await squirrelApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(10 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { this.Log().ErrorException("Coudln't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); } }); } // Include dead folders in folders to :fire: toCleanup = di.GetDirectories() .Where(x => x.Name.ToLowerInvariant().Contains("app-")) .Where(x => x.Name != currentVersionFolder && x.Name != originalVersionFolder); // Get the current process list in an attempt to not burn // directories which have running processes var runningProcesses = UnsafeUtility.EnumerateProcesses(); // Finally, clean up the app-X.Y.Z directories await toCleanup.ForEachAsync(async x => { try { if (runningProcesses.All(p => !p.Item1.StartsWith(x.FullName, StringComparison.OrdinalIgnoreCase))) { await Utility.DeleteDirectoryOrJustGiveUp(x.FullName); } if (Directory.Exists(x.FullName)) { // NB: If we cannot clean up a directory, we need to make // sure that anyone finding it later won't attempt to run // Squirrel events on it. We'll mark it with a .dead file markAppFolderAsDead(x.FullName); } } catch (UnauthorizedAccessException ex) { this.Log().WarnException("Couldn't delete directory: " + x.FullName, ex); // NB: Same deal as above markAppFolderAsDead(x.FullName); } }); // Clean up the packages directory too var releasesFile = Utility.LocalReleaseFileForAppDir(rootAppDirectory); var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesFile, Encoding.UTF8)); var pkgDir = Utility.PackageDirectoryForAppDir(rootAppDirectory); var releaseEntry = default(ReleaseEntry); foreach (var entry in entries) { if (entry.Version == currentVersion) { releaseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(pkgDir, entry.Filename)); continue; } File.Delete(Path.Combine(pkgDir, entry.Filename)); } ReleaseEntry.WriteReleaseFile(new[] { releaseEntry }, releasesFile); }
async Task invokePostInstall(SemanticVersion currentVersion, bool isInitialInstall, bool firstRunOnly, bool silentInstall) { var targetDir = getDirectoryForRelease(currentVersion); var args = isInitialInstall ? String.Format("--squirrel-install {0}", currentVersion) : String.Format("--squirrel-updated {0}", currentVersion); var incomingArgs = Environment.GetCommandLineArgs(); var pathKeyArgIndex = Array.FindIndex(incomingArgs, arg => String.Equals(arg, "--installer-path")); if (pathKeyArgIndex != -1 && incomingArgs.Length > pathKeyArgIndex + 1) { var installerPath = incomingArgs[pathKeyArgIndex + 1]; args += String.Format(" --squirrel-installer-path \"{0}\"", installerPath); } this.Log().Info("Incoming args to UpdateManager process: {0}", String.Join(",", incomingArgs)); this.Log().Info("Running app with args: {0}", args); var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(targetDir.FullName); this.Log().Info("Squirrel Enabled Apps: [{0}]", String.Join(",", squirrelApps)); // For each app, run the install command in-order and wait if (!firstRunOnly) { await squirrelApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(15 * 1000); try { await Utility.InvokeProcessAsync(exe, args, cts.Token); } catch (Exception ex) { this.Log().ErrorException("Couldn't run Squirrel hook, continuing: " + exe, ex); } } }, 1 /* at a time */); } // If this is the first run, we run the apps with first-run and // *don't* wait for them, since they're probably the main EXE if (squirrelApps.Count == 0) { this.Log().Warn("No apps are marked as Squirrel-aware! Going to run them all"); squirrelApps = targetDir.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase)) .Select(x => x.FullName) .ToList(); // Create shortcuts for apps automatically if they didn't // create any Squirrel-aware apps squirrelApps.ForEach(x => CreateShortcutsForExecutable(Path.GetFileName(x), ShortcutLocation.Desktop | ShortcutLocation.StartMenu, isInitialInstall == false, null, null)); } if (!isInitialInstall || silentInstall) { return; } var firstRunParam = isInitialInstall ? "--squirrel-firstrun" : ""; squirrelApps .Select(exe => new ProcessStartInfo(exe, firstRunParam) { WorkingDirectory = Path.GetDirectoryName(exe) }) .ForEach(info => Process.Start(info)); }
public async Task FullUninstall() { var currentRelease = getReleases().MaxBy(x => x.Name.ToSemanticVersion()).FirstOrDefault(); this.Log().Info("Starting full uninstall"); if (currentRelease.Exists) { var version = currentRelease.Name.ToSemanticVersion(); try { var squirrelAwareApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(currentRelease.FullName); if (isAppFolderDead(currentRelease.FullName)) { throw new Exception("App folder is dead, but we're trying to uninstall it?"); } var allApps = currentRelease.EnumerateFiles() .Where(x => x.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) .Where(x => !x.Name.StartsWith("squirrel.", StringComparison.OrdinalIgnoreCase) && !x.Name.StartsWith("update.", StringComparison.OrdinalIgnoreCase)) .ToList(); if (squirrelAwareApps.Count > 0) { await squirrelAwareApps.ForEachAsync(async exe => { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(10 * 1000); try { await Utility.InvokeProcessAsync(exe, String.Format("--squirrel-uninstall {0}", version), cts.Token); } catch (Exception ex) { this.Log().ErrorException("Failed to run cleanup hook, continuing: " + exe, ex); } } }, 1 /*at a time*/); } else { allApps.ForEach(x => RemoveShortcutsForExecutable(x.Name, ShortcutLocations.Defaults)); } // NB: Some people attempt to uninstall apps while // they're still running. I cannot even. var toKill = allApps .SelectMany(x => Process.GetProcessesByName(x.Name.Replace(".exe", ""))) .ToList(); if (toKill.Count > 0) { toKill.ForEach(x => x.Kill()); Thread.Sleep(750); } } catch (Exception ex) { this.Log().WarnException("Failed to run pre-uninstall hooks, uninstalling anyways", ex); } } fixPinnedExecutables(new SemanticVersion(255, 255, 255, 255)); bool didSucceedDeleting = false; const int retryAttempts = 10; for (int i = 0; i < retryAttempts; ++i) { try { await Utility.DeleteDirectory(rootAppDirectory); didSucceedDeleting = true; } catch (Exception) { Thread.Sleep(1000); // Give the OS a second to release handles and we'll try again } } if (!didSucceedDeleting) { await this.ErrorIfThrows(() => Utility.DeleteDirectoryOrJustGiveUp(rootAppDirectory), "Failed to delete app directory: " + rootAppDirectory); } // NB: We drop this file here so that --checkInstall will ignore // this folder - if we don't do this, users who "accidentally" run as // administrator will find the app reinstalling itself on every // reboot File.WriteAllText(Path.Combine(rootAppDirectory, ".dead"), " "); }
// NB: Once we uninstall the old version of the app, we try to schedule // it to be deleted at next reboot. Unfortunately, depending on whether // the user has admin permissions, this can fail. So as a failsafe, // before we try to apply any update, we assume previous versions in the // directory are "dead" (i.e. already uninstalled, but not deleted), and // we blow them away. This is to make sure that we don't attempt to run // an uninstaller on an already-uninstalled version. async Task cleanDeadVersions(Version originalVersion, Version currentVersion, bool forceUninstall = false) { if (currentVersion == null) { return; } var di = new DirectoryInfo(rootAppDirectory); if (!di.Exists) { return; } this.Log().Info("cleanDeadVersions: for version {0}", currentVersion); string originalVersionFolder = null; if (originalVersion != null) { originalVersionFolder = getDirectoryForRelease(originalVersion).Name; this.Log().Info("cleanDeadVersions: exclude folder {0}", originalVersionFolder); } string currentVersionFolder = null; if (currentVersion != null) { currentVersionFolder = getDirectoryForRelease(currentVersion).Name; this.Log().Info("cleanDeadVersions: exclude folder {0}", currentVersionFolder); } // NB: If we try to access a directory that has already been // scheduled for deletion by MoveFileEx it throws what seems like // NT's only error code, ERROR_ACCESS_DENIED. Squelch errors that // come from here. var toCleanup = di.GetDirectories() .Where(x => x.Name.ToLowerInvariant().Contains("app-")) .Where(x => x.Name != currentVersionFolder && x.Name != originalVersionFolder); if (forceUninstall == false) { await toCleanup.ForEachAsync(async x => { var squirrelApps = SquirrelAwareExecutableDetector.GetAllSquirrelAwareApps(x.FullName); var args = String.Format("--squirrel-obsolete {0}", x.Name.Replace("app-", "")); if (squirrelApps.Count > 0) { // For each app, run the install command in-order and wait await squirrelApps.ForEachAsync(exe => Utility.InvokeProcessAsync(exe, args), 1 /* at a time */); } }); } // Finally, clean up the app-X.Y.Z directories await toCleanup.ForEachAsync(async x => { try { await Utility.DeleteDirectoryWithFallbackToNextReboot(x.FullName); } catch (UnauthorizedAccessException ex) { this.Log().WarnException("Couldn't delete directory: " + x.FullName, ex); } }); // Clean up the packages directory too var releasesFile = Utility.LocalReleaseFileForAppDir(rootAppDirectory); var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesFile, Encoding.UTF8)); var pkgDir = Utility.PackageDirectoryForAppDir(rootAppDirectory); var releaseEntry = default(ReleaseEntry); foreach (var entry in entries) { if (entry.Version == currentVersion) { releaseEntry = ReleaseEntry.GenerateFromFile(Path.Combine(pkgDir, entry.Filename)); continue; } File.Delete(Path.Combine(pkgDir, entry.Filename)); } ReleaseEntry.WriteReleaseFile(new[] { releaseEntry }, releasesFile); }