diff --git a/scripts/desktop_release.py b/scripts/desktop_release.py index ce9ceaab932..48e7ce86ced 100755 --- a/scripts/desktop_release.py +++ b/scripts/desktop_release.py @@ -257,12 +257,15 @@ def validate(args: argparse.Namespace) -> None: bad = [str(path.relative_to(ROOT)) for path, value in manifests.items() if value != version] if bad: raise SystemExit(f"version mismatch in: {', '.join(bad)}") - author = git("show", "-s", "--format=%an <%ae>", candidate) + name = git("show", "-s", "--format=%an", candidate) + email = git("show", "-s", "--format=%ae", candidate) + if not name or not email: + raise SystemExit("candidate has no author identity") + author = f"{name} <{email}>" body = git("show", "-s", "--format=%B", candidate) - if author != "Wes ": - raise SystemExit(f"unexpected candidate author: {author}") - if "Signed-off-by: Wes " not in body: - raise SystemExit("candidate is missing Wes Signed-off-by trailer") + signoffs = re.findall(rf"(?m)^Signed-off-by: {re.escape(author)}$", body) + if len(signoffs) != 1: + raise SystemExit(f"candidate must carry exactly one Signed-off-by trailer matching its author {author}") if not re.search(r"(?m)^Co-authored-by: .+ <.+>$", body): raise SystemExit("candidate is missing automation Co-authored-by trailer") print(f"validated immutable desktop candidate {candidate} for desktop-v{version}") diff --git a/scripts/prepare-desktop-release.sh b/scripts/prepare-desktop-release.sh index 43785075a15..eec08ac8c56 100755 --- a/scripts/prepare-desktop-release.sh +++ b/scripts/prepare-desktop-release.sh @@ -42,8 +42,7 @@ chore(release): release Buzz Desktop version $version Co-authored-by: $agent_name <$agent_email> EOF -git -c user.name='Wes' -c user.email='wesbillman@users.noreply.github.com' \ - commit -s -F "$msg" +git commit -s -F "$msg" scripts/desktop_release.py validate --candidate HEAD --version "$version" --repo block/buzz candidate_sha="$(git rev-parse HEAD)" diff --git a/scripts/test-desktop-release-candidate.sh b/scripts/test-desktop-release-candidate.sh index c63a157c006..503c64eb37d 100755 --- a/scripts/test-desktop-release-candidate.sh +++ b/scripts/test-desktop-release-candidate.sh @@ -64,8 +64,42 @@ for path in ('desktop/package.json', 'desktop/src-tauri/tauri.conf.json'): open('desktop/src-tauri/Cargo.toml','w').write('[package]\nversion = "1.0.1"\n') PY git add . - git -c user.name=Wes -c user.email=wesbillman@users.noreply.github.com commit -q -s -m 'chore(release): release Buzz Desktop version 1.0.1' -m 'Co-authored-by: Test Automation ' + git commit -q -s -m 'chore(release): release Buzz Desktop version 1.0.1' -m 'Co-authored-by: Test Automation ' PATH="$mock_bin:$PATH" scripts/desktop_release.py validate --version 1.0.1 --repo block/buzz + good_candidate=$(git rev-parse HEAD) + + # A candidate whose Signed-off-by does not match its author is a dishonest + # DCO sign-off and must be rejected. Rewrite the author while keeping the + # original trailer body, then restore the honest candidate. + git -c user.name=Impostor -c user.email=impostor@example.com commit -q --amend --no-edit --reset-author + if PATH="$mock_bin:$PATH" scripts/desktop_release.py validate --version 1.0.1 --repo block/buzz >/dev/null 2>&1; then + echo "validator accepted a candidate whose sign-off does not match its author" >&2; exit 1 + fi + git reset -q --hard "$good_candidate" + + # The trailer must be a complete anchored line, not substring-matched. A prose + # line that merely contains the sign-off text, or a real trailer with trailing + # garbage, must be rejected. Both were accepted before the anchored parse. + for bogus in \ + 'not-a-trailer Signed-off-by: test ' \ + 'Signed-off-by: test trailing-garbage'; do + git commit -q --amend -m 'chore(release): release Buzz Desktop version 1.0.1' \ + -m 'Co-authored-by: Test Automation ' -m "$bogus" + if PATH="$mock_bin:$PATH" scripts/desktop_release.py validate --version 1.0.1 --repo block/buzz >/dev/null 2>&1; then + echo "validator accepted a malformed sign-off: $bogus" >&2; exit 1 + fi + git reset -q --hard "$good_candidate" + done + + # Two matching sign-offs are also invalid: the contract is exactly one. + git commit -q --amend -m 'chore(release): release Buzz Desktop version 1.0.1' \ + -m 'Co-authored-by: Test Automation ' \ + -m 'Signed-off-by: test ' -m 'Signed-off-by: test ' + if PATH="$mock_bin:$PATH" scripts/desktop_release.py validate --version 1.0.1 --repo block/buzz >/dev/null 2>&1; then + echo "validator accepted duplicate Signed-off-by trailers" >&2; exit 1 + fi + git reset -q --hard "$good_candidate" + grep -Fq "$unrelated_before" CHANGELOG.md grep -Fq "$unrelated_after" CHANGELOG.md ! grep -Fq "$prior_merge" CHANGELOG.md