Files
wehub-resource-sync 869b84f27c
Build / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

158 lines
6.5 KiB
Markdown

# Release pipeline secrets
`.github/workflows/release.yml` requires the following GitHub Actions secrets
on `momenbasel/PureMac`. All must be set before the next tag push or the
notarize/staple steps will fail and ship a broken signature (the cause of #86).
The pipeline uses an **App Store Connect API key** for notarization (modern,
no rotation, scoped to one team) instead of the legacy
`APPLE_ID + app-specific-password` flow.
## Required secrets (6)
| Secret | Source | Notes |
|--------|--------|-------|
| `BUILD_CERTIFICATE_BASE64` | Filtered Developer ID Application `.p12` (cert + matching private key only), base64 | `base64 -i cert.p12 \| pbcopy` |
| `P12_PASSWORD` | Password set when exporting the `.p12` | Random — only you and CI need it |
| `KEYCHAIN_PASSWORD` | Random string | Only used to lock the runner's temp keychain — never leaves CI |
| `APP_STORE_CONNECT_KEY_ID` | The 10-char ID from the `.p8` filename (`AuthKey_XXXXXXXXXX.p8`) | e.g. `5G7R52L8RK` |
| `APP_STORE_CONNECT_ISSUER_ID` | UUID from <https://appstoreconnect.apple.com/access/integrations/api> | e.g. `5de3898a-cd31-4061-850f-ae17b389e46a` |
| `APP_STORE_CONNECT_PRIVATE_KEY` | Full contents of the `.p8` file (`-----BEGIN PRIVATE KEY-----` ... `-----END PRIVATE KEY-----`) | Paste raw, including the BEGIN/END lines |
## Optional secret (1)
| Secret | Source | Notes |
|--------|--------|-------|
| `HOMEBREW_TAP_TOKEN` | Fine-grained PAT with `Contents: read+write` on `momenbasel/homebrew-tap` | Without this the tap formula bump step is skipped (in-repo `homebrew/puremac.rb` still bumps via the default `GITHUB_TOKEN`) |
## Extracting your Developer ID cert as a filtered `.p12`
```bash
mkdir -p ~/Desktop/PureMac-secrets && cd ~/Desktop/PureMac-secrets
# 1. Export everything from login keychain
P12_PWD=$(openssl rand -base64 24)
echo "$P12_PWD" > P12_PASSWORD.txt
security export -k login.keychain-db -t identities -f pkcs12 -P "$P12_PWD" -o all.p12
# 2. Dump to PEM, isolate the Developer ID cert + matching private key
openssl pkcs12 -in all.p12 -passin "pass:$P12_PWD" -nodes -out all.pem
security find-certificate -c "Developer ID Application: Moamen Basel" -p login.keychain-db > devid.crt
# 3. Use python to split private keys, then match by modulus
python3 - <<'PY'
import re
content = open("all.pem").read()
for i, k in enumerate(re.findall(r"-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----", content, re.DOTALL), 1):
open(f"key_{i}.pem", "w").write(k + "\n")
PY
CERT_MOD=$(openssl x509 -in devid.crt -modulus -noout | shasum -a 256 | awk '{print $1}')
for k in key_*.pem; do
if [[ "$(openssl rsa -in "$k" -modulus -noout 2>/dev/null | shasum -a 256 | awk '{print $1}')" == "$CERT_MOD" ]]; then
cp "$k" devid.key && break
fi
done
# 4. Re-pack as a clean p12 with ONLY Developer ID + key
openssl pkcs12 -export \
-in devid.crt -inkey devid.key \
-name "Developer ID Application: Moamen Basel (H3WXHVTP97)" \
-out PureMac-DeveloperID.p12 \
-passout "pass:$P12_PWD" \
-macalg sha256 -keypbe AES-256-CBC -certpbe AES-256-CBC
# 5. Base64 for the GH secret + scrub intermediates
base64 -i PureMac-DeveloperID.p12 -o PureMac-DeveloperID.p12.b64
rm -P all.p12 all.pem devid.key key_*.pem
```
## Storing the ASC API key locally for `notarytool`
Already set up — confirmed via `xcrun notarytool history --keychain-profile AC_NOTARY`.
For reference:
```bash
xcrun notarytool store-credentials AC_NOTARY \
--key ~/.appstoreconnect/private_keys/AuthKey_5G7R52L8RK.p8 \
--key-id 5G7R52L8RK \
--issuer 5de3898a-cd31-4061-850f-ae17b389e46a
```
That keychain profile is consumed by `scripts/release-local.sh` for emergency
hotfixes. The CI workflow uses raw secrets instead (no keychain dependency on
the runner).
## Setting them all via gh CLI
```bash
# Fill in the 4 you control:
P12_PWD=$(cat ~/Desktop/PureMac-secrets/P12_PASSWORD.txt)
KC_PWD=$(cat ~/Desktop/PureMac-secrets/KEYCHAIN_PASSWORD.txt)
gh secret set BUILD_CERTIFICATE_BASE64 --repo momenbasel/PureMac < ~/Desktop/PureMac-secrets/PureMac-DeveloperID.p12.b64
gh secret set P12_PASSWORD --repo momenbasel/PureMac --body "$P12_PWD"
gh secret set KEYCHAIN_PASSWORD --repo momenbasel/PureMac --body "$KC_PWD"
gh secret set APP_STORE_CONNECT_KEY_ID --repo momenbasel/PureMac --body "5G7R52L8RK"
gh secret set APP_STORE_CONNECT_ISSUER_ID --repo momenbasel/PureMac --body "5de3898a-cd31-4061-850f-ae17b389e46a"
gh secret set APP_STORE_CONNECT_PRIVATE_KEY --repo momenbasel/PureMac < ~/.appstoreconnect/private_keys/AuthKey_5G7R52L8RK.p8
# Optional:
gh secret set HOMEBREW_TAP_TOKEN --repo momenbasel/PureMac --body "<your fine-grained PAT>"
# Verify:
gh secret list --repo momenbasel/PureMac
```
After upload:
```bash
rm -P ~/Desktop/PureMac-secrets/PureMac-DeveloperID.p12* \
~/Desktop/PureMac-secrets/P12_PASSWORD.txt \
~/Desktop/PureMac-secrets/KEYCHAIN_PASSWORD.txt
```
## Triggering a release
Dry run first (build + sign + notarize, no upload, no homebrew bump):
```bash
gh workflow run release.yml --repo momenbasel/PureMac -f version=2.2.0 -f dry_run=true
gh run watch --repo momenbasel/PureMac
```
Real release (after dry run is green):
```bash
git tag v2.2.0
git push origin v2.2.0
```
## What ships
| Artifact | Purpose |
|----------|---------|
| `PureMac-X.Y.Z.dmg` | Direct download link in release notes (signed + notarized + stapled) |
| `PureMac-X.Y.Z.zip` | Source for the homebrew cask (notarized + stapled `.app` inside) |
Both checksums land in `build/CHECKSUMS.md` and the GH release body.
## Troubleshooting #86 (`code or signature have been modified`)
Root causes that the pipeline guards against, that the previous manual
release path did not:
- Files modified after `codesign` (e.g. running `xcodegen` post-sign breaks the
signature). The pipeline runs `xcodegen` before `archive` and never edits the
bundle after the export step.
- Notarizing the `.app` but shipping a `.zip` made before the staple. The
pipeline staples the `.app` first, then re-zips it. Order matters — Gatekeeper
on first launch checks the stapled ticket on the `.app`, not the zip.
- Using `Apple Development` cert (default in `project.yml`) for distribution —
the pipeline overrides with `Developer ID Application` at archive time.
- Skipping `--options=runtime` (no hardened runtime → notary rejects). Pipeline
passes it via `OTHER_CODE_SIGN_FLAGS`.
- Universal-binary signing race where `lipo` is run after sign. The archive
step builds universal in one pass via `ARCHS="arm64 x86_64"` so the codesign
covers both slices atomically.