@aspruyt/xfg 3.5.8 → 3.7.0
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 -1
- package/dist/config-normalizer.js +13 -4
- package/dist/config-validator.d.ts +1 -1
- package/dist/config-validator.js +22 -3
- package/dist/config.d.ts +3 -1
- package/dist/repo-settings-diff.js +2 -0
- package/dist/repo-settings-plan-formatter.js +3 -0
- package/dist/strategies/github-repo-settings-strategy.js +2 -0
- package/dist/strategies/repo-settings-strategy.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://codecov.io/gh/anthony-spruyt/xfg)
|
|
5
5
|
[](https://www.npmjs.com/package/@aspruyt/xfg)
|
|
6
6
|
[](https://www.npmjs.com/package/@aspruyt/xfg)
|
|
7
|
-
[](https://github.com/marketplace/actions/xfg-
|
|
7
|
+
[](https://github.com/marketplace/actions/xfg-repo-as-code)
|
|
8
8
|
[](https://anthony-spruyt.github.io/xfg/)
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
|
|
@@ -92,10 +92,19 @@ export function mergeSettings(root, perRepo) {
|
|
|
92
92
|
result.deleteOrphaned = deleteOrphaned;
|
|
93
93
|
}
|
|
94
94
|
// Merge repo settings: per-repo overrides root (shallow merge)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
// repo: false means opt out of all root repo settings
|
|
96
|
+
if (perRepo?.repo === false) {
|
|
97
|
+
// Opt-out: don't include any repo settings
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const rootRepo = root?.repo;
|
|
101
|
+
const perRepoRepo = perRepo?.repo;
|
|
102
|
+
if (rootRepo || perRepoRepo) {
|
|
103
|
+
result.repo = {
|
|
104
|
+
...(rootRepo === false ? {} : rootRepo),
|
|
105
|
+
...perRepoRepo,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
99
108
|
}
|
|
100
109
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
101
110
|
}
|
|
@@ -7,7 +7,7 @@ export declare function validateRawConfig(config: RawConfig): void;
|
|
|
7
7
|
/**
|
|
8
8
|
* Validates settings object containing rulesets.
|
|
9
9
|
*/
|
|
10
|
-
export declare function validateSettings(settings: unknown, context: string, rootRulesetNames?: string[]): void;
|
|
10
|
+
export declare function validateSettings(settings: unknown, context: string, rootRulesetNames?: string[], hasRootRepoSettings?: boolean): void;
|
|
11
11
|
/**
|
|
12
12
|
* Validates that config is suitable for the sync command.
|
|
13
13
|
* @throws Error if files section is missing or empty
|
package/dist/config-validator.js
CHANGED
|
@@ -252,7 +252,8 @@ export function validateRawConfig(config) {
|
|
|
252
252
|
const rootRulesetNames = config.settings?.rulesets
|
|
253
253
|
? Object.keys(config.settings.rulesets).filter((k) => k !== "inherit")
|
|
254
254
|
: [];
|
|
255
|
-
|
|
255
|
+
const hasRootRepoSettings = config.settings?.repo !== undefined && config.settings.repo !== false;
|
|
256
|
+
validateSettings(repo.settings, `Repo ${getGitDisplayName(repo.git)}`, rootRulesetNames, hasRootRepoSettings);
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
}
|
|
@@ -318,12 +319,17 @@ function validateRepoSettings(repo, context) {
|
|
|
318
319
|
"secretScanning",
|
|
319
320
|
"secretScanningPushProtection",
|
|
320
321
|
"privateVulnerabilityReporting",
|
|
322
|
+
"webCommitSignoffRequired",
|
|
321
323
|
];
|
|
322
324
|
for (const field of booleanFields) {
|
|
323
325
|
if (r[field] !== undefined && typeof r[field] !== "boolean") {
|
|
324
326
|
throw new Error(`${context}: ${field} must be a boolean`);
|
|
325
327
|
}
|
|
326
328
|
}
|
|
329
|
+
// Validate string fields
|
|
330
|
+
if (r.defaultBranch !== undefined && typeof r.defaultBranch !== "string") {
|
|
331
|
+
throw new Error(`${context}: defaultBranch must be a string`);
|
|
332
|
+
}
|
|
327
333
|
// Validate enum fields
|
|
328
334
|
if (r.visibility !== undefined &&
|
|
329
335
|
!VALID_VISIBILITY.includes(r.visibility)) {
|
|
@@ -552,7 +558,7 @@ function validateRuleset(ruleset, name, context) {
|
|
|
552
558
|
/**
|
|
553
559
|
* Validates settings object containing rulesets.
|
|
554
560
|
*/
|
|
555
|
-
export function validateSettings(settings, context, rootRulesetNames) {
|
|
561
|
+
export function validateSettings(settings, context, rootRulesetNames, hasRootRepoSettings) {
|
|
556
562
|
if (typeof settings !== "object" ||
|
|
557
563
|
settings === null ||
|
|
558
564
|
Array.isArray(settings)) {
|
|
@@ -585,7 +591,20 @@ export function validateSettings(settings, context, rootRulesetNames) {
|
|
|
585
591
|
}
|
|
586
592
|
// Validate repo settings
|
|
587
593
|
if (s.repo !== undefined) {
|
|
588
|
-
|
|
594
|
+
if (s.repo === false) {
|
|
595
|
+
if (!rootRulesetNames) {
|
|
596
|
+
// Root level — repo: false not valid here
|
|
597
|
+
throw new Error(`${context}: repo: false is not valid at root level. Define repo settings or remove the field.`);
|
|
598
|
+
}
|
|
599
|
+
// Per-repo level — check root has repo settings to opt out of
|
|
600
|
+
if (!hasRootRepoSettings) {
|
|
601
|
+
throw new Error(`${context}: Cannot opt out of repo settings — not defined in root settings.repo`);
|
|
602
|
+
}
|
|
603
|
+
// Valid opt-out, skip further repo validation
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
validateRepoSettings(s.repo, context);
|
|
607
|
+
}
|
|
589
608
|
}
|
|
590
609
|
}
|
|
591
610
|
// =============================================================================
|
package/dist/config.d.ts
CHANGED
|
@@ -239,6 +239,8 @@ export interface GitHubRepoSettings {
|
|
|
239
239
|
allowForking?: boolean;
|
|
240
240
|
visibility?: RepoVisibility;
|
|
241
241
|
archived?: boolean;
|
|
242
|
+
webCommitSignoffRequired?: boolean;
|
|
243
|
+
defaultBranch?: string;
|
|
242
244
|
allowSquashMerge?: boolean;
|
|
243
245
|
allowMergeCommit?: boolean;
|
|
244
246
|
allowRebaseMerge?: boolean;
|
|
@@ -289,7 +291,7 @@ export interface RawRepoSettings {
|
|
|
289
291
|
rulesets?: Record<string, Ruleset | false> & {
|
|
290
292
|
inherit?: boolean;
|
|
291
293
|
};
|
|
292
|
-
repo?: GitHubRepoSettings;
|
|
294
|
+
repo?: GitHubRepoSettings | false;
|
|
293
295
|
deleteOrphaned?: boolean;
|
|
294
296
|
}
|
|
295
297
|
export interface RawRepoConfig {
|
|
@@ -20,6 +20,8 @@ const PROPERTY_MAPPING = {
|
|
|
20
20
|
squashMergeCommitMessage: "squash_merge_commit_message",
|
|
21
21
|
mergeCommitTitle: "merge_commit_title",
|
|
22
22
|
mergeCommitMessage: "merge_commit_message",
|
|
23
|
+
webCommitSignoffRequired: "web_commit_signoff_required",
|
|
24
|
+
defaultBranch: "default_branch",
|
|
23
25
|
vulnerabilityAlerts: "_vulnerability_alerts",
|
|
24
26
|
automatedSecurityFixes: "_automated_security_fixes",
|
|
25
27
|
secretScanning: "_secret_scanning",
|
|
@@ -29,6 +29,9 @@ function getWarning(change) {
|
|
|
29
29
|
change.newValue === false) {
|
|
30
30
|
return `disabling ${change.property} may hide existing content`;
|
|
31
31
|
}
|
|
32
|
+
if (change.property === "defaultBranch") {
|
|
33
|
+
return `changing default branch may affect existing PRs, CI workflows, and branch protections`;
|
|
34
|
+
}
|
|
32
35
|
return undefined;
|
|
33
36
|
}
|
|
34
37
|
/**
|
|
@@ -26,6 +26,8 @@ export interface CurrentRepoSettings {
|
|
|
26
26
|
squash_merge_commit_message?: string;
|
|
27
27
|
merge_commit_title?: string;
|
|
28
28
|
merge_commit_message?: string;
|
|
29
|
+
web_commit_signoff_required?: boolean;
|
|
30
|
+
default_branch?: string;
|
|
29
31
|
security_and_analysis?: {
|
|
30
32
|
secret_scanning?: {
|
|
31
33
|
status: string;
|