Skip to content

Improve package signing verification in CI workflows - #304

Merged
yarikoptic merged 3 commits into
masterfrom
claude/vibrant-curie-ew7ny7
Oct 30, 2026
Merged

yarikoptic merged 3 commits into
masterfrom
claude/vibrant-curie-ew7ny7

Conversation

@yarikoptic-gitmate

Copy link
Copy Markdown
Collaborator

Summary

This PR refactors the package signing logic in the Ubuntu build workflows to be more explicit and adds verification that built packages are properly signed.

Key Changes

  • Extracted signing decision logic: Created a new "Decide whether to sign" step that centralizes the logic for determining whether packages should be signed. This replaces inline conditional expressions and makes the decision process more maintainable.

    • Signs packages when the GPG key secret is available
    • Skips signing for pull requests (which don't have access to secrets)
    • For non-PR builds in non-fork repositories without a signing key, fails with a clear error message
    • For non-PR builds in fork repositories without a signing key, skips signing
  • Updated GPG key import step: Changed the conditional from a complex GitHub expression to use the new steps.signing.outputs.sign output, making it clearer when GPG keys are imported. Also added verification that the secret key was successfully imported.

  • Updated package build step: Simplified the signing flag logic to use the centralized decision output instead of duplicating the conditional logic.

  • Added package signature verification: Introduced a new step that verifies all built packages (.dsc, .buildinfo, and .changes files) are signed with the correct key when signing is enabled. This ensures the build process actually produced signed artifacts as expected.

Implementation Details

  • The signing decision step uses GitHub CLI to check if the repository is a fork when needed
  • Signature verification uses gpg --verify with status output parsing to confirm the correct key ID signed each package
  • Changes are applied to both the main workflow file and the template file to keep them in sync

https://claude.ai/code/session_01PvaGRY7auLbAfwNGj21YXd

Every Dependabot PR touching build-ubuntu.yaml has failed build-package
at "Import GPG keys" (all 29 such runs since 2025-09; most recently
run 40638632117 for #303):

  echo '' | gpg --import
  gpg: no valid OpenPGP data found.

GitHub withholds Actions secrets from Dependabot-triggered runs, as it
does for fork PRs.  The step was guarded only against forks
(head.repo.fork == false), and Dependabot's branches are in this
repository, so the step ran with an empty key.

Decide on signing from whether a PR actually has the key rather than
from where its branch lives, and use that one decision both for
importing the keys and for the -uc -us of the binary build.  The
unsigned path is the one fork PRs already take: no ~/.gnupg is created,
so dpkg-buildpackage does not try to sign the source package either.
Non-PR events (schedule, workflow_dispatch) still always sign, so a
missing or empty key keeps failing them loudly.

Edited the template and regenerated with `make -C .github/workflows/template`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvaGRY7auLbAfwNGj21YXd
The previous commit had non-PR events always import the key, so a
missing key failed them only through `gpg --import` of an empty
string, and nothing checked that the packages actually came out signed.

"Decide whether to sign" now makes the decision explicitly:

- with the key, always sign;
- a PR without it (fork, Dependabot) builds unsigned, as before;
- any other build without it fails with an error naming the secret,
  unless the repository is a fork (asked of the API, as schedule
  payloads need not carry it; an unclear answer counts as not a fork).

When signing, "Import GPG keys" checks that the secret key for
DEB_SIGN_KEYID is there, and a new "Check that the packages are signed"
step requires a good signature by DEB_SIGN_KEYID on the .dsc and on the
source and binary .buildinfo and .changes, since dpkg-buildpackage can
skip signing without failing (e.g. when ~/.gnupg is missing).

Tested locally with dpkg-buildpackage 1.22.6 and a throwaway key: an
unsigned, wrong-key, partially signed, tampered, or missing file fails
the check, and a fully signed build passes.  The comment on the
decision is cut down to two lines.

Edited the template and regenerated with `make -C .github/workflows/template`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvaGRY7auLbAfwNGj21YXd

Copy link
Copy Markdown
Collaborator Author

build-package (Build git-annex on Ubuntu) fails at "Build binary package from the source package". This is not caused by this PR: master's nightly fails the same way at the same step today (run 40844225757, with the same log tail ending in make[3]: *** [Makefile:62: git-annex] Error 1).

It started with upstream git-annex switching MagicMime from the Haskell magic package to libmagic (>= 1.3) on 2026-10-27 (c51f910bc4, 5556371681). Debian doesn't package that yet.

  • The 2026-10-28 nightly (run 40659421290) failed one step earlier, on unmet build dependencies: libghc-libmagic-dev.
  • Upstream then commented that build-dependency out (b043867592). The source package builds again since then, but the compile still fails.
  • The GHC error itself isn't in the job log, because the step only prints the last 50 lines of the build log. So the link to libmagic is very likely but not confirmed.

There is no fix for this in the repo yet. It will need either Haskell libmagic >= 1.3 in the buildenv image or upstream changes for the case where it's missing, which is outside this PR's scope.

What this run does confirm for this PR: "Decide whether to sign" chose to sign, "Import GPG keys" imported the secret key and found 13A1093296154584245E0300C98FC49D36DAB17F, and the signed source package built. "Check that the packages are signed" was skipped only because the binary build failed before reaching it. Re-running the failed job once.


Generated by Claude Code

…e-ew7ny7

Brings in #305 (libmagic in the buildenv image) so that this PR's CI
runs again now that the image with it is published.
@yarikoptic
yarikoptic marked this pull request as ready for review October 29, 2026 22:20

yarikoptic-gitmate commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

With the new buildenv image (#305), build-package now passes on 128d11dbb9, including the new "Check that the packages are signed" step. All five files (.dsc and the source and amd64 .buildinfo and .changes) report VALIDSIG 13A1093296154584245E0300C98FC49D36DAB17F.

test-annex (nfs-home, ubuntu-24.04) failed in a single test:

import: FAIL
  import --duplicate failed with unexpected exit code
  import5/f changed while it was being added

This isn't caused by this PR, which only changes signing steps in build-package. It's the NFS mtime race tracked in #294, which lists import among the affected tests: git-annex compares the file's InodeCache exactly before and after, and the NFS client reports a different mtime for a file nothing wrote to. The actimeo=0 mount from #302 is in effect on this branch, but the race still hit once here.

No fix exists in this repo beyond #302's mitigation; the real fix is upstream (relaxing the exact comparison, as #294 describes).

Update after the one re-run: it failed again, this time in different places, all from the same race: two testremote storeKey failures (content changed while it was being sent) and drop (with remote) (git status reporting M ./foo). It now looks as if actimeo=0 has no effect on the current runner image:

master nightly, 2026-10-27 (passed) this PR's re-run, 2026-10-29 (failed)
runner image ubuntu-24.04 20261012.334.1 ubuntu-24.04 20261025.353.1
mount command -o actimeo=0 -o actimeo=0
700 unit tests all passed in 1286 s 4 failed, in 282 s

#294 measured actimeo=0 at about 4× slower. On the 20261025 image, the suite is 4.5× faster than on the 20261012 image and fails the way an attribute-caching mount does. Something in that image, perhaps its kernel, seems to make the option ineffective. I haven't confirmed this; checking would mean printing /proc/mounts and uname -r in that job. I'm not re-running this again.


Generated by Claude Code

yarikoptic-gitmate commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

test-datalad (master) failed with 3 failed, 1376 passed. This also isn't caused by this PR: master's last Ubuntu nightly that ran these tests, on 2026-10-27 (job 122083437969), failed the same 3 tests:

  • test_sibling_enable_sameas, and the RIA post-clone-config test: git-annex now refuses an unverified special remote (This special remote cannot be used since it's not verified. Run this command to verify it: git-annex verifyremote r_rsync). That's upstream's new verifyremote requirement meeting DataLad's tests, so the fix belongs in DataLad or upstream.
  • test_assure_unicode: Could not decode value as utf-8, or to guess its encoding. That's DataLad's encoding guessing and doesn't involve git-annex.

No fix for these exists in this repo.

Update: test-datalad (maint) and (release) both passed on this run. I've re-run the failed jobs once, nfs-home and test-datalad (master), now that the run has finished.


Generated by Claude Code

@yarikoptic
yarikoptic merged commit 854e599 into master Oct 30, 2026
18 of 22 checks passed
@yarikoptic
yarikoptic deleted the claude/vibrant-curie-ew7ny7 branch October 30, 2026 00:50
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.

3 participants