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
80 changes: 80 additions & 0 deletions .github/scripts/validate-version-alignment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env bash
# Validates that the Python SDK's version declarations match the most
# recent released section of CHANGELOG.md. Patterned on the same
# script in axonflow-enterprise and axonflow-sdk-go.
#
# Why: the release workflow sed-rewrites pyproject.toml + axonflow/
# _version.py at publish time but never commits the bump back to main,
# so the repo version silently lags the registry version between
# releases. This gate enforces the invariant on every PR:
#
# pyproject.toml::version
# == axonflow/_version.py::__version__
# == most recent `## [X.Y.Z]` section in CHANGELOG.md
#
# When it's time to release, a single release-prep PR renames
# [Unreleased] → [X.Y.Z] - DATE AND bumps both manifest files in the
# same commit so this gate always sees them together.
#
# Run locally:
# ./.github/scripts/validate-version-alignment.sh

set -euo pipefail

ERRORS=0

# Latest RELEASED version = first `## [x.y.z]` line that isn't
# [Unreleased] (which starts with a letter, not a digit).
#
# `{ grep || true; }` is deliberate: under `set -euo pipefail`, a
# failing grep (no match) aborts the whole command substitution
# before we reach the -z check, killing the script silently. The
# wrapper lets the `-z` check produce the real user-facing error.
LATEST_VERSION=$({ grep -m1 -E '^## \[[0-9]' CHANGELOG.md || true; } | sed 's/## \[\(.*\)\].*/\1/' | sed 's/^v//')

if [ -z "${LATEST_VERSION:-}" ]; then
echo "❌ Could not extract a released version (## [X.Y.Z]) from CHANGELOG.md"
exit 1
fi

echo "📋 Latest CHANGELOG version: $LATEST_VERSION"
echo ""

# Check pyproject.toml::version
echo "📦 Checking pyproject.toml..."
PYPROJECT_VER=$(grep -m1 -E '^version = "' pyproject.toml | sed 's/version = "\(.*\)"/\1/' || true)
if [ -z "${PYPROJECT_VER:-}" ]; then
echo " ❌ pyproject.toml — could not read version"
ERRORS=$((ERRORS + 1))
elif [ "$PYPROJECT_VER" != "$LATEST_VERSION" ]; then
echo " ❌ pyproject.toml — version is \"$PYPROJECT_VER\", expected \"$LATEST_VERSION\""
ERRORS=$((ERRORS + 1))
else
echo " ✅ pyproject.toml — $PYPROJECT_VER"
fi

# Check axonflow/_version.py::__version__
echo "🔧 Checking axonflow/_version.py..."
VERSION_PY=$(grep -m1 -E '^__version__ = "' axonflow/_version.py | sed 's/__version__ = "\(.*\)"/\1/' || true)
if [ -z "${VERSION_PY:-}" ]; then
echo " ❌ axonflow/_version.py — could not read __version__"
ERRORS=$((ERRORS + 1))
elif [ "$VERSION_PY" != "$LATEST_VERSION" ]; then
echo " ❌ axonflow/_version.py — __version__ is \"$VERSION_PY\", expected \"$LATEST_VERSION\""
ERRORS=$((ERRORS + 1))
else
echo " ✅ axonflow/_version.py — $VERSION_PY"
fi

echo ""

if [ "$ERRORS" -gt 0 ]; then
echo "❌ Found $ERRORS version misalignment(s)."
echo ""
echo "Fix: bump the stale file(s) to match CHANGELOG v$LATEST_VERSION."
echo "Or, if CHANGELOG is behind a tag you already pushed, add the"
echo "missing '## [X.Y.Z] - YYYY-MM-DD' section."
exit 1
fi

echo "✅ All version constants match CHANGELOG v$LATEST_VERSION."
22 changes: 19 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,28 @@ jobs:

- name: Update version in source
run: |
set -e
VERSION=${{ steps.version.outputs.VERSION }}
# The real __version__ single source of truth lives in
# axonflow/_version.py; axonflow/__init__.py only re-exports
# via `from axonflow._version import __version__`. Targeting
# __init__.py was a silent no-op and shipped wheels with a
# runtime __version__ that lagged the PyPI metadata version.
sed -i "s/^version = .*/version = \"${VERSION}\"/" pyproject.toml
sed -i "s/^__version__ = .*/__version__ = \"${VERSION}\"/" axonflow/__init__.py
sed -i "s/^__version__ = .*/__version__ = \"${VERSION}\"/" axonflow/_version.py
echo "Updated version to ${VERSION}"
grep version pyproject.toml | head -1
grep __version__ axonflow/__init__.py
grep "^version" pyproject.toml | head -1
grep "^__version__" axonflow/_version.py
# Fail fast if either sed missed its target — don't let a
# silent no-op ship a wheel with mismatched runtime version.
if ! grep -q "^version = \"${VERSION}\"" pyproject.toml; then
echo "::error::sed did not bump pyproject.toml to ${VERSION}"
exit 1
fi
if ! grep -q "^__version__ = \"${VERSION}\"" axonflow/_version.py; then
echo "::error::sed did not bump axonflow/_version.py to ${VERSION}"
exit 1
fi

- name: Set up Python
uses: actions/setup-python@v5
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/validate-version-alignment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Version Alignment Check

# Blocks merges that would leave pyproject.toml / axonflow/_version.py
# out of sync with CHANGELOG.md's most recent released section. The
# invariant on main:
# pyproject.toml::version
# == axonflow/_version.py::__version__
# == first ## [X.Y.Z] section in CHANGELOG
#
# When it's time to ship a new release, a single release-prep PR
# renames [Unreleased] → [X.Y.Z] - YYYY-MM-DD AND bumps both manifest
# files in the same commit, so this gate always sees them together.
#
# See .github/scripts/validate-version-alignment.sh for the script.

on:
pull_request:
branches: [main]
paths:
- 'CHANGELOG.md'
- 'pyproject.toml'
- 'axonflow/_version.py'
- '.github/scripts/validate-version-alignment.sh'
- '.github/workflows/validate-version-alignment.yml'
push:
branches: [main]
paths:
- 'CHANGELOG.md'
- 'pyproject.toml'
- 'axonflow/_version.py'
- '.github/scripts/validate-version-alignment.sh'
- '.github/workflows/validate-version-alignment.yml'

permissions:
contents: read

jobs:
validate-versions:
name: Validate Version Alignment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check version alignment
run: ./.github/scripts/validate-version-alignment.sh
2 changes: 1 addition & 1 deletion axonflow/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Single source of truth for the AxonFlow SDK version."""

__version__ = "6.6.0"
__version__ = "6.6.1"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "axonflow"
version = "6.6.0"
version = "6.6.1"
description = "AxonFlow Python SDK - Enterprise AI Governance in 3 Lines of Code"
readme = "README.md"
license = {text = "MIT"}
Expand Down
Loading