-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add automated changelog generation with git-cliff #180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add automated changelog generation with git-cliff #180
Conversation
Set up git-cliff for PR-based changelog generation with manual triggers. This implementation focuses on merge commits only to avoid noise from individual commits, as requested. **Added:** - cliff.toml: git-cliff configuration - Filters to only include PR merge commits - Categorizes by branch name patterns (feat/, fix/, optimize/, etc.) - Extracts PR descriptions from commit bodies - Generates clean, readable changelogs - .github/workflows/changelog.yml: Manual workflow for changelog generation - Can generate changelog for specific tags or unreleased changes - Optional commit and push of CHANGELOG.md - Uploads changelog as artifact - CHANGELOG.md: Initial changelog file - Follows Keep a Changelog format - Generated from PR merge commits only **Modified:** - .github/workflows/release.yml: - Updated to use git-cliff for release notes instead of bash script - Maintains installation instructions and verification sections - Generates cleaner release notes from PR descriptions **Features:** - PR-based changelog: Focuses on merge commits, filters out noise - Categorized changes: Features, Bug Fixes, Performance, etc. - Manual trigger: Uses workflow_dispatch for control - Dual output: Both CHANGELOG.md and GitHub release notes - Works with existing uv-dynamic-versioning setup
- Replace old changeset-based changelog task with git-cliff tasks - Add mise tasks: changelog, changelog-unreleased, changelog-update, changelog-tag - All tasks support both Linux and Windows - Update docs/versioning.md to replace changesets section with git-cliff documentation - Add usage examples and best practices for changelog management
Reviewer's GuideIntroduces git-cliff–based automated, PR-focused changelog generation, wires it into local mise tasks and GitHub workflows (including releases), replaces the previous changesets flow, and updates docs and the new CHANGELOG.md accordingly. Sequence diagram for release workflow using git-cliffsequenceDiagram
actor Maintainer
participant GitHub
participant release_workflow
participant git_cliff
participant Repo
participant GitHubRelease
Maintainer->>GitHub: Push tag vX.Y.Z
GitHub->>release_workflow: Trigger release workflow
release_workflow->>Repo: Checkout repository (fetch-depth 0)
release_workflow->>release_workflow: Determine VERSION and CURRENT_TAG
release_workflow->>Repo: git tag --sort to find PREVIOUS_TAG
release_workflow->>git_cliff: Install via taiki_e_install_action
alt Previous tag exists and differs from CURRENT_TAG
release_workflow->>git_cliff: git-cliff --strip header --tag CURRENT_TAG PREVIOUS_TAG..CURRENT_TAG
git_cliff-->>release_workflow: changelog_section.md
else First release or no previous tag
release_workflow->>git_cliff: git-cliff --strip header --tag CURRENT_TAG
git_cliff-->>release_workflow: changelog_section.md
end
release_workflow->>release_workflow: Build release_notes.md
release_workflow->>release_workflow: Append changelog_section.md
alt Pre-release version (alpha, beta, rc)
release_workflow->>release_workflow: Add pip install --pre code-weaver instructions
else Stable version
release_workflow->>release_workflow: Add pip install code-weaver instructions
end
release_workflow->>release_workflow: Append verification info and Full Changelog link
release_workflow->>GitHubRelease: Create release with release_notes.md
GitHubRelease-->>Maintainer: Published release with categorized PR-based notes
Sequence diagram for manual Generate Changelog workflowsequenceDiagram
actor Maintainer
participant GitHubUI
participant changelog_workflow
participant git_cliff
participant Repo
participant ArtifactStore
Maintainer->>GitHubUI: Open Actions / Generate Changelog
Maintainer->>GitHubUI: Run workflow (tag, commit options)
GitHubUI->>changelog_workflow: workflow_dispatch event
changelog_workflow->>Repo: Checkout repository (fetch-depth 0)
changelog_workflow->>git_cliff: Install via taiki_e_install_action
alt Tag input provided
changelog_workflow->>git_cliff: git-cliff --tag tag --output CHANGELOG.md --prepend
else No tag (unreleased changes)
changelog_workflow->>git_cliff: git-cliff --unreleased --prepend --output CHANGELOG.md
end
git_cliff-->>changelog_workflow: Updated CHANGELOG.md
changelog_workflow->>changelog_workflow: Display CHANGELOG.md in logs
alt Commit option true
changelog_workflow->>Repo: git add CHANGELOG.md
changelog_workflow->>Repo: git diff --staged --quiet
alt Has staged changes
changelog_workflow->>Repo: git commit with docs message
changelog_workflow->>Repo: git push
else No staged changes
changelog_workflow->>Maintainer: Log "No changes to commit"
end
end
changelog_workflow->>ArtifactStore: Upload CHANGELOG.md artifact
ArtifactStore-->>Maintainer: Downloadable changelog artifact
Flow diagram for local changelog mise tasksflowchart TD
Start([Developer wants changelog])
Start --> Choice{What do you want to do?}
Choice --> FullStdout["Run task changelog\n- git-cliff\n- Full history to stdout"]
Choice --> UnreleasedStdout["Run task changelog-unreleased\n- git-cliff --unreleased\n- Unreleased only to stdout"]
Choice --> UpdateFile["Run task changelog-update\n- git-cliff --unreleased --prepend --output CHANGELOG.md\n- Updates top of changelog"]
Choice --> TagUpdate["Run task changelog-tag <tag>\n- git-cliff --tag <tag> --prepend --output CHANGELOG.md\n- Adds tag section at top"]
FullStdout --> ViewOutput([View changelog in terminal])
UnreleasedStdout --> ViewOutput
UpdateFile --> InspectFile["Inspect CHANGELOG.md changes"]
TagUpdate --> InspectFile
InspectFile --> CommitChanges{Commit changes?}
CommitChanges -->|Yes| GitCommit["git add CHANGELOG.md\ngit commit"]
CommitChanges -->|No| End([Keep local-only changes])
GitCommit --> End
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- The
tag_pattern = "v[0-9].*"incliff.tomlwill match any tag starting withvand a single digit (including non-SemVer tags likev1beta); if you only want real release tags, consider tightening this to something like^v[0-9]+\.[0-9]+\.[0-9]+.*$to avoid pulling in unexpected tags. - The
commit_preprocessorspattern incliff.tomlassumes a single-line PR description and a specific merge commit format (Merge pull request #... from ...\n\nPR Description); if your PR bodies often have multiple lines or blank leading lines, you may want to adjust this regex to be more tolerant so descriptions are still captured reliably.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `tag_pattern = "v[0-9].*"` in `cliff.toml` will match any tag starting with `v` and a single digit (including non-SemVer tags like `v1beta`); if you only want real release tags, consider tightening this to something like `^v[0-9]+\.[0-9]+\.[0-9]+.*$` to avoid pulling in unexpected tags.
- The `commit_preprocessors` pattern in `cliff.toml` assumes a single-line PR description and a specific merge commit format (`Merge pull request #... from ...\n\nPR Description`); if your PR bodies often have multiple lines or blank leading lines, you may want to adjust this regex to be more tolerant so descriptions are still captured reliably.
## Individual Comments
### Comment 1
<location> `cliff.toml:77` </location>
<code_context>
+ # Extract PR description and append to subject for template use
+ # Input: "Merge pull request #123 from org/branch\n\nPR Description"
+ # Output: "Merge pull request #123 from org/branch | PR Description"
+ { pattern = 'Merge pull request (#[0-9]+) from ([^\n]+)\n+(.+)', replace = "Merge pull request ${1} from ${2} | ${3}" },
+]
+# Parse merge commits to extract PR info
</code_context>
<issue_to_address>
**issue:** The commit preprocessor and parsers assume merge-commit format and will drop other merge strategies due to `filter_commits = true`.
Because `filter_commits = true` and the current `commit_parsers` only match `Merge pull request ...`, any squash/rebase merges or custom merge messages will be excluded from the changelog. If you expect other merge strategies, consider adding parsers for those patterns or turning off `filter_commits` and using grouping/skipping instead, so future changes in merge strategy don’t produce empty or incomplete changelog sections.
</issue_to_address>
### Comment 2
<location> `mise.toml:354` </location>
<code_context>
+run = '''
+#!/usr/bin/env zsh
+print -P "%F{209}[codeweaver]%f Generating changelog for tag ${usage_tag}..."
+git-cliff --tag "${usage_tag}" --output CHANGELOG.md &&
+print -P "%F{209}[codeweaver]%f ✅ CHANGELOG.md generated for ${usage_tag}!"
+'''
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Generating a changelog for a tag overwrites the entire CHANGELOG.md, which may unexpectedly drop existing history.
Here `git-cliff --tag "${usage_tag}" --output CHANGELOG.md` fully replaces the file. If `CHANGELOG.md` is meant to be cumulative, this will erase older entries each time the task runs. To preserve history, consider either using `--prepend --output CHANGELOG.md` or writing per-tag output to a separate file (e.g., `CHANGELOG-${usage_tag}.md`).
Suggested implementation:
```
run = '''
#!/usr/bin/env zsh
set -e
print -P "%F{209}[codeweaver]%f Generating changelog for tag ${usage_tag}..."
git-cliff --tag "${usage_tag}" --prepend --output CHANGELOG.md &&
print -P "%F{209}[codeweaver]%f ✅ CHANGELOG.md updated for ${usage_tag}!"
'''
[tasks.changelog]
```
If you prefer per-tag files instead of a cumulative `CHANGELOG.md`, replace the `git-cliff` line with something like:
`git-cliff --tag "${usage_tag}" --output "CHANGELOG-${usage_tag}.md"` and adjust the success message accordingly.
</issue_to_address>
### Comment 3
<location> `.github/workflows/changelog.yml:42-48` </location>
<code_context>
+ run: |
+ if [ -n "${{ github.event.inputs.tag }}" ]; then
+ echo "Generating changelog for tag ${{ github.event.inputs.tag }}"
+ git-cliff --tag "${{ github.event.inputs.tag }}" --output CHANGELOG.md
+ else
+ echo "Generating changelog for unreleased changes"
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The workflow path for a specific tag also fully overwrites CHANGELOG.md, which might conflict with the unreleased/prepend mode.
With `tag` set, this step replaces `CHANGELOG.md` entirely, unlike the `--unreleased` path which prepends. With `commit=true`, the repo ends up with a changelog containing only that tag’s entries, dropping previous history. If that’s not intentional, consider matching the prepend behavior here or writing tagged output to a separate file (e.g., `CHANGELOG-${tag}.md`) to avoid truncating the main changelog.
```suggestion
if [ -n "${{ github.event.inputs.tag }}" ]; then
echo "Generating changelog for tag ${{ github.event.inputs.tag }}"
git-cliff --tag "${{ github.event.inputs.tag }}" --output CHANGELOG.md --prepend
else
echo "Generating changelog for unreleased changes"
git-cliff --unreleased --output CHANGELOG.md --prepend
fi
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Adam Poulemanos <[email protected]>
|
👋 Hey @bashandbone, Thanks for your contribution to codeweaver! 🧵You need to agree to the CLA first... 🖊️Before we can accept your contribution, you need to agree to our Contributor License Agreement (CLA). To agree to the CLA, please comment:
Those exact words are important1, so please don't change them. 😉 You can read the full CLA here: Contributor License Agreement ✅ @bashandbone has signed the CLA. 0 out of 2 committers have signed the CLA. Footnotes
|
Code Review - PR #180: Automated Changelog Generation with git-cliffI've reviewed this pull request that introduces automated changelog generation using ✅ Strengths
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review of PR #180: Automated Changelog Generation with git-cliff
I've reviewed this PR that introduces git-cliff for automated changelog generation. Overall, this is a well-structured and thoughtful implementation that successfully replaces the changesets-based approach with a PR-focused changelog system.
✅ Strengths
- Clean Architecture: The separation of concerns is excellent - configuration in
cliff.toml, workflows in.github/workflows/, and local tooling inmise.toml - Comprehensive Documentation: The updates to
docs/versioning.mdprovide clear guidance on usage, best practices, and workflow integration - Cross-platform Support: Both Unix and Windows support in mise tasks shows attention to detail
- Consistent Code Style: Follows existing project conventions (SPDX headers, formatting, etc.)
- PR-focused Approach: Filtering merge commits reduces noise and focuses on meaningful changes
- Good Template Design: The git-cliff template properly extracts PR descriptions and handles edge cases
🔍 Regarding Comment #2579192676
The Sourcery AI bot raised a valid concern about the changelog-tag task in mise.toml:354 potentially overwriting CHANGELOG.md history. However, I believe the current implementation is correct for the following reasons:
- Intended Behavior: The
changelog-tagtask appears designed for generating release-specific changelogs, not for maintaining cumulative history - Separate Use Cases: The PR provides distinct tasks:
changelog-update: Updates CHANGELOG.md cumulatively (uses--prepend)changelog-tag: Generates tag-specific output (overwrites by design)
- Release Workflow Context: Looking at
.github/workflows/release.yml:104, git-cliff generates release notes for specific tag ranges (--strip all), which aligns with the tag-specific approach
However, there is a legitimate issue in the GitHub Actions workflow at .github/workflows/changelog.yml:42-48:
The workflow has inconsistent behavior:
- With tag: Overwrites CHANGELOG.md entirely (line 44)
- Without tag: Prepends to CHANGELOG.md (line 47 uses
--prepend)
This inconsistency could lead to accidental history loss if someone runs the workflow with a tag and commits=true.
📝 Recommendations
1. Fix the Workflow Inconsistency (Important)
The .github/workflows/changelog.yml should use consistent behavior. I recommend using --prepend for both paths:
# .github/workflows/changelog.yml:42-48
if [ -n "${{ github.event.inputs.tag }}" ]; then
echo "Generating changelog for tag ${{ github.event.inputs.tag }}"
git-cliff --tag "${{ github.event.inputs.tag }}" --output CHANGELOG.md --prepend
else
echo "Generating changelog for unreleased changes"
git-cliff --unreleased --output CHANGELOG.md --prepend
fi2. Consider Tightening Tag Pattern (Optional)
The tag_pattern = "v[0-9].*" in cliff.toml:116 is quite permissive. If you want to strictly match SemVer tags, consider:
tag_pattern = "^v[0-9]+\.[0-9]+\.[0-9]+.*$"However, the current pattern may be intentional to catch alpha/beta tags like v0.1.0a1.
3. Enhance Commit Preprocessor Robustness (Optional)
The regex at cliff.toml:77 assumes single-line PR descriptions. Consider handling multi-line descriptions:
{ pattern = 'Merge pull request (#[0-9]+) from ([^\n]+)\n+([\s\S]+?)(?=\n\nCo-authored-by:|\$)', replace = "Merge pull request ${1} from ${2} | ${3}" }Though this may be over-engineering if your PR descriptions are typically single-line.
4. Clarify mise Task Documentation (Minor)
Consider adding a note to mise.toml:345-346 explaining that changelog-tag overwrites CHANGELOG.md:
description = "Generate changelog for a specific tag (overwrites CHANGELOG.md)"🔒 Security & Performance
- ✅ No security concerns identified
- ✅ Pinned action versions with commit SHAs (good practice)
- ✅ Appropriate permissions (
contents: writeonly where needed) - ✅ Performance is acceptable (git-cliff is efficient)
🎯 Verdict
This PR is ready to merge with one recommended fix for the workflow inconsistency. The rest are optional improvements.
Recommended Action: Address the workflow --prepend inconsistency, then merge.
Great work on this comprehensive changelog automation! 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request replaces the changesets-based changelog system with git-cliff for automated changelog generation, implementing a PR-focused approach that categorizes changes based on branch naming conventions.
- Adds git-cliff configuration with custom templates for PR-based changelog generation
- Integrates git-cliff into local development workflows via mise tasks and GitHub Actions workflows
- Updates documentation to reflect the new changelog management process
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| cliff.toml | New git-cliff configuration defining templates, commit parsers, and branch-based categorization rules |
| mise.toml | Updated changelog tasks to use git-cliff instead of changesets, adding commands for viewing, updating, and tag-specific generation |
| .github/workflows/changelog.yml | New workflow for manual changelog generation with options for tags and auto-commit |
| .github/workflows/release.yml | Modified release workflow to generate release notes using git-cliff |
| CHANGELOG.md | Initial changelog file generated by git-cliff showing unreleased changes |
| docs/versioning.md | Updated documentation explaining the new git-cliff-based changelog workflow and usage patterns |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
cliff.toml
Outdated
| # Extract PR description and append to subject for template use | ||
| # Input: "Merge pull request #123 from org/branch\n\nPR Description" | ||
| # Output: "Merge pull request #123 from org/branch | PR Description" | ||
| { pattern = 'Merge pull request (#[0-9]+) from ([^\n]+)\n+(.+)', replace = "Merge pull request ${1} from ${2} | ${3}" }, |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex pattern in the commit preprocessor may not handle PR descriptions correctly if they contain newlines within the description. The pattern \n+(.+) will only capture text up to the first newline within the PR description, truncating multi-paragraph PR descriptions to just the first line.
Consider using (.+) with the DOTALL flag or ([\s\S]+) to capture the full PR description including newlines.
| { pattern = 'Merge pull request (#[0-9]+) from ([^\n]+)\n+(.+)', replace = "Merge pull request ${1} from ${2} | ${3}" }, | |
| { pattern = 'Merge pull request (#[0-9]+) from ([^\n]+)\n+([\s\S]+)', replace = "Merge pull request ${1} from ${2} | ${3}" }, |
| ### Bug Fixes | ||
| - Verify sparse-only vector search fix (ISSUE-001) ([#176](https://github.com/knitli/codeweaver/pull/176)) | ||
| - Fix Docker Compose CI test failures - correct Qdrant health endpoint ([#178](https://github.com/knitli/codeweaver/pull/178)) | ||
| - chore: Add plans for server separation of services refactor to enable… ([#179](https://github.com/knitli/codeweaver/pull/179)) |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entry appears to be miscategorized. The description starts with "chore:" which suggests it should be in a "Maintenance" or "Chores" section, not "Bug Fixes". This is likely caused by the branch name containing "issue-" which matches the fix pattern on line 91 of cliff.toml.
The commit parsers should be refined to handle cases where branch names might match fix patterns but the actual change is not a fix (based on conventional commit prefixes like "chore:").
| mise run changelog-update | ||
|
|
||
| # Generate changelog for specific tag | ||
| mise run changelog-tag v0.1.0 |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states to use mise run changelog-tag v0.1.0, but according to the task definition in mise.toml (line 348), the correct syntax should be mise run changelog-tag -- --usage_tag=v0.1.0 or the task should accept positional arguments. The current task definition uses usage field with an argument named <tag>, which requires specific mise argument syntax.
Verify the correct syntax for invoking this task and update the documentation accordingly.
| mise run changelog-tag v0.1.0 | |
| mise run changelog-tag -- --usage_tag=v0.1.0 |
| commit_parsers = [ | ||
| # Skip non-PR merge commits (like "Merge branch 'main'") | ||
| { message = "^Merge branch", skip = true }, | ||
| # Features - check branch name patterns | ||
| { message = "^Merge pull request #[0-9]+ from .*/feat", group = "Features" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/feature", group = "Features" }, | ||
| # Fixes - multiple patterns for fix-related branches | ||
| { message = "^Merge pull request #[0-9]+ from .*/fix", group = "Bug Fixes" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/bugfix", group = "Bug Fixes" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/hotfix", group = "Bug Fixes" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/issue-", group = "Bug Fixes" }, | ||
| # Documentation | ||
| { message = "^Merge pull request #[0-9]+ from .*/docs", group = "Documentation" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/doc", group = "Documentation" }, | ||
| # Performance | ||
| { message = "^Merge pull request #[0-9]+ from .*/perf", group = "Performance" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/optimize", group = "Performance" }, | ||
| # Refactoring | ||
| { message = "^Merge pull request #[0-9]+ from .*/refactor", group = "Refactoring" }, | ||
| # Testing | ||
| { message = "^Merge pull request #[0-9]+ from .*/test", group = "Testing" }, | ||
| # CI/CD and tooling | ||
| { message = "^Merge pull request #[0-9]+ from .*/ci", group = "CI/CD" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/workflow", group = "CI/CD" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/build", group = "Build System" }, | ||
| # Chores and maintenance | ||
| { message = "^Merge pull request #[0-9]+ from .*/chore", group = "Maintenance" }, | ||
| # Catch-all for other PRs (will be categorized as miscellaneous) | ||
| { message = "^Merge pull request #[0-9]+", group = "Other Changes" }, | ||
| ] |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit parser patterns rely solely on branch names for categorization, which can lead to incorrect categorization when PR descriptions contain conventional commit prefixes (e.g., "feat:", "chore:", "fix:") that conflict with the branch name pattern. This is evidenced in the generated CHANGELOG.md where entries like "chore: Add plans..." appear under "Bug Fixes" and "feat: implement..." appears under "Performance".
Consider enhancing the commit_preprocessors to extract and use conventional commit prefixes from PR descriptions for categorization, or document that branch naming conventions should take precedence over commit message conventions to avoid confusion.
mise.toml
Outdated
| [tasks.changelog-tag] | ||
| description = "Generate changelog for a specific tag" | ||
| usage = ''' | ||
| arg "<tag>" help="Git tag to generate changelog for (e.g., v0.1.0)" required=true |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The usage definition expects an argument named <tag> (line 348) but the script references it as ${usage_tag} (line 353) and %usage_tag% (line 358). According to mise documentation, usage arguments should be referenced with the usage_ prefix, but the angle brackets in the argument name definition might cause issues.
The argument definition should likely be arg "tag" without angle brackets, and the references ${usage_tag} and %usage_tag% are correct for accessing it.
| arg "<tag>" help="Git tag to generate changelog for (e.g., v0.1.0)" required=true | |
| arg "tag" help="Git tag to generate changelog for (e.g., v0.1.0)" required=true |
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| cat >> release_notes.md << EOF | ||
| **This is a pre-release version.** Install with: | ||
| \`\`\`bash | ||
| pip install --pre code-weaver |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The installation instruction suggests pip install --pre codeweaver but the package name should be code-weaver (with hyphen) for consistency with line 131. This inconsistency could lead users to install the wrong package.
| run: | | ||
| if [ -n "${{ github.event.inputs.tag }}" ]; then | ||
| echo "Generating changelog for tag ${{ github.event.inputs.tag }}" | ||
| git-cliff --tag "${{ github.event.inputs.tag }}" --output CHANGELOG.md --prepend |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The --prepend flag is used here but not in the changelog-tag task (line 354). This inconsistency means that changelog-tag will overwrite CHANGELOG.md instead of prepending to it. For consistency, either add --prepend to the changelog-tag task or document that it intentionally overwrites the file.
| { message = "^Merge pull request #[0-9]+ from .*/feat", group = "Features" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/feature", group = "Features" }, | ||
| # Fixes - multiple patterns for fix-related branches | ||
| { message = "^Merge pull request #[0-9]+ from .*/fix", group = "Bug Fixes" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/bugfix", group = "Bug Fixes" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/hotfix", group = "Bug Fixes" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/issue-", group = "Bug Fixes" }, | ||
| # Documentation | ||
| { message = "^Merge pull request #[0-9]+ from .*/docs", group = "Documentation" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/doc", group = "Documentation" }, | ||
| # Performance | ||
| { message = "^Merge pull request #[0-9]+ from .*/perf", group = "Performance" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/optimize", group = "Performance" }, | ||
| # Refactoring | ||
| { message = "^Merge pull request #[0-9]+ from .*/refactor", group = "Refactoring" }, | ||
| # Testing | ||
| { message = "^Merge pull request #[0-9]+ from .*/test", group = "Testing" }, | ||
| # CI/CD and tooling | ||
| { message = "^Merge pull request #[0-9]+ from .*/ci", group = "CI/CD" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/workflow", group = "CI/CD" }, | ||
| { message = "^Merge pull request #[0-9]+ from .*/build", group = "Build System" }, | ||
| # Chores and maintenance | ||
| { message = "^Merge pull request #[0-9]+ from .*/chore", group = "Maintenance" }, |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The branch pattern matching uses partial matches (e.g., /feat, /feature) which will also match branches like /feature-xyz or /feats. While this might be intentional for flexibility, it could lead to unexpected categorization. Consider using more explicit patterns like /feat/ or /feature/ if branches are expected to follow a strict type/description format, or document that partial matching is intentional.
| { message = "^Merge pull request #[0-9]+ from .*/feat", group = "Features" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/feature", group = "Features" }, | |
| # Fixes - multiple patterns for fix-related branches | |
| { message = "^Merge pull request #[0-9]+ from .*/fix", group = "Bug Fixes" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/bugfix", group = "Bug Fixes" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/hotfix", group = "Bug Fixes" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/issue-", group = "Bug Fixes" }, | |
| # Documentation | |
| { message = "^Merge pull request #[0-9]+ from .*/docs", group = "Documentation" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/doc", group = "Documentation" }, | |
| # Performance | |
| { message = "^Merge pull request #[0-9]+ from .*/perf", group = "Performance" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/optimize", group = "Performance" }, | |
| # Refactoring | |
| { message = "^Merge pull request #[0-9]+ from .*/refactor", group = "Refactoring" }, | |
| # Testing | |
| { message = "^Merge pull request #[0-9]+ from .*/test", group = "Testing" }, | |
| # CI/CD and tooling | |
| { message = "^Merge pull request #[0-9]+ from .*/ci", group = "CI/CD" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/workflow", group = "CI/CD" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/build", group = "Build System" }, | |
| # Chores and maintenance | |
| { message = "^Merge pull request #[0-9]+ from .*/chore", group = "Maintenance" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/feat/", group = "Features" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/feature/", group = "Features" }, | |
| # Fixes - multiple patterns for fix-related branches | |
| { message = "^Merge pull request #[0-9]+ from .*/fix/", group = "Bug Fixes" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/bugfix/", group = "Bug Fixes" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/hotfix/", group = "Bug Fixes" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/issue-", group = "Bug Fixes" }, | |
| # Documentation | |
| { message = "^Merge pull request #[0-9]+ from .*/docs/", group = "Documentation" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/doc/", group = "Documentation" }, | |
| # Performance | |
| { message = "^Merge pull request #[0-9]+ from .*/perf/", group = "Performance" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/optimize/", group = "Performance" }, | |
| # Refactoring | |
| { message = "^Merge pull request #[0-9]+ from .*/refactor/", group = "Refactoring" }, | |
| # Testing | |
| { message = "^Merge pull request #[0-9]+ from .*/test/", group = "Testing" }, | |
| # CI/CD and tooling | |
| { message = "^Merge pull request #[0-9]+ from .*/ci/", group = "CI/CD" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/workflow/", group = "CI/CD" }, | |
| { message = "^Merge pull request #[0-9]+ from .*/build/", group = "Build System" }, | |
| # Chores and maintenance | |
| { message = "^Merge pull request #[0-9]+ from .*/chore/", group = "Maintenance" }, |
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - **Automatic categorization**: PRs are grouped by branch name patterns: | ||
| - `feat/` or `feature/` → Features | ||
| - `fix/`, `bugfix/`, `hotfix/`, `issue-` → Bug Fixes | ||
| - `optimize/`, `perf/` → Performance |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The example shows branch name pattern issue- (line 223) which would match branches like issue-123-fix-bug, but the corresponding commit parser in cliff.toml (line 95) uses from .*/issue- which requires the pattern to appear after a slash. This means a branch named issue-123 from a fork like user/issue-123 would be matched, but the documentation doesn't make this clear.
Consider clarifying in the documentation that the pattern matching is based on the full branch path after the repository name, not just the branch name itself.
| conventional_commits = false | ||
| # Filter out commits that are not conventional | ||
| filter_unconventional = false | ||
| # Process merge commits - THIS IS KEY for PR-based changelog |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The comment on line 73 states "THIS IS KEY for PR-based changelog" but the actual setting split_commits = false is on line 74. The comment seems to indicate this is an important setting, but the description could be clearer about what split_commits does and why false is the correct value for PR-based changelogs.
Consider expanding the comment to explain: "Set to false to treat merge commits as single entries (important for PR-based changelogs)" for better maintainability.
| # Process merge commits - THIS IS KEY for PR-based changelog | |
| # Set to false to treat merge commits (such as PR merges) as single entries in the changelog. | |
| # This is important for PR-based changelogs, as it ensures each PR appears as one changelog entry. |
| git commit -m "docs: update CHANGELOG.md for ${{ github.event.inputs.tag }}" | ||
| else |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit message also uses unsanitized user input from github.event.inputs.tag. While less critical than command injection, this could allow malicious commit messages to be created.
Consider validating the tag input or using a sanitized version in the commit message.
| git commit -m "docs: update CHANGELOG.md for ${{ github.event.inputs.tag }}" | |
| else | |
| # Sanitize the tag input: allow only alphanumerics, dots, dashes, and underscores | |
| SAFE_TAG=$(echo "${{ github.event.inputs.tag }}" | grep -Eo '^[A-Za-z0-9._-]+$') | |
| if [ -z "$SAFE_TAG" ]; then | |
| echo "Tag input contains invalid characters. Using 'unknown-tag' in commit message." | |
| git commit -m "docs: update CHANGELOG.md for unknown-tag" | |
| else | |
| git commit -m "docs: update CHANGELOG.md for $SAFE_TAG" | |
| fi |
|
@copilot please address the following issues identified in these PR comments (#180): |
|
@bashandbone I've opened a new pull request, #181, to work on those changes. Once the pull request is ready, I'll request review from you. |
Addresses all review comments from PR #180: **1. Add squash merge support** - Added preprocessors to detect GitHub squash merge format: "type: description (#123)" - Added commit parsers for conventional commit types in squash merges - Updated template to handle both merge commits and squash merges - Squash merges are now categorized by conventional commit prefix (feat, fix, etc.) **2. Fix malformed HEREDOC in release.yml** - Fixed broken HEREDOC at lines 128-138 that was missing proper closure - Removed incomplete sed command - Changed to non-quoted HEREDOC (EOF instead of 'EOF') to allow $VERSION expansion - Properly escaped backticks in the heredoc content **3. Fix package name consistency** - Updated all pip install commands to use "code-weaver" (PyPI package name) - Fixed docs/versioning.md lines 184, 189, 202, 208 - "CodeWeaver" remains for product name, "code-weaver" for PyPI package **4. Remove Windows commands from mise tasks** - Removed run_windows sections from all changelog tasks - Project only uses Linux runners, Windows support not needed - Simplified to bash-only scripts **5. Fix mise task usage syntax** - Updated usage syntax to use proper KDL block format with curly braces - Removed redundant `required=true` (angle brackets indicate required) - Added proper error checking with `${usage_tag:?}` expansion - Follows mise documentation standards from usage.jdx.dev **6. Document changelog overwrite behavior** - Added comprehensive documentation about --prepend behavior in docs/versioning.md - Added warnings about potential duplicate entries and git conflicts - Added best practices section for changelog management - Added inline comments in changelog.yml workflow explaining --prepend risks - Documented that CHANGELOG.md should be treated as generated content All changes maintain backward compatibility while preparing for the move to squash commits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- The new
misechangelog tasks only define Unixruncommands and dropped the previousrun_windowsimplementation, which may break the existing Windows workflow; consider adding Windows variants for the new git-cliff–based tasks to keep cross-platform parity. - You pin
[email protected]in the GitHub Actions workflows but usetools."cargo:git-cliff" = "latest"inmise.toml; aligning these to a single pinned version would avoid subtle differences in changelog output between local runs and CI.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `mise` changelog tasks only define Unix `run` commands and dropped the previous `run_windows` implementation, which may break the existing Windows workflow; consider adding Windows variants for the new git-cliff–based tasks to keep cross-platform parity.
- You pin `[email protected]` in the GitHub Actions workflows but use `tools."cargo:git-cliff" = "latest"` in `mise.toml`; aligning these to a single pinned version would avoid subtle differences in changelog output between local runs and CI.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| run = ''' | ||
| #!/usr/bin/env zsh | ||
| print -P "%F{209}[codeweaver]%f Generating changelog..." | ||
| mise x -- npm exec changeset version | ||
| #!/usr/bin/env bash | ||
| echo "Generating changelog..." | ||
| git-cliff | ||
| ''' |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The run_windows script has been removed but Windows support appears incomplete. The new tasks only provide bash scripts (lines 306-342) without Windows equivalents. This breaks changelog generation on Windows systems.
Consider adding run_windows sections for each new task that use PowerShell or cmd syntax, similar to how other tasks in this file handle cross-platform support.
| cat >> release_notes.md << EOF | ||
| **This is a pre-release version.** Install with: | ||
| \`\`\`bash | ||
| pip install --pre code-weaver |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent package name. The documentation uses codeweaver (no hyphen) in the pip install command, but elsewhere in the file it's been corrected to code-weaver (with hyphen). This line should use code-weaver for consistency.
pip install --pre code-weaverCo-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| git-cliff --strip header --tag "$CURRENT_TAG" "$PREVIOUS_TAG..$CURRENT_TAG" > changelog_section.md | ||
| else | ||
| # First release - get all commits | ||
| git-cliff --strip header --tag "$CURRENT_TAG" > changelog_section.md |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The --strip header flag is used on lines 106 and 109, but this flag doesn't exist in git-cliff 2.8.1. The correct flag is --strip all or --body to exclude the header. This will cause the release notes generation to fail.
| git-cliff --strip header --tag "$CURRENT_TAG" "$PREVIOUS_TAG..$CURRENT_TAG" > changelog_section.md | |
| else | |
| # First release - get all commits | |
| git-cliff --strip header --tag "$CURRENT_TAG" > changelog_section.md | |
| git-cliff --strip all --tag "$CURRENT_TAG" "$PREVIOUS_TAG..$CURRENT_TAG" > changelog_section.md | |
| else | |
| # First release - get all commits | |
| git-cliff --strip all --tag "$CURRENT_TAG" > changelog_section.md |
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
Co-authored-by: Copilot <[email protected]> Signed-off-by: Adam Poulemanos <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 2. **Use branch name conventions**: Follow the naming patterns (`feat/`, `fix/`, etc.) for automatic categorization | ||
| 3. **Update before releases**: Run `mise run changelog-update` before creating release tags to keep CHANGELOG.md current | ||
| 4. **Squash vs Merge**: The setup works with both squash commits and merge commits, but handles them differently: | ||
| - **Squash merges** REQUIRE conventional commit prefixes (e.g., `feat:`, `fix:`) in the commit message for proper changelog categorization. |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The documentation mentions that squash merges "REQUIRE conventional commit prefixes" but doesn't list all the supported prefixes. The cliff.toml configuration supports: feat, fix, docs, style, refactor, perf, test, build, ci, chore, and revert (line 96), but the documentation only lists a subset of branch patterns. Consider adding a complete list of conventional commit prefixes to help developers understand all options for squash commits.
| - **Squash merges** REQUIRE conventional commit prefixes (e.g., `feat:`, `fix:`) in the commit message for proper changelog categorization. | |
| - **Squash merges** REQUIRE conventional commit prefixes in the commit message for proper changelog categorization. Supported prefixes are: `feat:`, `fix:`, `docs:`, `style:`, `refactor:`, `perf:`, `test:`, `build:`, `ci:`, `chore:`, and `revert:`. |
| ### Bug Fixes | ||
| - Verify sparse-only vector search fix (ISSUE-001) ([#176](https://github.com/knitli/codeweaver/pull/176)) | ||
| - Fix Docker Compose CI test failures - correct Qdrant health endpoint ([#178](https://github.com/knitli/codeweaver/pull/178)) | ||
| - chore: Add plans for server separation of services refactor to enable… ([#179](https://github.com/knitli/codeweaver/pull/179)) |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] This changelog entry appears to be miscategorized. The PR description mentions "Add plans for server separation of services refactor" which sounds like planning/documentation rather than a bug fix. Based on the branch naming conventions in the documentation, this should likely be in "Documentation" or "Other Changes" rather than "Bug Fixes".
| # 2. Mark squash merge commits for easier template detection | ||
| # Input: "feat: some feature (#123)" | ||
| # Output: "SQUASH | feat: some feature | #123" | ||
| { pattern = '^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+?\))?: (.+) \(#([0-9]+)\)$', replace = "SQUASH | ${1}${2}: ${3} | #${4}" }, |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex pattern for squash merges with conventional commit prefixes is missing the optimize type which is mentioned in the documentation (docs/versioning.md line 223: optimize/, perf/ → Performance). Consider adding |optimize to the pattern to ensure consistency between branch naming conventions and squash merge commit parsing:
{ pattern = '^(feat|fix|docs|style|refactor|perf|optimize|test|build|ci|chore|revert)(\(.+?\))?: (.+) \(#([0-9]+)\)$', replace = "SQUASH | ${1}${2}: ${3} | #${4}" },| { pattern = '^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+?\))?: (.+) \(#([0-9]+)\)$', replace = "SQUASH | ${1}${2}: ${3} | #${4}" }, | |
| { pattern = '^(feat|fix|docs|style|refactor|perf|optimize|test|build|ci|chore|revert)(\(.+?\))?: (.+) \(#([0-9]+)\)$', replace = "SQUASH | ${1}${2}: ${3} | #${4}" }, |
|
|
||
| # === Squash Merge Parsers (conventional commits) === | ||
| # After preprocessing, squash merges are in format: "SQUASH | type: description | #123" | ||
| { message = "^SQUASH \\| feat", group = "Features" }, | ||
| { message = "^SQUASH \\| fix", group = "Bug Fixes" }, | ||
| { message = "^SQUASH \\| docs", group = "Documentation" }, | ||
| { message = "^SQUASH \\| perf", group = "Performance" }, | ||
| { message = "^SQUASH \\| refactor", group = "Refactoring" }, | ||
| { message = "^SQUASH \\| test", group = "Testing" }, | ||
| { message = "^SQUASH \\| build", group = "Build System" }, | ||
| { message = "^SQUASH \\| ci", group = "CI/CD" }, |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The squash merge commit parsers are missing handlers for optimize and revert types, which are mentioned in the branch naming documentation and in the preprocessor regex (line 96). Add the following parsers to maintain consistency:
{ message = "^SQUASH \\| optimize", group = "Performance" },
{ message = "^SQUASH \\| revert", group = "Reverts" },Also note that revert is already handled at line 120, but optimize is missing entirely from the squash parsers.
This pull request introduces automated changelog generation using
git-cliff, replacing the previous changesets-based approach. It adds configuration, workflow, and documentation changes to support PR-focused, categorized changelogs, and integrates changelog generation into both local development and GitHub release workflows.Changelog Automation & Configuration
cliff.tomlto configuregit-clifffor PR-based changelog generation, with custom grouping and formatting based on branch naming conventions..github/workflows/changelog.ymlworkflow to allow manual changelog generation via GitHub Actions, supporting both unreleased changes and specific tags, with optional auto-commit.mise.tomltasks to provide local commands for generating, updating, and viewing changelogs usinggit-cliff, including support for unreleased changes and specific tags.Release Workflow Integration
.github/workflows/release.ymlto usegit-clifffor generating release notes, ensuring categorized PR descriptions and consistent formatting in GitHub releases.Documentation & Changelog File
CHANGELOG.mdfile, generated and formatted bygit-cliff, with categorized sections for unreleased changes.docs/versioning.mdto document the new changelog management process, explaining how changelogs are generated, categorized, and integrated into workflows.Summary by Sourcery
Replace the previous changesets-based changelog process with git-cliff–driven, PR-focused changelog generation integrated into local tooling and release workflows.
Enhancements:
Build:
CI:
Documentation: