404 lines
16 KiB
YAML
404 lines
16 KiB
YAML
name: Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
|
|
jobs:
|
|
release:
|
|
permissions:
|
|
contents: write
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- platform: "macos-latest"
|
|
args: "--target aarch64-apple-darwin"
|
|
python-version: "3.12"
|
|
backend: "mlx"
|
|
- platform: "macos-15-intel"
|
|
args: "--target x86_64-apple-darwin"
|
|
python-version: "3.12"
|
|
backend: "pytorch"
|
|
- platform: "windows-latest"
|
|
args: ""
|
|
python-version: "3.12"
|
|
backend: "pytorch"
|
|
|
|
runs-on: ${{ matrix.platform }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
# Ubuntu runners ship with ~14 GB free; pip + PyInstaller + torch can
|
|
# peak well above that during the build. Reclaim ~25 GB by pruning
|
|
# preinstalled toolchains we don't use. This is what likely tripped
|
|
# the March 2026 Linux release attempts (see commit 103e98b
|
|
# "github runners suck") — not a code issue, a disk-pressure one.
|
|
- name: Free up disk space (ubuntu)
|
|
if: contains(matrix.platform, 'ubuntu') || contains(matrix.platform, 'namespace')
|
|
# Pinned to v1.3.1 (SHA) — this job runs with contents: write and
|
|
# handles signing secrets later, so we don't want a floating ref.
|
|
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
|
with:
|
|
tool-cache: false
|
|
android: true
|
|
dotnet: true
|
|
haskell: true
|
|
# large-packages: true would `apt-get remove '^llvm-.*'`, which
|
|
# cascade-removes reverse deps that won't be pulled back in by the
|
|
# `llvm-dev` install below. The other flags already free ~20 GB,
|
|
# enough for the Python + torch + PyInstaller build.
|
|
large-packages: false
|
|
swap-storage: true
|
|
|
|
- name: Install dependencies (ubuntu only)
|
|
if: contains(matrix.platform, 'ubuntu') || contains(matrix.platform, 'namespace')
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf llvm-dev libasound2-dev
|
|
|
|
- name: Install LLVM (macOS)
|
|
if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-15-intel'
|
|
run: |
|
|
brew install llvm@20
|
|
echo "$(brew --prefix llvm@20)/bin" >> $GITHUB_PATH
|
|
echo "LLVM_CONFIG=$(brew --prefix llvm@20)/bin/llvm-config" >> $GITHUB_ENV
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
cache: "pip"
|
|
|
|
- name: Install CPU-only PyTorch (Linux)
|
|
if: contains(matrix.platform, 'ubuntu') || contains(matrix.platform, 'namespace')
|
|
run: |
|
|
pip install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
|
|
|
|
- name: Install Python dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install pyinstaller
|
|
pip install -r backend/requirements.txt
|
|
pip install --no-deps chatterbox-tts
|
|
pip install --no-deps hume-tada
|
|
|
|
- name: Install MLX dependencies (Apple Silicon only)
|
|
if: matrix.backend == 'mlx'
|
|
run: |
|
|
pip install -r backend/requirements-mlx.txt
|
|
# mlx-audio>=0.3.1 and mlx-lm>=0.31.1 both declare transformers>=5.x,
|
|
# which conflicts with our 4.57.x cap. The runtime APIs we use work
|
|
# fine on transformers 4.57.x in practice (verified in dev), so install
|
|
# them --no-deps. mlx-audio's other runtime deps (huggingface_hub,
|
|
# librosa, numpy, numba, pyloudnorm) are already in requirements.txt;
|
|
# miniaudio is in requirements-mlx.txt (needed by mlx_audio.stt,
|
|
# not transitively pulled by anything else — see issue #505); the
|
|
# rest (sounddevice, protobuf, sentencepiece, pyyaml, jinja2) are
|
|
# pulled in by other engines.
|
|
pip install --no-deps mlx-lm==0.31.1
|
|
pip install --no-deps mlx-audio==0.4.1
|
|
|
|
- name: Build Python server (Linux/macOS)
|
|
if: matrix.platform != 'windows-latest'
|
|
run: |
|
|
chmod +x scripts/build-server.sh
|
|
./scripts/build-server.sh
|
|
|
|
- name: Build Python server (Windows)
|
|
if: matrix.platform == 'windows-latest'
|
|
shell: bash
|
|
run: |
|
|
cd backend
|
|
python build_binary.py
|
|
python build_binary.py --shim
|
|
|
|
# Get platform tuple
|
|
PLATFORM=$(rustc --print host-tuple)
|
|
|
|
# Create binaries directory
|
|
mkdir -p ../tauri/src-tauri/binaries
|
|
|
|
# Copy with platform suffix
|
|
cp dist/voicebox-server.exe ../tauri/src-tauri/binaries/voicebox-server-${PLATFORM}.exe
|
|
cp dist/voicebox-mcp.exe ../tauri/src-tauri/binaries/voicebox-mcp-${PLATFORM}.exe
|
|
echo "Built voicebox-server-${PLATFORM}.exe"
|
|
echo "Built voicebox-mcp-${PLATFORM}.exe"
|
|
|
|
- name: Setup Bun
|
|
uses: oven-sh/setup-bun@v2
|
|
|
|
- name: Install Rust stable
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ (matrix.platform == 'macos-latest' && 'aarch64-apple-darwin') || (matrix.platform == 'macos-15-intel' && 'x86_64-apple-darwin') || '' }}
|
|
|
|
- name: Rust cache
|
|
uses: swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: "./tauri/src-tauri -> target"
|
|
|
|
- name: Install dependencies
|
|
run: bun install
|
|
|
|
- name: Install Apple API key
|
|
if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-15-intel'
|
|
run: |
|
|
mkdir -p ~/.appstoreconnect/private_keys/
|
|
cd ~/.appstoreconnect/private_keys/
|
|
echo ${{ secrets.APPLE_API_KEY_BASE64 }} >> AuthKey_${{ secrets.APPLE_API_KEY }}.p8.base64
|
|
base64 --decode -i AuthKey_${{ secrets.APPLE_API_KEY }}.p8.base64 -o AuthKey_${{ secrets.APPLE_API_KEY }}.p8
|
|
rm AuthKey_${{ secrets.APPLE_API_KEY }}.p8.base64
|
|
|
|
- name: Install Codesigning Certificate
|
|
if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-15-intel'
|
|
uses: apple-actions/import-codesign-certs@v3
|
|
with:
|
|
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
|
|
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
|
|
- name: Disk / environment snapshot (pre-bundle debug)
|
|
if: contains(matrix.platform, 'ubuntu') || contains(matrix.platform, 'namespace')
|
|
run: |
|
|
echo "=== df -h ==="
|
|
df -h
|
|
echo "=== free -h ==="
|
|
free -h
|
|
echo "=== Rust / Cargo ==="
|
|
rustc --version
|
|
cargo --version
|
|
echo "=== Bun ==="
|
|
bun --version
|
|
echo "=== Tauri CLI ==="
|
|
cd tauri && bun run tauri --version
|
|
|
|
- name: Extract release notes from CHANGELOG.md
|
|
id: changelog
|
|
shell: bash
|
|
run: |
|
|
# Get the version from the tag (strip leading 'v')
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
|
|
# Extract the section for this version from CHANGELOG.md
|
|
# Matches from "## [X.Y.Z]" until the next "## [" heading
|
|
NOTES=$(sed -n "/^## \[${VERSION}\]/,/^## \[/{/^## \[${VERSION}\]/d;/^## \[/d;p;}" CHANGELOG.md)
|
|
|
|
# Fall back to a placeholder if the version isn't in the changelog
|
|
if [ -z "$(echo "$NOTES" | tr -d '[:space:]')" ]; then
|
|
NOTES="See the assets below to download and install this version."
|
|
fi
|
|
|
|
# Use multiline output syntax
|
|
{
|
|
echo "notes<<CHANGELOG_EOF"
|
|
echo "$NOTES"
|
|
echo "CHANGELOG_EOF"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
# Linux hang watchdog: previous releases silently wedged inside tauri
|
|
# bundling (possibly linuxdeploy/AppImage download, possibly cargo link).
|
|
# Cap the step at 30 min so we get logs instead of waiting out the 6hr
|
|
# job timeout. Other platforms historically complete in ~25 min, so 45
|
|
# is comfortable.
|
|
- uses: tauri-apps/tauri-action@v0.6
|
|
timeout-minutes: ${{ (contains(matrix.platform, 'ubuntu') || contains(matrix.platform, 'namespace')) && 30 || 45 }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
|
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
|
APPLE_PROVIDER_SHORT_NAME: ${{ secrets.APPLE_PROVIDER_SHORT_NAME }}
|
|
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
|
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
|
# Stream subprocess stdout/stderr so the hang is visible in logs.
|
|
CARGO_TERM_VERBOSE: "true"
|
|
RUST_BACKTRACE: "1"
|
|
with:
|
|
projectPath: tauri
|
|
tagName: v__VERSION__
|
|
releaseName: "voicebox v__VERSION__"
|
|
releaseBody: ${{ steps.changelog.outputs.notes }}
|
|
releaseDraft: true
|
|
prerelease: false
|
|
args: ${{ matrix.args }}
|
|
includeUpdaterJson: true
|
|
|
|
# Tauri's bundler signs the .app and notarizes it, but the .dmg wrapper
|
|
# ships unnotarized. Gatekeeper rejects that on macOS 15 Sequoia (caught
|
|
# by Homebrew Cask CI) and causes "app isn't signed" dialogs on older
|
|
# Intel Macs when Apple's notarization servers are slow (see issue #509).
|
|
# Submit the .dmg to notarytool, staple the ticket, and overwrite the
|
|
# release asset uploaded by tauri-action.
|
|
- name: Notarize and staple DMG (macOS)
|
|
if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-15-intel'
|
|
env:
|
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY }}
|
|
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
KEY_PATH="$HOME/.appstoreconnect/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8"
|
|
TARGET=$(echo "${{ matrix.args }}" | sed -n 's/.*--target \([a-z0-9_-]*\).*/\1/p')
|
|
DMG_DIR="tauri/src-tauri/target/${TARGET}/release/bundle/dmg"
|
|
# Match the release tag tauri-action resolved from tauri.conf.json's
|
|
# version field; GITHUB_REF_NAME is a branch name under workflow_dispatch.
|
|
RELEASE_TAG="v$(jq -r '.version' tauri/src-tauri/tauri.conf.json)"
|
|
shopt -s nullglob
|
|
dmgs=("${DMG_DIR}"/*.dmg)
|
|
if [ ${#dmgs[@]} -eq 0 ]; then
|
|
echo "::error::No DMGs found in ${DMG_DIR} — tauri bundler output path may have changed"
|
|
exit 1
|
|
fi
|
|
for dmg in "${dmgs[@]}"; do
|
|
echo "::group::Notarize $(basename "$dmg")"
|
|
xcrun notarytool submit "$dmg" \
|
|
--key "$KEY_PATH" \
|
|
--key-id "$APPLE_API_KEY_ID" \
|
|
--issuer "$APPLE_API_ISSUER" \
|
|
--wait --timeout 20m
|
|
xcrun stapler staple "$dmg"
|
|
spctl -a -t open --context context:primary-signature -vv "$dmg"
|
|
gh release upload "${RELEASE_TAG}" "$dmg" --clobber \
|
|
--repo "${GITHUB_REPOSITORY}"
|
|
echo "::endgroup::"
|
|
done
|
|
|
|
build-cuda-windows:
|
|
runs-on: windows-latest
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
cache: "pip"
|
|
|
|
- name: Install Python dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install pyinstaller
|
|
pip install -r backend/requirements.txt
|
|
pip install --no-deps chatterbox-tts
|
|
pip install --no-deps hume-tada
|
|
|
|
- name: Install PyTorch with CUDA 12.8
|
|
run: |
|
|
pip install torch --index-url https://download.pytorch.org/whl/cu128 --force-reinstall --no-deps
|
|
pip install torchaudio --index-url https://download.pytorch.org/whl/cu128 --force-reinstall --no-deps
|
|
|
|
- name: Verify CUDA support in torch
|
|
run: |
|
|
python -c "import torch; print(f'CUDA available in build: {torch.cuda.is_available()}'); print(f'CUDA version: {torch.version.cuda}')"
|
|
|
|
- name: Build CUDA server binary (onedir)
|
|
shell: bash
|
|
working-directory: backend
|
|
env:
|
|
# Include Blackwell (sm_120) via PTX forward compatibility.
|
|
# Pre-built PyTorch cu128 wheels ship native kernels for sm_80/86/89/90
|
|
# but not sm_120. Setting this env var causes torch.utils.cpp_extension
|
|
# (and any JIT-compiled kernels) to target Blackwell GPUs as well.
|
|
TORCH_CUDA_ARCH_LIST: "8.0;8.6;8.9;9.0;12.0+PTX"
|
|
run: python build_binary.py --cuda
|
|
|
|
- name: Package into server core + CUDA libs archives
|
|
shell: bash
|
|
run: |
|
|
python scripts/package_cuda.py \
|
|
backend/dist/voicebox-server-cuda/ \
|
|
--output release-assets/ \
|
|
--cuda-libs-version cu128-v1 \
|
|
--torch-compat ">=2.7.0,<2.11.0"
|
|
|
|
- name: Upload archives to GitHub Release
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: |
|
|
release-assets/voicebox-server-cuda.tar.gz
|
|
release-assets/voicebox-server-cuda.tar.gz.sha256
|
|
release-assets/cuda-libs-cu128-v1.tar.gz
|
|
release-assets/cuda-libs-cu128-v1.tar.gz.sha256
|
|
release-assets/cuda-libs.json
|
|
draft: true
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Upload onedir as workflow artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: voicebox-server-cuda-windows
|
|
path: backend/dist/voicebox-server-cuda/
|
|
retention-days: 7
|
|
|
|
build-rocm-windows:
|
|
runs-on: windows-latest
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
# ROCm wheels are cp312-cp312-specific — build_binary.py --rocm enforces this.
|
|
python-version: "3.12"
|
|
cache: "pip"
|
|
|
|
- name: Install Python dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install pyinstaller
|
|
pip install -r backend/requirements.txt
|
|
pip install --no-deps chatterbox-tts
|
|
pip install --no-deps hume-tada
|
|
|
|
- name: Build ROCm server binary (onedir)
|
|
shell: bash
|
|
working-directory: backend
|
|
# build_binary.py --rocm pulls the official AMD Radeon torch + rocm_sdk
|
|
# wheels (rocm-rel-7.2.1) itself when ROCm torch is not already present,
|
|
# then restores the dev torch afterwards.
|
|
run: python build_binary.py --rocm
|
|
|
|
- name: Package into server core + ROCm libs archives
|
|
shell: bash
|
|
run: |
|
|
python scripts/package_rocm.py \
|
|
backend/dist/voicebox-server-rocm/ \
|
|
--output release-assets/ \
|
|
--rocm-libs-version rocm7.2-v1 \
|
|
--torch-compat ">=2.9.0,<2.10.0"
|
|
|
|
- name: Upload archives to GitHub Release
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: |
|
|
release-assets/voicebox-server-rocm.tar.gz
|
|
release-assets/voicebox-server-rocm.tar.gz.sha256
|
|
release-assets/rocm-libs-rocm7.2-v1.tar.gz
|
|
release-assets/rocm-libs-rocm7.2-v1.tar.gz.sha256
|
|
release-assets/rocm-libs.json
|
|
draft: true
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Upload onedir as workflow artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: voicebox-server-rocm-windows
|
|
path: backend/dist/voicebox-server-rocm/
|
|
retention-days: 7
|