-
-
Notifications
You must be signed in to change notification settings - Fork 0
321 lines (274 loc) · 13.2 KB
/
actor.yml
File metadata and controls
321 lines (274 loc) · 13.2 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
name: Process Application
# This workflow processes validated application PRs by:
# 1. Validating members.json structure
# 2. Checking/updating email to match GitHub account
# 3. Merging the PR
# 4. Adding user to organization as member
#
# Note: For adding users to organization, you may need to set up an ORG_TOKEN secret
# with a Personal Access Token that has 'org:write' scope. If not set, the workflow
# will warn but continue (you can add users manually).
on:
workflow_run:
workflows: ["Validate Application PR"]
types:
- completed
branches:
- apply
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to process'
required: true
type: string
jobs:
process-application:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Determine PR number
id: pr-info
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
PR_NUMBER="${{ github.event.inputs.pr_number }}"
else
# Get PR number from the workflow run that triggered this
# Try to get from head branch
HEAD_BRANCH="${{ github.event.workflow_run.head_branch }}"
if [ -n "$HEAD_BRANCH" ]; then
PR_NUMBER=$(gh pr list --head "$HEAD_BRANCH" --base apply --json number --jq '.[0].number' 2>/dev/null || echo "")
fi
# If still not found, try to get from workflow run head repository
if [ -z "$PR_NUMBER" ]; then
HEAD_REPO="${{ github.event.workflow_run.head_repository.full_name }}"
HEAD_REF="${{ github.event.workflow_run.head_branch }}"
if [ -n "$HEAD_REPO" ] && [ -n "$HEAD_REF" ]; then
PR_NUMBER=$(gh pr list --repo "$HEAD_REPO" --head "$HEAD_REF" --base apply --json number --jq '.[0].number' 2>/dev/null || echo "")
fi
fi
# Last resort: try to find PR by searching all open PRs to apply branch
if [ -z "$PR_NUMBER" ] && [ -n "$HEAD_BRANCH" ]; then
PR_NUMBER=$(gh pr list --base apply --state open --json number,headRefName --jq ".[] | select(.headRefName == \"$HEAD_BRANCH\") | .number" | head -n 1)
fi
fi
if [ -z "$PR_NUMBER" ]; then
echo "❌ Could not determine PR number"
echo "Please use workflow_dispatch with a PR number, or ensure the workflow_run event has proper branch information"
exit 1
fi
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "PR Number: $PR_NUMBER"
- name: Get PR details
id: pr-details
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}"
# Get PR information
PR_DATA=$(gh pr view $PR_NUMBER --json title,author,headRefName,state)
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
PR_BRANCH=$(echo "$PR_DATA" | jq -r '.headRefName')
PR_STATE=$(echo "$PR_DATA" | jq -r '.state')
if [ "$PR_STATE" != "OPEN" ]; then
echo "❌ PR is not open (state: $PR_STATE)"
exit 1
fi
# Extract username from PR title: "Application for OpenStack by [username]"
USERNAME=$(echo "$PR_TITLE" | sed -n 's/.*Application for OpenStack by \([^ ]*\).*/\1/p')
if [ -z "$USERNAME" ]; then
echo "❌ Could not extract username from PR title: $PR_TITLE"
exit 1
fi
echo "pr_title=$PR_TITLE" >> $GITHUB_OUTPUT
echo "pr_author=$PR_AUTHOR" >> $GITHUB_OUTPUT
echo "pr_branch=$PR_BRANCH" >> $GITHUB_OUTPUT
echo "username=$USERNAME" >> $GITHUB_OUTPUT
echo "PR Title: $PR_TITLE"
echo "PR Author: $PR_AUTHOR"
echo "Extracted Username: $USERNAME"
- name: Checkout PR branch
run: |
PR_BRANCH="${{ steps.pr-details.outputs.pr_branch }}"
git fetch origin $PR_BRANCH
git checkout $PR_BRANCH
- name: Validate members.json structure
id: validate-json
run: |
if [ ! -f "members.json" ]; then
echo "❌ members.json file not found"
exit 1
fi
# Validate JSON syntax
if ! jq empty members.json 2>/dev/null; then
echo "❌ members.json is not valid JSON"
exit 1
fi
# Check structure
if ! jq -e '.members' members.json > /dev/null 2>&1; then
echo "❌ members.json must have a 'members' object"
exit 1
fi
USERNAME="${{ steps.pr-details.outputs.username }}"
# Check if username exists in members.json
if ! jq -e ".members.\"$USERNAME\"" members.json > /dev/null 2>&1; then
echo "❌ Username '$USERNAME' not found in members.json"
exit 1
fi
# Validate required fields
USER_DATA=$(jq -r ".members.\"$USERNAME\"" members.json)
if ! echo "$USER_DATA" | jq -e '.email' > /dev/null 2>&1; then
echo "❌ Missing 'email' field for user $USERNAME"
exit 1
fi
# Get the email from members.json
MEMBER_EMAIL=$(echo "$USER_DATA" | jq -r '.email')
echo "member_email=$MEMBER_EMAIL" >> $GITHUB_OUTPUT
echo "✅ members.json structure is valid"
echo "Member email from JSON: $MEMBER_EMAIL"
- name: Get GitHub user email
id: github-email
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
USERNAME="${{ steps.pr-details.outputs.username }}"
# Try to get user's public email
USER_DATA=$(gh api users/$USERNAME 2>/dev/null || echo "{}")
PUBLIC_EMAIL=$(echo "$USER_DATA" | jq -r '.email // empty')
# If no public email, skip email update and keep existing email
if [ -z "$PUBLIC_EMAIL" ] || [ "$PUBLIC_EMAIL" == "null" ]; then
echo "⚠️ No public email found for GitHub user $USERNAME"
echo "Skipping email update - keeping existing email in members.json"
echo "has_public_email=false" >> $GITHUB_OUTPUT
echo "github_email=" >> $GITHUB_OUTPUT
else
GITHUB_EMAIL="$PUBLIC_EMAIL"
echo "has_public_email=true" >> $GITHUB_OUTPUT
echo "github_email=$GITHUB_EMAIL" >> $GITHUB_OUTPUT
echo "GitHub email: $GITHUB_EMAIL"
fi
- name: Update email if needed
id: update-email
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
MEMBER_EMAIL="${{ steps.validate-json.outputs.member_email }}"
GITHUB_EMAIL="${{ steps.github-email.outputs.github_email }}"
HAS_PUBLIC_EMAIL="${{ steps.github-email.outputs.has_public_email }}"
USERNAME="${{ steps.pr-details.outputs.username }}"
PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}"
# Skip email update if no public email is available
if [ "$HAS_PUBLIC_EMAIL" != "true" ]; then
echo "✅ Skipping email update - no public email available, keeping existing email"
echo "email_updated=false" >> $GITHUB_OUTPUT
exit 0
fi
if [ "$MEMBER_EMAIL" != "$GITHUB_EMAIL" ]; then
echo "⚠️ Email mismatch detected"
echo "Email in members.json: $MEMBER_EMAIL"
echo "GitHub account email: $GITHUB_EMAIL"
echo "Updating members.json with GitHub email..."
# Update the email in members.json
jq ".members.\"$USERNAME\".email = \"$GITHUB_EMAIL\"" members.json > members.json.tmp
mv members.json.tmp members.json
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Commit the change
git add members.json
git commit -m "chore: update email to match GitHub account email"
# Try to push - this will fail for fork PRs, which is expected
if git push; then
echo "email_updated=true" >> $GITHUB_OUTPUT
echo "✅ Email updated and committed"
else
echo "⚠️ Could not push email update (PR might be from a fork)"
echo "Creating PR comment to request email update..."
# Create a comment on the PR
COMMENT_BODY="⚠️ **Email Mismatch Detected**\n\nThe email in your \`members.json\` (\`$MEMBER_EMAIL\`) doesn't match your GitHub account email (\`$GITHUB_EMAIL\`).\n\nPlease update your \`members.json\` to use: \`$GITHUB_EMAIL\`\n\nThe workflow will automatically update this when the PR is from the same repository, but for fork PRs, please update it manually."
gh pr comment $PR_NUMBER --body "$COMMENT_BODY" || echo "Could not create PR comment"
# For fork PRs, we can't push, so we need to fail and ask user to update
echo "email_updated=false" >> $GITHUB_OUTPUT
echo "❌ Cannot proceed: Email must match GitHub account email"
echo "Please update your members.json with the correct email: $GITHUB_EMAIL"
exit 1
fi
else
echo "✅ Email matches GitHub account"
echo "email_updated=false" >> $GITHUB_OUTPUT
fi
- name: Wait for email update commit (if updated)
if: steps.update-email.outputs.email_updated == 'true'
run: |
echo "Waiting 10 seconds for commit to propagate..."
sleep 10
- name: Merge PR
id: merge-pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}"
MERGED="false"
echo "Merging PR #$PR_NUMBER..."
# Check if already merged first
PR_STATE=$(gh pr view $PR_NUMBER --json state --jq '.state')
if [ "$PR_STATE" == "MERGED" ]; then
echo "✅ PR is already merged"
MERGED="true"
else
# Merge the PR
if gh pr merge $PR_NUMBER --squash --delete-branch; then
MERGED="true"
echo "✅ PR merged successfully"
else
echo "⚠️ Failed to merge PR, it may have conflicts or require review"
exit 1
fi
fi
echo "merged=$MERGED" >> $GITHUB_OUTPUT
- name: Add user to organization team
env:
GITHUB_TOKEN: ${{ secrets.ORG_TOKEN || secrets.GITHUB_TOKEN }}
run: |
USERNAME="${{ steps.pr-details.outputs.username }}"
ORG_NAME="${{ github.repository_owner }}"
echo "Adding $USERNAME to organization $ORG_NAME as member..."
# Check if user is already a member first
MEMBER_STATUS=$(gh api "/orgs/${ORG_NAME}/memberships/${USERNAME}" --jq '.state' 2>/dev/null || echo "none")
if [ "$MEMBER_STATUS" == "active" ]; then
echo "✅ User is already a member of the organization"
else
# Try to add user to organization
# Note: This requires a PAT with org:write permissions if GITHUB_TOKEN doesn't have org access
if gh api \
-X PUT \
"/orgs/${ORG_NAME}/memberships/${USERNAME}" \
-f role="member" 2>/dev/null; then
echo "✅ User added to organization successfully"
else
echo "⚠️ Failed to add user to organization"
echo "This requires a Personal Access Token (PAT) with 'org:write' scope"
echo "Please set up ORG_TOKEN secret with a PAT that has organization write permissions"
echo "Alternatively, add the user manually to the organization"
# Don't fail the workflow, just warn
echo "::warning::Could not automatically add user to organization. Please add manually or configure ORG_TOKEN secret."
fi
fi
- name: Summary
run: |
echo "## ✅ Application Processed Successfully" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Username:** ${{ steps.pr-details.outputs.username }}" >> $GITHUB_STEP_SUMMARY
echo "- **Email Updated:** ${{ steps.update-email.outputs.email_updated }}" >> $GITHUB_STEP_SUMMARY
echo "- **PR Merged:** ${{ steps.merge-pr.outputs.merged }}" >> $GITHUB_STEP_SUMMARY
echo "- **Added to Org:** ✅" >> $GITHUB_STEP_SUMMARY