Skip to content

Commit 6f7feb8

Browse files
authored
Add changelog and automate version bump and release workflows (#42)
- add a change log - updates CI triggers - adds a workflow to bump version numbers and create a GitHub release The new changelog is hosted in docs, but it is not updated until bumping version numbers (and creating a release). The intention is to keep merge conflicts minimal among simultaneous PRs.
1 parent 7732676 commit 6f7feb8

15 files changed

+534
-38
lines changed

.github/stale.yml

-1
This file was deleted.

.github/workflows/binary-builds.yml

+16-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@ permissions:
66
on:
77
push:
88
branches: [main]
9+
paths:
10+
- cpp-linter/src/**
11+
- cpp-linter/Cargo.toml
12+
- Cargo.toml
13+
- Cargo.lock
914
tags:
10-
- v[0-9]+.*
15+
- v*
1116
pull_request:
1217
branches: [main]
18+
paths:
19+
- cpp-linter/src/**
20+
- cpp-linter/Cargo.toml
21+
- Cargo.toml
22+
- Cargo.lock
1323

1424
env:
1525
CARGO_TERM_COLOR: always
@@ -124,7 +134,7 @@ jobs:
124134
path: cpp-linter-${{ matrix.target }}*
125135
if-no-files-found: error
126136

127-
create-release:
137+
publish:
128138
if: startswith(github.ref, 'refs/tags')
129139
runs-on: ubuntu-latest
130140
needs: [create-assets]
@@ -136,6 +146,9 @@ jobs:
136146
persist-credentials: false
137147
- name: Install Rust
138148
run: rustup update stable --no-self-update
149+
- uses: actions/setup-python@v5
150+
with:
151+
python-version: 3.x
139152
- name: Download built assets
140153
uses: actions/download-artifact@v4
141154
with:
@@ -147,7 +160,7 @@ jobs:
147160
GH_TOKEN: ${{ github.token }}
148161
run: |
149162
files=$(ls dist/cpp-linter*)
150-
gh release create ${{ github.ref_name }} ${{ contains(github.ref_name, 'rc') && '--prerelease' || '' }} --generate-notes $files
163+
gh release upload "${{ github.ref_name }}" $files
151164
- run: cargo publish -p cpp-linter
152165
env:
153166
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

.github/workflows/build-docs.yml

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
name: Docs
22

3-
on: [push, workflow_dispatch]
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- docs/**
8+
- .github/workflows/build-docs.yml
9+
- cpp-linter/src/**
10+
- '*.md'
11+
- '*/*.md'
12+
pull_request:
13+
branches: [main]
14+
paths:
15+
- docs/**
16+
- .github/workflows/build-docs.yml
17+
- cpp-linter/src/**
18+
- '*.md'
19+
- '*/*.md'
20+
workflow_dispatch:
421

522
env:
623
CARGO_TERM_COLOR: always

.github/workflows/bump-n-release.yml

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Bump-n-Release
2+
3+
# NOTE: The change log is only updated in the remote upon release (in `bump-release` job)
4+
5+
on:
6+
push:
7+
branches:
8+
- "main"
9+
pull_request:
10+
branches:
11+
- "main"
12+
workflow_dispatch:
13+
inputs:
14+
component:
15+
type: choice
16+
required: true
17+
default: patch
18+
options:
19+
- major
20+
- minor
21+
- patch
22+
- rc
23+
24+
jobs:
25+
bump-release:
26+
if: github.event_name == 'workflow_dispatch'
27+
runs-on: ubuntu-latest
28+
steps:
29+
- uses: actions/checkout@v4
30+
with:
31+
token: ${{ secrets.BUMP_N_RELEASE }}
32+
fetch-depth: 0
33+
- uses: actions/setup-python@v5
34+
with:
35+
python-version: 3.x
36+
- uses: cargo-bins/cargo-binstall@main
37+
- run: cargo binstall -y git-cliff
38+
- name: Bump ${{ inputs.component }} verion
39+
env:
40+
GITHUB_TOKEN: ${{ secrets.BUMP_N_RELEASE }}
41+
GH_TOKEN: ${{ secrets.BUMP_N_RELEASE }}
42+
run: python .github/workflows/bump_version.py ${{ inputs.component }}
43+
id: tagged
44+
45+
update-changelog:
46+
if: github.event_name != 'workflow_dispatch'
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/checkout@v4
50+
with:
51+
token: ${{ secrets.BUMP_N_RELEASE }}
52+
fetch-depth: 0
53+
- name: Generate a changelog
54+
uses: orhun/git-cliff-action@v4
55+
id: git-cliff
56+
with:
57+
config: cliff.toml
58+
args: --unreleased
59+
env:
60+
OUTPUT: ${{ runner.temp }}/changes.md
61+
GITHUB_REPO: ${{ github.repository }}
62+
GITHUB_TOKEN: ${{ secrets.BUMP_N_RELEASE }}
63+
- run: cat "${{ runner.temp }}/changes.md" >> "$GITHUB_STEP_SUMMARY"

.github/workflows/bump_version.py

+79-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
"""This script automates the release process for all of the packages in this repository.
2+
In order, this script does the following:
3+
4+
1. Bump version number in manifest files according to given required arg (see `--help`).
5+
This alters the Cargo.toml in repo root and the package.json files in node-binding.
6+
7+
Requires `yarn` (see https://yarnpkg.com) and `napi` (see https://napi.rs) to be
8+
installed to bump node-binding versions.
9+
10+
2. Updates the CHANGELOG.md
11+
12+
Requires `git-cliff` (see https://git-cliff.org) to be installed
13+
to regenerate the change logs from git history.
14+
15+
NOTE: `git cliff` uses GITHUB_TOKEN env var to access GitHub's REST API for
16+
fetching certain data (like PR labels and commit author's username).
17+
18+
3. Pushes the changes from above 2 steps to remote
19+
20+
4. Creates a GitHub Release and uses the section from the CHANGELOG about the new tag
21+
as a release description.
22+
23+
Requires `gh-cli` (see https://cli.github.com) to be installed to create the release
24+
and push the tag.
25+
26+
NOTE: This step also tags the commit from step 3.
27+
When a tag is pushed to the remote, the CI builds are triggered and
28+
29+
- release assets are uploaded to the Github Release corresponding to the new tag
30+
- packages are published for npm, pip, and cargo
31+
32+
NOTE: In a CI run, the GH_TOKEN env var to authenticate access.
33+
Locally, you can use `gh login` to interactively authenticate the user account.
34+
"""
35+
136
import argparse
237
from pathlib import Path
338
import subprocess
@@ -8,6 +43,7 @@
843
)
944
VER_REPLACE = 'version = "%d.%d.%d%s" # auto'
1045
COMPONENTS = ("major", "minor", "patch", "rc")
46+
VERSION_LOG = re.compile(r"^## \[\d+\.\d+\.\d+(?:\-rc)?\d*\]")
1147

