@aspruyt/xfg 3.7.5 → 3.7.7
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.
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +70 -0
- package/dist/cli/settings-command.d.ts +10 -0
- package/dist/cli/settings-command.js +228 -0
- package/dist/cli/sync-command.d.ts +25 -0
- package/dist/cli/sync-command.js +155 -0
- package/dist/cli/types.d.ts +45 -0
- package/dist/cli/types.js +15 -0
- package/dist/cli.js +2 -19
- package/dist/{file-reference-resolver.d.ts → config/file-reference-resolver.d.ts} +1 -1
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.js +12 -0
- package/dist/config/loader.d.ts +9 -0
- package/dist/{config.js → config/loader.js} +3 -24
- package/dist/{config-normalizer.d.ts → config/normalizer.d.ts} +1 -1
- package/dist/{config-normalizer.js → config/normalizer.js} +1 -1
- package/dist/{config.d.ts → config/types.d.ts} +5 -9
- package/dist/config/types.js +16 -0
- package/dist/{config-validator.d.ts → config/validator.d.ts} +5 -5
- package/dist/{config-validator.js → config/validator.js} +60 -372
- package/dist/config/validators/file-validator.d.ts +22 -0
- package/dist/config/validators/file-validator.js +46 -0
- package/dist/config/validators/index.d.ts +3 -0
- package/dist/config/validators/index.js +6 -0
- package/dist/config/validators/repo-settings-validator.d.ts +10 -0
- package/dist/config/validators/repo-settings-validator.js +71 -0
- package/dist/config/validators/ruleset-validator.d.ts +18 -0
- package/dist/config/validators/ruleset-validator.js +201 -0
- package/dist/index.d.ts +3 -66
- package/dist/index.js +3 -474
- package/dist/output/index.d.ts +4 -0
- package/dist/output/index.js +8 -0
- package/dist/{summary-utils.d.ts → output/summary-utils.d.ts} +3 -3
- package/dist/settings/index.d.ts +3 -0
- package/dist/settings/index.js +6 -0
- package/dist/{repo-settings-diff.d.ts → settings/repo-settings/diff.d.ts} +2 -2
- package/dist/{repo-settings-plan-formatter.d.ts → settings/repo-settings/formatter.d.ts} +1 -1
- package/dist/{strategies → settings/repo-settings}/github-repo-settings-strategy.d.ts +4 -4
- package/dist/{strategies → settings/repo-settings}/github-repo-settings-strategy.js +3 -3
- package/dist/settings/repo-settings/index.d.ts +5 -0
- package/dist/settings/repo-settings/index.js +10 -0
- package/dist/{repo-settings-processor.d.ts → settings/repo-settings/processor.d.ts} +4 -4
- package/dist/{repo-settings-processor.js → settings/repo-settings/processor.js} +6 -6
- package/dist/{strategies/repo-settings-strategy.d.ts → settings/repo-settings/types.d.ts} +2 -2
- package/dist/{resource-converters.d.ts → settings/resource-converters.d.ts} +4 -4
- package/dist/settings/rulesets/diff-algorithm.d.ts +18 -0
- package/dist/settings/rulesets/diff-algorithm.js +166 -0
- package/dist/{ruleset-diff.d.ts → settings/rulesets/diff.d.ts} +2 -2
- package/dist/{ruleset-diff.js → settings/rulesets/diff.js} +1 -1
- package/dist/{ruleset-plan-formatter.d.ts → settings/rulesets/formatter.d.ts} +4 -12
- package/dist/{ruleset-plan-formatter.js → settings/rulesets/formatter.js} +5 -166
- package/dist/{strategies → settings/rulesets}/github-ruleset-strategy.d.ts +4 -4
- package/dist/{strategies → settings/rulesets}/github-ruleset-strategy.js +3 -3
- package/dist/settings/rulesets/index.d.ts +6 -0
- package/dist/settings/rulesets/index.js +10 -0
- package/dist/{ruleset-processor.d.ts → settings/rulesets/processor.d.ts} +4 -4
- package/dist/{ruleset-processor.js → settings/rulesets/processor.js} +6 -6
- package/dist/{strategies/ruleset-strategy.d.ts → settings/rulesets/types.d.ts} +2 -2
- package/dist/{command-executor.d.ts → shared/command-executor.d.ts} +10 -2
- package/dist/{command-executor.js → shared/command-executor.js} +2 -1
- package/dist/shared/index.d.ts +8 -0
- package/dist/shared/index.js +16 -0
- package/dist/{logger.d.ts → shared/logger.d.ts} +1 -1
- package/dist/{logger.js → shared/logger.js} +1 -1
- package/dist/sync/auth-options-builder.d.ts +12 -0
- package/dist/sync/auth-options-builder.js +54 -0
- package/dist/sync/branch-manager.d.ts +7 -0
- package/dist/sync/branch-manager.js +36 -0
- package/dist/sync/commit-message.d.ts +11 -0
- package/dist/sync/commit-message.js +27 -0
- package/dist/sync/commit-push-manager.d.ts +8 -0
- package/dist/sync/commit-push-manager.js +71 -0
- package/dist/sync/file-sync-orchestrator.d.ts +11 -0
- package/dist/sync/file-sync-orchestrator.js +58 -0
- package/dist/sync/file-writer.d.ts +18 -0
- package/dist/sync/file-writer.js +101 -0
- package/dist/sync/index.d.ts +14 -0
- package/dist/sync/index.js +17 -0
- package/dist/sync/manifest-manager.d.ts +10 -0
- package/dist/sync/manifest-manager.js +64 -0
- package/dist/sync/pr-merge-handler.d.ts +11 -0
- package/dist/sync/pr-merge-handler.js +62 -0
- package/dist/sync/repository-processor.d.ts +30 -0
- package/dist/sync/repository-processor.js +278 -0
- package/dist/sync/repository-session.d.ts +9 -0
- package/dist/sync/repository-session.js +35 -0
- package/dist/sync/types.d.ts +296 -0
- package/dist/{xfg-template.d.ts → sync/xfg-template.d.ts} +2 -2
- package/dist/{authenticated-git-ops.js → vcs/authenticated-git-ops.js} +3 -3
- package/dist/{strategies → vcs}/azure-pr-strategy.d.ts +2 -2
- package/dist/{strategies → vcs}/azure-pr-strategy.js +5 -5
- package/dist/{strategies → vcs}/commit-strategy-selector.d.ts +3 -3
- package/dist/{strategies → vcs}/commit-strategy-selector.js +1 -1
- package/dist/{strategies → vcs}/git-commit-strategy.d.ts +2 -2
- package/dist/{strategies → vcs}/git-commit-strategy.js +3 -3
- package/dist/{git-ops.d.ts → vcs/git-ops.d.ts} +1 -1
- package/dist/{git-ops.js → vcs/git-ops.js} +4 -4
- package/dist/{github-app-token-manager.d.ts → vcs/github-app-token-manager.d.ts} +1 -1
- package/dist/{github-app-token-manager.js → vcs/github-app-token-manager.js} +1 -1
- package/dist/{strategies → vcs}/github-pr-strategy.d.ts +2 -2
- package/dist/{strategies → vcs}/github-pr-strategy.js +30 -33
- package/dist/{strategies → vcs}/gitlab-pr-strategy.d.ts +2 -2
- package/dist/{strategies → vcs}/gitlab-pr-strategy.js +5 -5
- package/dist/{strategies → vcs}/graphql-commit-strategy.d.ts +2 -2
- package/dist/{strategies → vcs}/graphql-commit-strategy.js +3 -3
- package/dist/vcs/index.d.ts +16 -0
- package/dist/{strategies → vcs}/index.js +15 -10
- package/dist/{pr-creator.d.ts → vcs/pr-creator.d.ts} +4 -4
- package/dist/{pr-creator.js → vcs/pr-creator.js} +3 -3
- package/dist/vcs/pr-strategy.d.ts +41 -0
- package/dist/{strategies → vcs}/pr-strategy.js +1 -1
- package/dist/{strategies/pr-strategy.d.ts → vcs/types.d.ts} +32 -35
- package/dist/vcs/types.js +1 -0
- package/package.json +2 -2
- package/dist/repository-processor.d.ts +0 -79
- package/dist/repository-processor.js +0 -659
- package/dist/strategies/commit-strategy.d.ts +0 -36
- package/dist/strategies/index.d.ts +0 -18
- /package/dist/{file-reference-resolver.js → config/file-reference-resolver.js} +0 -0
- /package/dist/{config-formatter.d.ts → config/formatter.d.ts} +0 -0
- /package/dist/{config-formatter.js → config/formatter.js} +0 -0
- /package/dist/{merge.d.ts → config/merge.d.ts} +0 -0
- /package/dist/{merge.js → config/merge.js} +0 -0
- /package/dist/{github-summary.d.ts → output/github-summary.d.ts} +0 -0
- /package/dist/{github-summary.js → output/github-summary.js} +0 -0
- /package/dist/{plan-formatter.d.ts → output/plan-formatter.d.ts} +0 -0
- /package/dist/{plan-formatter.js → output/plan-formatter.js} +0 -0
- /package/dist/{plan-summary.d.ts → output/plan-summary.d.ts} +0 -0
- /package/dist/{plan-summary.js → output/plan-summary.js} +0 -0
- /package/dist/{summary-utils.js → output/summary-utils.js} +0 -0
- /package/dist/{repo-settings-diff.js → settings/repo-settings/diff.js} +0 -0
- /package/dist/{repo-settings-plan-formatter.js → settings/repo-settings/formatter.js} +0 -0
- /package/dist/{strategies/repo-settings-strategy.js → settings/repo-settings/types.js} +0 -0
- /package/dist/{resource-converters.js → settings/resource-converters.js} +0 -0
- /package/dist/{strategies/commit-strategy.js → settings/rulesets/types.js} +0 -0
- /package/dist/{env.d.ts → shared/env.d.ts} +0 -0
- /package/dist/{env.js → shared/env.js} +0 -0
- /package/dist/{repo-detector.d.ts → shared/repo-detector.d.ts} +0 -0
- /package/dist/{repo-detector.js → shared/repo-detector.js} +0 -0
- /package/dist/{retry-utils.d.ts → shared/retry-utils.d.ts} +0 -0
- /package/dist/{retry-utils.js → shared/retry-utils.js} +0 -0
- /package/dist/{sanitize-utils.d.ts → shared/sanitize-utils.d.ts} +0 -0
- /package/dist/{sanitize-utils.js → shared/sanitize-utils.js} +0 -0
- /package/dist/{shell-utils.d.ts → shared/shell-utils.d.ts} +0 -0
- /package/dist/{shell-utils.js → shared/shell-utils.js} +0 -0
- /package/dist/{workspace-utils.d.ts → shared/workspace-utils.d.ts} +0 -0
- /package/dist/{workspace-utils.js → shared/workspace-utils.js} +0 -0
- /package/dist/{diff-utils.d.ts → sync/diff-utils.d.ts} +0 -0
- /package/dist/{diff-utils.js → sync/diff-utils.js} +0 -0
- /package/dist/{manifest.d.ts → sync/manifest.d.ts} +0 -0
- /package/dist/{manifest.js → sync/manifest.js} +0 -0
- /package/dist/{strategies/ruleset-strategy.js → sync/types.js} +0 -0
- /package/dist/{xfg-template.js → sync/xfg-template.js} +0 -0
- /package/dist/{authenticated-git-ops.d.ts → vcs/authenticated-git-ops.d.ts} +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// src/settings/rulesets/diff-algorithm.ts
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// Helpers
|
|
4
|
+
// =============================================================================
|
|
5
|
+
export function isObject(val) {
|
|
6
|
+
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
7
|
+
}
|
|
8
|
+
export function deepEqual(a, b) {
|
|
9
|
+
if (a === b)
|
|
10
|
+
return true;
|
|
11
|
+
if (a === null || b === null || a === undefined || b === undefined)
|
|
12
|
+
return a === b;
|
|
13
|
+
if (typeof a !== typeof b)
|
|
14
|
+
return false;
|
|
15
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
16
|
+
if (a.length !== b.length)
|
|
17
|
+
return false;
|
|
18
|
+
return a.every((val, i) => deepEqual(val, b[i]));
|
|
19
|
+
}
|
|
20
|
+
if (isObject(a) && isObject(b)) {
|
|
21
|
+
const keysA = Object.keys(a);
|
|
22
|
+
const keysB = Object.keys(b);
|
|
23
|
+
if (keysA.length !== keysB.length)
|
|
24
|
+
return false;
|
|
25
|
+
return keysA.every((key) => deepEqual(a[key], b[key]));
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
export function isArrayOfObjects(arr) {
|
|
30
|
+
return arr.length > 0 && arr.every((item) => isObject(item));
|
|
31
|
+
}
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Property Diff Algorithm
|
|
34
|
+
// =============================================================================
|
|
35
|
+
/**
|
|
36
|
+
* Recursively compute property-level diffs between two objects.
|
|
37
|
+
*/
|
|
38
|
+
export function computePropertyDiffs(current, desired, parentPath = []) {
|
|
39
|
+
const diffs = [];
|
|
40
|
+
const allKeys = new Set([...Object.keys(current), ...Object.keys(desired)]);
|
|
41
|
+
for (const key of allKeys) {
|
|
42
|
+
const path = [...parentPath, key];
|
|
43
|
+
const currentVal = current[key];
|
|
44
|
+
const desiredVal = desired[key];
|
|
45
|
+
if (!(key in current)) {
|
|
46
|
+
// Added property
|
|
47
|
+
diffs.push({ path, action: "add", newValue: desiredVal });
|
|
48
|
+
}
|
|
49
|
+
else if (!(key in desired)) {
|
|
50
|
+
// Removed property
|
|
51
|
+
diffs.push({ path, action: "remove", oldValue: currentVal });
|
|
52
|
+
}
|
|
53
|
+
else if (!deepEqual(currentVal, desiredVal)) {
|
|
54
|
+
// Changed property
|
|
55
|
+
if (isObject(currentVal) && isObject(desiredVal)) {
|
|
56
|
+
// Recurse into nested objects
|
|
57
|
+
diffs.push(...computePropertyDiffs(currentVal, desiredVal, path));
|
|
58
|
+
}
|
|
59
|
+
else if (Array.isArray(currentVal) &&
|
|
60
|
+
Array.isArray(desiredVal) &&
|
|
61
|
+
isArrayOfObjects(currentVal) &&
|
|
62
|
+
isArrayOfObjects(desiredVal)) {
|
|
63
|
+
// Recurse into arrays of objects
|
|
64
|
+
diffs.push(...diffObjectArrays(currentVal, desiredVal, path));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
diffs.push({
|
|
68
|
+
path,
|
|
69
|
+
action: "change",
|
|
70
|
+
oldValue: currentVal,
|
|
71
|
+
newValue: desiredVal,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Unchanged properties are not included
|
|
76
|
+
}
|
|
77
|
+
return diffs;
|
|
78
|
+
}
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Array Diffing
|
|
81
|
+
// =============================================================================
|
|
82
|
+
/**
|
|
83
|
+
* Diff two arrays of objects by matching items on `type` field (or by index).
|
|
84
|
+
*/
|
|
85
|
+
export function diffObjectArrays(currentArr, desiredArr, parentPath) {
|
|
86
|
+
const diffs = [];
|
|
87
|
+
const hasType = desiredArr.every((item) => isObject(item) && "type" in item);
|
|
88
|
+
if (hasType) {
|
|
89
|
+
// Match by type field
|
|
90
|
+
const currentByType = new Map();
|
|
91
|
+
for (let i = 0; i < currentArr.length; i++) {
|
|
92
|
+
const item = currentArr[i];
|
|
93
|
+
const type = item.type;
|
|
94
|
+
if (type)
|
|
95
|
+
currentByType.set(type, { item, index: i });
|
|
96
|
+
}
|
|
97
|
+
const matchedTypes = new Set();
|
|
98
|
+
for (let i = 0; i < desiredArr.length; i++) {
|
|
99
|
+
const desiredItem = desiredArr[i];
|
|
100
|
+
const type = desiredItem.type;
|
|
101
|
+
const label = `[${i}] (${type})`;
|
|
102
|
+
const currentEntry = currentByType.get(type);
|
|
103
|
+
if (currentEntry) {
|
|
104
|
+
matchedTypes.add(type);
|
|
105
|
+
// Recurse into matched pair
|
|
106
|
+
const itemDiffs = computePropertyDiffs(currentEntry.item, desiredItem, [
|
|
107
|
+
...parentPath,
|
|
108
|
+
label,
|
|
109
|
+
]);
|
|
110
|
+
diffs.push(...itemDiffs);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// New item in desired
|
|
114
|
+
diffs.push({
|
|
115
|
+
path: [...parentPath, label],
|
|
116
|
+
action: "add",
|
|
117
|
+
newValue: desiredItem,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Items in current but not in desired
|
|
122
|
+
for (const [type, entry] of currentByType) {
|
|
123
|
+
if (!matchedTypes.has(type)) {
|
|
124
|
+
diffs.push({
|
|
125
|
+
path: [...parentPath, `[${entry.index}] (${type})`],
|
|
126
|
+
action: "remove",
|
|
127
|
+
oldValue: entry.item,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Fallback: match by index
|
|
134
|
+
const maxLen = Math.max(currentArr.length, desiredArr.length);
|
|
135
|
+
for (let i = 0; i < maxLen; i++) {
|
|
136
|
+
const label = `[${i}]`;
|
|
137
|
+
if (i >= currentArr.length) {
|
|
138
|
+
diffs.push({
|
|
139
|
+
path: [...parentPath, label],
|
|
140
|
+
action: "add",
|
|
141
|
+
newValue: desiredArr[i],
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else if (i >= desiredArr.length) {
|
|
145
|
+
diffs.push({
|
|
146
|
+
path: [...parentPath, label],
|
|
147
|
+
action: "remove",
|
|
148
|
+
oldValue: currentArr[i],
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else if (isObject(currentArr[i]) && isObject(desiredArr[i])) {
|
|
152
|
+
const itemDiffs = computePropertyDiffs(currentArr[i], desiredArr[i], [...parentPath, label]);
|
|
153
|
+
diffs.push(...itemDiffs);
|
|
154
|
+
}
|
|
155
|
+
else if (!deepEqual(currentArr[i], desiredArr[i])) {
|
|
156
|
+
diffs.push({
|
|
157
|
+
path: [...parentPath, label],
|
|
158
|
+
action: "change",
|
|
159
|
+
oldValue: currentArr[i],
|
|
160
|
+
newValue: desiredArr[i],
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return diffs;
|
|
166
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Ruleset } from "
|
|
2
|
-
import type { GitHubRuleset } from "./
|
|
1
|
+
import { type Ruleset } from "../../config/index.js";
|
|
2
|
+
import type { GitHubRuleset } from "./github-ruleset-strategy.js";
|
|
3
3
|
export type RulesetAction = "create" | "update" | "delete" | "unchanged";
|
|
4
4
|
export interface RulesetChange {
|
|
5
5
|
action: RulesetAction;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RULESET_COMPARABLE_FIELDS } from "
|
|
1
|
+
import { RULESET_COMPARABLE_FIELDS } from "../../config/index.js";
|
|
2
2
|
// =============================================================================
|
|
3
3
|
// Normalization (for comparison)
|
|
4
4
|
// =============================================================================
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { type RulesetChange, type RulesetAction } from "./
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
action: DiffAction;
|
|
6
|
-
oldValue?: unknown;
|
|
7
|
-
newValue?: unknown;
|
|
8
|
-
}
|
|
1
|
+
import { type RulesetChange, type RulesetAction } from "./diff.js";
|
|
2
|
+
import { type PropertyDiff } from "./index.js";
|
|
3
|
+
export type { DiffAction, PropertyDiff } from "./index.js";
|
|
4
|
+
export { computePropertyDiffs } from "./index.js";
|
|
9
5
|
export interface RulesetPlanEntry {
|
|
10
6
|
name: string;
|
|
11
7
|
action: RulesetAction;
|
|
@@ -24,10 +20,6 @@ export interface RulesetPlanResult {
|
|
|
24
20
|
unchanged: number;
|
|
25
21
|
entries: RulesetPlanEntry[];
|
|
26
22
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Recursively compute property-level diffs between two objects.
|
|
29
|
-
*/
|
|
30
|
-
export declare function computePropertyDiffs(current: Record<string, unknown>, desired: Record<string, unknown>, parentPath?: string[]): PropertyDiff[];
|
|
31
23
|
/**
|
|
32
24
|
* Format property diffs as an indented tree structure.
|
|
33
25
|
*/
|
|
@@ -1,168 +1,7 @@
|
|
|
1
|
-
// src/ruleset-plan-formatter.ts
|
|
2
1
|
import chalk from "chalk";
|
|
3
|
-
import { projectToDesiredShape, normalizeRuleset, } from "./
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// =============================================================================
|
|
7
|
-
/**
|
|
8
|
-
* Recursively compute property-level diffs between two objects.
|
|
9
|
-
*/
|
|
10
|
-
export function computePropertyDiffs(current, desired, parentPath = []) {
|
|
11
|
-
const diffs = [];
|
|
12
|
-
const allKeys = new Set([...Object.keys(current), ...Object.keys(desired)]);
|
|
13
|
-
for (const key of allKeys) {
|
|
14
|
-
const path = [...parentPath, key];
|
|
15
|
-
const currentVal = current[key];
|
|
16
|
-
const desiredVal = desired[key];
|
|
17
|
-
if (!(key in current)) {
|
|
18
|
-
// Added property
|
|
19
|
-
diffs.push({ path, action: "add", newValue: desiredVal });
|
|
20
|
-
}
|
|
21
|
-
else if (!(key in desired)) {
|
|
22
|
-
// Removed property
|
|
23
|
-
diffs.push({ path, action: "remove", oldValue: currentVal });
|
|
24
|
-
}
|
|
25
|
-
else if (!deepEqual(currentVal, desiredVal)) {
|
|
26
|
-
// Changed property
|
|
27
|
-
if (isObject(currentVal) && isObject(desiredVal)) {
|
|
28
|
-
// Recurse into nested objects
|
|
29
|
-
diffs.push(...computePropertyDiffs(currentVal, desiredVal, path));
|
|
30
|
-
}
|
|
31
|
-
else if (Array.isArray(currentVal) &&
|
|
32
|
-
Array.isArray(desiredVal) &&
|
|
33
|
-
isArrayOfObjects(currentVal) &&
|
|
34
|
-
isArrayOfObjects(desiredVal)) {
|
|
35
|
-
// Recurse into arrays of objects
|
|
36
|
-
diffs.push(...diffObjectArrays(currentVal, desiredVal, path));
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
diffs.push({
|
|
40
|
-
path,
|
|
41
|
-
action: "change",
|
|
42
|
-
oldValue: currentVal,
|
|
43
|
-
newValue: desiredVal,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// Unchanged properties are not included
|
|
48
|
-
}
|
|
49
|
-
return diffs;
|
|
50
|
-
}
|
|
51
|
-
// =============================================================================
|
|
52
|
-
// Helpers
|
|
53
|
-
// =============================================================================
|
|
54
|
-
function isObject(val) {
|
|
55
|
-
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
56
|
-
}
|
|
57
|
-
function deepEqual(a, b) {
|
|
58
|
-
if (a === b)
|
|
59
|
-
return true;
|
|
60
|
-
if (a === null || b === null || a === undefined || b === undefined)
|
|
61
|
-
return a === b;
|
|
62
|
-
if (typeof a !== typeof b)
|
|
63
|
-
return false;
|
|
64
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
65
|
-
if (a.length !== b.length)
|
|
66
|
-
return false;
|
|
67
|
-
return a.every((val, i) => deepEqual(val, b[i]));
|
|
68
|
-
}
|
|
69
|
-
if (isObject(a) && isObject(b)) {
|
|
70
|
-
const keysA = Object.keys(a);
|
|
71
|
-
const keysB = Object.keys(b);
|
|
72
|
-
if (keysA.length !== keysB.length)
|
|
73
|
-
return false;
|
|
74
|
-
return keysA.every((key) => deepEqual(a[key], b[key]));
|
|
75
|
-
}
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
function isArrayOfObjects(arr) {
|
|
79
|
-
return arr.length > 0 && arr.every((item) => isObject(item));
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Diff two arrays of objects by matching items on `type` field (or by index).
|
|
83
|
-
*/
|
|
84
|
-
function diffObjectArrays(currentArr, desiredArr, parentPath) {
|
|
85
|
-
const diffs = [];
|
|
86
|
-
const hasType = desiredArr.every((item) => isObject(item) && "type" in item);
|
|
87
|
-
if (hasType) {
|
|
88
|
-
// Match by type field
|
|
89
|
-
const currentByType = new Map();
|
|
90
|
-
for (let i = 0; i < currentArr.length; i++) {
|
|
91
|
-
const item = currentArr[i];
|
|
92
|
-
const type = item.type;
|
|
93
|
-
if (type)
|
|
94
|
-
currentByType.set(type, { item, index: i });
|
|
95
|
-
}
|
|
96
|
-
const matchedTypes = new Set();
|
|
97
|
-
for (let i = 0; i < desiredArr.length; i++) {
|
|
98
|
-
const desiredItem = desiredArr[i];
|
|
99
|
-
const type = desiredItem.type;
|
|
100
|
-
const label = `[${i}] (${type})`;
|
|
101
|
-
const currentEntry = currentByType.get(type);
|
|
102
|
-
if (currentEntry) {
|
|
103
|
-
matchedTypes.add(type);
|
|
104
|
-
// Recurse into matched pair
|
|
105
|
-
const itemDiffs = computePropertyDiffs(currentEntry.item, desiredItem, [
|
|
106
|
-
...parentPath,
|
|
107
|
-
label,
|
|
108
|
-
]);
|
|
109
|
-
diffs.push(...itemDiffs);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
// New item in desired
|
|
113
|
-
diffs.push({
|
|
114
|
-
path: [...parentPath, label],
|
|
115
|
-
action: "add",
|
|
116
|
-
newValue: desiredItem,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// Items in current but not in desired
|
|
121
|
-
for (const [type, entry] of currentByType) {
|
|
122
|
-
if (!matchedTypes.has(type)) {
|
|
123
|
-
diffs.push({
|
|
124
|
-
path: [...parentPath, `[${entry.index}] (${type})`],
|
|
125
|
-
action: "remove",
|
|
126
|
-
oldValue: entry.item,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
// Fallback: match by index
|
|
133
|
-
const maxLen = Math.max(currentArr.length, desiredArr.length);
|
|
134
|
-
for (let i = 0; i < maxLen; i++) {
|
|
135
|
-
const label = `[${i}]`;
|
|
136
|
-
if (i >= currentArr.length) {
|
|
137
|
-
diffs.push({
|
|
138
|
-
path: [...parentPath, label],
|
|
139
|
-
action: "add",
|
|
140
|
-
newValue: desiredArr[i],
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
else if (i >= desiredArr.length) {
|
|
144
|
-
diffs.push({
|
|
145
|
-
path: [...parentPath, label],
|
|
146
|
-
action: "remove",
|
|
147
|
-
oldValue: currentArr[i],
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
else if (isObject(currentArr[i]) && isObject(desiredArr[i])) {
|
|
151
|
-
const itemDiffs = computePropertyDiffs(currentArr[i], desiredArr[i], [...parentPath, label]);
|
|
152
|
-
diffs.push(...itemDiffs);
|
|
153
|
-
}
|
|
154
|
-
else if (!deepEqual(currentArr[i], desiredArr[i])) {
|
|
155
|
-
diffs.push({
|
|
156
|
-
path: [...parentPath, label],
|
|
157
|
-
action: "change",
|
|
158
|
-
oldValue: currentArr[i],
|
|
159
|
-
newValue: desiredArr[i],
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return diffs;
|
|
165
|
-
}
|
|
2
|
+
import { projectToDesiredShape, normalizeRuleset, } from "./diff.js";
|
|
3
|
+
import { computePropertyDiffs, isObject, } from "./index.js";
|
|
4
|
+
export { computePropertyDiffs } from "./index.js";
|
|
166
5
|
/**
|
|
167
6
|
* Build a tree structure from flat property diffs.
|
|
168
7
|
*/
|
|
@@ -372,10 +211,10 @@ function formatFullConfig(ruleset, indent = 2) {
|
|
|
372
211
|
lines.push(style.color(`${pad}+ ${key}:`));
|
|
373
212
|
for (const item of value) {
|
|
374
213
|
if (typeof item === "object" && item !== null) {
|
|
375
|
-
lines.push(style.color(`${pad}
|
|
214
|
+
lines.push(style.color(`${pad} + ${JSON.stringify(item)}`));
|
|
376
215
|
}
|
|
377
216
|
else {
|
|
378
|
-
lines.push(style.color(`${pad}
|
|
217
|
+
lines.push(style.color(`${pad} + ${formatValue(item)}`));
|
|
379
218
|
}
|
|
380
219
|
}
|
|
381
220
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ICommandExecutor } from "
|
|
2
|
-
import { RepoInfo } from "
|
|
3
|
-
import type { Ruleset } from "
|
|
4
|
-
import type { IRulesetStrategy } from "./
|
|
1
|
+
import { ICommandExecutor } from "../../shared/command-executor.js";
|
|
2
|
+
import { RepoInfo } from "../../shared/repo-detector.js";
|
|
3
|
+
import type { Ruleset } from "../../config/index.js";
|
|
4
|
+
import type { IRulesetStrategy } from "./types.js";
|
|
5
5
|
/**
|
|
6
6
|
* GitHub Ruleset response from API (snake_case).
|
|
7
7
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defaultExecutor } from "
|
|
2
|
-
import { isGitHubRepo } from "
|
|
3
|
-
import { escapeShellArg } from "
|
|
1
|
+
import { defaultExecutor, } from "../../shared/command-executor.js";
|
|
2
|
+
import { isGitHubRepo, } from "../../shared/repo-detector.js";
|
|
3
|
+
import { escapeShellArg } from "../../shared/shell-utils.js";
|
|
4
4
|
// =============================================================================
|
|
5
5
|
// Conversion Functions
|
|
6
6
|
// =============================================================================
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { IRulesetStrategy } from "./types.js";
|
|
2
|
+
export { computePropertyDiffs, diffObjectArrays, deepEqual, isObject, isArrayOfObjects, type DiffAction, type PropertyDiff, } from "./diff-algorithm.js";
|
|
3
|
+
export { RulesetProcessor, type IRulesetProcessor, type RulesetProcessorOptions, type RulesetProcessorResult, } from "./processor.js";
|
|
4
|
+
export { diffRulesets, normalizeRuleset, projectToDesiredShape, type RulesetAction, type RulesetChange, } from "./diff.js";
|
|
5
|
+
export { formatRulesetPlan, type RulesetPlanResult, type RulesetPlanEntry, } from "./formatter.js";
|
|
6
|
+
export { GitHubRulesetStrategy, configToGitHub, type GitHubRuleset, type GitHubBypassActor, type GitHubRulesetConditions, type GitHubRule, type RulesetStrategyOptions, } from "./github-ruleset-strategy.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Diff algorithm - property-level diffing for ruleset comparisons
|
|
2
|
+
export { computePropertyDiffs, diffObjectArrays, deepEqual, isObject, isArrayOfObjects, } from "./diff-algorithm.js";
|
|
3
|
+
// Ruleset processor
|
|
4
|
+
export { RulesetProcessor, } from "./processor.js";
|
|
5
|
+
// Ruleset diff
|
|
6
|
+
export { diffRulesets, normalizeRuleset, projectToDesiredShape, } from "./diff.js";
|
|
7
|
+
// Ruleset formatter
|
|
8
|
+
export { formatRulesetPlan, } from "./formatter.js";
|
|
9
|
+
// Ruleset strategies
|
|
10
|
+
export { GitHubRulesetStrategy, configToGitHub, } from "./github-ruleset-strategy.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { RepoConfig } from "
|
|
2
|
-
import type { RepoInfo } from "
|
|
3
|
-
import { GitHubRulesetStrategy } from "./
|
|
4
|
-
import { RulesetPlanResult } from "./
|
|
1
|
+
import type { RepoConfig } from "../../config/index.js";
|
|
2
|
+
import type { RepoInfo } from "../../shared/repo-detector.js";
|
|
3
|
+
import { GitHubRulesetStrategy } from "./github-ruleset-strategy.js";
|
|
4
|
+
import { RulesetPlanResult } from "./formatter.js";
|
|
5
5
|
export interface IRulesetProcessor {
|
|
6
6
|
process(repoConfig: RepoConfig, repoInfo: RepoInfo, options: RulesetProcessorOptions): Promise<RulesetProcessorResult>;
|
|
7
7
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { isGitHubRepo, getRepoDisplayName } from "
|
|
2
|
-
import { GitHubRulesetStrategy, } from "./
|
|
3
|
-
import { diffRulesets } from "./
|
|
4
|
-
import { formatRulesetPlan
|
|
5
|
-
import { hasGitHubAppCredentials } from "
|
|
6
|
-
import { GitHubAppTokenManager } from "
|
|
1
|
+
import { isGitHubRepo, getRepoDisplayName, } from "../../shared/repo-detector.js";
|
|
2
|
+
import { GitHubRulesetStrategy, } from "./github-ruleset-strategy.js";
|
|
3
|
+
import { diffRulesets } from "./diff.js";
|
|
4
|
+
import { formatRulesetPlan } from "./formatter.js";
|
|
5
|
+
import { hasGitHubAppCredentials } from "../../vcs/index.js";
|
|
6
|
+
import { GitHubAppTokenManager } from "../../vcs/github-app-token-manager.js";
|
|
7
7
|
// =============================================================================
|
|
8
8
|
// Processor Implementation
|
|
9
9
|
// =============================================================================
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { RepoInfo } from "
|
|
2
|
-
import type { Ruleset } from "
|
|
1
|
+
import type { RepoInfo } from "../../shared/repo-detector.js";
|
|
2
|
+
import type { Ruleset } from "../../config/index.js";
|
|
3
3
|
import type { GitHubRuleset, RulesetStrategyOptions } from "./github-ruleset-strategy.js";
|
|
4
4
|
export interface IRulesetStrategy {
|
|
5
5
|
list(repoInfo: RepoInfo, options?: RulesetStrategyOptions): Promise<GitHubRuleset[]>;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for command execution.
|
|
3
|
+
*/
|
|
4
|
+
export interface ExecOptions {
|
|
5
|
+
/** Additional environment variables to set for the command */
|
|
6
|
+
env?: Record<string, string>;
|
|
7
|
+
}
|
|
1
8
|
/**
|
|
2
9
|
* Interface for executing shell commands.
|
|
3
10
|
* Enables dependency injection for testing and alternative implementations.
|
|
@@ -7,17 +14,18 @@ export interface ICommandExecutor {
|
|
|
7
14
|
* Execute a shell command and return the output.
|
|
8
15
|
* @param command The command to execute
|
|
9
16
|
* @param cwd The working directory for the command
|
|
17
|
+
* @param options Optional execution options (env vars, etc.)
|
|
10
18
|
* @returns Promise resolving to the trimmed stdout output
|
|
11
19
|
* @throws Error if the command fails
|
|
12
20
|
*/
|
|
13
|
-
exec(command: string, cwd: string): Promise<string>;
|
|
21
|
+
exec(command: string, cwd: string, options?: ExecOptions): Promise<string>;
|
|
14
22
|
}
|
|
15
23
|
/**
|
|
16
24
|
* Default implementation that uses Node.js child_process.execSync.
|
|
17
25
|
* Note: Commands are escaped using escapeShellArg before being passed here.
|
|
18
26
|
*/
|
|
19
27
|
export declare class ShellCommandExecutor implements ICommandExecutor {
|
|
20
|
-
exec(command: string, cwd: string): Promise<string>;
|
|
28
|
+
exec(command: string, cwd: string, options?: ExecOptions): Promise<string>;
|
|
21
29
|
}
|
|
22
30
|
/**
|
|
23
31
|
* Default executor instance for production use.
|
|
@@ -5,12 +5,13 @@ import { sanitizeCredentials } from "./sanitize-utils.js";
|
|
|
5
5
|
* Note: Commands are escaped using escapeShellArg before being passed here.
|
|
6
6
|
*/
|
|
7
7
|
export class ShellCommandExecutor {
|
|
8
|
-
async exec(command, cwd) {
|
|
8
|
+
async exec(command, cwd, options) {
|
|
9
9
|
try {
|
|
10
10
|
return execSync(command, {
|
|
11
11
|
cwd,
|
|
12
12
|
encoding: "utf-8",
|
|
13
13
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14
|
+
env: options?.env ? { ...process.env, ...options.env } : undefined,
|
|
14
15
|
}).trim();
|
|
15
16
|
}
|
|
16
17
|
catch (error) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Logger, logger, type ILogger, type LoggerStats } from "./logger.js";
|
|
2
|
+
export { withRetry, isPermanentError, isTransientError, promisify, DEFAULT_PERMANENT_ERROR_PATTERNS, DEFAULT_TRANSIENT_ERROR_PATTERNS, AbortError, type RetryOptions, } from "./retry-utils.js";
|
|
3
|
+
export { ShellCommandExecutor, defaultExecutor, type ICommandExecutor, } from "./command-executor.js";
|
|
4
|
+
export { escapeShellArg } from "./shell-utils.js";
|
|
5
|
+
export { sanitizeCredentials } from "./sanitize-utils.js";
|
|
6
|
+
export { interpolateEnvVars, interpolateEnvVarsInString, interpolateEnvVarsInLines, interpolateContent, type EnvInterpolationOptions, } from "./env.js";
|
|
7
|
+
export { generateWorkspaceName } from "./workspace-utils.js";
|
|
8
|
+
export { detectRepoType, parseGitUrl, getRepoDisplayName, isGitHubRepo, isAzureDevOpsRepo, isGitLabRepo, type RepoType, type RepoInfo, type GitHubRepoInfo, type AzureDevOpsRepoInfo, type GitLabRepoInfo, type RepoDetectorContext, } from "./repo-detector.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Logging
|
|
2
|
+
export { Logger, logger } from "./logger.js";
|
|
3
|
+
// Retry utilities
|
|
4
|
+
export { withRetry, isPermanentError, isTransientError, promisify, DEFAULT_PERMANENT_ERROR_PATTERNS, DEFAULT_TRANSIENT_ERROR_PATTERNS, AbortError, } from "./retry-utils.js";
|
|
5
|
+
// Command execution
|
|
6
|
+
export { ShellCommandExecutor, defaultExecutor, } from "./command-executor.js";
|
|
7
|
+
// Shell utilities
|
|
8
|
+
export { escapeShellArg } from "./shell-utils.js";
|
|
9
|
+
// Sanitization
|
|
10
|
+
export { sanitizeCredentials } from "./sanitize-utils.js";
|
|
11
|
+
// Environment
|
|
12
|
+
export { interpolateEnvVars, interpolateEnvVarsInString, interpolateEnvVarsInLines, interpolateContent, } from "./env.js";
|
|
13
|
+
// Workspace utilities
|
|
14
|
+
export { generateWorkspaceName } from "./workspace-utils.js";
|
|
15
|
+
// Repository detection
|
|
16
|
+
export { detectRepoType, parseGitUrl, getRepoDisplayName, isGitHubRepo, isAzureDevOpsRepo, isGitLabRepo, } from "./repo-detector.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RepoInfo } from "../shared/repo-detector.js";
|
|
2
|
+
import { ILogger } from "../shared/logger.js";
|
|
3
|
+
import { GitHubAppTokenManager } from "../vcs/github-app-token-manager.js";
|
|
4
|
+
import type { AuthResult, IAuthOptionsBuilder } from "./types.js";
|
|
5
|
+
export declare class AuthOptionsBuilder implements IAuthOptionsBuilder {
|
|
6
|
+
private readonly tokenManager;
|
|
7
|
+
private readonly log;
|
|
8
|
+
constructor(tokenManager: GitHubAppTokenManager | null, log: ILogger);
|
|
9
|
+
resolve(repoInfo: RepoInfo, repoName: string): Promise<AuthResult>;
|
|
10
|
+
private getInstallationToken;
|
|
11
|
+
private buildAuthOptions;
|
|
12
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { isGitHubRepo, } from "../shared/repo-detector.js";
|
|
2
|
+
export class AuthOptionsBuilder {
|
|
3
|
+
tokenManager;
|
|
4
|
+
log;
|
|
5
|
+
constructor(tokenManager, log) {
|
|
6
|
+
this.tokenManager = tokenManager;
|
|
7
|
+
this.log = log;
|
|
8
|
+
}
|
|
9
|
+
async resolve(repoInfo, repoName) {
|
|
10
|
+
// 1. Get installation token if GitHub App configured
|
|
11
|
+
const installationToken = await this.getInstallationToken(repoInfo);
|
|
12
|
+
// 2. Handle "no installation found" case
|
|
13
|
+
if (installationToken === null) {
|
|
14
|
+
return {
|
|
15
|
+
skipResult: {
|
|
16
|
+
success: true,
|
|
17
|
+
repoName,
|
|
18
|
+
message: `No GitHub App installation found for ${repoInfo.owner}`,
|
|
19
|
+
skipped: true,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// 3. Build effective token (installation token or PAT fallback)
|
|
24
|
+
const token = installationToken ??
|
|
25
|
+
(isGitHubRepo(repoInfo) ? process.env.GH_TOKEN : undefined);
|
|
26
|
+
// 4. Build auth options if we have a token
|
|
27
|
+
const authOptions = token
|
|
28
|
+
? this.buildAuthOptions(repoInfo, token)
|
|
29
|
+
: undefined;
|
|
30
|
+
return { token, authOptions };
|
|
31
|
+
}
|
|
32
|
+
async getInstallationToken(repoInfo) {
|
|
33
|
+
if (!this.tokenManager || !isGitHubRepo(repoInfo)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return await this.tokenManager.getTokenForRepo(repoInfo);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
this.log.info(`Warning: Failed to get GitHub App token: ${error instanceof Error ? error.message : String(error)}`);
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
buildAuthOptions(repoInfo, token) {
|
|
45
|
+
return {
|
|
46
|
+
token,
|
|
47
|
+
host: isGitHubRepo(repoInfo)
|
|
48
|
+
? repoInfo.host
|
|
49
|
+
: "github.com",
|
|
50
|
+
owner: repoInfo.owner,
|
|
51
|
+
repo: repoInfo.repo,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|