Skip to content

Commit 92915a2

Browse files
committed
add release-notes output
Add a new `release-notes` output to the action containing the release notes for the newly released versions, allowing consumers to leverage it in their workflows (e.g. by passing it down to the GitHub Release API).
1 parent 64c61ea commit 92915a2

File tree

11 files changed

+114
-1
lines changed

11 files changed

+114
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add a new `release-notes` output to the action containing the release notes for the newly released versions.
13+
1014
## [3.0.0] - 2024-04-08
1115

1216
### Fixed

__tests__/fixtures/empty_release/release-notes.expected.md

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Added
2+
3+
- Everything since the beginning!

__tests__/fixtures/lowercase_link_reference/release-notes.expected.md

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Changed
2+
3+
- Our main theme is now blue instead of red.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Changed
2+
3+
- Our main theme is now blue instead of red.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Changed
2+
3+
- Our main theme is now blue instead of red.

__tests__/getReleaseNotes.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import getReleaseNotes from "../src/getReleaseNotes";
2+
import { read } from "to-vfile";
3+
4+
interface Fixture {
5+
tag: string;
6+
version: string;
7+
date: string;
8+
genesisHash: string;
9+
owner: string;
10+
repo: string;
11+
}
12+
13+
it.each(["empty_release", "standard", "first_release", "lowercase_link_reference", "tag_release", "tag_on_tag"])(
14+
`should extract %s release-notes output`,
15+
async function(testcase) {
16+
const expectedChangelog = await read(
17+
`./__tests__/fixtures/${testcase}/CHANGELOG.expected.md`,
18+
{
19+
encoding: "utf-8"
20+
}
21+
);
22+
const release: Fixture = await import(
23+
`./fixtures/${testcase}/fixture`
24+
).then(module => module.default);
25+
26+
const expectedReleaseNotes = await read(
27+
`./__tests__/fixtures/${testcase}/release-notes.expected.md`,
28+
{
29+
encoding: "utf-8"
30+
}
31+
).then(expected => expected.toString("utf-8"));
32+
const actualReleaseNotes = getReleaseNotes(expectedChangelog, release.version);
33+
expect(actualReleaseNotes).toEqual(expectedReleaseNotes);
34+
}
35+
);

action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ inputs:
1717
changelogPath:
1818
description: 'The path to the changelog file. Defaults to `./CHANGELOG.md`'
1919
required: false
20+
outputs:
21+
release-notes:
22+
description: 'The release notes of the newly released version'
2023
runs:
2124
using: 'node20'
2225
main: 'dist/index.js'

src/getReleaseNotes.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import unified, { Transformer } from "unified";
2+
import markdown from "remark-parse";
3+
import stringify from "remark-stringify";
4+
import { VFile } from "vfile";
5+
import { Node } from "unist";
6+
import { MarkdownRootNode } from "markdown-nodes";
7+
8+
function releaseNotesExtraction(version: string) {
9+
return transformer as unknown as Transformer;
10+
11+
function transformer(tree: MarkdownRootNode, _file: VFile) {
12+
const children = tree.children;
13+
14+
const firstNodeIndex = children.findIndex(
15+
node => node.type === "heading" && node.depth === 2 &&
16+
node.children.length > 1 && node.children[0].type === "linkReference" &&
17+
node.children[0].identifier === version
18+
) + 1;
19+
const firstNode = children.slice(firstNodeIndex);
20+
21+
let lastNodeIndex = firstNode.findIndex(
22+
node => node.type === "heading" && node.depth === 2
23+
);
24+
// special case: release notes for first release will not end with another
25+
// section, instead they end with the compare URLs
26+
if (lastNodeIndex === -1) {
27+
lastNodeIndex = firstNode.findIndex(
28+
node => node.type === "definition" && node.identifier === "unreleased"
29+
);
30+
}
31+
32+
const releaseNotesNodes = firstNode.slice(0, lastNodeIndex);
33+
tree.children = releaseNotesNodes;
34+
return tree as Node;
35+
}
36+
}
37+
38+
export default function getReleaseNotes(
39+
file: VFile,
40+
version: string
41+
): string {
42+
// @ts-ignore
43+
return unified()
44+
.use(markdown)
45+
.use(releaseNotesExtraction, version)
46+
.data("settings", {
47+
listItemIndent: "1",
48+
tightDefinitions: true,
49+
bullet: "-"
50+
})
51+
.use(stringify)
52+
.processSync(file)
53+
.toString("utf-8")
54+
.trim();
55+
}

src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { setFailed } from "@actions/core";
2+
import { setOutput } from "@actions/core/lib/core";
23
import { read, write } from "to-vfile";
34
import updateChangelog from "./updateChangelog";
45
import getInputs from "./getInputs";
56
import getGenesisHash from "./getGenesisHash";
7+
import getReleaseNotes from "./getReleaseNotes";
68

79
async function run(): Promise<void> {
810
try {
@@ -20,8 +22,10 @@ async function run(): Promise<void> {
2022
owner,
2123
repo
2224
);
23-
2425
await write(newChangelog, { encoding: "utf-8" });
26+
27+
const releaseNotes = getReleaseNotes(newChangelog, version);
28+
setOutput("release-notes", releaseNotes);
2529
} catch (error) {
2630
setFailed(error.message);
2731
}

0 commit comments

Comments
 (0)