Add probo-agent binary, installer, and CI
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
@@ -12,14 +12,17 @@
|
||||
#
|
||||
# We intentionally do not abort the install if enrollment fails:
|
||||
# the binary is laid down regardless, and the operator can finish
|
||||
# enrollment with `sudo probo-agent install ...` from Terminal.
|
||||
# enrollment from the menu bar helper.
|
||||
|
||||
set -u
|
||||
|
||||
LOG_FILE="/var/log/probo-agent-install.log"
|
||||
BINARY="/usr/local/bin/probo-agent"
|
||||
STATE_DIR="/var/lib/probo-agent"
|
||||
RUN_DIR="/var/run/probo-agent"
|
||||
CONF_FILE="/tmp/probo-agent.conf"
|
||||
TRAY_LABEL="com.probo.agent.tray"
|
||||
TRAY_PLIST_NAME="${TRAY_LABEL}.plist"
|
||||
|
||||
# Mirror everything to the install log. We keep stdout/stderr open
|
||||
# too so failures still surface in macOS Installer.app's log pane.
|
||||
@@ -39,11 +42,123 @@ mkdir -p "${STATE_DIR}"
|
||||
chown root:wheel "${STATE_DIR}"
|
||||
chmod 0700 "${STATE_DIR}"
|
||||
|
||||
mkdir -p "${RUN_DIR}"
|
||||
chown root:wheel "${RUN_DIR}"
|
||||
chmod 0755 "${RUN_DIR}"
|
||||
|
||||
register_tray_launchagent() {
|
||||
local current_user user_uid agents_dir plist_path
|
||||
|
||||
agents_dir="/Library/LaunchAgents"
|
||||
plist_path="${agents_dir}/${TRAY_PLIST_NAME}"
|
||||
|
||||
mkdir -p "${agents_dir}"
|
||||
cat > "${plist_path}" <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>${TRAY_LABEL}</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>${BINARY}</string>
|
||||
<string>tray</string>
|
||||
<string>--run-dir</string>
|
||||
<string>${RUN_DIR}</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
chmod 0644 "${plist_path}"
|
||||
echo "Installed tray LaunchAgent at ${plist_path}."
|
||||
|
||||
bootstrap_tray_for_user() {
|
||||
local username="$1"
|
||||
local user_uid
|
||||
|
||||
if [ -z "${username}" ] || \
|
||||
[ "${username}" = "root" ] || \
|
||||
[ "${username}" = "loginwindow" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
user_uid="$(id -u "${username}" 2>/dev/null || true)"
|
||||
if [ -z "${user_uid}" ]; then
|
||||
echo "warning: cannot resolve uid for ${username}; skipping tray bootstrap."
|
||||
return 1
|
||||
fi
|
||||
|
||||
launchctl bootout "gui/${user_uid}/${TRAY_LABEL}" 2>/dev/null || true
|
||||
if ! launchctl bootstrap "gui/${user_uid}" "${plist_path}"; then
|
||||
echo "warning: could not start tray helper for ${username}; it will start at next GUI login."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Started tray LaunchAgent for ${username}."
|
||||
return 0
|
||||
}
|
||||
|
||||
started_any=false
|
||||
seen_users=" "
|
||||
|
||||
for username in $(users 2>/dev/null || true); do
|
||||
case "${seen_users}" in
|
||||
*" ${username} "*) continue ;;
|
||||
esac
|
||||
seen_users="${seen_users}${username} "
|
||||
|
||||
if bootstrap_tray_for_user "${username}"; then
|
||||
started_any=true
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${started_any}" = false ]; then
|
||||
current_user=$(stat -f "%Su" /dev/console 2>/dev/null || true)
|
||||
if bootstrap_tray_for_user "${current_user}"; then
|
||||
started_any=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${started_any}" = false ]; then
|
||||
echo "No active GUI session found; tray helper will start at next GUI login."
|
||||
fi
|
||||
}
|
||||
|
||||
register_enrollment_url_scheme() {
|
||||
local app_path lsregister
|
||||
|
||||
app_path="/Applications/Probo Agent.app"
|
||||
if [ ! -d "${app_path}" ]; then
|
||||
echo "warning: ${app_path} not found; cannot register probo:// URL scheme."
|
||||
return 0
|
||||
fi
|
||||
|
||||
lsregister="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister"
|
||||
if [ ! -x "${lsregister}" ]; then
|
||||
echo "warning: lsregister is unavailable; URL scheme registration skipped."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! "${lsregister}" -f "${app_path}"; then
|
||||
echo "warning: failed to register probo:// URL scheme."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Registered probo:// URL scheme."
|
||||
}
|
||||
|
||||
# An admin (or MDM) may stage /tmp/probo-agent.conf to drive an
|
||||
# unattended enrollment. Recognized keys (shell-style):
|
||||
#
|
||||
# PROBO_SERVER_URL=https://app.getprobo.com
|
||||
# PROBO_ENROLLMENT_TOKEN=<token>
|
||||
# PROBO_SERVER_URL=https://your-probo-host.example.com
|
||||
# PROBO_ENROLLMENT_TOKEN=<enrollment-token>
|
||||
# PROBO_NO_AUTO_UPDATE=true
|
||||
#
|
||||
# Parse KEY=VALUE lines without sourcing or eval so a crafted conf
|
||||
@@ -61,7 +176,7 @@ if [ -f "${CONF_FILE}" ]; then
|
||||
echo "Found ${CONF_FILE}, attempting unattended enrollment."
|
||||
|
||||
CONF_SERVER=""
|
||||
CONF_TOKEN=""
|
||||
CONF_ENROLLMENT_TOKEN=""
|
||||
CONF_NOUPDATE=""
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
line="${line%%#*}"
|
||||
@@ -74,7 +189,7 @@ if [ -f "${CONF_FILE}" ]; then
|
||||
CONF_SERVER="$(strip_conf_value "${line#PROBO_SERVER_URL=}")"
|
||||
;;
|
||||
PROBO_ENROLLMENT_TOKEN=*)
|
||||
CONF_TOKEN="$(strip_conf_value "${line#PROBO_ENROLLMENT_TOKEN=}")"
|
||||
CONF_ENROLLMENT_TOKEN="$(strip_conf_value "${line#PROBO_ENROLLMENT_TOKEN=}")"
|
||||
;;
|
||||
PROBO_NO_AUTO_UPDATE=*)
|
||||
CONF_NOUPDATE="$(strip_conf_value "${line#PROBO_NO_AUTO_UPDATE=}")"
|
||||
@@ -82,32 +197,38 @@ if [ -f "${CONF_FILE}" ]; then
|
||||
esac
|
||||
done < "${CONF_FILE}"
|
||||
|
||||
if [ -z "${CONF_SERVER}" ] || [ -z "${CONF_TOKEN}" ]; then
|
||||
if [ -z "${CONF_SERVER}" ] || [ -z "${CONF_ENROLLMENT_TOKEN}" ]; then
|
||||
echo "warning: ${CONF_FILE} is missing PROBO_SERVER_URL or PROBO_ENROLLMENT_TOKEN; skipping enrollment."
|
||||
else
|
||||
EXTRA_FLAGS=()
|
||||
# Build argv from the first element so "${INSTALL_ARGS[@]}"
|
||||
# is never empty — macOS /bin/bash 3.2 treats an unset empty
|
||||
# array as unbound under `set -u`.
|
||||
INSTALL_ARGS=(
|
||||
install
|
||||
--server "${CONF_SERVER}"
|
||||
--enrollment-token "${CONF_ENROLLMENT_TOKEN}"
|
||||
)
|
||||
case "${CONF_NOUPDATE}" in
|
||||
1|true|TRUE|yes|YES) EXTRA_FLAGS+=("--no-auto-update") ;;
|
||||
1|true|TRUE|yes|YES) INSTALL_ARGS+=(--no-auto-update) ;;
|
||||
esac
|
||||
|
||||
if "${BINARY}" install \
|
||||
--server "${CONF_SERVER}" \
|
||||
--enrollment-token "${CONF_TOKEN}" \
|
||||
"${EXTRA_FLAGS[@]}"; then
|
||||
if "${BINARY}" "${INSTALL_ARGS[@]}"; then
|
||||
echo "Device enrolled and service installed."
|
||||
else
|
||||
echo "warning: probo-agent install failed; the binary is in place and can be re-run by an admin."
|
||||
fi
|
||||
fi
|
||||
|
||||
# The token in the conf file is sensitive; clear it whatever
|
||||
# The enrollment token in the conf file is sensitive; clear it
|
||||
# the outcome so a successful install does not leave secrets
|
||||
# in /tmp.
|
||||
rm -f "${CONF_FILE}"
|
||||
else
|
||||
echo "No ${CONF_FILE} found; skipping automatic enrollment."
|
||||
echo "Finish setup with: sudo ${BINARY} install --server <URL> --enrollment-token <TOKEN>"
|
||||
echo "No ${CONF_FILE} found; enrollment can be completed from the menu bar icon."
|
||||
fi
|
||||
|
||||
register_tray_launchagent
|
||||
register_enrollment_url_scheme
|
||||
|
||||
echo "=== postinstall done ==="
|
||||
exit 0
|
||||
|
||||
9
cmd/probo-agent/installer/macos/scripts/preinstall
Normal file
9
cmd/probo-agent/installer/macos/scripts/preinstall
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# probo-agent macOS PKG preinstall script.
|
||||
#
|
||||
# Enrollment is handled by the menu bar helper after installation.
|
||||
# MDM may still pre-stage /tmp/probo-agent.conf for unattended
|
||||
# enrollment in postinstall.
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user