150 lines
5.0 KiB
JavaScript
150 lines
5.0 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { execFileSync } from "node:child_process";
|
|
import { mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const extensionsRoot = path.join(root, "extensions");
|
|
const registry = "https://git.jb.codes/api/packages/jb/npm/";
|
|
const expectedWorkspaces = [
|
|
"bash-cwd",
|
|
"copy-all",
|
|
"dictate",
|
|
"effort-controls",
|
|
"file-search",
|
|
"interactive-subagents",
|
|
"lm-studio",
|
|
"native-hooks",
|
|
"prompt-snippets",
|
|
"shared",
|
|
"subagents",
|
|
"todo-herdr",
|
|
"update",
|
|
];
|
|
|
|
function readJson(file) {
|
|
return JSON.parse(readFileSync(file, "utf8"));
|
|
}
|
|
|
|
function workspaceManifest(directory) {
|
|
return readJson(path.join(extensionsRoot, directory, "package.json"));
|
|
}
|
|
|
|
function dependencyText(manifest) {
|
|
return JSON.stringify({
|
|
dependencies: manifest.dependencies,
|
|
optionalDependencies: manifest.optionalDependencies,
|
|
peerDependencies: manifest.peerDependencies,
|
|
});
|
|
}
|
|
|
|
test("workspace inventory contains only active extensions", () => {
|
|
const rootManifest = readJson(path.join(root, "package.json"));
|
|
assert.equal(rootManifest.private, true);
|
|
assert.deepEqual(rootManifest.workspaces.packages, [
|
|
"extensions/*",
|
|
]);
|
|
|
|
const actual = readdirSync(extensionsRoot)
|
|
.filter((directory) => {
|
|
try {
|
|
workspaceManifest(directory);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
})
|
|
.sort();
|
|
assert.deepEqual(actual, expectedWorkspaces);
|
|
});
|
|
|
|
test("imported package snapshots retain explicit main-branch provenance", () => {
|
|
const inventory = readJson(path.join(root, "config", "imported-packages.json"));
|
|
assert.equal(inventory.version, 1);
|
|
assert.deepEqual(
|
|
inventory.packages.map(({ name, branch, target }) => ({ name, branch, target })),
|
|
[
|
|
{
|
|
name: "pi-interactive-subagents",
|
|
branch: "main",
|
|
target: "extensions/interactive-subagents",
|
|
},
|
|
{
|
|
name: "pi-native-hooks",
|
|
branch: "main",
|
|
target: "extensions/native-hooks",
|
|
},
|
|
{
|
|
name: "pi-todo-herdr",
|
|
branch: "main",
|
|
target: "extensions/todo-herdr",
|
|
},
|
|
],
|
|
);
|
|
for (const entry of inventory.packages) {
|
|
assert.match(entry.commit, /^[0-9a-f]{40}$/);
|
|
assert.match(entry.source, /^ssh:\/\/git@git\.jb\.codes:7722\/jb\//);
|
|
assert.match(
|
|
readJson(path.join(root, entry.target, "package.json")).name,
|
|
/^@jbcode\/pi-/,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("publishable workspaces have Gitea-safe package contracts", () => {
|
|
for (const directory of expectedWorkspaces) {
|
|
const packageRoot = path.join(extensionsRoot, directory);
|
|
const manifest = workspaceManifest(directory);
|
|
if (manifest.private) continue;
|
|
|
|
assert.match(manifest.name, /^@jbcode\/pi-[a-z0-9-]+$/);
|
|
assert.equal(manifest.publishConfig?.registry, registry);
|
|
assert.equal(manifest.engines?.node, ">=22.19.0");
|
|
assert.equal(readFileSync(path.join(packageRoot, "LICENSE"), "utf8").length > 0, true);
|
|
assert.equal(readFileSync(path.join(packageRoot, "README.md"), "utf8").length > 0, true);
|
|
|
|
for (const entry of manifest.pi?.extensions ?? []) {
|
|
assert.equal(readFileSync(path.resolve(packageRoot, entry), "utf8").length > 0, true);
|
|
}
|
|
for (const entry of manifest.pi?.skills ?? []) {
|
|
assert.equal(readFileSync(path.resolve(packageRoot, entry, "SKILL.md"), "utf8").length > 0, true);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("Bun packs concrete dependency versions and excludes tests", () => {
|
|
const outputRoot = mkdtempSync(path.join(tmpdir(), "harness-tools-pack-"));
|
|
try {
|
|
for (const directory of expectedWorkspaces) {
|
|
const manifest = workspaceManifest(directory);
|
|
if (manifest.private) continue;
|
|
const packageRoot = path.join(extensionsRoot, directory);
|
|
const before = new Set(readdirSync(outputRoot));
|
|
execFileSync(
|
|
"bun",
|
|
["pm", "pack", "--destination", outputRoot, "--ignore-scripts", "--quiet"],
|
|
{ cwd: packageRoot, stdio: "pipe" },
|
|
);
|
|
const created = readdirSync(outputRoot).filter((entry) => !before.has(entry));
|
|
assert.equal(created.length, 1, `${manifest.name} should create one tarball`);
|
|
const tarball = path.join(outputRoot, created[0]);
|
|
const packedManifest = JSON.parse(
|
|
execFileSync("tar", ["-xOf", tarball, "package/package.json"], {
|
|
encoding: "utf8",
|
|
}),
|
|
);
|
|
assert.equal(dependencyText(packedManifest).includes("catalog:"), false);
|
|
assert.equal(dependencyText(packedManifest).includes("workspace:"), false);
|
|
const files = execFileSync("tar", ["-tzf", tarball], { encoding: "utf8" });
|
|
assert.match(files, /^package\/LICENSE$/m);
|
|
assert.match(files, /^package\/README\.md$/m);
|
|
assert.doesNotMatch(files, /\.(?:test|spec)\.ts$/m);
|
|
}
|
|
} finally {
|
|
rmSync(outputRoot, { recursive: true, force: true });
|
|
}
|
|
});
|