chore: add bun local release smoke install

This commit is contained in:
Mario Zechner
2026-05-20 16:02:30 +02:00
parent 17cc86a479
commit beefa6a4fe
3 changed files with 53 additions and 25 deletions

View File

@@ -194,9 +194,11 @@ Create provider file exporting:
```bash ```bash
npm run release:local -- --out /tmp/pi-local-release --force npm run release:local -- --out /tmp/pi-local-release --force
cd /tmp cd /tmp
/tmp/pi-local-release/bin/pi --help /tmp/pi-local-release/node/pi --help
/tmp/pi-local-release/bin/pi --version /tmp/pi-local-release/node/pi --version
/tmp/pi-local-release/bin/pi /tmp/pi-local-release/node/pi
/tmp/pi-local-release/bun/pi --help
/tmp/pi-local-release/bun/pi --version
``` ```
In the interactive smoke test, verify startup, model/account listing, and at least one real prompt with the intended default provider. Treat failures as release blockers unless the user explicitly accepts the risk. In the interactive smoke test, verify startup, model/account listing, and at least one real prompt with the intended default provider. Treat failures as release blockers unless the user explicitly accepts the risk.

View File

@@ -79,7 +79,7 @@ We treat npm dependency changes as reviewed code changes.
- `package-lock.json` is the dependency ground truth. Pre-commit blocks accidental lockfile commits unless `PI_ALLOW_LOCKFILE_CHANGE=1` is set. - `package-lock.json` is the dependency ground truth. Pre-commit blocks accidental lockfile commits unless `PI_ALLOW_LOCKFILE_CHANGE=1` is set.
- `npm run check` verifies pinned direct deps, native TypeScript import compatibility, and the generated coding-agent shrinkwrap. - `npm run check` verifies pinned direct deps, native TypeScript import compatibility, and the generated coding-agent shrinkwrap.
- The published CLI package includes `packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile, to pin transitive deps for npm users. - The published CLI package includes `packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile, to pin transitive deps for npm users.
- Release smoke tests use `npm run release:local` to build, pack, and install outside the repo before publishing. - Release smoke tests use `npm run release:local` to build, pack, and create isolated npm and Bun installs outside the repo before publishing.
- Local release installs, documented npm installs, and `pi update --self` use `--ignore-scripts` where supported. - Local release installs, documented npm installs, and `pi update --self` use `--ignore-scripts` where supported.
- CI installs with `npm ci --ignore-scripts`, and a scheduled GitHub workflow runs `npm audit --omit=dev` plus `npm audit signatures --omit=dev`. - CI installs with `npm ci --ignore-scripts`, and a scheduled GitHub workflow runs `npm audit --omit=dev` plus `npm audit signatures --omit=dev`.
- Shrinkwrap generation has an explicit allowlist for dependency lifecycle scripts; new lifecycle-script deps fail checks until reviewed. - Shrinkwrap generation has an explicit allowlist for dependency lifecycle scripts; new lifecycle-script deps fail checks until reviewed.

View File

