SYM-Lite is a lean, purpose-built script for executing MDM-agnostic Installomator labels and Homebrew casks / formulas, as well as Jamf Pro-specific policy triggers, all through a unified swiftDialog selection and reporting interface.
|
|
|
|
|
|
✓ Unified execution support — Installomator labels, Homebrew packages, and (optionally) Jamf Pro policies in a single session
✓ Interactive selection UI — User-friendly checkbox dialog with per-item icons; optional install-state labels disable already-installed items and exit cleanly when nothing remains selectable
✓ Alphabetical sorting — All items sorted together by display name in selection dialog
✓ Silent mode — CSV-based automation support
✓ Early Installomator label validation — Configured Installomator labels are verified against the active Installomator file before they can appear or run
✓ Homebrew package support — Approved casks and formulas run in the logged-in user context when brew is available
✓ Inspect Mode monitoring — Rich status updates for Installomator labels and path-based progress for Homebrew/Jamf items
✓ Log monitoring — Parses Installomator.log for intermediate states and captures Homebrew/Jamf output into the main log
✓ Path-based validation — Pre/post-execution checks via file system monitoring
✓ Cache monitoring — Detects in-progress downloads
✓ Completion report — Per-item results summary and optional restart prompt
✓ Graceful interruption — Clean shutdown on SIGINT/SIGTERM with 30-second timeout
Edit the installomatorLabels array near the top of SYM-Lite.zsh:
installomatorLabels=(
"label | Display Name | Validation Path | Icon URL"
)Example:
installomatorLabels=(
"microsoftword | Microsoft Word | /Applications/Microsoft Word.app | https://icon.url"
"googlechrome | Google Chrome | /Applications/Google Chrome.app | https://icon.url"
"zoom | Zoom | /Applications/zoom.us.app | https://icon.url"
)At runtime, SYM-Lite validates each configured label against single-line and continued top-level alias arms in organizationInstallomatorFile before building the picker or accepting silent-mode CSV input. If a label is missing from that Installomator file, or if the Installomator file is unavailable, unreadable, or cannot be parsed, SYM-Lite logs a warning or error and removes Installomator labels from the current run while leaving other item types available.
Because root executes Installomator, organizationInstallomatorFile must be an absolute path that is not a symlink, and the file and every parent directory up to / must be owned by root with no group or other write bit. SYM-Lite checks this during pre-flight (hiding Installomator labels on failure) and again immediately before each label runs (failing that item). The default App Auto-Patch location meets this requirement.
Notes on bundled labels:
firefoxesrinstallsFirefox.app, so it shares/Applications/Firefox.appwith regular Firefox and shows as "Already installed" on Macs that already have Firefoxotterrequires an Installomator build that includes theotterlabel; older builds hide the item and log an error each run
Edit the homebrewItems array near the top of SYM-Lite.zsh:
homebrewItems=(
"cask:token | Display Name | Validation Path | Icon URL"
"formula:token | Display Name | Validation Path | Icon URL"
)Example:
homebrewItems=(
"cask:docker | Docker Desktop | /Applications/Docker.app | https://icon.url"
"formula:node | Node.js | /opt/homebrew/bin/node | SF=terminal"
"formula:python@3.12 | Python 3.12 | /opt/homebrew/bin/python3.12 | SF=terminal"
)Important:
- Use
cask:orformula:prefixes in the item ID - Homebrew examples and default validation paths in this repo assume Apple silicon with Homebrew installed in
/opt/homebrew - Homebrew items are hidden for the current run if no working
brewbinary is available - Homebrew items also require a valid logged-in user because package installs run in user context rather than as
root - Third-party tap items use fully-qualified tokens (e.g.,
formula:hashicorp/tap/terraform); whenhomebrewAutoTrustItems="true", SYM-Lite runsbrew trustfor that item before install (official taps are always trusted) - If the logged-in user is not a local administrator, casks install to
~/Applications(validation paths under/Applications/also match~/Applications/) - Homebrew runs with
HOMEBREW_NO_SUDO=1; casks needingsudo(e.g.,pkg-based installers) fail fast and report "Requires administrator rights" rather than prompting for a password - When
homebrewCreateCompletionDirectories="true"(default), before the first Homebrew install of each run, SYM-Lite createsshare/zsh/site-functionsandshare/fish/vendor_completions.dunder the brew prefix as the Homebrew user (neverroot); brew's completion child process can write into these directories but can't create them, so cask shell completions would otherwise fail withOperation not permitted - If
brewexits0but its output reports a child-process exception,Operation not permitted, orPermission denied(e.g., cask shell completions under${homebrewPrefix}/sharefailing even though the Homebrew user owns the prefix), SYM-Lite logs a[WARNING]and reports "Ready to use; Homebrew reported warnings"; the installed command or app still works, only extras such as shell completions may be missing - Validation paths use
${homebrewPrefix}(/opt/homebrewon Apple silicon,/usr/localon Intel); pre-flight warns when the detected or configuredbrewlives under a different prefix, because skip and completion checks may then be wrong - The Homebrew user is pinned during pre-flight; if the console user changes mid-run, remaining Homebrew items fail instead of running as the new user
Edit the jamfPolicyItems array near the top of SYM-Lite.zsh:
jamfPolicyItems=(
"trigger | Display Name | Validation Path | Icon URL"
)Example:
jamfPolicyItems=(
"installRosetta | Install Rosetta 2 | /usr/bin/arch | SF=cpu"
"enableFileVault | Enable FileVault | /Library/Preferences/com.apple.fdesetup.plist | SF=lock.shield"
"configureDock | Configure Dock | /usr/local/bin/dockutil | SF=dock.rectangle"
)Icon Options:
- Full URL:
https://... - SF Symbol:
SF=symbolname,weight=semibold,colour1=auto,colour2=auto
If your environment does not use Jamf Pro, set enableJamfPolicyItems="false" near the top of SYM-Lite.zsh.
When Jamf policy items are disabled:
- Jamf policy items do not appear in the interactive selection UI
- Jamf policy items do not execute
- Jamf binary pre-flight validation is skipped
- Silent mode warns and skips Jamf item IDs in the CSV input
If Jamf policy items are enabled but the Jamf binary is missing at jamfBinary, pre-flight logs a warning and removes Jamf policy items from the run; silent mode warns and skips their IDs in the CSV input.
If your environment does not use Homebrew packages through SYM-Lite, set enableHomebrewItems="false" near the top of SYM-Lite.zsh.
When Homebrew items are disabled:
- Homebrew items do not appear in the interactive selection UI
- Homebrew items do not execute
- Homebrew pre-flight detection is skipped
- Silent mode warns and skips Homebrew item IDs in the CSV input
Run the script as root with no parameters:
sudo ~/Downloads/SYM-Lite.zshUser experience:
- Selection dialog appears with all available items (or only the items listed in Parameter 5; see Limit the Interactive Selection Dialog); selectable items start checked when
selectionDialogDefaultChecked="true" - User selects one or more items using checkboxes
- Inspect Mode dialog launches showing real-time progress
- Completion report shows one row per selected item
- Optional restart prompt
selectionDialogDefaultChecked affects interactive mode only. Users can deselect prechecked items before continuing, and already-installed items disabled by status sublabels remain unchecked. Silent mode does not show a selection dialog; it runs the items listed in operationsCSV.
If the user clicks Cancel in the selection dialog, interactive mode exits cleanly without launching Inspect Mode. If selectionDialogStatusSublabelsEnabled="true" and every remaining valid item is already installed, interactive mode shows an informational dialog and exits without launching Inspect Mode. If no valid items remain after configuration validation, interactive mode exits cleanly with a generic unavailable-items message.
Interactive mode requirements:
- Requires an active logged-in GUI user
- Waits up to 120 seconds for a valid console user before exiting
- If the Mac is at the login window or otherwise headless, use
silentmode instead
Parameter 5 (operationsCSV) is optional in interactive mode. When it's set, the selection dialog shows only the listed item IDs, so one copy of SYM-Lite can back several focused Self Service policies.
Via Jamf Policy (e.g., "Developer Tools"):
- Parameter 4:
interactive - Parameter 5:
homebrew,cask:1password-cli,cask:claude-code,cask:codex,formula:direnv
Direct execution:
sudo /path/to/SYM-Lite.zsh "" "" "" interactive "cask:codex,formula:direnv"- An empty Parameter 5 (including separator-only values such as
,) shows all available items, as before - Listed items are still sorted by display name; CSV order is ignored
- Item IDs are parsed and normalized exactly as in silent mode; unknown or unavailable IDs are warned and skipped
- Status sublabels and
selectionDialogDefaultCheckedwork the same way for the listed items - If Parameter 5 contains no valid item IDs, SYM-Lite logs the valid item IDs for the run, shows the "no selectable items" dialog, and exits without falling back to the full list
- Homebrew items are hidden when
brewis not installed, so a Homebrew-focused list should also include thehomebrewInstallomator label; once Homebrew is installed, run the policy again to see the casks and formulae
Run with Jamf parameters or direct positional arguments:
Via Jamf Policy:
- Parameter 4:
silent - Parameter 5:
androidstudio,appleXcode,cask:codex
Parameter 5 must contain item identifiers exactly as they are defined in the configured item arrays. In this repo, that means values such as androidstudio, appleXcode, homebrew, cask:1password-cli, cask:codex, or formula:direnv, not a full Jamf command such as jamf policy -event homebrew.
Direct execution:
sudo /path/to/SYM-Lite.zsh "" "" "" silent "androidstudio,appleXcode,cask:codex"Silent mode also normalizes surrounding straight quotes and common smart quotes copied from rich-text sources, including when the entire CSV is wrapped once or when individual item IDs are quoted. That normalization is safe even when Jamf launches the script under a non-UTF shell locale. Plain comma-separated item IDs are still the recommended input format.
If SYM-Lite reports an unknown item ID, compare Parameter 5 against the identifiers configured near the top of SYM-Lite.zsh. For the current repo state, googleChrome is not a configured item ID, so silent mode will reject it until it is added to the appropriate item array.
Silent mode behavior:
- No selection dialog
- CSV list parsed directly
- No Inspect Mode or completion dialogs
- No restart prompt
- Same pre-flight checks still run, including
swiftDialogvalidation / installation - Installomator labels filtered out during pre-flight validation are warned and skipped in the CSV input
- If Jamf policy items are disabled or the Jamf binary is missing, Jamf item IDs in the CSV are warned and skipped
- If Homebrew items are disabled or unavailable for the current run, Homebrew item IDs in the CSV are warned and skipped
- Exits with an error if the CSV contains no valid item IDs
- Suitable for automated deployment
- macOS 15+ (required by swiftDialog 3.x)
- Root access — Script must run as
root - swiftDialog 3.1.0.4994+ (auto-installed if missing)
- Installomator — Required only when Installomator labels are configured and available for the current run
- Configured Installomator labels are validated early against the active
organizationInstallomatorFile - If the Installomator file is unavailable or cannot be parsed, Installomator labels are hidden and skipped for that run
- Configured Installomator labels are validated early against the active
- Homebrew Binary — Required only when
enableHomebrewItems="true"and Homebrew items are configured - Jamf Pro Binary — Required only when
enableJamfPolicyItems="true"and Jamf policy items are configured
PRE-FLIGHT CHECKS
├─ Verify root
├─ Check/install swiftDialog
├─ Normalize Installomator labels
├─ Normalize Homebrew item availability and detect brew path
├─ Normalize Jamf item availability from configuration
├─ Verify Jamf binary (if enabled and items configured; removes Jamf items when missing)
├─ Warn on item IDs configured in more than one item array
↓
SELECTION INTERFACE
├─ Show dialog (interactive; optional Parameter 5 allowlist) or parse CSV (silent)
├─ Validate at least one selection
└─ Collect selected item IDs (interactive: display-name order; silent: CSV order)
↓
INSPECT MODE CONFIGURATION
├─ Interactive mode only
├─ Build unified JSON config
├─ Merge Installomator + Homebrew + Jamf items
├─ Add cachePaths for download detection
└─ Validate JSON with plutil
↓
EXECUTION ENGINE
├─ Interactive mode launches Inspect Mode dialog (background)
│ └─ Silent mode logs progress without UI
├─ Process items sequentially in selection order
│ ├─ Installomator: executeInstallomatorLabel()
│ ├─ Homebrew: executeHomebrewItem()
│ └─ Jamf: executeJamfPolicy()
├─ Interactive mode waits for Inspect Mode to close
└─ Silent mode exits when execution completes
↓
COMPLETION & RESTART
├─ Interactive mode shows a completion report for selected items
└─ Interactive mode prompts for restart (if enabled and something was newly installed); "Restart Now" asks `loginwindow` to restart as the logged-in user, so apps can prompt to save
swiftDialog's Inspect Mode uses dual monitoring for comprehensive progress tracking:
Log Monitoring:
- Parses
/var/log/Installomator.login real-time - Uses undocumented
"preset": "installomator"feature - Shows intermediate states: "Downloading...", "Installing...", "Verifying...", "Completed"
File System Monitoring:
- Watches validation path via FSEvents API
- Item marks complete when app appears at specified path
File System Monitoring Only:
- Shows binary states: "Waiting" → "Completed"
- Watches validation path (e.g.,
/Applications/Docker.appor/opt/homebrew/bin/node)
File System Monitoring Only:
- Shows binary states: "Waiting" → "Completed"
- Watches validation path (e.g.,
/usr/bin/arch)
cachePathsmonitoring for in-progress downloadsscanInterval: 2— Checks every 2 seconds- Auto-enable Close button when all items complete
- 30-second timeout if dialog doesn't close naturally
- Pre-check: If validation path exists → skip
- Re-check Installomator ownership and permissions (fails the item if the check no longer passes)
- Execute:
Installomator.sh <label>withDEBUG=0 NOTIFY=silent - Inspect Mode: Log parsing + path monitoring
- Post-check: Exit code determines success/failure
- Pre-check: If validation path exists → skip
- Completion directories: when
homebrewCreateCompletionDirectories="true", once per run, createshare/zsh/site-functionsandshare/fish/vendor_completions.dunder the brew prefix as the logged-in user - Trust:
brew trustfor configured third-party tap items (user/tap/name) whenhomebrewAutoTrustItems="true" - Execute:
brew install(or--cask; adds--appdir=~/Applicationsfor non-admin users) as the logged-in user withHOMEBREW_NO_SUDO=1 - Inspect Mode: Path monitoring only
- Post-check: Exit code + path validation; a successful install whose output reports child-process or permission errors logs a
[WARNING]and reports "Ready to use; Homebrew reported warnings" - Quarantine (optional): when
homebrewAutoRemoveQuarantine="true"and the cask validation path is an.app, runspctl --assess --type execute; only if Gatekeeper accepts, removecom.apple.quarantineas the logged-in user (xattr -drs) so first launch skips the "downloaded from the Internet" prompt
- Pre-check: If validation path exists → skip
- Execute:
jamf policy -event <trigger> - Inspect Mode: Path monitoring only
- Post-check: Exit code + path validation
| Variable | Default | Purpose |
|---|---|---|
organizationPreset |
"2" |
swiftDialog Inspect Mode preset (1-4) |
organizationInstallomatorFile |
/Library/Application Support/AppAutoPatch/Installomator/Installomator.sh |
Path to Installomator.sh |
installomatorLog |
/var/log/Installomator.log |
Installomator log path for monitoring |
jamfBinary |
/usr/local/jamf/bin/jamf |
Path to jamf binary |
enableJamfPolicyItems |
"true" |
Show and execute Jamf policy items |
brewPath |
"" |
Optional Homebrew binary override |
enableHomebrewItems |
"true" |
Show and execute Homebrew cask/formula items |
homebrewUpdateBeforeInstall |
"false" |
Run brew update once before the first Homebrew package install |
homebrewCreateCompletionDirectories |
"true" |
Before the first Homebrew install, create share/zsh/site-functions and share/fish/vendor_completions.d under the brew prefix as the logged-in user so cask shell completions can install |
homebrewAutoTrustItems |
"true" |
Run brew trust for configured third-party tap items (user/tap/name) before install |
homebrewAutoRemoveQuarantine |
"false" |
After a cask install, remove com.apple.quarantine from its .app validation path, only when Gatekeeper accepts the app |
organizationOverlayiconURL |
swiftDialog logo | Overlay icon URL |
mainDialogIcon |
GitHub raw SYM_icon.png URL |
Main dialog icon |
fontSize |
"14" |
Dialog message font size |
selectionDialogDefaultChecked |
"true" |
Start selectable interactive-mode items checked |
selectionDialogStatusSublabelsEnabled |
"true" |
Show install-state sublabels, disable already-installed items, and exit cleanly if no selectable items remain |
restartPromptEnabled |
"true" |
Show restart prompt after completion |
scriptLog |
/var/log/...log |
Client-side log path (created 0640; rotated at 10 MB, keeping the three newest .old files) |
Item IDs must be unique across installomatorLabels, jamfPolicyItems, and homebrewItems; pre-flight warns on duplicates, and the first match (Installomator, then Jamf, then Homebrew) wins.
Version: 1.5.1
Date: 06-Oct-2026
Author: Dan K. Snelson (@dan-snelson)





