// The git hook every push runs, as a Node.js script. See lib/push-hooks.ts for the other side.
/**
* The pre-receive hook, run by git with the push's "<old> <new> <ref>" lines on stdin. It refuses
* refs with a path component starting with "-" (git commands could mistake them for options) by
* itself, and asks the server about everything else. Without a session (receive-pack started by
* something other than Vectrum) it only does the first.
*/
export function hookScript() {
return `#!${process.execPath}
"use strict";
// Written by Vectrum (lib/hook-script.ts); rewritten on every start.
const { execFileSync } = require("node:child_process");
const net = require("node:net");
const ZERO = /^0+$/;
let input = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (d) => (input += d));
process.stdin.on("end", () => {
const updates = input.split("\\n").filter(Boolean).map((line) => {
const [oldSha, newSha, ref] = line.trim().split(" ");
return { old: oldSha, new: newSha, ref };
});
const bad = updates.filter((u) => ("/" + u.ref).includes("/-"));
if (bad.length) {
for (const u of bad) console.error("Vectrum: refusing " + u.ref + " (ref name components must not start with '-')");
process.exit(1);
}
const socket = process.env.VECTRUM_HOOK_SOCKET;
const token = process.env.VECTRUM_PUSH_TOKEN;
if (!socket || !token) process.exit(0);
for (const u of updates) {
u.ff = true;
if (ZERO.test(u.old) || ZERO.test(u.new) || !u.ref.startsWith("refs/heads/")) continue;
try {
execFileSync("git", ["merge-base", "--is-ancestor", u.old, u.new], { stdio: "ignore" });
} catch {
u.ff = false;
}
}
const fail = (message) => {
console.error("Vectrum: " + message);
process.exit(1);
};
const timer = setTimeout(() => fail("timed out checking the branch rules."), 20000);
let reply = "";
const conn = net.createConnection(socket, () => conn.write(JSON.stringify({ token, updates }) + "\\n"));
conn.setEncoding("utf8");
conn.on("data", (d) => (reply += d));
conn.on("error", () => fail("couldn't check the branch rules. Try again."));
conn.on("end", () => {
clearTimeout(timer);
let answer;
try {
answer = JSON.parse(reply);
} catch {
return fail("couldn't check the branch rules. Try again.");
}
for (const m of answer.messages || []) for (const line of String(m).split("\\n")) console.error("Vectrum: " + line);
process.exit(answer.ok ? 0 : 1);
});
});
`;
}