1248

1349
class Updater:
@@ -40,10 +76,34 @@ def replace(match: re.Match[str]) -> str:
4076
return VER_REPLACE % (tuple(ver[:3]) + (rc_str,))
4177

4278

79+
def get_release_notes(tag: str = Updater.new_version):
80+
title, buf = "", ""
81+
log_file = Path(__file__).parent.parent.parent / "CHANGELOG.md"
82+
tag_pattern = f"[{tag}]"
83+
with open(str(log_file), "r", encoding="utf-8") as logs:
84+
found_notes = False
85+
for line in logs:
86+
matched = VERSION_LOG.match(line)
87+
if matched is not None:
88+
if tag_pattern in matched.group(0):
89+
title = tag + line[matched.end() :]
90+
found_notes = True
91+
else:
92+
found_notes = False
93+
elif line.startswith("[unreleased]: ") and found_notes:
94+
found_notes = False
95+
elif found_notes:
96+
buf += line
97+
elif line.startswith(tag_pattern + ": "):
98+
buf += line.replace(tag_pattern, "Full commit diff", 1)
99+
return title.rstrip(), buf.strip()
100+
101+
43102
def main():
44103
parser = argparse.ArgumentParser()
45104
parser.add_argument("component", default="patch", choices=COMPONENTS)
46105
parser.parse_args(namespace=Updater)
106+
47107
cargo_path = Path("Cargo.toml")
48108
doc = cargo_path.read_text(encoding="utf-8")
49109
doc = VER_PATTERN.sub(Updater.replace, doc)
@@ -63,20 +123,31 @@ def main():
63123
)
64124
subprocess.run(["napi", "version"], cwd="node-binding", check=True)
65125
print("Updated version in node-binding/**package.json")
66-
tag = "v" + Updater.new_version
126+
127+
subprocess.run(["git", "cliff", "--tag", Updater.new_version], check=True)
128+
print("Updated CHANGELOG.md")
129+
67130
subprocess.run(["git", "add", "--all"], check=True)
68-
subprocess.run(["git", "commit", "-m", f"bump version to {tag}"], check=True)
131+
tag = "v" + Updater.new_version
132+
subprocess.run(["git", "commit", "-m", f"Bump version to {tag}"], check=True)
69133
try:
70134
subprocess.run(["git", "push"], check=True)
71135
except subprocess.CalledProcessError as exc:
72-
raise RuntimeError("Failed to push commit for version bump") from exc
73-
print("Pushed commit to 'bump version to", tag, "'")
136+
raise RuntimeError(
137+
"""Failed to push commit for version bump. Please ensure that
138+
- You have the necessary permissions and are authenticated properly.
139+
- All other commits on the branch have ben pushed already."""
140+
) from exc
141+
title, notes = get_release_notes()
142+
print("Pushed commit to 'Bump version to", tag, "'")
143+
gh_cmd = ["gh", "release", "create", tag, "--title", title, "--notes", notes]
144+
if Updater.component == "rc":
145+
gh_cmd.append("--prerelease")
74146
try:
75-
subprocess.run(["git", "tag", tag], check=True)
147+
subprocess.run(gh_cmd, check=True)
148+
print("Created tag", tag, "and corresponding GitHub Release")
76149
except subprocess.CalledProcessError as exc:
77-
raise RuntimeError("Failed to create tag for commit") from exc
78-
print("Created tag", tag)
79-
print(f"Use 'git push origin refs/tags/{tag}' to publish a release")
150+
raise RuntimeError("Failed to create GitHub Release") from exc
80151

