Skip to content

ci: add release automation via git-cliff#34

Merged
olantwin merged 2 commits into
mainfrom
worktree-release-automation
Jun 17, 2026
Merged

ci: add release automation via git-cliff#34
olantwin merged 2 commits into
mainfrom
worktree-release-automation

Conversation

@olantwin

@olantwin olantwin commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New scripts/release.sh to cut a release locally: validates input, bumps CMakeLists.txt VERSION, regenerates CHANGELOG.md via git cliff --tag v<version> -o, creates the release commit and annotated tag. Pushing is left to the operator.
  • New .github/workflows/release.yml triggered on v* tag push: uses orhun/git-cliff-action@v4 to extract the per-tag notes (--latest --strip header) and softprops/action-gh-release@v2 to publish the GitHub Release.

Together these turn the release procedure into: ./scripts/release.sh <version>git push origin main v<version> → workflow publishes the Release.

Test plan

  • release.sh rejects bad input: no args (exit 64), invalid semver (exit 64), pre-existing tag (exit 65), dirty working tree (exit 65).
  • Happy path in a throwaway clone: produces a single chore(release): v0.2.0 commit (touching only CMakeLists.txt + CHANGELOG.md), an annotated v0.2.0 tag, and a regenerated CHANGELOG.md covering all 37 commits since v0.1.0 grouped per cliff.toml.
  • After merge, cut v0.2.0 and confirm the workflow creates a GitHub Release with the expected notes.

Notes

  • The chore(release): ... commit message is already filtered out by cliff.toml, so it won't appear in future changelogs.
  • Local git cliff currently fails in clones using git's reftable ref storage (libgit2 limitation); run the script in a clone using the classic files ref backend, or convert with git refs migrate --ref-format=files. CI is unaffected.

Summary by CodeRabbit

  • Chores
    • Automated release creation workflow implemented.
    • Release management script added for streamlined version updates and changelog generation.

scripts/release.sh bumps CMakeLists.txt VERSION, regenerates
CHANGELOG.md with git-cliff, then commits and tags. Pushing is left
to the operator.

.github/workflows/release.yml triggers on tag push, regenerates the
per-tag release notes with git-cliff, and creates the GitHub Release.
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@olantwin, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 32 minutes and 4 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2c831d8e-5983-457f-b804-6f23061d23bb

📥 Commits

Reviewing files that changed from the base of the PR and between 2a96039 and 42db3ff.

📒 Files selected for processing (1)
  • scripts/release.sh
📝 Walkthrough

Walkthrough

Two new release automation artifacts are added: scripts/release.sh, a local Bash script that validates semver input, bumps VERSION in CMakeLists.txt, regenerates CHANGELOG.md via git-cliff, and creates a release commit plus annotated tag; and .github/workflows/release.yml, a GitHub Actions workflow that triggers on v* tag pushes to generate release notes and publish a GitHub Release.

Changes

Release Automation

Layer / File(s) Summary
Local release preparation script
scripts/release.sh
New executable Bash script that validates semver format, checks git and git-cliff availability, guards against a dirty working tree and duplicate tags, bumps VERSION in CMakeLists.txt with rollback on failure, regenerates CHANGELOG.md via git cliff, stages both files, creates a chore(release): <tag> commit and an annotated tag, and prints push instructions.
GitHub Actions release workflow
.github/workflows/release.yml
New workflow triggered by v* tag pushes that checks out the full repo history, runs orhun/git-cliff-action@v4 with cliff.toml to generate release notes, and creates a non-draft non-prerelease GitHub Release via softprops/action-gh-release@v2 using the generated changelog.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ci: add release automation via git-cliff' directly addresses the main changes: introducing CI/release automation through the git-cliff integration and release scripts.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-release-automation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@olantwin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
.github/workflows/release.yml (1)

17-20: 💤 Low value

Consider setting persist-credentials: false on checkout.

While this workflow doesn't perform subsequent git operations (so it's not strictly necessary), setting persist-credentials: false is security best practice to prevent credential leakage:

- name: Checkout
  uses: actions/checkout@v4
  with:
    fetch-depth: 0
    persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 17 - 20, The checkout step in the
