mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-20 17:43:26 +00:00
2b752ba194
* feat(release): sign + notarize macOS binaries (IDEA-830)
Adds Developer ID code-signing and Apple notarization to the release
pipeline so users installing via `brew install perpetualsoftware/tap/pad`
or downloading binaries directly no longer hit Gatekeeper's "cannot
verify the developer" warning.
Uses GoReleaser v2's built-in `notarize:` block (Anchore/Quill backend),
which signs and notarizes in-process from the existing ubuntu-latest
runner — no rcodesign install, no macOS runner needed.
Both the .p12 cert and the .p8 App Store Connect notary key are stored
as base64-encoded repo secrets; Quill decodes them in-process. The
notarize block is gated on `MACOS_CERT_P12` being set, so snapshot
builds, fork PRs, and any context where the cert isn't available skip
cleanly without failing.
Verification plan: tag v0.0.1-rc.3, confirm Action goes green end-to-end,
then on a clean Mac run `brew install perpetualsoftware/tap/pad` and
verify `codesign -dv $(which pad)` shows the Developer ID signature and
`spctl -a -t exec -vv $(which pad)` reports "accepted" with the notary
ticket stapled.
* harden(release): isolate npm build from macOS secrets + pin goreleaser
Addresses Codex review findings on PR #278:
MEDIUM — Apple signing secrets were exposed to the npm web build.
The previous `before.hooks` block ran `npm ci && npm run build` inside
the GoReleaser process, which means npm lifecycle scripts and the
SvelteKit build inherited the Developer ID .p12 cert, cert password,
and .p8 notary key from the goreleaser-action's env. Adding a 5-year
signing cert to that environment meaningfully widened the blast radius
of any npm supply-chain compromise.
Fix: move the web build into a dedicated workflow step before the
goreleaser-action invocation. The MACOS_* secrets are scoped only to
the goreleaser env block, so the npm phase no longer sees them.
LOW — GoReleaser binary version was floated as `~> v2`, while every
third-party Action in this workflow is SHA-pinned per the policy at
the top of the file. With Apple signing credentials now in the env, a
compromised or regressed GoReleaser release would carry meaningful
blast radius. Pinned to v2.15.4 (current latest) so bumps go through
explicit review like the rest of the action pins.
No functional change to the signing/notarization itself — same schema,
same gating, same secrets.
* docs(release): document web/build prereq for local snapshot builds
Addresses second-pass Codex review finding on PR #278.
Removing the goreleaser `before.hooks` web build (done in 6f4a52c to
isolate npm from MACOS_* secrets) means `goreleaser release --snapshot`
from a clean checkout now fails with "embed: no matching files found"
because embed.go declares `//go:embed all:web/build` and there's no
longer a hook that auto-populates that directory.
CI is unaffected (the workflow has a dedicated `Build web UI` step), but
contributors testing goreleaser config locally would hit a confusing
error before reaching the part they wanted to validate.
Doc-only fix: extend the existing comment block at the top of
.goreleaser.yaml to point contributors at `make web` (or the explicit
npm commands) as the prerequisite for local snapshot builds. Note that
`make build` and `make install` already handle this automatically.
* harden(release): add concurrency guard + bump goreleaser timeout
Addresses third-pass Codex review findings on PR #278.
Both findings were LOW (no ship blockers) and pre-existing concerns
that the macOS notarize block makes slightly more visible. Folded into
this PR rather than deferring because both relate directly to the
goreleaser invocation we already touched.
LOW #1 — GoReleaser overall timeout was the default 1h, while the
notarize block now allows up to 20m of Apple notary wait time on top
of build + cosign blob-sign + SBOM + multi-arch docker. On a slow
notary day (or first-cert-use latency), that could come close to or
hit the default ceiling. Bumped `release --clean` to
`release --clean --timeout=2h` for comfortable headroom without
burning Action minutes on the happy path (worker exits as soon as
Apple replies).
LOW #2 — No workflow-level concurrency guard. If two `v*` tags landed
close together (rc.3 then rc.4 within a minute), runs would race on
shared mutable outputs: GHCR `:latest`, the homebrew cask in the
separate tap repo, the GitHub Releases page. Added a top-level
concurrency block that serializes all release runs.
Group is intentionally NOT keyed by `github.ref` — different tag names
share the same mutable infrastructure, so we want all release tags to
serialize, not just repeat pushes of the same tag. cancel-in-progress
is false so a queued tag never aborts a release mid-publish, which
could leave GHCR and the brew tap in inconsistent states.
No functional change to signing/notarization itself.