repopo 0.13.0 → 0.14.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +13 -6
  3. package/esm/api.d.ts +2 -0
  4. package/esm/api.d.ts.map +1 -1
  5. package/esm/api.js +1 -0
  6. package/esm/api.js.map +1 -1
  7. package/esm/commands/check-native.d.ts +2 -0
  8. package/esm/commands/check-native.d.ts.map +1 -1
  9. package/esm/commands/check-native.js +9 -0
  10. package/esm/commands/check-native.js.map +1 -1
  11. package/esm/commands/check.d.ts +2 -0
  12. package/esm/commands/check.d.ts.map +1 -1
  13. package/esm/commands/check.js +26 -2
  14. package/esm/commands/check.js.map +1 -1
  15. package/esm/commands/list.d.ts.map +1 -1
  16. package/esm/commands/list.js +1 -1
  17. package/esm/commands/list.js.map +1 -1
  18. package/esm/config.d.ts +5 -0
  19. package/esm/config.d.ts.map +1 -1
  20. package/esm/config.js.map +1 -1
  21. package/esm/context.d.ts +2 -0
  22. package/esm/context.d.ts.map +1 -1
  23. package/esm/context.js +11 -0
  24. package/esm/context.js.map +1 -1
  25. package/esm/makePolicy.d.ts.map +1 -1
  26. package/esm/makePolicy.js +3 -1
  27. package/esm/makePolicy.js.map +1 -1
  28. package/esm/policies/MaxChangedFiles.d.ts +17 -0
  29. package/esm/policies/MaxChangedFiles.d.ts.map +1 -0
  30. package/esm/policies/MaxChangedFiles.js +22 -0
  31. package/esm/policies/MaxChangedFiles.js.map +1 -0
  32. package/esm/policies/RequireCompanionChanges.d.ts +16 -0
  33. package/esm/policies/RequireCompanionChanges.d.ts.map +1 -0
  34. package/esm/policies/RequireCompanionChanges.js +42 -0
  35. package/esm/policies/RequireCompanionChanges.js.map +1 -0
  36. package/esm/policies.d.ts +2 -0
  37. package/esm/policies.d.ts.map +1 -1
  38. package/esm/policies.js +2 -0
  39. package/esm/policies.js.map +1 -1
  40. package/esm/policy.d.ts +9 -3
  41. package/esm/policy.d.ts.map +1 -1
  42. package/esm/policy.js.map +1 -1
  43. package/esm/policyExecution.d.ts +4 -3
  44. package/esm/policyExecution.d.ts.map +1 -1
  45. package/esm/policyExecution.js +13 -3
  46. package/esm/policyExecution.js.map +1 -1
  47. package/esm/repositoryChanges.d.ts +26 -0
  48. package/esm/repositoryChanges.d.ts.map +1 -0
  49. package/esm/repositoryChanges.js +172 -0
  50. package/esm/repositoryChanges.js.map +1 -0
  51. package/esm/repositoryFiles.d.ts +2 -0
  52. package/esm/repositoryFiles.d.ts.map +1 -1
  53. package/esm/repositoryFiles.js +6 -3
  54. package/esm/repositoryFiles.js.map +1 -1
  55. package/esm/runner.d.ts +5 -4
  56. package/esm/runner.d.ts.map +1 -1
  57. package/esm/runner.js +26 -27
  58. package/esm/runner.js.map +1 -1
  59. package/oclif.manifest.json +39 -1
  60. package/package.json +1 -1
  61. package/sidecar/sidecar.mjs +48 -8
@@ -0,0 +1,172 @@
1
+ import { env } from "node:process";
2
+ import { simpleGit } from "simple-git";
3
+ import { readRepositoryFiles, splitGitPaths } from "./repositoryFiles.js";
4
+ /** Validate options independently of whether a policy requests changes. @internal */
5
+ export function validateChangeOptions(options) {
6
+ if (options.mode !== undefined &&
7
+ !["pr", "local", "staged"].includes(options.mode)) {
8
+ throw new TypeError("Change mode must be pr, local, or staged.");
9
+ }
10
+ if (options.base !== undefined && (options.mode ?? "pr") !== "pr") {
11
+ throw new TypeError("A change base is only valid in pr mode.");
12
+ }
13
+ if (options.base !== undefined &&
14
+ (typeof options.base !== "string" || options.base.trim() === "")) {
15
+ throw new TypeError("Change branch references must be nonempty strings.");
16
+ }
17
+ }
18
+ function parseChanges(output) {
19
+ const tokens = splitGitPaths(output);
20
+ const entries = [];
21
+ for (let index = 0; index < tokens.length;) {
22
+ const status = tokens[index++];
23
+ const path = tokens[index++];
24
+ if (!path) {
25
+ throw new Error("Invalid Git name-status output: missing path.");
26
+ }
27
+ if (status?.startsWith("R")) {
28
+ const destination = tokens[index++];
29
+ if (!destination) {
30
+ throw new Error("Invalid Git rename output: missing destination.");
31
+ }
32
+ entries.push({
33
+ status: "renamed",
34
+ previousPath: path,
35
+ path: destination,
36
+ });
37
+ }
38
+ else {
39
+ switch (status) {
40
+ case "A":
41
+ entries.push({ status: "added", path });
42
+ break;
43
+ case "M":
44
+ entries.push({ status: "modified", path });
45
+ break;
46
+ case "D":
47
+ entries.push({ status: "deleted", path });
48
+ break;
49
+ case "T":
50
+ entries.push({ status: "type-changed", path });
51
+ break;
52
+ default:
53
+ throw new Error(`Unsupported Git change status: ${status}`);
54
+ }
55
+ }
56
+ }
57
+ return entries;
58
+ }
59
+ /** Normalize legacy paths and copy records before policies can observe them. @internal */
60
+ export function snapshotInventory(files) {
61
+ return Object.freeze(files.map((file) => Object.freeze(typeof file === "string"
62
+ ? { path: file, status: "unchanged" }
63
+ : { ...file })));
64
+ }
65
+ /** Derive the legacy working-tree inventory, excluding absent paths. @internal */
66
+ export function inventoryPaths(entries) {
67
+ return [
68
+ ...new Set(entries
69
+ .filter((entry) => entry.exists ?? entry.status !== "deleted")
70
+ .map((entry) => entry.path)),
71
+ ];
72
+ }
73
+ /** Refresh live paths without changing the statuses captured before fixes. @internal */
74
+ export function annotateInventory(files, snapshot) {
75
+ const present = new Set(files);
76
+ const changed = snapshot.filter((entry) => entry.status !== "unchanged");
77
+ const changedPaths = new Set(changed.map((entry) => entry.path));
78
+ return snapshotInventory([
79
+ ...files
80
+ .filter((path) => !changedPaths.has(path))
81
+ .map((path) => ({ path, status: "unchanged" })),
82
+ ...changed.map((entry) => ({
83
+ ...entry,
84
+ exists: present.has(entry.path),
85
+ })),
86
+ ]);
87
+ }
88
+ /** Collect the full inventory with Git statuses; does not fetch or require an actual pull request. @public */
89
+ export async function collectRepositoryInventory(root, options = {}) {
90
+ validateChangeOptions(options);
91
+ const mode = options.mode ?? "pr";
92
+ const git = simpleGit({ baseDir: root });
93
+ if (await git.raw(["ls-files", "--unmerged", "-z"])) {
94
+ throw new Error("Cannot collect repository changes with unresolved Git conflicts.");
95
+ }
96
+ // symbolic HEAD exists before the first commit, but no HEAD object does.
97
+ const unborn = !(await git.raw(["rev-parse", "--revs-only", "HEAD"])).trim();
98
+ const head = unborn
99
+ ? undefined
100
+ : (await git.revparse(["--verify", "HEAD^{commit}"])).trim();
101
+ if (mode === "pr") {
102
+ if (!head) {
103
+ throw new Error("PR changes require a HEAD commit. Use staged/local mode for an unborn repository.");
104
+ }
105
+ return annotateInventory(await readRepositoryFiles(git), await collectPrChanges(git, head, options));
106
+ }
107
+ const entries = await collectWorkingChanges(git, mode, head);
108
+ return annotateInventory(await readRepositoryFiles(git), entries);
109
+ }
110
+ async function resolveTarget(git, options) {
111
+ const target = options.base ??
112
+ (env.GITHUB_BASE_REF || env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME);
113
+ if (!target) {
114
+ throw new Error("Cannot determine PR target. Set changes.base or --change-base.");
115
+ }
116
+ // Enumerate refs rather than masking errors from failed Git subprocesses.
117
+ const refs = (await git.raw(["for-each-ref", "--format=%(refname)"]))
118
+ .trim()
119
+ .split("\n");
120
+ if (target.startsWith("refs/")) {
121
+ return target;
122
+ }
123
+ const candidates = [`refs/heads/${target}`, `refs/remotes/origin/${target}`];
124
+ return candidates.find((candidate) => refs.includes(candidate)) ?? target;
125
+ }
126
+ async function collectPrChanges(git, head, options) {
127
+ const target = await resolveTarget(git, options);
128
+ let output;
129
+ try {
130
+ output = await git.raw([
131
+ "diff",
132
+ "--merge-base",
133
+ "--name-status",
134
+ "-z",
135
+ "--find-renames",
136
+ "--no-ext-diff",
137
+ "--end-of-options",
138
+ target,
139
+ head,
140
+ "--",
141
+ ]);
142
+ }
143
+ catch (cause) {
144
+ throw new Error(`Cannot resolve PR base '${target}' and its merge base with HEAD. Set --change-base or fetch the missing branch/history.`, { cause });
145
+ }
146
+ return parseChanges(output);
147
+ }
148
+ async function collectWorkingChanges(git, mode, head) {
149
+ if (!head && mode === "local") {
150
+ return (await readRepositoryFiles(git)).map((path) => ({
151
+ status: "added",
152
+ path,
153
+ }));
154
+ }
155
+ const entries = parseChanges(await git.raw([
156
+ "diff",
157
+ "--name-status",
158
+ "-z",
159
+ "--find-renames",
160
+ "--no-ext-diff",
161
+ ...(mode === "staged" ? ["--cached"] : []),
162
+ ...(head ? [head] : []),
163
+ "--",
164
+ ]));
165
+ if (mode === "local") {
166
+ for (const path of splitGitPaths(await git.raw(["ls-files", "--others", "--exclude-standard", "-z"]))) {
167
+ entries.push({ status: "added", path });
168
+ }
169
+ }
170
+ return entries;
171
+ }
172
+ //# sourceMappingURL=repositoryChanges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repositoryChanges.js","sourceRoot":"","sources":["../src/repositoryChanges.ts"],"names":[],"mappings":"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","sourcesContent":["import { env } from \"node:process\";\nimport { simpleGit } from \"simple-git\";\nimport type { RepopoConfig } from \"./config.js\";\nimport { readRepositoryFiles, splitGitPaths } from \"./repositoryFiles.js\";\n\ntype Git = ReturnType<typeof simpleGit>;\n\ntype ChangeOptions = NonNullable<RepopoConfig[\"changes\"]>;\n\n/** Repo-relative Git paths; renames retain both endpoints. @public */\nexport type RepositoryFile = (\n\t| {\n\t\t\treadonly status:\n\t\t\t\t| \"unchanged\"\n\t\t\t\t| \"added\"\n\t\t\t\t| \"modified\"\n\t\t\t\t| \"deleted\"\n\t\t\t\t| \"type-changed\";\n\t\t\treadonly path: string;\n\t }\n\t| {\n\t\t\treadonly status: \"renamed\";\n\t\t\treadonly path: string;\n\t\t\treadonly previousPath: string;\n\t }\n) & {\n\t/** Working-tree presence when it differs from status (for example an unstaged deletion in PR mode). */\n\treadonly exists?: boolean | undefined;\n};\n\n/** Validate options independently of whether a policy requests changes. @internal */\nexport function validateChangeOptions(options: ChangeOptions): void {\n\tif (\n\t\toptions.mode !== undefined &&\n\t\t![\"pr\", \"local\", \"staged\"].includes(options.mode)\n\t) {\n\t\tthrow new TypeError(\"Change mode must be pr, local, or staged.\");\n\t}\n\tif (options.base !== undefined && (options.mode ?? \"pr\") !== \"pr\") {\n\t\tthrow new TypeError(\"A change base is only valid in pr mode.\");\n\t}\n\tif (\n\t\toptions.base !== undefined &&\n\t\t(typeof options.base !== \"string\" || options.base.trim() === \"\")\n\t) {\n\t\tthrow new TypeError(\"Change branch references must be nonempty strings.\");\n\t}\n}\n\nfunction parseChanges(output: string): RepositoryFile[] {\n\tconst tokens = splitGitPaths(output);\n\tconst entries: RepositoryFile[] = [];\n\tfor (let index = 0; index < tokens.length; ) {\n\t\tconst status = tokens[index++];\n\t\tconst path = tokens[index++];\n\t\tif (!path) {\n\t\t\tthrow new Error(\"Invalid Git name-status output: missing path.\");\n\t\t}\n\t\tif (status?.startsWith(\"R\")) {\n\t\t\tconst destination = tokens[index++];\n\t\t\tif (!destination) {\n\t\t\t\tthrow new Error(\"Invalid Git rename output: missing destination.\");\n\t\t\t}\n\t\t\tentries.push({\n\t\t\t\tstatus: \"renamed\",\n\t\t\t\tpreviousPath: path,\n\t\t\t\tpath: destination,\n\t\t\t});\n\t\t} else {\n\t\t\tswitch (status) {\n\t\t\t\tcase \"A\":\n\t\t\t\t\tentries.push({ status: \"added\", path });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"M\":\n\t\t\t\t\tentries.push({ status: \"modified\", path });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"D\":\n\t\t\t\t\tentries.push({ status: \"deleted\", path });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"T\":\n\t\t\t\t\tentries.push({ status: \"type-changed\", path });\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new Error(`Unsupported Git change status: ${status}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn entries;\n}\n\n/** Normalize legacy paths and copy records before policies can observe them. @internal */\nexport function snapshotInventory(\n\tfiles: readonly (string | RepositoryFile)[],\n): readonly RepositoryFile[] {\n\treturn Object.freeze(\n\t\tfiles.map((file) =>\n\t\t\tObject.freeze(\n\t\t\t\ttypeof file === \"string\"\n\t\t\t\t\t? { path: file, status: \"unchanged\" as const }\n\t\t\t\t\t: { ...file },\n\t\t\t),\n\t\t),\n\t);\n}\n\n/** Derive the legacy working-tree inventory, excluding absent paths. @internal */\nexport function inventoryPaths(entries: readonly RepositoryFile[]): string[] {\n\treturn [\n\t\t...new Set(\n\t\t\tentries\n\t\t\t\t.filter((entry) => entry.exists ?? entry.status !== \"deleted\")\n\t\t\t\t.map((entry) => entry.path),\n\t\t),\n\t];\n}\n\n/** Refresh live paths without changing the statuses captured before fixes. @internal */\nexport function annotateInventory(\n\tfiles: readonly string[],\n\tsnapshot: readonly RepositoryFile[],\n): readonly RepositoryFile[] {\n\tconst present = new Set(files);\n\tconst changed = snapshot.filter((entry) => entry.status !== \"unchanged\");\n\tconst changedPaths = new Set(changed.map((entry) => entry.path));\n\treturn snapshotInventory([\n\t\t...files\n\t\t\t.filter((path) => !changedPaths.has(path))\n\t\t\t.map((path) => ({ path, status: \"unchanged\" as const })),\n\t\t...changed.map((entry) => ({\n\t\t\t...entry,\n\t\t\texists: present.has(entry.path),\n\t\t})),\n\t]);\n}\n\n/** Collect the full inventory with Git statuses; does not fetch or require an actual pull request. @public */\nexport async function collectRepositoryInventory(\n\troot: string,\n\toptions: NonNullable<RepopoConfig[\"changes\"]> = {},\n): Promise<readonly RepositoryFile[]> {\n\tvalidateChangeOptions(options);\n\tconst mode = options.mode ?? \"pr\";\n\tconst git = simpleGit({ baseDir: root });\n\tif (await git.raw([\"ls-files\", \"--unmerged\", \"-z\"])) {\n\t\tthrow new Error(\n\t\t\t\"Cannot collect repository changes with unresolved Git conflicts.\",\n\t\t);\n\t}\n\t// symbolic HEAD exists before the first commit, but no HEAD object does.\n\tconst unborn = !(await git.raw([\"rev-parse\", \"--revs-only\", \"HEAD\"])).trim();\n\tconst head = unborn\n\t\t? undefined\n\t\t: (await git.revparse([\"--verify\", \"HEAD^{commit}\"])).trim();\n\tif (mode === \"pr\") {\n\t\tif (!head) {\n\t\t\tthrow new Error(\n\t\t\t\t\"PR changes require a HEAD commit. Use staged/local mode for an unborn repository.\",\n\t\t\t);\n\t\t}\n\t\treturn annotateInventory(\n\t\t\tawait readRepositoryFiles(git),\n\t\t\tawait collectPrChanges(git, head, options),\n\t\t);\n\t}\n\tconst entries = await collectWorkingChanges(git, mode, head);\n\treturn annotateInventory(await readRepositoryFiles(git), entries);\n}\n\nasync function resolveTarget(\n\tgit: Git,\n\toptions: ChangeOptions,\n): Promise<string> {\n\tconst target =\n\t\toptions.base ??\n\t\t(env.GITHUB_BASE_REF || env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME);\n\tif (!target) {\n\t\tthrow new Error(\n\t\t\t\"Cannot determine PR target. Set changes.base or --change-base.\",\n\t\t);\n\t}\n\t// Enumerate refs rather than masking errors from failed Git subprocesses.\n\tconst refs = (await git.raw([\"for-each-ref\", \"--format=%(refname)\"]))\n\t\t.trim()\n\t\t.split(\"\\n\");\n\tif (target.startsWith(\"refs/\")) {\n\t\treturn target;\n\t}\n\tconst candidates = [`refs/heads/${target}`, `refs/remotes/origin/${target}`];\n\treturn candidates.find((candidate) => refs.includes(candidate)) ?? target;\n}\n\nasync function collectPrChanges(\n\tgit: Git,\n\thead: string,\n\toptions: ChangeOptions,\n): Promise<RepositoryFile[]> {\n\tconst target = await resolveTarget(git, options);\n\tlet output: string;\n\ttry {\n\t\toutput = await git.raw([\n\t\t\t\"diff\",\n\t\t\t\"--merge-base\",\n\t\t\t\"--name-status\",\n\t\t\t\"-z\",\n\t\t\t\"--find-renames\",\n\t\t\t\"--no-ext-diff\",\n\t\t\t\"--end-of-options\",\n\t\t\ttarget,\n\t\t\thead,\n\t\t\t\"--\",\n\t\t]);\n\t} catch (cause) {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve PR base '${target}' and its merge base with HEAD. Set --change-base or fetch the missing branch/history.`,\n\t\t\t{ cause },\n\t\t);\n\t}\n\treturn parseChanges(output);\n}\n\nasync function collectWorkingChanges(\n\tgit: Git,\n\tmode: \"local\" | \"staged\",\n\thead: string | undefined,\n): Promise<RepositoryFile[]> {\n\tif (!head && mode === \"local\") {\n\t\treturn (await readRepositoryFiles(git)).map((path) => ({\n\t\t\tstatus: \"added\",\n\t\t\tpath,\n\t\t}));\n\t}\n\tconst entries = parseChanges(\n\t\tawait git.raw([\n\t\t\t\"diff\",\n\t\t\t\"--name-status\",\n\t\t\t\"-z\",\n\t\t\t\"--find-renames\",\n\t\t\t\"--no-ext-diff\",\n\t\t\t...(mode === \"staged\" ? [\"--cached\"] : []),\n\t\t\t...(head ? [head] : []),\n\t\t\t\"--\",\n\t\t]),\n\t);\n\tif (mode === \"local\") {\n\t\tfor (const path of splitGitPaths(\n\t\t\tawait git.raw([\"ls-files\", \"--others\", \"--exclude-standard\", \"-z\"]),\n\t\t)) {\n\t\t\tentries.push({ status: \"added\", path });\n\t\t}\n\t}\n\treturn entries;\n}\n"]}
@@ -1,4 +1,6 @@
1
1
  import type { SimpleGit } from "simple-git";
