Skip to content

Commit

Permalink
Merge pull request #1850 from sparkle-project/better-appcast-selection
Browse files Browse the repository at this point in the history
Better appcast selection
  • Loading branch information
zorgiepoo authored May 4, 2021
2 parents 3cafb68 + 6269537 commit 039adc1
Show file tree
Hide file tree
Showing 18 changed files with 283 additions and 97 deletions.
4 changes: 4 additions & 0 deletions Sparkle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
725264A91D5FBD9100CD6400 /* Sparkle.strings in Resources */ = {isa = PBXBuildFile; fileRef = 61AAE8220A321A7F00D8810D /* Sparkle.strings */; };
725602D51C83551C00DAA70E /* SUApplicationInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 725602D31C83551C00DAA70E /* SUApplicationInfo.h */; };
725602D61C83551C00DAA70E /* SUApplicationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 725602D41C83551C00DAA70E /* SUApplicationInfo.m */; };
725B3A82263FBF0C0041AB8E /* testappcast_minimumAutoupdateVersion.xml in Resources */ = {isa = PBXBuildFile; fileRef = 725B3A81263FBF0C0041AB8E /* testappcast_minimumAutoupdateVersion.xml */; };
725CB9571C7120410064365A /* SPUUserDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 725CB9561C7120410064365A /* SPUUserDriver.h */; settings = {ATTRIBUTES = (Public, ); }; };
725CB95A1C7121830064365A /* SPUStandardUserDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 725CB9581C7121830064365A /* SPUStandardUserDriver.h */; settings = {ATTRIBUTES = (Public, ); }; };
725CB95B1C7121830064365A /* SPUStandardUserDriver.m in Sources */ = {isa = PBXBuildFile; fileRef = 725CB9591C7121830064365A /* SPUStandardUserDriver.m */; };
Expand Down Expand Up @@ -1181,6 +1182,7 @@
724BB3B51D35AAC3005D534A /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
725602D31C83551C00DAA70E /* SUApplicationInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SUApplicationInfo.h; sourceTree = "<group>"; };
725602D41C83551C00DAA70E /* SUApplicationInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUApplicationInfo.m; sourceTree = "<group>"; };
725B3A81263FBF0C0041AB8E /* testappcast_minimumAutoupdateVersion.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = testappcast_minimumAutoupdateVersion.xml; sourceTree = "<group>"; };
725CB9561C7120410064365A /* SPUUserDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPUUserDriver.h; sourceTree = "<group>"; };
725CB9581C7121830064365A /* SPUStandardUserDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPUStandardUserDriver.h; sourceTree = "<group>"; };
725CB9591C7121830064365A /* SPUStandardUserDriver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPUStandardUserDriver.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1763,6 +1765,7 @@
14958C6C19AEBC610061B14F /* test-pubkey.pem */,
5AF6C74E1AEA46D10014A3AB /* test.pkg */,
5AD0FA7E1C73F2E2004BCEFF /* testappcast.xml */,
725B3A81263FBF0C0041AB8E /* testappcast_minimumAutoupdateVersion.xml */,
5F1510A11C96E591006E1629 /* testnamespaces.xml */,
FA30773C24CBC295007BA37D /* testlocalizedreleasenotesappcast.xml */,
5A5DD41B249F0F4B0045EB3E /* test-relative-urls.xml */,
Expand Down Expand Up @@ -3046,6 +3049,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
725B3A82263FBF0C0041AB8E /* testappcast_minimumAutoupdateVersion.xml in Resources */,
14958C6E19AEBC950061B14F /* signed-test-file.txt in Resources */,
72AC6B2E1B9B218C00F62325 /* SparkleTestCodeSignApp.dmg in Resources */,
C23E885B1BE7B24F0050BB73 /* SparkleTestCodeSignApp.enc.dmg in Resources */,
Expand Down
8 changes: 8 additions & 0 deletions Sparkle/Base.lproj/Sparkle.strings
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@
/* Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates. */
"You're up-to-date!" = "You’re up to date!";

