Skip to content

Commit 3aa1525

Browse files
committed
try automatic issue and copilot assignment on failed release workflow
1 parent c449680 commit 3aa1525

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
3+
name: Copilot Release Autofix
4+
5+
on:
6+
workflow_run:
7+
workflows:
8+
- Release
9+
types:
10+
- completed
11+
12+
permissions:
13+
contents: read
14+
issues: write
15+
16+
jobs:
17+
copilot-autofix:
18+
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
19+
name: Create Copilot Autofix Task
20+
runs-on:
21+
group: default
22+
steps:
23+
- name: Open issue and assign Copilot
24+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
25+
env:
26+
RUN_ID: ${{ github.event.workflow_run.id }}
27+
RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
28+
RUN_URL: ${{ github.event.workflow_run.html_url }}
29+
RUN_SHA: ${{ github.event.workflow_run.head_sha }}
30+
with:
31+
github-token: ${{ secrets.OR_PAT }}
32+
script: |-
33+
const owner = context.repo.owner;
34+
const repo = context.repo.repo;
35+
const runId = Number(process.env.RUN_ID);
36+
const runAttempt = Number(process.env.RUN_ATTEMPT);
37+
const runUrl = process.env.RUN_URL;
38+
const runSha = process.env.RUN_SHA;
39+
40+
const jobs = await github.paginate(
41+
github.rest.actions.listJobsForWorkflowRunAttempt,
42+
{
43+
owner,
44+
repo,
45+
run_id: runId,
46+
attempt_number: runAttempt,
47+
per_page: 100,
48+
},
49+
);
50+
51+
const failedJobs = jobs.filter((job) => job.conclusion === 'failure');
52+
if (failedJobs.length === 0) {
53+
core.info('No failed jobs found for failed workflow run. Skipping.');
54+
return;
55+
}
56+
57+
const appMatches = new Set();
58+
for (const job of failedJobs) {
59+
const buildMatch = job.name.match(/Build\s+(.+?)(?:\s+\/|$)/);
60+
if (buildMatch && buildMatch[1]) {
61+
appMatches.add(buildMatch[1].trim());
62+
}
63+
}
64+
65+
const appList = [...appMatches].sort();
66+
const appText = appList.length > 0 ? appList.join(', ') : 'unknown-app';
67+
const title = `fix(ci): release failure for ${appText} (run ${runId})`;
68+
69+
const existingIssues = await github.paginate(github.rest.issues.listForRepo, {
70+
owner,
71+
repo,
72+
state: 'open',
73+
labels: 'copilot-autofix',
74+
per_page: 100,
75+
});
76+
77+
const duplicate = existingIssues.find((issue) => issue.title === title);
78+
if (duplicate) {
79+
core.info(`Autofix issue already exists: #${duplicate.number}`);
80+
return;
81+
}
82+
83+
const failedJobLines = failedJobs.slice(0, 20).map((job) => {
84+
const failedSteps = (job.steps || [])
85+
.filter((step) => step.conclusion === 'failure')
86+
.map((step) => ` - ${step.name}`)
87+
.join('\n');
88+
return [
89+
`- ${job.name}`,
90+
` - URL: ${job.html_url}`,
91+
failedSteps || ' - Failed step: (not reported by API)',
92+
].join('\n');
93+
}).join('\n');
94+
95+
const body = [
96+
'Automated release build failed. Please investigate and propose a code fix in a pull request.',
97+
'',
98+
'### Context',
99+
`- Workflow run: ${runUrl}`,
100+
`- Commit: ${runSha}`,
101+
`- Apps: ${appText}`,
102+
'',
103+
'### Failed jobs / steps',
104+
failedJobLines,
105+
'',
106+
'### Task',
107+
'- Reproduce the failure from this run.',
108+
'- Make the smallest safe fix in the affected app directory.',
109+
'- Ensure any changed docker-bake.hcl values are used by the corresponding Dockerfile.',
110+
'- Open a pull request with a Conventional Commits style title.',
111+
'',
112+
'### Validation',
113+
'- Validate with `docker buildx bake --print` from the affected app directory.',
114+
'- Include a concise root-cause explanation in the PR body.',
115+
'',
116+
'_Created automatically by `copilot-release-autofix.yaml`._',
117+
].join('\n');
118+
119+
await github.rest.issues.createLabel({
120+
owner,
121+
repo,
122+
name: 'copilot-autofix',
123+
color: '1d76db',
124+
description: 'Automated Copilot issue created from failed release builds',
125+
}).catch((error) => {
126+
if (error.status !== 422) {
127+
throw error;
128+
}
129+
});
130+
131+
try {
132+
const issue = await github.rest.issues.create({
133+
owner,
134+
repo,
135+
title,
136+
body,
137+
assignees: ['copilot'],
138+
labels: ['copilot-autofix'],
139+
});
140+
core.info(`Created Copilot autofix issue #${issue.data.number}`);
141+
} catch (error) {
142+
core.warning(`Failed to assign issue to Copilot: ${error.message}`);
143+
144+
const issue = await github.rest.issues.create({
145+
owner,
146+
repo,
147+
title,
148+
body: `${body}\n\n> Note: auto-assignment to @copilot failed. Assign manually in the issue UI if needed.`,
149+
labels: ['copilot-autofix'],
150+
});
151+
152+
core.info(`Created fallback autofix issue #${issue.data.number} without assignee`);
153+
}

0 commit comments

Comments
 (0)