Skip to main content

Deploying Command-Line Tools & Homebrew Packages on macOS

Overview

You can deploy command-line tools from Homebrew, such as wget, jq, or git, to managed Macs as Swif custom apps. You don't need to build a .pkg or .dmg installer. Instead, you add three short scripts, and Swif installs, checks, upgrades, and removes the tool for you.

To do this, create a custom app with the Script type. Swif runs your scripts on each Mac, in the background.

Homebrew runs as the Mac's user, not as root. Swif runs scripts as root, but Homebrew refuses to run as root, for security. The scripts in this article handle this: they find the user who owns Homebrew on the Mac, and run Homebrew as that user.


Before you start

Command-line tools only

Works

Doesn't work

Homebrew formulae: command-line tools and libraries, such as wget, jq, git, or openssl@3

Homebrew casks: apps with a graphical interface, such as browsers or editors

Casks often run the vendor's own installer, which can ask for a password or user input. That fails when it runs in the background. For apps with a graphical interface, use an application template (View All Templates on the Applications page), apps imported from the Apple App Store or Apple VPP, or a custom app with a .pkg or .dmg installer.

Apple silicon and Intel Macs

Mac

If Homebrew isn't installed

If Homebrew is already installed

Apple silicon

The script installs Homebrew first, for the Mac's signed-in user

The script uses it

Intel

The install stops with "Automatic install works only on Apple silicon Macs". Homebrew's official installer no longer supports Intel Macs.

The script uses it, from /usr/local

To use these scripts on Intel Macs, install Homebrew on them first.

A signed-in user

On an Apple silicon Mac without Homebrew, the script installs Homebrew for the user who's signed in, or the last user who signed in. If no one has signed in yet, the install waits, and is retried later.


Create the custom app

  1. In Swif, go to Device Management > Applications, and select Add Custom Application.

  2. Set Operating System to macOS.

  3. Set Type to Script.

    With Script selected, the Installer File and Installed App Name fields are hidden, since command-line tools aren't installed as apps in /Applications.

  4. Fill in the app details:

    • Name: such as wget (Homebrew)

    • Version: the version you're deploying, such as 1.25.0

    • Tags or categories, as needed

  5. Paste the scripts from Script templates below:
    In each script, change FORMULA to your tool's name. In the install check script, also set MIN_VERSION.

Field

Script

Pre-Install Command Script

Install script

Validation Rules > Script

Install check script

Uninstall Script

Uninstall script

6. Choose the devices or device groups to deploy to, review the settings, and click Finish.


Script templates

What to change

Script

Change

Example

All three

FORMULA: the Homebrew formula's name

wget, jq, openssl@3

Install check

MIN_VERSION: the oldest version you accept

1.25.0

To find a formula's exact name and current version, search formulae.brew.sh.

Install script

Paste into Pre-Install Command Script. It installs Homebrew if needed (Apple silicon only), then installs the formula as the user who owns Homebrew.

#!/bin/sh
# Installs a Homebrew formula as the Mac user who owns Homebrew.
# If Homebrew is missing, the official Homebrew installer is installed first (Apple silicon only).

FORMULA="hello" # <-- Replace with your target Homebrew formula (e.g. wget, jq, git)

# Pinned official Homebrew installer package
HOMEBREW_PKG_URL="https://github.com/Homebrew/brew/releases/download/7.0.7/Homebrew.pkg"
HOMEBREW_PKG_SHA256="75f699be3059d0068262cab830b5eadce048229aecb469018b5a883b4f3206d7"

WORK_DIR=""
fail() {
echo "ERROR: $1"
[ -n "$WORK_DIR" ] && /bin/rm -rf "$WORK_DIR"
exit 1
}

# 1. Install Homebrew if missing
if [ ! -x /opt/homebrew/bin/brew ] && [ ! -x /usr/local/bin/brew ]; then
[ "$(/usr/bin/uname -m)" = "arm64" ] || fail "Homebrew is not installed. Automatic install works only on Apple silicon Macs."

# Identify user who will own Homebrew
BREW_USER=$(/usr/bin/stat -f %Su /dev/console)
case "$BREW_USER" in
""|root|_*) BREW_USER=$(/usr/bin/defaults read /Library/Preferences/com.apple.loginwindow lastUserName 2>/dev/null) ;;
esac
case "$BREW_USER" in
""|root|_*) fail "Homebrew is not installed and no active user was found to own it. Will retry later." ;;
esac
/usr/bin/id "$BREW_USER" >/dev/null 2>&1 || fail "User $BREW_USER does not exist."

# Set installer target user
/bin/rm -f /var/tmp/.homebrew_pkg_user.plist
/usr/bin/defaults write /var/tmp/.homebrew_pkg_user HOMEBREW_PKG_USER "$BREW_USER"
/usr/sbin/chown root:wheel /var/tmp/.homebrew_pkg_user.plist
/bin/chmod 600 /var/tmp/.homebrew_pkg_user.plist

# Download, verify SHA-256, and install
echo "Installing Homebrew for user $BREW_USER..."
WORK_DIR=$(/usr/bin/mktemp -d /tmp/homebrew-install.XXXXXX) || fail "Could not create temporary directory."
PKG="$WORK_DIR/Homebrew.pkg"
/usr/bin/curl -fsSL --retry 3 --connect-timeout 30 --max-time 1800 -o "$PKG" "$HOMEBREW_PKG_URL" || fail "Failed to download Homebrew installer."
[ "$(/usr/bin/shasum -a 256 "$PKG" | /usr/bin/awk '{print $1}')" = "$HOMEBREW_PKG_SHA256" ] || fail "Checksum verification failed for Homebrew installer."
/usr/sbin/installer -pkg "$PKG" -target / || fail "Homebrew package installation failed."
/bin/rm -rf "$WORK_DIR"
WORK_DIR=""
fi

# 2. Locate Homebrew installation path
if [ -x /opt/homebrew/bin/brew ]; then
PREFIX="/opt/homebrew"
elif [ -x /usr/local/bin/brew ]; then
PREFIX="/usr/local"
else
fail "Homebrew is not installed on this Mac."
fi

# 3. Locate the owner of Homebrew
OWNER=$(/usr/bin/stat -L -f %Su "$PREFIX/bin/brew")
case "$OWNER" in
""|root) fail "Could not determine the non-root owner of Homebrew." ;;
esac
OWNER_HOME=$(/usr/bin/dscl /Search -read "/Users/$OWNER" NFSHomeDirectory 2>/dev/null | /usr/bin/awk '{print $2}')
[ -n "$OWNER_HOME" ] || fail "Could not find home directory for $OWNER."

# 4. Install formula in clean environment as Homebrew owner
cd /tmp || fail "Could not access /tmp."
exec /usr/bin/sudo -u "$OWNER" /usr/bin/env -i \
HOME="$OWNER_HOME" USER="$OWNER" LOGNAME="$OWNER" \
PATH="$PREFIX/bin:/usr/bin:/bin:/usr/sbin:/sbin" \
HOMEBREW_NO_ANALYTICS=1 HOMEBREW_NO_ENV_HINTS=1 \
"$PREFIX/bin/brew" install --formula "$FORMULA"

About the pinned Homebrew installer. The script downloads Homebrew 7.0.7, and checks its SHA-256 fingerprint before installing it. You don't need to update it for new Homebrew releases: Homebrew updates itself the first time it runs.

Install check script

Paste into Validation Rules > Script. It checks the installed version directly from the file system, without running Homebrew, so it's fast.

Result

Meaning

What Swif does

Exit 0

The formula isn't installed, or is older than MIN_VERSION

Runs the install script

Exit 1

The formula is installed, at MIN_VERSION or newer

Nothing

#!/bin/sh
# Install check
# exit 0 -> Formula missing or older than MIN_VERSION (install/upgrade required)
# exit 1 -> Formula installed at MIN_VERSION or newer (compliant)
# This script inspects the filesystem directly and does not run brew.

FORMULA="${FORMULA:-hello}" # <-- Target formula name
MIN_VERSION="${MIN_VERSION:-2.12.3}" # <-- Increment this value to trigger fleet-wide upgrades

# Semantic version comparison: returns 0 if $1 < $2
version_lt() {
/usr/bin/awk -v a="$1" -v b="$2" 'BEGIN {
na = split(a, x, /[._+-]/); nb = split(b, y, /[._+-]/)
n = (na > nb) ? na : nb
for (i = 1; i <= n; i++) {
if (x[i] == y[i]) continue
if (x[i] ~ /^[0-9]+$/ && y[i] ~ /^[0-9]+$/) exit !(x[i] + 0 < y[i] + 0)
exit !(x[i] < y[i])
}
exit 1
}'
}

