Files
can1357--oh-my-pi/scripts/ci-macos-upload-secrets.sh
2026-07-13 12:22:59 +08:00

116 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Upload the macOS signing/notarization secrets to GitHub Actions WITHOUT ever
# printing a secret value. Every value is read from a file on disk and piped to
# `gh secret set` over stdin, so nothing lands in argv, the shell history, or a
# terminal transcript.
#
# Prepare a directory (default ~/omp-signing) containing:
# *.p12 Developer ID Application identity exported from Keychain
# Access (right-click identity -> Export -> .p12).
# p12-password.txt the password you set on that .p12 export.
# AuthKey_<KEYID>.p8 App Store Connect API key (download-once from the web).
# issuer-id.txt App Store Connect API issuer id (UUID).
# key-id.txt optional; otherwise the <KEYID> is read from the .p8
# filename.
#
# Usage:
# scripts/ci-macos-upload-secrets.sh [dir] [--dry-run]
# OMP_REPO=owner/repo scripts/ci-macos-upload-secrets.sh ~/omp-signing
set -euo pipefail
DIR=""
DRY_RUN=0
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=1 ;;
*) DIR="$arg" ;;
esac
done
DIR="${DIR:-${OMP_SIGNING_DIR:-$HOME/omp-signing}}"
REPO="${OMP_REPO:-can1357/oh-my-pi}"
die() {
echo "ci-macos-upload-secrets: $1" >&2
exit 1
}
[[ -d "$DIR" ]] || die "directory not found: $DIR"
find_one() {
# Echo the single file in $DIR matching the glob, or fail.
local pattern="$1" matches=()
while IFS= read -r f; do matches+=("$f"); done < <(find "$DIR" -maxdepth 1 -type f -name "$pattern" | sort)
((${#matches[@]} == 1)) || die "expected exactly one '$pattern' in $DIR, found ${#matches[@]}"
printf '%s' "${matches[0]}"
}
read_file_value() {
# Trim a single trailing newline; reject empty.
local path="$1" name="$2" value
[[ -f "$path" ]] || die "missing $name file: $path"
value="$(cat "$path")"
[[ -n "$value" ]] || die "$name file is empty: $path"
printf '%s' "$value"
}
P12="$(find_one '*.p12')"
P8="$(find_one '*.p8')"
PW="$(read_file_value "$DIR/p12-password.txt" "p12-password.txt")"
ISSUER="$(read_file_value "$DIR/issuer-id.txt" "issuer-id.txt")"
if [[ -f "$DIR/key-id.txt" ]]; then
KEYID="$(read_file_value "$DIR/key-id.txt" "key-id.txt")"
else
# AuthKey_ABCDE12345.p8 -> ABCDE12345
KEYID="$(basename "$P8" .p8)"
KEYID="${KEYID#AuthKey_}"
[[ -n "$KEYID" && "$KEYID" != "$(basename "$P8" .p8)" ]] \
|| die "could not derive key id from '$(basename "$P8")'; add key-id.txt"
fi
# Validate the .p12 + password the same way CI consumes it — `security import`
# into a throwaway keychain — and confirm a Developer ID identity is inside, so a
# typo or wrong cert fails here instead of in CI. (We avoid `openssl pkcs12`:
# OpenSSL 3.x can't read the legacy RC2-40-CBC algorithm Keychain Access still
# uses, which `security import` handles fine.)
validate_p12=$(
kc="$(mktemp -d)/validate.keychain-db"
kp="$(openssl rand -hex 16)"
security create-keychain -p "$kp" "$kc" >/dev/null 2>&1
security unlock-keychain -p "$kp" "$kc" >/dev/null 2>&1
if security import "$P12" -P "$PW" -k "$kc" -T /usr/bin/codesign >/dev/null 2>&1 \
&& security find-identity -v -p codesigning "$kc" 2>/dev/null | grep -q "Developer ID Application"; then
echo ok
fi
security delete-keychain "$kc" >/dev/null 2>&1 || true
)
[[ "$validate_p12" == ok ]] \
|| die "the .p12 did not import with the password in p12-password.txt, or holds no Developer ID Application identity"
grep -q "BEGIN PRIVATE KEY" "$P8" \
|| die "the .p8 does not look like a PEM private key"
echo "ci-macos-upload-secrets: repo=$REPO"
echo " cert : $(basename "$P12")"
echo " key : $(basename "$P8") (key id $KEYID)"
echo " -> APPLE_CERTIFICATE_P12, APPLE_CERTIFICATE_PASSWORD, APPLE_API_KEY_ID, APPLE_API_ISSUER_ID, APPLE_API_KEY"
if ((DRY_RUN)); then
echo "ci-macos-upload-secrets: --dry-run, not uploading"
exit 0
fi
set_secret_stdin() {
# $1 = secret name; value piped on stdin. Never echoes the value.
gh secret set "$1" --repo "$REPO"
}
base64 <"$P12" | tr -d '\n' | set_secret_stdin APPLE_CERTIFICATE_P12
printf '%s' "$PW" | set_secret_stdin APPLE_CERTIFICATE_PASSWORD
printf '%s' "$KEYID" | set_secret_stdin APPLE_API_KEY_ID
printf '%s' "$ISSUER" | set_secret_stdin APPLE_API_ISSUER_ID
base64 <"$P8" | tr -d '\n' | set_secret_stdin APPLE_API_KEY
echo "ci-macos-upload-secrets: done. Verify with: gh secret list --repo $REPO"