Skip to content

Commit cc646d9

Browse files
authored
chore: custom changesets plugin (#3694)
This PR implements a custom changesets changelog plugin for GitHub that improves the changelog generation process. The changes include: Custom changelog plugin: Created @spectrum-tools/changesets-changelog-github plugin to replace the standard @changesets/changelog-github Enhanced formatting: Added emoji prefix (📝) and improved spacing in changelog entries Better commit handling: Simplified commit link generation and improved test coverage CI integration: Added distinct GitHub token environment variable for plugin's access to user's GitHub username and reading pull request titles. The plugin provides better control over changelog formatting and ensures consistent output across releases.
1 parent 7dc1f48 commit cc646d9

File tree

7 files changed

+396
-6
lines changed

7 files changed

+396
-6
lines changed

.changeset/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
33
"changelog": [
4-
"@changesets/changelog-github",
4+
"@spectrum-tools/changesets-changelog-github",
55
{
66
"repo": "adobe/spectrum-css"
77
}

.github/workflows/development.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ jobs:
183183
## --- Run plugins test suites --- ##
184184
- name: Run plugin tests
185185
run: yarn test:plugins
186+
env:
187+
GITHUB_TOKEN: ${{ secrets.GH_ACCESS_FOR_CHANGESETS }}
186188

187189
# -------------------------------------------------------------
188190
# RUN VISUAL REGRESSION TESTS --- #
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { getInfo, getInfoFromPullRequest } from "@changesets/get-github-info";
2+
import { config } from "dotenv";
3+
4+
config();
5+
6+
/**
7+
* @type {import("@changesets/types").ChangelogFunctions}
8+
*/
9+
const changelogFunctions = {
10+
getDependencyReleaseLine: async (
11+
changesets,
12+
dependenciesUpdated,
13+
options,
14+
) => {
15+
if (!options.repo) {
16+
throw new Error(
17+
"Please provide a repo to this changelog generator like this:\n\"changelog\": [\"@changesets/changelog-github\", { \"repo\": \"org/repo\" }]",
18+
);
19+
}
20+
if (dependenciesUpdated.length === 0) return "";
21+
22+
const changesetLink = `Updated dependencies [${(
23+
await Promise.all(
24+
changesets.map(async (cs) => {
25+
if (cs.commit) {
26+
let { links } = await getInfo({
27+
repo: options.repo,
28+
commit: cs.commit,
29+
});
30+
return links.commit;
31+
}
32+
}),
33+
)
34+
)
35+
.filter((_) => _)
36+
.join(", ")}]:`;
37+
38+
const updatedDepenenciesList = dependenciesUpdated.map(
39+
(dependency) => ` - ${dependency.name}@${dependency.newVersion}`,
40+
);
41+
42+
return [changesetLink, ...updatedDepenenciesList].join("\n");
43+
},
44+
getReleaseLine: async (changeset, type, options) => {
45+
if (!options || !options.repo) {
46+
throw new Error(
47+
"Please provide a repo to this changelog generator like this:\n\"changelog\": [\"@changesets/changelog-github\", { \"repo\": \"org/repo\" }]",
48+
);
49+
}
50+
51+
/** @type {number | undefined} */
52+
let prFromSummary;
53+
/** @type {string | undefined} */
54+
let commitFromSummary;
55+
/** @type {string[]} */
56+
let usersFromSummary = [];
57+
58+
const replacedChangelog = changeset.summary
59+
.replace(/^\s*(?:pr|pull|pull\s+request):\s*#?(\d+)/im, (_, pr) => {
60+
let num = Number(pr);
61+
if (!isNaN(num)) prFromSummary = num;
62+
return "";
63+
})
64+
.replace(/^\s*commit:\s*([^\s]+)/im, (_, commit) => {
65+
commitFromSummary = commit;
66+
return "";
67+
})
68+
.replace(/^\s*(?:author|user):\s*@?([^\s]+)/gim, (_, user) => {
69+
usersFromSummary.push(user);
70+
return "";
71+
})
72+
.trim();
73+
74+
const changelogLines = replacedChangelog
75+
.split("\n")
76+
.map((l) => l.trimRight());
77+
78+
const links = await (async () => {
79+
if (prFromSummary !== undefined) {
80+
let { links } = await getInfoFromPullRequest({
81+
repo: options.repo,
82+
pull: prFromSummary,
83+
});
84+
if (commitFromSummary) {
85+
links.commit = `[\`${commitFromSummary.slice(0, 7)}\`](https://github.com/${options.repo}/commit/${commitFromSummary})`;
86+
}
87+
return links;
88+
}
89+
const commitToFetchFrom = commitFromSummary || changeset.commit;
90+
if (commitToFetchFrom) {
91+
let { links } = await getInfo({
92+
repo: options.repo,
93+
commit: commitToFetchFrom,
94+
});
95+
return links;
96+
}
97+
return { commit: null, pull: null, user: null };
98+
})();
99+
100+
const users = usersFromSummary.length
101+
? usersFromSummary
102+
.map(
103+
(userFromSummary) =>
104+
`[@${userFromSummary}](https://github.com/${userFromSummary})`,
105+
)
106+
.join(", ")
107+
: links.user;
108+
109+
const prefix = [
110+
links.pull === null ? "" : ` ${links.pull}`,
111+
links.commit === null ? "" : ` ${links.commit}`,
112+
users === null ? "" : ` Thanks ${users}!`,
113+
].join("");
114+
115+
return `${prefix ? `\n\n📝 ${prefix}` : ""}\n\n${changelogLines.join("\n")}\n`;
116+
},
117+
};
118+
119+
export default changelogFunctions;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "@spectrum-tools/changesets-changelog-github",
3+
"version": "0.0.0",
4+
"description": "A changelog entry generator for GitHub that links to commits, PRs and users",
5+
"license": "Apache-2.0",
6+
"author": "Adobe",
7+
"homepage": "https://opensource.adobe.com/spectrum-css/",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/adobe/spectrum-css.git",
11+
"directory": "plugins/changesets-changelog-github"
12+
},
13+
"bugs": {
14+
"url": "https://github.com/adobe/spectrum-css/issues"
15+
},
16+
"type": "module",
17+
"module": "index.js",
18+
"dependencies": {
19+
"@changesets/get-github-info": "^0.6.0",
20+
"@changesets/types": "^6.1.0",
21+
"dotenv": "^16.5.0"
22+
},
23+
"devDependencies": {
24+
"@changesets/parse": "^0.4.1",
25+
"ava": "^6.4.0",
26+
"sinon": "^20.0.0"
27+
},
28+
"keywords": [
29+
"design-system",
30+
"spectrum",
31+
"spectrum-css",
32+
"adobe",
33+
"adobe-spectrum"
34+
]
35+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
3+
"name": "changesets-changelog-github",
4+
"tags": ["tooling", "changesets", "plugin"],
5+
"targets": {
6+
"format": { "defaultConfiguration": "plugins" },
7+
"lint": { "defaultConfiguration": "plugins" },
8+
"test": { "defaultConfiguration": "plugins" }
9+
}
10+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import parse from "@changesets/parse";
2+
import test from "ava";
3+
import sinon from "sinon";
4+
import changelogFunctions from "./index.js";
5+
6+
/** @type {sinon.SinonSandbox} */
7+
let sandbox = sinon.createSandbox();
8+
9+
const data = {
10+
commit: "a085003d4c8ca284c116668d7217fb747802ed85",
11+
user: "Andarist",
12+
pull: 1613,
13+
repo: "emotion-js/emotion",
14+
};
15+
16+
test.beforeEach((t) => {
17+
sandbox.stub({
18+
getInfo: () => ({
19+
pull: data.pull,
20+
user: data.user,
21+
links: {
22+
user: `[@${data.user}](https://github.com/${data.user})`,
23+
pull: `[#${data.pull}](https://github.com/${data.repo}/pull/${data.pull})`,
24+
commit: `[\`${data.commit.slice(0, 7)}\`](https://github.com/${data.repo}/commit/${data.commit})`,
25+
},
26+
})
27+
}, "getInfo");
28+
sandbox.stub({
29+
getInfoFromPullRequest: () => ({
30+
commit: data.commit,
31+
user: data.user,
32+
links: {
33+
user: `[@${data.user}](https://github.com/${data.user})`,
34+
pull: `[#${data.pull}](https://github.com/${data.repo}/pull/${data.pull})`,
35+
commit: `[\`${data.commit.slice(0, 7)}\`](https://github.com/${data.repo}/commit/${data.commit})`,
36+
},
37+
}),
38+
}, "getInfoFromPullRequest");
39+
});
40+
41+
test.afterEach.always(() => {
42+
sandbox.restore();
43+
});
44+
45+
/**
46+
*
47+
* @param {string} content
48+
* @param {string|undefined} commit
49+
* @returns
50+
*/
51+
const getChangeset = (content, commit) => {
52+
return [
53+
{
54+
...parse(
55+
`---
56+
pkg: "minor"
57+
---
58+
59+
something
60+
${content}
61+
`
62+
),
63+
id: "some-id",
64+
commit,
65+
},
66+
"minor",
67+
{ repo: data.repo },
68+
];
69+
};
70+
71+
[data.commit, "wrongcommit", undefined].forEach((commitFromChangeset) => {
72+
["pr", "pull request", "pull"].forEach((keyword) => {
73+
test(`with commit from changeset of ${commitFromChangeset} override pr with ${keyword} keyword with #`, async (t) => {
74+
t.is(
75+
await changelogFunctions.getReleaseLine(
76+
...getChangeset(
77+
`${keyword}: #${data.pull}`,
78+
commitFromChangeset
79+
)
80+
),
81+
"\n\n📝 [#1613](https://github.com/emotion-js/emotion/pull/1613) [`a085003`](https://github.com/emotion-js/emotion/commit/a085003d4c8ca284c116668d7217fb747802ed85) Thanks [@Andarist](https://github.com/Andarist)!\n\nsomething\n"
82+
);
83+
});
84+
85+
test(`with commit from changeset of ${commitFromChangeset} override pr with pr ${keyword} without #`, async (t) => {
86+
t.is(
87+
await changelogFunctions.getReleaseLine(
88+
...getChangeset(
89+
`pr: ${data.pull}`,
90+
commitFromChangeset
91+
)
92+
),
93+
"\n\n📝 [#1613](https://github.com/emotion-js/emotion/pull/1613) [`a085003`](https://github.com/emotion-js/emotion/commit/a085003d4c8ca284c116668d7217fb747802ed85) Thanks [@Andarist](https://github.com/Andarist)!\n\nsomething\n"
94+
);
95+
});
96+
});
97+
98+
test(`override commit ${commitFromChangeset} with commit keyword`, async (t) => {
99+
t.is(
100+
await changelogFunctions.getReleaseLine(
101+
...getChangeset(`commit: ${data.commit}`, commitFromChangeset)
102+
),
103+
"\n\n📝 [#1613](https://github.com/emotion-js/emotion/pull/1613) [`a085003`](https://github.com/emotion-js/emotion/commit/a085003d4c8ca284c116668d7217fb747802ed85) Thanks [@Andarist](https://github.com/Andarist)!\n\nsomething\n"
104+
);
105+
});
106+
});
107+
108+
["author", "user"].forEach((keyword) => {
109+
test(`override author with ${keyword} keyword with @`, async (t) => {
110+
t.is(
111+
await changelogFunctions.getReleaseLine(
112+
...getChangeset(
113+
`${keyword}: @other`,
114+
data.commit
115+
)
116+
),
117+
"\n\n📝 [#1613](https://github.com/emotion-js/emotion/pull/1613) [`a085003`](https://github.com/emotion-js/emotion/commit/a085003d4c8ca284c116668d7217fb747802ed85) Thanks [@other](https://github.com/other)!\n\nsomething\n"
118+
);
119+
});
120+
121+
test(`override author with ${keyword} keyword without @`, async (t) => {
122+
t.is(
123+
await changelogFunctions.getReleaseLine(
124+
...getChangeset(
125+
`${keyword}: other`,
126+
data.commit
127+
)
128+
),
129+
"\n\n📝 [#1613](https://github.com/emotion-js/emotion/pull/1613) [`a085003`](https://github.com/emotion-js/emotion/commit/a085003d4c8ca284c116668d7217fb747802ed85) Thanks [@other](https://github.com/other)!\n\nsomething\n"
130+
);
131+
});
132+
});
133+
134+
test("with multiple authors", async (t) => {
135+
t.is(
136+
await changelogFunctions.getReleaseLine(
137+
...getChangeset(
138+
["author: @Andarist", "author: @mitchellhamilton"].join("\n"),
139+
data.commit
140+
)
141+
),
142+
`\n\n📝 [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003d4c8ca284c116668d7217fb747802ed85) Thanks [@Andarist](https://github.com/Andarist), [@mitchellhamilton](https://github.com/mitchellhamilton)!\n\nsomething\n`
143+
);
144+
});

0 commit comments

Comments
 (0)