name: Rust on: push: branches: [main] paths: - "lib/apps/**" - "lib/components/**" - "lib/foundation/**" - "test/**" - "Cargo.toml" - "Cargo.lock" - ".cargo/**" - ".config/**" - "bin/dev/**" - "docs/public/reference/cli.mdx" - "docs/public/reference/user-configuration.mdx" - "openapi/**" - ".github/workflows/rust.yml" pull_request: branches: [main] paths: - "lib/apps/**" - "lib/components/**" - "lib/foundation/**" - "test/**" - "Cargo.toml" - "Cargo.lock" - ".cargo/**" - ".config/**" - "bin/dev/**" - "docs/public/reference/cli.mdx" - "docs/public/reference/user-configuration.mdx" - "openapi/**" - ".github/workflows/rust.yml" workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: {} env: CARGO_TERM_COLOR: always CARGO_NET_RETRY: "10" jobs: fmt: name: Format runs-on: ubuntu-24.04-x86-32-cores permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable with: toolchain: nightly-2026-04-14 components: rustfmt - run: cargo +nightly-2026-04-14 fmt --check --all clippy: name: Clippy runs-on: ubuntu-24.04-x86-32-cores permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable with: toolchain: nightly-2026-04-14 components: clippy - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: cache-on-failure: true - name: Verify legacy auth identity removal run: | if git grep -nE 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*"disabled"' \ -- lib/apps lib/components lib/foundation apps lib/packages docs/public/api-reference/fabro-api.yaml; then echo "::error::Legacy auth identities remain in the repository" exit 1 else status=$? if [ "$status" -ne 1 ]; then exit "$status" fi fi - run: cargo +nightly-2026-04-14 clippy --locked --workspace --all-targets -- -D warnings rustdoc: name: Rustdoc runs-on: ubuntu-24.04-x86-32-cores permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable with: toolchain: 1.97.1 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: cache-on-failure: true # Broken intra-doc links and the other rustdoc lints fail the build. - run: cargo doc --locked --workspace --no-deps env: RUSTDOCFLAGS: -D warnings generated-docs: name: Generated Docs runs-on: ubuntu-24.04-x86-32-cores permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable with: toolchain: 1.97.1 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: cache-on-failure: true - run: cargo --locked dev docs check test: name: Test (Linux) runs-on: ubuntu-24.04-x86-32-cores permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable with: toolchain: 1.97.1 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: cache-on-failure: true - uses: taiki-e/install-action@773334c0e05d7e699e4d78234494308223f3a2cf # nextest # Every Petri run takes its scope through a sandbox-driver plugin # executable that Petri finds on PATH: `sandbox-driver-host` for the # `local` provider, `sandbox-driver-docker` for `docker`. Installed # at the rev the workspace pins, so the plugins and the in-process # driver are one build; a from-source build, so the two executables # are cached by OS and rev and only rebuilt when the pin moves. - name: Read the sandbox-driver rev the workspace pins id: sandbox-driver run: | rev="$(sed -n 's/^sandbox-driver = { git = "[^"]*", rev = "\([0-9a-f]*\)" }$/\1/p' Cargo.toml)" test -n "$rev" echo "rev=$rev" >> "$GITHUB_OUTPUT" - name: Restore the sandbox-driver plugin executables id: sandbox-driver-cache uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ~/.cargo/bin/sandbox-driver-host ~/.cargo/bin/sandbox-driver-docker key: sandbox-driver-plugins-${{ runner.os }}-${{ steps.sandbox-driver.outputs.rev }} - name: Install the sandbox-driver plugin executables if: steps.sandbox-driver-cache.outputs.cache-hit != 'true' run: cargo install --locked --git https://github.com/lithoscomputer/sandbox-driver --rev "${{ steps.sandbox-driver.outputs.rev }}" sandbox-driver-host sandbox-driver-docker # The images the suite's Docker tests run. The plugin pulls a missing # image on first use, but a 1 GiB pull inside a run's wait is a flake, # so pull them here, where a registry problem reads as one. Most tests # leave the image to Petri, whose Docker scope runs on its default # runner image at the pin the checked-out Petri names; the # fabro-server catalog scenarios name CATALOG_IMAGE in # lib/apps/fabro-server/tests/it/scenario/petri.rs. - name: Pull the images the Docker tests run run: | backend="$(dirname "$(cargo metadata --locked --format-version 1 | jq -r '.packages[] | select(.name == "petri-executor-sandbox") | .manifest_path')")/src/backend.rs" pin="$(sed -n 's/^const RUNNER_PIN: &str = "\([0-9a-f]*\)";$/\1/p' "$backend")" test -n "$pin" docker pull "ghcr.io/lithoscomputer/ubuntu-24.04:slim-$pin" docker pull ghcr.io/lithoscomputer/ubuntu-22.04:slim - run: cargo nextest run --locked --workspace --status-level slow --profile ci # The twin-mode ignored suites this job once ran belonged to fabro-agent, # which pebble's coding agent replaced; the agent loop's workflow-level # tests run in the suite above, and pebble's own suite covers the loop. # Re-add a `--run-ignored only -E 'package(...)'` step here when a # package has ignored suites that are fully green in twin mode. sandbox-docker: name: Sandbox providers (Docker) runs-on: ubuntu-24.04-x86-32-cores permissions: contents: read env: # The Docker scenarios skip when the executable, the daemon or the # image is missing; in CI a skip is a failure. FABRO_REQUIRE_SANDBOX_PLUGINS: "1" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable with: toolchain: 1.97.1 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: cache-on-failure: true - uses: taiki-e/install-action@773334c0e05d7e699e4d78234494308223f3a2cf # nextest # The image the Docker scenarios' environment names, and Petri's # default runner image, which the fabro-petri Docker test runs. - run: docker pull buildpack-deps:noble - name: Pull Petri's default runner image run: | backend="$(dirname "$(cargo metadata --locked --format-version 1 | jq -r '.packages[] | select(.name == "petri-executor-sandbox") | .manifest_path')")/src/backend.rs" pin="$(sed -n 's/^const RUNNER_PIN: &str = "\([0-9a-f]*\)";$/\1/p' "$backend")" test -n "$pin" docker pull "ghcr.io/lithoscomputer/ubuntu-24.04:slim-$pin" # Every Petri run takes its scope through a sandbox-driver plugin # executable that Petri finds on PATH: `sandbox-driver-host` for the # `local` provider, `sandbox-driver-docker` for `docker`. Installed # at the rev the workspace pins, so the plugins and the in-process # driver are one build; a from-source build, so the two executables # are cached by OS and rev and only rebuilt when the pin moves. - name: Read the sandbox-driver rev the workspace pins id: sandbox-driver run: | rev="$(sed -n 's/^sandbox-driver = { git = "[^"]*", rev = "\([0-9a-f]*\)" }$/\1/p' Cargo.toml)" test -n "$rev" echo "rev=$rev" >> "$GITHUB_OUTPUT" - name: Restore the sandbox-driver plugin executables id: sandbox-driver-cache uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ~/.cargo/bin/sandbox-driver-host ~/.cargo/bin/sandbox-driver-docker key: sandbox-driver-plugins-${{ runner.os }}-${{ steps.sandbox-driver.outputs.rev }} - name: Install the sandbox-driver plugin executables if: steps.sandbox-driver-cache.outputs.cache-hit != 'true' run: cargo install --locked --git https://github.com/lithoscomputer/sandbox-driver --rev "${{ steps.sandbox-driver.outputs.rev }}" sandbox-driver-host sandbox-driver-docker # The workflow scenarios on the Docker provider. The scenarios are e2e # tests (ignored by default); the key-free ones run here, the # LLM-backed ones self-skip without credentials. - run: cargo nextest run --locked --profile ci --status-level slow --run-ignored only -p fabro-cli --test it -E 'test(/::docker_/)' # The Petri runs (not ignored: they skip without the host plugin, which # the environment above forbids). - run: cargo nextest run --locked --profile ci --status-level slow -p fabro-petri test-macos: name: Test (macOS) if: github.event_name == 'workflow_dispatch' runs-on: macos-15 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable with: toolchain: 1.97.1 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: cache-on-failure: true - uses: taiki-e/install-action@773334c0e05d7e699e4d78234494308223f3a2cf # nextest # Every Petri run takes its scope through a sandbox-driver plugin # executable that Petri finds on PATH: `sandbox-driver-host` for the # `local` provider, `sandbox-driver-docker` for `docker`. Installed # at the rev the workspace pins, so the plugins and the in-process # driver are one build; a from-source build, so the two executables # are cached by OS and rev and only rebuilt when the pin moves. - name: Read the sandbox-driver rev the workspace pins id: sandbox-driver run: | rev="$(sed -n 's/^sandbox-driver = { git = "[^"]*", rev = "\([0-9a-f]*\)" }$/\1/p' Cargo.toml)" test -n "$rev" echo "rev=$rev" >> "$GITHUB_OUTPUT" - name: Restore the sandbox-driver plugin executables id: sandbox-driver-cache uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ~/.cargo/bin/sandbox-driver-host ~/.cargo/bin/sandbox-driver-docker key: sandbox-driver-plugins-${{ runner.os }}-${{ steps.sandbox-driver.outputs.rev }} - name: Install the sandbox-driver plugin executables if: steps.sandbox-driver-cache.outputs.cache-hit != 'true' run: cargo install --locked --git https://github.com/lithoscomputer/sandbox-driver --rev "${{ steps.sandbox-driver.outputs.rev }}" sandbox-driver-host sandbox-driver-docker # No Docker daemon on the macOS runner: the Docker tests skip there. - run: cargo nextest run --locked --workspace --status-level slow --profile ci