1
- name : CLA Approval Handler
1
+ name : CLA Signature Recorder
2
2
3
3
on :
4
- workflow_dispatch :
5
- inputs :
6
- pr_number :
7
- description : ' PR number to approve CLA for'
8
- required : true
9
- type : string
10
4
pull_request_target :
11
5
types : [labeled]
12
- issue_comment :
13
- types : [created]
14
6
15
7
permissions : write-all
16
8
17
9
jobs :
18
- process -cla-approval :
10
+ record -cla-signature :
19
11
runs-on : ubuntu-latest
20
- if : |
21
- (
22
- github.event_name == 'workflow_dispatch' ||
23
- (github.event_name == 'pull_request_target' && github.event.label.name == 'cla-signed') ||
24
- github.event_name == 'issue_comment'
25
- ) && github.actor != 'workflow-authentication-public[bot]'
12
+ if : github.event.label.name == 'cla-signed' && github.actor != 'workflow-authentication-public[bot]'
26
13
27
14
steps :
28
15
@@ -40,236 +27,42 @@ jobs:
40
27
fetch-depth : 0
41
28
token : ${{ steps.generate-token.outputs.token || secrets.GITHUB_TOKEN }}
42
29
43
- - name : Process CLA approval
44
- id : process-cla-approval
30
+ - name : Extract PR Information
31
+ id : pr-info
45
32
uses : actions/github-script@v7
46
33
with :
47
34
github-token : ${{ steps.generate-token.outputs.token || secrets.GITHUB_TOKEN }}
48
35
script : |
49
- let prNumber;
50
- let approvedBy ;
51
- let prAuthor;
52
- let isCommentApproval = false ;
36
+ // Extract PR details from the event
37
+ const prNumber = context.payload.pull_request.number ;
38
+ const prAuthor = context.payload.pull_request.user.login ;
39
+ const approvedBy = context.actor ;
53
40
54
- // Handle different event types
55
- if (context.eventName === 'workflow_dispatch') {
56
- prNumber = parseInt('${{ github.event.inputs.pr_number }}');
57
- approvedBy = context.actor;
58
- } else if (context.eventName === 'pull_request_target') {
59
- prNumber = context.payload.pull_request.number;
60
- approvedBy = context.actor;
61
- } else if (context.eventName === 'issue_comment') {
62
- // Only process comments on pull requests
63
- if (!context.payload.issue.pull_request) {
64
- console.log('Comment is not on a pull request, skipping...');
65
- return;
66
- }
67
-
68
- prNumber = context.payload.issue.number;
69
- const commentBody = context.payload.comment.body;
70
- const commenter = context.payload.comment.user.login;
71
-
72
- // Check if this is a CLA agreement comment
73
- const isClaAgreement = commentBody.includes('I agree to the Trademark License Addendum') &&
74
- commentBody.includes('CLA-SIGNATURE:');
75
-
76
- if (!isClaAgreement) {
77
- console.log('Comment is not a CLA agreement, skipping...');
78
- return;
79
- }
80
-
81
- // Extract the signature from the comment
82
- const signatureMatch = commentBody.match(/CLA-SIGNATURE:\s*(\S+)/);
83
- if (!signatureMatch) {
84
- console.log('CLA signature format is invalid');
85
- return;
86
- }
87
-
88
- const signatureUser = signatureMatch[1];
89
-
90
- // Get PR details to verify the commenter is the PR author
91
- const { data: pr } = await github.rest.pulls.get({
92
- owner: context.repo.owner,
93
- repo: context.repo.repo,
94
- pull_number: prNumber
95
- });
96
-
97
- // If someone other than PR author is trying to sign, silently ignore
98
- if (commenter !== pr.user.login) {
99
- console.log(`Comment with CLA text from ${commenter} (not PR author ${pr.user.login}), ignoring silently`);
100
- return;
101
- }
102
-
103
- // If PR author is signing but signature doesn't match their username, silently ignore
104
- if (signatureUser !== commenter) {
105
- console.log(`PR author ${commenter} used incorrect signature '${signatureUser}', ignoring silently`);
106
- return;
107
- }
108
-
109
- // This is a valid CLA agreement comment from the PR author
110
- approvedBy = commenter; // The PR author approved themselves
111
- prAuthor = commenter;
112
- isCommentApproval = true;
113
- console.log(`Valid CLA agreement from PR author: ${commenter}`);
114
-
115
- } else {
116
- console.log('Unknown event type, skipping...');
117
- return;
118
- }
119
-
120
- // Get PR details if not already retrieved
121
- if (!prAuthor) {
122
- const { data: pr } = await github.rest.pulls.get({
123
- owner: context.repo.owner,
124
- repo: context.repo.repo,
125
- pull_number: prNumber
126
- });
127
- prAuthor = pr.user.login;
128
- }
129
-
130
- // For non-comment approvals, check if the person has the right permissions
131
- console.log(`isCommentApproval: ${isCommentApproval}, context.eventName: ${context.eventName}, context.actor: ${context.actor}`);
132
- if (!isCommentApproval) {
133
- try {
134
- const { data: collaboration } = await github.rest.repos.getCollaboratorPermissionLevel({
135
- owner: context.repo.owner,
136
- repo: context.repo.repo,
137
- username: context.actor
138
- });
139
-
140
- // Only admin, maintain, or write permissions can manually approve CLA
141
- const isAuthorized = ['admin', 'maintain', 'write'].includes(collaboration.permission);
142
-
143
- if (!isAuthorized) {
144
- // If this was a label event, remove the label
145
- if (context.eventName !== 'workflow_dispatch') {
146
- await github.rest.issues.removeLabel({
147
- owner: context.repo.owner,
148
- repo: context.repo.repo,
149
- issue_number: prNumber,
150
- name: 'cla-signed'
151
- });
152
- }
153
-
154
- // Add a comment explaining why the action was blocked
155
- await github.rest.issues.createComment({
156
- owner: context.repo.owner,
157
- repo: context.repo.repo,
158
- issue_number: prNumber,
159
- body: `@${context.actor} Only repository maintainers can manually approve CLAs. ${context.eventName !== 'workflow_dispatch' ? 'The label has been removed.' : ''}`
160
- });
161
-
162
- return;
163
- }
164
- } catch (error) {
165
- console.error('Error checking permissions:', error);
166
- return;
167
- }
168
- }
169
-
170
- // Check if PR has cla-required label
171
- const { data: labels } = await github.rest.issues.listLabelsOnIssue({
172
- owner: context.repo.owner,
173
- repo: context.repo.repo,
174
- issue_number: prNumber
175
- });
176
-
177
- const hasClaRequired = labels.some(label => label.name === 'cla-required');
178
-
179
- if (!hasClaRequired) {
180
- console.log('PR does not have cla-required label, skipping...');
181
- return;
182
- }
41
+ console.log(`Recording CLA signature for PR #${prNumber}`);
42
+ console.log(`PR Author: ${prAuthor}`);
43
+ console.log(`Approved by: ${approvedBy}`);
183
44
184
- // Remove blocking labels and add cla-signed label
185
- try {
186
- await github.rest.issues.removeLabel({
187
- owner: context.repo.owner,
188
- repo: context.repo.repo,
189
- issue_number: prNumber,
190
- name: 'cla-required'
191
- });
192
- } catch (e) {
193
- // Label not found or already removed
194
- }
195
-
196
- try {
197
- await github.rest.issues.removeLabel({
198
- owner: context.repo.owner,
199
- repo: context.repo.repo,
200
- issue_number: prNumber,
201
- name: 'integrations-with-image-change'
202
- });
203
- } catch (e) {
204
- // Label not found or already removed
205
- }
206
-
207
- // Add cla-signed label
208
- await github.rest.issues.addLabels({
209
- owner: context.repo.owner,
210
- repo: context.repo.repo,
211
- issue_number: prNumber,
212
- labels: ['cla-signed']
213
- });
214
-
215
- // Store the approval information for the next step
45
+ // Store the information for the signature recording step
216
46
core.setOutput('pr_number', prNumber);
217
47
core.setOutput('pr_author', prAuthor);
218
48
core.setOutput('approved_by', approvedBy);
219
-
220
- console.log(`Outputs set - pr_number: ${prNumber}, pr_author: ${prAuthor}, approved_by: ${approvedBy}`);
221
-
222
- // Check if confirmation comment already exists
223
- const comments = await github.rest.issues.listComments({
224
- issue_number: prNumber,
225
- owner: context.repo.owner,
226
- repo: context.repo.repo,
227
- });
228
-
229
- const confirmationExists = comments.data.some(comment =>
230
- (comment.user.login === 'github-actions[bot]' || comment.user.type === 'Bot') &&
231
- comment.body.includes('Trademark addendum agreement confirmed ✅')
232
- );
233
-
234
- const method = isCommentApproval ? 'Self-signed agreement' :
235
- (context.eventName === 'workflow_dispatch' ? 'Manual approval' : 'Label approval');
236
-
237
- if (!confirmationExists) {
238
- await github.rest.issues.createComment({
239
- owner: context.repo.owner,
240
- repo: context.repo.repo,
241
- issue_number: prNumber,
242
- body: `## Trademark license agreement
243
-
244
- The trademark license agreement process has been completed for @${prAuthor}.
245
-
246
- **Status:** Complete
247
- **Date:** ${new Date().toISOString()}
248
- **Signed by:** @${approvedBy}
249
- **Method:** ${method}
250
-
251
- This PR is now unblocked and can proceed with normal review.`
252
- });
253
- }
254
-
255
- console.log(`CLA approved for ${prAuthor} by ${approvedBy} via ${method}`)
256
49
257
- - name : Record manual CLA approval
258
- if : success() && steps.process-cla-approval .outputs.pr_number != ''
50
+ - name : Record CLA Signature
51
+ if : success() && steps.pr-info .outputs.pr_number != ''
259
52
run : |
260
53
set -e # Exit on any error
261
54
262
- echo "=== DEBUG: Record manual CLA approval step starting ==="
55
+ echo "=== Recording CLA signature ==="
263
56
echo "Available outputs:"
264
- echo " pr_number: '${{ steps.process-cla-approval .outputs.pr_number }}'"
265
- echo " pr_author: '${{ steps.process-cla-approval .outputs.pr_author }}'"
266
- echo " approved_by: '${{ steps.process-cla-approval .outputs.approved_by }}'"
57
+ echo " pr_number: '${{ steps.pr-info .outputs.pr_number }}'"
58
+ echo " pr_author: '${{ steps.pr-info .outputs.pr_author }}'"
59
+ echo " approved_by: '${{ steps.pr-info .outputs.approved_by }}'"
267
60
268
- # Extract and validate approval details from previous step outputs
269
- USERNAME="${{ steps.process-cla-approval .outputs.pr_author }}"
61
+ # Extract signature details
62
+ USERNAME="${{ steps.pr-info .outputs.pr_author }}"
270
63
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
271
- PR_NUMBER="${{ steps.process-cla-approval .outputs.pr_number }}"
272
- APPROVED_BY="${{ steps.process-cla-approval .outputs.approved_by }}"
64
+ PR_NUMBER="${{ steps.pr-info .outputs.pr_number }}"
65
+ APPROVED_BY="${{ steps.pr-info .outputs.approved_by }}"
273
66
274
67
# Validate required fields
275
68
if [ -z "$USERNAME" ] || [ -z "$PR_NUMBER" ] || [ -z "$APPROVED_BY" ]; then
0 commit comments