diff --git a/.github/workflows/commit_signoff.yml b/.github/workflows/commit_signoff.yml new file mode 100644 index 0000000..0a726af --- /dev/null +++ b/.github/workflows/commit_signoff.yml @@ -0,0 +1,76 @@ +name: Commit Signoff + +'on': + pull_request: + merge_group: + types: [checks_requested] + +permissions: + contents: read + pull-requests: read + +jobs: + commit-signoff: + name: commit-signoff + runs-on: ubuntu-latest + steps: + - name: Check PR commits for Signed-off-by + if: ${{ github.event_name == 'pull_request' }} + uses: actions/github-script@v7 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const pull_number = context.payload.pull_request.number; + + const commits = await github.paginate( + github.rest.pulls.listCommits, + { + owner, + repo, + pull_number, + per_page: 100, + } + ); + + const trailerPattern = /^Signed-off-by:\s+.+\s+<[^<>]+>$/im; + const missing = commits.filter(({ commit }) => !trailerPattern.test(commit.message)); + + const writeSummary = async (lines) => { + await core.summary + .addHeading('Signed-off-by check') + .addRaw(lines.join('\n') + '\n') + .write(); + }; + + if (missing.length === 0) { + await writeSummary([ + `Checked ${commits.length} commit(s).`, + 'All commits include a Signed-off-by trailer.', + ]); + return; + } + + await writeSummary([ + `Found ${missing.length} commit(s) without a valid Signed-off-by trailer:`, + '', + ...missing.map(({ sha, commit }) => `- ${sha.slice(0, 12)} ${commit.message.split('\n')[0]}`), + '', + 'Please rewrite the commit message(s) and push again.', + 'Examples: `git commit --amend -s` or `git rebase --signoff `.', + ]); + + core.setFailed( + [ + `Found ${missing.length} commit(s) without a valid Signed-off-by trailer.`, + ...missing.map(({ sha, commit }) => `- ${sha.slice(0, 12)} ${commit.message.split('\n')[0]}`), + 'Please rewrite the commit message(s) and push again.', + 'Examples: git commit --amend -s or git rebase --signoff .', + ].join('\n') + ); + + - name: Report merge queue compatibility + if: ${{ github.event_name == 'merge_group' }} + run: | + echo "Signed-off-by is enforced on pull_request workflows." + echo "This merge_group run reports the required status for merge queue compatibility." diff --git a/src/developers.md b/src/developers.md index 68eff76..41d085f 100644 --- a/src/developers.md +++ b/src/developers.md @@ -61,3 +61,5 @@ Then you just add a line saying: ``` using your real name (sorry, no pseudonyms or anonymous contributions.) +Pull requests are checked automatically, and any commit without a valid +`Signed-off-by:` trailer will be blocked until it is fixed.