Public release release-6d1a121b0a8a
Source commit: 6d1a121b0a8a636aac95998fe5d15c24c78eb7d0 Public tree identity: sha256:2bc22807f5b838286678b65d3f550b8ae4714ae673622041d4c2516a7c5b7926
This commit is contained in:
commit
21f097d9a8
210 changed files with 78649 additions and 0 deletions
111
scripts/check-docs.js
Normal file
111
scripts/check-docs.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env node
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const root = path.resolve(__dirname, "..");
|
||||
const publicDocs = [
|
||||
"README.md",
|
||||
"SECURITY.md",
|
||||
"docs/getting-started.md",
|
||||
"docs/architecture.md",
|
||||
"docs/nodes.md",
|
||||
"docs/environments.md",
|
||||
"docs/artifacts.md",
|
||||
"docs/debugging.md",
|
||||
"docs/task-abi.md",
|
||||
"docs/self-hosting.md",
|
||||
"docs/security.md",
|
||||
];
|
||||
const privateDocs = [
|
||||
"private/docs/hosted-deployment.md",
|
||||
"private/docs/authentik.md",
|
||||
"private/docs/community-policy.md",
|
||||
"private/docs/bandwidth-and-cost-controls.md",
|
||||
"private/docs/publishing.md",
|
||||
];
|
||||
const internalDocs = ["internal/finish_mvp_2.md"];
|
||||
const filteredPublicTree =
|
||||
process.env.CLUSTERFLUX_FILTERED_PUBLIC_TREE === "1" ||
|
||||
fs.existsSync(path.join(root, "CLUSTERFLUX_PUBLIC_TREE.json"));
|
||||
|
||||
const failures = [];
|
||||
const expectExactMarkdownSet = (directory, expected) => {
|
||||
const actual = fs
|
||||
.readdirSync(path.join(root, directory), { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
|
||||
.map((entry) => path.posix.join(directory, entry.name))
|
||||
.sort();
|
||||
const wanted = [...expected].sort();
|
||||
if (JSON.stringify(actual) !== JSON.stringify(wanted)) {
|
||||
failures.push(`${directory} markdown set is ${actual.join(", ")}; expected ${wanted.join(", ")}`);
|
||||
}
|
||||
};
|
||||
|
||||
const requiredDocs = filteredPublicTree
|
||||
? publicDocs
|
||||
: [...publicDocs, ...privateDocs, ...internalDocs];
|
||||
for (const file of requiredDocs) {
|
||||
if (!fs.existsSync(path.join(root, file))) failures.push(`missing canonical document: ${file}`);
|
||||
}
|
||||
expectExactMarkdownSet("docs", publicDocs.filter((file) => file.startsWith("docs/")));
|
||||
if (filteredPublicTree) {
|
||||
for (const directory of ["private", "internal"]) {
|
||||
if (fs.existsSync(path.join(root, directory))) {
|
||||
failures.push(`${directory}/ must not exist in the filtered public tree`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const forbidden = [
|
||||
[/\bmvp\b/i, "internal milestone term"],
|
||||
[/acceptance criteria/i, "internal gate language"],
|
||||
[/release verification/i, "internal release language"],
|
||||
[/public\/private source split/i, "source split narrative"],
|
||||
[/founder|business decisions/i, "business planning narrative"],
|
||||
[/hacker news|\bHN\b/, "launch-channel narrative"],
|
||||
[/\busers can\b/i, "indirect reader wording"],
|
||||
[/node\s+scripts\//i, "developer script instruction"],
|
||||
[/scripts\/[^\s)]*smoke/i, "smoke script instruction"],
|
||||
[/internal\/[^\s)]*/i, "internal tooling reference"],
|
||||
[/private\/[^\s)]*/i, "private source reference"],
|
||||
];
|
||||
const topLevelCommands = new Set([
|
||||
"doctor", "login", "logout", "auth", "agent", "key", "project", "inspect",
|
||||
"build", "bundle", "run", "node", "process", "task", "logs", "artifact",
|
||||
"dap", "debug", "quota", "admin",
|
||||
]);
|
||||
|
||||
for (const file of publicDocs) {
|
||||
const absolute = path.join(root, file);
|
||||
const content = fs.readFileSync(absolute, "utf8");
|
||||
for (const [pattern, description] of forbidden) {
|
||||
if (pattern.test(content)) failures.push(`${file}: contains ${description}`);
|
||||
}
|
||||
for (const match of content.matchAll(/\]\(([^)#]+)(?:#[^)]+)?\)/g)) {
|
||||
const target = match[1];
|
||||
if (/^(?:https?:|mailto:)/.test(target)) continue;
|
||||
const resolved = path.resolve(path.dirname(absolute), target);
|
||||
if (!fs.existsSync(resolved)) failures.push(`${file}: broken link ${target}`);
|
||||
}
|
||||
for (const line of content.split(/\r?\n/)) {
|
||||
const command = line.trim().match(/^clusterflux\s+([a-z][a-z-]*)\b/);
|
||||
if (command && !topLevelCommands.has(command[1])) {
|
||||
failures.push(`${file}: unknown top-level CLI command ${command[1]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rootMarkdown = fs
|
||||
.readdirSync(root, { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
|
||||
.map((entry) => entry.name)
|
||||
.sort();
|
||||
if (JSON.stringify(rootMarkdown) !== JSON.stringify(["README.md", "SECURITY.md"])) {
|
||||
failures.push(`top-level markdown set is ${rootMarkdown.join(", ")}; expected README.md, SECURITY.md`);
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
for (const failure of failures) console.error(failure);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("documentation checks passed");
|
||||
Loading…
Add table
Add a link
Reference in a new issue