InstallApplications Swiftly

InstallApplications Swiftly (IAS) intends to be a replacement for InstallApplications (IA) tool. Code is not yet ready for public release. I still need to finish some parts and do the final refactoring not to mention proper testing. Expect alpha version in three to four weeks. 

This blog post outlines the development goals, implementation decisions and differences between IA and IAS.

Main goals

  • Swift code using Apple frameworks whenever possible. Minimum external dependencies.
  • Smallest footprint possible. Less than 10 files with the total size under 1 MB. 
  • Speed. Files downlod in parallel on multiple threads. Option to mark the item to be executed in parallel with other items.
  • Compatibility with InstallApplications JSON control file format.

Implementation

  • OOP model.
  • File downloads handled by URLSession.
  • Multithreading implemented with GCD.
  • LaunchDaemon (iasd) + LaunchAgent (iasagent) model stays but with differences. IAS logic is implemented solely in the iasd. Userscript execution is delegated to iasagent using the XPC remote object calls.
  • No argument parsing. Configuration is provided via Plist.
  • Logging exclusively to the system unified log with Logger.
  • Target is macOS 11.0 or newer.
Main Thread
JSONControlItem.download()
JSONControlItem.download()
IAS.beginRun()
semaphore.signal()
semaphore.signal()
JSONControlItem.parse()
JSONControlItem.parse()
R
R
R
R
R
R
R
R
DispatchGroup.wait()
DispatchGroup.wait()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
Dowload threads
Dowload threads
R
R
R
R
J
J
Extra execution threads
Extra execution thre…
Prefligh.downloadResources()
Prefligh.downloadResources()
Prefligh.execute()
Prefligh.execute()
SetupAssistant phase
SetupAssistant phase
Prefligh phase
Prefligh phase
P
P
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
P
P
R
R
SetupAssistant.downloadResources()
SetupAssistant.downloadResources()
Userland.downloadResources()
Userland.downloadResources()
SetupAssistant.execute()
SetupAssistant.execute()
Userland.execute()
Userland.execute()
R
R
R
R
R
R
U
U
U
U
P
P
Wait for iasagent to be loaded
Wait for iasagent to be loaded
R
R
P
P
DispatchGroup.wait()
DispatchGroup.wait()
R
R
semaphore.signal()
semaphore.signal()
R
R
Userland phase
Userland phase
(1) Sequential task execution
(1) Sequential task execution
(2) Parallel group execution
(2) Parallel group execution
(3) Async (donotwait) task execution
(3) Async (donotwait) task executi…
(4) Sequential task execution
(4) Sequential task execution
Connection to iasagent etablished
Connection to iasagent etablished
DispatchGroup.enter()
DispatchGroup.enter()
semaphore.signal()
semaphore.signal()
R
R
U
U
U
U
IAS.cleanUp() && exit()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
semaphore.signal()
donotwait
item
donotwait…
synchronous item
synchronous i…
parallel group
item
parallel grou…
XPC calls
XPC calls
P = package
P = package
J = JSON
J = JSON
R = Rooscript
R = Rooscript
U = Userscript
U = Userscript
Return value
Return value
NSXPCConnection()
NSXPCConnection()
Viewer does not support full SVG 1.1

Advantages over IA

  • IAS download and install should be much quicker. Current Python.framework (IA 2.1 Alpha 1) has 6000+ files and is 40+ MB in size (compressed). Size of IAS binaries is currently less than 1 MB.
  • Workflow should complete faster since it’s no longer linear cycle download -> execute -> download -> execute.
  • More secure file permission model bacause iasagent only needs to read and execute userscript files.
  • Code signing and notarization should be easier compared to Python framework with many executable files.

Disadvantages over IA

  • If you run Python scripts as part of IA run you will have to package your own Python.framework and deploy as one of the first items in the IAS workflow.
  • Swift standard library does not have argument parser. I did not want to use any Swift package for this so I decided do configure iasd solely via plist (Either passed as first argument or known filename in the same directory).
  • I am not aiming for backward compatibility. macOS 11.0 or newer is currently the target.

Other new features

Multiple preflight scripts

Ability to run multiple preflight rootscript items if desired. Preflight script are always executed in parallel. If either one of them exists with non zero exit code IAS proceeds to the SetupAssistant phase.

Hash validation modes

Ability to control SHA-256 validation. There are three options set in the configuration Plist: 

  • Fail: Abort run if computed hash does not match after multiple redownloads (IA behavior, default)
  • Warning: Proceed with the execution if the hash of the two subsequent file downloads is the same. Issue a warning to the system log.
  • None: Do not check the SHA-256 hash at all.

Item failure responses

Ability to configure response to item failure. There is a new option applicable for every SetupAssistant and Userland phase items:

  • Failable: If download fails item execution is skipped with an error message in the log.
  • FailableExecution: Download failure aborts the IAS run. Item execution finishing with non-zero exit code does not end the IAS run (IA behavior, default).
  • FailureIsNotAnOption: If either download fails or execution returns non-zero exit code IAS run is aborted.

Possible future features

  • Support configuration via the configuration profiles (NSUserDefaults).
  • Reintroduce the option to automatically write some of the status messages to DEP notify control file.
  • (Danger zone) Use private frameworks to get list of package receipts (PKInstallHistory) and start package installs (unkown if possible).