diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 839ed79..f44d6d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,36 @@ jobs: - run: pnpm --filter '!@opencodehub/docs' -r build - run: pnpm --filter '!@opencodehub/docs' -r test + test-platform: + # Platform / fixture lane (audit P1). Runs the SAME suite as `test` but with + # the CODEHUB_PLATFORM gate ON, so the vendor/runtime/fixture cases that + # self-skip in the required `test` lane execute here: vendored-WASM disk + # checks, MCP ToolResult-shape smoke, and the HTTP-download + SHA256 + + # chmod/exec-bit + atomic-rename mechanics of the scip/embedder downloaders. + # + # `continue-on-error: true` + NOT in branch-protection required checks = + # informational. A Windows chmod/path/file-url quirk turns this job amber + # without blocking a logic-correct PR or the release-please PR; the required + # `test` job above carries the logic-regression signal alone (it runs the + # identical command with the gate OFF). Same env-var `{ skip }` idiom as + # packages/embedder/src/sagemaker-embedder.integration.test.ts. + continue-on-error: true + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + node-version: [22] + runs-on: ${{ matrix.os }} + env: + MISE_NODE_VERSION: ${{ matrix.node-version }} + CODEHUB_PLATFORM: "1" # set via env: (not an inline prefix) so it works on Windows cmd too + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4 + - run: pnpm install --frozen-lockfile --ignore-scripts + - run: pnpm --filter '!@opencodehub/docs' -r build + - run: pnpm --filter '!@opencodehub/docs' -r test + sarif-validate: runs-on: ubuntu-latest steps: diff --git a/packages/cli/src/commands/init.test.ts b/packages/cli/src/commands/init.test.ts index 31880c5..7336429 100644 --- a/packages/cli/src/commands/init.test.ts +++ b/packages/cli/src/commands/init.test.ts @@ -41,7 +41,7 @@ function resolvePluginSource(): string { if (parent === dir) break; dir = parent; } - throw new Error("init.test: could not locate plugins/opencodehub from " + import.meta.url); + throw new Error(`init.test: could not locate plugins/opencodehub from ${import.meta.url}`); } const BUNDLED_ASSETS = resolvePluginSource(); diff --git a/packages/cli/src/commands/setup-embeddings.test.ts b/packages/cli/src/commands/setup-embeddings.test.ts index dbf5200..3723275 100644 --- a/packages/cli/src/commands/setup-embeddings.test.ts +++ b/packages/cli/src/commands/setup-embeddings.test.ts @@ -17,6 +17,15 @@ import { GTE_MODERNBERT_BASE_PINS } from "@opencodehub/embedder"; import { runSetupEmbeddings } from "./setup.js"; +// Platform/fixture lane (audit P1): this suite drives the HTTP-download + +// SHA256 + atomic-write mechanics of weight fetching — vendor/runtime surface, +// not OCH decision logic. Gated so it runs only in the platform lane +// (CODEHUB_PLATFORM=1); same idiom as sagemaker-embedder.integration.test.ts. +const platformSkip = + process.env["CODEHUB_PLATFORM"] === "1" + ? undefined + : "platform lane only (set CODEHUB_PLATFORM=1)"; + function sha256(buf: Uint8Array): string { return createHash("sha256").update(buf).digest("hex"); } @@ -33,7 +42,7 @@ function makeResponse(body: Uint8Array): Response { }); } -describe("runSetupEmbeddings", () => { +describe("runSetupEmbeddings", { skip: platformSkip }, () => { it("downloads every file for the fp32 variant and reports the summary", async () => { const dir = await mkdtemp(join(tmpdir(), "och-cli-setup-emb-")); try { diff --git a/packages/cli/src/embedder-downloader.test.ts b/packages/cli/src/embedder-downloader.test.ts index e446f20..515c9dd 100644 --- a/packages/cli/src/embedder-downloader.test.ts +++ b/packages/cli/src/embedder-downloader.test.ts @@ -25,6 +25,16 @@ import { Sha256MismatchError, } from "./embedder-downloader.js"; +// Platform/fixture lane (audit P1): the download-MECHANIC cases (HTTP fetch + +// SHA256 + atomic rename + byte accounting) are vendor/runtime surface, gated +// to the platform lane (CODEHUB_PLATFORM=1). The error-taxonomy contract +// (Sha256MismatchError shape) and the retry/backoff POLICY are OCH logic and +// stay ungated in the default lane. +const platformSkip = + process.env["CODEHUB_PLATFORM"] === "1" + ? undefined + : "platform lane only (set CODEHUB_PLATFORM=1)"; + function sha256(buf: Uint8Array): string { return createHash("sha256").update(buf).digest("hex"); } @@ -111,7 +121,9 @@ function withOverridePins( } describe("downloadEmbedderWeights", () => { - it("downloads a pinned file, verifies SHA256, and atomically renames", async () => { + it("downloads a pinned file, verifies SHA256, and atomically renames", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-dl-happy-")); try { const body = new TextEncoder().encode("hello-gte-mb"); @@ -142,7 +154,9 @@ describe("downloadEmbedderWeights", () => { } }); - it("is idempotent — a second call with matching SHA256 skips every file", async () => { + it("is idempotent — a second call with matching SHA256 skips every file", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-dl-idem-")); try { const body = new TextEncoder().encode("bytes-for-idempotency"); @@ -175,7 +189,9 @@ describe("downloadEmbedderWeights", () => { } }); - it("force=true re-downloads even when the on-disk SHA256 already matches", async () => { + it("force=true re-downloads even when the on-disk SHA256 already matches", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-dl-force-")); try { const body = new TextEncoder().encode("force-re-download"); @@ -275,7 +291,7 @@ describe("downloadEmbedderWeights", () => { } }); - it("skips on-disk file that matches SHA256 (no fetch call)", async () => { + it("skips on-disk file that matches SHA256 (no fetch call)", { skip: platformSkip }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-dl-preexist-")); try { const body = new TextEncoder().encode("pre-existing-bytes"); @@ -300,7 +316,9 @@ describe("downloadEmbedderWeights", () => { } }); - it("returns totalBytes equal to the sum of newly downloaded sizes", async () => { + it("returns totalBytes equal to the sum of newly downloaded sizes", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-dl-total-")); try { const a = new TextEncoder().encode("a".repeat(10)); diff --git a/packages/cli/src/scip-downloader.test.ts b/packages/cli/src/scip-downloader.test.ts index cdff808..64359a0 100644 --- a/packages/cli/src/scip-downloader.test.ts +++ b/packages/cli/src/scip-downloader.test.ts @@ -39,6 +39,17 @@ import { UnsupportedPlatformError, } from "./scip-downloader.js"; +// Platform/fixture lane (audit P1): the download/extract MECHANIC cases below +// (HTTP fetch + SHA256 + chmod + atomic-rename + gunzip/untar) are vendor/ +// runtime surface and carry the OS-dependent chmod/exec-bit checks that flaked +// on Windows. Gated to the platform lane (CODEHUB_PLATFORM=1). The SECURITY/ +// ROUTING decision cases (pin-mismatch, UnsupportedPlatform, placeholder-hash, +// SHA-mismatch, dotnet-SDK, install ordering) are OCH logic and stay ungated. +const platformSkip = + process.env["CODEHUB_PLATFORM"] === "1" + ? undefined + : "platform lane only (set CODEHUB_PLATFORM=1)"; + function sha256(buf: Uint8Array): string { return createHash("sha256").update(buf).digest("hex"); } @@ -130,7 +141,9 @@ function withOverridePin( const LINUX_X64 = { os: "linux", arch: "x64" } as const; describe("installScipTool", () => { - it("downloads a pinned binary, verifies SHA256, chmods +x, and atomically renames", async () => { + it("downloads a pinned binary, verifies SHA256, chmods +x, and atomically renames", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-scip-happy-")); try { const body = new TextEncoder().encode("#!/usr/bin/env scip-clang\n"); @@ -174,7 +187,9 @@ describe("installScipTool", () => { } }); - it("is idempotent — a second call with matching SHA256 skips and makes no fetch", async () => { + it("is idempotent — a second call with matching SHA256 skips and makes no fetch", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-scip-idem-")); try { const body = new TextEncoder().encode("scip-clang-bytes"); @@ -209,7 +224,9 @@ describe("installScipTool", () => { } }); - it("re-downloads when the on-disk file's SHA256 drifts from the pin", async () => { + it("re-downloads when the on-disk file's SHA256 drifts from the pin", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-scip-drift-")); try { const body = new TextEncoder().encode("correct-bytes"); @@ -289,7 +306,9 @@ describe("installScipTool", () => { } }); - it("serializes concurrent installs of the same tool into a single fetch", async () => { + it("serializes concurrent installs of the same tool into a single fetch", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-scip-concurrent-")); try { const body = new TextEncoder().encode("concurrent-install-body"); @@ -472,7 +491,9 @@ describe("installScipTool", () => { describe("scip-go (archive/tarball extraction)", () => { const LINUX_X64_GO = { os: "linux", arch: "x64" } as const; - it("extracts the binary from the gzip tarball, chmods it, and verifies the tarball SHA256", async () => { + it("extracts the binary from the gzip tarball, chmods it, and verifies the tarball SHA256", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-scip-go-")); try { const binBytes = new TextEncoder().encode("\x7fELF fake scip-go binary"); @@ -563,7 +584,9 @@ describe("scip-go (archive/tarball extraction)", () => { } }); - it("skips re-install when the extracted binary already exists (archive idempotency)", async () => { + it("skips re-install when the extracted binary already exists (archive idempotency)", { + skip: platformSkip, + }, async () => { const dir = await mkdtemp(join(tmpdir(), "och-scip-go-idem-")); try { const tarGz = makeTarGz("scip-go", new TextEncoder().encode("scip-go-bin")); diff --git a/packages/ingestion/src/parse/wasm-grammar-resolution.test.ts b/packages/ingestion/src/parse/wasm-grammar-resolution.test.ts index bcb3ff4..5018bf2 100644 --- a/packages/ingestion/src/parse/wasm-grammar-resolution.test.ts +++ b/packages/ingestion/src/parse/wasm-grammar-resolution.test.ts @@ -20,6 +20,16 @@ import path from "node:path"; import { describe, it } from "node:test"; import { _resolveGrammarWasmPathForTests } from "./wasm-runtime.js"; +// Platform/fixture lane (audit P1): the per-language loop below statSync's the +// vendored .wasm blobs on disk — a packaging/build-artifact guard, not OCH +// logic. Gated to the platform lane (CODEHUB_PLATFORM=1). The sibling +// "non-tree-sitter languages" describe (php->php_only routing, cobol->undefined) +// is real resolver logic and stays in the default lane, ungated. +const platformSkip = + process.env["CODEHUB_PLATFORM"] === "1" + ? undefined + : "platform lane only (set CODEHUB_PLATFORM=1)"; + const EXPECTED: Readonly> = { typescript: "tree-sitter-typescript.wasm", tsx: "tree-sitter-tsx.wasm", @@ -38,7 +48,7 @@ const EXPECTED: Readonly> = { php: "tree-sitter-php_only.wasm", }; -describe("resolveGrammarWasmPath — vendored WASM resolver", () => { +describe("resolveGrammarWasmPath — vendored WASM resolver", { skip: platformSkip }, () => { for (const [lang, fname] of Object.entries(EXPECTED)) { it(`resolves ${lang} to vendor/wasms/${fname} on disk`, () => { const wasmPath = _resolveGrammarWasmPathForTests(lang as never); diff --git a/packages/mcp/src/tools/run-smoke.test.ts b/packages/mcp/src/tools/run-smoke.test.ts index 0020ab6..dca0ff0 100644 --- a/packages/mcp/src/tools/run-smoke.test.ts +++ b/packages/mcp/src/tools/run-smoke.test.ts @@ -15,7 +15,7 @@ import { strict as assert } from "node:assert"; import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; -import { test } from "node:test"; +import { test as nodeTest, type TestContext } from "node:test"; import type { KnowledgeGraph } from "@opencodehub/core-types"; import type { BulkLoadStats, @@ -59,6 +59,19 @@ import { runSql } from "./sql.js"; import { runToolMap } from "./tool-map.js"; import { runVerdict } from "./verdict.js"; +// Platform/fixture lane (audit P1): every case here is a ToolResult-shape +// smoke against an in-memory fake store — harness mechanics, not OCH decision +// logic. They self-skip in the default (logic) lane and run only when the +// platform lane sets CODEHUB_PLATFORM=1, so a fixture-shape quirk can't mask a +// logic regression on the release-gating `test` job. Same env-var `{ skip }` +// idiom as packages/embedder/src/sagemaker-embedder.integration.test.ts. +const platformSkip = + process.env["CODEHUB_PLATFORM"] === "1" + ? undefined + : "platform lane only (set CODEHUB_PLATFORM=1)"; +const test = (name: string, fn: (t: TestContext) => void | Promise): Promise => + nodeTest(name, { skip: platformSkip }, fn); + /** * Wrap an in-memory IGraphStore-shaped fake as the composed `Store` * (`OpenStoreResult`) that the connection pool returns. The same fake