Skip to content

fix(ci): generate mac updater artifacts for preview channel (#430) #1120

fix(ci): generate mac updater artifacts for preview channel (#430)

fix(ci): generate mac updater artifacts for preview channel (#430) #1120

Workflow file for this run

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