feat: enforce lockstep major/minor versioning in release automation #219

Merged
jwilger merged 2 commits from fix/release-versioning into main 2025-12-27 06:43:57 -08:00
jwilger commented 2025-12-27 06:30:32 -08:00 (Migrated from github.com)

Summary

Implements ADR-025's lockstep versioning policy to ensure all workspace crates maintain identical major.minor versions while allowing independent patch versions.

Problem

The current release-plz configuration was creating release PRs where one crate would bump to 0.3.0 while others remained at 0.2.x, violating ADR-025's lockstep major/minor versioning policy.

Root Cause: release-plz performs independent semver analysis on each crate. When one crate has breaking changes, only that crate gets bumped to a new major/minor version.

Solution

Added post-processing to enforce lockstep versioning after release-plz's semver analysis:

Three-Step Workflow

Modified .github/workflows/release-plz.yml:

  1. release-plz update - Analyze commits and update versions (independent semver)
  2. enforce-lockstep-versions.sh - Adjust all crates to highest major.minor version
  3. release-plz release-pr - Create PR with lockstep-enforced versions

New Scripts

  • .github/scripts/enforce-lockstep-versions.sh - Fixes version skew (used in release workflow)
  • .github/scripts/validate-lockstep-versions.sh - Validates lockstep compliance (used in CI)
  • .github/scripts/test-lockstep-scripts.sh - Integration tests proving correctness

CI Validation

Added version-lockstep job to .github/workflows/ci.yml:

  • Runs on all PRs and pushes
  • Fails if version skew is detected
  • Prevents manual Cargo.toml edits from violating policy

Documentation

Updated docs/RELEASE_PROCESS.md with comprehensive "Version Lockstep Enforcement" section explaining:

  • The problem and solution
  • How the three-step workflow works
  • When and why major/minor bumps occur
  • Troubleshooting guide for violations

Example

Before this fix:

release-plz semver analysis:
  eventcore-types: 0.2.0 → 0.3.0 (breaking change)
  eventcore: 0.2.0 → 0.2.1 (no changes)

Result: ❌ Version skew (violates ADR-025)

After this fix:

release-plz semver analysis:
  eventcore-types: 0.2.0 → 0.3.0 (breaking change)
  eventcore: 0.2.0 → 0.2.1 (no changes)

enforce-lockstep-versions.sh:
  Target major.minor: 0.3
  eventcore-types: 0.3.0 (unchanged)
  eventcore: 0.2.1 → 0.3.1 (bumped major.minor, preserved patch)

Result: ✅ All crates at 0.3.x (complies with ADR-025)

Testing

All integration tests pass:

./.github/scripts/test-lockstep-scripts.sh

Test results:

  • Validation detects version skew
  • Enforcement fixes version skew
  • Validation passes after enforcement
  • Patch versions preserved independently

Verification

After merging this PR, the next release PR created by the workflow will have all crates sharing the same major.minor version, regardless of which crate actually had breaking changes.

References

  • ADR-025: Release Management and Versioning Policy
  • ARCHITECTURE.md: Section on Versioning and Release Policy (lines 769-820)
## Summary Implements ADR-025's lockstep versioning policy to ensure all workspace crates maintain identical major.minor versions while allowing independent patch versions. ## Problem The current release-plz configuration was creating release PRs where one crate would bump to 0.3.0 while others remained at 0.2.x, violating ADR-025's lockstep major/minor versioning policy. **Root Cause:** release-plz performs independent semver analysis on each crate. When one crate has breaking changes, only that crate gets bumped to a new major/minor version. ## Solution Added post-processing to enforce lockstep versioning after release-plz's semver analysis: ### Three-Step Workflow Modified `.github/workflows/release-plz.yml`: 1. **`release-plz update`** - Analyze commits and update versions (independent semver) 2. **`enforce-lockstep-versions.sh`** - Adjust all crates to highest major.minor version 3. **`release-plz release-pr`** - Create PR with lockstep-enforced versions ### New Scripts - `.github/scripts/enforce-lockstep-versions.sh` - Fixes version skew (used in release workflow) - `.github/scripts/validate-lockstep-versions.sh` - Validates lockstep compliance (used in CI) - `.github/scripts/test-lockstep-scripts.sh` - Integration tests proving correctness ### CI Validation Added `version-lockstep` job to `.github/workflows/ci.yml`: - Runs on all PRs and pushes - Fails if version skew is detected - Prevents manual Cargo.toml edits from violating policy ### Documentation Updated `docs/RELEASE_PROCESS.md` with comprehensive "Version Lockstep Enforcement" section explaining: - The problem and solution - How the three-step workflow works - When and why major/minor bumps occur - Troubleshooting guide for violations ## Example **Before this fix:** ``` release-plz semver analysis: eventcore-types: 0.2.0 → 0.3.0 (breaking change) eventcore: 0.2.0 → 0.2.1 (no changes) Result: ❌ Version skew (violates ADR-025) ``` **After this fix:** ``` release-plz semver analysis: eventcore-types: 0.2.0 → 0.3.0 (breaking change) eventcore: 0.2.0 → 0.2.1 (no changes) enforce-lockstep-versions.sh: Target major.minor: 0.3 eventcore-types: 0.3.0 (unchanged) eventcore: 0.2.1 → 0.3.1 (bumped major.minor, preserved patch) Result: ✅ All crates at 0.3.x (complies with ADR-025) ``` ## Testing All integration tests pass: ```bash ./.github/scripts/test-lockstep-scripts.sh ``` Test results: - ✅ Validation detects version skew - ✅ Enforcement fixes version skew - ✅ Validation passes after enforcement - ✅ Patch versions preserved independently ## Verification After merging this PR, the next release PR created by the workflow will have all crates sharing the same major.minor version, regardless of which crate actually had breaking changes. ## References - **ADR-025**: Release Management and Versioning Policy - **ARCHITECTURE.md**: Section on Versioning and Release Policy (lines 769-820)
claude[bot] (Migrated from github.com) requested changes 2025-12-27 06:32:56 -08:00
claude[bot] (Migrated from github.com) left a comment

