diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml new file mode 100644 index 0000000..7864542 --- /dev/null +++ b/.gitea/workflows/release.yaml @@ -0,0 +1,67 @@ +name: release + +on: + push: + tags: + - "v*" + +# Runner host prerequisites: git, uv, zig, and a C compiler. +jobs: + build-wheels: + strategy: + fail-fast: false + matrix: + include: + - os: macos + shell: bash + - os: windows + shell: cmd + - os: linux + shell: bash + runs-on: ${{ matrix.os }} + steps: + # Plain git clone: full history so setuptools_scm sees tags, and both + # shells work. Windows uses cmd: bash resolves to WSL (refuses SYSTEM + # accounts) and powershell hits the script execution policy under SYSTEM. + # --recursive pulls in the libaegis submodule needed by the build backend. + - name: Checkout + shell: ${{ matrix.shell }} + run: | + git clone --recursive "${{ gitea.server_url }}/${{ gitea.repository }}.git" . + git checkout -f "${{ gitea.sha }}" + + # Builds sdist + all wheel variants for this platform, runs tests and + # benchmarks, leaves everything in dist/. Upload is left to publish-pypi. + - name: Build wheels for this platform + shell: ${{ matrix.shell }} + run: uv run tools/release.py --no-upload + + - name: Upload wheels as artifact + uses: actions/upload-artifact@v4 + with: + name: dist-${{ matrix.os }} + path: dist/ + retention-days: 1 + + # Publish to PyPI only once every platform's wheel build has succeeded. + # Jobs don't share a workspace, so the per-platform dist/ directories are + # passed around as artifacts and merged here. No Gitea release is created. + publish-pypi: + needs: build-wheels + runs-on: linux + steps: + - name: Download all platform wheels + uses: actions/download-artifact@v4 + with: + pattern: dist-* + path: dist + merge-multiple: true + + # The sdist is identical across platforms; --skip-existing also makes + # reruns of a partially-published release safe. + - name: Publish to PyPI + shell: bash + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: uvx twine upload --skip-existing dist/** diff --git a/.gitignore b/.gitignore index dc02d6a..f748760 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ /src/aeg/_aegis*.pyd __pycache__ !.gitignore +!.gitea/ !.gitmodules diff --git a/tools/release.py b/tools/release.py index ad076fc..7bb0d7f 100755 --- a/tools/release.py +++ b/tools/release.py @@ -312,6 +312,10 @@ def run_benchmark(wheel: Path, py_version: str) -> bool: def main(): """Build wheels for all supported Python versions.""" + # CI matrix jobs build per-platform wheels but never upload; a separate + # publish job uploads everything once all platforms have succeeded. + no_upload = "--no-upload" in sys.argv + repo_root = Path(__file__).parent.parent dist_dir = repo_root / "dist" @@ -460,6 +464,10 @@ def main(): print(f" - {file.name}") # Only upload if this is a clean release version + if no_upload: + print("\n--no-upload given; upload to PyPI skipped.") + return 0 + if not is_release: print(make_release_message(version)) return 0