@aspruyt/xfg 5.1.3 → 5.1.4
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/README.md +1 -0
- package/dist/cli/lifecycle-report-builder.d.ts +2 -0
- package/dist/cli/lifecycle-report-builder.js +15 -0
- package/dist/cli/program.js +16 -11
- package/dist/cli/settings-report-builder.d.ts +1 -1
- package/dist/cli/settings-report-builder.js +20 -32
- package/dist/cli/sync-command.js +59 -64
- package/dist/cli/types.d.ts +3 -6
- package/dist/config/formatter.js +1 -6
- package/dist/config/index.d.ts +2 -1
- package/dist/config/index.js +2 -0
- package/dist/config/merge.d.ts +0 -6
- package/dist/config/merge.js +0 -13
- package/dist/config/types.d.ts +0 -9
- package/dist/config/validator.d.ts +1 -1
- package/dist/config/validator.js +1 -1
- package/dist/lifecycle/github-lifecycle-provider.js +28 -45
- package/dist/lifecycle/lifecycle-helpers.d.ts +1 -1
- package/dist/lifecycle/repo-lifecycle-manager.d.ts +1 -1
- package/dist/lifecycle/types.d.ts +4 -4
- package/dist/output/github-summary.d.ts +0 -50
- package/dist/output/github-summary.js +0 -201
- package/dist/output/lifecycle-report.d.ts +0 -1
- package/dist/output/lifecycle-report.js +0 -15
- package/dist/output/settings-report.d.ts +1 -1
- package/dist/output/settings-report.js +15 -77
- package/dist/output/sync-report.d.ts +1 -0
- package/dist/output/sync-report.js +19 -1
- package/dist/output/unified-summary.d.ts +1 -2
- package/dist/output/unified-summary.js +1 -19
- package/dist/settings/base-processor.d.ts +8 -0
- package/dist/settings/base-processor.js +7 -0
- package/dist/settings/index.d.ts +2 -2
- package/dist/settings/index.js +3 -1
- package/dist/settings/labels/converter.d.ts +1 -1
- package/dist/settings/labels/diff.d.ts +1 -1
- package/dist/settings/labels/formatter.d.ts +1 -1
- package/dist/settings/repo-settings/processor.js +9 -5
- package/dist/settings/rulesets/formatter.js +97 -85
- package/dist/settings/rulesets/github-ruleset-strategy.d.ts +2 -2
- package/dist/settings/rulesets/github-ruleset-strategy.js +7 -4
- package/dist/settings/rulesets/index.d.ts +1 -1
- package/dist/settings/rulesets/index.js +0 -2
- package/dist/settings/rulesets/processor.js +5 -1
- package/dist/settings/rulesets/types.d.ts +6 -1
- package/dist/shared/gh-api-utils.d.ts +1 -1
- package/dist/shared/logger.d.ts +8 -8
- package/dist/shared/logger.js +9 -8
- package/dist/shared/repo-detector.js +0 -1
- package/dist/sync/branch-manager.js +2 -2
- package/dist/sync/commit-push-manager.js +2 -2
- package/dist/sync/diff-utils.d.ts +0 -9
- package/dist/sync/diff-utils.js +0 -9
- package/dist/sync/file-sync-orchestrator.js +1 -1
- package/dist/sync/file-sync-strategy.d.ts +1 -1
- package/dist/sync/file-writer.d.ts +1 -1
- package/dist/sync/file-writer.js +3 -1
- package/dist/sync/index.d.ts +1 -2
- package/dist/sync/index.js +1 -1
- package/dist/sync/manifest-manager.d.ts +1 -1
- package/dist/sync/manifest-manager.js +1 -1
- package/dist/sync/manifest.js +9 -1
- package/dist/sync/pr-merge-handler.js +2 -2
- package/dist/sync/sync-workflow.d.ts +1 -1
- package/dist/sync/types.d.ts +2 -2
- package/dist/vcs/authenticated-git-ops.d.ts +2 -1
- package/dist/vcs/commit-strategy-selector.d.ts +1 -1
- package/dist/vcs/commit-strategy-selector.js +1 -1
- package/dist/vcs/git-ops.d.ts +0 -1
- package/dist/vcs/git-ops.js +15 -8
- package/dist/vcs/github-pr-strategy.d.ts +0 -3
- package/dist/vcs/github-pr-strategy.js +4 -7
- package/dist/vcs/index.d.ts +3 -3
- package/dist/vcs/index.js +2 -2
- package/dist/vcs/pr-creator.js +9 -9
- package/dist/vcs/pr-strategy-factory.d.ts +1 -1
- package/dist/vcs/pr-strategy-factory.js +1 -1
- package/dist/vcs/pr-strategy.js +6 -11
- package/package.json +1 -1
- package/dist/output/summary-utils.d.ts +0 -20
- package/dist/output/summary-utils.js +0 -79
|
@@ -35,6 +35,26 @@ const POST_CREATE_PERMANENT_PATTERNS = DEFAULT_PERMANENT_ERROR_PATTERNS.filter((
|
|
|
35
35
|
* Interval between fork readiness checks (2 seconds).
|
|
36
36
|
*/
|
|
37
37
|
const FORK_POLL_INTERVAL_MS = 2_000;
|
|
38
|
+
function buildRepoCreateFlags(parts, settings) {
|
|
39
|
+
if (settings?.visibility === "public") {
|
|
40
|
+
parts.push("--public");
|
|
41
|
+
}
|
|
42
|
+
else if (settings?.visibility === "internal") {
|
|
43
|
+
parts.push("--internal");
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
parts.push("--private");
|
|
47
|
+
}
|
|
48
|
+
if (settings?.description) {
|
|
49
|
+
parts.push("--description", escapeShellArg(settings.description));
|
|
50
|
+
}
|
|
51
|
+
if (settings?.hasIssues === false) {
|
|
52
|
+
parts.push("--disable-issues");
|
|
53
|
+
}
|
|
54
|
+
if (settings?.hasWiki === false) {
|
|
55
|
+
parts.push("--disable-wiki");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
38
58
|
export class GitHubLifecycleProvider {
|
|
39
59
|
platform = "github";
|
|
40
60
|
executor;
|
|
@@ -127,27 +147,7 @@ export class GitHubLifecycleProvider {
|
|
|
127
147
|
"gh repo create",
|
|
128
148
|
escapeShellArg(`${repoInfo.owner}/${repoInfo.repo}`),
|
|
129
149
|
];
|
|
130
|
-
|
|
131
|
-
if (settings?.visibility === "public") {
|
|
132
|
-
parts.push("--public");
|
|
133
|
-
}
|
|
134
|
-
else if (settings?.visibility === "internal") {
|
|
135
|
-
parts.push("--internal");
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
parts.push("--private");
|
|
139
|
-
}
|
|
140
|
-
// Description
|
|
141
|
-
if (settings?.description) {
|
|
142
|
-
parts.push("--description", escapeShellArg(settings.description));
|
|
143
|
-
}
|
|
144
|
-
// Disable features if specified
|
|
145
|
-
if (settings?.hasIssues === false) {
|
|
146
|
-
parts.push("--disable-issues");
|
|
147
|
-
}
|
|
148
|
-
if (settings?.hasWiki === false) {
|
|
149
|
-
parts.push("--disable-wiki");
|
|
150
|
-
}
|
|
150
|
+
buildRepoCreateFlags(parts, settings);
|
|
151
151
|
// Add --add-readme to establish the default branch via an initial commit.
|
|
152
152
|
// This avoids empty repos where HEAD doesn't resolve.
|
|
153
153
|
parts.push("--add-readme");
|
|
@@ -312,27 +312,7 @@ export class GitHubLifecycleProvider {
|
|
|
312
312
|
escapeShellArg(sourceDir),
|
|
313
313
|
"--push",
|
|
314
314
|
];
|
|
315
|
-
|
|
316
|
-
if (settings?.visibility === "public") {
|
|
317
|
-
parts.push("--public");
|
|
318
|
-
}
|
|
319
|
-
else if (settings?.visibility === "internal") {
|
|
320
|
-
parts.push("--internal");
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
parts.push("--private");
|
|
324
|
-
}
|
|
325
|
-
// Description
|
|
326
|
-
if (settings?.description) {
|
|
327
|
-
parts.push("--description", escapeShellArg(settings.description));
|
|
328
|
-
}
|
|
329
|
-
// Disable features if specified
|
|
330
|
-
if (settings?.hasIssues === false) {
|
|
331
|
-
parts.push("--disable-issues");
|
|
332
|
-
}
|
|
333
|
-
if (settings?.hasWiki === false) {
|
|
334
|
-
parts.push("--disable-wiki");
|
|
335
|
-
}
|
|
315
|
+
buildRepoCreateFlags(parts, settings);
|
|
336
316
|
const command = parts.join(" ");
|
|
337
317
|
await withRetry(() => this.executor.exec(command, this.cwd, { env: tokenEnv }), {
|
|
338
318
|
retries: this.retries,
|
|
@@ -361,8 +341,8 @@ export class GitHubLifecycleProvider {
|
|
|
361
341
|
const timeoutMs = options?.timeoutMs ?? 15000;
|
|
362
342
|
const pollMs = options?.pollMs ?? 1000;
|
|
363
343
|
const { tokenEnv, prefix, apiPath } = this.buildGhApiPrefix(repoInfo, options?.token);
|
|
364
|
-
const
|
|
365
|
-
while (Date.now()
|
|
344
|
+
const deadline = Date.now() + timeoutMs;
|
|
345
|
+
while (Date.now() < deadline) {
|
|
366
346
|
try {
|
|
367
347
|
const branch = (await this.executor.exec(`${prefix}${apiPath} --jq '.default_branch'`, this.cwd, { env: tokenEnv })).trim();
|
|
368
348
|
if (branch === expectedBranch) {
|
|
@@ -372,7 +352,10 @@ export class GitHubLifecycleProvider {
|
|
|
372
352
|
catch (error) {
|
|
373
353
|
this.log?.debug(`Polling default branch: ${toErrorMessage(error)}`);
|
|
374
354
|
}
|
|
375
|
-
|
|
355
|
+
const remaining = deadline - Date.now();
|
|
356
|
+
if (remaining <= 0)
|
|
357
|
+
break;
|
|
358
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(pollMs, remaining)));
|
|
376
359
|
}
|
|
377
360
|
// Don't throw — rename succeeded, this is just a best-effort wait
|
|
378
361
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RepoConfig, GitHubRepoSettings } from "../config/
|
|
1
|
+
import type { RepoConfig, GitHubRepoSettings } from "../config/index.js";
|
|
2
2
|
import type { RepoInfo } from "../shared/repo-detector.js";
|
|
3
3
|
import type { IRepoLifecycleManager, CreateRepoSettings, LifecycleResult } from "./types.js";
|
|
4
4
|
interface LifecycleCheckOptions {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type RepoInfo } from "../shared/repo-detector.js";
|
|
2
2
|
import { type DebugInfoWarnLog } from "../shared/logger.js";
|
|
3
3
|
import type { ICommandExecutor } from "../shared/command-executor.js";
|
|
4
|
-
import type { RepoConfig } from "../config/
|
|
4
|
+
import type { RepoConfig } from "../config/index.js";
|
|
5
5
|
import type { IRepoLifecycleManager, IRepoLifecycleFactory, LifecycleResult, LifecycleOptions, CreateRepoSettings } from "./types.js";
|
|
6
6
|
/**
|
|
7
7
|
* Orchestrates repo lifecycle operations before sync.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RepoInfo } from "../shared/repo-detector.js";
|
|
2
|
-
import type { RepoConfig } from "../config/
|
|
2
|
+
import type { RepoConfig } from "../config/index.js";
|
|
3
3
|
export type LifecyclePlatform = "github" | "azure-devops" | "gitlab";
|
|
4
4
|
export interface LifecycleResult {
|
|
5
5
|
repoInfo: RepoInfo;
|
|
@@ -32,7 +32,7 @@ export interface IRepoLifecycleProvider {
|
|
|
32
32
|
readonly platform: LifecyclePlatform;
|
|
33
33
|
/**
|
|
34
34
|
* Check if a repository exists on this platform.
|
|
35
|
-
* @throws
|
|
35
|
+
* @throws LifecycleError on network/auth failures (NOT for "repo not found")
|
|
36
36
|
*/
|
|
37
37
|
exists(repoInfo: RepoInfo, token?: string): Promise<boolean>;
|
|
38
38
|
/**
|
|
@@ -64,12 +64,12 @@ export interface IMigrationSource {
|
|
|
64
64
|
export interface IRepoLifecycleFactory {
|
|
65
65
|
/**
|
|
66
66
|
* Get lifecycle provider for a platform.
|
|
67
|
-
* @throws
|
|
67
|
+
* @throws LifecycleError if platform not supported as target
|
|
68
68
|
*/
|
|
69
69
|
getProvider(platform: LifecyclePlatform): IRepoLifecycleProvider;
|
|
70
70
|
/**
|
|
71
71
|
* Get migration source for a platform.
|
|
72
|
-
* @throws
|
|
72
|
+
* @throws LifecycleError if platform not supported as source
|
|
73
73
|
*/
|
|
74
74
|
getMigrationSource(platform: LifecyclePlatform): IMigrationSource;
|
|
75
75
|
}
|
|
@@ -1,56 +1,6 @@
|
|
|
1
1
|
import type { DebugLog } from "../shared/logger.js";
|
|
2
|
-
import type { SettingsAction } from "../settings/index.js";
|
|
3
|
-
export type MergeOutcome = "manual" | "auto" | "force" | "direct";
|
|
4
|
-
export interface FileChanges {
|
|
5
|
-
added: number;
|
|
6
|
-
modified: number;
|
|
7
|
-
deleted: number;
|
|
8
|
-
unchanged: number;
|
|
9
|
-
}
|
|
10
|
-
export interface RulesetPlanDetail {
|
|
11
|
-
name: string;
|
|
12
|
-
action: SettingsAction;
|
|
13
|
-
propertyCount?: number;
|
|
14
|
-
propertyChanges?: {
|
|
15
|
-
added: number;
|
|
16
|
-
changed: number;
|
|
17
|
-
removed: number;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
export interface RepoSettingsPlanDetail {
|
|
21
|
-
property: string;
|
|
22
|
-
action: "create" | "update";
|
|
23
|
-
}
|
|
24
|
-
export interface LabelsPlanDetail {
|
|
25
|
-
name: string;
|
|
26
|
-
action: SettingsAction;
|
|
27
|
-
newName?: string;
|
|
28
|
-
}
|
|
29
|
-
export interface RepoResult {
|
|
30
|
-
repoName: string;
|
|
31
|
-
status: "succeeded" | "skipped" | "failed";
|
|
32
|
-
message: string;
|
|
33
|
-
prUrl?: string;
|
|
34
|
-
mergeOutcome?: MergeOutcome;
|
|
35
|
-
fileChanges?: FileChanges;
|
|
36
|
-
rulesetPlanDetails?: RulesetPlanDetail[];
|
|
37
|
-
repoSettingsPlanDetails?: RepoSettingsPlanDetail[];
|
|
38
|
-
labelsPlanDetails?: LabelsPlanDetail[];
|
|
39
|
-
}
|
|
40
|
-
export interface SummaryData {
|
|
41
|
-
title: string;
|
|
42
|
-
dryRun?: boolean;
|
|
43
|
-
total: number;
|
|
44
|
-
succeeded: number;
|
|
45
|
-
skipped: number;
|
|
46
|
-
failed: number;
|
|
47
|
-
results: RepoResult[];
|
|
48
|
-
}
|
|
49
|
-
export declare function formatSummary(data: SummaryData): string;
|
|
50
|
-
export declare function shouldWriteSummary(summaryPath: string | undefined): boolean;
|
|
51
2
|
/**
|
|
52
3
|
* Append markdown content to GITHUB_STEP_SUMMARY.
|
|
53
4
|
* No-op if summaryPath is not provided.
|
|
54
5
|
*/
|
|
55
6
|
export declare function writeGitHubStepSummary(markdown: string, summaryPath: string | undefined, log?: DebugLog): void;
|
|
56
|
-
export declare function writeSummary(data: SummaryData, summaryPath: string | undefined): void;
|
|
@@ -1,202 +1,5 @@
|
|
|
1
1
|
import { appendFileSync } from "node:fs";
|
|
2
2
|
import { toErrorMessage } from "../shared/type-guards.js";
|
|
3
|
-
function escapeMarkdown(text) {
|
|
4
|
-
// Escape backslashes first, then pipes (order matters to prevent double-escaping)
|
|
5
|
-
return text.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
|
|
6
|
-
}
|
|
7
|
-
function formatFileChanges(changes) {
|
|
8
|
-
if (!changes)
|
|
9
|
-
return "-";
|
|
10
|
-
return `+${changes.added} ~${changes.modified} -${changes.deleted}`;
|
|
11
|
-
}
|
|
12
|
-
function formatChangesColumn(result) {
|
|
13
|
-
if (result.fileChanges) {
|
|
14
|
-
return formatFileChanges(result.fileChanges);
|
|
15
|
-
}
|
|
16
|
-
// For settings results, derive changes from plan details
|
|
17
|
-
const parts = [];
|
|
18
|
-
if (result.rulesetPlanDetails && result.rulesetPlanDetails.length > 0) {
|
|
19
|
-
parts.push(formatPlanSummary(result.rulesetPlanDetails));
|
|
20
|
-
}
|
|
21
|
-
if (result.repoSettingsPlanDetails &&
|
|
22
|
-
result.repoSettingsPlanDetails.length > 0) {
|
|
23
|
-
parts.push(formatPlanSummary(result.repoSettingsPlanDetails));
|
|
24
|
-
}
|
|
25
|
-
if (result.labelsPlanDetails && result.labelsPlanDetails.length > 0) {
|
|
26
|
-
parts.push(formatPlanSummary(result.labelsPlanDetails));
|
|
27
|
-
}
|
|
28
|
-
return parts.length > 0 ? parts.join("; ") : "-";
|
|
29
|
-
}
|
|
30
|
-
function formatStatus(result, dryRun) {
|
|
31
|
-
if (result.status === "skipped")
|
|
32
|
-
return dryRun ? "⏭️ Would Skip" : "⏭️ Skipped";
|
|
33
|
-
if (result.status === "failed")
|
|
34
|
-
return dryRun ? "❌ Would Fail" : "❌ Failed";
|
|
35
|
-
// Succeeded - format based on merge outcome
|
|
36
|
-
switch (result.mergeOutcome) {
|
|
37
|
-
case "manual":
|
|
38
|
-
return dryRun ? "✅ Would Open" : "✅ Open";
|
|
39
|
-
case "auto":
|
|
40
|
-
return dryRun ? "✅ Would Auto-merge" : "✅ Auto-merge";
|
|
41
|
-
case "force":
|
|
42
|
-
return dryRun ? "✅ Would Merge" : "✅ Merged";
|
|
43
|
-
case "direct":
|
|
44
|
-
return dryRun ? "✅ Would Push" : "✅ Pushed";
|
|
45
|
-
default:
|
|
46
|
-
return dryRun ? "✅ Would Succeed" : "✅ Succeeded";
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function formatResult(result) {
|
|
50
|
-
if (result.prUrl) {
|
|
51
|
-
// Extract PR number from URL
|
|
52
|
-
const prMatch = result.prUrl.match(/\/pull\/(\d+)/);
|
|
53
|
-
const prNum = prMatch ? prMatch[1] : "PR";
|
|
54
|
-
return `[PR #${prNum}](${result.prUrl})`;
|
|
55
|
-
}
|
|
56
|
-
if (result.mergeOutcome === "direct") {
|
|
57
|
-
return "Direct to main";
|
|
58
|
-
}
|
|
59
|
-
return escapeMarkdown(result.message);
|
|
60
|
-
}
|
|
61
|
-
function formatRulesetAction(action) {
|
|
62
|
-
switch (action) {
|
|
63
|
-
case "create":
|
|
64
|
-
return "+ Create";
|
|
65
|
-
case "update":
|
|
66
|
-
return "~ Update";
|
|
67
|
-
case "delete":
|
|
68
|
-
return "- Delete";
|
|
69
|
-
case "unchanged":
|
|
70
|
-
return "= Unchanged";
|
|
71
|
-
default:
|
|
72
|
-
return action;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function formatRulesetProperties(detail) {
|
|
76
|
-
if (detail.propertyChanges) {
|
|
77
|
-
return `+${detail.propertyChanges.added} ~${detail.propertyChanges.changed} -${detail.propertyChanges.removed}`;
|
|
78
|
-
}
|
|
79
|
-
if (detail.propertyCount !== undefined) {
|
|
80
|
-
return `${detail.propertyCount} properties`;
|
|
81
|
-
}
|
|
82
|
-
return "-";
|
|
83
|
-
}
|
|
84
|
-
function formatPlanSummary(details) {
|
|
85
|
-
const counts = {};
|
|
86
|
-
for (const d of details) {
|
|
87
|
-
counts[d.action] = (counts[d.action] ?? 0) + 1;
|
|
88
|
-
}
|
|
89
|
-
const parts = [];
|
|
90
|
-
if (counts.create)
|
|
91
|
-
parts.push(`${counts.create} to create`);
|
|
92
|
-
if (counts.update)
|
|
93
|
-
parts.push(`${counts.update} to update`);
|
|
94
|
-
if (counts.delete)
|
|
95
|
-
parts.push(`${counts.delete} to delete`);
|
|
96
|
-
return parts.join(", ") || "no changes";
|
|
97
|
-
}
|
|
98
|
-
function formatSettingsAction(action) {
|
|
99
|
-
switch (action) {
|
|
100
|
-
case "create":
|
|
101
|
-
return "+ Create";
|
|
102
|
-
case "update":
|
|
103
|
-
return "~ Update";
|
|
104
|
-
default:
|
|
105
|
-
return action;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
export function formatSummary(data) {
|
|
109
|
-
const lines = [];
|
|
110
|
-
// Header
|
|
111
|
-
const titleSuffix = data.dryRun ? " (Dry Run)" : "";
|
|
112
|
-
lines.push(`## ${data.title}${titleSuffix}`);
|
|
113
|
-
lines.push("");
|
|
114
|
-
// Dry-run warning banner
|
|
115
|
-
if (data.dryRun) {
|
|
116
|
-
lines.push("> [!WARNING]");
|
|
117
|
-
lines.push("> This was a dry run — no changes were applied");
|
|
118
|
-
lines.push("");
|
|
119
|
-
}
|
|
120
|
-
// Stats table
|
|
121
|
-
const succeededLabel = data.dryRun ? "✅ Would Succeed" : "✅ Succeeded";
|
|
122
|
-
const skippedLabel = data.dryRun ? "⏭️ Would Skip" : "⏭️ Skipped";
|
|
123
|
-
const failedLabel = data.dryRun ? "❌ Would Fail" : "❌ Failed";
|
|
124
|
-
lines.push("| Status | Count |");
|
|
125
|
-
lines.push("|--------|-------|");
|
|
126
|
-
lines.push(`| ${succeededLabel} | ${data.succeeded} |`);
|
|
127
|
-
lines.push(`| ${skippedLabel} | ${data.skipped} |`);
|
|
128
|
-
lines.push(`| ${failedLabel} | ${data.failed} |`);
|
|
129
|
-
lines.push(`| **Total** | **${data.total}** |`);
|
|
130
|
-
// Repo details table (only if there are results)
|
|
131
|
-
if (data.results.length > 0) {
|
|
132
|
-
lines.push("");
|
|
133
|
-
lines.push("<details>");
|
|
134
|
-
lines.push("<summary>Repository Details</summary>");
|
|
135
|
-
lines.push("");
|
|
136
|
-
lines.push("| Repository | Status | Changes | Result |");
|
|
137
|
-
lines.push("|------------|--------|---------|--------|");
|
|
138
|
-
for (const result of data.results) {
|
|
139
|
-
const repo = result.repoName;
|
|
140
|
-
const status = formatStatus(result, data.dryRun);
|
|
141
|
-
const changes = formatChangesColumn(result);
|
|
142
|
-
const resultText = formatResult(result);
|
|
143
|
-
lines.push(`| ${repo} | ${status} | ${changes} | ${resultText} |`);
|
|
144
|
-
}
|
|
145
|
-
// Plan details nested sections
|
|
146
|
-
for (const result of data.results) {
|
|
147
|
-
if (result.rulesetPlanDetails && result.rulesetPlanDetails.length > 0) {
|
|
148
|
-
lines.push("");
|
|
149
|
-
lines.push("<details>");
|
|
150
|
-
lines.push(`<summary>${result.repoName} — Rulesets: ${formatPlanSummary(result.rulesetPlanDetails)}</summary>`);
|
|
151
|
-
lines.push("");
|
|
152
|
-
lines.push("| Ruleset | Action | Properties |");
|
|
153
|
-
lines.push("|---------|--------|------------|");
|
|
154
|
-
for (const detail of result.rulesetPlanDetails) {
|
|
155
|
-
lines.push(`| ${detail.name} | ${formatRulesetAction(detail.action)} | ${formatRulesetProperties(detail)} |`);
|
|
156
|
-
}
|
|
157
|
-
lines.push("");
|
|
158
|
-
lines.push("</details>");
|
|
159
|
-
}
|
|
160
|
-
if (result.repoSettingsPlanDetails &&
|
|
161
|
-
result.repoSettingsPlanDetails.length > 0) {
|
|
162
|
-
lines.push("");
|
|
163
|
-
lines.push("<details>");
|
|
164
|
-
lines.push(`<summary>${result.repoName} — Repo Settings: ${formatPlanSummary(result.repoSettingsPlanDetails)}</summary>`);
|
|
165
|
-
lines.push("");
|
|
166
|
-
lines.push("| Setting | Action |");
|
|
167
|
-
lines.push("|---------|--------|");
|
|
168
|
-
for (const detail of result.repoSettingsPlanDetails) {
|
|
169
|
-
lines.push(`| ${detail.property} | ${formatSettingsAction(detail.action)} |`);
|
|
170
|
-
}
|
|
171
|
-
lines.push("");
|
|
172
|
-
lines.push("</details>");
|
|
173
|
-
}
|
|
174
|
-
if (result.labelsPlanDetails && result.labelsPlanDetails.length > 0) {
|
|
175
|
-
lines.push("");
|
|
176
|
-
lines.push("<details>");
|
|
177
|
-
lines.push(`<summary>${result.repoName} — Labels: ${formatPlanSummary(result.labelsPlanDetails)}</summary>`);
|
|
178
|
-
lines.push("");
|
|
179
|
-
lines.push("| Label | Action |");
|
|
180
|
-
lines.push("|-------|--------|");
|
|
181
|
-
for (const detail of result.labelsPlanDetails) {
|
|
182
|
-
const action = formatRulesetAction(detail.action);
|
|
183
|
-
const name = detail.newName
|
|
184
|
-
? `${detail.name} \u2192 ${detail.newName}`
|
|
185
|
-
: detail.name;
|
|
186
|
-
lines.push(`| ${name} | ${action} |`);
|
|
187
|
-
}
|
|
188
|
-
lines.push("");
|
|
189
|
-
lines.push("</details>");
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
lines.push("");
|
|
193
|
-
lines.push("</details>");
|
|
194
|
-
}
|
|
195
|
-
return lines.join("\n");
|
|
196
|
-
}
|
|
197
|
-
export function shouldWriteSummary(summaryPath) {
|
|
198
|
-
return !!summaryPath;
|
|
199
|
-
}
|
|
200
3
|
/**
|
|
201
4
|
* Append markdown content to GITHUB_STEP_SUMMARY.
|
|
202
5
|
* No-op if summaryPath is not provided.
|
|
@@ -212,7 +15,3 @@ export function writeGitHubStepSummary(markdown, summaryPath, log) {
|
|
|
212
15
|
log?.debug(`Failed to write GitHub step summary: ${toErrorMessage(error)}`);
|
|
213
16
|
}
|
|
214
17
|
}
|
|
215
|
-
export function writeSummary(data, summaryPath) {
|
|
216
|
-
const markdown = formatSummary(data);
|
|
217
|
-
writeGitHubStepSummary(markdown, summaryPath);
|
|
218
|
-
}
|
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { writeGitHubStepSummary } from "./github-summary.js";
|
|
3
3
|
import { formatCountEntry } from "./settings-report.js";
|
|
4
|
-
export function buildLifecycleReport(results) {
|
|
5
|
-
const actions = [];
|
|
6
|
-
const totals = { created: 0, forked: 0, migrated: 0, existed: 0 };
|
|
7
|
-
for (const result of results) {
|
|
8
|
-
actions.push({
|
|
9
|
-
repoName: result.repoName,
|
|
10
|
-
action: result.action,
|
|
11
|
-
upstream: result.upstream,
|
|
12
|
-
source: result.source,
|
|
13
|
-
settings: result.settings,
|
|
14
|
-
});
|
|
15
|
-
totals[result.action]++;
|
|
16
|
-
}
|
|
17
|
-
return { actions, totals };
|
|
18
|
-
}
|
|
19
4
|
function formatLifecycleSummary(totals) {
|
|
20
5
|
const entry = formatCountEntry("repo", "repos", [
|
|
21
6
|
{ label: "to create", value: totals.created },
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { formatPropertyTree } from "../settings/index.js";
|
|
3
2
|
import { writeGitHubStepSummary } from "./github-summary.js";
|
|
4
3
|
/**
|
|
5
4
|
* Shared recursive renderer for ruleset config objects.
|
|
@@ -54,9 +53,6 @@ function renderRulesetConfig(config, startDepth, formatLine) {
|
|
|
54
53
|
}
|
|
55
54
|
return lines;
|
|
56
55
|
}
|
|
57
|
-
function formatRulesetConfig(config, indent) {
|
|
58
|
-
return renderRulesetConfig(config, indent, (depth, text) => chalk.green(`${" ".repeat(depth)}${text}`));
|
|
59
|
-
}
|
|
60
56
|
/**
|
|
61
57
|
* Formats a summary entry like "3 files (1 to create, 2 to update)".
|
|
62
58
|
* Returns null if total is 0.
|
|
@@ -98,6 +94,17 @@ function formatSettingsSummary(totals) {
|
|
|
98
94
|
}
|
|
99
95
|
return `Plan: ${parts.join(", ")}`;
|
|
100
96
|
}
|
|
97
|
+
function colorizeDiffLine(line) {
|
|
98
|
+
const prefix = line.charAt(0);
|
|
99
|
+
const indented = ` ${line}`;
|
|
100
|
+
if (prefix === "+")
|
|
101
|
+
return chalk.green(indented);
|
|
102
|
+
if (prefix === "!")
|
|
103
|
+
return chalk.yellow(indented);
|
|
104
|
+
if (prefix === "-")
|
|
105
|
+
return chalk.red(indented);
|
|
106
|
+
return indented;
|
|
107
|
+
}
|
|
101
108
|
export function formatSettingsReportCLI(report) {
|
|
102
109
|
const lines = [];
|
|
103
110
|
for (const repo of report.repos) {
|
|
@@ -107,80 +114,11 @@ export function formatSettingsReportCLI(report) {
|
|
|
107
114
|
!repo.error) {
|
|
108
115
|
continue;
|
|
109
116
|
}
|
|
110
|
-
// Repo header
|
|
111
117
|
lines.push(chalk.yellow(`~ ${repo.repoName}`));
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (setting.action === "create") {
|
|
119
|
-
lines.push(chalk.green(` + ${setting.name}: ${formatValuePlain(setting.newValue)}`));
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
lines.push(chalk.yellow(` ~ ${setting.name}: ${formatValuePlain(setting.oldValue)} → ${formatValuePlain(setting.newValue)}`));
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Rulesets
|
|
126
|
-
for (const ruleset of repo.rulesets) {
|
|
127
|
-
if (ruleset.action === "create") {
|
|
128
|
-
lines.push(chalk.green(` + ruleset "${ruleset.name}"`));
|
|
129
|
-
if (ruleset.config) {
|
|
130
|
-
lines.push(...formatRulesetConfig(ruleset.config, 2));
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
else if (ruleset.action === "update") {
|
|
134
|
-
lines.push(chalk.yellow(` ~ ruleset "${ruleset.name}"`));
|
|
135
|
-
if (ruleset.propertyDiffs && ruleset.propertyDiffs.length > 0) {
|
|
136
|
-
const treeLines = formatPropertyTree(ruleset.propertyDiffs);
|
|
137
|
-
for (const line of treeLines) {
|
|
138
|
-
lines.push(` ${line}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
else if (ruleset.action === "delete") {
|
|
143
|
-
lines.push(chalk.red(` - ruleset "${ruleset.name}"`));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// Labels
|
|
147
|
-
for (const label of repo.labels) {
|
|
148
|
-
if (label.action === "create") {
|
|
149
|
-
lines.push(chalk.green(` + label "${label.name}"`));
|
|
150
|
-
if (label.config) {
|
|
151
|
-
lines.push(chalk.green(` color: "${label.config.color}"`));
|
|
152
|
-
if (label.config.description !== undefined) {
|
|
153
|
-
lines.push(chalk.green(` description: "${label.config.description}"`));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
else if (label.action === "update") {
|
|
158
|
-
if (label.newName) {
|
|
159
|
-
lines.push(chalk.yellow(` ~ label "${label.name}" \u2192 "${label.newName}"`));
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
lines.push(chalk.yellow(` ~ label "${label.name}"`));
|
|
163
|
-
}
|
|
164
|
-
if (label.propertyChanges) {
|
|
165
|
-
for (const prop of label.propertyChanges) {
|
|
166
|
-
if (prop.property === "new_name")
|
|
167
|
-
continue;
|
|
168
|
-
if (prop.oldValue !== undefined) {
|
|
169
|
-
lines.push(chalk.yellow(` ${prop.property}: "${prop.oldValue}" \u2192 "${prop.newValue}"`));
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
lines.push(chalk.yellow(` ${prop.property}: "${prop.newValue}"`));
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else if (label.action === "delete") {
|
|
178
|
-
lines.push(chalk.red(` - label "${label.name}"`));
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// Error
|
|
182
|
-
if (repo.error) {
|
|
183
|
-
lines.push(chalk.red(` Error: ${repo.error}`));
|
|
118
|
+
const diffLines = [];
|
|
119
|
+
renderRepoSettingsDiffLines(repo, diffLines);
|
|
120
|
+
for (const diffLine of diffLines) {
|
|
121
|
+
lines.push(colorizeDiffLine(diffLine));
|
|
184
122
|
}
|
|
185
123
|
lines.push(""); // Blank line between repos
|
|
186
124
|
}
|
|
@@ -2,4 +2,5 @@ import type { SyncReport, RepoFileChanges, ReportFileChange } from "./types.js";
|
|
|
2
2
|
export type { SyncReport, RepoFileChanges, ReportFileChange };
|
|
3
3
|
export declare function formatSyncReportCLI(report: SyncReport): string[];
|
|
4
4
|
export declare function formatSyncReportMarkdown(report: SyncReport, dryRun: boolean): string;
|
|
5
|
+
export declare function renderSyncLines(syncRepo: RepoFileChanges, diffLines: string[]): void;
|
|
5
6
|
export declare function writeSyncReportSummary(report: SyncReport, dryRun: boolean, summaryPath: string | undefined): void;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { writeGitHubStepSummary } from "./github-summary.js";
|
|
3
3
|
import { formatCountEntry } from "./settings-report.js";
|
|
4
|
-
import { renderSyncLines } from "./unified-summary.js";
|
|
5
4
|
import { formatDiffLine } from "../sync/index.js";
|
|
6
5
|
function formatSyncSummary(totals) {
|
|
7
6
|
const entry = formatCountEntry("file", "files", [
|
|
@@ -78,6 +77,25 @@ export function formatSyncReportMarkdown(report, dryRun) {
|
|
|
78
77
|
lines.push(`**${formatSyncSummary(report.totals)}**`);
|
|
79
78
|
return lines.join("\n");
|
|
80
79
|
}
|
|
80
|
+
export function renderSyncLines(syncRepo, diffLines) {
|
|
81
|
+
for (const file of syncRepo.files) {
|
|
82
|
+
if (file.action === "create") {
|
|
83
|
+
diffLines.push(`+ ${file.path}`);
|
|
84
|
+
}
|
|
85
|
+
else if (file.action === "update") {
|
|
86
|
+
diffLines.push(`! ${file.path}`);
|
|
87
|
+
}
|
|
88
|
+
else if (file.action === "delete") {
|
|
89
|
+
diffLines.push(`- ${file.path}`);
|
|
90
|
+
}
|
|
91
|
+
if (file.diffLines) {
|
|
92
|
+
diffLines.push(...file.diffLines);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (syncRepo.error) {
|
|
96
|
+
diffLines.push(`- Error: ${syncRepo.error}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
81
99
|
export function writeSyncReportSummary(report, dryRun, summaryPath) {
|
|
82
100
|
const markdown = formatSyncReportMarkdown(report, dryRun);
|
|
83
101
|
writeGitHubStepSummary(markdown, summaryPath);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LifecycleReport } from "./lifecycle-report.js";
|
|
2
|
-
import type { SyncReport
|
|
2
|
+
import type { SyncReport } from "./types.js";
|
|
3
3
|
import type { SettingsReport } from "./settings-report.js";
|
|
4
4
|
interface UnifiedSummaryInput {
|
|
5
5
|
lifecycle?: LifecycleReport;
|
|
@@ -8,7 +8,6 @@ interface UnifiedSummaryInput {
|
|
|
8
8
|
dryRun: boolean;
|
|
9
9
|
summaryPath?: string | undefined;
|
|
10
10
|
}
|
|
11
|
-
export declare function renderSyncLines(syncRepo: RepoFileChanges, diffLines: string[]): void;
|
|
12
11
|
export declare function formatUnifiedSummaryMarkdown(input: UnifiedSummaryInput): string;
|
|
13
12
|
export declare function writeUnifiedSummary(input: UnifiedSummaryInput): void;
|
|
14
13
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hasLifecycleChanges } from "./lifecycle-report.js";
|
|
2
2
|
import { writeGitHubStepSummary } from "./github-summary.js";
|
|
3
|
+
import { renderSyncLines } from "./sync-report.js";
|
|
3
4
|
import { renderRepoSettingsDiffLines, formatCountEntry, } from "./settings-report.js";
|
|
4
5
|
// =============================================================================
|
|
5
6
|
// Helpers
|
|
@@ -130,25 +131,6 @@ function renderLifecycleLines(lcAction, diffLines) {
|
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
|
-
export function renderSyncLines(syncRepo, diffLines) {
|
|
134
|
-
for (const file of syncRepo.files) {
|
|
135
|
-
if (file.action === "create") {
|
|
136
|
-
diffLines.push(`+ ${file.path}`);
|
|
137
|
-
}
|
|
138
|
-
else if (file.action === "update") {
|
|
139
|
-
diffLines.push(`! ${file.path}`);
|
|
140
|
-
}
|
|
141
|
-
else if (file.action === "delete") {
|
|
142
|
-
diffLines.push(`- ${file.path}`);
|
|
143
|
-
}
|
|
144
|
-
if (file.diffLines) {
|
|
145
|
-
diffLines.push(...file.diffLines);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (syncRepo.error) {
|
|
149
|
-
diffLines.push(`- Error: ${syncRepo.error}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
134
|
// =============================================================================
|
|
153
135
|
// Markdown Formatter
|
|
154
136
|
// =============================================================================
|