for PREFIX in /opt/homebrew /usr/local; do
if [ -x "$PREFIX/bin/brew" ]; then
LINK=$(/usr/bin/readlink "$PREFIX/opt/$FORMULA") || exit 0 # Formula not installed
INSTALLED=$(/usr/bin/basename "$LINK") # Resolves version from Cellar path
if version_lt "$INSTALLED" "$MIN_VERSION"; then
exit 0 # Installed version is older than MIN_VERSION
fi
exit 1 # Already installed at or above MIN_VERSION
fi
done

exit 0 # Homebrew not found; proceed to preinstall to bootstrap or report error

Uninstall script

Paste into Uninstall Script. It removes the formula, as the user who owns Homebrew, when the app is removed from a device.

#!/bin/sh
# Uninstalls a Homebrew formula as the Mac user who owns Homebrew.

FORMULA="hello" # <-- Replace with formula to remove

fail() {
echo "ERROR: $1"
exit 1
}

# 1. Locate Homebrew installation path
if [ -x /opt/homebrew/bin/brew ]; then
PREFIX="/opt/homebrew"
elif [ -x /usr/local/bin/brew ]; then
PREFIX="/usr/local"
else
fail "Homebrew is not installed on this Mac."
fi

# 2. Locate Homebrew owner
OWNER=$(/usr/bin/stat -L -f %Su "$PREFIX/bin/brew")
case "$OWNER" in
""|root) fail "Could not find Homebrew owner." ;;
esac
OWNER_HOME=$(/usr/bin/dscl /Search -read "/Users/$OWNER" NFSHomeDirectory 2>/dev/null | /usr/bin/awk '{print $2}')
[ -n "$OWNER_HOME" ] || fail "Could not find home directory for $OWNER."

# 3. Remove formula in clean environment as Homebrew owner
cd /tmp || fail "Could not access /tmp."
exec /usr/bin/sudo -u "$OWNER" /usr/bin/env -i \
HOME="$OWNER_HOME" USER="$OWNER" LOGNAME="$OWNER" \
PATH="$PREFIX/bin:/usr/bin:/bin:/usr/sbin:/sbin" \
HOMEBREW_NO_ANALYTICS=1 HOMEBREW_NO_ENV_HINTS=1 \
"$PREFIX/bin/brew" uninstall --formula "$FORMULA"

Upgrades and security fixes

Upgrades happen only when you choose. They don't run automatically, so an update can't unexpectedly break a developer's tools, such as Python or Node.js.

To upgrade a tool across your Macs, for example to fix a security issue:

  1. In the install check script, raise MIN_VERSION to the version you need.

  2. Update the app's Version in Swif, to match.

  3. Save the app.

On each Mac with an older version, the install check returns exit 0, and Swif runs the install script, which upgrades the tool.

Homebrew installs its latest version. MIN_VERSION sets the oldest version you accept, not the exact version installed. A Mac can end up on a newer version than MIN_VERSION.

Only set MIN_VERSION to a version Homebrew has. If MIN_VERSION is newer than Homebrew's latest version, the install check keeps asking for an upgrade that can't happen. Check the current version on formulae.brew.sh first.


Troubleshooting

"Automatic install works only on Apple silicon Macs"

The Mac is an Intel Mac without Homebrew. Homebrew's official installer no longer supports Intel Macs. Install Homebrew on the Mac first, then the scripts use it.

"Homebrew is not installed and no active user was found to own it. Will retry later."

No user has signed in to the Mac yet, so there's no one to install Homebrew for. The install is retried later. It succeeds once a user signs in.

"Could not determine the non-root owner of Homebrew" or "Could not find Homebrew owner"

Homebrew on this Mac is owned by root, which Homebrew doesn't support. Reinstall Homebrew as a regular user.

"Checksum verification failed for Homebrew installer"

The downloaded Homebrew installer didn't match its expected fingerprint, so it wasn't installed. This can happen if the download was interrupted, or changed by a network proxy. The install is retried. If it keeps failing, check the Mac's network.

The tool keeps reinstalling

MIN_VERSION is probably newer than the version Homebrew offers. Set it to Homebrew's current version, or lower.

A GUI app doesn't install

Homebrew casks aren't supported. Use an application template (View All Templates on the Applications page), apps imported from the Apple App Store or Apple VPP, or a custom app with a .pkg or .dmg installer.


Related resources

Did this answer your question?