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
4 changes: 4 additions & 0 deletions packages/gittensory-miner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"files": [
"bin",
"lib",
"docs",
"schema",
"DEPLOYMENT.md",
"Dockerfile",
"expected-engine.version"
],
"scripts": {
Expand Down
17 changes: 16 additions & 1 deletion scripts/check-miner-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,20 @@ const ALLOWED = [
/^package\.json$/,
/^README\.md$/,
/^expected-engine\.version$/,
// Operational material shipped for `npm install -g` users so the quickstart doesn't require a repo visit (#4874):
/^DEPLOYMENT\.md$/,
/^Dockerfile$/,
/^docs\/[a-z0-9-]+\.md$/,
/^schema\/[a-z0-9.-]+\.json$/,
];
const REQUIRED = [
"bin/gittensory-miner.js",
"package.json",
// The operational files #4874 shipped — asserted present so they can never silently drop out of the package again.
"DEPLOYMENT.md",
"Dockerfile",
"schema/miner-goal-spec.schema.json",
];
const REQUIRED = ["bin/gittensory-miner.js", "package.json"];
const FORBIDDEN_PATH = /(^|\/)(\.dev\.vars|\.env|\.npmrc|.*\.pem|.*private.*key.*|.*secret.*)$/i;
const FORBIDDEN_CONTENT =
/(BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|github_pat_[A-Za-z0-9_]+|gh[pousr]_[A-Za-z0-9_]+|gts_[0-9a-f]{64}|[A-Z0-9_]*(TOKEN|SECRET|PRIVATE_KEY)=)/;
Expand All @@ -31,6 +43,9 @@ export function validateMinerPackFileList(files, readContent) {
if (!paths.some((file) => /^lib\/([a-z0-9-]+\/)?[a-z0-9-]+\.js$/.test(file))) {
throw new Error("Miner package is missing lib/*.js artifacts");
}
if (!paths.some((file) => /^docs\/[a-z0-9-]+\.md$/.test(file))) {
throw new Error("Miner package is missing docs/*.md operational documentation");
}
return paths;
}

Expand Down
69 changes: 68 additions & 1 deletion test/unit/check-miner-package.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,14 @@ describe("check-miner-package script", () => {

it("rejects a package missing lib artifacts", () => {
const result = runChecker({
CHECK_MINER_PACK_TEST_FILES: JSON.stringify(["package.json", "bin/gittensory-miner.js"]),
// Every REQUIRED file present so the check reaches (and fails on) the lib-artifacts guard specifically.
CHECK_MINER_PACK_TEST_FILES: JSON.stringify([
"package.json",
"bin/gittensory-miner.js",
"DEPLOYMENT.md",
"Dockerfile",
"schema/miner-goal-spec.schema.json",
]),
CHECK_MINER_PACK_TEST_CONTENT: "{}",
});
expect(result.status).toBe(1);
Expand All @@ -101,4 +108,64 @@ describe("check-miner-package script", () => {
expect(result.status).toBe(1);
expect(result.out).toContain("Secret-like content found in miner package file:");
});

describe("operational files (#4874)", () => {
// A complete, valid package including the operational material — DEPLOYMENT.md, the Dockerfile, a docs/*.md,
// and the schema — is accepted.
const FULL_PACKAGE = [
"package.json",
"bin/gittensory-miner.js",
"lib/cli.js",
"DEPLOYMENT.md",
"Dockerfile",
"docs/coding-agent-driver.md",
"schema/miner-goal-spec.schema.json",
];

it("accepts DEPLOYMENT.md, the Dockerfile, docs/*.md, and schema/*.json", () => {
const result = runChecker({
CHECK_MINER_PACK_TEST_FILES: JSON.stringify(FULL_PACKAGE),
CHECK_MINER_PACK_TEST_CONTENT: "operational docs, nothing secret",
});
expect(result.status).toBe(0);
expect(result.out).toMatch(/^Miner package dry-run ok:/);
expect(result.out).toContain("DEPLOYMENT.md");
expect(result.out).toContain("docs/coding-agent-driver.md");
expect(result.out).toContain("schema/miner-goal-spec.schema.json");
});

it("requires DEPLOYMENT.md to be published (regression guard for #4874)", () => {
const result = runChecker({
CHECK_MINER_PACK_TEST_FILES: JSON.stringify(FULL_PACKAGE.filter((f) => f !== "DEPLOYMENT.md")),
CHECK_MINER_PACK_TEST_CONTENT: "ok",
});
expect(result.status).toBe(1);
expect(result.out).toContain("Miner package is missing required file: DEPLOYMENT.md");
});

it("requires at least one docs/*.md file to be published", () => {
const result = runChecker({
CHECK_MINER_PACK_TEST_FILES: JSON.stringify(FULL_PACKAGE.filter((f) => !f.startsWith("docs/"))),
CHECK_MINER_PACK_TEST_CONTENT: "ok",
});
expect(result.status).toBe(1);
expect(result.out).toContain("Miner package is missing docs/*.md operational documentation");
});

it("keeps the docs allowlist tight — a non-.md or nested docs file is still rejected", () => {
const nonMarkdown = runChecker({
CHECK_MINER_PACK_TEST_FILES: JSON.stringify([...FULL_PACKAGE, "docs/notes.txt"]),
CHECK_MINER_PACK_TEST_CONTENT: "ok",
});
expect(nonMarkdown.status).toBe(1);
expect(nonMarkdown.out).toContain("Unexpected file in miner package: docs/notes.txt");

const nested = runChecker({
CHECK_MINER_PACK_TEST_FILES: JSON.stringify([...FULL_PACKAGE, "docs/nested/guide.md"]),
CHECK_MINER_PACK_TEST_CONTENT: "ok",
});
expect(nested.status).toBe(1);
expect(nested.out).toContain("Unexpected file in miner package: docs/nested/guide.md");
});
});
});