Require notarization for GUI releases

Shipping an unnotarized build left the user with an app Gatekeeper
rejects, and the skill previously suggested quarantine workarounds that
hid the problem. Notarization is now a hard gate, with the account-level
agreement failure documented so it is diagnosed instead of bypassed.

💘 Generated with Crush

Assisted-by: Crush:kimi-k3
This commit is contained in:
2026-10-08 10:04:08 +08:00
parent 30290801fe
commit e141187460
+36 -6
View File
@@ -19,8 +19,14 @@ The reference implementations to copy from are `~/Git/kapture` and
`gitea.tzgyn.dev`. SSH: `git@gitea.tzgyn.dev:TZGyn/<repo>.git`.
- **Always print the upgrade script** (the `brew` commands below) in the final
answer after any release or version bump. The user wants it every time.
- **Every GUI release must be notarized and stapled.** An unnotarized build is
not a release: never publish one, and never paper over Gatekeeper with
`xattr -dr com.apple.quarantine` or `brew install --cask --no-quarantine`.
If notarization fails, fix the cause (see
[Troubleshooting](#notarization-troubleshooting)) before publishing.
- Never commit build artifacts. `.gitignore` covers `.build/`, `*.app/`,
`dist/`, `.swiftpm/`, `xcuserdata/`, `DerivedData/`, `.DS_Store`.
(`go build ./...` drops a binary next to the source — ignore it explicitly.)
- Never commit secrets (`.p8`, API keys) or push to a remote unless asked.
- Commit messages: concise, why-focused, first line < 72 chars; add a body only
when it helps a future reader.
@@ -124,7 +130,9 @@ Two scripts per repo: `build-app.sh <version>` and `release.sh <version>`.
4. **Bump the cask** (`version` + `sha256`), push the tap, pull Homebrew's
clone, `brew audit`. Verify by downloading the asset back and comparing the
sha256, and `spctl -a -vv <App>.app` → `accepted` /
`source=Notarized Developer ID`.
`source=Notarized Developer ID`. If `spctl` does not say
`source=Notarized Developer ID`, stop and fix notarization first — do not
publish the release or the cask.
5. **Print the upgrade script**:
@@ -162,11 +170,33 @@ xcrun stapler staple <App>.app && xcrun stapler validate <App>.app
spctl -a -vv <App>.app # want: accepted, source=Notarized Developer ID
```
Wire `release.sh` so it notarizes when `NOTARY_*` env vars are set and otherwise
skips gracefully (build + zip + sha only). An unnotarized app is still signed but
Gatekeeper warns on other machines; the user can bypass with
`xattr -dr com.apple.quarantine <App>.app` or install with
`brew install --cask --no-quarantine <name>`.
`release.sh` **must** notarize. Treat any skip or failure as a failed release:
do not publish the tag, the release, or the cask, and do not suggest
quarantine workarounds (`xattr -dr com.apple.quarantine`,
`brew install --cask --no-quarantine`) — those hand the user a build macOS
rejects. Only publish once `spctl -a -vv <App>.app` reports `accepted` with
`source=Notarized Developer ID`.
### Notarization troubleshooting
| Symptom | Cause / fix |
| --- | --- |
| `HTTP status code: 403. A required agreement is missing or has expired.` | The **Apple Developer Program License Agreement** for the team is unsigned or expired. The Account Holder must accept it in App Store Connect → **Business → Agreements** *and* at `developer.apple.com/account` → **Agreements**. Account-wide: every call fails, including read-only `notarytool history`, until it is accepted; allow a few minutes to propagate. |
| The same 403 *after* accepting | The wrong agreement was accepted (App Store Connect "Paid Apps" instead of the Program License Agreement) or it was accepted under a different team's account. |
| `401 Authentication credentials are incorrect` | Wrong key id / issuer, or the key belongs to another team. |
Confirm the team before blaming the credentials — do not "fix" this by
switching teams or re-signing:
```bash
security find-identity -v -p codesigning # the Developer ID team must match the key's team
spctl -a -vv /Applications/<App>.app # an already-notarized app proves the team/key pair works
xcrun notarytool history --key ... --key-id ... --issuer ... # 403 here = account agreement, not the app
```
`notarytool history` failing the same way as the submit is the tell: the
problem is the account, not the bundle, so there is nothing to change in the
app or the scripts.
## 5. Mobile