name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
id-token: write
jobs:
build-binaries:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
artifact: ries-rs-linux-x86_64
- target: x86_64-apple-darwin
os: macos-latest
artifact: ries-rs-macos-x86_64
- target: aarch64-apple-darwin
os: macos-latest
artifact: ries-rs-macos-aarch64
- target: x86_64-pc-windows-msvc
os: windows-latest
artifact: ries-rs-windows-x86_64.exe
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build
run: cargo build --release --locked --target ${{ matrix.target }}
- name: Package (Unix)
if: runner.os != 'Windows'
run: |
cd target/${{ matrix.target }}/release
strip ries-rs || true
tar -czvf ${{ matrix.artifact }}.tar.gz ries-rs
- name: Package (Windows)
if: runner.os == 'Windows'
run: |
cd target/${{ matrix.target }}/release
7z a ${{ matrix.artifact }}.zip ries-rs.exe
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.artifact }}
path: target/${{ matrix.target }}/release/${{ matrix.artifact }}.*
build-wasm:
name: Build WASM
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust (nightly + wasm32)
uses: dtolnay/rust-toolchain@nightly
with:
targets: wasm32-unknown-unknown
components: rust-src
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '22'
package-manager-cache: false
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Install Node dependencies
run: npm install --no-fund --no-audit
- name: Build WASM
run: npm run build:all
env:
CI: true
- name: Package WASM
run: tar -czvf ries-rs-wasm.tar.gz pkg pkg-node pkg-bundler
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: ries-rs-wasm
path: ries-rs-wasm.tar.gz
build-python:
name: Build Python Wheels
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64
- os: macos-latest
target: aarch64
- os: windows-latest
target: x86_64
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Build wheel
uses: PyO3/maturin-action@v1
with:
command: build
args: --release --locked --out dist
target: ${{ matrix.target }}
working-directory: ries-py
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: python-wheel-${{ matrix.os }}
path: ries-py/dist/*.whl
build-python-sdist:
name: Build Python sdist
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
working-directory: ries-py
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: python-sdist
path: ries-py/dist/*.tar.gz
publish-crate:
name: Publish crate to crates.io
needs: [build-binaries, build-wasm, build-python, build-python-sdist]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Read crate version
id: crate_meta
shell: bash
run: |
VERSION=$(cargo metadata --no-deps --format-version 1 | python3 -c 'import json, sys; data=json.load(sys.stdin); print(next(pkg["version"] for pkg in data["packages"] if pkg["name"] == "ries"))')
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Check crates.io for existing version
id: crate_exists
shell: bash
run: |
http_code=$(curl \
--silent \
--show-error \
--output /tmp/crates-version.json \
--write-out "%{http_code}" \
--user-agent "ries-rs-release/${{ steps.crate_meta.outputs.version }} github-actions" \
"https://crates.io/api/v1/crates/ries/${{ steps.crate_meta.outputs.version }}")
case "$http_code" in
200)
echo "exists=true" >> "$GITHUB_OUTPUT"
;;
404)
echo "exists=false" >> "$GITHUB_OUTPUT"
;;
*)
echo "exists=unknown" >> "$GITHUB_OUTPUT"
echo "Unexpected crates.io response code: $http_code; continuing to publish attempt."
;;
esac
- name: Publish crate
if: steps.crate_exists.outputs.exists != 'true'
shell: bash
run: |
set -o pipefail
publish_log="$(mktemp)"
if cargo publish --locked 2>&1 | tee "$publish_log"; then
exit 0
fi
status=${PIPESTATUS[0]}
if grep -Eqi 'already (exists|uploaded|present)' "$publish_log"; then
echo "ries ${{ steps.crate_meta.outputs.version }} is already published on crates.io; treating rerun as success."
exit 0
fi
exit "$status"
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
- name: Note skipped crate publish
if: steps.crate_exists.outputs.exists == 'true'
run: echo "ries ${{ steps.crate_meta.outputs.version }} is already published on crates.io; skipping upload."
publish-python:
name: Publish Python distributions to PyPI
needs: [build-python, build-python-sdist]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/ries-rs/
steps:
- name: Download wheel artifacts
uses: actions/download-artifact@v7
with:
pattern: python-wheel-*
path: python-dist
merge-multiple: true
- name: Download sdist artifact
uses: actions/download-artifact@v7
with:
name: python-sdist
path: python-dist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python-dist
password: ${{ secrets.PYPI_API_TOKEN }}
skip-existing: true
create-release:
name: Create Release
needs: [build-binaries, build-wasm, build-python, build-python-sdist, publish-crate, publish-python]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Download all artifacts
uses: actions/download-artifact@v7
with:
path: artifacts
- name: Display structure of downloaded files
run: ls -la artifacts/
- name: Select release notes
id: release_notes
shell: bash
run: |
BODY_PATH=".github/release-template.md"
if [ -f "docs/releases/${GITHUB_REF_NAME}.md" ]; then
BODY_PATH="docs/releases/${GITHUB_REF_NAME}.md"
fi
echo "body_path=${BODY_PATH}" >> "$GITHUB_OUTPUT"
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/**/*
body_path: ${{ steps.release_notes.outputs.body_path }}