Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: Squirrel/OldSquirrelForWindows
...
head fork: Squirrel/OldSquirrelForWindows
Checking mergeability… Don't worry, you can still create the pull request.
  • 5 commits
  • 14 files changed
  • 0 commit comments
  • 1 contributor
View
BIN  src/SampleUpdatingApp/OriginalNugetPackages/SampleUpdatingApp.1.0.0.0.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/OriginalNugetPackages/SampleUpdatingApp.1.1.0.0.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/OriginalNugetPackages/SampleUpdatingApp.1.2.0.0.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/OriginalNugetPackages/SampleUpdatingApp.1.3.0.0.nupkg
Binary file not shown
View
14 src/SampleUpdatingApp/SampleReleasesFolder/RELEASES
@@ -1,7 +1,7 @@
-9F60275640EE3C50F0466F1D1562B2FC013D6B04 SampleUpdatingApp-1.0.0.0-full.nupkg 1878551
-ECDD4EDC1B8DF44FCC9DFD29C5B3840CD7C4698A SampleUpdatingApp-1.1.0.0-delta.nupkg 17654
-99BF5171370C779E4E65D5FD51BCCAEC63918832 SampleUpdatingApp-1.3.0.0-delta.nupkg 17566
-C66B62BF3D739C0D619CB6B6C88EDC1E36E3532D SampleUpdatingApp-1.2.0.0-full.nupkg 1878572
-060783B2A30B4B1E13A32489C94F30FC2CB51E20 SampleUpdatingApp-1.3.0.0-full.nupkg 1878572
-737DA0DC4618133354FDA30D334C2514070D1AAF SampleUpdatingApp-1.2.0.0-delta.nupkg 17558
-E3A6B0C86BFE84D4B496EFA052BD512178E7DD74 SampleUpdatingApp-1.1.0.0-full.nupkg 1878571
+3C8915D9AE711F32C8BB9E0E8ECAA923F30EA60F SampleUpdatingApp-1.1.0.0-delta.nupkg 17133
+0F97240BCFF588995AC10738BAF5B2732C9617CC SampleUpdatingApp-1.0.0.0-full.nupkg 1664647
+95888AC7969C94446A7166A9A2DE68D48A76E813 SampleUpdatingApp-1.3.0.0-delta.nupkg 17042
+F12D51CBA84EE8B7791F6ADE7A0600A44DF674BB SampleUpdatingApp-1.1.0.0-full.nupkg 1664663
+ACB6CC4268B95E429CEBF1687ABA1C8824883C2E SampleUpdatingApp-1.2.0.0-delta.nupkg 17037
+A22B42619E95659302ABD35BDD422614803771BE SampleUpdatingApp-1.3.0.0-full.nupkg 1664665
+336D7E26FD6001DC97877473758B38A49F282689 SampleUpdatingApp-1.2.0.0-full.nupkg 1664670
View
BIN  src/SampleUpdatingApp/SampleReleasesFolder/SampleUpdatingApp-1.0.0.0-full.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/SampleReleasesFolder/SampleUpdatingApp-1.1.0.0-delta.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/SampleReleasesFolder/SampleUpdatingApp-1.1.0.0-full.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/SampleReleasesFolder/SampleUpdatingApp-1.2.0.0-delta.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/SampleReleasesFolder/SampleUpdatingApp-1.2.0.0-full.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/SampleReleasesFolder/SampleUpdatingApp-1.3.0.0-delta.nupkg
Binary file not shown
View
BIN  src/SampleUpdatingApp/SampleReleasesFolder/SampleUpdatingApp-1.3.0.0-full.nupkg
Binary file not shown
View
100 src/Shimmer.Client/UpdateManager.cs
@@ -100,7 +100,7 @@ public IObservable<UpdateInfo> CheckForUpdate(bool ignoreDeltaUpdates = false)
}
}
- var ret = releaseFile
+ var ret = releaseFile
.Select(ReleaseEntry.ParseReleaseFile)
.SelectMany(releases => determineUpdateInfo(localReleases, releases, ignoreDeltaUpdates))
.Multicast(new AsyncSubject<UpdateInfo>());
@@ -142,7 +142,8 @@ public IObservable<Unit> ApplyReleases(UpdateInfo updateInfo)
// once the entire operation has completed, even though we technically
// could do it after DownloadUpdates finishes. We do this so that if
// we get interrupted / killed during this operation, we'll start over
- return createFullPackagesFromDeltas(updateInfo.ReleasesToApply, updateInfo.CurrentlyInstalledVersion)
+ return cleanDeadVersions(updateInfo.CurrentlyInstalledVersion != null ? updateInfo.CurrentlyInstalledVersion.Version : null)
+ .SelectMany(_ => createFullPackagesFromDeltas(updateInfo.ReleasesToApply, updateInfo.CurrentlyInstalledVersion))
.SelectMany(release =>
Observable.Start(() => installPackageToAppDir(updateInfo, release), RxApp.TaskpoolScheduler))
.SelectMany(_ => UpdateLocalReleasesFile());
@@ -344,6 +345,7 @@ void runPostInstallAndCleanup(Version newCurrentVersion, bool isBootstrapping)
var shortcutsToIgnore = cleanUpOldVersions(newCurrentVersion);
var targetPath = getDirectoryForRelease(newCurrentVersion);
+
runPostInstallOnDirectory(targetPath.FullName, isBootstrapping, newCurrentVersion, shortcutsToIgnore);
}
@@ -405,17 +407,27 @@ IEnumerable<ShortcutCreationRequest> cleanUpOldVersions(Version newCurrentVersio
.Where(x => x.Name.StartsWith("app-", StringComparison.InvariantCultureIgnoreCase))
.Where(x => x.Name != "app-" + newCurrentVersion)
.OrderBy(x => x.Name)
- .SelectMany(x => AppDomainHelper.ExecuteInNewAppDomain(x, runAppSetupCleanups));
+ .SelectMany(oldAppRoot => {
+ var path = oldAppRoot.FullName;
+ var ret = AppDomainHelper.ExecuteInNewAppDomain(path, runAppSetupCleanups);
+
+ try {
+ Utility.DeleteDirectoryAtNextReboot(oldAppRoot.FullName);
+ } catch (Exception ex) {
+ this.Log().WarnException("Couldn't delete old app directory on next reboot", ex);
+ }
+ return ret;
+ });
}
- IEnumerable<ShortcutCreationRequest> runAppSetupCleanups(DirectoryInfoBase dir)
+ IEnumerable<ShortcutCreationRequest> runAppSetupCleanups(string fullDirectoryPath)
{
- var apps = findAppSetupsToRun(dir.FullName);
- var ver = new Version(dir.Name.Replace("app-", ""));
+ var dirName = Path.GetFileName(fullDirectoryPath);
+ var apps = findAppSetupsToRun(fullDirectoryPath);
+ var ver = new Version(dirName.Replace("app-", ""));
var ret = apps.SelectMany(app => uninstallAppVersion(app, ver)).ToArray();
- Utility.DeleteDirectory(dir.FullName);
return ret;
}
@@ -509,28 +521,58 @@ void installAppVersion(IAppSetup app, Version newCurrentVersion, IEnumerable<Sho
IEnumerable<IAppSetup> findAppSetupsToRun(string appDirectory)
{
- return fileSystem.GetDirectoryInfo(appDirectory).GetFiles("*.exe")
- .Select(x => {
- try {
- var ret = Assembly.LoadFile(x.FullName);
- return ret;
- } catch (Exception ex) {
- this.Log().WarnException("Post-install: load failed for " + x.FullName, ex);
- return null;
- }
- })
- .Where(x => x != null)
- .SelectMany(x => x.GetModules()).SelectMany(x => x.GetTypes().Where(y => typeof(IAppSetup).IsAssignableFrom(y)))
- .Select(x => {
- try {
- return (IAppSetup)Activator.CreateInstance(x);
- } catch (Exception ex) {
- this.Log().WarnException("Post-install: Failed to create type " + x.FullName, ex);
- return null;
- }
- })
- .Where(x => x != null)
- .ToArray();
+ try {
+ return fileSystem.GetDirectoryInfo(appDirectory).GetFiles("*.exe")
+ .Select(x => {
+ try {
+ var ret = Assembly.LoadFile(x.FullName);
+ return ret;
+ } catch (Exception ex) {
+ this.Log().WarnException("Post-install: load failed for " + x.FullName, ex);
+ return null;
+ }
+ })
+ .Where(x => x != null)
+ .SelectMany(x => x.GetModules()).SelectMany(x => x.GetTypes().Where(y => typeof(IAppSetup).IsAssignableFrom(y)))
+ .Select(x => {
+ try {
+ return (IAppSetup)Activator.CreateInstance(x);
+ } catch (Exception ex) {
+ this.Log().WarnException("Post-install: Failed to create type " + x.FullName, ex);
+ return null;
+ }
+ })
+ .Where(x => x != null)
+ .ToArray();
+ } catch (UnauthorizedAccessException ex) {
+ // NB: This can happen if we run into a MoveFileEx'd directory,
+ // where we can't even get the list of files in it.
+ this.Log().WarnException("Couldn't search directory for IAppSetups: " + appDirectory, ex);
+ return null;
+ }
+ }
+
+ // 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.
+ IObservable<Unit> cleanDeadVersions(Version currentVersion)
+ {
+ var di = fileSystem.GetDirectoryInfo(rootAppDirectory);
+
+ // 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.
+ return di.GetDirectories().ToObservable()
+ .Where(x => x.Name.ToLowerInvariant().Contains("app-"))
+ .Where(x => currentVersion != null ? x.Name != getDirectoryForRelease(currentVersion).Name : true)
+ .MapReduce(x => Observable.Start(() => Utility.DeleteDirectory(x.FullName), RxApp.TaskpoolScheduler)
+ .LoggedCatch<Unit, UpdateManager, UnauthorizedAccessException>(this, _ => Observable.Return(Unit.Default)))
+ .Aggregate(Unit.Default, (acc, x) => acc);
}
}
}
View
32 src/Shimmer.Core/Utility.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
@@ -7,6 +8,7 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Cryptography;
using System.Security.Principal;
@@ -159,6 +161,36 @@ public static void DeleteDirectory(string directoryPath)
return acc;
}
+
+ public static void DeleteDirectoryAtNextReboot(string directoryPath)
+ {
+ var di = new DirectoryInfo(directoryPath);
+
+ // NB: MoveFileEx blows up if you're a non-admin, so you always need a backup plan
+ di.GetFiles().ForEach(x => safeDeleteFileAtNextDir(x.FullName));
+ di.GetDirectories().ForEach(x => DeleteDirectoryAtNextReboot(di.FullName));
+
+ safeDeleteFileAtNextDir(directoryPath);
+ }
+
+ static void safeDeleteFileAtNextDir(string name)
+ {
+ if (!MoveFileEx(name, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT)) throw new Win32Exception();
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
+
+ [Flags]
+ enum MoveFileFlags
+ {
+ MOVEFILE_REPLACE_EXISTING = 0x00000001,
+ MOVEFILE_COPY_ALLOWED = 0x00000002,
+ MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
+ MOVEFILE_WRITE_THROUGH = 0x00000008,
+ MOVEFILE_CREATE_HARDLINK = 0x00000010,
+ MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
+ }
}
public sealed class SingleGlobalInstance : IDisposable

No commit comments for this range

Something went wrong with that request. Please try again.