Skip to content

Commit d0f87eb

Browse files
committed
test(e2e): allow external PRs via ok-to-test label
Add ok-to-test label gating as a fallback for external contributors who are not org members or repo collaborators. Maintainers can approve external PRs by adding the ok-to-test label after code review. The label is automatically removed on synchronize, opened, and reopened events to prevent running CI on new untrusted code without re-approval. Signed-off-by: Zaki Shaikh <zashaikh@redhat.com> Assisted-by: Claude Opus 4.6 (via Claude Code)
1 parent 71d5588 commit d0f87eb

File tree

1 file changed

+37
-11
lines changed

1 file changed

+37
-11
lines changed

.github/workflows/e2e.yaml

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ on:
1919
- opened
2020
- reopened
2121
- synchronize
22+
- labeled
2223
paths:
2324
- "**.go"
2425
- ".github/workflows/**"
@@ -32,7 +33,8 @@ jobs:
3233
if: >
3334
github.event_name == 'schedule' ||
3435
github.event_name == 'workflow_dispatch' ||
35-
github.event_name == 'pull_request_target'
36+
(github.event_name == 'pull_request_target' && github.event.action != 'labeled') ||
37+
(github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.event.label.name == 'ok-to-test')
3638
concurrency:
3739
group: ${{ github.workflow }}-${{ matrix.provider }}-${{ github.event.pull_request.number || github.ref_name }}
3840
cancel-in-progress: true
@@ -137,6 +139,27 @@ jobs:
137139
core.info(`📋 Repository: ${repoOwner}/${repoName}`);
138140
core.info(`🏢 Target organization: ${targetOrg}`);
139141
142+
// Security: On non-labeled events (opened, reopened, synchronize),
143+
// remove the ok-to-test label if present. This prevents an external
144+
// contributor from pushing malicious code after a maintainer approved via label.
145+
if (context.payload.action !== 'labeled') {
146+
const currentLabels = context.payload.pull_request.labels.map(l => l.name);
147+
if (currentLabels.includes('ok-to-test')) {
148+
core.info(`🔒 Removing ok-to-test label due to '${context.payload.action}' event — re-approval required.`);
149+
try {
150+
await github.rest.issues.removeLabel({
151+
owner: repoOwner,
152+
repo: repoName,
153+
issue_number: context.payload.pull_request.number,
154+
name: 'ok-to-test',
155+
});
156+
core.info(` Label removed successfully.`);
157+
} catch (err) {
158+
core.warning(` Failed to remove ok-to-test label: ${err.message}`);
159+
}
160+
}
161+
}
162+
140163
// Condition 1: Check if the user is a trusted bot.
141164
const trustedBots = ["dependabot[bot]", "renovate[bot]"];
142165
core.info(`🤖 Checking if @${actor} is a trusted bot...`);
@@ -257,21 +280,24 @@ jobs:
257280
);
258281
return; // Success
259282
} else {
260-
// If we reach here, no conditions were met. This is the final failure.
261283
core.info(` ❌ Permission '${permission}' is insufficient (requires 'write' or 'admin')`);
262-
core.setFailed(
263-
`❌ Permission check failed. User @${actor} did not meet any required conditions (trusted bot, org member, or repo write access).`,
264-
);
265-
return;
266284
}
267285
} catch (error) {
268-
// This error means they are not even a collaborator.
269286
core.info(` ❌ Collaborator permission check failed: ${error.message}`);
270-
core.setFailed(
271-
`❌ Permission check failed. User @${actor} is not a collaborator on this repository and did not meet other conditions.`,
272-
);
273-
return;
274287
}
288+
289+
// Condition 4: Check for ok-to-test label (for external contributors approved by maintainers).
290+
// Only users with repo write access can add labels, so this is inherently gated.
291+
core.info(`\n🏷️ Condition 4: Checking for ok-to-test label...`);
292+
if (context.payload.action === 'labeled' && context.payload.label && context.payload.label.name === 'ok-to-test') {
293+
core.info(`✅ Condition met: ok-to-test label applied by @${context.actor}. Proceeding with tests.`);
294+
return; // Success
295+
}
296+
297+
core.info(` ❌ No ok-to-test label event detected.`);
298+
core.setFailed(
299+
`❌ Permission check failed. User @${actor} did not meet any required conditions (trusted bot, org/team member, repo write access, or ok-to-test label).`,
300+
);
275301
}
276302
277303
run().catch(err => {

0 commit comments

Comments
 (0)