From 6f4e84e2b7c555bb7e8682d1e9c4e1033b46bd00 Mon Sep 17 00:00:00 2001 From: Keys <70819367+keys-i@users.noreply.github.com> Date: Wed, 12 Aug 2026 21:47:56 +1000 Subject: [PATCH] feat: verify source inputs --- tools/krad.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 tools/krad.js diff --git a/tools/krad.js b/tools/krad.js new file mode 100755 index 0000000..2890f80 --- /dev/null +++ b/tools/krad.js @@ -0,0 +1,83 @@ +#!/usr/bin/env node +import { execFileSync } from "node:child_process"; +import { existsSync, statSync } from "node:fs"; +import { resolve } from "node:path"; +import { component as clang } from "../packages/krad-clang/index.js"; +import { component as libs } from "../packages/krad-libs/index.js"; +import { component as musl } from "../packages/krad-musl/index.js"; +import { component as utils, orderComponents } from "../packages/krad-utils/index.js"; + +const components = orderComponents([libs, musl, utils, clang]); +const canonical = (value) => value.trim().replace(/\/+$/, "").replace(/\.git$/, ""); +const git = (directory, ...args) => { + try { + return execFileSync("git", ["-C", directory, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); + } catch (error) { + const detail = error.stderr?.toString().trim(); + throw new Error(`${directory}: git ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`); + } +}; + +const verify = (root) => + components.map((component) => { + const directory = resolve(root, component.source.directory); + if (!existsSync(directory) || !statSync(directory).isDirectory()) throw new Error(`${component.name}: missing ${directory}`); + const revision = git(directory, "rev-parse", "HEAD"); + if (revision !== component.source.revision) { + throw new Error(`${component.name}: expected ${component.source.revision}, found ${revision}`); + } + const repository = canonical(git(directory, "remote", "get-url", "origin")); + if (repository !== canonical(component.source.repository)) { + throw new Error(`${component.name}: expected ${component.source.repository}, found ${repository}`); + } + for (const required of [component.source.licenseFile, ...component.source.focus]) { + if (!existsSync(resolve(directory, required))) throw new Error(`${component.name}: missing ${required}`); + } + return { + name: component.name, + directory, + license: component.source.license, + repository: component.source.repository, + revision, + }; + }); + +const usage = `Usage: krad [options] + +Commands: + plan [--json] Print the component dependency order + verify [root] [--json] Verify source origins, revisions, licenses, and focused paths + help Show this help + +The default verify root is ../research relative to the current directory.`; + +const args = process.argv.slice(2); +const command = args.shift() ?? "help"; +const json = args.includes("--json"); +const values = args.filter((argument) => argument !== "--json"); + +try { + if (args.some((argument) => argument.startsWith("-") && argument !== "--json")) throw new Error("unknown option"); + if (command === "help" || command === "--help" || command === "-h") { + if (values.length) throw new Error("help takes no arguments"); + console.log(usage); + } else if (command === "plan") { + if (values.length) throw new Error("plan takes no arguments"); + const plan = components.map(({ name, dependencies, source }) => ({ + name, + dependencies, + source: source.directory, + revision: source.revision, + })); + console.log(json ? JSON.stringify(plan, null, 2) : plan.map((entry, index) => `${index + 1}. ${entry.name}`).join("\n")); + } else if (command === "verify") { + if (values.length > 1) throw new Error("verify accepts at most one source root"); + const result = verify(resolve(values[0] ?? "../research")); + console.log(json ? JSON.stringify(result, null, 2) : result.map((entry) => `ok ${entry.name} ${entry.revision}`).join("\n")); + } else { + throw new Error(`unknown command: ${command}`); + } +} catch (error) { + console.error(`krad: ${error.message}`); + process.exitCode = 1; +}