diff --git a/Alcatraz.xcodeproj/project.pbxproj b/Alcatraz.xcodeproj/project.pbxproj index 1390780..1eff9b9 100644 --- a/Alcatraz.xcodeproj/project.pbxproj +++ b/Alcatraz.xcodeproj/project.pbxproj @@ -188,7 +188,10 @@ 890A4B41171F031300AFE577 /* Frameworks */, 890A4B40171F031300AFE577 /* Products */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; + usesTabs = 0; }; 890A4B40171F031300AFE577 /* Products */ = { isa = PBXGroup; diff --git a/Alcatraz/Controllers/ATZPluginWindowController.m b/Alcatraz/Controllers/ATZPluginWindowController.m index 72e655b..94483eb 100644 --- a/Alcatraz/Controllers/ATZPluginWindowController.m +++ b/Alcatraz/Controllers/ATZPluginWindowController.m @@ -85,6 +85,18 @@ - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentN return YES; } +- (NSError *)willPresentError:(NSError *)error +{ + const NSUInteger maxLength = 500; + NSString *recoverySuggestion = error.userInfo[NSLocalizedRecoverySuggestionErrorKey]; + if (recoverySuggestion.length > maxLength) { + NSMutableDictionary *userInfo = [error.userInfo mutableCopy]; + userInfo[NSLocalizedRecoverySuggestionErrorKey] = [[recoverySuggestion substringToIndex:MIN(maxLength, recoverySuggestion.length)] stringByAppendingString:@"…"]; + return [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; + } + return error; +} + #pragma mark - Bindings - (IBAction)installPressed:(ATZFillableButton *)button { @@ -209,7 +221,9 @@ - (void)installPackage:(ATZPackage *)package andUpdateControl:(ATZFillableButton [button setFillRatio:progress animated:YES]; } completion:^(NSError *failure) { [ATZStyleKit updateButton:button forPackageState:package animated:YES]; - if (package.requiresRestart) { + if (failure) { + [self presentError:failure]; + } else if (package.requiresRestart) { [self postNotificationForInstalledPackage:package]; } }]; diff --git a/Alcatraz/Helpers/ATZShell.h b/Alcatraz/Helpers/ATZShell.h index 38f9dbb..d2fa854 100644 --- a/Alcatraz/Helpers/ATZShell.h +++ b/Alcatraz/Helpers/ATZShell.h @@ -22,6 +22,13 @@ #import +static NSString *const ATZShellErrorDomain = @"ATZShellErrorDomain"; +NS_ENUM(NSInteger) +{ + ATZShellTerminationStatusError = 666, + ATZShellLaunchError = 667 +}; + @interface ATZShell : NSObject - (void)executeCommand:(NSString *)command withArguments:(NSArray *)arguments diff --git a/Alcatraz/Helpers/ATZShell.m b/Alcatraz/Helpers/ATZShell.m index 8fe3f5a..1d07003 100644 --- a/Alcatraz/Helpers/ATZShell.m +++ b/Alcatraz/Helpers/ATZShell.m @@ -77,8 +77,9 @@ - (void)setUpTerminationHandlerForTask:(NSTask *)task completion:(void(^)(NSStri if (task.terminationStatus == 0) { completion(output, nil); } else { - NSString* reason = [NSString stringWithFormat:@"Task exited with status %d", task.terminationStatus]; - completion(output, [NSError errorWithDomain:reason code:666 userInfo:@{ NSLocalizedDescriptionKey: reason }]); + NSString *description = [NSString stringWithFormat:@"Task exited with status %d\n\n%@ %@", task.terminationStatus, task.launchPath, [task.arguments componentsJoinedByString:@" "]]; + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: description, NSLocalizedRecoverySuggestionErrorKey: output }; + completion(output, [NSError errorWithDomain:ATZShellErrorDomain code:ATZShellTerminationStatusError userInfo:userInfo]); } }]; @@ -92,8 +93,9 @@ - (void)tryToLaunchTask:(NSTask *)shellTask completionIfFailed:(void(^)(NSString [shellTask launch]; } @catch (NSException *exception) { - NSLog(@"Shell command execution failed! %@", exception); - completion(nil, [NSError errorWithDomain:exception.reason code:667 userInfo:nil]); + NSLog(@"Shell command execution failed! %@ (%@)", exception, shellTask.launchPath); + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: exception.reason, NSLocalizedRecoverySuggestionErrorKey: shellTask.launchPath }; + completion(nil, [NSError errorWithDomain:ATZShellErrorDomain code:ATZShellLaunchError userInfo:userInfo]); } } diff --git a/Alcatraz/Installers/ATZInstaller.h b/Alcatraz/Installers/ATZInstaller.h index da0d46b..99cd214 100644 --- a/Alcatraz/Installers/ATZInstaller.h +++ b/Alcatraz/Installers/ATZInstaller.h @@ -31,6 +31,13 @@ static NSString *const DOWNLOADING_FORMAT = @"Downloading %@..."; static NSString *const INSTALLING_FORMAT = @"Installing %@..."; static NSString *const UPDATING_FORMAT = @"Updating %@..."; +static NSString *const ATZInstallerErrorDomain = @"ATZInstallerErrorDomain"; +NS_ENUM(NSInteger) +{ + ATZInstallerXcodeProjectNotFoundError = 666, + ATZInstallerBundleNotFoundError = 669 +}; + @interface ATZInstaller : NSObject + (instancetype)sharedInstaller; diff --git a/Alcatraz/Installers/ATZPluginInstaller.m b/Alcatraz/Installers/ATZPluginInstaller.m index 20268b8..582dcdc 100644 --- a/Alcatraz/Installers/ATZPluginInstaller.m +++ b/Alcatraz/Installers/ATZPluginInstaller.m @@ -35,7 +35,7 @@ static NSString *const PROJECT = @"-project"; static NSString *const CLEAN = @"clean"; static NSString *const BUILD = @"build"; -static NSString *const XCODEPROJ = @".xcodeproj"; +static NSString *const XCODEPROJ = @"xcodeproj"; static NSString *const PROJECT_PBXPROJ = @"project.pbxproj"; @implementation ATZPluginInstaller @@ -92,7 +92,9 @@ - (void)reloadXcodeForPackage:(ATZPackage *)plugin completion:(void(^)(NSError * NSLog(@"Trying to reload plugin: %@ with bundle: %@", plugin.name, pluginBundle); if (!pluginBundle) { - completion([NSError errorWithDomain:@"Bundle was not found" code:669 userInfo:nil]); + NSString *description = [NSString stringWithFormat:@"Bundle for %@ was not found", plugin.name]; + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: description }; + completion([NSError errorWithDomain:ATZInstallerErrorDomain code:ATZInstallerBundleNotFoundError userInfo:userInfo]); return; } else if ([pluginBundle isLoaded]) { @@ -113,12 +115,10 @@ - (void)reloadXcodeForPackage:(ATZPackage *)plugin completion:(void(^)(NSError * #pragma mark - Private - (void)buildPlugin:(ATZPlugin *)plugin completion:(void (^)(NSError *))completion { - - NSString *xcodeProjPath; - - @try { xcodeProjPath = [self findXcodeprojPathForPlugin:plugin]; } - @catch (NSException *exception) { - completion([NSError errorWithDomain:exception.reason code:666 userInfo:nil]); + NSError *xcodeProjError; + NSString *xcodeProjPath = [self findXcodeprojPathForPackage:plugin error:&xcodeProjError]; + if (!xcodeProjPath) { + completion(xcodeProjError); return; } @@ -130,25 +130,42 @@ - (void)buildPlugin:(ATZPlugin *)plugin completion:(void (^)(NSError *))completi }]; } -- (NSString *)findXcodeprojPathForPlugin:(ATZPlugin *)plugin { - NSString *clonedDirectory = [self pathForDownloadedPackage:plugin]; - NSString *xcodeProjFilename = [plugin.name stringByAppendingString:XCODEPROJ]; +- (NSString *)findXcodeprojPathForPackage:(ATZPackage *)package error:(NSError **)error { + NSString *clonedDirectory = [self pathForDownloadedPackage:package]; + NSString *xcodeProjFilename = [package.name stringByAppendingPathExtension:XCODEPROJ]; + NSMutableArray *allXcodeProjFilenames = [NSMutableArray new]; NSDirectoryEnumerator *enumerator = [[NSFileManager sharedManager] enumeratorAtPath:clonedDirectory]; - NSString *directoryEntry; - - while (directoryEntry = [enumerator nextObject]) - if ([directoryEntry.pathComponents.lastObject isEqualToString:xcodeProjFilename]) + for (NSString *directoryEntry in enumerator) + { + NSString *fileName = directoryEntry.pathComponents.lastObject; + if ([fileName isEqualToString:xcodeProjFilename]) { return [clonedDirectory stringByAppendingPathComponent:directoryEntry]; + } else if ([fileName.pathExtension isEqualToString:XCODEPROJ]) { + [allXcodeProjFilenames addObject:directoryEntry]; + } + } - NSLog(@"Wasn't able to find: %@ in %@", xcodeProjFilename, clonedDirectory); - @throw [NSException exceptionWithName:@"Not found" reason:@".xcodeproj was not found" userInfo:nil]; + if (allXcodeProjFilenames.count == 1) { + return [clonedDirectory stringByAppendingPathComponent:allXcodeProjFilenames[0]]; + } + + if (error) { + NSString *description = [NSString stringWithFormat:@"No .xcodeproj file was found for %@", package.name]; + NSString *recoverySuggestion; + if (allXcodeProjFilenames.count == 0) { + recoverySuggestion = [NSString stringWithFormat:@"No .xcodeproj file was found in \"%@\"", clonedDirectory]; + } else { + recoverySuggestion = [NSString stringWithFormat:@"Found several .xcodeproj files in \"%@\" {%@}", clonedDirectory, [allXcodeProjFilenames componentsJoinedByString:@","]]; + } + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: description, NSLocalizedRecoverySuggestionErrorKey: recoverySuggestion }; + *error = [NSError errorWithDomain:ATZInstallerErrorDomain code:ATZInstallerXcodeProjectNotFoundError userInfo:userInfo]; + } + return nil; } - (NSString *)installNameFromPbxproj:(ATZPackage *)package { - NSString *pbxprojPath = [[[[self pathForDownloadedPackage:package] - stringByAppendingPathComponent:package.name] stringByAppendingString:XCODEPROJ] - stringByAppendingPathComponent:PROJECT_PBXPROJ]; + NSString *pbxprojPath = [[self findXcodeprojPathForPackage:package error:NULL] stringByAppendingPathComponent:PROJECT_PBXPROJ]; return [ATZPbxprojParser xcpluginNameFromPbxproj:pbxprojPath]; } diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b7aa7b..9cfe6a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.0 + +- Plugins are no longer required to have a .xcodeproj filename matching their `name` in `packages.json` (#471) + ## 1.1.19 - Fix a crash caused by invalid downloaded image data being displayed anyway. #320 #334 #335 #479