@aspruyt/xfg 4.0.0 → 4.0.1
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 +1 -2
- package/dist/cli/index.js +0 -1
- package/dist/cli/program.js +7 -2
- package/dist/cli/{settings/results-collector.d.ts → results-collector.d.ts} +1 -1
- package/dist/cli/{settings/results-collector.js → results-collector.js} +2 -1
- package/dist/cli/settings-report-builder.d.ts +1 -3
- package/dist/cli/sync-command.d.ts +2 -24
- package/dist/cli/sync-command.js +295 -301
- package/dist/cli/types.d.ts +60 -40
- package/dist/cli/types.js +1 -12
- package/dist/config/errors.d.ts +9 -0
- package/dist/config/errors.js +11 -0
- package/dist/config/file-reference-resolver.d.ts +2 -1
- package/dist/config/file-reference-resolver.js +10 -8
- package/dist/config/formatter.d.ts +3 -2
- package/dist/config/index.d.ts +4 -6
- package/dist/config/index.js +4 -8
- package/dist/config/loader.js +4 -2
- package/dist/config/merge.d.ts +0 -9
- package/dist/config/merge.js +2 -7
- package/dist/config/normalizer.d.ts +4 -0
- package/dist/config/normalizer.js +61 -110
- package/dist/config/types.d.ts +15 -19
- package/dist/config/types.js +1 -1
- package/dist/config/validator.d.ts +0 -4
- package/dist/config/validator.js +286 -363
- package/dist/config/validators/file-validator.d.ts +2 -8
- package/dist/config/validators/file-validator.js +6 -17
- package/dist/config/validators/index.d.ts +3 -3
- package/dist/config/validators/index.js +3 -3
- package/dist/config/validators/repo-settings-validator.d.ts +0 -6
- package/dist/config/validators/repo-settings-validator.js +9 -9
- package/dist/config/validators/ruleset-validator.d.ts +0 -14
- package/dist/config/validators/ruleset-validator.js +28 -28
- package/dist/lifecycle/ado-migration-source.js +2 -1
- package/dist/lifecycle/github-lifecycle-provider.d.ts +6 -5
- package/dist/lifecycle/github-lifecycle-provider.js +79 -90
- package/dist/lifecycle/index.d.ts +2 -6
- package/dist/lifecycle/index.js +0 -4
- package/dist/lifecycle/lifecycle-formatter.d.ts +2 -1
- package/dist/lifecycle/lifecycle-formatter.js +4 -0
- package/dist/lifecycle/lifecycle-helpers.d.ts +3 -2
- package/dist/lifecycle/repo-lifecycle-manager.js +4 -11
- package/dist/lifecycle/types.d.ts +0 -8
- package/dist/output/github-summary.d.ts +5 -0
- package/dist/output/github-summary.js +9 -2
- package/dist/output/index.d.ts +2 -2
- package/dist/output/index.js +1 -1
- package/dist/output/lifecycle-report.js +5 -23
- package/dist/output/settings-report.d.ts +14 -3
- package/dist/output/settings-report.js +137 -197
- package/dist/output/summary-utils.d.ts +1 -1
- package/dist/output/summary-utils.js +2 -1
- package/dist/output/sync-report.js +5 -8
- package/dist/output/unified-summary.d.ts +2 -1
- package/dist/output/unified-summary.js +71 -133
- package/dist/settings/base-processor.d.ts +67 -0
- package/dist/settings/base-processor.js +91 -0
- package/dist/settings/index.d.ts +4 -3
- package/dist/settings/index.js +3 -3
- package/dist/settings/labels/converter.d.ts +2 -1
- package/dist/settings/labels/github-labels-strategy.d.ts +9 -18
- package/dist/settings/labels/github-labels-strategy.js +17 -73
- package/dist/settings/labels/index.d.ts +2 -6
- package/dist/settings/labels/index.js +1 -9
- package/dist/settings/labels/processor.d.ts +6 -30
- package/dist/settings/labels/processor.js +62 -152
- package/dist/settings/labels/types.d.ts +5 -8
- package/dist/settings/repo-settings/formatter.d.ts +2 -2
- package/dist/settings/repo-settings/formatter.js +6 -6
- package/dist/settings/repo-settings/github-repo-settings-strategy.d.ts +11 -12
- package/dist/settings/repo-settings/github-repo-settings-strategy.js +32 -79
- package/dist/settings/repo-settings/index.d.ts +2 -5
- package/dist/settings/repo-settings/index.js +1 -9
- package/dist/settings/repo-settings/processor.d.ts +6 -27
- package/dist/settings/repo-settings/processor.js +51 -104
- package/dist/settings/repo-settings/types.d.ts +7 -9
- package/dist/settings/rulesets/diff-algorithm.d.ts +0 -4
- package/dist/settings/rulesets/diff-algorithm.js +1 -10
- package/dist/settings/rulesets/diff.d.ts +1 -1
- package/dist/settings/rulesets/diff.js +2 -21
- package/dist/settings/rulesets/formatter.d.ts +1 -3
- package/dist/settings/rulesets/formatter.js +1 -8
- package/dist/settings/rulesets/github-ruleset-strategy.d.ts +11 -51
- package/dist/settings/rulesets/github-ruleset-strategy.js +24 -85
- package/dist/settings/rulesets/index.d.ts +3 -6
- package/dist/settings/rulesets/index.js +5 -9
- package/dist/settings/rulesets/processor.d.ts +8 -33
- package/dist/settings/rulesets/processor.js +58 -151
- package/dist/settings/rulesets/types.d.ts +35 -6
- package/dist/shared/command-executor.d.ts +2 -22
- package/dist/shared/command-executor.js +8 -7
- package/dist/shared/env.d.ts +0 -8
- package/dist/shared/env.js +14 -70
- package/dist/shared/file-status.d.ts +2 -0
- package/dist/shared/file-status.js +13 -0
- package/dist/shared/gh-api-utils.d.ts +46 -0
- package/dist/shared/gh-api-utils.js +107 -0
- package/dist/shared/index.d.ts +5 -5
- package/dist/shared/index.js +3 -3
- package/dist/shared/interpolation-engine.d.ts +31 -0
- package/dist/shared/interpolation-engine.js +50 -0
- package/dist/shared/logger.d.ts +3 -7
- package/dist/shared/logger.js +4 -1
- package/dist/shared/repo-detector.d.ts +17 -2
- package/dist/shared/repo-detector.js +27 -0
- package/dist/shared/retry-utils.d.ts +9 -17
- package/dist/shared/retry-utils.js +22 -28
- package/dist/shared/sanitize-utils.d.ts +0 -7
- package/dist/shared/sanitize-utils.js +0 -7
- package/dist/shared/shell-utils.d.ts +1 -0
- package/dist/shared/shell-utils.js +3 -0
- package/dist/shared/string-utils.d.ts +4 -0
- package/dist/shared/string-utils.js +6 -0
- package/dist/shared/type-guards.d.ts +17 -0
- package/dist/shared/type-guards.js +26 -0
- package/dist/shared/workspace-utils.d.ts +0 -4
- package/dist/shared/workspace-utils.js +0 -4
- package/dist/{sync → shared}/xfg-template.d.ts +3 -2
- package/dist/{sync → shared}/xfg-template.js +13 -54
- package/dist/sync/auth-options-builder.d.ts +4 -5
- package/dist/sync/auth-options-builder.js +15 -26
- package/dist/sync/branch-manager.d.ts +5 -0
- package/dist/sync/branch-manager.js +12 -10
- package/dist/sync/commit-push-manager.d.ts +1 -1
- package/dist/sync/commit-push-manager.js +22 -18
- package/dist/sync/diff-utils.d.ts +4 -9
- package/dist/sync/diff-utils.js +2 -19
- package/dist/sync/file-sync-orchestrator.js +9 -8
- package/dist/sync/file-writer.d.ts +2 -1
- package/dist/sync/file-writer.js +3 -6
- package/dist/sync/index.d.ts +2 -15
- package/dist/sync/index.js +0 -19
- package/dist/sync/manifest-manager.d.ts +4 -0
- package/dist/sync/manifest-manager.js +5 -1
- package/dist/sync/manifest.d.ts +10 -41
- package/dist/sync/manifest.js +11 -56
- package/dist/sync/pr-merge-handler.d.ts +2 -6
- package/dist/sync/pr-merge-handler.js +6 -3
- package/dist/sync/repository-processor.d.ts +1 -2
- package/dist/sync/repository-processor.js +20 -12
- package/dist/sync/repository-session.js +5 -14
- package/dist/sync/sync-workflow.js +31 -38
- package/dist/sync/types.d.ts +43 -178
- package/dist/vcs/authenticated-git-ops.d.ts +27 -70
- package/dist/vcs/authenticated-git-ops.js +70 -96
- package/dist/vcs/azure-pr-strategy.d.ts +6 -4
- package/dist/vcs/azure-pr-strategy.js +34 -82
- package/dist/vcs/branch-utils.d.ts +6 -0
- package/dist/vcs/branch-utils.js +29 -0
- package/dist/vcs/commit-strategy-selector.d.ts +5 -0
- package/dist/vcs/commit-strategy-selector.js +10 -0
- package/dist/vcs/git-commit-strategy.js +1 -2
- package/dist/vcs/git-ops.d.ts +15 -59
- package/dist/vcs/git-ops.js +46 -110
- package/dist/vcs/github-app-token-manager.d.ts +0 -6
- package/dist/vcs/github-app-token-manager.js +5 -12
- package/dist/vcs/github-pr-strategy.d.ts +5 -5
- package/dist/vcs/github-pr-strategy.js +44 -122
- package/dist/vcs/gitlab-pr-strategy.d.ts +6 -4
- package/dist/vcs/gitlab-pr-strategy.js +39 -87
- package/dist/vcs/graphql-commit-strategy.d.ts +3 -4
- package/dist/vcs/graphql-commit-strategy.js +31 -63
- package/dist/vcs/index.d.ts +3 -16
- package/dist/vcs/index.js +2 -33
- package/dist/vcs/pr-creator.d.ts +9 -9
- package/dist/vcs/pr-creator.js +11 -10
- package/dist/vcs/pr-strategy-factory.d.ts +5 -0
- package/dist/vcs/pr-strategy-factory.js +17 -0
- package/dist/vcs/pr-strategy.d.ts +13 -26
- package/dist/vcs/pr-strategy.js +20 -25
- package/dist/vcs/types.d.ts +87 -21
- package/package.json +2 -1
|
@@ -2,40 +2,19 @@ import type { RepoConfig } from "../../config/index.js";
|
|
|
2
2
|
import type { RepoInfo } from "../../shared/repo-detector.js";
|
|
3
3
|
import type { IRepoSettingsStrategy } from "./types.js";
|
|
4
4
|
import { RepoSettingsPlanResult } from "./formatter.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export interface
|
|
9
|
-
|
|
10
|
-
token?: string;
|
|
11
|
-
}
|
|
12
|
-
export interface RepoSettingsProcessorResult {
|
|
13
|
-
success: boolean;
|
|
14
|
-
repoName: string;
|
|
15
|
-
message: string;
|
|
16
|
-
skipped?: boolean;
|
|
17
|
-
dryRun?: boolean;
|
|
18
|
-
changes?: {
|
|
19
|
-
adds: number;
|
|
20
|
-
changes: number;
|
|
21
|
-
};
|
|
5
|
+
import { type BaseProcessorOptions, type BaseProcessorResult, type ISettingsProcessor, type ChangeCounts } from "../base-processor.js";
|
|
6
|
+
export type IRepoSettingsProcessor = ISettingsProcessor<RepoSettingsProcessorOptions, RepoSettingsProcessorResult>;
|
|
7
|
+
export type RepoSettingsProcessorOptions = BaseProcessorOptions;
|
|
8
|
+
export interface RepoSettingsProcessorResult extends BaseProcessorResult {
|
|
9
|
+
changes?: ChangeCounts;
|
|
22
10
|
warnings?: string[];
|
|
23
11
|
planOutput?: RepoSettingsPlanResult;
|
|
24
12
|
}
|
|
25
13
|
export declare class RepoSettingsProcessor implements IRepoSettingsProcessor {
|
|
26
14
|
private readonly strategy;
|
|
27
|
-
private readonly tokenManager;
|
|
28
15
|
constructor(strategy?: IRepoSettingsStrategy);
|
|
29
16
|
process(repoConfig: RepoConfig, repoInfo: RepoInfo, options: RepoSettingsProcessorOptions): Promise<RepoSettingsProcessorResult>;
|
|
17
|
+
private processSettings;
|
|
30
18
|
private applyChanges;
|
|
31
|
-
/**
|
|
32
|
-
* Validates that desired security settings are compatible with the repo's
|
|
33
|
-
* visibility and owner type. Returns error messages for incompatible settings.
|
|
34
|
-
*/
|
|
35
19
|
private validateSecuritySettings;
|
|
36
|
-
/**
|
|
37
|
-
* Resolves a GitHub App installation token for the given repo.
|
|
38
|
-
* Returns undefined if no token manager or token resolution fails.
|
|
39
|
-
*/
|
|
40
|
-
private getInstallationToken;
|
|
41
20
|
}
|
|
@@ -1,108 +1,75 @@
|
|
|
1
|
-
import { isGitHubRepo, getRepoDisplayName, } from "../../shared/repo-detector.js";
|
|
2
1
|
import { GitHubRepoSettingsStrategy } from "./github-repo-settings-strategy.js";
|
|
3
2
|
import { diffRepoSettings, hasChanges } from "./diff.js";
|
|
4
3
|
import { formatRepoSettingsPlan } from "./formatter.js";
|
|
5
|
-
import {
|
|
6
|
-
import { GitHubAppTokenManager } from "../../vcs/github-app-token-manager.js";
|
|
4
|
+
import { withGitHubGuards, buildDryRunResult, buildApplyResult, } from "../base-processor.js";
|
|
7
5
|
export class RepoSettingsProcessor {
|
|
8
6
|
strategy;
|
|
9
|
-
tokenManager;
|
|
10
7
|
constructor(strategy) {
|
|
11
8
|
this.strategy = strategy ?? new GitHubRepoSettingsStrategy();
|
|
12
|
-
if (hasGitHubAppCredentials()) {
|
|
13
|
-
this.tokenManager = new GitHubAppTokenManager(process.env.XFG_GITHUB_APP_ID, process.env.XFG_GITHUB_APP_PRIVATE_KEY);
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
this.tokenManager = null;
|
|
17
|
-
}
|
|
18
9
|
}
|
|
19
10
|
async process(repoConfig, repoInfo, options) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
return withGitHubGuards(repoConfig, repoInfo, options, {
|
|
12
|
+
hasDesiredSettings: (rc) => {
|
|
13
|
+
const s = rc.settings?.repo;
|
|
14
|
+
return !!s && Object.keys(s).length > 0;
|
|
15
|
+
},
|
|
16
|
+
emptySettingsMessage: "No repo settings configured",
|
|
17
|
+
processSettings: (githubRepo, rc, opts, token, repoName) => this.processSettings(githubRepo, rc, opts, token, repoName),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async processSettings(githubRepo, repoConfig, options, effectiveToken, repoName) {
|
|
21
|
+
const { dryRun } = options;
|
|
22
|
+
const desiredSettings = repoConfig.settings.repo;
|
|
23
|
+
const strategyOptions = { token: effectiveToken, host: githubRepo.host };
|
|
24
|
+
// Fetch current settings
|
|
25
|
+
const currentSettings = await this.strategy.getSettings(githubRepo, strategyOptions);
|
|
26
|
+
// Validate security settings compatibility
|
|
27
|
+
const securityErrors = this.validateSecuritySettings(desiredSettings, currentSettings);
|
|
28
|
+
if (securityErrors.length > 0) {
|
|
24
29
|
return {
|
|
25
|
-
success:
|
|
30
|
+
success: false,
|
|
26
31
|
repoName,
|
|
27
|
-
message: `
|
|
28
|
-
skipped: true,
|
|
32
|
+
message: `Failed: ${securityErrors.join("; ")}`,
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
// Compute diff
|
|
36
|
+
const changes = diffRepoSettings(currentSettings, desiredSettings);
|
|
37
|
+
if (!hasChanges(changes)) {
|
|
38
|
+
const unchangedCount = changes.filter((c) => c.action === "unchanged").length;
|
|
35
39
|
return {
|
|
36
40
|
success: true,
|
|
37
41
|
repoName,
|
|
38
|
-
message: "No
|
|
39
|
-
|
|
42
|
+
message: "No changes needed",
|
|
43
|
+
changes: { create: 0, update: 0, delete: 0, unchanged: unchangedCount },
|
|
40
44
|
};
|
|
41
45
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
success: false,
|
|
53
|
-
repoName,
|
|
54
|
-
message: `Failed: ${securityErrors.join("; ")}`,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
// Compute diff
|
|
58
|
-
const changes = diffRepoSettings(currentSettings, desiredSettings);
|
|
59
|
-
if (!hasChanges(changes)) {
|
|
60
|
-
return {
|
|
61
|
-
success: true,
|
|
62
|
-
repoName,
|
|
63
|
-
message: "No changes needed",
|
|
64
|
-
changes: { adds: 0, changes: 0 },
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
// Format plan output
|
|
68
|
-
const planOutput = formatRepoSettingsPlan(changes);
|
|
69
|
-
// Dry run mode - report planned changes without applying
|
|
70
|
-
if (dryRun) {
|
|
71
|
-
return {
|
|
72
|
-
success: true,
|
|
73
|
-
repoName,
|
|
74
|
-
message: `[DRY RUN] ${planOutput.adds} to add, ${planOutput.changes} to change`,
|
|
75
|
-
dryRun: true,
|
|
76
|
-
changes: { adds: planOutput.adds, changes: planOutput.changes },
|
|
77
|
-
warnings: planOutput.warnings,
|
|
78
|
-
planOutput,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
// Apply changes - only send settings that actually changed
|
|
82
|
-
const changedSettings = changes.reduce((acc, change) => {
|
|
83
|
-
if (change.action !== "unchanged") {
|
|
84
|
-
acc[change.property] = change.newValue;
|
|
85
|
-
}
|
|
86
|
-
return acc;
|
|
87
|
-
}, {});
|
|
88
|
-
await this.applyChanges(githubRepo, changedSettings, strategyOptions);
|
|
89
|
-
return {
|
|
90
|
-
success: true,
|
|
91
|
-
repoName,
|
|
92
|
-
message: `Applied: ${planOutput.adds} added, ${planOutput.changes} changed`,
|
|
93
|
-
changes: { adds: planOutput.adds, changes: planOutput.changes },
|
|
46
|
+
// Format plan output
|
|
47
|
+
const planOutput = formatRepoSettingsPlan(changes);
|
|
48
|
+
const changeCounts = {
|
|
49
|
+
create: planOutput.creates,
|
|
50
|
+
update: planOutput.updates,
|
|
51
|
+
delete: 0,
|
|
52
|
+
unchanged: changes.filter((c) => c.action === "unchanged").length,
|
|
53
|
+
};
|
|
54
|
+
if (dryRun) {
|
|
55
|
+
return buildDryRunResult(repoName, changeCounts, {
|
|
94
56
|
warnings: planOutput.warnings,
|
|
95
57
|
planOutput,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
-
return {
|
|
101
|
-
success: false,
|
|
102
|
-
repoName,
|
|
103
|
-
message: `Failed: ${message}`,
|
|
104
|
-
};
|
|
58
|
+
});
|
|
105
59
|
}
|
|
60
|
+
// Apply changes - only send settings that actually changed
|
|
61
|
+
const changedSettings = changes.reduce((acc, change) => {
|
|
62
|
+
if (change.action !== "unchanged") {
|
|
63
|
+
acc[change.property] = change.newValue;
|
|
64
|
+
}
|
|
65
|
+
return acc;
|
|
66
|
+
}, {});
|
|
67
|
+
await this.applyChanges(githubRepo, changedSettings, strategyOptions);
|
|
68
|
+
const appliedCount = Object.keys(changedSettings).length;
|
|
69
|
+
return buildApplyResult(repoName, changeCounts, appliedCount, {
|
|
70
|
+
warnings: planOutput.warnings,
|
|
71
|
+
planOutput,
|
|
72
|
+
});
|
|
106
73
|
}
|
|
107
74
|
async applyChanges(repoInfo, settings, options) {
|
|
108
75
|
// Extract settings that need separate API calls
|
|
@@ -126,10 +93,6 @@ export class RepoSettingsProcessor {
|
|
|
126
93
|
await this.strategy.setAutomatedSecurityFixes(repoInfo, automatedSecurityFixes, options);
|
|
127
94
|
}
|
|
128
95
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Validates that desired security settings are compatible with the repo's
|
|
131
|
-
* visibility and owner type. Returns error messages for incompatible settings.
|
|
132
|
-
*/
|
|
133
96
|
validateSecuritySettings(desiredSettings, currentSettings) {
|
|
134
97
|
const errors = [];
|
|
135
98
|
const isPublic = currentSettings.visibility === "public";
|
|
@@ -155,20 +118,4 @@ export class RepoSettingsProcessor {
|
|
|
155
118
|
}
|
|
156
119
|
return errors;
|
|
157
120
|
}
|
|
158
|
-
/**
|
|
159
|
-
* Resolves a GitHub App installation token for the given repo.
|
|
160
|
-
* Returns undefined if no token manager or token resolution fails.
|
|
161
|
-
*/
|
|
162
|
-
async getInstallationToken(repoInfo) {
|
|
163
|
-
if (!this.tokenManager) {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
const token = await this.tokenManager.getTokenForRepo(repoInfo);
|
|
168
|
-
return token ?? undefined;
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
121
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import type { RepoInfo } from "../../shared/repo-detector.js";
|
|
2
2
|
import type { GitHubRepoSettings } from "../../config/index.js";
|
|
3
|
-
|
|
4
|
-
token?: string;
|
|
5
|
-
host?: string;
|
|
6
|
-
}
|
|
3
|
+
import type { GhApiOptions } from "../../shared/gh-api-utils.js";
|
|
7
4
|
/**
|
|
8
5
|
* Current repository settings from GitHub API (snake_case).
|
|
9
6
|
*/
|
|
10
7
|
export interface CurrentRepoSettings {
|
|
8
|
+
description?: string;
|
|
11
9
|
has_issues?: boolean;
|
|
12
10
|
has_projects?: boolean;
|
|
13
11
|
has_wiki?: boolean;
|
|
@@ -48,23 +46,23 @@ export interface IRepoSettingsStrategy {
|
|
|
48
46
|
/**
|
|
49
47
|
* Gets current repository settings.
|
|
50
48
|
*/
|
|
51
|
-
getSettings(repoInfo: RepoInfo, options?:
|
|
49
|
+
getSettings(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentRepoSettings>;
|
|
52
50
|
/**
|
|
53
51
|
* Updates repository settings.
|
|
54
52
|
*/
|
|
55
|
-
updateSettings(repoInfo: RepoInfo, settings: GitHubRepoSettings, options?:
|
|
53
|
+
updateSettings(repoInfo: RepoInfo, settings: GitHubRepoSettings, options?: GhApiOptions): Promise<void>;
|
|
56
54
|
/**
|
|
57
55
|
* Enables or disables vulnerability alerts.
|
|
58
56
|
*/
|
|
59
|
-
setVulnerabilityAlerts(repoInfo: RepoInfo, enable: boolean, options?:
|
|
57
|
+
setVulnerabilityAlerts(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
60
58
|
/**
|
|
61
59
|
* Enables or disables automated security fixes.
|
|
62
60
|
*/
|
|
63
|
-
setAutomatedSecurityFixes(repoInfo: RepoInfo, enable: boolean, options?:
|
|
61
|
+
setAutomatedSecurityFixes(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
64
62
|
/**
|
|
65
63
|
* Enables or disables private vulnerability reporting.
|
|
66
64
|
*/
|
|
67
|
-
setPrivateVulnerabilityReporting(repoInfo: RepoInfo, enable: boolean, options?:
|
|
65
|
+
setPrivateVulnerabilityReporting(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
68
66
|
}
|
|
69
67
|
/**
|
|
70
68
|
* Type guard to check if an object implements IRepoSettingsStrategy.
|
|
@@ -12,7 +12,3 @@ export declare function isArrayOfObjects(arr: unknown[]): boolean;
|
|
|
12
12
|
* Recursively compute property-level diffs between two objects.
|
|
13
13
|
*/
|
|
14
14
|
export declare function computePropertyDiffs(current: Record<string, unknown>, desired: Record<string, unknown>, parentPath?: string[]): PropertyDiff[];
|
|
15
|
-
/**
|
|
16
|
-
* Diff two arrays of objects by matching items on `type` field (or by index).
|
|
17
|
-
*/
|
|
18
|
-
export declare function diffObjectArrays(currentArr: unknown[], desiredArr: unknown[], parentPath: string[]): PropertyDiff[];
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
// src/settings/rulesets/diff-algorithm.ts
|
|
2
|
-
// =============================================================================
|
|
3
|
-
// Helpers
|
|
4
|
-
// =============================================================================
|
|
5
2
|
export function isObject(val) {
|
|
6
3
|
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
7
4
|
}
|
|
@@ -29,9 +26,6 @@ export function deepEqual(a, b) {
|
|
|
29
26
|
export function isArrayOfObjects(arr) {
|
|
30
27
|
return arr.length > 0 && arr.every((item) => isObject(item));
|
|
31
28
|
}
|
|
32
|
-
// =============================================================================
|
|
33
|
-
// Property Diff Algorithm
|
|
34
|
-
// =============================================================================
|
|
35
29
|
/**
|
|
36
30
|
* Recursively compute property-level diffs between two objects.
|
|
37
31
|
*/
|
|
@@ -76,13 +70,10 @@ export function computePropertyDiffs(current, desired, parentPath = []) {
|
|
|
76
70
|
}
|
|
77
71
|
return diffs;
|
|
78
72
|
}
|
|
79
|
-
// =============================================================================
|
|
80
|
-
// Array Diffing
|
|
81
|
-
// =============================================================================
|
|
82
73
|
/**
|
|
83
74
|
* Diff two arrays of objects by matching items on `type` field (or by index).
|
|
84
75
|
*/
|
|
85
|
-
|
|
76
|
+
function diffObjectArrays(currentArr, desiredArr, parentPath) {
|
|
86
77
|
const diffs = [];
|
|
87
78
|
const hasType = desiredArr.every((item) => isObject(item) && "type" in item);
|
|
88
79
|
if (hasType) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Ruleset } from "../../config/index.js";
|
|
2
|
-
import type { GitHubRuleset } from "./
|
|
2
|
+
import type { GitHubRuleset } from "./types.js";
|
|
3
3
|
export type RulesetAction = "create" | "update" | "delete" | "unchanged";
|
|
4
4
|
export interface RulesetChange {
|
|
5
5
|
action: RulesetAction;
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { RULESET_COMPARABLE_FIELDS } from "../../config/index.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// =============================================================================
|
|
5
|
-
/**
|
|
6
|
-
* Converts camelCase to snake_case for comparison.
|
|
7
|
-
*/
|
|
8
|
-
function camelToSnake(str) {
|
|
9
|
-
return str.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
10
|
-
}
|
|
2
|
+
import { isPlainObject } from "../../shared/type-guards.js";
|
|
3
|
+
import { camelToSnake } from "../../shared/string-utils.js";
|
|
11
4
|
/**
|
|
12
5
|
* Normalizes a value recursively, converting keys to a consistent format (snake_case).
|
|
13
6
|
* This allows comparing GitHub API responses (snake_case) with config (camelCase).
|
|
@@ -91,9 +84,6 @@ function deepEqual(a, b) {
|
|
|
91
84
|
}
|
|
92
85
|
return false;
|
|
93
86
|
}
|
|
94
|
-
// =============================================================================
|
|
95
|
-
// Desired-Side Projection
|
|
96
|
-
// =============================================================================
|
|
97
87
|
/**
|
|
98
88
|
* Projects `current` onto the shape of `desired`.
|
|
99
89
|
* Only keeps keys/structure present in `desired`, filtering out API noise.
|
|
@@ -116,9 +106,6 @@ export function projectToDesiredShape(current, desired) {
|
|
|
116
106
|
// Scalars — return current as-is
|
|
117
107
|
return current;
|
|
118
108
|
}
|
|
119
|
-
function isPlainObject(val) {
|
|
120
|
-
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
121
|
-
}
|
|
122
109
|
function projectObjects(current, desired) {
|
|
123
110
|
const result = {};
|
|
124
111
|
for (const key of Object.keys(desired)) {
|
|
@@ -193,9 +180,6 @@ function matchByIndex(current, desired) {
|
|
|
193
180
|
}
|
|
194
181
|
return result;
|
|
195
182
|
}
|
|
196
|
-
// =============================================================================
|
|
197
|
-
// Diff Algorithm
|
|
198
|
-
// =============================================================================
|
|
199
183
|
/**
|
|
200
184
|
* Compares current rulesets (from GitHub) with desired rulesets (from config).
|
|
201
185
|
*
|
|
@@ -265,9 +249,6 @@ export function diffRulesets(current, desired, deleteOrphaned) {
|
|
|
265
249
|
};
|
|
266
250
|
return changes.sort((a, b) => actionOrder[a.action] - actionOrder[b.action]);
|
|
267
251
|
}
|
|
268
|
-
// =============================================================================
|
|
269
|
-
// Formatting
|
|
270
|
-
// =============================================================================
|
|
271
252
|
/**
|
|
272
253
|
* Formats a ruleset change for display.
|
|
273
254
|
*/
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { type RulesetChange, type RulesetAction } from "./diff.js";
|
|
2
2
|
import type { Ruleset } from "../../config/index.js";
|
|
3
|
-
import { type PropertyDiff } from "./
|
|
4
|
-
export type { DiffAction, PropertyDiff } from "./index.js";
|
|
5
|
-
export { computePropertyDiffs } from "./index.js";
|
|
3
|
+
import { type PropertyDiff } from "./diff-algorithm.js";
|
|
6
4
|
export interface RulesetPlanEntry {
|
|
7
5
|
name: string;
|
|
8
6
|
action: RulesetAction;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { projectToDesiredShape, normalizeRuleset, } from "./diff.js";
|
|
3
|
-
import { computePropertyDiffs, isObject, } from "./
|
|
4
|
-
export { computePropertyDiffs } from "./index.js";
|
|
3
|
+
import { computePropertyDiffs, isObject, } from "./diff-algorithm.js";
|
|
5
4
|
/**
|
|
6
5
|
* Build a tree structure from flat property diffs.
|
|
7
6
|
*/
|
|
@@ -187,9 +186,6 @@ export function formatPropertyTree(diffs) {
|
|
|
187
186
|
const tree = buildTree(diffs);
|
|
188
187
|
return renderTree(tree);
|
|
189
188
|
}
|
|
190
|
-
// =============================================================================
|
|
191
|
-
// Ruleset Plan Formatter
|
|
192
|
-
// =============================================================================
|
|
193
189
|
/**
|
|
194
190
|
* Format a full ruleset config as tree lines (for create action).
|
|
195
191
|
*/
|
|
@@ -253,7 +249,6 @@ export function formatRulesetPlan(changes) {
|
|
|
253
249
|
updates = updateChanges.length;
|
|
254
250
|
deletes = deleteChanges.length;
|
|
255
251
|
unchanged = unchangedChanges.length;
|
|
256
|
-
// Format creates
|
|
257
252
|
if (createChanges.length > 0) {
|
|
258
253
|
lines.push(chalk.bold(" Create:"));
|
|
259
254
|
for (const change of createChanges) {
|
|
@@ -273,7 +268,6 @@ export function formatRulesetPlan(changes) {
|
|
|
273
268
|
lines.push(""); // Blank line between rulesets
|
|
274
269
|
}
|
|
275
270
|
}
|
|
276
|
-
// Format updates
|
|
277
271
|
if (updateChanges.length > 0) {
|
|
278
272
|
lines.push(chalk.bold(" Update:"));
|
|
279
273
|
for (const change of updateChanges) {
|
|
@@ -303,7 +297,6 @@ export function formatRulesetPlan(changes) {
|
|
|
303
297
|
lines.push(""); // Blank line between rulesets
|
|
304
298
|
}
|
|
305
299
|
}
|
|
306
|
-
// Format deletes
|
|
307
300
|
if (deleteChanges.length > 0) {
|
|
308
301
|
lines.push(chalk.bold(" Delete:"));
|
|
309
302
|
for (const change of deleteChanges) {
|
|
@@ -1,41 +1,13 @@
|
|
|
1
1
|
import { ICommandExecutor } from "../../shared/command-executor.js";
|
|
2
2
|
import { RepoInfo } from "../../shared/repo-detector.js";
|
|
3
|
+
import { type GhApiOptions } from "../../shared/gh-api-utils.js";
|
|
3
4
|
import type { Ruleset } from "../../config/index.js";
|
|
4
|
-
import type { IRulesetStrategy } from "./types.js";
|
|
5
|
-
/**
|
|
6
|
-
* GitHub Ruleset response from API (snake_case).
|
|
7
|
-
*/
|
|
8
|
-
export interface GitHubRuleset {
|
|
9
|
-
id: number;
|
|
10
|
-
name: string;
|
|
11
|
-
target: "branch" | "tag";
|
|
12
|
-
enforcement: "active" | "disabled" | "evaluate";
|
|
13
|
-
bypass_actors?: GitHubBypassActor[];
|
|
14
|
-
conditions?: GitHubRulesetConditions;
|
|
15
|
-
rules?: GitHubRule[];
|
|
16
|
-
source_type?: string;
|
|
17
|
-
source?: string;
|
|
18
|
-
}
|
|
19
|
-
export interface GitHubBypassActor {
|
|
20
|
-
actor_id: number;
|
|
21
|
-
actor_type: "Team" | "User" | "Integration";
|
|
22
|
-
bypass_mode?: "always" | "pull_request";
|
|
23
|
-
}
|
|
24
|
-
export interface GitHubRulesetConditions {
|
|
25
|
-
ref_name?: {
|
|
26
|
-
include?: string[];
|
|
27
|
-
exclude?: string[];
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
export interface GitHubRule {
|
|
31
|
-
type: string;
|
|
32
|
-
parameters?: Record<string, unknown>;
|
|
33
|
-
}
|
|
5
|
+
import type { IRulesetStrategy, GitHubRuleset, GitHubBypassActor, GitHubRulesetConditions, GitHubRule } from "./types.js";
|
|
34
6
|
/**
|
|
35
7
|
* Converts camelCase config ruleset to snake_case GitHub API format.
|
|
36
8
|
*/
|
|
37
9
|
export declare function configToGitHub(name: string, ruleset: Ruleset): GitHubRulesetPayload;
|
|
38
|
-
|
|
10
|
+
interface GitHubRulesetPayload {
|
|
39
11
|
name: string;
|
|
40
12
|
target: "branch" | "tag";
|
|
41
13
|
enforcement: "active" | "disabled" | "evaluate";
|
|
@@ -43,11 +15,7 @@ export interface GitHubRulesetPayload {
|
|
|
43
15
|
conditions?: GitHubRulesetConditions;
|
|
44
16
|
rules?: GitHubRule[];
|
|
45
17
|
}
|
|
46
|
-
|
|
47
|
-
token?: string;
|
|
48
|
-
host?: string;
|
|
49
|
-
}
|
|
50
|
-
export interface GitHubRulesetStrategyOptions {
|
|
18
|
+
interface GitHubRulesetStrategyOptions {
|
|
51
19
|
retries?: number;
|
|
52
20
|
}
|
|
53
21
|
/**
|
|
@@ -55,35 +23,27 @@ export interface GitHubRulesetStrategyOptions {
|
|
|
55
23
|
* Uses `gh api` CLI for authentication and API calls.
|
|
56
24
|
*/
|
|
57
25
|
export declare class GitHubRulesetStrategy implements IRulesetStrategy {
|
|
58
|
-
private
|
|
59
|
-
private retries;
|
|
26
|
+
private api;
|
|
60
27
|
constructor(executor?: ICommandExecutor, options?: GitHubRulesetStrategyOptions);
|
|
61
28
|
/**
|
|
62
29
|
* Lists all rulesets for a repository.
|
|
63
30
|
*/
|
|
64
|
-
list(repoInfo: RepoInfo, options?:
|
|
31
|
+
list(repoInfo: RepoInfo, options?: GhApiOptions): Promise<GitHubRuleset[]>;
|
|
65
32
|
/**
|
|
66
33
|
* Gets a single ruleset by ID.
|
|
67
34
|
*/
|
|
68
|
-
get(repoInfo: RepoInfo, rulesetId: number, options?:
|
|
35
|
+
get(repoInfo: RepoInfo, rulesetId: number, options?: GhApiOptions): Promise<GitHubRuleset>;
|
|
69
36
|
/**
|
|
70
37
|
* Creates a new ruleset.
|
|
71
38
|
*/
|
|
72
|
-
create(repoInfo: RepoInfo, name: string, ruleset: Ruleset, options?:
|
|
39
|
+
create(repoInfo: RepoInfo, name: string, ruleset: Ruleset, options?: GhApiOptions): Promise<GitHubRuleset>;
|
|
73
40
|
/**
|
|
74
41
|
* Updates an existing ruleset.
|
|
75
42
|
*/
|
|
76
|
-
update(repoInfo: RepoInfo, rulesetId: number, name: string, ruleset: Ruleset, options?:
|
|
43
|
+
update(repoInfo: RepoInfo, rulesetId: number, name: string, ruleset: Ruleset, options?: GhApiOptions): Promise<GitHubRuleset>;
|
|
77
44
|
/**
|
|
78
45
|
* Deletes a ruleset.
|
|
79
46
|
*/
|
|
80
|
-
delete(repoInfo: RepoInfo, rulesetId: number, options?:
|
|
81
|
-
/**
|
|
82
|
-
* Validates that the repo is a GitHub repository.
|
|
83
|
-
*/
|
|
84
|
-
private validateGitHub;
|
|
85
|
-
/**
|
|
86
|
-
* Executes a GitHub API call using the gh CLI.
|
|
87
|
-
*/
|
|
88
|
-
private ghApi;
|
|
47
|
+
delete(repoInfo: RepoInfo, rulesetId: number, options?: GhApiOptions): Promise<void>;
|
|
89
48
|
}
|
|
49
|
+
export {};
|