Skip to content

Commit 2f256e0

Browse files
authored
fix: add changelog generation (#16)
1 parent 7bb74b2 commit 2f256e0

14 files changed

Lines changed: 342 additions & 1 deletion

File tree

justfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,38 @@ default:
1313
just --list --unsorted
1414

1515
# Create a new project from a template.
16+
[group('general')]
1617
create *args:
1718
just develop ./tools/scripts/create.sh "$@"
1819

1920
# Format the whole repository.
21+
[group('general')]
2022
format *args:
2123
"{{root_dir}}/tools/scripts/setup-config-files.sh"
2224
nix run --accept-flake-config {{flake_dir}}#treefmt -- "$@"
2325

2426
# Setup the repository.
27+
[group('general')]
2528
setup *args:
2629
just maintenance::setup "$@"
2730

2831
# Clean output folder.
32+
[group('general')]
2933
clean:
3034
rm -rf ./.output
3135

3236
# Test everything.
37+
[group('general')]
3338
test:
3439
just maintenance::test-all
3540

3641
# Show all packages configured in the Nix `flake.nix`.
42+
[group('general')]
3743
nix-list *args:
3844
cd tools/nix && nix flake --no-pure-eval show
3945

4046
# Enter the default Nix development shell.
47+
[group('general')]
4148
develop *args:
4249
just nix::develop default "$@"
4350

src/.jinja/common/justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ output_dir := root_dir / ".output"
77
build_dir := output_dir / "build"
88

99
mod nix "./tools/just/nix.just"
10+
mod changelog "./tools/just/changelog.just"
1011

1112
# Default target if you do not specify a target.
1213
default:

src/generic/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
<!-- next-content -->

src/generic/justfile.jinja

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
{% include ".jinja/common/justfile" %}
22
{%- raw %}
33
# Lint the project.
4+
[group('general')]
45
lint *args:
56
echo "TODO: Not implemented"
67

78
# Build the module.
9+
[group('general')]
810
build *args:
911
echo "TODO: Not implemented"
1012

1113
# Test the project.
14+
[group('general')]
1215
test *args:
1316
echo "TODO: Not implemented"
1417

1518
# Run an executable.
19+
[group('general')]
1620
run *args:
1721
echo "TODO: Not implemented"
1822
{%- endraw %}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
{% raw %}
2+
# git-cliff ~ configuration file
3+
# https://git-cliff.org/docs/configuration
4+
5+
6+
[changelog]
7+
header = """
8+
# Changelog\n
9+
All notable changes to this project will be documented in this file.
10+
See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.\n
11+
"""
12+
13+
# A Tera template to be rendered for each release in the changelog.
14+
# See https://keats.github.io/tera/docs/#introduction
15+
body = """
16+
<!-- next-content -->
17+
18+
{% if version %}\
19+
## Version `{{ version | trim_start_matches(pat="v") }}` - {{ timestamp | date(format="%Y-%m-%d") }}
20+
{% else %}\
21+
## [unreleased]
22+
{% endif %}\
23+
{% for group, commits in commits | unique(attribute="message") | group_by(attribute="group") %}
24+
### {{ group | striptags | trim | upper_first }}
25+
{% for commit in commits %}
26+
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
27+
{% if commit.breaking %}[**breaking**] {% endif %}\
28+
{{ commit.message | upper_first }} [[{{ commit.id | truncate(length=4, end="") }}]($COMMIT_URL/{{ commit.id }}), {{ commit.author.name }}]\
29+
{% endfor %}
30+
{% endfor %}
31+
"""
32+
# Remove leading and trailing whitespaces from the changelog's body.
33+
trim = true
34+
# Render body even when there are no releases to process.
35+
render_always = true
36+
# An array of regex based postprocessors to modify the changelog.
37+
postprocessors = [
38+
# Replace some placeholders.
39+
{% endraw %}
40+
{%- if "github" in project_hosts[0].host %}
41+
{{ "{" }} pattern = '\$PR_URL', replace = "$$REPO/pull" {{ "}" }},
42+
{{ "{" }} pattern = '\$REPO', replace = "{{ project_url }}" {{ "}" }},
43+
{{ "{" }} pattern = '\$COMMIT_URL', replace = "{{ project_url }}/commit" {{ "}" }},
44+
{%- elif "gitlab" in project_hosts[0].host %}
45+
{{ "{" }} pattern = '\$PR_URL', replace = "$$REPO/-/merge_request" {{ "}" }},
46+
{{ "{" }} pattern = '\$REPO', replace = "{{ project_url }}" {{ "}" }},
47+
{{ "{" }} pattern = '\$COMMIT_URL', replace = "{{ project_url }}/-/tree" {{ "}" }},
48+
{%- endif %}
49+
{% raw %}
50+
]
51+
# render body even when there are no releases to process
52+
# render_always = true
53+
# output file path
54+
# output = "test.md"
55+
56+
[git]
57+
# Parse commits according to the conventional commits specification.
58+
# See https://www.conventionalcommits.org
59+
conventional_commits = true
60+
# Exclude commits that do not match the conventional commits specification.
61+
filter_unconventional = true
62+
# Require all commits to be conventional.
63+
# Takes precedence over filter_unconventional.
64+
require_conventional = false
65+
# Split commits on newlines, treating each line as an individual commit.
66+
split_commits = false
67+
# An array of regex based parsers to modify commit messages prior to further processing.
68+
commit_preprocessors = [{ pattern = "\\(!(\\d+)\\)", replace = "[**(!$1)**]($$PR_URL/$1)"}]
69+
# Prevent commits that are breaking from being excluded by commit parsers.
70+
protect_breaking_commits = false
71+
# An array of regex based parsers for extracting data from the commit message.
72+
# Assigns commits to groups.
73+
# Optionally sets the commit's scope and can decide to exclude commits from further processing.
74+
commit_parsers = [
75+
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
76+
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
77+
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
78+
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
79+
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
80+
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
81+
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
82+
{ message = "^chore\\(release\\): prepare for", skip = true },
83+
{ message = "^chore\\(deps.*\\)", skip = true },
84+
{ message = "^chore\\(pr\\)", skip = true },
85+
{ message = "^chore\\(pull\\)", skip = true },
86+
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
87+
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
88+
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
89+
{ message = ".*", group = "<!-- 10 -->💼 Other" },
90+
]
91+
# Exclude commits that are not matched by any commit parser.
92+
filter_commits = false
93+
# An array of link parsers for extracting external references, and turning them into URLs, using regex.
94+
link_parsers = [
95+
]
96+
# Include only the tags that belong to the current branch.
97+
use_branch_tags = false
98+
# Order releases topologically instead of chronologically.
99+
topo_order = true
100+
# Order releases topologically instead of chronologically.
101+
topo_order_commits = true
102+
# Order of commits in each group/release within the changelog.
103+
# Allowed values: newest, oldest
104+
sort_commits = "newest"
105+
# Process submodules commits
106+
recurse_submodules = false
107+
{% endraw %}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
set positional-arguments
2+
set shell := ["bash", "-cue"]
3+
4+
default:
5+
just --list --unsorted -f "{{source_file()}}"
6+
7+
# Generate the changelog in the range `<lastTag>..HEAD`
8+
# Only the first-parent reachable commits are taken.
9+
# See: https://github.com/git-chglog/git-chglog/issues/284
10+
[group('aux')]
11+
gen start="HEAD" end="" config="./tools/configs/git-cliff/config.toml" changelog="CHANGELOG.md":
12+
just nix::develop changelog \
13+
generate-changelog "{{start}}" "{{end}}" "{{config}}" "{{changelog}}"
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
...
3+
}:
4+
{
5+
perSystem =
6+
{ pkgs, ... }:
7+
{
8+
# Generate a changelog from `HEAD` to the last tag on the current branch.
9+
# With the following arguments.
10+
# `$1: <new-tag>`
11+
# `$2: <git-cliff-config>` (optional)
12+
# `$3`: <changelog-file> (optional)
13+
# NOTE: Due to Nix you need to escape with `''${VAR}`
14+
packages.generate-changelog = pkgs.writeShellApplication {
15+
name = "generate-changelog";
16+
runtimeInputs = [
17+
pkgs.git
18+
pkgs.gnugrep
19+
pkgs.coreutils
20+
pkgs.git-cliff
21+
];
22+
23+
text =
24+
# bash
25+
''
26+
#!/usr/bin/env bash
27+
root_dir=$(git rev-parse --show-toplevel) || exit 1
28+
cd "$root_dir"
29+
30+
tag="$1"
31+
config="''${2:-tools/config/git-cliff/config.toml}"
32+
file="''${3:-CHANGELOG.md}"
33+
34+
lastTag=$(git describe --abbrev=0 --tags)
35+
if [ "$lastTag" = "" ]; then
36+
echo "No last tag found!" >&2
37+
echo "Create one!" >&2
38+
39+
exit 1
40+
fi
41+
42+
start=HEAD
43+
end="$lastTag"
44+
45+
echo "Changelog in '$end..$start'" >&2
46+
47+
if ! grep -q '<!-- next-content -->' "$file"; then
48+
echo "no '<!-- next-content -->' tag in '$file'"
49+
exit 1
50+
fi
51+
52+
non_first_parent_commits=()
53+
readarray -t non_first_parent_commits < <(
54+
comm -23 <(git rev-list "$end..$start" | sort) \
55+
<(git rev-list --ancestry-path --first-parent "$end..$start" |sort)
56+
)
57+
58+
skip_args=()
59+
if [ "''${#non_first_parent_commits[@]}" -ne 0 ]; then
60+
# shellcheck disable=SC2086,SC2206
61+
skip_args=(--skip-commit)
62+
skip_args+=("''${non_first_parent_commits[@]}")
63+
fi
64+
65+
out=$(git-cliff --config "$config" \
66+
"$end..$start" \
67+
--strip header \
68+
"''${skip_args[@]}" \
69+
--tag "$tag")
70+
71+
# Replace in file.
72+
echo "$out" | sed -i '/<!-- next-content -->/{
73+
r /dev/stdin
74+
d
75+
}' "$file"
76+
'';
77+
};
78+
};
79+
}

