Skip to content

Commit 0598248

Browse files
authored
feat: git-cliff-release and prepare-pypi-distribution actions, python_bump_and_update_changelog workflow (#133)
* Add release-metadata action * Checkout the repo under the github actions workspace * Add github token * Remove unnecessary 'v' * Look before you leap * Add version constraint * Fix <REPO> substitution * Add PyPI publishing workflow * Add bump_and_update_changelog workflow * Access github token better * Fix workflow output * PyPI publication must be an action instead of reusable workflow * Add missing shell * Add readme for prepare-pypi-distribution * Rename to git-cliff-release
1 parent 114e7e1 commit 0598248

File tree

8 files changed

+548
-0
lines changed

8 files changed

+548
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Bump version and update changelog
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
python_version:
7+
required: false
8+
type: string
9+
default: 3.12
10+
version_number:
11+
description: Version number of the new release (no leading "v")
12+
required: true
13+
type: string
14+
changelog:
15+
description: The complete changelog
16+
required: true
17+
type: string
18+
secrets:
19+
APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN:
20+
required: true
21+
outputs:
22+
changelog_commitish:
23+
description: Git HEAD after the commit and push
24+
value: ${{ jobs.bump_and_update_changelog.outputs.changelog_commitish }}
25+
26+
jobs:
27+
bump_and_update_changelog:
28+
runs-on: ubuntu-latest
29+
outputs:
30+
changelog_commitish: ${{ steps.commit.outputs.commit_long_sha || github.sha }}
31+
32+
steps:
33+
- name: Checkout repository
34+
uses: actions/checkout@v4
35+
with:
36+
token: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
37+
38+
- name: Set up Python
39+
uses: actions/setup-python@v5
40+
with:
41+
python-version: ${{ inputs.python_version }}
42+
43+
- name: Install poetry
44+
run: pipx install --python ${{ inputs.python_version }} poetry
45+
46+
- name: Update package version in pyproject.toml
47+
run: poetry version ${{ inputs.version_number }}
48+
49+
- name: Update CHANGELOG.md
50+
uses: DamianReeves/write-file-action@master
51+
with:
52+
path: CHANGELOG.md
53+
write-mode: overwrite
54+
contents: ${{ inputs.changelog }}
55+
56+
- name: Commit changes
57+
id: commit
58+
uses: EndBug/add-and-commit@v9
59+
with:
60+
author_name: Apify Release Bot
61+
author_email: [email protected]
62+
message: "chore(release): Update changelog and package version [skip ci]"

git-cliff-release/README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# `git-cliff-release` Github Action
2+
3+
This action uses conventional commit history to determine the recommended version for a release and generate a changelog and release notes.
4+
5+
## Inputs
6+
7+
- **release_type**: One of `auto` (default), `patch`, `minor`, `major` and `custom`. `auto` means that the version will be determined based on the commit history, `custom` will use the value of the `custom_version` input parameter, and `patch`, `minor` and `major` allow forcing the bump type.
8+
- **custom_version**: Optional unless the `release_type` is set to `custom`.
9+
- **cliff_config_path**: Path to a configuration file for git-cliff. If none is given, a built-in configuration will be used.
10+
11+
## Outputs
12+
13+
- **is_prerelease**: For convenience - was the action triggered with release_type = "prerelease"?
14+
- **version_number**: Version number of the new release (no leading "v")
15+
- **tag_name**: Tag name for the new release (with a leading "v")
16+
- **release_notes**: Release notes for the new release
17+
- **changelog**: The complete changelog
18+
19+
## Example usage
20+
21+
Update the changelog on each push to master so that it contains an up-to-date description of the not-yet-released changes:
22+
23+
```yaml
24+
name: Pre-release
25+
26+
on:
27+
push:
28+
branches:
29+
- master
30+
31+
jobs:
32+
test:
33+
runs-on: ubuntu-latest
34+
steps:
35+
- name: Checkout
36+
uses: actions/checkout@v4
37+
- name: Prepare release metadata
38+
id: metadata
39+
uses: apify/workflows/git-cliff-release@main
40+
with:
41+
release_type: prerelease
42+
- name: Update CHANGELOG.md
43+
uses: DamianReeves/write-file-action@master
44+
with:
45+
path: CHANGELOG.md
46+
write-mode: overwrite
47+
contents: ${{ steps.metadata.outputs.changelog }}
48+
- name: Commit changes
49+
uses: EndBug/add-and-commit@v9
50+
with:
51+
author_name: Foo
52+
author_email: [email protected]
53+
message: "chore(release): Update changelog and package version [skip ci]"
54+
```
55+
56+
Manually trigger a release:
57+
58+
```yaml
59+
name: Release
60+
61+
on:
62+
workflow_dispatch:
63+
inputs:
64+
release_type:
65+
description: Release type
66+
required: true
67+
type: choice
68+
default: auto
69+
options:
70+
- auto
71+
- patch
72+
- minor
73+
- major
74+
75+
jobs:
76+
test:
77+
runs-on: ubuntu-latest
78+
env:
79+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
80+
steps:
81+
- name: Checkout
82+
uses: actions/checkout@v4
83+
- name: Prepare release metadata
84+
id: metadata
85+
uses: apify/workflows/git-cliff-release@main
86+
with:
87+
release_type: ${{ inputs.release_type }}
88+
- name: Update CHANGELOG.md
89+
uses: DamianReeves/write-file-action@master
90+
with:
91+
path: CHANGELOG.md
92+
write-mode: overwrite
93+
contents: ${{ steps.metadata.outputs.changelog }}
94+
- name: Commit changes
95+
id: commit
96+
uses: EndBug/add-and-commit@v9
97+
with:
98+
author_name: Foo
99+
author_email: [email protected]
100+
message: "chore(release): Update changelog and package version [skip ci]"
101+
- name: Create release
102+
uses: softprops/action-gh-release@v2
103+
with:
104+
tag_name: ${{ steps.metadata.outputs.tag_name }}
105+
name: ${{ steps.metadata.outputs.version_number }}
106+
target_commitish: ${{ steps.commit.commit_long_sha || github.sha }}
107+
body: ${{ steps.metadata.outputs.release_notes }}
108+
```

git-cliff-release/action.yaml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Prepare release metadata using git-cliff
2+
description: Prepare release metadata based on conventional commits using git-cliff
3+
inputs:
4+
release_type:
5+
description: Release type
6+
required: true
7+
type: choice
8+
default: auto
9+
options:
10+
- auto
11+
- prerelease
12+
- patch
13+
- minor
14+
- major
15+
- custom
16+
custom_version:
17+
description: The custom version to bump to (only for "custom" type), with or without a leading "v"
18+
required: false
19+
type: string
20+
default: ""
21+
cliff_config_path:
22+
description: Path to a custom git-cliff configuration file
23+
required: false
24+
type: string
25+
default: cliff.toml
26+
outputs:
27+
is_prerelease:
28+
description: For convenience - was the action triggered with release_type = "prerelease"?
29+
value: ${{ steps.version_number.outputs.is_prerelease }}
30+
version_number:
31+
description: Version number of the new release (no leading "v")
32+
value: ${{ steps.version_number.outputs.version_number }}
33+
tag_name:
34+
description: Tag name for the new release (with a leading "v")
35+
value: ${{ steps.version_number.outputs.tag_name }}
36+
release_notes:
37+
description: Release notes for the new release
38+
value: ${{ steps.release_notes.outputs.release_notes }}
39+
changelog:
40+
description: The complete changelog
41+
value: ${{ steps.changelog.outputs.changelog }}
42+
runs:
43+
using: composite
44+
steps:
45+
- name: Checkout
46+
uses: actions/checkout@v4
47+
with:
48+
fetch-depth: 0
49+
path: ${{ github.workspace }}/__release_metadata_repo
50+
- name: Install git-cliff
51+
shell: bash
52+
run: pip install git-cliff
53+
- name: Locally remove beta tags
54+
shell: bash
55+
working-directory: ${{ github.workspace }}/__release_metadata_repo
56+
run: |
57+
if git tag | grep 'b[0-9]' > /dev/null 2> /dev/null; then
58+
git tag | grep 'b[0-9]' | xargs -r git tag --delete
59+
fi
60+
- name: Determine version number
61+
id: version_number
62+
shell: bash
63+
env:
64+
GIT_CLIFF_CONFIG: ${{ inputs.cliff_config_path }}
65+
GIT_CLIFF_REPOSITORY: ${{ github.workspace }}/__release_metadata_repo
66+
GIT_CLIFF_WORKDIR: ${{ github.action_path }}
67+
GITHUB_REPO: ${{ github.repository }}
68+
GH_TOKEN: ${{ github.token }}
69+
run: |
70+
if [[ '${{ inputs.release_type }}' = custom ]]; then
71+
version_number=$(echo ${{ inputs.custom_version }} | sed s/^v//)
72+
elif [[ '${{ inputs.release_type }}' = auto || '${{ inputs.release_type }}' = prerelease ]]; then
73+
version_number=$(git-cliff --bumped-version | sed s/^v//)
74+
else
75+
version_number=$(git-cliff --bump '${{ inputs.release_type }}' --context | jq -r '.[0].version' | sed s/^v//)
76+
fi
77+
echo version_number=$version_number | tee -a $GITHUB_OUTPUT
78+
echo tag_name=v$version_number | tee -a $GITHUB_OUTPUT
79+
echo is_prerelease=${{ inputs.release_type == 'prerelease' }} | tee -a $GITHUB_OUTPUT
80+
- name: Generate release notes
81+
id: release_notes
82+
shell: bash
83+
env:
84+
GIT_CLIFF_CONFIG: ${{ inputs.cliff_config_path }}
85+
GIT_CLIFF_REPOSITORY: ${{ github.workspace }}/__release_metadata_repo
86+
GIT_CLIFF_WORKDIR: ${{ github.action_path }}
87+
GITHUB_REPO: ${{ github.repository }}
88+
GH_TOKEN: ${{ github.token }}
89+
run: |
90+
echo 'release_notes<<EOF' >> $GITHUB_OUTPUT
91+
git-cliff --tag "${{ steps.version_number.outputs.tag_name }}" --unreleased --strip all | tee -a $GITHUB_OUTPUT
92+
echo 'EOF' >> $GITHUB_OUTPUT
93+
- name: Generate changelog
94+
id: changelog
95+
shell: bash
96+
env:
97+
GIT_CLIFF_CONFIG: ${{ inputs.cliff_config_path }}
98+
GIT_CLIFF_REPOSITORY: ${{ github.workspace }}/__release_metadata_repo
99+
GIT_CLIFF_WORKDIR: ${{ github.action_path }}
100+
GITHUB_REPO: ${{ github.repository }}
101+
GH_TOKEN: ${{ github.token }}
102+
run: |
103+
if [[ ${{ inputs.release_type }} = prerelease ]]; then
104+
echo 'changelog<<EOF' >> $GITHUB_OUTPUT
105+
git-cliff --with-tag-message "${{ steps.version_number.outputs.tag_name }}" >> $GITHUB_OUTPUT
106+
echo 'EOF' >> $GITHUB_OUTPUT
107+
else
108+
echo 'changelog<<EOF' >> $GITHUB_OUTPUT
109+
git-cliff --tag "${{ steps.version_number.outputs.tag_name }}" >> $GITHUB_OUTPUT
110+
echo 'EOF' >> $GITHUB_OUTPUT
111+
fi
112+
- name: Clean up
113+
shell: bash
114+
env:
115+
GIT_CLIFF_REPOSITORY: ${{ github.workspace }}/__release_metadata_repo
116+
run: |
117+
rm -rf $GIT_CLIFF_REPOSITORY

git-cliff-release/cliff.toml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# git-cliff ~ default configuration file
2+
# https://git-cliff.org/docs/configuration
3+
#
4+
# Lines starting with "#" are comments.
5+
# Configuration options are organized into tables and keys.
6+
# See documentation for more information on available options.
7+
8+
[changelog]
9+
# changelog header
10+
header = """
11+
# Changelog\n
12+
All notable changes to this project will be documented in this file.\n
13+
"""
14+
# template for the changelog body
15+
# https://keats.github.io/tera/docs/#introduction
16+
body = """
17+
{% if version %}\
18+
## [{{ version | trim_start_matches(pat="v") }}](<REPO>/releases/tag/{{ version }}) ({{ timestamp | date(format="%Y-%m-%d") }})
19+
{% elif message %}\
20+
## {{ message | trim_start_matches(pat="v") }} - **not yet released**
21+
{% else %}\
22+
## unreleased
23+
{% endif %}\
24+
{% for group, commits in commits | group_by(attribute="group") %}
25+
### {{ group | striptags | trim | upper_first }}
26+
{% for commit in commits %}
27+
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
28+
{% if commit.breaking %}[**breaking**] {% endif %}\
29+
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length = 7, end = "") }}](<REPO>/commit/{{ commit.id }}))\
30+
{% if commit.remote.username %} by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }}){%- endif %}\
31+
{% endfor %}
32+
{% endfor %}\n
33+
"""
34+
# template for the changelog footer
35+
footer = """
36+
<!-- generated by git-cliff -->
37+
"""
38+
# remove the leading and trailing s
39+
trim = true
40+
# postprocessors
41+
postprocessors = [
42+
{ pattern = '<REPO>', replace_command = 'sed "s^<REPO>^https://github.com/$GITHUB_REPO^g"' }, # replace repository URL
43+
]
44+
45+
[bump]
46+
# With 0.x.y version, breaking commits should only increase the minor version and feature commits should only increase the patch version
47+
breaking_always_bump_major = false
48+
features_always_bump_minor = false
49+
50+
[git]
51+
# parse the commits based on https://www.conventionalcommits.org
52+
conventional_commits = true
53+
# filter out the commits that are not conventional
54+
filter_unconventional = true
55+
# process each line of a commit as an individual commit
56+
split_commits = false
57+
# regex for preprocessing the commit messages
58+
commit_preprocessors = [
59+
# Replace PR and issue numbers in commit messages
60+
{ pattern = '.*', replace_command = 'python $GIT_CLIFF_WORKDIR/preprocess_commit_message.py'},
61+
# Check spelling of the commit with https://github.com/crate-ci/typos
62+
# If the spelling is incorrect, it will be automatically fixed.
63+
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
64+
]
65+
# regex for parsing and grouping commits
66+
commit_parsers = [
67+
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
68+
{ message = "^fix|^bug", group = "<!-- 1 -->🐛 Bug Fixes" },
69+
# { message = "^doc", group = "<!-- 3 -->📚 Documentation" },
70+
{ message = "^doc", skip = true },
71+
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
72+
# { message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
73+
{ message = "^refactor", skip = true },
74+
# { message = "^style", group = "<!-- 5 -->🎨 Styling" },
75+
{ message = "^style", skip = true },
76+
# { message = "^test", group = "<!-- 6 -->🧪 Testing" },
77+
{ message = "^test", skip = true },
78+
{ message = "^chore\\(release\\): prepare for", skip = true },
79+
{ message = "^chore\\(deps.*\\)", skip = true },
80+
{ message = "^chore\\(pr\\)", skip = true },
81+
{ message = "^chore\\(pull\\)", skip = true },
82+
# { message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
83+
{ message = "^chore|^ci", skip = true },
84+
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
85+
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
86+
]
87+
# protect breaking changes from being skipped due to matching a skipping commit_parser
88+
protect_breaking_commits = true
89+
# filter out the commits that are not matched by commit parsers
90+
filter_commits = false
91+
# regex for matching git tags
92+
tag_pattern = "v[0-9]+\\."
93+
# sort the tags topologically
94+
topo_order = false
95+
# sort the commits inside sections by oldest/newest order
96+
sort_commits = "oldest"
97+
# limit the number of commits included in the changelog.
98+
# limit_commits = 42

0 commit comments

Comments
 (0)