@@ -19,16 +19,17 @@ Builds and packs the publishable packages, then installs the tarballs into an
isolated directory outside the repository for local release testing. isolated directory outside the repository for local release testing.
Options: Options:
--out <dir> Output directory. Defaults to a new directory under ${tmpdir()} --out <dir> Output directory. Defaults to a new directory under ${tmpdir()}
--force Remove --out first if it already exists --force Remove --out first if it already exists
--skip-check Do not run npm run check before building --skip-check Do not run npm run check before building
--skip-install Only create tarballs; do not create the isolated install --skip-install Only create tarballs; do not create isolated installs
--help Show this help --skip-bun-install Do not create the isolated Bun install
--help Show this help
`); `);
} }
function parseArgs() { function parseArgs() {
const options = { force: false, outDir: undefined, skipCheck: false, skipInstall: false }; const options = { force: false, outDir: undefined, skipBunInstall: false, skipCheck: false, skipInstall: false };
const args = process.argv.slice(2); const args = process.argv.slice(2);
for (let i = 0; i < args.length; i++) { for (let i = 0; i < args.length; i++) {
@@ -49,6 +50,10 @@ function parseArgs() {
options.skipInstall = true; options.skipInstall = true;
continue; continue;
} }
if (arg === "--skip-bun-install") {
options.skipBunInstall = true;
continue;
}
if (arg === "--out") { if (arg === "--out") {
const value = args[++i]; const value = args[++i];
if (!value) { if (!value) {
@@ -82,6 +87,10 @@ function readPackageJson(directory) {
return JSON.parse(readFileSync(join(directory, "package.json"), "utf8")); return JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
} }
function commandExists(command) {
return spawnSync(command, ["--version"], { stdio: "ignore" }).status === 0;
}
function isInsidePath(child, parent) { function isInsidePath(child, parent) {
const relativePath = relative(parent, child); const relativePath = relative(parent, child);
return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath)); return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath));
@@ -138,8 +147,8 @@ if (rootPackageJson.name !== "pi-monorepo") {
const outDir = prepareOutputDirectory(options, repoRoot); const outDir = prepareOutputDirectory(options, repoRoot);
const tarballDirectory = join(outDir, "tarballs"); const tarballDirectory = join(outDir, "tarballs");
const installDirectory = join(outDir, "install"); const nodeInstallDirectory = join(outDir, "node");
const binDirectory = join(outDir, "bin"); const bunInstallDirectory = join(outDir, "bun");
mkdirSync(tarballDirectory, { recursive: true }); mkdirSync(tarballDirectory, { recursive: true });
if (!options.skipCheck) { if (!options.skipCheck) {
@@ -158,18 +167,28 @@ for (const pkg of packages) {
} }
if (!options.skipInstall) { if (!options.skipInstall) {
mkdirSync(installDirectory, { recursive: true }); mkdirSync(nodeInstallDirectory, { recursive: true });
const dependencies = Object.fromEntries( const dependencies = Object.fromEntries(
packages.map((pkg) => [pkg.name, fileSpecifier(installDirectory, tarballs.get(pkg.name))]), packages.map((pkg) => [pkg.name, fileSpecifier(nodeInstallDirectory, tarballs.get(pkg.name))]),
);
writeFileSync(
join(installDirectory, "package.json"),
`${JSON.stringify({ private: true, dependencies }, undefined, "\t")}\n`,
); );
const installPackageJson = `${JSON.stringify({ private: true, dependencies }, undefined, "\t")}\n`;
writeFileSync(join(nodeInstallDirectory, "package.json"), installPackageJson);
run("npm", ["install", "--omit=dev", "--ignore-scripts"], { cwd: installDirectory }); run("npm", ["install", "--omit=dev", "--ignore-scripts"], { cwd: nodeInstallDirectory });
mkdirSync(binDirectory, { recursive: true }); symlinkSync(join("node_modules", ".bin", "pi"), join(nodeInstallDirectory, "pi"));
symlinkSync(join(installDirectory, "node_modules", ".bin", "pi"), join(binDirectory, "pi"));
if (!options.skipBunInstall) {
if (!commandExists("bun")) {
throw new Error("Bun is required for the isolated Bun install. Use --skip-bun-install to skip it.");
}
mkdirSync(bunInstallDirectory, { recursive: true });
const bunDependencies = Object.fromEntries(
packages.map((pkg) => [pkg.name, fileSpecifier(bunInstallDirectory, tarballs.get(pkg.name))]),
);
writeFileSync(join(bunInstallDirectory, "package.json"), `${JSON.stringify({ private: true, dependencies: bunDependencies }, undefined, "\t")}\n`);
run("bun", ["install", "--production", "--ignore-scripts"], { cwd: bunInstallDirectory });
symlinkSync(join("node_modules", ".bin", "pi"), join(bunInstallDirectory, "pi"));
}
} }
console.log("\nLocal release artifacts created:"); console.log("\nLocal release artifacts created:");
@@ -180,8 +199,15 @@ for (const tarball of tarballs.values()) {
} }
if (!options.skipInstall) { if (!options.skipInstall) {
console.log("\nIsolated install:"); console.log("\nIsolated npm install:");
console.log(` ${installDirectory}`); console.log(` ${nodeInstallDirectory}`);
console.log("\nRun the locally packed CLI from outside the repository:"); console.log("\nRun the locally packed npm CLI from outside the repository:");
console.log(` ${join(binDirectory, "pi")} --help`); console.log(` ${join(nodeInstallDirectory, "pi")} --help`);
if (!options.skipBunInstall) {
console.log("\nIsolated Bun install:");
console.log(` ${bunInstallDirectory}`);
console.log("\nRun the locally packed Bun CLI from outside the repository:");
console.log(` ${join(bunInstallDirectory, "pi")} --help`);
}
} }