github-action-readme-generator 1.12.8 → 2.0.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/CHANGELOG.md +17 -0
- package/README.md +14 -10
- package/action.yml +3 -1
- package/dist/bin/index.js +759 -529
- package/dist/mjs/index.d.ts +2 -3
- package/dist/mjs/index.js +118 -22
- package/package.json +6 -5
package/dist/mjs/index.d.ts
CHANGED
package/dist/mjs/index.js
CHANGED
|
@@ -10,7 +10,6 @@ import * as feather from "feather-icons";
|
|
|
10
10
|
import { icons } from "feather-icons";
|
|
11
11
|
import chalkPkg from "chalk";
|
|
12
12
|
import { execFileSync, execSync } from "node:child_process";
|
|
13
|
-
import { EOL } from "node:os";
|
|
14
13
|
import * as markdown from "prettier/plugins/markdown";
|
|
15
14
|
import * as yaml from "prettier/plugins/yaml";
|
|
16
15
|
import { format } from "prettier/standalone";
|
|
@@ -717,14 +716,15 @@ function getCurrentVersionString(inputs) {
|
|
|
717
716
|
const actionDir = path$1.dirname(inputs.action.path);
|
|
718
717
|
log.debug(`version source: ${versionSource}`);
|
|
719
718
|
let detectedVersion;
|
|
720
|
-
if (versionSource === "explicit")
|
|
721
|
-
|
|
722
|
-
|
|
719
|
+
if (versionSource === "explicit") {
|
|
720
|
+
if (override && override.length > 0) {
|
|
721
|
+
detectedVersion = override;
|
|
722
|
+
log.debug(`using explicit version override: ${detectedVersion}`);
|
|
723
|
+
} else {
|
|
724
|
+
log.debug("explicit mode but no version_override set, falling back to 0.0.0");
|
|
725
|
+
detectedVersion = "0.0.0";
|
|
726
|
+
}
|
|
723
727
|
} else {
|
|
724
|
-
log.debug("explicit mode but no version_override set, falling back to 0.0.0");
|
|
725
|
-
detectedVersion = "0.0.0";
|
|
726
|
-
}
|
|
727
|
-
else {
|
|
728
728
|
switch (versionSource) {
|
|
729
729
|
case "git-branch":
|
|
730
730
|
detectedVersion = getVersionFromGitBranch(actionDir, log);
|
|
@@ -859,14 +859,51 @@ const startTokenFormat = "(^|[^`\\\\])<!--\\s+start\\s+%s\\s+-->";
|
|
|
859
859
|
* The format for the end token of a section.
|
|
860
860
|
*/
|
|
861
861
|
const endTokenFormat = "(^|[^`\\\\])<!--\\s+end\\s+%s\\s+-->";
|
|
862
|
+
/**
|
|
863
|
+
* Lays out section content the way it sits between its markers.
|
|
864
|
+
* @param {string} content - The trimmed section content.
|
|
865
|
+
* @param {boolean} addNewlines - Whether to pad the content with newlines.
|
|
866
|
+
* @returns {string} - The text that goes between the markers.
|
|
867
|
+
*/
|
|
868
|
+
function layoutSpan(content, addNewlines) {
|
|
869
|
+
return addNewlines ? `\n\n${content}\n` : content;
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* True when every line break in `text` is CRLF, and there is at least one.
|
|
873
|
+
*
|
|
874
|
+
* Only a document that is CRLF throughout is edited as CRLF. Removing the `\r`
|
|
875
|
+
* before each `\n` and adding it back is then an exact round trip, so the
|
|
876
|
+
* bytes outside the markers survive. A document that mixes the two is left as
|
|
877
|
+
* it is, since no single ending would reproduce it.
|
|
878
|
+
* @param {string} text - The document.
|
|
879
|
+
* @returns {boolean} - Whether the document uses CRLF line endings.
|
|
880
|
+
*/
|
|
881
|
+
function usesCrlf(text) {
|
|
882
|
+
return text.includes("\r\n") && !/(^|[^\r])\n/.test(text);
|
|
883
|
+
}
|
|
862
884
|
var ReadmeEditor = class {
|
|
863
885
|
log = new LogTask("ReadmeEditor");
|
|
864
886
|
/**
|
|
865
887
|
* The path to the README file.
|
|
866
888
|
*/
|
|
867
889
|
filePath;
|
|
890
|
+
/**
|
|
891
|
+
* The document with LF line endings, whatever the file uses. Every edit and
|
|
892
|
+
* every formatter pass works on LF; `dumpToFile` restores the file's own
|
|
893
|
+
* ending on the way out.
|
|
894
|
+
*/
|
|
868
895
|
fileContent;
|
|
869
896
|
/**
|
|
897
|
+
* Whether the file on disk is CRLF throughout — see `usesCrlf`.
|
|
898
|
+
*/
|
|
899
|
+
crlf = false;
|
|
900
|
+
/**
|
|
901
|
+
* The padded sections this editor has replaced, each against the content it
|
|
902
|
+
* wrote. `dumpToFile` formats these spans and nothing else — see
|
|
903
|
+
* `formatUpdatedSections`.
|
|
904
|
+
*/
|
|
905
|
+
updatedSections = /* @__PURE__ */ new Map();
|
|
906
|
+
/**
|
|
870
907
|
* Creates a new instance of `ReadmeEditor`.
|
|
871
908
|
* @param {string} filePath - The path to the README file.
|
|
872
909
|
*/
|
|
@@ -874,15 +911,17 @@ var ReadmeEditor = class {
|
|
|
874
911
|
this.filePath = filePath;
|
|
875
912
|
try {
|
|
876
913
|
fs.accessSync(filePath);
|
|
877
|
-
|
|
878
|
-
if (process.env.GITHUB_ACTIONS) core.setOutput("readme_before",
|
|
914
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
915
|
+
if (process.env.GITHUB_ACTIONS) core.setOutput("readme_before", raw);
|
|
916
|
+
this.crlf = usesCrlf(raw);
|
|
917
|
+
this.fileContent = this.crlf ? raw.replaceAll("\r\n", "\n") : raw;
|
|
879
918
|
} catch (error) {
|
|
880
919
|
this.log.fail(`Readme at '${filePath}' does not exist.`);
|
|
881
920
|
throw error;
|
|
882
921
|
}
|
|
883
922
|
}
|
|
884
923
|
/**
|
|
885
|
-
* Gets the current README content.
|
|
924
|
+
* Gets the current README content, with LF line endings.
|
|
886
925
|
* @returns {string} - The README file content.
|
|
887
926
|
*/
|
|
888
927
|
getReadmeContent() {
|
|
@@ -917,24 +956,66 @@ var ReadmeEditor = class {
|
|
|
917
956
|
*/
|
|
918
957
|
updateSection(name, providedContent, addNewlines = true) {
|
|
919
958
|
const log = new LogTask(name);
|
|
920
|
-
const content = (Array.isArray(providedContent) ? providedContent.join(
|
|
959
|
+
const content = (Array.isArray(providedContent) ? providedContent.join("\n") : providedContent ?? "").replaceAll("\r\n", "\n").trim();
|
|
921
960
|
log.info(`Looking for the ${name} token in ${this.filePath}`);
|
|
922
961
|
const [startIndex, stopIndex] = this.getTokenIndexes(name, log);
|
|
923
962
|
if (startIndex && stopIndex) {
|
|
924
963
|
const beforeContent = this.fileContent.slice(0, startIndex);
|
|
925
964
|
const afterContent = this.fileContent.slice(stopIndex);
|
|
926
|
-
this.fileContent =
|
|
965
|
+
this.fileContent = `${beforeContent}${layoutSpan(content, addNewlines)}${afterContent}`;
|
|
966
|
+
if (addNewlines) this.updatedSections.set(name, content);
|
|
927
967
|
}
|
|
928
968
|
}
|
|
929
969
|
/**
|
|
970
|
+
* Formats the span of one section in isolation and splices it back.
|
|
971
|
+
*
|
|
972
|
+
* The content is formatted on its own and reassembled with the same
|
|
973
|
+
* surrounding newlines `updateSection` wrote, so the markers and every byte
|
|
974
|
+
* outside them survive untouched.
|
|
975
|
+
*
|
|
976
|
+
* The span is formatted only while its markers still bound exactly the text
|
|
977
|
+
* `updateSection` wrote. The markers are paired again here, after every
|
|
978
|
+
* section has been written, and a marker another section wrote can win that
|
|
979
|
+
* pairing; the text between such a pair is not this tool's to format.
|
|
980
|
+
* @param {string} name - The name of the section.
|
|
981
|
+
* @param {string} content - The content `updateSection` wrote, padded.
|
|
982
|
+
*/
|
|
983
|
+
async formatSection(name, content) {
|
|
984
|
+
const [startIndex, stopIndex] = this.getTokenIndexes(name);
|
|
985
|
+
if (!startIndex || !stopIndex) return;
|
|
986
|
+
if (startIndex > stopIndex || this.fileContent.slice(startIndex, stopIndex) !== layoutSpan(content, true)) {
|
|
987
|
+
this.log.warn(`The '${name}' markers no longer bound the text written to them. Leaving the section unformatted`);
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
const formatted = content === "" ? "" : (await formatMarkdown(content)).trim();
|
|
991
|
+
const span = formatted === "" ? "\n" : layoutSpan(formatted, true);
|
|
992
|
+
this.fileContent = `${this.fileContent.slice(0, startIndex)}${span}${this.fileContent.slice(stopIndex)}`;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Formats every span this editor replaced, one span at a time.
|
|
996
|
+
*
|
|
997
|
+
* Each span is located again before it is formatted, because formatting the
|
|
998
|
+
* previous one moves the indexes of the spans after it. A formatted span no
|
|
999
|
+
* longer holds the text `updateSection` wrote, so it is forgotten once
|
|
1000
|
+
* formatted.
|
|
1001
|
+
* @returns {Promise<void>}
|
|
1002
|
+
*/
|
|
1003
|
+
async formatUpdatedSections() {
|
|
1004
|
+
for (const [name, content] of this.updatedSections) await this.formatSection(name, content);
|
|
1005
|
+
this.updatedSections.clear();
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
930
1008
|
* Dumps the modified content back to the README file.
|
|
931
|
-
* @param {boolean} [prettier=true] - Run the
|
|
932
|
-
* writing. Callers pass the resolved `pretty` input; it defaults to
|
|
933
|
-
* constructing a ReadmeEditor directly keeps the formatting
|
|
1009
|
+
* @param {boolean} [prettier=true] - Run the replaced spans through prettier
|
|
1010
|
+
* before writing. Callers pass the resolved `pretty` input; it defaults to
|
|
1011
|
+
* true so constructing a ReadmeEditor directly keeps the formatting
|
|
1012
|
+
* behaviour. Text outside the markers is never formatted, whatever this
|
|
1013
|
+
* flag says — see `docs/tool-contract.md`.
|
|
934
1014
|
* @returns {Promise<void>}
|
|
935
1015
|
*/
|
|
936
1016
|
async dumpToFile(prettier = true) {
|
|
937
|
-
|
|
1017
|
+
if (prettier) await this.formatUpdatedSections();
|
|
1018
|
+
const content = this.crlf ? this.fileContent.replaceAll("\n", "\r\n") : this.fileContent;
|
|
938
1019
|
if (process.env.GITHUB_ACTIONS) core.setOutput("readme_after", content);
|
|
939
1020
|
return fs.promises.writeFile(this.filePath, content, "utf8");
|
|
940
1021
|
}
|
|
@@ -1489,10 +1570,12 @@ function loadConfig(log, providedConfig, configFilePath) {
|
|
|
1489
1570
|
const config = providedConfig ?? new Provider();
|
|
1490
1571
|
if (process.env.GITHUB_ACTION === "true") log.info("Running in GitHub action");
|
|
1491
1572
|
config.argv(argvOptions);
|
|
1492
|
-
if (configFilePath)
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1573
|
+
if (configFilePath) {
|
|
1574
|
+
if (fs.existsSync(configFilePath)) {
|
|
1575
|
+
log.info(`Config file found: ${configFilePath}`);
|
|
1576
|
+
config.file(configFilePath);
|
|
1577
|
+
} else log.debug(`Config file not found: ${configFilePath}`);
|
|
1578
|
+
}
|
|
1496
1579
|
config.env({
|
|
1497
1580
|
lowerCase: true,
|
|
1498
1581
|
parseValues: true,
|
|
@@ -2229,6 +2312,13 @@ function updateTitle(sectionToken, inputs) {
|
|
|
2229
2312
|
}
|
|
2230
2313
|
//#endregion
|
|
2231
2314
|
//#region src/sections/update-usage.ts
|
|
2315
|
+
/**
|
|
2316
|
+
* Renders the `usage` section: a fenced workflow snippet naming every action input,
|
|
2317
|
+
* each preceded by its description and default as YAML comments.
|
|
2318
|
+
* @param {ReadmeSection} sectionToken - The README marker pair to write the section into.
|
|
2319
|
+
* @param {Inputs} inputs - The parsed action metadata and the README editor to write with.
|
|
2320
|
+
* @returns {Promise<Record<string, string>>} The rendered section, keyed by its section token.
|
|
2321
|
+
*/
|
|
2232
2322
|
async function updateUsage(sectionToken, inputs) {
|
|
2233
2323
|
const log = new LogTask(sectionToken);
|
|
2234
2324
|
log.start();
|
|
@@ -2238,6 +2328,8 @@ async function updateUsage(sectionToken, inputs) {
|
|
|
2238
2328
|
log.info(`Version string: ${versionString}`);
|
|
2239
2329
|
const actionReference = `${actionName}@${versionString}`;
|
|
2240
2330
|
const indent = " # ";
|
|
2331
|
+
const defaultLabel = "Default: ";
|
|
2332
|
+
const defaultHang = " ".repeat(9);
|
|
2241
2333
|
const content = [];
|
|
2242
2334
|
content.push("```yaml", `- uses: ${actionReference}`, " with:");
|
|
2243
2335
|
const inp = inputs.action.inputs;
|
|
@@ -2264,7 +2356,11 @@ async function updateUsage(sectionToken, inputs) {
|
|
|
2264
2356
|
if (input !== void 0) {
|
|
2265
2357
|
if (!firstInput) content.push("");
|
|
2266
2358
|
content.push(...descriptions[key]);
|
|
2267
|
-
if (input.default !== void 0)
|
|
2359
|
+
if (input.default !== void 0) {
|
|
2360
|
+
const [firstLine, ...rest] = `${input.default}`.split(/\r\n|\n|\r/);
|
|
2361
|
+
content.push(`${indent}${defaultLabel}${firstLine}`.trimEnd());
|
|
2362
|
+
for (const line of rest) content.push(`${indent}${defaultHang}${line}`.trimEnd());
|
|
2363
|
+
}
|
|
2268
2364
|
content.push(` ${key}: ''`);
|
|
2269
2365
|
firstInput = false;
|
|
2270
2366
|
}
|
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": "
|
|
4
|
+
"version": "2.0.0",
|
|
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",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"@semantic-release/github",
|
|
129
129
|
"@semantic-release/git"
|
|
130
130
|
],
|
|
131
|
-
"preset": "
|
|
131
|
+
"preset": "conventionalcommits"
|
|
132
132
|
},
|
|
133
133
|
"dependencies": {
|
|
134
134
|
"@actions/core": "^3.0.0",
|
|
@@ -149,13 +149,14 @@
|
|
|
149
149
|
"@commitlint/prompt": "^21.0.1",
|
|
150
150
|
"@semantic-release/changelog": "^7.0.0",
|
|
151
151
|
"@semantic-release/exec": "^7.1.0",
|
|
152
|
-
"@semantic-release/git": "^
|
|
152
|
+
"@semantic-release/git": "^11.0.1",
|
|
153
153
|
"@tsconfig/node24": "^24.0.4",
|
|
154
154
|
"@types/nconf": "^0.10.5",
|
|
155
155
|
"@types/node": "^26.1.1",
|
|
156
|
-
"@vitest/coverage-v8": "4.1.
|
|
156
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
157
157
|
"@voidzero-dev/vite-plus-core": "0.2.8",
|
|
158
158
|
"commitizen": "^4.3.1",
|
|
159
|
+
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
159
160
|
"conventional-commits": "^1.6.0",
|
|
160
161
|
"cz-conventional-changelog": "^3.3.0",
|
|
161
162
|
"dotenv": "^17.2.4",
|
|
@@ -168,7 +169,7 @@
|
|
|
168
169
|
"semantic-release": "^25.0.2",
|
|
169
170
|
"types-package-json": "^2.0.39",
|
|
170
171
|
"typescript": "7.0.2",
|
|
171
|
-
"vite-plus": "0.
|
|
172
|
+
"vite-plus": "0.3.1"
|
|
172
173
|
},
|
|
173
174
|
"engines": {
|
|
174
175
|
"node": ">=24.19.0 <30.0.0",
|