@vivswan/github-settings-as-code 2.0.1-main.1072.gfded5f5 → 2.0.1-main.1074.gc449c5c
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.
Potentially problematic release.
This version of @vivswan/github-settings-as-code might be problematic. Click here for more details.
- package/lib/pkg/cli.js +92 -25
- package/package.json +1 -1
package/lib/pkg/cli.js
CHANGED
|
@@ -285,32 +285,98 @@ function inputsForMode(mode) {
|
|
|
285
285
|
};
|
|
286
286
|
return INPUT_NAMES.filter((name) => !hidden.includes(name) && reads(name));
|
|
287
287
|
}
|
|
288
|
+
/** A mode's subcommand: its flags are the inputs the mode reads. */
|
|
289
|
+
function modeSubcommand(mode) {
|
|
290
|
+
return {
|
|
291
|
+
flags: new Set(inputsForMode(mode)),
|
|
292
|
+
mode
|
|
293
|
+
};
|
|
294
|
+
}
|
|
288
295
|
/**
|
|
289
|
-
* The init subcommand
|
|
290
|
-
* settings-file as the destination in place of snapshot-file.
|
|
291
|
-
*
|
|
296
|
+
* The init subcommand: the snapshot inputs of one repository, with
|
|
297
|
+
* settings-file as the destination in place of snapshot-file. No mode runs
|
|
298
|
+
* it, so the clauses restricted to modes leave its help.
|
|
292
299
|
*/
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
300
|
+
const INIT_SUBCOMMAND = {
|
|
301
|
+
flags: /* @__PURE__ */ new Set([
|
|
302
|
+
"repository",
|
|
303
|
+
"settings-file",
|
|
304
|
+
"on-missing-permission",
|
|
305
|
+
"sections",
|
|
306
|
+
"api-version"
|
|
307
|
+
]),
|
|
308
|
+
mode: null
|
|
309
|
+
};
|
|
310
|
+
/** init's flags in declaration order, the order the help keeps. */
|
|
311
|
+
const INIT_INPUTS = INPUT_NAMES.filter((name) => INIT_SUBCOMMAND.flags.has(name));
|
|
301
312
|
/**
|
|
302
313
|
* init's one reworded flag: the declaration describes the file apply and check
|
|
303
314
|
* READ, and init WRITES it; every other init flag keeps its declared text.
|
|
304
315
|
*/
|
|
305
316
|
const INIT_SETTINGS_FILE_DESCRIPTION = "Where the settings document is written: the file apply and check read. One path; an existing file is kept unless --force is passed.";
|
|
317
|
+
/** `text` as the declaration spells it; a reworded declaration fails here rather than leave the help stale. */
|
|
318
|
+
function declared(input, text) {
|
|
319
|
+
if (!INPUT_DECLS[input].description.includes(text)) throw new Error(`BUG: the ${input} input's description no longer says "${text}"; reword the CLI's clause with it`);
|
|
320
|
+
return text;
|
|
321
|
+
}
|
|
322
|
+
const MULTI_REPO_FLAGS = ["repos", "repos-dir"];
|
|
323
|
+
const CLAUSES = [
|
|
324
|
+
{
|
|
325
|
+
input: "repository",
|
|
326
|
+
text: declared("repository", " Single-repo mode only; cannot be combined with repos or repos-dir."),
|
|
327
|
+
flags: MULTI_REPO_FLAGS
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
input: "settings-file",
|
|
331
|
+
text: declared("settings-file", " Single-repo and merge modes only; multi-repo targets read repos-dir files or each repository's own .github/settings.yml, so overriding it alongside repos or repos-dir fails the run."),
|
|
332
|
+
flags: MULTI_REPO_FLAGS
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
input: "snapshot-dir",
|
|
336
|
+
text: declared("snapshot-dir", "; defaults-file does not apply"),
|
|
337
|
+
flags: ["defaults-file"]
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
input: "sections",
|
|
341
|
+
text: declared("sections", " apply, check, and snapshot only: mode: merge writes every section its layers declare, so the allowlist belongs on the step that runs the merged document and fails the merge when set."),
|
|
342
|
+
modes: [
|
|
343
|
+
"apply",
|
|
344
|
+
"check",
|
|
345
|
+
"snapshot"
|
|
346
|
+
]
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
input: "private-report",
|
|
350
|
+
text: declared("private-report", " Under artifact, those reports are concatenated, age-encrypted to report-public-key, and uploaded as one workflow artifact (settings-as-code-private-report) for readers who hold the key but no GitHub access to the targets; the artifact channel needs the Actions artifact service, so on GitHub Enterprise Server it warns and uploads nothing."),
|
|
351
|
+
flags: ["report-public-key"]
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
input: "private-report",
|
|
355
|
+
text: declared("private-report", "issue, issue-on-failure, or artifact."),
|
|
356
|
+
replacement: "issue, or issue-on-failure.",
|
|
357
|
+
flags: ["report-public-key"]
|
|
358
|
+
}
|
|
359
|
+
];
|
|
360
|
+
function meets(subcommand, clause) {
|
|
361
|
+
const flags = (clause.flags ?? []).every((flag) => subcommand.flags.has(flag));
|
|
362
|
+
const mode = clause.modes === void 0 || subcommand.mode !== null && clause.modes.includes(subcommand.mode);
|
|
363
|
+
return flags && mode;
|
|
364
|
+
}
|
|
306
365
|
/** The sentence the action's `repository` description spends on a default a terminal never has. */
|
|
307
|
-
const ACTIONS_DEFAULT_SENTENCE = "Defaults to the current repository.";
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
366
|
+
const ACTIONS_DEFAULT_SENTENCE = declared("repository", "Defaults to the current repository.");
|
|
367
|
+
/**
|
|
368
|
+
* A flag's help text under `subcommand`: the declaration's, minus the clauses
|
|
369
|
+
* about flags and modes the subcommand lacks, and reworded where it assumes
|
|
370
|
+
* the Actions runner.
|
|
371
|
+
*/
|
|
372
|
+
function inputDescription(name, subcommand) {
|
|
373
|
+
let description = INPUT_DECLS[name].description;
|
|
374
|
+
for (const clause of CLAUSES) if (clause.input === name && !meets(subcommand, clause)) description = description.replace(clause.text, clause.replacement ?? "");
|
|
375
|
+
if (name === "repository") {
|
|
376
|
+
const unless = MULTI_REPO_FLAGS.every((flag) => subcommand.flags.has(flag)) ? " unless repos or repos-dir is set" : "";
|
|
377
|
+
description = description.replace(ACTIONS_DEFAULT_SENTENCE, `Required${unless} (inside GitHub Actions, GITHUB_REPOSITORY supplies it).`);
|
|
378
|
+
}
|
|
379
|
+
return description;
|
|
314
380
|
}
|
|
315
381
|
/** Whether the declaration is a list; read through InputDecl since only the list members carry the field. */
|
|
316
382
|
function isList(name) {
|
|
@@ -328,16 +394,16 @@ function once(flag) {
|
|
|
328
394
|
};
|
|
329
395
|
}
|
|
330
396
|
/**
|
|
331
|
-
* The commander option for one input
|
|
332
|
-
* declaration is a list; `description` replaces the
|
|
333
|
-
* subcommand reads the input for another purpose.
|
|
397
|
+
* The commander option for one input under `subcommand`: `--<name> <value>`,
|
|
398
|
+
* repeatable when the declaration is a list; `description` replaces the
|
|
399
|
+
* declaration's where the subcommand reads the input for another purpose.
|
|
334
400
|
*/
|
|
335
|
-
function inputOption(name, description = inputDescription(name)) {
|
|
401
|
+
function inputOption(name, subcommand, description = inputDescription(name, subcommand)) {
|
|
336
402
|
const parse = isList(name) ? accumulate : once(name);
|
|
337
403
|
return new Option(`--${name} <value>`, description).argParser(parse);
|
|
338
404
|
}
|
|
339
405
|
/** Commander's attribute for each flag (camelCase of the name), read from commander itself. */
|
|
340
|
-
const ATTRIBUTE = Object.fromEntries(INPUT_NAMES.map((name) => [name,
|
|
406
|
+
const ATTRIBUTE = Object.fromEntries(INPUT_NAMES.map((name) => [name, new Option(`--${name} <value>`).attributeName()]));
|
|
341
407
|
/** A flag value as the runner would hand it over: trimmed, as @actions/core trims every input. */
|
|
342
408
|
function inputValue(value) {
|
|
343
409
|
return typeof value === "string" ? value.trim() : "";
|
|
@@ -534,7 +600,8 @@ function buildProgram(options) {
|
|
|
534
600
|
};
|
|
535
601
|
for (const [name, mode] of Object.entries(MODE_COMMANDS)) {
|
|
536
602
|
const command = program.command(name).description(DESCRIPTION[name]);
|
|
537
|
-
|
|
603
|
+
const subcommand = modeSubcommand(mode);
|
|
604
|
+
for (const input of subcommand.flags) command.addOption(inputOption(input, subcommand));
|
|
538
605
|
command.action(async function() {
|
|
539
606
|
const values = this.optsWithGlobals();
|
|
540
607
|
const { io, flush } = openIo(values);
|
|
@@ -544,7 +611,7 @@ function buildProgram(options) {
|
|
|
544
611
|
});
|
|
545
612
|
}
|
|
546
613
|
const init = program.command("init").description(DESCRIPTION.init);
|
|
547
|
-
for (const input of INIT_INPUTS) init.addOption(input === "settings-file" ? inputOption(input, INIT_SETTINGS_FILE_DESCRIPTION) : inputOption(input));
|
|
614
|
+
for (const input of INIT_INPUTS) init.addOption(input === "settings-file" ? inputOption(input, INIT_SUBCOMMAND, INIT_SETTINGS_FILE_DESCRIPTION) : inputOption(input, INIT_SUBCOMMAND));
|
|
548
615
|
init.option("--force", "Replace the settings file when it already exists; without it, init refuses").action(async function() {
|
|
549
616
|
const values = this.optsWithGlobals();
|
|
550
617
|
const { io } = openIo(values);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vivswan/github-settings-as-code",
|
|
3
|
-
"version": "2.0.1-main.
|
|
3
|
+
"version": "2.0.1-main.1074.gc449c5c",
|
|
4
4
|
"description": "GitHub Action applying declarative repository settings: rulesets, labels, branch protection, and more.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"type": "module",
|