Repository navigation
195 lines (186 loc) · 10.5 KB
/
Copy pathtrusted-external-sweep.yml
File metadata and controls
195 lines (186 loc) · 10.5 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
name: Trusted External Sweep Dispatch
run-name: Trusted external sweep - PR #${{ github.event.pull_request.number }} @ ${{ github.event.pull_request.head.sha }}
# This workflow is intentionally control-plane only. It never checks out or
# executes pull-request code. A write-authorized collaborator applies one primary
# sweep label to approve the exact external head SHA, then this trusted event
# dispatches the existing e2e workflow from main with repository secrets.
# Each authorized label event approves an exact PR head; no event may cancel another.
on: # zizmor: ignore[concurrency-limits]
# Control plane only: verifies writer permission and the exact labeled head, never executes PR code.
pull_request_target: # zizmor: ignore[dangerous-triggers]
branches:
- main
types:
- labeled
permissions: {}
jobs:
dispatch:
name: dispatch
permissions:
actions: write # Dispatch authorized sweeps.
contents: read
pull-requests: write # Publish PR feedback.
if: >-
github.event.pull_request.head.repo.full_name != github.repository &&
contains(
fromJson('["full-sweep-enabled","non-canary-full-sweep-enabled","full-sweep-fail-fast"]'),
github.event.label.name
)
runs-on: ubuntu-latest
steps:
- name: Authorize exact external revision and dispatch
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ github.token }}
script: |
const primaryLabels = new Set([
'full-sweep-enabled',
'non-canary-full-sweep-enabled',
'full-sweep-fail-fast',
]);
const {data: access} = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: context.actor,
});
if (typeof access?.permission !== 'string' || !access.permission ||
typeof access?.role_name !== 'string' || !access.role_name) {
throw new Error('Invalid repository permission response.');
}
const {permission, role_name: role} = access;
const trustedPermissions = ['admin', 'maintain', 'write'];
if (!trustedPermissions.includes(permission) || !trustedPermissions.includes(role)) {
core.setFailed(
`@${context.actor} has repository permission "${permission}" and role "${role}"; ` +
'both must be write, maintain, or admin to authorize external code.',
);
return;
}
const eventPull = context.payload.pull_request;
const {data: pull} = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: eventPull.number,
});
if (pull.state !== 'open' || pull.draft) {
core.setFailed('The pull request must be open and ready for review.');
return;
}
if (pull.head.repo?.full_name === context.payload.repository.full_name) {
core.setFailed('Trusted external dispatch is only for fork pull requests.');
return;
}
if (pull.head.sha !== eventPull.head.sha) {
core.setFailed(
`The PR advanced from ${eventPull.head.sha} to ${pull.head.sha}; ` +
'remove and re-add the sweep label to approve the new revision.',
);
return;
}
const labels = pull.labels.map((label) => label.name);
const primary = labels.filter((label) => primaryLabels.has(label));
if (primary.length !== 1 || primary[0] !== context.payload.label.name) {
core.setFailed(`Expected exactly the newly applied primary sweep label; found ${primary}.`);
return;
}
// GitHub recomputes the test merge commit asynchronously, so merge_commit_sha
// can still describe an older head. Wait for a settled mergeable state and
// require the merge commit to be built from the exact approved head.
let mergePull = pull;
for (let attempt = 0; attempt < 10 && mergePull.mergeable === null; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 3000));
({data: mergePull} = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pull.number,
}));
}
if (mergePull.head.sha !== pull.head.sha) {
core.setFailed(
`The PR advanced from ${pull.head.sha} to ${mergePull.head.sha}; ` +
'remove and re-add the sweep label to approve the new revision.',
);
return;
}
if (mergePull.mergeable !== true) {
core.setFailed(
mergePull.mergeable === null
? 'GitHub has not finished computing mergeability; re-add the label shortly.'
: 'The PR does not merge cleanly into main; resolve conflicts and re-add the label.',
);
return;
}
const mergeSha = mergePull.merge_commit_sha;
if (!mergeSha) {
core.setFailed('GitHub has not produced a merge commit; resolve conflicts and re-add the label.');
return;
}
const {data: mergeCommit} = await github.rest.git.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: mergeSha,
});
const mergeParents = mergeCommit.parents.map((parent) => parent.sha);
if (mergeParents.length !== 2 || mergeParents[1] !== pull.head.sha) {
core.setFailed(
`Merge commit ${mergeSha} has parents [${mergeParents.join(', ')}], not the approved ` +
`head ${pull.head.sha}; GitHub may still be recomputing it. Re-add the label shortly.`,
);
return;
}
const marker = `External PR #${pull.number} @ ${pull.head.sha.slice(0, 12)}`;
const dispatchStarted = Date.now();
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'e2e-tests.yml',
ref: context.payload.repository.default_branch,
inputs: {
'test-name': marker,
'ref': mergeSha,
'changelog-base-ref': pull.base.sha,
'changelog-head-ref': pull.head.sha,
'trim-conc': 'false',
'all-evals': String(labels.includes('all-evals')),
'evals-only': String(labels.includes('evals-only')),
'fail-fast': String(primary[0].includes('fail-fast')),
'agentx-fast': String(labels.includes('agentx-fast')),
},
});
let dispatchedRun;
for (let attempt = 0; attempt < 15 && !dispatchedRun; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 2000));
const runs = await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'e2e-tests.yml',
event: 'workflow_dispatch',
per_page: 20,
});
dispatchedRun = runs.data.workflow_runs.find((run) =>
run.display_title === `e2e Test - ${marker}` &&
Date.parse(run.created_at) >= dispatchStarted - 5000
);
}
const runLink = dispatchedRun
? `[trusted sweep run](${dispatchedRun.html_url})`
: '[End-to-End Tests workflow](https://github.com/SemiAnalysisAI/InferenceX/actions/workflows/e2e-tests.yml)';
const body = [
`Dispatched ${runLink} for approved external revision \`${pull.head.sha}\`.`,
'New commits are not trusted automatically; remove and re-add the primary sweep label to approve a new SHA.',
'',
`已为获批的外部提交 \`${pull.head.sha}\` 调度${runLink}。`,
'后续新提交不会自动获得信任;如需批准新的 SHA,请移除并重新添加主扫描标签。',
].join('\n');
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pull.number,
body,
});
} catch (error) {
core.warning(
`Sweep dispatched successfully, but the PR comment failed: ${error.message}`,
);
}