@aspruyt/xfg 6.0.0 → 6.0.2

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 (99) hide show
  1. package/dist/cli/sync-command.js +33 -35
  2. package/dist/cli/types.d.ts +12 -11
  3. package/dist/{output → cli}/unified-summary.d.ts +3 -3
  4. package/dist/{output → cli}/unified-summary.js +4 -4
  5. package/dist/config/file-reference-resolver.js +24 -56
  6. package/dist/config/normalizer.js +29 -40
  7. package/dist/config/validator.js +94 -102
  8. package/dist/lifecycle/ado-migration-source.d.ts +1 -1
  9. package/dist/lifecycle/ado-migration-source.js +1 -1
  10. package/dist/lifecycle/github-lifecycle-provider.d.ts +5 -6
  11. package/dist/lifecycle/github-lifecycle-provider.js +50 -20
  12. package/dist/lifecycle/lifecycle-formatter.d.ts +2 -1
  13. package/dist/lifecycle/lifecycle-formatter.js +1 -1
  14. package/dist/lifecycle/lifecycle-helpers.d.ts +1 -1
  15. package/dist/lifecycle/repo-lifecycle-manager.d.ts +1 -1
  16. package/dist/lifecycle/repo-lifecycle-manager.js +16 -6
  17. package/dist/lifecycle/types.d.ts +30 -8
  18. package/dist/output/lifecycle-report.d.ts +4 -2
  19. package/dist/output/settings-report.d.ts +4 -4
  20. package/dist/repo/detector.d.ts +8 -0
  21. package/dist/{shared/repo-detector.js → repo/detector.js} +1 -4
  22. package/dist/repo/index.d.ts +4 -0
  23. package/dist/repo/index.js +3 -0
  24. package/dist/{shared/repo-metadata-provider.d.ts → repo/metadata-provider.d.ts} +3 -3
  25. package/dist/{shared/repo-metadata-provider.js → repo/metadata-provider.js} +3 -3
  26. package/dist/{shared/repo-detector.d.ts → repo/types.d.ts} +1 -7
  27. package/dist/repo/types.js +1 -0
  28. package/dist/{shared/repo-info-utils.d.ts → repo/utils.d.ts} +1 -1
  29. package/dist/{shared/repo-info-utils.js → repo/utils.js} +1 -1
  30. package/dist/settings/base-processor.d.ts +1 -1
  31. package/dist/settings/base-processor.js +1 -1
  32. package/dist/settings/code-scanning/github-code-scanning-strategy.d.ts +1 -1
  33. package/dist/settings/code-scanning/github-code-scanning-strategy.js +1 -1
  34. package/dist/settings/code-scanning/processor.d.ts +2 -2
  35. package/dist/settings/code-scanning/types.d.ts +1 -1
  36. package/dist/settings/index.d.ts +1 -1
  37. package/dist/settings/labels/formatter.js +16 -11
  38. package/dist/settings/labels/github-labels-strategy.d.ts +1 -1
  39. package/dist/settings/labels/github-labels-strategy.js +1 -1
  40. package/dist/settings/labels/processor.d.ts +1 -1
  41. package/dist/settings/labels/types.d.ts +1 -1
  42. package/dist/settings/repo-settings/diff.d.ts +1 -1
  43. package/dist/settings/repo-settings/diff.js +1 -1
  44. package/dist/settings/repo-settings/formatter.js +2 -4
  45. package/dist/settings/repo-settings/github-repo-settings-strategy.d.ts +4 -4
  46. package/dist/settings/repo-settings/github-repo-settings-strategy.js +4 -4
  47. package/dist/settings/repo-settings/processor.d.ts +2 -2
  48. package/dist/settings/repo-settings/processor.js +5 -5
  49. package/dist/settings/repo-settings/types.d.ts +4 -4
  50. package/dist/settings/rulesets/diff-algorithm.js +1 -1
  51. package/dist/settings/rulesets/formatter.js +0 -3
  52. package/dist/settings/rulesets/github-ruleset-strategy.d.ts +1 -1
  53. package/dist/settings/rulesets/github-ruleset-strategy.js +1 -1
  54. package/dist/settings/rulesets/processor.d.ts +1 -1
  55. package/dist/settings/rulesets/types.d.ts +1 -1
  56. package/dist/shared/command-executor.js +3 -3
  57. package/dist/shared/gh-api-utils.d.ts +7 -4
  58. package/dist/shared/gh-api-utils.js +2 -2
  59. package/dist/shared/retry-utils.js +1 -1
  60. package/dist/shared/xfg-template.d.ts +22 -2
  61. package/dist/sync/auth-options-builder.d.ts +1 -1
  62. package/dist/sync/auth-options-builder.js +1 -1
  63. package/dist/sync/branch-manager.d.ts +1 -1
  64. package/dist/sync/commit-push-manager.d.ts +2 -2
  65. package/dist/sync/commit-push-manager.js +5 -3
  66. package/dist/sync/file-sync-orchestrator.d.ts +1 -1
  67. package/dist/sync/file-sync-strategy.d.ts +1 -1
  68. package/dist/sync/file-writer.js +44 -10
  69. package/dist/sync/repository-processor.d.ts +1 -1
  70. package/dist/sync/repository-session.d.ts +1 -1
  71. package/dist/sync/sync-workflow.d.ts +1 -1
  72. package/dist/sync/sync-workflow.js +2 -1
  73. package/dist/sync/types.d.ts +7 -4
  74. package/dist/vcs/{azure-pr-strategy.d.ts → ado-pr-strategy.d.ts} +2 -2
  75. package/dist/vcs/{azure-pr-strategy.js → ado-pr-strategy.js} +4 -4
  76. package/dist/vcs/authenticated-git-ops.d.ts +2 -0
  77. package/dist/vcs/authenticated-git-ops.js +6 -0
  78. package/dist/vcs/commit-strategy-selector.d.ts +1 -1
  79. package/dist/vcs/commit-strategy-selector.js +1 -1
  80. package/dist/vcs/file-mode-fixup-commit-strategy.d.ts +8 -6
  81. package/dist/vcs/file-mode-fixup-commit-strategy.js +79 -30
  82. package/dist/vcs/git-ops.d.ts +15 -3
  83. package/dist/vcs/git-ops.js +57 -24
  84. package/dist/vcs/github-app-token-manager.d.ts +1 -1
  85. package/dist/vcs/github-pr-strategy.d.ts +1 -1
  86. package/dist/vcs/github-pr-strategy.js +4 -4
  87. package/dist/vcs/gitlab-pr-strategy.d.ts +1 -1
  88. package/dist/vcs/gitlab-pr-strategy.js +4 -4
  89. package/dist/vcs/graphql-commit-strategy.js +8 -3
  90. package/dist/vcs/index.d.ts +1 -1
  91. package/dist/vcs/pr-creator.d.ts +1 -1
  92. package/dist/vcs/pr-strategy-factory.d.ts +1 -1
  93. package/dist/vcs/pr-strategy-factory.js +3 -3
  94. package/dist/vcs/pr-strategy.d.ts +1 -1
  95. package/dist/vcs/pr-strategy.js +1 -1
  96. package/dist/vcs/types.d.ts +10 -3
  97. package/package.json +3 -3
  98. /package/dist/{shared → vcs}/sanitize-utils.d.ts +0 -0
  99. /package/dist/{shared → vcs}/sanitize-utils.js +0 -0
