Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
76 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The runner base keeps its old comment, so the pushed base image still matches its folder1// Which units a lockfile change reaches. A change to Cargo.lock or
2// package-lock.json touches only the units whose dependency graph includes
3// a package that changed, read from the lockfiles themselves (their graph
4// is a superset of any one target's, so this errs toward deploying).
5
6/** Cargo.lock: name -> list of entries { version, source, checksum, deps: [names] }. */
7export function parseCargoLock(text) {
8 const packages = new Map();
9 if (!text) return packages;
10 for (const block of text.split(/^\[\[package\]\]\s*$/m).slice(1)) {
11 const field = (key) => new RegExp(`^${key} = "([^"]*)"`, "m").exec(block)?.[1] ?? null;
12 const depsBlock = /^dependencies = \[([\s\S]*?)\]/m.exec(block)?.[1] ?? "";
13 const deps = [...depsBlock.matchAll(/"([^"\s]+)[^"]*"/g)].map((m) => m[1]);
14 const entry = { version: field("version"), source: field("source"), checksum: field("checksum"), deps };
15 const name = field("name");
16 if (!packages.has(name)) packages.set(name, []);
17 packages.get(name).push(entry);
18 }
19 return packages;
20}
21
22/** package-lock.json (v2/v3): name -> list of entries; workspace folders keep their path as name. */
23export function parseNpmLock(text) {
24 const packages = new Map();
25 if (!text) return packages;
26 const lock = JSON.parse(text);
27 for (const [key, entry] of Object.entries(lock.packages ?? {})) {
28 const at = key.lastIndexOf("node_modules/");
29 const name = at < 0 ? key : key.slice(at + "node_modules/".length);
30 // A workspace package is a link to its folder's entry.
31 const deps = entry.link
32 ? [entry.resolved]
33 : Object.keys({ ...entry.dependencies, ...entry.devDependencies, ...entry.optionalDependencies, ...entry.peerDependencies });
34 const record = { key, version: entry.version ?? null, resolved: entry.resolved ?? null, integrity: entry.integrity ?? null, deps };
35 if (!packages.has(name)) packages.set(name, []);
36 packages.get(name).push(record);
37 }
38 return packages;
39}
40
41const signature = (entries) => JSON.stringify((entries ?? []).map((e) => ({ ...e, deps: [...e.deps].sort() })).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))));
42
43/** Names whose entries differ between two parsed lockfiles. */
44export function changedNames(before, after) {
45 const names = new Set();
46 for (const name of new Set([...before.keys(), ...after.keys()])) {
47 if (signature(before.get(name)) !== signature(after.get(name))) names.add(name);
48 }
49 return names;
50}
51
52/** Whether any of `names` is reachable from `roots` in a parsed lockfile (roots included). */
53export function reaches(packages, roots, names) {
54 if (!names.size) return false;
55 const seen = new Set();
56 const stack = [...roots];
57 while (stack.length) {
58 const name = stack.pop();
59 if (seen.has(name)) continue;
60 seen.add(name);
61 if (names.has(name)) return true;
62 for (const entry of packages.get(name) ?? []) stack.push(...entry.deps);
63 }
64 return false;
65}
66
67/**
68 * Where a unit starts in each lockfile: its crate (or its image's) in
69 * Cargo.lock; its folder, and the root's tools (Wrangler bundles every
70 * TypeScript Worker), in package-lock.json.
71 */
72export function lockRoots(unit) {
73 const cargo = [unit.crate, unit.image?.crate].filter(Boolean);
74 const npm = unit.kind === "rust-worker" ? [] : [unit.path, ""];
75 return { cargo, npm };
76}

This file's history is long; its oldest lines are credited to the oldest commit read.