fix(ci): generate mac updater artifacts for preview channel (#430) #1120
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: Build | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| changes: | |
| name: Detect source changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| source_changed: ${{ steps.filter.outputs.source }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check changed paths | |
| id: filter | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| source: | |
| - ".github/workflows/**" | |
| - "apps/**" | |
| - "bin/**" | |
| - "crates/**" | |
| - "e2e/**" | |
| - "python/**" | |
| - "scripts/**" | |
| - "src/**" | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "rust-toolchain.toml" | |
| - "package.json" | |
| - "package-lock.json" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - "biome.json" | |
| - "components.json" | |
| - "tailwind.config.js" | |
| - "tsconfig.json" | |
| - "vitest.config.ts" | |
| lint: | |
| name: Lint & Format | |
| needs: [changes] | |
| if: needs.changes.outputs.source_changed == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install rust | |
| uses: dsherret/rust-toolchain-file@v1 | |
| - name: Check Rust formatting | |
| run: cargo fmt --check | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Enable corepack | |
| run: corepack enable | |
| - name: Install JS dependencies | |
| run: pnpm install | |
| - name: Check Biome (format + lint + imports) | |
| run: npx @biomejs/biome check apps/notebook/src/ e2e/ | |
| - name: Lint GitHub Actions workflows | |
| uses: raven-actions/actionlint@v2 | |
| with: | |
| shellcheck: false | |
| build-linux: | |
| name: Linux | |
| needs: [changes] | |
| if: needs.changes.outputs.source_changed == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libxdo-dev | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Install rust | |
| uses: dsherret/rust-toolchain-file@v1 | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: ubuntu-latest | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Enable corepack | |
| run: corepack enable | |
| - name: Set pnpm store directory | |
| run: pnpm config set store-dir ~/.pnpm-store | |
| - name: Cache pnpm store | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.pnpm-store | |
| key: pnpm-store-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| pnpm-store-${{ runner.os }}- | |
| - name: Install JS dependencies | |
| run: pnpm install | |
| - name: Run JS tests | |
| run: pnpm test:run | |
| - name: Build UIs | |
| run: | | |
| pnpm --dir apps/sidecar build | |
| pnpm --dir apps/notebook build | |
| - name: Upload UI build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ui-build-dist | |
| path: | | |
| apps/notebook/dist/ | |
| apps/sidecar/dist/ | |
| retention-days: 1 | |
| - name: Build external binaries | |
| shell: bash | |
| run: | | |
| cargo build --release -p runtimed -p runt-cli | |
| TARGET=$(rustc --print host-tuple) | |
| mkdir -p crates/notebook/binaries | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then | |
| cp target/release/runtimed.exe "crates/notebook/binaries/runtimed-$TARGET.exe" | |
| cp target/release/runt.exe "crates/notebook/binaries/runt-$TARGET.exe" | |
| else | |
| cp target/release/runtimed "crates/notebook/binaries/runtimed-$TARGET" | |
| cp target/release/runt "crates/notebook/binaries/runt-$TARGET" | |
| fi | |
| - name: Clippy | |
| run: cargo clippy --all-targets -- -D warnings | |
| - name: Build | |
| run: cargo build --release | |
| - name: Run tests | |
| run: cargo test --verbose | |
| build: | |
| name: ${{ matrix.platform.name }} | |
| needs: [changes, build-linux] | |
| if: ${{ always() && (needs.changes.outputs.source_changed != 'true' || needs.build-linux.result == 'success') }} | |
| runs-on: ${{ needs.changes.outputs.source_changed == 'true' && matrix.platform.runner || 'ubuntu-latest' }} | |
| strategy: | |
| matrix: | |
| platform: | |
| - name: macOS | |
| runner: macos-latest | |
| setup: echo "No extra deps needed on macOS" | |
| - name: Windows | |
| runner: windows-latest | |
| setup: echo "No extra deps needed on Windows" | |
| steps: | |
| - name: Skip non-source changes | |
| if: needs.changes.outputs.source_changed != 'true' | |
| run: echo "No source changes detected; skipping this platform leg." | |
| - uses: actions/checkout@v4 | |
| if: needs.changes.outputs.source_changed == 'true' | |
| - name: Download UI build artifacts | |
| if: needs.changes.outputs.source_changed == 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ui-build-dist | |
| path: apps | |
| - name: Install system dependencies | |
| if: needs.changes.outputs.source_changed == 'true' | |
| run: ${{ matrix.platform.setup }} | |
| - name: Install uv | |
| if: needs.changes.outputs.source_changed == 'true' | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Install rust | |
| if: needs.changes.outputs.source_changed == 'true' | |
| uses: dsherret/rust-toolchain-file@v1 | |
| - uses: Swatinem/rust-cache@v2 | |
| if: needs.changes.outputs.source_changed == 'true' | |
| with: | |
| shared-key: ${{ matrix.platform.runner }} | |
| - name: Build external binaries | |
| if: needs.changes.outputs.source_changed == 'true' | |
| shell: bash | |
| run: | | |
| cargo build --release -p runtimed -p runt-cli | |
| TARGET=$(rustc --print host-tuple) | |
| mkdir -p crates/notebook/binaries | |
| if [[ "$RUNNER_OS" == "Windows" ]]; then | |
| cp target/release/runtimed.exe "crates/notebook/binaries/runtimed-$TARGET.exe" | |
| cp target/release/runt.exe "crates/notebook/binaries/runt-$TARGET.exe" | |
| else | |
| cp target/release/runtimed "crates/notebook/binaries/runtimed-$TARGET" | |
| cp target/release/runt "crates/notebook/binaries/runt-$TARGET" | |
| fi | |
| - name: Clippy | |
| if: needs.changes.outputs.source_changed == 'true' | |
| run: cargo clippy --all-targets -- -D warnings | |
| - name: Build | |
| if: needs.changes.outputs.source_changed == 'true' | |
| run: cargo build --release | |
| - name: Run tests | |
| if: needs.changes.outputs.source_changed == 'true' | |
| run: cargo test --verbose | |
| # Build Tauri app once and share with E2E shards | |
| build-e2e-app: | |
| name: Build E2E Test App | |
| needs: [changes] | |
| if: needs.changes.outputs.source_changed == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libxdo-dev | |
| - name: Install rust | |
| uses: dsherret/rust-toolchain-file@v1 | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: ubuntu-latest | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Enable corepack | |
| run: corepack enable | |
| - name: Set pnpm store directory | |
| run: pnpm config set store-dir ~/.pnpm-store | |
| - name: Cache pnpm store | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.pnpm-store | |
| key: pnpm-store-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| pnpm-store-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Build frontend | |
| run: | | |
| pnpm --dir apps/sidecar build | |
| pnpm --dir apps/notebook build | |
| - name: Cache cargo bin | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/bin | |
| key: cargo-bin-ubuntu-tauri-cli-tauri-driver-${{ hashFiles('rust-toolchain.toml') }} | |
| restore-keys: | | |
| cargo-bin-ubuntu-tauri-cli-tauri-driver- | |
| - name: Install tauri-cli and tauri-driver | |
| run: | | |
| if ! command -v cargo-tauri &> /dev/null || ! command -v tauri-driver &> /dev/null; then | |
| cargo install tauri-cli tauri-driver --locked | |
| fi | |
| - name: Build external binaries | |
| run: | | |
| cargo build --release -p runtimed -p runt-cli | |
| TARGET=$(rustc --print host-tuple) | |
| mkdir -p crates/notebook/binaries | |
| cp target/release/runtimed "crates/notebook/binaries/runtimed-$TARGET" | |
| cp target/release/runt "crates/notebook/binaries/runt-$TARGET" | |
| mkdir -p target/release/binaries | |
| cp target/release/runtimed "target/release/binaries/runtimed-$TARGET" | |
| cp target/release/runt "target/release/binaries/runt-$TARGET" | |
| - name: Build Tauri app | |
| run: cd crates/notebook && cargo tauri build --ci --no-bundle --config '{"build":{"beforeBuildCommand":""}}' | |
| - name: Upload E2E artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-app-linux | |
| path: | | |
| target/release/notebook | |
| target/release/binaries/ | |
| retention-days: 1 | |
| # E2E smoke test - verifies basic app functionality | |
| e2e: | |
| name: E2E Smoke Test | |
| runs-on: ubuntu-latest | |
| needs: [changes, build-e2e-app] | |
| if: needs.changes.outputs.source_changed == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libgtk-3-dev \ | |
| libwebkit2gtk-4.1-dev \ | |
| libxdo-dev \ | |
| xvfb \ | |
| webkit2gtk-driver | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Enable corepack | |
| run: corepack enable | |
| - name: Set pnpm store directory | |
| run: pnpm config set store-dir ~/.pnpm-store | |
| - name: Cache pnpm store | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.pnpm-store | |
| key: pnpm-store-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| pnpm-store-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Download E2E artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: e2e-app-linux | |
| path: ./target/release | |
| - name: Make binaries executable | |
| run: | | |
| chmod +x target/release/notebook | |
| chmod +x target/release/binaries/* | |
| - name: Cache tauri-driver | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/bin/tauri-driver | |
| key: cargo-bin-tauri-driver-v0.1 | |
| restore-keys: | | |
| cargo-bin-tauri-driver- | |
| - name: Install tauri-driver | |
| run: | | |
| if ! command -v tauri-driver &> /dev/null; then | |
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable | |
| source ~/.cargo/env | |
| cargo install tauri-driver --locked | |
| fi | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Start E2E daemon | |
| run: | | |
| TARGET=$(ls target/release/binaries/ | grep runtimed | head -1) | |
| CONDUCTOR_WORKSPACE_PATH="${GITHUB_WORKSPACE}" \ | |
| ./target/release/binaries/$TARGET --dev run \ | |
| --uv-pool-size 2 --conda-pool-size 0 & | |
| echo "RUNTIMED_PID=$!" >> $GITHUB_ENV | |
| # Wait for daemon to initialize and pool to warm up | |
| echo "Waiting for daemon to be ready..." | |
| sleep 30 | |
| - name: Run E2E smoke test | |
| timeout-minutes: 5 | |
| run: | | |
| mkdir -p e2e-logs | |
| # Start Xvfb | |
| Xvfb :99 -screen 0 1920x1080x24 2>/dev/null & | |
| export DISPLAY=:99 | |
| sleep 2 | |
| # Start tauri-driver | |
| ~/.cargo/bin/tauri-driver --port 4444 & | |
| DRIVER_PID=$! | |
| sleep 3 | |
| # Run the smoke test (capture output) | |
| pnpm test:e2e 2>&1 | tee e2e-logs/test-output.log || FAIL=1 | |
| # Cleanup | |
| kill $DRIVER_PID 2>/dev/null || true | |
| kill $RUNTIMED_PID 2>/dev/null || true | |
| exit ${FAIL:-0} | |
| env: | |
| TAURI_APP_PATH: ./target/release/notebook | |
| CONDUCTOR_WORKSPACE_PATH: ${{ github.workspace }} | |
| NO_AT_BRIDGE: 1 | |
| LIBGL_ALWAYS_SOFTWARE: 1 | |
| RUST_LOG: info,notebook=debug,runtimed=debug | |
| - name: Collect daemon logs | |
| if: always() | |
| run: | | |
| mkdir -p e2e-logs | |
| # Find and copy daemon logs from worktree cache | |
| find ~/.cache/runt/worktrees -name "runtimed.log" -exec cp {} e2e-logs/ \; 2>/dev/null || true | |
| find ~/.cache/runt/worktrees -name "daemon.json" -exec cp {} e2e-logs/ \; 2>/dev/null || true | |
| # Also capture any app logs | |
| ls -la ~/.cache/runt/worktrees/*/ 2>/dev/null || true | |
| echo "=== Daemon log ===" && cat e2e-logs/runtimed.log 2>/dev/null || echo "No daemon log found" | |
| - name: Upload failure screenshots | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-screenshots | |
| path: e2e-screenshots/failures/ | |
| retention-days: 7 | |
| - name: Upload daemon logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-daemon-logs | |
| path: e2e-logs/ | |
| retention-days: 7 | |
| # E2E fixture tests - kernel launch scenarios (runs after smoke test passes) | |
| e2e-fixtures: | |
| name: E2E Kernel Launch Tests | |
| runs-on: ubuntu-latest | |
| needs: [changes, build-e2e-app, e2e] | |
| if: needs.changes.outputs.source_changed == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libgtk-3-dev \ | |
| libwebkit2gtk-4.1-dev \ | |
| libxdo-dev \ | |
| xvfb \ | |
| webkit2gtk-driver | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Enable corepack | |
| run: corepack enable | |
| - name: Set pnpm store directory | |
| run: pnpm config set store-dir ~/.pnpm-store | |
| - name: Cache pnpm store | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.pnpm-store | |
| key: pnpm-store-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| pnpm-store-${{ runner.os }}- | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Download E2E artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: e2e-app-linux | |
| path: ./target/release | |
| - name: Make binaries executable | |
| run: | | |
| chmod +x target/release/notebook | |
| chmod +x target/release/binaries/* | |
| - name: Cache tauri-driver | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/bin/tauri-driver | |
| key: cargo-bin-tauri-driver-v0.1 | |
| restore-keys: | | |
| cargo-bin-tauri-driver- | |
| - name: Install tauri-driver | |
| run: | | |
| if ! command -v tauri-driver &> /dev/null; then | |
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable | |
| source ~/.cargo/env | |
| cargo install tauri-driver --locked | |
| fi | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Run kernel launch fixture tests | |
| timeout-minutes: 15 | |
| run: | | |
| set -o pipefail | |
| mkdir -p e2e-logs | |
| # Start Xvfb | |
| Xvfb :99 -screen 0 1920x1080x24 2>/dev/null & | |
| export DISPLAY=:99 | |
| sleep 2 | |
| FAIL=0 | |
| # Helper function to run a fixture test | |
| run_fixture_test() { | |
| local notebook="$1" | |
| local spec="$2" | |
| local name="$3" | |
| echo "=== Running $name ===" | |
| # Start daemon fresh for each test | |
| TARGET=$(ls target/release/binaries/ | grep runtimed | head -1) | |
| CONDUCTOR_WORKSPACE_PATH="${GITHUB_WORKSPACE}" \ | |
| ./target/release/binaries/$TARGET --dev run \ | |
| --uv-pool-size 2 --conda-pool-size 0 & | |
| DAEMON_PID=$! | |
| sleep 20 | |
| # Start tauri-driver | |
| ~/.cargo/bin/tauri-driver --port 4444 & | |
| DRIVER_PID=$! | |
| sleep 3 | |
| # Run test and capture exit code | |
| local exit_code=0 | |
| NOTEBOOK_PATH="$notebook" E2E_SPEC="$spec" \ | |
| pnpm test:e2e 2>&1 | tee -a e2e-logs/fixtures-output.log || exit_code=$? | |
| # Cleanup | |
| kill $DRIVER_PID 2>/dev/null || true | |
| kill $DAEMON_PID 2>/dev/null || true | |
| sleep 2 | |
| echo "=== $name complete (exit: $exit_code) ===" | |
| return $exit_code | |
| } | |
| # Run each fixture test | |
| run_fixture_test \ | |
| "crates/notebook/fixtures/audit-test/1-vanilla.ipynb" \ | |
| "e2e/specs/prewarmed-uv.spec.js" \ | |
| "Prewarmed Pool Test" || FAIL=1 | |
| run_fixture_test \ | |
| "crates/notebook/fixtures/audit-test/10-deno.ipynb" \ | |
| "e2e/specs/deno.spec.js" \ | |
| "Deno Kernel Test" || FAIL=1 | |
| exit $FAIL | |
| env: | |
| TAURI_APP_PATH: ./target/release/notebook | |
| CONDUCTOR_WORKSPACE_PATH: ${{ github.workspace }} | |
| NO_AT_BRIDGE: 1 | |
| LIBGL_ALWAYS_SOFTWARE: 1 | |
| RUST_LOG: info,notebook=debug,runtimed=debug | |
| - name: Collect daemon logs | |
| if: always() | |
| run: | | |
| mkdir -p e2e-logs | |
| find ~/.cache/runt/worktrees -name "runtimed.log" -exec cp {} e2e-logs/ \; 2>/dev/null || true | |
| echo "=== Daemon log ===" && cat e2e-logs/runtimed.log 2>/dev/null || echo "No daemon log found" | |
| - name: Upload failure screenshots | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-fixture-screenshots | |
| path: e2e-screenshots/failures/ | |
| retention-days: 7 | |
| - name: Upload test logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-fixture-logs | |
| path: e2e-logs/ | |
| retention-days: 7 | |
| # Python daemon integration tests | |
| runtimed-py-integration: | |
| name: runtimed-py Integration Tests | |
| runs-on: ubuntu-latest | |
| needs: [changes, build-linux] | |
| if: needs.changes.outputs.source_changed == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libxdo-dev | |
| - name: Install rust | |
| uses: dsherret/rust-toolchain-file@v1 | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| shared-key: ubuntu-latest | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Build runtimed binary | |
| run: cargo build --release -p runtimed | |
| - name: Build runtimed-py | |
| working-directory: python/runtimed | |
| run: uv run maturin develop --manifest-path ../../crates/runtimed-py/Cargo.toml | |
| - name: Run unit tests | |
| working-directory: python/runtimed | |
| run: uv run pytest tests/test_session_unit.py -v --tb=short | |
| - name: Run integration tests | |
| timeout-minutes: 10 | |
| working-directory: python/runtimed | |
| run: | | |
| mkdir -p integration-logs | |
| # Set environment for CI mode | |
| export RUNTIMED_INTEGRATION_TEST=1 | |
| export RUNTIMED_BINARY="${GITHUB_WORKSPACE}/target/release/runtimed" | |
| export RUNTIMED_LOG_LEVEL=debug | |
| export CONDUCTOR_WORKSPACE_PATH="${GITHUB_WORKSPACE}" | |
| # Run tests (daemon is spawned by the test fixture) | |
| uv run pytest tests/test_daemon_integration.py -v --tb=short 2>&1 | tee integration-logs/test-output.log || FAIL=1 | |
| exit ${FAIL:-0} | |
| - name: Upload test logs | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: runtimed-py-integration-logs | |
| path: python/runtimed/integration-logs/ | |
| retention-days: 7 |