chore(release): pin signing to self-hosted Mac runner, drop repo secrets#6
Merged
Conversation
The repo is public; storing the Apple Developer ID cert, notary app-specific password, or a homebrew-tap PAT in GitHub Actions secrets puts them one compromised workflow_run away from leaking. Move the signing-sensitive jobs (macos-pkg, bump-tap) to a self-hosted Mac runner that holds the cert in its login keychain and the PAT in its .env. The github-hosted jobs (image build + cosign, tarball-hash, publish-release) never touch the sensitive material. The runner expects three values in its .env, none of which are repo secrets: - TIMENEST_SIGN_IDENTITY (Developer ID Installer identity string) - TIMENEST_NOTARY_PROFILE (notarytool keychain profile name) - TIMENEST_TAP_TOKEN (PAT scoped to momenbasel/homebrew-timenest) cosign keylessly signs every image via the workflow's OIDC token, so the docker pipeline needs no secret either. docs/release-pipeline.md updated end-to-end: bootstrap procedure, runner status checks, release ritual, verification commands.
There was a problem hiding this comment.
Pull request overview
Moves macOS package signing/notarization and Homebrew tap bumping off GitHub-hosted runners and onto a labeled self-hosted macOS runner, eliminating the need to store Apple signing/notary credentials and the tap PAT in GitHub Actions secrets.
Changes:
- Update
release.ymlto runmacos-pkgandbump-tapon a self-hosted macOS runner and use keychain-based signing/notary credentials. - Remove reliance on repository secrets for Apple cert import/keychain setup and tap token usage.
- Rewrite
docs/release-pipeline.mdto document the new self-hosted bootstrap and verification steps.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
docs/release-pipeline.md |
Updates release/bootstrap documentation to reflect self-hosted signing and keychain profile setup. |
.github/workflows/release.yml |
Pins signing + tap bump jobs to a labeled self-hosted macOS runner and removes secret-based credential handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+55
to
62
| - name: Codesign .pkg (identity from local keychain) | ||
| env: | ||
| SIGN_IDENTITY: ${{ secrets.APPLE_DEVELOPER_ID_INSTALLER_IDENTITY }} | ||
| # SIGN_IDENTITY is set in the runner's environment (e.g. the | ||
| # service plist or `~/.timenest-runner.env`), NOT in repo | ||
| # secrets. Example value: | ||
| # Developer ID Installer: Greycore Labs (TEAMID12) | ||
| SIGN_IDENTITY: ${{ env.TIMENEST_SIGN_IDENTITY }} | ||
| VERSION: ${{ steps.ver.outputs.version }} |
Comment on lines
+72
to
80
| - name: Notarize + staple (keychain profile) | ||
| env: | ||
| APPLE_ID: ${{ secrets.APPLE_ID }} | ||
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | ||
| APPLE_NOTARY_PASSWORD: ${{ secrets.APPLE_NOTARY_PASSWORD }} | ||
| # TIMENEST_NOTARY_PROFILE is the name of the keychain profile | ||
| # created on the runner with: | ||
| # xcrun notarytool store-credentials timenest-notary \ | ||
| # --apple-id <APPLE_ID> --team-id <TEAM_ID> --password <APP_PASSWORD> | ||
| # The actual credentials never leave the keychain. | ||
| NOTARY_PROFILE: ${{ env.TIMENEST_NOTARY_PROFILE }} | ||
| VERSION: ${{ steps.ver.outputs.version }} |
Comment on lines
+194
to
+200
| env: | ||
| TAP_TOKEN: ${{ env.TIMENEST_TAP_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| : "${TAP_TOKEN:?TIMENEST_TAP_TOKEN not set on the runner}" | ||
| rm -rf tap | ||
| git clone "https://x-access-token:${TAP_TOKEN}@github.com/momenbasel/homebrew-timenest.git" tap |
Comment on lines
+70
to
+84
| ### 4. Wire runner env | ||
|
|
||
| Edit `~/actions-runner-timenest/.env`: | ||
|
|
||
| ```env | ||
| TIMENEST_SIGN_IDENTITY=Developer ID Installer: Moamen Basel (H3WXHVTP97) | ||
| TIMENEST_NOTARY_PROFILE=timenest-notary | ||
| TIMENEST_TAP_TOKEN=github_pat_... | ||
| ``` | ||
|
|
||
| Then restart the runner service so it picks up the new env: | ||
|
|
||
| ```bash | ||
| cd ~/actions-runner-timenest && ./svc.sh stop && ./svc.sh start | ||
| ``` |
- Add Sorbet sigil + frozen_string_literal pragma, move version above sha256, swap deprecated assert_predicate for assert_path_exists. Clears every brew style nit, leaving only the Sorbet "false vs strict" advisory that mainline Homebrew taps also keep at false. - Sync the same formula into the tap repo. - Rewrite docs/release-pipeline.md to reflect the actual state of this Mac: tap created, runner installed + online, AC_NOTARY profile reused, CSR pre-generated. Note the one remaining manual step (Apple Developer ID Installer cert request) is unavoidable because Apple's App Store Connect API only exposes the Application and Kext variants of Developer ID, not Installer.
The bump-tap job is already self-hosted, so it can authenticate against github.com with `gh auth token` from the runner's logged-in CLI session (scopes include `repo`). This eliminates the last manual credential the maintainer had to provision: no PAT to generate, no .env var to rotate, no extra secret to keep in sync. If the runner ever loses the repo scope, `gh auth refresh -s repo` restores it without touching this workflow.
5ca68c2 to
c075eac
Compare
Single command tells the maintainer exactly what's wired up and what's blocking a release: tap repo, runner status, both Apple cert variants, notarytool keychain profile, runner .env, gh CLI auth, and cosign signatures on the currently published images. Exits 0 only when every prerequisite is met.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Repo is public. Storing the Apple Developer ID cert / notary password / tap PAT in GitHub Actions secrets puts them one compromised workflow run away from leaking. Move the signing-sensitive jobs to a self-hosted Mac runner so those credentials never enter GitHub.
Changes
release.ymlmacos-pkg + bump-tap jobs nowruns-on: [self-hosted, macOS, timenest-release]..env(none of which are repo secrets):TIMENEST_SIGN_IDENTITY- Developer ID Installer identity stringTIMENEST_NOTARY_PROFILE- notarytool keychain profile nameTIMENEST_TAP_TOKEN- fine-grained PAT scoped tomomenbasel/homebrew-timenestxcrun notarytool store-credentials); no password ever touches a file.tarball-hash,publish-release, all image builds + cosign signing) stay onubuntu-latest/macos-14and never touch sensitive material.docs/release-pipeline.mdrewritten with the new bootstrap procedure (cert request flow, notary profile setup, PAT generation, runner env wire-up) plus verification commands.State on the user's Mac (already wired)
Moamens-MacBook-Pro-timenest(labelsself-hosted,macOS,ARM64,timenest-release, statusonline).envplaceholders written under~/actions-runner-timenest/.envWhat's still missing before cutting
v0.2.0Developer ID Installercert (Developer ID Applicationexists but is for.appcodesign, not.pkg). Request the Installer variant at developer.apple.com.xcrun notarytool store-credentials timenest-notary ...once the app-specific password exists.TIMENEST_TAP_TOKENfine-grained PAT pasted into~/actions-runner-timenest/.env.Test plan
v0.0.2-test, watchrelease.ymlend-to-end, verifypkgutil --check-signature+ cosign verify + tap bump commit