Skip to content

Commit 317b933

Browse files
feat: add automatic release notes generation (#3183)
1 parent add35fe commit 317b933

File tree

4 files changed

+228
-3
lines changed

4 files changed

+228
-3
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,11 +301,10 @@ jobs:
301301
# refresh token before Saturday, May 25, 2024
302302
NPM_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
303303
NODE_AUTH_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
304-
- name: Create Release Pull Request
305-
# Create release notes
306-
uses: changesets/action@v1
304+
- name: Create Release Notes
307305
env:
308306
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
307+
run: node releaseNotes.mjs
309308
- name: Create PR to v2-develop branch
310309
env:
311310
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,22 @@
6060
"@commitlint/cli": "^17.0.0",
6161
"@commitlint/config-conventional": "^17.0.0",
6262
"@frsource/frs-replace": "^4.1.1",
63+
"@octokit/core": "^6.1.2",
6364
"@storefront-ui/typescript-config": "workspace:*",
6465
"@vue-storefront/prettier-config": "^2.0.0-rc.1",
6566
"commitizen": "^4.2.5",
6667
"concurrently": "^8.2.2",
6768
"husky": "^9.0.11",
6869
"hygen": "^6.2.8",
70+
"mdast-util-to-string": "^4.0.0",
6971
"prettier": "latest",
72+
"remark-parse": "^11.0.0",
73+
"remark-stringify": "^11.0.0",
7074
"rimraf": "^5.0.0",
7175
"tailwindcss": "^3.4.3",
7276
"turbo": "latest",
7377
"typescript": "^5.4.5",
78+
"unified": "^11.0.4",
7479
"wait-on": "^7.0.1"
7580
},
7681
"engines": {

releaseNotes.mjs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Octokit } from '@octokit/core';
2+
import { unified } from 'unified';
3+
import remarkParse from 'remark-parse';
4+
import remarkStringify from 'remark-stringify';
5+
import { toString } from 'mdast-util-to-string';
6+
import { readFileSync } from 'node:fs';
7+
import { join } from 'node:path';
8+
9+
const OWNER = 'vuestorefront';
10+
const REPO = 'storefront-ui';
11+
const octokit = new Octokit({ auth: process.env['GITHUB_TOKEN'] });
12+
13+
const BumpLevels = {
14+
dep: 0,
15+
patch: 1,
16+
minor: 2,
17+
major: 3,
18+
};
19+
20+
// Implementation copied from changeset and its generation release notes https://github.com/changesets/action/blob/main/src/utils.ts#L37
21+
function getChangelogEntry(changelog, version) {
22+
let ast = unified().use(remarkParse).parse(changelog);
23+
24+
let highestLevel = BumpLevels.dep;
25+
26+
let nodes = ast.children;
27+
let headingStartInfo;
28+
let endIndex;
29+
30+
for (let i = 0; i < nodes.length; i++) {
31+
let node = nodes[i];
32+
if (node.type === 'heading') {
33+
let stringified = toString(node);
34+
let match = stringified.toLowerCase().match(/(major|minor|patch)/);
35+
if (match !== null) {
36+
let level = BumpLevels[match[0]];
37+
highestLevel = Math.max(level, highestLevel);
38+
}
39+
if (headingStartInfo === undefined && stringified === version) {
40+
headingStartInfo = {
41+
index: i,
42+
depth: node.depth,
43+
};
44+
continue;
45+
}
46+
if (endIndex === undefined && headingStartInfo !== undefined && headingStartInfo.depth === node.depth) {
47+
endIndex = i;
48+
break;
49+
}
50+
}
51+
}
52+
if (headingStartInfo) {
53+
ast.children = ast.children.slice(headingStartInfo.index + 1, endIndex);
54+
}
55+
return {
56+
content: unified().use(remarkStringify).stringify(ast),
57+
highestLevel: highestLevel,
58+
};
59+
}
60+
61+
const data = await octokit.request(`GET /repos/${OWNER}/${REPO}/commits`, {
62+
owner: OWNER,
63+
repo: REPO,
64+
sha: 'v2',
65+
headers: {
66+
'X-GitHub-Api-Version': '2022-11-28',
67+
},
68+
});
69+
70+
const commitSha = data.data[0].sha;
71+
const commitBody = await octokit.request(`GET /repos/${OWNER}/${REPO}/commits/${commitSha}`, {
72+
owner: OWNER,
73+
repo: REPO,
74+
ref: commitSha,
75+
headers: {
76+
'X-GitHub-Api-Version': '2022-11-28',
77+
},
78+
});
79+
80+
const allChangelogFiles = commitBody.data.files.filter((file) => file.filename.endsWith('CHANGELOG.md'));
81+
82+
if (!allChangelogFiles.length) {
83+
console.log('No CHANGELOG file has been found');
84+
} else {
85+
await Promise.all(
86+
allChangelogFiles.map(async (changelogFile) => {
87+
const changelogFilePath = changelogFile.filename;
88+
const updatedPackageDir = changelogFilePath.replace('/CHANGELOG.md', '');
89+
const packageJsonPath = join(updatedPackageDir, 'package.json');
90+
91+
const changelogContent = readFileSync(changelogFilePath, 'utf-8');
92+
const packageJsonContent = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
93+
94+
const pkgName = packageJsonContent.name;
95+
const pkgVersion = packageJsonContent.version;
96+
const tagName = `${pkgName}@${pkgVersion}`;
97+
98+
let changelogEntry = getChangelogEntry(changelogContent, pkgVersion);
99+
100+
try {
101+
await octokit.request(`POST /repos/${OWNER}/${REPO}/releases`, {
102+
owner: OWNER,
103+
repo: REPO,
104+
tag_name: tagName,
105+
target_commitish: commitSha,
106+
name: tagName,
107+
body: changelogEntry.content,
108+
draft: false,
109+
prerelease: pkgVersion.includes('-'),
110+
generate_release_notes: false,
111+
headers: {
112+
'X-GitHub-Api-Version': '2022-11-28',
113+
},
114+
});
115+
} catch (e) {
116+
console.log(e);
117+
}
118+
119+
console.log(`Release notes for ${tagName} has been created 🎉`);
120+
}),
121+
);
122+
}

yarn.lock

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3639,6 +3639,86 @@ __metadata:
36393639
languageName: node
36403640
linkType: hard
36413641

3642+
"@octokit/auth-token@npm:^5.0.0":
3643+
version: 5.1.1
3644+
resolution: "@octokit/auth-token@npm:5.1.1"
3645+
checksum: b39516dda44aeced0326227c53aade621effe1d59c4b0f48ebe2b9fd32b5156e02705bcb2fb1bf48b11f26cc6aff1a0683c32c3d5424e0118dae6596e431d489
3646+
languageName: node
3647+
linkType: hard
3648+
3649+
"@octokit/core@npm:^6.1.2":
3650+
version: 6.1.2
3651+
resolution: "@octokit/core@npm:6.1.2"
3652+
dependencies:
3653+
"@octokit/auth-token": ^5.0.0
3654+
"@octokit/graphql": ^8.0.0
3655+
"@octokit/request": ^9.0.0
3656+
"@octokit/request-error": ^6.0.1
3657+
"@octokit/types": ^13.0.0
3658+
before-after-hook: ^3.0.2
3659+
universal-user-agent: ^7.0.0
3660+
checksum: e794fb11b3942f55033f4cf6c0914953fd974587309498e8709c428660fa5c098334d83af5e41457dbe67d92d70a8b559c6cc00457d6c95290fa6c9e1d4bfc42
3661+
languageName: node
3662+
linkType: hard
3663+
3664+
"@octokit/endpoint@npm:^10.0.0":
3665+
version: 10.1.1
3666+
resolution: "@octokit/endpoint@npm:10.1.1"
3667+
dependencies:
3668+
"@octokit/types": ^13.0.0
3669+
universal-user-agent: ^7.0.2
3670+
checksum: fde158f40dc9a88e92a8ac1d347a54599aa5715ec24045be9cb8ff8decb3c17b63c91eca1bab12dfe0e0cd37433127dd05cd05db14a719dca749bc56093aa915
3671+
languageName: node
3672+
linkType: hard
3673+
3674+
"@octokit/graphql@npm:^8.0.0":
3675+
version: 8.1.1
3676+
resolution: "@octokit/graphql@npm:8.1.1"
3677+
dependencies:
3678+
"@octokit/request": ^9.0.0
3679+
"@octokit/types": ^13.0.0
3680+
universal-user-agent: ^7.0.0
3681+
checksum: 07239666b0ca38a7d8c581570b544ee9fd1a2616c8dd436af31879662b3345c44ed52e3d7b311840a1c5772a23f02caf7585aca56f36e50f38f0207a87577a9c
3682+
languageName: node
3683+
linkType: hard
3684+
3685+
"@octokit/openapi-types@npm:^22.2.0":
3686+
version: 22.2.0
3687+
resolution: "@octokit/openapi-types@npm:22.2.0"
3688+
checksum: eca41feac2b83298e0d95e253ac1c5b6d65155ac57f65c5fd8d4a485d9728922d85ff4bee0e815a1f3a5421311db092bdb6da9d6104a1b1843d8b274bcad9630
3689+
languageName: node
3690+
linkType: hard
3691+
3692+
"@octokit/request-error@npm:^6.0.1":
3693+
version: 6.1.1
3694+
resolution: "@octokit/request-error@npm:6.1.1"
3695+
dependencies:
3696+
"@octokit/types": ^13.0.0
3697+
checksum: cae7bc4078629a02edcf35977f496a4b943e730165f6d7828795073f99a1d884ac67343b02eff69e553a5057765e466d70ddd9d266787f505aa29018858ab06d
3698+
languageName: node
3699+
linkType: hard
3700+
3701+
"@octokit/request@npm:^9.0.0":
3702+
version: 9.1.1
3703+
resolution: "@octokit/request@npm:9.1.1"
3704+
dependencies:
3705+
"@octokit/endpoint": ^10.0.0
3706+
"@octokit/request-error": ^6.0.1
3707+
"@octokit/types": ^13.1.0
3708+
universal-user-agent: ^7.0.2
3709+
checksum: 0c41654911c217eb2892ce6c9c273cc2139e5510b025c71e72e1528f0d8bad2a9e578e5b305595599f2e1cb630c1812cd7d9e4f6d16a63007a7d1745f1c682ce
3710+
languageName: node
3711+
linkType: hard
3712+
3713+
"@octokit/types@npm:^13.0.0, @octokit/types@npm:^13.1.0":
3714+
version: 13.5.0
3715+
resolution: "@octokit/types@npm:13.5.0"
3716+
dependencies:
3717+
"@octokit/openapi-types": ^22.2.0
3718+
checksum: 8e92f2b145b3c28a35312f93714245824a7b6b7353caa88edfdc85fc2ed4108321ed0c3988001ea53449fbb212febe0e8e9582744e85c3574dabe9d0441af5a0
3719+
languageName: node
3720+
linkType: hard
3721+
36423722
"@open-draft/deferred-promise@npm:^2.2.0":
36433723
version: 2.2.0
36443724
resolution: "@open-draft/deferred-promise@npm:2.2.0"
@@ -4705,17 +4785,22 @@ __metadata:
47054785
"@commitlint/cli": ^17.0.0
47064786
"@commitlint/config-conventional": ^17.0.0
47074787
"@frsource/frs-replace": ^4.1.1
4788+
"@octokit/core": ^6.1.2
47084789
"@storefront-ui/typescript-config": "workspace:*"
47094790
"@vue-storefront/prettier-config": ^2.0.0-rc.1
47104791
commitizen: ^4.2.5
47114792
concurrently: ^8.2.2
47124793
husky: ^9.0.11
47134794
hygen: ^6.2.8
4795+
mdast-util-to-string: ^4.0.0
47144796
prettier: latest
4797+
remark-parse: ^11.0.0
4798+
remark-stringify: ^11.0.0
47154799
rimraf: ^5.0.0
47164800
tailwindcss: ^3.4.3
47174801
turbo: latest
47184802
typescript: ^5.4.5
4803+
unified: ^11.0.4
47194804
wait-on: ^7.0.1
47204805
languageName: unknown
47214806
linkType: soft
@@ -7683,6 +7768,13 @@ __metadata:
76837768
languageName: node
76847769
linkType: hard
76857770

