Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 1 addition & 39 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,41 +31,10 @@ 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"
-
# 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
Expand Down Expand Up @@ -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
Expand Down
24 changes: 0 additions & 24 deletions .goreleaser/.goreleaser.darwin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
34 changes: 8 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,47 +614,28 @@ 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
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.

Comment on lines 626 to 629
Release notes come from the annotated tag message, so write the tag with the notes you want.

The workflow needs a `RELEASE_TOKEN` repository secret: a personal access token with `repo` scope on
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
Expand All @@ -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
Expand Down
Loading