Simplify macOS pkg to one fat darwin.pkg
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
10
.github/workflows/release-probo-agent.yaml
vendored
10
.github/workflows/release-probo-agent.yaml
vendored
@@ -80,7 +80,7 @@ jobs:
|
|||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
name: "macos (universal pkg + darwin archives)"
|
name: "macos (darwin pkg + darwin archives)"
|
||||||
runs-on: "macos-26"
|
runs-on: "macos-26"
|
||||||
permissions:
|
permissions:
|
||||||
contents: "read"
|
contents: "read"
|
||||||
@@ -112,7 +112,7 @@ jobs:
|
|||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||||
security list-keychain -d user -s "$KEYCHAIN_PATH"
|
security list-keychain -d user -s "$KEYCHAIN_PATH"
|
||||||
rm certificate.p12
|
rm certificate.p12
|
||||||
- name: "Build CGO darwin binaries and universal pkg"
|
- name: "Build CGO darwin binaries and darwin pkg"
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "1"
|
CGO_ENABLED: "1"
|
||||||
CODESIGN_IDENTITY: "${{ secrets.CODESIGN_IDENTITY }}"
|
CODESIGN_IDENTITY: "${{ secrets.CODESIGN_IDENTITY }}"
|
||||||
@@ -151,18 +151,16 @@ jobs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
chmod +x cmd/probo-agent/installer/macos/build.sh \
|
chmod +x cmd/probo-agent/installer/macos/build.sh \
|
||||||
cmd/probo-agent/installer/macos/enroll-ui/build-app.sh \
|
|
||||||
cmd/probo-agent/installer/macos/scripts/preinstall \
|
cmd/probo-agent/installer/macos/scripts/preinstall \
|
||||||
cmd/probo-agent/installer/macos/scripts/postinstall
|
cmd/probo-agent/installer/macos/scripts/postinstall
|
||||||
|
|
||||||
cmd/probo-agent/installer/macos/build.sh \
|
cmd/probo-agent/installer/macos/build.sh \
|
||||||
--binary dist/probo-agent_universal \
|
--binary dist/probo-agent_universal \
|
||||||
--arch universal \
|
|
||||||
--version "${VERSION}" \
|
--version "${VERSION}" \
|
||||||
--output "archives/probo-agent_${VERSION}_darwin_universal.pkg"
|
--output "archives/probo-agent_${VERSION}_darwin.pkg"
|
||||||
- uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7
|
- uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7
|
||||||
with:
|
with:
|
||||||
name: "archive-darwin-universal"
|
name: "archive-darwin"
|
||||||
path: "archives/"
|
path: "archives/"
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,12 @@ ARCHIVE_PATH= $(RELEASE_DIR)/$(ARCHIVE_NAME)
|
|||||||
STAGING_DIR= $(CACHE_ROOT)/staging/$(AGENT_DIR)
|
STAGING_DIR= $(CACHE_ROOT)/staging/$(AGENT_DIR)
|
||||||
BUILD_BINARY= $(STAGING_DIR)/probo-agent
|
BUILD_BINARY= $(STAGING_DIR)/probo-agent
|
||||||
|
|
||||||
PKG= $(REPO_ROOT)/dist/probo-agent_$(VERSION)_darwin_$(ARCH_LABEL).pkg
|
# Fat binary required by installer/macos/build.sh (arm64 + x86_64).
|
||||||
|
FAT_BINARY= $(REPO_ROOT)/dist/probo-agent_fat
|
||||||
|
THIN_ARM64= $(REPO_ROOT)/dist/probo-agent_arm64
|
||||||
|
THIN_AMD64= $(REPO_ROOT)/dist/probo-agent_amd64
|
||||||
|
PKG= $(REPO_ROOT)/dist/probo-agent_$(VERSION)_darwin.pkg
|
||||||
|
AGENT_LDFLAGS= -ldflags "-X 'main.version=$(VERSION)'"
|
||||||
|
|
||||||
INSTALL_ARGS?= --skip-service --dir "$(STATE_DIR)"
|
INSTALL_ARGS?= --skip-service --dir "$(STATE_DIR)"
|
||||||
INSTALL_ENV= PROBO_AGENT_RELEASE_TAG="$(DEV_TAG)" \
|
INSTALL_ENV= PROBO_AGENT_RELEASE_TAG="$(DEV_TAG)" \
|
||||||
@@ -80,7 +85,7 @@ INSTALL_ENV= PROBO_AGENT_RELEASE_TAG="$(DEV_TAG)" \
|
|||||||
PROBO_ENROLLMENT_TOKEN="$(PROBO_ENROLLMENT_TOKEN)"
|
PROBO_ENROLLMENT_TOKEN="$(PROBO_ENROLLMENT_TOKEN)"
|
||||||
|
|
||||||
.PHONY: help all pkg install uninstall clean clean-build install-cli run
|
.PHONY: help all pkg install uninstall clean clean-build install-cli run
|
||||||
.PHONY: $(PROBO_AGENT_BIN)
|
.PHONY: $(PROBO_AGENT_BIN) $(FAT_BINARY)
|
||||||
|
|
||||||
all: help
|
all: help
|
||||||
|
|
||||||
@@ -122,8 +127,7 @@ clean: uninstall clean-build ## uninstall + wipe build caches
|
|||||||
|
|
||||||
clean-build: ## Wipe local build caches (no sudo)
|
clean-build: ## Wipe local build caches (no sudo)
|
||||||
$(RMRF) "$(STATE_DIR)" "$(CACHE_ROOT)" "$(ENROLL_UI_BUILD)"
|
$(RMRF) "$(STATE_DIR)" "$(CACHE_ROOT)" "$(ENROLL_UI_BUILD)"
|
||||||
@# Unquoted globs so the shell can expand dist artifacts.
|
-$(RMRF) "$(PKG)" "$(FAT_BINARY)" "$(THIN_ARM64)" "$(THIN_AMD64)"
|
||||||
-$(RMRF) $(REPO_ROOT)/dist/probo-agent_*.pkg
|
|
||||||
@# Native binary under /usr/local is owned by root after install; leave it
|
@# Native binary under /usr/local is owned by root after install; leave it
|
||||||
@# to `uninstall`. Never remove it here without sudo.
|
@# to `uninstall`. Never remove it here without sudo.
|
||||||
|
|
||||||
@@ -131,7 +135,20 @@ clean-build: ## Wipe local build caches (no sudo)
|
|||||||
|
|
||||||
pkg: $(PKG) ## Build signed .pkg into dist/
|
pkg: $(PKG) ## Build signed .pkg into dist/
|
||||||
|
|
||||||
$(PKG): $(PROBO_AGENT_BIN)
|
$(FAT_BINARY):
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
@$(MKDIR) "$(dir $(FAT_BINARY))"
|
||||||
|
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build $(AGENT_LDFLAGS) \
|
||||||
|
-o "$(THIN_ARM64)" "$(REPO_ROOT)/cmd/probo-agent"
|
||||||
|
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build $(AGENT_LDFLAGS) \
|
||||||
|
-o "$(THIN_AMD64)" "$(REPO_ROOT)/cmd/probo-agent"
|
||||||
|
lipo -create "$(THIN_ARM64)" "$(THIN_AMD64)" -output "$(FAT_BINARY)"
|
||||||
|
else
|
||||||
|
@echo 'error: fat binary target is macOS-only' >&2
|
||||||
|
@exit 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(PKG): $(FAT_BINARY)
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
@if [ -z "$(CODESIGN_IDENTITY)" ]; then \
|
@if [ -z "$(CODESIGN_IDENTITY)" ]; then \
|
||||||
echo 'error: CODESIGN_IDENTITY is required' >&2; exit 2; \
|
echo 'error: CODESIGN_IDENTITY is required' >&2; exit 2; \
|
||||||
@@ -139,9 +156,6 @@ ifeq ($(UNAME_S),Darwin)
|
|||||||
@if [ -z "$(APPLE_TEAM_ID)" ]; then \
|
@if [ -z "$(APPLE_TEAM_ID)" ]; then \
|
||||||
echo 'error: APPLE_TEAM_ID is required' >&2; exit 2; \
|
echo 'error: APPLE_TEAM_ID is required' >&2; exit 2; \
|
||||||
fi
|
fi
|
||||||
@if [ -z "$(BUILD_ARCH)" ]; then \
|
|
||||||
echo 'error: unsupported arch $(UNAME_M)' >&2; exit 2; \
|
|
||||||
fi
|
|
||||||
@$(MKDIR) "$(dir $(PKG))"
|
@$(MKDIR) "$(dir $(PKG))"
|
||||||
@CODESIGN_IDENTITY="$(CODESIGN_IDENTITY)" \
|
@CODESIGN_IDENTITY="$(CODESIGN_IDENTITY)" \
|
||||||
APPLE_TEAM_ID="$(APPLE_TEAM_ID)" \
|
APPLE_TEAM_ID="$(APPLE_TEAM_ID)" \
|
||||||
@@ -150,8 +164,7 @@ ifeq ($(UNAME_S),Darwin)
|
|||||||
APPLE_ID_PASSWORD="$(APPLE_ID_PASSWORD)" \
|
APPLE_ID_PASSWORD="$(APPLE_ID_PASSWORD)" \
|
||||||
NOTARYTOOL_KEYCHAIN_PROFILE="$(NOTARYTOOL_KEYCHAIN_PROFILE)" \
|
NOTARYTOOL_KEYCHAIN_PROFILE="$(NOTARYTOOL_KEYCHAIN_PROFILE)" \
|
||||||
sh "$(MACOS_BUILD_SCRIPT)" \
|
sh "$(MACOS_BUILD_SCRIPT)" \
|
||||||
--binary "$(PROBO_AGENT_BIN)" \
|
--binary "$(FAT_BINARY)" \
|
||||||
--arch "$(BUILD_ARCH)" \
|
|
||||||
--version "$(VERSION)" \
|
--version "$(VERSION)" \
|
||||||
--output "$(PKG)"
|
--output "$(PKG)"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -6,10 +6,9 @@
|
|||||||
Placeholders are substituted by build.sh:
|
Placeholders are substituted by build.sh:
|
||||||
|
|
||||||
@@VERSION@@ agent version, e.g. 0.1.0
|
@@VERSION@@ agent version, e.g. 0.1.0
|
||||||
@@PKG_ARCH@@ reserved (legacy; host filter uses @@HOST_ARCHS@@)
|
|
||||||
@@HOST_ARCHS@@ host arch filter used by Installer.app
|
|
||||||
(e.g. arm64 or arm64,x86_64 for universal)
|
|
||||||
@@IDENTIFIER@@ package identifier (default com.probo.agent)
|
@@IDENTIFIER@@ package identifier (default com.probo.agent)
|
||||||
|
|
||||||
|
hostArchitectures is fixed to arm64,x86_64 (fat binary installer).
|
||||||
-->
|
-->
|
||||||
<installer-gui-script minSpecVersion="2">
|
<installer-gui-script minSpecVersion="2">
|
||||||
<title>Probo Device Posture Agent @@VERSION@@</title>
|
<title>Probo Device Posture Agent @@VERSION@@</title>
|
||||||
@@ -22,7 +21,7 @@
|
|||||||
<options customize="never"
|
<options customize="never"
|
||||||
require-scripts="true"
|
require-scripts="true"
|
||||||
rootVolumeOnly="true"
|
rootVolumeOnly="true"
|
||||||
hostArchitectures="@@HOST_ARCHS@@"/>
|
hostArchitectures="arm64,x86_64"/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Refuse installation on macOS older than 11 (Big Sur). The agent
|
Refuse installation on macOS older than 11 (Big Sur). The agent
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Build a Probo device posture agent macOS installer (.pkg) from a
|
# Build a Probo device posture agent macOS installer (.pkg) from a
|
||||||
# pre-built `probo-agent` binary.
|
# pre-built fat `probo-agent` binary (arm64 + x86_64).
|
||||||
#
|
#
|
||||||
# Required arguments:
|
# Required arguments:
|
||||||
# --binary PATH Path to a compiled probo-agent binary.
|
# --binary PATH Path to a compiled probo-agent fat binary.
|
||||||
# --arch ARCH Target architecture: amd64, arm64, or universal.
|
|
||||||
# --version VER Agent version, e.g. 0.1.0. Defaults to the
|
# --version VER Agent version, e.g. 0.1.0. Defaults to the
|
||||||
# content of cmd/probo-agent/VERSION.
|
# content of cmd/probo-agent/VERSION.
|
||||||
# --output PATH Output .pkg path. Defaults to
|
# --output PATH Output .pkg path. Defaults to
|
||||||
# dist/probo-agent_${VER}_darwin_${ARCH}.pkg.
|
# dist/probo-agent_${VER}_darwin.pkg.
|
||||||
#
|
#
|
||||||
# Required environment variables:
|
# Required environment variables:
|
||||||
# CODESIGN_IDENTITY Developer ID Application identity. Signs the
|
# CODESIGN_IDENTITY Developer ID Application identity. Signs the
|
||||||
@@ -34,18 +33,21 @@
|
|||||||
#
|
#
|
||||||
# Must run on macOS: pkgbuild, productbuild, and swift build are
|
# Must run on macOS: pkgbuild, productbuild, and swift build are
|
||||||
# Apple-only tools. The build also compiles Probo Agent.app (the
|
# Apple-only tools. The build also compiles Probo Agent.app (the
|
||||||
# probo:// URL handler) from enroll-ui/.
|
# probo:// URL handler + privileged helper) from enroll-ui/.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../../.." && pwd)"
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../../.." && pwd)"
|
||||||
|
ENROLL_UI_DIR="${SCRIPT_DIR}/enroll-ui"
|
||||||
|
|
||||||
BINARY=""
|
BINARY=""
|
||||||
ARCH=""
|
|
||||||
VERSION=""
|
VERSION=""
|
||||||
OUTPUT=""
|
OUTPUT=""
|
||||||
IDENTIFIER="com.probo.agent"
|
IDENTIFIER="com.probo.agent"
|
||||||
|
APP_NAME="Probo Agent.app"
|
||||||
|
URL_HANDLER_NAME="probo-agent-url-handler"
|
||||||
|
HELPER_LABEL="com.probo.agent.helper"
|
||||||
CODESIGN_IDENTITY="${CODESIGN_IDENTITY:-}"
|
CODESIGN_IDENTITY="${CODESIGN_IDENTITY:-}"
|
||||||
INSTALLER_IDENTITY="${INSTALLER_IDENTITY:-}"
|
INSTALLER_IDENTITY="${INSTALLER_IDENTITY:-}"
|
||||||
APPLE_ID="${APPLE_ID:-}"
|
APPLE_ID="${APPLE_ID:-}"
|
||||||
@@ -60,7 +62,6 @@ usage() {
|
|||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--binary) BINARY="$2"; shift 2 ;;
|
--binary) BINARY="$2"; shift 2 ;;
|
||||||
--arch) ARCH="$2"; shift 2 ;;
|
|
||||||
--version) VERSION="$2"; shift 2 ;;
|
--version) VERSION="$2"; shift 2 ;;
|
||||||
--output) OUTPUT="$2"; shift 2 ;;
|
--output) OUTPUT="$2"; shift 2 ;;
|
||||||
--identifier) IDENTIFIER="$2"; shift 2 ;;
|
--identifier) IDENTIFIER="$2"; shift 2 ;;
|
||||||
@@ -73,35 +74,10 @@ if [ -z "${BINARY}" ] || [ ! -x "${BINARY}" ]; then
|
|||||||
echo "error: --binary <path-to-probo-agent> is required and must be executable" >&2
|
echo "error: --binary <path-to-probo-agent> is required and must be executable" >&2
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
case "${ARCH}" in
|
|
||||||
amd64)
|
|
||||||
PKG_ARCH="x86_64"
|
|
||||||
HOST_ARCHS="x86_64"
|
|
||||||
OUTPUT_ARCH="x86_64"
|
|
||||||
;;
|
|
||||||
arm64)
|
|
||||||
PKG_ARCH="arm64"
|
|
||||||
HOST_ARCHS="arm64"
|
|
||||||
OUTPUT_ARCH="arm64"
|
|
||||||
;;
|
|
||||||
universal)
|
|
||||||
PKG_ARCH="arm64"
|
|
||||||
HOST_ARCHS="arm64,x86_64"
|
|
||||||
OUTPUT_ARCH="universal"
|
|
||||||
;;
|
|
||||||
"")
|
|
||||||
echo "error: --arch (amd64|arm64|universal) is required" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "error: unsupported --arch '${ARCH}' (want amd64, arm64, or universal)" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# --arch sets hostArchitectures in Distribution.xml. Refuse a binary that
|
# Distribution.xml advertises hostArchitectures=arm64,x86_64. Refuse a
|
||||||
# lacks the advertised slice(s) so Installer cannot install on a CPU the
|
# binary that lacks either slice so Installer cannot install on a CPU
|
||||||
# agent cannot run on.
|
# the agent cannot run on.
|
||||||
if ! command -v lipo >/dev/null 2>&1; then
|
if ! command -v lipo >/dev/null 2>&1; then
|
||||||
echo "error: lipo is required to validate --binary architecture (run on macOS)" >&2
|
echo "error: lipo is required to validate --binary architecture (run on macOS)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -115,33 +91,17 @@ for arch_slice in ${BINARY_ARCHS}; do
|
|||||||
x86_64) has_x86_64=true ;;
|
x86_64) has_x86_64=true ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
case "${ARCH}" in
|
if [ "${has_arm64}" != true ] || [ "${has_x86_64}" != true ]; then
|
||||||
amd64)
|
echo "error: --binary must be a fat binary with arm64 and x86_64 slices (got: ${BINARY_ARCHS}); use lipo -create" >&2
|
||||||
if [ "${has_x86_64}" != true ]; then
|
exit 2
|
||||||
echo "error: --arch amd64 requires a binary with an x86_64 slice (got: ${BINARY_ARCHS})" >&2
|
fi
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
arm64)
|
|
||||||
if [ "${has_arm64}" != true ]; then
|
|
||||||
echo "error: --arch arm64 requires a binary with an arm64 slice (got: ${BINARY_ARCHS})" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
universal)
|
|
||||||
if [ "${has_arm64}" != true ] || [ "${has_x86_64}" != true ]; then
|
|
||||||
echo "error: --arch universal requires a fat binary with arm64 and x86_64 slices (got: ${BINARY_ARCHS}); use lipo -create" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${VERSION}" ]; then
|
if [ -z "${VERSION}" ]; then
|
||||||
VERSION="$(cat "${REPO_ROOT}/cmd/probo-agent/VERSION")"
|
VERSION="$(cat "${REPO_ROOT}/cmd/probo-agent/VERSION")"
|
||||||
fi
|
fi
|
||||||
if [ -z "${OUTPUT}" ]; then
|
if [ -z "${OUTPUT}" ]; then
|
||||||
mkdir -p "${REPO_ROOT}/dist"
|
mkdir -p "${REPO_ROOT}/dist"
|
||||||
OUTPUT="${REPO_ROOT}/dist/probo-agent_${VERSION}_darwin_${OUTPUT_ARCH}.pkg"
|
OUTPUT="${REPO_ROOT}/dist/probo-agent_${VERSION}_darwin.pkg"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v pkgbuild >/dev/null 2>&1 || ! command -v productbuild >/dev/null 2>&1; then
|
if ! command -v pkgbuild >/dev/null 2>&1 || ! command -v productbuild >/dev/null 2>&1; then
|
||||||
@@ -170,7 +130,7 @@ if [ "${notarize_enabled}" = true ] && [ -z "${INSTALLER_IDENTITY}" ]; then
|
|||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sign_macho() {
|
codesign_runtime() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
codesign \
|
codesign \
|
||||||
--force \
|
--force \
|
||||||
@@ -181,44 +141,130 @@ sign_macho() {
|
|||||||
codesign --verify --verbose=2 "${path}"
|
codesign --verify --verbose=2 "${path}"
|
||||||
}
|
}
|
||||||
|
|
||||||
sign_app_bundle() {
|
client_requirement() {
|
||||||
local app_path="$1"
|
printf 'anchor apple generic and identifier "com.probo.agent.url-handler" and certificate leaf[subject.OU] = "%s"' "${APPLE_TEAM_ID}"
|
||||||
local helper_path="${app_path}/Contents/Library/LaunchServices/com.probo.agent.helper"
|
|
||||||
if [ -x "${helper_path}" ]; then
|
|
||||||
codesign \
|
|
||||||
--force \
|
|
||||||
--options runtime \
|
|
||||||
--timestamp \
|
|
||||||
--sign "${CODESIGN_IDENTITY}" \
|
|
||||||
"${helper_path}"
|
|
||||||
codesign --verify --verbose=2 "${helper_path}"
|
|
||||||
fi
|
|
||||||
codesign \
|
|
||||||
--force \
|
|
||||||
--options runtime \
|
|
||||||
--timestamp \
|
|
||||||
--sign "${CODESIGN_IDENTITY}" \
|
|
||||||
"${app_path}/Contents/MacOS/probo-agent-url-handler"
|
|
||||||
codesign \
|
|
||||||
--force \
|
|
||||||
--options runtime \
|
|
||||||
--timestamp \
|
|
||||||
--sign "${CODESIGN_IDENTITY}" \
|
|
||||||
"${app_path}"
|
|
||||||
codesign --verify --verbose=2 "${app_path}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_notarytool_credentials() {
|
team_id_option() {
|
||||||
if [ -z "${APPLE_ID}" ] || [ -z "${APPLE_ID_PASSWORD}" ]; then
|
printf '"%s"' "${APPLE_TEAM_ID}"
|
||||||
echo "error: APPLE_ID and APPLE_ID_PASSWORD are required to store notarytool credentials" >&2
|
}
|
||||||
exit 2
|
|
||||||
|
# Build Probo Agent.app (URL handler + embedded privileged helper) into
|
||||||
|
# parent_dir. Signs nested Mach-Os then the .app bundle (bottom-up).
|
||||||
|
build_probo_agent_app() {
|
||||||
|
local parent_dir="$1"
|
||||||
|
local build_dir render_dir
|
||||||
|
local helper_info_plist helper_launchd_plist
|
||||||
|
local helper_binary url_handler_binary bin_dir
|
||||||
|
local app_root contents macos launch_services launch_daemons
|
||||||
|
local plist embedded_helper embedded_launchd
|
||||||
|
local helper_requirement
|
||||||
|
local -a helper_linker_flags swift_arch_args
|
||||||
|
|
||||||
|
build_dir="${STAGE}/enroll-ui-build"
|
||||||
|
render_dir="${build_dir}/rendered"
|
||||||
|
mkdir -p "${render_dir}"
|
||||||
|
|
||||||
|
swift_arch_args=(--arch arm64 --arch x86_64)
|
||||||
|
|
||||||
|
sed \
|
||||||
|
-e "s|@@VERSION@@|${VERSION}|g" \
|
||||||
|
"${ENROLL_UI_DIR}/Shared/HelperVersion.generated.swift.tmpl" \
|
||||||
|
> "${ENROLL_UI_DIR}/Shared/HelperVersion.generated.swift"
|
||||||
|
|
||||||
|
sed \
|
||||||
|
-e "s|@@TEAM_ID_OPTION@@|$(team_id_option)|g" \
|
||||||
|
"${ENROLL_UI_DIR}/Shared/SigningConstants.generated.swift.tmpl" \
|
||||||
|
> "${ENROLL_UI_DIR}/Shared/SigningConstants.generated.swift"
|
||||||
|
|
||||||
|
helper_info_plist="${render_dir}/helper-info.plist"
|
||||||
|
helper_launchd_plist="${render_dir}/helper-launchd.plist"
|
||||||
|
|
||||||
|
sed \
|
||||||
|
-e "s|@@VERSION@@|${VERSION}|g" \
|
||||||
|
-e "s|@@CLIENT_DESIGNATED_REQUIREMENT@@|$(client_requirement)|g" \
|
||||||
|
"${ENROLL_UI_DIR}/HelperTool/Info.plist.tmpl" > "${helper_info_plist}"
|
||||||
|
|
||||||
|
cp "${ENROLL_UI_DIR}/HelperTool/Launchd.plist.tmpl" "${helper_launchd_plist}"
|
||||||
|
|
||||||
|
helper_linker_flags=(
|
||||||
|
-Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist
|
||||||
|
-Xlinker "${helper_info_plist}"
|
||||||
|
-Xlinker -sectcreate -Xlinker __TEXT -Xlinker __launchd_plist
|
||||||
|
-Xlinker "${helper_launchd_plist}"
|
||||||
|
)
|
||||||
|
|
||||||
|
pushd "${ENROLL_UI_DIR}" >/dev/null
|
||||||
|
swift build -c release "${swift_arch_args[@]}" \
|
||||||
|
--scratch-path "${build_dir}/swift" \
|
||||||
|
--product "${HELPER_LABEL}" \
|
||||||
|
"${helper_linker_flags[@]}"
|
||||||
|
|
||||||
|
swift build -c release "${swift_arch_args[@]}" \
|
||||||
|
--scratch-path "${build_dir}/swift" \
|
||||||
|
--product "${URL_HANDLER_NAME}"
|
||||||
|
|
||||||
|
bin_dir="$(swift build -c release "${swift_arch_args[@]}" \
|
||||||
|
--scratch-path "${build_dir}/swift" --show-bin-path)"
|
||||||
|
helper_binary="${bin_dir}/${HELPER_LABEL}"
|
||||||
|
url_handler_binary="${bin_dir}/${URL_HANDLER_NAME}"
|
||||||
|
popd >/dev/null
|
||||||
|
|
||||||
|
if [ ! -x "${helper_binary}" ] || [ ! -x "${url_handler_binary}" ]; then
|
||||||
|
echo "error: expected release binaries were not produced" >&2
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Password appears on argv only for this short-lived store. Submits
|
|
||||||
# use --keychain-profile so concurrent processes cannot read it.
|
app_root="${parent_dir}/${APP_NAME}"
|
||||||
xcrun notarytool store-credentials "${NOTARYTOOL_KEYCHAIN_PROFILE}" \
|
contents="${app_root}/Contents"
|
||||||
--apple-id "${APPLE_ID}" \
|
macos="${contents}/MacOS"
|
||||||
--password "${APPLE_ID_PASSWORD}" \
|
launch_services="${contents}/Library/LaunchServices"
|
||||||
--team-id "${APPLE_TEAM_ID}"
|
launch_daemons="${contents}/Library/LaunchDaemons"
|
||||||
|
plist="${contents}/Info.plist"
|
||||||
|
embedded_helper="${launch_services}/${HELPER_LABEL}"
|
||||||
|
embedded_launchd="${launch_daemons}/${HELPER_LABEL}.plist"
|
||||||
|
|
||||||
|
rm -rf "${app_root}"
|
||||||
|
mkdir -p "${macos}" "${launch_services}" "${launch_daemons}"
|
||||||
|
|
||||||
|
install -m 0755 "${url_handler_binary}" "${macos}/${URL_HANDLER_NAME}"
|
||||||
|
install -m 0755 "${helper_binary}" "${embedded_helper}"
|
||||||
|
install -m 0644 "${helper_launchd_plist}" "${embedded_launchd}"
|
||||||
|
|
||||||
|
# Sign helper before writing Info.plist so SMPrivilegedExecutables
|
||||||
|
# can embed the helper's designated requirement.
|
||||||
|
codesign_runtime "${embedded_helper}"
|
||||||
|
# codesign prints "Executable=…" on stderr and either
|
||||||
|
# "# designated => …" (modern) or "designated => …" (older) on stdout.
|
||||||
|
helper_requirement="$(
|
||||||
|
codesign -d -r- "${embedded_helper}" 2>&1 \
|
||||||
|
| sed -n -e 's/^# designated => //p' -e 's/^designated => //p'
|
||||||
|
)"
|
||||||
|
if [ -z "${helper_requirement}" ]; then
|
||||||
|
echo "error: cannot extract designated requirement from signed helper" >&2
|
||||||
|
codesign -d -r- "${embedded_helper}" 2>&1 >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Helper designated requirement: ${helper_requirement}"
|
||||||
|
|
||||||
|
sed \
|
||||||
|
-e "s|@@VERSION@@|${VERSION}|g" \
|
||||||
|
-e "s|@@HELPER_DESIGNATED_REQUIREMENT@@|${helper_requirement}|g" \
|
||||||
|
"${ENROLL_UI_DIR}/Info.plist.tmpl" > "${plist}"
|
||||||
|
|
||||||
|
if ! plutil -lint "${plist}" >/dev/null; then
|
||||||
|
echo "error: rendered Info.plist failed plutil -lint" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -q '<string>probo</string>' "${plist}"; then
|
||||||
|
echo "error: Info.plist is missing probo URL scheme" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
codesign_runtime "${macos}/${URL_HANDLER_NAME}"
|
||||||
|
codesign_runtime "${app_root}"
|
||||||
|
|
||||||
|
echo "Built ${app_root}"
|
||||||
}
|
}
|
||||||
|
|
||||||
notarytool_submit() {
|
notarytool_submit() {
|
||||||
@@ -228,79 +274,6 @@ notarytool_submit() {
|
|||||||
--wait
|
--wait
|
||||||
}
|
}
|
||||||
|
|
||||||
notarize_and_staple_app() {
|
|
||||||
local app_path="$1"
|
|
||||||
local zip_path
|
|
||||||
|
|
||||||
zip_path="${STAGE}/probo-agent-app.zip"
|
|
||||||
ditto -c -k --keepParent "${app_path}" "${zip_path}"
|
|
||||||
notarytool_submit "${zip_path}"
|
|
||||||
rm -f "${zip_path}"
|
|
||||||
xcrun stapler staple "${app_path}"
|
|
||||||
}
|
|
||||||
|
|
||||||
notarize_and_staple_pkg() {
|
|
||||||
local pkg_path="$1"
|
|
||||||
notarytool_submit "${pkg_path}"
|
|
||||||
xcrun stapler staple "${pkg_path}"
|
|
||||||
}
|
|
||||||
|
|
||||||
STAGE="$(mktemp -d -t probo-agent-pkg)"
|
|
||||||
trap 'rm -rf "${STAGE}"' EXIT
|
|
||||||
|
|
||||||
PAYLOAD="${STAGE}/payload"
|
|
||||||
SCRIPTS="${STAGE}/scripts"
|
|
||||||
RESOURCES="${STAGE}/Resources"
|
|
||||||
mkdir -p "${PAYLOAD}/usr/local/bin" "${SCRIPTS}" "${RESOURCES}"
|
|
||||||
|
|
||||||
install -m 0755 "${BINARY}" "${PAYLOAD}/usr/local/bin/probo-agent"
|
|
||||||
sign_macho "${PAYLOAD}/usr/local/bin/probo-agent"
|
|
||||||
|
|
||||||
mkdir -p "${PAYLOAD}/Applications"
|
|
||||||
"${SCRIPT_DIR}/enroll-ui/build-app.sh" \
|
|
||||||
--arch "${ARCH}" \
|
|
||||||
--version "${VERSION}" \
|
|
||||||
--output "${PAYLOAD}/Applications"
|
|
||||||
|
|
||||||
APP_PATH="${PAYLOAD}/Applications/Probo Agent.app"
|
|
||||||
sign_app_bundle "${APP_PATH}"
|
|
||||||
|
|
||||||
if [ "${notarize_enabled}" = true ]; then
|
|
||||||
ensure_notarytool_credentials
|
|
||||||
echo "Notarizing Probo Agent.app before packaging..."
|
|
||||||
notarize_and_staple_app "${APP_PATH}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Avoid AppleDouble (._*) and resource-fork noise in the package.
|
|
||||||
export COPYFILE_DISABLE=1
|
|
||||||
|
|
||||||
# ditto --norsrc/--noextattr copies without resource forks / xattrs.
|
|
||||||
ditto --norsrc --noextattr "${SCRIPT_DIR}/scripts/preinstall" "${SCRIPTS}/preinstall"
|
|
||||||
ditto --norsrc --noextattr "${SCRIPT_DIR}/scripts/postinstall" "${SCRIPTS}/postinstall"
|
|
||||||
ditto --norsrc --noextattr \
|
|
||||||
"${REPO_ROOT}/pkg/deviceagent/tray/launchagent.plist.tmpl" \
|
|
||||||
"${SCRIPTS}/launchagent.plist.tmpl"
|
|
||||||
chmod 0755 "${SCRIPTS}/preinstall" "${SCRIPTS}/postinstall"
|
|
||||||
chmod 0644 "${SCRIPTS}/launchagent.plist.tmpl"
|
|
||||||
|
|
||||||
ditto --norsrc --noextattr "${SCRIPT_DIR}/Resources/welcome.html" "${RESOURCES}/welcome.html"
|
|
||||||
ditto --norsrc --noextattr "${SCRIPT_DIR}/Resources/conclusion.html" "${RESOURCES}/conclusion.html"
|
|
||||||
ditto --norsrc --noextattr "${REPO_ROOT}/LICENSE" "${RESOURCES}/license.txt"
|
|
||||||
|
|
||||||
# Strip any xattrs that tools may have reattached (codesign, etc.).
|
|
||||||
xattr -cr "${PAYLOAD}" "${SCRIPTS}" "${RESOURCES}" 2>/dev/null || true
|
|
||||||
find "${PAYLOAD}" "${SCRIPTS}" "${RESOURCES}" -name '._*' -delete 2>/dev/null || true
|
|
||||||
|
|
||||||
# Component package: payload + scripts only.
|
|
||||||
COMPONENT_PKG="${STAGE}/probo-agent-component.pkg"
|
|
||||||
pkgbuild \
|
|
||||||
--root "${PAYLOAD}" \
|
|
||||||
--scripts "${SCRIPTS}" \
|
|
||||||
--identifier "${IDENTIFIER}" \
|
|
||||||
--version "${VERSION}" \
|
|
||||||
--install-location "/" \
|
|
||||||
"${COMPONENT_PKG}"
|
|
||||||
|
|
||||||
# pkgbuild records protected com.apple.provenance xattrs as empty
|
# pkgbuild records protected com.apple.provenance xattrs as empty
|
||||||
# AppleDouble (._*) Bom entries. Rewrite the Bom with mkbom so the
|
# AppleDouble (._*) Bom entries. Rewrite the Bom with mkbom so the
|
||||||
# installer does not lay down those stubs next to real files.
|
# installer does not lay down those stubs next to real files.
|
||||||
@@ -331,14 +304,70 @@ rewrite_component_bom() {
|
|||||||
mv "${flat_pkg}" "${pkg}"
|
mv "${flat_pkg}" "${pkg}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d -t probo-agent-pkg)"
|
||||||
|
trap 'rm -rf "${STAGE}"' EXIT
|
||||||
|
|
||||||
|
PAYLOAD="${STAGE}/payload"
|
||||||
|
SCRIPTS="${STAGE}/scripts"
|
||||||
|
RESOURCES="${STAGE}/Resources"
|
||||||
|
mkdir -p "${PAYLOAD}/usr/local/bin" "${SCRIPTS}" "${RESOURCES}"
|
||||||
|
|
||||||
|
install -m 0755 "${BINARY}" "${PAYLOAD}/usr/local/bin/probo-agent"
|
||||||
|
codesign_runtime "${PAYLOAD}/usr/local/bin/probo-agent"
|
||||||
|
|
||||||
|
mkdir -p "${PAYLOAD}/Applications"
|
||||||
|
build_probo_agent_app "${PAYLOAD}/Applications"
|
||||||
|
APP_PATH="${PAYLOAD}/Applications/${APP_NAME}"
|
||||||
|
|
||||||
|
if [ "${notarize_enabled}" = true ]; then
|
||||||
|
# Password appears on argv only for this short-lived store. Submits
|
||||||
|
# use --keychain-profile so concurrent processes cannot read it.
|
||||||
|
xcrun notarytool store-credentials "${NOTARYTOOL_KEYCHAIN_PROFILE}" \
|
||||||
|
--apple-id "${APPLE_ID}" \
|
||||||
|
--password "${APPLE_ID_PASSWORD}" \
|
||||||
|
--team-id "${APPLE_TEAM_ID}"
|
||||||
|
echo "Notarizing Probo Agent.app before packaging..."
|
||||||
|
zip_path="${STAGE}/probo-agent-app.zip"
|
||||||
|
ditto -c -k --keepParent "${APP_PATH}" "${zip_path}"
|
||||||
|
notarytool_submit "${zip_path}"
|
||||||
|
rm -f "${zip_path}"
|
||||||
|
xcrun stapler staple "${APP_PATH}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# AppleDouble / xattr hygiene: COPYFILE_DISABLE + ditto --norsrc/--noextattr
|
||||||
|
# avoid forks on copy; xattr -cr / find '._*' strip anything codesign
|
||||||
|
# reattached; rewrite_component_bom drops provenance stubs from the Bom.
|
||||||
|
export COPYFILE_DISABLE=1
|
||||||
|
|
||||||
|
ditto --norsrc --noextattr "${SCRIPT_DIR}/scripts/preinstall" "${SCRIPTS}/preinstall"
|
||||||
|
ditto --norsrc --noextattr "${SCRIPT_DIR}/scripts/postinstall" "${SCRIPTS}/postinstall"
|
||||||
|
ditto --norsrc --noextattr \
|
||||||
|
"${REPO_ROOT}/pkg/deviceagent/tray/launchagent.plist.tmpl" \
|
||||||
|
"${SCRIPTS}/launchagent.plist.tmpl"
|
||||||
|
chmod 0755 "${SCRIPTS}/preinstall" "${SCRIPTS}/postinstall"
|
||||||
|
chmod 0644 "${SCRIPTS}/launchagent.plist.tmpl"
|
||||||
|
|
||||||
|
ditto --norsrc --noextattr "${SCRIPT_DIR}/Resources/welcome.html" "${RESOURCES}/welcome.html"
|
||||||
|
ditto --norsrc --noextattr "${SCRIPT_DIR}/Resources/conclusion.html" "${RESOURCES}/conclusion.html"
|
||||||
|
ditto --norsrc --noextattr "${REPO_ROOT}/LICENSE" "${RESOURCES}/license.txt"
|
||||||
|
|
||||||
|
xattr -cr "${PAYLOAD}" "${SCRIPTS}" "${RESOURCES}" 2>/dev/null || true
|
||||||
|
find "${PAYLOAD}" "${SCRIPTS}" "${RESOURCES}" -name '._*' -delete 2>/dev/null || true
|
||||||
|
|
||||||
|
COMPONENT_PKG="${STAGE}/probo-agent-component.pkg"
|
||||||
|
pkgbuild \
|
||||||
|
--root "${PAYLOAD}" \
|
||||||
|
--scripts "${SCRIPTS}" \
|
||||||
|
--identifier "${IDENTIFIER}" \
|
||||||
|
--version "${VERSION}" \
|
||||||
|
--install-location "/" \
|
||||||
|
"${COMPONENT_PKG}"
|
||||||
|
|
||||||
rewrite_component_bom "${COMPONENT_PKG}"
|
rewrite_component_bom "${COMPONENT_PKG}"
|
||||||
|
|
||||||
# Render Distribution.xml from its template.
|
|
||||||
DISTRIBUTION="${STAGE}/Distribution.xml"
|
DISTRIBUTION="${STAGE}/Distribution.xml"
|
||||||
sed \
|
sed \
|
||||||
-e "s|@@VERSION@@|${VERSION}|g" \
|
-e "s|@@VERSION@@|${VERSION}|g" \
|
||||||
-e "s|@@PKG_ARCH@@|${PKG_ARCH}|g" \
|
|
||||||
-e "s|@@HOST_ARCHS@@|${HOST_ARCHS}|g" \
|
|
||||||
-e "s|@@IDENTIFIER@@|${IDENTIFIER}|g" \
|
-e "s|@@IDENTIFIER@@|${IDENTIFIER}|g" \
|
||||||
"${SCRIPT_DIR}/Distribution.xml.tmpl" > "${DISTRIBUTION}"
|
"${SCRIPT_DIR}/Distribution.xml.tmpl" > "${DISTRIBUTION}"
|
||||||
|
|
||||||
@@ -358,7 +387,8 @@ productbuild "${PRODUCTBUILD_ARGS[@]}"
|
|||||||
|
|
||||||
if [ "${notarize_enabled}" = true ]; then
|
if [ "${notarize_enabled}" = true ]; then
|
||||||
echo "Notarizing ${OUTPUT}..."
|
echo "Notarizing ${OUTPUT}..."
|
||||||
notarize_and_staple_pkg "${OUTPUT}"
|
notarytool_submit "${OUTPUT}"
|
||||||
|
xcrun stapler staple "${OUTPUT}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Built ${OUTPUT}"
|
echo "Built ${OUTPUT}"
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
.build/
|
.build/
|
||||||
.swiftpm/
|
.swiftpm/
|
||||||
|
|
||||||
# Rendered by build-app.sh from *.tmpl (do not commit)
|
# Rendered by build.sh from *.tmpl (do not commit)
|
||||||
Shared/*.generated.swift
|
Shared/*.generated.swift
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<!--
|
<!--
|
||||||
Info.plist for the Probo Agent URL handler app bundle.
|
Info.plist for the Probo Agent URL handler app bundle.
|
||||||
|
|
||||||
Placeholders are substituted by build-app.sh:
|
Placeholders are substituted by build.sh:
|
||||||
|
|
||||||
@@VERSION@@ agent version, e.g. 0.1.0
|
@@VERSION@@ agent version, e.g. 0.1.0
|
||||||
@@HELPER_DESIGNATED_REQUIREMENT@@ codesign requirement for embedded helper
|
@@HELPER_DESIGNATED_REQUIREMENT@@ codesign requirement for embedded helper
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Generated by build-app.sh — do not edit.
|
// Generated by build.sh — do not edit.
|
||||||
enum ProboAgentHelperVersion {
|
enum ProboAgentHelperVersion {
|
||||||
static let value = "@@VERSION@@"
|
static let value = "@@VERSION@@"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Generated by build-app.sh — do not edit.
|
// Generated by build.sh — do not edit.
|
||||||
enum ProboAgentSigningConstants {
|
enum ProboAgentSigningConstants {
|
||||||
static let teamID: String? = @@TEAM_ID_OPTION@@
|
static let teamID: String? = @@TEAM_ID_OPTION@@
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,212 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Build Probo Agent.app — headless macOS app bundle with probo:// handler
|
|
||||||
# and embedded privileged helper (installed by PKG postinstall).
|
|
||||||
#
|
|
||||||
# Required arguments:
|
|
||||||
# --arch amd64, arm64, or universal
|
|
||||||
# --version Agent version, e.g. 0.1.0
|
|
||||||
# --output Parent directory; creates "Probo Agent.app" inside it
|
|
||||||
#
|
|
||||||
# Required environment variables:
|
|
||||||
# CODESIGN_IDENTITY Developer ID Application identity. Signs the
|
|
||||||
# embedded helper (for SMPrivilegedExecutables DR),
|
|
||||||
# URL handler, and app bundle.
|
|
||||||
# APPLE_TEAM_ID Apple Developer Team ID for SMAuthorizedClients.
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
ARCH=""
|
|
||||||
VERSION=""
|
|
||||||
OUTPUT=""
|
|
||||||
APP_NAME="Probo Agent.app"
|
|
||||||
URL_HANDLER_NAME="probo-agent-url-handler"
|
|
||||||
HELPER_LABEL="com.probo.agent.helper"
|
|
||||||
CODESIGN_IDENTITY="${CODESIGN_IDENTITY:-}"
|
|
||||||
APPLE_TEAM_ID="${APPLE_TEAM_ID:-}"
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
sed -ne '/^#/!q; s/^# \{0,1\}//; 2,$ p' < "$0"
|
|
||||||
}
|
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--arch) ARCH="$2"; shift 2 ;;
|
|
||||||
--version) VERSION="$2"; shift 2 ;;
|
|
||||||
--output) OUTPUT="$2"; shift 2 ;;
|
|
||||||
-h|--help) usage; exit 0 ;;
|
|
||||||
*) echo "unknown flag: $1" >&2; usage >&2; exit 2 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "${ARCH}" ]; then
|
|
||||||
echo "error: --arch (amd64|arm64|universal) is required" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
case "${ARCH}" in
|
|
||||||
amd64) SWIFT_ARCH_ARGS=(--arch x86_64) ;;
|
|
||||||
arm64) SWIFT_ARCH_ARGS=(--arch arm64) ;;
|
|
||||||
universal) SWIFT_ARCH_ARGS=(--arch arm64 --arch x86_64) ;;
|
|
||||||
*)
|
|
||||||
echo "error: unsupported --arch '${ARCH}' (want amd64, arm64, or universal)" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
if [ -z "${VERSION}" ]; then
|
|
||||||
echo "error: --version is required" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
if [ -z "${OUTPUT}" ]; then
|
|
||||||
echo "error: --output is required" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v swift >/dev/null 2>&1; then
|
|
||||||
echo "error: swift is required (run on macOS with Xcode or Swift toolchain)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "${CODESIGN_IDENTITY}" ]; then
|
|
||||||
echo "error: CODESIGN_IDENTITY is required (privileged helper must be signed)" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
if [ -z "${APPLE_TEAM_ID}" ]; then
|
|
||||||
echo "error: APPLE_TEAM_ID is required (SMAuthorizedClients team requirement)" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
BUILD_DIR="$(mktemp -d -t probo-agent-enroll-ui-build)"
|
|
||||||
RENDER_DIR="${BUILD_DIR}/rendered"
|
|
||||||
trap 'rm -rf "${BUILD_DIR}"' EXIT
|
|
||||||
mkdir -p "${RENDER_DIR}"
|
|
||||||
|
|
||||||
client_requirement() {
|
|
||||||
if [ -z "${APPLE_TEAM_ID}" ]; then
|
|
||||||
echo "error: APPLE_TEAM_ID is required (client designated requirement)" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
printf 'anchor apple generic and identifier "com.probo.agent.url-handler" and certificate leaf[subject.OU] = "%s"' "${APPLE_TEAM_ID}"
|
|
||||||
}
|
|
||||||
|
|
||||||
team_id_option() {
|
|
||||||
if [ -n "${APPLE_TEAM_ID}" ]; then
|
|
||||||
printf '"%s"' "${APPLE_TEAM_ID}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
printf 'nil'
|
|
||||||
}
|
|
||||||
|
|
||||||
sed \
|
|
||||||
-e "s|@@VERSION@@|${VERSION}|g" \
|
|
||||||
"${SCRIPT_DIR}/Shared/HelperVersion.generated.swift.tmpl" \
|
|
||||||
> "${SCRIPT_DIR}/Shared/HelperVersion.generated.swift"
|
|
||||||
|
|
||||||
sed \
|
|
||||||
-e "s|@@TEAM_ID_OPTION@@|$(team_id_option)|g" \
|
|
||||||
"${SCRIPT_DIR}/Shared/SigningConstants.generated.swift.tmpl" \
|
|
||||||
> "${SCRIPT_DIR}/Shared/SigningConstants.generated.swift"
|
|
||||||
|
|
||||||
HELPER_INFO_PLIST="${RENDER_DIR}/helper-info.plist"
|
|
||||||
HELPER_LAUNCHD_PLIST="${RENDER_DIR}/helper-launchd.plist"
|
|
||||||
|
|
||||||
sed \
|
|
||||||
-e "s|@@VERSION@@|${VERSION}|g" \
|
|
||||||
-e "s|@@CLIENT_DESIGNATED_REQUIREMENT@@|$(client_requirement)|g" \
|
|
||||||
"${SCRIPT_DIR}/HelperTool/Info.plist.tmpl" > "${HELPER_INFO_PLIST}"
|
|
||||||
|
|
||||||
cp "${SCRIPT_DIR}/HelperTool/Launchd.plist.tmpl" "${HELPER_LAUNCHD_PLIST}"
|
|
||||||
|
|
||||||
HELPER_LINKER_FLAGS=(
|
|
||||||
-Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist
|
|
||||||
-Xlinker "${HELPER_INFO_PLIST}"
|
|
||||||
-Xlinker -sectcreate -Xlinker __TEXT -Xlinker __launchd_plist
|
|
||||||
-Xlinker "${HELPER_LAUNCHD_PLIST}"
|
|
||||||
)
|
|
||||||
|
|
||||||
pushd "${SCRIPT_DIR}" >/dev/null
|
|
||||||
swift build -c release "${SWIFT_ARCH_ARGS[@]}" \
|
|
||||||
--scratch-path "${BUILD_DIR}/swift" \
|
|
||||||
--product "${HELPER_LABEL}" \
|
|
||||||
"${HELPER_LINKER_FLAGS[@]}"
|
|
||||||
|
|
||||||
swift build -c release "${SWIFT_ARCH_ARGS[@]}" \
|
|
||||||
--scratch-path "${BUILD_DIR}/swift" \
|
|
||||||
--product "${URL_HANDLER_NAME}"
|
|
||||||
|
|
||||||
BIN_DIR="$(swift build -c release "${SWIFT_ARCH_ARGS[@]}" \
|
|
||||||
--scratch-path "${BUILD_DIR}/swift" --show-bin-path)"
|
|
||||||
HELPER_BINARY="${BIN_DIR}/${HELPER_LABEL}"
|
|
||||||
URL_HANDLER_BINARY="${BIN_DIR}/${URL_HANDLER_NAME}"
|
|
||||||
popd >/dev/null
|
|
||||||
|
|
||||||
if [ ! -x "${HELPER_BINARY}" ] || [ ! -x "${URL_HANDLER_BINARY}" ]; then
|
|
||||||
echo "error: expected release binaries were not produced" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
APP_ROOT="${OUTPUT}/${APP_NAME}"
|
|
||||||
CONTENTS="${APP_ROOT}/Contents"
|
|
||||||
MACOS="${CONTENTS}/MacOS"
|
|
||||||
LAUNCH_SERVICES="${CONTENTS}/Library/LaunchServices"
|
|
||||||
LAUNCH_DAEMONS="${CONTENTS}/Library/LaunchDaemons"
|
|
||||||
PLIST="${CONTENTS}/Info.plist"
|
|
||||||
EMBEDDED_HELPER="${LAUNCH_SERVICES}/${HELPER_LABEL}"
|
|
||||||
EMBEDDED_LAUNCHD="${LAUNCH_DAEMONS}/${HELPER_LABEL}.plist"
|
|
||||||
|
|
||||||
rm -rf "${APP_ROOT}"
|
|
||||||
mkdir -p "${MACOS}" "${LAUNCH_SERVICES}" "${LAUNCH_DAEMONS}"
|
|
||||||
|
|
||||||
install -m 0755 "${URL_HANDLER_BINARY}" "${MACOS}/${URL_HANDLER_NAME}"
|
|
||||||
install -m 0755 "${HELPER_BINARY}" "${EMBEDDED_HELPER}"
|
|
||||||
install -m 0644 "${HELPER_LAUNCHD_PLIST}" "${EMBEDDED_LAUNCHD}"
|
|
||||||
|
|
||||||
codesign \
|
|
||||||
--force \
|
|
||||||
--options runtime \
|
|
||||||
--timestamp \
|
|
||||||
--sign "${CODESIGN_IDENTITY}" \
|
|
||||||
"${EMBEDDED_HELPER}"
|
|
||||||
codesign --verify --verbose=2 "${EMBEDDED_HELPER}"
|
|
||||||
# codesign prints "Executable=…" on stderr and either
|
|
||||||
# "# designated => …" (modern) or "designated => …" (older) on stdout.
|
|
||||||
HELPER_REQUIREMENT="$(
|
|
||||||
codesign -d -r- "${EMBEDDED_HELPER}" 2>&1 \
|
|
||||||
| sed -n -e 's/^# designated => //p' -e 's/^designated => //p'
|
|
||||||
)"
|
|
||||||
if [ -z "${HELPER_REQUIREMENT}" ]; then
|
|
||||||
echo "error: cannot extract designated requirement from signed helper" >&2
|
|
||||||
codesign -d -r- "${EMBEDDED_HELPER}" 2>&1 >&2 || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Helper designated requirement: ${HELPER_REQUIREMENT}"
|
|
||||||
|
|
||||||
sed \
|
|
||||||
-e "s|@@VERSION@@|${VERSION}|g" \
|
|
||||||
-e "s|@@HELPER_DESIGNATED_REQUIREMENT@@|${HELPER_REQUIREMENT}|g" \
|
|
||||||
"${SCRIPT_DIR}/Info.plist.tmpl" > "${PLIST}"
|
|
||||||
|
|
||||||
if ! plutil -lint "${PLIST}" >/dev/null; then
|
|
||||||
echo "error: rendered Info.plist failed plutil -lint" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if ! grep -q '<string>probo</string>' "${PLIST}"; then
|
|
||||||
echo "error: Info.plist is missing probo URL scheme" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
codesign \
|
|
||||||
--force \
|
|
||||||
--options runtime \
|
|
||||||
--timestamp \
|
|
||||||
--sign "${CODESIGN_IDENTITY}" \
|
|
||||||
"${MACOS}/${URL_HANDLER_NAME}"
|
|
||||||
codesign \
|
|
||||||
--force \
|
|
||||||
--options runtime \
|
|
||||||
--timestamp \
|
|
||||||
--sign "${CODESIGN_IDENTITY}" \
|
|
||||||
"${APP_ROOT}"
|
|
||||||
codesign --verify --verbose=2 "${APP_ROOT}"
|
|
||||||
|
|
||||||
echo "Built ${APP_ROOT}"
|
|
||||||
@@ -36,8 +36,8 @@ and FreeBSD builds stay pure Go (no tray).
|
|||||||
|
|
||||||
CI builds binaries for linux, windows, and freebsd (amd64/arm64) on
|
CI builds binaries for linux, windows, and freebsd (amd64/arm64) on
|
||||||
Linux runners, and builds **CGO-enabled** darwin archives plus a
|
Linux runners, and builds **CGO-enabled** darwin archives plus a
|
||||||
signed/notarized **universal** `.pkg` on a macOS runner. The GitHub
|
signed/notarized fat `.pkg` on a macOS runner. The GitHub
|
||||||
Release includes those archives, `probo-agent_*_darwin_universal.pkg`,
|
Release includes those archives, `probo-agent_*_darwin.pkg`,
|
||||||
`install.sh`, signed checksums, SBOM, and build attestations. The agent
|
`install.sh`, signed checksums, SBOM, and build attestations. The agent
|
||||||
auto-update path downloads the matching archive plus `checksums.txt` and
|
auto-update path downloads the matching archive plus `checksums.txt` and
|
||||||
verifies the cosign bundle before installing.
|
verifies the cosign bundle before installing.
|
||||||
@@ -53,15 +53,16 @@ and the `.pkg` are built on macOS with `CGO_ENABLED=1`.
|
|||||||
|
|
||||||
Release and local builds use
|
Release and local builds use
|
||||||
`cmd/probo-agent/installer/macos/build.sh` (requires macOS, a
|
`cmd/probo-agent/installer/macos/build.sh` (requires macOS, a
|
||||||
pre-built binary — preferably universal via `lipo` — and the Swift
|
pre-built fat binary via `lipo`, and the Swift toolchain).
|
||||||
toolchain). **Signing is mandatory:** `CODESIGN_IDENTITY` and
|
**Signing is mandatory:** `CODESIGN_IDENTITY` and `APPLE_TEAM_ID`
|
||||||
`APPLE_TEAM_ID` must be set. The script compiles `Probo Agent.app`
|
must be set. The script compiles `Probo Agent.app` (the headless
|
||||||
(the headless `probo://` URL handler + privileged helper) from
|
`probo://` URL handler + privileged helper) from
|
||||||
`cmd/probo-agent/installer/macos/enroll-ui/`, signs the binary and
|
`cmd/probo-agent/installer/macos/enroll-ui/`, signs nested Mach-Os
|
||||||
app, optionally signs the product with `INSTALLER_IDENTITY`, and
|
then the app bundle, optionally signs the product with
|
||||||
notarizes/staples when `APPLE_ID` and `APPLE_ID_PASSWORD` are set
|
`INSTALLER_IDENTITY`, and notarizes/staples when `APPLE_ID` and
|
||||||
(password is stored into a keychain profile; submits use
|
`APPLE_ID_PASSWORD` are set (password is stored into a keychain
|
||||||
`--keychain-profile` so the secret is not on `notarytool submit` argv).
|
profile; submits use `--keychain-profile` so the secret is not on
|
||||||
|
`notarytool submit` argv).
|
||||||
|
|
||||||
There is no unsigned PKG path and no osascript elevation fallback.
|
There is no unsigned PKG path and no osascript elevation fallback.
|
||||||
Local testing of browser enrollment requires a Developer ID–signed
|
Local testing of browser enrollment requires a Developer ID–signed
|
||||||
@@ -78,7 +79,6 @@ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -o dist/probo-agent_amd64 ./cmd/
|
|||||||
lipo -create dist/probo-agent_arm64 dist/probo-agent_amd64 -output dist/probo-agent_universal
|
lipo -create dist/probo-agent_arm64 dist/probo-agent_amd64 -output dist/probo-agent_universal
|
||||||
cmd/probo-agent/installer/macos/build.sh \
|
cmd/probo-agent/installer/macos/build.sh \
|
||||||
--binary dist/probo-agent_universal \
|
--binary dist/probo-agent_universal \
|
||||||
--arch universal \
|
|
||||||
--version "$(cat cmd/probo-agent/VERSION)"
|
--version "$(cat cmd/probo-agent/VERSION)"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ Manual QA checklist (macOS PKG):
|
|||||||
4. `sudo make -C cmd/probo-agent uninstall` removes daemon, tray, helper, app, and state.
|
4. `sudo make -C cmd/probo-agent uninstall` removes daemon, tray, helper, app, and state.
|
||||||
5. MDM `/tmp/probo-agent.conf` postinstall still enrolls without a browser prompt.
|
5. MDM `/tmp/probo-agent.conf` postinstall still enrolls without a browser prompt.
|
||||||
6. Notarized PKG passes Gatekeeper; `codesign --verify --deep` succeeds on the app bundle.
|
6. Notarized PKG passes Gatekeeper; `codesign --verify --deep` succeeds on the app bundle.
|
||||||
7. Unsigned `build-app.sh` / `build.sh` exits with an error requiring `CODESIGN_IDENTITY`.
|
7. Unsigned `build.sh` exits with an error requiring `CODESIGN_IDENTITY`.
|
||||||
|
|
||||||
### Apple signing secrets (GitHub)
|
### Apple signing secrets (GitHub)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user