Author SHA1 Message Date
Keith Duncan 0828897f1b 🍩 pass the request path around 2014-09-02 17:08:50 +02:00
Keith Duncan ff31a5fcdd Replace the installer with an XPC version
Doesn’t read install requests from the file system, also makes the installer installable once rather than once per install request.
2014-09-01 22:28:31 +02:00
Keith Duncan 016cd25fda Update docs 2014-09-01 22:23:13 +02:00
Keith Duncan afebefc78a Remove the watcher 2014-08-05 21:46:50 +01:00
Keith Duncan 3c7916e02e More building++ 2014-08-05 21:20:47 +01:00
Keith Duncan 8d6063d9a9 Fix missed conflict in merge 2014-08-05 21:17:50 +01:00
Keith Duncan 24c118fa1e xctest? 2014-08-05 21:17:48 +01:00
Keith Duncan ff7b9de34f Remove the watcher 2014-08-05 21:17:19 +01:00
Keith Duncan 5994cf9584 Merge remote-tracking branch 'origin/master' into dont-link-appkit
Conflicts:
	Squirrel.xcodeproj/project.pbxproj
	Squirrel/SQRLShipItRequest.m
	SquirrelTests/SQRLTestCase.m
2014-08-05 21:05:49 +01:00
Keith Duncan 518ee666a3 Restrict the permissions of the wait files directory 2014-02-19 14:46:17 +00:00
Keith Duncan 73cc6802d3 Clarify the installer args 2014-02-19 13:58:26 +00:00
Keith Duncan 1403a7ad81 Make the watcher job dictionary method return a signal
Fix the signal names for the job dictionary methods.
Extract the watcher job label into a method.
2014-02-19 13:57:16 +00:00
Keith Duncan 24d9ef1f86 Remove the debug launchd option 2014-02-18 13:51:52 +00:00
Keith Duncan 51bd593666 Remove the domain/authorisation tuple 2014-02-18 13:12:27 +00:00
Keith Duncan 44a471b903 Concat the submit job signals inline 2014-02-18 13:12:13 +00:00
Keith Duncan 6e2f4faa3f Style++ 2014-02-18 13:06:33 +00:00
Keith Duncan 43fd18e4c9 Less URL->path conversions 2014-02-18 13:06:02 +00:00
Keith Duncan d08804ddce Less URL->path conversions 2014-02-18 13:01:04 +00:00
Keith Duncan 39a47ac14f s/command/executableName 2014-02-18 12:58:35 +00:00
Keith Duncan 49148013dc Request cannot be nil 2014-02-18 12:57:51 +00:00
Keith Duncan 15c80b1cbf Better docs for the send request method 2014-02-18 12:57:14 +00:00
Keith Duncan f89762550f Rename the connection initialiser 2014-02-18 12:54:59 +00:00
Keith Duncan b224b30a27 s/[[:space:]]/[[:tab:]] 2014-02-14 19:12:08 +00:00
Keith Duncan bfa660b5bc Add docs on the multiprocess topology to SQRLShipItConnection 2014-02-14 19:03:00 +00:00
Keith Duncan 3ed1dc26fa Add docs for the installer args 2014-02-12 16:34:23 +00:00
Keith Duncan 442e51b89d Change the watcher args to be URLs 2014-02-12 16:34:04 +00:00
Keith Duncan a8b6a9f262 Condense the job dictionary creation 2014-02-12 16:26:34 +00:00
Keith Duncan 05019c8e82 New file listener API
Now with 💯% more error checking
2014-02-12 16:13:45 +00:00
Keith Duncan d569a6524e Document the shipit-watcher arguments 2014-02-12 12:57:29 +00:00
Keith Duncan 5c64d501f4 Style++ 2014-02-12 12:43:25 +00:00
Keith Duncan ee8c781284 Fix signal names 2014-02-06 21:22:38 +00:00
Keith Duncan d8b1c4b6ca Merge remote-tracking branch 'origin/master' into dont-link-appkit
Conflicts:
	Squirrel.xcodeproj/project.pbxproj
	Squirrel/SQRLInstaller.m
	Squirrel/SQRLShipItState.h
	Squirrel/SQRLShipItState.m
	Squirrel/SQRLUpdater.m
	Squirrel/ShipIt-main.m
	SquirrelTests/SQRLInstallerSpec.m
	SquirrelTests/SQRLShipItStateSpec.m
	SquirrelTests/SQRLTestCase.h
	SquirrelTests/SQRLTestCase.m
