Skip to content

Commit f30b558

Browse files
authored
Move Internal Tests to its own workflow (#5147)
# Description of Changes Moving `Internal Tests` to its own workflow so it can be canceled and re-run independently from the rest of CI. # API and ABI breaking changes <!-- If this is an API or ABI breaking change, please apply the corresponding GitHub label. --> # Expected complexity level and risk 1 # Testing - [x] `Internal Tests` succeed on this PR and appear to have run properly Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
1 parent 5c04860 commit f30b558

2 files changed

Lines changed: 159 additions & 145 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -878,151 +878,6 @@ jobs:
878878
exit 1
879879
}
880880
881-
internal-tests:
882-
name: Internal Tests
883-
needs: [lints]
884-
# Skip if not a PR or a push to master
885-
# Skip if this is an external contribution. GitHub secrets will be empty, so the step would fail anyway.
886-
if: ${{ (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master'))
887-
&& (github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork) }}
888-
permissions:
889-
contents: read
890-
pull-requests: read
891-
runs-on: ubuntu-latest
892-
env:
893-
TARGET_OWNER: clockworklabs
894-
TARGET_REPO: SpacetimeDBPrivate
895-
steps:
896-
# Skip the private dispatch entirely when only `docs/` is touched. The job
897-
# itself still completes successfully so required-status-check gating is
898-
# satisfied without spending private-runner time on a docs-only change.
899-
- name: Detect non-docs changes
900-
id: filter
901-
uses: dorny/paths-filter@v3
902-
with:
903-
filters: |
904-
non_docs:
905-
- '!docs/**'
906-
907-
- id: dispatch
908-
name: Trigger tests
909-
if: steps.filter.outputs.non_docs == 'true'
910-
uses: actions/github-script@v7
911-
with:
912-
github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}
913-
script: |
914-
const workflowId = 'ci.yml';
915-
const targetRef = 'master';
916-
const targetOwner = process.env.TARGET_OWNER;
917-
const targetRepo = process.env.TARGET_REPO;
918-
// Use the ref for pull requests because the head sha is brittle (github does some extra dance where it merges in master).
919-
const publicRef = (context.eventName === 'pull_request') ? context.payload.pull_request.head.ref : context.sha;
920-
const publicPrNumber = context.payload.pull_request?.number ?? context.payload.inputs?.pr_number;
921-
const preDispatch = new Date().toISOString();
922-
const inputs = { public_ref: publicRef };
923-
if (publicPrNumber) {
924-
inputs.public_pr_number = String(publicPrNumber);
925-
}
926-
927-
// Dispatch the workflow in the target repository
928-
await github.rest.actions.createWorkflowDispatch({
929-
owner: targetOwner,
930-
repo: targetRepo,
931-
workflow_id: workflowId,
932-
ref: targetRef,
933-
inputs,
934-
});
935-
936-
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
937-
938-
// Find the dispatched run by name
939-
let runId = null;
940-
for (let attempt = 0; attempt < 20 && !runId; attempt++) { // up to ~10 minutes to locate the run
941-
await sleep(5000);
942-
const runsResp = await github.rest.actions.listWorkflowRuns({
943-
owner: targetOwner,
944-
repo: targetRepo,
945-
workflow_id: workflowId,
946-
event: 'workflow_dispatch',
947-
branch: targetRef,
948-
per_page: 50,
949-
});
950-
951-
const expectedName = `CI [public_ref=${publicRef}]`;
952-
const candidates = runsResp.data.workflow_runs
953-
.filter(r => r.name === expectedName && new Date(r.created_at) >= new Date(preDispatch))
954-
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
955-
956-
if (candidates.length > 0) {
957-
runId = candidates[0].id;
958-
break;
959-
}
960-
}
961-
962-
if (!runId) {
963-
core.setFailed('Failed to locate dispatched run in the private repository.');
964-
return;
965-
}
966-
967-
const runUrl = `https://github.com/${targetOwner}/${targetRepo}/actions/runs/${runId}`;
968-
core.info(`View run: ${runUrl}`);
969-
core.setOutput('run_id', String(runId));
970-
core.setOutput('run_url', runUrl);
971-
972-
- name: Wait for Internal Tests to complete
973-
if: steps.filter.outputs.non_docs == 'true'
974-
uses: actions/github-script@v7
975-
with:
976-
github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}
977-
script: |
978-
const targetOwner = process.env.TARGET_OWNER;
979-
const targetRepo = process.env.TARGET_REPO;
980-
const runId = Number(`${{ steps.dispatch.outputs.run_id }}`);
981-
const runUrl = `${{ steps.dispatch.outputs.run_url }}`;
982-
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
983-
984-
core.info(`Waiting for workflow result... ${runUrl}`);
985-
986-
let conclusion = null;
987-
for (let attempt = 0; attempt < 240; attempt++) { // up to ~2 hours
988-
const runResp = await github.rest.actions.getWorkflowRun({
989-
owner: targetOwner,
990-
repo: targetRepo,
991-
run_id: runId
992-
});
993-
const { status, conclusion: c } = runResp.data;
994-
if (status === 'completed') {
995-
conclusion = c || 'success';
996-
break;
997-
}
998-
await sleep(30000);
999-
}
1000-
1001-
if (!conclusion) {
1002-
core.setFailed('Timed out waiting for private workflow to complete.');
1003-
return;
1004-
}
1005-
1006-
if (conclusion !== 'success') {
1007-
core.setFailed(`Private workflow failed with conclusion: ${conclusion}`);
1008-
}
1009-
1010-
- name: Cancel invoked run if workflow cancelled
1011-
if: ${{ cancelled() && steps.dispatch.outputs.run_id }}
1012-
uses: actions/github-script@v7
1013-
with:
1014-
github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}
1015-
script: |
1016-
const targetOwner = process.env.TARGET_OWNER;
1017-
const targetRepo = process.env.TARGET_REPO;
1018-
const runId = Number(`${{ steps.dispatch.outputs.run_id }}`);
1019-
if (!runId) return;
1020-
await github.rest.actions.cancelWorkflowRun({
1021-
owner: targetOwner,
1022-
repo: targetRepo,
1023-
run_id: runId,
1024-
});
1025-
1026881
global_json_policy:
1027882
name: Verify global.json files are symlinks
1028883
runs-on: ubuntu-latest
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
name: Internal Tests
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- master
8+
merge_group:
9+
workflow_dispatch:
10+
11+
run-name: Internal Tests [ref=${{ github.ref }}]
12+
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
permissions:
18+
contents: read
19+
pull-requests: read
20+
21+
jobs:
22+
internal-tests:
23+
name: Internal Tests
24+
# Skip if this is an external contribution. GitHub secrets will be empty, so the step would fail anyway.
25+
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}
26+
runs-on: ubuntu-latest
27+
env:
28+
TARGET_OWNER: clockworklabs
29+
TARGET_REPO: SpacetimeDBPrivate
30+
steps:
31+
# Skip the private dispatch entirely when only `docs/` is touched. The job
32+
# itself still completes successfully so required-status-check gating is
33+
# satisfied without spending private-runner time on a docs-only change.
34+
- name: Detect non-docs changes
35+
id: filter
36+
uses: dorny/paths-filter@v3
37+
with:
38+
filters: |
39+
non_docs:
40+
- '!docs/**'
41+
42+
- id: dispatch
43+
name: Trigger tests
44+
if: steps.filter.outputs.non_docs == 'true'
45+
uses: actions/github-script@v7
46+
with:
47+
github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}
48+
script: |
49+
const workflowId = 'ci.yml';
50+
const targetRef = 'master';
51+
const targetOwner = process.env.TARGET_OWNER;
52+
const targetRepo = process.env.TARGET_REPO;
53+
// Use the ref for pull requests because the head sha is brittle (github does some extra dance where it merges in master).
54+
const publicRef = (context.eventName === 'pull_request') ? context.payload.pull_request.head.ref : context.sha;
55+
const publicPrNumber = context.payload.pull_request?.number;
56+
const preDispatch = new Date().toISOString();
57+
const inputs = { public_ref: publicRef };
58+
if (publicPrNumber) {
59+
inputs.public_pr_number = String(publicPrNumber);
60+
}
61+
62+
// Dispatch the workflow in the target repository
63+
await github.rest.actions.createWorkflowDispatch({
64+
owner: targetOwner,
65+
repo: targetRepo,
66+
workflow_id: workflowId,
67+
ref: targetRef,
68+
inputs,
69+
});
70+
71+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
72+
73+
// Find the dispatched run by name
74+
let runId = null;
75+
for (let attempt = 0; attempt < 20 && !runId; attempt++) { // up to ~10 minutes to locate the run
76+
await sleep(5000);
77+
const runsResp = await github.rest.actions.listWorkflowRuns({
78+
owner: targetOwner,
79+
repo: targetRepo,
80+
workflow_id: workflowId,
81+
event: 'workflow_dispatch',
82+
branch: targetRef,
83+
per_page: 50,
84+
});
85+
86+
const expectedName = `CI [public_ref=${publicRef}]`;
87+
const candidates = runsResp.data.workflow_runs
88+
.filter(r => r.name === expectedName && new Date(r.created_at) >= new Date(preDispatch))
89+
.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
90+
91+
if (candidates.length > 0) {
92+
runId = candidates[0].id;
93+
break;
94+
}
95+
}
96+
97+
if (!runId) {
98+
core.setFailed('Failed to locate dispatched run in the private repository.');
99+
return;
100+
}
101+
102+
const runUrl = `https://github.com/${targetOwner}/${targetRepo}/actions/runs/${runId}`;
103+
core.info(`View run: ${runUrl}`);
104+
core.setOutput('run_id', String(runId));
105+
core.setOutput('run_url', runUrl);
106+
107+
- name: Wait for Internal Tests to complete
108+
if: steps.filter.outputs.non_docs == 'true'
109+
uses: actions/github-script@v7
110+
with:
111+
github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}
112+
script: |
113+
const targetOwner = process.env.TARGET_OWNER;
114+
const targetRepo = process.env.TARGET_REPO;
115+
const runId = Number(`${{ steps.dispatch.outputs.run_id }}`);
116+
const runUrl = `${{ steps.dispatch.outputs.run_url }}`;
117+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
118+
119+
core.info(`Waiting for workflow result... ${runUrl}`);
120+
121+
let conclusion = null;
122+
for (let attempt = 0; attempt < 240; attempt++) { // up to ~2 hours
123+
const runResp = await github.rest.actions.getWorkflowRun({
124+
owner: targetOwner,
125+
repo: targetRepo,
126+
run_id: runId
127+
});
128+
const { status, conclusion: c } = runResp.data;
129+
if (status === 'completed') {
130+
conclusion = c || 'success';
131+
break;
132+
}
133+
await sleep(30000);
134+
}
135+
136+
if (!conclusion) {
137+
core.setFailed('Timed out waiting for private workflow to complete.');
138+
return;
139+
}
140+
141+
if (conclusion !== 'success') {
142+
core.setFailed(`Private workflow failed with conclusion: ${conclusion}`);
143+
}
144+
145+
- name: Cancel invoked run if workflow cancelled
146+
if: ${{ cancelled() && steps.dispatch.outputs.run_id }}
147+
uses: actions/github-script@v7
148+
with:
149+
github-token: ${{ secrets.SPACETIMEDB_PRIVATE_TOKEN }}
150+
script: |
151+
const targetOwner = process.env.TARGET_OWNER;
152+
const targetRepo = process.env.TARGET_REPO;
153+
const runId = Number(`${{ steps.dispatch.outputs.run_id }}`);
154+
if (!runId) return;
155+
await github.rest.actions.cancelWorkflowRun({
156+
owner: targetOwner,
157+
repo: targetRepo,
158+
run_id: runId,
159+
});

0 commit comments

Comments
 (0)