2
+ /** Split Git's NUL-delimited paths without changing literal filename characters. @internal */
3
+ export declare function splitGitPaths(output: string): string[];
2
4
  /** Git's cached inventory includes unstaged deletions; those are not live files. */
3
5
  export declare function readRepositoryFiles(git: SimpleGit): Promise<string[]>;
4
6
  //# sourceMappingURL=repositoryFiles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repositoryFiles.d.ts","sourceRoot":"","sources":["../src/repositoryFiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,oFAAoF;AACpF,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ3E"}
1
+ {"version":3,"file":"repositoryFiles.d.ts","sourceRoot":"","sources":["../src/repositoryFiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtD;AAED,oFAAoF;AACpF,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ3E"}
@@ -1,8 +1,11 @@
1
- import { parseFilePaths } from "./filePaths.js";
1
+ /** Split Git's NUL-delimited paths without changing literal filename characters. @internal */
2
+ export function splitGitPaths(output) {
3
+ return output.split("\0").filter((path) => path !== "");
4
+ }
2
5
  /** Git's cached inventory includes unstaged deletions; those are not live files. */
3
6
  export async function readRepositoryFiles(git) {
4
- const files = parseFilePaths(await git.raw("ls-files", "-co", "-z", "--exclude-standard", "--full-name"));
5
- const deleted = new Set(parseFilePaths(await git.raw("ls-files", "--deleted", "-z", "--full-name")));
7
+ const files = splitGitPaths(await git.raw("ls-files", "-co", "-z", "--exclude-standard", "--full-name"));
8
+ const deleted = new Set(splitGitPaths(await git.raw("ls-files", "--deleted", "-z", "--full-name")));
6
9
  return files.filter((file) => !deleted.has(file));
7
10
  }
8
11
  //# sourceMappingURL=repositoryFiles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"repositoryFiles.js","sourceRoot":"","sources":["../src/repositoryFiles.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAc;IACvD,MAAM,KAAK,GAAG,cAAc,CAC3B,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,aAAa,CAAC,CAC3E,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,GAAG,CACtB,cAAc,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAC3E,CAAC;IACF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AACnD,CAAC","sourcesContent":["import type { SimpleGit } from \"simple-git\";\nimport { parseFilePaths } from \"./filePaths.js\";\n\n/** Git's cached inventory includes unstaged deletions; those are not live files. */\nexport async function readRepositoryFiles(git: SimpleGit): Promise<string[]> {\n\tconst files = parseFilePaths(\n\t\tawait git.raw(\"ls-files\", \"-co\", \"-z\", \"--exclude-standard\", \"--full-name\"),\n\t);\n\tconst deleted = new Set(\n\t\tparseFilePaths(await git.raw(\"ls-files\", \"--deleted\", \"-z\", \"--full-name\")),\n\t);\n\treturn files.filter((file) => !deleted.has(file));\n}\n"]}
1
+ {"version":3,"file":"repositoryFiles.js","sourceRoot":"","sources":["../src/repositoryFiles.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,MAAc;IAC3C,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAc;IACvD,MAAM,KAAK,GAAG,aAAa,CAC1B,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,aAAa,CAAC,CAC3E,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,GAAG,CACtB,aAAa,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAC1E,CAAC;IACF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AACnD,CAAC","sourcesContent":["import type { SimpleGit } from \"simple-git\";\n\n/** Split Git's NUL-delimited paths without changing literal filename characters. @internal */\nexport function splitGitPaths(output: string): string[] {\n\treturn output.split(\"\\0\").filter((path) => path !== \"\");\n}\n\n/** Git's cached inventory includes unstaged deletions; those are not live files. */\nexport async function readRepositoryFiles(git: SimpleGit): Promise<string[]> {\n\tconst files = splitGitPaths(\n\t\tawait git.raw(\"ls-files\", \"-co\", \"-z\", \"--exclude-standard\", \"--full-name\"),\n\t);\n\tconst deleted = new Set(\n\t\tsplitGitPaths(await git.raw(\"ls-files\", \"--deleted\", \"-z\", \"--full-name\")),\n\t);\n\treturn files.filter((file) => !deleted.has(file));\n}\n"]}
package/esm/runner.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { type Operation } from "effection";
2
2
  import { type PolicyHandlerPerfStats } from "./perf.js";
3
3
  import type { ConfiguredPolicy, PolicyFinding } from "./policy.js";
4
+ import { type RepositoryFile } from "./repositoryChanges.js";
4
5
  /** A finding with runner-owned identity and scope-safe location. @public */
5
6
  export type PolicyRunFinding = (Omit<PolicyFinding, "files"> & {
6
7
  scope: "file";
@@ -9,7 +10,7 @@ export type PolicyRunFinding = (Omit<PolicyFinding, "files"> & {
9
10
  policy: string;
10
11
  policyId: string;
11
12
  }) | (PolicyFinding & {
12
- scope: "repository";
13
+ scope: "repository" | "changes";
13
14
  file?: never;
14
15
  policy: string;
15
16
  policyId: string;
@@ -27,7 +28,7 @@ export type PolicyExecutionError = {
27
28
  file: string;
28
29
  files?: never;
29
30
  } | {
30
- scope: "repository";
31
+ scope: "repository" | "changes";
31
32
  files: readonly string[];
32
33
  file?: never;
33
34
  });
@@ -40,7 +41,7 @@ export type PolicySkippedWork = {
40
41
  scope: "file";
41
42
  file: string;
42
43
  } | {
43
- scope: "repository";
44
+ scope: "repository" | "changes";
44
45
  file?: never;
45
46
  });
46
47
  /** Findings, failures, skipped units, and file/per-instance timing statistics. @public */
@@ -51,7 +52,7 @@ export interface PolicyRunResults {
51
52
  perfStats: PolicyHandlerPerfStats;
52
53
  }
53
54
  /** Full repository inventory, never inferred from a file selection. @public */
54
- export type RepositoryInventory = readonly string[] | (() => Promise<readonly string[]>);
55
+ export type RepositoryInventory = readonly (string | RepositoryFile)[] | (() => Promise<readonly (string | RepositoryFile)[]>);
55
56
  /** Shared Promise and Effection runner options. @public */
56
57
  export interface PolicyRunnerOptions {
57
58
  policies: readonly ConfiguredPolicy[];
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
package/esm/runner.js CHANGED
@@ -1,20 +1,11 @@
1
1
  import { all, call, run, spawn } from "effection";
2
2
  import { DEFAULT_CONCURRENCY, validateConcurrency } from "./concurrency.js";
3
- import { createExcludedPolicyFileMap, exclusionRegex, } from "./context.js";
3
+ import { createExcludedPolicyFileMap, exclusionRegex, matchesPath, } from "./context.js";
4
4
  import { ParsedFileCache } from "./fileCache.js";
5
5
  import { identifyPolicyInstances } from "./makePolicy.js";
6
6
  import { newPerfStats } from "./perf.js";
7
7
  import { PolicyExecutor } from "./policyExecution.js";
8
- function matches(regex, file) {
9
- const index = regex.lastIndex;
10
- try {
11
- regex.lastIndex = 0;
12
- return regex.test(file);
13
- }
14
- finally {
15
- regex.lastIndex = index;
16
- }
17
- }
8
+ import { annotateInventory, inventoryPaths, snapshotInventory, } from "./repositoryChanges.js";
18
9
  /** Effection runner; cancellation unwinds active generator handlers and resolvers. @public */
19
10
  export class PolicyRunner {
20
11
  options;
@@ -31,12 +22,13 @@ export class PolicyRunner {
31
22
  this.exclusions = (options.exclude ?? []).map(exclusionRegex);
32
23
  this.policyExclusions = createExcludedPolicyFileMap(this.policies);
33
24
  this.concurrency = validateConcurrency(options.concurrency ?? DEFAULT_CONCURRENCY);
34
- if (this.policies.some((policy) => policy.scope === "repository") &&
25
+ if (this.policies.some((policy) => policy.scope === "repository" || policy.scope === "changes") &&
35
26
  options.inventory === undefined) {
36
27
  throw new TypeError("Repository policies require an explicit inventory array or loader; files is only a selection.");
37
28
  }
38
29
  for (const policy of this.policies) {
39
- if (policy.scope === "repository" && policy.resolver !== undefined) {
30
+ if ((policy.scope === "repository" || policy.scope === "changes") &&
31
+ policy.resolver !== undefined) {
40
32
  throw new TypeError("Repository policies support inline fixes only; remove resolver.");
41
33
  }
42
34
  if (policy.requiresConfig &&
@@ -76,11 +68,13 @@ export class PolicyRunner {
76
68
  };
77
69
  const fix = this.options.fix ?? false;
78
70
  const cache = fix ? undefined : new ParsedFileCache();
79
- let inventory;
80
- if (this.options.files === undefined) {
81
- inventory = yield* this.loadInventory();
71
+ const rawInventory = yield* this.loadInventory();
72
+ if (rawInventory.some((entry) => typeof entry === "string") &&
73
+ this.policies.some((policy) => policy.scope === "changes")) {
74
+ throw new TypeError("Change-aware policies require inventory entries with Git statuses, not string paths.");
82
75
  }
83
- const files = this.options.files ?? inventory ?? [];
76
+ const inventory = snapshotInventory(rawInventory);
77
+ const files = this.options.files ?? inventoryPaths(inventory);
84
78
  let fileFixError = false;
85
79
  try {
86
80
  if (fix) {
@@ -128,42 +122,47 @@ export class PolicyRunner {
128
122
  const fix = this.options.fix ?? false;
129
123
  let repositoryFixError = false;
130
124
  for (const policy of this.policies) {
131
- if (policy.scope !== "repository") {
125
+ if (policy.scope !== "repository" && policy.scope !== "changes") {
132
126
  continue;
133
127
  }
134
128
  if (fileFixError || repositoryFixError) {
135
129
  result.skipped.push({
136
- scope: "repository",
130
+ scope: policy.scope,
137
131
  policy: policy.name,
138
132
  policyId: policy.instanceId,
139
133
  reason: fileFixError ? "file-fix-error" : "repository-fix-error",
140
134
  });
141
135
  continue;
142
136
  }
143
- if (inventory === undefined || fix) {
144
- inventory = yield* this.loadInventory();
137
+ if (fix) {
138
+ const refreshed = yield* this.loadInventory();
139
+ inventory = annotateInventory(inventoryPaths(snapshotInventory(refreshed)), initialInventory);
145
140
  }
146
- const eligible = (inventory ?? []).filter((file) => !this.excluded(file, policy));
141
+ const eligible = inventory.filter((entry) => !this.excluded(entry.path, policy));
147
142
  repositoryFixError = yield* new PolicyExecutor(this.options.root, fix).repository(policy, eligible, result);
148
143
  }
149
144
  }
150
145
  *loadInventory() {
151
146
  const inventory = this.options.inventory;
152
- return typeof inventory === "function" ? yield* call(inventory) : inventory;
147
+ return ((typeof inventory === "function" ? yield* call(inventory) : inventory) ??
148
+ []);
153
149
  }
154
150
  excluded(file, policy) {
155
- return (this.exclusions.some((regex) => matches(regex, file)) ||
156
- (this.policyExclusions.get(policy.instanceId) ?? []).some((regex) => matches(regex, file)));
151
+ return (this.exclusions.some((regex) => matchesPath(regex, file)) ||
152
+ (this.policyExclusions.get(policy.instanceId) ?? []).some((regex) => matchesPath(regex, file)));
157
153
  }
158
154
  *runFile(file, output, cache) {
159
155
  output.perfStats.count++;
160
- const globallyExcluded = this.exclusions.some((regex) => matches(regex, file));
156
+ const globallyExcluded = this.exclusions.some((regex) => matchesPath(regex, file));
161
157
  if (!globallyExcluded) {
162
158
  output.perfStats.processed++;
163
159
  }
164
160
  let stopped = false;
165
161
  for (const policy of this.policies) {
166
- if (policy.scope === "repository" || !matches(policy.match, file)) {
162
+ if (policy.scope !== undefined && policy.scope !== "file") {
163
+ continue;
164
+ }
165
+ if (!matchesPath(policy.match, file)) {
167
166
  continue;
168
167
  }
169
168
  if (stopped || this.excluded(file, policy)) {
package/esm/runner.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import { all, call, type Operation, run, spawn } from \"effection\";\nimport { DEFAULT_CONCURRENCY, validateConcurrency } from \"./concurrency.js\";\nimport {\n\tcreateExcludedPolicyFileMap,\n\ttype ExcludedPolicyFileMap,\n\texclusionRegex,\n} from \"./context.js\";\nimport { ParsedFileCache } from \"./fileCache.js\";\nimport { identifyPolicyInstances } from \"./makePolicy.js\";\nimport { newPerfStats, type PolicyHandlerPerfStats } from \"./perf.js\";\nimport type {\n\tConfiguredPolicy,\n\tIdentifiedPolicy,\n\tPolicyFinding,\n} from \"./policy.js\";\nimport { PolicyExecutor } from \"./policyExecution.js\";\n\n/** A finding with runner-owned identity and scope-safe location. @public */\nexport type PolicyRunFinding =\n\t| (Omit<PolicyFinding, \"files\"> & {\n\t\t\tscope: \"file\";\n\t\t\tfile: string;\n\t\t\tfiles?: never;\n\t\t\tpolicy: string;\n\t\t\tpolicyId: string;\n\t })\n\t| (PolicyFinding & {\n\t\t\tscope: \"repository\";\n\t\t\tfile?: never;\n\t\t\tpolicy: string;\n\t\t\tpolicyId: string;\n\t });\n\n/** An execution failure, separate from ordinary policy findings. @public */\nexport type PolicyExecutionError = {\n\tpolicy: string;\n\tpolicyId: string;\n\tphase: \"handle\" | \"resolve\";\n\tkind: \"contract\" | \"exception\";\n\tmessage: string;\n\tcause: unknown;\n} & (\n\t| { scope: \"file\"; file: string; files?: never }\n\t| { scope: \"repository\"; files: readonly string[]; file?: never }\n);\n\n/** A unit that was not executed. @public */\nexport type PolicySkippedWork = {\n\tpolicy: string;\n\tpolicyId: string;\n\treason: \"excluded\" | \"file-fix-error\" | \"repository-fix-error\";\n} & ({ scope: \"file\"; file: string } | { scope: \"repository\"; file?: never });\n\n/** Findings, failures, skipped units, and file/per-instance timing statistics. @public */\nexport interface PolicyRunResults {\n\tfindings: PolicyRunFinding[];\n\terrors: PolicyExecutionError[];\n\tskipped: PolicySkippedWork[];\n\tperfStats: PolicyHandlerPerfStats;\n}\n\n/** Full repository inventory, never inferred from a file selection. @public */\nexport type RepositoryInventory =\n\t| readonly string[]\n\t| (() => Promise<readonly string[]>);\n\n/** Shared Promise and Effection runner options. @public */\nexport interface PolicyRunnerOptions {\n\tpolicies: readonly ConfiguredPolicy[];\n\troot: string;\n\tinventory?: RepositoryInventory | undefined;\n\t/** Selection limits only file policies. Defaults to inventory when supplied. */\n\tfiles?: readonly string[] | undefined;\n\texclude?: readonly (string | RegExp)[] | undefined;\n\tfix?: boolean | undefined;\n\tconcurrency?: number | undefined;\n\tsignal?: AbortSignal | undefined;\n}\n\nfunction matches(regex: RegExp, file: string): boolean {\n\tconst index = regex.lastIndex;\n\ttry {\n\t\tregex.lastIndex = 0;\n\t\treturn regex.test(file);\n\t} finally {\n\t\tregex.lastIndex = index;\n\t}\n}\n\n/** Effection runner; cancellation unwinds active generator handlers and resolvers. @public */\nexport class PolicyRunner {\n\tprivate readonly policies: IdentifiedPolicy[];\n\tprivate readonly exclusions: RegExp[];\n\tprivate readonly policyExclusions: ExcludedPolicyFileMap;\n\tprivate readonly concurrency: number;\n\n\tpublic constructor(private readonly options: PolicyRunnerOptions) {\n\t\tthis.policies = identifyPolicyInstances(\n\t\t\toptions.policies.map((policy) => ({\n\t\t\t\t...policy,\n\t\t\t\tconfig:\n\t\t\t\t\tpolicy.config === undefined ? policy.defaultConfig : policy.config,\n\t\t\t})),\n\t\t);\n\t\tthis.exclusions = (options.exclude ?? []).map(exclusionRegex);\n\t\tthis.policyExclusions = createExcludedPolicyFileMap(this.policies);\n\t\tthis.concurrency = validateConcurrency(\n\t\t\toptions.concurrency ?? DEFAULT_CONCURRENCY,\n\t\t);\n\t\tif (\n\t\t\tthis.policies.some((policy) => policy.scope === \"repository\") &&\n\t\t\toptions.inventory === undefined\n\t\t) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"Repository policies require an explicit inventory array or loader; files is only a selection.\",\n\t\t\t);\n\t\t}\n\t\tfor (const policy of this.policies) {\n\t\t\tif (policy.scope === \"repository\" && policy.resolver !== undefined) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t\"Repository policies support inline fixes only; remove resolver.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tpolicy.requiresConfig &&\n\t\t\t\tpolicy.config === undefined &&\n\t\t\t\tpolicy.defaultConfig === undefined\n\t\t\t) {\n\t\t\t\tthrow new TypeError(`Policy '${policy.name}' requires config.`);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic *run(): Operation<PolicyRunResults> {\n\t\tconst signal = this.options.signal;\n\t\tif (signal?.aborted) {\n\t\t\tthrow signal.reason;\n\t\t}\n\t\tconst task = yield* spawn(() => this.runInternal());\n\t\t// halt() is lazy; consuming it initiates teardown. The task retains any teardown error.\n\t\tconst abort = () => {\n\t\t\ttask.halt().catch(() => undefined);\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", abort, { once: true });\n\t\ttry {\n\t\t\tif (signal?.aborted) {\n\t\t\t\tabort();\n\t\t\t}\n\t\t\treturn yield* task;\n\t\t} finally {\n\t\t\tsignal?.removeEventListener(\"abort\", abort);\n\t\t}\n\t}\n\n\tprivate *runInternal(): Operation<PolicyRunResults> {\n\t\tconst result: PolicyRunResults = {\n\t\t\tfindings: [],\n\t\t\terrors: [],\n\t\t\tskipped: [],\n\t\t\tperfStats: newPerfStats(),\n\t\t};\n\t\tconst fix = this.options.fix ?? false;\n\t\tconst cache = fix ? undefined : new ParsedFileCache();\n\t\tlet inventory: readonly string[] | undefined;\n\t\tif (this.options.files === undefined) {\n\t\t\tinventory = yield* this.loadInventory();\n\t\t}\n\t\tconst files = this.options.files ?? inventory ?? [];\n\t\tlet fileFixError = false;\n\t\ttry {\n\t\t\tif (fix) {\n\t\t\t\tfor (const file of files) {\n\t\t\t\t\tif (yield* this.runFile(file, result, cache)) {\n\t\t\t\t\t\tfileFixError = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tyield* this.runReadOnlyFiles(files, result, cache);\n\t\t\t}\n\t\t} finally {\n\t\t\tcache?.clear();\n\t\t}\n\t\tyield* this.runRepositories(result, inventory, fileFixError);\n\t\treturn result;\n\t}\n\n\tprivate *runReadOnlyFiles(\n\t\tfiles: readonly string[],\n\t\tresult: PolicyRunResults,\n\t\tcache: ParsedFileCache | undefined,\n\t): Operation<void> {\n\t\tconst entries = files.entries();\n\t\tconst outputs: PolicyRunResults[] = [];\n\t\tconst runner = this;\n\t\tyield* all(\n\t\t\tArray.from(\n\t\t\t\t{ length: Math.min(this.concurrency, files.length) },\n\t\t\t\tfunction* () {\n\t\t\t\t\tfor (\n\t\t\t\t\t\tlet entry = entries.next();\n\t\t\t\t\t\t!entry.done;\n\t\t\t\t\t\tentry = entries.next()\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst [index, file] = entry.value;\n\t\t\t\t\t\tconst local: PolicyRunResults = {\n\t\t\t\t\t\t\tfindings: [],\n\t\t\t\t\t\t\terrors: [],\n\t\t\t\t\t\t\tskipped: [],\n\t\t\t\t\t\t\tperfStats: result.perfStats,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tyield* runner.runFile(file, local, cache);\n\t\t\t\t\t\toutputs[index] = local;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\tfor (const output of outputs) {\n\t\t\tresult.findings.push(...output.findings);\n\t\t\tresult.errors.push(...output.errors);\n\t\t\tresult.skipped.push(...output.skipped);\n\t\t}\n\t}\n\n\tprivate *runRepositories(\n\t\tresult: PolicyRunResults,\n\t\tinitialInventory: readonly string[] | undefined,\n\t\tfileFixError: boolean,\n\t): Operation<void> {\n\t\tlet inventory = initialInventory;\n\t\tconst fix = this.options.fix ?? false;\n\t\tlet repositoryFixError = false;\n\t\tfor (const policy of this.policies) {\n\t\t\tif (policy.scope !== \"repository\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (fileFixError || repositoryFixError) {\n\t\t\t\tresult.skipped.push({\n\t\t\t\t\tscope: \"repository\",\n\t\t\t\t\tpolicy: policy.name,\n\t\t\t\t\tpolicyId: policy.instanceId,\n\t\t\t\t\treason: fileFixError ? \"file-fix-error\" : \"repository-fix-error\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (inventory === undefined || fix) {\n\t\t\t\tinventory = yield* this.loadInventory();\n\t\t\t}\n\t\t\tconst eligible = (inventory ?? []).filter(\n\t\t\t\t(file) => !this.excluded(file, policy),\n\t\t\t);\n\t\t\trepositoryFixError = yield* new PolicyExecutor(\n\t\t\t\tthis.options.root,\n\t\t\t\tfix,\n\t\t\t).repository(policy, eligible, result);\n\t\t}\n\t}\n\n\tprivate *loadInventory(): Operation<readonly string[] | undefined> {\n\t\tconst inventory = this.options.inventory;\n\t\treturn typeof inventory === \"function\" ? yield* call(inventory) : inventory;\n\t}\n\n\tprivate excluded(file: string, policy: IdentifiedPolicy): boolean {\n\t\treturn (\n\t\t\tthis.exclusions.some((regex) => matches(regex, file)) ||\n\t\t\t(this.policyExclusions.get(policy.instanceId) ?? []).some((regex) =>\n\t\t\t\tmatches(regex, file),\n\t\t\t)\n\t\t);\n\t}\n\n\tprivate *runFile(\n\t\tfile: string,\n\t\toutput: PolicyRunResults,\n\t\tcache: ParsedFileCache | undefined,\n\t): Operation<boolean> {\n\t\toutput.perfStats.count++;\n\t\tconst globallyExcluded = this.exclusions.some((regex) =>\n\t\t\tmatches(regex, file),\n\t\t);\n\t\tif (!globallyExcluded) {\n\t\t\toutput.perfStats.processed++;\n\t\t}\n\t\tlet stopped = false;\n\t\tfor (const policy of this.policies) {\n\t\t\tif (policy.scope === \"repository\" || !matches(policy.match, file)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (stopped || this.excluded(file, policy)) {\n\t\t\t\toutput.skipped.push({\n\t\t\t\t\tscope: \"file\",\n\t\t\t\t\tfile,\n\t\t\t\t\tpolicy: policy.name,\n\t\t\t\t\tpolicyId: policy.instanceId,\n\t\t\t\t\treason: stopped ? \"file-fix-error\" : \"excluded\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstopped = yield* new PolicyExecutor(\n\t\t\t\tthis.options.root,\n\t\t\t\tthis.options.fix ?? false,\n\t\t\t).file(file, policy, output, cache);\n\t\t}\n\t\treturn stopped;\n\t}\n}\n\n/** Promise entry point sharing the Effection runner's contracts and cancellation. @public */\nexport async function runPolicies(\n\toptions: PolicyRunnerOptions,\n): Promise<PolicyRunResults> {\n\tconst runner = new PolicyRunner(options);\n\treturn await run(() => runner.run());\n}\n"]}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import { all, call, type Operation, run, spawn } from \"effection\";\nimport { DEFAULT_CONCURRENCY, validateConcurrency } from \"./concurrency.js\";\nimport {\n\tcreateExcludedPolicyFileMap,\n\ttype ExcludedPolicyFileMap,\n\texclusionRegex,\n\tmatchesPath,\n} from \"./context.js\";\nimport { ParsedFileCache } from \"./fileCache.js\";\nimport { identifyPolicyInstances } from \"./makePolicy.js\";\nimport { newPerfStats, type PolicyHandlerPerfStats } from \"./perf.js\";\nimport type {\n\tConfiguredPolicy,\n\tIdentifiedPolicy,\n\tPolicyFinding,\n} from \"./policy.js\";\nimport { PolicyExecutor } from \"./policyExecution.js\";\nimport {\n\tannotateInventory,\n\tinventoryPaths,\n\ttype RepositoryFile,\n\tsnapshotInventory,\n} from \"./repositoryChanges.js\";\n\n/** A finding with runner-owned identity and scope-safe location. @public */\nexport type PolicyRunFinding =\n\t| (Omit<PolicyFinding, \"files\"> & {\n\t\t\tscope: \"file\";\n\t\t\tfile: string;\n\t\t\tfiles?: never;\n\t\t\tpolicy: string;\n\t\t\tpolicyId: string;\n\t })\n\t| (PolicyFinding & {\n\t\t\tscope: \"repository\" | \"changes\";\n\t\t\tfile?: never;\n\t\t\tpolicy: string;\n\t\t\tpolicyId: string;\n\t });\n\n/** An execution failure, separate from ordinary policy findings. @public */\nexport type PolicyExecutionError = {\n\tpolicy: string;\n\tpolicyId: string;\n\tphase: \"handle\" | \"resolve\";\n\tkind: \"contract\" | \"exception\";\n\tmessage: string;\n\tcause: unknown;\n} & (\n\t| { scope: \"file\"; file: string; files?: never }\n\t| { scope: \"repository\" | \"changes\"; files: readonly string[]; file?: never }\n);\n\n/** A unit that was not executed. @public */\nexport type PolicySkippedWork = {\n\tpolicy: string;\n\tpolicyId: string;\n\treason: \"excluded\" | \"file-fix-error\" | \"repository-fix-error\";\n} & (\n\t| { scope: \"file\"; file: string }\n\t| { scope: \"repository\" | \"changes\"; file?: never }\n);\n\n/** Findings, failures, skipped units, and file/per-instance timing statistics. @public */\nexport interface PolicyRunResults {\n\tfindings: PolicyRunFinding[];\n\terrors: PolicyExecutionError[];\n\tskipped: PolicySkippedWork[];\n\tperfStats: PolicyHandlerPerfStats;\n}\n\n/** Full repository inventory, never inferred from a file selection. @public */\nexport type RepositoryInventory =\n\t| readonly (string | RepositoryFile)[]\n\t| (() => Promise<readonly (string | RepositoryFile)[]>);\n\n/** Shared Promise and Effection runner options. @public */\nexport interface PolicyRunnerOptions {\n\tpolicies: readonly ConfiguredPolicy[];\n\troot: string;\n\tinventory?: RepositoryInventory | undefined;\n\t/** Selection limits only file policies. Defaults to inventory when supplied. */\n\tfiles?: readonly string[] | undefined;\n\texclude?: readonly (string | RegExp)[] | undefined;\n\tfix?: boolean | undefined;\n\tconcurrency?: number | undefined;\n\tsignal?: AbortSignal | undefined;\n}\n\n/** Effection runner; cancellation unwinds active generator handlers and resolvers. @public */\nexport class PolicyRunner {\n\tprivate readonly policies: IdentifiedPolicy[];\n\tprivate readonly exclusions: RegExp[];\n\tprivate readonly policyExclusions: ExcludedPolicyFileMap;\n\tprivate readonly concurrency: number;\n\n\tpublic constructor(private readonly options: PolicyRunnerOptions) {\n\t\tthis.policies = identifyPolicyInstances(\n\t\t\toptions.policies.map((policy) => ({\n\t\t\t\t...policy,\n\t\t\t\tconfig:\n\t\t\t\t\tpolicy.config === undefined ? policy.defaultConfig : policy.config,\n\t\t\t})),\n\t\t);\n\t\tthis.exclusions = (options.exclude ?? []).map(exclusionRegex);\n\t\tthis.policyExclusions = createExcludedPolicyFileMap(this.policies);\n\t\tthis.concurrency = validateConcurrency(\n\t\t\toptions.concurrency ?? DEFAULT_CONCURRENCY,\n\t\t);\n\t\tif (\n\t\t\tthis.policies.some(\n\t\t\t\t(policy) => policy.scope === \"repository\" || policy.scope === \"changes\",\n\t\t\t) &&\n\t\t\toptions.inventory === undefined\n\t\t) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"Repository policies require an explicit inventory array or loader; files is only a selection.\",\n\t\t\t);\n\t\t}\n\t\tfor (const policy of this.policies) {\n\t\t\tif (\n\t\t\t\t(policy.scope === \"repository\" || policy.scope === \"changes\") &&\n\t\t\t\tpolicy.resolver !== undefined\n\t\t\t) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t\"Repository policies support inline fixes only; remove resolver.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tpolicy.requiresConfig &&\n\t\t\t\tpolicy.config === undefined &&\n\t\t\t\tpolicy.defaultConfig === undefined\n\t\t\t) {\n\t\t\t\tthrow new TypeError(`Policy '${policy.name}' requires config.`);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic *run(): Operation<PolicyRunResults> {\n\t\tconst signal = this.options.signal;\n\t\tif (signal?.aborted) {\n\t\t\tthrow signal.reason;\n\t\t}\n\t\tconst task = yield* spawn(() => this.runInternal());\n\t\t// halt() is lazy; consuming it initiates teardown. The task retains any teardown error.\n\t\tconst abort = () => {\n\t\t\ttask.halt().catch(() => undefined);\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", abort, { once: true });\n\t\ttry {\n\t\t\tif (signal?.aborted) {\n\t\t\t\tabort();\n\t\t\t}\n\t\t\treturn yield* task;\n\t\t} finally {\n\t\t\tsignal?.removeEventListener(\"abort\", abort);\n\t\t}\n\t}\n\n\tprivate *runInternal(): Operation<PolicyRunResults> {\n\t\tconst result: PolicyRunResults = {\n\t\t\tfindings: [],\n\t\t\terrors: [],\n\t\t\tskipped: [],\n\t\t\tperfStats: newPerfStats(),\n\t\t};\n\t\tconst fix = this.options.fix ?? false;\n\t\tconst cache = fix ? undefined : new ParsedFileCache();\n\t\tconst rawInventory = yield* this.loadInventory();\n\t\tif (\n\t\t\trawInventory.some((entry) => typeof entry === \"string\") &&\n\t\t\tthis.policies.some((policy) => policy.scope === \"changes\")\n\t\t) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"Change-aware policies require inventory entries with Git statuses, not string paths.\",\n\t\t\t);\n\t\t}\n\t\tconst inventory = snapshotInventory(rawInventory);\n\t\tconst files = this.options.files ?? inventoryPaths(inventory);\n\t\tlet fileFixError = false;\n\t\ttry {\n\t\t\tif (fix) {\n\t\t\t\tfor (const file of files) {\n\t\t\t\t\tif (yield* this.runFile(file, result, cache)) {\n\t\t\t\t\t\tfileFixError = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tyield* this.runReadOnlyFiles(files, result, cache);\n\t\t\t}\n\t\t} finally {\n\t\t\tcache?.clear();\n\t\t}\n\t\tyield* this.runRepositories(result, inventory, fileFixError);\n\t\treturn result;\n\t}\n\n\tprivate *runReadOnlyFiles(\n\t\tfiles: readonly string[],\n\t\tresult: PolicyRunResults,\n\t\tcache: ParsedFileCache | undefined,\n\t): Operation<void> {\n\t\tconst entries = files.entries();\n\t\tconst outputs: PolicyRunResults[] = [];\n\t\tconst runner = this;\n\t\tyield* all(\n\t\t\tArray.from(\n\t\t\t\t{ length: Math.min(this.concurrency, files.length) },\n\t\t\t\tfunction* () {\n\t\t\t\t\tfor (\n\t\t\t\t\t\tlet entry = entries.next();\n\t\t\t\t\t\t!entry.done;\n\t\t\t\t\t\tentry = entries.next()\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst [index, file] = entry.value;\n\t\t\t\t\t\tconst local: PolicyRunResults = {\n\t\t\t\t\t\t\tfindings: [],\n\t\t\t\t\t\t\terrors: [],\n\t\t\t\t\t\t\tskipped: [],\n\t\t\t\t\t\t\tperfStats: result.perfStats,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tyield* runner.runFile(file, local, cache);\n\t\t\t\t\t\toutputs[index] = local;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t),\n\t\t);\n\t\tfor (const output of outputs) {\n\t\t\tresult.findings.push(...output.findings);\n\t\t\tresult.errors.push(...output.errors);\n\t\t\tresult.skipped.push(...output.skipped);\n\t\t}\n\t}\n\n\tprivate *runRepositories(\n\t\tresult: PolicyRunResults,\n\t\tinitialInventory: readonly RepositoryFile[],\n\t\tfileFixError: boolean,\n\t): Operation<void> {\n\t\tlet inventory = initialInventory;\n\t\tconst fix = this.options.fix ?? false;\n\t\tlet repositoryFixError = false;\n\t\tfor (const policy of this.policies) {\n\t\t\tif (policy.scope !== \"repository\" && policy.scope !== \"changes\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (fileFixError || repositoryFixError) {\n\t\t\t\tresult.skipped.push({\n\t\t\t\t\tscope: policy.scope,\n\t\t\t\t\tpolicy: policy.name,\n\t\t\t\t\tpolicyId: policy.instanceId,\n\t\t\t\t\treason: fileFixError ? \"file-fix-error\" : \"repository-fix-error\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (fix) {\n\t\t\t\tconst refreshed = yield* this.loadInventory();\n\t\t\t\tinventory = annotateInventory(\n\t\t\t\t\tinventoryPaths(snapshotInventory(refreshed)),\n\t\t\t\t\tinitialInventory,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst eligible = inventory.filter(\n\t\t\t\t(entry) => !this.excluded(entry.path, policy),\n\t\t\t);\n\t\t\trepositoryFixError = yield* new PolicyExecutor(\n\t\t\t\tthis.options.root,\n\t\t\t\tfix,\n\t\t\t).repository(policy, eligible, result);\n\t\t}\n\t}\n\n\tprivate *loadInventory(): Operation<readonly (string | RepositoryFile)[]> {\n\t\tconst inventory = this.options.inventory;\n\t\treturn (\n\t\t\t(typeof inventory === \"function\" ? yield* call(inventory) : inventory) ??\n\t\t\t[]\n\t\t);\n\t}\n\n\tprivate excluded(file: string, policy: IdentifiedPolicy): boolean {\n\t\treturn (\n\t\t\tthis.exclusions.some((regex) => matchesPath(regex, file)) ||\n\t\t\t(this.policyExclusions.get(policy.instanceId) ?? []).some((regex) =>\n\t\t\t\tmatchesPath(regex, file),\n\t\t\t)\n\t\t);\n\t}\n\n\tprivate *runFile(\n\t\tfile: string,\n\t\toutput: PolicyRunResults,\n\t\tcache: ParsedFileCache | undefined,\n\t): Operation<boolean> {\n\t\toutput.perfStats.count++;\n\t\tconst globallyExcluded = this.exclusions.some((regex) =>\n\t\t\tmatchesPath(regex, file),\n\t\t);\n\t\tif (!globallyExcluded) {\n\t\t\toutput.perfStats.processed++;\n\t\t}\n\t\tlet stopped = false;\n\t\tfor (const policy of this.policies) {\n\t\t\tif (policy.scope !== undefined && policy.scope !== \"file\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!matchesPath(policy.match, file)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (stopped || this.excluded(file, policy)) {\n\t\t\t\toutput.skipped.push({\n\t\t\t\t\tscope: \"file\",\n\t\t\t\t\tfile,\n\t\t\t\t\tpolicy: policy.name,\n\t\t\t\t\tpolicyId: policy.instanceId,\n\t\t\t\t\treason: stopped ? \"file-fix-error\" : \"excluded\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstopped = yield* new PolicyExecutor(\n\t\t\t\tthis.options.root,\n\t\t\t\tthis.options.fix ?? false,\n\t\t\t).file(file, policy, output, cache);\n\t\t}\n\t\treturn stopped;\n\t}\n}\n\n/** Promise entry point sharing the Effection runner's contracts and cancellation. @public */\nexport async function runPolicies(\n\toptions: PolicyRunnerOptions,\n): Promise<PolicyRunResults> {\n\tconst runner = new PolicyRunner(options);\n\treturn await run(() => runner.run());\n}\n"]}
@@ -5,6 +5,25 @@
5
5
  "args": {},
6
6
  "description": "Runs the Rust-based repopo-core engine with the Node.js sidecar. Rust handles file matching, scheduling and reporting; the sidecar shares per-policy contracts with 'check'. Repository policies always run with the full repository inventory, including with --stdin. With --fix, repository policies can modify files outside the stdin selection.",
7
7
  "flags": {
8
+ "change-mode": {
9
+ "description": "Change context: pr (default), local, or staged. Does not select file-policy paths.",
10
+ "name": "change-mode",
11
+ "hasDynamicHelp": false,
12
+ "multiple": false,
13
+ "options": [
14
+ "pr",
15
+ "local",
16
+ "staged"
17
+ ],
18
+ "type": "option"
19
+ },
20
+ "change-base": {
21
+ "description": "Override the PR change context base ref.",
22
+ "name": "change-base",
23
+ "hasDynamicHelp": false,
24
+ "multiple": false,
25
+ "type": "option"
26
+ },
8
27
  "concurrency": {
9
28
  "description": "Maximum concurrent files in read-only checks. Fixes remain sequential.",
10
29
  "name": "concurrency",
@@ -133,6 +152,25 @@
133
152
  "allowNo": false,
134
153
  "type": "boolean"
135
154
  },
155
+ "change-mode": {
156
+ "description": "Change context: pr (default), local, or staged. Does not select file-policy paths.",
157
+ "name": "change-mode",
158
+ "hasDynamicHelp": false,
159
+ "multiple": false,
160
+ "options": [
161
+ "pr",
162
+ "local",
163
+ "staged"
164
+ ],
165
+ "type": "option"
166
+ },
167
+ "change-base": {
168
+ "description": "Override the PR change context base ref.",
169
+ "name": "change-base",
170
+ "hasDynamicHelp": false,
171
+ "multiple": false,
172
+ "type": "option"
173
+ },
136
174
  "concurrency": {
137
175
  "description": "Maximum concurrent files in read-only checks. Fixes remain sequential.",
138
176
  "name": "concurrency",
@@ -239,5 +277,5 @@
239
277
  ]
240
278
  }
241
279
  },
242
- "version": "0.13.0"
280
+ "version": "0.14.0"
243
281
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repopo",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Enforce policies on all or some of the files in a git repository.",
5
5
  "keywords": [
6
6
  "policy",