Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 69 additions & 23 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ concurrency:

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -Dwarnings

jobs:
# REQUIRED CI CHECKS - All must pass before release
Expand All @@ -44,6 +45,11 @@ jobs:
with:
components: rustfmt, clippy

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'

- name: Cache cargo registry
uses: actions/cache@v4
with:
Expand All @@ -59,10 +65,10 @@ jobs:
run: cargo fmt --all -- --check

- name: Run Clippy
run: cargo clippy --all-targets --all-features -- -D warnings
run: cargo clippy --all-targets --all-features

- name: Check file size limit
run: python3 scripts/check_file_size.py
run: node scripts/check-file-size.mjs

# Test on multiple OS
test:
Expand Down Expand Up @@ -140,11 +146,11 @@ jobs:

echo "Coverage check passed: $COVERAGE% >= 90%"

# Build package - only runs if lint, test, and coverage pass
# Build package - only runs if lint and test pass
build:
name: Build Package
runs-on: ubuntu-latest
needs: [lint, test, coverage]
needs: [lint, test]
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -203,7 +209,8 @@ jobs:

echo "Changelog check passed"

# Automatic release on push to main (if version changed)
# Automatic release on push to main using changelog fragments
# This job automatically bumps version based on fragments in changelog.d/
auto-release:
name: Auto Release
needs: [lint, test, build]
Expand All @@ -215,37 +222,71 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Check if version changed
id: version_check
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'

- name: Configure git
run: |
# Get current version from Cargo.toml
CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' Cargo.toml)
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Determine bump type from changelog fragments
id: bump_type
run: node scripts/get-bump-type.mjs

# Check if tag exists
if git rev-parse "v$CURRENT_VERSION" >/dev/null 2>&1; then
echo "Tag v$CURRENT_VERSION already exists, skipping release"
echo "should_release=false" >> $GITHUB_OUTPUT
- name: Check if version already released or no fragments
id: check
run: |
# Check if there are changelog fragments
if [ "${{ steps.bump_type.outputs.has_fragments }}" != "true" ]; then
# No fragments - check if current version tag exists
CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' Cargo.toml)
if git rev-parse "v$CURRENT_VERSION" >/dev/null 2>&1; then
echo "No changelog fragments and v$CURRENT_VERSION already released"
echo "should_release=false" >> $GITHUB_OUTPUT
else
echo "No changelog fragments but v$CURRENT_VERSION not yet released"
echo "should_release=true" >> $GITHUB_OUTPUT
echo "skip_bump=true" >> $GITHUB_OUTPUT
fi
else
echo "New version detected: $CURRENT_VERSION"
echo "Found changelog fragments, proceeding with release"
echo "should_release=true" >> $GITHUB_OUTPUT
echo "skip_bump=false" >> $GITHUB_OUTPUT
fi

- name: Collect changelog and bump version
id: version
if: steps.check.outputs.should_release == 'true' && steps.check.outputs.skip_bump != 'true'
run: |
node scripts/version-and-commit.mjs \
--bump-type "${{ steps.bump_type.outputs.bump_type }}"

- name: Get current version
id: current_version
if: steps.check.outputs.should_release == 'true'
run: |
CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' Cargo.toml)
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT

- name: Build release
if: steps.version_check.outputs.should_release == 'true'
if: steps.check.outputs.should_release == 'true'
run: cargo build --release

- name: Create GitHub Release
if: steps.version_check.outputs.should_release == 'true'
if: steps.check.outputs.should_release == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 scripts/create_github_release.py \
--version "${{ steps.version_check.outputs.current_version }}" \
node scripts/create-github-release.mjs \
--release-version "${{ steps.current_version.outputs.version }}" \
--repository "${{ github.repository }}"

# Manual release via workflow_dispatch - only after CI passes
Expand All @@ -265,6 +306,11 @@ jobs:
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'

- name: Configure git
run: |
git config user.name "github-actions[bot]"
Expand All @@ -276,15 +322,15 @@ jobs:
FRAGMENTS=$(find changelog.d -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
if [ "$FRAGMENTS" -gt 0 ]; then
echo "Found $FRAGMENTS changelog fragment(s), collecting..."
python3 scripts/collect_changelog.py
node scripts/collect-changelog.mjs
else
echo "No changelog fragments found, skipping collection"
fi

- name: Version and commit
id: version
run: |
python3 scripts/version_and_commit.py \
node scripts/version-and-commit.mjs \
--bump-type "${{ github.event.inputs.bump_type }}" \
--description "${{ github.event.inputs.description }}"

Expand All @@ -297,6 +343,6 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 scripts/create_github_release.py \
--version "${{ steps.version.outputs.new_version }}" \
node scripts/create-github-release.mjs \
--release-version "${{ steps.version.outputs.new_version }}" \
--repository "${{ github.repository }}"
12 changes: 8 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ Thank you for your interest in contributing! This document provides guidelines a
cargo clippy --all-targets --all-features

# Check file sizes
python3 scripts/check_file_size.py
node scripts/check-file-size.mjs

# Run all checks together
cargo fmt --check && cargo clippy --all-targets --all-features && python3 scripts/check_file_size.py
cargo fmt --check && cargo clippy --all-targets --all-features && node scripts/check-file-size.mjs
```

4. **Run tests**
Expand Down Expand Up @@ -94,17 +94,21 @@ Thank you for your interest in contributing! This document provides guidelines a
touch changelog.d/$(date +%Y%m%d_%H%M%S)_my_change.md
```

Edit the file to document your changes:
Edit the file to document your changes with a bump type in the frontmatter:

```markdown
---
bump: minor
---

### Added
- Description of new feature

### Fixed
- Description of bug fix
```

**Why fragments?** This prevents merge conflicts in CHANGELOG.md when multiple PRs are open simultaneously.
**Why fragments?** This prevents merge conflicts in CHANGELOG.md when multiple PRs are open simultaneously. The bump type (`major`, `minor`, or `patch`) in the frontmatter enables automatic version bumping during release.

6. **Commit your changes**

Expand Down
4 changes: 4 additions & 0 deletions changelog.d/20251227_100_coverage.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
bump: minor
---

### Added
- Test coverage infrastructure using cargo-tarpaulin with 90% minimum threshold
- Comprehensive unit tests achieving 94.57% code coverage
Expand Down
4 changes: 4 additions & 0 deletions changelog.d/20251227_173319_add_cicd_pipeline.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
bump: minor
---

### Added
- Modern CI/CD pipeline from rust-ai-driven-development-pipeline-template
- GitHub Actions workflow for automated testing, linting, and releases
Expand Down
4 changes: 4 additions & 0 deletions changelog.d/20251227_183811_migrate_to_stable_rust.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
bump: major
---

### Changed
- Migrated from nightly Rust to **stable Rust** toolchain (requires Rust 1.79+)
- Removed all unstable feature flags:
Expand Down
17 changes: 17 additions & 0 deletions changelog.d/20251227_232300_mjs_scripts_and_changeset_support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
bump: minor
---

### Changed
- Migrated all CI/CD scripts from Python to JavaScript ES modules (.mjs) for enhanced performance
- Updated release workflow to use Node.js 20.x for script execution
- Added automatic version bumping based on changelog fragment frontmatter

### Added
- New `get-bump-type.mjs` script that parses changelog fragments and determines version bump type
- Frontmatter support in changelog fragments with `bump: major|minor|patch` specification
- Automatic version bumping during release based on highest priority bump type from fragments

### Documentation
- Updated `changelog.d/README.md` with comprehensive frontmatter documentation and examples
- Updated `CONTRIBUTING.md` with new script references and fragment format instructions
94 changes: 89 additions & 5 deletions changelog.d/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,34 @@ touch changelog.d/$(date +%Y%m%d_%H%M%S)_description.md

## Fragment Format

Each fragment should contain relevant sections. Use the appropriate sections:
Each fragment should include a **frontmatter section** specifying the version bump type:

```markdown
---
bump: patch
---

### Fixed
- Description of bug fix
```

### Bump Types

Use semantic versioning bump types in the frontmatter:

- **`major`**: Breaking changes (incompatible API changes)
- **`minor`**: New features (backward compatible)
- **`patch`**: Bug fixes (backward compatible)

### Content Categories

Use these categories in your fragment content:

```markdown
---
bump: minor
---

### Added
- Description of new feature

Expand All @@ -37,15 +62,74 @@ Each fragment should contain relevant sections. Use the appropriate sections:
- Description of security fix
```

## Examples

### Adding a new feature (minor bump)

```markdown
---
bump: minor
---

### Added
- New async processing mode for batch operations
```

### Fixing a bug (patch bump)

```markdown
---
bump: patch
---

### Fixed
- Fixed memory leak in connection pool handling
```

### Breaking change (major bump)

```markdown
---
bump: major
---

### Changed
- Renamed `process()` to `process_async()` - this is a breaking change

### Removed
- Removed deprecated `legacy_mode` option
```

## Why Fragments?

Using changelog fragments (similar to [Changesets](https://github.com/changesets/changesets) in JavaScript and [Scriv](https://scriv.readthedocs.io/) in Python):

1. **No merge conflicts**: Multiple PRs can add fragments without conflicts
2. **Per-PR documentation**: Each PR documents its own changes
3. **Automated collection**: Fragments are automatically collected during release
4. **Consistent format**: Template ensures consistent changelog entries
3. **Automated version bumping**: Version bump type is specified per-change
4. **Automated collection**: Fragments are automatically collected during release
5. **Consistent format**: Template ensures consistent changelog entries

## How It Works

1. **During PR**: Add a fragment file with your changes and bump type
2. **On merge to main**: The release workflow automatically:
- Reads all fragment files and determines the highest bump type
- Bumps the version in `Cargo.toml` accordingly
- Collects fragments into `CHANGELOG.md`
- Creates a git tag and GitHub release
- Removes processed fragment files

## Multiple PRs and Bump Priority

When multiple PRs are merged before a release, all pending fragments are processed together. The **highest** bump type wins:

- If any fragment specifies `major`, the release is a major version bump
- Otherwise, if any specifies `minor`, the release is a minor version bump
- Otherwise, the release is a patch version bump

This ensures that breaking changes are never missed, even when combined with smaller changes.

## During Release
## Default Behavior

Fragments are automatically collected into `CHANGELOG.md` by running the collection script. This is handled automatically by the release workflow.
If a fragment doesn't include a bump type in the frontmatter, it defaults to `patch`.
Loading