Compare commits
69
Commits
0.1
...
dont-link-appkit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0828897f1b | ||
|
|
ff31a5fcdd | ||
|
|
016cd25fda | ||
|
|
afebefc78a | ||
|
|
3c7916e02e | ||
|
|
8d6063d9a9 | ||
|
|
24c118fa1e | ||
|
|
ff7b9de34f | ||
|
|
5994cf9584 | ||
|
|
518ee666a3 | ||
|
|
73cc6802d3 | ||
|
|
1403a7ad81 | ||
|
|
24d9ef1f86 | ||
|
|
51bd593666 | ||
|
|
44a471b903 | ||
|
|
6e2f4faa3f | ||
|
|
43fd18e4c9 | ||
|
|
d08804ddce | ||
|
|
39a47ac14f | ||
|
|
49148013dc | ||
|
|
15c80b1cbf | ||
|
|
f89762550f | ||
|
|
b224b30a27 | ||
|
|
bfa660b5bc | ||
|
|
3ed1dc26fa | ||
|
|
442e51b89d | ||
|
|
a8b6a9f262 | ||
|
|
05019c8e82 | ||
|
|
d569a6524e | ||
|
|
5c64d501f4 | ||
|
|
ee8c781284 | ||
|
|
d8b1c4b6ca | ||
|
|
3582cdec0b | ||
|
|
ef169d19e9 | ||
|
|
582593dca7 | ||
|
|
ba6df2df14 | ||
|
|
65463d7612 | ||
|
|
1f38d749ea | ||
|
|
c4871aa371 | ||
|
|
f067d6b778 | ||
|
|
2c10c116e9 | ||
|
|
cfc2d55ca9 | ||
|
|
daaf62645e | ||
|
|
f34c02b79b | ||
|
|
5d5f5eaa12 | ||
|
|
40e5ac130e | ||
|
|
e449f18bd3 | ||
|
|
6d4f8a731b | ||
|
|
9debcdcd57 | ||
|
|
4d489e5add | ||
|
|
60f4089f6f | ||
|
|
33d2c31db9 | ||
|
|
267cd56bd6 | ||
|
|
2a4147f3b1 | ||
|
|
fb991f348b | ||
|
|
839f4a9708 | ||
|
|
e142e1bef0 | ||
|
|
14892783cd | ||
|
|
88c65394c1 | ||
|
|
afc0ad0b20 | ||
|
|
0c533da4d7 | ||
|
|
a5f8798ce8 | ||
|
|
ab52a12630 | ||
|
|
954de5d480 | ||
|
|
df71c4d256 | ||
|
|
d73df99d57 | ||
|
|
bfc2652481 | ||
|
|
90f31dd0f8 | ||
|
|
470338574f |
@@ -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 */,
|
||||
|
||||
+12
-2
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user