From 723640bca1c8c0ae7c95f3a6b86f193b605adf7c Mon Sep 17 00:00:00 2001 From: Vyncint Ng Date: Fri, 11 Sep 2026 08:48:29 +0700 Subject: [PATCH] ci: the DCO squash-rewrite equivalence runs both ways MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 +@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 --- .github/workflows/no-ai-attribution.yml | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/no-ai-attribution.yml b/.github/workflows/no-ai-attribution.yml index 3caa0e3..51ced68 100644 --- a/.github/workflows/no-ai-attribution.yml +++ b/.github/workflows/no-ai-attribution.yml @@ -48,12 +48,28 @@ jobs: echo "::error::$sha contains the robot emoji"; fail=1 fi # DCO: the trailer must match the author. One documented - # equivalence: GitHub's squash/rebase merges rewrite the author - # email to +@users.noreply.github.com while the - # trailer keeps the real address — same person, so a trailer - # matching the author NAME satisfies the rule there. + # equivalence, and it runs in **both** directions: GitHub's + # squash/rebase merges rewrite the author email, pairing an + # account's real address with its + # +@users.noreply.github.com form. Which of the two + # lands in the author field is GitHub's choice and has changed: + # every merge on this branch before e1ceae0 was authored as the + # noreply address with a real address in the trailer, and + # e1ceae0 is the same person the other way round. The rule the + # original comment states — same person, so a trailer matching + # the author NAME satisfies it — covers both; only the test + # assumed one side. + # + # So the exemption asks whether *either* address is a GitHub + # noreply form, which is the signature of a rewrite, and then + # requires the names to be equal. A commit with neither form + # still needs an exact email match, which is every commit made + # on a branch by a human. + trailer_email=$(printf '%s' "$body" | sed -n 's/^Signed-off-by: .*<\(.*\)>$/\1/p' | head -1) + noreply='^[0-9]+\+.*@users\.noreply\.github\.com$' if ! printf '%s' "$body" | grep -q "Signed-off-by:.*<$author_email>"; then - if printf '%s' "$author_email" | grep -qE '^[0-9]+\+.*@users\.noreply\.github\.com$' \ + if { printf '%s' "$author_email" | grep -qE "$noreply" \ + || printf '%s' "$trailer_email" | grep -qE "$noreply"; } \ && printf '%s' "$body" | grep -q "Signed-off-by: $author_name <"; then : else