Skip to content

Commit b682b63

Browse files
committed
feat(changelog): integrate git-cliff for automated changelog generation
1 parent 381834c commit b682b63

3 files changed

Lines changed: 337 additions & 32 deletions

File tree

CHANGELOG.md

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,78 @@
1-
# Change Log
1+
# Changelog
2+
23
All notable changes to this project will be documented in this file.
34

5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
48
#### 1.x Releases
59
- `1.2.x` Releases - [1.2.0](#120)
610
- `1.1.x` Releases - [1.1.0](#110)
7-
- `1.0.x` Releases - [1.0.0](#100) | [1.0.1](#101)
11+
- `1.0.x` Releases - [1.0.1](#101) | [1.0.0](#100)
12+
13+
## [Unreleased]
14+
15+
### Features
16+
- Auto generation
817

9-
## [1.1.0](https://github.com/space-code/validator/releases/tag/1.2.0)
10-
Released on 2025-11-13.
1118

12-
#### Updated
13-
- Update the Swift version to 6.2.
14-
- Added in Pull Request [#18](https://github.com/space-code/validator/pull/18).
19+
## [1.2.0](https://github.com/space-code/validator/releases/tag/1.2.0)
20+
21+
Released on 2025-11-14. All issues associated with this milestone can be found using this [filter](https://github.com/space-code/validator/milestones?state=closed&q=1.2.0).
22+
23+
### Uncategorized Changes
24+
- Add support for Swift 6.2
25+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#18](https://github.com/space-code/validator/pull/18).
26+
27+
### New Contributors
28+
* @ns-vasilev made their first contributionin [#18](https://github.com/space-code/validator/pull/18)
1529

1630
## [1.1.0](https://github.com/space-code/validator/releases/tag/1.1.0)
17-
Released on 2024-12-24.
1831

19-
#### Added
20-
- Add support for the Swift version to 6.0.
21-
- Added in Pull Request [#13](https://github.com/space-code/validator/pull/13).
32+
Released on 2024-12-24. All issues associated with this milestone can be found using this [filter](https://github.com/space-code/validator/milestones?state=closed&q=1.1.0).
33+
34+
### Uncategorized Changes
35+
- Update `README.md`
36+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#17](https://github.com/space-code/validator/pull/17).
37+
- Update gem dependencies
38+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#15](https://github.com/space-code/validator/pull/15).
39+
- Update `CHANGELOG.md`
40+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#14](https://github.com/space-code/validator/pull/14).
41+
- Increase the `Swift` version to 6.0
42+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#13](https://github.com/space-code/validator/pull/13).
2243

23-
#### Updated
24-
- Update gem dependencies.
25-
- Updated in Pull Request [#15](https://github.com/space-code/validator/pull/15).
44+
### New Contributors
45+
* @ns-vasilev made their first contributionin [#17](https://github.com/space-code/validator/pull/17)
2646

2747
## [1.0.1](https://github.com/space-code/validator/releases/tag/1.0.1)
28-
Released on 2024-01-10.
29-
30-
#### Added
31-
- Support `visionOS`
32-
- Added in Pull Request [#10](https://github.com/space-code/validator/pull/10).
33-
- Integrate `danger`
34-
- Added in Pull Request [#9](https://github.com/space-code/validator/pull/9).
35-
- Add an issue template & a pull request template
36-
- Added in Pull Request [#8](https://github.com/space-code/validator/pull/8).
37-
- Update GitHub Action Configuration
38-
- Added in Pull Request [#7](https://github.com/space-code/validator/pull/7).
39-
- Add `Swift Compatibility` & `Platform Compatibility` badges
40-
- Added in Pull Request [#6](https://github.com/space-code/validator/pull/6).
48+
49+
Released on 2024-01-10. All issues associated with this milestone can be found using this [filter](https://github.com/space-code/validator/milestones?state=closed&q=1.0.1).
50+
51+
### Uncategorized Changes
52+
- Update `CHANGELOG.md`
53+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#12](https://github.com/space-code/validator/pull/12).
54+
55+
### New Contributors
56+
* @ns-vasilev made their first contributionin [#12](https://github.com/space-code/validator/pull/12)
4157

4258
## [1.0.0](https://github.com/space-code/validator/releases/tag/1.0.0)
43-
Released on 2023-09-28.
4459

45-
#### Added
46-
- Initial release of Validator.
47-
- Added by [Nikita Vasilev](https://github.com/nik3212).
60+
Released on 2023-10-09. All issues associated with this milestone can be found using this [filter](https://github.com/space-code/validator/milestones?state=closed&q=1.0.0).
61+
62+
### Uncategorized Changes
63+
- Update `CHANGELOG.md`
64+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#4](https://github.com/space-code/validator/pull/4).
65+
- Update `CHANGELOG.md`
66+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#3](https://github.com/space-code/validator/pull/3).
67+
- Implement `Validator` package
68+
- Contributed by [ns-vasilev](https://github.com/ns-vasilev) in Pull Request [#1](https://github.com/space-code/validator/pull/1).
69+
70+
### New Contributors
71+
* @ns-vasilev made their first contributionin [#4](https://github.com/space-code/validator/pull/4)
72+
73+
[unreleased]: https://github.com/space-code/validator/compare/1.2.0..HEAD
74+
[1.2.0]: https://github.com/space-code/validator/compare/1.1.0..1.2.0
75+
[1.1.0]: https://github.com/space-code/validator/compare/1.0.1..1.1.0
76+
[1.0.1]: https://github.com/space-code/validator/compare/1.0.0..1.0.1
77+
78+
<!-- generated by git-cliff -->

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ lint:
1616
fmt:
1717
mint run swiftformat Sources Tests
1818

19-
.PHONY: all bootstrap hook mint lint fmt
19+
changelog:
20+
git cliff --config cliff.toml --output CHANGELOG.md
21+
22+
.PHONY: all bootstrap hook mint lint fmt changelog

cliff.toml

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
# git-cliff ~ configuration file
2+
# https://git-cliff.org/docs/configuration
3+
4+
[remote.github]
5+
owner = "space-code"
6+
repo = "validator"
7+
# token = ""
8+
9+
[changelog]
10+
header = """
11+
# Changelog
12+
13+
All notable changes to this project will be documented in this file.
14+
15+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
16+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
17+
18+
{#- LOGIC: Generate table of contents - group versions by major version -#}
19+
{%- set_global major_versions = [] -%}
20+
{%- for release in releases -%}
21+
{%- if release.version -%}
22+
{%- set version_clean = release.version | trim_start_matches(pat="v") -%}
23+
{%- set major = version_clean | split(pat=".") | first -%}
24+
{%- if major not in major_versions -%}
25+
{%- set_global major_versions = major_versions | concat(with=major) -%}
26+
{%- endif -%}
27+
{%- endif -%}
28+
{%- endfor -%}
29+
{%- set sorted_majors = major_versions | sort | reverse -%}
30+
31+
{#- MAIN LOOP: Iterate over major versions -#}
32+
{%- for major in sorted_majors -%}
33+
{#- VISUAL: Add double newline before the header to separate from previous block -#}
34+
{{ "\n\n" }}#### {{ major }}.x Releases
35+
36+
{#- LOGIC: Filter releases for the current major version -#}
37+
{%- set_global major_releases = [] -%}
38+
{%- for release in releases -%}
39+
{%- if release.version -%}
40+
{%- set version_clean = release.version | trim_start_matches(pat="v") -%}
41+
{%- set rel_major = version_clean | split(pat=".") | first -%}
42+
{%- if rel_major == major -%}
43+
{%- set_global major_releases = major_releases | concat(with=version_clean) -%}
44+
{%- endif -%}
45+
{%- endif -%}
46+
{%- endfor -%}
47+
48+
{#- LOGIC: Separate into Stable, RC, and Beta -#}
49+
{%- set_global stable_versions = [] -%}
50+
{%- set_global rc_versions = [] -%}
51+
{%- set_global beta_versions = [] -%}
52+
{%- for version in major_releases -%}
53+
{%- if version is containing("-rc") -%}
54+
{%- set_global rc_versions = rc_versions | concat(with=version) -%}
55+
{%- elif version is containing("-beta") -%}
56+
{%- set_global beta_versions = beta_versions | concat(with=version) -%}
57+
{%- else -%}
58+
{%- set_global stable_versions = stable_versions | concat(with=version) -%}
59+
{%- endif -%}
60+
{%- endfor -%}
61+
62+
{#- LOGIC: Group stable versions by minor version -#}
63+
{%- set_global minor_versions = [] -%}
64+
{%- for version in stable_versions -%}
65+
{%- set parts = version | split(pat=".") -%}
66+
{%- set minor_key = parts | slice(end=2) | join(sep=".") -%}
67+
{%- if minor_key not in minor_versions -%}
68+
{%- set_global minor_versions = minor_versions | concat(with=minor_key) -%}
69+
{%- endif -%}
70+
{%- endfor -%}
71+
{%- set sorted_minors = minor_versions | sort | reverse -%}
72+
73+
{#- OUTPUT: Stable releases -#}
74+
{%- for minor_key in sorted_minors -%}
75+
{%- set_global minor_release_versions = [] -%}
76+
{%- for version in stable_versions -%}
77+
{%- set parts = version | split(pat=".") -%}
78+
{%- set ver_minor = parts | slice(end=2) | join(sep=".") -%}
79+
{%- if ver_minor == minor_key -%}
80+
{%- set_global minor_release_versions = minor_release_versions | concat(with=version) -%}
81+
{%- endif -%}
82+
{%- endfor -%}
83+
{%- set versions_list = minor_release_versions | sort | reverse -%}
84+
{{ "\n" }}- `{{ minor_key }}.x` Releases - {% for version in versions_list -%}
85+
[{{ version }}](#{{ version | replace(from=".", to="") | replace(from="-", to="") | lower }})
86+
{%- if not loop.last %} | {% endif -%}
87+
{%- endfor -%}
88+
{%- endfor -%}
89+
90+
{#- OUTPUT: RC versions -#}
91+
{%- if rc_versions | length > 0 -%}
92+
{%- set rc_versions_sorted = rc_versions | sort | reverse -%}
93+
{%- set rc_base = rc_versions_sorted | first | split(pat="-") | first -%}
94+
{{ "\n" }}- `{{ rc_base }}` Release Candidates - {% for version in rc_versions_sorted -%}
95+
[{{ version }}](#{{ version | replace(from=".", to="") | replace(from="-", to="") | lower }})
96+
{%- if not loop.last %} | {% endif -%}
97+
{%- endfor -%}
98+
{%- endif -%}
99+
100+
{#- OUTPUT: Beta versions -#}
101+
{%- if beta_versions | length > 0 -%}
102+
{%- set beta_versions_sorted = beta_versions | sort | reverse -%}
103+
{%- set beta_base = beta_versions_sorted | first | split(pat="-") | first -%}
104+
{{ "\n" }}- `{{ beta_base }}` Betas - {% for version in beta_versions_sorted -%}
105+
[{{ version }}](#{{ version | replace(from=".", to="") | replace(from="-", to="") | lower }})
106+
{%- if not loop.last %} | {% endif -%}
107+
{%- endfor -%}
108+
{%- endif -%}
109+
{%- endfor -%}{{ "\n" }}
110+
"""
111+
# template for the changelog body
112+
# https://keats.github.io/tera/docs/#introduction
113+
body = """
114+
{%- macro remote_url() -%}
115+
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
116+
{%- endmacro -%}
117+
118+
{%- set_global renderable_commits = [] -%}
119+
{%- for commit in commits -%}
120+
{%- if commit.conventional or commit.remote.pr_number -%}
121+
{%- set_global renderable_commits = renderable_commits | concat(with=commit) -%}
122+
{%- endif -%}
123+
{%- endfor -%}
124+
125+
{%- if renderable_commits | length > 0 -%}
126+
{#- LOGIC: Version Header with Link -#}
127+
{%- if version -%}
128+
## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/releases/tag/{{ version }})
129+
{{ "\n" }}
130+
{#- LOGIC: Date and Milestone link (using version name search as proxy for ID) -#}
131+
Released on {{ timestamp | date(format="%Y-%m-%d") }}. All issues associated with this milestone can be found using this [filter]({{ self::remote_url() }}/milestones?state=closed&q={{ version }}).
132+
{%- else -%}
133+
## [Unreleased]
134+
{%- endif -%}
135+
{{ "\n" }}
136+
{%- endif -%}
137+
138+
{#- LOGIC: Loop through commit groups (Fixed, Added, etc.) -#}
139+
{%- for group, commits_in_group in renderable_commits | group_by(attribute="group") -%}
140+
141+
{#- LOGIC: Determine the verb based on the group name -#}
142+
{%- set action_verb = "Contributed by" -%}
143+
{%- if group == "Features" -%}
144+
{%- set action_verb = "Implemented by" -%}
145+
{%- elif group == "Bug Fixes" -%}
146+
{%- set action_verb = "Fixed by" -%}
147+
{%- elif group == "Performance" -%}
148+
{%- set action_verb = "Optimized by" -%}
149+
{%- elif group == "Documentation" -%}
150+
{%- set action_verb = "Documented by" -%}
151+
{%- endif -%}
152+
153+
{%- if group == "Uncategorized Changes" -%}
154+
{%- if commits_in_group | length == 0 -%}
155+
{%- continue -%}
156+
{%- endif -%}
157+
{%- endif -%}
158+
159+
{{ "\n" }}### {{ group | upper_first }}
160+
161+
{#- LOGIC: Loop through commits -#}
162+
{%- for commit in commits -%}
163+
{%- if commit.conventional or commit.remote.pr_number -%}
164+
{%- set message = commit.message | split(pat="\n") | first | upper_first | trim -%}
165+
166+
{#- VISUAL: Commit message line -#}
167+
{{ "\n" }}- {{ message }}
168+
169+
{%- if commit.remote.username and commit.remote.pr_number -%}
170+
{#- VISUAL: Dynamic verb line -#}
171+
{{ "\n" }} - {{ action_verb }} [{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }}) in Pull Request [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}).
172+
{%- endif -%}
173+
{%- endif -%}
174+
{%- endfor -%}
175+
{%- endfor -%}
176+
{{ "\n" }}
177+
178+
{#- LOGIC: New Contributors Section -#}
179+
{%- set new_contributors = github.contributors | filter(attribute="is_first_time", value=true) -%}
180+
181+
{%- if new_contributors | length > 0 -%}
182+
183+
{%- set_global real_new_contributors = [] -%}
184+
{%- for contributor in new_contributors -%}
185+
{%- set_global real_new_contributors = real_new_contributors | concat(with=contributor) -%}
186+
{%- endfor -%}
187+
188+
{%- if real_new_contributors | length > 0 -%}
189+
{{ "\n" }}### New Contributors
190+
{%- for contributor in real_new_contributors -%}
191+
{{ "\n" }}* @{{ contributor.username }} made their first contribution
192+
{%- if contributor.pr_number -%}
193+
in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }})
194+
{%- endif -%}
195+
{%- endfor -%}
196+
{%- endif -%}
197+
198+
{%- endif -%}
199+
{{ "\n\n" }}
200+
"""
201+
# template for the changelog footer
202+
footer = """
203+
{%- macro remote_url() -%}
204+
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
205+
{%- endmacro -%}
206+
207+
{% for release in releases -%}
208+
{% if release.version -%}
209+
{% if release.previous.version -%}
210+
[{{ release.version | trim_start_matches(pat="v") }}]: \
211+
{{ self::remote_url() }}/compare/{{ release.previous.version }}..{{ release.version }}
212+
{% endif -%}
213+
{% else -%}
214+
[unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}..HEAD
215+
{% endif -%}
216+
{% endfor %}
217+
<!-- generated by git-cliff -->
218+
"""
219+
# remove the leading and trailing whitespace from the templates
220+
trim = true
221+
# postprocessors
222+
postprocessors = []
223+
224+
[git]
225+
# parse the commits based on https://www.conventionalcommits.org
226+
conventional_commits = true
227+
# filter out the commits that are not conventional
228+
filter_unconventional = false
229+
# process each line of a commit as an individual commit
230+
split_commits = false
231+
# regex for preprocessing the commit messages
232+
commit_preprocessors = [
233+
# remove issue numbers from commits
234+
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" },
235+
]
236+
commit_parsers = [
237+
{ message = "^feat", group = "Features" },
238+
{ message = "^fix", group = "Bug Fixes" },
239+
{ message = "^doc", group = "Documentation" },
240+
{ message = "^perf", group = "Performance" },
241+
{ message = "^refactor", group = "Refactor" },
242+
{ message = "^style", group = "Styling" },
243+
{ message = "^test", group = "Testing" },
244+
{ message = "^chore\\(spm.*\\)", skip = false },
245+
{ message = "^chore\\(deps.*\\)", skip = true },
246+
{ message = "^chore\\(pr\\)", skip = true },
247+
{ message = "^chore\\(pull\\)", skip = true },
248+
{ message = "^chore\\(release\\): prepare for", skip = true },
249+
{ message = "^chore|^ci", group = "Miscellaneous Tasks" },
250+
{ body = ".*security", group = "Security" },
251+
{ message = ".*", group = "Uncategorized Changes" },
252+
]
253+
# protect breaking changes from being skipped due to matching a skipping commit_parser
254+
protect_breaking_commits = false
255+
# filter out the commits that are not matched by commit parsers
256+
filter_commits = false
257+
# regex for matching git tags (only CLI package tags)
258+
tag_pattern = "^[0-9].*"
259+
# regex for skipping tags
260+
skip_tags = "beta|alpha|cli-.*"
261+
# regex for ignoring tags
262+
ignore_tags = "rc|web-.*"
263+
# sort the tags topologically
264+
topo_order = false
265+
# sort the commits inside sections by oldest/newest order
266+
sort_commits = "newest"
267+
268+
[bump]
269+
breaking_always_bump_major = true
270+
features_always_bump_minor = true
271+
initial_tag = "0.1.0"

0 commit comments

Comments
 (0)