Skip to content

Commit 7026e20

Browse files
feat(ci): add test gating
1 parent 33da296 commit 7026e20

10 files changed

Lines changed: 350 additions & 2 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: "Robot Test Check Labels"
2+
description: "Fails if test-robot-needed is present but test-robot-done is not."
3+
4+
inputs:
5+
label-test-robot-needed:
6+
description: 'Label indicating the PR needs robot testing'
7+
required: false
8+
default: 'test-robot-needed'
9+
label-test-robot-done:
10+
description: 'Label indicating the PR has been robot-tested'
11+
required: false
12+
default: 'test-robot-done'
13+
token:
14+
description: 'GitHub token or PAT used to post comments (defaults to github.token)'
15+
required: false
16+
default: ''
17+
18+
runs:
19+
using: "composite"
20+
steps:
21+
- name: Check robot test labels
22+
uses: actions/github-script@v7
23+
with:
24+
github-token: ${{ inputs.token || github.token }}
25+
script: |
26+
const labelNeeded = '${{ inputs.label-test-robot-needed }}';
27+
const labelDone = '${{ inputs.label-test-robot-done }}';
28+
const { number: pr, head: { sha } } = context.payload.pull_request;
29+
const repo = context.repo;
30+
31+
const postComment = async (body) => {
32+
try {
33+
await github.rest.issues.createComment({ ...repo, issue_number: pr, body });
34+
} catch (err) {
35+
core.warning(`Could not post comment: ${err.message}`);
36+
}
37+
};
38+
39+
const { data: labels } = await github.rest.issues.listLabelsOnIssue({ ...repo, issue_number: pr });
40+
const has = new Set(labels.map(l => l.name));
41+
42+
if (!has.has(labelNeeded)) {
43+
core.info(`\`${labelNeeded}\` not present — no robot testing required.`);
44+
return;
45+
}
46+
47+
if (has.has(labelDone)) {
48+
core.info(`\`${labelDone}\` present — robot testing confirmed.`);
49+
return;
50+
}
51+
52+
const shaLine = sha ? `\n\n**Commit:** \`${sha}\`` : '';
53+
await postComment(
54+
`### ❌ Robot Testing Required\n\n` +
55+
`\`${labelNeeded}\` is present but \`${labelDone}\` has not been added. This PR cannot be merged until robot testing is complete.${shaLine}\n\n` +
56+
`> [!IMPORTANT]\n> Add \`${labelDone}\` once robot testing is complete.`
57+
);
58+
core.setFailed(`\`${labelNeeded}\` is present but \`${labelDone}\` has not been added — robot testing must be completed before merging.`);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: "Robot Test Label"
2+
description: "When the test-robot-done label is added, posts a comment confirming the robot test status."
3+
4+
inputs:
5+
label-test-robot-needed:
6+
description: 'Label indicating the PR needs robot testing'
7+
required: false
8+
default: 'test-robot-needed'
9+
label-test-robot-done:
10+
description: 'Label indicating the PR has been robot-tested'
11+
required: false
12+
default: 'test-robot-done'
13+
token:
14+
description: 'GitHub token or PAT used to post comments (defaults to github.token)'
15+
required: false
16+
default: ''
17+
18+
runs:
19+
using: "composite"
20+
steps:
21+
- name: Comment on label added
22+
uses: actions/github-script@v7
23+
with:
24+
github-token: ${{ inputs.token || github.token }}
25+
script: |
26+
const labelDone = '${{ inputs.label-test-robot-done }}';
27+
const labelNeeded = '${{ inputs.label-test-robot-needed }}';
28+
const { number: pr, head: { sha } } = context.payload.pull_request;
29+
const repo = context.repo;
30+
31+
const postComment = async (body) => {
32+
try {
33+
await github.rest.issues.createComment({ ...repo, issue_number: pr, body });
34+
} catch (err) {
35+
core.warning(`Could not post comment: ${err.message}`);
36+
}
37+
};
38+
39+
const { data: labels } = await github.rest.issues.listLabelsOnIssue({ ...repo, issue_number: pr });
40+
const has = new Set(labels.map(l => l.name));
41+
const shaLine = sha ? `\n\n**Commit:** \`${sha}\`` : '';
42+
43+
if (!has.has(labelDone)) {
44+
if (has.has(labelNeeded)) {
45+
core.warning(`\`${labelDone}\` not found — \`${labelNeeded}\` is present but testing is not confirmed.`);
46+
await postComment(
47+
`### ⚠️ Robot Testing Incomplete\n\n` +
48+
`\`${labelNeeded}\` is present but \`${labelDone}\` has not been added yet.${shaLine}\n\n` +
49+
`> [!WARNING]\n> Add \`${labelDone}\` once robot testing is complete.`
50+
);
51+
} else {
52+
core.info(`\`${labelDone}\` not present — nothing to do.`);
53+
}
54+
return;
55+
}
56+
57+
core.info(`\`${labelDone}\` found — posting confirmation comment.`);
58+
await postComment(
59+
`### ✅ Robot Test Complete\n\n` +
60+
`\`${labelDone}\` has been added — robot testing has been confirmed for this PR.${shaLine}`
61+
);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: "Robot Test Unlabel"
2+
description: "On new commits, removes the test-robot-done label if present and posts a comment."
3+
4+
inputs:
5+
label-test-robot-needed:
6+
description: 'Label indicating the PR needs robot testing'
7+
required: false
8+
default: 'test-robot-needed'
9+
label-test-robot-done:
10+
description: 'Label indicating the PR has been robot-tested'
11+
required: false
12+
default: 'test-robot-done'
13+
token:
14+
description: 'GitHub token or PAT used to remove labels and post comments (defaults to github.token)'
15+
required: false
16+
default: ''
17+
18+
runs:
19+
using: "composite"
20+
steps:
21+
- name: Remove label and comment
22+
uses: actions/github-script@v7
23+
with:
24+
github-token: ${{ inputs.token || github.token }}
25+
script: |
26+
const labelDone = '${{ inputs.label-test-robot-done }}';
27+
const labelNeeded = '${{ inputs.label-test-robot-needed }}';
28+
const { number: pr, head: { sha } } = context.payload.pull_request;
29+
const repo = context.repo;
30+
31+
const postComment = async (body) => {
32+
try {
33+
await github.rest.issues.createComment({ ...repo, issue_number: pr, body });
34+
} catch (err) {
35+
core.warning(`Could not post comment: ${err.message}`);
36+
}
37+
};
38+
39+
const { data: labels } = await github.rest.issues.listLabelsOnIssue({ ...repo, issue_number: pr });
40+
const has = new Set(labels.map(l => l.name));
41+
42+
if (!has.has(labelDone)) {
43+
core.info(`\`${labelDone}\` not present — nothing to do.`);
44+
return;
45+
}
46+
47+
await github.rest.issues.removeLabel({ ...repo, issue_number: pr, name: labelDone });
48+
core.info(`\`${labelDone}\` removed.`);
49+
50+
const shaLine = sha ? `\n\n**Commit:** \`${sha}\`` : '';
51+
const warning = !has.has(labelNeeded)
52+
? `\n\n> [!WARNING]\n> \`${labelDone}\` was removed but \`${labelNeeded}\` was not present — this PR may not be queued for robot testing.`
53+
: '';
54+
55+
await postComment(
56+
`### 🔄 Robot Test Label Removed\n\n` +
57+
`New commits were pushed. \`${labelDone}\` has been removed and robot testing must be repeated.${shaLine}${warning}`
58+
);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: PR
2+
3+
on:
4+
pull_request:
5+
types: [labeled]
6+
7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.ref }}
9+
cancel-in-progress: true
10+
11+
permissions:
12+
contents: read
13+
pull-requests: write
14+
15+
jobs:
16+
test-robot-label:
17+
if: github.event.label.name == 'test-robot-done'
18+
name: Job
19+
uses: ./.github/workflows/test-robot-label.yml
20+
with:
21+
label-test-robot-done: test-robot-done
22+
label-test-robot-needed: test-robot-needed
23+
24+
test-robot-check:
25+
needs: test-robot-label
26+
name: Job
27+
uses: ./.github/workflows/test-robot-check.yml
28+
with:
29+
label-test-robot-done: test-robot-done
30+
label-test-robot-needed: test-robot-needed
31+
32+
allchecks:
33+
needs: test-robot-check
34+
runs-on: ubuntu-latest
35+
name: Job
36+
steps:
37+
- uses: wechuli/allcheckspassed@v2
38+
with:
39+
poll: false
40+
delay: 0
41+
retries: 0

