detached table commits replace recovery sidecars #430
Workflow file for this run
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: GQ Logic Tests | |
| on: | |
| # Code-bearing events only. The body and label events belong to | |
| # `fix-regression-gate.yml`, which builds nothing; listing them here | |
| # would re-run this Rust build on every label change. | |
| pull_request: | |
| types: | |
| - opened | |
| - synchronize | |
| - reopened | |
| # `GQ Logic Tests` is a required context, so it must also report on the | |
| # merge queue's temporary branch (docs/dev/branch-protection.md, Merge queue). | |
| merge_group: | |
| types: [checks_requested] | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| concurrency: | |
| group: gq-logic-tests-${{ github.ref }} | |
| # Superseded PR runs are safe to cancel; post-merge main runs stay alive. | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| # A job cannot depend on another workflow's job, so this is a verbatim | |
| # copy of `ci.yml`'s `classify_changes`; `ci.yml` is the source of truth, | |
| # and an unconditional step at the top of the test job, right after | |
| # checkout, asserts every copy stays identical (the copies are discovered | |
| # by `scripts/check-classify-copy.py`, which globs the workflows). | |
| classify_changes: | |
| name: Classify Changes (GQ Logic Tests) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| outputs: | |
| run_full_ci: ${{ steps.filter.outputs.run_full_ci }} | |
| run_gqt: ${{ steps.filter.outputs.run_gqt }} | |
| run_deployment: ${{ steps.filter.outputs.run_deployment }} | |
| steps: | |
| - name: Checkout source | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Classify changed paths | |
| id: filter | |
| env: | |
| BEFORE_SHA: ${{ github.event.before }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} | |
| MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} | |
| PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| REF_TYPE: ${{ github.ref_type }} | |
| run: | | |
| set -euo pipefail | |
| # No diff to classify (dispatch, tag, unknown base, empty diff): | |
| # every job runs. | |
| run_everything() { | |
| printf 'run_full_ci=true\nrun_gqt=true\nrun_deployment=true\n' >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| } | |
| if [[ "$EVENT_NAME" == "workflow_dispatch" || "$REF_TYPE" == "tag" ]]; then | |
| run_everything | |
| fi | |
| if [[ "$EVENT_NAME" == "pull_request" ]]; then | |
| base="$PR_BASE_SHA" | |
| head="$PR_HEAD_SHA" | |
| elif [[ "$EVENT_NAME" == "merge_group" ]]; then | |
| base="$MERGE_GROUP_BASE_SHA" | |
| head="$MERGE_GROUP_HEAD_SHA" | |
| else | |
| base="$BEFORE_SHA" | |
| head="$GITHUB_SHA" | |
| if [[ "$base" == "0000000000000000000000000000000000000000" ]]; then | |
| base="$(git rev-parse "${head}^" 2>/dev/null || true)" | |
| fi | |
| fi | |
| if [[ -z "${base:-}" ]]; then | |
| run_everything | |
| fi | |
| # Merge base (a branch behind main must not inherit its newer files), | |
| # no rename collapsing (a source file moved into docs keeps its | |
| # deletion), no path quoting (a non-ASCII name reaches the arms raw). | |
| mapfile -t changed < <(git -c core.quotePath=false diff --name-only --no-renames --merge-base "$base" "$head") | |
| if [[ "${#changed[@]}" -eq 0 ]]; then | |
| run_everything | |
| fi | |
| run_full_ci=false | |
| run_gqt=false | |
| run_deployment=false | |
| for path in "${changed[@]}"; do | |
| case "$path" in | |
| docs/*.md|docs/*.mdx|docs/*.rst|docs/*.adoc) ;; | |
| README.md|AGENTS.md|CLAUDE.md|CHANGELOG.md|CONTRIBUTING.md|CODE_OF_CONDUCT.md) ;; | |
| LICENSE|LICENSE.md) ;; | |
| crates/omnigraph-gqt/cases/*.gqt) run_gqt=true ;; | |
| Dockerfile|.dockerignore|docker/*|deploy/*) run_deployment=true ;; | |
| *) | |
| run_full_ci=true | |
| ;; | |
| esac | |
| done | |
| if [[ "$run_full_ci" == "true" ]]; then | |
| run_gqt=true | |
| run_deployment=true | |
| fi | |
| printf 'Changed files:\n' | |
| printf ' %s\n' "${changed[@]}" | |
| printf 'run_full_ci=%s\nrun_gqt=%s\nrun_deployment=%s\n' \ | |
| "$run_full_ci" "$run_gqt" "$run_deployment" | tee -a "$GITHUB_OUTPUT" | |
| qualification: | |
| name: GQT (${{ matrix.mode }}) | |
| needs: classify_changes | |
| runs-on: ubuntu-latest | |
| # Cold-build budget and observations: docs/dev/ci.md, GQ Logic Tests. | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| mode: [ordinary, dst, engine-v2, dst-clippy] | |
| permissions: | |
| contents: read | |
| env: | |
| CARGO_TERM_COLOR: always | |
| # Same floor as the Test Workspace job: unoptimized builds keep every | |
| # nested async engine frame, deeper than a default 2 MiB thread stack. | |
| RUST_MIN_STACK: 16777216 | |
| steps: | |
| # Check the classification copies on skipped changes too. | |
| - name: Checkout source | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 | |
| - name: Assert the classification copies match ci.yml | |
| run: python3 scripts/check-classify-copy.py | |
| # No GQT input changed (engine code or a `.gqt` case): report success | |
| # without building, so the required context is never left pending. | |
| - name: Skip when no GQT input changed | |
| if: needs.classify_changes.outputs.run_gqt != 'true' | |
| run: echo "No GQT input changed; skipping the GQ logic tests." | |
| - name: Install system dependencies | |
| if: needs.classify_changes.outputs.run_gqt == 'true' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y protobuf-compiler libprotobuf-dev | |
| # The `rust-toolchain.toml` pin, not a floating `stable` action (see | |
| # the `fmt` job in ci.yml): rustc is part of the cache key. | |
| - name: Install pinned toolchain | |
| if: needs.classify_changes.outputs.run_gqt == 'true' | |
| run: rustup toolchain install | |
| - name: Cache Rust build data | |
| if: needs.classify_changes.outputs.run_gqt == 'true' | |
| uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| workspaces: | | |
| . -> target | |
| key: gqt-${{ matrix.mode }} | |
| # Save only from main, red or green (same rule as ci.yml's `lint`). | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| cache-on-failure: true | |
| - name: Test unavailable DST runtime refusal | |
| if: needs.classify_changes.outputs.run_gqt == 'true' && matrix.mode == 'ordinary' | |
| # The flagless build shape this test exists for: an env RUSTFLAGS, | |
| # empty included, replaces the workspace `[build] rustflags`. | |
| env: | |
| RUSTFLAGS: "" | |
| run: cargo test -p omnigraph-gqt --locked --timings --lib --test runner_dispatch -- --nocapture | |
| # A case's `at:` name arms a seam in the seam guard, so a cases-only | |
| # diff can change its verdict while `Test Workspace` is skipped. | |
| - name: Run the seam guard the case corpus arms | |
| if: needs.classify_changes.outputs.run_gqt == 'true' && matrix.mode == 'ordinary' | |
| env: | |
| RUSTFLAGS: "" | |
| run: cargo test -p omnigraph-seams --locked --timings --test failpoint_names_guard | |
| # The workspace .cargo/config.toml enables the seeded Tokio runtime | |
| # from any directory; these steps run from the repo root. | |
| - name: Run complete GQ logic tests with DST | |
| if: needs.classify_changes.outputs.run_gqt == 'true' && matrix.mode == 'dst' | |
| run: cargo test -p omnigraph-gqt --locked --timings -- --nocapture | |
| # The same corpus on the plan route (RFC 0068 milestone 3): every | |
| # embedded-engine case must answer the same rows on both routes. | |
| - name: Run complete GQ logic tests on engine v2 | |
| if: needs.classify_changes.outputs.run_gqt == 'true' && matrix.mode == 'engine-v2' | |
| env: | |
| OMNIGRAPH_GQ_ENGINE: v2 | |
| run: cargo test -p omnigraph-gqt --locked --timings -- --nocapture | |
| - name: Clippy GQT with DST | |
| if: needs.classify_changes.outputs.run_gqt == 'true' && matrix.mode == 'dst-clippy' | |
| run: cargo clippy -p omnigraph-gqt --all-targets --locked --timings -- -D warnings -W clippy::dbg_macro | |
| - name: Retain GQT invocation reports | |
| if: always() && needs.classify_changes.outputs.run_gqt == 'true' && matrix.mode != 'dst-clippy' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: gqt-invocation-reports-${{ matrix.mode }} | |
| path: target/gqt-artifacts/*.json | |
| if-no-files-found: error | |
| - name: Retain Cargo build timings | |
| if: always() && needs.classify_changes.outputs.run_gqt == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: gqt-build-timings-${{ matrix.mode }} | |
| path: target/cargo-timings/*.html | |
| if-no-files-found: warn | |
| gq_logic_tests: | |
| name: GQ Logic Tests | |
| needs: [classify_changes, qualification] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: {} | |
| steps: | |
| - name: Require all GQT qualification jobs | |
| env: | |
| CLASSIFICATION_RESULT: ${{ needs.classify_changes.result }} | |
| QUALIFICATION_RESULT: ${{ needs.qualification.result }} | |
| run: | | |
| if [[ "$CLASSIFICATION_RESULT" != "success" || "$QUALIFICATION_RESULT" != "success" ]]; then | |
| echo "GQT qualification failed: classification=$CLASSIFICATION_RESULT qualification=$QUALIFICATION_RESULT" | |
| exit 1 | |
| fi |