mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-22 06:42:19 +03:00
fix(ci): pin lockfile installer and align acceptance runtime
This commit is contained in:
22
.github/actions/npm-ci-retry/action.yml
vendored
22
.github/actions/npm-ci-retry/action.yml
vendored
@@ -12,6 +12,26 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Select the lockfile-compatible npm version
|
||||
id: npm
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
expected=$(node -p "require('./config/ci/toolchain.json').npm")
|
||||
if ! [[ "$expected" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Invalid pinned npm version" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$(npm --version)" != "$expected" ]; then
|
||||
npm install --global "npm@$expected" --no-audit --no-fund
|
||||
fi
|
||||
actual=$(npm --version)
|
||||
if [ "$actual" != "$expected" ]; then
|
||||
echo "npm version differs from the lockfile toolchain" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "version=$actual" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Resolve Node version for the cache key
|
||||
id: node
|
||||
shell: bash
|
||||
@@ -35,7 +55,7 @@ runs:
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: node_modules
|
||||
key: node-modules-${{ runner.os }}-${{ runner.arch }}-${{ steps.node.outputs.version }}-${{ hashFiles('package-lock.json', '.npmrc', 'scripts/build/postinstall.mjs', 'scripts/build/postinstallSupport.mjs', 'scripts/build/colocateOptionals.mjs', 'scripts/build/wreqJsNative.mjs', 'scripts/build/fixPlaywrightAndroid.mjs', 'scripts/build/native-binary-compat.mjs') }}
|
||||
key: node-modules-v2-${{ runner.os }}-${{ runner.arch }}-${{ steps.node.outputs.version }}-${{ steps.npm.outputs.version }}-${{ hashFiles('package-lock.json', '.npmrc', 'scripts/build/postinstall.mjs', 'scripts/build/postinstallSupport.mjs', 'scripts/build/colocateOptionals.mjs', 'scripts/build/wreqJsNative.mjs', 'scripts/build/fixPlaywrightAndroid.mjs', 'scripts/build/native-binary-compat.mjs') }}
|
||||
|
||||
- name: npm ci (with retry)
|
||||
if: steps.node-modules.outputs.cache-hit != 'true'
|
||||
|
||||
6
.github/workflows/release-acceptance.yml
vendored
6
.github/workflows/release-acceptance.yml
vendored
@@ -23,9 +23,11 @@ jobs:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: "22"
|
||||
node-version-file: ".node-version"
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- uses: ./.github/actions/npm-ci-retry
|
||||
with:
|
||||
cache: "false"
|
||||
- name: Emit shadow acceptance report
|
||||
run: |
|
||||
node scripts/quality/validate-release-acceptance.mjs \
|
||||
|
||||
3
config/ci/toolchain.json
Normal file
3
config/ci/toolchain.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"npm": "11.15.0"
|
||||
}
|
||||
97
tests/unit/build/ci-toolchain-parity.test.ts
Normal file
97
tests/unit/build/ci-toolchain-parity.test.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { parse } from "yaml";
|
||||
|
||||
const root = new URL("../../../", import.meta.url);
|
||||
const action = parse(
|
||||
readFileSync(new URL(".github/actions/npm-ci-retry/action.yml", root), "utf8")
|
||||
);
|
||||
const acceptance = parse(
|
||||
readFileSync(new URL(".github/workflows/release-acceptance.yml", root), "utf8")
|
||||
);
|
||||
|
||||
test("release acceptance uses the repository's supported development runtime and installer", () => {
|
||||
const steps = acceptance.jobs.acceptance.steps;
|
||||
const setup = steps.find((step: { uses?: string }) =>
|
||||
step.uses?.startsWith("actions/setup-node@")
|
||||
);
|
||||
assert.equal(setup.with["node-version-file"], ".node-version");
|
||||
assert.ok(
|
||||
steps.some((step: { uses?: string }) => step.uses === "./.github/actions/npm-ci-retry")
|
||||
);
|
||||
});
|
||||
|
||||
test("the exact npm version participates in dependency cache identity", () => {
|
||||
const cache = action.runs.steps.find((step: { id?: string }) => step.id === "node-modules");
|
||||
assert.match(cache.with.key, /steps\.npm\.outputs\.version/);
|
||||
assert.match(cache.with.key, /node-modules-v2-/);
|
||||
const bootstrap = action.runs.steps.findIndex((step: { id?: string }) => step.id === "npm");
|
||||
const restore = action.runs.steps.findIndex(
|
||||
(step: { id?: string }) => step.id === "node-modules"
|
||||
);
|
||||
assert.ok(
|
||||
bootstrap >= 0 && bootstrap < restore,
|
||||
"resolve the installer before restoring its tree"
|
||||
);
|
||||
});
|
||||
|
||||
for (const fixture of [
|
||||
{ initial: "11.15.0", installed: "11.15.0", installExit: 0, exit: 0, installs: 0 },
|
||||
{ initial: "10.9.8", installed: "11.15.0", installExit: 0, exit: 0, installs: 1 },
|
||||
{ initial: "10.9.8", installed: "11.15.0", installExit: 42, exit: 42, installs: 1 },
|
||||
{ initial: "10.9.8", installed: "10.9.8", installExit: 0, exit: 1, installs: 1 },
|
||||
]) {
|
||||
test(`npm bootstrap ${JSON.stringify(fixture)}`, () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "omni-npm-toolchain-"));
|
||||
try {
|
||||
mkdirSync(join(dir, "config/ci"), { recursive: true });
|
||||
writeFileSync(
|
||||
join(dir, "config/ci/toolchain.json"),
|
||||
readFileSync(new URL("config/ci/toolchain.json", root))
|
||||
);
|
||||
writeFileSync(join(dir, "version"), fixture.initial);
|
||||
writeFileSync(join(dir, "calls"), "");
|
||||
const run = action.runs.steps.find((step: { id?: string }) => step.id === "npm").run;
|
||||
const result = spawnSync(
|
||||
"bash",
|
||||
[
|
||||
"-euo",
|
||||
"pipefail",
|
||||
"-c",
|
||||
`
|
||||
npm() {
|
||||
if [ "$1" = "--version" ]; then cat version; return; fi
|
||||
printf '%s\\n' "$*" >> calls
|
||||
if [ "$FIXTURE_INSTALL_EXIT" != "0" ]; then return "$FIXTURE_INSTALL_EXIT"; fi
|
||||
printf '%s' "$FIXTURE_INSTALLED_VERSION" > version
|
||||
}
|
||||
${run}
|
||||
`,
|
||||
],
|
||||
{
|
||||
cwd: dir,
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
FIXTURE_INSTALL_EXIT: String(fixture.installExit),
|
||||
FIXTURE_INSTALLED_VERSION: fixture.installed,
|
||||
GITHUB_OUTPUT: join(dir, "outputs"),
|
||||
},
|
||||
}
|
||||
);
|
||||
assert.equal(result.status, fixture.exit, result.stderr);
|
||||
const calls = readFileSync(join(dir, "calls"), "utf8").trim();
|
||||
assert.equal(calls ? calls.split("\n").length : 0, fixture.installs);
|
||||
if (fixture.installs)
|
||||
assert.match(calls, /^install --global npm@11\.15\.0 --no-audit --no-fund$/);
|
||||
if (fixture.exit === 0)
|
||||
assert.equal(readFileSync(join(dir, "outputs"), "utf8"), "version=11.15.0\n");
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user