151 lines
4.3 KiB
JavaScript
151 lines
4.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import test from "node:test";
|
|
|
|
const repoRoot = new URL("../..", import.meta.url).pathname.replace(/\/$/, "");
|
|
|
|
function writeExecutable(path, body) {
|
|
writeFileSync(path, body, { mode: 0o755 });
|
|
}
|
|
|
|
function createReleaseFixture() {
|
|
const fixtureDir = mkdtempSync(join(tmpdir(), "paperclip-release-dry-run-"));
|
|
const scriptsDir = join(fixtureDir, "scripts");
|
|
const binDir = join(fixtureDir, "bin");
|
|
const callLog = join(fixtureDir, "calls.log");
|
|
|
|
mkdirSync(scriptsDir, { recursive: true });
|
|
mkdirSync(join(fixtureDir, "releases"));
|
|
mkdirSync(binDir);
|
|
writeFileSync(callLog, "");
|
|
|
|
copyFileSync(join(repoRoot, "scripts", "release.sh"), join(scriptsDir, "release.sh"));
|
|
chmodSync(join(scriptsDir, "release.sh"), 0o755);
|
|
|
|
writeFileSync(
|
|
join(scriptsDir, "release-lib.sh"),
|
|
`#!/usr/bin/env bash
|
|
release_info() { echo "$@"; }
|
|
release_fail() { echo "Error: $*" >&2; exit 1; }
|
|
resolve_release_remote() { printf 'origin\\n'; }
|
|
fetch_release_remote() { :; }
|
|
git_current_branch() { printf 'master\\n'; }
|
|
get_last_stable_tag() { printf 'v2026.709.0\\n'; }
|
|
get_current_stable_version() { printf '2026.709.0\\n'; }
|
|
utc_date_iso() { printf '2026-07-10\\n'; }
|
|
list_public_package_info() { printf 'cli\\tpaperclipai\\t0.0.0\\n'; }
|
|
next_stable_version() { printf '2026.710.0\\n'; }
|
|
next_canary_version() { printf '2026.710.0-canary.0\\n'; }
|
|
release_notes_file() { printf '%s/releases/v%s.md\\n' "$REPO_ROOT" "$1"; }
|
|
stable_tag_name() { printf 'v%s\\n' "$1"; }
|
|
canary_tag_name() { printf 'canary/v%s\\n' "$1"; }
|
|
require_clean_worktree() { :; }
|
|
require_npm_publish_auth() { :; }
|
|
git_local_tag_exists() { return 1; }
|
|
git_remote_tag_exists() { return 1; }
|
|
npm_package_version_exists() { return 1; }
|
|
set_public_package_version() { :; }
|
|
`,
|
|
);
|
|
|
|
writeExecutable(
|
|
join(scriptsDir, "release-registry-versions.mjs"),
|
|
`#!/usr/bin/env node
|
|
const [mode] = process.argv.slice(2);
|
|
if (mode === "fetch") {
|
|
process.stdout.write('{"paperclipai":[]}\\n');
|
|
process.exit(0);
|
|
}
|
|
if (mode === "assert-absent") {
|
|
process.exit(0);
|
|
}
|
|
process.exit(2);
|
|
`,
|
|
);
|
|
|
|
writeExecutable(
|
|
join(binDir, "git"),
|
|
`#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
if [ "$1" = "-C" ]; then
|
|
shift 2
|
|
fi
|
|
printf 'git %s\\n' "$*" >> "$FAKE_CALL_LOG"
|
|
case "$1" in
|
|
rev-parse)
|
|
if [ "\${2:-}" = "HEAD" ]; then
|
|
echo abcdef1234567890
|
|
exit 0
|
|
fi
|
|
;;
|
|
diff|ls-files)
|
|
exit 0
|
|
;;
|
|
checkout)
|
|
exit 0
|
|
;;
|
|
esac
|
|
exit 0
|
|
`,
|
|
);
|
|
|
|
writeExecutable(
|
|
join(binDir, "pnpm"),
|
|
`#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
printf 'pnpm %s\\n' "$*" >> "$FAKE_CALL_LOG"
|
|
if [ "$*" = "build" ]; then
|
|
echo "fixture stopped at workspace build"
|
|
exit 42
|
|
fi
|
|
exit 0
|
|
`,
|
|
);
|
|
|
|
return { binDir, callLog, fixtureDir, script: join(scriptsDir, "release.sh") };
|
|
}
|
|
|
|
function runRelease(args) {
|
|
const fixture = createReleaseFixture();
|
|
const result = spawnSync(fixture.script, args, {
|
|
cwd: fixture.fixtureDir,
|
|
encoding: "utf8",
|
|
env: {
|
|
...process.env,
|
|
PATH: `${fixture.binDir}:${process.env.PATH}`,
|
|
FAKE_CALL_LOG: fixture.callLog,
|
|
},
|
|
});
|
|
|
|
const calls = readFileSync(fixture.callLog, "utf8");
|
|
rmSync(fixture.fixtureDir, { recursive: true, force: true });
|
|
|
|
return {
|
|
calls,
|
|
output: result.stdout + result.stderr,
|
|
status: result.status,
|
|
};
|
|
}
|
|
|
|
test("stable dry-run preview does not require a pre-authored release notes file", () => {
|
|
const result = runRelease(["stable", "--skip-verify", "--dry-run"]);
|
|
|
|
assert.equal(result.status, 42);
|
|
assert.match(result.output, /==> Release plan/);
|
|
assert.match(result.output, /==> Step 2\/7: Building workspace artifacts/);
|
|
assert.doesNotMatch(result.output, /stable release notes file is required/);
|
|
assert.match(result.calls, /^pnpm build$/m);
|
|
});
|
|
|
|
test("stable publish still requires release notes before publish work starts", () => {
|
|
const result = runRelease(["stable", "--skip-verify"]);
|
|
|
|
assert.equal(result.status, 1);
|
|
assert.match(result.output, /stable release notes file is required/);
|
|
assert.doesNotMatch(result.output, /==> Step 2\/7: Building workspace artifacts/);
|
|
assert.doesNotMatch(result.calls, /^pnpm /m);
|
|
});
|