Skip to content

perf(cli): stabilize subagent inspection and dispatch portability #4269

perf(cli): stabilize subagent inspection and dispatch portability

perf(cli): stabilize subagent inspection and dispatch portability #4269

Workflow file for this run

name: CI
on:
pull_request:
branches: [main]
paths-ignore:
- '**/*.md'
- 'png/**'
push:
branches: [main]
paths-ignore:
- '**/*.md'
- 'png/**'
# Cancel previous runs on same branch/PR
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# ── Shell deploy assets: LF endings + syntax ───────────────────────
# Relay one-click deploy embeds these scripts into the Desktop binary and
# uploads them verbatim to a Linux host, where a single CR aborts the deploy
# with `$'\r': command not found`. .gitattributes pins LF; this is the guard.
shell-scripts:
name: Shell Deploy Scripts
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v5
- name: Reject CRLF in shell and deploy assets
run: |
bad=$(git ls-files -z \
'*.sh' '*.bash' 'Dockerfile' 'Dockerfile.*' '*.Dockerfile' 'Caddyfile' \
'docker-compose.yml' 'docker-compose.*.yml' \
| xargs -0 -r grep -lU $'\r' || true)
if [ -n "$bad" ]; then
echo "::error::CRLF line endings found; these must stay LF (see .gitattributes):"
echo "$bad"
exit 1
fi
echo "All shell and deploy assets are LF-only."
- name: bash -n every tracked shell script
run: |
rc=0
while IFS= read -r -d '' f; do
bash -n "$f" || { echo "::error file=$f::bash syntax error"; rc=1; }
done < <(git ls-files -z '*.sh' '*.bash')
exit "$rc"
- name: Verify minisign download fallback
run: |
set -euo pipefail
test_root="$(mktemp -d)"
trap 'rm -rf "$test_root"' EXIT
mkdir -p "$test_root/stubs"
printf '#!/usr/bin/env bash\nexit 1\n' >"$test_root/stubs/sudo"
chmod +x "$test_root/stubs/sudo"
PATH="$test_root/stubs:/usr/bin:/bin" \
RUNNER_TEMP="$test_root" \
BITFUN_SIGNING_KEY="YQ==" \
bash scripts/sign-release-assets.sh "$test_root/missing-asset"
"$test_root/bitfun-minisign-0.12/bin/minisign" -v
# ── CLI: independent tests ─────────────────────────────────────────
cli-test:
name: CLI Tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
cache_key: ubuntu
- os: macos-15
cache_key: macos
steps:
- uses: actions/checkout@v5
- name: Install Linux system dependencies
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config \
build-essential \
libssl-dev \
libxcb1-dev \
libxcb-render0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev
- uses: dtolnay/rust-toolchain@stable
- uses: swatinem/rust-cache@v2
with:
shared-key: "cli-ci-v2-${{ matrix.cache_key }}"
cache-bin: false
# PRs restore trusted caches but never publish merge-ref artifacts.
save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
cache-on-failure: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- name: Run CLI and ACP tests on macOS
if: runner.os == 'macOS'
run: cargo test --locked -p bitfun-cli -p bitfun-acp
- name: Run CLI, ACP, and agent runtime tests
if: runner.os == 'Linux'
run: cargo test --locked -p bitfun-cli -p bitfun-acp -p bitfun-agent-runtime
- name: Run SDK Host tests
if: runner.os == 'Linux'
run: cargo test --locked -p bitfun-sdk-host -p bitfun-sdk-host-app
- name: Run SDK Host terminal cleanup regressions
if: runner.os == 'Linux'
run: |
cargo test --locked -p terminal-core shutdown_returns_only_after_process_exit_is_confirmed -- --test-threads=1
cargo test --locked -p terminal-core shutdown_evicts_a_process_whose_controller_already_confirmed_exit -- --test-threads=1
cargo test --locked -p terminal-core background_only_binding_is_owned_by_the_session -- --test-threads=1
# ── Rust: build check ─────────────────────────────────────────────
rust-build-check:
name: Rust Build Check (${{ matrix.os }})
runs-on: ${{ matrix.os }}
env:
# Keep the workspace check plus desktop test profiles within hosted-runner disk limits.
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-15
- windows-latest
steps:
- uses: actions/checkout@v5
# Tauri code generation only requires its configured resource roots to
# exist during check/test; distributable assets remain frontend-build's owner.
- name: Create Tauri resource directories
shell: bash
run: mkdir -p dist src/mobile-web/dist
- name: Install Linux system dependencies (Tauri)
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update
if apt-cache show libwebkit2gtk-4.1-dev >/dev/null 2>&1; then
WEBKIT_PKG=libwebkit2gtk-4.1-dev
else
WEBKIT_PKG=libwebkit2gtk-4.0-dev
fi
if apt-cache show libappindicator3-dev >/dev/null 2>&1; then
APPINDICATOR_PKG=libappindicator3-dev
else
APPINDICATOR_PKG=libayatana-appindicator3-dev
fi
sudo apt-get install -y --no-install-recommends \
pkg-config \
libglib2.0-dev \
libgtk-3-dev \
libxdo-dev \
"$WEBKIT_PKG" \
"$APPINDICATOR_PKG" \
librsvg2-dev \
patchelf \
libleptonica-dev \
libtesseract-dev \
tesseract-ocr \
tesseract-ocr-eng
- uses: dtolnay/rust-toolchain@stable
- uses: swatinem/rust-cache@v2
with:
shared-key: "ci-check-v3-${{ runner.os }}-no-cargo-bin-v1"
cache-bin: false
# PR caches are scoped to merge refs; trusted main pushes own shared
# refreshes and retain completed dependency builds after late test failures.
save-if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
cache-on-failure: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- name: Check compilation
run: cargo check --locked --workspace
- name: Run core and desktop library tests
run: cargo test --locked -p bitfun-core -p bitfun-desktop --lib
# These crates own platform-sensitive behavior that is not exercised by
# testing bitfun-core/bitfun-desktop alone. Keep their focused contract
# suites in the OS matrix so Linux success cannot hide Windows/macOS
# regressions in worker interruption, relay storage, or OAuth handling.
- name: Run Page Functions runtime tests
run: cargo test --locked -p bitfun-page-function-runtime
- name: Run Relay service tests
run: cargo test --locked -p bitfun-relay-service
- name: Run subscription authentication tests
run: cargo test --locked -p bitfun-ai-adapters --features subscription-auth --lib subscription_auth
# File watching is backed by a different OS API on every platform
# (ReadDirectoryChangesW / FSEvents / inotify), so watch registration
# regressions surface per-OS. The suite is behind a non-default feature
# and would otherwise never run anywhere.
#
# macOS is excluded: this suite already fails there before any of this
# branch's changes (the debounce/atomic-rename contracts time out under
# FSEvents coalescing), which is worth fixing separately rather than
# blocking unrelated work.
- name: Run file watch contract tests
if: runner.os != 'macOS'
run: cargo test --locked -p bitfun-services-integrations --no-default-features --features file-watch --test file_watch_contracts
# Search tools resolve paths and symlinks directly, which also differs
# across platforms. Scoped to the search module: the glob tests in this
# crate fail on Windows independently of this branch (walk-root
# derivation treats separators differently) and need their own fix.
- name: Run search tool tests
run: "cargo test --locked -p tool-runtime --lib search::"
# ── Frontend: build ────────────────────────────────────────────────
frontend-build:
name: Frontend Build
runs-on: ubuntu-latest
env:
NODE_OPTIONS: --max-old-space-size=6144
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 2
- name: Setup pnpm
uses: pnpm/action-setup@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: pnpm
- name: Check repository hygiene
run: pnpm run check:repo-hygiene
- name: Check core boundaries
run: node --test scripts/check-core-boundaries.test.mjs
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Validate PPT Live generated-file contract
run: pnpm run test:ppt-live
- name: Validate GitHub config
run: pnpm run check:github-config
- name: Select i18n contract profile
shell: bash
run: |
if git rev-parse HEAD^1 >/dev/null 2>&1 &&
git diff --name-only HEAD^1 HEAD -- scripts/i18n-audit.mjs scripts/i18n-contract.test.mjs | grep -q .; then
echo "BITFUN_I18N_CONTRACT_TEST_AUDIT_INTEGRATION=1" >> "$GITHUB_ENV"
fi
- name: Validate i18n contract
run: pnpm run i18n:contract:test:ci
- name: Audit i18n resources
run: pnpm run i18n:audit
- name: Validate theme color audit contract
run: pnpm run theme:color-audit:test
- name: Audit theme color governance
run: pnpm run theme:color-audit:all
- name: Validate theme visual governance contract
run: pnpm run theme:visual-contract
- name: Lint web UI
run: pnpm run lint:web
- name: Run web UI tests
run: pnpm --dir src/web-ui run test:run
- name: Build web UI
run: pnpm run build:web
- name: Type-check mobile web
run: pnpm --dir src/mobile-web run type-check
- name: Build mobile web
run: pnpm run build:mobile-web