"Your macOS version is too old" = "Your macOS version is too old";

"%1$@ %2$@ is available but your macOS version is too old to install it. At least macOS %3$@ is required." = "%1$@ %2$@ is available but your macOS version is too old to install it. At least macOS %3$@ is required.";

"Your macOS version is too new" = "Your macOS version is too new";

"%1$@ %2$@ is available but your macOS version is too new for this update. This update only supports up to macOS %3$@." = "%1$@ %2$@ is available but your macOS version is too new for this update. This update only supports up to macOS %3$@.";

/* Authorization message shown when app wants permission to update itself. */
"%1$@ wants permission to update." = "%1$@ wants permission to update.";

Expand Down
2 changes: 1 addition & 1 deletion Sparkle/SPUAutomaticUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ - (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString
if (error != nil) {
[self abortUpdateWithError:error];
} else {
[self.coreDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:YES includesSkippedUpdates:NO requiresSilentInstall:YES];
[self.coreDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:YES requiresSilentInstall:YES];
}
}];
}
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/SPUBasicUpdateDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN

- (void)prepareCheckForUpdatesWithCompletion:(SPUUpdateDriverCompletion)completionBlock;

- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background includesSkippedUpdates:(BOOL)includesSkippedUpdates;
- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background;

- (void)resumeInstallingUpdateWithCompletion:(SPUUpdateDriverCompletion)completionBlock;

Expand Down
47 changes: 37 additions & 10 deletions Sparkle/SPUBasicUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ - (void)prepareCheckForUpdatesWithCompletion:(SPUUpdateDriverCompletion)completi
self.completionBlock = completionBlock;
}

- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background includesSkippedUpdates:(BOOL)includesSkippedUpdates
- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background
{
if ([self.host isRunningOnReadOnlyVolume]) {
NSString *hostName = self.host.name;
Expand All @@ -75,7 +75,7 @@ - (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString
[self.delegate basicDriverIsRequestingAbortUpdateWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SURunningFromDiskImageError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:SULocalizedString(@"%1$@ can't be updated, because it was opened from a read-only or a temporary location.", nil), hostName], NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:SULocalizedString(@"Use Finder to copy %1$@ to the Applications folder, relaunch it from there, and try again.", nil), hostName] }]];
}
} else {
[self.appcastDriver loadAppcastFromURL:appcastURL userAgent:userAgent httpHeaders:httpHeaders inBackground:background includesSkippedUpdates:includesSkippedUpdates];
[self.appcastDriver loadAppcastFromURL:appcastURL userAgent:userAgent httpHeaders:httpHeaders inBackground:background];
}
}

Expand Down Expand Up @@ -157,7 +157,7 @@ - (void)didFindValidUpdateWithAppcastItem:(SUAppcastItem *)updateItem secondaryA
[self notifyFoundValidUpdateWithAppcastItem:updateItem secondaryAppcastItem:secondaryAppcastItem preventsAutoupdate:preventsAutoupdate systemDomain:nil];
}

- (void)didNotFindUpdateWithLatestAppcastItem:(nullable SUAppcastItem *)latestAppcastItem hostToLatestAppcastItemComparisonResult:(NSComparisonResult)hostToLatestAppcastItemComparisonResult
- (void)didNotFindUpdateWithLatestAppcastItem:(nullable SUAppcastItem *)latestAppcastItem hostToLatestAppcastItemComparisonResult:(NSComparisonResult)hostToLatestAppcastItemComparisonResult passesMinOSVersion:(BOOL)passesMinOSVersion passesMaxOSVersion:(BOOL)passesMaxOSVersion
{
if (!self.aborted) {
if ([self.updaterDelegate respondsToSelector:@selector((updaterDidNotFindUpdate:))]) {
Expand All @@ -169,17 +169,44 @@ - (void)didNotFindUpdateWithLatestAppcastItem:(nullable SUAppcastItem *)latestAp
NSString *recoverySuggestion;
NSString *recoveryOption;

if (latestAppcastItem != nil) { // if the appcast was successfully loaded
localizedDescription = SULocalizedString(@"You're up-to-date!", "Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates.");

if (hostToLatestAppcastItemComparisonResult == NSOrderedDescending) { // this means the user is a 'newer than latest' version. give a slight hint to the user instead of wrongly claiming this version is identical to the latest feed version.
recoverySuggestion = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.\n(You are currently running version %@.)", nil), [self.host name], latestAppcastItem.displayVersionString, [self.host displayVersion]];
} else {
recoverySuggestion = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [self.host name], [self.host displayVersion]];
if (latestAppcastItem != nil) {
switch (hostToLatestAppcastItemComparisonResult) {
case NSOrderedDescending:
// This means the user is a 'newer than latest' version. give a slight hint to the user instead of wrongly claiming this version is identical to the latest feed version.
localizedDescription = SULocalizedString(@"You're up-to-date!", "Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates.");

recoverySuggestion = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.\n(You are currently running version %@.)", nil), [self.host name], latestAppcastItem.displayVersionString, [self.host displayVersion]];
break;
case NSOrderedSame:
// No new update is available and we're on the latest
localizedDescription = SULocalizedString(@"You're up-to-date!", "Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates.");

recoverySuggestion = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [self.host name], [self.host displayVersion]];
break;
case NSOrderedAscending:
// This means a new update doesn't match the OS requirements
if (!passesMinOSVersion) {
localizedDescription = SULocalizedString(@"Your macOS version is too old", nil);

recoverySuggestion = [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ is available but your macOS version is too old to install it. At least macOS %3$@ is required.", nil), [self.host name], latestAppcastItem.versionString, latestAppcastItem.minimumSystemVersion];
} else if (!passesMaxOSVersion) {
localizedDescription = SULocalizedString(@"Your macOS version is too new", nil);

recoverySuggestion = [NSString stringWithFormat:SULocalizedString(@"%1$@ %2$@ is available but your macOS version is too new for this update. This update only supports up to macOS %3$@.", nil), [self.host name], latestAppcastItem.versionString, latestAppcastItem.maximumSystemVersion];
} else {
// We shouldn't realistically get here
localizedDescription = SULocalizedString(@"You're up-to-date!", "Status message shown when the user checks for updates but is already current or the feed doesn't contain any updates.");

recoverySuggestion = [NSString stringWithFormat:SULocalizedString(@"%@ %@ is currently the newest version available.", nil), [self.host name], [self.host displayVersion]];
}
break;
}

recoveryOption = @"OK";
} else {
// When no updates are found in the appcast, or latest appcast item info
// was not provided (i.e, for a background update check)
// In the case no info was provided for a background check, the error isn't shown anywhere
localizedDescription = SULocalizedString(@"Update Error!", nil);
recoverySuggestion = SULocalizedString(@"No valid update information could be loaded.", nil);
recoveryOption = SULocalizedString(@"Cancel Update", nil);
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/SPUCoreBasedUpdateDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ NS_ASSUME_NONNULL_BEGIN

- (void)preflightForUpdatePermissionPreventingInstallerInteraction:(BOOL)preventsInstallerInteraction reply:(void (^)(NSError * _Nullable))reply;

- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background includesSkippedUpdates:(BOOL)includesSkippedUpdates requiresSilentInstall:(BOOL)silentInstall;
- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background requiresSilentInstall:(BOOL)silentInstall;

- (void)resumeInstallingUpdateWithCompletion:(SPUUpdateDriverCompletion)completionBlock;

Expand Down
4 changes: 2 additions & 2 deletions Sparkle/SPUCoreBasedUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ - (void)preflightForUpdatePermissionPreventingInstallerInteraction:(BOOL)prevent
}
}

- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background includesSkippedUpdates:(BOOL)includesSkippedUpdates requiresSilentInstall:(BOOL)silentInstall
- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background requiresSilentInstall:(BOOL)silentInstall
{
self.userAgent = userAgent;
self.httpHeaders = httpHeaders;
self.silentInstall = silentInstall;

[self.basicDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:background includesSkippedUpdates:includesSkippedUpdates];
[self.basicDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:background];
}

- (void)resumeInstallingUpdateWithCompletion:(SPUUpdateDriverCompletion)completionBlock
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/SPUProbingUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ - (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString

[self.basicDriver prepareCheckForUpdatesWithCompletion:completionBlock];

[self.basicDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:YES includesSkippedUpdates:NO];
[self.basicDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:YES];
}

- (void)resumeInstallingUpdateWithCompletion:(SPUUpdateDriverCompletion)completionBlock
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/SPUScheduledUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ - (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString
// Don't tell the user about the permission error for scheduled update checks
[self abortUpdateWithError:nil];
} else {
[self.uiDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:YES includesSkippedUpdates:NO];
[self.uiDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:YES];
}
}];
}
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/SPUUIBasedUpdateDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN

- (void)preflightForUpdatePermissionPreventingInstallerInteraction:(BOOL)preventsInstallerInteraction reply:(void (^)(NSError * _Nullable))reply;

- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background includesSkippedUpdates:(BOOL)includesSkippedUpdates;
- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background;

- (void)resumeInstallingUpdateWithCompletion:(SPUUpdateDriverCompletion)completionBlock;

Expand Down
4 changes: 2 additions & 2 deletions Sparkle/SPUUIBasedUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ - (void)preflightForUpdatePermissionPreventingInstallerInteraction:(BOOL)prevent
[self.coreDriver preflightForUpdatePermissionPreventingInstallerInteraction:preventsInstallerInteraction reply:reply];
}

- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background includesSkippedUpdates:(BOOL)includesSkippedUpdates
- (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString *)userAgent httpHeaders:(NSDictionary * _Nullable)httpHeaders inBackground:(BOOL)background
{
[self.coreDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:background includesSkippedUpdates:includesSkippedUpdates requiresSilentInstall:NO];
[self.coreDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:background requiresSilentInstall:NO];
}

- (void)resumeInstallingUpdateWithCompletion:(SPUUpdateDriverCompletion)completionBlock
Expand Down
11 changes: 11 additions & 0 deletions Sparkle/SPUUpdaterDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,17 @@ typedef NS_ENUM(NSInteger, SPUUpdateCheck)
implement this to use your own logic for finding a valid update, if any,
in the given appcast.
Do not base your logic by filtering out items with a minimum or maximum OS version or minimum autoupdate version,
because Sparkle already has logic for determining whether or not those items should be filtered out.
Also do not return a non-top level item from the appcast such as a delta item. Delta items will be ignored.
Sparkle picks the delta item from your selection if the appropriate one is available.
This method will not be invoked with an appcast that has zero items. Pick the best item from the appcast.
If an item is available that has the same version as the application or bundle to update,
do not pick an item that is worse than that version.
This method may be called multiple times for different selections and filters. This method should be efficient.
\param appcast The appcast that was downloaded from the remote server.
\param updater The updater instance.
\return The best valid appcast item, or nil if you don't want to be delegated this task.
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/SPUUserInitiatedUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ - (void)checkForUpdatesAtAppcastURL:(NSURL *)appcastURL withUserAgent:(NSString
}];
}

[self.uiDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:NO includesSkippedUpdates:YES];
[self.uiDriver checkForUpdatesAtAppcastURL:appcastURL withUserAgent:userAgent httpHeaders:httpHeaders inBackground:NO];
}
}
}];
Expand Down
Loading

0 comments on commit 039adc1

Please sign in to comment.