fix: block extension declaration deletions
This commit is contained in:
parent
180f8e131e
commit
0d440bed7a
3 changed files with 68 additions and 6 deletions
|
|
@ -96,7 +96,8 @@
|
|||
"typecheck": "npm run build:pi && tsc --noEmit",
|
||||
"typecheck:extensions": "npm run check:versioned-json && tsc --noEmit --project tsconfig.extensions.json",
|
||||
"check:sf-inventory": "node scripts/check-sf-extension-inventory.mjs",
|
||||
"check:versioned-json": "node scripts/check-versioned-json.mjs && npm run check:sf-inventory",
|
||||
"check:protected-deletions": "node scripts/check-protected-deletions.mjs",
|
||||
"check:versioned-json": "node scripts/check-protected-deletions.mjs && node scripts/check-versioned-json.mjs && npm run check:sf-inventory",
|
||||
"format": "biome format --write .",
|
||||
"format:check": "biome format .",
|
||||
"lint": "npm run check:versioned-json && biome check .",
|
||||
|
|
|
|||
40
scripts/check-protected-deletions.mjs
Normal file
40
scripts/check-protected-deletions.mjs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env node
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const PROTECTED_PATHS = [":(glob)src/resources/extensions/**/*.d.ts"];
|
||||
|
||||
function git(args) {
|
||||
return execFileSync("git", args, {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
}
|
||||
|
||||
function listDeleted(cached) {
|
||||
const args = ["diff", "--name-only", "--diff-filter=D"];
|
||||
if (cached) args.push("--cached");
|
||||
args.push("--", ...PROTECTED_PATHS);
|
||||
const out = git(args);
|
||||
return out ? out.split("\n").filter(Boolean) : [];
|
||||
}
|
||||
|
||||
const stagedOnly = process.argv.includes("--cached");
|
||||
const deleted = stagedOnly
|
||||
? listDeleted(true)
|
||||
: [...new Set([...listDeleted(false), ...listDeleted(true)])];
|
||||
|
||||
if (deleted.length > 0) {
|
||||
const mode = stagedOnly ? "staged " : "";
|
||||
process.stderr.write(
|
||||
`check-protected-deletions: refusing ${mode}protected declaration deletions:\n`,
|
||||
);
|
||||
for (const path of deleted) {
|
||||
process.stderr.write(` ${path}\n`);
|
||||
}
|
||||
process.stderr.write(
|
||||
"\nRestore these files or make an explicit reviewed deletion outside automation.\n",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.stdout.write("check-protected-deletions: ok\n");
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
import { join } from "node:path";
|
||||
|
||||
const MARKER = "# sf-secret-scan";
|
||||
const PROTECTED_DELETIONS_MARKER = "# sf-protected-deletions";
|
||||
|
||||
function git(args) {
|
||||
return execFileSync("git", args, {
|
||||
|
|
@ -24,19 +25,35 @@ const repoRoot = git(["rev-parse", "--show-toplevel"]);
|
|||
const hookDir = join(gitDir, "hooks");
|
||||
const hookFile = join(hookDir, "pre-commit");
|
||||
const hookCommand = `node "${join(repoRoot, "scripts", "secret-scan.mjs")}"`;
|
||||
const protectedDeletionsCommand = `node "${join(
|
||||
repoRoot,
|
||||
"scripts",
|
||||
"check-protected-deletions.mjs",
|
||||
)}" --cached`;
|
||||
|
||||
mkdirSync(hookDir, { recursive: true });
|
||||
|
||||
if (existsSync(hookFile)) {
|
||||
const current = readFileSync(hookFile, "utf8");
|
||||
if (current.includes(MARKER)) {
|
||||
process.stdout.write("secret-scan pre-commit hook already installed.\n");
|
||||
const additions = [];
|
||||
if (!current.includes(MARKER)) {
|
||||
additions.push(MARKER, hookCommand);
|
||||
}
|
||||
if (!current.includes(PROTECTED_DELETIONS_MARKER)) {
|
||||
additions.push(
|
||||
PROTECTED_DELETIONS_MARKER,
|
||||
"# Pre-commit hook: block accidental deletion of hand-written extension declarations",
|
||||
protectedDeletionsCommand,
|
||||
);
|
||||
}
|
||||
if (additions.length === 0) {
|
||||
process.stdout.write("sf pre-commit hooks already installed.\n");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const next = `${current.replace(/\s*$/, "\n")}${MARKER}\n${hookCommand}\n`;
|
||||
const next = `${current.replace(/\s*$/, "\n")}${additions.join("\n")}\n`;
|
||||
writeFileSync(hookFile, next, "utf8");
|
||||
process.stdout.write("secret-scan appended to existing pre-commit hook.\n");
|
||||
process.stdout.write("sf pre-commit hooks appended to existing hook.\n");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +63,10 @@ const hookBody = [
|
|||
"# Pre-commit hook: scan staged files for hardcoded secrets",
|
||||
hookCommand,
|
||||
"",
|
||||
"# sf-protected-deletions",
|
||||
"# Pre-commit hook: block accidental deletion of hand-written extension declarations",
|
||||
protectedDeletionsCommand,
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
writeFileSync(hookFile, hookBody, "utf8");
|
||||
|
|
@ -55,4 +76,4 @@ try {
|
|||
// Best effort on Windows filesystems that do not honor chmod.
|
||||
}
|
||||
|
||||
process.stdout.write("secret-scan pre-commit hook installed.\n");
|
||||
process.stdout.write("sf pre-commit hooks installed.\n");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue