Skip to content

Commit c3e5b0e

Browse files
clubandersonclaude
andcommitted
ci: integrate gate logic into e2e workflow
Integrate permission checking and ok-to-test gating directly into the e2e workflow as a gate job. This allows the gating to work immediately without needing a separate workflow file on main first. Flow: - Privileged users (admin/maintain/write): tests run automatically - External contributors: instructions posted, wait for /ok-to-test - /ok-to-test from privileged user: rocket reaction, tests run - /ok-to-test from non-privileged: thumbs down reaction, no tests The gate job outputs should_run and ref, which downstream jobs use to determine whether to proceed and which SHA to checkout. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent da16a4b commit c3e5b0e

File tree

1 file changed

+133
-4
lines changed

1 file changed

+133
-4
lines changed

.github/workflows/ci-e2e-openshift.yaml

Lines changed: 133 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
name: CI - OpenShift E2E Tests
22

3-
# Permissions needed for the build-image job to push to GHCR
43
permissions:
54
contents: read
65
packages: write
6+
pull-requests: write
7+
issues: write
78

89
on:
910
pull_request:
1011
branches:
1112
- main
1213
- dev
14+
issue_comment:
15+
types: [created]
1316
workflow_dispatch:
1417
inputs:
1518
model_id:
@@ -42,14 +45,137 @@ on:
4245
default: '30'
4346

4447
jobs:
45-
# Build the WVA controller image on GitHub-hosted runner (has proper Docker setup)
48+
# Gate: Check permissions and post instructions for external contributors
49+
gate:
50+
runs-on: ubuntu-latest
51+
outputs:
52+
should_run: ${{ steps.check.outputs.should_run }}
53+
ref: ${{ steps.check.outputs.ref }}
54+
steps:
55+
- name: Check permissions and determine if tests should run
56+
id: check
57+
uses: actions/github-script@v7
58+
with:
59+
script: |
60+
const privilegedRoles = ['admin', 'maintain', 'write'];
61+
62+
// Handle workflow_dispatch - always run
63+
if (context.eventName === 'workflow_dispatch') {
64+
core.setOutput('should_run', 'true');
65+
core.setOutput('ref', context.sha);
66+
console.log('workflow_dispatch: running tests');
67+
return;
68+
}
69+
70+
// Handle issue_comment (/ok-to-test)
71+
if (context.eventName === 'issue_comment') {
72+
// Only process comments on PRs that contain /ok-to-test
73+
if (!context.payload.issue.pull_request ||
74+
!context.payload.comment.body.includes('/ok-to-test')) {
75+
core.setOutput('should_run', 'false');
76+
console.log('issue_comment: not a /ok-to-test comment on a PR');
77+
return;
78+
}
79+
80+
// Check commenter permission
81+
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
82+
owner: context.repo.owner,
83+
repo: context.repo.repo,
84+
username: context.payload.comment.user.login
85+
});
86+
const isPrivileged = privilegedRoles.includes(permission.permission);
87+
console.log(`Commenter ${context.payload.comment.user.login} permission: ${permission.permission}, privileged: ${isPrivileged}`);
88+
89+
// Add reaction
90+
await github.rest.reactions.createForIssueComment({
91+
owner: context.repo.owner,
92+
repo: context.repo.repo,
93+
comment_id: context.payload.comment.id,
94+
content: isPrivileged ? 'rocket' : '-1'
95+
});
96+
97+
if (isPrivileged) {
98+
// Get PR SHA
99+
const { data: pr } = await github.rest.pulls.get({
100+
owner: context.repo.owner,
101+
repo: context.repo.repo,
102+
pull_number: context.payload.issue.number
103+
});
104+
core.setOutput('should_run', 'true');
105+
core.setOutput('ref', pr.head.sha);
106+
console.log(`Approved by ${context.payload.comment.user.login}, running tests for PR #${pr.number} at ${pr.head.sha}`);
107+
} else {
108+
core.setOutput('should_run', 'false');
109+
console.log('Commenter is not privileged, not running tests');
110+
}
111+
return;
112+
}
113+
114+
// Handle pull_request
115+
if (context.eventName === 'pull_request') {
116+
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
117+
owner: context.repo.owner,
118+
repo: context.repo.repo,
119+
username: context.payload.pull_request.user.login
120+
});
121+
const isPrivileged = privilegedRoles.includes(permission.permission);
122+
console.log(`PR author ${context.payload.pull_request.user.login} permission: ${permission.permission}, privileged: ${isPrivileged}`);
123+
124+
if (isPrivileged) {
125+
core.setOutput('should_run', 'true');
126+
core.setOutput('ref', context.payload.pull_request.head.sha);
127+
console.log('PR author is privileged, running tests');
128+
} else {
129+
core.setOutput('should_run', 'false');
130+
console.log('PR author is not privileged, posting instructions');
131+
132+
// Check if we already posted instructions
133+
const comments = await github.rest.issues.listComments({
134+
owner: context.repo.owner,
135+
repo: context.repo.repo,
136+
issue_number: context.payload.pull_request.number
137+
});
138+
139+
const botComment = comments.data.find(c =>
140+
c.user.type === 'Bot' &&
141+
c.body.includes('OpenShift E2E tests require approval')
142+
);
143+
144+
if (!botComment) {
145+
await github.rest.issues.createComment({
146+
owner: context.repo.owner,
147+
repo: context.repo.repo,
148+
issue_number: context.payload.pull_request.number,
149+
body: `## OpenShift E2E Tests
150+
151+
OpenShift E2E tests require approval before running on external contributions.
152+
153+
**For maintainers/admins:** Comment \`/ok-to-test\` to approve and run the OpenShift E2E tests.
154+
155+
**For contributors:** Please wait for a maintainer to review and approve your PR for E2E testing.
156+
157+
_This check uses GPU resources on a shared OpenShift cluster._`
158+
});
159+
}
160+
}
161+
return;
162+
}
163+
164+
// Unknown event
165+
core.setOutput('should_run', 'false');
166+
167+
# Build the WVA controller image on GitHub-hosted runner
46168
build-image:
169+
needs: gate
170+
if: needs.gate.outputs.should_run == 'true'
47171
runs-on: ubuntu-latest
48172
outputs:
49173
image_tag: ${{ steps.build.outputs.image_tag }}
50174
steps:
51175
- name: Checkout source
52176
uses: actions/checkout@v4
177+
with:
178+
ref: ${{ needs.gate.outputs.ref }}
53179

