@clemrig/factory 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,6 +11,37 @@ export declare const implementationOutput: z.ZodObject<{
11
11
  }, z.core.$strict>;
12
12
  export type Implementation = z.output<typeof implementationOutput>;
13
13
  export declare function implementationProblem(implementation: Implementation, criteria: Criterion[]): string | undefined;
14
+ /**
15
+ * What a finding is. Its severity follows from it in code, never from the
16
+ * reviewer's own judgment: the first six block the change, the others do not.
17
+ */
18
+ export declare const findingNatures: readonly ["incorrect_behavior", "data_loss", "outage", "security", "regression", "spec_violation", "weak_test", "maintainability", "documentation"];
19
+ export type FindingNature = (typeof findingNatures)[number];
20
+ /** Whether a finding of this nature blocks the change. */
21
+ export declare function isBlocking(nature: FindingNature): boolean;
22
+ /** A nature as people read it: `incorrect_behavior` is "incorrect behavior". */
23
+ export declare function natureLabel(nature: FindingNature): string;
24
+ /** Above this many lines of diff, a change is large: its reviewer reads the diff from a file and may split the review. */
25
+ export declare const largeChangeLines = 1500;
26
+ /** A changed file and its added and removed lines; none for a binary file. */
27
+ export interface ChangedFile {
28
+ path: string;
29
+ /** Its former path, when the diff sees it renamed. */
30
+ from?: string;
31
+ added?: number;
32
+ removed?: number;
33
+ }
34
+ /** The change as its reviewer receives it: the whole diff when it is small, where to read it when it is large. */
35
+ export type Changes = {
36
+ files: ChangedFile[];
37
+ lines: number;
38
+ } & ({
39
+ large: false;
40
+ diff: string;
41
+ } | {
42
+ large: true;
43
+ diffFile: string;
44
+ });
14
45
  /** The reviewer's response: its approval follows from these facts alone. */
