Skip to content

fix(release): verify the tag, and publish the changelog not the blurb - #38

Closed
serialexperimentslainnnn wants to merge 2 commits into
developfrom
feature/workflow-release-fix
Closed

serialexperimentslainnnn wants to merge 2 commits into
developfrom
feature/workflow-release-fix

Conversation

@serialexperimentslainnnn

Copy link
Copy Markdown
Owner

Summary

v5.0.0 shipped with an unverified tag. Three things had to be wrong at once, and all three were:

Where What was wrong
gen-ci-signing-key.sh generated the key with Name-Real but no Name-Email — GitHub reported the registered key as emails=, empty
bootstrap-ci.sh never registered the key on the GitHub account. It certified it with the YubiKey, which is a different mechanism entirely
release.yml tagged as github-actions[bot]@users.noreply.github.com, an address that cannot appear on anyone's key

GitHub marks a signature Verified only when the tagger email, an email on a uid of a registered
key
, and a verified account email all agree. Any one of the above defeats it permanently, and the
failure is silent: the signature is valid, gpg --verify passes, only the badge is missing.

Certification and registration were the conflated pair. Certifying with the hardware key makes the CI
key trustworthy to a human checking a signature by hand; registration is what the badge reads. The
scripts did the first and never the second.

The fix, and why it cannot drift again

The address is derived, never written down:

gen-ci-signing-key.sh  → reads the uid of the maintainer key (git config user.signingkey)
release.yml            → reads the uid of the key it just imported

Each link derives from the previous one, so rotating the key is sufficient on its own — there is no
second place to remember to update. It also keeps the address out of the repository, which matters
beyond tidiness: this project deliberately publishes no contact email anywhere (CHANGELOG 5.0.0), and a
committed script is published. The first draft of this fix hardcoded it and undid that decision.

Both now fail loudly where they used to continue. The workflow aborts if the imported key has no
email rather than minting another unverifiable tag; the generator aborts rather than producing another
unusable key.

Also in here

  • bootstrap-ci.sh no longer re-adds a required reviewer. Publication is automatic on merge to
    main now, and re-running the script would have silently restored the gate. On a single-collaborator
    repository the merge is already the human act. What is lost is named in the script rather than glossed:
    nobody confirms which version is about to go out.
  • bootstrap-ci.sh adds main to the deployment branch policy. It only ever added v*.*.*, so the
    push-to-main path was rejected before the workflow was even reached — that is how the first attempt
    failed.
  • The GitHub Release now carries CHANGELOG.md. RELEASE_NOTES.md is the Marketplace copy —
    emoji-led, second person, "one more thing" — and that register belongs on a storefront page where
    someone is deciding whether to install, not in front of a person who arrived at a release page because
    something broke. Different readers, different documents: build.gradle.kts still feeds the Marketplace
    panel from RELEASE_NOTES.md. Measured before switching: the section is ~27 KB against GitHub's
    125 000-character limit, and an empty extraction now fails the release instead of publishing blank notes.

Type of change

  • Bug fix
  • Security fix
  • Docs / build / CI

Risk and rollback

Risk: confined to the release path. The sharpest edge is that this branch changes what happens on a
merge to main, and there is no manual approval any more — the merge publishes. The failure mode
that remains is the one worth watching: if the CI key is ever rotated without an email, the release now
fails rather than shipping unverified, which is the intended direction.

Rollback: revert. Nothing here has reached a user; v5.0.0's release was deleted before this.

How was this tested?

  • release.yml parses; both scripts pass bash -n.
  • The email extraction run against the real maintainer key — resolves correctly, and no address
    appears in any committed file (grep for it comes back clean).
  • The changelog extraction run against the real CHANGELOG.md — 70 lines, 26 871 characters.
  • The rotated CI key is registered and now carries an email, confirmed via gh api user/gpg_keys;
    the old email-less key has been removed from the account.
  • End to end. It cannot be tested without publishing — the next merge to main is the test.

Notes for reviewers

The prerequisite is already done: the CI key was rotated and re-registered before this PR, so the
account now lists a signing key with an email. Without that, this change would abort the release
rather than fix it — which is the intended behaviour, but worth knowing before merging.

v5.0.0 shipped with an UNVERIFIED tag. Three things had to be wrong at once, and
all three were:

  - gen-ci-signing-key.sh generated the key with Name-Real but no Name-Email, so
    its uid carried no address at all. GitHub reported it as `emails=` — empty.
  - bootstrap-ci.sh never registered the key on the GitHub ACCOUNT. It certified
    the key with the YubiKey, which is a different mechanism: certification makes
    `gpg --verify` meaningful to a human, registration is what the "Verified"
    badge reads. The two were conflated.
  - release.yml tagged as `github-actions[bot]@users.noreply.github.com`, an
    address that cannot appear on anyone's key.

GitHub marks a signature verified only when the tagger email, an email on a uid
of a registered key, and a verified account email all agree. Any one of the above
defeats it permanently.

The address is now DERIVED, never written down. gen-ci-signing-key.sh reads it
from the maintainer key via `git config user.signingkey`; release.yml reads it
from the signing key it just imported. Each link derives from the previous one,
so rotating the key is sufficient on its own and nothing can drift out of step.
It also keeps the address out of the repository — this project deliberately
publishes no contact email anywhere (CHANGELOG 5.0.0), and a committed script is
published.

Both now fail loudly where they used to continue: the workflow aborts if the
imported key has no email rather than producing another unverifiable tag, and the
generator aborts rather than minting another unusable key.

Also in bootstrap-ci.sh: it no longer re-adds a required reviewer (publication is
automatic on merge to main, and on a single-collaborator repository the merge
already is the human act), and it adds `main` to the deployment branch policy —
without that entry the job is rejected before the workflow is even reached, which
is how the first attempt failed.

Finally, the GitHub Release now carries CHANGELOG.md instead of RELEASE_NOTES.md.
The latter is the Marketplace copy — emoji-led, second person — and that register
belongs on a storefront where someone is deciding whether to install, not in
front of a person who arrived at a release page because something broke.
Different readers, different documents: build.gradle.kts still feeds the
Marketplace panel from RELEASE_NOTES.md. The extracted section is ~27 KB against
a 125 000-character limit, and an empty extraction now fails the release rather
than publishing blank notes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@serialexperimentslainnnn
serialexperimentslainnnn deleted the feature/workflow-release-fix branch August 10, 2026 19:59
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