@aspruyt/xfg 3.9.12 → 3.9.14

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.
Files changed (57) hide show
  1. package/dist/cli/index.d.ts +1 -1
  2. package/dist/cli/index.js +1 -1
  3. package/dist/cli/settings/lifecycle-checks.d.ts +11 -0
  4. package/dist/cli/settings/lifecycle-checks.js +64 -0
  5. package/dist/cli/settings/process-labels.d.ts +9 -0
  6. package/dist/cli/settings/process-labels.js +125 -0
  7. package/dist/cli/settings/process-repo-settings.d.ts +9 -0
  8. package/dist/cli/settings/process-repo-settings.js +80 -0
  9. package/dist/cli/settings/process-rulesets.d.ts +9 -0
  10. package/dist/cli/settings/process-rulesets.js +118 -0
  11. package/dist/cli/settings/results-collector.d.ts +11 -0
  12. package/dist/cli/settings/results-collector.js +28 -0
  13. package/dist/cli/settings-command.d.ts +3 -3
  14. package/dist/cli/settings-command.js +28 -268
  15. package/dist/cli/settings-report-builder.d.ts +6 -0
  16. package/dist/cli/settings-report-builder.js +23 -0
  17. package/dist/cli/types.d.ts +12 -2
  18. package/dist/cli/types.js +5 -0
  19. package/dist/config/index.d.ts +1 -1
  20. package/dist/config/normalizer.d.ts +0 -4
  21. package/dist/config/normalizer.js +56 -0
  22. package/dist/config/types.d.ts +17 -0
  23. package/dist/config/validator.d.ts +2 -3
  24. package/dist/config/validator.js +62 -7
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.js +1 -1
  27. package/dist/output/github-summary.d.ts +6 -0
  28. package/dist/output/github-summary.js +39 -0
  29. package/dist/output/settings-report.d.ts +18 -1
  30. package/dist/output/settings-report.js +84 -0
  31. package/dist/output/unified-summary.js +40 -1
  32. package/dist/settings/index.d.ts +1 -0
  33. package/dist/settings/index.js +2 -0
  34. package/dist/settings/labels/converter.d.ts +15 -0
  35. package/dist/settings/labels/converter.js +22 -0
  36. package/dist/settings/labels/diff.d.ts +33 -0
  37. package/dist/settings/labels/diff.js +156 -0
  38. package/dist/settings/labels/formatter.d.ts +25 -0
  39. package/dist/settings/labels/formatter.js +92 -0
  40. package/dist/settings/labels/github-labels-strategy.d.ts +51 -0
  41. package/dist/settings/labels/github-labels-strategy.js +102 -0
  42. package/dist/settings/labels/index.d.ts +6 -0
  43. package/dist/settings/labels/index.js +10 -0
  44. package/dist/settings/labels/processor.d.ts +57 -0
  45. package/dist/settings/labels/processor.js +189 -0
  46. package/dist/settings/labels/types.d.ts +33 -0
  47. package/dist/settings/labels/types.js +1 -0
  48. package/dist/sync/index.d.ts +1 -1
  49. package/dist/sync/index.js +1 -1
  50. package/dist/sync/manifest-strategy.d.ts +2 -1
  51. package/dist/sync/manifest-strategy.js +23 -5
  52. package/dist/sync/manifest.d.ts +24 -0
  53. package/dist/sync/manifest.js +98 -6
  54. package/dist/sync/repository-processor.d.ts +2 -1
  55. package/dist/sync/repository-processor.js +21 -5
  56. package/dist/sync/types.d.ts +2 -1
  57. package/package.json +4 -3
@@ -1,6 +1,6 @@
1
1
  export { runSync } from "./sync-command.js";
2
2
  export { runSettings } from "./settings-command.js";
3
3
  export { program } from "./program.js";
4
- export { type IRepositoryProcessor, type ProcessorFactory, type IRulesetProcessor, type RulesetProcessorFactory, type RepoSettingsProcessorFactory, type IRepoSettingsProcessor, defaultProcessorFactory, defaultRulesetProcessorFactory, defaultRepoSettingsProcessorFactory, } from "./types.js";
4
+ export { type IRepositoryProcessor, type ProcessorFactory, type IRulesetProcessor, type RulesetProcessorFactory, type RepoSettingsProcessorFactory, type IRepoSettingsProcessor, type ILabelsProcessor, type LabelsProcessorFactory, defaultProcessorFactory, defaultRulesetProcessorFactory, defaultRepoSettingsProcessorFactory, defaultLabelsProcessorFactory, } from "./types.js";
5
5
  export type { SyncOptions, SharedOptions } from "./sync-command.js";
6
6
  export type { SettingsOptions } from "./settings-command.js";
package/dist/cli/index.js CHANGED
@@ -6,4 +6,4 @@ export { program } from "./program.js";
6
6
  // For ESM compatibility, re-export everything from types.js
7
7
  export {
8
8
  // Runtime values
9
- defaultProcessorFactory, defaultRulesetProcessorFactory, defaultRepoSettingsProcessorFactory, } from "./types.js";
9
+ defaultProcessorFactory, defaultRulesetProcessorFactory, defaultRepoSettingsProcessorFactory, defaultLabelsProcessorFactory, } from "./types.js";
@@ -0,0 +1,11 @@
1
+ import type { GitHubAppTokenManager } from "../../vcs/index.js";
2
+ import type { RepoResult } from "../../output/github-summary.js";
3
+ import type { IRepoLifecycleManager } from "../../lifecycle/index.js";
4
+ import type { Config, RepoConfig } from "../../config/types.js";
5
+ import type { ResultsCollector } from "./results-collector.js";
6
+ import type { SettingsOptions } from "../settings-command.js";
7
+ /**
8
+ * Run lifecycle checks for all unique repos before processing.
9
+ * Returns a Set of git URLs to skip (lifecycle errors or repos that would be created in dry-run).
10
+ */
11
+ export declare function runLifecycleChecks(allRepos: RepoConfig[], config: Config, options: SettingsOptions, lifecycleManager: IRepoLifecycleManager, results: RepoResult[], collector: ResultsCollector, tokenManager: GitHubAppTokenManager | null): Promise<Set<string>>;
@@ -0,0 +1,64 @@
1
+ import { parseGitUrl, getRepoDisplayName, isGitHubRepo, } from "../../shared/repo-detector.js";
2
+ import { logger } from "../../shared/logger.js";
3
+ import { buildErrorResult } from "../../output/summary-utils.js";
4
+ import { runLifecycleCheck } from "../../lifecycle/index.js";
5
+ /**
6
+ * Run lifecycle checks for all unique repos before processing.
7
+ * Returns a Set of git URLs to skip (lifecycle errors or repos that would be created in dry-run).
8
+ */
9
+ export async function runLifecycleChecks(allRepos, config, options, lifecycleManager, results, collector, tokenManager) {
10
+ const checked = new Set();
11
+ const skippedRepos = new Set();
12
+ for (let i = 0; i < allRepos.length; i++) {
13
+ const repoConfig = allRepos[i];
14
+ if (checked.has(repoConfig.git)) {
15
+ continue;
16
+ }
17
+ checked.add(repoConfig.git);
18
+ let repoInfo;
19
+ try {
20
+ repoInfo = parseGitUrl(repoConfig.git, {
21
+ githubHosts: config.githubHosts,
22
+ });
23
+ }
24
+ catch {
25
+ // URL parsing errors are handled in individual processors
26
+ continue;
27
+ }
28
+ const repoName = getRepoDisplayName(repoInfo);
29
+ // Resolve auth token for lifecycle gh commands
30
+ let lifecycleToken;
31
+ if (isGitHubRepo(repoInfo)) {
32
+ try {
33
+ lifecycleToken =
34
+ (await tokenManager?.getTokenForRepo(repoInfo)) ??
35
+ process.env.GH_TOKEN;
36
+ }
37
+ catch {
38
+ lifecycleToken = process.env.GH_TOKEN;
39
+ }
40
+ }
41
+ try {
42
+ const { outputLines, lifecycleResult } = await runLifecycleCheck(repoConfig, repoInfo, i, {
43
+ dryRun: options.dryRun ?? false,
44
+ workDir: options.workDir,
45
+ githubHosts: config.githubHosts,
46
+ token: lifecycleToken,
47
+ }, lifecycleManager, config.settings?.repo);
48
+ for (const line of outputLines) {
49
+ logger.info(line);
50
+ }
51
+ // In dry-run, skip processing repos that don't exist yet
52
+ if (options.dryRun && lifecycleResult.action !== "existed") {
53
+ skippedRepos.add(repoConfig.git);
54
+ }
55
+ }
56
+ catch (error) {
57
+ logger.error(i + 1, repoName, `Lifecycle error: ${error instanceof Error ? error.message : String(error)}`);
58
+ results.push(buildErrorResult(repoName, error));
59
+ collector.appendError(repoName, error);
60
+ skippedRepos.add(repoConfig.git);
61
+ }
62
+ }
63
+ return skippedRepos;
64
+ }
@@ -0,0 +1,9 @@
1
+ import type { RepoResult } from "../../output/github-summary.js";
2
+ import type { ILabelsProcessor, IRepositoryProcessor } from "../types.js";
3
+ import type { Config, RepoConfig } from "../../config/types.js";
4
+ import type { ResultsCollector } from "./results-collector.js";
5
+ import type { SettingsOptions } from "../settings-command.js";
6
+ /**
7
+ * Process labels for all configured repositories.
8
+ */
9
+ export declare function processLabels(repos: RepoConfig[], config: Config, options: SettingsOptions, processor: ILabelsProcessor, repoProcessor: IRepositoryProcessor, results: RepoResult[], collector: ResultsCollector, lifecycleSkipped: Set<string>, indexOffset: number): Promise<void>;
@@ -0,0 +1,125 @@
1
+ import { resolve, join } from "node:path";
2
+ import chalk from "chalk";
3
+ import { parseGitUrl, getRepoDisplayName, isGitHubRepo, } from "../../shared/repo-detector.js";
4
+ import { logger } from "../../shared/logger.js";
5
+ import { generateWorkspaceName } from "../../shared/workspace-utils.js";
6
+ import { getManagedLabels, parseManifestContent, MANIFEST_FILENAME, } from "../../sync/manifest.js";
7
+ import { defaultExecutor } from "../../shared/command-executor.js";
8
+ import { escapeShellArg } from "../../shared/shell-utils.js";
9
+ /**
10
+ * Process labels for all configured repositories.
11
+ */
12
+ export async function processLabels(repos, config, options, processor, repoProcessor, results, collector, lifecycleSkipped, indexOffset) {
13
+ if (repos.length === 0) {
14
+ return;
15
+ }
16
+ console.log(`\nProcessing labels for ${repos.length} repositories\n`);
17
+ for (let i = 0; i < repos.length; i++) {
18
+ const repoConfig = repos[i];
19
+ const current = indexOffset + i + 1;
20
+ if (lifecycleSkipped.has(repoConfig.git)) {
21
+ continue;
22
+ }
23
+ let repoInfo;
24
+ try {
25
+ repoInfo = parseGitUrl(repoConfig.git, {
26
+ githubHosts: config.githubHosts,
27
+ });
28
+ }
29
+ catch (error) {
30
+ logger.error(current, repoConfig.git, String(error));
31
+ collector.appendError(repoConfig.git, error);
32
+ continue;
33
+ }
34
+ const repoName = getRepoDisplayName(repoInfo);
35
+ if (!isGitHubRepo(repoInfo)) {
36
+ logger.skip(current, repoName, "GitHub Labels only supported for GitHub repos");
37
+ continue;
38
+ }
39
+ const managedLabels = await fetchManagedLabels(repoInfo, config.id);
40
+ try {
41
+ logger.progress(current, repoName, "Processing labels...");
42
+ const result = await processor.process(repoConfig, repoInfo, {
43
+ configId: config.id,
44
+ dryRun: options.dryRun,
45
+ managedLabels,
46
+ noDelete: options.noDelete,
47
+ });
48
+ if (result.planOutput && result.planOutput.lines.length > 0) {
49
+ logger.info("");
50
+ logger.info(chalk.bold(`${repoName} - Labels:`));
51
+ for (const line of result.planOutput.lines) {
52
+ logger.info(line);
53
+ }
54
+ }
55
+ if (result.skipped) {
56
+ logger.skip(current, repoName, result.message);
57
+ }
58
+ else if (result.success) {
59
+ logger.success(current, repoName, result.message);
60
+ if (result.manifestUpdate && result.manifestUpdate.labels.length > 0) {
61
+ const workDir = resolve(join(options.workDir ?? "./tmp", generateWorkspaceName(i)));
62
+ logger.progress(current, repoName, "Updating manifest...");
63
+ const manifestResult = await repoProcessor.updateManifestOnly(repoInfo, repoConfig, {
64
+ branchName: "chore/sync-labels",
65
+ workDir,
66
+ configId: config.id,
67
+ dryRun: options.dryRun,
68
+ retries: options.retries,
69
+ }, { labels: result.manifestUpdate.labels });
70
+ if (!manifestResult.success && !manifestResult.skipped) {
71
+ logger.info(`Warning: Failed to update manifest for ${repoName}: ${manifestResult.message}`);
72
+ }
73
+ }
74
+ }
75
+ else {
76
+ logger.error(current, repoName, result.message);
77
+ collector.appendError(repoName, result.message);
78
+ }
79
+ const existing = results.find((r) => r.repoName === repoName);
80
+ if (existing) {
81
+ existing.labelsPlanDetails = result.planOutput?.entries;
82
+ }
83
+ else {
84
+ results.push({
85
+ repoName,
86
+ status: result.skipped
87
+ ? "skipped"
88
+ : result.success
89
+ ? "succeeded"
90
+ : "failed",
91
+ message: result.message,
92
+ labelsPlanDetails: result.planOutput?.entries,
93
+ });
94
+ }
95
+ if (!result.skipped) {
96
+ collector.getOrCreate(repoName).labelsResult = result;
97
+ }
98
+ }
99
+ catch (error) {
100
+ logger.error(current, repoName, String(error));
101
+ collector.appendError(repoName, error);
102
+ }
103
+ }
104
+ }
105
+ /**
106
+ * Fetches the managed labels list from a remote GitHub repo's manifest.
107
+ * Returns an empty array if the manifest doesn't exist or can't be read.
108
+ *
109
+ * Uses the project's ICommandExecutor + escapeShellArg pattern for safe
110
+ * command execution. All inputs are from parsed config (owner/repo), not
111
+ * user input.
112
+ */
113
+ async function fetchManagedLabels(repoInfo, configId) {
114
+ try {
115
+ const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}/contents/${MANIFEST_FILENAME}`;
116
+ const command = `gh api ${escapeShellArg(endpoint)} --jq '.content'`;
117
+ const base64Content = await defaultExecutor.exec(command, process.cwd());
118
+ const content = Buffer.from(base64Content.trim(), "base64").toString("utf-8");
119
+ const manifest = parseManifestContent(content);
120
+ return getManagedLabels(manifest, configId);
121
+ }
122
+ catch {
123
+ return [];
124
+ }
125
+ }
@@ -0,0 +1,9 @@
1
+ import type { RepoResult } from "../../output/github-summary.js";
2
+ import type { RepoSettingsProcessorFactory } from "../types.js";
3
+ import type { Config, RepoConfig } from "../../config/types.js";
4
+ import type { ResultsCollector } from "./results-collector.js";
5
+ import type { SettingsOptions } from "../settings-command.js";
6
+ /**
7
+ * Process repo settings for all configured repositories.
8
+ */
9
+ export declare function processRepoSettings(repos: RepoConfig[], config: Config, options: SettingsOptions, processorFactory: RepoSettingsProcessorFactory, results: RepoResult[], collector: ResultsCollector, lifecycleSkipped: Set<string>, indexOffset: number): Promise<void>;
@@ -0,0 +1,80 @@
1
+ import chalk from "chalk";
2
+ import { parseGitUrl, getRepoDisplayName } from "../../shared/repo-detector.js";
3
+ import { logger } from "../../shared/logger.js";
4
+ /**
5
+ * Process repo settings for all configured repositories.
6
+ */
7
+ export async function processRepoSettings(repos, config, options, processorFactory, results, collector, lifecycleSkipped, indexOffset) {
8
+ if (repos.length === 0) {
9
+ return;
10
+ }
11
+ const processor = processorFactory();
12
+ console.log(`\nProcessing repo settings for ${repos.length} repositories\n`);
13
+ for (let i = 0; i < repos.length; i++) {
14
+ const repoConfig = repos[i];
15
+ const current = indexOffset + i + 1;
16
+ if (lifecycleSkipped.has(repoConfig.git)) {
17
+ continue;
18
+ }
19
+ let repoInfo;
20
+ try {
21
+ repoInfo = parseGitUrl(repoConfig.git, {
22
+ githubHosts: config.githubHosts,
23
+ });
24
+ }
25
+ catch (error) {
26
+ logger.error(current, repoConfig.git, String(error));
27
+ collector.appendError(repoConfig.git, error);
28
+ continue;
29
+ }
30
+ const repoName = getRepoDisplayName(repoInfo);
31
+ try {
32
+ const result = await processor.process(repoConfig, repoInfo, {
33
+ dryRun: options.dryRun,
34
+ });
35
+ if (result.planOutput && result.planOutput.lines.length > 0) {
36
+ logger.info("");
37
+ logger.info(chalk.bold(`${repoName} - Repo Settings:`));
38
+ for (const line of result.planOutput.lines) {
39
+ logger.info(line);
40
+ }
41
+ if (result.warnings && result.warnings.length > 0) {
42
+ for (const warning of result.warnings) {
43
+ logger.info(chalk.yellow(`Warning: ${warning}`));
44
+ }
45
+ }
46
+ }
47
+ if (result.skipped) {
48
+ // Silent skip
49
+ }
50
+ else if (result.success) {
51
+ logger.success(current, repoName, result.message);
52
+ }
53
+ else {
54
+ logger.error(current, repoName, result.message);
55
+ collector.appendError(repoName, result.message);
56
+ }
57
+ if (!result.skipped) {
58
+ const existing = results.find((r) => r.repoName === repoName);
59
+ if (existing) {
60
+ existing.repoSettingsPlanDetails = result.planOutput?.entries;
61
+ }
62
+ else {
63
+ results.push({
64
+ repoName,
65
+ status: result.success ? "succeeded" : "failed",
66
+ message: result.message,
67
+ repoSettingsPlanDetails: result.planOutput?.entries,
68
+ });
69
+ }
70
+ }
71
+ if (!result.skipped) {
72
+ collector.getOrCreate(repoName).settingsResult = result;
73
+ }
74
+ }
75
+ catch (error) {
76
+ logger.error(current, repoName, String(error));
77
+ collector.appendError(repoName, error);
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,9 @@
1
+ import type { RepoResult } from "../../output/github-summary.js";
2
+ import type { IRulesetProcessor, IRepositoryProcessor } from "../types.js";
3
+ import type { Config, RepoConfig } from "../../config/types.js";
4
+ import type { ResultsCollector } from "./results-collector.js";
5
+ import type { SettingsOptions } from "../settings-command.js";
6
+ /**
7
+ * Process rulesets for all configured repositories.
8
+ */
9
+ export declare function processRulesets(repos: RepoConfig[], config: Config, options: SettingsOptions, processor: IRulesetProcessor, repoProcessor: IRepositoryProcessor, results: RepoResult[], collector: ResultsCollector, lifecycleSkipped: Set<string>): Promise<void>;
@@ -0,0 +1,118 @@
1
+ import { resolve, join } from "node:path";
2
+ import chalk from "chalk";
3
+ import { parseGitUrl, getRepoDisplayName, isGitHubRepo, } from "../../shared/repo-detector.js";
4
+ import { logger } from "../../shared/logger.js";
5
+ import { generateWorkspaceName } from "../../shared/workspace-utils.js";
6
+ import { buildErrorResult } from "../../output/summary-utils.js";
7
+ import { getManagedRulesets, parseManifestContent, MANIFEST_FILENAME, } from "../../sync/manifest.js";
8
+ import { defaultExecutor } from "../../shared/command-executor.js";
9
+ import { escapeShellArg } from "../../shared/shell-utils.js";
10
+ /**
11
+ * Process rulesets for all configured repositories.
12
+ */
13
+ export async function processRulesets(repos, config, options, processor, repoProcessor, results, collector, lifecycleSkipped) {
14
+ for (let i = 0; i < repos.length; i++) {
15
+ const repoConfig = repos[i];
16
+ if (lifecycleSkipped.has(repoConfig.git)) {
17
+ continue;
18
+ }
19
+ let repoInfo;
20
+ try {
21
+ repoInfo = parseGitUrl(repoConfig.git, {
22
+ githubHosts: config.githubHosts,
23
+ });
24
+ }
25
+ catch (error) {
26
+ logger.error(i + 1, repoConfig.git, String(error));
27
+ results.push(buildErrorResult(repoConfig.git, error));
28
+ collector.appendError(repoConfig.git, error);
29
+ continue;
30
+ }
31
+ const repoName = getRepoDisplayName(repoInfo);
32
+ if (!isGitHubRepo(repoInfo)) {
33
+ logger.skip(i + 1, repoName, "GitHub Rulesets only supported for GitHub repos");
34
+ continue;
35
+ }
36
+ const managedRulesets = await fetchManagedRulesets(repoInfo, config.id);
37
+ try {
38
+ logger.progress(i + 1, repoName, "Processing rulesets...");
39
+ const result = await processor.process(repoConfig, repoInfo, {
40
+ configId: config.id,
41
+ dryRun: options.dryRun,
42
+ managedRulesets,
43
+ noDelete: options.noDelete,
44
+ });
45
+ if (result.planOutput && result.planOutput.lines.length > 0) {
46
+ logger.info("");
47
+ logger.info(chalk.bold(`${repoName} - Rulesets:`));
48
+ for (const line of result.planOutput.lines) {
49
+ logger.info(line);
50
+ }
51
+ }
52
+ if (result.skipped) {
53
+ logger.skip(i + 1, repoName, result.message);
54
+ }
55
+ else if (result.success) {
56
+ logger.success(i + 1, repoName, result.message);
57
+ if (result.manifestUpdate &&
58
+ result.manifestUpdate.rulesets.length > 0) {
59
+ const workDir = resolve(join(options.workDir ?? "./tmp", generateWorkspaceName(i)));
60
+ logger.progress(i + 1, repoName, "Updating manifest...");
61
+ const manifestResult = await repoProcessor.updateManifestOnly(repoInfo, repoConfig, {
62
+ branchName: "chore/sync-rulesets",
63
+ workDir,
64
+ configId: config.id,
65
+ dryRun: options.dryRun,
66
+ retries: options.retries,
67
+ }, result.manifestUpdate);
68
+ if (!manifestResult.success && !manifestResult.skipped) {
69
+ logger.info(`Warning: Failed to update manifest for ${repoName}: ${manifestResult.message}`);
70
+ }
71
+ }
72
+ }
73
+ else {
74
+ logger.error(i + 1, repoName, result.message);
75
+ collector.appendError(repoName, result.message);
76
+ }
77
+ results.push({
78
+ repoName,
79
+ status: result.skipped
80
+ ? "skipped"
81
+ : result.success
82
+ ? "succeeded"
83
+ : "failed",
84
+ message: result.message,
85
+ rulesetPlanDetails: result.planOutput?.entries,
86
+ });
87
+ if (!result.skipped) {
88
+ collector.getOrCreate(repoName).rulesetResult = result;
89
+ }
90
+ }
91
+ catch (error) {
92
+ logger.error(i + 1, repoName, String(error));
93
+ results.push(buildErrorResult(repoName, error));
94
+ collector.appendError(repoName, error);
95
+ }
96
+ }
97
+ }
98
+ /**
99
+ * Fetches the managed rulesets list from a remote GitHub repo's manifest.
100
+ * Returns an empty array if the manifest doesn't exist or can't be read.
101
+ *
102
+ * Uses the project's ICommandExecutor + escapeShellArg pattern for safe
103
+ * command execution. All inputs are from parsed config (owner/repo), not
104
+ * user input.
105
+ */
106
+ async function fetchManagedRulesets(repoInfo, configId) {
107
+ try {
108
+ const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}/contents/${MANIFEST_FILENAME}`;
109
+ const command = `gh api ${escapeShellArg(endpoint)} --jq '.content'`;
110
+ const base64Content = await defaultExecutor.exec(command, process.cwd());
111
+ const content = Buffer.from(base64Content.trim(), "base64").toString("utf-8");
112
+ const manifest = parseManifestContent(content);
113
+ return getManagedRulesets(manifest, configId);
114
+ }
115
+ catch {
116
+ return [];
117
+ }
118
+ }
@@ -0,0 +1,11 @@
1
+ import type { ProcessorResults } from "../settings-report-builder.js";
2
+ /**
3
+ * Collects processing results for the SettingsReport.
4
+ * Provides a centralized way to track results across rulesets, repo settings, and labels.
5
+ */
6
+ export declare class ResultsCollector {
7
+ private readonly results;
8
+ getOrCreate(repoName: string): ProcessorResults;
9
+ appendError(repoName: string, error: unknown): void;
10
+ getAll(): ProcessorResults[];
11
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Collects processing results for the SettingsReport.
3
+ * Provides a centralized way to track results across rulesets, repo settings, and labels.
4
+ */
5
+ export class ResultsCollector {
6
+ results = [];
7
+ getOrCreate(repoName) {
8
+ let result = this.results.find((r) => r.repoName === repoName);
9
+ if (!result) {
10
+ result = { repoName };
11
+ this.results.push(result);
12
+ }
13
+ return result;
14
+ }
15
+ appendError(repoName, error) {
16
+ const existing = this.getOrCreate(repoName);
17
+ const errorMsg = error instanceof Error ? error.message : String(error);
18
+ if (existing.error) {
19
+ existing.error += `; ${errorMsg}`;
20
+ }
21
+ else {
22
+ existing.error = errorMsg;
23
+ }
24
+ }
25
+ getAll() {
26
+ return this.results;
27
+ }
28
+ }
@@ -1,11 +1,11 @@
1
1
  import { SharedOptions } from "./sync-command.js";
2
- import { ProcessorFactory, RulesetProcessorFactory, RepoSettingsProcessorFactory } from "./types.js";
2
+ import { ProcessorFactory, RulesetProcessorFactory, RepoSettingsProcessorFactory, LabelsProcessorFactory } from "./types.js";
3
3
  import { type IRepoLifecycleManager } from "../lifecycle/index.js";
4
4
  /**
5
5
  * Options for the settings command.
6
6
  */
7
7
  export type SettingsOptions = SharedOptions;
8
8
  /**
9
- * Run the settings command - manages GitHub Rulesets and repo settings.
9
+ * Run the settings command - manages GitHub Rulesets, repo settings, and labels.
10
10
  */
11
- export declare function runSettings(options: SettingsOptions, processorFactory?: RulesetProcessorFactory, repoProcessorFactory?: ProcessorFactory, repoSettingsProcessorFactory?: RepoSettingsProcessorFactory, lifecycleManager?: IRepoLifecycleManager): Promise<void>;
11
+ export declare function runSettings(options: SettingsOptions, processorFactory?: RulesetProcessorFactory, repoProcessorFactory?: ProcessorFactory, repoSettingsProcessorFactory?: RepoSettingsProcessorFactory, lifecycleManager?: IRepoLifecycleManager, labelsProcessorFactory?: LabelsProcessorFactory): Promise<void>;