15
46
  export declare const reviewOutput: z.ZodObject<{
16
47
  criteria: z.ZodArray<z.ZodObject<{
@@ -22,9 +53,16 @@ export declare const reviewOutput: z.ZodObject<{
22
53
  evidence: z.ZodString;
23
54
  }, z.core.$strict>>;
24
55
  findings: z.ZodArray<z.ZodObject<{
25
- severity: z.ZodEnum<{
26
- blocking: "blocking";
27
- minor: "minor";
56
+ nature: z.ZodEnum<{
57
+ data_loss: "data_loss";
58
+ documentation: "documentation";
59
+ incorrect_behavior: "incorrect_behavior";
60
+ maintainability: "maintainability";
61
+ outage: "outage";
62
+ regression: "regression";
63
+ security: "security";
64
+ spec_violation: "spec_violation";
65
+ weak_test: "weak_test";
28
66
  }>;
29
67
  file: z.ZodOptional<z.ZodString>;
30
68
  summary: z.ZodString;
@@ -11,6 +11,39 @@ export const implementationOutput = z.strictObject({
11
11
  export function implementationProblem(implementation, criteria) {
12
12
  return citationProblem(implementation.criteria.map(({ id }) => id), criteria, true);
13
13
  }
14
+ /**
15
+ * What a finding is. Its severity follows from it in code, never from the
16
+ * reviewer's own judgment: the first six block the change, the others do not.
17
+ */
18
+ export const findingNatures = [
19
+ "incorrect_behavior",
20
+ "data_loss",
21
+ "outage",
22
+ "security",
23
+ "regression",
24
+ "spec_violation",
25
+ "weak_test",
26
+ "maintainability",
27
+ "documentation",
28
+ ];
29
+ const blockingNatures = new Set([
30
+ "incorrect_behavior",
31
+ "data_loss",
32
+ "outage",
33
+ "security",
34
+ "regression",
35
+ "spec_violation",
36
+ ]);
37
+ /** Whether a finding of this nature blocks the change. */
38
+ export function isBlocking(nature) {
39
+ return blockingNatures.has(nature);
40
+ }
41
+ /** A nature as people read it: `incorrect_behavior` is "incorrect behavior". */
42
+ export function natureLabel(nature) {
43
+ return nature.replaceAll("_", " ");
44
+ }
45
+ /** Above this many lines of diff, a change is large: its reviewer reads the diff from a file and may split the review. */
46
+ export const largeChangeLines = 1500;
14
47
  /** The reviewer's response: its approval follows from these facts alone. */
15
48
  export const reviewOutput = z.strictObject({
16
49
  criteria: z.array(z.strictObject({
@@ -19,9 +52,9 @@ export const reviewOutput = z.strictObject({
19
52
  evidence: text,
20
53
  })),
21
54
  findings: z.array(z.strictObject({
22
- /** Blocking: an unmet criterion, a defect or a regression; minor: an improvement not required. */
23
- severity: z.enum(["blocking", "minor"]),
55
+ nature: z.enum(findingNatures),
24
56
  file: text.optional(),
57
+ /** The trigger, then its consequence. */
25
58
  summary: text,
26
59
  })),
27
60
  });
@@ -35,8 +68,8 @@ export function reviewChanges(review) {
35
68
  .filter(({ status }) => status === "not_met")
36
69
  .map(({ id, evidence }) => `- ${id} is not met: ${evidence}`),
37
70
  ...review.findings
38
- .filter(({ severity }) => severity === "blocking")
39
- .map(({ file, summary }) => `- Blocking${file ? ` (${file})` : ""}: ${summary}`),
71
+ .filter(({ nature }) => isBlocking(nature))
72
+ .map(({ nature, file, summary }) => `- Blocking, ${natureLabel(nature)}${file ? ` (${file})` : ""}: ${summary}`),
40
73
  ];
41
74
  return changes.length > 0
42
75
  ? ["The review requires changes:", ...changes].join("\n")
@@ -1 +1 @@
1
- {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/domain/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAkB,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5D,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEtC,yCAAyC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,YAAY,CAAC;IACjD,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,qDAAqD;IACrD,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;CAC1B,CAAC,CAAC;AAGH,MAAM,UAAU,qBAAqB,CACnC,cAA8B,EAC9B,QAAqB;IAErB,OAAO,eAAe,CACpB,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAC3C,QAAQ,EACR,IAAI,CACL,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,YAAY,CAAC;QACb,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAClC,QAAQ,EAAE,IAAI;KACf,CAAC,CACH;IACD,QAAQ,EAAE,CAAC,CAAC,KAAK,CACf,CAAC,CAAC,YAAY,CAAC;QACb,kGAAkG;QAClG,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACvC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;QACrB,OAAO,EAAE,IAAI;KACd,CAAC,CACH;CACF,CAAC,CAAC;AAGH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,QAAqB;IAErB,OAAO,eAAe,CACpB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EACnC,QAAQ,EACR,IAAI,CACL,CAAC;AACJ,CAAC;AAED,gHAAgH;AAChH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,OAAO,GAAG;QACd,GAAG,MAAM,CAAC,QAAQ;aACf,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC;aAC5C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,gBAAgB,QAAQ,EAAE,CAAC;QAC/D,GAAG,MAAM,CAAC,QAAQ;aACf,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,KAAK,UAAU,CAAC;aACjD,GAAG,CACF,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CACpB,aAAa,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,EAAE,CACtD;KACJ,CAAC;IACF,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC;QACvB,CAAC,CAAC,CAAC,8BAA8B,EAAE,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzD,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/domain/review.ts"],"names":[],"mappings":"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"}
@@ -11,8 +11,10 @@ export interface Spec {
11
11
  outOfScope: string;
12
12
  criteria: Criterion[];
13
13
  constraints: string;
14
+ /** The issues that must be delivered before this one starts, by slug. */
15
+ dependencies: string[];
14
16
  }
15
- /** Reads spec.md: a `# ` title, then exactly the five sections, in order. */
16
- export declare function parseSpec(text: string): Parsed<Spec>;
17
+ /** Reads issue `slug`'s spec.md: a `# ` title, then exactly the six sections, in order. */
18
+ export declare function parseSpec(text: string, slug: string): Parsed<Spec>;
17
19
  /** What is wrong with the criteria a response cites: all of the spec's, no other, and each once if required. */
18
20
  export declare function citationProblem(cited: string[], criteria: Criterion[], once?: boolean): string | undefined;
@@ -1,12 +1,14 @@
1
+ import { isSlug } from "./issue.js";
1
2
  const sectionHeadings = [
2
3
  "Objective",
3
4
  "Scope",
4
5
  "Out of scope",
5
6
  "Acceptance criteria",
6
7
  "Constraints",
8
+ "Dependencies",
7
9
  ];
8
- /** Reads spec.md: a `# ` title, then exactly the five sections, in order. */
9
- export function parseSpec(text) {
10
+ /** Reads issue `slug`'s spec.md: a `# ` title, then exactly the six sections, in order. */
11
+ export function parseSpec(text, slug) {
10
12
  const [first = "", ...lines] = text.trim().split(/\r?\n/);
11
13
  const title = /^# (.*)$/.exec(first)?.[1]?.trim() ?? "";
12
14
  const preamble = [];
@@ -25,6 +27,7 @@ export function parseSpec(text) {
25
27
  }
26
28
  const body = (heading) => (sections.get(heading) ?? []).join("\n").trim();
27
29
  const { criteria, problems: criteriaProblems } = parseCriteria(sections.get("Acceptance criteria") ?? []);
30
+ const { dependencies, problems: dependencyProblems } = parseDependencies(sections.get("Dependencies") ?? [], slug);
28
31
  const problems = [
29
32
  ...(title ? [] : ["The first line must be a title: # <Title>"]),
30
33
  ...(preamble.join("").trim()
@@ -35,6 +38,7 @@ export function parseSpec(text) {
35
38
  .filter((heading) => sections.has(heading) && !body(heading))
36
39
  .map((heading) => `Empty section: ## ${heading}`),
37
40
  ...criteriaProblems,
41
+ ...dependencyProblems,
38
42
  ];
39
43
  if (problems.length > 0)
40
44
  return { ok: false, problems };
@@ -47,6 +51,7 @@ export function parseSpec(text) {
47
51
  outOfScope: body("Out of scope"),
48
52
  criteria,
49
53
  constraints: body("Constraints"),
54
+ dependencies,
50
55
  },
51
56
  };
52
57
  }
@@ -58,7 +63,7 @@ function sectionProblems(headings) {
58
63
  const problems = [
59
64
  ...[...new Set(unknown)].map((heading) => `Unknown section: ## ${heading}`),
60
65
  ...duplicate.map((heading) => `Duplicate section: ## ${heading}`),
61
- ...missing.map((heading) => `Missing section: ## ${heading}`),
66
+ ...missing.map((heading) => `Missing section: ## ${heading}${heading === "Dependencies" ? " (the slugs of the issues this one depends on, or None.)" : ""}`),
62
67
  ];
63
68
  if (problems.length === 0 &&
64
69
  headings.join("\n") !== sectionHeadings.join("\n"))
@@ -98,6 +103,28 @@ function parseCriteria(lines) {
98
103
  close();
99
104
  return { criteria, problems };
100
105
  }
106
+ /** `- <slug>` items, or `None.`: the issues this one depends on, never itself, each once. */
107
+ function parseDependencies(lines, slug) {
108
+ const items = lines.map((line) => line.trim()).filter(Boolean);
109
+ if (items.length === 1 && items[0] === "None.")
110
+ return { dependencies: [], problems: [] };
111
+ const dependencies = [];
112
+ const problems = [];
113
+ for (const line of items) {
114
+ const dependency = /^- (.*)$/.exec(line)?.[1]?.trim();
115
+ if (dependency === undefined)
116
+ problems.push(`Dependencies: unexpected line "${line}" (write "- <slug>" for each issue, or None.)`);
117
+ else if (!isSlug(dependency))
118
+ problems.push(`Dependencies: "${dependency}" is not an issue slug`);
119
+ else if (dependency === slug)
120
+ problems.push(`Dependencies: the issue cannot depend on itself`);
121
+ else if (dependencies.includes(dependency))
122
+ problems.push(`Dependencies: ${dependency} is listed twice`);
123
+ else
124
+ dependencies.push(dependency);
125
+ }
126
+ return { dependencies, problems };
127
+ }
101
128
  /** What is wrong with the criteria a response cites: all of the spec's, no other, and each once if required. */
102
129
  export function citationProblem(cited, criteria, once = false) {
103
130
  const ids = criteria.map((criterion) => criterion.id);
@@ -1 +1 @@
1
- {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/domain/spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/domain/spec.ts"],"names":[],"mappings":"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"}
@@ -3,12 +3,16 @@ export interface VerifyCommand {
3
3
  name: string;
4
4
  command: string[];
5
5
  timeoutMs: number;
6
+ /** Globs of the files whose change makes the check run; without it, it always runs. */
7
+ when?: string[];
6
8
  }
7
9
  /** A check that passed, as the pull request reports it. */
8
10
  export interface CheckResult {
9
11
  name: string;
10
12
  command: string[];
11
13
  durationMs: number;
14
+ /** Where the run kept what the check wrote, for the review. */
15
+ outputFile?: string;
12
16
  }
13
17
  /** The correction sent to the implementing agent: the failed check, its cause and the end of its output. */
14
18
  export declare function failureFeedback(check: VerifyCommand, cause: string, output: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/domain/verification.ts"],"names":[],"mappings":"AAcA,4GAA4G;AAC5G,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,KAAa,EACb,MAAc;IAEd,MAAM,MAAM,GAAG,wBAAwB,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC;IAChG,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,wBAAwB,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AACjE,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,OAAO,CAAC,UAAkB;IACxC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/domain/verification.ts"],"names":[],"mappings":"AAkBA,4GAA4G;AAC5G,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,KAAa,EACb,MAAc;IAEd,MAAM,MAAM,GAAG,wBAAwB,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC;IAChG,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,wBAAwB,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;AACjE,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,OAAO,CAAC,UAAkB;IACxC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;AAC9C,CAAC"}
@@ -2,6 +2,7 @@ import type { CheckResult, VerifyCommand } from "../domain/verification.js";
2
2
  export type CheckOutcome = {
3
3
  ok: true;
4
4
  durationMs: number;
5
+ output: string;
5
6
  } | {
6
7
  ok: false;
7
8
  cause: string;
@@ -9,24 +10,38 @@ export type CheckOutcome = {
9
10
  };
10
11
  /** Runs one of the repository's checks; a timeout or a command that cannot start fails it, Ctrl-C stops the run. */
11
12
  export declare function runCheck(dir: string, check: VerifyCommand): Promise<CheckOutcome>;
13
+ /** A check that passed, with what it wrote. */
14
+ export type PassedCheck = CheckResult & {
15
+ output: string;
16
+ };
12
17
  export type ChecksOutcome = {
13
18
  ok: true;
14
- passed: CheckResult[];
19
+ passed: PassedCheck[];
20
+ skipped: VerifyCommand[];
15
21
  } | {
16
22
  ok: false;
17
- passed: CheckResult[];
23
+ passed: PassedCheck[];
24
+ skipped: VerifyCommand[];
18
25
  check: VerifyCommand;
19
26
  cause: string;
20
27
  output: string;
21
28
  };
22
- /** A check that starts, or passes with its duration. */
29
+ /** A check that starts, passes with its duration, or is skipped: no changed file matches its `when`. */
23
30
  export type CheckProgress = {
24
31
  check: VerifyCommand;
25
32
  status: "started";
33
+ } | {
34
+ check: VerifyCommand;
35
+ status: "skipped";
26
36
  } | {
27
37
  check: VerifyCommand;
28
38
  status: "passed";
29
39
  durationMs: number;
30
40
  };
31
- /** Runs the checks in order, stopping at the first failure. */
32
- export declare function runChecks(dir: string, checks: VerifyCommand[], progress?: (event: CheckProgress) => void): Promise<ChecksOutcome>;
41
+ /** Whether one of the files matches one of the globs. */
42
+ export declare function matchesAny(files: string[], globs: string[]): boolean;
43
+ /**
44
+ * Runs the checks in order, stopping at the first failure. A check with `when`
45
+ * runs only if one of the `changed` files matches it, and never without them.
46
+ */
47
+ export declare function runChecks(dir: string, checks: VerifyCommand[], progress?: (event: CheckProgress) => void, changed?: string[]): Promise<ChecksOutcome>;
@@ -1,3 +1,4 @@
1
+ import { matchesGlob } from "node:path";
1
2
  import { ProcessInterruptedError, ProcessTimeoutError, runProcess, } from "@clemrig/flow";
2
3
  /** Runs one of the repository's checks; a timeout or a command that cannot start fails it, Ctrl-C stops the run. */
3
4
  export async function runCheck(dir, check) {
@@ -5,7 +6,12 @@ export async function runCheck(dir, check) {
5
6
  try {
6
7
  const result = await runProcess(check.command, dir, check.timeoutMs);
7
8
  if (result.exitCode === 0)
8
- return { ok: true, durationMs: Date.now() - started };
9
+ return {
10
+ ok: true,
11
+ durationMs: Date.now() - started,
12
+ // Over 2 MiB, each stream keeps its end, headed by the engine's notice.
13
+ output: result.stdout + result.stderr,
14
+ };
9
15
  return {
10
16
  ok: false,
11
17
  cause: `exit code ${result.exitCode}`,
@@ -23,16 +29,30 @@ export async function runCheck(dir, check) {
23
29
  };
24
30
  }
25
31
  }
26
- /** Runs the checks in order, stopping at the first failure. */
27
- export async function runChecks(dir, checks, progress) {
32
+ /** Whether one of the files matches one of the globs. */
33
+ export function matchesAny(files, globs) {
34
+ return files.some((file) => globs.some((glob) => matchesGlob(file, glob)));
35
+ }
36
+ /**
37
+ * Runs the checks in order, stopping at the first failure. A check with `when`
38
+ * runs only if one of the `changed` files matches it, and never without them.
39
+ */
40
+ export async function runChecks(dir, checks, progress, changed) {
28
41
  const passed = [];
42
+ const skipped = [];
29
43
  for (const check of checks) {
44
+ if (check.when && !(changed && matchesAny(changed, check.when))) {
45
+ progress?.({ check, status: "skipped" });
46
+ skipped.push(check);
47
+ continue;
48
+ }
30
49
  progress?.({ check, status: "started" });
31
50
  const outcome = await runCheck(dir, check);
32
51
  if (!outcome.ok)
33
52
  return {
34
53
  ok: false,
35
54
  passed,
55
+ skipped,
36
56
  check,
37
57
  cause: outcome.cause,
38
58
  output: outcome.output,
@@ -42,8 +62,9 @@ export async function runChecks(dir, checks, progress) {
42
62
  name: check.name,
43
63
  command: check.command,
44
64
  durationMs: outcome.durationMs,
65
+ output: outcome.output,
45
66
  });
46
67
  }
47
- return { ok: true, passed };
68
+ return { ok: true, passed, skipped };
48
69
  }
49
70
  //# sourceMappingURL=checks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/system/checks.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,GACX,MAAM,eAAe,CAAC;AAOvB,oHAAoH;AACpH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAW,EACX,KAAoB;IAEpB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACrE,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;YACvB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;QACxD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,aAAa,MAAM,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;SACtC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,uBAAuB;YAAE,MAAM,KAAK,CAAC;QAC1D,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YAC7D,uDAAuD;YACvD,MAAM,EACJ,KAAK,YAAY,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;SAC1E,CAAC;IACJ,CAAC;AACH,CAAC;AAiBD,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,MAAuB,EACvB,QAAyC;IAEzC,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,EAAE;YACb,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM;gBACN,KAAK;gBACL,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC;QACJ,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,UAAU,EAAE,OAAO,CAAC,UAAU;SAC/B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC"}
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/system/checks.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { ChangedFile } from "../domain/review.js";
1
2
  /** A branch's full reference: a tag of the same name never answers for it. */
2
3
  export declare function branchRef(branch: string): string;
3
4
  /** The full reference of `origin`'s copy of a branch. */
@@ -12,6 +13,8 @@ export declare function currentBranch(dir: string): Promise<string>;
12
13
  /** Modified, staged or untracked files, in the whole tree or under a path, `except` one of its files. */
13
14
  export declare function hasChanges(dir: string, path?: string, except?: string): Promise<boolean>;
14
15
  export declare function fetchBranch(dir: string, branch: string): Promise<void>;
16
+ /** Whether Git still has this object. */
17
+ export declare function hasObject(dir: string, id: string): Promise<boolean>;
15
18
  export declare function commitOf(dir: string, ref: string): Promise<string>;
16
19
  /** Switches to a branch; `from` creates it at that commit, replacing a branch of that name. */
17
20
  export declare function switchTo(dir: string, branch: string, from?: string): Promise<void>;
@@ -22,4 +25,42 @@ export declare function stagedDiff(dir: string, since: string): Promise<{
22
25
  text: string;
23
26
  truncated: boolean;
24
27
  }>;
28
+ /**
29
+ * Each file the staged changes touch since a commit, with its added and
30
+ * removed lines, renames seen as the diff sees them; call it after `stagedDiff`.
31
+ */
32
+ export declare function stagedStat(dir: string, since: string): Promise<ChangedFile[]>;
33
+ /** The files changed since a commit, untracked ones included, without staging anything. */
34
+ export declare function changedFiles(dir: string, since: string): Promise<string[]>;
25
35
  export declare function forcePush(dir: string, branch: string): Promise<void>;
36
+ /**
37
+ * The working tree as a tree object: tracked and untracked files, ignored ones
38
+ * aside. It stages every change, as a delivery does.
39
+ */
40
+ export declare function snapshot(dir: string): Promise<string>;
41
+ /** A file that differs between two snapshots: added, deleted or modified. */
42
+ export interface TreeChange {
43
+ status: "added" | "deleted" | "modified";
44
+ path: string;
45
+ }
46
+ /** The files that differ from `before` to `after`, renames seen as a deletion and an addition. */
47
+ export declare function changesBetween(dir: string, before: string, after: string): Promise<TreeChange[]>;
48
+ /**
49
+ * Puts back the files that changed since the snapshot `before`, in the index
50
+ * and the working tree. The added ones go first, with the folders they leave
51
+ * empty: a folder that replaced a file or a link is gone before the file or
52
+ * the link comes back, and nothing is ever removed through a link put back.
53
+ * Then the modified and deleted ones are rewritten. Ignored files stay.
54
+ */
55
+ export declare function restore(dir: string, before: string, changes: TreeChange[]): Promise<void>;
56
+ /**
57
+ * Puts back everything that changed in the working tree since the snapshot
58
+ * `before`, and returns the paths it put back; none when nothing changed.
59
+ * A snapshot depends on the ignore rules, so they come back first, each time
60
+ * followed by a new snapshot: the `.gitignore` files that were there, then the
61
+ * shallowest new ones. A rule only exposes files in its folder and below, so
62
+ * a deeper `.gitignore` that seems new may be an ignored file it exposed: it
63
+ * waits for the next snapshot. The other changes are then what they really
64
+ * are, and an ignored file is never taken for one.
65
+ */
66
+ export declare function putBack(dir: string, before: string): Promise<string[]>;
@@ -1,3 +1,5 @@
1
+ import { rm, rmdir } from "node:fs/promises";
2
+ import { basename, dirname, join } from "node:path";
1
3
  import { runProcess } from "@clemrig/flow";
2
4
  /** Runs git in the repository; a failure carries git's own message. */
3
5
  async function git(dir, args, timeoutMs = 60_000) {
@@ -47,6 +49,11 @@ export async function hasChanges(dir, path, except) {
47
49
  export async function fetchBranch(dir, branch) {
48
50
  await git(dir, ["fetch", "origin", `+${branchRef(branch)}:${remoteRef(branch)}`], 120_000);
49
51
  }
52
+ /** Whether Git still has this object. */
53
+ export async function hasObject(dir, id) {
54
+ return ((await runProcess(["git", "cat-file", "-e", id], dir, 60_000)).exitCode ===
55
+ 0);
56
+ }
50
57
  export function commitOf(dir, ref) {
51
58
  return output(dir, ["rev-parse", ref]);
52
59
  }
@@ -66,8 +73,173 @@ export async function stagedDiff(dir, since) {
66
73
  const result = await git(dir, ["diff", "--cached", since]);
67
74
  return { text: result.stdout, truncated: result.stdoutTruncated };
68
75
  }
76
+ /**
77
+ * Git's output when the run read all of it: a list cut by the capture limit
78
+ * would name wrong paths, so the run stops instead.
79
+ */
80
+ async function whole(dir, args) {
81
+ const result = await git(dir, args);
82
+ if (result.stdoutTruncated)
83
+ throw new Error(`git ${args[0]} wrote more than the run reads (2 MiB): the run stops`);
84
+ return result.stdout;
85
+ }
86
+ /** NUL-separated fields of Git's `-z` output. */
87
+ async function fields(dir, args) {
88
+ return (await whole(dir, args)).split("\0").filter(Boolean);
89
+ }
90
+ /**
91
+ * Each file the staged changes touch since a commit, with its added and
92
+ * removed lines, renames seen as the diff sees them; call it after `stagedDiff`.
93
+ */
94
+ export async function stagedStat(dir, since) {
95
+ const records = await fields(dir, [
96
+ "diff",
97
+ "--cached",
98
+ "--numstat",
99
+ "-z",
100
+ since,
101
+ ]);
102
+ const files = [];
103
+ for (let index = 0; index < records.length; index += 1) {
104
+ const [added = "-", removed = "-", ...rest] = (records[index] ?? "").split("\t");
105
+ let path = rest.join("\t");
106
+ let source;
107
+ // A rename leaves the path empty, then gives its source and destination.
108
+ if (path === "") {
109
+ source = records[index + 1] ?? "";
110
+ path = records[index + 2] ?? "";
111
+ index += 2;
112
+ }
113
+ files.push({
114
+ path,
115
+ ...(source === undefined ? {} : { from: source }),
116
+ ...(added === "-" ? {} : { added: Number(added) }),
117
+ ...(removed === "-" ? {} : { removed: Number(removed) }),
118
+ });
119
+ }
120
+ return files;
121
+ }
122
+ /** The files changed since a commit, untracked ones included, without staging anything. */
123
+ export async function changedFiles(dir, since) {
124
+ return [
125
+ ...new Set([
126
+ ...(await fields(dir, [
127
+ "diff",
128
+ "--name-only",
129
+ "-z",
130
+ "--no-renames",
131
+ since,
132
+ ])),
133
+ ...(await fields(dir, [
134
+ "ls-files",
135
+ "--others",
136
+ "--exclude-standard",
137
+ "-z",
138
+ ])),
139
+ ]),
140
+ ];
141
+ }
69
142
  export async function forcePush(dir, branch) {
70
143
  const ref = branchRef(branch);
71
144
  await git(dir, ["push", "--force", "-u", "origin", `${ref}:${ref}`], 120_000);
72
145
  }
146
+ /**
147
+ * The working tree as a tree object: tracked and untracked files, ignored ones
148
+ * aside. It stages every change, as a delivery does.
149
+ */
150
+ export async function snapshot(dir) {
151
+ await git(dir, ["add", "-A"]);
152
+ return output(dir, ["write-tree"]);
153
+ }
154
+ /** The files that differ from `before` to `after`, renames seen as a deletion and an addition. */
155
+ export async function changesBetween(dir, before, after) {
156
+ const records = await fields(dir, [
157
+ "diff-tree",
158
+ "-r",
159
+ "-z",
160
+ "--no-renames",
161
+ "--name-status",
162
+ before,
163
+ after,
164
+ ]);
165
+ const changes = [];
166
+ for (let index = 0; index + 1 < records.length; index += 2) {
167
+ const code = records[index] ?? "";
168
+ const path = records[index + 1] ?? "";
169
+ changes.push({
170
+ status: code === "A" ? "added" : code === "D" ? "deleted" : "modified",
171
+ path,
172
+ });
173
+ }
174
+ return changes;
175
+ }
176
+ /**
177
+ * Puts back the files that changed since the snapshot `before`, in the index
178
+ * and the working tree. The added ones go first, with the folders they leave
179
+ * empty: a folder that replaced a file or a link is gone before the file or
180
+ * the link comes back, and nothing is ever removed through a link put back.
181
+ * Then the modified and deleted ones are rewritten. Ignored files stay.
182
+ */
183
+ export async function restore(dir, before, changes) {
184
+ await git(dir, ["read-tree", before]);
185
+ for (const { status, path } of changes) {
186
+ if (status !== "added")
187
+ continue;
188
+ await rm(join(dir, path), { force: true });
189
+ await removeEmptyParents(dir, path);
190
+ }
191
+ const rewritten = changes
192
+ .filter(({ status }) => status !== "added")
193
+ .map(({ path }) => path);
194
+ if (rewritten.length > 0)
195
+ await git(dir, ["checkout-index", "-f", "--", ...rewritten]);
196
+ }
197
+ /** Removes the folders of `path` that are now empty, from the deepest up to the repository. */
198
+ async function removeEmptyParents(dir, path) {
199
+ for (let parent = dirname(path); parent !== "."; parent = dirname(parent))
200
+ try {
201
+ await rmdir(join(dir, parent));
202
+ }
203
+ catch {
204
+ // Not empty, or already gone: the folders above it stay too.
205
+ return;
206
+ }
207
+ }
208
+ /**
209
+ * Puts back everything that changed in the working tree since the snapshot
210
+ * `before`, and returns the paths it put back; none when nothing changed.
211
+ * A snapshot depends on the ignore rules, so they come back first, each time
212
+ * followed by a new snapshot: the `.gitignore` files that were there, then the
213
+ * shallowest new ones. A rule only exposes files in its folder and below, so
214
+ * a deeper `.gitignore` that seems new may be an ignored file it exposed: it
215
+ * waits for the next snapshot. The other changes are then what they really
216
+ * are, and an ignored file is never taken for one.
217
+ */
218
+ export async function putBack(dir, before) {
219
+ const put = [];
220
+ // Each round puts back at least one ignore file; past that many, something else rewrites them.
221
+ for (let round = 0; round < 20; round += 1) {
222
+ const after = await snapshot(dir);
223
+ if (after === before)
224
+ return [...new Set(put)].sort();
225
+ const changes = await changesBetween(dir, before, after);
226
+ const ignores = changes.filter(({ path }) => basename(path) === ".gitignore");
227
+ const existing = ignores.filter(({ status }) => status !== "added");
228
+ const shallowest = Math.min(...ignores.map(({ path }) => depth(path)));
229
+ const next = existing.length > 0
230
+ ? existing
231
+ : ignores.length > 0
232
+ ? ignores.filter(({ path }) => depth(path) === shallowest)
233
+ : changes;
234
+ await restore(dir, before, next);
235
+ put.push(...next.map(({ path }) => path));
236
+ }
237
+ if ((await snapshot(dir)) === before)
238
+ return [...new Set(put)].sort();
239
+ throw new Error("The working tree's .gitignore files keep changing: the run stops");
240
+ }
241
+ /** How many folders a path is in. */
242
+ function depth(path) {
243
+ return path.split("/").length;
244
+ }
73
245
  //# sourceMappingURL=git.js.map