From 5553c6a66ec36bb9e6330280d18ba2dfd0957e31 Mon Sep 17 00:00:00 2001 From: "John E. Malmberg" Date: Tue, 2 Jun 2026 14:35:01 -0500 Subject: [PATCH] SRE-3607: Enforce Signed-off-by in commit messages This adds a check for the presence of a `Signed-off-by` line in commit messages, which is a policy of this group. This is commonly known as a DCO (Developer Certificate of Origin) signoff. - .github/workflows/dco-signoff.yml: New file. GitHub Actions workflow to check for DCO signoff in commit messages. - docs/git-commit-message-guidelines.md: - docs/maintenance.md: Update documentation for the DCO check. - vscode-project-words.txt: Minor updates. Signed-off-by: John E. Malmberg --- .github/workflows/dco-signoff.yml | 52 +++++++++++++++++++++++++++ docs/git-commit-message-guidelines.md | 3 ++ docs/maintenance.md | 21 +++++++---- vscode-project-words.txt | 2 ++ 4 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/dco-signoff.yml diff --git a/.github/workflows/dco-signoff.yml b/.github/workflows/dco-signoff.yml new file mode 100644 index 0000000..282c953 --- /dev/null +++ b/.github/workflows/dco-signoff.yml @@ -0,0 +1,52 @@ +# Copyright 2026 Hewlett Packard Enterprise Development LP +--- +name: dco-signoff + +'on': + pull_request: + +permissions: + contents: read + +jobs: + dco-signoff-check: + name: DCO / Signed-off-by + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Verify Signed-off-by trailers + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + + mapfile -t commits < <(git rev-list --reverse "${BASE_SHA}..${HEAD_SHA}") + + if [[ ${#commits[@]} -eq 0 ]]; then + echo "[dco] no commits found in PR range" + exit 0 + fi + + missing=0 + for commit in "${commits[@]}"; do + trailers="$(git log -1 --pretty=%B "${commit}" | git interpret-trailers --parse)" + if ! grep -qi '^signed-off-by:' <<< "${trailers}"; then + subject="$(git log -1 --pretty=%s "${commit}")" + echo "::error title=Missing Signed-off-by::${commit}: ${subject}" + missing=1 + fi + done + + if [[ ${missing} -ne 0 ]]; then + echo "[dco] one or more commits are missing Signed-off-by trailers" >&2 + echo "[dco] amend commits with: git commit --amend --signoff" >&2 + echo "[dco] for multiple commits: git rebase -i --signoff " >&2 + exit 1 + fi + + echo "[dco] all commits include Signed-off-by trailers" diff --git a/docs/git-commit-message-guidelines.md b/docs/git-commit-message-guidelines.md index 3186516..8c54ba3 100644 --- a/docs/git-commit-message-guidelines.md +++ b/docs/git-commit-message-guidelines.md @@ -75,6 +75,9 @@ contributors. Local group policy is stricter: require `Signed-off-by:` for all commits, including employee-authored commits. +This repository enforces `Signed-off-by:` trailers on pull requests through the +`DCO / Signed-off-by` required status check. + ## References For broader background, see the Git Book section on commit guidelines: diff --git a/docs/maintenance.md b/docs/maintenance.md index b3e3c44..0327ed9 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -12,8 +12,12 @@ merge protections used here. ### Overview -The `basic-source-checks` job from the `checks.yml` workflow is configured as a -required status check. This consolidates all code quality gates: +The following jobs are configured as required status checks: + +- `Basic Source checks` from `checks.yml` +- `DCO / Signed-off-by` from `dco-signoff.yml` + +`Basic Source checks` consolidates code quality gates: - `code-checking-ref` guard — blocks accidental commits of local overrides - Executable mode verification — ensures scripts have correct git index mode @@ -21,7 +25,7 @@ required status check. This consolidates all code quality gates: (shellcheck, etc.) This consolidation avoids coupling branch protection rules to per-linter checks. -All rules are enforced in a single, stable status check. +The separate DCO check enforces `Signed-off-by:` commit trailer policy. ### Setup (GitHub Web UI) @@ -47,7 +51,9 @@ This section describes the underlying structure; exact interface paths may vary. 3. Under Branch targeting criteria, use `Default`. 4. Enable these branch rules: - **Restrict deletions** - - **Require signed commits** + - **Require signed commits** (optional) + - Enable only when all contributors are set up for verified commit + signing (GPG/SSH/S/MIME). - **Require a pull request before merging** - **Required approvals**: 1 - **Dismiss stale pull request approvals when new commits are pushed** @@ -56,7 +62,9 @@ This section describes the underlying structure; exact interface paths may vary. - **Allowed merge methods**: disable merge commits; allow squash and rebase merges - **Require status checks to pass** - - **Status checks that are required**: `Basic Source checks` + - **Status checks that are required**: + - `Basic Source checks` + - `DCO / Signed-off-by` - GitHub does not make this selectable until the workflow exists on the default `main` branch. - **Block force pushes** @@ -66,8 +74,9 @@ This section describes the underlying structure; exact interface paths may vary. Once configured, attempts to merge a PR will fail with: - "`Basic Source checks` – required check" +- "`DCO / Signed-off-by` - required check" -if any of the check steps fail (guard, executable modes, or linters). +if any of the check steps fail. ### Testing the Configuration diff --git a/vscode-project-words.txt b/vscode-project-words.txt index 16a9366..6071cc9 100644 --- a/vscode-project-words.txt +++ b/vscode-project-words.txt @@ -17,9 +17,11 @@ isort LASTEXITCODE longpaths makefiles +mapfile MSYSTEM nicolasvuillamy nonblank +pipefail Pipenv pyenv pylint