From 545efa518ba626a156675019733ece99ec1fdf3b Mon Sep 17 00:00:00 2001 From: carlh171112 Date: Sun, 12 Jul 2026 18:29:56 -0700 Subject: [PATCH] feat(miner): update package structure and validation for operational files This commit enhances the gittensory-miner package by adding new operational files to the package.json, including "docs", "schema", "DEPLOYMENT.md", and "Dockerfile". The validation script has been updated to ensure these files are present, preventing silent omissions. Additionally, unit tests have been added to verify the presence of these files, ensuring compliance with the new structure. Closes #4874. --- packages/gittensory-miner/package.json | 4 ++ scripts/check-miner-package.mjs | 17 ++++++- test/unit/check-miner-package.test.ts | 69 +++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/packages/gittensory-miner/package.json b/packages/gittensory-miner/package.json index 8281d9d470..0bfdac0b17 100644 --- a/packages/gittensory-miner/package.json +++ b/packages/gittensory-miner/package.json @@ -30,6 +30,10 @@ "files": [ "bin", "lib", + "docs", + "schema", + "DEPLOYMENT.md", + "Dockerfile", "expected-engine.version" ], "scripts": { diff --git a/scripts/check-miner-package.mjs b/scripts/check-miner-package.mjs index 3c3dfabb5f..3ab81c3313 100644 --- a/scripts/check-miner-package.mjs +++ b/scripts/check-miner-package.mjs @@ -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)=)/; @@ -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; } diff --git a/test/unit/check-miner-package.test.ts b/test/unit/check-miner-package.test.ts index 62cf075027..eb228ab1cb 100644 --- a/test/unit/check-miner-package.test.ts +++ b/test/unit/check-miner-package.test.ts @@ -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); @@ -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"); + }); + }); });