From 5d24d5dc9effbf7f0d3acc63c7f4bc36b64c63c5 Mon Sep 17 00:00:00 2001 From: "Victor A." <52110451+cs50victor@users.noreply.github.com> Date: Sat, 30 May 2026 12:22:37 -0700 Subject: [PATCH] feat: generate changelog and release notes from commits via git-cliff --- .github/workflows/release.yml | 30 ++++++++++++++--- CHANGELOG.md | 17 ++++++++++ cliff.toml | 61 +++++++++++++++++++++++++++++++++++ scripts/release.sh | 39 ++++++++++++++++++++++ 4 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 cliff.toml create mode 100755 scripts/release.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81c82ac..1234a42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,10 +1,12 @@ name: Release -# Tag a version to cut a release: -# cargo set-version 0.1.0 # or edit Cargo.toml -# git tag v0.1.0 && git push origin v0.1.0 +# Cut a release with scripts/release.sh, which bumps the version, regenerates +# CHANGELOG.md from Conventional Commits, commits, and tags: +# scripts/release.sh 0.2.0 +# git push origin main v0.2.0 # -# This builds a binary per platform, attaches them to the GitHub Release, and +# Pushing the tag runs this workflow: it builds a binary per platform, attaches them +# to the GitHub Release (notes generated from the same history by git-cliff), and # publishes the crate to crates.io. Because the assets are named # `frd-.{tar.gz,zip}` with the binary at the archive root, `cargo binstall frd` # finds them with no `[package.metadata.binstall]` config (it matches binstall's @@ -22,8 +24,26 @@ env: CARGO_TERM_COLOR: always jobs: + changelog: + name: release notes + runs-on: ubuntu-latest + outputs: + notes: ${{ steps.git-cliff.outputs.content }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 # git-cliff needs full history and tags + + - name: Generate release notes for the tag + id: git-cliff + uses: orhun/git-cliff-action@v4 + with: + config: cliff.toml + args: --latest --strip header + build: name: build ${{ matrix.target }} + needs: changelog runs-on: ${{ matrix.os }} permissions: contents: write # create the release and upload assets @@ -68,7 +88,7 @@ jobs: with: files: ${{ env.ASSET }} fail_on_unmatched_files: true - generate_release_notes: true + body: ${{ needs.changelog.outputs.notes }} publish-crate: name: publish to crates.io diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..91554d8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +## [unreleased] + +### Features +- Add doctor subcommand for read-only optimization audit ([#2](https://github.com/cs50victor/fast-rust-dev/pull/2)) + +## [0.1.0] - 2026-05-30 + +### Features +- Initial frd build and disk optimizer for macOS +- Cross-platform system probing via sysinfo plus release workflow and README ([#1](https://github.com/cs50victor/fast-rust-dev/pull/1)) + +### Documentation +- Rewrite package description to emphasize build speed and target size + + diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..7254975 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,61 @@ +# git-cliff configuration. See https://git-cliff.org/docs/configuration +# Generates CHANGELOG.md from Conventional Commits. Regenerated on every release by +# scripts/release.sh; the GitHub Release body is produced from the same history in CI. + +[remote.github] +owner = "cs50victor" +repo = "fast-rust-dev" + +[changelog] +header = "# Changelog\n\n" +body = """ +{% 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 | striptags | trim | upper_first }} + {% for commit in commits %}\ + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**] {% endif %}\ + {{ commit.message | upper_first }} + {% endfor %}\ +{% endfor %}\n +""" +footer = "\n" +trim = true +postprocessors = [ + { pattern = '', replace = "https://github.com/cs50victor/fast-rust-dev" }, +] + +[git] +conventional_commits = true +filter_unconventional = true +split_commits = false +commit_preprocessors = [ + # Turn "(#12)" into a linked PR reference, resolved by the postprocessor above. + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/pull/${2}))" }, +] +# First matching parser wins. Ordering controls section order via the prefix, +# which `striptags` removes from the rendered header. Internal types are skipped so the +# changelog stays user-facing, matching how uv and ruff ignore ci/chore/testing. +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Refactor" }, + { message = "^doc", group = "Documentation" }, + { message = "^style", skip = true }, + { message = "^test", skip = true }, + { message = "^ci", skip = true }, + { message = "^build", skip = true }, + { message = "^chore", skip = true }, + { message = "^revert", group = "Revert" }, + { body = ".*security", group = "Security" }, +] +protect_breaking_commits = false +filter_commits = false +tag_pattern = "v[0-9]*" +topo_order = false +sort_commits = "oldest" diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..1c737cf --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Cut a release: bump the version, regenerate CHANGELOG.md from Conventional Commits, +# commit, and tag. Review the result, then push to trigger the Release workflow. +# +# scripts/release.sh 0.2.0 +# git push origin main v0.2.0 +# +# Requires git-cliff (cargo binstall git-cliff) and cargo-set-version (cargo install cargo-edit). +set -euo pipefail + +version="${1:-}" +if [[ -z "$version" ]]; then + echo "usage: scripts/release.sh e.g. scripts/release.sh 0.2.0" >&2 + exit 2 +fi +tag="v${version}" + +cd "$(dirname "$0")/.." + +for tool in git-cliff cargo; do + command -v "$tool" >/dev/null || { echo "missing required tool: $tool" >&2; exit 1; } +done + +if [[ -n "$(git status --porcelain)" ]]; then + echo "working tree is dirty; commit or stash before releasing" >&2 + exit 1 +fi + +cargo set-version "$version" +# --tag assigns the unreleased commits to this version so the new section is dated now. +git-cliff --config cliff.toml --tag "$tag" --output CHANGELOG.md + +git add CHANGELOG.md Cargo.toml Cargo.lock +git commit -m "chore(release): $tag" +git tag "$tag" + +echo +echo "Tagged $tag. Review the commit, then push:" +echo " git push origin main $tag"