Skip to content
Merged
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
30 changes: 28 additions & 2 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -496,11 +496,37 @@ jobs:
# Clean-room install of the generated npm packages under Node: bare-install
# CLI startup, optional-peer metadata, extension package loading, and a real
# starter dev-server render (see scripts/test/npm-install-smoke.sh).
npm-smoke-node-versions:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
timeout-minutes: 10
name: npm smoke Node version contract
outputs:
node_versions: ${{ steps.versions.outputs.node_versions }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup-deno
timeout-minutes: 5
- name: Read npm smoke Node versions
id: versions
run: |
deno eval '
import { NPM_SMOKE_NODE_VERSIONS } from "./scripts/build/runtime-support.ts";
console.log(`node_versions=${JSON.stringify(NPM_SMOKE_NODE_VERSIONS)}`);
' >> "$GITHUB_OUTPUT"

tests-npm-install-smoke:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
needs: npm-smoke-node-versions
runs-on: ubuntu-latest
timeout-minutes: 30
name: tests (npm install smoke)
name: "tests (npm install smoke: Node ${{ matrix.node-version }})"
strategy:
fail-fast: false
matrix:
node-version: ${{ fromJSON(needs.npm-smoke-node-versions.outputs.node_versions) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -511,7 +537,7 @@ jobs:
warm-cache: "true"
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6
with:
node-version: "24"
node-version: ${{ matrix.node-version }}
package-manager-cache: false
- name: Install TypeScript toolchain
run: npm --prefix storybook ci --ignore-scripts
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@
"lint:render-mode-defaults": "deno run --allow-read scripts/lint/audit-render-mode-defaults.ts",
"lint:dnt-meta-properties": "deno run --config=scripts/test.deno.json --frozen --allow-read scripts/build/dnt-meta-property-safety.ts",
"test:layout": "deno run --config=scripts/test.deno.json --no-check --allow-read --allow-run=git --allow-env=TEST_LAYOUT_MIGRATION_BASE_REF scripts/test/test-layout.ts",
"test:scripts": "deno test --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-env=DENO_DIR,HOME,XDG_CACHE_HOME,LOCALAPPDATA,USERPROFILE --allow-run scripts/lint/check-deno-version.test.ts scripts/ci/automated-review-gate.test.ts scripts/ci/prepare-rc-build.test.ts scripts/ci/publish-npm-packages.test.ts scripts/ci/setup-deno-workflow.test.ts scripts/test/coverage-ci.test.ts scripts/test/run-suite.test.ts scripts/test/run-test-file.test.ts scripts/test/suites.test.ts scripts/test/test-layout.test.ts scripts/build/compile-binary.test.ts scripts/build/dnt-jsx-runtime.test.ts scripts/build/dnt-meta-property-safety.test.ts scripts/build/dnt-polyfill.test.ts scripts/build/generate-sbom.test.ts scripts/build/generated-artifact-checks.test.ts scripts/build/npm-dependency-sources.test.ts scripts/build/npm-extension-package-metadata.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/npm-react-shims.test.ts scripts/build/npm-runtime-helper-contract.test.ts scripts/build/prepare-framework-sources.test.ts scripts/build/report-artifact-sizes.test.ts scripts/build/run-generate.test.ts scripts/docs/docs-coverage.test.ts scripts/docs/generate-api-reference.test.ts scripts/docs/guide-validation.test.ts scripts/docs/validate-tracked-docs.test.ts scripts/lint/audit-chat-composability.test.ts scripts/lint/audit-core-deps.test.ts scripts/lint/audit-cwd-relative-test-reads.test.ts scripts/lint/audit-cross-runtime-jsr.test.ts scripts/lint/audit-dependency-boundaries.test.ts scripts/lint/audit-extension-capabilities.test.ts scripts/lint/audit-extension-contracts.test.ts scripts/lint/audit-deps.test.ts scripts/lint/check-module-boundaries.test.ts scripts/lint/lint-config.test.ts scripts/lint/ban-test-only.test.ts scripts/lint/check-sanitizer-baseline.test.ts scripts/lint/check-skipped-tests-baseline.test.ts scripts/lint/audit-anti-slop.test.ts scripts/lint/audit-render-mode-defaults.test.ts scripts/lint/check-test-typecheck-baseline.test.ts scripts/lint/check-coverage.test.ts scripts/security/audit-npm.test.ts scripts/security/secret-scanning-config.test.ts scripts/security/submit-dependency-snapshot.test.ts scripts/test/template-runtime-e2e.test.ts scripts/test/runtime-inference-critical-flow.test.ts && deno task test:tool-search-live",
"test:scripts": "deno test --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-env=DENO_DIR,HOME,XDG_CACHE_HOME,LOCALAPPDATA,USERPROFILE --allow-run scripts/lint/check-deno-version.test.ts scripts/ci/automated-review-gate.test.ts scripts/ci/prepare-rc-build.test.ts scripts/ci/publish-npm-packages.test.ts scripts/ci/setup-deno-workflow.test.ts scripts/test/coverage-ci.test.ts scripts/test/run-suite.test.ts scripts/test/run-test-file.test.ts scripts/test/suites.test.ts scripts/test/test-layout.test.ts scripts/build/compile-binary.test.ts scripts/build/dnt-jsx-runtime.test.ts scripts/build/dnt-meta-property-safety.test.ts scripts/build/dnt-polyfill.test.ts scripts/build/generate-sbom.test.ts scripts/build/generated-artifact-checks.test.ts scripts/build/npm-dependency-sources.test.ts scripts/build/npm-extension-package-metadata.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/npm-react-shims.test.ts scripts/build/npm-runtime-helper-contract.test.ts scripts/build/prepare-framework-sources.test.ts scripts/build/report-artifact-sizes.test.ts scripts/build/runtime-support.test.ts scripts/build/run-generate.test.ts scripts/docs/docs-coverage.test.ts scripts/docs/generate-api-reference.test.ts scripts/docs/guide-validation.test.ts scripts/docs/validate-tracked-docs.test.ts scripts/lint/audit-chat-composability.test.ts scripts/lint/audit-core-deps.test.ts scripts/lint/audit-cwd-relative-test-reads.test.ts scripts/lint/audit-cross-runtime-jsr.test.ts scripts/lint/audit-dependency-boundaries.test.ts scripts/lint/audit-extension-capabilities.test.ts scripts/lint/audit-extension-contracts.test.ts scripts/lint/audit-deps.test.ts scripts/lint/check-module-boundaries.test.ts scripts/lint/lint-config.test.ts scripts/lint/ban-test-only.test.ts scripts/lint/check-sanitizer-baseline.test.ts scripts/lint/check-skipped-tests-baseline.test.ts scripts/lint/audit-anti-slop.test.ts scripts/lint/audit-render-mode-defaults.test.ts scripts/lint/check-test-typecheck-baseline.test.ts scripts/lint/check-coverage.test.ts scripts/security/audit-npm.test.ts scripts/security/secret-scanning-config.test.ts scripts/security/submit-dependency-snapshot.test.ts scripts/test/template-runtime-e2e.test.ts scripts/test/runtime-inference-critical-flow.test.ts && deno task test:tool-search-live",
"test:sentry-runtime-packages": "deno test --config=scripts/test.deno.json --no-check --no-lock --allow-read --allow-write --allow-run --allow-env=DENO_DIR,HOME,XDG_CACHE_HOME,LOCALAPPDATA,USERPROFILE scripts/build/sentry-runtime-packages.test.ts",
"test:tool-search-live": "VF_DISABLE_LRU_INTERVAL=1 deno test --no-check -A tests/agent/verify-tool-search-live.test.ts",
"test:cross-runtime": "deno run --allow-all src/platform/compat/cross-runtime.test.ts",
Expand Down
117 changes: 117 additions & 0 deletions scripts/build/runtime-support.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { assert, assertEquals } from "#veryfront/testing/assert.ts";
import { describe, it } from "#veryfront/testing/bdd.ts";
import { parse } from "#std/yaml/parse";
import {
CURRENT_NPM_SMOKE_NODE_RELEASE_LINE,
MINIMUM_NODE_RELEASE_LINE,
MINIMUM_NODE_VERSION,
NPM_SMOKE_NODE_VERSIONS,
} from "./runtime-support.ts";

const WORKFLOW_PATH = new URL(
"../../.github/workflows/cicd.yml",
import.meta.url,
);

function record(value: unknown, label: string): Record<string, unknown> {
if (typeof value !== "object" || value === null || Array.isArray(value)) {
throw new TypeError(`${label} must be a record`);
}
return value as Record<string, unknown>;
}

function steps(
job: Record<string, unknown>,
label: string,
): Array<Record<string, unknown>> {
if (!Array.isArray(job.steps)) {
throw new TypeError(`${label} steps must be an array`);
}
return job.steps.map((step, index) => record(step, `${label} step ${index}`));
}

describe("npm smoke Node support contract", () => {
it("runs the packed npm smoke on the oldest supported and current release lines", async () => {
const workflow = record(
parse(await Deno.readTextFile(WORKFLOW_PATH)),
"CI workflow",
);
const jobs = record(workflow.jobs, "CI workflow jobs");
const versionsJob = record(
jobs["npm-smoke-node-versions"],
"npm smoke Node versions job",
);
const versionsStep = steps(versionsJob, "npm smoke Node versions job").find(
(step) => step.id === "versions",
);
assert(
versionsStep,
"The version contract job must publish its Node matrix",
);
assertEquals(
record(versionsJob.outputs, "npm smoke Node versions outputs")
.node_versions,
"${{ steps.versions.outputs.node_versions }}",
);
assertEquals(
typeof versionsStep.run === "string" &&
versionsStep.run.includes("NPM_SMOKE_NODE_VERSIONS"),
true,
"The workflow must derive the matrix from the runtime support module",
);

const smokeJob = record(
jobs["tests-npm-install-smoke"],
"npm install smoke job",
);
assertEquals(smokeJob.needs, "npm-smoke-node-versions");
assertEquals(
record(
record(smokeJob.strategy, "npm smoke strategy").matrix,
"npm smoke matrix",
)[
"node-version"
],
"${{ fromJSON(needs.npm-smoke-node-versions.outputs.node_versions) }}",
);
assertEquals(
smokeJob.name,
"tests (npm install smoke: Node ${{ matrix.node-version }})",
);

const smokeSteps = steps(smokeJob, "npm install smoke job");
const setupNode = smokeSteps.find((step) =>
step.uses ===
"actions/setup-node@820762786026740c76f36085b0efc47a31fe5020"
);
assert(setupNode, "The npm smoke job must install its matrix Node release");
assertEquals(
record(setupNode.with, "npm smoke setup-node inputs")["node-version"],
"${{ matrix.node-version }}",
);
assert(
smokeSteps.some((step) =>
step.name === "Clean-room install/import smoke" &&
step.run === "bash scripts/test/npm-install-smoke.sh"
),
"Every matrix leg must run the existing packed clean-room smoke",
);

for (const jobName of ["prerelease", "release"]) {
const releaseJob = record(jobs[jobName], `${jobName} job`);
assert(
Array.isArray(releaseJob.needs) &&
releaseJob.needs.includes("tests-npm-install-smoke"),
`${jobName} must wait for every npm install smoke matrix leg`,
);
}

assertEquals(
NPM_SMOKE_NODE_VERSIONS,
[MINIMUM_NODE_RELEASE_LINE, CURRENT_NPM_SMOKE_NODE_RELEASE_LINE],
);
assertEquals(MINIMUM_NODE_RELEASE_LINE, "22");
assertEquals(MINIMUM_NODE_VERSION, "22.3.0");
assertEquals(CURRENT_NPM_SMOKE_NODE_RELEASE_LINE, "24");
});
});
24 changes: 24 additions & 0 deletions scripts/build/runtime-support.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
/** Minimum Node.js release supported by the root and first-party npm packages. */
export const MINIMUM_NODE_VERSION = "22.3.0";

/** Current Node.js release line used by the clean-room npm smoke. */
export const CURRENT_NPM_SMOKE_NODE_RELEASE_LINE = "24";

/** Oldest supported Node.js release line, exercised at its latest patch. */
export const MINIMUM_NODE_RELEASE_LINE = MINIMUM_NODE_VERSION.slice(
0,
MINIMUM_NODE_VERSION.indexOf("."),
Comment thread
kwakayama marked this conversation as resolved.
);

/**
* Node.js releases that exercise the clean-room npm artifact boundary.
*
* These are release lines, so `setup-node` resolves each to its newest patch.
* That covers the lines, not the exact `MINIMUM_NODE_VERSION` the published
* `engines.node` accepts: the packed artifact fails from 22.3.0 through
* 22.12.0, 22.13.x is unverified, and 22.14.0 is the first observed passing
* version. Pin the exact floor here once veryfront-issue-inbox#748 either fixes
* that range or raises the floor.
*/
export const NPM_SMOKE_NODE_VERSIONS = Object.freeze([
MINIMUM_NODE_RELEASE_LINE,
CURRENT_NPM_SMOKE_NODE_RELEASE_LINE,
]);

/** npm `engines.node` range derived from the single runtime-version contract. */
export const NPM_NODE_ENGINE = `>=${MINIMUM_NODE_VERSION}`;

Expand Down
Loading