Skip to content

Prime gpg-agent so reprepro can sign in CI - #32

Merged
Cam8863 merged 1 commit into
linuxfrom
fix/ci-gpg-passphrase
Aug 27, 2026
Merged

Cam8863 merged 1 commit into
linuxfrom
fix/ci-gpg-passphrase

Conversation

@guys-inc-ops

@guys-inc-ops guys-inc-ops Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

The publish run after the key rotation failed:

Adding incoming/GitHubDesktop-linux-amd64-3.4.9.deb
gpgme gave error Pinentry:32870:  Inappropriate ioctl for device
ERROR: Could not finish exporting 'stable'!

Nothing was published — the sync steps were skipped, so the bucket is untouched.

Cause

The rotated signing subkey is passphrase-protected; the old one was not.
reprepro signs through gpgme, which has no way to be handed a passphrase —
it can only ask an agent. With no TTY in CI, that request has nowhere to go.

Passing --passphrase to gpg doesn't help, because reprepro never invokes gpg
directly.

Fix

Preset the passphrase into gpg-agent against the signing subkey's keygrip
before reprepro runs, with allow-preset-passphrase enabled.

Verified locally by reproducing the failure and then the fix:

# passphrase-protected key, no TTY
gpg: signing failed: Inappropriate ioctl for device

# same key, agent primed, no passphrase argument at all
SIGNED OK
Good signature

That last condition — signing with no passphrase argument — is exactly what
gpgme does, so it's the right thing to have tested.

The preset is skipped when APT_GPG_PASSPHRASE is empty, so an unprotected key
still works and this is safe to merge before the secret exists.

Also

Adds the certify-capability guard from archivist's release workflow. This
repo's CI holds a signing subkey too and nothing here checked it wasn't handed
a full secret key. Field 15 (# = no private material) is the discriminator,
since a stubbed primary and a real one both report caps=cSC.

Before this can publish

Add the passphrase as a secret:

gh secret set APT_GPG_PASSPHRASE --repo Guys-Inc-Public/github-desktop-linux

Then re-run publish-apt.yml with tag=release-3.4.9-linux1.

The publish job failed at the export step:

  gpgme gave error Pinentry:32870: Inappropriate ioctl for device
  ERROR: Could not finish exporting 'stable'!

The rotated signing subkey is passphrase-protected; the previous one was not.
reprepro signs through gpgme, which has no mechanism for being handed a
passphrase - it can only ask an agent, and with no TTY in CI that request has
nowhere to go.

Passing --passphrase to gpg does not help, because reprepro never invokes gpg
directly. The agent has to already hold the passphrase, so preset it against
the signing subkey's keygrip before reprepro runs. Verified locally: with the
agent primed, gpg signs with no passphrase argument at all, which is exactly
the condition gpgme needs.

The preset is skipped when APT_GPG_PASSPHRASE is unset, so an unprotected key
still works.

Also adds the certify-capability guard already used in archivist's release
workflow. This repository's CI holds a signing subkey too, and nothing here
checked that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AH1v3tR8Xw2DmKqJSipPd
@guys-inc-ops
guys-inc-ops Bot requested a review from Cam8863 as a code owner August 27, 2026 03:37
@Cam8863
Cam8863 enabled auto-merge August 27, 2026 03:37
@Cam8863
Cam8863 merged commit f12d087 into linux Aug 27, 2026
9 checks passed
@Cam8863
Cam8863 deleted the fix/ci-gpg-passphrase branch August 27, 2026 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant