bindings-release #5
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: bindings-release | |
| # Build librockbox_ffi for macOS + Linux + FreeBSD/NetBSD, package the prebuilt | |
| # binary into the Ruby / Python / TypeScript bindings, and attach every | |
| # artifact to a GitHub Release. No registry tokens required — uploads use the | |
| # built-in GITHUB_TOKEN. | |
| # | |
| # Manually triggerable (workflow_dispatch, pick a tag) or on a `bindings-v*` | |
| # tag push. Targets: darwin-arm64, darwin-x64, linux-x64, linux-arm64 (native | |
| # runners); freebsd-x64, netbsd-x64 (vmactions VMs). BSD jobs are | |
| # continue-on-error and every packaging step skips a target whose artifact is | |
| # missing, so a flaky BSD build never blocks the macOS/Linux release. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag to create / upload assets to" | |
| required: true | |
| default: "bindings-v0.1.1" | |
| push: | |
| tags: | |
| - "bindings-v*" | |
| permissions: | |
| contents: read | |
| env: | |
| # Ruby gem version (drives the .gem filenames). Python and npm carry their | |
| # own versions in their manifests — the Ruby gem intentionally lags at 0.1.0. | |
| BINDING_VERSION: "0.1.0" | |
| jobs: | |
| # 1. Build the shared library on a native runner per target. | |
| build-lib: | |
| name: build-lib (${{ matrix.target }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: darwin-arm64 | |
| os: macos-latest | |
| libfile: librockbox_ffi.dylib | |
| - target: darwin-x64 | |
| os: macos-15-intel | |
| libfile: librockbox_ffi.dylib | |
| - target: linux-x64 | |
| os: ubuntu-latest | |
| libfile: librockbox_ffi.so | |
| - target: linux-arm64 | |
| os: ubuntu-24.04-arm | |
| libfile: librockbox_ffi.so | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - uses: actions-rust-lang/setup-rust-toolchain@v1 | |
| - name: Install Linux audio deps | |
| if: startsWith(matrix.os, 'ubuntu') | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libasound2-dev pkg-config | |
| - name: Build librockbox_ffi | |
| run: cargo build --release -p rockbox-ffi | |
| - name: Stage binary | |
| run: | | |
| mkdir -p out | |
| cp "target/release/${{ matrix.libfile }}" "out/${{ matrix.libfile }}" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: librockbox_ffi-${{ matrix.target }} | |
| path: out/${{ matrix.libfile }} | |
| if-no-files-found: error | |
| # 1b. FreeBSD amd64 — built inside a VM (no native GitHub runner). | |
| build-lib-freebsd: | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Build in FreeBSD VM | |
| uses: vmactions/freebsd-vm@v1 | |
| with: | |
| usesh: true | |
| prepare: | | |
| pkg install -y rust alsa-lib pkgconf | |
| run: | | |
| cargo build --release -p rockbox-ffi | |
| mkdir -p out | |
| cp target/release/librockbox_ffi.so out/librockbox_ffi.so | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: librockbox_ffi-freebsd-x64 | |
| path: out/librockbox_ffi.so | |
| if-no-files-found: error | |
| # 1c. NetBSD amd64 — built inside a VM (no native GitHub runner). | |
| build-lib-netbsd: | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Build in NetBSD VM | |
| uses: vmactions/netbsd-vm@v1 | |
| with: | |
| prepare: | | |
| /usr/sbin/pkg_add rust alsa-lib pkgconf | |
| run: | | |
| cargo build --release -p rockbox-ffi | |
| mkdir -p out | |
| cp target/release/librockbox_ffi.so out/librockbox_ffi.so | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: librockbox_ffi-netbsd-x64 | |
| path: out/librockbox_ffi.so | |
| if-no-files-found: error | |
| # 2a. Ruby platform gems (packaging is host-independent — build all on one runner). | |
| ruby: | |
| needs: [build-lib, build-lib-freebsd, build-lib-netbsd] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: "3.3" | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Build platform gems | |
| working-directory: bindings/ruby | |
| run: | | |
| set -euxo pipefail | |
| declare -A PLAT=( | |
| [darwin-arm64]=arm64-darwin | |
| [darwin-x64]=x86_64-darwin | |
| [linux-x64]=x86_64-linux | |
| [linux-arm64]=aarch64-linux | |
| [freebsd-x64]=x86_64-freebsd | |
| [netbsd-x64]=x86_64-netbsd | |
| ) | |
| declare -A LIB=( | |
| [darwin-arm64]=librockbox_ffi.dylib | |
| [darwin-x64]=librockbox_ffi.dylib | |
| [linux-x64]=librockbox_ffi.so | |
| [linux-arm64]=librockbox_ffi.so | |
| [freebsd-x64]=librockbox_ffi.so | |
| [netbsd-x64]=librockbox_ffi.so | |
| ) | |
| mkdir -p pkg | |
| for target in "${!PLAT[@]}"; do | |
| src="$GITHUB_WORKSPACE/artifacts/librockbox_ffi-${target}/${LIB[$target]}" | |
| if [ ! -f "$src" ]; then echo "skip ${target}: no artifact"; continue; fi | |
| rm -rf vendor && mkdir vendor | |
| cp "$src" vendor/ | |
| ROCKBOX_GEM_PLATFORM="${PLAT[$target]}" gem build rockbox_ffi.gemspec -o "pkg/rockbox_ffi-${BINDING_VERSION}-${PLAT[$target]}.gem" | |
| done | |
| rm -rf vendor | |
| # source gem (fallback for unlisted platforms / repo checkouts) | |
| gem build rockbox_ffi.gemspec -o "pkg/rockbox_ffi-${BINDING_VERSION}.gem" | |
| ls -l pkg | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: ruby-gems | |
| path: bindings/ruby/pkg/*.gem | |
| # 2b. Python wheels. macOS/BSD: retag the pure wheel; Linux: auditwheel | |
| # repair (bundles libasound + sets a real manylinux tag). BSD wheels are | |
| # best-effort — pip falls back to the sdist when the tag does not match. | |
| python: | |
| needs: [build-lib, build-lib-freebsd, build-lib-netbsd] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Install build tooling | |
| run: pip install build wheel auditwheel patchelf | |
| - name: Build wheels | |
| working-directory: bindings/python | |
| run: | | |
| set -euxo pipefail | |
| declare -A LIB=( | |
| [darwin-arm64]=librockbox_ffi.dylib | |
| [darwin-x64]=librockbox_ffi.dylib | |
| [linux-x64]=librockbox_ffi.so | |
| [linux-arm64]=librockbox_ffi.so | |
| [freebsd-x64]=librockbox_ffi.so | |
| [netbsd-x64]=librockbox_ffi.so | |
| ) | |
| declare -A TAG=( | |
| [darwin-arm64]=macosx_11_0_arm64 | |
| [darwin-x64]=macosx_10_12_x86_64 | |
| [freebsd-x64]=freebsd_13_amd64 | |
| [netbsd-x64]=netbsd_10_amd64 | |
| ) | |
| mkdir -p wheelhouse | |
| python -m build --sdist --outdir wheelhouse | |
| for target in darwin-arm64 darwin-x64 linux-x64 linux-arm64 freebsd-x64 netbsd-x64; do | |
| src="$GITHUB_WORKSPACE/artifacts/librockbox_ffi-${target}/${LIB[$target]}" | |
| if [ ! -f "$src" ]; then echo "skip ${target}: no artifact"; continue; fi | |
| rm -rf src/rockbox_ffi/_lib dist && mkdir -p src/rockbox_ffi/_lib | |
| cp "$src" src/rockbox_ffi/_lib/ | |
| python -m build --wheel --outdir dist | |
| case "$target" in | |
| linux-*) | |
| # auditwheel sets the manylinux tag. libasound is treated as an | |
| # external system dependency (--exclude): it can't be vendored | |
| # cross-arch on a single-arch runner, and a bundled libasound | |
| # can't reliably dlopen the system's ALSA plugins anyway. | |
| # Linux users need the libasound2 system package at runtime. | |
| auditwheel repair --exclude libasound.so.2 --wheel-dir wheelhouse dist/*.whl | |
| ;; | |
| *) | |
| python -m wheel tags --platform-tag "${TAG[$target]}" --remove dist/*.whl | |
| cp dist/*.whl wheelhouse/ | |
| ;; | |
| esac | |
| done | |
| rm -rf src/rockbox_ffi/_lib | |
| ls -l wheelhouse | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: python-wheels | |
| path: bindings/python/wheelhouse/* | |
| # 2c. npm: pack the main package + per-platform binary packages into tarballs. | |
| npm: | |
| needs: [build-lib, build-lib-freebsd, build-lib-netbsd] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Build dist, stage binaries, pack tarballs | |
| working-directory: bindings/typescript | |
| run: | | |
| set -euxo pipefail | |
| bun install | |
| bun run build | |
| declare -A LIB=( | |
| [darwin-arm64]=librockbox_ffi.dylib | |
| [darwin-x64]=librockbox_ffi.dylib | |
| [linux-x64]=librockbox_ffi.so | |
| [linux-arm64]=librockbox_ffi.so | |
| [freebsd-x64]=librockbox_ffi.so | |
| [netbsd-x64]=librockbox_ffi.so | |
| ) | |
| dest="$GITHUB_WORKSPACE/bindings/typescript/tarballs" | |
| mkdir -p "$dest" | |
| for target in darwin-arm64 darwin-x64 linux-x64 linux-arm64 freebsd-x64 netbsd-x64; do | |
| src="$GITHUB_WORKSPACE/artifacts/librockbox_ffi-${target}/${LIB[$target]}" | |
| if [ ! -f "$src" ]; then echo "skip ${target}: no artifact"; continue; fi | |
| cp "$src" "npm/${target}/${LIB[$target]}" | |
| (cd "npm/${target}" && npm pack --pack-destination "$dest") | |
| done | |
| npm pack --pack-destination "$dest" | |
| ls -l "$dest" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: npm-tarballs | |
| path: bindings/typescript/tarballs/*.tgz | |
| # 3. Collect every artifact and attach it to the GitHub Release. | |
| release: | |
| needs: [build-lib, build-lib-freebsd, build-lib-netbsd, ruby, python, npm] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Collect release assets | |
| run: | | |
| set -euxo pipefail | |
| mkdir -p release | |
| # packaged: gems, wheels + sdist, npm tarballs | |
| cp artifacts/ruby-gems/*.gem release/ 2>/dev/null || true | |
| cp artifacts/python-wheels/* release/ 2>/dev/null || true | |
| cp artifacts/npm-tarballs/*.tgz release/ 2>/dev/null || true | |
| # raw shared libs, renamed per target so they don't collide | |
| for d in artifacts/librockbox_ffi-*; do | |
| [ -d "$d" ] || continue | |
| t="$(basename "$d" | sed 's/^librockbox_ffi-//')" | |
| for f in "$d"/librockbox_ffi.*; do | |
| [ -f "$f" ] || continue | |
| cp "$f" "release/librockbox_ffi-${t}.${f##*.}" | |
| done | |
| done | |
| ls -l release | |
| - name: Determine tag | |
| id: tag | |
| run: | | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Upload to GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.tag.outputs.tag }} | |
| files: release/* | |
| fail_on_unmatched_files: true |