Five issues found: sed portability problems (macOS/Linux), version parsing breaks on pre-release/build metadata, missing validation after enforcement in release workflow.

Five issues found: sed portability problems (macOS/Linux), version parsing breaks on pre-release/build metadata, missing validation after enforcement in release workflow.
@ -0,0 +1,137 @@
#!/usr/bin/env bash
claude[bot] (Migrated from github.com) commented 2025-12-27 06:32:56 -08:00

Portability issue: sed -i syntax differs between macOS and Linux. On macOS, requires empty string for in-place edit: sed -i "" .... On Linux, sed -i works without it.

Recommended fix:

# Detect OS and use appropriate sed syntax
if [[ "$OSTYPE" == "darwin"* ]]; then
    sed -i "" "0,/^version = \".*\"/s/^version = \".*\"/version = \"${new_version}\"/" "$toml"
else
    sed -i "0,/^version = \".*\"/s/^version = \".*\"/version = \"${new_version}\"/" "$toml"
fi
**Portability issue**: `sed -i` syntax differs between macOS and Linux. On macOS, requires empty string for in-place edit: `sed -i "" ...`. On Linux, `sed -i` works without it. Recommended fix: ```bash # Detect OS and use appropriate sed syntax if [[ "$OSTYPE" == "darwin"* ]]; then sed -i "" "0,/^version = \".*\"/s/^version = \".*\"/version = \"${new_version}\"/" "$toml" else sed -i "0,/^version = \".*\"/s/^version = \".*\"/version = \"${new_version}\"/" "$toml" fi ```
claude[bot] (Migrated from github.com) commented 2025-12-27 06:32:56 -08:00

Version parsing issue: This breaks on semver pre-release/build versions like 0.2.0-alpha.1 or 0.2.0+build.123. The IFS-based split only handles major.minor.patch.

Either:

  1. Document that pre-release versions are unsupported
  2. Strip pre-release/build suffixes before parsing:
version_base="${version%%-*}"  # Strip pre-release
version_base="${version_base%%+*}"  # Strip build metadata
IFS='.' read -r major minor patch <<< "$version_base"
**Version parsing issue**: This breaks on semver pre-release/build versions like `0.2.0-alpha.1` or `0.2.0+build.123`. The IFS-based split only handles `major.minor.patch`. Either: 1. Document that pre-release versions are unsupported 2. Strip pre-release/build suffixes before parsing: ```bash version_base="${version%%-*}" # Strip pre-release version_base="${version_base%%+*}" # Strip build metadata IFS='.' read -r major minor patch <<< "$version_base" ```
@ -0,0 +1,102 @@
#!/usr/bin/env bash
claude[bot] (Migrated from github.com) commented 2025-12-27 06:32:56 -08:00

sed portability issue: Same macOS/Linux incompatibility as enforce-lockstep-versions.sh:108. Also affects lines 41 and throughout the script.

**sed portability issue**: Same macOS/Linux incompatibility as enforce-lockstep-versions.sh:108. Also affects lines 41 and throughout the script.
@ -0,0 +1,95 @@
#!/usr/bin/env bash
claude[bot] (Migrated from github.com) commented 2025-12-27 06:32:56 -08:00

Same version parsing issue: Breaks on pre-release/build metadata. Apply same fix as enforce-lockstep-versions.sh:53.

**Same version parsing issue**: Breaks on pre-release/build metadata. Apply same fix as enforce-lockstep-versions.sh:53.
@ -50,0 +54,4 @@
- name: Enforce lockstep major/minor versioning
run: |
# ADR-025 requires all crates share identical major.minor versions
# This script adjusts versions after release-plz's semver analysis
claude[bot] (Migrated from github.com) commented 2025-12-27 06:32:56 -08:00

Missing validation: After running enforce-lockstep-versions.sh, should validate the result with validate-lockstep-versions.sh to catch any bugs in the enforcement script. Add:

- name: Validate lockstep enforcement
  run: |
    # Verify enforcement script produced valid versions
    ./.github/scripts/validate-lockstep-versions.sh
**Missing validation**: After running `enforce-lockstep-versions.sh`, should validate the result with `validate-lockstep-versions.sh` to catch any bugs in the enforcement script. Add: ```yaml - name: Validate lockstep enforcement run: | # Verify enforcement script produced valid versions ./.github/scripts/validate-lockstep-versions.sh ```
claude[bot] (Migrated from github.com) approved these changes 2025-12-27 06:40:02 -08:00
claude[bot] (Migrated from github.com) left a comment

All previously requested changes have been addressed.

All previously requested changes have been addressed.
Sign in to join this conversation.
No description provided.