Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

scripts/scope.ts

Raw
import { execFileSync } from "node:child_process";

/**
 * Pre-commit scope: run check + lint only for the extensions whose files are
 * staged. Any staged file outside `extensions/<name>/` (scripts, root config,
 * shared test helpers) widens the scope to the whole repository, because it can
 * affect every extension. Pre-push stays repository-wide; this keeps the fast,
 * offline pre-commit proportional to the change.
 */
function git(args: string[]): string {
	return execFileSync("git", args, { encoding: "utf8" });
}

function run(args: string[]): void {
	execFileSync("mise", ["run", ...args], { stdio: "inherit" });
}

const dryRun = process.argv.includes("--dry-run");
const staged = git(["diff", "--cached", "--name-only"])
	.split("\n")
	.map((line) => line.trim())
	.filter(Boolean);

const extensions = new Set<string>();
let repositoryWide = false;
for (const file of staged) {
	const match = /^extensions\/([^/]+)\//.exec(file);
	if (match) extensions.add(match[1] as string);
	else repositoryWide = true;
}

if (staged.length === 0) {
	console.log("scope: nothing staged");
} else if (repositoryWide || extensions.size === 0) {
	console.log("scope: repository-wide (shared files staged) → check + lint");
	if (!dryRun) run(["//:check", ":::", "//:lint"]);
} else {
	const names = [...extensions].sort();
	console.log(`scope: ${names.join(", ")} → check + lint`);
	if (!dryRun)
		for (const name of names) {
			run([`//extensions/${name}:check`]);
			run([`//extensions/${name}:lint`]);
		}
}