github-action-readme-generator 1.12.5 → 1.12.7
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/CHANGELOG.md +14 -0
- package/README.md +132 -38
- package/action.yml +15 -10
- package/dist/bin/index.js +22984 -127169
- package/dist/mjs/index.js +331 -25
- package/package.json +1 -1
package/dist/mjs/index.js
CHANGED
|
@@ -11,7 +11,9 @@ import { icons } from "feather-icons";
|
|
|
11
11
|
import chalkPkg from "chalk";
|
|
12
12
|
import { execFileSync, execSync } from "node:child_process";
|
|
13
13
|
import { EOL } from "node:os";
|
|
14
|
-
import
|
|
14
|
+
import * as markdown from "prettier/plugins/markdown";
|
|
15
|
+
import * as yaml from "prettier/plugins/yaml";
|
|
16
|
+
import { format } from "prettier/standalone";
|
|
15
17
|
import { SVG, registerWindow } from "@svgdotjs/svg.js";
|
|
16
18
|
import { createSVGWindow } from "svgdom";
|
|
17
19
|
//#region src/constants.ts
|
|
@@ -35,6 +37,29 @@ const README_SECTIONS = [
|
|
|
35
37
|
* Represents the file name for the configuration file.
|
|
36
38
|
*/
|
|
37
39
|
const configFileName = ".ghadocs.json";
|
|
40
|
+
let ConfigKeys = /* @__PURE__ */ function(ConfigKeys) {
|
|
41
|
+
ConfigKeys["Owner"] = "owner";
|
|
42
|
+
ConfigKeys["Repo"] = "repo";
|
|
43
|
+
ConfigKeys["TitlePrefix"] = "title_prefix";
|
|
44
|
+
ConfigKeys["Prettier"] = "prettier";
|
|
45
|
+
ConfigKeys["Save"] = "save";
|
|
46
|
+
ConfigKeys["pathsAction"] = "paths:action";
|
|
47
|
+
ConfigKeys["pathsReadme"] = "paths:readme";
|
|
48
|
+
ConfigKeys["BrandingSvgPath"] = "branding_svg_path";
|
|
49
|
+
ConfigKeys["BrandingAsTitlePrefix"] = "branding_as_title_prefix";
|
|
50
|
+
ConfigKeys["VersioningEnabled"] = "versioning:enabled";
|
|
51
|
+
ConfigKeys["VersioningOverride"] = "versioning:override";
|
|
52
|
+
ConfigKeys["VersioningPrefix"] = "versioning:prefix";
|
|
53
|
+
ConfigKeys["VersioningBranch"] = "versioning:branch";
|
|
54
|
+
ConfigKeys["VersioningSource"] = "versioning:source";
|
|
55
|
+
ConfigKeys["IncludeGithubVersionBadge"] = "versioning:badge";
|
|
56
|
+
ConfigKeys["DebugNconf"] = "debug:nconf";
|
|
57
|
+
ConfigKeys["DebugReadme"] = "debug:readme";
|
|
58
|
+
ConfigKeys["DebugConfig"] = "debug:config";
|
|
59
|
+
ConfigKeys["DebugAction"] = "debug:action";
|
|
60
|
+
ConfigKeys["DebugGithub"] = "debug:github";
|
|
61
|
+
return ConfigKeys;
|
|
62
|
+
}({});
|
|
38
63
|
/**
|
|
39
64
|
* Represents the default brand color.
|
|
40
65
|
*/
|
|
@@ -666,6 +691,21 @@ function getVersionFromPackageJson(actionDir, log) {
|
|
|
666
691
|
log.debug(`package.json not found at ${packageJsonPath}`);
|
|
667
692
|
}
|
|
668
693
|
}
|
|
694
|
+
/**
|
|
695
|
+
* Whether the generated README should be run through prettier before it is
|
|
696
|
+
* written.
|
|
697
|
+
*
|
|
698
|
+
* Unset means enabled, matching `action.yml`'s `pretty` default of `"true"`.
|
|
699
|
+
* The value arrives as a real boolean from `.ghadocs.json` and as a string from
|
|
700
|
+
* action inputs and CLI args, so both spellings are accepted; anything else
|
|
701
|
+
* (`false`, `"false"`, `"no"`, …) disables formatting.
|
|
702
|
+
* @param {Inputs} inputs - The resolved inputs to read the flag from.
|
|
703
|
+
* @returns {boolean} True when prettier formatting should run.
|
|
704
|
+
*/
|
|
705
|
+
function isPrettierEnabled(inputs) {
|
|
706
|
+
const prettier = inputs.config.get("prettier");
|
|
707
|
+
return prettier === void 0 || prettier === true || prettier === "true";
|
|
708
|
+
}
|
|
669
709
|
function getCurrentVersionString(inputs) {
|
|
670
710
|
let versionString = "";
|
|
671
711
|
const log = new LogTask("getCurrentVersionString");
|
|
@@ -740,16 +780,32 @@ function lastIndexOfRegex(str, providedRegex) {
|
|
|
740
780
|
}
|
|
741
781
|
//#endregion
|
|
742
782
|
//#region src/prettier.ts
|
|
783
|
+
const log$1 = new LogTask("prettier");
|
|
743
784
|
/**
|
|
744
|
-
*
|
|
785
|
+
* The languages a GitHub Action's README is known in advance to contain.
|
|
745
786
|
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
787
|
+
* `markdown` is the parser itself. `yaml` covers the workflow snippets that are
|
|
788
|
+
* what an action README is mostly made of — and the ```yaml usage block this
|
|
789
|
+
* tool generates.
|
|
749
790
|
*
|
|
750
|
-
*
|
|
791
|
+
* Nothing else is bundled. `embeddedLanguageFormatting: 'auto'` reformats fenced
|
|
792
|
+
* code blocks, and `standalone` silently leaves a fence alone when its plugin is
|
|
793
|
+
* absent, so this list is exactly the set of fences the tool reformats. That is
|
|
794
|
+
* the intended contract, not a gap: this tool exists for GitHub Actions, and a
|
|
795
|
+
* fence in some other language is prose the action's author wrote, which the
|
|
796
|
+
* tool has no business rewriting.
|
|
797
|
+
*
|
|
798
|
+
* The bar for adding one: a language that action READMEs are known in advance
|
|
799
|
+
* to contain, not one they could. Every plugin here is weight in a binary that
|
|
800
|
+
* ships to every consumer, spent to reformat code the action's author wrote.
|
|
801
|
+
*
|
|
802
|
+
* Extending this per-project — naming extra prettier plugins in configuration —
|
|
803
|
+
* is tracked separately; see the README.
|
|
804
|
+
*
|
|
805
|
+
* Exported so __tests__/prettier.test.ts derives the formatted-parser set from
|
|
806
|
+
* this array rather than restating dependency-owned parser metadata.
|
|
751
807
|
*/
|
|
752
|
-
const
|
|
808
|
+
const plugins = [markdown, yaml];
|
|
753
809
|
/**
|
|
754
810
|
* Formats a Markdown string using `prettier`.
|
|
755
811
|
* @param {string} value - The Markdown string to format.
|
|
@@ -761,6 +817,7 @@ async function formatMarkdown(value, filepath) {
|
|
|
761
817
|
semi: false,
|
|
762
818
|
parser: "markdown",
|
|
763
819
|
embeddedLanguageFormatting: "auto",
|
|
820
|
+
plugins,
|
|
764
821
|
...filepath ? { filepath } : {}
|
|
765
822
|
});
|
|
766
823
|
}
|
|
@@ -778,7 +835,8 @@ async function wrapDescription(value, content, prefix = " # ") {
|
|
|
778
835
|
formattedString = await format(value, {
|
|
779
836
|
semi: false,
|
|
780
837
|
parser: "markdown",
|
|
781
|
-
proseWrap: "always"
|
|
838
|
+
proseWrap: "always",
|
|
839
|
+
plugins
|
|
782
840
|
});
|
|
783
841
|
} catch (error) {
|
|
784
842
|
log$1.error(`${String(error)}`);
|
|
@@ -870,10 +928,13 @@ var ReadmeEditor = class {
|
|
|
870
928
|
}
|
|
871
929
|
/**
|
|
872
930
|
* Dumps the modified content back to the README file.
|
|
931
|
+
* @param {boolean} [prettier=true] - Run the result through prettier before
|
|
932
|
+
* writing. Callers pass the resolved `pretty` input; it defaults to true so
|
|
933
|
+
* constructing a ReadmeEditor directly keeps the formatting behaviour.
|
|
873
934
|
* @returns {Promise<void>}
|
|
874
935
|
*/
|
|
875
|
-
async dumpToFile() {
|
|
876
|
-
const content = await formatMarkdown(this.fileContent);
|
|
936
|
+
async dumpToFile(prettier = true) {
|
|
937
|
+
const content = prettier ? await formatMarkdown(this.fileContent) : this.fileContent;
|
|
877
938
|
if (process.env.GITHUB_ACTIONS) core.setOutput("readme_after", content);
|
|
878
939
|
return fs.promises.writeFile(this.filePath, content, "utf8");
|
|
879
940
|
}
|
|
@@ -1134,7 +1195,9 @@ const ConfigKeysInputsMap = {
|
|
|
1134
1195
|
owner: "owner",
|
|
1135
1196
|
repo: "repo",
|
|
1136
1197
|
title_prefix: "title_prefix",
|
|
1137
|
-
pretty: "prettier"
|
|
1198
|
+
pretty: "prettier",
|
|
1199
|
+
debug_config: "debug:config",
|
|
1200
|
+
debug_nconf: "debug:nconf"
|
|
1138
1201
|
};
|
|
1139
1202
|
function transformGitHubInputsToArgv(log, _config, obj) {
|
|
1140
1203
|
/** The obj.key is always in lowercase, but it checks for it without case sensitivity */
|
|
@@ -1198,6 +1261,224 @@ function collectAllDefaultValuesFromAction(log, providedMetaActionPath, provided
|
|
|
1198
1261
|
return {};
|
|
1199
1262
|
}
|
|
1200
1263
|
}
|
|
1264
|
+
/** Key fragments whose values are masked before the resolved config is printed. */
|
|
1265
|
+
const SENSITIVE_KEY_PATTERN = /auth|credential|key|passw|secret|token/i;
|
|
1266
|
+
/** Stand-in written in place of a masked value. */
|
|
1267
|
+
const REDACTED = "***REDACTED***";
|
|
1268
|
+
/**
|
|
1269
|
+
* `sections` is the one key holding an array, and its domain is the finite
|
|
1270
|
+
* {@link README_SECTIONS} set — `updateSection` ignores anything else. Naming
|
|
1271
|
+
* the leaf after that domain, rather than calling it a generic array, is what
|
|
1272
|
+
* lets an unrecognised entry be masked instead of printed.
|
|
1273
|
+
*/
|
|
1274
|
+
const SECTION_LIST_KEYS = /* @__PURE__ */ new Set(["sections"]);
|
|
1275
|
+
/**
|
|
1276
|
+
* Keys whose declared domain is a boolean. No code path reads the bytes of a
|
|
1277
|
+
* value under one of these: the readers either compare against boolean literals
|
|
1278
|
+
* (`save.ts` on `=== true`; `isPrettierEnabled`, `getCurrentVersionString` and
|
|
1279
|
+
* `isDebugConfigEnabled` on `=== true || === 'true'`) or gate on truthiness
|
|
1280
|
+
* (`update-title.ts`, `update-badges.ts`), which consumes the value's
|
|
1281
|
+
* truthiness and nothing else.
|
|
1282
|
+
*
|
|
1283
|
+
* The domain comes from the key's contract, not from how its reader happens to
|
|
1284
|
+
* test it — `action.yml` declares `branding_as_title_prefix` as
|
|
1285
|
+
* `type: boolean` and `include_github_version_badge` with a boolean default,
|
|
1286
|
+
* so a string under either is malformed input whose content is never read.
|
|
1287
|
+
*/
|
|
1288
|
+
const BOOLEAN_KEYS = /* @__PURE__ */ new Set([
|
|
1289
|
+
"save",
|
|
1290
|
+
"prettier",
|
|
1291
|
+
"versioning:enabled",
|
|
1292
|
+
"branding_as_title_prefix",
|
|
1293
|
+
"versioning:badge",
|
|
1294
|
+
"debug:config",
|
|
1295
|
+
"debug:nconf"
|
|
1296
|
+
]);
|
|
1297
|
+
/** The boolean forms nconf can hand back, parsed or still as a string. */
|
|
1298
|
+
const BOOLEAN_VALUES = /* @__PURE__ */ new Set([
|
|
1299
|
+
true,
|
|
1300
|
+
false,
|
|
1301
|
+
"true",
|
|
1302
|
+
"false"
|
|
1303
|
+
]);
|
|
1304
|
+
/**
|
|
1305
|
+
* `versioning:source` selects a detection strategy from a closed set;
|
|
1306
|
+
* `getCurrentVersionString` switches over these and treats anything else as
|
|
1307
|
+
* `git-tag`, so a value outside the set is never used for its content.
|
|
1308
|
+
*/
|
|
1309
|
+
const VERSION_SOURCE_KEYS = /* @__PURE__ */ new Set(["versioning:source"]);
|
|
1310
|
+
/** The strategies `getCurrentVersionString` switches over. */
|
|
1311
|
+
const VERSION_SOURCES = /* @__PURE__ */ new Set([
|
|
1312
|
+
"git-tag",
|
|
1313
|
+
"git-branch",
|
|
1314
|
+
"git-sha",
|
|
1315
|
+
"package-json",
|
|
1316
|
+
"explicit"
|
|
1317
|
+
]);
|
|
1318
|
+
/**
|
|
1319
|
+
* `image_generated` caches the branding the SVG on disk was drawn from.
|
|
1320
|
+
*
|
|
1321
|
+
* It is the one key the tool writes and reads back that `ConfigKeys` does not
|
|
1322
|
+
* declare: `generateImgMarkup` sets it to `${icon}${color}` and, on a later
|
|
1323
|
+
* run, regenerates the SVG unless the saved value equals that same string.
|
|
1324
|
+
*/
|
|
1325
|
+
const BRANDING_HASH_KEYS = /* @__PURE__ */ new Set(["image_generated"]);
|
|
1326
|
+
/**
|
|
1327
|
+
* Whether a value is a branding hash `generateImgMarkup`'s comparison can match.
|
|
1328
|
+
*
|
|
1329
|
+
* The icon and colour it concatenates come from closed sets, so the producible
|
|
1330
|
+
* hashes are a finite domain — the same reason `sections` and `versioning:source`
|
|
1331
|
+
* are validated against their contents rather than their shape. The two sets are
|
|
1332
|
+
* joined without a separator, so the colour is recovered by suffix.
|
|
1333
|
+
* @param {unknown} value - The value found under a branding-hash key.
|
|
1334
|
+
* @returns {boolean} True when the value is an icon name followed by a colour.
|
|
1335
|
+
*/
|
|
1336
|
+
function isBrandingHash(value) {
|
|
1337
|
+
if (typeof value !== "string") return false;
|
|
1338
|
+
return GITHUB_ACTIONS_BRANDING_COLORS.some((color) => value.endsWith(color) && GITHUB_ACTIONS_BRANDING_ICONS.has(value.slice(0, -color.length)));
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Splits colon-separated config paths into the nested shape nconf resolves them
|
|
1342
|
+
* to, so `versioning:prefix` becomes `{ versioning: { prefix: 'scalar' } }`.
|
|
1343
|
+
* @param {readonly string[]} paths - Colon-separated key paths.
|
|
1344
|
+
* @returns {KnownKeyTree} The paths as a nested tree of declared shapes.
|
|
1345
|
+
*/
|
|
1346
|
+
function buildKnownKeyTree(paths) {
|
|
1347
|
+
const tree = {};
|
|
1348
|
+
for (const path of paths) {
|
|
1349
|
+
const segments = path.split(":");
|
|
1350
|
+
let node = tree;
|
|
1351
|
+
for (const [index, segment] of segments.entries()) {
|
|
1352
|
+
if (index === segments.length - 1) {
|
|
1353
|
+
if (SECTION_LIST_KEYS.has(path)) node[segment] = "section-names";
|
|
1354
|
+
else if (BOOLEAN_KEYS.has(path)) node[segment] = "boolean";
|
|
1355
|
+
else if (VERSION_SOURCE_KEYS.has(path)) node[segment] = "version-source";
|
|
1356
|
+
else if (BRANDING_HASH_KEYS.has(path)) node[segment] = "branding-hash";
|
|
1357
|
+
else node[segment] = "scalar";
|
|
1358
|
+
break;
|
|
1359
|
+
}
|
|
1360
|
+
const next = node[segment];
|
|
1361
|
+
node = next === void 0 || typeof next === "string" ? node[segment] = {} : next;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
return tree;
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Canonical keys {@link ConfigKeys} declares that nothing in `src` reads, and
|
|
1368
|
+
* that no `action.yml` input or CLI flag can set. A value can only reach one of
|
|
1369
|
+
* them through `.ghadocs.json`, and no code path consumes it once there — so it
|
|
1370
|
+
* is a caller's value, not the tool's, and it is masked like any other unknown
|
|
1371
|
+
* key. Drop an entry from here once a reader for it lands.
|
|
1372
|
+
*/
|
|
1373
|
+
const UNREAD_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
1374
|
+
"debug:readme",
|
|
1375
|
+
"debug:action",
|
|
1376
|
+
"debug:github"
|
|
1377
|
+
]);
|
|
1378
|
+
/**
|
|
1379
|
+
* Every key this tool resolves a value out of: the canonical config keys it
|
|
1380
|
+
* actually reads, plus `sections`, which `Inputs` sets and reads directly.
|
|
1381
|
+
*
|
|
1382
|
+
* The action-input and CLI spellings in {@link ConfigKeysInputsMap} are
|
|
1383
|
+
* deliberately absent. Those are input spellings, not resolved keys — the file
|
|
1384
|
+
* store performs no mapping, so `{"action": "…"}` in `.ghadocs.json` leaves a
|
|
1385
|
+
* bare `action` key that nothing ever reads, and listing it here would have
|
|
1386
|
+
* printed that value. Nothing is lost by masking them: when a value does arrive
|
|
1387
|
+
* through an alias, nconf's argv store records the canonical key too, so the
|
|
1388
|
+
* dump still shows it under `paths:action` / `debug:config`.
|
|
1389
|
+
*/
|
|
1390
|
+
const KNOWN_KEY_TREE = buildKnownKeyTree([
|
|
1391
|
+
...Object.values(ConfigKeys).filter((key) => !UNREAD_CONFIG_KEYS.has(key)),
|
|
1392
|
+
"sections",
|
|
1393
|
+
...BRANDING_HASH_KEYS
|
|
1394
|
+
]);
|
|
1395
|
+
/**
|
|
1396
|
+
* Whether a value is shaped like something a known leaf key can hold: a scalar,
|
|
1397
|
+
* or an array of scalars.
|
|
1398
|
+
*
|
|
1399
|
+
* Every key in {@link KNOWN_KEY_TREE} holds a scalar except `sections`, which
|
|
1400
|
+
* holds a string array. Anything richer arriving under one of those names came
|
|
1401
|
+
* from a caller, not from this tool, and nothing here reads it.
|
|
1402
|
+
* @param {unknown} value - The value found under a known leaf key.
|
|
1403
|
+
* @returns {boolean} True when the value is a scalar or an array of scalars.
|
|
1404
|
+
*/
|
|
1405
|
+
function isScalarLeafValue(value) {
|
|
1406
|
+
return !Array.isArray(value) && (value === null || typeof value !== "object");
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Whether a value is an array holding only scalars — the shape `sections` has.
|
|
1410
|
+
* @param {unknown} value - The value found under an array-valued key.
|
|
1411
|
+
* @returns {boolean} True for an array with no object or array elements.
|
|
1412
|
+
*/
|
|
1413
|
+
function isSectionNameList(value) {
|
|
1414
|
+
const names = README_SECTIONS;
|
|
1415
|
+
return Array.isArray(value) && value.every((entry) => names.includes(entry));
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Masks every value the tool does not itself read, plus anything under a
|
|
1419
|
+
* sensitive-looking key, before the resolved config is printed.
|
|
1420
|
+
*
|
|
1421
|
+
* `action.yml` declares no secret input, and the env store admits only
|
|
1422
|
+
* `INPUT_*` keys, so ambient variables such as `GITHUB_TOKEN` never reach the
|
|
1423
|
+
* config. A caller can still land one: GitHub sets `INPUT_*` for every key
|
|
1424
|
+
* under `with:`, declared or not, and `.ghadocs.json` and CLI args are
|
|
1425
|
+
* user-controlled. The dump exists to be pasted into bug reports, so it masks
|
|
1426
|
+
* rather than relying on the runner's own secret masking.
|
|
1427
|
+
*
|
|
1428
|
+
* Because those three sources admit arbitrary keys, a name heuristic alone
|
|
1429
|
+
* cannot make the dump safe — `--webhook=https://hooks.example/...` carries a
|
|
1430
|
+
* credential under a name no pattern would flag. So the allow-list is the
|
|
1431
|
+
* primary defence: a key the tool reads is printed, anything else is masked.
|
|
1432
|
+
* That still shows a reporter which unexpected keys were supplied, without
|
|
1433
|
+
* printing what they held. `SENSITIVE_KEY_PATTERN` stays as a second line,
|
|
1434
|
+
* covering a future known key whose value is genuinely secret.
|
|
1435
|
+
*
|
|
1436
|
+
* Values under nconf and yargs bookkeeping keys (`_`, `$0`) are masked by the
|
|
1437
|
+
* same rule; they are not configuration and nothing reads them.
|
|
1438
|
+
*
|
|
1439
|
+
* Recurses so nested groups (`versioning:*`, `paths:*`) are covered. Arrays are
|
|
1440
|
+
* walked; a masked key's value is replaced whole rather than descended into.
|
|
1441
|
+
* @param {unknown} value - The resolved config, or a nested part of it.
|
|
1442
|
+
* @param {KnownKeyTree | true} known - Allow-list for this level; `true` means
|
|
1443
|
+
* every key below is already within a known leaf's value.
|
|
1444
|
+
* @returns {unknown} A copy with unknown and sensitive values masked.
|
|
1445
|
+
*/
|
|
1446
|
+
function redactSensitiveValues(value, known = KNOWN_KEY_TREE) {
|
|
1447
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return value;
|
|
1448
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
1449
|
+
if (SENSITIVE_KEY_PATTERN.test(key)) return [key, REDACTED];
|
|
1450
|
+
const branch = known[key];
|
|
1451
|
+
switch (branch) {
|
|
1452
|
+
case void 0: return [key, REDACTED];
|
|
1453
|
+
case "scalar": return [key, isScalarLeafValue(entry) ? entry : REDACTED];
|
|
1454
|
+
case "section-names": return [key, isSectionNameList(entry) ? entry : REDACTED];
|
|
1455
|
+
case "boolean": return [key, BOOLEAN_VALUES.has(entry) ? entry : REDACTED];
|
|
1456
|
+
case "version-source": return [key, VERSION_SOURCES.has(entry) ? entry : REDACTED];
|
|
1457
|
+
case "branding-hash": return [key, isBrandingHash(entry) ? entry : REDACTED];
|
|
1458
|
+
default: return [key, entry !== null && typeof entry === "object" && !Array.isArray(entry) ? redactSensitiveValues(entry, branch) : REDACTED];
|
|
1459
|
+
}
|
|
1460
|
+
}));
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Whether the resolved nconf object was asked for.
|
|
1464
|
+
*
|
|
1465
|
+
* Two flags carry the identical promise "Print out the resolved nconf object
|
|
1466
|
+
* with all values" — `--debug_config` and the older `--debug_nconf` — so either
|
|
1467
|
+
* triggers the dump rather than one of them continuing to be inert.
|
|
1468
|
+
*
|
|
1469
|
+
* Unset means off, the opposite of `pretty`: this is a diagnostic, and neither
|
|
1470
|
+
* flag is declared in `action.yml`. The value arrives as a real boolean from
|
|
1471
|
+
* `.ghadocs.json` and as a string from env and CLI args, so both spellings are
|
|
1472
|
+
* accepted.
|
|
1473
|
+
* @param {ProviderInstance} config - The resolved config instance.
|
|
1474
|
+
* @returns {boolean} True when the resolved config should be printed.
|
|
1475
|
+
*/
|
|
1476
|
+
function isDebugConfigEnabled(config) {
|
|
1477
|
+
return ["debug:config", "debug:nconf"].some((key) => {
|
|
1478
|
+
const value = config.get(key);
|
|
1479
|
+
return value === true || value === "true";
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1201
1482
|
/**
|
|
1202
1483
|
* Loads the configuration
|
|
1203
1484
|
*
|
|
@@ -1207,6 +1488,7 @@ function loadConfig(log, providedConfig, configFilePath) {
|
|
|
1207
1488
|
log.debug("Loading config from env and argv");
|
|
1208
1489
|
const config = providedConfig ?? new Provider();
|
|
1209
1490
|
if (process.env.GITHUB_ACTION === "true") log.info("Running in GitHub action");
|
|
1491
|
+
config.argv(argvOptions);
|
|
1210
1492
|
if (configFilePath) if (fs.existsSync(configFilePath)) {
|
|
1211
1493
|
log.info(`Config file found: ${configFilePath}`);
|
|
1212
1494
|
config.file(configFilePath);
|
|
@@ -1217,7 +1499,7 @@ function loadConfig(log, providedConfig, configFilePath) {
|
|
|
1217
1499
|
transform: (obj) => {
|
|
1218
1500
|
return transformGitHubInputsToArgv(log, config, obj);
|
|
1219
1501
|
}
|
|
1220
|
-
})
|
|
1502
|
+
});
|
|
1221
1503
|
return config;
|
|
1222
1504
|
}
|
|
1223
1505
|
/**
|
|
@@ -1321,7 +1603,13 @@ var Inputs = class {
|
|
|
1321
1603
|
this.configPath = inputContext.configPath ?? path$1.resolve(".ghadocs.json");
|
|
1322
1604
|
this.config = inputContext.config ?? new Provider();
|
|
1323
1605
|
loadConfig(log, this.config, this.configPath);
|
|
1324
|
-
|
|
1606
|
+
try {
|
|
1607
|
+
loadDefaultConfig(log, this.config);
|
|
1608
|
+
} catch (error) {
|
|
1609
|
+
this.dumpResolvedConfig();
|
|
1610
|
+
throw error;
|
|
1611
|
+
}
|
|
1612
|
+
this.dumpResolvedConfig();
|
|
1325
1613
|
loadRequiredConfig(log, this.config);
|
|
1326
1614
|
this.action = inputContext.action ?? loadAction(log, this.config.get("paths:action"));
|
|
1327
1615
|
this.config.set("sections", inputContext.sections ?? this.config.get("sections"));
|
|
@@ -1341,9 +1629,23 @@ var Inputs = class {
|
|
|
1341
1629
|
*/
|
|
1342
1630
|
this.repo = inputContext.repo ?? this.config.get("repo");
|
|
1343
1631
|
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Prints the resolved configuration when `--debug_config` (or the older
|
|
1634
|
+
* `--debug_nconf`) asked for it.
|
|
1635
|
+
*
|
|
1636
|
+
* Called from two places in the constructor because both of the failures this
|
|
1637
|
+
* flag diagnoses throw, and each throws from a different call — see the
|
|
1638
|
+
* comments at those call sites. Rerunning it after a successful
|
|
1639
|
+
* `loadDefaultConfig` is the only path that reaches the second call, so no
|
|
1640
|
+
* run prints the dump twice.
|
|
1641
|
+
* @returns {void}
|
|
1642
|
+
*/
|
|
1643
|
+
dumpResolvedConfig() {
|
|
1644
|
+
if (isDebugConfigEnabled(this.config)) this.log.info(`Resolved config:\n${this.stringify()}`);
|
|
1645
|
+
}
|
|
1344
1646
|
stringify() {
|
|
1345
1647
|
if (this?.config) try {
|
|
1346
|
-
return YAML.stringify(this.config.get());
|
|
1648
|
+
return YAML.stringify(redactSensitiveValues(this.config.get()));
|
|
1347
1649
|
} catch (error) {
|
|
1348
1650
|
this.log.error(`${String(error)}`);
|
|
1349
1651
|
}
|
|
@@ -1724,7 +2026,7 @@ function updateDescription(sectionToken, inputs) {
|
|
|
1724
2026
|
* @returns The escaped text.
|
|
1725
2027
|
*/
|
|
1726
2028
|
function markdownEscapeTableCell(text) {
|
|
1727
|
-
return text.replaceAll("\n", "<br />").replaceAll("|", "\\|");
|
|
2029
|
+
return text.replaceAll("\n", "<br />").replaceAll(/\\+(?=\|)/g, (slashes) => slashes.repeat(2)).replaceAll("|", "\\|");
|
|
1728
2030
|
}
|
|
1729
2031
|
/**
|
|
1730
2032
|
* Escapes inline code blocks in a Markdown string.
|
|
@@ -1831,13 +2133,12 @@ function updateInputs(sectionToken, inputs) {
|
|
|
1831
2133
|
for (const key of Object.keys(vars)) {
|
|
1832
2134
|
const values = vars[key];
|
|
1833
2135
|
let description = values?.description ?? "";
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
description = description.trim().replace("\n", "<br />");
|
|
2136
|
+
description = description.trim().split("\n\n")[0] ?? "";
|
|
2137
|
+
description = description.replaceAll("\n", "<br />");
|
|
1837
2138
|
const row = [
|
|
1838
2139
|
rowHeader(key),
|
|
1839
2140
|
description,
|
|
1840
|
-
values?.default ? `<code>${values.default}</code
|
|
2141
|
+
values?.default === void 0 || values.default === "" ? "" : `<code>${values.default}</code>`,
|
|
1841
2142
|
values?.required ? "**true**" : "__false__"
|
|
1842
2143
|
];
|
|
1843
2144
|
log.debug(JSON.stringify(row));
|
|
@@ -1847,7 +2148,10 @@ function updateInputs(sectionToken, inputs) {
|
|
|
1847
2148
|
log.info(`Action has ${tI} total ${sectionToken}`);
|
|
1848
2149
|
inputs.readmeEditor.updateSection(sectionToken, content);
|
|
1849
2150
|
log.success();
|
|
1850
|
-
} else
|
|
2151
|
+
} else {
|
|
2152
|
+
log.debug(`Action has no ${sectionToken}`);
|
|
2153
|
+
inputs.readmeEditor.updateSection(sectionToken, content);
|
|
2154
|
+
}
|
|
1851
2155
|
const ret = {};
|
|
1852
2156
|
ret[sectionToken] = content.join("\n");
|
|
1853
2157
|
return ret;
|
|
@@ -1873,9 +2177,8 @@ function updateOutputs(sectionToken, inputs) {
|
|
|
1873
2177
|
for (const key of Object.keys(vars)) {
|
|
1874
2178
|
const values = vars[key];
|
|
1875
2179
|
let description = values?.description ?? "";
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
description = description.trim().replace("\n", "<br />");
|
|
2180
|
+
description = description.trim().split("\n\n")[0] ?? "";
|
|
2181
|
+
description = description.replaceAll("\n", "<br />");
|
|
1879
2182
|
const value = values?.value ? `\`${values.value}\`` : "";
|
|
1880
2183
|
const row = [
|
|
1881
2184
|
rowHeader(key),
|
|
@@ -1889,7 +2192,10 @@ function updateOutputs(sectionToken, inputs) {
|
|
|
1889
2192
|
log.info(`Action has ${tI} total ${sectionToken}`);
|
|
1890
2193
|
inputs.readmeEditor.updateSection(sectionToken, content);
|
|
1891
2194
|
log.success();
|
|
1892
|
-
} else
|
|
2195
|
+
} else {
|
|
2196
|
+
log.debug(`Action has no ${sectionToken}`);
|
|
2197
|
+
inputs.readmeEditor.updateSection(sectionToken, content);
|
|
2198
|
+
}
|
|
1893
2199
|
const ret = {};
|
|
1894
2200
|
ret[sectionToken] = content.join("\n");
|
|
1895
2201
|
return ret;
|
|
@@ -2061,7 +2367,7 @@ var ReadmeGenerator = class {
|
|
|
2061
2367
|
const sectionPromises = this.updateSections(providedSections);
|
|
2062
2368
|
const sections = await this.resolveUpdates(sectionPromises);
|
|
2063
2369
|
this.outputSections(sections);
|
|
2064
|
-
return this.inputs.readmeEditor.dumpToFile();
|
|
2370
|
+
return this.inputs.readmeEditor.dumpToFile(isPrettierEnabled(this.inputs));
|
|
2065
2371
|
}
|
|
2066
2372
|
};
|
|
2067
2373
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "github-action-readme-generator",
|
|
3
3
|
"displayName": "bitflight-devops/github-action-readme-generator",
|
|
4
|
-
"version": "1.12.
|
|
4
|
+
"version": "1.12.7",
|
|
5
5
|
"description": "The docs generator for GitHub Actions. Auto-syncs action.yml to README.md with 8 sections: inputs, outputs, usage, badges, branding & more. Works as CLI or GitHub Action.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"github-actions",
|