Files
goose/.github/workflows/bundle-desktop-windows.yml
Zane 77ea27f5f5 UI update with sidebar and settings tabs (#3288)
Co-authored-by: Nahiyan Khan <nahiyan@squareup.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Lily Delalande <119957291+lily-de@users.noreply.github.com>
Co-authored-by: Spence <spencrmartin@gmail.com>
Co-authored-by: spencrmartin <spencermartin@squareup.com>
Co-authored-by: Judson Stephenson <Jud@users.noreply.github.com>
Co-authored-by: Max Novich <mnovich@squareup.com>
Co-authored-by: Best Codes <106822363+The-Best-Codes@users.noreply.github.com>
Co-authored-by: caroline-a-mckenzie <cmckenzie@squareup.com>
Co-authored-by: Michael Neale <michael.neale@gmail.com>
2025-07-15 17:24:41 -07:00

414 lines
17 KiB
YAML

name: "Bundle Desktop (Windows)"
on:
# push:
# branches: [ "main" ]
# pull_request:
# branches: [ "main" ]
workflow_call:
inputs:
version:
description: 'Version to build'
required: false
type: string
signing:
description: 'Whether to sign the Windows executable'
required: false
type: boolean
default: false
ref:
description: 'Git ref to checkout'
required: false
type: string
default: ''
secrets:
WINDOWS_CODESIGN_CERTIFICATE:
required: false
WINDOW_SIGNING_ROLE:
required: false
WINDOW_SIGNING_ROLE_TAG:
required: false
# Permissions required for OIDC authentication with AWS
permissions:
id-token: write # Required to fetch the OIDC token
contents: read # Required by actions/checkout
actions: read # May be needed for some workflows
jobs:
build-desktop-windows:
name: Build Desktop (Windows)
runs-on: ubuntu-latest # Use Ubuntu for cross-compilation
steps:
# 1) Check out source
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4
with:
# Only pass ref if it's explicitly set, otherwise let checkout action use its default behavior
ref: ${{ inputs.ref != '' && inputs.ref || '' }}
fetch-depth: 0
# 2) Configure AWS credentials for code signing
- name: Configure AWS credentials
if: inputs.signing && inputs.signing == true
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # ratchet:aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.WINDOW_SIGNING_ROLE || secrets.WINDOW_SIGNING_ROLE_TAG }}
aws-region: us-west-2
# 2) Set up Node.js
- name: Set up Node.js
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # pin@v3
with:
node-version: 22
# 3) Cache dependencies
- name: Cache node_modules
uses: actions/cache@2f8e54208210a422b2efd51efaa6bd6d7ca8920f # pin@v3
with:
path: |
node_modules
ui/desktop/node_modules
key: ${{ runner.os }}-build-desktop-windows-node22-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-desktop-windows-node22-
# Cache Cargo registry and git dependencies
- name: Cache Cargo registry
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)
- name: Cache Cargo build
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-
# 4) Build Rust for Windows using Docker (cross-compilation with enhanced caching)
- name: Build Windows executable using Docker cross-compilation with enhanced caching
run: |
echo "🚀 Building Windows 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 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 --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/goosed.exe" ]; then
echo "❌ Windows binary not found."
ls -la ./target/x86_64-pc-windows-gnu/release/ || echo "Release directory doesn't exist"
exit 1
fi
echo "✅ Windows binary found!"
ls -la ./target/x86_64-pc-windows-gnu/release/goosed.exe
ls -la ./target/x86_64-pc-windows-gnu/release/*.dll
# 4.5) Build temporal-service for Windows using build.sh script
- name: Build temporal-service for Windows
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
"
echo "temporal-service.exe built successfully"
# 4.6) Download temporal CLI for Windows
- name: Download temporal CLI for Windows
run: |
echo "Downloading temporal CLI for Windows..."
TEMPORAL_VERSION="1.3.0"
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
echo "temporal CLI downloaded successfully"
# 5) Prepare Windows binary and DLLs
- name: Prepare Windows binary and DLLs
run: |
if [ ! -f "./target/x86_64-pc-windows-gnu/release/goosed.exe" ]; then
echo "Windows binary not found."
exit 1
fi
if [ ! -f "./temporal-service/temporal-service.exe" ]; then
echo "temporal-service.exe not found."
exit 1
fi
if [ ! -f "./temporal.exe" ]; then
echo "temporal.exe not found."
exit 1
fi
echo "Cleaning destination directory..."
rm -rf ./ui/desktop/src/bin
mkdir -p ./ui/desktop/src/bin
echo "Copying Windows binary and DLLs..."
cp -f ./target/x86_64-pc-windows-gnu/release/goosed.exe ./ui/desktop/src/bin/
cp -f ./target/x86_64-pc-windows-gnu/release/*.dll ./ui/desktop/src/bin/
echo "Copying temporal-service.exe..."
cp -f ./temporal-service/temporal-service.exe ./ui/desktop/src/bin/
echo "Copying temporal.exe..."
cp -f ./temporal.exe ./ui/desktop/src/bin/
# Copy Windows platform files (tools, scripts, etc.)
if [ -d "./ui/desktop/src/platform/windows/bin" ]; then
echo "Copying Windows platform files..."
for file in ./ui/desktop/src/platform/windows/bin/*.{exe,dll,cmd}; do
if [ -f "$file" ] && [ "$(basename "$file")" != "goosed.exe" ]; then
cp -f "$file" ./ui/desktop/src/bin/
fi
done
if [ -d "./ui/desktop/src/platform/windows/bin/goose-npm" ]; then
echo "Setting up npm environment..."
rsync -a --delete ./ui/desktop/src/platform/windows/bin/goose-npm/ ./ui/desktop/src/bin/goose-npm/
fi
echo "Windows-specific files copied successfully"
fi
# 6) Install & build UI desktop
- name: Build desktop UI with npm
run: |
cd ui/desktop
# Fix for rollup native module issue (npm optional dependencies bug)
echo "🔧 Fixing npm optional dependencies issue..."
rm -rf node_modules package-lock.json
npm install
# Verify rollup native module is installed
if [ ! -d "node_modules/@rollup/rollup-linux-x64-gnu" ]; then
echo "⚠️ Rollup native module missing, installing manually..."
npm install @rollup/rollup-linux-x64-gnu --save-optional
fi
npm run bundle:windows
# 7) Copy exe/dll to final out folder and prepare flat distribution
- name: Copy exe/dll to final out folder and prepare flat distribution
run: |
cd ui/desktop
mkdir -p ./out/Goose-win32-x64/resources/bin
rsync -av src/bin/ out/Goose-win32-x64/resources/bin/
# Create flat distribution structure
mkdir -p ./dist-windows
cp -r ./out/Goose-win32-x64/* ./dist-windows/
# Verify the final structure
echo "📋 Final flat distribution structure:"
ls -la ./dist-windows/
echo "📋 Binary files in resources/bin:"
ls -la ./dist-windows/resources/bin/
# 8) Sign Windows executables with jsign + AWS KMS
- name: Sign Windows executables with jsign + AWS KMS
if: inputs.signing && inputs.signing == true
run: |
set -exuo pipefail
echo "🔐 Starting Windows code signing with jsign + AWS KMS..."
# Create certificate file from secret
echo "📝 Creating certificate file from GitHub secret..."
echo "${{ secrets.WINDOWS_CODESIGN_CERTIFICATE }}" > block-codesign-cert.pem
# Install Java (required for jsign)
echo "☕ Installing Java runtime..."
sudo apt-get update
sudo apt-get install -y openjdk-11-jre-headless osslsigncode
# Download jsign
echo "📥 Downloading jsign..."
wget -q https://github.com/ebourg/jsign/releases/download/6.0/jsign-6.0.jar -O jsign.jar
echo "05ca18d4ab7b8c2183289b5378d32860f0ea0f3bdab1f1b8cae5894fb225fa8a jsign.jar" | sha256sum -c
# Sign the main Electron executable (Goose.exe)
echo "🔐 Signing main Electron executable: Goose.exe"
cd ui/desktop/dist-windows/
java -jar ${GITHUB_WORKSPACE}/jsign.jar \
--storetype AWS \
--keystore us-west-2 \
--storepass "${AWS_ACCESS_KEY_ID}|${AWS_SECRET_ACCESS_KEY}|${AWS_SESSION_TOKEN}" \
--alias windows-codesign \
--certfile "${GITHUB_WORKSPACE}/block-codesign-cert.pem" \
--tsaurl "http://timestamp.digicert.com" \
--name "Goose" \
--url "https://github.com/block/goose" \
"Goose.exe"
osslsigncode verify Goose.exe
echo "✅ Main executable Goose.exe signed successfully"
# Sign the backend executable (goosed.exe)
echo "🔐 Signing backend executable: goosed.exe"
cd resources/bin/
java -jar ${GITHUB_WORKSPACE}/jsign.jar \
--storetype AWS \
--keystore us-west-2 \
--storepass "${AWS_ACCESS_KEY_ID}|${AWS_SECRET_ACCESS_KEY}|${AWS_SESSION_TOKEN}" \
--alias windows-codesign \
--certfile "${GITHUB_WORKSPACE}/block-codesign-cert.pem" \
--tsaurl "http://timestamp.digicert.com" \
--name "Goose Backend" \
--url "https://github.com/block/goose" \
"goosed.exe"
osslsigncode verify goosed.exe
echo "✅ Backend executable goosed.exe signed successfully"
# Show final file status
echo "📋 Final signed files:"
cd ../../
ls -la Goose.exe
sha256sum Goose.exe
ls -la resources/bin/goosed.exe
sha256sum resources/bin/goosed.exe
# Clean up certificate file
rm -f ${GITHUB_WORKSPACE}/block-codesign-cert.pem
# 9) Verify signed executables are in final distribution
- name: Verify signed executables are in final distribution
if: inputs.signing && inputs.signing == true
run: |
echo "📋 Verifying both signed executables in final distribution:"
echo "Main executable:"
ls -la ui/desktop/dist-windows/Goose.exe
osslsigncode verify ui/desktop/dist-windows/Goose.exe
echo "✅ Main executable signature verification passed"
echo "Backend executable:"
ls -la ui/desktop/dist-windows/resources/bin/goosed.exe
osslsigncode verify ui/desktop/dist-windows/resources/bin/goosed.exe
echo "✅ Backend executable signature verification passed"
# 10) Create Windows zip package
- name: Create Windows zip package
run: |
cd ui/desktop
echo "📦 Creating Windows zip package..."
# Create a zip file from the dist-windows directory
zip -r "Goose-win32-x64.zip" dist-windows/
echo "✅ Windows zip package created:"
ls -la Goose-win32-x64.zip
# Also create the zip in the expected output structure for consistency
mkdir -p out/Goose-win32-x64/
cp Goose-win32-x64.zip out/Goose-win32-x64/
# 11) Upload the final Windows build
- name: Upload Windows build artifacts
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # pin@v4
with:
name: Goose-win32-x64
path: ui/desktop/out/Goose-win32-x64/Goose-win32-x64.zip