GoReleaser GPG "signing failed" in CI
GoReleaser can sign checksums and artifacts with GPG. Signing fails in CI when the private key was never imported into the runner keyring, or when gpg prompts for a passphrase it cannot get non-interactively.
What this error means
GoReleaser fails at the sign step with "signing failed" and a gpg error such as "No secret key" or "Inappropriate ioctl for device".
⨯ sign failed error=sign: signing failed: exit status 2:
gpg: signing failed: No secret keyCommon causes
The signing key is not imported
The private key is not in the runner keyring, so gpg has no secret key to sign with.
gpg cannot read the passphrase
Without --batch and --pinentry-mode loopback, gpg tries to prompt on a tty that CI does not have.
How to fix it
Import the key and configure batch signing
- Store the exported private key as a secret and import it in a setup step.
- Provide the passphrase via a secret and loopback pinentry.
- Point the sign config at the imported key.
signs:
- artifacts: checksum
args:
- "--batch"
- "--pinentry-mode=loopback"
- "--passphrase=${GPG_PASSPHRASE}"
- "--sign"Import the secret key before GoReleaser
Load the private key into gpg so a secret key is available at sign time.
echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --importHow to prevent it
- Import the signing key in a dedicated setup step.
- Use
--batchand loopback pinentry for non-interactive gpg. - Keep the key and passphrase in secrets, never in the repo.