From dc1646fcb952c8fb9ab62b838a5ccfb7bfdbbaae Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Sun, 26 Jul 2026 10:43:06 -0700 Subject: [PATCH] docs: document required DCO sign-off and add commit-msg sign-off hook (#2993) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `DCO Check` is a required status check on this repo and the top failing check on open contributor PRs, but nothing documented it and nothing surfaced it locally — a missing `Signed-off-by` trailer only showed up as a red check after the PR was already open. ## `lefthook.yml` New `commit-msg` hook that appends the `Signed-off-by` trailer: ```yaml commit-msg: commands: signoff: run: 'git interpret-trailers --if-exists doNothing --trailer "Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed ''s/ [0-9]* [+-][0-9]*$//'')" --in-place {1}' ``` `GIT_COMMITTER_IDENT` is the identity that performed the commit, which is what a DCO sign-off certifies and what native `git commit -s` uses. Committing someone else's work with `--author` or `git commit -C` therefore signs off as you, not as the original author. `--if-exists doNothing` makes it idempotent: `git commit -s` still yields exactly one trailer, and an existing sign-off from a different signer is preserved rather than supplemented. An empty commit message still aborts — the hook does not turn one into a commit body containing only a trailer. Git runs `commit-msg` for `git commit` and `git merge` only. Other flows bypass it and need their own sign-off flag — `git rebase --signoff`, `git cherry-pick -s`. Note `-s` is `--strategy` on `git rebase`, so only the long flag works there. The header comment and both docs state the scope rather than promising blanket coverage. Installed by `just hooks`; `commit-msg` carries no `glob` because it rewrites the message, not files. ## `CONTRIBUTING.md` `Before You Open a PR` gains a paragraph on sign-off: commit with `git commit -s`, what the trailer certifies, that the required `DCO Check` blocks merge without it, `git rebase --signoff main` to repair already-pushed commits, and what the hook does and does not cover. `CI Gate` gains one sentence pointing at `just fix-all` for formatting-only failures. ## `AGENTS.md` `Quality Gates` gains the same requirement framed for agents, including the sequencer caveat and the reminder to include `-s` in programmatically built commit commands. ## Related issue none found --------- Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 Signed-off-by: Will Pfleger Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 --- AGENTS.md | 2 ++ CONTRIBUTING.md | 4 +++- lefthook.yml | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 3edfb34f60..79bae9d494 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,6 +108,8 @@ re-install hooks after env changes. Before agents run Git or hooks, activate the repo's Hermit environment (`. ./bin/activate-hermit`); do not rewrite hook commands to compensate for an unconfigured shell `PATH`. +**Commit with `git commit -s`.** The required **DCO Check** fails any PR with a commit missing a `Signed-off-by` trailer, and `just hooks` installs a `commit-msg` hook that adds it to commits you create locally (`git rebase` and `git cherry-pick` still need `--signoff`) — if you build commit commands programmatically, include `-s` every time. To repair a branch that already has unsigned commits: `git rebase --signoff main`, then force-push. + Additional rules: - No `unsafe` code - Do not introduce new `unwrap()` or `expect()` in production paths — use `?` and proper error types diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 565d740129..87c12bf29d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,6 +43,8 @@ Buzz is an agent platform, so AI-assisted PRs are welcome. No need to disclose t We squash-merge, so your PR title becomes the commit subject in `main`. Use [Conventional Commits](https://www.conventionalcommits.org/) format: `feat(mcp): add get_feed_actions tool`. The type prefix (`feat`, `fix`, `docs`, `refactor`, `test`, `chore`) is required. See the [Commit Messages](#commit-messages) section for the full reference. +Every commit needs a Developer Certificate of Origin sign-off, so commit with `git commit -s` — it appends the `Signed-off-by` trailer that certifies you wrote the change and can contribute it. The required **DCO Check** blocks merge without it on every commit, and it's the most common reason new PRs stall. If you already pushed unsigned commits, run `git rebase --signoff main` and force-push. Running `just hooks` installs a `commit-msg` hook that adds the trailer to commits created by `git commit` and `git merge`; other flows need their own flag — `git rebase --signoff`, `git cherry-pick -s`. + We review as capacity allows — focused PRs that follow this guide move fastest. --- @@ -185,7 +187,7 @@ just ci ``` This is the same check that runs in CI. PRs that fail `just ci` will not be -merged. +merged. If `just ci` fails on formatting, `just fix-all` fixes it in one shot (`rustfmt` + Tauri fmt + desktop, web, and mobile formatters). --- diff --git a/lefthook.yml b/lefthook.yml index 2d3e921278..87eaa87c21 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -9,6 +9,9 @@ # drops deleted paths from push-file discovery (`extractFiles` existence # check, repository.go). CI's dorny/paths-filter catches deletions. # Deliberate — accepted, not worked around. +# - `commit-msg` has no glob: it rewrites the commit message, not files. Note +# Git only runs it for `git commit` and `git merge` — other flows need their own +# flag: `git rebase --signoff`, `git cherry-pick -s`. pre-commit: parallel: true commands: @@ -34,6 +37,14 @@ pre-commit: run: just mobile-fix stage_fixed: true +# Appends the DCO Signed-off-by trailer the required "DCO Check" enforces. +# `--if-exists doNothing` makes it idempotent and preserves an existing +# sign-off (including `git commit -s` and a different signer's trailer). +commit-msg: + commands: + signoff: + run: 'git interpret-trailers --if-exists doNothing --trailer "Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed ''s/ [0-9]* [+-][0-9]*$//'')" --in-place {1}' + pre-push: parallel: true commands: