Skip to content

[failproofaid] Split failproofai into a CLI + Rust background daemon #1401

[failproofaid] Split failproofai into a CLI + Rust background daemon

[failproofaid] Split failproofai into a CLI + Rust background daemon #1401

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
runs-on: ubuntu-latest
env:
FAILPROOFAI_TELEMETRY_DISABLED: "1"
steps:
- uses: actions/checkout@v7.0.1
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: bun-${{ runner.os }}-
- name: Install dependencies
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 5
command: bun install --frozen-lockfile
- name: Check version consistency
run: |
ROOT_VERSION=$(jq -r .version package.json)
echo "Root version: $ROOT_VERSION"
MISMATCH=0
for pkg in packages/*/package.json; do
[ -f "$pkg" ] || continue
PKG_VERSION=$(jq -r .version "$pkg")
if [ "$PKG_VERSION" != "$ROOT_VERSION" ]; then
echo "::error file=$pkg::Version mismatch: $pkg has $PKG_VERSION, expected $ROOT_VERSION"
MISMATCH=1
fi
done
# Check optionalDependencies in wrapper
for dep_version in $(jq -r '.optionalDependencies // {} | values[]' packages/wrapper/package.json 2>/dev/null || true); do
if [ "$dep_version" != "$ROOT_VERSION" ]; then
echo "::error file=packages/wrapper/package.json::Dependency version mismatch: $dep_version, expected $ROOT_VERSION"
MISMATCH=1
fi
done
# The daemon binaries DO ship as npm platform packages
# (@failproofai/failproofaid-<os>-<arch>), but their pins are injected
# into package.json at publish time by
# scripts/build-daemon-packages.mjs — the same invocation that
# publishes them, so they cannot drift — and are deliberately absent
# from the committed tree. Nothing to check here.
#
# The Cargo version still has to match, because the release tag the
# CLI builds its download URL from is the npm version, and the binary
# at that URL reports the Cargo one.
# Check the Cargo workspace version (failproofaid) against root package.json
if [ -f Cargo.toml ]; then
CARGO_VERSION=$(grep -m1 '^version = ' Cargo.toml | sed -E 's/version = "(.*)"/\1/')
if [ "$CARGO_VERSION" != "$ROOT_VERSION" ]; then
echo "::error file=Cargo.toml::Version mismatch: Cargo.toml has $CARGO_VERSION, expected $ROOT_VERSION"
MISMATCH=1
fi
fi
if [ "$MISMATCH" -eq 1 ]; then
echo "::error::Version mismatch detected across package.json files"
exit 1
fi
echo "All versions match: $ROOT_VERSION"
- name: Lint
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 5
command: bun run lint
- name: Type check
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 5
command: bunx tsc --noEmit
rust-quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
with:
# This job runs `cargo clippy`/`cargo test` over the full
# dependency tree, executing third-party build scripts. The
# default (`true`) would leave GITHUB_TOKEN in .git/config where
# any of them could read it; nothing here needs push access.
persist-credentials: false
# Stage 1 lands an empty Cargo workspace (zero crates/*/Cargo.toml) so
# the CI plumbing itself can go green before any Rust code exists.
# `cargo build/clippy/test --workspace` (and even `cargo fmt --all`)
# all hard-error on a zero-member workspace ("the workspace has no
# members"), so every real step below is gated on at least one crate
# being present rather than relying on any of them to no-op cleanly.
- name: Detect crates
id: crates
run: |
if ls crates/*/Cargo.toml >/dev/null 2>&1; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "No crates/*/Cargo.toml yet — rust-quality has nothing to check."
fi
- if: steps.crates.outputs.present == 'true'
run: rustup show
# cargo test spawns the real TS worker via `bun bin/failproofai-worker.mjs`
# (crates/failproofaid/src/server.rs's live end-to-end test) — bun has to
# be on PATH for that test, not just for the TS-side jobs.
- if: steps.crates.outputs.present == 'true'
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- if: steps.crates.outputs.present == 'true'
uses: actions/cache@v6
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: cargo-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml', 'Cargo.lock', 'crates/*/Cargo.toml') }}
restore-keys: cargo-${{ runner.os }}-
- name: cargo fmt --check
if: steps.crates.outputs.present == 'true'
run: cargo fmt --all -- --check
- name: cargo clippy
if: steps.crates.outputs.present == 'true'
run: cargo clippy --workspace --all-targets -- -D warnings
- name: cargo test
if: steps.crates.outputs.present == 'true'
run: cargo test --workspace
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
env-config:
- { name: default, env: {} }
- { name: log-debug, env: { FAILPROOFAI_LOG_LEVEL: debug } }
- { name: hook-log-file, env: { FAILPROOFAI_HOOK_LOG_FILE: "1" } }
env:
FAILPROOFAI_TELEMETRY_DISABLED: "1"
NEXT_TELEMETRY_DISABLED: "1"
steps:
- uses: actions/checkout@v7.0.1
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: bun-${{ runner.os }}-
- name: Install dependencies
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 5
command: bun install --frozen-lockfile
- name: Test (${{ matrix.env-config.name }})
uses: nick-fields/retry@v4
env: ${{ matrix.env-config.env }}
with:
max_attempts: 3
timeout_minutes: 10
command: bun run test:run
build:
runs-on: ubuntu-latest
env:
FAILPROOFAI_TELEMETRY_DISABLED: "1"
NEXT_TELEMETRY_DISABLED: "1"
steps:
- uses: actions/checkout@v7.0.1
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: bun-${{ runner.os }}-
- name: Install dependencies
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 5
command: bun install --frozen-lockfile
- name: Build
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 10
command: bun run build
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: bun-${{ runner.os }}-
- name: Install dependencies
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 5
command: bun install --frozen-lockfile
- uses: actions/setup-node@v7
with:
node-version: 22
- name: Install Mintlify CLI
run: npm install -g mintlify
# Validates docs.json structure + nav-link resolution.
- name: Validate docs config
working-directory: docs
run: mintlify validate
# Parses every MDX page with the same engine Mintlify runs at deploy
# time. `mintlify validate` does NOT parse page content, so a syntax
# error (e.g. a translation that breaks a JSX tag or injects a `{#id}`
# heading anchor) passes that step but fails the post-merge deploy.
# This catches it on the PR instead.
- name: Validate MDX pages parse
run: bun run validate:mdx
test-e2e:
runs-on: ubuntu-latest
env:
FAILPROOFAI_TELEMETRY_DISABLED: "1"
NEXT_TELEMETRY_DISABLED: "1"
steps:
- uses: actions/checkout@v7.0.1
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: bun-${{ runner.os }}-
- name: Install dependencies
uses: nick-fields/retry@v4
with:
max_attempts: 3
timeout_minutes: 5
command: bun install --frozen-lockfile
- name: Build E2E fixtures
run: bun build src/index.ts --outdir dist --target node --format cjs
- name: E2E Hook Tests
uses: nick-fields/retry@v4
with:
max_attempts: 2
timeout_minutes: 10
command: bun run test:e2e