-
Notifications
You must be signed in to change notification settings - Fork 35
137 lines (125 loc) · 4.64 KB
/
Copy pathcommit-signature.yml
File metadata and controls
137 lines (125 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
name: commit-signature
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: read
pull-requests: write
jobs:
check:
name: signed commits
runs-on: ubuntu-latest
# Skip draft PRs — only enforce when ready for review
if: github.event.pull_request.draft == false
steps:
- name: Check commit signatures
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo
const pr = context.payload.pull_request
const prNumber = pr.number
const author = pr.user.login
const baseSha = pr.base.sha
const headSha = pr.head.sha
const marker = '<!-- commit-signature-check -->'
// Fetch all commits on the PR (paginated)
const commits = await github.paginate(github.rest.pulls.listCommits, {
owner,
repo,
pull_number: prNumber,
per_page: 100,
})
const unsigned = []
for (const commit of commits) {
const verified = commit.commit?.verification?.verified === true
if (!verified) {
unsigned.push({
sha: commit.sha,
short: commit.sha.slice(0, 7),
message: (commit.commit?.message || '').split('\n')[0],
author: commit.author?.login || commit.commit?.author?.name || 'unknown',
reason: commit.commit?.verification?.reason || 'unsigned',
})
}
}
// Find an existing bot comment so we can update or remove it
const comments = await github.paginate(github.rest.issues.listComments, {
owner,
repo,
issue_number: prNumber,
per_page: 100,
})
const existing = comments.find(
(c) => c.user?.type === 'Bot' && c.body?.includes(marker)
)
if (unsigned.length === 0) {
core.info(`All ${commits.length} commit(s) are signed.`)
if (existing) {
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: existing.id,
})
}
return
}
const list = unsigned
.map(
(c) =>
`- \`${c.short}\` — ${c.message} _(by @${c.author}, reason: \`${c.reason}\`)`
)
.join('\n')
const body = [
marker,
'## ✍️ Unsigned commit(s) detected',
'',
`@${author} one or more commits on this PR are **not signed**. Please sign them and update the PR.`,
'',
'### Unsigned commits',
'',
list,
'',
'### How to fix',
'',
'Sign your commits with GPG or SSH, then force-push the updated history:',
'',
'```bash',
'# Configure signing once (GPG example)',
'git config --global commit.gpgsign true',
'# or SSH: git config --global gpg.format ssh && git config --global user.signingkey ~/.ssh/id_ed25519.pub',
'',
'# Re-sign all commits on this branch (from the merge base)',
`git rebase --exec 'git commit --amend --no-edit -S' ${baseSha}`,
'',
'# Push the rewritten history',
'git push --force-with-lease',
'```',
'',
'If you only need to re-sign the tip commit:',
'',
'```bash',
'git commit --amend --no-edit -S',
'git push --force-with-lease',
'```',
'',
'See GitHub\'s guide: [About commit signature verification](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification).',
].join('\n')
if (existing) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
})
} else {
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body,
})
}
core.setFailed(
`${unsigned.length} unsigned commit(s) found. @${author} please sign and update the commits.`
)