Release #60
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 | |
| # Triggers on version tags: v9.0.0, v9.1.0, etc. | |
| # | |
| # Release checklist (run before pushing the tag): | |
| # 1. All tests green on main. | |
| # 2. Version bumped in setup.py and metbit/__init__.py. | |
| # 3. CHANGELOG.md updated. | |
| # 4. git tag v9.0.0 && git push origin v9.0.0 | |
| # | |
| # PyPI trusted publishing must be configured at: | |
| # https://pypi.org/manage/project/metbit/settings/publishing/ | |
| # with workflow name "python-publish.yml" and environment "pypi". | |
| on: | |
| push: | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Tag to release (e.g. v9.0.0)" | |
| required: true | |
| type: string | |
| # Normalize the release tag across both trigger types. | |
| env: | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| permissions: | |
| contents: write # create GitHub Release + upload assets | |
| id-token: write # OIDC trusted publishing to PyPI | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # 1. Gate: full functional test suite must be green on all supported Pythons | |
| # --------------------------------------------------------------------------- | |
| test: | |
| name: "Test – Python ${{ matrix.python-version }}" | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install build tools + dev dependencies | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel | |
| pip install -r requirements-dev.txt | |
| - name: Build and install (including C extension) | |
| run: pip install -e . --no-build-isolation | |
| - name: Run test suite (excluding slow/perf) | |
| run: | | |
| pytest \ | |
| -m "not slow and not perf" \ | |
| --junit-xml=test-results.xml \ | |
| --cov-report=xml:coverage.xml | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results-py${{ matrix.python-version }} | |
| path: test-results.xml | |
| - name: Upload coverage report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-py${{ matrix.python-version }} | |
| path: coverage.xml | |
| # --------------------------------------------------------------------------- | |
| # 2. Source distribution | |
| # --------------------------------------------------------------------------- | |
| build-sdist: | |
| name: Build sdist | |
| needs: test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Build source distribution | |
| run: | | |
| pip install build | |
| python -m build --sdist | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: sdist | |
| path: dist/*.tar.gz | |
| # --------------------------------------------------------------------------- | |
| # 3. Binary wheels via cibuildwheel | |
| # Linux: manylinux_2_28 x86_64 + aarch64 (QEMU) | |
| # macOS: x86_64 + arm64 | |
| # Windows: AMD64 | |
| # --------------------------------------------------------------------------- | |
| build-wheels: | |
| name: "Wheels – ${{ matrix.os }}" | |
| needs: test | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up QEMU (Linux aarch64 cross-compilation) | |
| if: matrix.os == 'ubuntu-latest' | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: arm64 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install cibuildwheel | |
| run: pip install "cibuildwheel>=3.0,<4" | |
| - name: Build wheels | |
| env: | |
| CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" | |
| CIBW_SKIP: "*-win32 *-manylinux_i686 *-musllinux_* pp*" | |
| CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 | |
| CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 | |
| CIBW_ARCHS_LINUX: "x86_64 aarch64" | |
| CIBW_ARCHS_MACOS: "x86_64 arm64" | |
| CIBW_ARCHS_WINDOWS: "AMD64" | |
| CIBW_ENVIRONMENT: "METBIT_PORTABLE_BUILD=1" | |
| CIBW_TEST_SKIP: "*-macosx_x86_64" | |
| CIBW_TEST_COMMAND: 'python -c "import metbit; assert metbit.__version__, \"version missing\"; from metbit._native import pearson_columns, backend_info; import numpy as np; r = pearson_columns(np.eye(5), anchor_index=0); assert abs(r[0] - 1.0) < 1e-12, r[0]; print(\"OK - backend:\", backend_info())"' | |
| CIBW_TEST_REQUIRES: "numpy" | |
| CIBW_BUILD_VERBOSITY: 1 | |
| run: cibuildwheel --output-dir wheelhouse | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.os }} | |
| path: wheelhouse/*.whl | |
| # --------------------------------------------------------------------------- | |
| # 4. Verify all artifacts: integrity check + security scan | |
| # --------------------------------------------------------------------------- | |
| verify: | |
| name: Verify artifacts | |
| needs: [build-sdist, build-wheels] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download source distribution | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: sdist | |
| path: dist/ | |
| - name: Download wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: "wheels-*" | |
| path: dist/ | |
| merge-multiple: true | |
| - name: List artifacts | |
| run: ls -lh dist/ | |
| - name: Integrity check (twine) | |
| run: | | |
| pip install twine | |
| twine check dist/* | |
| - name: Security scan (Trivy, warn only) | |
| uses: aquasecurity/trivy-action@v0.36.0 | |
| with: | |
| scan-type: fs | |
| scan-ref: . | |
| severity: "CRITICAL,HIGH" | |
| exit-code: "0" | |
| # --------------------------------------------------------------------------- | |
| # 5. Publish to PyPI (OIDC trusted publishing - no API token needed) | |
| # --------------------------------------------------------------------------- | |
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: verify | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/project/metbit/${{ env.RELEASE_TAG }} | |
| steps: | |
| - name: Download source distribution | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: sdist | |
| path: dist/ | |
| - name: Download wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: "wheels-*" | |
| path: dist/ | |
| merge-multiple: true | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ | |
| skip-existing: false | |
| # --------------------------------------------------------------------------- | |
| # 6. Create GitHub Release and attach all artifacts | |
| # --------------------------------------------------------------------------- | |
| github-release: | |
| name: GitHub Release | |
| needs: publish-pypi | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Extract changelog section for this version | |
| id: changelog | |
| shell: bash | |
| run: | | |
| VERSION="${RELEASE_TAG#v}" | |
| NOTES=$(awk "/^## \[${VERSION}\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md) | |
| { | |
| echo "notes<<EOF" | |
| echo "$NOTES" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| env: | |
| RELEASE_TAG: ${{ env.RELEASE_TAG }} | |
| - name: Download source distribution | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: sdist | |
| path: release-assets/ | |
| - name: Download wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: "wheels-*" | |
| path: release-assets/ | |
| merge-multiple: true | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ env.RELEASE_TAG }} | |
| name: "metbit ${{ env.RELEASE_TAG }}" | |
| body: ${{ steps.changelog.outputs.notes }} | |
| draft: false | |
| prerelease: false | |
| files: release-assets/* | |
| generate_release_notes: true |