@aspruyt/xfg 3.9.0 → 3.9.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/sync-command.js
CHANGED
|
@@ -9,8 +9,9 @@ import { logger } from "../shared/logger.js";
|
|
|
9
9
|
import { generateWorkspaceName } from "../shared/workspace-utils.js";
|
|
10
10
|
import { defaultProcessorFactory } from "./types.js";
|
|
11
11
|
import { buildSyncReport } from "./sync-report-builder.js";
|
|
12
|
-
import { formatSyncReportCLI
|
|
13
|
-
import { buildLifecycleReport, formatLifecycleReportCLI,
|
|
12
|
+
import { formatSyncReportCLI } from "../output/sync-report.js";
|
|
13
|
+
import { buildLifecycleReport, formatLifecycleReportCLI, hasLifecycleChanges, } from "../output/lifecycle-report.js";
|
|
14
|
+
import { writeUnifiedSummary } from "../output/unified-summary.js";
|
|
14
15
|
import { RepoLifecycleManager, runLifecycleCheck, toCreateRepoSettings, } from "../lifecycle/index.js";
|
|
15
16
|
/**
|
|
16
17
|
* Get unique file names from all repos in the config
|
|
@@ -238,14 +239,14 @@ export async function runSync(options, processorFactory = defaultProcessorFactor
|
|
|
238
239
|
console.log(line);
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
|
-
writeLifecycleReportSummary(lifecycleReport, options.dryRun ?? false);
|
|
242
242
|
// Build and display sync report
|
|
243
243
|
const report = buildSyncReport(reportResults);
|
|
244
244
|
console.log("");
|
|
245
245
|
for (const line of formatSyncReportCLI(report)) {
|
|
246
246
|
console.log(line);
|
|
247
247
|
}
|
|
248
|
-
|
|
248
|
+
// Write unified summary to GITHUB_STEP_SUMMARY
|
|
249
|
+
writeUnifiedSummary(lifecycleReport, report, options.dryRun ?? false);
|
|
249
250
|
// Exit with error if any failures
|
|
250
251
|
const hasErrors = reportResults.some((r) => r.error);
|
|
251
252
|
if (hasErrors) {
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
// src/output/lifecycle-report.ts
|
|
2
2
|
import { appendFileSync } from "node:fs";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
function escapeHtml(text) {
|
|
5
|
-
return text
|
|
6
|
-
.replace(/&/g, "&")
|
|
7
|
-
.replace(/</g, "<")
|
|
8
|
-
.replace(/>/g, ">");
|
|
9
|
-
}
|
|
10
4
|
// =============================================================================
|
|
11
5
|
// Builder
|
|
12
6
|
// =============================================================================
|
|
@@ -103,35 +97,35 @@ export function formatLifecycleReportMarkdown(report, dryRun) {
|
|
|
103
97
|
lines.push("> This was a dry run — no changes were applied");
|
|
104
98
|
lines.push("");
|
|
105
99
|
}
|
|
106
|
-
//
|
|
100
|
+
// Diff block
|
|
107
101
|
const diffLines = [];
|
|
108
102
|
for (const action of report.actions) {
|
|
109
103
|
if (action.action === "existed")
|
|
110
104
|
continue;
|
|
111
105
|
switch (action.action) {
|
|
112
106
|
case "created":
|
|
113
|
-
diffLines.push(
|
|
107
|
+
diffLines.push(`+ CREATE ${action.repoName}`);
|
|
114
108
|
break;
|
|
115
109
|
case "forked":
|
|
116
|
-
diffLines.push(
|
|
110
|
+
diffLines.push(`+ FORK ${action.upstream ?? "upstream"} -> ${action.repoName}`);
|
|
117
111
|
break;
|
|
118
112
|
case "migrated":
|
|
119
|
-
diffLines.push(
|
|
113
|
+
diffLines.push(`+ MIGRATE ${action.source ?? "source"} -> ${action.repoName}`);
|
|
120
114
|
break;
|
|
121
115
|
}
|
|
122
116
|
if (action.settings) {
|
|
123
117
|
if (action.settings.visibility) {
|
|
124
|
-
diffLines.push(
|
|
118
|
+
diffLines.push(` visibility: ${action.settings.visibility}`);
|
|
125
119
|
}
|
|
126
120
|
if (action.settings.description) {
|
|
127
|
-
diffLines.push(
|
|
121
|
+
diffLines.push(` description: "${action.settings.description}"`);
|
|
128
122
|
}
|
|
129
123
|
}
|
|
130
124
|
}
|
|
131
125
|
if (diffLines.length > 0) {
|
|
132
|
-
lines.push("
|
|
126
|
+
lines.push("```diff");
|
|
133
127
|
lines.push(...diffLines);
|
|
134
|
-
lines.push("
|
|
128
|
+
lines.push("```");
|
|
135
129
|
lines.push("");
|
|
136
130
|
}
|
|
137
131
|
// Summary
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { appendFileSync } from "node:fs";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { formatPropertyTree, } from "../settings/rulesets/formatter.js";
|
|
4
|
-
function escapeHtml(text) {
|
|
5
|
-
return text
|
|
6
|
-
.replace(/&/g, "&")
|
|
7
|
-
.replace(/</g, "<")
|
|
8
|
-
.replace(/>/g, ">");
|
|
9
|
-
}
|
|
10
4
|
// =============================================================================
|
|
11
5
|
// Helpers
|
|
12
6
|
// =============================================================================
|
|
@@ -239,57 +233,55 @@ export function formatSettingsReportMarkdown(report, dryRun) {
|
|
|
239
233
|
!repo.error) {
|
|
240
234
|
continue;
|
|
241
235
|
}
|
|
242
|
-
diffLines.push(
|
|
236
|
+
diffLines.push(`@@ ${repo.repoName} @@`);
|
|
243
237
|
for (const setting of repo.settings) {
|
|
244
238
|
// Skip settings where both values are undefined
|
|
245
239
|
if (setting.oldValue === undefined && setting.newValue === undefined) {
|
|
246
240
|
continue;
|
|
247
241
|
}
|
|
248
242
|
if (setting.action === "add") {
|
|
249
|
-
diffLines.push(
|
|
243
|
+
diffLines.push(`+ ${setting.name}: ${formatValuePlain(setting.newValue)}`);
|
|
250
244
|
}
|
|
251
245
|
else {
|
|
252
|
-
diffLines.push(
|
|
246
|
+
diffLines.push(`! ${setting.name}: ${formatValuePlain(setting.oldValue)} → ${formatValuePlain(setting.newValue)}`);
|
|
253
247
|
}
|
|
254
248
|
}
|
|
255
249
|
for (const ruleset of repo.rulesets) {
|
|
256
250
|
if (ruleset.action === "create") {
|
|
257
|
-
diffLines.push(
|
|
251
|
+
diffLines.push(`+ ruleset "${ruleset.name}"`);
|
|
258
252
|
if (ruleset.config) {
|
|
259
|
-
|
|
260
|
-
diffLines.push(`<span style="color:#3fb950">${escapeHtml(line)}</span>`);
|
|
261
|
-
}
|
|
253
|
+
diffLines.push(...formatRulesetConfigPlain(ruleset.config));
|
|
262
254
|
}
|
|
263
255
|
}
|
|
264
256
|
else if (ruleset.action === "update") {
|
|
265
|
-
diffLines.push(
|
|
257
|
+
diffLines.push(`! ruleset "${ruleset.name}"`);
|
|
266
258
|
if (ruleset.propertyDiffs && ruleset.propertyDiffs.length > 0) {
|
|
267
259
|
for (const diff of ruleset.propertyDiffs) {
|
|
268
260
|
const path = diff.path.join(".");
|
|
269
261
|
if (diff.action === "add") {
|
|
270
|
-
diffLines.push(
|
|
262
|
+
diffLines.push(`+ ${path}: ${formatValuePlain(diff.newValue)}`);
|
|
271
263
|
}
|
|
272
264
|
else if (diff.action === "change") {
|
|
273
|
-
diffLines.push(
|
|
265
|
+
diffLines.push(`! ${path}: ${formatValuePlain(diff.oldValue)} → ${formatValuePlain(diff.newValue)}`);
|
|
274
266
|
}
|
|
275
267
|
else if (diff.action === "remove") {
|
|
276
|
-
diffLines.push(
|
|
268
|
+
diffLines.push(`- ${path}`);
|
|
277
269
|
}
|
|
278
270
|
}
|
|
279
271
|
}
|
|
280
272
|
}
|
|
281
273
|
else if (ruleset.action === "delete") {
|
|
282
|
-
diffLines.push(
|
|
274
|
+
diffLines.push(`- ruleset "${ruleset.name}"`);
|
|
283
275
|
}
|
|
284
276
|
}
|
|
285
277
|
if (repo.error) {
|
|
286
|
-
diffLines.push(
|
|
278
|
+
diffLines.push(`- Error: ${repo.error}`);
|
|
287
279
|
}
|
|
288
280
|
}
|
|
289
281
|
if (diffLines.length > 0) {
|
|
290
|
-
lines.push("
|
|
282
|
+
lines.push("```diff");
|
|
291
283
|
lines.push(...diffLines);
|
|
292
|
-
lines.push("
|
|
284
|
+
lines.push("```");
|
|
293
285
|
lines.push("");
|
|
294
286
|
}
|
|
295
287
|
// Summary
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
// src/output/sync-report.ts
|
|
2
2
|
import { appendFileSync } from "node:fs";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
function escapeHtml(text) {
|
|
5
|
-
return text
|
|
6
|
-
.replace(/&/g, "&")
|
|
7
|
-
.replace(/</g, "<")
|
|
8
|
-
.replace(/>/g, ">");
|
|
9
|
-
}
|
|
10
4
|
function formatSummary(totals) {
|
|
11
5
|
const total = totals.files.create + totals.files.update + totals.files.delete;
|
|
12
6
|
if (total === 0) {
|
|
@@ -64,33 +58,32 @@ export function formatSyncReportMarkdown(report, dryRun) {
|
|
|
64
58
|
lines.push("> This was a dry run — no changes were applied");
|
|
65
59
|
lines.push("");
|
|
66
60
|
}
|
|
67
|
-
//
|
|
68
|
-
// Colors: green (#3fb950) for creates, yellow (#d29922) for changes, red (#f85149) for deletes
|
|
61
|
+
// Diff block
|
|
69
62
|
const diffLines = [];
|
|
70
63
|
for (const repo of report.repos) {
|
|
71
64
|
if (repo.files.length === 0 && !repo.error) {
|
|
72
65
|
continue;
|
|
73
66
|
}
|
|
74
|
-
diffLines.push(
|
|
67
|
+
diffLines.push(`@@ ${repo.repoName} @@`);
|
|
75
68
|
for (const file of repo.files) {
|
|
76
69
|
if (file.action === "create") {
|
|
77
|
-
diffLines.push(
|
|
70
|
+
diffLines.push(`+ ${file.path}`);
|
|
78
71
|
}
|
|
79
72
|
else if (file.action === "update") {
|
|
80
|
-
diffLines.push(
|
|
73
|
+
diffLines.push(`! ${file.path}`);
|
|
81
74
|
}
|
|
82
75
|
else if (file.action === "delete") {
|
|
83
|
-
diffLines.push(
|
|
76
|
+
diffLines.push(`- ${file.path}`);
|
|
84
77
|
}
|
|
85
78
|
}
|
|
86
79
|
if (repo.error) {
|
|
87
|
-
diffLines.push(
|
|
80
|
+
diffLines.push(`- Error: ${repo.error}`);
|
|
88
81
|
}
|
|
89
82
|
}
|
|
90
83
|
if (diffLines.length > 0) {
|
|
91
|
-
lines.push("
|
|
84
|
+
lines.push("```diff");
|
|
92
85
|
lines.push(...diffLines);
|
|
93
|
-
lines.push("
|
|
86
|
+
lines.push("```");
|
|
94
87
|
lines.push("");
|
|
95
88
|
}
|
|
96
89
|
// Summary
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { LifecycleReport } from "./lifecycle-report.js";
|
|
2
|
+
import type { SyncReport } from "./sync-report.js";
|
|
3
|
+
export declare function formatUnifiedSummaryMarkdown(lifecycle: LifecycleReport, sync: SyncReport, dryRun: boolean): string;
|
|
4
|
+
export declare function writeUnifiedSummary(lifecycle: LifecycleReport, sync: SyncReport, dryRun: boolean): void;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// src/output/unified-summary.ts
|
|
2
|
+
import { appendFileSync } from "node:fs";
|
|
3
|
+
import { hasLifecycleChanges } from "./lifecycle-report.js";
|
|
4
|
+
// =============================================================================
|
|
5
|
+
// Helpers
|
|
6
|
+
// =============================================================================
|
|
7
|
+
function formatCombinedSummary(lifecycleTotals, syncTotals) {
|
|
8
|
+
const parts = [];
|
|
9
|
+
// Lifecycle totals
|
|
10
|
+
const repoTotal = lifecycleTotals.created + lifecycleTotals.forked + lifecycleTotals.migrated;
|
|
11
|
+
if (repoTotal > 0) {
|
|
12
|
+
const repoParts = [];
|
|
13
|
+
if (lifecycleTotals.created > 0)
|
|
14
|
+
repoParts.push(`${lifecycleTotals.created} to create`);
|
|
15
|
+
if (lifecycleTotals.forked > 0)
|
|
16
|
+
repoParts.push(`${lifecycleTotals.forked} to fork`);
|
|
17
|
+
if (lifecycleTotals.migrated > 0)
|
|
18
|
+
repoParts.push(`${lifecycleTotals.migrated} to migrate`);
|
|
19
|
+
const repoWord = repoTotal === 1 ? "repo" : "repos";
|
|
20
|
+
parts.push(`${repoTotal} ${repoWord} (${repoParts.join(", ")})`);
|
|
21
|
+
}
|
|
22
|
+
// Sync totals
|
|
23
|
+
const fileTotal = syncTotals.files.create + syncTotals.files.update + syncTotals.files.delete;
|
|
24
|
+
if (fileTotal > 0) {
|
|
25
|
+
const fileParts = [];
|
|
26
|
+
if (syncTotals.files.create > 0)
|
|
27
|
+
fileParts.push(`${syncTotals.files.create} to create`);
|
|
28
|
+
if (syncTotals.files.update > 0)
|
|
29
|
+
fileParts.push(`${syncTotals.files.update} to update`);
|
|
30
|
+
if (syncTotals.files.delete > 0)
|
|
31
|
+
fileParts.push(`${syncTotals.files.delete} to delete`);
|
|
32
|
+
const fileWord = fileTotal === 1 ? "file" : "files";
|
|
33
|
+
parts.push(`${fileTotal} ${fileWord} (${fileParts.join(", ")})`);
|
|
34
|
+
}
|
|
35
|
+
if (parts.length === 0) {
|
|
36
|
+
return "No changes";
|
|
37
|
+
}
|
|
38
|
+
return `Plan: ${parts.join(", ")}`;
|
|
39
|
+
}
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// Markdown Formatter
|
|
42
|
+
// =============================================================================
|
|
43
|
+
export function formatUnifiedSummaryMarkdown(lifecycle, sync, dryRun) {
|
|
44
|
+
const hasLifecycle = hasLifecycleChanges(lifecycle);
|
|
45
|
+
const hasSync = sync.repos.some((r) => r.files.length > 0 || r.error);
|
|
46
|
+
if (!hasLifecycle && !hasSync) {
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
const lines = [];
|
|
50
|
+
// Title
|
|
51
|
+
const titleSuffix = dryRun ? " (Dry Run)" : "";
|
|
52
|
+
lines.push(`## xfg Sync Summary${titleSuffix}`);
|
|
53
|
+
lines.push("");
|
|
54
|
+
// Dry-run warning
|
|
55
|
+
if (dryRun) {
|
|
56
|
+
lines.push("> [!WARNING]");
|
|
57
|
+
lines.push("> This was a dry run — no changes were applied");
|
|
58
|
+
lines.push("");
|
|
59
|
+
}
|
|
60
|
+
// Build lookup maps by repoName
|
|
61
|
+
const lifecycleByRepo = new Map(lifecycle.actions.map((a) => [a.repoName, a]));
|
|
62
|
+
const syncByRepo = new Map(sync.repos.map((r) => [r.repoName, r]));
|
|
63
|
+
// Collect all repo names in order (lifecycle first, then sync-only)
|
|
64
|
+
const allRepos = [];
|
|
65
|
+
for (const action of lifecycle.actions) {
|
|
66
|
+
if (!allRepos.includes(action.repoName)) {
|
|
67
|
+
allRepos.push(action.repoName);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const repo of sync.repos) {
|
|
71
|
+
if (!allRepos.includes(repo.repoName)) {
|
|
72
|
+
allRepos.push(repo.repoName);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Diff block
|
|
76
|
+
const diffLines = [];
|
|
77
|
+
for (const repoName of allRepos) {
|
|
78
|
+
const lcAction = lifecycleByRepo.get(repoName);
|
|
79
|
+
const syncRepo = syncByRepo.get(repoName);
|
|
80
|
+
const hasLcChange = lcAction && lcAction.action !== "existed";
|
|
81
|
+
const hasSyncChanges = syncRepo && (syncRepo.files.length > 0 || syncRepo.error);
|
|
82
|
+
if (!hasLcChange && !hasSyncChanges)
|
|
83
|
+
continue;
|
|
84
|
+
// Repo header
|
|
85
|
+
diffLines.push(`@@ ${repoName} @@`);
|
|
86
|
+
// Lifecycle action
|
|
87
|
+
if (lcAction && lcAction.action !== "existed") {
|
|
88
|
+
switch (lcAction.action) {
|
|
89
|
+
case "created":
|
|
90
|
+
diffLines.push(`+ CREATE`);
|
|
91
|
+
break;
|
|
92
|
+
case "forked":
|
|
93
|
+
diffLines.push(`+ FORK ${lcAction.upstream ?? "upstream"} -> ${repoName}`);
|
|
94
|
+
break;
|
|
95
|
+
case "migrated":
|
|
96
|
+
diffLines.push(`+ MIGRATE ${lcAction.source ?? "source"} -> ${repoName}`);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (lcAction.settings) {
|
|
100
|
+
if (lcAction.settings.visibility) {
|
|
101
|
+
diffLines.push(`+ visibility: ${lcAction.settings.visibility}`);
|
|
102
|
+
}
|
|
103
|
+
if (lcAction.settings.description) {
|
|
104
|
+
diffLines.push(`+ description: "${lcAction.settings.description}"`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// File changes
|
|
109
|
+
if (syncRepo) {
|
|
110
|
+
for (const file of syncRepo.files) {
|
|
111
|
+
if (file.action === "create") {
|
|
112
|
+
diffLines.push(`+ ${file.path}`);
|
|
113
|
+
}
|
|
114
|
+
else if (file.action === "update") {
|
|
115
|
+
diffLines.push(`! ${file.path}`);
|
|
116
|
+
}
|
|
117
|
+
else if (file.action === "delete") {
|
|
118
|
+
diffLines.push(`- ${file.path}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (syncRepo.error) {
|
|
122
|
+
diffLines.push(`- Error: ${syncRepo.error}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (diffLines.length > 0) {
|
|
127
|
+
lines.push("```diff");
|
|
128
|
+
lines.push(...diffLines);
|
|
129
|
+
lines.push("```");
|
|
130
|
+
lines.push("");
|
|
131
|
+
}
|
|
132
|
+
// Combined summary
|
|
133
|
+
lines.push(`**${formatCombinedSummary(lifecycle.totals, sync.totals)}**`);
|
|
134
|
+
return lines.join("\n");
|
|
135
|
+
}
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// File Writer
|
|
138
|
+
// =============================================================================
|
|
139
|
+
export function writeUnifiedSummary(lifecycle, sync, dryRun) {
|
|
140
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
141
|
+
if (!summaryPath)
|
|
142
|
+
return;
|
|
143
|
+
const markdown = formatUnifiedSummaryMarkdown(lifecycle, sync, dryRun);
|
|
144
|
+
if (!markdown)
|
|
145
|
+
return;
|
|
146
|
+
appendFileSync(summaryPath, "\n" + markdown + "\n");
|
|
147
|
+
}
|
package/package.json
CHANGED