2014-02-06 21:22:15 +00:00
Keith Duncan 3582cdec0b Fix the signal tests to use the new process name 2014-01-20 16:26:39 -08:00
Keith Duncan ef169d19e9 Fix task launching on 10.7 2014-01-20 16:19:19 -08:00
Keith Duncan 582593dca7 Fix 10.7 support 2014-01-20 16:14:24 -08:00
Keith Duncan ba6df2df14 Fix argument docs 2014-01-20 16:09:13 -08:00
Keith Duncan 65463d7612 Rename signal 2014-01-20 16:07:54 -08:00
Keith Duncan 1f38d749ea Use the job label when removing and adding 2014-01-20 16:00:14 -08:00
Keith Duncan c4871aa371 Add launch support using LaunchServices directly 2014-01-20 15:18:15 -08:00
Keith Duncan f067d6b778 Zip++ 2014-01-20 15:18:11 -08:00
Keith Duncan 2c10c116e9 Clean up the wait file when the installer completes 2014-01-20 12:53:05 -08:00
Keith Duncan cfc2d55ca9 Write the state before submitting the jobs 2014-01-20 12:40:22 -08:00
Keith Duncan daaf62645e Don’t pass the label to the watcher 2014-01-20 12:40:15 -08:00
Keith Duncan f34c02b79b Run watcher on launch 2014-01-20 12:29:23 -08:00
Keith Duncan 5d5f5eaa12 Make submitJob:... a cold signal 2014-01-20 12:05:01 -08:00
Keith Duncan 40e5ac130e Fix types issue, the authorization in the tuple is a signal 2014-01-20 11:57:18 -08:00
Keith Duncan e449f18bd3 Tweak test description 2014-01-20 11:56:54 -08:00
Keith Duncan 6d4f8a731b Xcode updating schemes 2014-01-17 17:20:16 -08:00
Keith Duncan 9debcdcd57 Update to the new NSURL argument for state read/write 2014-01-17 17:19:57 -08:00
Keith Duncan 4d489e5add Style++ 2014-01-17 17:19:37 -08:00
Keith Duncan 60f4089f6f Use the new file watcher rather than termination listener 2014-01-17 17:19:13 -08:00
Keith Duncan 33d2c31db9 Switch to using the NSURL argument type 2014-01-17 17:18:46 -08:00
Keith Duncan 267cd56bd6 Add file listener class
Parallels the termination listener
2014-01-17 17:13:22 -08:00
Keith Duncan 2a4147f3b1 Make ShipIt state take plain NSURL arguments
Get the watcher job to launch too, still needs to listen for its file in the installer.
2014-01-17 12:30:12 -08:00
Keith Duncan fb991f348b Rename job submit method 2014-01-17 11:37:04 -08:00
Keith Duncan 839f4a9708 Remove Mach service, this isn’t used 2014-01-17 11:31:37 -08:00
Keith Duncan e142e1bef0 Implement the new watcher process 2014-01-15 13:48:40 -08:00
Keith Duncan 14892783cd Start a new target for the watcher 2014-01-15 10:25:42 -08:00
Keith Duncan 88c65394c1 Implement a separate watch command
This is so that we can split the waiting into running in the user launchd domain, and the install still runs in the root domain.
2014-01-15 10:16:17 -08:00
Keith Duncan afc0ad0b20 Don’t use NSRunningApplication in SQRLDirectoryManager
This is AppKit API and `SQRLDirectoryManager` should be Foundation only.
2014-01-15 10:11:21 -08:00
Keith Duncan 0c533da4d7 Start launch logic in SQRLShipItConnection 2014-01-12 20:45:17 +00:00
Keith Duncan a5f8798ce8 Remove the Cocoa linkage from ShipIt, link Foundation instead 2014-01-12 20:20:50 +00:00
Keith Duncan ab52a12630 Remove launch handing from ShipIt-main
This will be handled external to ShipIt which can run as root and therefore should not fork or comminicate with LaunchServices.
2014-01-12 20:18:17 +00:00
Keith Duncan 954de5d480 Move SQRLTestCase to the top of the group 2014-01-12 20:17:31 +00:00
Keith Duncan df71c4d256 Renme the launchShipIt method 2014-01-12 20:17:15 +00:00
Keith Duncan d73df99d57 Remove un-necessary +defer 2014-01-12 20:16:54 +00:00
Keith Duncan bfc2652481 Pass the SQRLShipItState to SQRLShipItConnection for writing
The launched ShipIt is now a function of the written request.
2014-01-12 19:56:24 +00:00
Keith Duncan 90f31dd0f8 Use a connection instance to send the request
Whether to launch the target is now an argument, this needs to be handled by this process not the aux. process which may be root.
2014-01-12 19:41:20 +00:00
Keith Duncan 470338574f Rename SQRLShipItLauncher to SQRLShipItConnection 2014-01-12 19:34:08 +00:00
21 changed files with 598 additions and 673 deletions
+40 -50
View File
@@ -19,14 +19,16 @@
5371816018A29DC8005ED798 /* TestAppConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 5371815E18A29DC8005ED798 /* TestAppConstants.m */; };
5374DCC6187AD0D8006B7056 /* SQRLAuthorization.h in Headers */ = {isa = PBXBuildFile; fileRef = 5374DCC4187AD0D8006B7056 /* SQRLAuthorization.h */; };
5374DCC7187AD0D8006B7056 /* SQRLAuthorization.m in Sources */ = {isa = PBXBuildFile; fileRef = 5374DCC5187AD0D8006B7056 /* SQRLAuthorization.m */; };
537648FE18873592008FB816 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0366C0017B18FEE0086ED44 /* AppKit.framework */; };
5395C0E317E9D013001648E8 /* SQRLUpdateSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5395C0E217E9D013001648E8 /* SQRLUpdateSpec.m */; };
5397A5CE187D92810014A477 /* SQRLShipItRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 5397A5CC187D92810014A477 /* SQRLShipItRequest.h */; };
5397A5CF187D92810014A477 /* SQRLShipItRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5397A5CD187D92810014A477 /* SQRLShipItRequest.m */; };
5397A5D0187D92810014A477 /* SQRLShipItRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5397A5CD187D92810014A477 /* SQRLShipItRequest.m */; };
5397A5EC187DA2570014A477 /* SQRLInstallerOwnedBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 5397A5EB187DA2570014A477 /* SQRLInstallerOwnedBundle.m */; };
5397A69D187DF8610014A477 /* SQRLInstallerOwnedBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 5397A5EB187DA2570014A477 /* SQRLInstallerOwnedBundle.m */; };
53AACF4B17E9CA0500B41027 /* SQRLUpdate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53AACF4917E9CA0500B41027 /* SQRLUpdate.h */; settings = {ATTRIBUTES = (Public, ); }; };
53AACF4C17E9CA0500B41027 /* SQRLUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53AACF4A17E9CA0500B41027 /* SQRLUpdate.m */; };
53C816A218A4314500A8B595 /* SQRLInstallerOwnedBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C816A118A4314500A8B595 /* SQRLInstallerOwnedBundle.m */; };
53C816A318A4314500A8B595 /* SQRLInstallerOwnedBundle.m in Sources */ = {isa = PBXBuildFile; fileRef = 53C816A118A4314500A8B595 /* SQRLInstallerOwnedBundle.m */; };
53CE752D188330590085C0EB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0366C0217B18FEE0086ED44 /* Foundation.framework */; };
BE312944196B999300F1A675 /* SQRLTestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BE312943196B999300F1A675 /* SQRLTestHelper.m */; };
BE31BEA0196B74A900376A76 /* libSpecta-OSX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BE31BE9F196B74A900376A76 /* libSpecta-OSX.a */; };
BE31BEA1196B74B000376A76 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0366C1C17B18FEE0086ED44 /* XCTest.framework */; };
@@ -36,19 +38,16 @@
D00F5B8B17E82D15009A4818 /* NSProcessInfo+SQRLVersionExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D00F5B8917E82D15009A4818 /* NSProcessInfo+SQRLVersionExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; };
D00F5B8C17E82D15009A4818 /* NSProcessInfo+SQRLVersionExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D00F5B8A17E82D15009A4818 /* NSProcessInfo+SQRLVersionExtensions.m */; };
D00F5B8E17E82DE6009A4818 /* NSProcessInfoExtensionsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D00F5B8D17E82DE6009A4818 /* NSProcessInfoExtensionsSpec.m */; };
D014ABB817B96CB3007D79D0 /* SQRLShipItLauncher.m in Sources */ = {isa = PBXBuildFile; fileRef = D014ABB617B96CB3007D79D0 /* SQRLShipItLauncher.m */; };
D014ABB817B96CB3007D79D0 /* SQRLShipItConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D014ABB617B96CB3007D79D0 /* SQRLShipItConnection.m */; };
D014ABBA17B97403007D79D0 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D014ABB917B97403007D79D0 /* ServiceManagement.framework */; };
D014AC0517B97885007D79D0 /* ShipIt-main.m in Sources */ = {isa = PBXBuildFile; fileRef = D014AC0417B97885007D79D0 /* ShipIt-main.m */; };
D014AC0517B97885007D79D0 /* ShipItInstaller-main.m in Sources */ = {isa = PBXBuildFile; fileRef = D014AC0417B97885007D79D0 /* ShipItInstaller-main.m */; };
D014AC1117B9789C007D79D0 /* SQRLInstaller.m in Sources */ = {isa = PBXBuildFile; fileRef = D014AC1017B9789C007D79D0 /* SQRLInstaller.m */; };
D014AC1817B97945007D79D0 /* ShipIt in Copy ShipIt */ = {isa = PBXBuildFile; fileRef = D014AC0117B97885007D79D0 /* ShipIt */; };
D014AC1817B97945007D79D0 /* shipit-installer in Copy ShipIt */ = {isa = PBXBuildFile; fileRef = D014AC0117B97885007D79D0 /* shipit-installer */; };
D014AC1917B979A8007D79D0 /* SQRLCodeSignature.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F2ED8217A33C4900F4D220 /* SQRLCodeSignature.m */; };
D014AC1A17B979AA007D79D0 /* NSError+SQRLVerbosityExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EDBE9B17B0C68E0058BC3C /* NSError+SQRLVerbosityExtensions.m */; };
D014AC1D17B979B7007D79D0 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6EB217F179D0E4D001108CF /* Security.framework */; };
D014AC1E17B979BD007D79D0 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D014ABB917B97403007D79D0 /* ServiceManagement.framework */; };
D014AC1F17B979C4007D79D0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C22BDA179CC00E00158214 /* Cocoa.framework */; };
D03BE98D17B6174500458593 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D03BE98C17B6174500458593 /* MainMenu.xib */; };
D043F77217FF40C90012F996 /* SQRLTerminationListener.m in Sources */ = {isa = PBXBuildFile; fileRef = D043F77117FF40C90012F996 /* SQRLTerminationListener.m */; };
D043F77E17FF43640012F996 /* SQRLTerminationListener.m in Sources */ = {isa = PBXBuildFile; fileRef = D043F77117FF40C90012F996 /* SQRLTerminationListener.m */; };
D049059B180554BA004E683E /* SQRLDirectoryManagerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D049059A180554BA004E683E /* SQRLDirectoryManagerSpec.m */; };
D049059D18055671004E683E /* SQRLShipItRequestSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D049059C18055671004E683E /* SQRLShipItRequestSpec.m */; };
D04905A0180558B7004E683E /* SQRLTestUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = D049059F180558B7004E683E /* SQRLTestUpdate.m */; };
@@ -57,7 +56,6 @@
D061E40317FD581C00CC134A /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D097B9BC17BB4777006C3FEB /* IOKit.framework */; };
D06B58B518032B1500656D97 /* RACSignal+SQRLTransactionExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D06B58B318032B1500656D97 /* RACSignal+SQRLTransactionExtensions.m */; };
D06B58B618032B1500656D97 /* RACSignal+SQRLTransactionExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D06B58B318032B1500656D97 /* RACSignal+SQRLTransactionExtensions.m */; };
D06B58D118035B5A00656D97 /* SQRLTerminationListenerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D06B58D018035B5A00656D97 /* SQRLTerminationListenerSpec.m */; };
D08D4E0D17B451500012B22D /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C22BDA179CC00E00158214 /* Cocoa.framework */; };
D08D4E1317B451500012B22D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D08D4E1117B451500012B22D /* InfoPlist.strings */; };
D08D4E1517B451500012B22D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D08D4E1417B451500012B22D /* main.m */; };
@@ -177,7 +175,7 @@
dstPath = "";
dstSubfolderSpec = 7;
files = (
D014AC1817B97945007D79D0 /* ShipIt in Copy ShipIt */,
D014AC1817B97945007D79D0 /* shipit-installer in Copy ShipIt */,
);
name = "Copy ShipIt";
runOnlyForDeploymentPostprocessing = 0;
@@ -229,11 +227,11 @@
5395C0E217E9D013001648E8 /* SQRLUpdateSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLUpdateSpec.m; sourceTree = "<group>"; };
5397A5CC187D92810014A477 /* SQRLShipItRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLShipItRequest.h; sourceTree = "<group>"; };
5397A5CD187D92810014A477 /* SQRLShipItRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLShipItRequest.m; sourceTree = "<group>"; };
5397A5EA187DA2570014A477 /* SQRLInstallerOwnedBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLInstallerOwnedBundle.h; sourceTree = "<group>"; };
5397A5EB187DA2570014A477 /* SQRLInstallerOwnedBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLInstallerOwnedBundle.m; sourceTree = "<group>"; };
5397A69C187DF6490014A477 /* SQRLInstaller+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SQRLInstaller+Private.h"; sourceTree = "<group>"; };
53AACF4917E9CA0500B41027 /* SQRLUpdate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLUpdate.h; sourceTree = "<group>"; };
53AACF4A17E9CA0500B41027 /* SQRLUpdate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLUpdate.m; sourceTree = "<group>"; };
53C816A018A4314500A8B595 /* SQRLInstallerOwnedBundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLInstallerOwnedBundle.h; sourceTree = "<group>"; };
53C816A118A4314500A8B595 /* SQRLInstallerOwnedBundle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLInstallerOwnedBundle.m; sourceTree = "<group>"; };
BE312942196B999300F1A675 /* SQRLTestHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLTestHelper.h; sourceTree = "<group>"; };
BE312943196B999300F1A675 /* SQRLTestHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLTestHelper.m; sourceTree = "<group>"; };
BE31BE9F196B74A900376A76 /* libSpecta-OSX.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libSpecta-OSX.a"; path = "External/ReactiveCocoa/external/specta/build/Debug/libSpecta-OSX.a"; sourceTree = "<group>"; };
@@ -244,11 +242,11 @@
D00F5B8917E82D15009A4818 /* NSProcessInfo+SQRLVersionExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSProcessInfo+SQRLVersionExtensions.h"; sourceTree = "<group>"; };
D00F5B8A17E82D15009A4818 /* NSProcessInfo+SQRLVersionExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSProcessInfo+SQRLVersionExtensions.m"; sourceTree = "<group>"; };
D00F5B8D17E82DE6009A4818 /* NSProcessInfoExtensionsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSProcessInfoExtensionsSpec.m; sourceTree = "<group>"; };
D014ABB517B96CB3007D79D0 /* SQRLShipItLauncher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLShipItLauncher.h; sourceTree = "<group>"; };
D014ABB617B96CB3007D79D0 /* SQRLShipItLauncher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLShipItLauncher.m; sourceTree = "<group>"; };
D014ABB517B96CB3007D79D0 /* SQRLShipItConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLShipItConnection.h; sourceTree = "<group>"; };
D014ABB617B96CB3007D79D0 /* SQRLShipItConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLShipItConnection.m; sourceTree = "<group>"; };
D014ABB917B97403007D79D0 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
D014AC0117B97885007D79D0 /* ShipIt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ShipIt; sourceTree = BUILT_PRODUCTS_DIR; };
D014AC0417B97885007D79D0 /* ShipIt-main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "ShipIt-main.m"; path = "Squirrel/ShipIt-main.m"; sourceTree = SOURCE_ROOT; };
D014AC0117B97885007D79D0 /* shipit-installer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "shipit-installer"; sourceTree = BUILT_PRODUCTS_DIR; };
D014AC0417B97885007D79D0 /* ShipItInstaller-main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "ShipItInstaller-main.m"; path = "Squirrel/ShipItInstaller-main.m"; sourceTree = SOURCE_ROOT; };
D014AC0F17B9789C007D79D0 /* SQRLInstaller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SQRLInstaller.h; path = Squirrel/SQRLInstaller.h; sourceTree = SOURCE_ROOT; };
D014AC1017B9789C007D79D0 /* SQRLInstaller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SQRLInstaller.m; path = Squirrel/SQRLInstaller.m; sourceTree = SOURCE_ROOT; };
D0366C0017B18FEE0086ED44 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -256,15 +254,12 @@
D0366C0217B18FEE0086ED44 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
D0366C1C17B18FEE0086ED44 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
D03BE98C17B6174500458593 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
D043F77017FF40C90012F996 /* SQRLTerminationListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLTerminationListener.h; sourceTree = "<group>"; };
D043F77117FF40C90012F996 /* SQRLTerminationListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLTerminationListener.m; sourceTree = "<group>"; };
D049059A180554BA004E683E /* SQRLDirectoryManagerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLDirectoryManagerSpec.m; sourceTree = "<group>"; };
D049059C18055671004E683E /* SQRLShipItRequestSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLShipItRequestSpec.m; sourceTree = "<group>"; };
D049059E180558B7004E683E /* SQRLTestUpdate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQRLTestUpdate.h; sourceTree = "<group>"; };
D049059F180558B7004E683E /* SQRLTestUpdate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLTestUpdate.m; sourceTree = "<group>"; };
D06B58B218032B1500656D97 /* RACSignal+SQRLTransactionExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RACSignal+SQRLTransactionExtensions.h"; sourceTree = "<group>"; };
D06B58B318032B1500656D97 /* RACSignal+SQRLTransactionExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RACSignal+SQRLTransactionExtensions.m"; sourceTree = "<group>"; };
D06B58D018035B5A00656D97 /* SQRLTerminationListenerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQRLTerminationListenerSpec.m; sourceTree = "<group>"; };
D08D4E0C17B451500012B22D /* TestApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestApplication.app; sourceTree = BUILT_PRODUCTS_DIR; };
D08D4E1017B451500012B22D /* TestApplication-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestApplication-Info.plist"; sourceTree = "<group>"; };
D08D4E1217B451500012B22D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -347,8 +342,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
537648FE18873592008FB816 /* AppKit.framework in Frameworks */,
53CE752D188330590085C0EB /* Foundation.framework in Frameworks */,
D097B9BD17BB4777006C3FEB /* IOKit.framework in Frameworks */,
D014AC1F17B979C4007D79D0 /* Cocoa.framework in Frameworks */,
D014AC1D17B979B7007D79D0 /* Security.framework in Frameworks */,
D014AC1E17B979BD007D79D0 /* ServiceManagement.framework in Frameworks */,
D4C490A118E46E8200786EFE /* libMantle-Mac.a in Frameworks */,
@@ -455,19 +451,15 @@
name = Extensions;
sourceTree = "<group>";
};
D014AC0317B97885007D79D0 /* ShipIt */ = {
D014AC0317B97885007D79D0 /* ShipIt Installer */ = {
isa = PBXGroup;
children = (
D014AC0417B97885007D79D0 /* ShipIt-main.m */,
D014AC0417B97885007D79D0 /* ShipItInstaller-main.m */,
D014AC0F17B9789C007D79D0 /* SQRLInstaller.h */,
5397A69C187DF6490014A477 /* SQRLInstaller+Private.h */,
D014AC1017B9789C007D79D0 /* SQRLInstaller.m */,
D043F77017FF40C90012F996 /* SQRLTerminationListener.h */,
D043F77117FF40C90012F996 /* SQRLTerminationListener.m */,
5397A5EA187DA2570014A477 /* SQRLInstallerOwnedBundle.h */,
5397A5EB187DA2570014A477 /* SQRLInstallerOwnedBundle.m */,
);
name = ShipIt;
name = "ShipIt Installer";
path = Squirrel;
sourceTree = "<group>";
};
@@ -555,8 +547,8 @@
D0964B3517F2DF3100D88BF7 /* IPC */ = {
isa = PBXGroup;
children = (
D014ABB517B96CB3007D79D0 /* SQRLShipItLauncher.h */,
D014ABB617B96CB3007D79D0 /* SQRLShipItLauncher.m */,
D014ABB517B96CB3007D79D0 /* SQRLShipItConnection.h */,
D014ABB617B96CB3007D79D0 /* SQRLShipItConnection.m */,
);
name = IPC;
sourceTree = "<group>";
@@ -568,6 +560,8 @@
D0D2B6241804E903000EA901 /* SQRLDirectoryManager.m */,
5397A5CC187D92810014A477 /* SQRLShipItRequest.h */,
5397A5CD187D92810014A477 /* SQRLShipItRequest.m */,
53C816A018A4314500A8B595 /* SQRLInstallerOwnedBundle.h */,
53C816A118A4314500A8B595 /* SQRLInstallerOwnedBundle.m */,
);
name = State;
sourceTree = "<group>";
@@ -586,9 +580,9 @@
children = (
D0EDBE9F17B0C6930058BC3C /* Shared */,
D0C22BE0179CC00E00158214 /* Squirrel */,
D014AC0317B97885007D79D0 /* ShipIt */,
D0C22BF6179CC00E00158214 /* SquirrelTests */,
D08D4E0E17B451500012B22D /* TestApplication */,
D014AC0317B97885007D79D0 /* ShipIt Installer */,
D0C22BD9179CC00E00158214 /* Frameworks */,
53710D7C17D8F5C300A992DE /* Scripts */,
D0C22C07179CC02E00158214 /* Configuration */,
@@ -603,7 +597,7 @@
D0C22BD7179CC00E00158214 /* Squirrel.framework */,
D0C22BEF179CC00E00158214 /* SquirrelTests.xctest */,
D08D4E0C17B451500012B22D /* TestApplication.app */,
D014AC0117B97885007D79D0 /* ShipIt */,
D014AC0117B97885007D79D0 /* shipit-installer */,
534FF36017D8E90A0020A51A /* com.github.Squirrel.TestApplication.TestService.xpc */,
);
name = Products;
@@ -760,7 +754,6 @@
D049059A180554BA004E683E /* SQRLDirectoryManagerSpec.m */,
D09D244017B595470001FAF8 /* SQRLInstallerSpec.m */,
D049059C18055671004E683E /* SQRLShipItRequestSpec.m */,
D06B58D018035B5A00656D97 /* SQRLTerminationListenerSpec.m */,
D0EDBEDB17B0DB3E0058BC3C /* SQRLTestCase.h */,
D0EDBEDC17B0DB3E0058BC3C /* SQRLTestCase.m */,
BE312942196B999300F1A675 /* SQRLTestHelper.h */,
@@ -823,9 +816,9 @@
productReference = 534FF36017D8E90A0020A51A /* com.github.Squirrel.TestApplication.TestService.xpc */;
productType = "com.apple.product-type.bundle";
};
D014AC0017B97885007D79D0 /* ShipIt */ = {
D014AC0017B97885007D79D0 /* ShipIt Installer */ = {
isa = PBXNativeTarget;
buildConfigurationList = D014AC0A17B97885007D79D0 /* Build configuration list for PBXNativeTarget "ShipIt" */;
buildConfigurationList = D014AC0A17B97885007D79D0 /* Build configuration list for PBXNativeTarget "ShipIt Installer" */;
buildPhases = (
D014ABFD17B97885007D79D0 /* Sources */,
D014ABFE17B97885007D79D0 /* Frameworks */,
@@ -834,9 +827,9 @@
);
dependencies = (
);
name = ShipIt;
name = "ShipIt Installer";
productName = ShipIt;
productReference = D014AC0117B97885007D79D0 /* ShipIt */;
productReference = D014AC0117B97885007D79D0 /* shipit-installer */;
productType = "com.apple.product-type.tool";
};
D08D4E0B17B451500012B22D /* TestApplication */ = {
@@ -928,7 +921,7 @@
D0C22BEE179CC00E00158214 /* SquirrelTests */,
D08D4E0B17B451500012B22D /* TestApplication */,
534FF35F17D8E90A0020A51A /* TestService */,
D014AC0017B97885007D79D0 /* ShipIt */,
D014AC0017B97885007D79D0 /* ShipIt Installer */,
);
};
/* End PBXProject section */
@@ -990,12 +983,11 @@
buildActionMask = 2147483647;
files = (
D014AC1117B9789C007D79D0 /* SQRLInstaller.m in Sources */,
D043F77217FF40C90012F996 /* SQRLTerminationListener.m in Sources */,
D014AC0517B97885007D79D0 /* ShipIt-main.m in Sources */,
D014AC0517B97885007D79D0 /* ShipItInstaller-main.m in Sources */,
D014AC1917B979A8007D79D0 /* SQRLCodeSignature.m in Sources */,
5397A5D0187D92810014A477 /* SQRLShipItRequest.m in Sources */,
D06B58B618032B1500656D97 /* RACSignal+SQRLTransactionExtensions.m in Sources */,
5397A5EC187DA2570014A477 /* SQRLInstallerOwnedBundle.m in Sources */,
53C816A318A4314500A8B595 /* SQRLInstallerOwnedBundle.m in Sources */,
D014AC1A17B979AA007D79D0 /* NSError+SQRLVerbosityExtensions.m in Sources */,
D0D2B6271804E903000EA901 /* SQRLDirectoryManager.m in Sources */,
D0964B3E17F2E20B00D88BF7 /* NSBundle+SQRLVersionExtensions.m in Sources */,
@@ -1021,7 +1013,7 @@
F6F2ED8417A33C4900F4D220 /* SQRLCodeSignature.m in Sources */,
D0D2B6261804E903000EA901 /* SQRLDirectoryManager.m in Sources */,
D0964B3917F2E01500D88BF7 /* SQRLDownloadedUpdate.m in Sources */,
D014ABB817B96CB3007D79D0 /* SQRLShipItLauncher.m in Sources */,
D014ABB817B96CB3007D79D0 /* SQRLShipItConnection.m in Sources */,
D000218F17BACEFF0050109A /* SQRLZipArchiver.m in Sources */,
D0C22C4D179CC15100158214 /* SQRLUpdater.m in Sources */,
D0EDBE9D17B0C68E0058BC3C /* NSError+SQRLVerbosityExtensions.m in Sources */,
@@ -1039,17 +1031,15 @@
files = (
5395C0E317E9D013001648E8 /* SQRLUpdateSpec.m in Sources */,
D000219917BAD35C0050109A /* SQRLZipArchiverSpec.m in Sources */,
D043F77E17FF43640012F996 /* SQRLTerminationListener.m in Sources */,
D09D244117B595470001FAF8 /* SQRLInstallerSpec.m in Sources */,
D0C22C50179CC18C00158214 /* SQRLUpdaterSpec.m in Sources */,
D0EDBEDD17B0DB3E0058BC3C /* SQRLTestCase.m in Sources */,
D06B58D118035B5A00656D97 /* SQRLTerminationListenerSpec.m in Sources */,
5371815F18A29DC8005ED798 /* TestAppConstants.m in Sources */,
D0EDBEE117B0E3650058BC3C /* SQRLCodeSignatureSpec.m in Sources */,
53710D7417D8F59700A992DE /* SQRLDeepCodesignSpec.m in Sources */,
D00F5B8E17E82DE6009A4818 /* NSProcessInfoExtensionsSpec.m in Sources */,
D049059B180554BA004E683E /* SQRLDirectoryManagerSpec.m in Sources */,
5397A69D187DF8610014A477 /* SQRLInstallerOwnedBundle.m in Sources */,
53C816A218A4314500A8B595 /* SQRLInstallerOwnedBundle.m in Sources */,
BE312944196B999300F1A675 /* SQRLTestHelper.m in Sources */,
D061E40217FD580D00CC134A /* SQRLInstaller.m in Sources */,
D04905A0180558B7004E683E /* SQRLTestUpdate.m in Sources */,
@@ -1067,7 +1057,7 @@
};
D014AC1717B9793A007D79D0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D014AC0017B97885007D79D0 /* ShipIt */;
target = D014AC0017B97885007D79D0 /* ShipIt Installer */;
targetProxy = D014AC1617B9793A007D79D0 /* PBXContainerItemProxy */;
};
D08D4E4617B451E10012B22D /* PBXTargetDependency */ = {
@@ -1188,7 +1178,7 @@
"\"$(OBJROOT)/UninstalledProducts/include/\"",
);
OTHER_LDFLAGS = "-all_load";
PRODUCT_NAME = "$(TARGET_NAME)";
PRODUCT_NAME = "shipit-installer";
SKIP_INSTALL = YES;
};
name = Debug;
@@ -1202,7 +1192,7 @@
"\"$(OBJROOT)/UninstalledProducts/include/\"",
);
OTHER_LDFLAGS = "-all_load";
PRODUCT_NAME = "$(TARGET_NAME)";
PRODUCT_NAME = "shipit-installer";
SKIP_INSTALL = YES;
};
name = Release;
@@ -1216,7 +1206,7 @@
"\"$(OBJROOT)/UninstalledProducts/include/\"",
);
OTHER_LDFLAGS = "-all_load";
PRODUCT_NAME = "$(TARGET_NAME)";
PRODUCT_NAME = "shipit-installer";
SKIP_INSTALL = YES;
};
name = Test;
@@ -1230,7 +1220,7 @@
"\"$(OBJROOT)/UninstalledProducts/include/\"",
);
OTHER_LDFLAGS = "-all_load";
PRODUCT_NAME = "$(TARGET_NAME)";
PRODUCT_NAME = "shipit-installer";
SKIP_INSTALL = YES;
};
name = Profile;
@@ -1495,7 +1485,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D014AC0A17B97885007D79D0 /* Build configuration list for PBXNativeTarget "ShipIt" */ = {
D014AC0A17B97885007D79D0 /* Build configuration list for PBXNativeTarget "ShipIt Installer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D014AC0B17B97885007D79D0 /* Debug */,
@@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D014AC0017B97885007D79D0"
BuildableName = "ShipIt"
BlueprintName = "ShipIt"
BuildableName = "shipit-installer"
BlueprintName = "ShipIt Installer"
ReferencedContainer = "container:Squirrel.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -28,6 +28,16 @@
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Test">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0C22BEE179CC00E00158214"
BuildableName = "SquirrelTests.xctest"
BlueprintName = "SquirrelTests"
ReferencedContainer = "container:Squirrel.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
+1 -1
View File
@@ -23,7 +23,7 @@
// Should only fallback to when running under otest, where
// NSBundle.mainBundle doesn't return useful data.
if (identifier == nil) {
identifier = NSRunningApplication.currentApplication.localizedName;
identifier = NSProcessInfo.processInfo.processName;
}
NSAssert(identifier != nil, @"Could not automatically determine the current application's identifier");
-1
View File
@@ -13,7 +13,6 @@
#import "SQRLCodeSignature.h"
#import "SQRLDirectoryManager.h"
#import "SQRLShipItRequest.h"
#import "SQRLTerminationListener.h"
#import <libkern/OSAtomic.h>
#import <ReactiveCocoa/EXTScope.h>
#import <ReactiveCocoa/ReactiveCocoa.h>
+81
View File
@@ -0,0 +1,81 @@
//
// SQRLShipItConnection.h
// Squirrel
//
// Created by Justin Spahr-Summers on 2013-08-12.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import <Foundation/Foundation.h>
@class RACSignal;
@class SQRLShipItRequest;
// The domain for errors originating within SQRLShipItConnection.
extern NSString * const SQRLShipItConnectionErrorDomain;
// The ShipIt service could not be started.
extern const NSInteger SQRLShipItConnectionErrorCouldNotStartService;
// Installing an update requires the coordination of multiple processes,
// `SQRLShipItConnection` is responsible for submitting the launchd jobs
// required to perform an installation request.
//
// The multiprocess approach to waiting for termination and installing is
// designed to keep uses of AppKit API (i.e. `NSRunningApplication`) out of the
// installer process - which may be running in the root bootstrap context - and
// put them in a process running in the user bootstrap context. These processes
// can then communicate using the file system as a message bus.
//
// Frustratingly although LaunchServices is daemon and root safe since 10.5, the
// LaunchServices API for querying the running applications isn't public
// (internally `NSRunningApplication` uses this LaunchServices private API,
// though of course this is subject to change and `NSRunningApplication` could
// also do other non-root safe things).
//
// When a user application running Squirrel wants to install an update, Squirrel
// submits two launchd jobs, one which will wait for application termination and
// write an empty file to the given location when the criteria are met, and the
// other which will wait for that file to appear and then perform the install as
// before. The "wait for termination" job is always submitted to the user
// domain, but the installer job is submitted to a domain based on whether the
// install location is writable by the current user.
//
// To perform the relaunch when the install is complete, it is safe to use the
// LaunchServices API from the installer process, the app will be launched in
// the user's GUI session.
//
// From <https://developer.apple.com/library/mac/technotes/tn2083/>
//
// > If the EUID of the calling process is zero, the application is launched in
// > the context of the currently active GUI login session. If there is no
// > currently active GUI login session (no one is logged in, or a logged in
// > user has fast user switched to the login window), the behavior is
// > unspecified (r. 5321293).
@interface SQRLShipItConnection : NSObject
// Returns the label for the ShipIt launchd job.
+ (NSString *)shipItInstallerJobLabel;
// Designated initialiser.
//
// privileged - Determines which launchd domain to launch the job in.
// If YES, ShipIt is launched in the root domain, otherwise it is
// launched in the current user’s domain.
//
// Returns an initialised connection which can be used to start an install.
- (instancetype)initWithRootPrivileges:(BOOL)rootPrivileges;
// Submits the request to the process network which will wait for termination
// and then install.
//
// After sending the request, the update will be attempted when the sending
// process terminates.
//
// request - The install parameters, target bundle, update bundle, whether to
// launch when install is complete etc. Must not be nil.
//
// Returns a signal which will complete, or error, on a background scheduler.
- (RACSignal *)sendRequest:(SQRLShipItRequest *)request;
@end
+203
View File
@@ -0,0 +1,203 @@
//
// SQRLShipItConnection.m
// Squirrel
//
// Created by Justin Spahr-Summers on 2013-08-12.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import "SQRLShipItConnection.h"
#import "EXTScope.h"
#import "SQRLDirectoryManager.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import <Security/Security.h>
#import <ServiceManagement/ServiceManagement.h>
#import <launch.h>
#import "SQRLAuthorization.h"
#import "SQRLShipItRequest.h"
NSString * const SQRLShipItConnectionErrorDomain = @"SQRLShipItConnectionErrorDomain";
const NSInteger SQRLShipItConnectionErrorCouldNotStartService = 1;
@interface SQRLShipItConnection ()
@property (readonly, nonatomic, assign) BOOL privileged;
@end
@implementation SQRLShipItConnection
+ (NSString *)shipItInstallerJobLabel {
NSString *currentAppIdentifier = NSBundle.mainBundle.bundleIdentifier ?: [NSString stringWithFormat:@"%@:%d", NSProcessInfo.processInfo.processName, NSProcessInfo.processInfo.processIdentifier];
return [currentAppIdentifier stringByAppendingString:@".ShipIt"];
}
+ (NSMutableDictionary *)jobDictionaryWithLabel:(NSString *)jobLabel executableName:(NSString *)executableName arguments:(NSArray *)arguments {
NSParameterAssert(jobLabel != nil);
NSParameterAssert(executableName != nil);
NSParameterAssert(arguments != nil);
NSMutableDictionary *jobDict = [NSMutableDictionary dictionary];
jobDict[@(LAUNCH_JOBKEY_LABEL)] = jobLabel;
jobDict[@(LAUNCH_JOBKEY_NICE)] = @(-1);
jobDict[@(LAUNCH_JOBKEY_ENABLETRANSACTIONS)] = @NO;
jobDict[@(LAUNCH_JOBKEY_THROTTLEINTERVAL)] = @2;
NSBundle *squirrelBundle = [NSBundle bundleForClass:self.class];
NSAssert(squirrelBundle != nil, @"Could not open Squirrel.framework bundle");
NSMutableArray *fullArguments = [NSMutableArray arrayWithObject:[squirrelBundle pathForResource:executableName ofType:nil]];
[fullArguments addObjectsFromArray:arguments];
jobDict[@(LAUNCH_JOBKEY_PROGRAMARGUMENTS)] = fullArguments;
return jobDict;
}
+ (RACSignal *)shipItInstallerJobDictionary {
NSString *jobLabel = self.shipItInstallerJobLabel;
return [[[RACSignal
defer:^{
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:jobLabel];
return [directoryManager applicationSupportURL];
}]
map:^(NSURL *appSupportURL) {
NSMutableArray *arguments = [[NSMutableArray alloc] init];
// Pass in the service name so ShipIt knows how to broadcast itself.
[arguments addObject:jobLabel];
NSMutableDictionary *jobDict = [self jobDictionaryWithLabel:jobLabel executableName:@"shipit-installer" arguments:arguments];
jobDict[@(LAUNCH_JOBKEY_KEEPALIVE)] = @{
@(LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT): @NO
};
jobDict[@(LAUNCH_JOBKEY_STANDARDOUTPATH)] = [appSupportURL URLByAppendingPathComponent:@"ShipIt_stdout.log"].path;
jobDict[@(LAUNCH_JOBKEY_STANDARDERRORPATH)] = [appSupportURL URLByAppendingPathComponent:@"ShipIt_stderr.log"].path;
return jobDict;
}]
setNameWithFormat:@"+shipItInstallerJobDictionary"];
}
+ (RACSignal *)shipItAuthorization {
return [[RACSignal
createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
AuthorizationItem rightItems[] = {
{
.name = kSMRightModifySystemDaemons,
},
};
AuthorizationRights rights = {
.count = sizeof(rightItems) / sizeof(*rightItems),
.items = rightItems,
};
NSString *prompt = NSLocalizedString(@"An update is ready to install.", @"SQRLShipItConnection, launch shipit, authorization prompt");
NSString *iconName = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleIconFile"];
NSString *iconPath = (iconName == nil ? nil : [NSBundle.mainBundle pathForImageResource:iconName]);
AuthorizationItem environmentItems[] = {
{
.name = kAuthorizationEnvironmentPrompt,
.valueLength = strlen(prompt.UTF8String),
.value = (void *)prompt.UTF8String,
},
{
.name = kAuthorizationEnvironmentIcon,
.valueLength = (iconPath == nil ? 0 : strlen(iconPath.UTF8String)),
.value = (void *)iconPath.UTF8String,
},
};
AuthorizationEnvironment environment = {
.count = sizeof(environmentItems) / sizeof(*environmentItems),
.items = environmentItems,
};
AuthorizationRef authorization = NULL;
OSStatus authorizationError = AuthorizationCreate(&rights, &environment, kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, &authorization);
if (authorizationError == noErr) {
[subscriber sendNext:[[SQRLAuthorization alloc] initWithAuthorization:authorization]];
[subscriber sendCompleted];
} else {
[subscriber sendError:[NSError errorWithDomain:NSOSStatusErrorDomain code:authorizationError userInfo:nil]];
}
return nil;
}]
setNameWithFormat:@"+shipItAuthorization"];
}
- (instancetype)initWithRootPrivileges:(BOOL)rootPrivileges {
self = [self init];
if (self == nil) return nil;
_privileged = rootPrivileges;
return self;
}
- (RACSignal *)sendRequest:(SQRLShipItRequest *)request {
NSParameterAssert(request != nil);
return [[[self
submitInstallerJobForRequestIfNeeded:request]
concat:[RACSignal defer:^{
}]]
setNameWithFormat:@"%@ -sendRequest: %@", self, request];
}
- (RACSignal *)submitInstallerJobForRequestIfNeeded:(SQRLShipItRequest *)request {
// TODO implement lazy submission when the job is already loaded in launchd
CFStringRef domain = NULL; RACSignal *authorization;
if (self.privileged) {
domain = kSMDomainSystemLaunchd;
authorization = self.class.shipItAuthorization;
} else {
domain = kSMDomainUserLaunchd;
authorization = [RACSignal return:nil];
}
return [[[RACSignal
zip:@[
self.class.shipItInstallerJobDictionary,
authorization,
] reduce:^(NSDictionary *job, SQRLAuthorization *authorization) {
return [self submitJob:job domain:(__bridge id)domain authorization:authorization];
}]
flatten]
setNameWithFormat:@"%@ -submitInstallerJobForRequestIfNeeded: %@", self, request];
}
- (RACSignal *)submitJob:(NSDictionary *)job domain:(NSString *)domain authorization:(SQRLAuthorization *)authorizationValue {
return [[RACSignal
defer:^{
NSString *jobLabel = job[@(LAUNCH_JOBKEY_LABEL)];
AuthorizationRef authorization = authorizationValue.authorization;
CFErrorRef cfError;
if (!SMJobRemove((__bridge CFStringRef)domain, (__bridge CFStringRef)jobLabel, authorization, true, &cfError)) {
NSError *error = CFBridgingRelease(cfError);
cfError = NULL;
if (![error.domain isEqual:(__bridge id)kSMErrorDomainLaunchd] || error.code != kSMErrorJobNotFound) {
NSLog(@"Could not remove previous ShipIt job %@: %@", jobLabel, error);
}
}
if (!SMJobSubmit((__bridge CFStringRef)domain, (__bridge CFDictionaryRef)job, authorization, &cfError)) {
return [RACSignal error:CFBridgingRelease(cfError)];
}
return [RACSignal empty];
}]
setNameWithFormat:@"%@ -submitJob: %@ domain: %@ authorization: %@", self, job, domain, authorizationValue];
}
@end
-34
View File
@@ -1,34 +0,0 @@
//
// SQRLShipItLauncher.h
// Squirrel
//
// Created by Justin Spahr-Summers on 2013-08-12.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import <Foundation/Foundation.h>
@class RACSignal;
// The domain for errors originating within SQRLShipItLauncher.
extern NSString * const SQRLShipItLauncherErrorDomain;
// The ShipIt service could not be started.
extern const NSInteger SQRLShipItLauncherErrorCouldNotStartService;
// Responsible for launching the ShipIt service to actually install an update.
@interface SQRLShipItLauncher : NSObject
// Returns the label for the ShipIt launchd job.
+ (NSString *)shipItJobLabel;
// Attempts to launch ShipIt.
//
// privileged - Determines which launchd domain to launch the job in.
// If YES, ShipIt is launched in the root domain, otherwise it is
// launched in the current user’s domain.
//
// Returns a signal which will complete, or error, on a background scheduler.
+ (RACSignal *)launchPrivileged:(BOOL)privileged;
@end
-155
View File
@@ -1,155 +0,0 @@
//
// SQRLShipItLauncher.m
// Squirrel
//
// Created by Justin Spahr-Summers on 2013-08-12.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import "SQRLShipItLauncher.h"
#import "EXTScope.h"
#import "SQRLDirectoryManager.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import <Security/Security.h>
#import <ServiceManagement/ServiceManagement.h>
#import <launch.h>
#import "SQRLAuthorization.h"
NSString * const SQRLShipItLauncherErrorDomain = @"SQRLShipItLauncherErrorDomain";
const NSInteger SQRLShipItLauncherErrorCouldNotStartService = 1;
@implementation SQRLShipItLauncher
+ (NSString *)shipItJobLabel {
NSString *currentAppIdentifier = NSBundle.mainBundle.bundleIdentifier ?: [NSString stringWithFormat:@"%@:%d", NSProcessInfo.processInfo.processName, NSProcessInfo.processInfo.processIdentifier];
return [currentAppIdentifier stringByAppendingString:@".ShipIt"];
}
+ (RACSignal *)shipItJobDictionary {
NSString *jobLabel = self.shipItJobLabel;
return [[[RACSignal
defer:^{
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:jobLabel];
return [directoryManager applicationSupportURL];
}]
map:^(NSURL *appSupportURL) {
NSBundle *squirrelBundle = [NSBundle bundleForClass:self.class];
NSAssert(squirrelBundle != nil, @"Could not open Squirrel.framework bundle");
NSMutableDictionary *jobDict = [NSMutableDictionary dictionary];
jobDict[@(LAUNCH_JOBKEY_LABEL)] = jobLabel;
jobDict[@(LAUNCH_JOBKEY_NICE)] = @(-1);
jobDict[@(LAUNCH_JOBKEY_ENABLETRANSACTIONS)] = @NO;
jobDict[@(LAUNCH_JOBKEY_THROTTLEINTERVAL)] = @2;
jobDict[@(LAUNCH_JOBKEY_KEEPALIVE)] = @{
@(LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT): @NO
};
jobDict[@(LAUNCH_JOBKEY_MACHSERVICES)] = @{
jobLabel: @YES
};
NSMutableArray *arguments = [[NSMutableArray alloc] init];
[arguments addObject:[squirrelBundle URLForResource:@"ShipIt" withExtension:nil].path];
// Pass in the service name so ShipIt knows how to broadcast itself.
[arguments addObject:jobLabel];
jobDict[@(LAUNCH_JOBKEY_PROGRAMARGUMENTS)] = arguments;
jobDict[@(LAUNCH_JOBKEY_STANDARDOUTPATH)] = [appSupportURL URLByAppendingPathComponent:@"ShipIt_stdout.log"].path;
jobDict[@(LAUNCH_JOBKEY_STANDARDERRORPATH)] = [appSupportURL URLByAppendingPathComponent:@"ShipIt_stderr.log"].path;
#if DEBUG
jobDict[@(LAUNCH_JOBKEY_DEBUG)] = @YES;
#endif
return jobDict;
}]
setNameWithFormat:@"+shipItJobDictionary"];
}
+ (RACSignal *)shipItAuthorization {
return [[RACSignal
createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
AuthorizationItem rightItems[] = {
{
.name = kSMRightModifySystemDaemons,
},
};
AuthorizationRights rights = {
.count = sizeof(rightItems) / sizeof(*rightItems),
.items = rightItems,
};
NSString *prompt = NSLocalizedString(@"An update is ready to install.", @"SQRLShipItLauncher, launch shipit, authorization prompt");
NSString *iconName = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleIconFile"];
NSString *iconPath = (iconName == nil ? nil : [NSBundle.mainBundle.resourceURL URLByAppendingPathComponent:iconName].path);
AuthorizationItem environmentItems[] = {
{
.name = kAuthorizationEnvironmentPrompt,
.valueLength = strlen(prompt.UTF8String),
.value = (void *)prompt.UTF8String,
},
{
.name = kAuthorizationEnvironmentIcon,
.valueLength = iconPath == nil ? 0 : strlen(iconPath.UTF8String),
.value = (void *)iconPath.UTF8String,
},
};
AuthorizationEnvironment environment = {
.count = sizeof(environmentItems) / sizeof(*environmentItems),
.items = environmentItems,
};
AuthorizationRef authorization = NULL;
OSStatus authorizationError = AuthorizationCreate(&rights, &environment, kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, &authorization);
if (authorizationError == noErr) {
[subscriber sendNext:[[SQRLAuthorization alloc] initWithAuthorization:authorization]];
[subscriber sendCompleted];
} else {
[subscriber sendError:[NSError errorWithDomain:NSOSStatusErrorDomain code:authorizationError userInfo:nil]];
}
return nil;
}]
setNameWithFormat:@"+shipItAuthorization"];
}
+ (RACSignal *)launchPrivileged:(BOOL)privileged {
return [[[RACSignal
zip:@[
self.shipItJobDictionary,
(privileged ? self.shipItAuthorization : [RACSignal return:nil])
] reduce:^(NSDictionary *jobDictionary, SQRLAuthorization *authorizationValue) {
CFStringRef domain = (privileged ? kSMDomainSystemLaunchd : kSMDomainUserLaunchd);
AuthorizationRef authorization = authorizationValue.authorization;
CFErrorRef cfError;
if (!SMJobRemove(domain, (__bridge CFStringRef)self.shipItJobLabel, authorization, true, &cfError)) {
NSError *error = CFBridgingRelease(cfError);
cfError = NULL;
if (![error.domain isEqual:(__bridge id)kSMErrorDomainLaunchd] || error.code != kSMErrorJobNotFound) {
NSLog(@"Could not remove previous ShipIt job: %@", error);
}
}
if (!SMJobSubmit(domain, (__bridge CFDictionaryRef)jobDictionary, authorization, &cfError)) {
return [RACSignal error:CFBridgingRelease(cfError)];
}
return [RACSignal empty];
}]
flatten]
setNameWithFormat:@"+launchPrivileged: %i", (int)privileged];
}
@end
+21 -11
View File
@@ -42,24 +42,21 @@ extern NSString * const SQRLShipItRequestPropertyErrorKey;
// single update request from the client's perspective.
@interface SQRLShipItRequest : MTLModel
// Reads a `SQRLShipItState` from disk, at the location specified by the given
// URL signal.
// Reads a `SQRLShipItState` from disk, at the location specified by the URL.
//
// URLSignal - Determines the file location to read from, the signal should send
// an `NSURL` object then complete, or error. This must not be nil.
// URL - The file location to read from. This must not be nil.
//
// Returns a signal which will synchronously send a `SQRLShipItRequest` then
// complete, or error.
+ (RACSignal *)readUsingURL:(RACSignal *)URLSignal;
+ (RACSignal *)readFromURL:(NSURL *)URL;
// Writes the receiver to disk, at the location specified by the given URL
// signal.
// Reads a `SQRLShipItState` from encoded data.
//
// URLSignal - Determines the file location to write to. The signal should send
// an `NSURL` object then complete, or error. This must not be nil.
// data - Serialised request from `serialization`.
//
// Returns a signal which will synchronously complete or error.
- (RACSignal *)writeUsingURL:(RACSignal *)URLSignal;
// Returns a signal which decodes the serialisation and sends a
// `SQRLShipItRequest` then completes, or errors.
+ (RACSignal *)readFromData:(NSData *)data;
// Designated initialiser.
//
@@ -93,4 +90,17 @@ extern NSString * const SQRLShipItRequestPropertyErrorKey;
// Whether to launch the application after an update is successfully installed.
@property (nonatomic, assign, readonly) BOOL launchAfterInstallation;
// Writes the receiver's serialization to disk, at the location specified by the
// URL.
//
// URL - The file location to write to. This must not be nil.
//
// Returns a signal which will synchronously complete or error.
- (RACSignal *)writeToURL:(NSURL *)URL;
// Encode the receiver for saving to disk or sending over IPC.
//
// Returns a signal which sends a `NSData` then completes, or errors.
- (RACSignal *)serialization;
@end
+9 -12
View File
@@ -69,13 +69,13 @@ NSString * const SQRLShipItRequestPropertyErrorKey = @"SQRLShipItRequestProperty
return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}
+ (RACSignal *)readUsingURL:(RACSignal *)URL {
+ (RACSignal *)readFromURL:(NSURL *)URL {
NSParameterAssert(URL != nil);
return [[[[URL
flattenMap:^(NSURL *stateURL) {
return [[[[RACSignal
defer:^{
NSError *error;
NSData *data = [self readFromURL:stateURL error:&error];
NSData *data = [self readFromURL:URL error:&error];
if (data == nil) {
return [RACSignal error:error];
}
@@ -130,23 +130,20 @@ NSString * const SQRLShipItRequestPropertyErrorKey = @"SQRLShipItRequestProperty
setNameWithFormat:@"+readFromData: <NSData %p>", data];
}
- (RACSignal *)writeUsingURL:(RACSignal *)URL {
- (RACSignal *)writeToURL:(NSURL *)URL {
NSParameterAssert(URL != nil);
return [[[[RACSignal
zip:@[
URL,
[self serialization]
] reduce:^(NSURL *stateURL, NSData *data) {
return [[[[self
serialization]
flattenMap:^(NSData *data) {
NSError *error;
BOOL write = [self writeData:data toURL:stateURL error:&error];
BOOL write = [self writeData:data toURL:URL error:&error];
if (!write) {
return [RACSignal error:error];
}
return [RACSignal empty];
}]
flatten]
catch:^(NSError *error) {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Could not write update request", nil),
-32
View File
@@ -1,32 +0,0 @@
//
// SQRLTerminationListener.h
// Squirrel
//
// Created by Justin Spahr-Summers on 2013-10-04.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import <Foundation/Foundation.h>
@class RACSignal;
// Waits for the termination of a GUI application.
@interface SQRLTerminationListener : NSObject
// Initializes the receiver to wait for termination of the app at the given
// location.
//
// bundleURL - The URL to the application bundle to watch. This must not be nil.
// bundleID - The identifier of the application bundle to watch. This must not
// be nil.
- (id)initWithURL:(NSURL *)bundleURL bundleIdentifier:(NSString *)bundleID;
// Lazily waits for termination of all instances of the application identified
// at initialization.
//
// Returns a signal which send an `NSRunningApplication` for each instance of the
// application that is being watched (before the instance terminates), then
// completes on a background scheduler.
- (RACSignal *)waitForTermination;
@end
-89
View File
@@ -1,89 +0,0 @@
//
// SQRLTerminationListener.m
// Squirrel
//
// Created by Justin Spahr-Summers on 2013-10-04.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import "SQRLTerminationListener.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@interface SQRLTerminationListener ()
@property (nonatomic, copy, readonly) NSURL *bundleURL;
@property (nonatomic, copy, readonly) NSString *bundleIdentifier;
// Waits for the process identified by the given PID to terminate.
//
// Returns a signal which sends `processIdentifier` as soon as the process is
// being monitored, then completes once it exits, all on a background thread.
- (RACSignal *)waitForTerminationOfProcessIdentifier:(pid_t)processIdentifier;
@end
@implementation SQRLTerminationListener
#pragma mark Lifecycle
- (id)initWithURL:(NSURL *)bundleURL bundleIdentifier:(NSString *)bundleID {
NSParameterAssert(bundleURL != nil);
NSParameterAssert(bundleID != nil);
self = [super init];
if (self == nil) return nil;
_bundleURL = bundleURL.URLByStandardizingPath;
_bundleIdentifier = [bundleID copy];
return self;
}
#pragma mark Termination Listening
- (RACSignal *)waitForTermination {
return [[[[RACSignal
defer:^{
NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:self.bundleIdentifier];
return [apps.rac_sequence signalWithScheduler:RACScheduler.immediateScheduler];
}]
filter:^(NSRunningApplication *application) {
return [application.bundleURL.URLByStandardizingPath isEqual:self.bundleURL];
}]
flattenMap:^(NSRunningApplication *application) {
return [[self
waitForTerminationOfProcessIdentifier:application.processIdentifier]
mapReplace:application];
}]
setNameWithFormat:@"%@ -waitForTermination", self];
}
- (RACSignal *)waitForTerminationOfProcessIdentifier:(pid_t)processIdentifier {
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, processIdentifier, DISPATCH_PROC_EXIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
dispatch_source_set_registration_handler(source, ^{
[subscriber sendNext:@(processIdentifier)];
});
dispatch_source_set_event_handler(source, ^{
[subscriber sendCompleted];
});
dispatch_resume(source);
return [RACDisposable disposableWithBlock:^{
dispatch_source_cancel(source);
dispatch_release(source);
}];
}]
setNameWithFormat:@"%@ -waitForTerminationOfProcessIdentifier: %i", self, (int)processIdentifier];
}
#pragma mark NSObject
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p>{ bundleURL: %@, bundleIdentifier: %@ }", self.class, self, self.bundleURL, self.bundleIdentifier];
}
@end
+37 -36
View File
@@ -14,7 +14,8 @@
#import "SQRLCodeSignature.h"
#import "SQRLDirectoryManager.h"
#import "SQRLDownloadedUpdate.h"
#import "SQRLShipItLauncher.h"
#import "SQRLShipItConnection.h"
#import "SQRLShipItRequest.h"
#import "SQRLUpdate.h"
#import "SQRLZipArchiver.h"
#import "SQRLShipItRequest.h"
@@ -44,10 +45,8 @@ static NSString * const SQRLUpdaterUniqueTemporaryDirectoryPrefix = @"update.";
// sending them to ShipIt.
@property (nonatomic, strong, readonly) SQRLCodeSignature *signature;
// Lazily launches ShipIt upon first subscription.
//
// Sends completed or error.
@property (nonatomic, strong, readonly) RACSignal *shipItLauncher;
// When executed with an `SQRLShipItState`, launches ShipIt.
@property (nonatomic, strong, readonly) RACCommand *shipItLauncher;
// Lazily removes outdated temporary directories (used for previous updates)
// upon first subscription.
@@ -171,7 +170,7 @@ static NSString * const SQRLUpdaterUniqueTemporaryDirectoryPrefix = @"update.";
_prunedUpdateDirectories = [[[[RACSignal
defer:^{
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItConnection.shipItJobLabel];
return [directoryManager applicationSupportURL];
}]
flattenMap:^(NSURL *appSupportURL) {
@@ -257,20 +256,19 @@ static NSString * const SQRLUpdaterUniqueTemporaryDirectoryPrefix = @"update.";
deliverOn:RACScheduler.mainThreadScheduler];
}];
_shipItLauncher = [[[RACSignal
defer:^{
NSURL *targetURL = NSRunningApplication.currentApplication.bundleURL;
_shipItLauncher = [[RACCommand alloc] initWithSignalBlock:^(SQRLShipItRequest *request) {
NSURL *targetURL = request.targetBundleURL;
NSNumber *targetWritable = nil;
NSError *targetWritableError = nil;
BOOL gotWritable = [targetURL getResourceValue:&targetWritable forKey:NSURLIsWritableKey error:&targetWritableError];
NSNumber *targetWritable = nil;
NSError *targetWritableError = nil;
BOOL gotWritable = [targetURL getResourceValue:&targetWritable forKey:NSURLIsWritableKey error:&targetWritableError];
// If we can't determine whether it can be written, assume nonprivileged and
// wait for another, more canonical error.
return [SQRLShipItLauncher launchPrivileged:(gotWritable && !targetWritable.boolValue)];
}]
replayLazily]
setNameWithFormat:@"shipItLauncher"];
// If we can't determine whether it can be written, assume
// nonprivileged and wait for another, more canonical error.
SQRLShipItConnection *connection = [[SQRLShipItConnection alloc] initWithRootPrivileges:(gotWritable && !targetWritable.boolValue)];
return [connection sendRequest:request];
}];
return self;
}
@@ -420,7 +418,7 @@ static NSString * const SQRLUpdaterUniqueTemporaryDirectoryPrefix = @"update.";
- (RACSignal *)uniqueTemporaryDirectoryForUpdate {
return [[[RACSignal
defer:^{
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItConnection.shipItJobLabel];
return [directoryManager applicationSupportURL];
}]
flattenMap:^(NSURL *appSupportURL) {
@@ -494,12 +492,8 @@ static NSString * const SQRLUpdaterUniqueTemporaryDirectoryPrefix = @"update.";
}
- (RACSignal *)shipItStateURL {
return [[RACSignal
defer:^{
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
return directoryManager.shipItStateURL;
}]
setNameWithFormat:@"%@ -shipItStateURL", self];
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItConnection.shipItJobLabel];
return directoryManager.shipItStateURL;
}
#pragma mark Installing Updates
@@ -529,24 +523,31 @@ static NSString * const SQRLUpdaterUniqueTemporaryDirectoryPrefix = @"update.";
defer:^{
NSRunningApplication *currentApplication = NSRunningApplication.currentApplication;
SQRLShipItRequest *request = [[SQRLShipItRequest alloc] initWithUpdateBundleURL:update.bundle.bundleURL targetBundleURL:currentApplication.bundleURL bundleIdentifier:currentApplication.bundleIdentifier launchAfterInstallation:NO];
return [request writeUsingURL:self.shipItStateURL];
return [[self.shipItStateURL
flattenMap:^(NSURL *location) {
return [request writeToURL:location];
}]
concat:[RACSignal return:request]];
}]
then:^{
return self.shipItLauncher;
flattenMap:^(SQRLShipItRequest *request) {
return [self.shipItLauncher execute:request];
}]
sqrl_addTransactionWithName:NSLocalizedString(@"Preparing update", nil) description:NSLocalizedString(@"An update for %@ is being prepared. Interrupting the process could corrupt the application.", nil), NSRunningApplication.currentApplication.bundleIdentifier]
setNameWithFormat:@"%@ -prepareUpdateForInstallation: %@", self, update];
}
- (RACSignal *)relaunchToInstallUpdate {
return [[[[[[[[SQRLShipItRequest
readUsingURL:self.shipItStateURL]
map:^(SQRLShipItRequest *request) {
return [[SQRLShipItRequest alloc] initWithUpdateBundleURL:request.updateBundleURL targetBundleURL:request.targetBundleURL bundleIdentifier:request.bundleIdentifier launchAfterInstallation:YES];
}]
flattenMap:^(SQRLShipItRequest *request) {
return [[request
writeUsingURL:self.shipItStateURL]
return [[[[[[self.shipItStateURL
flattenMap:^(NSURL *location) {
return [[[[SQRLShipItRequest
readFromURL:location]
map:^(SQRLShipItRequest *request) {
return [[SQRLShipItRequest alloc] initWithUpdateBundleURL:request.updateBundleURL targetBundleURL:request.targetBundleURL bundleIdentifier:request.bundleIdentifier launchAfterInstallation:YES];
}]
flattenMap:^(SQRLShipItRequest *request) {
return [request writeToURL:location];
}]
sqrl_addTransactionWithName:NSLocalizedString(@"Preparing to relaunch", nil) description:NSLocalizedString(@"%@ is preparing to relaunch to install an update. Interrupting the process could corrupt the application.", nil), NSRunningApplication.currentApplication.bundleIdentifier];
}]
deliverOn:RACScheduler.mainThreadScheduler]
-160
View File
@@ -1,160 +0,0 @@
//
// main.m
// shipit
//
// Created by Alan Rogers on 29/07/2013.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <ReactiveCocoa/EXTScope.h>
#import <ReactiveCocoa/ReactiveCocoa.h>
#import "NSError+SQRLVerbosityExtensions.h"
#import "RACSignal+SQRLTransactionExtensions.h"
#import "SQRLDirectoryManager.h"
#import "SQRLInstaller.h"
#import "SQRLInstaller+Private.h"
#import "SQRLTerminationListener.h"
#import "SQRLShipItRequest.h"
// The maximum number of times ShipIt should run the same installation state, in
// an attempt to update.
//
// If ShipIt is launched in the same state more than this number of times,
// updating will abort.
static const NSUInteger SQRLShipItMaximumInstallationAttempts = 3;
// The domain for errors generated here.
static NSString * const SQRLShipItErrorDomain = @"SQRLShipItErrorDomain";
static NSUInteger installationAttempts(NSString *applicationIdentifier) {
return CFPreferencesGetAppIntegerValue((__bridge CFStringRef)SQRLShipItInstallationAttemptsKey, (__bridge CFStringRef)applicationIdentifier, NULL);
}
static BOOL setInstallationAttempts(NSString *applicationIdentifier, NSUInteger attempts) {
CFPreferencesSetValue((__bridge CFStringRef)SQRLShipItInstallationAttemptsKey, (__bridge CFPropertyListRef)@(attempts), (__bridge CFStringRef)applicationIdentifier, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
return CFPreferencesSynchronize((__bridge CFStringRef)applicationIdentifier, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
}
static BOOL clearInstallationAttempts(NSString *applicationIdentifier) {
CFPreferencesSetValue((__bridge CFStringRef)SQRLShipItInstallationAttemptsKey, NULL, (__bridge CFStringRef)applicationIdentifier, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
return CFPreferencesSynchronize((__bridge CFStringRef)applicationIdentifier, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
}
// Waits for all instances of the target application (as described in the
// `request`) to exit, then sends completed.
static RACSignal *waitForTerminationIfNecessary(SQRLShipItRequest *request) {
return [[RACSignal
defer:^{
if (request.bundleIdentifier == nil) return [RACSignal empty];
SQRLTerminationListener *listener = [[SQRLTerminationListener alloc] initWithURL:request.targetBundleURL bundleIdentifier:request.bundleIdentifier];
return [listener waitForTermination];
}]
setNameWithFormat:@"waitForTerminationIfNecessary"];
}
static void installRequest(RACSignal *readRequestSignal, SQRLDirectoryManager *directoryManager) {
NSString *applicationIdentifier = directoryManager.applicationIdentifier;
[[[[[readRequestSignal
flattenMap:^(SQRLShipItRequest *request) {
return waitForTerminationIfNecessary(request);
}]
ignoreValues]
concat:readRequestSignal]
flattenMap:^(SQRLShipItRequest *request) {
SQRLInstaller *installer = [[SQRLInstaller alloc] initWithApplicationIdentifier:applicationIdentifier];
NSUInteger attempt = installationAttempts(applicationIdentifier) + 1;
setInstallationAttempts(applicationIdentifier, attempt);
RACSignal *action;
if (attempt > SQRLShipItMaximumInstallationAttempts) {
action = [[[installer.abortInstallationCommand
execute:request]
initially:^{
NSLog(@"Too many attempts to install, aborting update");
}]
catch:^(NSError *error) {
NSLog(@"Error aborting installation: %@", error);
// Exit successfully so launchd doesn't restart us
// again.
return [RACSignal empty];
}];
} else {
action = [[[[installer.installUpdateCommand
execute:request]
initially:^{
BOOL freshInstall = (attempt == 1);
if (freshInstall) {
NSLog(@"Beginning installation");
} else {
NSLog(@"Resuming installation attempt %i", (int)attempt);
}
}]
doCompleted:^{
NSLog(@"Installation completed successfully");
}]
sqrl_addTransactionWithName:NSLocalizedString(@"Updating", nil) description:NSLocalizedString(@"%@ is being updated, and interrupting the process could corrupt the application", nil), request.targetBundleURL.path];
}
// Clear the installation attempts for a successful abort or
// install.
action = [action doCompleted:^{
clearInstallationAttempts(applicationIdentifier);
}];
if (request.launchAfterInstallation) {
// Launch regardless of whether installation succeeds or fails.
action = [[action
deliverOn:RACScheduler.mainThreadScheduler]
finally:^{
NSURL *bundleURL = request.targetBundleURL;
if (bundleURL == nil) {
NSLog(@"Missing target bundle URL, cannot launch application");
return;
}
NSError *error;
if (![NSWorkspace.sharedWorkspace launchApplicationAtURL:bundleURL options:NSWorkspaceLaunchDefault configuration:nil error:&error]) {
NSLog(@"Could not launch application at %@: %@", bundleURL, error);
return;
}
NSLog(@"Application launched at %@", bundleURL);
}];
}
return action;
}]
subscribeError:^(NSError *error) {
NSLog(@"Installation error: %@", error);
exit(EXIT_FAILURE);
} completed:^{
exit(EXIT_SUCCESS);
}];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
atexit_b(^{
NSLog(@"ShipIt quitting");
});
if (argc < 2) {
NSLog(@"Missing launchd job label for ShipIt");
return EXIT_FAILURE;
}
char const *jobLabel = argv[1];
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:@(jobLabel)];
installRequest([SQRLShipItRequest readUsingURL:directoryManager.shipItStateURL], directoryManager);
dispatch_main();
}
return EXIT_SUCCESS;
}
+170
View File
@@ -0,0 +1,170 @@
//
// main.m
// shipit
//
// Created by Alan Rogers on 29/07/2013.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <ReactiveCocoa/EXTScope.h>
#import <ReactiveCocoa/ReactiveCocoa.h>
#import "NSError+SQRLVerbosityExtensions.h"
#import "RACSignal+SQRLTransactionExtensions.h"
#import "SQRLDirectoryManager.h"
#import "SQRLInstaller.h"
#import "SQRLInstaller+Private.h"
#import "SQRLShipItRequest.h"
// The domain for errors generated here.
static NSString * const SQRLShipItErrorDomain = @"SQRLShipItErrorDomain";
typedef NS_ENUM(NSInteger, SQRLShipItError) {
SQRLShipItErrorUnknownAction,
SQRLShipItErrorMissingRequestData,
};
static void reply(xpc_object_t response) {
xpc_connection_t connection = xpc_dictionary_get_remote_connection(response);
xpc_connection_send_message(connection, response);
}
static void replyWithError(xpc_object_t response, NSError *error) {
xpc_dictionary_set_bool(response, "success", false);
if (error != nil) {
xpc_object_t errorDictionary = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(errorDictionary, "domain", error.domain.UTF8String);
xpc_dictionary_set_int64(errorDictionary, "code", error.code);
xpc_dictionary_set_string(errorDictionary, "description", error.localizedDescription.UTF8String);
xpc_dictionary_set_string(errorDictionary, "failureReason", error.localizedFailureReason.UTF8String);
xpc_dictionary_set_string(errorDictionary, "recoverySuggestion", error.localizedRecoverySuggestion.UTF8String);
xpc_dictionary_set_value(response, "error", errorDictionary);
xpc_release(errorDictionary);
}
reply(response);
}
static void replyWithSuccess(xpc_object_t response) {
xpc_dictionary_set_bool(response, "success", true);
reply(response);
}
// Client requests from peer connections.
//
// applicationIdentifier - Current process reverse DNS identifier.
// request - XPC dictionary request object.
//
// Returns nothing.
static void handleRequest(NSString *applicationIdentifier, xpc_object_t request) {
xpc_object_t response = xpc_dictionary_create_reply(request);
SQRLInstaller *installer = [[SQRLInstaller alloc] initWithApplicationIdentifier:applicationIdentifier];
char const *action = xpc_dictionary_get_string(request, "action");
if (strcmp(action, "install") != 0) {
NSDictionary *errorInfo = @{
NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Cannot process \"%s\" requests", action],
};
replyWithError(response, [NSError errorWithDomain:SQRLShipItErrorDomain code:SQRLShipItErrorUnknownAction userInfo:errorInfo]);
return;
}
size_t length;
void const *requestBytes = xpc_dictionary_get_data(request, "request", &length);
if (requestBytes == NULL) {
NSDictionary *errorInfo = @{
NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Cannot process \"%s\" requests without \"request\" data", action],
};
replyWithError(response, [NSError errorWithDomain:SQRLShipItErrorDomain code:SQRLShipItErrorUnknownAction userInfo:errorInfo]);
return;
}
[[[SQRLShipItRequest
readFromData:[NSData dataWithBytes:requestBytes length:length] ]
flattenMap:^(SQRLShipItRequest *request) {
return [[installer.installUpdateCommand
execute:request]
catch:^(NSError *error) {
return [[[installer.abortInstallationCommand
execute:nil]
doCompleted:^{
NSLog(@"Abort completed successfully");
}]
concat:[RACSignal error:error]];
}];
}]
subscribeError:^(NSError *error){
NSLog(@"Installation failed with error: %@ %@", error, error.userInfo);
replyWithError(response, error);
} completed:^{
NSLog(@"Installation completed successfully");
replyWithSuccess(response);
}];
}
// Peer connections from the listener connection.
//
// applicationIdentifier - Current process reverse DNS identifier.
// newConnection - The newly accepted connection from the listener.
//
// Returns nothing.
static void handleConnection(NSString *applicationIdentifier, xpc_connection_t newConnection) {
xpc_connection_set_event_handler(newConnection, ^(xpc_object_t object) {
xpc_type_t type = xpc_get_type(object);
if (type == XPC_TYPE_ERROR) {
char *description = xpc_copy_description(object);
NSLog(@"XPC peer error: %s", description);
free(description);
return;
}
if (type != XPC_TYPE_DICTIONARY) {
NSLog(@"XPC peer expected dictionary");
return;
}
handleRequest(applicationIdentifier, object);
});
xpc_connection_resume(newConnection);
}
// Listens for XPC messages from clients.
//
// Arguments are expected in the following order:
//
// jobLabel - The launchd job label for this task.
//
// Returns 0 on successful termination, non 0 otherwise.
int main(int argc, const char * argv[]) {
@autoreleasepool {
atexit_b(^{
NSLog(@"ShipIt quitting");
});
if (argc < 2) {
NSLog(@"Missing launchd job label for ShipIt");
return EXIT_FAILURE;
}
char const *jobLabel = argv[1];
NSString *applicationIdentifier = @(jobLabel);
xpc_connection_t server = xpc_connection_create_mach_service(jobLabel, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
xpc_connection_set_event_handler(server, ^(xpc_object_t object) {
xpc_type_t type = xpc_get_type(object);
if (type == XPC_TYPE_ERROR) {
char *description = xpc_copy_description(object);
NSLog(@"XPC listener error: %s", description);
free(description);
} else if (type == XPC_TYPE_CONNECTION) {
handleConnection(applicationIdentifier, object);
}
});
xpc_connection_resume(server);
dispatch_main();
}
return EXIT_SUCCESS;
}
+7 -7
View File
@@ -10,7 +10,7 @@
#import "SQRLDirectoryManager.h"
#import "SQRLInstaller.h"
#import "SQRLInstaller+Private.h"
#import "SQRLShipItLauncher.h"
#import "SQRLShipItConnection.h"
#import "SQRLShipItRequest.h"
#import "SQRLInstallerOwnedBundle.h"
@@ -185,19 +185,19 @@ describe(@"signal handling", ^{
});
it(@"should handle SIGHUP", ^{
system("killall -HUP ShipIt");
system("killall -HUP shipit-installer");
});
it(@"should handle SIGTERM", ^{
system("killall -TERM ShipIt");
system("killall -TERM shipit-installer");
});
it(@"should handle SIGINT", ^{
system("killall -INT ShipIt");
system("killall -INT shipit-installer");
});
it(@"should handle SIGQUIT", ^{
system("killall -QUIT ShipIt");
system("killall -QUIT shipit-installer");
});
it(@"should handle SIGKILL", ^{
@@ -206,8 +206,8 @@ describe(@"signal handling", ^{
//
// Two SIGKILL signals means that it'll be launched three times (which
// matches the maximum number of attempts per state).
for (int i = 0; i < 2; i++) {
system("killall -KILL ShipIt");
for (int i = 0; i < 3; i++) {
system("killall -KILL shipit-installer");
// Wait at least for the launchd throttle interval.
NSTimeInterval delay = 2 + (arc4random_uniform(100) / 1000.0);
+6 -6
View File
@@ -40,20 +40,20 @@ it(@"should copy", ^{
expect(requestCopy).notTo.beIdenticalTo(request);
});
it(@"should fail to read state when no file exists yet", ^{
it(@"should fail to read when no file exists yet", ^{
NSError *error;
BOOL success = [[SQRLShipItRequest readUsingURL:directoryManager.shipItStateURL] waitUntilCompleted:&error];
BOOL success = [[SQRLShipItRequest readFromURL:[directoryManager.shipItStateURL first]] waitUntilCompleted:&error];
expect(success).to.beFalsy();
expect(error).notTo.beNil();
});
it(@"should write and read state to disk", ^{
it(@"should write and read to disk", ^{
NSError *error;
BOOL success = [[request writeUsingURL:directoryManager.shipItStateURL] waitUntilCompleted:&error];
BOOL success = [[request writeToURL:[directoryManager.shipItStateURL first]] waitUntilCompleted:&error];
expect(success).to.beTruthy();
expect(error).to.beNil();
SQRLShipItRequest *readRequest = [[SQRLShipItRequest readUsingURL:directoryManager.shipItStateURL] firstOrDefault:nil success:&success error:&error];
SQRLShipItRequest *readRequest = [[SQRLShipItRequest readFromURL:[directoryManager.shipItStateURL first]] firstOrDefault:nil success:&success error:&error];
expect(success).to.beTruthy();
expect(error).to.beNil();
@@ -69,7 +69,7 @@ it(@"should fail gracefully with archives encoding a different class", ^{
expect(error).to.beNil();
BOOL success = NO;
SQRLShipItRequest *request = [[SQRLShipItRequest readUsingURL:[RACSignal return:archiveLocation]] firstOrDefault:nil success:&success error:&error];
SQRLShipItRequest *request = [[SQRLShipItRequest readFromURL:archiveLocation] firstOrDefault:nil success:&success error:&error];
expect(request).to.beNil();
expect(success).to.beFalsy();
expect(error.domain).to.equal(SQRLShipItRequestErrorDomain);
@@ -1,68 +0,0 @@
//
// SQRLTerminationListenerSpec.m
// Squirrel
//
// Created by Justin Spahr-Summers on 2013-10-07.
// Copyright (c) 2013 GitHub. All rights reserved.
//
#import "SQRLTerminationListener.h"
SpecBegin(SQRLTerminationListener)
__block SQRLTerminationListener *listener;
beforeEach(^{
listener = [[SQRLTerminationListener alloc] initWithURL:self.testApplicationURL bundleIdentifier:self.testApplicationBundle.bundleIdentifier];
expect(listener).notTo.beNil();
});
it(@"should complete immediately when the app is not running", ^{
__block BOOL completed = NO;
[[listener waitForTermination] subscribeCompleted:^{
completed = YES;
}];
expect(completed).to.beTruthy();
});
it(@"should wait until one instance terminates", ^{
NSRunningApplication *app = [self launchTestApplicationWithEnvironment:nil];
__block NSRunningApplication *observedApp = nil;
__block BOOL completed = NO;
[[listener waitForTermination] subscribeNext:^(id x) {
observedApp = x;
} completed:^{
completed = YES;
}];
expect(observedApp).will.equal(app);
expect(completed).to.beFalsy();
[app forceTerminate];
expect(app.terminated).will.beTruthy();
expect(completed).will.beTruthy();
});
it(@"should wait until multiple instances terminate", ^{
NSRunningApplication *app1 = [self launchTestApplicationWithEnvironment:nil];
NSRunningApplication *app2 = [self launchTestApplicationWithEnvironment:nil];
__block BOOL completed = NO;
[[listener waitForTermination] subscribeCompleted:^{
completed = YES;
}];
expect(completed).to.beFalsy();
[app1 forceTerminate];
expect(app1.terminated).will.beTruthy();
expect(completed).to.beFalsy();
[app2 forceTerminate];
expect(app2.terminated).will.beTruthy();
expect(completed).will.beTruthy();
});
SpecEnd
+2
View File
@@ -72,6 +72,8 @@ extern NSString * const SQRLBundleShortVersionStringKey;
- (NSURL *)createTestApplicationUpdate;
// Runs the installer in process or submits ShipIt's launchd job to start it up.
//
// request - The install to send to ShipIt.
- (void)installWithRequest:(SQRLShipItRequest *)request remote:(BOOL)remote;
// Creates a disk image, then mounts it.
+7 -7
View File
@@ -10,8 +10,8 @@
#import "SQRLCodeSignature.h"
#import "SQRLDirectoryManager.h"
#import "SQRLShipItConnection.h"
#import "SQRLInstaller.h"
#import "SQRLShipItLauncher.h"
#import "SQRLShipItRequest.h"
#import "SQRLTestHelper.h"
#import <ServiceManagement/ServiceManagement.h>
@@ -148,7 +148,7 @@ static void SQRLSignalHandler(int sig) {
if (_baseTemporaryDirectoryURL == nil) {
NSURL *globalTemporaryDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory() isDirectory:YES];
_baseTemporaryDirectoryURL = [[globalTemporaryDirectory URLByAppendingPathComponent:@"com.github.SquirrelTests"] URLByAppendingPathComponent:[NSProcessInfo.processInfo globallyUniqueString]];
NSError *error = nil;
BOOL success = [NSFileManager.defaultManager createDirectoryAtURL:_baseTemporaryDirectoryURL withIntermediateDirectories:YES attributes:nil error:&error];
XCTAssertTrue(success, @"Couldn't create temporary directory at %@: %@", _baseTemporaryDirectoryURL, error);
@@ -204,7 +204,7 @@ static void SQRLSignalHandler(int sig) {
NSURL *fixtureURL = self.testApplicationURL;
NSBundle *bundle = [NSBundle bundleWithURL:fixtureURL];
XCTAssertNotNil(bundle, @"Couldn't open bundle at %@", fixtureURL);
return bundle;
}
@@ -296,7 +296,7 @@ static void SQRLSignalHandler(int sig) {
}
- (SQRLDirectoryManager *)shipItDirectoryManager {
NSString *identifier = SQRLShipItLauncher.shipItJobLabel;
NSString *identifier = SQRLShipItConnection.shipItJobLabel;
SQRLDirectoryManager *manager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:identifier];
XCTAssertNotNil(manager, @"Could not create directory manager for %@", identifier);
@@ -305,15 +305,15 @@ static void SQRLSignalHandler(int sig) {
- (void)installWithRequest:(SQRLShipItRequest *)request remote:(BOOL)remote {
if (remote) {
expect([[request writeUsingURL:self.shipItDirectoryManager.shipItStateURL] waitUntilCompleted:NULL]).to.beTruthy();
SQRLShipItConnection *connection = [[SQRLShipItConnection alloc] initWithRootPrivileges:NO];
__block NSError *error = nil;
expect([[SQRLShipItLauncher launchPrivileged:NO] waitUntilCompleted:&error]).to.beTruthy();
expect([[connection sendRequest:request] waitUntilCompleted:&error]).to.beTruthy();
expect(error).to.beNil();
[self addCleanupBlock:^{
// Remove ShipIt's launchd job so it doesn't relaunch itself.
SMJobRemove(kSMDomainUserLaunchd, (__bridge CFStringRef)SQRLShipItLauncher.shipItJobLabel, NULL, true, NULL);
SMJobRemove(kSMDomainUserLaunchd, (__bridge CFStringRef)SQRLShipItConnection.shipItJobLabel, NULL, true, NULL);
NSError *lookupError;
NSURL *stateURL = [[self.shipItDirectoryManager shipItStateURL] firstOrDefault:nil success:NULL error:&lookupError];
+2 -2
View File
@@ -8,7 +8,7 @@
#import "TestAppDelegate.h"
#import "SQRLDirectoryManager.h"
#import "SQRLShipItLauncher.h"
#import "SQRLShipItConnection.h"
#import "SQRLTestUpdate.h"
#import <ReactiveCocoa/EXTScope.h>
#import <ReactiveCocoa/ReactiveCocoa.h>
@@ -35,7 +35,7 @@
NSLog(@"TestApplication quitting");
});
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItConnection.shipItJobLabel];
NSError *error = nil;
BOOL removed = [[[directoryManager