81152

82153
if __name__ == "__main__":

.github/workflows/node-js-packaging.yml

+27-10
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,30 @@ permissions:
88
id-token: write
99
on:
1010
push:
11-
branches:
12-
- main
11+
branches: [main]
12+
paths:
13+
- cpp-linter/src/**
14+
- cpp-linter/Cargo.toml
15+
- Cargo.toml
16+
- Cargo.lock
17+
- node-binding/**
18+
- package.json
19+
- yarn.lock
20+
- .github/workflows/node-js-packaging.yml
1321
tags:
1422
- '*'
15-
paths-ignore:
16-
- '**/*.md'
17-
- LICENSE
18-
- '**/*.gitignore'
19-
- .editorconfig
20-
- docs/**
2123
pull_request:
22-
branches:
23-
- main
24+
branches: [main]
25+
paths:
26+
- cpp-linter/src/**
27+
- cpp-linter/Cargo.toml
28+
- Cargo.toml
29+
- Cargo.lock
30+
- node-binding/**
31+
- package.json
32+
- yarn.lock
33+
- .github/workflows/node-js-packaging.yml
34+
2435
jobs:
2536
build:
2637
strategy:
@@ -218,3 +229,9 @@ jobs:
218229
env:
219230
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
220231
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
232+
# - name: Upload release assets
233+
# env:
234+
# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
235+
# run:
236+
# files=$(ls ./npm/**cpp-linter.*.node)
237+
# gh release upload "${{ github.ref_name }}" $files

.github/workflows/python-packaging.yml

+18-5
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,21 @@ name: Python builds
1414

1515
on:
1616
push:
17-
branches:
18-
- main
17+
branches: [main]
18+
paths:
19+
- cpp-linter/**.{rs,toml}
20+
- py-binding/**
21+
- Cargo.{toml,lock}
22+
- .github/workflows/python-packaging.yml
1923
tags:
2024
- '*'
2125
pull_request:
22-
branches:
23-
- main
24-
workflow_dispatch:
26+
branches: [main]
27+
paths:
28+
- cpp-linter/**.{rs,toml}
29+
- py-binding/**
30+
- Cargo.{toml,lock}
31+
- .github/workflows/python-packaging.yml
2532

2633
permissions:
2734
contents: read
@@ -180,3 +187,9 @@ jobs:
180187
# This workflow is registered as a trusted publisher (for test-pypi and pypi).
181188
# A token should not be required (and actually is discouraged with trusted publishers).
182189
repository-url: ${{ contains(github.ref_name, 'rc') && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }}
190+
# - name: Upload release assets
191+
# env:
192+
# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
193+
# run:
194+
# files=$(ls ./dist/cpp-linter*.{whl,atr.gz})
195+
# gh release upload "${{ github.ref_name }}" $files

.github/workflows/run-dev-tests.yml

+8-9
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ on:
44
push:
55
branches: [main]
66
paths:
7-
- "**.rs"
8-
- "*.toml"
9-
- "Cargo.lock"
10-
- ".github/workflows/run-dev-tests.yml"
7+
- cpp-linter/**
8+
- Cargo.toml
9+
- Cargo.lock
10+
- .github/workflows/run-dev-tests.yml
1111
pull_request:
12-
# types: opened
1312
branches: [main]
1413
paths:
15-
- "**.rs"
16-
- "*.toml"
17-
- "Cargo.lock"
18-
- ".github/workflows/run-dev-tests.yml"
14+
- cpp-linter/**
15+
- Cargo.toml
16+
- Cargo.lock
17+
- .github/workflows/run-dev-tests.yml
1918

2019
env:
2120
CARGO_TERM_COLOR: always

.github/workflows/stale.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: 'Close stale issues'
2+
on:
3+
schedule:
4+
- cron: '30 1 * * *'
5+
permissions:
6+
issues: write
7+
8+
jobs:
9+
stale:
10+
uses: cpp-linter/.github/.github/workflows/stale.yml@main

0 commit comments

Comments
 (0)