forked from EduCraft/curriculum-project-hub
9c1f9de9c1
Admin routes always construct OrganizationAgentConfiguration via readSkillStoreRoot(); CI and local runs without HUB_SKILL_STORE_ROOT failed open. Seed a tmp root when unset. Also make preflight CLI tests honor DATABASE_URL and set the skill-store env in hub-check.
166 lines
6.2 KiB
YAML
166 lines
6.2 KiB
YAML
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:
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
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 }}
|