ci: the DCO squash-rewrite equivalence runs both ways - #44
Merged
Merged
Conversation
`main` went red on e1ceae0, the termlens 0.11 merge, with "lacks a DCO Signed-off-by matching vyncint@icloud.com". Not an attribution problem and not a missing sign-off: GitHub's squash rewrite pairs an account's real address with its <id>+<login>@users.noreply.github.com form, and which of the two lands in the author field is GitHub's choice. Every merge on this branch before e1ceae0 was authored as the noreply address with the real one in the trailer; e1ceae0 is the same person the other way round. The rule the comment already states — same person, so a trailer matching the author NAME satisfies it — covers both directions. Only the test assumed one, so it read `author_email` and never the trailer's. The exemption now asks whether *either* address carries the noreply form, which is the signature of a rewrite, and then requires the names to match. A commit with neither form still needs an exact email match, which is every commit a human makes on a branch. Exercised against six cases before committing, three of which must fail: exact match, noreply author with a real trailer, real author with a noreply trailer (the case that broke), a different person, no trailer at all, and the same name with two unrelated addresses and no rewrite marker. The last three exit 1. The amended scan is also green over this repository's whole history. Signed-off-by: Vyncint Ng <vyncint@icloud.com>
vyncint
added a commit
that referenced
this pull request
Sep 13, 2026
…shape (#81) #44 widened the squash-rewrite exemption by asking whether either address is the <id>+<login>@users.noreply.github.com form. That shape is a symptom of a GitHub rewrite, not the rule. An outside contributor's squash landed authored as <jackchen@haloailabs.com> while every commit they pushed was authored and signed off as <nightcityblade@gmail.com>: two real addresses, neither a noreply form, and the exemption could not see it. Two commits on main failed the scan for this reason, and because the scan walks every reachable commit, no follow-up commit could clear them. The signature of a GitHub-composed commit is the committer, which is always GitHub <noreply@github.com>. The exemption keys on that now, which is the rule oxidelake and termlens already use. For such a commit the requirement is that a DCO assertion is present, not that it matches an address GitHub chose. Matching stays enforced where it means something: this job runs on every pull request over the contributor's own commits, which they authored and pushed, and those take the exact-match branch. A squash cannot reach main without that having passed. Exercised against crafted commits, each in isolation. Must fail, and does: a human commit signed by a different person, a human commit with no trailer, a web-flow commit with no trailer at all, one name with two unrelated addresses and no web-flow marker, a bot author, an AI co-author trailer, a generated-with watermark. Must pass, and does: a human commit with a matching trailer, the contributor-account-address shape, the maintainer-trailer-only shape, and the owner flip #44 was written for. What it does not catch is a maintainer dropping a contributor's trailer while composing a squash body, which is how ca56368 lost mikemikimike's. The PR-time check is what stands behind the assertion; CONTRIBUTING now says to carry those trailers. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mainis red, one1ceae0(the termlens 0.11 merge):Not an attribution problem, and not a missing sign-off. GitHub's squash rewrite pairs an account's real address with its
<id>+<login>@users.noreply.github.comform, and which of the two lands in the author field is GitHub's choice. Every merge on this branch beforee1ceae0was authored as the noreply address with the real one in the trailer;e1ceae0is the same person the other way round:4882818and earlier115854244+vyncint@users.noreply.github.comvyncint@icloud.come1ceae0vyncint@icloud.com115854244+vyncint@users.noreply.github.comThe job's own comment already states the rule that covers both — "same person, so a trailer matching the author NAME satisfies it" — but the test read
author_emailonly, so it recognised the rewrite in one direction. The exemption now asks whether either address carries the noreply form, which is the signature of a rewrite, and then requires the names to match. A commit with neither form still needs an exact email match, which is every commit a human makes on a branch.This widens an exemption, so it was exercised before it was trusted. Six cases against a scratch repository, three of which must fail:
The amended scan is also green over this repository's entire history.
The alternative — rewriting
e1ceae0— is not available:mainis protected and linear, and the next squash merge would reproduce it anyway, since the rewrite is GitHub's behaviour and not something this repository controls.