[failproofaid] Split failproofai into a CLI + Rust background daemon #1399
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |