Skip to content

Commit

Permalink
Merge pull request #149 from Squirrel/renames
Browse files Browse the repository at this point in the history
Renames
  • Loading branch information
Keith Duncan committed Jun 29, 2015
2 parents 9e04034 + 1e71daa commit 6252f3d
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 32 deletions.
3 changes: 3 additions & 0 deletions Squirrel/NSBundle+SQRLVersionExtensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@
// Info.plist, or nil if the key is not present.
@property (nonatomic, copy, readonly) NSString *sqrl_bundleVersion;

/// The value of the `kCFBundleExecutableKey` key.
@property (nonatomic, copy, readonly) NSString *sqrl_executableName;

@end
4 changes: 4 additions & 0 deletions Squirrel/NSBundle+SQRLVersionExtensions.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ - (NSString *)sqrl_bundleVersion {
return [self objectForInfoDictionaryKey:(id)kCFBundleVersionKey];
}

- (NSString *)sqrl_executableName {
return [self objectForInfoDictionaryKey:(id)kCFBundleExecutableKey];
}

@end
80 changes: 66 additions & 14 deletions Squirrel/SQRLInstaller.m
Original file line number Diff line number Diff line change
Expand Up @@ -262,27 +262,49 @@ - (RACSignal *)acquireTargetBundleURLForRequest:(SQRLShipItRequest *)request {
setNameWithFormat:@"%@ -acquireTargetBundleURLForRequest: %@", self, request];
}

- (RACSignal *)renameIfNeeded:(SQRLShipItRequest *)request updateBundleURL:(NSURL *)updateBundleURL {
if (!request.useUpdateBundleName) return [RACSignal return:request];

return [[self
renamedTargetIfNeededWithTargetURL:request.targetBundleURL sourceURL:updateBundleURL]
flattenMap:^(NSURL *newTargetURL) {
if ([newTargetURL isEqual:request.targetBundleURL]) return [RACSignal return:request];

SQRLShipItRequest *updatedRequest = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:request.updateBundleURL targetBundleURL:newTargetURL bundleIdentifier:request.bundleIdentifier launchAfterInstallation:request.launchAfterInstallation useUpdateBundleName:request.useUpdateBundleName];
return [[self
installItemToURL:newTargetURL fromURL:request.targetBundleURL]
concat:[RACSignal return:updatedRequest]];
}];
}

- (RACSignal *)installRequest:(SQRLShipItRequest *)request {
NSParameterAssert(request != nil);

return [[[[self
prepareAndValidateUpdateBundleURLForRequest:request]
flattenMap:^(NSURL *updateBundleURL) {
return [[[[[[[self
acquireTargetBundleURLForRequest:request]
concat:[self installItemToURL:request.targetBundleURL fromURL:updateBundleURL]]
concat:[RACSignal return:request.updateBundleURL]]
concat:[RACSignal return:updateBundleURL]]
concat:[RACSignal defer:^{
return [RACSignal return:self.ownedBundle.temporaryURL];
}]]
flattenMap:^(NSURL *location) {
return [[[self
deleteOwnedBundleAtURL:location]
doError:^(NSError *error) {
NSLog(@"Couldn't remove owned bundle at location %@, error %@", location, error.sqrl_verboseDescription);
return [[[[self
renameIfNeeded:request updateBundleURL:updateBundleURL]
flattenMap:^(SQRLShipItRequest *request) {
return [[self acquireTargetBundleURLForRequest:request] concat:[RACSignal return:request]];
}]
flattenMap:^(SQRLShipItRequest *request) {
return [[[[[[self
installItemToURL:request.targetBundleURL fromURL:updateBundleURL]
concat:[RACSignal return:request.updateBundleURL]]
concat:[RACSignal return:updateBundleURL]]
concat:[RACSignal defer:^{
return [RACSignal return:self.ownedBundle.temporaryURL];
}]]
flattenMap:^(NSURL *location) {
return [[[self
deleteOwnedBundleAtURL:location]
doError:^(NSError *error) {
NSLog(@"Couldn't remove owned bundle at location %@, error %@", location, error.sqrl_verboseDescription);
}]
catchTo:[RACSignal empty]];
}]
catchTo:[RACSignal empty]];
concat:[RACSignal return:request]];
}]
doCompleted:^{
self.ownedBundle = nil;
Expand Down Expand Up @@ -413,6 +435,36 @@ - (RACSignal *)verifyBundleAtURL:(NSURL *)bundleURL usingSignature:(SQRLCodeSign

#pragma mark Installation

/// Check if the target should be renamed and provide the renamed URL.
///
/// targetURL - The URL for the target. Cannot be nil.
/// sourceURL - The URL for the source. Cannot be nil.
///
/// Returns a signal which will send the URL for the renamed target. If a rename
/// isn't needed then it will send `targetURL`.
- (RACSignal *)renamedTargetIfNeededWithTargetURL:(NSURL *)targetURL sourceURL:(NSURL *)sourceURL {
return [RACSignal defer:^{
NSBundle *sourceBundle = [NSBundle bundleWithURL:sourceURL];
NSString *targetExecutableName = targetURL.lastPathComponent.stringByDeletingPathExtension;
NSString *sourceExecutableName = sourceBundle.sqrl_executableName;

// If they're already the same then we're good.
if ([targetExecutableName isEqual:sourceExecutableName]) {
return [RACSignal return:targetURL];
}

NSString *newAppName = [sourceExecutableName stringByAppendingPathExtension:@"app"];
NSURL *newTargetURL = [targetURL.URLByDeletingLastPathComponent URLByAppendingPathComponent:newAppName];

// If there's already something there then don't rename to it.
if ([NSFileManager.defaultManager fileExistsAtPath:newTargetURL.path]) {
return [RACSignal return:targetURL];
}

return [RACSignal return:newTargetURL];
}];
}

- (RACSignal *)installItemToURL:(NSURL *)targetURL fromURL:(NSURL *)sourceURL {
NSParameterAssert(targetURL != nil);
NSParameterAssert(sourceURL != nil);
Expand Down
6 changes: 5 additions & 1 deletion Squirrel/SQRLShipItRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,11 @@ extern NSString * const SQRLShipItRequestPropertyErrorKey;
// installing. Can be nil.
// launchAfterInstallation - Whether the updated application should be launched
// after installation.
// useUpdateBundleName - Should the target use the update bundle's name?
//
// Returns a request which can be written to disk for ShipIt to read and
// perform.
- (instancetype)initWithUpdateBundleURL:(NSURL *)updateBundleURL targetBundleURL:(NSURL *)targetBundleURL bundleIdentifier:(NSString *)bundleIdentifier launchAfterInstallation:(BOOL)launchAfterInstallation;
- (instancetype)initWithUpdateBundleURL:(NSURL *)updateBundleURL targetBundleURL:(NSURL *)targetBundleURL bundleIdentifier:(NSString *)bundleIdentifier launchAfterInstallation:(BOOL)launchAfterInstallation useUpdateBundleName:(BOOL)useUpdateBundleName;

// The URL to the downloaded update's app bundle.
@property (nonatomic, copy, readonly) NSURL *updateBundleURL;
Expand All @@ -93,4 +94,7 @@ extern NSString * const SQRLShipItRequestPropertyErrorKey;
// Whether to launch the application after an update is successfully installed.
@property (nonatomic, assign, readonly) BOOL launchAfterInstallation;

// Whether the app should use the update bundle's name.
@property (nonatomic, assign, readonly) BOOL useUpdateBundleName;

@end
3 changes: 2 additions & 1 deletion Squirrel/SQRLShipItRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@ - (id)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error {
return self;
}

- (instancetype)initWithUpdateBundleURL:(NSURL *)updateBundleURL targetBundleURL:(NSURL *)targetBundleURL bundleIdentifier:(NSString *)bundleIdentifier launchAfterInstallation:(BOOL)launchAfterInstallation {
- (instancetype)initWithUpdateBundleURL:(NSURL *)updateBundleURL targetBundleURL:(NSURL *)targetBundleURL bundleIdentifier:(NSString *)bundleIdentifier launchAfterInstallation:(BOOL)launchAfterInstallation useUpdateBundleName:(BOOL)useUpdateBundleName {
return [self initWithDictionary:@{
@keypath(self.updateBundleURL): updateBundleURL,
@keypath(self.targetBundleURL): targetBundleURL,
@keypath(self.bundleIdentifier): bundleIdentifier ?: NSNull.null,
@keypath(self.launchAfterInstallation): @(launchAfterInstallation),
@keypath(self.useUpdateBundleName): @(useUpdateBundleName),
} error:NULL];
}

Expand Down
9 changes: 7 additions & 2 deletions Squirrel/SQRLUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,12 @@ - (RACSignal *)prepareUpdateForInstallation:(SQRLDownloadedUpdate *)update {
return [[[[RACSignal
defer:^{
NSRunningApplication *currentApplication = NSRunningApplication.currentApplication;
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:update.bundle.bundleURL targetBundleURL:currentApplication.bundleURL bundleIdentifier:currentApplication.bundleIdentifier launchAfterInstallation:NO];
NSBundle *appBundle = [NSBundle bundleWithURL:currentApplication.bundleURL];
// Only use the update bundle's name if the user hasn't renamed the
// app themselves.
BOOL useUpdateBundleName = [appBundle.sqrl_executableName isEqual:currentApplication.bundleURL.lastPathComponent.stringByDeletingPathExtension];

SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:update.bundle.bundleURL targetBundleURL:currentApplication.bundleURL bundleIdentifier:currentApplication.bundleIdentifier launchAfterInstallation:NO useUpdateBundleName:useUpdateBundleName];
return [request writeUsingURL:self.shipItStateURL];
}]
then:^{
Expand All @@ -542,7 +547,7 @@ - (RACSignal *)relaunchToInstallUpdate {
return [[[[[[[[SQRLShipItRequest
readUsingURL:self.shipItStateURL]
map:^(SQRLShipItRequest *request) {
return [[SQRLShipItRequest alloc] initWithUpdateBundleURL:request.updateBundleURL targetBundleURL:request.targetBundleURL bundleIdentifier:request.bundleIdentifier launchAfterInstallation:YES];
return [[SQRLShipItRequest alloc] initWithUpdateBundleURL:request.updateBundleURL targetBundleURL:request.targetBundleURL bundleIdentifier:request.bundleIdentifier launchAfterInstallation:YES useUpdateBundleName:request.useUpdateBundleName];
}]
flattenMap:^(SQRLShipItRequest *request) {
return [[request
Expand Down
9 changes: 5 additions & 4 deletions Squirrel/ShipIt-main.m
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static void installRequest(RACSignal *readRequestSignal, SQRLDirectoryManager *d

RACSignal *action;
if (attempt > SQRLShipItMaximumInstallationAttempts) {
action = [[[installer.abortInstallationCommand
action = [[[[installer.abortInstallationCommand
execute:request]
initially:^{
NSLog(@"Too many attempts to install, aborting update");
Expand All @@ -86,7 +86,8 @@ static void installRequest(RACSignal *readRequestSignal, SQRLDirectoryManager *d
// Exit successfully so launchd doesn't restart us
// again.
return [RACSignal empty];
}];
}]
concat:[RACSignal return:request]];
} else {
action = [[[[installer.installUpdateCommand
execute:request]
Expand Down Expand Up @@ -114,8 +115,8 @@ static void installRequest(RACSignal *readRequestSignal, SQRLDirectoryManager *d
// Launch regardless of whether installation succeeds or fails.
action = [[action
deliverOn:RACScheduler.mainThreadScheduler]
finally:^{
NSURL *bundleURL = request.targetBundleURL;
doNext:^(SQRLShipItRequest *finalRequest) {
NSURL *bundleURL = finalRequest.targetBundleURL;
if (bundleURL == nil) {
NSLog(@"Missing target bundle URL, cannot launch application");
return;
Expand Down
18 changes: 9 additions & 9 deletions SquirrelTests/SQRLInstallerSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@
});

it(@"should install an update using ShipIt", ^{
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];

[self installWithRequest:request remote:YES];

expect(self.testApplicationBundleVersion).toEventually(equal(SQRLTestApplicationUpdatedShortVersionString));
});

it(@"should install an update in process", ^{
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];

[self installWithRequest:request remote:NO];

Expand All @@ -60,7 +60,7 @@
NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundleIdentifier];
expect(@(apps.count)).to(equal(@0));

SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:YES];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:YES useUpdateBundleName:NO];

[self installWithRequest:request remote:YES];

Expand All @@ -72,7 +72,7 @@
NSURL *diskImageURL = [self createAndMountDiskImageNamed:@"TestApplication 2.1" fromDirectory:updateURL.URLByDeletingLastPathComponent];
updateURL = [diskImageURL URLByAppendingPathComponent:updateURL.lastPathComponent];

SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];

[self installWithRequest:request remote:YES];

Expand All @@ -83,7 +83,7 @@
NSURL *diskImageURL = [self createAndMountDiskImageNamed:@"TestApplication" fromDirectory:self.testApplicationURL.URLByDeletingLastPathComponent];
NSURL *targetURL = [diskImageURL URLByAppendingPathComponent:self.testApplicationURL.lastPathComponent];

SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:targetURL bundleIdentifier:nil launchAfterInstallation:NO];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:targetURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];

[self installWithRequest:request remote:YES];

Expand Down Expand Up @@ -118,7 +118,7 @@
});

it(@"should not install an update after too many attempts", ^{
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:targetURL bundleIdentifier:nil launchAfterInstallation:NO];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:targetURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];
[self installWithRequest:request remote:YES];

__block NSError *error;
Expand All @@ -129,7 +129,7 @@
});

it(@"should relaunch even after failing to install an update", ^{
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:targetURL bundleIdentifier:nil launchAfterInstallation:YES];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:targetURL bundleIdentifier:nil launchAfterInstallation:YES useUpdateBundleName:NO];
[self installWithRequest:request remote:YES];

expect(@([NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.github.Squirrel.TestApplication"].count)).toEventually(equal(@1));
Expand All @@ -149,7 +149,7 @@
expect(@(modeOfURL(updateURL))).to(equal(@0777));
expect(@(modeOfURL([updateURL URLByAppendingPathComponent:@"Contents/MacOS/TestApplication"]))).to(equal(@0777));

SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];

[self installWithRequest:request remote:YES];

Expand Down Expand Up @@ -178,7 +178,7 @@
// accessing the property.
targetURL = self.testApplicationURL;

SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO];
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];

[self installWithRequest:request remote:YES];

Expand Down
2 changes: 1 addition & 1 deletion SquirrelTests/SQRLShipItRequestSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
directoryManager = SQRLDirectoryManager.currentApplicationManager;

NSURL *updateURL = [self createTestApplicationUpdate];
request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO];
request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:updateURL targetBundleURL:self.testApplicationURL bundleIdentifier:nil launchAfterInstallation:NO useUpdateBundleName:NO];
expect(request).notTo(beNil());

expect(request.targetBundleURL).to(equal(self.testApplicationURL));
Expand Down

0 comments on commit 6252f3d

Please sign in to comment.