Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -527,3 +527,18 @@ jobs:
- name: Validate installed CLI release candidate
if: steps.plan.outputs.cli_package == 'true'
run: npm run release:cli:smoke -- packages/cli/release/*.tgz

# Reuse the built output in this runner. These fixtures exercise the
# minimum supported Node's event-loop and experimental SQLite behavior.
- name: Select minimum supported Node
if: steps.plan.outputs.runtime_host == 'true' || contains(steps.plan.outputs.standard_workspaces, 'packages/storage')
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.19.0'
Comment thread
seekskyworld marked this conversation as resolved.

- name: Test Node 22 Host and storage lifetimes
if: steps.plan.outputs.runtime_host == 'true' || contains(steps.plan.outputs.standard_workspaces, 'packages/storage')
run: >-
node --test --test-concurrency=1
packages/runtime-host/dist/__tests__/resumable-peer-stream.test.js
packages/storage/dist/__tests__/managed-dependency-environment-crash.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,11 @@ function dependencySource() {
async function waitForChildReady(child: ChildProcess): Promise<void> {
await new Promise<void>((resolve, reject) => {
let output = '';
const timeout = setTimeout(() => finish(new Error('owner child did not become ready')), 15_000);
let diagnostics = '';
const timeout = setTimeout(
() => finish(new Error(`owner child did not become ready: ${diagnostics}`)),
15_000,
);
const finish = (error?: Error) => {
clearTimeout(timeout);
child.stdout?.off('data', onData);
Expand All @@ -186,9 +190,14 @@ async function waitForChildReady(child: ChildProcess): Promise<void> {
output += chunk.toString('utf8');
if (output.includes('READY\n')) finish();
};
const onErrorData = (chunk: Buffer) => finish(new Error(chunk.toString('utf8')));
// READY is the startup protocol; stderr may contain Node 22 SQLite warnings.
// Keep bounded diagnostics for exit/timeout failures without rejecting warnings.
const onErrorData = (chunk: Buffer) => {
diagnostics = (diagnostics + chunk.toString('utf8')).slice(-8_192);
};
const onError = (error: Error) => finish(error);
const onExit = (code: number | null) => finish(new Error(`owner child exited early: ${code}`));
const onExit = (code: number | null) =>
finish(new Error(`owner child exited early: ${code}: ${diagnostics}`));
child.stdout?.on('data', onData);
child.stderr?.on('data', onErrorData);
child.on('error', onError);
Expand Down
30 changes: 30 additions & 0 deletions scripts/ci-workflow-policy.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,3 +1173,33 @@ function checkoutSteps(name) {
[]
);
}

test('the minimum Node verifies Host and storage lifecycle fixtures after building', () => {
const workflow = readWorkflow('ci.yml');
const start = workflow.indexOf(' - name: Select minimum supported Node');
assert.ok(start > workflow.indexOf(' - name: Build\n'));
const steps = workflow.slice(start);
const gate =
"if: steps.plan.outputs.runtime_host == 'true' || contains(steps.plan.outputs.standard_workspaces, 'packages/storage')";
assert.equal(steps.split(gate).length - 1, 2, 'setup and tests share the affected-surface gate');
const engines = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
.engines.node;
const lowerBounds = engines.split('||').map((range) => {
const match = /^\s*(?:\^|>=)(\d+\.\d+\.\d+)\s*$/u.exec(range);
assert.ok(match, `unsupported Node engine range: ${range}`);
return match[1];
});
lowerBounds.sort((left, right) => {
const a = left.split('.').map(Number);
const b = right.split('.').map(Number);
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
});
assert.equal(/node-version: '([^']+)'/u.exec(steps)?.[1], lowerBounds[0]);
assert.match(steps, /node --test --test-concurrency=1/u);
assert.match(steps, /packages\/runtime-host\/dist\/__tests__\/resumable-peer-stream\.test\.js/u);
assert.match(
steps,
/packages\/storage\/dist\/__tests__\/managed-dependency-environment-crash\.test\.js/u,
);
assert.doesNotMatch(steps, /continue-on-error/u);
});
Loading