Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 11 additions & 21 deletions packages/mcp/scripts/smoke-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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";
}
Expand Down
13 changes: 4 additions & 9 deletions scripts/pack-root-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 3 additions & 9 deletions scripts/public-facade-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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"),
Expand Down
15 changes: 10 additions & 5 deletions scripts/root-release-guard.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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, ""))
Expand Down
9 changes: 9 additions & 0 deletions scripts/tests/root-release-guard.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
assertRootVersionPolicy,
assertVendoredCliManifest,
extractPackFilePaths,
findSingleTarball,
normalizePackedTarEntries,
runRootReleaseGuard,
} from "../root-release-guard.mjs";
Expand Down Expand Up @@ -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(
Expand Down
Loading