Skip to content

Reconnect updates the existing DCR connection #3790

Reconnect updates the existing DCR connection

Reconnect updates the existing DCR connection #3790

Workflow file for this run

name: CI
on:
pull_request:
push:
branches:
- main
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
name: Changed paths
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
outputs:
desktop_smoke: ${{ github.event_name != 'pull_request' || steps.filter.outputs.desktop_smoke == 'true' }}
selfhost_docker_smoke: ${{ github.event_name != 'pull_request' || steps.filter.outputs.selfhost_docker_smoke == 'true' }}
steps:
- uses: actions/checkout@v4
- name: Filter changed paths
id: filter
if: github.event_name == 'pull_request'
uses: dorny/paths-filter@v3
with:
filters: |
desktop_smoke:
- ".github/workflows/**"
- "bun.lock"
- "package.json"
- "turbo.json"
- "apps/desktop/**"
- "apps/local/**"
- "apps/cli/**"
- "packages/app/**"
- "packages/core/**"
- "packages/hosts/mcp/**"
- "packages/kernel/runtime-quickjs/**"
- "packages/plugins/**"
- "packages/react/**"
selfhost_docker_smoke:
- ".github/workflows/**"
- ".dockerignore"
- "bun.lock"
- "package.json"
- "turbo.json"
- "apps/host-selfhost/**"
- "packages/app/**"
- "packages/core/**"
- "packages/hosts/mcp/**"
- "packages/kernel/runtime-quickjs/**"
- "packages/plugins/**"
- "packages/react/**"
format:
name: Format
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11
- name: Cache Bun package cache
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1.3.11-
- run: bun install --frozen-lockfile --ignore-scripts
- run: bun run format:check
lint:
name: Lint
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11
- name: Cache Bun package cache
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1.3.11-
- run: bun install --frozen-lockfile --ignore-scripts
- run: bun run lint
typecheck:
name: Typecheck
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 15
env:
TURBO_API: ${{ vars.TURBO_API }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11
- name: Cache Bun package cache
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1.3.11-
# No prebuilt better-sqlite3 binary matches this runner, so `bun install`
# builds it from source via node-gyp, whose undici needs Node 22.10+
# (webidl.markAsUncloneable). Pin the same Node 24 runtime used by release.
- uses: actions/setup-node@v4
with:
node-version: 24
- run: bun install --frozen-lockfile
- run: bun run typecheck
test:
name: Test
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 15
# Tuned for Blacksmith's 4 vCPU runners.
env:
TURBO_API: ${{ vars.TURBO_API }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
TURBO_TEST_CONCURRENCY: 4
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11
- name: Cache Bun package cache
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1.3.11-
# apps/cloud's test script invokes `node` directly; undici 8.x (pulled
# in by @cloudflare/vitest-pool-workers) calls webidl.markAsUncloneable
# which only exists in Node 22.10+. Pin the Node 24 CI runtime.
- uses: actions/setup-node@v4
with:
node-version: 24
- run: bun install --frozen-lockfile
- run: bun run test
e2e:
name: E2E (${{ matrix.target }}${{ matrix['shard-name'] && format(' {0}', matrix['shard-name']) || '' }})
strategy:
fail-fast: false
matrix:
include:
# Each cloud shard boots its own fresh dev stack. On 4 vCPU runners,
# four fatter shards keep the longest shard below selfhost while saving
# four runner boots and four warm cache restores.
- { target: cloud, shard: 1/4, shard-name: 1of4 }
- { target: cloud, shard: 2/4, shard-name: 2of4 }
- { target: cloud, shard: 3/4, shard-name: 3of4 }
- { target: cloud, shard: 4/4, shard-name: 4of4 }
- target: selfhost
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11
- name: Cache Bun package cache
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1.3.11-
# The dev stacks spawn Node sidecars (vite/workerd tooling); pin the
# same Node 24 runtime the release and publish workflows use.
- uses: actions/setup-node@v4
with:
node-version: 24
- run: bun install --frozen-lockfile
- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-1.60.0
restore-keys: |
${{ runner.os }}-playwright-
# Install from e2e so bunx resolves ITS pinned playwright (the version
# the tests run against) rather than floating to the latest.
- name: Install Playwright Chromium
run: bunx playwright install --with-deps chromium chromium-headless-shell
working-directory: e2e
# The globalsetup boots the target's own dev server (ports are claimed
# per checkout, so this is hermetic) and tears it down after the run.
# --retry=2: browser scenarios can still hit isolated waitFor timeouts
# (single-test waitFor timeouts, not systemic failures); a retry on the
# same booted stack clears them.
- name: Run cloud scenarios
if: matrix.target == 'cloud'
env:
MCP_SESSION_TIMEOUT_MS: "3000"
MCP_PAUSED_SESSION_IDLE_TIMEOUT_MS: "6000"
run: bunx vitest run --project cloud --retry=2 ${{ matrix.shard && format('--shard={0}', matrix.shard) || '' }}
working-directory: e2e
- name: Run selfhost scenarios
if: matrix.target == 'selfhost'
run: bunx vitest run --project selfhost --retry=2
working-directory: e2e
# Failed runs keep their trace.zip / session.mp4 / step screenshots in
# runs/<target>/<slug>/ — surface them instead of a bare red X.
- name: Upload run artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: e2e-runs-${{ matrix.target }}${{ matrix['shard-name'] && format('-{0}', matrix['shard-name']) || '' }}
path: e2e/runs/
retention-days: 7
e2e-local:
name: E2E (stdio MCP)
# Skipped on pull_request: the local scenario boots a real `executor web`
# plus a browser and is currently flaky on PRs. Still runs on push to main.
if: github.event_name != 'pull_request'
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11
- name: Cache Bun package cache
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1.3.11-
# The local scenarios boot a real `executor web` (which spawns a Node
# sidecar) and some drive a browser, so pin Node 24 and install Chromium.
- uses: actions/setup-node@v4
with:
node-version: 24
- run: bun install --frozen-lockfile
- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-1.60.0
restore-keys: |
${{ runner.os }}-playwright-
# `chromium` and the new `chromium-headless-shell` ship as separate
# downloads; the browser-driven scenarios launch the headless shell.
# Install from e2e so bunx resolves ITS pinned playwright (the version the
# tests run against) rather than floating to the latest, which would fetch
# a browser build the test runtime does not look for.
- name: Install Playwright Chromium
run: bunx playwright install --with-deps chromium chromium-headless-shell
working-directory: e2e
# The `local` project is excluded from the default `test` chain (each
# scenario boots its own `executor web`). Run just the stdio MCP scenario
# here: it is the auto-connect / env-as-secret regression guard, and
# running it alone avoids the boot-resource accumulation and the
# pre-existing browser flakiness of the rest of the local suite. Expanding
# to the full `local` project (bun run test:local) is a follow-up once
# those are stabilized.
- name: Run the stdio MCP scenario
run: bunx vitest run --project local local/stdio-mcp.test.ts
working-directory: e2e
desktop-smoke:
name: Desktop smoke build
needs: changes
if: needs.changes.outputs.desktop_smoke == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.11
- name: Cache Bun package cache
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-1.3.11-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-1.3.11-
# No prebuilt better-sqlite3 binary matches this runner, so `bun install`
# builds it from source via node-gyp, whose undici needs Node 22.10+
# (webidl.markAsUncloneable). Pin the same Node 24 CI runtime.
- uses: actions/setup-node@v4
with:
node-version: 24
- run: bun install --frozen-lockfile
- name: Build web app
run: bun run --filter @executor-js/local build
- name: Build bundled executor
env:
BUN_TARGET: bun-linux-x64
run: bun ./scripts/build-sidecar.ts
working-directory: apps/desktop
# Run the compiled sidecar in a clean container where the build
# workspace does not exist. bun --compile bakes build-machine paths
# into __dirname for runtime-loaded assets; running the binary on the
# build machine hides that entire failure class (this exact gap
# shipped a broken 1Password SDK: its sdk-core wasm was read from the
# CI runner's node_modules path at runtime, ENOENT on every user
# machine). Pass condition: the 1Password endpoint reaches real
# credential validation, not a module-load or empty-namespace error.
- name: Run sidecar outside the workspace
working-directory: apps/desktop
run: |
docker run --rm -d --name sidecar-smoke -p 45841:45841 -e HOME=/tmp \
-v "$PWD/resources/executor:/opt/executor:ro" \
debian:bookworm-slim /opt/executor/executor daemon run --foreground \
--port 45841 --hostname 0.0.0.0 --auth-token=ci-smoke
for i in $(seq 1 60); do
code=$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: Bearer ci-smoke" http://127.0.0.1:45841/api/onepassword/vaults || true)
[ "$code" != "000" ] && break
docker ps -q -f name=sidecar-smoke | grep -q . || { docker logs sidecar-smoke; exit 1; }
sleep 1
done
body=$(curl -s -H "Authorization: Bearer ci-smoke" \
"http://127.0.0.1:45841/api/onepassword/vaults?authKind=service-account&account=ops_ci_fake_token")
docker stop sidecar-smoke
echo "$body"
if echo "$body" | grep -qE "sdk module load|ENOENT|is not a function|not a constructor"; then
echo "::error::1Password SDK failed to load inside the compiled binary (baked build path or missing sibling asset)"
exit 1
fi
if ! echo "$body" | grep -q "invalid service account token"; then
echo "::error::sidecar did not reach 1Password credential validation; unexpected response above"
exit 1
fi
- name: Build Electron main/preload/renderer
run: bunx --bun electron-vite build
working-directory: apps/desktop
selfhost-docker-smoke:
name: Self-host Docker image
needs: changes
if: needs.changes.outputs.selfhost_docker_smoke == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Setup Blacksmith Builder
uses: useblacksmith/setup-docker-builder@v1
- name: Build self-host image
uses: useblacksmith/build-push-action@v2
with:
context: .
file: apps/host-selfhost/Dockerfile
push: false
tags: executor-selfhost:ci