commit 9aa6fc738f8a1cfd6ec88c837c9e9131a5967e27
parent 38d9ca564e16f5189815cc76f879121bfba66440
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 13:12:09 -0400
common: build homepage publishes the source; `source publish|audit` rows; doctor's source block
buildHomepage runs publishSource between compose and `next build`, so the CLI,
the runbooks' home scripts and the /sites homepage jobs all produce it, and a
refusal fails the build before `next build`. `--no-source` (CLI only) removes
the previously published source instead of shipping one audited against
older rules. `archilyzer source publish [--force] [--check] [--keep-scratch]`
and `source audit [<git dir>]` are table rows. `archilyzer doctor` reports
which filter-repo would run (or the install line), gitleaks, the two operator
files by rule count and mode (never their contents) and the last publish —
never a failure.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
6 files changed, 286 insertions(+), 5 deletions(-)
diff --git a/common/bin/_cli.test.ts b/common/bin/_cli.test.ts
@@ -314,6 +314,40 @@ test("release show says the latest release, its date and what is pending, and wh
assert.equal(formatAllLine([editor]), null);
});
+// --- archilyzer source (release 12 slice R) --------------------------------
+
+test("usage lists source publish, source audit and build homepage's --no-source", () => {
+ const u = usage(COMMANDS);
+ assert.match(u, /archilyzer source publish\s+\[--force\] \[--check\] \[--keep-scratch\] the scrubbed git mirror/);
+ assert.match(u, /archilyzer source audit\s+\[<git dir>\] the denied-literal gate/);
+ assert.match(u, /archilyzer build homepage\s+\[--no-source\] compose \+ source publish \+ next build/);
+});
+
+test("source publish takes its three booleans and nothing else; none swallows a word", () => {
+ const hit = resolveCommand(COMMANDS, ["source", "publish"]);
+ assert.deepEqual(hit?.command.path, ["source", "publish"]);
+ const parsed = parseArgv(["source", "publish", "--check", "--force", "--keep-scratch"], booleanFlags(COMMANDS));
+ assert.deepEqual(parsed, {
+ positionals: ["source", "publish"],
+ flags: { check: true, force: true, "keep-scratch": true },
+ });
+ assert.equal(argumentProblem(hit!.command, parsed.flags, []), null);
+ assert.match(argumentProblem(hit!.command, { preview: "x" }, [])!, /unknown flag --preview \(accepts --force, --check, --keep-scratch\)/);
+ assert.match(argumentProblem(hit!.command, {}, ["extra"])!, /unexpected argument "extra"/);
+});
+
+test("source audit takes one git dir; build homepage takes --no-source only", () => {
+ const audit = resolveCommand(COMMANDS, ["source", "audit", "/tmp/clone/.git"]);
+ assert.deepEqual(audit?.rest, ["/tmp/clone/.git"]);
+ assert.equal(argumentProblem(audit!.command, {}, audit!.rest), null);
+ assert.match(argumentProblem(audit!.command, {}, ["a", "b"])!, /unexpected argument "b"/);
+ const home = resolveCommand(COMMANDS, ["build", "homepage"]);
+ const parsed = parseArgv(["build", "homepage", "--no-source"], booleanFlags(COMMANDS));
+ assert.deepEqual(parsed, { positionals: ["build", "homepage"], flags: { "no-source": true } });
+ assert.equal(argumentProblem(home!.command, parsed.flags, []), null);
+ assert.match(argumentProblem(home!.command, { force: true }, [])!, /unknown flag --force \(accepts --no-source\)/);
+});
+
// ── passthrough commands (one-core Phase 4 slice 3) ─────────────────────────
test("a passthrough command gets every word after its path, verbatim and unchecked", async () => {
diff --git a/common/bin/archilyzer.ts b/common/bin/archilyzer.ts
@@ -137,15 +137,45 @@ export const COMMANDS: Command[] = [
},
{
path: ["build", "homepage"],
- usage: "compose + next build in homepage/ (reads the index as it stands)",
- run: async () => {
+ usage:
+ "[--no-source] compose + source publish + next build in homepage/ (reads the index as it stands); --no-source removes the published source instead",
+ flags: { "no-source": "boolean" },
+ run: async ({ flags }) => {
const { buildHomepage } = await import("../publish/build");
- const code = await buildHomepage({ signal: interrupted() });
+ const code = await buildHomepage({
+ signal: interrupted(),
+ skipSource: flags["no-source"] === true,
+ });
if (code !== 0) console.error(`build homepage: failed (exit ${code})`);
return code;
},
},
{
+ path: ["source", "publish"],
+ usage:
+ "[--force] [--check] [--keep-scratch] the scrubbed git mirror, raw tree and tarball into homepage/public, behind the denied-literal gate (--check: audit and count, write nothing)",
+ flags: { force: "boolean", check: "boolean", "keep-scratch": "boolean" },
+ run: async ({ flags }) => {
+ const { publishSource } = await import("../publish/source");
+ return publishSource({
+ signal: interrupted(),
+ force: flags.force === true,
+ check: flags.check === true,
+ keepScratch: flags["keep-scratch"] === true,
+ });
+ },
+ },
+ {
+ path: ["source", "audit"],
+ usage:
+ "[<git dir>] the denied-literal gate (+ gitleaks) over a git dir; default the published homepage/public/source/archilyzer.git",
+ maxPositionals: 1,
+ run: async ({ positionals }) => {
+ const { auditSource } = await import("../publish/source");
+ return auditSource({ signal: interrupted(), gitDir: positionals[0] });
+ },
+ },
+ {
path: ["deploy", "site"],
usage:
"<id> [--preview <branch>] ship the site built in export/out to its Pages project (default id: SITE_ID)",
diff --git a/common/bin/doctor.test.ts b/common/bin/doctor.test.ts
@@ -56,6 +56,8 @@ function checkout(): { root: string; bin: string; paths: Paths } {
whisperModel: path.join(root, "models", "ggml-base.en.bin"),
parakeetModel: "",
parakeetCliBin: "parakeet-cli",
+ sourceScrubFile: path.join(root, ".config", "source-scrub.txt"),
+ sourceDenylistFile: path.join(root, ".config", "source-denylist.txt"),
} as unknown as Paths;
return { root, bin, paths };
}
@@ -229,3 +231,56 @@ test("umtool's table and the port block are reported, never failed", async () =>
assert.match(r.checks.find((x) => x.id === "EDITOR_PORT")!.detail, /^3301 in use/);
assert.equal(r.ok, true);
});
+
+test("the source publish block: the tools, the operator files by count and mode (never their contents), the last publish — never a failure", async () => {
+ const c = checkout();
+ const deps = (tools: Awaited<ReturnType<NonNullable<Parameters<typeof collectDoctorReport>[0]["sourceTools"]>>>) => ({
+ env: { PATH: c.bin },
+ paths: c.paths,
+ nodeVersion: "22.0.0",
+ portBlock: async () => null,
+ portInUse: async () => false,
+ umtoolTools: async () => null,
+ sourceTools: async () => tools,
+ });
+ // A clone that never publishes: notes, not warnings.
+ let r = await collectDoctorReport(deps({ filterRepo: null, gitleaks: null }));
+ assert.equal(r.ok, true, renderDoctorReport(r));
+ for (const id of ["filter-repo", "gitleaks", "scrub rules", "denylist", "published"]) {
+ assert.equal(status(r, id), "info", id);
+ }
+ assert.match(r.checks.find((x) => x.id === "filter-repo")!.detail, /install: `pipx install git-filter-repo`/);
+
+ // The operator's files exist (one readable by others), pipx only, a publish.
+ mkdirSync(path.dirname(c.paths.sourceScrubFile), { recursive: true });
+ writeFileSync(c.paths.sourceScrubFile, "# mine\nPLANTED-A==>x\n\nPLANTED-B==>y\n");
+ chmodSync(c.paths.sourceScrubFile, 0o600);
+ writeFileSync(c.paths.sourceDenylistFile, "PLANTED-SECRET\n");
+ chmodSync(c.paths.sourceDenylistFile, 0o644);
+ const pub = path.join(c.root, "homepage", "public", "source");
+ mkdirSync(pub, { recursive: true });
+ writeFileSync(path.join(pub, "manifest.json"), JSON.stringify({
+ version: 1, generatedAt: "2026-09-28T12:00:00.000Z", branch: "main",
+ sourceCommit: "1".repeat(40), mirrorHead: "2".repeat(40), subject: "s", files: 2400, bytes: 1,
+ mirror: {}, tree: {}, tarball: { href: "/downloads/archilyzer-source.tar.gz", bytes: 1, sha256: "3".repeat(64) },
+ audit: {}, tools: {},
+ }));
+ const before = tree(c.root);
+ r = await collectDoctorReport(deps({ filterRepo: { via: "pipx", version: "1.15.0" }, gitleaks: { version: "8.28.0" } }));
+ assert.equal(r.ok, true, renderDoctorReport(r));
+ assert.equal(status(r, "filter-repo"), "warn");
+ assert.match(r.checks.find((x) => x.id === "filter-repo")!.detail, /pipx run --spec git-filter-repo==2\.47\.0/);
+ assert.equal(status(r, "gitleaks"), "ok");
+ assert.equal(status(r, "scrub rules"), "ok");
+ assert.match(r.checks.find((x) => x.id === "scrub rules")!.detail, /\(2 rules, mode 600\)$/);
+ assert.equal(status(r, "denylist"), "warn");
+ assert.match(r.checks.find((x) => x.id === "denylist")!.detail, /\(1 literal, mode 644\) — readable by others: chmod 600/);
+ assert.match(r.checks.find((x) => x.id === "published")!.detail, /^main 111111111111 as 222222222222, 2026-09-28T12:00:00\.000Z \(2400 files\)/);
+ const text = renderDoctorReport(r);
+ assert.match(text, /\nsource publish\n/);
+ assert.ok(!/PLANTED/.test(text), "the doctor never prints an operator file's contents");
+ assert.deepEqual(tree(c.root), before);
+
+ r = await collectDoctorReport(deps({ filterRepo: { via: "git", version: "a40bce548d2c" }, gitleaks: null }));
+ assert.equal(status(r, "filter-repo"), "ok");
+});
diff --git a/common/bin/doctor.ts b/common/bin/doctor.ts
@@ -70,6 +70,14 @@ export type DoctorDeps = {
portBlock?: () => Promise<{ label: string; ports: Record<string, string> } | null>;
// umtool's report-pipeline table, or null when there is no umtool here.
umtoolTools?: () => Promise<ToolSpec[] | null>;
+ // The source publish's tools. Default: probeSourceTools(env).
+ sourceTools?: () => Promise<SourceTools>;
+};
+
+// Which git-filter-repo `archilyzer source publish` would run, and gitleaks.
+export type SourceTools = {
+ filterRepo: { via: "git"; version: string } | { via: "pipx"; version: string } | null;
+ gitleaks: { version: string } | null;
};
const MIN_NODE = [20, 9, 0] as const; // next 16's engines field
@@ -254,6 +262,50 @@ export async function collectDoctorReport(deps: DoctorDeps): Promise<DoctorRepor
}
}
+ // ── source publish ───────────────────────────────────────────────────────
+ // `archilyzer build homepage` runs it (common/publish/source.ts). Never a
+ // failure: a checkout that never publishes the homepage is not broken. A
+ // WARN is a machine that means to (its operator files exist) and cannot.
+ // The operator files are stat'd and their RULES COUNTED — their contents
+ // are never printed.
+ const SP = "source publish";
+ const src = await import("../publish/source");
+ const tools = await (deps.sourceTools ?? (() => probeSourceTools(env)))();
+ const scrubFile = paths.sourceScrubFile;
+ const denylistFile = paths.sourceDenylistFile;
+ const intends = [scrubFile, denylistFile].some((f) => f && existsSync(f));
+ if (tools.filterRepo?.via === "git") {
+ add(SP, "filter-repo", "ok", `git filter-repo ${tools.filterRepo.version}`);
+ } else if (tools.filterRepo?.via === "pipx") {
+ add(SP, "filter-repo", intends ? "warn" : "info",
+ `not installed; \`pipx run --spec ${src.FILTER_REPO_PIPX_SPEC}\` fetches it at build time (network on first use; pipx ${tools.filterRepo.version}) — \`${src.FILTER_REPO_INSTALL}\` once removes that`);
+ } else {
+ add(SP, "filter-repo", intends ? "warn" : "info",
+ `neither git-filter-repo nor pipx — \`archilyzer build homepage\` refuses; install: \`${src.FILTER_REPO_INSTALL}\``);
+ }
+ add(SP, "gitleaks", tools.gitleaks ? "ok" : "info",
+ tools.gitleaks ? `gitleaks ${tools.gitleaks.version}` : "absent — the gate skips the secret scan with a WARNING (the literal audit still runs)");
+ for (const [id, file, unit] of [
+ ["scrub rules", scrubFile, "rule"],
+ ["denylist", denylistFile, "literal"],
+ ] as const) {
+ const st = file ? statOrNull(file) : null;
+ if (!file || !st) {
+ add(SP, id, "info", `${file ?? "(unset)"} is missing — \`source publish\` (and so \`build homepage\`) refuses until it exists (PUBLISH.md, "The source mirror")`);
+ continue;
+ }
+ const count = (readOrNull(file) ?? "").split("\n").filter((l) => l.trim() && !l.trim().startsWith("#")).length;
+ const mode = st.mode & 0o777;
+ const open = (mode & 0o077) !== 0;
+ add(SP, id, open ? "warn" : "ok",
+ `${file} (${count} ${unit}${count === 1 ? "" : "s"}, mode ${mode.toString(8)})${open ? " — readable by others: chmod 600" : ""}`);
+ }
+ const publicDir = env.HOMEPAGE_PUBLIC_DIR || path.join(root, "homepage", "public");
+ const published = await src.readPublishedManifest(publicDir);
+ add(SP, "published", "info", published
+ ? `main ${published.sourceCommit.slice(0, 12)} as ${published.mirrorHead.slice(0, 12)}, ${published.generatedAt} (${published.files} files)`
+ : `nothing published in ${path.join(publicDir, "source")}`);
+
// ── ports ────────────────────────────────────────────────────────────────
const P = "ports";
const block = await (deps.portBlock ?? (() => worktreePortBlock(root)))();
@@ -396,6 +448,27 @@ async function worktreePortBlock(
}
}
+// What `source publish` would run, by version flags only: `git filter-repo
+// --version` answering 0 is an installed filter-repo; otherwise pipx's own
+// version (never `pipx run`, which would download). gitleaks likewise.
+async function probeSourceTools(env: NodeJS.ProcessEnv): Promise<SourceTools> {
+ const version = async (bin: string, args: string[]): Promise<string | null> => {
+ try {
+ const { stdout, stderr } = await execFileP(bin, args, { env, timeout: 10_000 });
+ return (stdout || stderr).trim().split("\n")[0] ?? "";
+ } catch {
+ return null;
+ }
+ };
+ const git = await version("git", ["filter-repo", "--version"]);
+ const pipx = git === null ? await version("pipx", ["--version"]) : null;
+ const leaks = await version("gitleaks", ["version"]);
+ return {
+ filterRepo: git !== null ? { via: "git", version: git } : pipx !== null ? { via: "pipx", version: pipx } : null,
+ gitleaks: leaks !== null ? { version: leaks } : null,
+ };
+}
+
// In use = something accepts a TCP connection on 127.0.0.1. Never binds.
function tcpPortInUse(port: number): Promise<boolean> {
return new Promise((resolve) => {
diff --git a/common/publish/build.test.ts b/common/publish/build.test.ts
@@ -1,8 +1,12 @@
import { test } from "node:test";
import assert from "node:assert/strict";
+import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
+import os from "node:os";
+import path from "node:path";
import type { Paths } from "../lib/paths";
import {
HOMEPAGE_PAGES_PROJECT,
+ buildHomepage,
buildHubSteps,
buildSiteSteps,
hubProjectProblem,
@@ -143,6 +147,65 @@ test("homepageOutDir is homepage/out of the checkout", () => {
assert.equal(homepageOutDir({ monorepoRoot: "/repo" } as Paths), "/repo/homepage/out");
});
+// buildHomepage's order — compose, the source step, `next build` — over a temp
+// homepage/ whose `compose` is `true` and whose `next` only says it ran.
+test("buildHomepage: the source step sits between compose and next build; a refusal stops the build; --no-source clears instead", async () => {
+ const root = mkdtempSync(path.join(os.tmpdir(), "build-homepage-"));
+ try {
+ const home = path.join(root, "homepage");
+ mkdirSync(path.join(home, "node_modules", ".bin"), { recursive: true });
+ writeFileSync(
+ path.join(home, "package.json"),
+ JSON.stringify({ name: "fake-homepage", private: true, scripts: { compose: "echo COMPOSED" } }),
+ );
+ const next = path.join(home, "node_modules", ".bin", "next");
+ writeFileSync(next, "#!/bin/sh\necho NEXT RAN\n");
+ chmodSync(next, 0o755);
+ const fakePaths = { monorepoRoot: root } as Paths;
+
+ const run = async (o: Parameters<typeof buildHomepage>[0]) => {
+ const logs: string[] = [];
+ const code = await buildHomepage({ paths: fakePaths, onLog: (l) => logs.push(l), ...o });
+ return { code, logs: logs.join("\n") };
+ };
+ const calls: string[] = [];
+
+ // A refusal (1) fails the build before next build runs.
+ let r = await run({
+ publishSource: async (p) => {
+ calls.push(`publish:${p.paths === fakePaths}`);
+ return 1;
+ },
+ });
+ assert.equal(r.code, 1);
+ assert.match(r.logs, /COMPOSED/);
+ assert.doesNotMatch(r.logs, /NEXT RAN/);
+ assert.deepEqual(calls, ["publish:true"]);
+
+ // Published: next build follows.
+ r = await run({ publishSource: async () => (calls.push("publish"), 0) });
+ assert.equal(r.code, 0, r.logs);
+ assert.ok(r.logs.indexOf("COMPOSED") < r.logs.indexOf("NEXT RAN"));
+
+ // --no-source: the publish is never called; the clear is.
+ calls.length = 0;
+ r = await run({
+ skipSource: true,
+ publishSource: async () => {
+ throw new Error("--no-source must not publish");
+ },
+ clearSource: async () => {
+ calls.push("clear");
+ },
+ });
+ assert.equal(r.code, 0, r.logs);
+ assert.deepEqual(calls, ["clear"]);
+ assert.match(r.logs, /NEXT RAN/);
+ } finally {
+ rmSync(root, { recursive: true, force: true });
+ }
+});
+
test("deployHomepage refuses a bad preview branch before it looks for a build", async () => {
const noBuild = { monorepoRoot: "/nonexistent-repo" } as Paths;
await assert.rejects(
diff --git a/common/publish/build.ts b/common/publish/build.ts
@@ -969,11 +969,37 @@ export async function composeHomepage(opts: PublishOpts = {}): Promise<number> {
]);
}
-/** composeHomepage, then `next build` in homepage/ (→ homepage/out). */
-export async function buildHomepage(opts: PublishOpts = {}): Promise<number> {
+/**
+ * composeHomepage, then `archilyzer source publish` (the git mirror, the raw
+ * tree and the tarball into homepage/public — source.ts), then `next build` in
+ * homepage/ (→ homepage/out). A source REFUSAL fails the build before `next
+ * build`: nothing is deployed with a stale or missing source, and the redacted
+ * audit report is in the log. `skipSource` (the CLI's `--no-source`) REMOVES
+ * the published source instead (the mirror, the tree, the tarball): a copy
+ * left from an earlier publish was audited against that day's rules, not
+ * today's, so a build that skips the gate ships none — the pages show their
+ * empty states. `publishSource` / `clearSource` are the test's seams.
+ */
+export async function buildHomepage(
+ opts: PublishOpts & {
+ skipSource?: boolean;
+ publishSource?: (o: PublishOpts) => Promise<number>;
+ clearSource?: (o: PublishOpts) => Promise<void>;
+ } = {},
+): Promise<number> {
const { paths, onLog, signal } = resolved(opts);
const code = await composeHomepage({ paths, onLog, signal });
if (code !== 0) return code;
+ // Loaded lazily: the source step pulls nothing the other publish entry
+ // points need, and it imports this module's types.
+ if (opts.skipSource) {
+ const clear = opts.clearSource ?? (await import("./source")).clearPublishedSource;
+ await clear({ paths, onLog, signal });
+ } else {
+ const publish = opts.publishSource ?? (await import("./source")).publishSource;
+ const sourceCode = await publish({ paths, onLog, signal });
+ if (sourceCode !== 0 || signal.aborted) return sourceCode || 1;
+ }
return runSteps(onLog, signal, [
{
command: "pnpm",