diff --git a/.github/workflows/calculate-alpha-release.bash b/.github/workflows/calculate-alpha-release.bash new file mode 100644 index 0000000..c84ebf5 --- /dev/null +++ b/.github/workflows/calculate-alpha-release.bash @@ -0,0 +1,52 @@ +#!/bin/bash + +# Exit on error. Append "|| true" if you expect an error. +set -o errexit +# Exit on error inside any functions or subshells. +set -o errtrace +# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR +set -o nounset +# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip` +set -o pipefail +# Turn on traces, useful while debugging but commented out by default +# set -o xtrace + +last_release="$(git tag --sort=committerdate | grep -P "v0+\.\d+\.\d+$" | tail -1)" +echo "🐭 Last release: ${last_release}" + +# detect breaking changes +if [ -n "$(git log --oneline ${last_release}..HEAD | grep '!:')" ]; then + echo "🐭 Breaking changes detected since ${last_release}" + git log --oneline ${last_release}..HEAD | grep '!:' + # increment the minor version + minor="${last_release##v0.}" + minor="${minor%.*}" + next_minor="$((minor + 1))" + next_release="v0.${next_minor}.0" +else + # increment the patch version + patch="${last_release##*.}" + next_patch="$((patch + 1))" + next_release="${last_release/%${patch}/${next_patch}}" +fi +echo "🐭 Next release: ${next_release}" + +suffix="alpha" +last_tag="$(git tag --sort=committerdate | tail -1)" +if [[ "${last_tag}" = "${next_release}-${suffix}"* ]]; then + echo "🐭 Last alpha release: ${last_tag}" + # increment the alpha version + # e.g. v0.22.1-alpha.12 -> v0.22.1-alpha.13 + alpha="${last_tag##*-${suffix}.}" + next_alpha="$((alpha + 1))" + next_tag="${last_tag/%${alpha}/${next_alpha}}" +else + # increment the patch and start the alpha version from 0 + # e.g. v0.22.0 -> v0.22.1-alpha.0 + next_tag="${next_release}-${suffix}.0" +fi +# update the crate version +msg="# crate version" +#sed -E -i "s/^version = .* ${msg}$/version = \"${next_tag#v}\" ${msg}/" Cargo.toml +echo "NEXT_TAG=${next_tag}" >> $GITHUB_ENV +echo "🐭 Next alpha release: ${next_tag}" diff --git a/.github/workflows/release_binaries.yml b/.github/workflows/release_binaries.yml index 81d090e..8a32f91 100644 --- a/.github/workflows/release_binaries.yml +++ b/.github/workflows/release_binaries.yml @@ -10,6 +10,10 @@ on: - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" workflow_dispatch: +defaults: + run: + shell: bash + jobs: changelog: name: Generate changelog @@ -22,16 +26,26 @@ jobs: with: fetch-depth: 0 + - name: Calculate the next release + run: .github/workflows/calculate-alpha-release.bash + - name: Generate a changelog uses: orhun/git-cliff-action@v3 id: git-cliff with: config: ./cliff.toml - args: -vv --latest --strip header + args: -vv --unreleased --tag ${{ env.NEXT_TAG }} --strip header env: OUTPUT: CHANGELOG.md GITHUB_REPO: ${{ github.repository }} + # - name: Publish on GitHub + # uses: ncipollo/release-action@v1 + # with: + # tag: ${{ env.NEXT_TAG }} + # prerelease: true + # bodyFile: CHANGELOG.md + build_binaries: runs-on: ubuntu-latest needs: changelog diff --git a/cliff.toml b/cliff.toml index 65968ee..a0f5e2d 100644 --- a/cliff.toml +++ b/cliff.toml @@ -1,41 +1,93 @@ -# git-cliff ~ default configuration file +# git-cliff ~ configuration file # https://git-cliff.org/docs/configuration -# -# Lines starting with "#" are comments. -# Configuration options are organized into tables and keys. -# See documentation for more information on available options. + +[remote.github] +owner = "utnet-org" +repo = "utility" [changelog] # changelog header header = """ -# Changelog\n -All notable changes to this project will be documented in this file.\n +# Changelog + +All notable changes to this project will be documented in this file. """ # template for the changelog body # https://keats.github.io/tera/docs/#introduction +# note that the - before / after the % controls whether whitespace is rendered between each line. +# Getting this right so that the markdown renders with the correct number of lines between headings +# code fences and list items is pretty finicky. Note also that the 4 backticks in the commit macro +# is intentional as this escapes any backticks in the commit body. body = """ -{% if version %}\ - ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} -{% else %}\ - ## [unreleased] -{% endif %}\ +{%- if not version %} +## [unreleased] +{% else -%} +## [{{ version }}](https://github.com/utnet-org/utility/releases/tag/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} +{% endif -%} + +{% macro commit(commit) -%} +- [{{ commit.id | truncate(length=7, end="") }}]({{ "https://github.com/utnet-org/utility/commit/" ~ commit.id }}) \ + *({{commit.scope | default(value = "uncategorized") | lower }})* {{ commit.message | upper_first | trim }}\ + {% if commit.github.username %} by @{{ commit.github.username }}{%- endif -%}\ + {% if commit.github.pr_number %} in [#{{ commit.github.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.github.pr_number }}){%- endif %}\ +{%- if commit.breaking %} [**breaking**]{% endif %} +{%- if commit.body %} + + ````text {#- 4 backticks escape any backticks in body #} + {{commit.body | indent(prefix=" ") }} + ```` +{%- endif %} +{%- for footer in commit.footers %} +{%- if footer.token != "Signed-off-by" and footer.token != "Co-authored-by" %} + + {{ footer.token | indent(prefix=" ") }}{{ footer.separator }}{{ footer.value }} +{%- endif %} +{%- endfor %} +{% endmacro -%} + {% for group, commits in commits | group_by(attribute="group") %} - ### {{ group | upper_first }} - {% for commit in commits %} - - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\ - {% endfor %} -{% endfor %}\n +### {{ group | striptags | trim | upper_first }} +{% for commit in commits | filter(attribute="scope") | sort(attribute="scope") %} +{{ self::commit(commit=commit) }} +{%- endfor -%} +{% for commit in commits %} +{%- if not commit.scope %} +{{ self::commit(commit=commit) }} +{%- endif -%} +{%- endfor -%} +{%- endfor %} + +{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} +### New Contributors +{%- endif %}\ +{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} +* @{{ contributor.username }} made their first contribution +{%- if contributor.pr_number %} in \ +[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ +{%- endif %} +{%- endfor -%} + +{% if version %} +{% if previous.version %} +**Full Changelog**: {{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }} +{% endif %} +{% else -%} + {% raw %}\n{% endraw %} +{% endif %} + +{%- macro remote_url() -%} +https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}\ +{% endmacro %} """ + + # remove the leading and trailing whitespace from the template -trim = true +trim = false # changelog footer footer = """ """ -# postprocessors -postprocessors = [ - # { pattern = '', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL -] + [git] # parse the commits based on https://www.conventionalcommits.org conventional_commits = true @@ -45,39 +97,46 @@ filter_unconventional = true split_commits = false # regex for preprocessing the commit messages commit_preprocessors = [ - # { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, # replace issue numbers + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, + { pattern = '(better safe shared layout cache)', replace = "perf(layout): ${1}" }, + { pattern = '(Clarify README.md)', replace = "docs(readme): ${1}" }, + { pattern = '(Update README.md)', replace = "docs(readme): ${1}" }, + { pattern = '(fix typos|Fix typos)', replace = "fix: ${1}" }, ] # regex for parsing and grouping commits 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 = "^feat", group = "Features" }, + { message = "^[fF]ix", group = "Bug Fixes" }, + { message = "^refactor", group = "Refactor" }, + { message = "^doc", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { 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 = "^chore\\(deps\\)", skip = true }, + { message = "^chore\\(changelog\\)", skip = true }, + { message = "^[cC]hore", group = "Miscellaneous Tasks" }, + { body = ".*security", group = "Security" }, + { message = "^build", group = "Build" }, + { message = "^ci", group = "Continuous Integration" }, + { message = "^revert", group = "Reverted Commits" }, + # handle some old commits styles from pre 0.4 + { message = "^(Buffer|buffer|Frame|frame|Gauge|gauge|Paragraph|paragraph):", group = "Miscellaneous Tasks" }, + { message = "^\\[", group = "Miscellaneous Tasks" }, ] # protect breaking changes from being skipped due to matching a skipping commit_parser -protect_breaking_commits = true +protect_breaking_commits = false # filter out the commits that are not matched by commit parsers filter_commits = false -# regex for matching git tags -tag_pattern = "v[0-9].*" - +# glob pattern for matching git tags +tag_pattern = "v[0-9]*" # regex for skipping tags -skip_tags = "v0.1.0-beta.1" +skip_tags = "v0.1.0-rc.1" # regex for ignoring tags -ignore_tags = "" +ignore_tags = "alpha" # sort the tags topologically -topo_order = true +topo_order = false # sort the commits inside sections by oldest/newest order -sort_commits = "oldest" -# limit the number of commits included in the changelog. -# limit_commits = 42 +sort_commits = "newest"