diff --git a/packages/cli/src/commands/doctor.test.ts b/packages/cli/src/commands/doctor.test.ts index 0c530e3..81a6df6 100644 --- a/packages/cli/src/commands/doctor.test.ts +++ b/packages/cli/src/commands/doctor.test.ts @@ -257,6 +257,30 @@ test("vendored-wasms check fails when the vendor dir cannot be resolved", async } }); +// The @opencodehub/sarif check must resolve the INSTALLED package (its +// prebuilt `dist/` ships in the tarball), not a `packages/sarif/dist` +// monorepo path. Pointing `repoRoot` at a bogus dir kills the source-checkout +// fallback, so an `ok` result proves the check resolves the real installed +// package via `import.meta.resolve` — the customer (`npm i -g`) case. A `warn` +// here would mean the check regressed to emitting the nonsensical +// `pnpm -r build` hint to end users. +test("sarif-build check reports ok against the installed package even with a bogus repoRoot", async () => { + const home = await mkdtemp(join(tmpdir(), "codehub-doctor-sarif-ok-")); + try { + const checks = buildChecks({ home, skipNative: true, repoRoot: join(home, "nope") }); + const sarif = checks.find((c) => c.name === "@opencodehub/sarif build"); + assert.ok(sarif, "sarif-build check must always be registered"); + const result = await sarif.run(); + assert.equal( + result.status, + "ok", + `expected ok against installed package; got ${result.status}: ${result.message}`, + ); + } finally { + await rm(home, { recursive: true, force: true }); + } +}); + // --------------------------------------------------------------------------- // SCIP indexers — warn by default, fail under --strict // --------------------------------------------------------------------------- diff --git a/packages/cli/src/commands/doctor.ts b/packages/cli/src/commands/doctor.ts index 2d27406..44439c7 100644 --- a/packages/cli/src/commands/doctor.ts +++ b/packages/cli/src/commands/doctor.ts @@ -539,6 +539,26 @@ function sarifSchemaCheck(repoRoot: string): Check { return { name: "@opencodehub/sarif build", async run() { + // 1. Installed deployment (the customer case): resolve the ESM entry + // the CLI would actually `import`. `@opencodehub/sarif`'s `exports` + // map declares only the `import` condition (no `require`), so + // `createRequire().resolve()` throws ERR_PACKAGE_PATH_NOT_EXPORTED — + // `import.meta.resolve` honors `import` and is the path that works in + // a real `npm i -g @opencodehub/cli`. A resolvable, on-disk entry + // means the package shipped its prebuilt `dist/`; there is no + // `packages/sarif/` tree to build, so `pnpm -r build` would be + // nonsensical advice here. + try { + const entryPath = fileURLToPath(import.meta.resolve("@opencodehub/sarif")); + if (existsSyncSafe(entryPath)) { + return { status: "ok", message: "@opencodehub/sarif built" }; + } + } catch { + // fall through to the monorepo source-checkout layout + } + // 2. Monorepo / source-checkout fallback: the CLI runs from + // `packages/cli/dist` while a sibling `@opencodehub/sarif` may be + // unbuilt. Only here is the `pnpm -r build` hint correct. const pkgDir = join(repoRoot, "packages", "sarif", "dist"); try { const s = await stat(pkgDir);