From 6e913d4edd9de1b7969e37745f2b5abcffd8a9de Mon Sep 17 00:00:00 2001 From: Jon Hadfield Date: Wed, 2 Sep 2026 20:08:35 +0100 Subject: [PATCH 1/2] remove the signing configuration. It was added to fix a homebrew install that did not run, and it did not fix it: a bare executable has nowhere to staple a notarization ticket, and an unstapled one did not satisfy gatekeeper on macos 26.5. Shipping a formula rather than a cask is what fixed it, by not being quarantined in the first place. What was left behind was thirty lines that did nothing. The notarize block was off unless a certificate was configured and no certificate was configured; preflight checked for five secrets that were never set; the release job carried them into an environment that never read them. Dead configuration that looks live is worse than none, because the next person to read it has to work out which. The explanation stays in the README, since why notarizing is not the answer here is worth knowing before somebody tries it again, and it now says what was actually tested rather than more than that: every attempt within ten minutes of the ticket being issued. A longer run was never completed. Verified by building the darwin config with no signing environment at all: goreleaser runs no signing step and writes the same formula. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01WcPAJjNzG6bqy2FKqKKY1v --- .github/workflows/release.yml | 38 ------------------------------ .goreleaser/.goreleaser.darwin.yml | 24 ------------------- README.md | 32 ++++++------------------- 3 files changed, 7 insertions(+), 87 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c166d7..8ad5076 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,37 +35,6 @@ jobs: exit 1 fi echo "RELEASE_TOKEN is configured" - - - # Signing is optional: what makes a homebrew install run is shipping a - # formula rather than a cask, since a cask quarantines what it installs. - # Half-configured is worth saying out loud though, because goreleaser - # silently skips signing when the certificate is absent. - name: check macos signing credentials - env: - MACOS_SIGN_P12: ${{ secrets.MACOS_SIGN_P12 }} - MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }} - MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} - MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} - MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }} - run: | - missing="" - present="" - for name in MACOS_SIGN_P12 MACOS_SIGN_PASSWORD MACOS_NOTARY_ISSUER_ID MACOS_NOTARY_KEY_ID MACOS_NOTARY_KEY; do - value="${!name}" - if [ -z "$value" ]; then - missing="$missing $name" - else - present="$present $name" - fi - done - if [ -z "$present" ]; then - echo "the darwin binaries will not be signed, which is supported: see the release section of the README" - elif [ -n "$missing" ]; then - echo "::error::macos signing is half configured, missing:$missing. goreleaser skips signing entirely when the certificate is absent, so set all five or none of them." - exit 1 - else - echo "macos signing credentials are configured" - fi - name: install goreleaser uses: goreleaser/goreleaser-action@v6 @@ -179,13 +148,6 @@ jobs: run: make ${{ matrix.target }} env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} - # only the darwin target signs, so only that job is given the - # credentials to do it with - MACOS_SIGN_P12: ${{ matrix.target == 'release-mac' && secrets.MACOS_SIGN_P12 || '' }} - MACOS_SIGN_PASSWORD: ${{ matrix.target == 'release-mac' && secrets.MACOS_SIGN_PASSWORD || '' }} - MACOS_NOTARY_ISSUER_ID: ${{ matrix.target == 'release-mac' && secrets.MACOS_NOTARY_ISSUER_ID || '' }} - MACOS_NOTARY_KEY_ID: ${{ matrix.target == 'release-mac' && secrets.MACOS_NOTARY_KEY_ID || '' }} - MACOS_NOTARY_KEY: ${{ matrix.target == 'release-mac' && secrets.MACOS_NOTARY_KEY || '' }} - # the linux and windows targets build as root inside the container, so # the archives come back owned by root and unreadable to the next step diff --git a/.goreleaser/.goreleaser.darwin.yml b/.goreleaser/.goreleaser.darwin.yml index 4fba759..cb4d792 100644 --- a/.goreleaser/.goreleaser.darwin.yml +++ b/.goreleaser/.goreleaser.darwin.yml @@ -34,30 +34,6 @@ archives: formats: - tar.gz -# Signing the binaries with a Developer ID certificate, for anyone who would -# rather have it than not. It is off unless the certificate is configured, and -# it is not what makes a homebrew install run: a bare binary has nowhere to -# staple a notarization ticket, and on macos 26 an unstapled ticket did not -# satisfy gatekeeper for a quarantined binary in testing. What fixes that is the -# brews block below, by not being quarantined in the first place. -notarize: - macos: - - enabled: '{{ isEnvSet "MACOS_SIGN_P12" }}' - ids: - - darwin-arm64 - - darwin-amd64 - sign: - certificate: "{{ .Env.MACOS_SIGN_P12 }}" - password: "{{ .Env.MACOS_SIGN_PASSWORD }}" - notarize: - issuer_id: "{{ .Env.MACOS_NOTARY_ISSUER_ID }}" - key_id: "{{ .Env.MACOS_NOTARY_KEY_ID }}" - key: "{{ .Env.MACOS_NOTARY_KEY }}" - # the archives are uploaded from this same run, so the binaries they - # carry must be signed and accepted before it moves on - wait: true - timeout: 20m - checksum: name_template: "checksums_darwin.txt" diff --git a/README.md b/README.md index 4ca2097..b3cb64c 100644 --- a/README.md +++ b/README.md @@ -621,11 +621,11 @@ git tag -a -m "add super cool feature" v1.0.0 git push --follow-tags ``` -### required secrets +### required secret Platform builds run in parallel, so a release takes about as long as its slowest target rather than the sum of all five. A prerelease tag (one containing a hyphen, such as `v1.0.0-rc1`) is published as -a prerelease and does not update the homebrew cask. +a prerelease and does not update the homebrew formula. Release notes come from the annotated tag message, so write the tag with the notes you want. @@ -633,28 +633,9 @@ The workflow needs a `RELEASE_TOKEN` repository secret: a personal access token both `jonhadfield/certreader` and `jonhadfield/homebrew-certreader`. The token built into Actions cannot write to another repository, and the darwin build pushes the formula update to the tap. -Signing the darwin binaries is optional, and off unless all five of these are set. It is not what -makes an install work — the formula is, see [why a formula and not a cask](#why-a-formula-and-not-a-cask) -— so treat it as something to have rather than something to fix: +Without it the workflow stops at its preflight job and publishes nothing. -| secret | what it is | -| --- | --- | -| `MACOS_SIGN_P12` | base64 of the Developer ID Application certificate, exported as `.p12` | -| `MACOS_SIGN_PASSWORD` | the password that `.p12` was exported with | -| `MACOS_NOTARY_KEY` | base64 of an App Store Connect `.p8` key | -| `MACOS_NOTARY_KEY_ID` | that key's id, also in its filename | -| `MACOS_NOTARY_ISSUER_ID` | the issuer uuid shown when the key was created | - -```shell script -gh secret set MACOS_SIGN_P12 --repo jonhadfield/certreader < <(base64 -i DeveloperID.p12) -gh secret set MACOS_NOTARY_KEY --repo jonhadfield/certreader < <(base64 -i AuthKey_XXXXXXXXXX.p8) -``` - -Without `RELEASE_TOKEN` the workflow stops at its preflight job and publishes nothing. The signing -secrets are all-or-nothing: GoReleaser skips signing entirely when the certificate is absent, so -preflight fails a half-configured set rather than letting it publish quietly unsigned. - -Run the workflow manually from the Actions tab to check the secrets and the GoReleaser configs +Run the workflow manually from the Actions tab to check the secret and the GoReleaser configs without cutting a tag; a manual run stops after preflight. ### why a formula and not a cask @@ -670,8 +651,9 @@ $ echo $? ``` A bare executable has nowhere to keep a ticket — `stapler` needs an app bundle, a disk image or an -installer package — so signing and notarizing the binary does not settle it. On macOS 26 a notarized -but unstapled binary was still refused in testing. +installer package — so signing and notarizing the binary does not settle it. On macOS 26.5 a binary +signed with a Developer ID certificate and accepted by the notary service was still refused under +quarantine, on every attempt within ten minutes of the ticket being issued. A **formula** is not quarantined, which is how every other Go command line tool in Homebrew arrives able to run. GoReleaser calls `brews` deprecated in favour of `homebrew_casks`; the cask is what From 095a3f0554dd84dbbd57d9dab1b161e50db27451 Mon Sep 17 00:00:00 2001 From: Jon Hadfield Date: Wed, 2 Sep 2026 20:23:57 +0100 Subject: [PATCH 2/2] say formula in the two places that still said cask. The release workflow's own error message, which somebody troubleshooting would paste, and the sentence in the README describing what a release does. Both described the tap being updated with a cask, which stopped being true one release ago. The other mentions are about casks on purpose: how to replace one that is already installed, and why certreader is not shipped as one. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01WcPAJjNzG6bqy2FKqKKY1v --- .github/workflows/release.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ad5076..d0cfb65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | if [ -z "$RELEASE_TOKEN" ]; then - echo "::error::RELEASE_TOKEN secret is not set. Releases need a personal access token with repo scope on both jonhadfield/certreader and jonhadfield/homebrew-certreader, because the darwin build pushes the cask update to the tap and the built-in GITHUB_TOKEN cannot write to another repository." + echo "::error::RELEASE_TOKEN secret is not set. Releases need a personal access token with repo scope on both jonhadfield/certreader and jonhadfield/homebrew-certreader, because the darwin build pushes the formula update to the tap and the built-in GITHUB_TOKEN cannot write to another repository." exit 1 fi echo "RELEASE_TOKEN is configured" diff --git a/README.md b/README.md index b3cb64c..3cf56ef 100644 --- a/README.md +++ b/README.md @@ -614,7 +614,7 @@ fixtures on every test run. Releases are built and published with [GoReleaser](https://goreleaser.com) from a tagged commit. Pushing the tag is all that is needed — the `release` workflow builds every platform and uploads the -artifacts to a single GitHub release, then updates the homebrew cask. +artifacts to a single GitHub release, then updates the homebrew formula. ```shell script git tag -a -m "add super cool feature" v1.0.0