Building Your Own Installomator, Part 3: Hands-On, Pulling and Curating Labels

Building Your Own Installomator, Part 3: Hands-On, Pulling and Curating Labels

 Forking, curating labels, and packaging an internal build
Forking, curating labels, and packaging an internal build

Part 3 of a 4-part series

Part 2 covered branches and how much process your team needs. This post is the hands-on middle of the series: how the project’s files actually fit together, and three different ways to pull, curate, and review labels depending on how comfortable you are with Git.

The Key Detail: Installomator Is Built from Fragments

Modern Installomator is not maintained as one giant hand-edited Installomator.sh file. The project is assembled from fragment files. This is documented in detail in the project’s own utils/README.md; the summary below covers what you need for curating labels.

The official repo notes that contributors should edit files in fragments/, not the generated root Installomator.sh. The full script is assembled from those fragments, and the root script can be overwritten during release work.

The simplified structure looks like this:

Installomator/
|-- assemble.sh -> utils/assemble.sh
|-- Installomator.sh
|-- Labels.txt
|-- fragments/
|   |-- header.sh
|   |-- version.sh
|   |-- functions.sh
|   |-- arguments.sh
|   |-- labels/
|   |   |-- zoom.sh
|   |   |-- firefox.sh
|   |   `-- ...
|   `-- main.sh
`-- utils/
    `-- assemble.sh

The assemble.sh tool combines those pieces and writes a built script to:

build/Installomator.sh

When requested, it can also copy the built script back to the repo root as Installomator.sh.

One subtle but very important point: assemble.sh --labels /path/to/custom-labels adds your custom labels before the default fragments/labels directory. That is great for testing or overriding labels. It does not, by itself, create a script containing only your custom labels. If you want a smaller script with only your selected labels, curate the fork’s fragments/labels folder or use a dedicated build branch where that folder contains only the labels you want.

Version Note as of This Post

As of this July 2026 update, Installomator v10.9 is the latest formal GitHub release. Work that will become part of a future 10.10 release can appear on main before a new packaged release is cut. This is exactly why it is useful to distinguish between a GitHub release, the main branch, and your own assembled internal build.

The version numbers can be a little confusing because the repo has more than one useful state:

Upstream GitHub release/tag  -> v10.9, as of this July 2026 update
Upstream release branch      -> latest formal release line
Upstream main branch         -> beta / next-release work, including newer merged labels headed for 10.10 or later
Your internal build          -> whatever commit, branch, or tag you assemble and deploy

For a curated fork, the important takeaway is that the version printed by the script tells you which upstream Installomator code line you started from, but it does not fully describe your internal label set. If you build from v10.9 and remove most labels, you still have an internal build based on v10.9, not the same artifact as the public v10.9 package. If you build from main after v10.9 to pull in newer merged label fixes, you may have labels or fixes that are likely headed for 10.10 or a later release even though no newer packaged release exists yet.

That is why I like tagging internal builds separately, for example:

internal-installomator-10.9-2026.07.07
internal-installomator-main-2026.07.07

Those tags make the relationship clear: which upstream release or branch you started from, plus the date of your reviewed internal build.

Workflow 1: GitHub Web Interface

This is the friendliest path if you are not living in Terminal all day.

  1. Open the Installomator repository:
https://github.com/Installomator/Installomator
  1. Click Fork.

  2. Choose your user or organization as the owner.

  3. Leave the name as Installomator, or rename it to something explicit like:

installomator-internal
  1. Clone your fork later using GitHub Desktop or the command line.

  2. Create a production branch in GitHub:

production
  1. In that branch, open:
fragments/labels/
  1. Delete labels you do not want in your internal build.

  2. Add or edit internal label files as needed.

  3. Commit the changes.

  4. Pull the repo locally and run:

./assemble.sh --script

That will assemble the curated version into the root Installomator.sh.

Pulling in a label from upstream using the web UI

When upstream adds or fixes a label you want:

  1. Open the upstream Installomator repo.
  2. Navigate to fragments/labels/.
  3. Open the label file you want.
  4. Copy the label content.
  5. Open your fork and switch to your production or testing branch.
  6. Create or edit the matching file in fragments/labels/.
  7. Commit the change with a clear message:
Update zoom label from upstream Installomator

This is manual, but it is understandable and reviewable.

Workflow 2: GitHub Desktop on macOS

GitHub Desktop is a nice middle ground: you get branches, diffs, commits, and sync without needing to memorize Git commands.

  1. Fork Installomator on GitHub.com.

  2. Open GitHub Desktop.

  3. Choose:

File > Clone Repository
  1. Select your fork.

  2. Clone it somewhere predictable, for example:

~/Developer/installomator-internal
  1. Create a branch:
production
  1. In Finder or your editor, open:
~/Developer/installomator-internal/fragments/labels/
  1. Remove the label files you do not want in your internal build.

  2. Add your local label files.

  3. Review the diff in GitHub Desktop.

  4. Commit:

Curate production label set
  1. Publish or push the branch.

  2. Open Terminal from the repo folder and build:

./assemble.sh --script

GitHub Desktop handles the Git work; Terminal handles the build.

Updating from upstream in GitHub Desktop

There are two common approaches.

The simple approach is to use GitHub’s Sync fork button on the website, then pull the changes in GitHub Desktop.

The more controlled approach is to maintain a separate upstream-tracking branch and selectively copy labels into your production branch. That keeps new upstream labels from automatically appearing in your internal production build.

For a curated Installomator, I prefer controlled copying. A small amount of manual work is a feature here, not a bug.

Workflow 3: Git Command Line

This is the most repeatable path and the easiest to automate later.

