Files
trade-message-center/scripts/release.test.mjs
T

203 lines
7.6 KiB
JavaScript

// 验证发布命令的版本和 Git 门禁。
import assert from "node:assert/strict";
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { prepareRelease, publishRelease } from "./release.mjs";
const createWorkspace = (version = "0.8.16") => {
const rootDir = mkdtempSync(join(tmpdir(), "tmc-release-"));
mkdirSync(join(rootDir, "apps"));
mkdirSync(join(rootDir, "packages"));
writeFileSync(
join(rootDir, "package.json"),
`${JSON.stringify({ name: "fixture", version }, null, 4)}\n`,
);
for (const [directory, name] of [
["apps", "extension"],
["apps", "server"],
["packages", "contract"],
]) {
const packageDir = join(rootDir, directory, name);
mkdirSync(packageDir);
writeFileSync(
join(packageDir, "package.json"),
`${JSON.stringify({ name, version }, null, 4)}\n`,
);
}
return rootDir;
};
const commandKey = (command, arguments_) => [command, ...arguments_].join(" ");
const createRunner = (responses) => {
const calls = [];
const execute = (command, arguments_) => {
calls.push({ command, arguments_ });
const key = commandKey(command, arguments_);
const response = responses.get(key);
if (response instanceof Error) throw response;
if (Array.isArray(response)) return response.shift() ?? "";
return response ?? "";
};
return { calls, execute };
};
const expectedFiles = [
"package.json",
"apps/extension/package.json",
"apps/server/package.json",
"packages/contract/package.json",
];
test("prepares a patch release using only root-derived version mirrors", () => {
const rootDir = createWorkspace();
const { calls, execute } = createRunner(
new Map([
[commandKey("git", ["status", "--porcelain"]), ""],
[commandKey("git", ["branch", "--show-current"]), "release/0.8.17\n"],
[commandKey("git", ["rev-parse", "origin/main"]), "main-sha\n"],
[commandKey("git", ["merge-base", "origin/main", "HEAD"]), "main-sha\n"],
[commandKey("git", ["diff", "--name-only"]), `${expectedFiles.join("\n")}\n`],
[commandKey("git", ["ls-files", "--others", "--exclude-standard"]), ""],
[commandKey("git", ["--no-pager", "diff", "--check"]), ""],
[
commandKey("git", ["diff", "--cached", "--name-only"]),
`${expectedFiles.join("\n")}\n`,
],
]),
);
assert.equal(prepareRelease({ rootDir, execute }), "0.8.17");
assert.equal(JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8")).version, "0.8.17");
for (const packagePath of expectedFiles.slice(1)) {
assert.equal(
JSON.parse(readFileSync(join(rootDir, packagePath), "utf8")).version,
"0.8.17",
);
}
assert.deepEqual(
calls.filter(({ command }) => command === "pnpm").map(({ arguments_ }) => arguments_),
[
["format:check"],
["--filter", "@trade-message-center/server", "db:check"],
["typecheck"],
["test"],
["build"],
],
);
assert.ok(
calls.some(
({ command, arguments_ }) =>
commandKey(command, arguments_) ===
commandKey("git", ["--no-pager", "diff", "--check"]),
),
);
assert.ok(
calls.some(
({ command, arguments_ }) =>
command === "git" &&
commandKey(command, arguments_) ===
commandKey("git", ["commit", "-m", "chore: release 0.8.17"]),
),
);
assert.ok(
calls.some(
({ command, arguments_ }) =>
command === "git" &&
commandKey(command, arguments_) ===
commandKey("git", ["push", "--set-upstream", "origin", "release/0.8.17"]),
),
);
});
test("prepares a release directly on main", () => {
const rootDir = createWorkspace();
const { calls, execute } = createRunner(
new Map([
[commandKey("git", ["status", "--porcelain"]), ""],
[commandKey("git", ["branch", "--show-current"]), "main\n"],
[commandKey("git", ["rev-parse", "origin/main"]), "main-sha\n"],
[commandKey("git", ["merge-base", "origin/main", "HEAD"]), "main-sha\n"],
[commandKey("git", ["diff", "--name-only"]), `${expectedFiles.join("\n")}\n`],
[commandKey("git", ["ls-files", "--others", "--exclude-standard"]), ""],
[commandKey("git", ["--no-pager", "diff", "--check"]), ""],
[
commandKey("git", ["diff", "--cached", "--name-only"]),
`${expectedFiles.join("\n")}\n`,
],
]),
);
assert.equal(prepareRelease({ rootDir, execute }), "0.8.17");
assert.ok(
calls.some(
({ command, arguments_ }) =>
commandKey(command, arguments_) ===
commandKey("git", ["push", "--set-upstream", "origin", "main"]),
),
);
});
test("restores all version files when staging fails before the release commit", () => {
const rootDir = createWorkspace();
const { calls, execute } = createRunner(
new Map([
[commandKey("git", ["status", "--porcelain"]), ""],
[commandKey("git", ["branch", "--show-current"]), "release/0.8.17\n"],
[commandKey("git", ["rev-parse", "origin/main"]), "main-sha\n"],
[commandKey("git", ["merge-base", "origin/main", "HEAD"]), "main-sha\n"],
[commandKey("git", ["diff", "--name-only"]), `${expectedFiles.join("\n")}\n`],
[commandKey("git", ["ls-files", "--others", "--exclude-standard"]), ""],
[commandKey("git", ["--no-pager", "diff", "--check"]), ""],
[commandKey("git", ["add", "--", ...expectedFiles]), new Error("index lock denied")],
]),
);
assert.throws(() => prepareRelease({ rootDir, execute }), /restored the root/u);
for (const filePath of expectedFiles) {
const version = JSON.parse(readFileSync(join(rootDir, filePath), "utf8")).version;
assert.equal(version, "0.8.16");
}
assert.ok(
calls.some(
({ command, arguments_ }) =>
commandKey(command, arguments_) ===
commandKey("git", ["add", "--", ...expectedFiles]),
),
);
});
test("publishes only the exact origin main commit with a new tag", () => {
const rootDir = createWorkspace();
const { calls, execute } = createRunner(
new Map([
[commandKey("git", ["status", "--porcelain"]), ""],
[commandKey("git", ["branch", "--show-current"]), "main\n"],
[commandKey("git", ["rev-parse", "HEAD"]), "main-sha\n"],
[commandKey("git", ["rev-parse", "origin/main"]), "main-sha\n"],
[commandKey("git", ["tag", "--list", "v0.8.16"]), ""],
[commandKey("git", ["ls-remote", "--tags", "origin", "refs/tags/v0.8.16"]), ""],
]),
);
assert.equal(publishRelease({ rootDir, execute }), "v0.8.16");
assert.ok(
calls.some(
({ command, arguments_ }) =>
commandKey(command, arguments_) ===
commandKey("git", ["tag", "--annotate", "v0.8.16", "--message", "Release v0.8.16"]),
),
);
assert.ok(
calls.some(
({ command, arguments_ }) =>
commandKey(command, arguments_) ===
commandKey("git", ["push", "origin", "v0.8.16"]),
),
);
});