release workflow uses actions/checkout@v4 but does not explicitly set
persist-credentials to false. Add persist-credentials: false to the with section
of the Checkout step alongside the existing fetch-depth: 0 configuration. This
follows security best practices by preventing GitHub credentials from being
persisted in the workflow environment, reducing the risk of credential leakage
even though this particular workflow doesn't perform subsequent git operations.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Line 24: The orhun/git-cliff-action reference in the workflow file is using a
version tag `@v4` instead of being pinned to a specific commit SHA, which can lead
to inconsistent behavior and security concerns. Replace the `@v4` reference with
the full commit SHA (d4c7180c0bf301e4aefbd7d5e91e93a3bd64e38a) to ensure
reproducibility and security. Verify this commit SHA matches the latest v4
release of the action by checking the action's official repository, then update
the uses field to use the commit SHA format with a comment indicating the
version for maintainability.
- Line 30: The `softprops/action-gh-release` action reference uses a version tag
(v2) instead of being pinned to a specific commit hash, which poses a security
risk. Replace the action reference from `softprops/action-gh-release@v2` with
the full commit SHA
`softprops/action-gh-release@de2c0eb89ae2a093876385947365ecca42febe4d` and add a
comment `# v2` to maintain readability while ensuring the action is locked to a
specific, immutable commit.
- Line 18: The actions/checkout action reference on line 18 is using a version
tag (v4) instead of being pinned to an exact commit hash, which violates
security best practices. Replace the `uses: actions/checkout@v4` reference with
the full commit SHA for that version, following the format `uses:
actions/checkout@<commit-sha>  # v4.x.x`, where the commit SHA should be
verified from the action's official release page or repository tags to ensure
you have the correct hash for the version you intend to use.

In `@scripts/release.sh`:
- Line 78: The sed -i -E command on line 78 that updates the CMAKE_FILE is not
portable to macOS, which requires a different syntax for the in-place edit flag.
Replace the sed -i -E invocation with a portable approach by either detecting
the sed variant (GNU vs BSD) and using the appropriate flag, or use a temp file
pattern that writes to a temporary file and then moves it back to CMAKE_FILE.
The temp file approach is more universally portable across different systems.
- Around line 47-55: The conditional logic at line 47 uses an AND operator to
check both git-cliff and git together, but this allows execution to proceed if
only git-cliff is found while git is missing, causing the subsequent git cliff
command at line 52 to fail with an uninformative error. Refactor by replacing
the compound AND conditional with two separate sequential checks: first verify
that the git command exists with a dedicated check and error message, then
separately verify that git-cliff is available with its own check and error
message. This ensures both required tools are validated before attempting to use
git cliff.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 17-20: The checkout step in the release workflow uses
actions/checkout@v4 but does not explicitly set persist-credentials to false.
Add persist-credentials: false to the with section of the Checkout step
alongside the existing fetch-depth: 0 configuration. This follows security best
practices by preventing GitHub credentials from being persisted in the workflow
environment, reducing the risk of credential leakage even though this particular
workflow doesn't perform subsequent git operations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 70f4a719-4c3d-4f7c-88cc-eac7df4ab6ba

📥 Commits

Reviewing files that changed from the base of the PR and between 9f6e8aa and 2a96039.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • scripts/release.sh

contents: write
steps:
- name: Checkout
uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Action reference actions/checkout@v4 is not pinned to a commit hash.

GitHub Actions best practice and security policy (enforced by zizmor) requires pinning actions to exact commit SHAs, not version tags. Tags can be re-pointed or hijacked.

Recommendation: Pin to a known commit hash. Example (verify exact SHA matches your intended version):

- uses: actions/checkout@a5ac7e51b41094c7418c8a7c5f280f24ab337563  # v4.1.0

You can find the correct SHA by:

  1. Visiting the action's release page and copying the commit hash from the tag, or
  2. Running: git ls-remote --heads https://github.com/actions/checkout refs/tags/v4 | awk '{print $1}'
🧰 Tools
🪛 zizmor (1.25.2)

[error] 18-18: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 18, The actions/checkout action
reference on line 18 is using a version tag (v4) instead of being pinned to an
exact commit hash, which violates security best practices. Replace the `uses:
actions/checkout@v4` reference with the full commit SHA for that version,
following the format `uses: actions/checkout@<commit-sha>  # v4.x.x`, where the
commit SHA should be verified from the action's official release page or
repository tags to ensure you have the correct hash for the version you intend
to use.

Source: Linters/SAST tools


- name: Generate release notes
id: cliff
uses: orhun/git-cliff-action@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Action reference orhun/git-cliff-action@v4 is not pinned to a commit hash.

Pin this action to a commit SHA for reproducibility and security:

- uses: orhun/git-cliff-action@d4c7180c0bf301e4aefbd7d5e91e93a3bd64e38a  # v4

Verify the exact commit SHA from the action's latest v4 release.

🧰 Tools
🪛 zizmor (1.25.2)

[error] 24-24: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 24, The orhun/git-cliff-action
reference in the workflow file is using a version tag `@v4` instead of being
pinned to a specific commit SHA, which can lead to inconsistent behavior and
security concerns. Replace the `@v4` reference with the full commit SHA
(d4c7180c0bf301e4aefbd7d5e91e93a3bd64e38a) to ensure reproducibility and
security. Verify this commit SHA matches the latest v4 release of the action by
checking the action's official repository, then update the uses field to use the
commit SHA format with a comment indicating the version for maintainability.

Source: Linters/SAST tools