If your organization needs the internal Installomator repo to be private, this workflow still applies. The only difference is that `origin` is your private repo and `upstream` is the public Installomator project. That lets you pull selected public label updates without publishing your custom or overridden labels.

First, fork the repo on GitHub. Then clone your fork:

cd ~/Developer
git clone git@github.com:YOUR-ORG/installomator-internal.git
cd installomator-internal

Add the official Installomator repo as upstream:

git remote add upstream https://github.com/Installomator/Installomator.git
git remote -v

You should see something like:

origin    git@github.com:YOUR-ORG/installomator-internal.git (fetch)
origin    git@github.com:YOUR-ORG/installomator-internal.git (push)
upstream  https://github.com/Installomator/Installomator.git (fetch)
upstream  https://github.com/Installomator/Installomator.git (push)

Fetch upstream:

git fetch upstream

Create your production branch:

git switch -c production

Curate your labels:

cd fragments/labels

Remove labels you do not want, or move them into an archive outside the active labels folder:

mkdir -p ../../archived-labels
git mv unwantedlabel.sh ../../archived-labels/

Add your local labels:

cp ~/Documents/internal-installomator-labels/*.sh .

Commit:

git add fragments/labels archived-labels
git commit -m "Curate production Installomator labels"

Build:

cd ../..
./assemble.sh --script

Test a label in debug mode:

./assemble.sh zoom

Test an actual install only when ready:

sudo ./assemble.sh zoom NOTIFY=silent DEBUG=0

Building a Script with Only the Labels You Want

If your goal is a smaller Installomator.sh, do not rely only on:

./assemble.sh --labels ~/Documents/MyLabels --script

That command adds your labels before the default labels. It is ideal for overrides and local testing, but the default labels still come along for the ride.

For a truly curated build, use one of these patterns.

Pattern A: Curate fragments/labels in Your Production Branch

This is the simplest and most visible.

production
`-- fragments/labels/
    |-- firefox.sh
    |-- googlechrome.sh
    |-- microsoftoffice365.sh
    |-- slack.sh
    `-- zoom.sh

Then build:

./assemble.sh --script

Pattern B: Keep Upstream Labels in One Branch, Production Labels in Another

Use main to track upstream and production for your reduced label set.

When you want a newer upstream label:

git fetch upstream
git switch production
git checkout upstream/main -- fragments/labels/zoom.sh
git commit -m "Update zoom label from upstream"
./assemble.sh --script

That pulls in one label, not the entire upstream label catalog.

Pattern C: Maintain an Internal Labels Folder for Overrides

This works well when you mostly use upstream labels but need local overrides:

./assemble.sh --labels ~/Documents/InstallomatorLabels zoom

Because custom label locations are inserted before the default labels, a custom zoom.sh can override the upstream zoom.sh.

This is not the best method for building a tiny script, but it is excellent for testing and overriding.

Example: Pulling in One Upstream Label

Say your internal production build does not currently include pgadmin4, but you decide you want it.

From your fork:

git fetch upstream
git switch production
git checkout upstream/main -- fragments/labels/pgadmin4.sh
git diff -- fragments/labels/pgadmin4.sh
git commit -m "Add pgAdmin 4 Installomator label"
./assemble.sh --script

Then test:

./assemble.sh pgadmin4

When satisfied:

sudo ./assemble.sh pgadmin4 NOTIFY=silent DEBUG=0

Example: Keeping a Local Override

Suppose upstream has a label you like, but your environment needs a different download source or a different blockingProcesses value.

Copy the upstream label into your production branch:

git checkout upstream/main -- fragments/labels/exampleapp.sh

Edit it locally:

code fragments/labels/exampleapp.sh

Commit the local policy decision:

git add fragments/labels/exampleapp.sh
git commit -m "Customize exampleapp label for internal deployment"

Later, when upstream changes that label, do not blindly overwrite your version. Compare first:

git fetch upstream
git diff HEAD..upstream/main -- fragments/labels/exampleapp.sh

Then decide whether to merge, copy, or ignore the upstream change.

Suggested Review Checklist for Labels

Before a label goes into your production branch, check:

  • Is the label using the vendor’s official source, or a clearly justified fallback such as a GitHub release, Sparkle feed, official API, Homebrew Cask, or AutoPkg recipe?
  • Does downloadURL resolve to the expected vendor artifact, including after redirects?
  • Are downloadURL and appNewVersion derived from the same trusted source when possible, so the version and installer cannot drift apart?
  • Does appNewVersion return a non-empty value that matches the downloaded app or package version format?
  • Is the artifact type correct: dmgpkgziptbz, or another Installomator-supported type?
  • Have you inspected the downloaded payload, not just the web page, to confirm the app name, package contents, or nested folder structure?
  • Does expectedTeamID match the signed app or package from the downloaded artifact?
  • Does the label need architecture logic for Apple Silicon vs Intel?
  • Does the installed app bundle match name, or do you need appName for cases like name="Snagit" and appName="Snagit 2024.app"?
  • Does versionKey need to be set because the app reports the useful version in CFBundleVersion instead of CFBundleShortVersionString?
  • For DMGs or ZIPs with an enclosing folder, do you need folderName, and have you confirmed Installomator will copy the required folder structure?
  • Are blockingProcesses correct and not overly broad?
  • Are helper variables useful, or can the label be made smaller without making it harder to review?
  • Is the parsing reasonably stable, preferring JSON, XML, Sparkle appcasts, GitHub release data, or other structured sources over broad HTML scraping?
  • Has the label been tested with DEBUG=1 first?
  • Has the install been tested on a sacrificial Mac or VM before production?

Next in this series: Part 4 covers turning your curated build into something deployable — a release flow, deploying the built script directly to Jamf Pro, and packaging a signed or unsigned installer with munkipkg.

No Comments

Leave a Reply