Skip to content

Commit 6a5a811

Browse files
hishamankclaude
andcommitted
fix(release): only GPG-sign when signing secrets are configured
Every Release workflow run since June has failed (8+ consecutive), and releases have been cut manually as a workaround. The `signs` block in .goreleaser.yml unconditionally GPG-signs checksums.txt using `{{ .Env.GPG_FINGERPRINT }}`, but the GPG_PRIVATE_KEY and GPG_FINGERPRINT repository secrets were never configured. An unset secret expands to the empty string, so goreleaser ran `gpg --local-user ""` and died with: gpg: skipped "": Invalid user ID gpg: signing failed: Invalid user ID release failed message=could not sign artifact cmd=gpg artifact=checksums.txt Everything before signing succeeded — all 12 binaries, archives, and the deb/rpm/apk packages — so the release died immediately before publishing. The asymmetry that caused this: release.yml guards the GPG *import* step with `if: env.GPG_PRIVATE_KEY != ''`, but nothing guarded the *signing*. goreleaser v2 has no config-level conditional on a `signs` entry: the config.Sign struct exposes only id/cmd/args/signature/artifacts/ids/ stdin/stdin_file/env/certificate/output — no `skip` and no `if` (verified against both v2.17.1, the version the action resolves, and main). Adding a `skip:` key is a hard parse error: yaml: unmarshal errors: line 334: field skip not found in type config.Sign `signs[].artifacts` is matched as a literal string in a switch rather than rendered as a template, so it cannot be toggled from the config either. goreleaser's own mechanism for this is the `--skip=sign` flag, so the guard goes in the workflow: pass `--skip=sign` when GPG_FINGERPRINT is empty. The signing block itself is preserved unchanged, with a comment explaining why it is conditional and how to enable it. Adding the GPG_PRIVATE_KEY and GPG_FINGERPRINT secrets re-enables real signing with no further config change. Verified with goreleaser v2.17.1 (the version goreleaser-action@v7 resolves): `goreleaser check` reports "configuration is valid" with a deprecation set byte-identical to main; a snapshot release with GPG_FINGERPRINT empty succeeds; and with a dummy fingerprint signing is attempted (gpg reports "No secret key" for the dummy ID rather than "Invalid user ID"), proving the guard is conditional and not permanently off. Note: skipping sign unmasks a second, independent pre-existing blocker in the `brews` section ("one tap can handle only one archive of an OS/Arch combination"), reproducible on unmodified main. That is out of scope here and needs its own fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d3bcc2a commit 6a5a811

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ jobs:
6464
uses: goreleaser/goreleaser-action@v7
6565
with:
6666
version: latest
67-
args: release --clean
67+
# The `signs` block in .goreleaser.yml needs GPG_FINGERPRINT. When that secret
68+
# is not configured goreleaser would run `gpg --local-user ""` and fail the
69+
# whole release, so skip the sign pipe instead. Mirrors the `if:` guard on the
70+
# "Import GPG key" step above. Adding the GPG secrets re-enables signing with
71+
# no further config change.
72+
args: release --clean ${{ secrets.GPG_FINGERPRINT == '' && '--skip=sign' || '' }}
6873
env:
6974
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
7075
GPG_FINGERPRINT: ${{ secrets.GPG_FINGERPRINT }}

.goreleaser.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,25 @@ docker_manifests:
329329
image_templates:
330330
- "ghcr.io/sebastienmelki/protoc-gen-openapiv3:latest-amd64"
331331

332+
# Checksum signing is intentional but CONDITIONAL: it only runs when this repo has
333+
# GPG signing secrets configured.
334+
#
335+
# A `signs` entry has no config-level conditional in goreleaser v2 -- the Sign struct
336+
# in pkg/config exposes only id/cmd/args/signature/artifacts/ids/stdin/stdin_file/env/
337+
# certificate/output (no `skip`, no `if`), and `artifacts` is matched as a literal
338+
# string, not a template, so it cannot be toggled from here either. The guard therefore
339+
# lives in .github/workflows/release.yml, which passes goreleaser's own `--skip=sign`
340+
# whenever the GPG_FINGERPRINT secret is empty.
341+
#
342+
# Without that guard `{{ .Env.GPG_FINGERPRINT }}` below expands to "", goreleaser runs
343+
# `gpg --local-user ""`, and the release dies with:
344+
# gpg: skipped "": Invalid user ID
345+
# gpg: signing failed: Invalid user ID
346+
# after every binary, archive and package has already been built successfully.
347+
#
348+
# To enable real signing: add the GPG_PRIVATE_KEY and GPG_FINGERPRINT repository
349+
# secrets. Nothing in this file or in release.yml needs to change -- the workflow
350+
# stops passing --skip=sign on its own and this block starts signing checksums.txt.
332351
signs:
333352
- artifacts: checksum
334353
args:

0 commit comments

Comments
 (0)