Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better appcast selection #1850

Merged
merged 13 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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