@hugojosefson/cli 0.11.0 → 0.12.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/esm/deno.js +16 -8
- package/esm/node_modules/@conventional-changelog/template/LICENSE.md +21 -0
- package/esm/node_modules/@conventional-changelog/template/README.md +66 -0
- package/esm/node_modules/@conventional-changelog/template/dist/elements.d.ts +78 -0
- package/esm/node_modules/@conventional-changelog/template/dist/elements.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/elements.js +132 -0
- package/esm/node_modules/@conventional-changelog/template/dist/index.d.ts +6 -0
- package/esm/node_modules/@conventional-changelog/template/dist/index.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/index.js +5 -0
- package/esm/node_modules/@conventional-changelog/template/dist/legacy.d.ts +20 -0
- package/esm/node_modules/@conventional-changelog/template/dist/legacy.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/legacy.js +22 -0
- package/esm/node_modules/@conventional-changelog/template/dist/references.d.ts +43 -0
- package/esm/node_modules/@conventional-changelog/template/dist/references.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/references.js +82 -0
- package/esm/node_modules/@conventional-changelog/template/dist/templates.d.ts +74 -0
- package/esm/node_modules/@conventional-changelog/template/dist/templates.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/templates.js +125 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/commit.d.ts +37 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/commit.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/commit.js +2 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/context.d.ts +113 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/context.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/context.js +2 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/index.d.ts +4 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/index.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/index.js +2 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/utils.d.ts +2 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/utils.d.ts.map +1 -0
- package/esm/node_modules/@conventional-changelog/template/dist/types/utils.js +2 -0
- package/esm/node_modules/@conventional-changelog/template/package.json +38 -0
- package/esm/node_modules/@simple-libs/stream-utils/LICENSE +21 -0
- package/esm/node_modules/@simple-libs/stream-utils/README.md +79 -0
- package/esm/node_modules/@simple-libs/stream-utils/dist/index.d.ts +51 -0
- package/esm/node_modules/@simple-libs/stream-utils/dist/index.d.ts.map +1 -0
- package/esm/node_modules/@simple-libs/stream-utils/dist/index.js +116 -0
- package/esm/node_modules/@simple-libs/stream-utils/package.json +38 -0
- package/esm/node_modules/argue-cli/LICENSE +21 -0
- package/esm/node_modules/argue-cli/README.md +239 -0
- package/esm/node_modules/argue-cli/dist/args.d.ts +30 -0
- package/esm/node_modules/argue-cli/dist/args.d.ts.map +1 -0
- package/esm/node_modules/argue-cli/dist/argv.d.ts +14 -0
- package/esm/node_modules/argue-cli/dist/argv.d.ts.map +1 -0
- package/esm/node_modules/argue-cli/dist/core.d.ts +24 -0
- package/esm/node_modules/argue-cli/dist/core.d.ts.map +1 -0
- package/esm/node_modules/argue-cli/dist/index.d.ts +6 -0
- package/esm/node_modules/argue-cli/dist/index.d.ts.map +1 -0
- package/esm/node_modules/argue-cli/dist/index.js +254 -0
- package/esm/node_modules/argue-cli/dist/index.js.map +1 -0
- package/esm/node_modules/argue-cli/dist/options.d.ts +9 -0
- package/esm/node_modules/argue-cli/dist/options.d.ts.map +1 -0
- package/esm/node_modules/argue-cli/dist/types.d.ts +16 -0
- package/esm/node_modules/argue-cli/dist/types.d.ts.map +1 -0
- package/esm/node_modules/argue-cli/dist/utils.d.ts +24 -0
- package/esm/node_modules/argue-cli/dist/utils.d.ts.map +1 -0
- package/esm/node_modules/argue-cli/package.json +34 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-changelog-writer/LICENSE.md +21 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-changelog-writer/README.md +369 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-changelog-writer/package.json +53 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-commits-filter/LICENSE +21 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-commits-filter/README.md +184 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-commits-filter/package.json +31 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-commits-parser/LICENSE.md +21 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-commits-parser/README.md +383 -0
- package/esm/node_modules/conventional-changelog-core/node_modules/conventional-commits-parser/package.json +47 -0
- package/esm/node_modules/conventional-changelog-writer/README.md +49 -342
- package/esm/node_modules/conventional-changelog-writer/dist/cli/index.d.ts +3 -0
- package/esm/node_modules/conventional-changelog-writer/dist/cli/index.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/cli/index.js +68 -0
- package/esm/node_modules/conventional-changelog-writer/dist/cli/utils.d.ts +4 -0
- package/esm/node_modules/conventional-changelog-writer/dist/cli/utils.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/cli/utils.js +39 -0
- package/esm/node_modules/conventional-changelog-writer/dist/commit.d.ts +10 -0
- package/esm/node_modules/conventional-changelog-writer/dist/commit.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/commit.js +43 -0
- package/esm/node_modules/conventional-changelog-writer/dist/context.d.ts +27 -0
- package/esm/node_modules/conventional-changelog-writer/dist/context.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/context.js +134 -0
- package/esm/node_modules/conventional-changelog-writer/dist/index.d.ts +7 -0
- package/esm/node_modules/conventional-changelog-writer/dist/index.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/index.js +6 -0
- package/esm/node_modules/conventional-changelog-writer/dist/options.d.ts +25 -0
- package/esm/node_modules/conventional-changelog-writer/dist/options.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/options.js +79 -0
- package/esm/node_modules/conventional-changelog-writer/dist/template.d.ts +10 -0
- package/esm/node_modules/conventional-changelog-writer/dist/template.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/template.js +18 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/index.d.ts +8 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/index.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/index.js +2 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/options.d.ts +122 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/options.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/options.js +2 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/utils.d.ts +9 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/utils.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/types/utils.js +2 -0
- package/esm/node_modules/conventional-changelog-writer/dist/utils.d.ts +20 -0
- package/esm/node_modules/conventional-changelog-writer/dist/utils.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/utils.js +70 -0
- package/esm/node_modules/conventional-changelog-writer/dist/writers.d.ts +29 -0
- package/esm/node_modules/conventional-changelog-writer/dist/writers.d.ts.map +1 -0
- package/esm/node_modules/conventional-changelog-writer/dist/writers.js +94 -0
- package/esm/node_modules/conventional-changelog-writer/package.json +24 -26
- package/esm/node_modules/conventional-commits-filter/README.md +51 -170
- package/esm/node_modules/conventional-commits-filter/dist/RevertedCommitsFilter.d.ts +17 -0
- package/esm/node_modules/conventional-commits-filter/dist/RevertedCommitsFilter.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-filter/dist/RevertedCommitsFilter.js +46 -0
- package/esm/node_modules/conventional-commits-filter/dist/filters.d.ts +19 -0
- package/esm/node_modules/conventional-commits-filter/dist/filters.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-filter/dist/filters.js +34 -0
- package/esm/node_modules/conventional-commits-filter/dist/index.d.ts +4 -0
- package/esm/node_modules/conventional-commits-filter/dist/index.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-filter/dist/index.js +3 -0
- package/esm/node_modules/conventional-commits-filter/dist/types.d.ts +6 -0
- package/esm/node_modules/conventional-commits-filter/dist/types.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-filter/dist/types.js +2 -0
- package/esm/node_modules/conventional-commits-filter/dist/utils.d.ts +16 -0
- package/esm/node_modules/conventional-commits-filter/dist/utils.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-filter/dist/utils.js +43 -0
- package/esm/node_modules/conventional-commits-filter/package.json +22 -16
- package/esm/node_modules/conventional-commits-parser/README.md +49 -353
- package/esm/node_modules/conventional-commits-parser/dist/CommitParser.d.ts +40 -0
- package/esm/node_modules/conventional-commits-parser/dist/CommitParser.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/CommitParser.js +319 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/index.d.ts +3 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/index.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/index.js +67 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/options.d.ts +3 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/options.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/options.js +40 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/utils.d.ts +5 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/utils.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/cli/utils.js +49 -0
- package/esm/node_modules/conventional-commits-parser/dist/index.d.ts +4 -0
- package/esm/node_modules/conventional-commits-parser/dist/index.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/index.js +3 -0
- package/esm/node_modules/conventional-commits-parser/dist/options.d.ts +3 -0
- package/esm/node_modules/conventional-commits-parser/dist/options.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/options.js +27 -0
- package/esm/node_modules/conventional-commits-parser/dist/regex.d.ts +8 -0
- package/esm/node_modules/conventional-commits-parser/dist/regex.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/regex.js +63 -0
- package/esm/node_modules/conventional-commits-parser/dist/stream.d.ts +14 -0
- package/esm/node_modules/conventional-commits-parser/dist/stream.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/stream.js +38 -0
- package/esm/node_modules/conventional-commits-parser/dist/types.d.ts +100 -0
- package/esm/node_modules/conventional-commits-parser/dist/types.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/types.js +2 -0
- package/esm/node_modules/conventional-commits-parser/dist/utils.d.ts +41 -0
- package/esm/node_modules/conventional-commits-parser/dist/utils.d.ts.map +1 -0
- package/esm/node_modules/conventional-commits-parser/dist/utils.js +75 -0
- package/esm/node_modules/conventional-commits-parser/package.json +22 -23
- package/esm/src/cli/command-help.js +15 -0
- package/esm/src/cli/repair-configuration-description.js +98 -0
- package/esm/src/cli/run-changelog.js +51 -0
- package/esm/src/cli/run-cli.js +8 -0
- package/esm/src/features/built-in-feature-registry.js +2 -0
- package/esm/src/features/changelog-feature.js +155 -0
- package/esm/src/features/github-ci-artifacts.js +17 -3
- package/esm/src/features/github-ci-runtime-matrix.js +50 -0
- package/esm/src/features/github-release-publish-artifacts.js +5 -4
- package/esm/src/features/github-release-publish-feature.js +4 -0
- package/esm/src/features/github-workflow-cache.js +18 -0
- package/esm/src/features/workflow-deno.js +1 -1
- package/esm/src/release/changelog-markdown.js +41 -0
- package/esm/src/release/changelog.js +155 -7
- package/esm/src/release/format-changelog.js +15 -0
- package/esm/src/release/migrate-changelog.js +92 -0
- package/esm/src/release/publish-github.js +7 -3
- package/esm/src/release/publish-tag-prepare.js +63 -78
- package/esm/src/release/release-history.js +53 -0
- package/npm-dependencies.lock.json +115 -48
- package/package.json +5 -3
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/dependencies.js +0 -11036
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/index.d.ts +0 -1416
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/index.js +0 -100
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/licenses.md +0 -1767
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/options.js +0 -92
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/parser.js +0 -84
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/utils.js +0 -7
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/build/validate.js +0 -122
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/license +0 -9
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/package.json +0 -107
- package/esm/node_modules/conventional-commits-parser/node_modules/meow/readme.md +0 -314
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core/node_modules/conventional-changelog-writer}/cli.mjs +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core/node_modules/conventional-changelog-writer}/index.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core/node_modules/conventional-changelog-writer}/lib/util.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core/node_modules/conventional-changelog-writer}/templates/commit.hbs +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core/node_modules/conventional-changelog-writer}/templates/footer.hbs +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core/node_modules/conventional-changelog-writer}/templates/header.hbs +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core/node_modules/conventional-changelog-writer}/templates/template.hbs +0 -0
- /package/esm/node_modules/{conventional-commits-filter → conventional-changelog-core/node_modules/conventional-commits-filter}/index.js +0 -0
- /package/esm/node_modules/{conventional-commits-parser → conventional-changelog-core/node_modules/conventional-commits-parser}/cli.mjs +0 -0
- /package/esm/node_modules/{conventional-commits-parser → conventional-changelog-core/node_modules/conventional-commits-parser}/index.js +0 -0
- /package/esm/node_modules/{conventional-commits-parser → conventional-changelog-core/node_modules/conventional-commits-parser}/lib/parser.js +0 -0
- /package/esm/node_modules/{conventional-commits-parser → conventional-changelog-core/node_modules/conventional-commits-parser}/lib/regex.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/dependencies.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/index.d.ts +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/index.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/licenses.md +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/options.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/parser.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/utils.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/build/validate.js +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/license +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/package.json +0 -0
- /package/esm/node_modules/{conventional-changelog-writer → conventional-changelog-core}/node_modules/meow/readme.md +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/** Optional changelog lifecycle that preserves existing release history. */
|
|
2
|
+
import { parseSemver } from "../release/semver.js";
|
|
3
|
+
import { changelogHeadings, changelogLines, } from "../release/changelog-markdown.js";
|
|
4
|
+
const id = "changelog";
|
|
5
|
+
const path = "CHANGELOG.md";
|
|
6
|
+
const subject = { kind: "file", identifier: path };
|
|
7
|
+
export const changelogStarter = "# Changelog\n";
|
|
8
|
+
const resolution = "Automatic repair is unsupported. Make CHANGELOG.md a readable regular file, preserving its history, then inspect it again.";
|
|
9
|
+
async function inspect(context) {
|
|
10
|
+
const file = await context.files.observe(path);
|
|
11
|
+
if (file.kind !== "file" && file.kind !== "absent") {
|
|
12
|
+
throw new Error("CHANGELOG.md is not a readable regular file.");
|
|
13
|
+
}
|
|
14
|
+
return file;
|
|
15
|
+
}
|
|
16
|
+
async function detect(context) {
|
|
17
|
+
try {
|
|
18
|
+
const file = await inspect(context);
|
|
19
|
+
let observation = "CHANGELOG.md is absent. Enable --changelog to create it with # Changelog.";
|
|
20
|
+
if (file.kind === "file") {
|
|
21
|
+
const headings = changelogHeadings(file.content).filter((heading) => heading.level === 2);
|
|
22
|
+
const releaseText = headings.flatMap((heading, index) => parseSemver(heading.title)
|
|
23
|
+
? [file.content.slice(heading.offset, headings[index + 1]?.offset)]
|
|
24
|
+
: []).join("\n");
|
|
25
|
+
const content = changelogLines(releaseText).map((line) => line.text).join("\n");
|
|
26
|
+
const flat = /^[-*] [a-z][a-z0-9-]*(?:\([\w./@ -]+\))?!?: .+/m.test(content);
|
|
27
|
+
const grouped = /^### (?:BREAKING CHANGE|Features|Fixes|Other)\r?$/m.test(content);
|
|
28
|
+
observation = flat
|
|
29
|
+
? `CHANGELOG.md contains ${grouped ? "mixed flat and grouped" : "flat"} entries. Existing history is preserved. Optional migration preview: hj changelog migrate.`
|
|
30
|
+
: grouped
|
|
31
|
+
? "CHANGELOG.md contains grouped release sections. Existing history is preserved."
|
|
32
|
+
: file.content === changelogStarter
|
|
33
|
+
? "CHANGELOG.md has an empty starter heading, ready for release entries."
|
|
34
|
+
: "CHANGELOG.md exists with custom content. Existing history is preserved.";
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
state: file.kind === "file" ? "enabled" : "disabled",
|
|
38
|
+
evidence: [{
|
|
39
|
+
code: "changelog-observed",
|
|
40
|
+
kind: id,
|
|
41
|
+
subject,
|
|
42
|
+
observation,
|
|
43
|
+
}],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
const issue = {
|
|
48
|
+
code: "changelog-unreadable",
|
|
49
|
+
kind: id,
|
|
50
|
+
subject,
|
|
51
|
+
observation: "CHANGELOG.md could not be read as a regular file.",
|
|
52
|
+
resolution,
|
|
53
|
+
};
|
|
54
|
+
return { state: "ambiguous", evidence: [issue], issues: [issue] };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function check(context, enabled) {
|
|
58
|
+
try {
|
|
59
|
+
const file = await inspect(context);
|
|
60
|
+
if ((enabled && file.kind === "file") || (!enabled && file.kind === "absent")) {
|
|
61
|
+
return {
|
|
62
|
+
result: "no-op",
|
|
63
|
+
warnings: [],
|
|
64
|
+
reason: enabled
|
|
65
|
+
? "Existing CHANGELOG.md is preserved."
|
|
66
|
+
: "CHANGELOG.md is absent.",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (!enabled && file.kind === "file" && file.content !== changelogStarter) {
|
|
70
|
+
return {
|
|
71
|
+
result: "blocked",
|
|
72
|
+
warnings: [],
|
|
73
|
+
blockers: [{
|
|
74
|
+
code: "changelog-history",
|
|
75
|
+
subjects: [subject],
|
|
76
|
+
message: "CHANGELOG.md contains history or custom content; automatic deletion is unsupported.",
|
|
77
|
+
resolution: "Keep the changelog enabled, or archive its contents and remove CHANGELOG.md manually after disabling dependent release publication.",
|
|
78
|
+
}],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
result: "allowed",
|
|
83
|
+
warnings: [],
|
|
84
|
+
preconditions: [{
|
|
85
|
+
kind: "file-digest",
|
|
86
|
+
path,
|
|
87
|
+
digest: file.kind === "file" ? file.digest : undefined,
|
|
88
|
+
}],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return {
|
|
93
|
+
result: "blocked",
|
|
94
|
+
warnings: [],
|
|
95
|
+
blockers: [{
|
|
96
|
+
code: "changelog-unreadable",
|
|
97
|
+
subjects: [subject],
|
|
98
|
+
message: "CHANGELOG.md could not be read as a regular file.",
|
|
99
|
+
resolution,
|
|
100
|
+
}],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function plan(context, enabled, allowed) {
|
|
105
|
+
const file = await inspect(context);
|
|
106
|
+
const checked = await check(context, enabled);
|
|
107
|
+
if (checked.result !== "allowed") {
|
|
108
|
+
throw new Error("Changelog operation no longer matches its checked state.");
|
|
109
|
+
}
|
|
110
|
+
let changes;
|
|
111
|
+
if (enabled) {
|
|
112
|
+
changes = [{
|
|
113
|
+
kind: "write-file",
|
|
114
|
+
path,
|
|
115
|
+
content: changelogStarter,
|
|
116
|
+
expectedDigest: undefined,
|
|
117
|
+
}];
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
if (file.kind !== "file") {
|
|
121
|
+
throw new Error("CHANGELOG.md disappeared during planning.");
|
|
122
|
+
}
|
|
123
|
+
changes = [{ kind: "remove-file", path, expectedDigest: file.digest }];
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
featureId: id,
|
|
127
|
+
action: enabled ? "enable" : "disable",
|
|
128
|
+
summary: enabled
|
|
129
|
+
? "Create CHANGELOG.md with # Changelog."
|
|
130
|
+
: "Remove the empty CHANGELOG.md starter.",
|
|
131
|
+
warnings: allowed.warnings,
|
|
132
|
+
preconditions: allowed.preconditions,
|
|
133
|
+
changes,
|
|
134
|
+
validations: [{
|
|
135
|
+
kind: "feature-redetection",
|
|
136
|
+
featureId: id,
|
|
137
|
+
expected: enabled ? "enabled" : "disabled",
|
|
138
|
+
}],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** Presence, rather than a required style, defines a valid changelog. */
|
|
142
|
+
export const changelogFeature = {
|
|
143
|
+
metadata: {
|
|
144
|
+
id,
|
|
145
|
+
name: "Changelog",
|
|
146
|
+
summary: "Keep release history, with optional migration from flat entries to grouped sections.",
|
|
147
|
+
},
|
|
148
|
+
dependencies: { requires: [] },
|
|
149
|
+
capabilities: { provides: [], requires: [] },
|
|
150
|
+
detect,
|
|
151
|
+
checkEnable: (context) => check(context, true),
|
|
152
|
+
checkDisable: (context) => check(context, false),
|
|
153
|
+
planEnable: (context, allowed) => plan(context, true, allowed),
|
|
154
|
+
planDisable: (context, allowed) => plan(context, false, allowed),
|
|
155
|
+
};
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { legacyCiCheckCompatibility } from "./github-ci-legacy.js";
|
|
3
3
|
import { workflowDenoVersion } from "./workflow-toolchain.js";
|
|
4
4
|
import { hjPackageReference } from "./hj-package.js";
|
|
5
|
+
import { githubCiRuntimeMatrix, githubCiRuntimeMatrixMarker, } from "./github-ci-runtime-matrix.js";
|
|
6
|
+
import { denoCacheInputs } from "./github-workflow-cache.js";
|
|
5
7
|
import { inspectArtifact } from "../artifacts/inspect-artifact.js";
|
|
6
8
|
import { workflowCliArtifact } from "./workflow-cli.js";
|
|
7
9
|
export const githubCiFeatureId = "github-ci";
|
|
@@ -18,6 +20,10 @@ on:
|
|
|
18
20
|
permissions:
|
|
19
21
|
contents: read
|
|
20
22
|
|
|
23
|
+
concurrency:
|
|
24
|
+
group: hj-ci-\${{ github.workflow }}-pr-\${{ github.event.pull_request.number }}
|
|
25
|
+
cancel-in-progress: true
|
|
26
|
+
|
|
21
27
|
jobs:
|
|
22
28
|
check:
|
|
23
29
|
runs-on: ubuntu-latest
|
|
@@ -28,7 +34,7 @@ jobs:
|
|
|
28
34
|
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
|
|
29
35
|
with:
|
|
30
36
|
deno-version: ${workflowDenoVersion}
|
|
31
|
-
- run: deno task all
|
|
37
|
+
${denoCacheInputs} - run: deno task all
|
|
32
38
|
hj-release-commit-validation:
|
|
33
39
|
runs-on: ubuntu-latest
|
|
34
40
|
permissions:
|
|
@@ -41,7 +47,7 @@ jobs:
|
|
|
41
47
|
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
|
|
42
48
|
with:
|
|
43
49
|
deno-version: ${workflowDenoVersion}
|
|
44
|
-
- name: Validate release commits
|
|
50
|
+
${denoCacheInputs} - name: Validate release commits
|
|
45
51
|
env:
|
|
46
52
|
HJ_RELEASE_ROUTE: source-validation
|
|
47
53
|
HJ_SOURCE_BASE_SHA: \${{ github.event.pull_request.base.sha }}
|
|
@@ -82,7 +88,7 @@ jobs:
|
|
|
82
88
|
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
|
|
83
89
|
with:
|
|
84
90
|
deno-version: ${workflowDenoVersion}
|
|
85
|
-
- name: Update dependencies
|
|
91
|
+
${denoCacheInputs} - name: Update dependencies
|
|
86
92
|
env:
|
|
87
93
|
GH_TOKEN: \${{ github.token }}
|
|
88
94
|
run: |
|
|
@@ -117,6 +123,14 @@ jobs:
|
|
|
117
123
|
}];
|
|
118
124
|
/** Render the same exact CI variant for local adoption and remote protection. */
|
|
119
125
|
export function renderGithubCiArtifact(artifact, context, observation) {
|
|
126
|
+
if (artifact.path === githubCiArtifacts[0].path &&
|
|
127
|
+
observation.kind === "file" &&
|
|
128
|
+
observation.content.split("\n").includes(githubCiRuntimeMatrixMarker.trimEnd())) {
|
|
129
|
+
artifact = {
|
|
130
|
+
...artifact,
|
|
131
|
+
content: githubCiRuntimeMatrix(artifact.content),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
120
134
|
const migrated = artifact.path === githubCiArtifacts[0].path &&
|
|
121
135
|
observation.kind === "file" &&
|
|
122
136
|
observation.content.includes("# Retains the git-hj-init required test check.");
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** @module Explicit managed CI variant for hj's shared runtime suite. */
|
|
2
|
+
import { nativeNpmCacheStep } from "./github-workflow-cache.js";
|
|
3
|
+
export const githubCiRuntimeMatrixMarker = "# hj-ci-runtime-matrix: deno-node-bun-v1\n";
|
|
4
|
+
/** Retain shared setup and release validation while replacing the check job. */
|
|
5
|
+
export function githubCiRuntimeMatrix(content) {
|
|
6
|
+
const start = content.indexOf(" check:\n");
|
|
7
|
+
const end = content.indexOf(" hj-release-commit-validation:\n", start);
|
|
8
|
+
const setup = content.slice(start, end).split(" steps:\n")[1]
|
|
9
|
+
.split(" - run: deno task all\n")[0];
|
|
10
|
+
const upload = (label) => ` - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
11
|
+
with:
|
|
12
|
+
name: runtime-report-${label}
|
|
13
|
+
path: .hj/test-results/${label}.json
|
|
14
|
+
include-hidden-files: true
|
|
15
|
+
if-no-files-found: error
|
|
16
|
+
retention-days: 1
|
|
17
|
+
overwrite: true
|
|
18
|
+
`;
|
|
19
|
+
const jobs = ` deno:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
${setup} - run: deno task ci-deno
|
|
23
|
+
${upload("deno")} native:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
strategy:
|
|
26
|
+
fail-fast: false
|
|
27
|
+
matrix:
|
|
28
|
+
runtime: [node24, node26, bun]
|
|
29
|
+
steps:
|
|
30
|
+
${setup}${nativeNpmCacheStep} - run: deno run --allow-all scripts/run-test-matrix.ts --runtime \${{ matrix.runtime }}
|
|
31
|
+
${upload("\${{ matrix.runtime }}")} check:
|
|
32
|
+
if: \${{ always() }}
|
|
33
|
+
needs: [deno, native]
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- name: Require successful checks and every runtime
|
|
37
|
+
env:
|
|
38
|
+
DENO_RESULT: \${{ needs.deno.result }}
|
|
39
|
+
NATIVE_RESULT: \${{ needs.native.result }}
|
|
40
|
+
run: test "$DENO_RESULT" = success && test "$NATIVE_RESULT" = success
|
|
41
|
+
${setup} - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
42
|
+
with:
|
|
43
|
+
pattern: runtime-report-*
|
|
44
|
+
path: .hj/test-results
|
|
45
|
+
merge-multiple: true
|
|
46
|
+
- name: Compare complete executed inventories
|
|
47
|
+
run: deno run --allow-read scripts/run-test-matrix.ts --compare-only
|
|
48
|
+
`;
|
|
49
|
+
return (content.slice(0, start) + jobs + content.slice(end)).replace("name: CI\n", githubCiRuntimeMatrixMarker + "name: CI\n");
|
|
50
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @module Exact workflows owned by release-publication features. */
|
|
2
2
|
import { workflowDenoVersion } from "./workflow-toolchain.js";
|
|
3
3
|
import { hjPackageReference } from "./hj-package.js";
|
|
4
|
+
import { denoCacheInputs, nativeNpmCacheStep, } from "./github-workflow-cache.js";
|
|
4
5
|
import { inspectArtifact } from "../artifacts/inspect-artifact.js";
|
|
5
6
|
import { workflowCliArtifact } from "./workflow-cli.js";
|
|
6
7
|
import { publishGithubWorkflow, publishJsrWorkflow, publishNpmWorkflow, publishTagWorkflow, } from "../release/names.js";
|
|
@@ -48,7 +49,7 @@ jobs:
|
|
|
48
49
|
- uses: ${deno}
|
|
49
50
|
with:
|
|
50
51
|
deno-version: ${workflowDenoVersion}
|
|
51
|
-
- name: Configure Git authentication
|
|
52
|
+
${denoCacheInputs}${nativeNpmCacheStep} - name: Configure Git authentication
|
|
52
53
|
env:
|
|
53
54
|
GH_TOKEN: \${{ github.token }}
|
|
54
55
|
run: gh auth setup-git
|
|
@@ -59,7 +60,7 @@ jobs:
|
|
|
59
60
|
HJ_RELEASE_TAG: \${{ inputs.tag }}
|
|
60
61
|
run: >-
|
|
61
62
|
deno run --no-lock
|
|
62
|
-
--allow-env=PATH,GITHUB_OUTPUT,GITHUB_STEP_SUMMARY,HJ_RELEASE_ROUTE,HJ_RELEASE_TAG,ESBUILD_BINARY_PATH,ESBUILD_WORKER_THREADS
|
|
63
|
+
--allow-env=PATH,GITHUB_REPOSITORY,GITHUB_OUTPUT,GITHUB_STEP_SUMMARY,HJ_RELEASE_ROUTE,HJ_RELEASE_TAG,ESBUILD_BINARY_PATH,ESBUILD_WORKER_THREADS
|
|
63
64
|
--allow-read=.,/tmp/opencode
|
|
64
65
|
--allow-write=deno.json,deno.jsonc,CHANGELOG.md,/tmp/opencode,"\${GITHUB_OUTPUT}","\${GITHUB_STEP_SUMMARY}"
|
|
65
66
|
--allow-run=deno,git
|
|
@@ -83,7 +84,7 @@ jobs:
|
|
|
83
84
|
- uses: ${deno}
|
|
84
85
|
with:
|
|
85
86
|
deno-version: ${workflowDenoVersion}
|
|
86
|
-
- name: Configure Git authentication
|
|
87
|
+
${denoCacheInputs} - name: Configure Git authentication
|
|
87
88
|
env:
|
|
88
89
|
GH_TOKEN: \${{ github.token }}
|
|
89
90
|
run: gh auth setup-git
|
|
@@ -140,7 +141,7 @@ ${job === "jsr"
|
|
|
140
141
|
- uses: ${deno}
|
|
141
142
|
with:
|
|
142
143
|
deno-version: ${workflowDenoVersion}
|
|
143
|
-
${job === "npm"
|
|
144
|
+
${denoCacheInputs}${job === "npm"
|
|
144
145
|
? ` - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
|
145
146
|
with:
|
|
146
147
|
node-version: "24"
|
|
@@ -37,6 +37,10 @@ function releaseFeature(id, name, artifact, requires, options = {}) {
|
|
|
37
37
|
}
|
|
38
38
|
export const githubReleasePublishTagFeature = releaseFeature(githubReleasePublishTagFeatureId, "GitHub release tag publication", publishTagArtifact, [
|
|
39
39
|
{ featureId: "git", reason: "Release publication requires Git." },
|
|
40
|
+
{
|
|
41
|
+
featureId: "changelog",
|
|
42
|
+
reason: "Release publication records changes in CHANGELOG.md.",
|
|
43
|
+
},
|
|
40
44
|
{
|
|
41
45
|
featureId: "github-repo",
|
|
42
46
|
reason: "Release publication requires GitHub.",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @module Download caches shared by managed CI and release workflows. */
|
|
2
|
+
import { workflowDenoVersion } from "./workflow-toolchain.js";
|
|
3
|
+
// setup-deno adds OS, architecture and job; its fallback also shares across jobs.
|
|
4
|
+
// Package version metadata is deliberately excluded from dependency cache keys.
|
|
5
|
+
export const denoCacheInputs = ` cache: true
|
|
6
|
+
cache-hash: deno-${workflowDenoVersion}-\${{ hashFiles('**/deno.lock', 'toolchain.json') }}
|
|
7
|
+
`;
|
|
8
|
+
// Frozen npm ci still validates the locks and installs with scripts disabled.
|
|
9
|
+
// Do not cache the build output or node_modules: test preparation clears them.
|
|
10
|
+
export const nativeNpmCacheStep = ` - name: Cache native test npm downloads
|
|
11
|
+
if: hashFiles('scripts/test-build/dependencies/package-lock.json') != '' && hashFiles('scripts/test-build/tools/package-lock.json') != ''
|
|
12
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
13
|
+
with:
|
|
14
|
+
path: ~/.npm
|
|
15
|
+
key: native-test-npm-\${{ runner.os }}-\${{ runner.arch }}-\${{ hashFiles('scripts/test-build/dependencies/package-lock.json', 'scripts/test-build/tools/package-lock.json', 'toolchain.json') }}
|
|
16
|
+
restore-keys: |
|
|
17
|
+
native-test-npm-\${{ runner.os }}-\${{ runner.arch }}-
|
|
18
|
+
`;
|
|
@@ -22,6 +22,6 @@ export function workflowDenoArtifact(artifact, context, observation) {
|
|
|
22
22
|
const version = validateDenoVersion(selected);
|
|
23
23
|
return {
|
|
24
24
|
...artifact,
|
|
25
|
-
content: artifact.content.replaceAll(`deno-version: ${workflowDenoVersion}\n`, `deno-version: ${version}\n`),
|
|
25
|
+
content: artifact.content.replaceAll(`deno-version: ${workflowDenoVersion}\n`, `deno-version: ${version}\n`).replaceAll(`cache-hash: deno-${workflowDenoVersion}-`, `cache-hash: deno-${version}-`),
|
|
26
26
|
};
|
|
27
27
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function changelogLines(text) {
|
|
2
|
+
const lines = [];
|
|
3
|
+
let offset = 0;
|
|
4
|
+
let comment = false;
|
|
5
|
+
let fence;
|
|
6
|
+
for (const line of text.split(/(?<=\n)/)) {
|
|
7
|
+
const value = line.replace(/\r?\n$/, "");
|
|
8
|
+
const marker = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(value);
|
|
9
|
+
if (fence) {
|
|
10
|
+
if (marker && marker[1][0] === fence.character &&
|
|
11
|
+
marker[1].length >= fence.length && !marker[2].trim())
|
|
12
|
+
fence = undefined;
|
|
13
|
+
}
|
|
14
|
+
else if (comment || value.replace(/(`+).*?\1/g, "").includes("<!--")) {
|
|
15
|
+
// Comments can contain example headings and fences; neither is structure.
|
|
16
|
+
const markers = value.replace(/(`+).*?\1/g, "").match(/<!--|-->/g) ?? [];
|
|
17
|
+
for (const token of markers) {
|
|
18
|
+
if (token === "<!--")
|
|
19
|
+
comment = true;
|
|
20
|
+
else
|
|
21
|
+
comment = false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (marker) {
|
|
25
|
+
fence = { character: marker[1][0], length: marker[1].length };
|
|
26
|
+
}
|
|
27
|
+
else if (!/^ {4}|^\t/.test(value)) {
|
|
28
|
+
lines.push({ text: value, offset });
|
|
29
|
+
}
|
|
30
|
+
offset += line.length;
|
|
31
|
+
}
|
|
32
|
+
return lines;
|
|
33
|
+
}
|
|
34
|
+
export function changelogHeadings(text) {
|
|
35
|
+
return changelogLines(text).flatMap((line) => {
|
|
36
|
+
const heading = /^ {0,3}(#{1,6})[ \t]+(.+?)(?:[ \t]+#+)?[ \t]*$/.exec(line.text);
|
|
37
|
+
return heading
|
|
38
|
+
? [{ title: heading[2], level: heading[1].length, offset: line.offset }]
|
|
39
|
+
: [];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -1,7 +1,117 @@
|
|
|
1
1
|
/** @module Immutable changelog release-section creation and insertion. */
|
|
2
|
+
import { changelogHeadings } from "./changelog-markdown.js";
|
|
3
|
+
import { CommitParser } from "conventional-commits-parser";
|
|
4
|
+
import { writeChangelogString } from "conventional-changelog-writer";
|
|
5
|
+
import { validateConventionalCommits } from "./conventional-commit.js";
|
|
6
|
+
import { parseSemver } from "./semver.js";
|
|
7
|
+
const sections = ["BREAKING CHANGE", "Features", "Fixes", "Other"];
|
|
8
|
+
/** Validates the target used for commit and issue links. */
|
|
9
|
+
export function changelogRepositoryUrl(repository) {
|
|
10
|
+
const parts = repository.split("/");
|
|
11
|
+
if (parts.length !== 2 ||
|
|
12
|
+
parts.some((part) => !/^[A-Za-z0-9_.-]+$/.test(part) || part === "." || part === ".."))
|
|
13
|
+
throw new TypeError("Changelog requires a GitHub owner/repository.");
|
|
14
|
+
return `https://github.com/${repository}`;
|
|
15
|
+
}
|
|
16
|
+
/** Groups validated full messages with the pinned Conventional Changelog libraries. */
|
|
17
|
+
export async function createReleaseSection(version, commits, repository) {
|
|
18
|
+
const repositoryUrl = changelogRepositoryUrl(repository);
|
|
19
|
+
if (!parseSemver(version) || commits.length === 0 ||
|
|
20
|
+
commits.some((commit) => !/^[0-9a-f]{40}(?:[0-9a-f]{24})?$/.test(commit.hash))) {
|
|
21
|
+
throw new TypeError("Release section requires a version and full commit SHAs.");
|
|
22
|
+
}
|
|
23
|
+
const validated = validateConventionalCommits(commits.map((commit) => commit.message));
|
|
24
|
+
const parser = new CommitParser({
|
|
25
|
+
headerPattern: /^([a-z][a-z0-9-]*)(?:\(([\w./@ -]+)\))?!?: (.+)$/,
|
|
26
|
+
headerCorrespondence: ["type", "scope", "subject"],
|
|
27
|
+
noteKeywords: ["BREAKING CHANGE", "BREAKING-CHANGE"],
|
|
28
|
+
});
|
|
29
|
+
const parsed = commits.map((commit, index) => {
|
|
30
|
+
const data = parser.parse(commit.message);
|
|
31
|
+
const references = [
|
|
32
|
+
...data.references,
|
|
33
|
+
...data.notes.flatMap((note) => parser.parse(`fix: note\n\n${note.text}`).references),
|
|
34
|
+
];
|
|
35
|
+
const refs = new Map();
|
|
36
|
+
for (const reference of references) {
|
|
37
|
+
if (!/^\d+$/.test(reference.issue))
|
|
38
|
+
continue;
|
|
39
|
+
const target = reference.repository
|
|
40
|
+
? `${reference.owner ?? repository.split("/")[0]}/${reference.repository}`
|
|
41
|
+
: repository;
|
|
42
|
+
const label = `${reference.owner ? `${reference.owner}/` : ""}${reference.repository ?? ""}#${reference.issue}`;
|
|
43
|
+
refs.set(label, `${changelogRepositoryUrl(target)}/issues/${reference.issue}`);
|
|
44
|
+
}
|
|
45
|
+
const used = new Set();
|
|
46
|
+
const subject = linkReferences(data.subject ?? "", refs, used);
|
|
47
|
+
const notes = data.notes.map((note) => ({
|
|
48
|
+
...note,
|
|
49
|
+
text: linkReferences(note.text, refs, used),
|
|
50
|
+
}));
|
|
51
|
+
const extraLinks = [...refs].flatMap(([label, url]) => {
|
|
52
|
+
if (used.has(url))
|
|
53
|
+
return [];
|
|
54
|
+
used.add(url);
|
|
55
|
+
return [`[${label}](${url})`];
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
...data,
|
|
59
|
+
hash: commit.hash,
|
|
60
|
+
scope: data.scope ?? "",
|
|
61
|
+
subject,
|
|
62
|
+
notes,
|
|
63
|
+
type: validated[index].breaking
|
|
64
|
+
? "BREAKING CHANGE"
|
|
65
|
+
: data.type === "feat"
|
|
66
|
+
? "Features"
|
|
67
|
+
: data.type === "fix"
|
|
68
|
+
? "Fixes"
|
|
69
|
+
: "Other",
|
|
70
|
+
links: [
|
|
71
|
+
`[${commit.hash.slice(0, 7)}](${repositoryUrl}/commit/${commit.hash})`,
|
|
72
|
+
...extraLinks,
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
const text = await writeChangelogString(parsed, { version }, {
|
|
77
|
+
// Preserve full hashes, every commit type, and the supplied range order.
|
|
78
|
+
transform: (commit) => commit,
|
|
79
|
+
ignoreReverted: false,
|
|
80
|
+
generateOn: () => false,
|
|
81
|
+
groupBy: "type",
|
|
82
|
+
commitsSort: (a, b) => compare(a.scope, b.scope),
|
|
83
|
+
commitGroupsSort: (a, b) => sections.indexOf(a.title) - sections.indexOf(b.title),
|
|
84
|
+
template: (context) => {
|
|
85
|
+
let output = `## ${version}\n\n`;
|
|
86
|
+
for (const group of context.commitGroups ?? []) {
|
|
87
|
+
output += `### ${group.title}\n\n`;
|
|
88
|
+
let scope;
|
|
89
|
+
for (const commit of group.commits) {
|
|
90
|
+
if (commit.scope !== scope) {
|
|
91
|
+
if (scope !== undefined)
|
|
92
|
+
output += "\n";
|
|
93
|
+
scope = commit.scope;
|
|
94
|
+
if (scope)
|
|
95
|
+
output += `#### ${scope}\n\n`;
|
|
96
|
+
}
|
|
97
|
+
output += `- ${commit.subject} (${commit.links.join(", ")})\n`;
|
|
98
|
+
for (const note of commit.notes) {
|
|
99
|
+
output += `\n ${note.text.replaceAll("\n", "\n ")}\n`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
output += "\n";
|
|
103
|
+
}
|
|
104
|
+
return output;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
return `${text.trimEnd()}\n\n`;
|
|
108
|
+
}
|
|
109
|
+
function compare(a, b) {
|
|
110
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
111
|
+
}
|
|
2
112
|
const heading = "# Changelog";
|
|
3
113
|
/** Creates a deterministic release heading and list from validated commit subjects. */
|
|
4
|
-
export function
|
|
114
|
+
export function createLegacyReleaseSection(version, subjects) {
|
|
5
115
|
if (!version || subjects.some((subject) => !subject)) {
|
|
6
116
|
throw new TypeError("Release section requires a version and non-empty subjects.");
|
|
7
117
|
}
|
|
@@ -15,12 +125,22 @@ export function createChangelogInsertion(oldText, releaseSection) {
|
|
|
15
125
|
if (!oldText) {
|
|
16
126
|
return { offset: 0, text: `${heading}\n\n${releaseSection}` };
|
|
17
127
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
128
|
+
const allHeadings = changelogHeadings(oldText);
|
|
129
|
+
const headings = allHeadings.filter((item) => item.level === 2);
|
|
130
|
+
const section = headings.find((item) => parseSemver(item.title) || /^\[?unreleased\]?$/i.test(item.title)) ?? headings[0];
|
|
131
|
+
// Without release headings, insert just after a leading title. Never enter a code block.
|
|
132
|
+
const titleEnd = allHeadings[0]?.level === 1 && allHeadings[0]?.offset === 0
|
|
133
|
+
? /^(?:[^\n]*\n)(?:[ \t]*\r?\n)*/.exec(oldText)?.[0].length ??
|
|
134
|
+
oldText.length
|
|
135
|
+
: 0;
|
|
136
|
+
const offset = section?.offset ?? titleEnd;
|
|
137
|
+
const prefix = oldText.slice(0, offset);
|
|
138
|
+
const separator = !prefix || prefix.endsWith("\n\n")
|
|
139
|
+
? ""
|
|
140
|
+
: prefix.endsWith("\n")
|
|
141
|
+
? "\n"
|
|
142
|
+
: "\n\n";
|
|
143
|
+
return { offset, text: separator + releaseSection };
|
|
24
144
|
}
|
|
25
145
|
/** Applies the recorded insertion and leaves every old byte outside it unchanged. */
|
|
26
146
|
export function applyChangelogInsertion(oldText, insertion) {
|
|
@@ -31,3 +151,31 @@ export function applyChangelogInsertion(oldText, insertion) {
|
|
|
31
151
|
return oldText.slice(0, insertion.offset) + insertion.text +
|
|
32
152
|
oldText.slice(insertion.offset);
|
|
33
153
|
}
|
|
154
|
+
/** Leave existing Markdown links, code and URLs intact while linking plain references. */
|
|
155
|
+
function linkReferences(text, references, used) {
|
|
156
|
+
return text.replace(/\[[^\]]*\]\([^)]*\)|`[^`]*`|https?:\/\/\S+|(?<![\w/])(?:[\w.-]+(?:\/[\w.-]+)?)?#\d+\b/g, (token) => {
|
|
157
|
+
const url = references.get(token);
|
|
158
|
+
if (!url) {
|
|
159
|
+
for (const target of references.values()) {
|
|
160
|
+
if (token.endsWith(`](${target})`))
|
|
161
|
+
used.add(target);
|
|
162
|
+
}
|
|
163
|
+
return token;
|
|
164
|
+
}
|
|
165
|
+
used.add(url);
|
|
166
|
+
return `[${token}](${url})`;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/** The original insertion algorithm remains frozen for exact legacy recovery. */
|
|
170
|
+
export function createLegacyChangelogInsertion(oldText, releaseSection) {
|
|
171
|
+
if (!oldText)
|
|
172
|
+
return { offset: 0, text: `${heading}\n\n${releaseSection}` };
|
|
173
|
+
if (!/^# Changelog(?:\r?\n|$)/.test(oldText)) {
|
|
174
|
+
throw new TypeError("Existing changelog must start with # Changelog.");
|
|
175
|
+
}
|
|
176
|
+
const section = /\n## [^\n]+(?:\r?\n|$)/.exec(oldText);
|
|
177
|
+
return {
|
|
178
|
+
offset: section ? section.index + 1 : oldText.length,
|
|
179
|
+
text: releaseSection,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Format new changelog text without rewriting previous content. */
|
|
2
|
+
import { createChangelogInsertion, } from "./changelog.js";
|
|
3
|
+
import { runOrThrow } from "./release-process.js";
|
|
4
|
+
export async function formatChangelogSection(process, section) {
|
|
5
|
+
return await runOrThrow(process, "deno", ["fmt", "--ext=md", "-"], {
|
|
6
|
+
stdin: section,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export async function formatChangelogInsertion(process, oldText, section) {
|
|
10
|
+
const formatted = await formatChangelogSection(process, section);
|
|
11
|
+
const insertion = createChangelogInsertion(oldText, `${formatted.trimEnd()}\n\n`);
|
|
12
|
+
return insertion.offset === oldText.length
|
|
13
|
+
? { ...insertion, text: `${insertion.text.trimEnd()}\n` }
|
|
14
|
+
: insertion;
|
|
15
|
+
}
|