-
Notifications
You must be signed in to change notification settings - Fork 355
Expand file tree
/
Copy pathauto-merge-api-doc-updates.yaml
More file actions
221 lines (189 loc) · 8.91 KB
/
Copy pathauto-merge-api-doc-updates.yaml
File metadata and controls
221 lines (189 loc) · 8.91 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
name: Auto-merge API doc updates
on:
pull_request_target:
types: [opened, synchronize, reopened]
# Trigger when the main testing workflow completes
workflow_run:
workflows: ["Get PR URL, start dependent jobs"]
types: [completed]
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
runs-on: ubuntu-latest
steps:
- name: Get PR details
id: pr
uses: actions/github-script@v7
with:
script: |
let prNumber;
// Determine PR number based on trigger type
if (context.eventName === 'pull_request_target') {
prNumber = context.payload.pull_request.number;
console.log(`trigger type is pull_request_target and PR number is ${prNumber}`)
} else if (context.eventName === 'workflow_run') {
console.log('trigger type is workflow_run')
const headBranch = context.payload.workflow_run.head_branch;
console.log(`workflow run head branch: ${headBranch}`)
// Check if workflow_run has pull_requests array
if (context.payload.workflow_run.pull_requests && context.payload.workflow_run.pull_requests.length > 0) {
console.log(`found ${context.payload.workflow_run.pull_requests.length} PR(s) in workflow_run payload`)
// Match PR by head branch to ensure we get the correct one
const matchingPr = context.payload.workflow_run.pull_requests.find(pr =>
pr.head.ref === headBranch
);
if (matchingPr) {
prNumber = matchingPr.number;
console.log(`matched PR #${prNumber} by head branch: ${headBranch}`)
} else {
// Fallback to first PR if no branch match (shouldn't happen)
// @todo - after testing let's remove this.
prNumber = context.payload.workflow_run.pull_requests[0].number;
console.log(`No branch match found, using first PR #${prNumber}`)
}
} else {
console.log('No pull_requests in workflow_run payload, attempting commit lookup')
// Fallback: Get PR associated with the workflow run's head SHA
const headSha = context.payload.workflow_run.head_sha;
console.log(`Head SHA: ${headSha}`)
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: headSha
});
// Filter to open PRs with matching head branch
const matchingPrs = prs.filter(pr =>
pr.state === 'open' && pr.head.ref === headBranch
);
if (matchingPrs.length === 0) {
console.log(`No open PR found with branch ${headBranch} for this workflow run`);
return null;
}
prNumber = matchingPrs[0].number;
console.log(`Retrieved PR #${prNumber} from commit lookup matching branch ${headBranch}`)
}
} else {
console.log(`Unexpected event type: ${context.eventName}`);
return null;
}
// Get full PR details
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
console.log(`PR #${pr.number} details:`);
console.log(`- Author: ${pr.user.login}`);
console.log(`- Branch: ${pr.head.ref}`);
console.log(`- From fork: ${pr.head.repo.fork}`);
console.log(`- Head SHA: ${pr.head.sha}`);
console.log(`- Triggered by: ${context.eventName}`);
return {
number: pr.number,
author: pr.user.login,
branch: pr.head.ref,
is_fork: pr.head.repo.fork,
head_sha: pr.head.sha
};
- name: Check all required checks have passed
id: check_status
if: steps.pr.outputs.result != 'null'
uses: actions/github-script@v7
with:
script: |
const prData = JSON.parse('${{ steps.pr.outputs.result }}');
const headSha = prData.head_sha;
// Get combined status for the commit
const { data: status } = await github.rest.repos.getCombinedStatusForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: headSha
});
// Get check runs for the commit
const { data: checkRuns } = await github.rest.checks.listForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: headSha
});
console.log(`Combined status state: ${status.state}`);
console.log(`Total statuses: ${status.statuses.length}`);
console.log(`Total check runs: ${checkRuns.total_count}`);
// Check if all commit statuses are success
const allStatusesSuccess = status.statuses.every(s => s.state === 'success');
// Check if all check runs are success (filtering out skipped and this workflow)
const relevantCheckRuns = checkRuns.check_runs.filter(run =>
run.name !== 'auto-merge' &&
run.conclusion !== 'skipped' &&
run.conclusion !== null
);
const allCheckRunsSuccess = relevantCheckRuns.every(run =>
run.conclusion === 'success' || run.conclusion === 'neutral'
);
console.log('Status check details:');
status.statuses.forEach(s => {
console.log(` - ${s.context}: ${s.state}`);
});
console.log('Check run details:');
relevantCheckRuns.forEach(run => {
console.log(` - ${run.name}: ${run.conclusion}`);
});
const allChecksPassed = allStatusesSuccess && allCheckRunsSuccess && status.state === 'success';
console.log(`All checks passed: ${allChecksPassed}`);
return allChecksPassed;
- name: Check PR conditions and merge if all conditions are met and tests have passed
id: check_pr_conditions
if: steps.check_status.outputs.result == 'true'
uses: actions/github-script@v7
with:
script: |
let prCanBeMerged = false
const prData = JSON.parse('${{ steps.pr.outputs.result }}');
console.log(`Evaluating merge conditions for PR #${prData.number}:`);
console.log(`- Author: ${prData.author} (expected: platformsh-devrel)`);
console.log(`- Branch: ${prData.branch} (expected: update-api-doc)`);
console.log(`- From fork: ${prData.is_fork} (expected: false)`);
// Check if PR meets all conditions for auto-merge
if (
prData.author === 'platformsh-devrel' &&
prData.branch === 'update-api-doc' &&
!prData.is_fork
) {
try {
await github.rest.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prData.number,
merge_method: 'merge',
commit_title: `Merge PR #${prData.number}: Update API documentation`,
commit_message: `Auto-merged after all checks passed.\n\nBranch: ${prData.branch}\nAuthor: ${prData.author}`
});
console.log('PR merged successfully');
prCanBeMerged = true
} catch (error) {
console.error('Failed to auto merge PR:', error.message);
core.setFailed(error.message);
}
} else {
console.log('Skipping as PR does not meet auto-merge criteria:');
if (prData.author !== 'platformsh-devrel') {
console.log(` - Author mismatch: ${prData.author}`);
}
if (prData.branch !== 'update-api-doc') {
console.log(` - Branch mismatch: ${prData.branch}`);
}
if (prData.is_fork) {
console.log(` - PR is from a fork`);
}
}
return prCanBeMerged
- name: Checks not passed yet
if: steps.check_status.outputs.result == 'false'
run: |
echo "::notice::Not all required checks have passed yet. Auto-merge will not proceed."
echo "This is normal for PRs that are still running checks. The workflow will run again when the PR is synchronized."
- name: Pr conditions not met
if: steps.check_pr_conditions.outputs.result == 'false'
run: |
echo "::notice::Conditions not met for a merge to occur. Skipping."