@@ -0,0 +1,8 @@
1
+ import type { RepoInfo } from "./types.js";
2
+ export type RepoPlatform = "github" | "azure-devops" | "gitlab";
3
+ interface RepoDetectorContext {
4
+ githubHosts?: string[];
5
+ }
6
+ export declare function detectRepoType(gitUrl: string, context?: RepoDetectorContext): RepoPlatform;
7
+ export declare function parseGitUrl(gitUrl: string, context?: RepoDetectorContext): RepoInfo;
8
+ export {};
@@ -1,7 +1,4 @@
1
- import { ValidationError } from "./errors.js";
2
- // Type guards, assertions, and display helpers live in repo-info-utils.ts.
3
- // Re-exported here for backward compatibility.
4
- export { isGitHubRepo, isAzureDevOpsRepo, isGitLabRepo, assertGitHubRepo, assertAzureDevOpsRepo, assertGitLabRepo, getRepoDisplayName, } from "./repo-info-utils.js";
1
+ import { ValidationError } from "../shared/errors.js";
5
2
  function extractHostFromUrl(gitUrl) {
6
3
  // SSH: git@hostname:path
7
4
  const sshMatch = gitUrl.match(/^git@([^:]+):/);
@@ -0,0 +1,4 @@
1
+ export type { RepoInfo, GitHubRepoInfo, AzureDevOpsRepoInfo, GitLabRepoInfo, } from "./types.js";
2
+ export { isGitHubRepo, isAzureDevOpsRepo, isGitLabRepo, assertGitHubRepo, assertAzureDevOpsRepo, assertGitLabRepo, getRepoDisplayName, } from "./utils.js";
3
+ export { detectRepoType, parseGitUrl, type RepoPlatform } from "./detector.js";
4
+ export { GitHubRepoMetadataProvider, type IRepoMetadataProvider, type RepoMetadata, } from "./metadata-provider.js";
@@ -0,0 +1,3 @@
1
+ export { isGitHubRepo, isAzureDevOpsRepo, isGitLabRepo, assertGitHubRepo, assertAzureDevOpsRepo, assertGitLabRepo, getRepoDisplayName, } from "./utils.js";
2
+ export { detectRepoType, parseGitUrl } from "./detector.js";
3
+ export { GitHubRepoMetadataProvider, } from "./metadata-provider.js";
@@ -1,6 +1,6 @@
1
- import type { ICommandExecutor } from "./command-executor.js";
2
- import { type RepoInfo } from "./repo-detector.js";
3
- import { type GhApiOptions } from "./gh-api-utils.js";
1
+ import type { ICommandExecutor } from "../shared/command-executor.js";
2
+ import type { RepoInfo } from "./types.js";
3
+ import { type GhApiOptions } from "../shared/gh-api-utils.js";
4
4
  import type { RepoVisibility } from "../config/index.js";
5
5
  export interface RepoMetadata {
6
6
  visibility: RepoVisibility;
@@ -1,6 +1,6 @@
1
- import { assertGitHubRepo } from "./repo-detector.js";
2
- import { GhApiClient } from "./gh-api-utils.js";
3
- import { parseApiJson } from "./json-utils.js";
1
+ import { assertGitHubRepo } from "./utils.js";
2
+ import { GhApiClient } from "../shared/gh-api-utils.js";
3
+ import { parseApiJson } from "../shared/json-utils.js";
4
4
  export class GitHubRepoMetadataProvider {
5
5
  api;
6
6
  constructor(executor, options) {
@@ -1,7 +1,3 @@
1
- type RepoType = "github" | "azure-devops" | "gitlab";
2
- interface RepoDetectorContext {
3
- githubHosts?: string[];
4
- }
5
1
  interface BaseRepoInfo {
6
2
  gitUrl: string;
7
3
  repo: string;
@@ -24,6 +20,4 @@ export interface GitLabRepoInfo extends BaseRepoInfo {
24
20
  host: string;
25
21
  }
26
22
  export type RepoInfo = GitHubRepoInfo | AzureDevOpsRepoInfo | GitLabRepoInfo;
27
- export { isGitHubRepo, isAzureDevOpsRepo, isGitLabRepo, assertGitHubRepo, assertAzureDevOpsRepo, assertGitLabRepo, getRepoDisplayName, } from "./repo-info-utils.js";
28
- export declare function detectRepoType(gitUrl: string, context?: RepoDetectorContext): RepoType;
29
- export declare function parseGitUrl(gitUrl: string, context?: RepoDetectorContext): RepoInfo;
23
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import type { RepoInfo, GitHubRepoInfo, AzureDevOpsRepoInfo, GitLabRepoInfo } from "./repo-detector.js";
1
+ import type { RepoInfo, GitHubRepoInfo, AzureDevOpsRepoInfo, GitLabRepoInfo } from "./types.js";
2
2
  export declare function isGitHubRepo(info: RepoInfo): info is GitHubRepoInfo;
3
3
  export declare function isAzureDevOpsRepo(info: RepoInfo): info is AzureDevOpsRepoInfo;
4
4
  export declare function isGitLabRepo(info: RepoInfo): info is GitLabRepoInfo;
@@ -1,4 +1,4 @@
1
- import { ValidationError } from "./errors.js";
1
+ import { ValidationError } from "../shared/errors.js";
2
2
  export function isGitHubRepo(info) {
3
3
  return info.type === "github";
4
4
  }
@@ -1,5 +1,5 @@
1
1
  import type { RepoConfig } from "../config/index.js";
2
- import type { RepoInfo, GitHubRepoInfo } from "../shared/repo-detector.js";
2
+ import type { RepoInfo, GitHubRepoInfo } from "../repo/index.js";
3
3
  export interface BaseProcessorOptions {
4
4
  dryRun?: boolean;
5
5
  /** Pre-resolved auth token. Callers (e.g. sync-command) must resolve via resolveGitHubToken before passing. */
@@ -1,4 +1,4 @@
1
- import { isGitHubRepo, getRepoDisplayName } from "../shared/repo-detector.js";
1
+ import { isGitHubRepo, getRepoDisplayName } from "../repo/index.js";
2
2
  import { toErrorMessage } from "../shared/type-guards.js";
3
3
  /**
4
4
  * Build a base result that satisfies TResult for guard early-returns.
@@ -1,5 +1,5 @@
1
1
  import type { ICommandExecutor } from "../../shared/command-executor.js";
2
- import { type RepoInfo } from "../../shared/repo-detector.js";
2
+ import { type RepoInfo } from "../../repo/index.js";
3
3
  import { type GhApiOptions } from "../../shared/gh-api-utils.js";
4
4
  import type { ICodeScanningStrategy, CurrentCodeScanningSettings } from "./types.js";
5
5
  interface GitHubCodeScanningStrategyOptions {
@@ -1,4 +1,4 @@
1
- import { assertGitHubRepo } from "../../shared/repo-detector.js";
1
+ import { assertGitHubRepo } from "../../repo/index.js";
2
2
  import { GhApiClient } from "../../shared/gh-api-utils.js";
3
3
  import { parseApiJson } from "../../shared/json-utils.js";
4
4
  export class GitHubCodeScanningStrategy {
@@ -1,7 +1,7 @@
1
1
  import type { RepoConfig } from "../../config/index.js";
2
- import type { RepoInfo } from "../../shared/repo-detector.js";
2
+ import type { RepoInfo } from "../../repo/index.js";
3
3
  import type { ICodeScanningStrategy } from "./types.js";
4
- import type { IRepoMetadataProvider } from "../../shared/repo-metadata-provider.js";
4
+ import type { IRepoMetadataProvider } from "../../repo/index.js";
5
5
  import { type CodeScanningPlanResult } from "./formatter.js";
6
6
  import { type BaseProcessorOptions, type BaseProcessorResult, type ISettingsProcessor, type ChangeCounts } from "../base-processor.js";
7
7
  export type ICodeScanningProcessor = ISettingsProcessor<CodeScanningProcessorOptions, CodeScanningProcessorResult>;
@@ -1,4 +1,4 @@
1
- import type { RepoInfo } from "../../shared/repo-detector.js";
1
+ import type { RepoInfo } from "../../repo/index.js";
2
2
  import type { GhApiOptions } from "../../shared/gh-api-utils.js";
3
3
  /**
4
4
  * Current code scanning default setup state from GitHub API.
@@ -1,4 +1,4 @@
1
- export { type BaseProcessorResult, type ISettingsProcessor, countActions, isActiveAction, } from "./base-processor.js";
1
+ export { type BaseProcessorResult, type ISettingsProcessor, type SettingsAction, type ActiveAction, countActions, isActiveAction, } from "./base-processor.js";
2
2
  export { type PropertyDiff, type RulesetPlanEntry, RulesetProcessor, type IRulesetProcessor, GitHubRulesetStrategy, } from "./rulesets/index.js";
3
3
  export { RepoSettingsProcessor, type IRepoSettingsProcessor, type RepoSettingsPlanEntry, GitHubRepoSettingsStrategy, } from "./repo-settings/index.js";
4
4
  export { type LabelsPlanEntry, LabelsProcessor, type ILabelsProcessor, GitHubLabelsStrategy, } from "./labels/index.js";
@@ -7,14 +7,19 @@ export function formatLabelsPlan(changes) {
7
7
  const lines = [];
8
8
  const entries = [];
9
9
  const { create: creates, update: updates, delete: deletes, unchanged, } = countActions(changes);
10
- const createChanges = changes.filter((c) => c.action === "create");
11
- const updateChanges = changes.filter((c) => c.action === "update");
12
- const deleteChanges = changes.filter((c) => c.action === "delete");
13
- const unchangedItems = changes.filter((c) => c.action === "unchanged");
10
+ const grouped = {
11
+ create: [],
12
+ update: [],
13
+ delete: [],
14
+ unchanged: [],
15
+ };
16
+ for (const c of changes) {
17
+ grouped[c.action].push(c);
18
+ }
14
19
  // Format creates
15
- if (createChanges.length > 0) {
20
+ if (grouped.create.length > 0) {
16
21
  lines.push(chalk.bold(" Create:"));
17
- for (const change of createChanges) {
22
+ for (const change of grouped.create) {
18
23
  lines.push(chalk.green(` + label "${change.name}"`));
19
24
  if (change.desired) {
20
25
  lines.push(chalk.green(` color: "${change.desired.color}"`));
@@ -31,9 +36,9 @@ export function formatLabelsPlan(changes) {
31
36
  }
32
37
  }
33
38
  // Format updates
34
- if (updateChanges.length > 0) {
39
+ if (grouped.update.length > 0) {
35
40
  lines.push(chalk.bold(" Update:"));
36
- for (const change of updateChanges) {
41
+ for (const change of grouped.update) {
37
42
  if (change.newName) {
38
43
  lines.push(chalk.yellow(` ~ label "${change.name}" \u2192 "${change.newName}"`));
39
44
  }
@@ -62,16 +67,16 @@ export function formatLabelsPlan(changes) {
62
67
  }
63
68
  }
64
69
  // Format deletes
65
- if (deleteChanges.length > 0) {
70
+ if (grouped.delete.length > 0) {
66
71
  lines.push(chalk.bold(" Delete:"));
67
- for (const change of deleteChanges) {
72
+ for (const change of grouped.delete) {
68
73
  lines.push(chalk.red(` - label "${change.name}"`));
69
74
  entries.push({ name: change.name, action: "delete" });
70
75
  }
71
76
  lines.push("");
72
77
  }
73
78
  // Unchanged (entries only, no output lines)
74
- for (const change of unchangedItems) {
79
+ for (const change of grouped.unchanged) {
75
80
  entries.push({ name: change.name, action: "unchanged" });
76
81
  }
77
82
  // Summary line
@@ -1,5 +1,5 @@
1
1
  import type { ICommandExecutor } from "../../shared/command-executor.js";
2
- import { type RepoInfo } from "../../shared/repo-detector.js";
2
+ import { type RepoInfo } from "../../repo/index.js";
3
3
  import { type GhApiOptions } from "../../shared/gh-api-utils.js";
4
4
  import type { ILabelsStrategy, GitHubLabel } from "./types.js";
5
5
  interface GitHubLabelsStrategyOptions {
@@ -1,4 +1,4 @@
1
- import { assertGitHubRepo } from "../../shared/repo-detector.js";
1
+ import { assertGitHubRepo } from "../../repo/index.js";
2
2
  import { GhApiClient } from "../../shared/gh-api-utils.js";
3
3
  import { parseApiJson } from "../../shared/json-utils.js";
4
4
  export class GitHubLabelsStrategy {
@@ -1,5 +1,5 @@
1
1
  import type { RepoConfig } from "../../config/index.js";
2
- import type { RepoInfo } from "../../shared/repo-detector.js";
2
+ import type { RepoInfo } from "../../repo/index.js";
3
3
  import { type LabelsPlanResult } from "./formatter.js";
4
4
  import type { ILabelsStrategy } from "./types.js";
5
5
  import { type BaseProcessorOptions, type BaseProcessorResult, type ISettingsProcessor, type ChangeCounts } from "../base-processor.js";
@@ -1,4 +1,4 @@
1
- import type { RepoInfo } from "../../shared/repo-detector.js";
1
+ import type { RepoInfo } from "../../repo/index.js";
2
2
  import type { GhApiOptions } from "../../shared/gh-api-utils.js";
3
3
  export interface GitHubLabel {
4
4
  id: number;
@@ -16,4 +16,4 @@ export declare function diffRepoSettings(current: CurrentRepoSettings, desired:
16
16
  /**
17
17
  * Checks if there are any changes to apply.
18
18
  */
19
- export declare function hasChanges(changes: RepoSettingsChange[]): boolean;
19
+ export declare function hasRepoSettingsChanges(changes: RepoSettingsChange[]): boolean;
@@ -82,6 +82,6 @@ export function diffRepoSettings(current, desired) {
82
82
  /**
83
83
  * Checks if there are any changes to apply.
84
84
  */
85
- export function hasChanges(changes) {
85
+ export function hasRepoSettingsChanges(changes) {
86
86
  return changes.some((c) => c.action !== "unchanged");
87
87
  }
@@ -1,5 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { formatScalarValue } from "../../shared/string-utils.js";
3
+ import { countActions } from "../base-processor.js";
3
4
  /**
4
5
  * Format a value for display.
5
6
  */
@@ -33,8 +34,7 @@ function getWarning(change) {
33
34
  export function formatRepoSettingsPlan(changes) {
34
35
  const lines = [];
35
36
  const warnings = [];
36
- let creates = 0;
37
- let updates = 0;
37
+ const { create: creates, update: updates } = countActions(changes);
38
38
  const entries = [];
39
39
  if (changes.length === 0) {
40
40
  return { lines, creates, updates, warnings, entries };
@@ -46,7 +46,6 @@ export function formatRepoSettingsPlan(changes) {
46
46
  }
47
47
  if (change.action === "create") {
48
48
  lines.push(chalk.green(` + ${change.property}: ${formatValue(change.newValue)}`));
49
- creates++;
50
49
  entries.push({
51
50
  property: change.property,
52
51
  action: "create",
@@ -55,7 +54,6 @@ export function formatRepoSettingsPlan(changes) {
55
54
  }
56
55
  else if (change.action === "update") {
57
56
  lines.push(chalk.yellow(` ~ ${change.property}: ${formatValue(change.oldValue)} → ${formatValue(change.newValue)}`));
58
- updates++;
59
57
  entries.push({
60
58
  property: change.property,
61
59
  action: "update",
@@ -1,5 +1,5 @@
1
1
  import type { ICommandExecutor } from "../../shared/command-executor.js";
2
- import { type RepoInfo } from "../../shared/repo-detector.js";
2
+ import { type RepoInfo } from "../../repo/index.js";
3
3
  import { type GhApiOptions } from "../../shared/gh-api-utils.js";
4
4
  import type { GitHubRepoSettings } from "../../config/index.js";
5
5
  import type { IRepoSettingsStrategy, CurrentRepoSettings } from "./types.js";
@@ -10,9 +10,9 @@ interface GitHubRepoSettingsStrategyOptions {
10
10
  export declare class GitHubRepoSettingsStrategy implements IRepoSettingsStrategy {
11
11
  private api;
12
12
  constructor(executor: ICommandExecutor, options: GitHubRepoSettingsStrategyOptions);
13
- getSettings(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentRepoSettings>;
14
- updateSettings(repoInfo: RepoInfo, settings: GitHubRepoSettings, options?: GhApiOptions): Promise<void>;
15
- setVulnerabilityAlerts(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
13
+ get(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentRepoSettings>;
14
+ update(repoInfo: RepoInfo, settings: GitHubRepoSettings, options?: GhApiOptions): Promise<void>;
15
+ updateVulnerabilityAlerts(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
16
16
  setAutomatedSecurityFixes(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
17
17
  setPrivateVulnerabilityReporting(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
18
18
  branchExists(repoInfo: RepoInfo, branch: string, options?: GhApiOptions): Promise<boolean>;
@@ -1,4 +1,4 @@
1
- import { assertGitHubRepo, } from "../../shared/repo-detector.js";
1
+ import { assertGitHubRepo, } from "../../repo/index.js";
2
2
  import { GhApiClient, isHttp404Error, } from "../../shared/gh-api-utils.js";
3
3
  import { parseApiJson } from "../../shared/json-utils.js";
4
4
  import { camelToSnake } from "../../shared/string-utils.js";
@@ -61,7 +61,7 @@ export class GitHubRepoSettingsStrategy {
61
61
  constructor(executor, options) {
62
62
  this.api = new GhApiClient(executor, options.retries ?? 3, options.cwd);
63
63
  }
64
- async getSettings(repoInfo, options) {
64
+ async get(repoInfo, options) {
65
65
  assertGitHubRepo(repoInfo, "GitHub Repo Settings strategy");
66
66
  const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}`;
67
67
  const result = await this.api.call("GET", endpoint, { options });
@@ -76,7 +76,7 @@ export class GitHubRepoSettingsStrategy {
76
76
  await this.getPrivateVulnerabilityReporting(repoInfo, options);
77
77
  return settings;
78
78
  }
79
- async updateSettings(repoInfo, settings, options) {
79
+ async update(repoInfo, settings, options) {
80
80
  assertGitHubRepo(repoInfo, "GitHub Repo Settings strategy");
81
81
  const payload = configToGitHubPayload(settings);
82
82
  // Skip if no settings to update
@@ -86,7 +86,7 @@ export class GitHubRepoSettingsStrategy {
86
86
  const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}`;
87
87
  await this.api.call("PATCH", endpoint, { payload, options });
88
88
  }
89
- async setVulnerabilityAlerts(repoInfo, enable, options) {
89
+ async updateVulnerabilityAlerts(repoInfo, enable, options) {
90
90
  assertGitHubRepo(repoInfo, "GitHub Repo Settings strategy");
91
91
  const endpoint = `/repos/${repoInfo.owner}/${repoInfo.repo}/vulnerability-alerts`;
92
92
  const method = enable ? "PUT" : "DELETE";
@@ -1,7 +1,7 @@
1
1
  import type { RepoConfig } from "../../config/index.js";
2
- import type { RepoInfo } from "../../shared/repo-detector.js";
2
+ import type { RepoInfo } from "../../repo/index.js";
3
3
  import type { IRepoSettingsStrategy } from "./types.js";
4
- import type { IRepoMetadataProvider } from "../../shared/repo-metadata-provider.js";
4
+ import type { IRepoMetadataProvider } from "../../repo/index.js";
5
5
  import { type RepoSettingsPlanResult } from "./formatter.js";
6
6
  import { type BaseProcessorOptions, type BaseProcessorResult, type ISettingsProcessor, type ChangeCounts } from "../base-processor.js";
7
7
  export type IRepoSettingsProcessor = ISettingsProcessor<RepoSettingsProcessorOptions, RepoSettingsProcessorResult>;
@@ -1,4 +1,4 @@
1
- import { diffRepoSettings, hasChanges } from "./diff.js";
1
+ import { diffRepoSettings, hasRepoSettingsChanges } from "./diff.js";
2
2
  import { formatRepoSettingsPlan, } from "./formatter.js";
3
3
  import { withGitHubGuards, buildDryRunResult, buildApplyResult, } from "../base-processor.js";
4
4
  export class RepoSettingsProcessor {
@@ -24,7 +24,7 @@ export class RepoSettingsProcessor {
24
24
  const strategyOptions = { token: effectiveToken, host: githubRepo.host };
25
25
  // Fetch current settings and metadata in parallel
26
26
  const [currentSettings, metadata] = await Promise.all([
27
- this.strategy.getSettings(githubRepo, strategyOptions),
27
+ this.strategy.get(githubRepo, strategyOptions),
28
28
  this.metadataProvider.getMetadata(githubRepo, strategyOptions),
29
29
  ]);
30
30
  // Validate security settings compatibility
@@ -38,7 +38,7 @@ export class RepoSettingsProcessor {
38
38
  }
39
39
  // Compute diff
40
40
  const changes = diffRepoSettings(currentSettings, desiredSettings);
41
- if (!hasChanges(changes)) {
41
+ if (!hasRepoSettingsChanges(changes)) {
42
42
  const unchangedCount = changes.filter((c) => c.action === "unchanged").length;
43
43
  return {
44
44
  success: true,
@@ -97,12 +97,12 @@ export class RepoSettingsProcessor {
97
97
  const { vulnerabilityAlerts, automatedSecurityFixes, privateVulnerabilityReporting, ...mainSettings } = settings;
98
98
  // Update main settings via PATCH /repos
99
99
  if (Object.keys(mainSettings).length > 0) {
100
- await this.strategy.updateSettings(repoInfo, mainSettings, options);
100
+ await this.strategy.update(repoInfo, mainSettings, options);
101
101
  }
102
102
  // Handle vulnerability alerts (separate endpoint)
103
103
  // Must be done before automated security fixes
104
104
  if (vulnerabilityAlerts !== undefined) {
105
- await this.strategy.setVulnerabilityAlerts(repoInfo, vulnerabilityAlerts, options);
105
+ await this.strategy.updateVulnerabilityAlerts(repoInfo, vulnerabilityAlerts, options);
106
106
  }
107
107
  // Handle private vulnerability reporting (separate endpoint)
108
108
  if (privateVulnerabilityReporting !== undefined) {
@@ -1,4 +1,4 @@
1
- import type { RepoInfo } from "../../shared/repo-detector.js";
1
+ import type { RepoInfo } from "../../repo/index.js";
2
2
  import type { GitHubRepoSettings, RepoVisibility, SquashMergeCommitTitle, SquashMergeCommitMessage, MergeCommitTitle, MergeCommitMessage } from "../../config/index.js";
3
3
  import type { GhApiOptions } from "../../shared/gh-api-utils.js";
4
4
  /**
@@ -46,15 +46,15 @@ export interface IRepoSettingsStrategy {
46
46
  /**
47
47
  * Gets current repository settings.
48
48
  */
49
- getSettings(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentRepoSettings>;
49
+ get(repoInfo: RepoInfo, options?: GhApiOptions): Promise<CurrentRepoSettings>;
50
50
  /**
51
51
  * Updates repository settings.
52
52
  */
53
- updateSettings(repoInfo: RepoInfo, settings: GitHubRepoSettings, options?: GhApiOptions): Promise<void>;
53
+ update(repoInfo: RepoInfo, settings: GitHubRepoSettings, options?: GhApiOptions): Promise<void>;
54
54
  /**
55
55
  * Enables or disables vulnerability alerts.
56
56
  */
57
- setVulnerabilityAlerts(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
57
+ updateVulnerabilityAlerts(repoInfo: RepoInfo, enable: boolean, options?: GhApiOptions): Promise<void>;
58
58
  /**
59
59
  * Enables or disables automated security fixes.
60
60
  */
@@ -3,7 +3,7 @@ export function deepEqual(a, b) {
3
3
  if (a === b)
4
4
  return true;
5
5
  if (a === null || b === null || a === undefined || b === undefined)
6
- return a === b;
6
+ return false;
7
7
  if (typeof a !== typeof b)
8
8
  return false;
9
9
  if (Array.isArray(a) && Array.isArray(b)) {
@@ -116,9 +116,6 @@ function getActionStyle(action) {
116
116
  return { symbol: "~", color: chalk.yellow };
117
117
  }
118
118
  }
119
- /**
120
- * Render a leaf tree node (no children) with its value.
121
- */
122
119
  function hasComplexValue(value) {
123
120
  return (isPlainObject(value) ||
124
121
  (Array.isArray(value) && value.some((v) => isPlainObject(v))));
@@ -1,5 +1,5 @@
1
1
  import type { ICommandExecutor } from "../../shared/command-executor.js";
2
- import { type RepoInfo } from "../../shared/repo-detector.js";
2
+ import { type RepoInfo } from "../../repo/index.js";
3
3
  import { type GhApiOptions } from "../../shared/gh-api-utils.js";
4
4
  import type { Ruleset } from "../../config/index.js";
5
5
  import type { IRulesetStrategy, GitHubRuleset, GitHubBypassActor, GitHubRulesetConditions, GitHubRule, RulesetUpdateParams } from "./types.js";
@@ -1,4 +1,4 @@
1
- import { assertGitHubRepo } from "../../shared/repo-detector.js";
1
+ import { assertGitHubRepo } from "../../repo/index.js";
2
2
  import { camelToSnake } from "../../shared/string-utils.js";
3
3
  import { GhApiClient } from "../../shared/gh-api-utils.js";
4
4
  import { parseApiJson } from "../../shared/json-utils.js";
@@ -1,5 +1,5 @@
1
1
  import type { RepoConfig } from "../../config/index.js";
2
- import type { RepoInfo } from "../../shared/repo-detector.js";
2
+ import type { RepoInfo } from "../../repo/index.js";
3
3
  import type { IRulesetStrategy } from "./types.js";
4
4
  import { type RulesetPlanResult } from "./formatter.js";
5
5
  import { type BaseProcessorOptions, type BaseProcessorResult, type ISettingsProcessor, type ChangeCounts } from "../base-processor.js";
@@ -1,4 +1,4 @@
1
- import type { RepoInfo } from "../../shared/repo-detector.js";
1
+ import type { RepoInfo } from "../../repo/index.js";
2
2
  import type { Ruleset } from "../../config/index.js";
3
3
  import type { GhApiOptions } from "../../shared/gh-api-utils.js";
4
4
  /**
@@ -1,5 +1,5 @@
1
- import { execSync } from "node:child_process";
2
- import { sanitizeCredentials } from "./sanitize-utils.js";
1
+ import { execFileSync } from "node:child_process";
2
+ import { sanitizeCredentials } from "../vcs/sanitize-utils.js";
3
3
  export class ShellCommandExecutor {
4
4
  baseEnv;
5
5
  constructor(baseEnv) {
@@ -7,7 +7,7 @@ export class ShellCommandExecutor {
7
7
  }
8
8
  async exec(command, cwd, options) {
9
9
  try {
10
- return execSync(command, {
10
+ return execFileSync("sh", ["-c", command], {
11
11
  cwd,
12
12
  encoding: "utf-8",
13
13
  stdio: ["pipe", "pipe", "pipe"],
@@ -1,8 +1,11 @@
1
1
  import type { ICommandExecutor } from "./command-executor.js";
2
- import type { GitHubRepoInfo } from "./repo-detector.js";
3
2
  import type { DebugWarnLog } from "./logger.js";
3
+ export interface GitHubApiTarget {
4
+ host: string;
5
+ owner: string;
6
+ }
4
7
  interface ITokenManager {
5
- getTokenForRepo(repoInfo: GitHubRepoInfo): Promise<string | null>;
8
+ getTokenForRepo(repoInfo: GitHubApiTarget): Promise<string | null>;
6
9
  }
7
10
  type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
8
11
  export interface GhApiOptions {
@@ -20,7 +23,7 @@ interface GhApiCallParams {
20
23
  * Get the hostname flag for gh commands.
21
24
  * Returns "--hostname HOST" for GHE, empty string for github.com.
22
25
  */
23
- export declare function getHostnameFlag(repoInfo: GitHubRepoInfo): string;
26
+ export declare function getHostnameFlag(repoInfo: Pick<GitHubApiTarget, "host">): string;
24
27
  export declare function buildTokenEnv(token?: string): Record<string, string> | undefined;
25
28
  /**
26
29
  * Strips HTTP response headers from `gh api --include` output.
@@ -57,7 +60,7 @@ export declare class GhApiClient {
57
60
  call(method: HttpMethod, endpoint: string, params?: GhApiCallParams): Promise<string>;
58
61
  }
59
62
  interface ResolveGitHubTokenOptions {
60
- repoInfo: GitHubRepoInfo;
63
+ repoInfo: GitHubApiTarget;
61
64
  tokenManager: ITokenManager | null;
62
65
  context: string;
63
66
  log?: DebugWarnLog;
@@ -130,9 +130,9 @@ async function ghApiCall(method, endpoint, opts) {
130
130
  (method === "POST" || method === "PUT" || method === "PATCH")) {
131
131
  const payloadJson = JSON.stringify(payload);
132
132
  const command = `echo ${escapeShellArg(payloadJson)} | ${baseCommand} --input -`;
133
- return await withRetry(() => execAndParse(command), retryOpts);
133
+ return withRetry(() => execAndParse(command), retryOpts);
134
134
  }
135
- return await withRetry(() => execAndParse(baseCommand), retryOpts);
135
+ return withRetry(() => execAndParse(baseCommand), retryOpts);
136
136
  }
137
137
  /**
138
138
  * Encapsulates executor + retries for GitHub API calls.
@@ -1,5 +1,5 @@
1
1
  import pRetry, { AbortError } from "p-retry";
2
- import { sanitizeCredentials } from "./sanitize-utils.js";
2
+ import { sanitizeCredentials } from "../vcs/sanitize-utils.js";
3
3
  import { ValidationError } from "./errors.js";
4
4
  /**
5
5
  * Core permanent error patterns shared across all strategies (API, GraphQL, CLI).
@@ -3,11 +3,31 @@
3
3
  * Supports ${xfg:variable} syntax for repo-specific content.
4
4
  * Use $${xfg:variable} to escape and output literal ${xfg:variable}.
5
5
  */
6
- import type { RepoInfo } from "./repo-detector.js";
7
6
  type TemplateContent = Record<string, unknown> | string | string[];
7
+ export type RepoDisplayInfo = {
8
+ type: "github";
9
+ gitUrl: string;
10
+ repo: string;
11
+ owner: string;
12
+ host: string;
13
+ } | {
14
+ type: "azure-devops";
15
+ gitUrl: string;
16
+ repo: string;
17
+ owner: string;
18
+ organization: string;
19
+ project: string;
20
+ } | {
21
+ type: "gitlab";
22
+ gitUrl: string;
23
+ repo: string;
24
+ owner: string;
25
+ namespace: string;
26
+ host: string;
27
+ };
8
28
  export interface XfgTemplateContext {
9
29
  /** Repository information from URL parsing */
10
- repoInfo: RepoInfo;
30
+ repoInfo: RepoDisplayInfo;
11
31
  /** Current file being processed */
12
32
  fileName: string;
13
33
  /** Custom variables defined in config */
@@ -1,4 +1,4 @@
1
- import { type RepoInfo } from "../shared/repo-detector.js";
1
+ import { type RepoInfo } from "../repo/index.js";
2
2
  import type { GitHubAppTokenManager } from "../vcs/index.js";
3
3
  import type { AuthResult, IAuthOptionsBuilder } from "./types.js";
4
4
  import type { ILogger } from "../shared/logger.js";
@@ -1,4 +1,4 @@
1
- import { isGitHubRepo } from "../shared/repo-detector.js";
1
+ import { isGitHubRepo } from "../repo/index.js";
2
2
  import { resolveGitHubToken } from "../shared/gh-api-utils.js";
3
3
  export class AuthOptionsBuilder {
4
4
  tokenManager;
@@ -1,5 +1,5 @@
1
1
  import type { IPRStrategy } from "../vcs/index.js";
2
- import type { RepoInfo } from "../shared/repo-detector.js";
2
+ import type { RepoInfo } from "../repo/index.js";
3
3
  import type { ICommandExecutor } from "../shared/command-executor.js";
4
4
  import type { DebugInfoWarnLog } from "../shared/logger.js";
5
5
  import type { IBranchManager, BranchSetupOptions } from "./types.js";
@@ -1,5 +1,5 @@
1
1
  import type { ICommitStrategy } from "../vcs/index.js";
2
- import type { RepoInfo } from "../shared/repo-detector.js";
2
+ import type { RepoInfo } from "../repo/index.js";
3
3
  import type { ICommandExecutor } from "../shared/command-executor.js";
4
4
  import type { CommitPushOptions, CommitPushResult, ICommitPushManager } from "./types.js";
5
5
  import type { DebugInfoLog } from "../shared/logger.js";
@@ -9,6 +9,6 @@ export declare class CommitPushManager implements ICommitPushManager {
9
9
  private readonly commitStrategyFactory;
10
10
  constructor(log: DebugInfoLog, commitStrategyFactory?: CommitStrategyFactory);
11
11
  commitAndPush(options: CommitPushOptions): Promise<CommitPushResult>;
12
- private rethrowIfUnexpectedCommitError;
12
+ private classifyCommitError;
13
13
  }
14
14
  export {};