# CI/CD Integration *Gate your PRs on semantic proofs, not just green tests.* ## Overview Tests prove that code runs. `aura prove` proves that code *does the thing you said it would do*. `aura pr-review` proves that the code change matches its stated intent. Running both in CI raises your merge gate from "tests pass" to "tests pass **and** the change is semantically consistent with its intent." Aura runs in any CI: - GitHub Actions (covered in [GitHub integration](/github-integration)) - GitLab CI (covered in [GitLab integration](/gitlab-integration)) - CircleCI - Jenkins - Buildkite - Drone - self-hosted TeamCity, Bamboo, Azure Pipelines This page covers generic CI patterns: exit codes, caching, containerization, and how to make Aura a hard gate without slowing the pipeline. ## Setup ### Binary distribution A single static binary. Install in any Linux/macOS container: ```bash curl -fsSL https://aura.build/install.sh | sh ``` Or pin a version: ```bash curl -fsSL https://aura.build/install.sh | sh -s -- --version 0.14.1 ``` Or use the official image: ``` ghcr.io/naridon-inc/aura:0.14.1 ghcr.io/naridon-inc/aura:latest ``` The image is ~40 MB, Alpine-based, and contains `aura` + git. It is designed as a CI image, not a dev image. ### Shadow branches CI needs shadow branches to compute semantic diffs. Fetch them before running Aura: ```bash git fetch origin '+refs/heads/aura/*:refs/remotes/origin/aura/*' ``` If shadow branches don't exist yet, `aura prove` degrades gracefully — it computes what it can from the current working tree and warns about missing history. ## Exit Codes Every Aura CI-relevant command returns deterministic exit codes so pipelines can branch on them. ### `aura prove` | Code | Meaning | Typical action | |------|---------|----------------| | 0 | All goals proved. | Pass. | | 1 | Some goals failed. | Fail the build. | | 2 | No goals logged for this branch. | Warn or pass (configurable). | | 3 | Engine error (missing shadow, corrupt index). | Fail with alert to platform team. | | 4 | Timeout. | Retry once, then fail. | ### `aura pr-review` | Code | Meaning | |------|---------| | 0 | No violations at or above `fail_on` threshold. | | 1 | Violations found. | | 2 | Intent not logged. | | 3 | Engine error. | ### `aura doctor` | Code | Meaning | |------|---------| | 0 | Healthy. | | 1 | Warnings (missing hooks, stale snapshots). | | 2 | Errors (corrupt DB, orphaned sessions). | Exit codes are stable across versions. CI scripts can rely on them. ## Configuration ### CircleCI `.circleci/config.yml`: ```yaml version: 2.1 jobs: aura: docker: - image: ghcr.io/naridon-inc/aura:latest steps: - checkout - run: name: Fetch shadow branches command: git fetch origin '+refs/heads/aura/*:refs/remotes/origin/aura/*' - run: name: aura prove command: aura prove --base origin/main - run: name: aura pr-review command: aura pr-review --base origin/main --format markdown --output review.md - store_artifacts: path: review.md workflows: review: jobs: - aura: filters: branches: ignore: main ``` ### Jenkins ```groovy pipeline { agent { docker { image 'ghcr.io/naridon-inc/aura:latest' } } stages { stage('Shadow') { steps { sh "git fetch origin '+refs/heads/aura/*:refs/remotes/origin/aura/*'" } } stage('Prove') { steps { sh 'aura prove --base origin/main' } } stage('Review') { steps { sh 'aura pr-review --base origin/main --format markdown --output review.md' archiveArtifacts artifacts: 'review.md' } } } post { failure { script { def review = readFile 'review.md' // Post to Slack / PR / etc. } } } } ``` ### Buildkite ```yaml steps: - label: "aura" command: - git fetch origin '+refs/heads/aura/*:refs/remotes/origin/aura/*' - aura prove --base origin/$BUILDKITE_PULL_REQUEST_BASE_BRANCH - aura pr-review --base origin/$BUILDKITE_PULL_REQUEST_BASE_BRANCH plugins: - docker#v5.0.0: image: "ghcr.io/naridon-inc/aura:latest" if: build.pull_request.id != null ``` ### Azure Pipelines ```yaml steps: - task: Docker@2 inputs: command: run arguments: > -v $(Build.SourcesDirectory):/src -w /src ghcr.io/naridon-inc/aura:latest sh -c "git fetch origin '+refs/heads/aura/*:refs/remotes/origin/aura/*' && aura prove --base origin/$(System.PullRequest.TargetBranch) && aura pr-review --base origin/$(System.PullRequest.TargetBranch)" ``` ## Caching Aura's AST index is deterministic given the commit SHA. Cache it between pipeline runs for a 5–10x speedup on large repos: ```yaml # GitHub Actions example, reusable pattern - uses: actions/cache@v4 with: path: .aura/index key: aura-index-${{ hashFiles('**/*.rs', '**/*.ts', '**/*.py') }} restore-keys: aura-index- ``` Cache keys should include every source-file extension Aura indexes. The cache is invalidated on any source change, which is correct — the index must match the tree. ## Examples ### Hard gate Fail the build if `aura prove` fails or if PR review finds `error`-level violations. This is the recommended baseline: ```bash aura prove --base origin/main || exit 1 aura pr-review --base origin/main --fail-on error || exit 1 ``` ### Soft gate (advisory mode) For teams rolling out Aura incrementally: ```bash aura prove --base origin/main || echo "::warning::aura prove failed (advisory)" aura pr-review --base origin/main --fail-on never ``` This runs the checks and posts the report, but never fails the build. ### Parallel pipelines `aura prove` and `aura pr-review` share an AST parse step. Run them in the same job to reuse it. If you must split them across jobs, share the cached index: ```bash # Job 1 aura index --output .aura/index.bin # Job 2 (depends on Job 1) aura prove --index .aura/index.bin # Job 3 (depends on Job 1) aura pr-review --index .aura/index.bin ``` ### Gating on a specific goal ```bash aura prove "Checkout flow redirects to success page on completed payment" ``` Useful for "I care about this one behavior this PR must preserve" gates. Multiple `--goal` flags are allowed. ### Intent-required enforcement Reject PRs that never called `aura_log_intent`: ```bash aura pr-review --require-intent --fail-on error ``` Exit code 2 is returned if no intent exists; turn it into a failure with `--require-intent`. ## Troubleshooting **Pipeline is slow.** Enable caching of `.aura/index`. Expect 30s–2m parse time without cache on mid-sized (~100K LOC) repos; <10s with cache. **`aura prove` returns 2 for every PR.** No intents are being logged. The pre-commit hook isn't firing locally. See [pre-commit hook](/pre-commit-hook-explained). As an interim, enable `--require-intent` only for `main` merges, not every PR. **Shadow branch fetch fails.** The CI bot user lacks read access to `aura/*` refs. Either widen the token scope or push shadow branches to a mirror repo. **Different results on CI vs local.** Almost always a version mismatch. Pin `aura` in CI (`--version 0.14.1`) and locally. **Timeouts on very large repos.** Set `AURA_PARSE_TIMEOUT=300` (seconds) and consider partitioning: `aura prove --scope apps/web`. Aura supports scope-restricted runs that parse only a subtree. ## Running Aura in Matrix Jobs If your CI runs a matrix (multiple OSes, multiple language versions), avoid running Aura once per matrix cell. Semantic analysis is platform-independent — running it N times wastes N-1 runs and risks races on shadow-branch pushes. Run Aura in a single dedicated job, gated on the matrix jobs' success: ```yaml jobs: test: strategy: matrix: { os: [ubuntu-latest, macos-latest, windows-latest] } steps: [ ... your tests ... ] aura: needs: test runs-on: ubuntu-latest steps: [ ... aura commands ... ] ``` This pattern keeps the semantic gate deterministic and fast. ## Reporting and Metrics in CI Every `aura prove` and `aura pr-review` run emits a machine-readable summary to `.aura/ci-summary.json`. Archive it as an artifact to build dashboards: ```json { "run_id": "run_01HW3K...", "branch": "feat/login", "base": "main", "timestamp": "2026-04-21T14:23:09Z", "prove": { "goals": 4, "passed": 4, "failed": 0, "duration_ms": 1823 }, "review": { "findings": { "error": 0, "warning": 2, "info": 5 }, "nodes_changed": 12, "duration_ms": 2410 } } ``` Feed this into your observability stack (Datadog, Grafana) to track CI-side semantic health over time: average Prove duration, error-finding rate per team, node churn. ## Authoring a Minimal CI Wrapper If your CI isn't covered above, the generic pattern is three commands: ```bash git fetch origin '+refs/heads/aura/*:refs/remotes/origin/aura/*' aura prove --base origin/main aura pr-review --base origin/main --format markdown --output review.md ``` Whatever CI you are on, reproduce those three lines in its syntax. Every other integration detail — annotations, artifacts, merge gates — is a nice-to-have on top of this minimum. ## Self-Hosted Runners For self-hosted GitHub Actions or GitLab runners, pre-install Aura in the base AMI/image. This saves the install step on every job and makes the runner's version stable. The official image `ghcr.io/naridon-inc/aura` is built reproducibly and has SBOM attestations published to the same registry. If your org policy forbids pulling images from public registries, mirror the release tarball internally: ```bash curl -fsSLO https://aura.build/releases/0.14.1/aura-x86_64-linux.tar.gz sha256sum -c aura-x86_64-linux.tar.gz.sha256 ``` All releases are published with Sigstore-signed SHA256 manifests. ## Security Considerations in CI A few practices worth enforcing: - **Scope the CI token.** `AURA_TOKEN` in CI should have `read_intents`, `write_impacts`, and `read_snapshots` — nothing broader. Avoid giving CI `write_messages` unless you intentionally want CI to post into team chats. - **Redact in logs.** Set `AURA_LOG_REDACT=true` to strip sensitive fields from daemon logs written to CI artifacts. - **Don't cache secrets in `.aura/`.** The cache directory is for the AST index only. Aura never writes tokens there, but a misconfigured hook could; a `.gitignore` entry of `.aura/secrets*` belongs in every repo. ## Monorepo Strategies Large monorepos benefit from two patterns: 1. **Scoped Prove runs.** Run `aura prove --scope apps/web` in each app's pipeline rather than one mega-run. Each team owns its Prove goals and sees only its failures. 2. **Shared index cache.** One cache key per language — `aura-index-rust-${hashFiles('**/*.rs')}` — so the Rust app's cache doesn't invalidate when TS changes. ## See Also - [GitHub integration](/github-integration) - [GitLab integration](/gitlab-integration) - [Pre-commit hook](/pre-commit-hook-explained) - [Semantic diff](/semantic-diff) - [Custom plugins](/custom-plugins) — drive your own CI steps through the plugin API.