Skip to content

feat: macOS code signing, notarization, and Gatekeeper-clean release artifacts#4

Merged
eudicy merged 16 commits into
small-coding-dojo:mainfrom
eudicy:main
May 4, 2026
Merged

feat: macOS code signing, notarization, and Gatekeeper-clean release artifacts#4
eudicy merged 16 commits into
small-coding-dojo:mainfrom
eudicy:main

Conversation

@eudicy

@eudicy eudicy commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • macOS release artifacts now signed (Developer ID) and notarized — zero Gatekeeper dialogs on first run
  • Signing steps gate on env.APPLE_CERTIFICATE != '': branch builds produce unsigned binary (no failure); tag push asserts secrets present and
    exits 1 with clear error if missing
  • Fixes zip structure: archive extracts to risus-macos-arm64/ not dist/
  • Docs updated: AGENTS.md secrets table, PLAYER.md, quickstart guides

What changed

  • .github/workflows/release.yml — codesign, notarytool, post-notarization verify; conditional on APPLE_CERTIFICATE; zip staged via /tmp;
    assert step on tag push
  • build/entitlements.plist — Hardened Runtime entitlements for PyInstaller Python runtime
  • AGENTS.md — Signing Setup subsection with 6 required secrets and how to obtain each
  • PLAYER.md / quickstart docs — remove outdated Gatekeeper bypass instructions

Required secrets

Secret Purpose
APPLE_CERTIFICATE Base64-encoded Developer ID .p12
APPLE_CERTIFICATE_PASSWORD .p12 export password
APPLE_SIGNING_IDENTITY Developer ID Application: Name (TeamID)
APPLE_API_KEY_ID App Store Connect API key ID
APPLE_API_ISSUER_ID App Store Connect issuer UUID
APPLE_API_KEY_CONTENT Base64-encoded .p8 key file

See specs/005-macos-signed-release/quickstart.md for setup steps.

Test plan

  • Tag push with secrets → risus-macos-arm64.zip, codesign --verify --deep --strict passes
  • Branch push without secrets → unsigned binary built, signing skipped, no failure
  • Tag push without secrets → fails at "Assert signing secrets" with clear error
  • Unzip artifact → extracts to risus-macos-arm64/, not dist/

galadriel and others added 16 commits May 3, 2026 21:18
Specification, implementation plan, research decisions, data model,
contracts, quickstart guide, and task list for notarized macOS release.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude Code <noreply@anthropic.com>
checkout v6, setup-python v6, upload-artifact v7,
download-artifact v8, action-gh-release v3,
import-codesign-certs v7.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude Code <noreply@anthropic.com>
- Add build/entitlements.plist with 4 Hardened Runtime entitlements
  required for PyInstaller binaries under codesign --options=runtime
- Add import-codesign-certs, codesign, ditto, notarytool, and spctl
  verification steps to macOS job in release.yml
- Split checksum step into macOS (hashes .zip) and Linux variants
- macOS artifact is now risus-macos-arm64.zip (signed + notarized)
- Add guaranteed API key cleanup step with if: always()
- Update build.yml to run CI on 005-macos-signed-release branch
- Document 6 Apple signing secrets and pre-tag verification in AGENTS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
APPLE_TEAM_ID alone is not a valid codesign identity string.
Replaced with APPLE_SIGNING_IDENTITY containing the full
"Developer ID Application: Name (TeamID)" format.
spctl --assess --type execute is for .app bundles, not plain
Mach-O executables. notarytool submit --wait already confirms
Apple acceptance. codesign --verify is sufficient.
Field was defined but never referenced in any step.
- PLAYER.md: binary is now notarized; remove Gatekeeper bypass steps
- quickstart (005): correct cert export method (Xcode not Keychain
  Access), add APPLE_SIGNING_IDENTITY secret, note Request Access
  gate in App Store Connect, warn to delete .p12 after upload,
  add version bump step, remove spctl verify (fails for CLI binaries)
- AGENTS.md: replace APPLE_TEAM_ID with APPLE_SIGNING_IDENTITY in
  signing secrets table
- quickstart (003): remove stale not-notarized macOS note

Closes risus-cli-255
- spec.md: FR-005 now allows unsigned builds on non-tag pushes; FR-003
  and SC-003 replace spctl with codesign --verify; FR-004 references
  APPLE_SIGNING_IDENTITY instead of APPLE_TEAM_ID
- plan.md: add Notes section; fix constitution check (codesign not spctl)
- tasks.md: fix T004/T009/T010 stale APPLE_TEAM_ID/spctl references;
  add T015 for conditional signing gate
- quickstart.md: add Conditional signing section
Extracting risus-macos-arm64.zip currently produces dist/ — a CI
build path leaking into the release artifact. Spec, plan, and tasks
for fixing the zip to extract into a risus-macos-arm64/ directory.
- Gate all macOS signing steps on env.APPLE_CERTIFICATE != ''
- Add Assert signing secrets step (fails loudly on tag push without secrets)
- Unsigned fallback path for branch/PR builds without secrets
- Fix zip structure: stage binary in /tmp/risus-macos-arm64/ before ditto
  so archive extracts to risus-macos-arm64/ not dist/
- Consolidate duplicate checksum and upload steps (non-signed path)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@eudicy eudicy merged commit dc66171 into small-coding-dojo:main May 4, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant