Custom Installomator
Build 48: documents how MDM admins can pin App Auto-Patch (AAP) 4.x to an internal Installomator fork instead of upstream. Two workflows are supported; pick the one that matches how tightly your environment controls Installomator source.
Why customize Installomator at all
Section titled “Why customize Installomator at all”AAP shells out to Installomator.sh plus the matching fragments/labels/*
directory for every patch run. Out of the box AAP fetches Installomator
from the upstream Installomator/Installomator repository and keeps it
under /Library/Application Support/AppAutoPatch/Installomator. That
default is appropriate for consumer Macs and for most managed fleets.
Some environments need to deviate from that default:
- Internal fork: your IT team maintains a fork that adds labels for in-house apps, tweaks blocking-process behavior, or pins upstream labels to vetted releases.
- Air-gapped or proxied networks: the target Macs cannot reach
github.comdirectly and you stage the Installomator payload from your software-distribution server. - Change-control: you must vet every change to Installomator before it lands on production Macs.
AAP 4.x exposes two workflows for these cases. They are independent of each other; pick whichever fits.
Workflow A — InstallomatorVersion managed preferences (recommended)
Section titled “Workflow A — InstallomatorVersion managed preferences (recommended)”The simplest option. AAP’s preference schema exposes a handful of keys that point its built-in updater at a non-default Installomator source:
| Key | Type | Notes |
|---|---|---|
InstallomatorVersion |
string | release, main, or custom. custom enables the next two keys. |
InstallomatorVersionCustomRepoPath |
string | HTTPS git URL of your fork (e.g. https://git.example.com/it/Installomator.git). |
InstallomatorVersionCustomBranchName |
string | Branch / tag inside that repo. Defaults to main. |
InstallomatorUpdateDisable |
bool | When true, AAP never updates Installomator on its own. See below. |
Deploy these via the same configuration profile you use for the rest
of AAP’s settings — the manifest in
xyz.techitout.appAutoPatch.json
(ProfileCreator / iMazing) and
xyz.techitout.appAutoPatch.jamf.json
(Jamf Pro) already include these keys.
When InstallomatorUpdateDisable is false (the default), the root
helper keeps the on-disk copy aligned with InstallomatorVersion.
For main and custom it compares the branch’s current commit with
source-commit next to Installomator.sh. That sees a same-day push
and a fragments/ change that did not regenerate Installomator.sh.
For release it compares fragments/version.sh with the latest
Installomator release tag. A difference re-downloads that source as a
GitHub archive. The check runs when the helper starts and about once a
day after that. Restarting the helper checks again immediately. Turning
InstallomatorUpdateDisable off while Installomator.sh is missing
retries within a minute. If the check cannot reach the source, the
installed copy stays.
InstallomatorVersionCustomRepoPath is a GitHub owner/repository
(MyOrg/Installomator). A https://github.com/owner/repository URL
for that same repository is accepted too.
When InstallomatorUpdateDisable is true, the helper does not download
or replace Installomator.sh or fragments/. It still refreshes
LabelVersionHelper.zsh. This is the mode to combine with Workflow B
when you want full change-control over the Installomator payload. A
string TRUE or FALSE from a 3.x profile is accepted.
GitHubAPIAuthEnabled and GitHubAPIToken authenticate the
api.github.com calls that check main, custom, and release.
Turn authentication on when a fleet or a custom repository is hitting
the unauthenticated limit of 60 requests an hour. A fine-grained token
needs Contents: Read on the repositories being queried. Archive
downloads on github.com stay unauthenticated. Both keys can be set
in Settings or in a profile. A managed value wins. The token is not
written to the log.
Workflow B — Push your own Installomator payload (Dan’s bundled script)
Section titled “Workflow B — Push your own Installomator payload (Dan’s bundled script)”Some environments prefer to bypass AAP’s built-in updater entirely and
push the Installomator payload through their existing
software-distribution pipeline. AAP 4.x ships with a builder script
that produces a self-extracting .zsh archive containing
Installomator.sh, fragments/functions.sh, and fragments/labels/:
- Script:
Scripts/build-appautopatch-installomator-sfx.zsh - Author: Originally authored by Dan Snelson (@dan-snelson) and contributed to AAP under the same license as the rest of the project. See the script’s header comment for the credit line.
What the builder does
Section titled “What the builder does”Given a git URL + branch, the builder:
- Sparse-clones the repo to a temp directory, fetching only
Installomator.sh,fragments/functions.sh, andfragments/labels/.functions.shis whatLabelVersionHelper.zshsources for the fast version check. A payload that omits it makes every label exit 2 and fall back to a full Installomator run. - Zips that subtree into a
payload.zipand validates that nothing unexpected slipped in (no.git/, no__MACOSX/, etc.). - Prepends a self-extracting zsh wrapper that, when run on a target
Mac, prompts for admin credentials via
osascript, extracts the payload into/Library/Application Support/AppAutoPatch/Installomator(configurable via--target-dir), setsroot:wheelownership, and logs the run to/var/log/Installomator-AppAutoPatch-Update.log. - Stamps the source repo URL, branch, and short commit into the generated wrapper’s header so you can audit which Installomator version a given Mac is running.
Typical invocation on a developer’s Mac (where /Users/admin/Installomator
is a working clone of your fork):
./Scripts/build-appautopatch-installomator-sfx.zsh \ --repo-url /Users/admin/Installomator \ --branch main-with-our-labels \ --target-dir "/Library/Application Support/AppAutoPatch/Installomator" \ --output-name "/Users/admin/Desktop/Installomator-for-AppAutoPatch-$(date +%F).zsh"The output is a single executable .zsh file you can hand to MDM as a
script policy, or wrap into a .pkg (see caveat below).
Pair Workflow B with InstallomatorUpdateDisable=true
Section titled “Pair Workflow B with InstallomatorUpdateDisable=true”If you push your own payload but leave AAP’s built-in updater
enabled, the next helper start or daily check re-downloads the
configured InstallomatorVersion and overwrites whatever you
deployed. Set InstallomatorUpdateDisable=true in the AAP
managed-preferences profile to disable that download. AAP will then
only use the on-disk copy your distribution pipeline maintains.
Jamf Pro caveat — .zsh rejection
Section titled “Jamf Pro caveat — .zsh rejection”Jamf Pro currently refuses to upload .zsh files as Mac Apps or
Packages. There are two practical workarounds:
- Script policy: Upload the self-extracting
.zshas a Script in Jamf and trigger it from a policy. Jamf does not reject.zshfor the Scripts payload; it only rejects the file extension for Mac App / Package uploads. - Wrap in a
.pkg: Usepkgbuild(or your build tool of choice) to wrap the.zshfile plus apostinstallthat executes it. AAP does not ship a sample wrapper because the choice of signing / notarization / payload-vs-component is environment-specific, but the minimal recipe is:
pkgbuild --identifier com.example.installomator-for-aap \ --version "$(date +%Y.%m.%d)" \ --root /tmp/installomator-payload-root \ --scripts /tmp/installomator-payload-scripts \ /Users/admin/Desktop/Installomator-for-AppAutoPatch.pkgSign with productsign before uploading to Jamf if your fleet
requires signed packages.
LabelVersionHelper.zsh — what about it?
Section titled “LabelVersionHelper.zsh — what about it?”A user piloting build 47 (TechTrekkie) asked whether the custom-
Installomator workflow breaks AAP’s LabelVersionHelper.zsh. The
short answer: no, the two are independent.
LabelVersionHelper.zsh is the small helper AAP installs alongside
Installomator (also under /Library/Application Support/AppAutoPatch/)
that AAP uses internally to derive an Installomator label’s
appNewVersion when the label’s own metadata doesn’t expose it. It is
installed and refreshed by the AAP root-helper daemon’s
ensureInstallomator() path, independent of which Installomator
source AAP is using.
In practice this means:
- Workflow A (custom managed-pref repo) refreshes both
Installomator.shandLabelVersionHelper.zshon the same daemon sweep. No action required from the admin. - Workflow B (pushed payload +
InstallomatorUpdateDisable=true) controlsInstallomator.sh,fragments/functions.sh, andfragments/labels/.LabelVersionHelper.zshis still installed and kept current by AAP’s helper becauseInstallomatorUpdateDisableis intentionally scoped to the Installomator payload, not to the helper’s own bootstrap files. Rebuild and redeploy any payload built beforefunctions.shwas included. Until that file is on disk, AAP logs one warning and uses the slow Installomator fallback for every label.
If you completely lock down /Library/Application Support/AppAutoPatch/
via filesystem permissions, you must whitelist
LabelVersionHelper.zsh so the helper can drop it in place; otherwise
the discovery pipeline degrades to metadata-only version comparisons.
Verifying
Section titled “Verifying”After deploying either workflow:
# Which Installomator AAP is actually running:ls -la "/Library/Application Support/AppAutoPatch/Installomator/Installomator.sh"/usr/bin/sed -nE 's/^VERSION="([^"]+)".*/\1/p' \ "/Library/Application Support/AppAutoPatch/Installomator/Installomator.sh" | head -n 1/usr/bin/sed -nE 's/^VERSIONDATE="([^"]+)".*/\1/p' \ "/Library/Application Support/AppAutoPatch/Installomator/Installomator.sh" | head -n 1# Commit installed for main or custom. Absent on a Workflow B payload.cat "/Library/Application Support/AppAutoPatch/Installomator/source-commit"The version line should match the commit / tag your distribution
pipeline expects. For main and custom, source-commit is the
commit the helper installed. For Workflow B, additionally:
# Header of the most recent self-extracting payload that ran:sudo head -n 30 /var/log/Installomator-AppAutoPatch-Update.logThat log records the source repo, branch, and short commit each payload was built from.
Related
Section titled “Related”MDM-ServiceManagement.md— approves the helper LaunchDaemon. The helper is what writes Installomator to disk; this profile must be in place for either workflow to run silently.MDM-AppManagement.md— pre-approves the Sequoia App Management TCC permission so Installomator can actually replace.appbundles after writing the new ones.
