# This is a **reuseable** workflow that builds the CLI for multiple platforms. # It doesn't get triggered on its own. It gets used in multiple workflows: # - release.yml # - canary.yml # # Platform Build Strategy: # - Linux standard (x86_64 + aarch64): Builds inside manylinux_2_28 container for glibc 2.28+ compat # - Linux Vulkan: Uses native Ubuntu 24.04 runners for newer Vulkan headers/tooling # - Linux musl: Uses native Ubuntu 22.04 runners with reduced features for musl compatibility # - macOS: Uses native macOS runners for each architecture # - Windows: Uses Windows runner with native MSVC build on: workflow_call: inputs: version: required: false default: "" type: string ref: type: string required: false default: "" name: "Reusable workflow to build CLI" jobs: build-cli: name: Build CLI runs-on: ${{ matrix.build-on }} container: ${{ matrix.container }} env: MACOSX_DEPLOYMENT_TARGET: "12.0" strategy: fail-fast: false matrix: include: - platform: linux architecture: x86_64 target-suffix: unknown-linux-gnu build-on: ubuntu-22.04 # Pinned by digest for reproducible builds; bump explicitly when newer manylinux_2_28 images ship. container: quay.io/pypa/manylinux_2_28_x86_64@sha256:441c35fdc6ee809ff9260894f8468ab4fea8c15dc880f8700a3f81b7922c1cda variant: standard - platform: linux architecture: aarch64 target-suffix: unknown-linux-gnu build-on: ubuntu-22.04-arm # Pinned by digest for reproducible builds; bump explicitly when newer manylinux_2_28 images ship. container: quay.io/pypa/manylinux_2_28_aarch64@sha256:8b5f2b4e8c072ae5aefeb659f22c03e1ff46e6a82f154b6c904b106c87e65ff7 variant: standard - platform: linux architecture: x86_64 target-suffix: unknown-linux-musl build-on: ubuntu-22.04 variant: musl - platform: linux architecture: aarch64 target-suffix: unknown-linux-musl build-on: ubuntu-22.04-arm variant: musl - platform: linux architecture: x86_64 target-suffix: unknown-linux-gnu build-on: ubuntu-24.04 variant: vulkan - platform: linux architecture: aarch64 target-suffix: unknown-linux-gnu build-on: ubuntu-24.04-arm variant: vulkan - platform: macos architecture: x86_64 target-suffix: apple-darwin build-on: macos-15-intel variant: standard - platform: macos architecture: aarch64 target-suffix: apple-darwin build-on: macos-latest variant: standard - platform: windows architecture: x86_64 target-suffix: pc-windows-msvc build-on: windows-latest variant: standard - platform: windows architecture: x86_64 target-suffix: pc-windows-msvc build-on: windows-2022 variant: cuda steps: - name: Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ inputs.ref }} - name: Update version in Cargo.toml if: ${{ inputs.version != '' }} shell: bash run: | sed -i.bak 's/^version = ".*"/version = "'${{ inputs.version }}'"/' Cargo.toml rm -f Cargo.toml.bak - name: Install Linux build dependencies (host runner) if: matrix.platform == 'linux' && matrix.container == '' run: | sudo apt-get update sudo apt-get install -y \ build-essential \ pkg-config \ libssl-dev \ libdbus-1-dev \ libxcb1-dev if [ "${{ matrix.variant }}" = "vulkan" ]; then sudo apt-get install -y \ libvulkan-dev \ libvulkan1 \ glslc fi if [ "${{ matrix.variant }}" = "musl" ]; then sudo apt-get install -y musl-tools fi - name: Install Linux build dependencies (manylinux container) if: matrix.platform == 'linux' && matrix.container != '' run: | # perl-core provides FindBin, File::Compare, etc. that openssl-sys's # vendored openssl build needs; in AlmaLinux 8 these aren't standalone packages. # clang provides libclang.so for bindgen (used by llama-cpp-sys-2). # Defensive: avoid actions/checkout falling back to a tarball download if base image changes. dnf install -y --setopt=install_weak_deps=False \ openssl-devel \ dbus-devel \ libxcb-devel \ cmake \ perl-core \ clang \ git \ tar - name: Cache Cargo artifacts (Linux/macOS) if: matrix.platform != 'windows' uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: key: ${{ matrix.architecture }}-${{ matrix.target-suffix }}-${{ matrix.build-on }}-${{ matrix.container || 'native' }}-macos-deployment-target-12 - name: Cache Cargo artifacts (Windows) if: matrix.platform == 'windows' uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: key: windows-msvc-cli-${{ matrix.variant }} - name: Build CLI (Linux/macOS host runner) if: matrix.platform != 'windows' && matrix.container == '' env: RUST_LOG: debug RUST_BACKTRACE: 1 run: | source ./bin/activate-hermit export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" rustup target add "${TARGET}" echo "Building for target: ${TARGET}" echo "Rust toolchain info:" rustup show FEATURE_ARGS=() if [ "${{ matrix.variant }}" = "vulkan" ]; then FEATURE_ARGS=(--features vulkan) fi if [ "${{ matrix.variant }}" = "musl" ]; then cargo build --release --target ${TARGET} -p goose-cli --bin goose \ --no-default-features \ --features portable-default else cargo build --release --target ${TARGET} -p goose-cli "${FEATURE_ARGS[@]}" fi - name: Build CLI (manylinux container) if: matrix.platform == 'linux' && matrix.container != '' env: RUST_BACKTRACE: 1 run: | # Hermit's tool cache is host-runner-scoped; inside the container we # bootstrap rustup directly and let rust-toolchain.toml pin the channel. curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ | sh -s -- -y --default-toolchain none --profile minimal --no-modify-path export PATH="$HOME/.cargo/bin:$PATH" TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" RUST_CHANNEL=$(grep '^channel' rust-toolchain.toml | cut -d'"' -f2) if [ -z "$RUST_CHANNEL" ]; then echo "Could not parse channel from rust-toolchain.toml" >&2 exit 1 fi rustup toolchain install "$RUST_CHANNEL" --profile minimal \ --component rustc,cargo --target "$TARGET" rustup show cargo build --release --target "$TARGET" -p goose-cli - name: Setup Rust (Windows) if: matrix.platform == 'windows' shell: bash run: | rustup show rustup target add x86_64-pc-windows-msvc - name: Install CUDA toolkit (Windows CUDA) if: ${{ matrix.platform == 'windows' && matrix.variant == 'cuda' }} uses: Jimver/cuda-toolkit@v0.2.35 with: cuda: '12.9.1' method: 'local' log-file-suffix: 'build-cli-windows-cuda.txt' - name: Set up MSVC developer environment (Windows CUDA) if: ${{ matrix.platform == 'windows' && matrix.variant == 'cuda' }} uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 with: arch: amd64 - name: Verify CUDA toolchain (Windows CUDA) if: ${{ matrix.platform == 'windows' && matrix.variant == 'cuda' }} shell: pwsh env: CUDA_COMPUTE_CAP: "80" run: | Write-Output "CUDA_PATH=$env:CUDA_PATH" Write-Output "CUDA_COMPUTE_CAP=$env:CUDA_COMPUTE_CAP" where.exe cl where.exe nvcc nvcc -V - name: Build CLI (Windows) if: matrix.platform == 'windows' shell: pwsh env: CUDA_COMPUTE_CAP: ${{ matrix.variant == 'cuda' && '80' || '' }} run: | Write-Output "Building Windows CLI executable..." if ("${{ matrix.variant }}" -eq "cuda") { cargo build --release --target x86_64-pc-windows-msvc -p goose-cli --features cuda } else { cargo build --release --target x86_64-pc-windows-msvc -p goose-cli } if (-not (Test-Path "./target/x86_64-pc-windows-msvc/release/goose.exe")) { Write-Error "Windows CLI binary not found." Get-ChildItem ./target/x86_64-pc-windows-msvc/release/ -ErrorAction SilentlyContinue exit 1 } Write-Output "Windows CLI binary found." Get-Item ./target/x86_64-pc-windows-msvc/release/goose.exe - name: Package CLI (Linux/macOS) if: matrix.platform != 'windows' run: | # Hermit isn't installed in the manylinux container; tar is all this step needs. if [ "${{ matrix.container }}" = '' ]; then source ./bin/activate-hermit fi export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" export VARIANT_SUFFIX="" if [ "${{ matrix.variant }}" = "vulkan" ]; then VARIANT_SUFFIX="-vulkan" fi # Create a directory for the package contents mkdir -p "target/${TARGET}/release/goose-package" # Copy the goose binary cp "target/${TARGET}/release/goose" "target/${TARGET}/release/goose-package/" cd "target/${TARGET}/release" tar -cjf "goose-${TARGET}${VARIANT_SUFFIX}.tar.bz2" -C goose-package . tar -czf "goose-${TARGET}${VARIANT_SUFFIX}.tar.gz" -C goose-package . echo "ARTIFACT_BZ2=target/${TARGET}/release/goose-${TARGET}${VARIANT_SUFFIX}.tar.bz2" >> $GITHUB_ENV echo "ARTIFACT_GZ=target/${TARGET}/release/goose-${TARGET}${VARIANT_SUFFIX}.tar.gz" >> $GITHUB_ENV - name: Package CLI (Windows) if: matrix.platform == 'windows' shell: bash run: | export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" export VARIANT_SUFFIX="" if [ "${{ matrix.variant }}" = "cuda" ]; then VARIANT_SUFFIX="-cuda" fi mkdir -p "target/${TARGET}/release/goose-package" cp "target/${TARGET}/release/goose.exe" "target/${TARGET}/release/goose-package/" cd "target/${TARGET}/release" 7z a -tzip "goose-${TARGET}${VARIANT_SUFFIX}.zip" goose-package/ echo "ARTIFACT_ZIP=target/${TARGET}/release/goose-${TARGET}${VARIANT_SUFFIX}.zip" >> $GITHUB_ENV - name: Upload CLI artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: goose-${{ matrix.architecture }}-${{ matrix.target-suffix }}${{ matrix.variant != 'standard' && matrix.variant != 'musl' && format('-{0}', matrix.variant) || '' }} path: | ${{ env.ARTIFACT_BZ2 }} ${{ env.ARTIFACT_GZ }} ${{ env.ARTIFACT_ZIP }}