Skip to content

Commit 37e253f

Browse files
authored
chore: automate releases using git cliff (#28)
* Automate releases using git cliff * Check that the PR title is conventional
1 parent ae61f91 commit 37e253f

File tree

5 files changed

+210
-59
lines changed

5 files changed

+210
-59
lines changed

.github/workflows/conventional-pr.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: conventional-pr
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
- master
7+
types:
8+
- opened
9+
- edited
10+
- synchronize
11+
jobs:
12+
lint-pr:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v2
16+
- uses: CondeNast/conventional-pull-request-action@v0.2.0
17+
env:
18+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/release.yml

Lines changed: 60 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -7,81 +7,84 @@ on:
77
workflow_dispatch:
88
inputs:
99
version:
10-
description: 'Version to release (optional)'
11-
required: false
10+
description: 'The version to release'
1211
type: string
1312

14-
concurrency:
15-
group: release-${{ github.workflow }}-${{ github.ref }}
16-
cancel-in-progress: true
17-
1813
permissions:
1914
contents: write
2015
pull-requests: read
2116
statuses: write
17+
packages: write
2218

2319
jobs:
24-
continuous:
25-
name: Continuous
26-
runs-on: ubuntu-latest
20+
release:
21+
name: Release
22+
runs-on: 'ubuntu-latest'
23+
timeout-minutes: 15
24+
if: "!startsWith(github.event.head_commit.message, '[Release]')"
2725
steps:
28-
- uses: actions/checkout@v4
29-
- name: Determine version
30-
id: get_version
31-
run: |
32-
if [ -n "${{ github.event.inputs.version }}" ]; then
33-
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
34-
else
35-
echo "No version provided, calculating next version..."
36-
fi
37-
- uses: rmeneely/git-next-version@v1
26+
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
3827
with:
39-
tag_pattern: '[0-9]*.[0-9]*.[0-9]*'
40-
increment: 'minor'
41-
- name: Set final version
42-
id: set_version
28+
fetch-depth: 0
29+
- uses: jdx/mise-action@v2
30+
with:
31+
experimental: true
32+
- name: Check if there are releasable changes
33+
id: is-releasable
4334
run: |
44-
if [ -n "${{ steps.get_version.outputs.VERSION }}" ]; then
45-
echo "version=${{ steps.get_version.outputs.VERSION }}" >> $GITHUB_OUTPUT
35+
# Run git cliff and save the output
36+
bumped_output=$(git cliff --bump)
37+
echo "Bumped output:"
38+
echo "${bumped_output}"
39+
40+
# Read the content of CHANGELOG.md
41+
changelog_content=$(cat CHANGELOG.md)
42+
echo "CHANGELOG.md content:"
43+
echo "${changelog_content}"
44+
45+
# Compare the outputs and set the result
46+
if [ "${bumped_output}" = "${changelog_content}" ]; then
47+
echo "should-release=false" >> $GITHUB_ENV
4648
else
47-
echo "version=${{ env.NEXT_VERSION }}" >> $GITHUB_OUTPUT
49+
echo "should-release=true" >> $GITHUB_ENV
4850
fi
49-
- name: "Generate Tuist Changelog"
50-
id: changelog
51-
uses: mikepenz/release-changelog-builder-action@v4
52-
with:
53-
owner: "tuist"
54-
repo: "XcodeGraph"
55-
configuration: ".github/changelog-configuration.json"
51+
52+
- name: Get next version
53+
id: next-version
54+
if: env.should-release == 'true'
55+
env:
56+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57+
run: echo "NEXT_VERSION=$(git cliff --bumped-version)" >> "$GITHUB_OUTPUT"
58+
- name: Get release notes
59+
id: release-notes
60+
if: env.should-release == 'true'
5661
env:
5762
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58-
- name: Check if there are categorized PRs
59-
id: check_prs
6063
run: |
61-
if [ "${{ steps.changelog.outputs.categorized_prs }}" = "0" ]; then
62-
echo "skip_next_steps=true" >> $GITHUB_OUTPUT
63-
else
64-
echo "skip_next_steps=false" >> $GITHUB_OUTPUT
65-
fi
66-
- name: Update Changelog
67-
uses: stefanzweifel/changelog-updater-action@v1
68-
if: steps.check_prs.outputs.skip_next_steps != 'true'
69-
with:
70-
latest-version: ${{ steps.set_version.outputs.version }}
71-
release-notes: ${{ steps.changelog.outputs.changelog }}
72-
path-to-changelog: CHANGELOG.md
73-
- uses: stefanzweifel/git-auto-commit-action@v5
74-
if: steps.check_prs.outputs.skip_next_steps != 'true'
64+
echo "RELEASE_NOTES<<EOF" >> "$GITHUB_OUTPUT"
65+
git cliff --unreleased >> "$GITHUB_OUTPUT"
66+
echo "EOF" >> "$GITHUB_OUTPUT"
67+
- name: Update CHANGELOG.md
68+
if: env.should-release == 'true'
69+
env:
70+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71+
run: git cliff --bump -o CHANGELOG.md
72+
- name: Commit changes
73+
id: auto-commit-action
74+
uses: stefanzweifel/git-auto-commit-action@v5
75+
if: env.should-release == 'true'
7576
with:
76-
commit_message: "Version ${{ steps.set_version.outputs.version }}"
77-
tagging_message: ${{ steps.set_version.outputs.version }}
7877
commit_options: '--allow-empty'
79-
- name: Create GitHub Release on the tuist/XcodeGraph repository
80-
if: steps.check_prs.outputs.skip_next_steps != 'true'
81-
uses: softprops/action-gh-release@v1
78+
tagging_message: ${{ steps.next-version.outputs.NEXT_VERSION }}
79+
skip_dirty_check: true
80+
commit_message: "[Release] XcodeGraph ${{ steps.next-version.outputs.NEXT_VERSION }}"
81+
- name: Create GitHub Release
82+
uses: softprops/action-gh-release@v2
83+
if: env.should-release == 'true'
8284
with:
8385
draft: false
8486
repository: tuist/XcodeGraph
85-
name: ${{ steps.set_version.outputs.version }}
86-
tag_name: ${{ steps.set_version.outputs.version }}
87-
body: ${{ steps.changelog.outputs.changelog }}
87+
name: ${{ steps.next-version.outputs.NEXT_VERSION }}
88+
tag_name: ${{ steps.next-version.outputs.NEXT_VERSION }}
89+
body: ${{ steps.release-notes.outputs.RELEASE_NOTES }}
90+
target_commitish: ${{ steps.auto-commit-action.outputs.commit_hash }}

.mise.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
tuist = "4.16.1"
33
swiftlint = "0.54.0"
44
swiftformat = "0.53.3"
5+
"git-cliff" = "2.4.0"

CHANGELOG.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Changelog
22

3-
## 0.9.0 - 2024-07-30
3+
All notable changes to this project will be documented in this file.
44

5-
- no changes
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.8.0] - 2024-07-30
9+
### Details
10+
#### Chore
11+
- Update mikepenz/release-changelog-builder-action action to v4
12+
13+
#### Docs
14+
- Update .all-contributorsrc [skip ci]
15+
- Update README.md [skip ci]
16+
17+
## [0.7.0] - 2024-06-25
18+
### Details
19+
#### Docs
20+
- Create .all-contributorsrc [skip ci]
21+
- Update README.md [skip ci]
22+
23+
[0.8.0]: https://github.com/tuist/command/compare/0.7.0..0.8.0
24+
[0.7.0]: https://github.com/tuist/command/compare/0.6.0..0.7.0
25+
26+
<!-- generated by git-cliff -->

