-
Notifications
You must be signed in to change notification settings - Fork 1.5k
245 lines (212 loc) Β· 9.84 KB
/
copilot-pr-handler.yml
File metadata and controls
245 lines (212 loc) Β· 9.84 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
# =====================================================================================
# Copilot SWE Agent PR Handler
# =====================================================================================
# This workflow automatically handles pull requests created by the GitHub Copilot
# SWE Agent (https://github.com/apps/copilot-swe-agent).
#
# It performs two key actions:
# 1. Marks draft PRs as "ready for review"
# 2. Approves pending workflow runs for the PR branch
#
# This is necessary because:
# - PRs from first-time contributors (including bots) require manual approval
# to run workflows for security reasons
# - The Copilot agent creates draft PRs that need to be marked as ready
# =====================================================================================
name: Copilot PR Handler
on:
# Use pull_request_target to get write permissions for PRs from forks/bots
# This is safe here because we're only performing administrative actions,
# not checking out or running code from the PR
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- master
# Allow manual triggering for testing and debugging
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to process (for manual testing)'
required: false
type: string
debug_mode:
description: 'Enable verbose debug logging'
required: false
default: 'false'
type: boolean
# Minimal permissions required for this workflow
# - actions: write - Required to approve workflow runs
# - pull-requests: write - Required to mark PRs as ready for review
# - contents: read - Required to access repository content
permissions:
actions: write
pull-requests: write
contents: read
jobs:
handle-copilot-pr:
name: Handle Copilot PR
runs-on: ubuntu-24.04
# Only run for the meshery/meshery repository
# Only run for PRs from the Copilot SWE agent (copilot[bot])
if: |
github.repository == 'meshery/meshery' &&
(
github.event_name == 'workflow_dispatch' ||
github.event.pull_request.user.login == 'copilot[bot]'
)
steps:
# -------------------------------------------------------------------------
# Step 1: Introspect and log all relevant context for debugging
# -------------------------------------------------------------------------
- name: π Introspect Inputs and Context
run: |
echo "::group::Workflow Context"
echo "Event Name: ${{ github.event_name }}"
echo "Actor: ${{ github.actor }}"
echo "Repository: ${{ github.repository }}"
echo "::endgroup::"
echo "::group::Pull Request Information"
echo "PR Number: ${{ github.event.pull_request.number || inputs.pr_number || 'N/A' }}"
echo "PR Author: ${{ github.event.pull_request.user.login || 'N/A' }}"
echo "PR Draft Status: ${{ github.event.pull_request.draft || 'N/A' }}"
echo "PR Head SHA: ${{ github.event.pull_request.head.sha || 'N/A' }}"
echo "PR Head Ref: ${{ github.event.pull_request.head.ref || 'N/A' }}"
echo "::endgroup::"
echo "::group::Debug Settings"
echo "Debug Mode: ${{ inputs.debug_mode || 'false' }}"
echo "::endgroup::"
# -------------------------------------------------------------------------
# Step 2: Mark PR as ready for review if it's in draft state
# -------------------------------------------------------------------------
- name: π Mark PR as Ready for Review
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
script: |
const prNumber = context.payload.pull_request?.number || parseInt('${{ inputs.pr_number }}') || null;
if (!prNumber) {
core.info('No PR number available, skipping ready for review step');
return;
}
core.info(`Processing PR #${prNumber}`);
try {
// Get PR details to check if it's a draft
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
core.info(`PR #${prNumber} draft status: ${pr.draft}`);
if (pr.draft) {
core.info(`Marking PR #${prNumber} as ready for review...`);
// Use GraphQL API to mark as ready for review
// The REST API doesn't support this operation
await github.graphql(`
mutation($pullRequestId: ID!) {
markPullRequestReadyForReview(input: {pullRequestId: $pullRequestId}) {
pullRequest {
isDraft
}
}
}
`, {
pullRequestId: pr.node_id
});
core.info(`β
PR #${prNumber} has been marked as ready for review`);
} else {
core.info(`PR #${prNumber} is already marked as ready for review`);
}
} catch (error) {
core.warning(`Failed to mark PR as ready for review: ${error.message}`);
// Don't fail the workflow, continue to next step
}
# -------------------------------------------------------------------------
# Step 3: Approve pending workflow runs for this PR
# -------------------------------------------------------------------------
- name: β
Approve Pending Workflow Runs
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
script: |
const prNumber = context.payload.pull_request?.number || parseInt('${{ inputs.pr_number }}') || null;
const headSha = context.payload.pull_request?.head?.sha || null;
const headRef = context.payload.pull_request?.head?.ref || null;
if (!headRef && !headSha) {
core.info('No head ref or SHA available, skipping workflow approval step');
return;
}
core.info(`Looking for pending workflow runs for PR #${prNumber || 'N/A'}`);
core.info(`Head SHA: ${headSha || 'N/A'}, Head Ref: ${headRef || 'N/A'}`);
try {
// List workflow runs that are pending approval
// These are runs with status 'action_required' (waiting for approval)
const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
status: 'action_required',
per_page: 100
});
core.info(`Found ${runs.total_count} workflow run(s) awaiting approval`);
// Filter runs for this PR's branch/SHA
const pendingRuns = runs.workflow_runs.filter(run => {
const matchesSha = headSha && run.head_sha === headSha;
const matchesRef = headRef && run.head_branch === headRef;
return matchesSha || matchesRef;
});
core.info(`Found ${pendingRuns.length} pending run(s) for this PR`);
// Approve each pending run
for (const run of pendingRuns) {
core.info(`Approving workflow run: ${run.name} (ID: ${run.id})`);
try {
await github.rest.actions.approveWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: run.id
});
core.info(`β
Approved workflow run: ${run.name} (ID: ${run.id})`);
} catch (approvalError) {
core.warning(`Failed to approve run ${run.id}: ${approvalError.message}`);
}
}
if (pendingRuns.length === 0) {
core.info('No pending workflow runs found for this PR');
}
} catch (error) {
core.warning(`Failed to approve workflow runs: ${error.message}`);
// Don't fail the workflow
}
# -------------------------------------------------------------------------
# Step 4: Post status comment on the PR
# -------------------------------------------------------------------------
- name: π’ Post Status Comment
if: always()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_ACCESS_TOKEN }}
script: |
const prNumber = context.payload.pull_request?.number || parseInt('${{ inputs.pr_number }}') || null;
if (!prNumber) {
core.info('No PR number available, skipping status comment');
return;
}
const jobStatus = '${{ job.status }}';
const statusEmoji = jobStatus === 'success' ? 'β
' : jobStatus === 'failure' ? 'β' : 'β οΈ';
// Only comment on success to avoid noise
if (jobStatus === 'success') {
const body = `### ${statusEmoji} Copilot PR Handler
This pull request from GitHub Copilot has been automatically processed:
- β
Marked as ready for review (if it was a draft)
- β
Approved pending workflow runs
The CI checks should now run automatically.`;
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: body
});
core.info(`Posted status comment on PR #${prNumber}`);
} catch (error) {
core.warning(`Failed to post status comment: ${error.message}`);
}
}