@aspruyt/xfg 3.8.0 → 3.8.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.
|
@@ -9,58 +9,42 @@ import { generateWorkspaceName } from "../shared/workspace-utils.js";
|
|
|
9
9
|
import { buildErrorResult } from "../output/summary-utils.js";
|
|
10
10
|
import { getManagedRulesets } from "../sync/manifest.js";
|
|
11
11
|
import { formatSettingsReportCLI, writeSettingsReportSummary, } from "../output/settings-report.js";
|
|
12
|
-
import { buildSettingsReport } from "./settings-report-builder.js";
|
|
12
|
+
import { buildSettingsReport, } from "./settings-report-builder.js";
|
|
13
13
|
import { defaultProcessorFactory, defaultRulesetProcessorFactory, defaultRepoSettingsProcessorFactory, } from "./types.js";
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Collects processing results for the SettingsReport.
|
|
16
|
+
* Provides a centralized way to track results across rulesets and repo settings.
|
|
16
17
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
console.log(`Loading config from: ${configPath}`);
|
|
24
|
-
if (options.dryRun) {
|
|
25
|
-
console.log("Running in DRY RUN mode - no changes will be made\n");
|
|
26
|
-
}
|
|
27
|
-
const rawConfig = loadRawConfig(configPath);
|
|
28
|
-
try {
|
|
29
|
-
validateForSettings(rawConfig);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
const config = normalizeConfig(rawConfig);
|
|
36
|
-
const reposWithRulesets = config.repos.filter((r) => r.settings?.rulesets && Object.keys(r.settings.rulesets).length > 0);
|
|
37
|
-
const reposWithRepoSettings = config.repos.filter((r) => r.settings?.repo && Object.keys(r.settings.repo).length > 0);
|
|
38
|
-
if (reposWithRulesets.length === 0 && reposWithRepoSettings.length === 0) {
|
|
39
|
-
console.log("No settings configured. Add settings.rulesets or settings.repo to your config.");
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
if (reposWithRulesets.length > 0) {
|
|
43
|
-
console.log(`Found ${reposWithRulesets.length} repositories with rulesets`);
|
|
44
|
-
}
|
|
45
|
-
if (reposWithRepoSettings.length > 0) {
|
|
46
|
-
console.log(`Found ${reposWithRepoSettings.length} repositories with repo settings`);
|
|
47
|
-
}
|
|
48
|
-
console.log("");
|
|
49
|
-
logger.setTotal(reposWithRulesets.length + reposWithRepoSettings.length);
|
|
50
|
-
const processor = processorFactory();
|
|
51
|
-
const repoProcessor = repoProcessorFactory();
|
|
52
|
-
const results = [];
|
|
53
|
-
const processingResults = [];
|
|
54
|
-
function getOrCreateResult(repoName) {
|
|
55
|
-
let result = processingResults.find((r) => r.repoName === repoName);
|
|
18
|
+
class ResultsCollector {
|
|
19
|
+
results = [];
|
|
20
|
+
getOrCreate(repoName) {
|
|
21
|
+
let result = this.results.find((r) => r.repoName === repoName);
|
|
56
22
|
if (!result) {
|
|
57
23
|
result = { repoName };
|
|
58
|
-
|
|
24
|
+
this.results.push(result);
|
|
59
25
|
}
|
|
60
26
|
return result;
|
|
61
27
|
}
|
|
62
|
-
|
|
63
|
-
const
|
|
28
|
+
appendError(repoName, error) {
|
|
29
|
+
const existing = this.getOrCreate(repoName);
|
|
30
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
31
|
+
if (existing.error) {
|
|
32
|
+
existing.error += `; ${errorMsg}`;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
existing.error = errorMsg;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
getAll() {
|
|
39
|
+
return this.results;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Process rulesets for all configured repositories.
|
|
44
|
+
*/
|
|
45
|
+
async function processRulesets(repos, config, options, processor, repoProcessor, results, collector) {
|
|
46
|
+
for (let i = 0; i < repos.length; i++) {
|
|
47
|
+
const repoConfig = repos[i];
|
|
64
48
|
let repoInfo;
|
|
65
49
|
try {
|
|
66
50
|
repoInfo = parseGitUrl(repoConfig.git, {
|
|
@@ -70,14 +54,12 @@ export async function runSettings(options, processorFactory = defaultRulesetProc
|
|
|
70
54
|
catch (error) {
|
|
71
55
|
logger.error(i + 1, repoConfig.git, String(error));
|
|
72
56
|
results.push(buildErrorResult(repoConfig.git, error));
|
|
73
|
-
|
|
74
|
-
error instanceof Error ? error.message : String(error);
|
|
57
|
+
collector.appendError(repoConfig.git, error);
|
|
75
58
|
continue;
|
|
76
59
|
}
|
|
77
60
|
const repoName = getRepoDisplayName(repoInfo);
|
|
78
61
|
if (!isGitHubRepo(repoInfo)) {
|
|
79
62
|
logger.skip(i + 1, repoName, "GitHub Rulesets only supported for GitHub repos");
|
|
80
|
-
// Skipped repos don't appear in the report
|
|
81
63
|
continue;
|
|
82
64
|
}
|
|
83
65
|
const managedRulesets = getManagedRulesets(null, config.id);
|
|
@@ -130,101 +112,133 @@ export async function runSettings(options, processorFactory = defaultRulesetProc
|
|
|
130
112
|
message: result.message,
|
|
131
113
|
rulesetPlanDetails: result.planOutput?.entries,
|
|
132
114
|
});
|
|
133
|
-
// Collect result for SettingsReport
|
|
134
115
|
if (!result.skipped) {
|
|
135
|
-
|
|
116
|
+
collector.getOrCreate(repoName).rulesetResult = result;
|
|
136
117
|
}
|
|
137
118
|
}
|
|
138
119
|
catch (error) {
|
|
139
120
|
logger.error(i + 1, repoName, String(error));
|
|
140
121
|
results.push(buildErrorResult(repoName, error));
|
|
141
|
-
|
|
142
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
143
|
-
if (existingResult.error) {
|
|
144
|
-
existingResult.error += `; ${errorMsg}`;
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
existingResult.error = errorMsg;
|
|
148
|
-
}
|
|
122
|
+
collector.appendError(repoName, error);
|
|
149
123
|
}
|
|
150
124
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
if (result.skipped) {
|
|
186
|
-
// Silent skip
|
|
187
|
-
}
|
|
188
|
-
else if (result.success) {
|
|
189
|
-
console.log(chalk.green(` ✓ ${repoName}: ${result.message}`));
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
console.log(chalk.red(` ✗ ${repoName}: ${result.message}`));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Process repo settings for all configured repositories.
|
|
128
|
+
*/
|
|
129
|
+
async function processRepoSettings(repos, config, options, processorFactory, results, collector) {
|
|
130
|
+
if (repos.length === 0) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const processor = processorFactory();
|
|
134
|
+
console.log(`\nProcessing repo settings for ${repos.length} repositories\n`);
|
|
135
|
+
for (let i = 0; i < repos.length; i++) {
|
|
136
|
+
const repoConfig = repos[i];
|
|
137
|
+
let repoInfo;
|
|
138
|
+
try {
|
|
139
|
+
repoInfo = parseGitUrl(repoConfig.git, {
|
|
140
|
+
githubHosts: config.githubHosts,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error(`Failed to parse ${repoConfig.git}: ${error}`);
|
|
145
|
+
collector.appendError(repoConfig.git, error);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const repoName = getRepoDisplayName(repoInfo);
|
|
149
|
+
try {
|
|
150
|
+
const result = await processor.process(repoConfig, repoInfo, {
|
|
151
|
+
dryRun: options.dryRun,
|
|
152
|
+
});
|
|
153
|
+
if (result.planOutput && result.planOutput.lines.length > 0) {
|
|
154
|
+
console.log(`\n ${chalk.bold(repoName)}:`);
|
|
155
|
+
console.log(" Repo Settings:");
|
|
156
|
+
for (const line of result.planOutput.lines) {
|
|
157
|
+
console.log(line);
|
|
193
158
|
}
|
|
194
|
-
if (
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
existing.repoSettingsPlanDetails = result.planOutput?.entries;
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
results.push({
|
|
201
|
-
repoName,
|
|
202
|
-
status: result.success ? "succeeded" : "failed",
|
|
203
|
-
message: result.message,
|
|
204
|
-
repoSettingsPlanDetails: result.planOutput?.entries,
|
|
205
|
-
});
|
|
159
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
160
|
+
for (const warning of result.warnings) {
|
|
161
|
+
console.log(chalk.yellow(` ⚠️ Warning: ${warning}`));
|
|
206
162
|
}
|
|
207
163
|
}
|
|
208
|
-
// Collect result for SettingsReport
|
|
209
|
-
if (!result.skipped) {
|
|
210
|
-
getOrCreateResult(repoName).settingsResult = result;
|
|
211
|
-
}
|
|
212
164
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
165
|
+
if (result.skipped) {
|
|
166
|
+
// Silent skip
|
|
167
|
+
}
|
|
168
|
+
else if (result.success) {
|
|
169
|
+
console.log(chalk.green(` ✓ ${repoName}: ${result.message}`));
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
console.log(chalk.red(` ✗ ${repoName}: ${result.message}`));
|
|
173
|
+
}
|
|
174
|
+
if (!result.skipped) {
|
|
175
|
+
const existing = results.find((r) => r.repoName === repoName);
|
|
176
|
+
if (existing) {
|
|
177
|
+
existing.repoSettingsPlanDetails = result.planOutput?.entries;
|
|
219
178
|
}
|
|
220
179
|
else {
|
|
221
|
-
|
|
180
|
+
results.push({
|
|
181
|
+
repoName,
|
|
182
|
+
status: result.success ? "succeeded" : "failed",
|
|
183
|
+
message: result.message,
|
|
184
|
+
repoSettingsPlanDetails: result.planOutput?.entries,
|
|
185
|
+
});
|
|
222
186
|
}
|
|
223
187
|
}
|
|
188
|
+
if (!result.skipped) {
|
|
189
|
+
collector.getOrCreate(repoName).settingsResult = result;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
console.error(` ✗ ${repoName}: ${error}`);
|
|
194
|
+
collector.appendError(repoName, error);
|
|
224
195
|
}
|
|
225
196
|
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Run the settings command - manages GitHub Rulesets and repo settings.
|
|
200
|
+
*/
|
|
201
|
+
export async function runSettings(options, processorFactory = defaultRulesetProcessorFactory, repoProcessorFactory = defaultProcessorFactory, repoSettingsProcessorFactory = defaultRepoSettingsProcessorFactory) {
|
|
202
|
+
const configPath = resolve(options.config);
|
|
203
|
+
if (!existsSync(configPath)) {
|
|
204
|
+
console.error(`Config file not found: ${configPath}`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
console.log(`Loading config from: ${configPath}`);
|
|
208
|
+
if (options.dryRun) {
|
|
209
|
+
console.log("Running in DRY RUN mode - no changes will be made\n");
|
|
210
|
+
}
|
|
211
|
+
const rawConfig = loadRawConfig(configPath);
|
|
212
|
+
try {
|
|
213
|
+
validateForSettings(rawConfig);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
const config = normalizeConfig(rawConfig);
|
|
220
|
+
const reposWithRulesets = config.repos.filter((r) => r.settings?.rulesets && Object.keys(r.settings.rulesets).length > 0);
|
|
221
|
+
const reposWithRepoSettings = config.repos.filter((r) => r.settings?.repo && Object.keys(r.settings.repo).length > 0);
|
|
222
|
+
if (reposWithRulesets.length === 0 && reposWithRepoSettings.length === 0) {
|
|
223
|
+
console.log("No settings configured. Add settings.rulesets or settings.repo to your config.");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (reposWithRulesets.length > 0) {
|
|
227
|
+
console.log(`Found ${reposWithRulesets.length} repositories with rulesets`);
|
|
228
|
+
}
|
|
229
|
+
if (reposWithRepoSettings.length > 0) {
|
|
230
|
+
console.log(`Found ${reposWithRepoSettings.length} repositories with repo settings`);
|
|
231
|
+
}
|
|
232
|
+
console.log("");
|
|
233
|
+
logger.setTotal(reposWithRulesets.length + reposWithRepoSettings.length);
|
|
234
|
+
const processor = processorFactory();
|
|
235
|
+
const repoProcessor = repoProcessorFactory();
|
|
236
|
+
const results = [];
|
|
237
|
+
const collector = new ResultsCollector();
|
|
238
|
+
await processRulesets(reposWithRulesets, config, options, processor, repoProcessor, results, collector);
|
|
239
|
+
await processRepoSettings(reposWithRepoSettings, config, options, repoSettingsProcessorFactory, results, collector);
|
|
226
240
|
console.log("");
|
|
227
|
-
const report = buildSettingsReport(
|
|
241
|
+
const report = buildSettingsReport(collector.getAll());
|
|
228
242
|
const lines = formatSettingsReportCLI(report);
|
|
229
243
|
for (const line of lines) {
|
|
230
244
|
console.log(line);
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { SettingsReport } from "../output/settings-report.js";
|
|
2
2
|
import type { RepoSettingsPlanEntry } from "../settings/repo-settings/formatter.js";
|
|
3
3
|
import type { RulesetPlanEntry } from "../settings/rulesets/formatter.js";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Result from processing a repository's settings and rulesets.
|
|
6
|
+
* Used to collect results during settings command execution.
|
|
7
|
+
*/
|
|
8
|
+
export interface ProcessorResults {
|
|
5
9
|
repoName: string;
|
|
6
10
|
settingsResult?: {
|
|
7
11
|
planOutput?: {
|
|
@@ -16,4 +20,3 @@ interface ProcessorResults {
|
|
|
16
20
|
error?: string;
|
|
17
21
|
}
|
|
18
22
|
export declare function buildSettingsReport(results: ProcessorResults[]): SettingsReport;
|
|
19
|
-
export {};
|