cliff.toml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# git-cliff ~ configuration file
2+
# https://git-cliff.org/docs/configuration
3+
4+
[remote.github]
5+
owner = "tuist"
6+
repo = "command"
7+
# token = ""
8+
9+
[changelog]
10+
# template for the changelog header
11+
header = """
12+
# Changelog\n
13+
All notable changes to this project will be documented in this file.
14+
15+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
16+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n
17+
"""
18+
# template for the changelog body
19+
# https://keats.github.io/tera/docs/#introduction
20+
body = """
21+
{%- macro remote_url() -%}
22+
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
23+
{%- endmacro -%}
24+
25+
{% if version -%}
26+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
27+
{% else -%}
28+
## [Unreleased]
29+
{% endif -%}
30+
31+
### Details\
32+
33+
{% for group, commits in commits | group_by(attribute="group") %}
34+
#### {{ group | upper_first }}
35+
{%- for commit in commits %}
36+
- {{ commit.message | upper_first | trim }}\
37+
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif -%}
38+
{% if commit.github.pr_number %} in \
39+
[#{{ commit.github.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.github.pr_number }}) \
40+
{%- endif -%}
41+
{% endfor %}
42+
{% endfor %}
43+
44+
{%- if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
45+
## New Contributors
46+
{%- endif -%}
47+
48+
{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
49+
* @{{ contributor.username }} made their first contribution
50+
{%- if contributor.pr_number %} in \
51+
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
52+
{%- endif %}
53+
{%- endfor %}\n
54+
"""
55+
# template for the changelog footer
56+
footer = """
57+
{%- macro remote_url() -%}
58+
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
59+
{%- endmacro -%}
60+
61+
{% for release in releases -%}
62+
{% if release.version -%}
63+
{% if release.previous.version -%}
64+
[{{ release.version | trim_start_matches(pat="v") }}]: \
65+
{{ self::remote_url() }}/compare/{{ release.previous.version }}..{{ release.version }}
66+
{% endif -%}
67+
{% else -%}
68+
[unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}..HEAD
69+
{% endif -%}
70+
{% endfor %}
71+
<!-- generated by git-cliff -->
72+
"""
73+
# remove the leading and trailing whitespace from the templates
74+
trim = true
75+
# postprocessors
76+
postprocessors = []
77+
78+
[git]
79+
# parse the commits based on https://www.conventionalcommits.org
80+
conventional_commits = true
81+
# filter out the commits that are not conventional
82+
filter_unconventional = true
83+
# process each line of a commit as an individual commit
84+
split_commits = false
85+
# regex for preprocessing the commit messages
86+
commit_preprocessors = [
87+
# remove issue numbers from commits
88+
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" },
89+
]
90+
# protect breaking changes from being skipped due to matching a skipping commit_parser
91+
protect_breaking_commits = false
92+
# filter out the commits that are not matched by commit parsers
93+
filter_commits = false
94+
# regex for matching git tags
95+
tag_pattern = "[0-9].*"
96+
# regex for skipping tags
97+
skip_tags = "beta|alpha"
98+
# regex for ignoring tags
99+
ignore_tags = "rc"
100+
# sort the tags topologically
101+
topo_order = false
102+
# sort the commits inside sections by oldest/newest order
103+
sort_commits = "newest"
104+
105+
[bump]
106+
breaking_always_bump_major = true
107+
features_always_bump_minor=true
108+
initial_tag="0.2.1"

0 commit comments

Comments
 (0)