7771+
"before-after-hook@npm:^3.0.2":
7772+
version: 3.0.2
7773+
resolution: "before-after-hook@npm:3.0.2"
7774+
checksum: 5f76a9d31909f7f1f7125b7e017ff018799308f5c1fc5a5bfeba9986149da77e6a5cdde0d151671cf374a7fa6452533237bb1de62dfd6c235c20e7c61cc9569d
7775+
languageName: node
7776+
linkType: hard
7777+
76867778
"better-path-resolve@npm:1.0.0":
76877779
version: 1.0.0
76887780
resolution: "better-path-resolve@npm:1.0.0"
@@ -23148,6 +23240,13 @@ __metadata:
2314823240
languageName: node
2314923241
linkType: hard
2315023242

23243+
"universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2":
23244+
version: 7.0.2
23245+
resolution: "universal-user-agent@npm:7.0.2"
23246+
checksum: 3f02cb6de0bb9fbaf379566bd0320d8e46af6e4358a2e88fce7e70687ed7b48b37f479d728bb22f4204a518e363f3038ac4841c033af1ee2253f6428a6c67e53
23247+
languageName: node
23248+
linkType: hard
23249+
2315123250
"universalify@npm:^0.1.0, universalify@npm:^0.1.2":
2315223251
version: 0.1.2
2315323252
resolution: "universalify@npm:0.1.2"

0 commit comments

Comments
 (0)