Spaces:
Running
Running
deploy(hf): sync szl-holdings/nexus@00edebc993282efd5b43b0144b3d3d27ab43e264 derived COPY set
31b3c47 verified Download scripts/ci-workflow.test.mjs from SZLHOLDINGS/nexus: direct link, hf CLI and curl.
- Browser
- Download file 7.37 kB
-
https://huggingface.co/spaces/SZLHOLDINGS/nexus/resolve/main/scripts/ci-workflow.test.mjs
- Command line
-
hf download hf://spaces/SZLHOLDINGS/nexus/scripts/ci-workflow.test.mjs
-
curl -L -o ci-workflow.test.mjs https://huggingface.co/spaces/SZLHOLDINGS/nexus/resolve/main/scripts/ci-workflow.test.mjs
7.37 kB
| import assert from "node:assert/strict"; | |
| import { readFileSync } from "node:fs"; | |
| import { dirname, join } from "node:path"; | |
| import { fileURLToPath } from "node:url"; | |
| import { test } from "node:test"; | |
| import ts from "typescript"; | |
| const ROOT = join(dirname(fileURLToPath(import.meta.url)), ".."); | |
| // CRLF-normalized so the line-anchored checks hold on a Windows checkout. | |
| const CI = readFileSync(join(ROOT, ".github/workflows/ci.yml"), "utf8").replace(/\r\n/g, "\n"); | |
| const PACKAGE = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")); | |
| const LOCK = JSON.parse(readFileSync(join(ROOT, "package-lock.json"), "utf8")); | |
| // The SHAs szl-holdings/.github already runs, so the org has one pin per action. | |
| const APPROVED_PINS = { | |
| "actions/checkout": "3d3c42e5aac5ba805825da76410c181273ba90b1", // v7.0.1 | |
| "actions/setup-node": "820762786026740c76f36085b0efc47a31fe5020", // v7.0.0 | |
| "actions/setup-python": "5fda3b95a4ea91299a34e894583c3862153e4b97", // v7.0.0 | |
| "step-security/harden-runner": "e14015d583714f6e62063499dc959a02595150a1", // v2.21.1 | |
| }; | |
| function onBlock() { | |
| const match = CI.match(/^on:\n([\s\S]*?)^(?=\S)/m); | |
| assert.ok(match, "missing on: block"); | |
| return match[1]; | |
| } | |
| function jobSteps(job) { | |
| const match = CI.match( | |
| new RegExp(String.raw`^ ${job}:\n([\s\S]*?)(?=^ [A-Za-z_][\w-]*:\n|(?![\s\S]))`, "m"), | |
| ); | |
| assert.ok(match, `missing job ${job}`); | |
| return match[1]; | |
| } | |
| test("CI runs on every pull request and every push to main, with no path filter", () => { | |
| const on = onBlock(); | |
| assert.match(on, /^ {2}push:\n {4}branches: \[main\]$/m); | |
| assert.match(on, /^ {2}pull_request:$/m); | |
| assert.doesNotMatch(on, /paths(?:-ignore)?:/); | |
| assert.doesNotMatch(on, /branches-ignore:/); | |
| }); | |
| test("CI is read-only", () => { | |
| assert.match(CI, /^permissions:\n {2}contents: read\n/m); | |
| assert.doesNotMatch(CI, /:\s*write\b/); | |
| assert.doesNotMatch(CI, /secrets\./); | |
| assert.doesNotMatch(CI, /persist-credentials: true/); | |
| }); | |
| test("every action is pinned to the SHA the org already uses", () => { | |
| const uses = [...CI.matchAll(/^\s*uses:\s*(\S+)/gm)].map((match) => match[1]); | |
| assert.ok(uses.length >= 6); | |
| for (const value of uses) { | |
| const [action, ref] = value.split("@"); | |
| assert.ok(action in APPROVED_PINS, `unapproved action: ${value}`); | |
| assert.equal(ref, APPROVED_PINS[action], `wrong pin for ${action}`); | |
| } | |
| }); | |
| test("each job hardens the runner before anything else runs", () => { | |
| for (const job of ["node", "python", "space"]) { | |
| const steps = jobSteps(job); | |
| const firstUses = steps.match(/^\s*uses:\s*(\S+)/m); | |
| assert.ok(firstUses, `${job}: no steps`); | |
| assert.match(firstUses[1], /^step-security\/harden-runner@/, `${job}: first step`); | |
| } | |
| }); | |
| test("the node job runs the full npm gate", () => { | |
| const steps = jobSteps("node"); | |
| const order = ["npm ci", "npm run typecheck", "npm run lint", "npm test", "npm run build"]; | |
| let last = -1; | |
| for (const command of order) { | |
| const at = steps.indexOf(`run: ${command}\n`); | |
| assert.notEqual(at, -1, `node job does not run: ${command}`); | |
| assert.ok(at > last, `${command} is out of order`); | |
| last = at; | |
| } | |
| for (const script of ["typecheck", "lint", "test", "build"]) { | |
| assert.ok(PACKAGE.scripts[script], `package.json has no "${script}" script`); | |
| } | |
| }); | |
| test("TypeScript stays on the supported 6.0 patch line with an exact matching lock", () => { | |
| // typescript-eslint 8.70.1 and 8.71.0 admit >=4.8.4 <6.1.0, not TS 7. | |
| // A future minor/major migration must qualify its lint peer contract too. | |
| assert.match(PACKAGE.devDependencies.typescript, /^~6\.0\.\d+$/); | |
| assert.equal(LOCK.packages[""].devDependencies.typescript, PACKAGE.devDependencies.typescript); | |
| assert.match(LOCK.packages["node_modules/typescript"].version, /^6\.0\.\d+$/); | |
| assert.equal( | |
| LOCK.packages["node_modules/typescript-eslint"].peerDependencies.typescript, | |
| ">=4.8.4 <6.1.0", | |
| ); | |
| }); | |
| test("CI never overrides incompatible dependency peers to pass installation", () => { | |
| assert.doesNotMatch(CI, /--legacy-peer-deps|--force\b|strict-peer-deps:\s*false/); | |
| }); | |
| test("Nitro's optional cache peer is explicit without overriding Babel's major", () => { | |
| assert.equal(PACKAGE.devDependencies["lru-cache"], "11.5.3"); | |
| assert.equal(LOCK.packages[""].devDependencies["lru-cache"], "11.5.3"); | |
| assert.equal(LOCK.packages["node_modules/lru-cache"].version, "11.5.3"); | |
| assert.equal( | |
| LOCK.packages["node_modules/@babel/helper-compilation-targets/node_modules/lru-cache"].version, | |
| "5.1.1", | |
| ); | |
| }); | |
| test("the Space smoke retries health, root and source readback", () => { | |
| const steps = jobSteps("space"); | |
| assert.match(steps, /fetch_with_retry\(\)/); | |
| assert.match( | |
| steps, | |
| /fetch_with_retry http:\/\/127\.0\.0\.1:7860\/healthz \/tmp\/nexus-health\.json/, | |
| ); | |
| assert.match(steps, /fetch_with_retry http:\/\/127\.0\.0\.1:7860\/ \/tmp\/nexus-root\.html/); | |
| assert.match( | |
| steps, | |
| /fetch_with_retry http:\/\/127\.0\.0\.1:7860\/api\/build-info \/tmp\/nexus-build\.json/, | |
| ); | |
| assert.match(steps, /--connect-timeout 2 --max-time 10/); | |
| }); | |
| test("the Space smoke waits for Docker health instead of sampling once", () => { | |
| const steps = jobSteps("space"); | |
| assert.match(steps, /wait_for_docker_health\(\)/); | |
| assert.match(steps, /for attempt in \$\(seq 1 60\)/); | |
| assert.match(steps, /if \[ "\$state" = healthy \]/); | |
| assert.match(steps, /if \[ "\$state" = unhealthy \]/); | |
| assert.match(steps, /container health did not stabilize/); | |
| assert.doesNotMatch( | |
| steps, | |
| /test "\$\(docker inspect --format '\{\{\.State\.Health\.Status\}\}' "\$name"\)" = healthy/, | |
| ); | |
| }); | |
| test("the source alias resolves without deprecated baseUrl or a suppression", () => { | |
| const { config, error } = ts.readConfigFile(join(ROOT, "tsconfig.json"), ts.sys.readFile); | |
| assert.equal(error, undefined); | |
| assert.equal(config.compilerOptions.baseUrl, undefined); | |
| assert.equal(config.compilerOptions.ignoreDeprecations, undefined); | |
| const parsed = ts.parseJsonConfigFileContent(config, ts.sys, ROOT); | |
| assert.equal(parsed.errors.length, 0); | |
| const resolved = ts.resolveModuleName( | |
| "@/lib/nexus/types", | |
| join(ROOT, "src/routes/index.tsx"), | |
| parsed.options, | |
| ts.sys, | |
| ).resolvedModule; | |
| assert.ok(resolved, "the compiler must retain the @/ source alias"); | |
| assert.equal( | |
| resolved.resolvedFileName.replaceAll("\\", "/"), | |
| join(ROOT, "src/lib/nexus/types.ts").replaceAll("\\", "/"), | |
| ); | |
| }); | |
| test("the node job runs on both Node LTS lines the repository uses", () => { | |
| const steps = jobSteps("node"); | |
| assert.match(steps, /^ {8}node: \["22", "24"\]$/m); | |
| assert.match(steps, /node-version: \$\{\{ matrix\.node \}\}/); | |
| assert.match(steps, /fail-fast: false/); | |
| }); | |
| test("npm test runs the cross-platform runner, not a shell glob", () => { | |
| assert.equal(PACKAGE.scripts.test, "node --experimental-strip-types scripts/run-tests.mjs"); | |
| assert.doesNotMatch(PACKAGE.scripts.test, /[*']/); | |
| }); | |
| test("the python job compiles, self-tests and runs pytest when tests exist", () => { | |
| const steps = jobSteps("python"); | |
| assert.match(steps, /git ls-files -z -- '\*\.py' \| xargs -0 -r python -m py_compile/); | |
| assert.match(steps, /run: python server\.py --selftest\n/); | |
| assert.match(steps, /run: python source_bound_server\.py --selftest\n/); | |
| assert.match(steps, /python -m pytest -q/); | |
| }); | |