src/generic/tools/nix/shells/general.parts.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
toolchains = self.lib.toolchain.import args;
1818
in
1919
{
20+
devShells.changelog = self.lib.shell.mkShell {
21+
inherit (args) system;
22+
modules = toolchains.changelog;
23+
};
24+
2025
devShells.format = self.lib.shell.mkShell {
2126
inherit (args) system;
2227
modules = toolchains.format;

src/generic/tools/nix/toolchains/toolchain-general.nix

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ let
1616
}
1717
];
1818

19+
changelog = [
20+
{
21+
packages = [
22+
self'.packages.generate-changelog
23+
];
24+
}
25+
];
26+
1927
general = format ++ [
2028
{
2129
packages = [
@@ -26,5 +34,5 @@ let
2634

2735
in
2836
{
29-
inherit format general;
37+
inherit format changelog general;
3038
}

src/go/justfile.jinja

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ main_package := "{{ package_name }}"
44

55
{%- raw %}
66
# Lint the project.
7+
[group('general')]
78
lint *args:
89
cd "src/{{main_package}}" && \
910
golangci-lint run \
@@ -14,6 +15,7 @@ lint *args:
1415
"$@"
1516

1617
# Build the project.
18+
[group('general')]
1719
build *args:
1820
mkdir -p "{{build_dir}}/{{main_package}}" && \
1921
export GOBIN="{{build_dir}}/{{main_package}}" && \
@@ -22,11 +24,13 @@ build *args:
2224
go install "$@" ./...
2325

2426
# Test the project.
27+
[group('general')]
2528
test *args:
2629
cd "src/{{main_package}}" && \
2730
go test "$@" ./...
2831

2932
# Run an executable.
33+
[group('general')]
3034
run *args:
3135
cd "src/{{main_package}}" && \
3236
go run "$@" ./cmd/cli/...

0 commit comments

Comments
 (0)