Release #10
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 | |
| # First-release pipeline (M1): Rust crates → crates.io, Node → npm, | |
| # Python → PyPI (trusted publishing), Go .a artifacts + C ABI artifacts → | |
| # GitHub Release. Swift / Kotlin / Flutter land in later milestones. | |
| on: | |
| push: | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| inputs: | |
| rust: | |
| description: 'Publish Rust crates to crates.io' | |
| type: boolean | |
| default: true | |
| node: | |
| description: 'Build + publish Node binding to npm' | |
| type: boolean | |
| default: false | |
| python: | |
| description: 'Publish Python wheels to PyPI' | |
| type: boolean | |
| default: false | |
| jvm: | |
| description: 'Publish Java + Kotlin to Maven Central' | |
| type: boolean | |
| default: false | |
| artifacts: | |
| description: 'Build Go .a + C ABI libs and create GitHub Release' | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: write | |
| id-token: write # npm provenance + PyPI trusted publishing | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| # ── Rust core → crates.io (dependency order) ───────────────────────────── | |
| rust-publish: | |
| name: Publish crates to crates.io | |
| runs-on: ubuntu-latest | |
| environment: release | |
| if: github.event_name == 'push' || github.event.inputs.rust == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| # Idempotent publish: skips a crate whose version already exists on | |
| # crates.io, so re-running the workflow after a partial failure is safe. | |
| # The version is read from the workspace Cargo.toml so this never goes | |
| # stale across releases. | |
| - name: Publish aimux-stream | |
| run: | | |
| VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/') | |
| if curl -sf -A "aimux-ci" "https://crates.io/api/v1/crates/aimux-stream/${VERSION}" > /dev/null 2>&1; then | |
| echo "aimux-stream@${VERSION} already published, skipping" | |
| else | |
| cargo publish -p aimux-stream | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Publish aimux-core | |
| run: | | |
| sleep 30 | |
| VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/') | |
| if curl -sf -A "aimux-ci" "https://crates.io/api/v1/crates/aimux-core/${VERSION}" > /dev/null 2>&1; then | |
| echo "aimux-core@${VERSION} already published, skipping" | |
| else | |
| cargo publish -p aimux-core | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Publish aimux-provider-utils | |
| run: | | |
| sleep 30 | |
| VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/') | |
| if curl -sf -A "aimux-ci" "https://crates.io/api/v1/crates/aimux-provider-utils/${VERSION}" > /dev/null 2>&1; then | |
| echo "aimux-provider-utils@${VERSION} already published, skipping" | |
| else | |
| cargo publish -p aimux-provider-utils | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Publish aimux-providers | |
| run: | | |
| sleep 30 | |
| VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/') | |
| if curl -sf -A "aimux-ci" "https://crates.io/api/v1/crates/aimux-providers/${VERSION}" > /dev/null 2>&1; then | |
| echo "aimux-providers@${VERSION} already published, skipping" | |
| else | |
| cargo publish -p aimux-providers | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Publish aimux-ffi | |
| run: | | |
| sleep 30 | |
| VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/') | |
| if curl -sf -A "aimux-ci" "https://crates.io/api/v1/crates/aimux-ffi/${VERSION}" > /dev/null 2>&1; then | |
| echo "aimux-ffi@${VERSION} already published, skipping" | |
| else | |
| cargo publish -p aimux-ffi | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| # ── Node binding: build per-platform .node, then publish to npm ────────── | |
| node-build: | |
| name: Node build (${{ matrix.settings.target }}) | |
| runs-on: ${{ matrix.settings.host }} | |
| env: | |
| MACOSX_DEPLOYMENT_TARGET: '10.13' | |
| if: github.event_name == 'push' || github.event.inputs.node == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| settings: | |
| - host: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| binary: aimux.win32-x64-msvc.node | |
| - host: windows-latest | |
| target: aarch64-pc-windows-msvc | |
| binary: aimux.win32-arm64-msvc.node | |
| - host: macos-15-intel | |
| target: x86_64-apple-darwin | |
| binary: aimux.darwin-x64.node | |
| - host: macos-latest | |
| target: aarch64-apple-darwin | |
| binary: aimux.darwin-arm64.node | |
| - host: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| binary: aimux.linux-x64-gnu.node | |
| - host: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| binary: aimux.linux-arm64-gnu.node | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.settings.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| - name: Install dependencies | |
| working-directory: bindings/node | |
| run: npm ci | |
| - name: Build Linux (glibc 2.17 baseline) | |
| if: contains(matrix.settings.target, 'unknown-linux-gnu') | |
| working-directory: bindings/node | |
| shell: bash | |
| env: | |
| TARGET_CC: clang | |
| TARGET_CXX: clang++ | |
| run: npx napi build --platform --release --target ${{ matrix.settings.target }} --use-napi-cross | |
| - name: Build | |
| if: ${{ !contains(matrix.settings.target, 'unknown-linux-gnu') }} | |
| working-directory: bindings/node | |
| shell: bash | |
| run: npx napi build --platform --release --target ${{ matrix.settings.target }} | |
| - name: Verify artifact | |
| working-directory: bindings/node | |
| shell: bash | |
| run: test -f '${{ matrix.settings.binary }}' | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: node-${{ matrix.settings.target }} | |
| path: bindings/node/*.node | |
| node-publish: | |
| name: Publish Node to npm | |
| needs: node-build | |
| runs-on: ubuntu-latest | |
| environment: release | |
| if: github.event_name == 'push' || github.event.inputs.node == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| registry-url: https://registry.npmjs.org | |
| - name: Install dependencies | |
| working-directory: bindings/node | |
| run: npm ci | |
| - name: Create platform packages | |
| working-directory: bindings/node | |
| run: npx napi create-npm-dirs | |
| - name: Download platform binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: bindings/node/artifacts | |
| pattern: node-* | |
| - name: Collect and verify platform packages | |
| working-directory: bindings/node | |
| shell: bash | |
| run: | | |
| npx napi artifacts --output-dir artifacts --npm-dir npm | |
| expected=( | |
| npm/win32-x64-msvc/aimux.win32-x64-msvc.node | |
| npm/win32-arm64-msvc/aimux.win32-arm64-msvc.node | |
| npm/darwin-x64/aimux.darwin-x64.node | |
| npm/darwin-arm64/aimux.darwin-arm64.node | |
| npm/linux-x64-gnu/aimux.linux-x64-gnu.node | |
| npm/linux-arm64-gnu/aimux.linux-arm64-gnu.node | |
| ) | |
| for binary in "${expected[@]}"; do | |
| test -f "$binary" || { echo "Missing $binary" >&2; exit 1; } | |
| done | |
| test "$(find npm -type f -name 'aimux.*.node' | wc -l | tr -d ' ')" = 6 | |
| - name: Publish | |
| working-directory: bindings/node | |
| run: npm publish --provenance --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| NPM_CONFIG_PROVENANCE: "true" | |
| NPM_CONFIG_ACCESS: public | |
| # ── Python binding: build + publish platform wheels (OIDC trusted) ─────── | |
| python-release: | |
| name: Publish Python (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| if: github.event_name == 'push' || github.event.inputs.python == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Build and publish wheel | |
| uses: PyO3/maturin-action@v1 | |
| with: | |
| command: publish | |
| args: --no-sdist | |
| working-directory: bindings/python | |
| # ── Go binding: build libaimux_ffi.a per platform, ship on GitHub Release | |
| # (users download the archive for their platform via `go generate`). | |
| # NOTE: Windows uses the gnu target — Go's cgo links with mingw gcc, | |
| # which cannot consume MSVC-format .a archives. ───────────────────────── | |
| go-build: | |
| name: Go binding (${{ matrix.settings.target }}) | |
| runs-on: ${{ matrix.settings.host }} | |
| if: github.event_name == 'push' || github.event.inputs.artifacts == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| settings: | |
| - host: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| - host: macos-13 | |
| target: x86_64-apple-darwin | |
| - host: macos-latest | |
| target: aarch64-apple-darwin | |
| - host: windows-latest | |
| target: x86_64-pc-windows-gnu | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.settings.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Build static archive | |
| run: cargo build -p aimux-ffi --release --target ${{ matrix.settings.target }} | |
| - name: Test Go binding | |
| working-directory: bindings/go | |
| shell: bash | |
| run: | | |
| export CGO_LDFLAGS="-L${{ github.workspace }}/target/${{ matrix.settings.target }}/release" | |
| go vet ./... | |
| go test ./... | |
| - name: Stage archive with platform name | |
| shell: bash | |
| run: | | |
| mkdir -p staging | |
| cp target/${{ matrix.settings.target }}/release/libaimux_ffi.a staging/libaimux_ffi-${{ matrix.settings.target }}.a | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: libaimux_ffi-${{ matrix.settings.target }}.a | |
| path: staging/libaimux_ffi-${{ matrix.settings.target }}.a | |
| # ── C ABI shared libraries (C/C++ + reference for other bindings) ──────── | |
| ffi-build: | |
| name: aimux-ffi (${{ matrix.target }}) | |
| runs-on: ${{ matrix.host }} | |
| if: github.event_name == 'push' || github.event.inputs.artifacts == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - host: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| artifact: libaimux_ffi-linux-x64.so | |
| - host: macos-latest | |
| target: aarch64-apple-darwin | |
| artifact: libaimux_ffi-macos-arm64.dylib | |
| - host: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| artifact: aimux_ffi-windows-x64.dll | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Build cdylib | |
| run: cargo build -p aimux-ffi --release --target ${{ matrix.target }} | |
| - name: Stage library with platform name | |
| shell: bash | |
| run: | | |
| mkdir -p staging | |
| case "${{ matrix.target }}" in | |
| *linux*) cp target/${{ matrix.target }}/release/libaimux_ffi.so staging/${{ matrix.artifact }} ;; | |
| *darwin*) cp target/${{ matrix.target }}/release/libaimux_ffi.dylib staging/${{ matrix.artifact }} ;; | |
| *windows*) cp target/${{ matrix.target }}/release/aimux_ffi.dll staging/${{ matrix.artifact }} ;; | |
| esac | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: staging/${{ matrix.artifact }} | |
| # ── JVM bindings: publish aimux-java + aimux-kotlin to Maven Central ────── | |
| jvm-publish: | |
| name: Publish Java+Kotlin to Maven Central | |
| runs-on: ubuntu-latest | |
| environment: release | |
| if: github.event_name == 'push' || github.event.inputs.jvm == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: "17" | |
| - uses: gradle/actions/setup-gradle@v4 | |
| - name: Publish aimux-java | |
| working-directory: bindings/java | |
| run: gradle publish | |
| env: | |
| ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.OSSRH_USERNAME }} | |
| ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.OSSRH_PASSWORD }} | |
| ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }} | |
| ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }} | |
| - name: Publish aimux-kotlin | |
| working-directory: bindings/kotlin | |
| run: gradle publish | |
| env: | |
| ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.OSSRH_USERNAME }} | |
| ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.OSSRH_PASSWORD }} | |
| ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }} | |
| ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }} | |
| # ── GitHub Release: aggregate all binary artifacts (C ABI libs + Go .a) ── | |
| github-release: | |
| name: Create GitHub Release | |
| needs: [go-build, ffi-build] | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' || github.event.inputs.artifacts == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: release-assets | |
| merge-multiple: true | |
| - name: List assets | |
| run: ls -la release-assets/ | |
| - name: Create release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: release-assets/** | |
| generate_release_notes: true |