From 63f315d744ab4bcd8a50dc9c5d504d4421c8a4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ricks?= Date: Thu, 13 Mar 2025 11:48:24 +0100 Subject: [PATCH] misc: Use git-cliff to generate release changelog git-cliff allows for more flexible release changelog generation. Add another workflow to show the changelog for all unreleased changes. This allows for checking the changes before making a new release and to decide which release type needs to be applied. --- .github/workflows/changelog.yml | 29 +++++++++++ .github/workflows/release.yml | 63 ++++++++++++++++++++--- poetry.lock | 24 ++++++++- pyproject.toml | 89 +++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/changelog.yml diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 00000000..94cf3500 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,29 @@ +name: Show changelog since last release + +on: + workflow_dispatch: + +jobs: + changelog: + name: Show changelog since last release + runs-on: 'ubuntu-latest' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # for conventional commits and getting all git tags + persist-credentials: false + - name: Install git-cliff + uses: greenbone/actions/uv@v3 + with: + install: git-cliff + - name: Determine changelog + env: + GITHUB_REPO: ${{ github.repository }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git-cliff -v --strip header --unreleased -o /tmp/changelog.md + - name: Show changelog + run: | + cat /tmp/changelog.md + cat /tmp/changelog.md >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6b14adf0..86eebd47 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,9 +19,60 @@ on: jobs: build-and-release: name: Create a new release - uses: greenbone/workflows/.github/workflows/release-generic.yml@main - with: - versioning-scheme: semver - release-type: ${{ inputs.release-type }} - release-version: ${{ inputs.release-version }} - secrets: inherit + # If the event is a workflow_dispatch or on of the labels 'pre release', + # 'patch release', 'minor release' or 'major release' is set and PR is + # closed because of a merge + # NOTE: priority of set labels will be alpha > release-candidate > patch > minor > major, + # so if 'major' and 'patch' labels are set, it will create a patch release. + if: | + ( github.event_name == 'workflow_dispatch') || ( + ( contains(github.event.pull_request.labels.*.name, 'alpha release') || + contains(github.event.pull_request.labels.*.name, 'rc release') || + contains(github.event.pull_request.labels.*.name, 'patch release') || + contains(github.event.pull_request.labels.*.name, 'minor release') || + contains(github.event.pull_request.labels.*.name, 'major release')) && + github.event.pull_request.merged == true ) + runs-on: "ubuntu-latest" + steps: + - name: Selecting the Release type + id: release-type + uses: greenbone/actions/release-type@v3 + with: + release-type-input: ${{ inputs.release-type }} + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # for conventional commits and getting all git tags + persist-credentials: false + ref: ${{ steps.release-type.outputs.release-ref }} + - name: Determine release version + id: release-version + uses: greenbone/actions/release-version@v3 + with: + release-type: ${{ steps.release-type.outputs.release-type }} + release-version: ${{ inputs.release-version }} + versioning-scheme: "semver" + - name: Install git-cliff + uses: greenbone/actions/uv@v3 + with: + install: git-cliff + - name: Determine changelog + env: + GITHUB_REPO: ${{ github.repository }} + GITHUB_TOKEN: ${{ github.token }} + run: | + git-cliff -v --strip header -o /tmp/changelog.md --unreleased --tag ${{ steps.release-version.outputs.release-version }} ${{ steps.release-version.outputs.last-release-version }}..HEAD + - name: Release with release action + id: release + uses: greenbone/actions/release@v3 + with: + github-user: ${{ secrets.GREENBONE_BOT }} + github-user-mail: ${{ secrets.GREENBONE_BOT_MAIL }} + github-user-token: ${{ secrets.GREENBONE_BOT_TOKEN }} + gpg-key: ${{ secrets.GPG_KEY }} + gpg-fingerprint: ${{ secrets.GPG_FINGERPRINT }} + gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }} + release-version: ${{ steps.release-version.outputs.release-version }} + changelog: /tmp/changelog.md + ref: ${{ steps.release-type.outputs.release-ref }} + versioning-scheme: "semver" diff --git a/poetry.lock b/poetry.lock index 6b803b48..5017f254 100644 --- a/poetry.lock +++ b/poetry.lock @@ -468,7 +468,7 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["dev"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -669,6 +669,26 @@ pygments = ">=2.7" sphinx = ">=6.0,<9.0" sphinx-basic-ng = ">=1.0.0.beta2" +[[package]] +name = "git-cliff" +version = "2.8.0" +description = "A highly customizable changelog generator ⛰️" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "git_cliff-2.8.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:bfd552f1bda845e85e0a570336b4dacaf76e481f9ebe00301fe5c869377e2289"}, + {file = "git_cliff-2.8.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:281504bb22da43b671f9131c3cd7f0d56fa4aa7c9a1922948f9a537a76b38ea7"}, + {file = "git_cliff-2.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:485b825fec2600bd2ab847cd5f1724c874ddc0aed7e57d7fb43e484fcc114f8a"}, + {file = "git_cliff-2.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dad66722d0df40bd3722cf274cae5f6f55d61f5faa5974c48a68c62179592f7"}, + {file = "git_cliff-2.8.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:96cc47b9716fdeddfa0d68f6c93d6f7deb056da3dcefc0b4e337edb463b5790d"}, + {file = "git_cliff-2.8.0-py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aef4513c51ccd05f21cb539d4125902dbe555abd56695c9793f01636775075ee"}, + {file = "git_cliff-2.8.0-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:db21d6f3bf2641bd45340d076e108692606b723ddf8fc7be6bef3ffc9faedaff"}, + {file = "git_cliff-2.8.0-py3-none-win32.whl", hash = "sha256:09611b9e909f2635378bf185616a82ec7e9bbccb6e79ae6ce204c2ea4005f215"}, + {file = "git_cliff-2.8.0-py3-none-win_amd64.whl", hash = "sha256:e284e6b1e7f701b9f2836f31fec38c408da54f361f1a281d0e4709f33f00d905"}, + {file = "git_cliff-2.8.0.tar.gz", hash = "sha256:ab252f0d31c6febb57b6d4f24f9584b779327af43bc94e2bdb00867248cb5d0d"}, +] + [[package]] name = "h11" version = "0.14.0" @@ -1763,4 +1783,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = "^3.9.2" -content-hash = "cdeca2bd48ea614f3143f00bcb2ea5b23ac81c14f3b0825c7b03d73d61b3321b" +content-hash = "95612b9c0fab79c11650ea68c99433fb4927a139be5bd0202bd246979d70c785" diff --git a/pyproject.toml b/pyproject.toml index f4c6b4c5..688dc3fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ pontos = ">=22.7.2" furo = ">=2022.6.21" lxml-stubs = "^0.5.1" types-paramiko = "^3.4.0.20240205" +git-cliff = "^2.8.0" [tool.black] line-length = 80 @@ -85,3 +86,91 @@ files = "gvm" ignore_missing_imports = true explicit_package_bases = true allow_redefinition = true + +[tool.git-cliff.changelog] +# template for the changelog header +header = """ +# Changelog\n +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% 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 | upper_first }} + {% for commit in commits %} + - {{ commit.message | split(pat="\n") | first | upper_first | trim }}\ + {% if commit.remote.username %} by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }}){%- endif -%} + {% if commit.remote.pr_number %} in \ + [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) \ + {% elif commit.id %} in \ + [{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }})\ + {%- endif -%} + {% endfor %} +{% endfor -%} +""" +# template for the changelog footer +footer = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% for release in releases %} + {% if release.version -%} + {% if release.previous.version -%} + [{{ release.version | trim_start_matches(pat="v") }}]: \ + {{ self::remote_url() }}/compare/{{ release.previous.version }}..{{ release.version }} + {% endif -%} + {% else -%} + [unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}..HEAD + {% endif -%} +{%- endfor -%} +""" +# remove the leading and trailing whitespace from the templates +trim = true + +[tool.git-cliff.git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not following the conventional commits format +filter_unconventional = false +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [ + # remove issue numbers from commits + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, +] +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^[a|A]dd", group = ":sparkles: Added" }, + { message = "^[c|C]hange", group = ":construction_worker: Changed" }, + { message = "^[f|F]ix", group = ":bug: Bug Fixes" }, + { message = "^[r|R]emove", group = ":fire: Removed" }, + { message = "^[d|D]rop", group = ":fire: Removed" }, + { message = "^[d|D]oc", group = ":books: Documentation" }, + { message = "^[t|T]est", group = ":white_check_mark: Testing" }, + { message = "^[c|C]hore", group = ":wrench: Miscellaneous" }, + { message = "^[c|C]i", group = "️:wrench: Miscellaneous" }, + { message = "^[m|M]isc", group = ":wrench: Miscellaneous" }, + { message = "^[d|D]eps", group = ":ship: Dependencies" }, +] +# filter out the commits that are not matched by commit parsers +filter_commits = true +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "oldest"