Skip to content

Commit fd0bc2e

Browse files
mixelburgAndarist
andauthored
Linkify issue references in GitHub changelog entries (#1850)
* feat(changelog-github): linkify issue references in changelog entries Converts plain #1234 references in changeset summaries into clickable GitHub links in the generated CHANGELOG.md. References that are already part of markdown links are left unchanged. Uses the configured repo and GITHUB_SERVER_URL for link generation. Fixes #538 * style: format with prettier * style: fix prettier formatting * simplif regex * extra tests * Create tall-meals-marry.md * explain tradeoff in the comment --------- Co-authored-by: Maks Pikov <mixelburg@users.noreply.github.com> Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
1 parent d8d06e7 commit fd0bc2e

3 files changed

Lines changed: 135 additions & 2 deletions

File tree

.changeset/tall-meals-marry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@changesets/changelog-github": minor
3+
---
4+
5+
Linkify issue references in changelog entries.

packages/changelog-github/src/index.test.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,112 @@ describe.each(["author", "user"])(
118118
}
119119
);
120120

121+
it("linkifies bare issue references", async () => {
122+
expect(
123+
await getReleaseLine(...getChangeset("fixes #1234 and #5678", data.commit))
124+
).toMatchInlineSnapshot(`
125+
"
126+
127+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
128+
fixes [#1234](https://github.com/emotion-js/emotion/issues/1234) and [#5678](https://github.com/emotion-js/emotion/issues/5678)"
129+
`);
130+
});
131+
132+
it("does not double-linkify existing markdown links", async () => {
133+
expect(
134+
await getReleaseLine(
135+
...getChangeset(
136+
"see [#1234](https://github.com/emotion-js/emotion/issues/1234)",
137+
data.commit
138+
)
139+
)
140+
).toMatchInlineSnapshot(`
141+
"
142+
143+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
144+
see [#1234](https://github.com/emotion-js/emotion/issues/1234)"
145+
`);
146+
});
147+
148+
it("does not linkify issue-like refs inside link text", async () => {
149+
expect(
150+
await getReleaseLine(
151+
...getChangeset("see [fix for #99](https://example.com)", data.commit)
152+
)
153+
).toMatchInlineSnapshot(`
154+
"
155+
156+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
157+
see [fix for #99](https://example.com)"
158+
`);
159+
});
160+
161+
it("does not linkify when preceded by a word character", async () => {
162+
expect(await getReleaseLine(...getChangeset("foo#123", data.commit)))
163+
.toMatchInlineSnapshot(`
164+
"
165+
166+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
167+
foo#123"
168+
`);
169+
});
170+
171+
it("does not linkify #0", async () => {
172+
expect(await getReleaseLine(...getChangeset("see #0", data.commit)))
173+
.toMatchInlineSnapshot(`
174+
"
175+
176+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
177+
see #0"
178+
`);
179+
});
180+
181+
it("linkifies issue ref at the start of a line", async () => {
182+
expect(await getReleaseLine(...getChangeset("#42 was fixed", data.commit)))
183+
.toMatchInlineSnapshot(`
184+
"
185+
186+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
187+
[#42](https://github.com/emotion-js/emotion/issues/42) was fixed"
188+
`);
189+
});
190+
191+
it("linkifies issue ref after punctuation", async () => {
192+
expect(await getReleaseLine(...getChangeset("fixed (#99)", data.commit)))
193+
.toMatchInlineSnapshot(`
194+
"
195+
196+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
197+
fixed ([#99](https://github.com/emotion-js/emotion/issues/99))"
198+
`);
199+
});
200+
201+
it("handles mixed linked and bare refs", async () => {
202+
expect(
203+
await getReleaseLine(
204+
...getChangeset(
205+
"fixes [#1](https://github.com/emotion-js/emotion/issues/1) and #2",
206+
data.commit
207+
)
208+
)
209+
).toMatchInlineSnapshot(`
210+
"
211+
212+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
213+
fixes [#1](https://github.com/emotion-js/emotion/issues/1) and [#2](https://github.com/emotion-js/emotion/issues/2)"
214+
`);
215+
});
216+
217+
it("linkifies issue ref followed by a dot", async () => {
218+
expect(await getReleaseLine(...getChangeset("this fixes #42.", data.commit)))
219+
.toMatchInlineSnapshot(`
220+
"
221+
222+
- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something
223+
this fixes [#42](https://github.com/emotion-js/emotion/issues/42)."
224+
`);
225+
});
226+
121227
it("with multiple authors", async () => {
122228
expect(
123229
await getReleaseLine(

packages/changelog-github/src/index.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ import { getInfo, getInfoFromPullRequest } from "@changesets/get-github-info";
55

66
config();
77

8+
// "match what you skip, capture what you want": the left alternative
9+
// consumes markdown links so the right alternative only matches bare refs
10+
function linkifyIssueRefs(
11+
line: string,
12+
{ serverUrl, repo }: { serverUrl: string; repo: string }
13+
): string {
14+
return line.replace(/\[.*?\]\(.*?\)|\B#([1-9]\d*)\b/g, (match, issue) =>
15+
// PRs and issues are the same thing on GitHub (to some extent, of course)
16+
// this relies on GitHub redirecting from /issues/1234 to /pull/1234 when necessary
17+
issue ? `[#${issue}](${serverUrl}/${repo}/issues/${issue})` : match
18+
);
19+
}
20+
821
function readEnv() {
922
const GITHUB_SERVER_URL =
1023
process.env.GITHUB_SERVER_URL || "https://github.com";
@@ -123,8 +136,17 @@ const changelogFunctions: ChangelogFunctions = {
123136
users === null ? "" : ` Thanks ${users}!`,
124137
].join("");
125138

126-
return `\n\n-${prefix ? `${prefix} -` : ""} ${firstLine}\n${futureLines
127-
.map((l) => ` ${l}`)
139+
return `\n\n-${prefix ? `${prefix} -` : ""} ${linkifyIssueRefs(firstLine, {
140+
serverUrl: GITHUB_SERVER_URL,
141+
repo: options!.repo,
142+
})}\n${futureLines
143+
.map(
144+
(l) =>
145+
` ${linkifyIssueRefs(l, {
146+
serverUrl: GITHUB_SERVER_URL,
147+
repo: options!.repo,
148+
})}`
149+
)
128150
.join("\n")}`;
129151
},
130152
};

0 commit comments

Comments
 (0)