@aspruyt/xfg 6.1.0 → 6.2.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.
- package/dist/cli/lifecycle-report-builder.d.ts +2 -2
- package/dist/cli/lifecycle-report-builder.js +3 -11
- package/dist/cli/program.d.ts +2 -1
- package/dist/cli/program.js +2 -3
- package/dist/cli/repo-sync-runner.d.ts +24 -0
- package/dist/cli/repo-sync-runner.js +156 -0
- package/dist/cli/results-collector.d.ts +1 -1
- package/dist/cli/results-collector.js +2 -2
- package/dist/cli/settings-factories.d.ts +7 -0
- package/dist/cli/settings-factories.js +27 -0
- package/dist/cli/settings-report-builder.d.ts +1 -1
- package/dist/cli/settings-report-builder.js +12 -23
- package/dist/cli/settings-runner.d.ts +2 -0
- package/dist/cli/settings-runner.js +87 -0
- package/dist/cli/sync-command.d.ts +1 -1
- package/dist/cli/sync-command.js +31 -372
- package/dist/cli/sync-report-builder.d.ts +1 -1
- package/dist/cli/sync-utils.d.ts +8 -0
- package/dist/cli/sync-utils.js +36 -0
- package/dist/cli/types.d.ts +5 -7
- package/dist/cli/unified-summary.d.ts +1 -3
- package/dist/cli/unified-summary.js +7 -5
- package/dist/cli.js +2 -1
- package/dist/{shared → config}/env.js +2 -2
- package/dist/config/extends-resolver.js +4 -3
- package/dist/config/file-reference-resolver.js +4 -2
- package/dist/config/formatter.js +1 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/loader.js +30 -6
- package/dist/config/merge.d.ts +11 -1
- package/dist/config/merge.js +78 -6
- package/dist/config/normalizer.js +43 -48
- package/dist/config/validator.d.ts +1 -4
- package/dist/config/validator.js +13 -609
- package/dist/config/validators/file-validator.d.ts +2 -1
- package/dist/config/validators/file-validator.js +9 -1
- package/dist/config/validators/group-validator.d.ts +3 -0
- package/dist/config/validators/group-validator.js +167 -0
- package/dist/config/validators/repo-entry-validator.d.ts +2 -0
- package/dist/config/validators/repo-entry-validator.js +165 -0
- package/dist/config/validators/repo-settings-validator.js +18 -7
- package/dist/config/validators/ruleset-validator.js +2 -5
- package/dist/config/validators/shared.d.ts +11 -0
- package/dist/config/validators/shared.js +242 -0
- package/dist/lifecycle/ado-migration-source.js +2 -4
- package/dist/lifecycle/github-lifecycle-provider.d.ts +7 -11
- package/dist/lifecycle/github-lifecycle-provider.js +125 -136
- package/dist/lifecycle/{lifecycle-helpers.d.ts → helpers.d.ts} +5 -1
- package/dist/lifecycle/{lifecycle-helpers.js → helpers.js} +9 -8
- package/dist/lifecycle/index.d.ts +2 -2
- package/dist/lifecycle/index.js +1 -1
- package/dist/lifecycle/repo-lifecycle-factory.d.ts +2 -2
- package/dist/output/github-summary.js +2 -3
- package/dist/output/index.d.ts +4 -0
- package/dist/output/index.js +4 -0
- package/dist/output/lifecycle-report.d.ts +1 -1
- package/dist/output/lifecycle-report.js +5 -0
- package/dist/output/sync-report.d.ts +25 -3
- package/dist/output/sync-report.js +11 -11
- package/dist/settings/base-processor.d.ts +18 -7
- package/dist/settings/base-processor.js +26 -5
- package/dist/settings/code-scanning/diff.js +2 -2
- package/dist/settings/code-scanning/formatter.d.ts +2 -6
- package/dist/settings/code-scanning/formatter.js +2 -25
- package/dist/settings/code-scanning/github-code-scanning-strategy.d.ts +3 -7
- package/dist/settings/code-scanning/github-code-scanning-strategy.js +2 -2
- package/dist/settings/code-scanning/processor.js +6 -4
- package/dist/settings/code-scanning/types.d.ts +10 -8
- package/dist/settings/labels/github-labels-strategy.d.ts +3 -11
- package/dist/settings/labels/types.d.ts +12 -10
- package/dist/settings/repo-settings/diff.d.ts +1 -1
- package/dist/settings/repo-settings/diff.js +1 -1
- package/dist/settings/repo-settings/formatter.d.ts +2 -6
- package/dist/settings/repo-settings/formatter.js +4 -23
- package/dist/settings/repo-settings/github-repo-settings-strategy.d.ts +2 -2
- package/dist/settings/repo-settings/github-repo-settings-strategy.js +8 -7
- package/dist/settings/repo-settings/processor.js +11 -11
- package/dist/settings/repo-settings/types.d.ts +2 -2
- package/dist/settings/rulesets/diff-algorithm.js +4 -2
- package/dist/settings/rulesets/diff.js +2 -51
- package/dist/settings/rulesets/formatter.js +4 -0
- package/dist/settings/rulesets/github-ruleset-strategy.d.ts +3 -3
- package/dist/settings/rulesets/github-ruleset-strategy.js +4 -6
- package/dist/settings/rulesets/index.d.ts +1 -1
- package/dist/settings/rulesets/index.js +0 -2
- package/dist/settings/rulesets/processor.js +1 -1
- package/dist/settings/rulesets/types.d.ts +6 -2
- package/dist/shared/command-executor.d.ts +4 -4
- package/dist/shared/command-executor.js +9 -7
- package/dist/shared/diff-format.d.ts +1 -0
- package/dist/shared/diff-format.js +10 -0
- package/dist/shared/errors.d.ts +7 -4
- package/dist/shared/errors.js +8 -8
- package/dist/shared/gh-api-utils.d.ts +3 -34
- package/dist/shared/gh-api-utils.js +23 -53
- package/dist/shared/gh-token-utils.d.ts +26 -0
- package/dist/shared/gh-token-utils.js +32 -0
- package/dist/shared/json-utils.js +1 -1
- package/dist/shared/regex-utils.d.ts +1 -0
- package/dist/shared/regex-utils.js +3 -0
- package/dist/shared/retry-utils.d.ts +1 -0
- package/dist/shared/retry-utils.js +13 -7
- package/dist/sync/auth-options-builder.js +1 -1
- package/dist/sync/branch-manager.js +5 -3
- package/dist/sync/commit-push-manager.js +2 -3
- package/dist/sync/diff-utils.d.ts +0 -1
- package/dist/sync/diff-utils.js +5 -10
- package/dist/sync/file-sync-orchestrator.js +0 -2
- package/dist/sync/file-writer.d.ts +3 -0
- package/dist/sync/file-writer.js +84 -81
- package/dist/sync/index.d.ts +0 -1
- package/dist/sync/index.js +0 -1
- package/dist/sync/manifest.js +1 -1
- package/dist/sync/pr-merge-handler.js +6 -6
- package/dist/sync/sync-workflow.js +1 -1
- package/dist/sync/types.d.ts +2 -2
- package/dist/vcs/ado-pr-strategy.d.ts +3 -5
- package/dist/vcs/ado-pr-strategy.js +131 -33
- package/dist/vcs/authenticated-git-ops.js +45 -23
- package/dist/vcs/git-commit-strategy.js +10 -6
- package/dist/vcs/git-ops.js +30 -24
- package/dist/vcs/github-pr-strategy.d.ts +3 -2
- package/dist/vcs/github-pr-strategy.js +80 -30
- package/dist/vcs/gitlab-pr-strategy.d.ts +2 -5
- package/dist/vcs/gitlab-pr-strategy.js +88 -87
- package/dist/vcs/graphql-commit-strategy.d.ts +1 -5
- package/dist/vcs/graphql-commit-strategy.js +21 -37
- package/dist/vcs/pr-creator.js +9 -2
- package/dist/vcs/pr-strategy.d.ts +2 -3
- package/dist/vcs/pr-strategy.js +0 -1
- package/dist/vcs/types.d.ts +9 -5
- package/package.json +5 -5
- package/dist/config/validators/index.d.ts +0 -3
- package/dist/config/validators/index.js +0 -6
- package/dist/output/types.d.ts +0 -20
- package/dist/output/types.js +0 -1
- package/dist/shared/shell-utils.d.ts +0 -6
- package/dist/shared/shell-utils.js +0 -17
- /package/dist/{shared → config}/env.d.ts +0 -0
- /package/dist/lifecycle/{lifecycle-formatter.d.ts → formatter.d.ts} +0 -0
- /package/dist/lifecycle/{lifecycle-formatter.js → formatter.js} +0 -0
- /package/dist/{vcs → shared}/sanitize-utils.d.ts +0 -0
- /package/dist/{vcs → shared}/sanitize-utils.js +0 -0
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { MergeMode } from "../config/index.js";
|
|
2
|
+
import type { ActiveAction } from "../settings/index.js";
|
|
3
|
+
export interface ReportFileChange {
|
|
4
|
+
path: string;
|
|
5
|
+
action: ActiveAction;
|
|
6
|
+
diffLines?: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface SyncReport {
|
|
9
|
+
repos: RepoFileChanges[];
|
|
10
|
+
totals: {
|
|
11
|
+
files: {
|
|
12
|
+
create: number;
|
|
13
|
+
update: number;
|
|
14
|
+
delete: number;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface RepoFileChanges {
|
|
19
|
+
repoName: string;
|
|
20
|
+
files: ReportFileChange[];
|
|
21
|
+
prUrl?: string;
|
|
22
|
+
mergeOutcome?: MergeMode;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
3
25
|
export declare function formatSyncReportCLI(report: SyncReport): string[];
|
|
4
26
|
export declare function formatSyncReportMarkdown(report: SyncReport, dryRun: boolean): string;
|
|
5
|
-
export declare function renderSyncLines(syncRepo: RepoFileChanges
|
|
27
|
+
export declare function renderSyncLines(syncRepo: RepoFileChanges): string[];
|
|
6
28
|
export declare function writeSyncReportSummary(report: SyncReport, dryRun: boolean, summaryPath: string | undefined): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { writeGitHubStepSummary } from "./github-summary.js";
|
|
3
3
|
import { formatCountEntry } from "./settings-report.js";
|
|
4
|
-
import { formatDiffLine } from "../
|
|
4
|
+
import { formatDiffLine } from "../shared/diff-format.js";
|
|
5
5
|
function formatSyncSummary(totals) {
|
|
6
6
|
const entry = formatCountEntry("file", "files", [
|
|
7
7
|
{ label: "to create", value: totals.files.create },
|
|
@@ -65,8 +65,7 @@ export function formatSyncReportMarkdown(report, dryRun) {
|
|
|
65
65
|
}
|
|
66
66
|
lines.push(`### ${repo.repoName}`);
|
|
67
67
|
lines.push("");
|
|
68
|
-
const diffLines =
|
|
69
|
-
renderSyncLines(repo, diffLines);
|
|
68
|
+
const diffLines = renderSyncLines(repo);
|
|
70
69
|
if (diffLines.length > 0) {
|
|
71
70
|
lines.push("```diff");
|
|
72
71
|
lines.push(...diffLines);
|
|
@@ -78,28 +77,29 @@ export function formatSyncReportMarkdown(report, dryRun) {
|
|
|
78
77
|
lines.push(`**${formatSyncSummary(report.totals)}**`);
|
|
79
78
|
return lines.join("\n");
|
|
80
79
|
}
|
|
81
|
-
export function renderSyncLines(syncRepo
|
|
80
|
+
export function renderSyncLines(syncRepo) {
|
|
81
|
+
const lines = [];
|
|
82
82
|
for (let i = 0; i < syncRepo.files.length; i++) {
|
|
83
83
|
const file = syncRepo.files[i];
|
|
84
|
-
// Blank line between files for readability
|
|
85
84
|
if (i > 0)
|
|
86
|
-
|
|
85
|
+
lines.push("");
|
|
87
86
|
if (file.action === "create") {
|
|
88
|
-
|
|
87
|
+
lines.push(`+ ${file.path}`);
|
|
89
88
|
}
|
|
90
89
|
else if (file.action === "update") {
|
|
91
|
-
|
|
90
|
+
lines.push(`! ${file.path}`);
|
|
92
91
|
}
|
|
93
92
|
else if (file.action === "delete") {
|
|
94
|
-
|
|
93
|
+
lines.push(`- ${file.path}`);
|
|
95
94
|
}
|
|
96
95
|
if (file.diffLines) {
|
|
97
|
-
|
|
96
|
+
lines.push(...file.diffLines);
|
|
98
97
|
}
|
|
99
98
|
}
|
|
100
99
|
if (syncRepo.error) {
|
|
101
|
-
|
|
100
|
+
lines.push(`- Error: ${syncRepo.error}`);
|
|
102
101
|
}
|
|
102
|
+
return lines;
|
|
103
103
|
}
|
|
104
104
|
export function writeSyncReportSummary(report, dryRun, summaryPath) {
|
|
105
105
|
const markdown = formatSyncReportMarkdown(report, dryRun);
|
|
@@ -33,11 +33,8 @@ interface SettingsGuards<TOptions extends BaseProcessorOptions, TResult extends
|
|
|
33
33
|
* empty settings check, token resolution, and error wrapping.
|
|
34
34
|
*/
|
|
35
35
|
export declare function withGitHubGuards<TOptions extends BaseProcessorOptions, TResult extends BaseProcessorResult>(repoConfig: RepoConfig, repoInfo: RepoInfo, options: TOptions, guards: SettingsGuards<TOptions, TResult>): Promise<TResult>;
|
|
36
|
-
/** Common action literals shared by all settings processors. */
|
|
37
36
|
export type SettingsAction = "create" | "update" | "delete" | "unchanged";
|
|
38
|
-
/** Actions that represent an actual change (excludes "unchanged"). */
|
|
39
37
|
export type ActiveAction = Exclude<SettingsAction, "unchanged">;
|
|
40
|
-
/** Type predicate that narrows entries with an active (non-"unchanged") action. */
|
|
41
38
|
export declare function isActiveAction<T extends {
|
|
42
39
|
action: SettingsAction;
|
|
43
40
|
}>(entry: T): entry is T & {
|
|
@@ -49,14 +46,28 @@ export interface ChangeCounts {
|
|
|
49
46
|
delete: number;
|
|
50
47
|
unchanged: number;
|
|
51
48
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Count actions from a diff result array.
|
|
54
|
-
* Works with any change type that has an `action` field.
|
|
55
|
-
*/
|
|
56
49
|
export declare function countActions(changes: ReadonlyArray<{
|
|
57
50
|
action: SettingsAction;
|
|
58
51
|
}>): ChangeCounts;
|
|
59
52
|
export declare function formatChangeSummary(counts: ChangeCounts): string;
|
|
53
|
+
export interface PlanEntry {
|
|
54
|
+
property: string;
|
|
55
|
+
action: "create" | "update";
|
|
56
|
+
oldValue?: unknown;
|
|
57
|
+
newValue?: unknown;
|
|
58
|
+
}
|
|
59
|
+
export interface FormatChangeResult {
|
|
60
|
+
lines: string[];
|
|
61
|
+
entries: PlanEntry[];
|
|
62
|
+
creates: number;
|
|
63
|
+
updates: number;
|
|
64
|
+
}
|
|
65
|
+
export declare function formatChangeLines(changes: ReadonlyArray<{
|
|
66
|
+
property: string;
|
|
67
|
+
action: SettingsAction;
|
|
68
|
+
oldValue?: unknown;
|
|
69
|
+
newValue?: unknown;
|
|
70
|
+
}>, formatValue: (val: unknown) => string): FormatChangeResult;
|
|
60
71
|
/**
|
|
61
72
|
* Build a standardized dry-run result for settings processors.
|
|
62
73
|
* Returns an intersection of BaseProcessorResult with the extra fields,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
1
2
|
import { isGitHubRepo, getRepoDisplayName } from "../repo/index.js";
|
|
2
3
|
import { toErrorMessage } from "../shared/type-guards.js";
|
|
3
4
|
/**
|
|
@@ -43,14 +44,9 @@ export async function withGitHubGuards(repoConfig, repoInfo, options, guards) {
|
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
|
-
/** Type predicate that narrows entries with an active (non-"unchanged") action. */
|
|
47
47
|
export function isActiveAction(entry) {
|
|
48
48
|
return entry.action !== "unchanged";
|
|
49
49
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Count actions from a diff result array.
|
|
52
|
-
* Works with any change type that has an `action` field.
|
|
53
|
-
*/
|
|
54
50
|
export function countActions(changes) {
|
|
55
51
|
const counts = {
|
|
56
52
|
create: 0,
|
|
@@ -75,6 +71,31 @@ export function formatChangeSummary(counts) {
|
|
|
75
71
|
parts.push(`${counts.unchanged} unchanged`);
|
|
76
72
|
return parts.length > 0 ? parts.join(", ") : "no changes";
|
|
77
73
|
}
|
|
74
|
+
export function formatChangeLines(changes, formatValue) {
|
|
75
|
+
const lines = [];
|
|
76
|
+
const entries = [];
|
|
77
|
+
const { create: creates, update: updates } = countActions(changes);
|
|
78
|
+
for (const change of changes) {
|
|
79
|
+
if (change.action === "create") {
|
|
80
|
+
lines.push(chalk.green(` + ${change.property}: ${formatValue(change.newValue)}`));
|
|
81
|
+
entries.push({
|
|
82
|
+
property: change.property,
|
|
83
|
+
action: "create",
|
|
84
|
+
newValue: change.newValue,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else if (change.action === "update") {
|
|
88
|
+
lines.push(chalk.yellow(` ~ ${change.property}: ${formatValue(change.oldValue)} → ${formatValue(change.newValue)}`));
|
|
89
|
+
entries.push({
|
|
90
|
+
property: change.property,
|
|
91
|
+
action: "update",
|
|
92
|
+
oldValue: change.oldValue,
|
|
93
|
+
newValue: change.newValue,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { lines, entries, creates, updates };
|
|
98
|
+
}
|
|
78
99
|
/**
|
|
79
100
|
* Build a standardized dry-run result for settings processors.
|
|
80
101
|
* Returns an intersection of BaseProcessorResult with the extra fields,
|
|
@@ -44,8 +44,8 @@ export function diffCodeScanning(current, desired) {
|
|
|
44
44
|
}
|
|
45
45
|
// languages: only diff if specified in desired (sorted comparison)
|
|
46
46
|
if (desired.languages !== undefined) {
|
|
47
|
-
const currentLangs = [...(current.languages ?? [])].sort();
|
|
48
|
-
const desiredLangs = [...desired.languages].sort();
|
|
47
|
+
const currentLangs = [...(current.languages ?? [])].sort((a, b) => a.localeCompare(b));
|
|
48
|
+
const desiredLangs = [...desired.languages].sort((a, b) => a.localeCompare(b));
|
|
49
49
|
const langsMatch = currentLangs.length === desiredLangs.length &&
|
|
50
50
|
currentLangs.every((lang, i) => lang === desiredLangs[i]);
|
|
51
51
|
if (!langsMatch) {
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import type { CodeScanningChange } from "./diff.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
action: "create" | "update";
|
|
5
|
-
oldValue?: unknown;
|
|
6
|
-
newValue?: unknown;
|
|
7
|
-
}
|
|
2
|
+
import { type PlanEntry } from "../base-processor.js";
|
|
3
|
+
export type CodeScanningPlanEntry = PlanEntry;
|
|
8
4
|
export interface CodeScanningPlanResult {
|
|
9
5
|
lines: string[];
|
|
10
6
|
creates: number;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
1
|
import { formatScalarValue } from "../../shared/string-utils.js";
|
|
3
|
-
import {
|
|
2
|
+
import { formatChangeLines } from "../base-processor.js";
|
|
4
3
|
function formatValue(val) {
|
|
5
4
|
if (Array.isArray(val)) {
|
|
6
5
|
return `[${val.join(", ")}]`;
|
|
@@ -11,27 +10,5 @@ function formatValue(val) {
|
|
|
11
10
|
* Formats code scanning changes as Terraform-style plan output.
|
|
12
11
|
*/
|
|
13
12
|
export function formatCodeScanningPlan(changes) {
|
|
14
|
-
|
|
15
|
-
const entries = [];
|
|
16
|
-
const { create: creates, update: updates } = countActions(changes);
|
|
17
|
-
for (const change of changes) {
|
|
18
|
-
if (change.action === "create") {
|
|
19
|
-
lines.push(chalk.green(` + ${change.property}: ${formatValue(change.newValue)}`));
|
|
20
|
-
entries.push({
|
|
21
|
-
property: change.property,
|
|
22
|
-
action: "create",
|
|
23
|
-
newValue: change.newValue,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
else if (change.action === "update") {
|
|
27
|
-
lines.push(chalk.yellow(` ~ ${change.property}: ${formatValue(change.oldValue)} → ${formatValue(change.newValue)}`));
|
|
28
|
-
entries.push({
|
|
29
|
-
property: change.property,
|
|
30
|
-
action: "update",
|
|
31
|
-
oldValue: change.oldValue,
|
|
32
|
-
newValue: change.newValue,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return { lines, creates, updates, entries };
|
|
13
|
+
return formatChangeLines(changes, formatValue);
|
|
37
14
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ICommandExecutor } from "../../shared/command-executor.js";
|
|
2
2
|
import { type RepoInfo } from "../../repo/index.js";
|
|
3
3
|
import { type GhApiOptions } from "../../shared/gh-api-utils.js";
|
|
4
|
-
import type { ICodeScanningStrategy, CurrentCodeScanningSettings } from "./types.js";
|
|
4
|
+
import type { ICodeScanningStrategy, CurrentCodeScanningSettings, CodeScanningUpdateParams } from "./types.js";
|
|
5
5
|
interface GitHubCodeScanningStrategyOptions {
|
|
6
6
|
retries?: number;
|
|
7
7
|
cwd: string;
|
|
@@ -9,11 +9,7 @@ interface GitHubCodeScanningStrategyOptions {
|
|
|
9
9
|
export declare class GitHubCodeScanningStrategy implements ICodeScanningStrategy {
|
|
10
10
|
private api;
|
|
11
11
|
constructor(executor: ICommandExecutor, options: GitHubCodeScanningStrategyOptions);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
state: string;
|
|
15
|
-
query_suite?: string;
|
|
16
|
-
languages?: string[];
|
|
17
|
-
}, options?: GhApiOptions): Promise<void>;
|
|
12
|
+
get(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentCodeScanningSettings>;
|
|
13
|
+
update(repoInfo: RepoInfo, settings: CodeScanningUpdateParams, options?: GhApiOptions): Promise<void>;
|
|
18
14
|
}
|
|
19
15
|
export {};
|
|
@@ -6,13 +6,13 @@ export class GitHubCodeScanningStrategy {
|
|
|
6
6
|
constructor(executor, options) {
|
|
7
7
|
this.api = new GhApiClient(executor, options.retries ?? 3, options.cwd);
|
|
8
8
|
}
|
|
9
|
-
async
|
|
9
|
+
async get(repoInfo, options) {
|
|
10
10
|
assertGitHubRepo(repoInfo, "GitHub Code Scanning strategy");
|
|
11
11
|
const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}/code-scanning/default-setup`;
|
|
12
12
|
const result = await this.api.call("GET", endpoint, { options });
|
|
13
13
|
return parseApiJson(result, "code scanning default setup response");
|
|
14
14
|
}
|
|
15
|
-
async
|
|
15
|
+
async update(repoInfo, settings, options) {
|
|
16
16
|
assertGitHubRepo(repoInfo, "GitHub Code Scanning strategy");
|
|
17
17
|
const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}/code-scanning/default-setup`;
|
|
18
18
|
await this.api.call("PATCH", endpoint, { payload: settings, options });
|
|
@@ -20,8 +20,10 @@ export class CodeScanningProcessor {
|
|
|
20
20
|
}
|
|
21
21
|
async applySettings(githubRepo, repoConfig, options, effectiveToken, repoName) {
|
|
22
22
|
const { dryRun } = options;
|
|
23
|
-
const desiredSettings = repoConfig.settings
|
|
24
|
-
|
|
23
|
+
const desiredSettings = repoConfig.settings?.codeScanning;
|
|
24
|
+
if (!desiredSettings || typeof desiredSettings !== "object") {
|
|
25
|
+
throw new Error("applySettings called without codeScanning settings");
|
|
26
|
+
}
|
|
25
27
|
const strategyOptions = { token: effectiveToken, host: githubRepo.host };
|
|
26
28
|
// Validate GHAS availability for private repos
|
|
27
29
|
const metadata = await this.metadataProvider.getMetadata(githubRepo, strategyOptions);
|
|
@@ -34,7 +36,7 @@ export class CodeScanningProcessor {
|
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
// Fetch current settings
|
|
37
|
-
const currentSettings = await this.strategy.
|
|
39
|
+
const currentSettings = await this.strategy.get(githubRepo, strategyOptions);
|
|
38
40
|
// Compute diff
|
|
39
41
|
const changes = diffCodeScanning(currentSettings, desiredSettings);
|
|
40
42
|
const changeCounts = countActions(changes);
|
|
@@ -61,7 +63,7 @@ export class CodeScanningProcessor {
|
|
|
61
63
|
if (desiredSettings.languages !== undefined) {
|
|
62
64
|
payload.languages = desiredSettings.languages;
|
|
63
65
|
}
|
|
64
|
-
await this.strategy.
|
|
66
|
+
await this.strategy.update(githubRepo, payload, strategyOptions);
|
|
65
67
|
const appliedCount = changes.filter((c) => c.action !== "unchanged").length;
|
|
66
68
|
return buildApplyResult(repoName, changeCounts, appliedCount, {
|
|
67
69
|
planOutput,
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import type { RepoInfo } from "../../repo/index.js";
|
|
2
2
|
import type { GhApiOptions } from "../../shared/gh-api-utils.js";
|
|
3
|
+
import type { CodeScanningState, CodeScanningQuerySuite } from "../../config/types.js";
|
|
3
4
|
/**
|
|
4
5
|
* Current code scanning default setup state from GitHub API.
|
|
5
6
|
*/
|
|
6
7
|
export interface CurrentCodeScanningSettings {
|
|
7
|
-
state:
|
|
8
|
-
query_suite?:
|
|
8
|
+
state: CodeScanningState;
|
|
9
|
+
query_suite?: CodeScanningQuerySuite;
|
|
10
|
+
languages?: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface CodeScanningUpdateParams {
|
|
13
|
+
state: string;
|
|
14
|
+
query_suite?: string;
|
|
9
15
|
languages?: string[];
|
|
10
16
|
}
|
|
11
17
|
/**
|
|
@@ -13,10 +19,6 @@ export interface CurrentCodeScanningSettings {
|
|
|
13
19
|
* Abstracts the GitHub API calls for testability.
|
|
14
20
|
*/
|
|
15
21
|
export interface ICodeScanningStrategy {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
state: string;
|
|
19
|
-
query_suite?: string;
|
|
20
|
-
languages?: string[];
|
|
21
|
-
}, options?: GhApiOptions): Promise<void>;
|
|
22
|
+
get(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentCodeScanningSettings>;
|
|
23
|
+
update(repoInfo: RepoInfo, settings: CodeScanningUpdateParams, options?: GhApiOptions): Promise<void>;
|
|
22
24
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ICommandExecutor } from "../../shared/command-executor.js";
|
|
2
2
|
import { type RepoInfo } from "../../repo/index.js";
|
|
3
3
|
import { type GhApiOptions } from "../../shared/gh-api-utils.js";
|
|
4
|
-
import type { ILabelsStrategy, GitHubLabel } from "./types.js";
|
|
4
|
+
import type { ILabelsStrategy, GitHubLabel, LabelCreateParams, LabelUpdateParams } from "./types.js";
|
|
5
5
|
interface GitHubLabelsStrategyOptions {
|
|
6
6
|
retries?: number;
|
|
7
7
|
cwd: string;
|
|
@@ -10,16 +10,8 @@ export declare class GitHubLabelsStrategy implements ILabelsStrategy {
|
|
|
10
10
|
private api;
|
|
11
11
|
constructor(executor: ICommandExecutor, options: GitHubLabelsStrategyOptions);
|
|
12
12
|
list(repoInfo: RepoInfo, options?: GhApiOptions): Promise<GitHubLabel[]>;
|
|
13
|
-
create(repoInfo: RepoInfo, label:
|
|
14
|
-
|
|
15
|
-
color: string;
|
|
16
|
-
description?: string;
|
|
17
|
-
}, options?: GhApiOptions): Promise<void>;
|
|
18
|
-
update(repoInfo: RepoInfo, currentName: string, label: {
|
|
19
|
-
new_name?: string;
|
|
20
|
-
color?: string;
|
|
21
|
-
description?: string;
|
|
22
|
-
}, options?: GhApiOptions): Promise<void>;
|
|
13
|
+
create(repoInfo: RepoInfo, label: LabelCreateParams, options?: GhApiOptions): Promise<void>;
|
|
14
|
+
update(repoInfo: RepoInfo, currentName: string, label: LabelUpdateParams, options?: GhApiOptions): Promise<void>;
|
|
23
15
|
delete(repoInfo: RepoInfo, name: string, options?: GhApiOptions): Promise<void>;
|
|
24
16
|
}
|
|
25
17
|
export {};
|
|
@@ -11,17 +11,19 @@ export interface GitHubLabel {
|
|
|
11
11
|
* Strategy interface for label operations.
|
|
12
12
|
* Abstracts platform-specific API calls.
|
|
13
13
|
*/
|
|
14
|
+
export interface LabelCreateParams {
|
|
15
|
+
name: string;
|
|
16
|
+
color: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface LabelUpdateParams {
|
|
20
|
+
new_name?: string;
|
|
21
|
+
color?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
}
|
|
14
24
|
export interface ILabelsStrategy {
|
|
15
25
|
list(repoInfo: RepoInfo, options?: GhApiOptions): Promise<GitHubLabel[]>;
|
|
16
|
-
create(repoInfo: RepoInfo, label:
|
|
17
|
-
|
|
18
|
-
color: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
}, options?: GhApiOptions): Promise<void>;
|
|
21
|
-
update(repoInfo: RepoInfo, currentName: string, label: {
|
|
22
|
-
new_name?: string;
|
|
23
|
-
color?: string;
|
|
24
|
-
description?: string;
|
|
25
|
-
}, options?: GhApiOptions): Promise<void>;
|
|
26
|
+
create(repoInfo: RepoInfo, label: LabelCreateParams, options?: GhApiOptions): Promise<void>;
|
|
27
|
+
update(repoInfo: RepoInfo, currentName: string, label: LabelUpdateParams, options?: GhApiOptions): Promise<void>;
|
|
26
28
|
delete(repoInfo: RepoInfo, name: string, options?: GhApiOptions): Promise<void>;
|
|
27
29
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { GitHubRepoSettings } from "../../config/index.js";
|
|
2
2
|
import type { SettingsAction } from "../base-processor.js";
|
|
3
3
|
import type { CurrentRepoSettings } from "./types.js";
|
|
4
|
-
export type RepoSettingsAction = Exclude<SettingsAction, "delete">;
|
|
4
|
+
export type RepoSettingsAction = Exclude<SettingsAction, "delete" | "unchanged">;
|
|
5
5
|
export interface RepoSettingsChange {
|
|
6
6
|
property: keyof GitHubRepoSettings;
|
|
7
7
|
action: RepoSettingsAction;
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
+
import { type PlanEntry } from "../base-processor.js";
|
|
1
2
|
import type { RepoSettingsChange } from "./diff.js";
|
|
2
|
-
export
|
|
3
|
-
property: string;
|
|
4
|
-
action: "create" | "update";
|
|
5
|
-
oldValue?: unknown;
|
|
6
|
-
newValue?: unknown;
|
|
7
|
-
}
|
|
3
|
+
export type RepoSettingsPlanEntry = PlanEntry;
|
|
8
4
|
export interface RepoSettingsPlanResult {
|
|
9
5
|
lines: string[];
|
|
10
6
|
creates: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { formatScalarValue } from "../../shared/string-utils.js";
|
|
3
|
-
import {
|
|
3
|
+
import { formatChangeLines } from "../base-processor.js";
|
|
4
4
|
/**
|
|
5
5
|
* Format a value for display.
|
|
6
6
|
*/
|
|
@@ -32,37 +32,18 @@ function getWarning(change) {
|
|
|
32
32
|
* Formats repo settings changes as Terraform-style plan output.
|
|
33
33
|
*/
|
|
34
34
|
export function formatRepoSettingsPlan(changes) {
|
|
35
|
-
const lines = [];
|
|
36
35
|
const warnings = [];
|
|
37
|
-
const { create: creates, update: updates } = countActions(changes);
|
|
38
|
-
const entries = [];
|
|
39
36
|
if (changes.length === 0) {
|
|
40
|
-
return { lines, creates, updates, warnings, entries };
|
|
37
|
+
return { lines: [], creates: 0, updates: 0, warnings, entries: [] };
|
|
41
38
|
}
|
|
42
39
|
for (const change of changes) {
|
|
43
40
|
const warning = getWarning(change);
|
|
44
41
|
if (warning) {
|
|
45
42
|
warnings.push(warning);
|
|
46
43
|
}
|
|
47
|
-
if (change.action === "create") {
|
|
48
|
-
lines.push(chalk.green(` + ${change.property}: ${formatValue(change.newValue)}`));
|
|
49
|
-
entries.push({
|
|
50
|
-
property: change.property,
|
|
51
|
-
action: "create",
|
|
52
|
-
newValue: change.newValue,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
else if (change.action === "update") {
|
|
56
|
-
lines.push(chalk.yellow(` ~ ${change.property}: ${formatValue(change.oldValue)} → ${formatValue(change.newValue)}`));
|
|
57
|
-
entries.push({
|
|
58
|
-
property: change.property,
|
|
59
|
-
action: "update",
|
|
60
|
-
oldValue: change.oldValue,
|
|
61
|
-
newValue: change.newValue,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
44
|
}
|
|
65
|
-
|
|
45
|
+
const result = formatChangeLines(changes, formatValue);
|
|
46
|
+
return { ...result, warnings };
|
|
66
47
|
}
|
|
67
48
|
/**
|
|
68
49
|
* Formats warnings for display.
|
|
@@ -13,8 +13,8 @@ export declare class GitHubRepoSettingsStrategy implements IRepoSettingsStrategy
|
|
|
13
13
|
get(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentRepoSettings>;
|
|
14
14
|
update(repoInfo: RepoInfo, settings: GitHubRepoSettings, options?: GhApiOptions): Promise<void>;
|
|
15
15
|
updateVulnerabilityAlerts(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
updateAutomatedSecurityFixes(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
17
|
+
updatePrivateVulnerabilityReporting(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
18
18
|
branchExists(repoInfo: RepoInfo, branch: string, options?: GhApiOptions): Promise<boolean>;
|
|
19
19
|
private getVulnerabilityAlerts;
|
|
20
20
|
private getAutomatedSecurityFixes;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assertGitHubRepo, } from "../../repo/index.js";
|
|
2
|
-
import { GhApiClient
|
|
2
|
+
import { GhApiClient } from "../../shared/gh-api-utils.js";
|
|
3
|
+
import { isHttp404Error } from "../../shared/gh-token-utils.js";
|
|
3
4
|
import { parseApiJson } from "../../shared/json-utils.js";
|
|
4
5
|
import { camelToSnake } from "../../shared/string-utils.js";
|
|
5
6
|
/**
|
|
@@ -71,7 +72,7 @@ export class GitHubRepoSettingsStrategy {
|
|
|
71
72
|
settings.owner_type = parsed.owner?.type;
|
|
72
73
|
settings.vulnerability_alerts = await this.getVulnerabilityAlerts(repoInfo, options);
|
|
73
74
|
// Pass vulnerability_alerts state - automated security fixes requires it enabled
|
|
74
|
-
settings.automated_security_fixes = await this.getAutomatedSecurityFixes(repoInfo, options
|
|
75
|
+
settings.automated_security_fixes = await this.getAutomatedSecurityFixes(repoInfo, options);
|
|
75
76
|
settings.private_vulnerability_reporting =
|
|
76
77
|
await this.getPrivateVulnerabilityReporting(repoInfo, options);
|
|
77
78
|
return settings;
|
|
@@ -92,13 +93,13 @@ export class GitHubRepoSettingsStrategy {
|
|
|
92
93
|
const method = enable ? "PUT" : "DELETE";
|
|
93
94
|
await this.api.call(method, endpoint, { options });
|
|
94
95
|
}
|
|
95
|
-
async
|
|
96
|
+
async updateAutomatedSecurityFixes(repoInfo, enable, options) {
|
|
96
97
|
assertGitHubRepo(repoInfo, "GitHub Repo Settings strategy");
|
|
97
98
|
const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}/automated-security-fixes`;
|
|
98
99
|
const method = enable ? "PUT" : "DELETE";
|
|
99
100
|
await this.api.call(method, endpoint, { options });
|
|
100
101
|
}
|
|
101
|
-
async
|
|
102
|
+
async updatePrivateVulnerabilityReporting(repoInfo, enable, options) {
|
|
102
103
|
assertGitHubRepo(repoInfo, "GitHub Repo Settings strategy");
|
|
103
104
|
const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}/private-vulnerability-reporting`;
|
|
104
105
|
const method = enable ? "PUT" : "DELETE";
|
|
@@ -128,10 +129,10 @@ export class GitHubRepoSettingsStrategy {
|
|
|
128
129
|
if (isHttp404Error(error)) {
|
|
129
130
|
return false; // 404 = disabled
|
|
130
131
|
}
|
|
131
|
-
throw error;
|
|
132
|
+
throw error;
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
|
-
async getAutomatedSecurityFixes(github, options
|
|
135
|
+
async getAutomatedSecurityFixes(github, options) {
|
|
135
136
|
// Note: GitHub returns JSON with {enabled: boolean} for this endpoint
|
|
136
137
|
const endpoint = `/repos/${github.owner}/${github.repo}/automated-security-fixes`;
|
|
137
138
|
try {
|
|
@@ -162,7 +163,7 @@ export class GitHubRepoSettingsStrategy {
|
|
|
162
163
|
if (isHttp404Error(error)) {
|
|
163
164
|
return false; // 404 = not available (e.g. private repos)
|
|
164
165
|
}
|
|
165
|
-
throw error;
|
|
166
|
+
throw error;
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -20,7 +20,10 @@ export class RepoSettingsProcessor {
|
|
|
20
20
|
}
|
|
21
21
|
async applySettings(githubRepo, repoConfig, options, effectiveToken, repoName) {
|
|
22
22
|
const { dryRun } = options;
|
|
23
|
-
const desiredSettings = repoConfig.settings
|
|
23
|
+
const desiredSettings = repoConfig.settings?.repo;
|
|
24
|
+
if (!desiredSettings || typeof desiredSettings !== "object") {
|
|
25
|
+
throw new Error("applySettings called without repo settings");
|
|
26
|
+
}
|
|
24
27
|
const strategyOptions = { token: effectiveToken, host: githubRepo.host };
|
|
25
28
|
// Fetch current settings and metadata in parallel
|
|
26
29
|
const [currentSettings, metadata] = await Promise.all([
|
|
@@ -39,16 +42,15 @@ export class RepoSettingsProcessor {
|
|
|
39
42
|
// Compute diff
|
|
40
43
|
const changes = diffRepoSettings(currentSettings, desiredSettings);
|
|
41
44
|
if (!hasRepoSettingsChanges(changes)) {
|
|
42
|
-
const unchangedCount = changes.filter((c) => c.action === "unchanged").length;
|
|
43
45
|
return {
|
|
44
46
|
success: true,
|
|
45
47
|
repoName,
|
|
46
48
|
message: "No changes needed",
|
|
47
|
-
changes: { create: 0, update: 0, delete: 0, unchanged:
|
|
49
|
+
changes: { create: 0, update: 0, delete: 0, unchanged: 0 },
|
|
48
50
|
};
|
|
49
51
|
}
|
|
50
52
|
// Validate defaultBranch target exists before attempting to apply
|
|
51
|
-
const defaultBranchChange = changes.find((c) => c.property === "defaultBranch"
|
|
53
|
+
const defaultBranchChange = changes.find((c) => c.property === "defaultBranch");
|
|
52
54
|
if (defaultBranchChange) {
|
|
53
55
|
const targetBranch = String(defaultBranchChange.newValue);
|
|
54
56
|
const exists = await this.strategy.branchExists(githubRepo, targetBranch, strategyOptions);
|
|
@@ -69,7 +71,7 @@ export class RepoSettingsProcessor {
|
|
|
69
71
|
create: planOutput.creates,
|
|
70
72
|
update: planOutput.updates,
|
|
71
73
|
delete: 0,
|
|
72
|
-
unchanged:
|
|
74
|
+
unchanged: 0,
|
|
73
75
|
};
|
|
74
76
|
if (dryRun) {
|
|
75
77
|
return buildDryRunResult(repoName, changeCounts, {
|
|
@@ -80,10 +82,8 @@ export class RepoSettingsProcessor {
|
|
|
80
82
|
// Apply changes - only send settings that actually changed
|
|
81
83
|
const changedSettings = {};
|
|
82
84
|
for (const change of changes) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
change.newValue;
|
|
86
|
-
}
|
|
85
|
+
changedSettings[change.property] =
|
|
86
|
+
change.newValue;
|
|
87
87
|
}
|
|
88
88
|
await this.applyChanges(githubRepo, changedSettings, strategyOptions);
|
|
89
89
|
const appliedCount = Object.keys(changedSettings).length;
|
|
@@ -106,12 +106,12 @@ export class RepoSettingsProcessor {
|
|
|
106
106
|
}
|
|
107
107
|
// Handle private vulnerability reporting (separate endpoint)
|
|
108
108
|
if (privateVulnerabilityReporting !== undefined) {
|
|
109
|
-
await this.strategy.
|
|
109
|
+
await this.strategy.updatePrivateVulnerabilityReporting(repoInfo, privateVulnerabilityReporting, options);
|
|
110
110
|
}
|
|
111
111
|
// Handle automated security fixes (separate endpoint)
|
|
112
112
|
// Done last to ensure vulnerability alerts have been fully processed
|
|
113
113
|
if (automatedSecurityFixes !== undefined) {
|
|
114
|
-
await this.strategy.
|
|
114
|
+
await this.strategy.updateAutomatedSecurityFixes(repoInfo, automatedSecurityFixes, options);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
validateSecuritySettings(desiredSettings, metadata) {
|
|
@@ -58,11 +58,11 @@ export interface IRepoSettingsStrategy {
|
|
|
58
58
|
/**
|
|
59
59
|
* Enables or disables automated security fixes.
|
|
60
60
|
*/
|
|
61
|
-
|
|
61
|
+
updateAutomatedSecurityFixes(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
62
62
|
/**
|
|
63
63
|
* Enables or disables private vulnerability reporting.
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
updatePrivateVulnerabilityReporting(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
|
|
66
66
|
/**
|
|
67
67
|
* Checks whether a branch exists in the repository.
|
|
68
68
|
*/
|