add check signed commits reusable workflow - #25
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a reusable GitHub Actions workflow that checks whether commits in a pull request are signed, and updates documentation to help downstream repos adopt it so unsigned commits are caught early (before merge time).
Changes:
- Added
.github/workflows/check_signed_commits.yamlreusable workflow to detect unsigned PR commits and manage a bot comment. - Updated
README.mdwith workflow catalog and usage instructions, including the new signed-commit check.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| README.md | Adds workflow inventory and usage examples, including how to wire up the signed-commit check. |
| .github/workflows/check_signed_commits.yaml | New reusable workflow that queries PR commits via gh, comments on unsigned commits, and fails the job when violations exist. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| commits=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/commits --paginate) | ||
| commit_count=$(echo "$commits" | jq length) | ||
| echo "Found $commit_count commits in PR" | ||
| echo "::endgroup::" | ||
|
|
||
| echo "::group::Commit verification status" | ||
| echo "$commits" | jq -r '.[] | " \(.sha[0:7]) | verified: \(.commit.verification.verified) | \(.commit.message | split("\n")[0])"' | ||
| echo "::endgroup::" | ||
|
|
||
| # Find unsigned commits | ||
| unsigned=$(echo "$commits" | jq -r '.[] | select(.commit.verification.verified == false) | "- `\(.sha[0:7])` \(.commit.message | split("\n")[0])"') | ||
| unsigned_count=$(echo "$commits" | jq '[.[] | select(.commit.verification.verified == false)] | length') | ||
|
|
There was a problem hiding this comment.
gh api ... --paginate can emit multiple JSON documents (one per page). Using jq length / jq '[...] | length' on that will produce multiple lengths (one per page), which breaks commit_count/unsigned_count for PRs with enough commits to paginate (e.g., >30). Consider using gh api --paginate --jq to stream individual items, or --slurp + a jq expression that merges pages before counting.
| comment_body="⚠️ **Unsigned Commits Detected** | ||
|
|
||
| The following commits are not signed: | ||
| $unsigned | ||
|
|
||
| Please sign your commits before merging. See [GitHub's guide on signing commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)." | ||
|
|
||
| echo "::group::Checking for existing bot comment" | ||
| existing_comment=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --paginate | \ | ||
| jq -r '.[] | select(.user.login == "github-actions[bot]" and (.body | contains("Unsigned Commits Detected"))) | .id' | head -1) | ||
| echo "Existing comment ID: ${existing_comment:-none}" | ||
| echo "::endgroup::" |
There was a problem hiding this comment.
The multi-line comment_body string includes leading indentation before several lines (e.g., “The following commits…” and the $unsigned list). In GitHub-flavored Markdown, leading spaces can render these lines as a code block and degrade readability; build the string without extra indentation/newline padding so it renders as intended.
| comment_body="⚠️ **Unsigned Commits Detected** | |
| The following commits are not signed: | |
| $unsigned | |
| Please sign your commits before merging. See [GitHub's guide on signing commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)." | |
| echo "::group::Checking for existing bot comment" | |
| existing_comment=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --paginate | \ | |
| jq -r '.[] | select(.user.login == "github-actions[bot]" and (.body | contains("Unsigned Commits Detected"))) | .id' | head -1) | |
| echo "Existing comment ID: ${existing_comment:-none}" | |
| echo "::endgroup::" | |
| comment_body="⚠️ **Unsigned Commits Detected**"$'\n\n'"The following commits are not signed:"$'\n'"$unsigned"$'\n\n'"Please sign your commits before merging. See [GitHub's guide on signing commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)." | |
| echo "::group::Checking for existing bot comment" | |
| existing_comment=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --paginate | \ | |
| jq -r '.[] | select(.user.login == "github-actions[bot]" and (.body | contains("Unsigned Commits Detected"))) | .id' | head -1) | |
| echo "Existing comment ID: ${existing_comment:-none}" | |
| echo "::endgroup::" | |
| existing_comment=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments --paginate | \ | |
| jq -r '.[] | select(.user.login == "github-actions[bot]" and (.body | contains("Unsigned Commits Detected"))) | .id' | head -1) | |
| echo "Existing comment ID: ${existing_comment:-none}" | |
| echo "::endgroup::" |
| echo "::endgroup::" | ||
|
|
||
| # Find unsigned commits | ||
| unsigned=$(echo "$commits" | jq -r '.[] | select(.commit.verification.verified == false) | "- `\(.sha[0:7])` \(.commit.message | split("\n")[0])"') |
There was a problem hiding this comment.
Commit subject lines are inserted into a PR comment without escaping/sanitization. A commit message can contain @mentions or other Markdown that triggers unwanted notifications or formatting. Consider neutralizing mentions (e.g., inserting a zero-width space after @) and/or wrapping the subject in inline code to prevent accidental pings.
| unsigned=$(echo "$commits" | jq -r '.[] | select(.commit.verification.verified == false) | "- `\(.sha[0:7])` \(.commit.message | split("\n")[0])"') | |
| unsigned=$(echo "$commits" | jq -r '.[] | select(.commit.verification.verified == false) | "- `\(.sha[0:7])` `\(.commit.message | split("\n")[0] | gsub(\"@\"; \"@\u200B\"))`"') |
| check-signatures: | ||
| uses: Azure/action-release-workflows/.github/workflows/check_signed_commits.yaml@main | ||
| permissions: | ||
| pull-requests: write |
There was a problem hiding this comment.
The example caller workflow grants only pull-requests: write, but this reusable workflow creates/updates/deletes PR conversation comments via the Issues Comments API. To avoid permission-related failures in repos with restricted default token permissions, document the required permissions explicitly (typically issues: write for commenting and pull-requests: read for listing commits).
| pull-requests: write | |
| pull-requests: read | |
| issues: write |
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened] | ||
|
|
There was a problem hiding this comment.
If this is intended to notify external contributors, note that workflows triggered by pull_request events from forks commonly run with a read-only GITHUB_TOKEN, which prevents creating/updating comments. Consider documenting this limitation and/or recommending a safe pull_request_target-based caller setup (since this workflow only uses the GitHub API, it can be safe when implemented carefully).
add a workflow for informing users when they don't sign commits instead of finding out at the last second when we can't merge
tested on fork here davidgamero/k8s-deploy#2
leaves a comment like this when unsigned commits are detected.
