From d23b5e7b01a1e28f7be9e91ba4274b671a2de639 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Fri, 16 Jan 2026 23:54:56 +0100 Subject: [PATCH 01/12] Add git-cliff and mvnd to devenv --- devenv.nix | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/devenv.nix b/devenv.nix index 3edcb77..6e86fd9 100644 --- a/devenv.nix +++ b/devenv.nix @@ -14,12 +14,21 @@ # https://devenv.sh/packages/ # https://search.nixos.org/packages # Note that the JDK and Maven are already pulled in through the `languages.java` props. - packages = [ + packages = with pkgs; [ # Basic utilities - pkgs.curl - pkgs.git - pkgs.go-task - pkgs.gnupg + curl + git + go-task + gnupg + + # Maven Daemon + # https://github.com/apache/maven-mvnd + mvnd + + # Highly customizable changelog generator + # https://github.com/orhun/git-cliff + # https://git-cliff.org/docs/ + git-cliff ]; git-hooks.hooks = { From 743c83aabce2a33c8e914c52f599bbd7733b0e2b Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Fri, 16 Jan 2026 23:56:18 +0100 Subject: [PATCH 02/12] Save the result of `git-cliff --init` command --- cliff.toml | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 cliff.toml diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..b278c49 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,92 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + + +[changelog] +# A Tera template to be rendered for each release in the changelog. +# See https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**] {% endif %}\ + {{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %} +""" +# Remove leading and trailing whitespaces from the changelog's body. +trim = true +# Render body even when there are no releases to process. +render_always = true +# An array of regex based postprocessors to modify the changelog. +postprocessors = [ + # Replace the placeholder with a URL. + #{ pattern = '', replace = "https://github.com/orhun/git-cliff" }, +] +# render body even when there are no releases to process +# render_always = true +# output file path +# output = "test.md" + +[git] +# Parse commits according to the conventional commits specification. +# See https://www.conventionalcommits.org +conventional_commits = true +# Exclude commits that do not match the conventional commits specification. +filter_unconventional = true +# Require all commits to be conventional. +# Takes precedence over filter_unconventional. +require_conventional = false +# Split commits on newlines, treating each line as an individual commit. +split_commits = false +# An array of regex based parsers to modify commit messages prior to further processing. +commit_preprocessors = [ + # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. + #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, + # Check spelling of the commit message using https://github.com/crate-ci/typos. + # If the spelling is incorrect, it will be fixed automatically. + #{ pattern = '.*', replace_command = 'typos --write-changes -' }, +] +# Prevent commits that are breaking from being excluded by commit parsers. +protect_breaking_commits = false +# An array of regex based parsers for extracting data from the commit message. +# Assigns commits to groups. +# Optionally sets the commit's scope and can decide to exclude commits from further processing. +commit_parsers = [ + { message = "^feat", group = "๐Ÿš€ Features" }, + { message = "^fix", group = "๐Ÿ› Bug Fixes" }, + { message = "^doc", group = "๐Ÿ“š Documentation" }, + { message = "^perf", group = "โšก Performance" }, + { message = "^refactor", group = "๐Ÿšœ Refactor" }, + { message = "^style", group = "๐ŸŽจ Styling" }, + { message = "^test", group = "๐Ÿงช Testing" }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, + { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert", group = "โ—€๏ธ Revert" }, + { message = ".*", group = "๐Ÿ’ผ Other" }, +] +# Exclude commits that are not matched by any commit parser. +filter_commits = false +# An array of link parsers for extracting external references, and turning them into URLs, using regex. +link_parsers = [] +# Include only the tags that belong to the current branch. +use_branch_tags = false +# Order releases topologically instead of chronologically. +topo_order = false +# Order releases topologically instead of chronologically. +topo_order_commits = true +# Order of commits in each group/release within the changelog. +# Allowed values: newest, oldest +sort_commits = "oldest" +# Process submodules commits +recurse_submodules = false From f4bc64f142a7f7ead784d3d072eac07cf6564b24 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 00:42:45 +0100 Subject: [PATCH 03/12] Start adjusting git-cliff configuration --- cliff.toml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cliff.toml b/cliff.toml index b278c49..c82d261 100644 --- a/cliff.toml +++ b/cliff.toml @@ -1,7 +1,7 @@ # git-cliff ~ configuration file +# https://github.com/orhun/git-cliff # https://git-cliff.org/docs/configuration - [changelog] # A Tera template to be rendered for each release in the changelog. # See https://keats.github.io/tera/docs/#introduction @@ -27,14 +27,14 @@ render_always = true # An array of regex based postprocessors to modify the changelog. postprocessors = [ # Replace the placeholder with a URL. - #{ pattern = '', replace = "https://github.com/orhun/git-cliff" }, + { pattern = '', replace = "https://github.com/tguzik/valueclasses" }, ] -# render body even when there are no releases to process -# render_always = true -# output file path -# output = "test.md" +# Default output file path +#output = "CHANGELOG.md" [git] +# Pattern for release tags used in this repo +tag_pattern = "^v[0-9].*" # Parse commits according to the conventional commits specification. # See https://www.conventionalcommits.org conventional_commits = true @@ -48,13 +48,11 @@ split_commits = false # An array of regex based parsers to modify commit messages prior to further processing. commit_preprocessors = [ # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. - #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, - # Check spelling of the commit message using https://github.com/crate-ci/typos. - # If the spelling is incorrect, it will be fixed automatically. - #{ pattern = '.*', replace_command = 'typos --write-changes -' }, + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))" }, ] # Prevent commits that are breaking from being excluded by commit parsers. -protect_breaking_commits = false +protect_breaking_commits = true + # An array of regex based parsers for extracting data from the commit message. # Assigns commits to groups. # Optionally sets the commit's scope and can decide to exclude commits from further processing. @@ -90,3 +88,5 @@ topo_order_commits = true sort_commits = "oldest" # Process submodules commits recurse_submodules = false + +# eof From 971aa06eca2133788760e7c47a1a67480822d2eb Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 01:14:25 +0100 Subject: [PATCH 04/12] Update group names and their patterns --- cliff.toml | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/cliff.toml b/cliff.toml index c82d261..e2eddff 100644 --- a/cliff.toml +++ b/cliff.toml @@ -26,8 +26,8 @@ trim = true render_always = true # An array of regex based postprocessors to modify the changelog. postprocessors = [ - # Replace the placeholder with a URL. - { pattern = '', replace = "https://github.com/tguzik/valueclasses" }, + # Replace the placeholder with a URL. + { pattern = '', replace = "https://github.com/tguzik/valueclasses" }, ] # Default output file path #output = "CHANGELOG.md" @@ -47,31 +47,33 @@ require_conventional = false split_commits = false # An array of regex based parsers to modify commit messages prior to further processing. commit_preprocessors = [ - # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. - { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))" }, + # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))" }, ] # Prevent commits that are breaking from being excluded by commit parsers. protect_breaking_commits = true - -# An array of regex based parsers for extracting data from the commit message. -# Assigns commits to groups. +# An array of regex-based parsers that assign commits to a group. # Optionally sets the commit's scope and can decide to exclude commits from further processing. +# The regex is applied over the whole message. + +# The entries here are processed in order and the first matched regex (the `message` prop) will assign the commit to a group. The +# groups in the output appear in the ascending lexical order of the group names, not the order of these entries. The HTML comment +# in the name of the group still participates in sorting but is omitted from the output - we're using it to manually set the +# order. +# Additional examples: https://git-cliff.org/docs/configuration/git#commit_parsers commit_parsers = [ - { message = "^feat", group = "๐Ÿš€ Features" }, - { message = "^fix", group = "๐Ÿ› Bug Fixes" }, - { message = "^doc", group = "๐Ÿ“š Documentation" }, - { message = "^perf", group = "โšก Performance" }, - { message = "^refactor", group = "๐Ÿšœ Refactor" }, - { message = "^style", group = "๐ŸŽจ Styling" }, - { message = "^test", group = "๐Ÿงช Testing" }, - { message = "^chore\\(release\\): prepare for", skip = true }, - { message = "^chore\\(deps.*\\)", skip = true }, - { message = "^chore\\(pr\\)", skip = true }, - { message = "^chore\\(pull\\)", skip = true }, - { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, - { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, - { message = "^revert", group = "โ—€๏ธ Revert" }, - { message = ".*", group = "๐Ÿ’ผ Other" }, + { message = "^feat", group = "Features:" }, + { message = "^fix", group = "Bug Fixes:" }, + { message = "^doc", group = "Documentation:" }, + { message = "^perf", group = "Performance:" }, + { message = "^refactor", group = "Refactor:" }, + { message = "^style", group = "Code Styling:" }, + { message = "^test", group = "Testing:" }, + { message = "^deps|^build\\(deps(-dev)?\\)", group = "Dependencies:" }, + { message = "^build|^ci|^chore", group = "Maintenance:" }, + { message = "^revert", group = "Revert:" }, + # { message = "^chore\\(pull\\)", skip = true }, + { message = ".*", group = "Other:" }, ] # Exclude commits that are not matched by any commit parser. filter_commits = false From 749bebe52d76538b003cc2fd0b2f7194c9ac4fd4 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 01:50:00 +0100 Subject: [PATCH 05/12] Update git-cliff config to handle legacy commits --- cliff.toml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/cliff.toml b/cliff.toml index e2eddff..434a149 100644 --- a/cliff.toml +++ b/cliff.toml @@ -7,16 +7,16 @@ # See https://keats.github.io/tera/docs/#introduction body = """ {% if version %}\ - ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} + ## [{{ version }}] - {{ timestamp | date(format="%Y-%m-%d") }} {% else %}\ ## [unreleased] {% endif %}\ {% for group, commits in commits | group_by(attribute="group") %} ### {{ group | striptags | trim | upper_first }} {% for commit in commits %} - - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + * {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ {% if commit.breaking %}[**breaking**] {% endif %}\ - {{ commit.message | upper_first }}\ + {{ commit.message | upper_first | split(pat='\n') | first }}\ {% endfor %} {% endfor %} """ @@ -33,13 +33,11 @@ postprocessors = [ #output = "CHANGELOG.md" [git] -# Pattern for release tags used in this repo -tag_pattern = "^v[0-9].*" # Parse commits according to the conventional commits specification. # See https://www.conventionalcommits.org conventional_commits = true # Exclude commits that do not match the conventional commits specification. -filter_unconventional = true +filter_unconventional = false # Require all commits to be conventional. # Takes precedence over filter_unconventional. require_conventional = false @@ -87,7 +85,7 @@ topo_order = false topo_order_commits = true # Order of commits in each group/release within the changelog. # Allowed values: newest, oldest -sort_commits = "oldest" +sort_commits = "newest" # Process submodules commits recurse_submodules = false From 059ff05428d7dc4462b278fef6779a385509d0d5 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 17:06:30 +0100 Subject: [PATCH 06/12] Add task to generate the list for changes in the last release and since --- Taskfile.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Taskfile.yaml b/Taskfile.yaml index c40de82..5aa7bda 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -22,6 +22,11 @@ tasks: cmds: - cmd: mvn -Pupgrades + maintenance:changelog: + desc: Creates list of changes since the last release tag + cmds: + - cmd: git-cliff --current --unreleased --output {{ .FILE | default "-" | shellQuote }} + maintenance:check-reproducibility: # See https://maven.apache.org/guides/mini/guide-reproducible-builds.html desc: Check if artifacts are reproducible From 27570e6bfc95293a858a233b5af5ea7c6e3bf960 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 17:19:09 +0100 Subject: [PATCH 07/12] Rename task prefix 'maintenance' to 'release' --- Taskfile.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Taskfile.yaml b/Taskfile.yaml index 5aa7bda..84596ce 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -22,20 +22,21 @@ tasks: cmds: - cmd: mvn -Pupgrades - maintenance:changelog: + release:changelog: desc: Creates list of changes since the last release tag cmds: - cmd: git-cliff --current --unreleased --output {{ .FILE | default "-" | shellQuote }} - maintenance:check-reproducibility: + release:check-reproducibility: # See https://maven.apache.org/guides/mini/guide-reproducible-builds.html desc: Check if artifacts are reproducible cmds: - cmd: mvn artifact:check-buildplan - cmd: mvn clean install - cmd: mvn clean verify artifact:compare + - cmd: mvn clean - maintenance:release: + release:create: desc: Cuts off a new release and deploys artifacts to Sonatype Maven Central summary: | This task verifies that appropriate credentials have been entered, cuts off a new @@ -86,6 +87,7 @@ tasks: echo - cmd: echo "This step primes GnuPG cache so it can be used in non-interactive mode" | gpg --clearsign + - task: release:check-reproducibility - cmd: mvn --settings={{ .SETTINGS_FILE | shellQuote }} -Prelease -DdryRun=true - cmd: mvn --settings={{ .SETTINGS_FILE | shellQuote }} -Prelease - silent: true From 5b4f9854e042940274832fe5030f7e719bc6a0cf Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 17:20:44 +0100 Subject: [PATCH 08/12] Create basic action to generate changelog --- .github/workflows/release-changelog.yaml | 53 ++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/release-changelog.yaml diff --git a/.github/workflows/release-changelog.yaml b/.github/workflows/release-changelog.yaml new file mode 100644 index 0000000..730b225 --- /dev/null +++ b/.github/workflows/release-changelog.yaml @@ -0,0 +1,53 @@ +name: Release Changelog + +on: + # Automatically create a GitHub Release entry when a tag that looks like a new release is pushed +# push: +# tags: +# - 'v[0-9]+.*' + + # + # Temporary to test the workflow: + pull_request: # No additional yaml keys are necessary + +jobs: + release: + name: Create list of changes + runs-on: ubuntu-latest + permissions: + contents: write # Needed for creating releases + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - uses: cachix/install-nix-action@v31 + + - uses: cachix/cachix-action@v16 + with: + name: devenv + + - name: Install devenv.sh + run: nix profile install nixpkgs#devenv + + - name: Generate change list + shell: devenv shell bash -- -e {0} + run: | + task release:changelog FILE=changes.md + + - name: Include the change list in summary + shell: devenv shell bash -- -e {0} + continue-on-error: true + run: | + echo -e "# Generated change list:\n\n----\n\n" >> "$GITHUB_STEP_SUMMARY" + cat changes.md >> "$GITHUB_STEP_SUMMARY" + +# - name: Create GitHub Release entry +# uses: softprops/action-gh-release@v2 +# with: +# body_path: changes.md +# draft: true +# files: | +# files-*.tar.gz + +# eof From c952564f4287932dd9ebbab07a6cafc246ba49c2 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 17:42:16 +0100 Subject: [PATCH 09/12] Change triggers - pull_request creates a temporary merge --- .github/workflows/release-changelog.yaml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release-changelog.yaml b/.github/workflows/release-changelog.yaml index 730b225..ce90400 100644 --- a/.github/workflows/release-changelog.yaml +++ b/.github/workflows/release-changelog.yaml @@ -1,14 +1,11 @@ name: Release Changelog on: - # Automatically create a GitHub Release entry when a tag that looks like a new release is pushed -# push: -# tags: -# - 'v[0-9]+.*' - - # - # Temporary to test the workflow: - pull_request: # No additional yaml keys are necessary + push: + # Automatically create a GitHub Release entry when a tag that looks like a new release is pushed + tags: + - 'v[0-9]+.*' + - 'tmp-.*' # Temporary to test the workflow jobs: release: From a8492e87bc76d61a0b4c5c35c554af3ec3dc8282 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 18:06:00 +0100 Subject: [PATCH 10/12] Continue testing the new workflow --- .github/workflows/release-changelog.yaml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-changelog.yaml b/.github/workflows/release-changelog.yaml index ce90400..1c2701b 100644 --- a/.github/workflows/release-changelog.yaml +++ b/.github/workflows/release-changelog.yaml @@ -5,14 +5,15 @@ on: # Automatically create a GitHub Release entry when a tag that looks like a new release is pushed tags: - 'v[0-9]+.*' - - 'tmp-.*' # Temporary to test the workflow + pull_request: # Temporary to test the workflow jobs: release: - name: Create list of changes + name: Create GitHub Release entry runs-on: ubuntu-latest permissions: - contents: write # Needed for creating releases + # Needed for creating the GitHub Release entry: + contents: write steps: - uses: actions/checkout@v6 with: @@ -39,12 +40,13 @@ jobs: echo -e "# Generated change list:\n\n----\n\n" >> "$GITHUB_STEP_SUMMARY" cat changes.md >> "$GITHUB_STEP_SUMMARY" -# - name: Create GitHub Release entry -# uses: softprops/action-gh-release@v2 -# with: -# body_path: changes.md -# draft: true -# files: | -# files-*.tar.gz + - name: Create GitHub Release entry + uses: softprops/action-gh-release@v2 + if: github.ref_type == 'tag' + with: + # Populate the release entry body with the notes we generated + body_path: changes.md + # A hooman still needs to push the butan + draft: true # eof From b4a425112b054bdfbf36e9a3ffcb8db3593e4197 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 18:11:11 +0100 Subject: [PATCH 11/12] Rename workflow file --- .github/workflows/{release-changelog.yaml => release-entry.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{release-changelog.yaml => release-entry.yaml} (100%) diff --git a/.github/workflows/release-changelog.yaml b/.github/workflows/release-entry.yaml similarity index 100% rename from .github/workflows/release-changelog.yaml rename to .github/workflows/release-entry.yaml From 4b338df857fdfdfbc19675489a36d7404d916379 Mon Sep 17 00:00:00 2001 From: Tomasz Guzik Date: Sat, 17 Jan 2026 18:26:58 +0100 Subject: [PATCH 12/12] Clean up the workflow --- .github/workflows/release-entry.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-entry.yaml b/.github/workflows/release-entry.yaml index 1c2701b..92078fc 100644 --- a/.github/workflows/release-entry.yaml +++ b/.github/workflows/release-entry.yaml @@ -2,17 +2,18 @@ name: Release Changelog on: push: - # Automatically create a GitHub Release entry when a tag that looks like a new release is pushed + # Automatically create a GitHub Release entry when a tag that looks like a new release is pushed. + # The tag pattern is defined in maven-release-plugin's settings in pom.xml authoritatively. The changelog + # generator - git-cliff - just goes through all relevant tags, regardless of their name. tags: - 'v[0-9]+.*' - pull_request: # Temporary to test the workflow jobs: release: name: Create GitHub Release entry runs-on: ubuntu-latest permissions: - # Needed for creating the GitHub Release entry: + # Needed by softprops/action-gh-release to create the GitHub Release entry: contents: write steps: - uses: actions/checkout@v6