name: hub check # Builds, type-checks, and tests the Hub TS package under hub/. # The Hub is the Feishu-group collaboration + agent runtime half. # This is an INTERNAL gate on the Hub's own # health, like checker-check is for the Rust half. on: push: workflow_dispatch: concurrency: group: hub-check-${{ github.ref }} cancel-in-progress: true jobs: hub-check: runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_USER: paradigm POSTGRES_PASSWORD: paradigm POSTGRES_DB: cph_hub_test # Avoid host-port binds: concurrent hub-check jobs on the shared # runner raced on published 5432/15432 ("port is already allocated"). # Reach the service by Docker DNS name from the job container instead. options: >- --health-cmd "pg_isready -U paradigm -d cph_hub_test" --health-interval 5s --health-timeout 5s --health-retries 20 defaults: run: working-directory: hub steps: - uses: actions/checkout@v5 - name: Install Rust toolchain (for cph binary) uses: dtolnay/rust-toolchain@1.92.0 - name: Cache cargo registry + build uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: cargo-hub-check-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} restore-keys: | cargo-hub-check-${{ runner.os }}- - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "24" cache: npm cache-dependency-path: | hub/package-lock.json hub/admin-web/package-lock.json - name: Install dependencies run: | npm ci npm ci --prefix admin-web - name: Audit production Node dependencies run: npm run audit:production - name: Install Linux sandbox dependency run: sudo apt-get update && sudo apt-get install --yes bubblewrap socat - name: Wait for Postgres run: | node <<'NODE' const net = require("node:net"); const deadline = Date.now() + 60000; const host = process.env.HUB_CHECK_PG_HOST || "postgres"; const port = Number(process.env.HUB_CHECK_PG_PORT || "5432"); function tryConnect() { const socket = net.createConnection({ host, port }); socket.once("connect", () => { socket.end(); process.exit(0); }); socket.once("error", () => { socket.destroy(); if (Date.now() > deadline) { console.error(`Postgres did not become reachable at ${host}:${port}`); process.exit(1); } setTimeout(tryConnect, 1000); }); } tryConnect(); NODE # Prisma client must be generated before tsc can check imports from # @prisma/client. Stub DATABASE_URL so prisma generate works. - name: Generate Prisma client run: DATABASE_URL="postgresql://stub:stub@127.0.0.1:5432/stub" npx prisma generate --schema prisma/schema.prisma - name: Type-check run: npx tsc -p tsconfig.json --noEmit - name: Build run: npm run build - name: Validate Prisma schema run: DATABASE_URL="postgresql://stub:stub@127.0.0.1:5432/stub" npx prisma validate --schema prisma/schema.prisma - name: Install cph binary run: | cd .. cargo install --path crates/cph-cli --locked # Make cph available to the unprivileged sandbox user below. sudo install -m 0755 "$HOME/.cargo/bin/cph" /usr/local/bin/cph - name: Prove real Claude SDK Bash sandbox boundary run: | set -euo pipefail # Nested act/docker runners often disallow unprivileged user # namespaces, which bwrap requires once CapEff is cleared. Skip the # live proof there; unit + non-sandbox integration still gate. sysctl -w kernel.unprivileged_userns_clone=1 2>/dev/null || true sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 2>/dev/null || true if ! unshare --user true 2>/dev/null; then echo "Skipping sandbox proof: unprivileged user namespaces unavailable on this runner" exit 0 fi if ! id cphci >/dev/null 2>&1; then useradd --create-home --shell /bin/bash cphci fi install -d -o cphci -g cphci -m 0700 /w/t REPO_ROOT="$(cd .. && pwd)" NODE_BIN_DIR="$(dirname "$(command -v node)")" NPX_BIN="$(command -v npx)" chown -R cphci:cphci "$REPO_ROOT/hub" /home/cphci /usr/bin/setpriv \ --reuid=cphci --regid=cphci --init-groups \ --inh-caps=-all --bounding-set=-all --ambient-caps=-all \ --no-new-privs \ env HOME=/home/cphci PATH="$NODE_BIN_DIR:/usr/local/bin:/usr/bin:/bin" CPH_SANDBOX_TEST_ROOT=/w/t \ bash -lc "cd '$REPO_ROOT/hub' && '$NPX_BIN' vitest run test/integration/agent-sandbox-linux.test.ts" - name: Run unit tests run: npx vitest run test/unit # Integration tests need PostgreSQL + cph. cph is installed above. # PostgreSQL is the job service container reachable as `postgres`. - name: Run integration tests (mock provider, real prisma + cph) run: | npx prisma migrate deploy --schema prisma/schema.prisma npx vitest run test/integration \ --exclude test/integration/real-model.test.ts \ --exclude test/integration/agent-sandbox-linux.test.ts env: DATABASE_URL: postgresql://paradigm:paradigm@postgres:5432/cph_hub_test HUB_SKILL_STORE_ROOT: /tmp/cph-hub-check-skills # Real-model tests are opt-in: set RUN_REAL_MODEL_TESTS=true and provide # OPENROUTER_API_KEY when a branch should hit live OpenRouter. - name: Run real-model integration tests (optional, needs secret) run: npx vitest run test/integration/real-model.test.ts env: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} RUN_REAL_MODEL_TESTS: ${{ vars.RUN_REAL_MODEL_TESTS }}