Skip to content

Commit 92c563e

Browse files
committed
ci(3.0): implement git cliff for highly customizable changelog
1 parent bbef8d2 commit 92c563e

File tree

4 files changed

+147
-25
lines changed

4 files changed

+147
-25
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
cache: 'yarn'
2222

2323
- name: Install dependencies
24-
run: yarn install --frozen-lockfile
24+
run: yarn install --immutable-cache
2525

2626
- name: Check linter
2727
run: yarn check

.github/workflows/release.yml

+34-23
Original file line numberDiff line numberDiff line change
@@ -26,50 +26,61 @@ jobs:
2626
uses: actions/setup-node@v4
2727
with:
2828
node-version: '22.x'
29+
cache: 'yarn'
2930
registry-url: 'https://registry.npmjs.org'
3031

3132
- name: Install dependencies
32-
run: yarn install --frozen-lockfile
33+
run: yarn install --immutable-cache
3334

3435
- name: Build the project
3536
run: yarn build
3637

3738
- name: Bump version
38-
run: yarn version ${{ github.event.inputs.strategy }}
39-
40-
- name: Update changelog
41-
id: changelog
42-
run: |
43-
yarn install
44-
CHANGELOG=$(yarn conventional-changelog -p conventionalcommits -r -u 0)
45-
echo -e "${CHANGELOG}\n\n$(cat CHANGELOG.md)" > CHANGELOG.md
46-
echo "::set-output name=body::$(echo "${CHANGELOG}" | tail -n +3 | sed ':a;N;$!ba;s/\n/%0A/g;s/\r/%0D/g')"
47-
48-
- name: Log changes
39+
id: versioning
4940
run: |
50-
echo "The changelog will be : ${{ steps.changelog.outputs.body }}"
51-
52-
- name: Get version
53-
id: package-version
54-
run: echo "::set-output name=current-version::$(node -p "require('./package.json').version")"
41+
yarn version ${{ github.event.inputs.strategy }}
42+
VERSION=$(node -p "require('./package.json').version")
43+
echo "current-version=$VERSION" >> $GITHUB_OUTPUT
5544
5645
- name: Commit and tag release
5746
run: |
5847
git config user.name github-actions[bot]
5948
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
6049
git add .
61-
git commit -m "chore(release): v${{ steps.package-version.outputs.current-version }}"
62-
git tag v${{ steps.package-version.outputs.current-version }}
50+
git commit -m "chore(release): v${{ steps.versioning.outputs.current-version }}"
51+
git tag v${{ steps.versioning.outputs.current-version }}
6352
git push --follow-tags
6453
54+
- name: Set up git-cliff
55+
uses: kenji-miyake/setup-git-cliff@v2
56+
57+
- name: Run git-cliff to generate changelog
58+
id: changelog
59+
run: |
60+
git-cliff --latest > temp_changelog
61+
cat temp_changelog CHANGELOG.md > temp && mv temp CHANGELOG.md
62+
echo "changelog-body<<EOF" >> $GITHUB_OUTPUT
63+
cat temp_changelog >> $GITHUB_OUTPUT
64+
echo "EOF" >> $GITHUB_OUTPUT
65+
66+
- name: Log changes
67+
run: |
68+
echo "The changelog will be: $(git cliff --latest)"
69+
70+
- name: Commit changelog
71+
run: |
72+
git add CHANGELOG.md
73+
git commit -m "docs: update changelog for v${{ steps.versioning.outputs.current-version }}"
74+
git push
75+
6576
- name: Create GitHub release
6677
uses: softprops/action-gh-release@v2
6778
with:
68-
body: ${{ steps.changelog.outputs.body }}
69-
tag_name: v${{ steps.package-version.outputs.current-version }}
70-
name: v${{ steps.package-version.outputs.current-version }}
79+
body: ${{ steps.changelog.outputs.changelog-body}}
80+
tag_name: v${{ steps.versioning.outputs.current-version }}
81+
name: v${{ steps.versioning.outputs.current-version }}
7182

7283
- name: Publish to NPM
7384
run: npm publish --access public
7485
env:
75-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
86+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

cliff.toml

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
[changelog]
2+
# header = """
3+
# # Changelog\n
4+
# All notable changes to this project will be documented in this file.\n
5+
# """
6+
# https://keats.github.io/tera/docs/#introduction
7+
body = """
8+
{%- macro remote_url() -%}
9+
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
10+
{%- endmacro -%}
11+
12+
{% macro print_commit(commit) -%}
13+
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
14+
{% if commit.breaking %}[**breaking**] {% endif %}\
15+
{{ commit.message | upper_first }} - \
16+
([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
17+
{% endmacro -%}
18+
19+
{% if version %}\
20+
{% if previous.version %}\
21+
## [{{ version | trim_start_matches(pat="v") }}]\
22+
({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
23+
{% else %}\
24+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
25+
{% endif %}\
26+
{% else %}\
27+
## [unreleased]
28+
{% endif %}\
29+
30+
{% for group, commits in commits | group_by(attribute="group") %}
31+
### {{ group | striptags | trim | upper_first }}
32+
{% for commit in commits
33+
| filter(attribute="scope")
34+
| sort(attribute="scope") %}
35+
{{ self::print_commit(commit=commit) }}
36+
{%- endfor %}
37+
{% for commit in commits %}
38+
{%- if not commit.scope -%}
39+
{{ self::print_commit(commit=commit) }}
40+
{% endif -%}
41+
{% endfor -%}
42+
{% endfor -%}
43+
44+
{%- if github -%}
45+
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
46+
## New Contributors ❤️
47+
{% endif %}\
48+
{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
49+
* @{{ contributor.username }} made their first contribution
50+
{%- if contributor.pr_number %} in \
51+
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
52+
{%- endif %}
53+
{%- endfor -%}
54+
{%- endif %}
55+
56+
"""
57+
# template for the changelog footer
58+
footer = """
59+
"""
60+
# remove the leading and trailing s
61+
trim = true
62+
# postprocessors
63+
postprocessors = [
64+
# { pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL
65+
]
66+
# render body even when there are no releases to process
67+
# render_always = true
68+
# output file path
69+
# output = "test.md"
70+
71+
[git]
72+
# parse the commits based on https://www.conventionalcommits.org
73+
conventional_commits = true
74+
# filter out the commits that are not conventional
75+
filter_unconventional = true
76+
# process each line of a commit as an individual commit
77+
split_commits = false
78+
# regex for preprocessing the commit messages
79+
commit_preprocessors = [
80+
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))" },
81+
# Check spelling of the commit with https://github.com/crate-ci/typos
82+
# If the spelling is incorrect, it will be automatically fixed.
83+
# { pattern = '.*', replace_command = 'typos --write-changes -' },
84+
]
85+
# regex for parsing and grouping commits
86+
commit_parsers = [
87+
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
88+
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
89+
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
90+
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
91+
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
92+
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
93+
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
94+
{ message = "^chore\\(release\\): prepare for", skip = true },
95+
{ message = "^chore\\(deps.*\\)", skip = true },
96+
{ message = "^chore\\(pr\\)", skip = true },
97+
{ message = "^chore\\(pull\\)", skip = true },
98+
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
99+
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
100+
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
101+
{ message = ".*", group = "<!-- 10 -->💼 Other" },
102+
]
103+
# protect breaking changes from being skipped due to matching a skipping commit_parser
104+
protect_breaking_commits = false
105+
# filter out the commits that are not matched by commit parsers
106+
filter_commits = false
107+
# regex for matching git tags
108+
# tag_pattern = "v[0-9].*"
109+
# sort the tags topologically
110+
topo_order = false
111+
# sort the commits inside sections by oldest/newest order
112+
sort_commits = "newest"

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
"@types/node": "22.7.7",
5656
"@vitest/coverage-v8": "2.1.3",
5757
"commitlint": "19.5.0",
58-
"conventional-changelog-cli": "5.0.0",
5958
"pino": "^9.5.0",
6059
"typescript": "5.6.3",
6160
"vitest": "^2.1.3",

0 commit comments

Comments
 (0)