merge to build-cli.yml #4
Workflow file for this run
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
| # 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: Uses Ubuntu runner with cross-compilation | ||
| # - macOS: Uses macOS runner with cross-compilation | ||
| # - Windows: Uses Ubuntu runner with Docker cross-compilation (same as desktop build) | ||
| on: | ||
| push: | ||
| branches: | ||
| - build-termux-xcap | ||
| #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.is-termux && format('termux/termux-docker:{0}', matrix.architecture) || '' }} | ||
| volumes: ${{ matrix.is-termux && fromJSON('["/tmp/node20:/__e/node20"]') || '[]' }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| # Linux builds | ||
| # - os: ubuntu-latest | ||
| # architecture: x86_64 | ||
| # target-suffix: unknown-linux-gnu | ||
| # build-on: ubuntu-latest | ||
| # use-cross: true | ||
| # - os: ubuntu-latest | ||
| # architecture: aarch64 | ||
| # target-suffix: unknown-linux-gnu | ||
| # build-on: ubuntu-latest | ||
| # use-cross: true | ||
| # # macOS builds | ||
| # - os: macos-latest | ||
| # architecture: x86_64 | ||
| # target-suffix: apple-darwin | ||
| # build-on: macos-latest | ||
| # use-cross: true | ||
| # - os: macos-latest | ||
| # architecture: aarch64 | ||
| # target-suffix: apple-darwin | ||
| # build-on: macos-latest | ||
| # use-cross: true | ||
| # # Windows builds (only x86_64 supported) | ||
| # - os: windows | ||
| # architecture: x86_64 | ||
| # target-suffix: pc-windows-gnu | ||
| # build-on: ubuntu-latest | ||
| # use-cross: false | ||
| # use-docker: true | ||
| # Termux build (only aarch64 supported) | ||
| - os: termux | ||
| architecture: aarch64 | ||
| target-suffix: aarch64-linux-android | ||
| build-on: ubuntu-24.04-arm | ||
| is-termux: true | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4 | ||
| with: | ||
| ref: ${{ inputs.ref }} | ||
| fetch-depth: 0 | ||
| - name: Set Termux Environment Variables | ||
| if: matrix.is-termux | ||
| run: | | ||
| echo "TERMUX_MAIN_PACKAGE_FORMAT=debian" >> $GITHUB_ENV | ||
| echo "ANDROID_ROOT=/system" >> $GITHUB_ENV | ||
| echo "ANDROID_DATA=/data" >> $GITHUB_ENV | ||
| echo "PREFIX=/data/data/com.termux/files/usr" >> $GITHUB_ENV | ||
| echo "HOME=/data/data/com.termux/files/home" >> $GITHUB_ENV | ||
| echo "PATH=/data/data/com.termux/files/usr/bin" >> $GITHUB_ENV | ||
| echo "TMPDIR=/data/data/com.termux/files/usr/tmp" >> $GITHUB_ENV | ||
| echo "LANG=en_US.UTF-8" >> $GITHUB_ENV | ||
| echo "TZ=UTC" >> $GITHUB_ENV | ||
| - name: Termux - set pkg command to use the packages-cf.termux.dev mirror | ||
| if: matrix.is-termux | ||
| run: | | ||
| ln -sf ${PREFIX}/etc/termux/mirrors/default ${PREFIX}/etc/termux/chosen_mirrors | ||
| /entrypoint.sh bash -c "yes | pkg upgrade -y" | ||
| chmod -R o+x ${PREFIX}/bin | ||
| /entrypoint.sh pkg install -y nodejs-lts | ||
| ln -sf ${PREFIX}/bin /__e/node20/bin | ||
| chown -R 1000:1000 . | ||
| /entrypoint.sh pkg install -y rust | ||
| /entrypoint.sh pkg install -y protobuf | ||
| /entrypoint.sh pkg install -y tar gzip zip | ||
| - name: Update version in Cargo.toml | ||
| if: ${{ inputs.version != '' }} | ||
| run: | | ||
| sed -i.bak 's/^version = ".*"/version = "'${{ inputs.version }}'"/' Cargo.toml | ||
| rm -f Cargo.toml.bak | ||
| - name: Install cross | ||
| if: matrix.use-cross | ||
| run: source ./bin/activate-hermit && cargo install cross --git https://github.com/cross-rs/cross | ||
| # Install Go for building temporal-service | ||
| - name: Set up Go | ||
| uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@v5 | ||
| with: | ||
| go-version: '1.21' | ||
| # Cache Cargo registry and git dependencies for Windows builds | ||
| - name: Cache Cargo registry (Windows) | ||
| if: matrix.use-docker | ||
| uses: actions/cache@2f8e54208210a422b2efd51efaa6bd6d7ca8920f | ||
| with: | ||
| path: | | ||
| ~/.cargo/registry/index | ||
| ~/.cargo/registry/cache | ||
| ~/.cargo/git/db | ||
| key: ${{ runner.os }}-cargo-registry-${{ hashFiles('Cargo.lock') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-cargo-registry- | ||
| # Cache compiled dependencies (target/release/deps) for Windows builds | ||
| - name: Cache Cargo build (Windows) | ||
| if: matrix.use-docker | ||
| uses: actions/cache@2f8e54208210a422b2efd51efaa6bd6d7ca8920f | ||
| with: | ||
| path: target | ||
| key: ${{ runner.os }}-cargo-build-${{ hashFiles('Cargo.lock') }}-${{ hashFiles('rust-toolchain.toml') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-cargo-build-${{ hashFiles('Cargo.lock') }}- | ||
| ${{ runner.os }}-cargo-build- | ||
| - name: Build CLI (Linux/macOS) | ||
| if: matrix.use-cross | ||
| env: | ||
| CROSS_NO_WARNINGS: 0 | ||
| RUST_LOG: debug | ||
| RUST_BACKTRACE: 1 | ||
| CROSS_VERBOSE: 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 | ||
| echo "Cross version:" | ||
| cross --version | ||
| echo "Building with explicit PROTOC path..." | ||
| cross build --release --target ${TARGET} -p goose-cli -vv | ||
| - name: Build CLI (Windows) | ||
| if: matrix.use-docker | ||
| run: | | ||
| echo "🚀 Building Windows CLI executable with enhanced GitHub Actions caching..." | ||
| # Create cache directories | ||
| mkdir -p ~/.cargo/registry ~/.cargo/git | ||
| # Use enhanced caching with GitHub Actions cache mounts | ||
| docker run --rm \ | ||
| -v "$(pwd)":/usr/src/myapp \ | ||
| -v "$HOME/.cargo/registry":/usr/local/cargo/registry \ | ||
| -v "$HOME/.cargo/git":/usr/local/cargo/git \ | ||
| -w /usr/src/myapp \ | ||
| rust:latest \ | ||
| bash -c " | ||
| set -e | ||
| echo '=== Setting up Rust environment with caching ===' | ||
| export CARGO_HOME=/usr/local/cargo | ||
| export PATH=/usr/local/cargo/bin:$PATH | ||
| # Check if Windows target is already installed in cache | ||
| if rustup target list --installed | grep -q x86_64-pc-windows-gnu; then | ||
| echo '✅ Windows cross-compilation target already installed' | ||
| else | ||
| echo '📦 Installing Windows cross-compilation target...' | ||
| rustup target add x86_64-pc-windows-gnu | ||
| fi | ||
| echo '=== Setting up build dependencies ===' | ||
| apt-get update | ||
| apt-get install -y mingw-w64 protobuf-compiler cmake time | ||
| echo '=== Setting up cross-compilation environment ===' | ||
| export CC_x86_64_pc_windows_gnu=x86_64-w64-mingw32-gcc | ||
| export CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++ | ||
| export AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar | ||
| export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc | ||
| export PKG_CONFIG_ALLOW_CROSS=1 | ||
| export PROTOC=/usr/bin/protoc | ||
| echo '=== Optimized Cargo configuration ===' | ||
| mkdir -p .cargo | ||
| echo '[build]' > .cargo/config.toml | ||
| echo 'jobs = 4' >> .cargo/config.toml | ||
| echo '' >> .cargo/config.toml | ||
| echo '[target.x86_64-pc-windows-gnu]' >> .cargo/config.toml | ||
| echo 'linker = "x86_64-w64-mingw32-gcc"' >> .cargo/config.toml | ||
| echo '' >> .cargo/config.toml | ||
| echo '[net]' >> .cargo/config.toml | ||
| echo 'git-fetch-with-cli = true' >> .cargo/config.toml | ||
| echo 'retry = 3' >> .cargo/config.toml | ||
| echo '' >> .cargo/config.toml | ||
| echo '[profile.release]' >> .cargo/config.toml | ||
| echo 'codegen-units = 1' >> .cargo/config.toml | ||
| echo 'lto = false' >> .cargo/config.toml | ||
| echo 'panic = "abort"' >> .cargo/config.toml | ||
| echo 'debug = false' >> .cargo/config.toml | ||
| echo 'opt-level = 2' >> .cargo/config.toml | ||
| echo '' >> .cargo/config.toml | ||
| echo '[registries.crates-io]' >> .cargo/config.toml | ||
| echo 'protocol = "sparse"' >> .cargo/config.toml | ||
| echo '=== Building with cached dependencies ===' | ||
| # Check if we have cached build artifacts | ||
| if [ -d target/x86_64-pc-windows-gnu/release/deps ] && [ "$(ls -A target/x86_64-pc-windows-gnu/release/deps)" ]; then | ||
| echo '✅ Found cached build artifacts, performing incremental build...' | ||
| CARGO_INCREMENTAL=1 | ||
| else | ||
| echo '🔨 No cached artifacts found, performing full build...' | ||
| CARGO_INCREMENTAL=0 | ||
| fi | ||
| echo '🔨 Building Windows CLI executable...' | ||
| CARGO_INCREMENTAL=$CARGO_INCREMENTAL \ | ||
| CARGO_NET_RETRY=3 \ | ||
| CARGO_HTTP_TIMEOUT=60 \ | ||
| RUST_BACKTRACE=1 \ | ||
| cargo build --release --target x86_64-pc-windows-gnu -p goose-cli --jobs 4 | ||
| echo '=== Copying Windows runtime DLLs ===' | ||
| GCC_DIR=$(ls -d /usr/lib/gcc/x86_64-w64-mingw32/*/ | head -n 1) | ||
| cp "$GCC_DIR/libstdc++-6.dll" target/x86_64-pc-windows-gnu/release/ | ||
| cp "$GCC_DIR/libgcc_s_seh-1.dll" target/x86_64-pc-windows-gnu/release/ | ||
| cp /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll target/x86_64-pc-windows-gnu/release/ | ||
| echo '✅ Build completed successfully!' | ||
| ls -la target/x86_64-pc-windows-gnu/release/ | ||
| " | ||
| # Verify build succeeded | ||
| if [ ! -f "./target/x86_64-pc-windows-gnu/release/goose.exe" ]; then | ||
| echo "❌ Windows CLI binary not found." | ||
| ls -la ./target/x86_64-pc-windows-gnu/release/ || echo "Release directory doesn't exist" | ||
| exit 1 | ||
| fi | ||
| echo "✅ Windows CLI binary found!" | ||
| ls -la ./target/x86_64-pc-windows-gnu/release/goose.exe | ||
| echo "✅ Windows runtime DLLs:" | ||
| ls -la ./target/x86_64-pc-windows-gnu/release/*.dll | ||
| - name: Build CLI (Termux) | ||
| if: matrix.is-termux | ||
| run: | | ||
| #/entrypoint.sh cargo build --release | ||
| # | ||
| export TARGET=. | ||
| # Create a directory for the package contents | ||
| mkdir -p "target/${TARGET}/release/goose-package" | ||
| touch "target/${TARGET}/release/goose" | ||
| # Copy binaries | ||
| cp "target/${TARGET}/release/goose" "target/${TARGET}/release/goose-package/" | ||
| cp "target/${TARGET}/release/goosed" "target/${TARGET}/release/goose-package/" | ||
| # Create the tar archive with all binaries | ||
| cd "target/${TARGET}/release" | ||
| tar -cjf "goose-${{ matrix.architecture }}-termux.tar.bz2" -C goose-package . | ||
| echo "ARTIFACT=target/${TARGET}/release/goose-${{ matrix.architecture }}-termux.tar.bz2" >> $GITHUB_ENV | ||
| - name: Build temporal-service for target platform using build.sh script (Linux/macOS) | ||
| if: matrix.use-cross | ||
| run: | | ||
| source ./bin/activate-hermit | ||
| export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" | ||
| # Set Go cross-compilation variables based on target | ||
| case "${TARGET}" in | ||
| "x86_64-unknown-linux-gnu") | ||
| export GOOS=linux | ||
| export GOARCH=amd64 | ||
| BINARY_NAME="temporal-service" | ||
| ;; | ||
| "aarch64-unknown-linux-gnu") | ||
| export GOOS=linux | ||
| export GOARCH=arm64 | ||
| BINARY_NAME="temporal-service" | ||
| ;; | ||
| "x86_64-apple-darwin") | ||
| export GOOS=darwin | ||
| export GOARCH=amd64 | ||
| BINARY_NAME="temporal-service" | ||
| ;; | ||
| "aarch64-apple-darwin") | ||
| export GOOS=darwin | ||
| export GOARCH=arm64 | ||
| BINARY_NAME="temporal-service" | ||
| ;; | ||
| *) | ||
| echo "Unsupported target: ${TARGET}" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| echo "Building temporal-service for ${GOOS}/${GOARCH} using build.sh script..." | ||
| cd temporal-service | ||
| # Run build.sh with cross-compilation environment | ||
| GOOS="${GOOS}" GOARCH="${GOARCH}" ./build.sh | ||
| # Move the built binary to the expected location | ||
| mv "${BINARY_NAME}" "../target/${TARGET}/release/${BINARY_NAME}" | ||
| echo "temporal-service built successfully for ${TARGET}" | ||
| - name: Build temporal-service for Windows | ||
| if: matrix.use-docker | ||
| run: | | ||
| echo "Building temporal-service for Windows using build.sh script..." | ||
| docker run --rm \ | ||
| -v "$(pwd)":/usr/src/myapp \ | ||
| -w /usr/src/myapp/temporal-service \ | ||
| golang:latest \ | ||
| sh -c " | ||
| # Make build.sh executable | ||
| chmod +x build.sh | ||
| # Set Windows build environment and run build script | ||
| GOOS=windows GOARCH=amd64 ./build.sh | ||
| # Move the built binary to the expected location (inside container) | ||
| mkdir -p ../target/x86_64-pc-windows-gnu/release | ||
| mv temporal-service.exe ../target/x86_64-pc-windows-gnu/release/temporal-service.exe | ||
| # Fix permissions for host access | ||
| chmod -R 755 ../target/x86_64-pc-windows-gnu | ||
| " | ||
| echo "temporal-service.exe built successfully for Windows" | ||
| - name: Download temporal CLI (Linux/macOS) | ||
| if: matrix.use-cross | ||
| run: | | ||
| export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" | ||
| TEMPORAL_VERSION="1.3.0" | ||
| # Set platform-specific download parameters | ||
| case "${TARGET}" in | ||
| "x86_64-unknown-linux-gnu") | ||
| TEMPORAL_OS="linux" | ||
| TEMPORAL_ARCH="amd64" | ||
| TEMPORAL_EXT="" | ||
| ;; | ||
| "aarch64-unknown-linux-gnu") | ||
| TEMPORAL_OS="linux" | ||
| TEMPORAL_ARCH="arm64" | ||
| TEMPORAL_EXT="" | ||
| ;; | ||
| "x86_64-apple-darwin") | ||
| TEMPORAL_OS="darwin" | ||
| TEMPORAL_ARCH="amd64" | ||
| TEMPORAL_EXT="" | ||
| ;; | ||
| "aarch64-apple-darwin") | ||
| TEMPORAL_OS="darwin" | ||
| TEMPORAL_ARCH="arm64" | ||
| TEMPORAL_EXT="" | ||
| ;; | ||
| *) | ||
| echo "Unsupported target for temporal CLI: ${TARGET}" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| echo "Downloading temporal CLI for ${TEMPORAL_OS}/${TEMPORAL_ARCH}..." | ||
| TEMPORAL_FILE="temporal_cli_${TEMPORAL_VERSION}_${TEMPORAL_OS}_${TEMPORAL_ARCH}.tar.gz" | ||
| curl -L "https://github.com/temporalio/cli/releases/download/v${TEMPORAL_VERSION}/${TEMPORAL_FILE}" -o "${TEMPORAL_FILE}" | ||
| # Extract temporal CLI | ||
| tar -xzf "${TEMPORAL_FILE}" | ||
| chmod +x temporal${TEMPORAL_EXT} | ||
| # Move to target directory | ||
| mv temporal${TEMPORAL_EXT} "target/${TARGET}/release/temporal${TEMPORAL_EXT}" | ||
| # Clean up | ||
| rm -f "${TEMPORAL_FILE}" | ||
| echo "temporal CLI downloaded successfully for ${TARGET}" | ||
| - name: Download temporal CLI (Windows) | ||
| if: matrix.use-docker | ||
| run: | | ||
| TEMPORAL_VERSION="1.3.0" | ||
| echo "Downloading temporal CLI for Windows..." | ||
| curl -L "https://github.com/temporalio/cli/releases/download/v${TEMPORAL_VERSION}/temporal_cli_${TEMPORAL_VERSION}_windows_amd64.zip" -o temporal-cli-windows.zip | ||
| unzip -o temporal-cli-windows.zip | ||
| chmod +x temporal.exe | ||
| # Fix permissions on target directory (created by Docker as root) | ||
| sudo chown -R $(whoami):$(whoami) target/x86_64-pc-windows-gnu/ || true | ||
| # Move to target directory | ||
| mv temporal.exe target/x86_64-pc-windows-gnu/release/temporal.exe | ||
| # Clean up | ||
| rm -f temporal-cli-windows.zip | ||
| echo "temporal CLI downloaded successfully for Windows" | ||
| - name: Package CLI with temporal-service (Linux/macOS) | ||
| if: matrix.use-cross | ||
| run: | | ||
| source ./bin/activate-hermit | ||
| export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" | ||
| # Create a directory for the package contents | ||
| mkdir -p "target/${TARGET}/release/goose-package" | ||
| # Copy binaries | ||
| cp "target/${TARGET}/release/goose" "target/${TARGET}/release/goose-package/" | ||
| cp "target/${TARGET}/release/temporal-service" "target/${TARGET}/release/goose-package/" | ||
| cp "target/${TARGET}/release/temporal" "target/${TARGET}/release/goose-package/" | ||
| # Create the tar archive with all binaries | ||
| cd "target/${TARGET}/release" | ||
| tar -cjf "goose-${TARGET}.tar.bz2" -C goose-package . | ||
| echo "ARTIFACT=target/${TARGET}/release/goose-${TARGET}.tar.bz2" >> $GITHUB_ENV | ||
| - name: Package CLI with temporal-service (Windows) | ||
| if: matrix.use-docker | ||
| run: | | ||
| export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" | ||
| # Create a directory for the package contents | ||
| mkdir -p "target/${TARGET}/release/goose-package" | ||
| # Copy binaries | ||
| cp "target/${TARGET}/release/goose.exe" "target/${TARGET}/release/goose-package/" | ||
| cp "target/${TARGET}/release/temporal-service.exe" "target/${TARGET}/release/goose-package/" | ||
| cp "target/${TARGET}/release/temporal.exe" "target/${TARGET}/release/goose-package/" | ||
| # Copy Windows runtime DLLs | ||
| cp "target/${TARGET}/release/"*.dll "target/${TARGET}/release/goose-package/" | ||
| # Create the zip archive with all binaries and DLLs | ||
| cd "target/${TARGET}/release" | ||
| zip -r "goose-${TARGET}.zip" goose-package/ | ||
| echo "ARTIFACT=target/${TARGET}/release/goose-${TARGET}.zip" >> $GITHUB_ENV | ||
| - name: Package CLI (Termux) | ||
| if: matrix.is-termux | ||
| run: | | ||
| cd target/release | ||
| zip -r goose-${{ inputs.version }}-${{ matrix.architecture }}-termux.zip goose goosed mcp-server generate_schema | ||
| echo "ARTIFACT=target/release/goose-${{ inputs.version }}-${{ matrix.architecture }}-termux.zip" >> $GITHUB_ENV | ||
| - name: Upload CLI artifact | ||
| uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # pin@v4 | ||
| with: | ||
| name: ${{ matrix.is-termux && format('goose-{0}-{1}-termux', inputs.version, matrix.architecture) || format('goose-{0}-{1}', matrix.architecture, matrix.target-suffix) }} | ||
| path: ${{ env.ARTIFACT }} | ||