feat(pep): decide → fulfill → forward Decision Mode PEP (#2571) (#211) #80
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| permissions: | |
| contents: read | |
| env: | |
| AXONFLOW_TELEMETRY: 'off' | |
| jobs: | |
| # Preflight: validate CHANGELOG has a section for the tag being released | |
| # BEFORE any publish step runs. A missing section fails the entire | |
| # workflow cleanly rather than publishing to PyPI first and then failing | |
| # at the GitHub release step. | |
| preflight: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Extract CHANGELOG section | |
| run: | | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| awk -v ver="$VERSION" '$0 ~ "^## \\["ver"\\]" {p=1; next} p && /^## \[/ {exit} p {print}' CHANGELOG.md > /tmp/release-body.md | |
| if [ ! -s /tmp/release-body.md ]; then | |
| echo "::error::No CHANGELOG.md section found for version $VERSION — refusing to publish." | |
| echo "::error::Add a '## [$VERSION] - YYYY-MM-DD' section to CHANGELOG.md and re-tag." | |
| exit 1 | |
| fi | |
| { | |
| echo "## AxonFlow Python SDK v${VERSION}" | |
| echo "" | |
| echo "### Installation" | |
| echo "" | |
| echo '```bash' | |
| echo "pip install axonflow==${VERSION}" | |
| echo '```' | |
| echo "" | |
| cat /tmp/release-body.md | |
| } > /tmp/release-body-final.md | |
| echo "Release body: $(wc -l < /tmp/release-body-final.md) lines" | |
| - name: Upload release body artifact | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: release-body | |
| path: /tmp/release-body-final.md | |
| retention-days: 1 | |
| build: | |
| needs: preflight | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Extract version from tag | |
| id: version | |
| run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT | |
| - 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/_version.py | |
| echo "Updated version to ${VERSION}" | |
| 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 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| cache-dependency-path: 'pyproject.toml' | |
| - name: Install dependencies | |
| run: pip install -e ".[dev,all]" | |
| - name: Run tests | |
| run: pytest | |
| - name: Install build tools | |
| run: pip install build | |
| - name: Build package | |
| run: python -m build | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| publish-pypi: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| environment: pypi | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ | |
| create-release: | |
| needs: [preflight, publish-pypi] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download dist artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Download release body artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-body | |
| path: /tmp/release-body | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| files: dist/* | |
| name: Release ${{ github.ref_name }} | |
| body_path: /tmp/release-body/release-body-final.md | |
| generate_release_notes: false | |
| verify-publish: | |
| needs: [preflight, publish-pypi] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Extract version from tag | |
| id: version | |
| run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT | |
| - name: Wait for PyPI propagation | |
| run: sleep 30 | |
| - name: Verify package on PyPI | |
| run: | | |
| VERSION=${{ steps.version.outputs.VERSION }} | |
| for i in 1 2 3 4 5; do | |
| echo "Attempt $i: Checking PyPI for axonflow==${VERSION}..." | |
| if pip install --dry-run axonflow==${VERSION} 2>&1 | grep -q "Would install"; then | |
| echo "SUCCESS: axonflow ${VERSION} is available on PyPI" | |
| exit 0 | |
| fi | |
| echo "Not yet available, waiting 30 seconds..." | |
| sleep 30 | |
| done | |
| echo "FAILURE: axonflow ${VERSION} not found on PyPI after 5 attempts" | |
| exit 1 |