Skip to content
This repository was archived by the owner on Mar 25, 2025. It is now read-only.

Commit 4484ad0

Browse files
committed
chore: refactor changelog, commitlint and release script
Rewrite the scripts in python and improve the changelog script to be able to generate changelogs for single tags. This feature is used in the release workflow on GitHub to generate a release changelog.
1 parent 97b430a commit 4484ad0

File tree

10 files changed

+284
-167
lines changed

10 files changed

+284
-167
lines changed

.github/workflows/publish.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ jobs:
2828
- name: Build
2929
run: yarn build:libs
3030

31+
- name: Release changelog
32+
run: ./sbin/changelog.py --skip-header single --tag ${{ github.ref }} > release-CHANGELOG.md
33+
3134
- name: Release
3235
id: release
3336
uses: actions/create-release@v1
@@ -36,6 +39,7 @@ jobs:
3639
with:
3740
tag_name: ${{ github.ref }}
3841
release_name: Release ${{ github.ref }}
42+
body_path: release-CHANGELOG.md
3943
draft: false
4044
prerelease: false
4145

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ yarn-error.log*
1616
!.yarn/versions
1717

1818
.DS_Store
19+
20+
.env

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
"lint:fix": "yarn lint:ts --fix && yarn reformat",
2525
"lint:ts": "eslint --ext \".ts,.tsx\" . --cache",
2626
"lint:css": "stylelint \"./**/*.tsx\"",
27-
"commitlint": "sbin/commitlint.sh",
27+
"commitlint": "sbin/commitlint.py",
2828
"prettier:check": "prettier --check '{packages/*/src/**/*.{js,ts,jsx,tsx},cypress/**/*.{js,ts,jsx,tsx,json}}'",
2929
"test": "jest",
3030
"build": "yarn workspaces foreach --topological run build",
3131
"prettier:fix": "prettier --write '{packages/*/src/**/*.{js,ts,jsx,tsx},cypress/**/*.{js,ts,jsx,tsx,json}}'",
32-
"release": "yarn && yarn lint && yarn test && yarn build && sbin/release.sh",
32+
"release": "yarn && yarn lint && yarn test && yarn build && sbin/release.py",
3333
"dev": "yarn workspaces foreach -i --parallel --exclude practical-react-components-ui-tests run dev",
3434
"cypress:ui-tests": "yarn workspace practical-react-components-ui-tests dev",
3535
"cypress:chrome": "yarn workspace practical-react-components-ui-tests wait-on http-get://localhost:9009 -i 2000 -d 5000 && yarn workspace practical-react-components-ui-tests cypress run --browser chrome --headless",

sbin/changelog.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
import re
5+
import subprocess
6+
import sys
7+
8+
import utils
9+
10+
GITHUB_COMPARE_URL = (
11+
"https://github.com/AxisCommunications/practical-react-components/compare"
12+
)
13+
GITHUB_COMMIT_URL = (
14+
"https://github.com/AxisCommunications/practical-react-components/commit"
15+
)
16+
17+
GROUP_TITLES = {
18+
"feat": "🚀 Features",
19+
"fix": "🐛 Bug fixes",
20+
"refactor": "🧰 Refactoring",
21+
"docs": "📝 Documentation",
22+
"chore": "🚧 Maintenance",
23+
"ci": "🚦 Continous integration",
24+
}
25+
26+
27+
def changelog_part(commitish_to: str, commitish_from: str, version: str):
28+
date = utils.cmd(["git", "log", "-1", "--format=%ci", commitish_to])
29+
30+
commit_range = (
31+
f"{commitish_from}..HEAD"
32+
if commitish_to == "HEAD"
33+
else f"{commitish_from}..{commitish_to}~"
34+
)
35+
36+
commits = utils.cmd(
37+
["git", "log", "--no-merges", "--date-order", "--format=%H%x09%s", commit_range]
38+
)
39+
40+
messages = {}
41+
42+
for commit in commits.split("\n"):
43+
sha, msg = commit.split(maxsplit=1)
44+
shortsha = utils.cmd(["git", "log", "-1", "--format=%h", sha])
45+
46+
try:
47+
data = utils.conventional_commit_parse(msg)
48+
messages.setdefault(data["type"], []).append(
49+
{**data, "sha": sha, "shortsha": shortsha}
50+
)
51+
except:
52+
# No conventional commit
53+
pass
54+
55+
content = [
56+
f"## [{version}]({GITHUB_COMPARE_URL}/{commitish_from}...{version}) ({date})"
57+
]
58+
59+
for group in GROUP_TITLES.keys():
60+
if group not in messages:
61+
continue
62+
63+
content.append(f"\n### {GROUP_TITLES[group]}\n")
64+
65+
for data in messages[group]:
66+
67+
prefix = (
68+
f' - **{data["scope"]}**: ' if data["scope"] is not None else " - "
69+
)
70+
postfix = f' ([{data["shortsha"]}]({GITHUB_COMMIT_URL}/{data["sha"]}))'
71+
72+
if data["breaking"]:
73+
content.append(f'{prefix}**BREAKING** {data["description"]}{postfix}')
74+
else:
75+
content.append(f'{prefix} {data["description"]}{postfix}')
76+
77+
return "\n".join(content)
78+
79+
80+
HEADER = """
81+
# Changelog
82+
83+
All notable changes to this project will be documented in this file.
84+
85+
"""
86+
87+
88+
if __name__ == "__main__":
89+
parser = argparse.ArgumentParser(
90+
description="Generate or update a CHANGELOG.md file."
91+
)
92+
93+
parser.add_argument(
94+
"-s",
95+
"--skip-header",
96+
action="store_true",
97+
help="Don't include a changelog header",
98+
)
99+
100+
subparsers = parser.add_subparsers(dest="type")
101+
102+
single = subparsers.add_parser(
103+
"single", description="Changelog for a single release"
104+
)
105+
single.add_argument("-tag", "--tag", type=str, metavar="TAG")
106+
107+
full = subparsers.add_parser("full")
108+
full.add_argument(
109+
"-release",
110+
"--release",
111+
type=str,
112+
metavar="RELEASE",
113+
help="New relase, includes full changelog with a new entry for things not tagged",
114+
)
115+
116+
args = parser.parse_args()
117+
118+
tags = utils.cmd(
119+
[
120+
"git",
121+
"-c",
122+
"versionsort.suffix=-alpha",
123+
"tag",
124+
"--list",
125+
"--sort=-version:refname",
126+
"--merged",
127+
"HEAD",
128+
]
129+
).split()
130+
if args.type == "full" and args.release is not None:
131+
tags.insert(0, "HEAD")
132+
133+
content = [HEADER] if not args.skip_header else []
134+
135+
for commitish_to, commitish_from in zip(tags[:-1], tags[1:]):
136+
if args.type == "single" and args.tag != commitish_to:
137+
continue
138+
139+
content.append(
140+
changelog_part(
141+
commitish_to,
142+
commitish_from,
143+
args.release if commitish_to == "HEAD" else commitish_to,
144+
)
145+
)
146+
content.append("")
147+
148+
sys.stdout.write("\n".join(content))
149+
sys.stdout.close()

sbin/changelog.sh

Lines changed: 0 additions & 107 deletions
This file was deleted.

sbin/commitlint.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
import sys
5+
import subprocess
6+
7+
import utils
8+
9+
10+
parser = argparse.ArgumentParser(
11+
description="""
12+
If no range is given, HEAD~..HEAD is used (so only the latest commit
13+
will be checked).
14+
15+
Note that the a range fa56eb..HEAD does not include the fa56eb commit
16+
(to start from e.g. fa56eb, you would write fa56eb~..HEAD to use the parent
17+
as starting point).
18+
19+
Check if message conforms to a conventional commit message, see
20+
https://www.conventionalcommits.org/en/v1.0.0/#specification
21+
"""
22+
)
23+
parser.add_argument(
24+
"range", metavar="RANGE", type=str, nargs="?", default="HEAD~..HEAD"
25+
)
26+
args = parser.parse_args()
27+
28+
29+
for sha in utils.cmd(["git", "rev-list", args.range]).split():
30+
message = utils.cmd(["git", "log", "-1", "--format=%s", sha])
31+
try:
32+
data = utils.conventional_commit_parse(message)
33+
print("ok:", message)
34+
except:
35+
print("ERROR:", message)
36+
sys.exit(1)

sbin/commitlint.sh

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)