args: --latest --strip header --tag ${{ github.ref_name }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Action reference softprops/action-gh-release@v2 is not pinned to a commit hash.

Pin this action to a commit SHA:

- uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365ecca42febe4d  # v2

Verify the exact commit SHA from the action's latest v2 release.

🧰 Tools
🪛 zizmor (1.25.2)

[error] 30-30: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[info] 30-30: action functionality is already included by the runner (superfluous-actions): use gh release in a script step

(superfluous-actions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 30, The `softprops/action-gh-release`
action reference uses a version tag (v2) instead of being pinned to a specific
commit hash, which poses a security risk. Replace the action reference from
`softprops/action-gh-release@v2` with the full commit SHA
`softprops/action-gh-release@de2c0eb89ae2a093876385947365ecca42febe4d` and add a
comment `# v2` to maintain readability while ensuring the action is locked to a
specific, immutable commit.

Source: Linters/SAST tools

Comment thread scripts/release.sh Outdated
Comment on lines +47 to +55
if ! command -v git-cliff >/dev/null 2>&1 && ! command -v git >/dev/null 2>&1; then
echo "error: git not found on PATH" >&2
exit 69
fi

if ! git cliff --version >/dev/null 2>&1; then
echo "error: 'git cliff' not available; install git-cliff (https://git-cliff.org/)" >&2
exit 69
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Logic error in tool availability check (line 47).

The conditional at line 47 evaluates to: "if (git-cliff NOT found) AND (git NOT found)", but then unconditionally attempts to use git cliff at line 52. If only git is missing, the first check passes (because git-cliff is present), but line 52 fails with an uninformative error.

Refactor to check git availability first (always required), then check git-cliff separately:

if ! command -v git >/dev/null 2>&1; then
    echo "error: git not found on PATH" >&2
    exit 69
fi

if ! git cliff --version >/dev/null 2>&1; then
    echo "error: 'git cliff' not available; install git-cliff (https://git-cliff.org/)" >&2
    exit 69
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release.sh` around lines 47 - 55, The conditional logic at line 47
uses an AND operator to check both git-cliff and git together, but this allows
execution to proceed if only git-cliff is found while git is missing, causing
the subsequent git cliff command at line 52 to fail with an uninformative error.
Refactor by replacing the compound AND conditional with two separate sequential
checks: first verify that the git command exists with a dedicated check and
error message, then separately verify that git-cliff is available with its own
check and error message. This ensures both required tools are validated before
attempting to use git cliff.

Comment thread scripts/release.sh
exit 70
fi

sed -i -E "s/^([[:space:]]*VERSION[[:space:]]+)[0-9]+\.[0-9]+\.[0-9]+/\1${VERSION}/" "${CMAKE_FILE}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

sed -i is not portable to macOS.

Line 78 uses sed -i -E, which works on Linux but fails on macOS (requires a backup suffix like sed -i '' -E or sed -i .bak -E).

If this script must run on developer machines or CI systems that include macOS, use a portable pattern via sed with -i and a platform-agnostic fallback, or use perl if it's a safe dependency. Alternatively, document that Linux is required.

🔧 Option 1: Use GNU sed-style with fallback
# Detect sed variant and use appropriate flag
if sed --version >/dev/null 2>&1; then
  # GNU sed
  sed -i -E "s/^([[:space:]]*VERSION[[:space:]]+)[0-9]+\.[0-9]+\.[0-9]+/\1${VERSION}/" "${CMAKE_FILE}"
else
  # BSD/macOS sed
  sed -i '' -E "s/^([[:space:]]*VERSION[[:space:]]+)[0-9]+\.[0-9]+\.[0-9]+/\1${VERSION}/" "${CMAKE_FILE}"
fi
🔧 Option 2: Use a temp file (most portable)
sed -E "s/^([[:space:]]*VERSION[[:space:]]+)[0-9]+\.[0-9]+\.[0-9]+/\1${VERSION}/" "${CMAKE_FILE}" > "${CMAKE_FILE}.tmp"
mv "${CMAKE_FILE}.tmp" "${CMAKE_FILE}"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release.sh` at line 78, The sed -i -E command on line 78 that updates
the CMAKE_FILE is not portable to macOS, which requires a different syntax for
the in-place edit flag. Replace the sed -i -E invocation with a portable
approach by either detecting the sed variant (GNU vs BSD) and using the
appropriate flag, or use a temp file pattern that writes to a temporary file and
then moves it back to CMAKE_FILE. The temp file approach is more universally
portable across different systems.

The compound `! cliff && ! git` check only fired when both were
missing — a missing git-cliff slipped through. Drop the block: git
is implicitly required by the `git rev-parse` call above, and the
`git cliff --version` check below already handles the missing-cliff
case with a clear error.
@olantwin olantwin merged commit f0fad18 into main Jun 17, 2026
3 checks passed
@olantwin olantwin deleted the worktree-release-automation branch June 17, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant