diff --git a/packages/mcp/scripts/smoke-package.mjs b/packages/mcp/scripts/smoke-package.mjs index 0b122b3..1b18ce2 100644 --- a/packages/mcp/scripts/smoke-package.mjs +++ b/packages/mcp/scripts/smoke-package.mjs @@ -10,6 +10,10 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import { buildStandaloneMcpPackage } from "./build-package-lib.mjs"; +import { + findSingleTarball, + normalizePackedTarEntries, +} from "../../../scripts/root-release-guard.mjs"; const REQUIRED_TOOLS = [ "martin_doctor", @@ -78,21 +82,16 @@ export async function runStandaloneMcpSmoke(options = {}) { try { await buildStandaloneMcpPackage({ packageDir }); - const packDryRun = await runCommand(npmCommand(), ["pack", "--ignore-scripts", "--json", "--dry-run"], { - cwd: packageDir, - }); - const dryRunEntry = parsePackEntry(packDryRun.stdout); - const tarballFiles = dryRunEntry.files.map((file) => file.path).sort(); - assertTarballFileSet(tarballFiles); - - const packRun = await runCommand( + await runCommand( npmCommand(), - ["pack", "--ignore-scripts", "--json", "--pack-destination", packDir], + ["pack", "--ignore-scripts", "--pack-destination", packDir], { cwd: packageDir }, ); - const packEntry = parsePackEntry(packRun.stdout); - const tarballFilename = packEntry.filename; - const tarballPath = path.join(packDir, packEntry.filename); + const tarballFilename = await findSingleTarball(packDir); + const tarballPath = path.join(packDir, tarballFilename); + const tarList = await runCommand(tarCommand(), ["-tf", tarballFilename], { cwd: packDir }); + const tarballFiles = normalizePackedTarEntries(tarList.stdout.split(/\r?\n/u)).sort(); + assertTarballFileSet(tarballFiles); const [packedManifestOutput, packedServerOutput] = await Promise.all([ runCommand( @@ -302,15 +301,6 @@ function readTextContent(result) { return first.text; } -function parsePackEntry(stdout) { - const parsed = JSON.parse(stdout); - const entry = Array.isArray(parsed) ? parsed[0] : null; - if (!entry || typeof entry.filename !== "string" || !Array.isArray(entry.files)) { - throw new Error("npm pack did not return a usable pack result."); - } - return entry; -} - function npmCommand() { return process.platform === "win32" ? "npm.cmd" : "npm"; } diff --git a/scripts/pack-root-release.mjs b/scripts/pack-root-release.mjs index 140b08a..21b7c9b 100644 --- a/scripts/pack-root-release.mjs +++ b/scripts/pack-root-release.mjs @@ -6,23 +6,18 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { resolveRcCommandExecution } from "./rc-validation.mjs"; -import { extractPackJsonPayload } from "./root-release-guard.mjs"; +import { findSingleTarball } from "./root-release-guard.mjs"; export async function packRootRelease(options = {}) { const rootDir = options.rootDir ?? process.cwd(); const outputDir = path.resolve(rootDir, options.outputDir ?? "dist-release"); await mkdir(outputDir, { recursive: true }); - const packRun = await runCommand( - ["npm", "pack", "--json", "--pack-destination", outputDir], + await runCommand( + ["npm", "pack", "--pack-destination", outputDir], { cwd: rootDir }, ); - const packArtifacts = extractPackJsonPayload(packRun.stdout); - const tarballName = Array.isArray(packArtifacts) ? packArtifacts[0]?.filename : null; - - if (typeof tarballName !== "string" || tarballName.length === 0) { - throw new Error("npm pack did not return a tarball filename."); - } + const tarballName = await findSingleTarball(outputDir); return { outputDir, diff --git a/scripts/public-facade-smoke.mjs b/scripts/public-facade-smoke.mjs index 16c72ce..91446ea 100644 --- a/scripts/public-facade-smoke.mjs +++ b/scripts/public-facade-smoke.mjs @@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url"; import { resolveRcCommandExecution } from "./rc-validation.mjs"; import { assertPackedSurface, - extractPackJsonPayload, + findSingleTarball, inspectPackedFiles, } from "./root-release-guard.mjs"; @@ -56,16 +56,10 @@ export async function runPublicFacadeSmoke(options = {}) { await mkdir(appDir, { recursive: true }); try { - const packRun = await runCommand(["npm", "pack", "--json", "--ignore-scripts", "--pack-destination", packDir], { + await runCommand(["npm", "pack", "--ignore-scripts", "--pack-destination", packDir], { cwd: rootDir, }); - const packArtifacts = extractPackJsonPayload(packRun.stdout); - const tarballName = Array.isArray(packArtifacts) ? packArtifacts[0]?.filename : undefined; - - if (typeof tarballName !== "string" || tarballName.trim().length === 0) { - throw new Error("npm pack did not return a tarball filename."); - } - + const tarballName = await findSingleTarball(packDir); const tarballPath = path.join(packDir, tarballName); await writeFile( path.join(appDir, "package.json"), diff --git a/scripts/root-release-guard.mjs b/scripts/root-release-guard.mjs index c0b0cef..78ebaae 100644 --- a/scripts/root-release-guard.mjs +++ b/scripts/root-release-guard.mjs @@ -114,13 +114,10 @@ export async function inspectPackedFiles(options = {}) { } await runCommand(command, { cwd: rootDir }); - const tarballs = (await readdir(packDestination)).filter((entry) => entry.endsWith(".tgz")); - if (tarballs.length !== 1) { - throw new Error(`npm pack produced ${tarballs.length} tarballs; expected exactly one.`); - } + const tarballName = await findSingleTarball(packDestination); const tarRun = await runCommand( - ["tar", "-tf", path.join(packDestination, tarballs[0])], + ["tar", "-tf", path.join(packDestination, tarballName)], { cwd: rootDir }, ); const files = normalizePackedTarEntries(tarRun.stdout.split(/\r?\n/u)); @@ -134,6 +131,14 @@ export async function inspectPackedFiles(options = {}) { } } +export async function findSingleTarball(directory) { + const tarballs = (await readdir(directory)).filter((entry) => entry.endsWith(".tgz")); + if (tarballs.length !== 1) { + throw new Error(`npm pack produced ${tarballs.length} tarballs; expected exactly one.`); + } + return tarballs[0]; +} + export function normalizePackedTarEntries(entries) { return entries .map((entry) => entry.trim().replace(/^package\//u, "")) diff --git a/scripts/tests/root-release-guard.test.mjs b/scripts/tests/root-release-guard.test.mjs index d852b69..e87d081 100644 --- a/scripts/tests/root-release-guard.test.mjs +++ b/scripts/tests/root-release-guard.test.mjs @@ -10,6 +10,7 @@ import { assertRootVersionPolicy, assertVendoredCliManifest, extractPackFilePaths, + findSingleTarball, normalizePackedTarEntries, runRootReleaseGuard, } from "../root-release-guard.mjs"; @@ -110,6 +111,14 @@ test("normalizePackedTarEntries reads the real npm tarball surface", () => { ); }); +test("findSingleTarball discovers the real npm pack artifact", async () => { + await withTempRoot(async (tempRoot) => { + await writeFile(path.join(tempRoot, "martin-loop-0.5.0.tgz"), "artifact", "utf8"); + + assert.equal(await findSingleTarball(tempRoot), "martin-loop-0.5.0.tgz"); + }); +}); + test("assertVendoredCliManifest accepts the sanitized vendored CLI package manifest", async () => { await withTempRoot(async (tempRoot) => { await writeFile(