diff --git a/Squirrel/SQRLDirectoryManager.h b/Squirrel/SQRLDirectoryManager.h index 1b10ce2f..5b65f6e1 100644 --- a/Squirrel/SQRLDirectoryManager.h +++ b/Squirrel/SQRLDirectoryManager.h @@ -39,4 +39,14 @@ // Returns a signal which synchronously sends a URL then completes, or errors. - (RACSignal *)shipItStateURL; +// Determines where ShipIt's stdout log should be saved. +// +// Returns a signal which synchronously sends a URL then completes, or errors. +- (RACSignal *)shipItStdoutURL; + +// Determines where ShipIt's stderr log should be saved. +// +// Returns a signal which synchronously sends a URL then completes, or errors. +- (RACSignal *)shipItStderrURL; + @end diff --git a/Squirrel/SQRLDirectoryManager.m b/Squirrel/SQRLDirectoryManager.m index e43b2a50..34f32107 100644 --- a/Squirrel/SQRLDirectoryManager.m +++ b/Squirrel/SQRLDirectoryManager.m @@ -75,6 +75,24 @@ - (RACSignal *)shipItStateURL { setNameWithFormat:@"%@ -shipItStateURL", self]; } +- (RACSignal *)shipItStdoutURL { + return [[[self + storageURL] + map:^(NSURL *folderURL) { + return [folderURL URLByAppendingPathComponent:@"ShipIt_stdout.log"]; + }] + setNameWithFormat:@"%@ -shipItStdoutURL", self]; +} + +- (RACSignal *)shipItStderrURL { + return [[[self + storageURL] + map:^(NSURL *folderURL) { + return [folderURL URLByAppendingPathComponent:@"ShipIt_stderr.log"]; + }] + setNameWithFormat:@"%@ -shipItStderrURL", self]; +} + #pragma mark NSObject - (NSString *)description { diff --git a/Squirrel/SQRLShipItLauncher.m b/Squirrel/SQRLShipItLauncher.m index 49c66c97..c39fca43 100644 --- a/Squirrel/SQRLShipItLauncher.m +++ b/Squirrel/SQRLShipItLauncher.m @@ -31,9 +31,9 @@ + (RACSignal *)shipItJobDictionary { return [[[RACSignal defer:^{ SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:jobLabel]; - return [directoryManager storageURL]; + return [RACSignal zip:@[ [directoryManager shipItStdoutURL], [directoryManager shipItStderrURL] ]]; }] - map:^(NSURL *storageURL) { + reduceEach:^(NSURL *stdoutURL, NSURL *stderrURL) { NSBundle *squirrelBundle = [NSBundle bundleForClass:self.class]; NSAssert(squirrelBundle != nil, @"Could not open Squirrel.framework bundle"); @@ -57,8 +57,8 @@ + (RACSignal *)shipItJobDictionary { [arguments addObject:jobLabel]; jobDict[@(LAUNCH_JOBKEY_PROGRAMARGUMENTS)] = arguments; - jobDict[@(LAUNCH_JOBKEY_STANDARDOUTPATH)] = [storageURL URLByAppendingPathComponent:@"ShipIt_stdout.log"].path; - jobDict[@(LAUNCH_JOBKEY_STANDARDERRORPATH)] = [storageURL URLByAppendingPathComponent:@"ShipIt_stderr.log"].path; + jobDict[@(LAUNCH_JOBKEY_STANDARDOUTPATH)] = stdoutURL.path; + jobDict[@(LAUNCH_JOBKEY_STANDARDERRORPATH)] = stderrURL.path; return jobDict; }] diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m index b9a8ed5c..57a094bb 100644 --- a/Squirrel/SQRLUpdater.m +++ b/Squirrel/SQRLUpdater.m @@ -171,13 +171,8 @@ - (id)initWithUpdateRequest:(NSURLRequest *)updateRequest { NSMutableURLRequest *request = [self.updateRequest mutableCopy]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; - // Prune old updates before the first update check. - return [[[[[[[[[self - pruneUpdateDirectories] - catch:^(NSError *error) { - NSLog(@"Error pruning old updates: %@", error); - return [RACSignal empty]; - }] + return [[[[[[[[self + performHousekeeping] then:^{ self.state = SQRLUpdaterStateCheckingForUpdate; @@ -480,6 +475,15 @@ - (RACSignal *)shipItStateURL { setNameWithFormat:@"%@ -shipItStateURL", self]; } +- (RACSignal *)performHousekeeping { + return [[RACSignal + merge:@[ [self pruneUpdateDirectories], [self truncateLogs] ]] + catch:^(NSError *error) { + NSLog(@"Error doing housekeeping: %@", error); + return [RACSignal empty]; + }]; +} + /// Lazily removes outdated temporary directories (used for previous updates) /// upon subscription. /// @@ -516,6 +520,39 @@ - (RACSignal *)pruneUpdateDirectories { setNameWithFormat:@"%@ -prunedUpdateDirectories", self]; } + +// Like truncation, but backwards. +- (RACSignal *)backwardTruncateFile:(NSURL *)fileURL { + return [RACSignal defer:^{ + static const NSInteger MAX_LENGTH = 1024 * 1024 * 8; + + NSError *error; + NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:fileURL error:&error]; + if (handle == nil) return [RACSignal error:error]; + + unsigned long long fileLength = [handle seekToEndOfFile]; + if (fileLength <= MAX_LENGTH) return [RACSignal empty]; + + [handle seekToFileOffset:fileLength - MAX_LENGTH]; + NSData *mostRecentData = [handle readDataToEndOfFile]; + [handle truncateFileAtOffset:0]; + [handle writeData:mostRecentData]; + + return [RACSignal empty]; + }]; +} + +- (RACSignal *)truncateLogs { + return [[RACSignal + defer:^{ + SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel]; + return [RACSignal zip:@[ [directoryManager shipItStdoutURL], [directoryManager shipItStderrURL] ]]; + }] + reduceEach:^(NSURL *stdoutURL, NSURL *stderrURL) { + return [RACSignal merge:@[ [self backwardTruncateFile:stdoutURL], [self backwardTruncateFile:stderrURL] ]]; + }]; +} + #pragma mark Installing Updates - (RACSignal *)verifyAndPrepareUpdate:(SQRLUpdate *)update fromBundle:(NSBundle *)updateBundle {