.github/workflows/pull-request.yml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,38 @@ concurrency:
1111
permissions:
1212
contents: read
1313
packages: write
14+
pull-requests: write
1415

1516
jobs:
17+
test-robot-unlabel:
18+
if: github.event.action == 'synchronize'
19+
name: Job
20+
uses: ./.github/workflows/test-robot-unlabel.yml
21+
with:
22+
label-test-robot-done: test-robot-done
23+
label-test-robot-needed: test-robot-needed
24+
1625
build:
26+
needs: test-robot-unlabel
27+
if: ${{ !failure() && !cancelled() && needs.test-robot-unlabel.result != 'failure' }}
28+
name: Job
29+
uses: ./.github/workflows/build.yml
30+
31+
test-robot-check:
32+
needs: build
33+
name: Job
34+
uses: ./.github/workflows/test-robot-check.yml
35+
with:
36+
label-test-robot-done: test-robot-done
37+
label-test-robot-needed: test-robot-needed
38+
39+
allchecks:
40+
needs: test-robot-check
41+
runs-on: ubuntu-latest
1742
name: Job
18-
uses: ./.github/workflows/build.yaml
43+
steps:
44+
- uses: wechuli/allcheckspassed@v2
45+
with:
46+
poll: false
47+
delay: 0
48+
retries: 0

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ permissions:
1919
jobs:
2020
build:
2121
name: Job
22-
uses: ./.github/workflows/build.yaml
22+
uses: ./.github/workflows/build.yml
2323

2424
promote:
2525
name: Job
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Robot Test Check
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
label-test-robot-done:
7+
description: 'Label name indicating the PR has been test-robot-done'
8+
type: string
9+
required: false
10+
default: 'test-robot-done'
11+
label-test-robot-needed:
12+
description: 'Label name indicating the PR needs robot testing'
13+
type: string
14+
required: false
15+
default: 'test-robot-needed'
16+
17+
permissions:
18+
contents: read
19+
pull-requests: write
20+
21+
jobs:
22+
test-robot-check-labels:
23+
if: contains(github.event.pull_request.labels.*.name, inputs.label-test-robot-needed) || github.run_attempt > 1
24+
name: Robot Test Check
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- name: Robot Test Check
30+
uses: ./.github/actions/test-robot-check
31+
with:
32+
label-test-robot-done: ${{ inputs.label-test-robot-done }}
33+
label-test-robot-needed: ${{ inputs.label-test-robot-needed }}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Robot Test Label
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
label-test-robot-done:
7+
description: 'Label name indicating the PR has been test-robot-done'
8+
type: string
9+
required: false
10+
default: 'test-robot-done'
11+
label-test-robot-needed:
12+
description: 'Label name indicating the PR needs robot testing'
13+
type: string
14+
required: false
15+
default: 'test-robot-needed'
16+
17+
permissions:
18+
contents: read
19+
pull-requests: write
20+
21+
jobs:
22+
test-robot-label:
23+
if: github.event.action == 'labeled' &&
24+
github.event.label.name == inputs['label-test-robot-done']
25+
name: Robot Test Label
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- name: Robot Test Label
31+
uses: ./.github/actions/test-robot-label
32+
with:
33+
label-test-robot-done: ${{ inputs.label-test-robot-done }}
34+
label-test-robot-needed: ${{ inputs.label-test-robot-needed }}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Robot Test Unlabel
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
label-test-robot-done:
7+
description: 'Label name indicating the PR has been test-robot-done'
8+
type: string
9+
required: false
10+
default: 'test-robot-done'
11+
label-test-robot-needed:
12+
description: 'Label name indicating the PR needs robot testing'
13+
type: string
14+
required: false
15+
default: 'test-robot-needed'
16+
17+
permissions:
18+
contents: read
19+
pull-requests: write
20+
21+
jobs:
22+
test-robot-unlabel:
23+
if: contains(github.event.pull_request.labels.*.name, inputs.label-test-robot-done)
24+
name: Robot Test Unlabel
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- name: Robot Test Unlabel
30+
uses: ./.github/actions/test-robot-unlabel
31+
with:
32+
label-test-robot-done: ${{ inputs.label-test-robot-done }}
33+
label-test-robot-needed: ${{ inputs.label-test-robot-needed }}

0 commit comments

Comments
 (0)