54180
- name: Log in to GHCR
55181
uses: docker/login-action@v3
@@ -63,7 +189,7 @@ jobs:
63189
env:
64190
REGISTRY: ghcr.io
65191
IMAGE_NAME: ${{ github.repository }}
66-
GIT_REF: ${{ github.sha }}
192+
GIT_REF: ${{ needs.gate.outputs.ref }}
67193
run: |
68194
# Build image with git ref tag for this PR
69195
# Use first 8 chars of the git ref
@@ -82,7 +208,8 @@ jobs:
82208
# Run e2e tests on OpenShift self-hosted runner
83209
e2e-openshift:
84210
runs-on: [self-hosted, openshift]
85-
needs: build-image
211+
needs: [gate, build-image]
212+
if: needs.gate.outputs.should_run == 'true'
86213
env:
87214
MODEL_ID: ${{ github.event.inputs.model_id || 'unsloth/Meta-Llama-3.1-8B' }}
88215
ACCELERATOR_TYPE: ${{ github.event.inputs.accelerator_type || 'H100' }}
@@ -99,6 +226,8 @@ jobs:
99226
steps:
100227
- name: Checkout source
101228
uses: actions/checkout@v4
229+
with:
230+
ref: ${{ needs.gate.outputs.ref }}
102231

103232
- name: Extract Go version from go.mod
104233
run: sed -En 's/^go (.*)$/GO_VERSION=\1/p' go.mod >> $GITHUB_ENV

0 commit comments

Comments
 (0)