name: smart-byte conformance gate # One run per branch at a time. A push supersedes the run before it. # # There is one `macos` runner and it is the dev Mac, so every macOS job in this # repo queues on it. With no concurrency group, a session pushing four versions # of a branch queued four complete sets of runs and every one of them had to # drain: on 2026-10-05 a PR's 21 contexts settled none in 70 minutes behind a # backlog of 25 runs, and the queue grew faster than it cleared. Only the newest # commit's verdict is wanted, so the older runs are cancelled. # # Verified on this forge (Forgejo 16.0.5) in `ci-test` before being added here: # the key parses, and a newer push moves an older run to `cancelled` even while # it is still queued, which is the case that matters. # # `cancel-in-progress: true` also applies to `main`. That is deliberate and # safe here: main advances by merge commits whose tree contains the commits # before them, so the newer run covers the older one's changes. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: pull_request: paths: - 'smart-byte/spec/**' - 'smart-byte/conformance/**' - '.github/workflows/conformance.yml' push: branches: - main paths: - 'smart-byte/spec/**' - 'smart-byte/conformance/**' - '.github/workflows/conformance.yml' jobs: # Does this pull request's own diff touch the paths below? This forge matches a # workflow's `paths:` against the push, so a merge of main into a branch fires the # workflow whatever the pull request changes; every job here waits on this answer. # A push to main, a schedule, or anything the script cannot determine answers # `true` (scripts/pr-touches-paths.py). relevant: name: does this change touch it runs-on: gate outputs: run: ${{ steps.gate.outputs.run }} steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - id: gate run: | set -euo pipefail r=$(python3 scripts/pr-touches-paths.py .github/workflows/conformance.yml \ "${{ github.event.pull_request.base.sha }}" \ "${{ github.event.pull_request.head.sha }}") echo "this change touches conformance: $r" echo "run=$r" >> "$GITHUB_OUTPUT" validate-json: needs: relevant if: needs.relevant.outputs.run == 'true' name: Validate conformance JSON files runs-on: linux steps: - name: Checkout uses: actions/checkout@v4 - name: Ensure jq is available run: jq --version - name: Validate every JSON file under smart-byte/conformance/v* run: | set -euo pipefail found=0 fail=0 while IFS= read -r -d '' f; do found=$((found+1)) if ! jq empty "$f" >/dev/null 2>&1; then echo "::error file=$f::invalid JSON" fail=$((fail+1)) else echo "ok: $f" fi done < <(find smart-byte/conformance -type f -name '*.json' -print0) if [ "$found" -eq 0 ]; then echo "::error::no JSON files found under smart-byte/conformance/v*" exit 1 fi if [ "$fail" -ne 0 ]; then echo "::error::$fail JSON file(s) failed validation" exit 1 fi echo "validated $found JSON file(s)" - name: Validate vector schema (top-level array, name field present) run: | set -euo pipefail fail=0 while IFS= read -r -d '' f; do if ! jq -e 'type == "array"' "$f" >/dev/null; then echo "::error file=$f::top-level value must be a JSON array" fail=$((fail+1)) continue fi if ! jq -e 'all(.[]; has("name") and has("description"))' "$f" >/dev/null; then echo "::error file=$f::every vector must have name and description fields" fail=$((fail+1)) fi done < <(find smart-byte/conformance -type f -name '*_vectors.json' -print0) if [ "$fail" -ne 0 ]; then echo "::error::$fail vector file(s) failed schema validation" exit 1 fi - name: Validate vector names are unique within each file run: | set -euo pipefail fail=0 while IFS= read -r -d '' f; do dupes=$(jq -r '[.[].name] | group_by(.) | map(select(length>1)) | .[][0] // empty' "$f") if [ -n "$dupes" ]; then echo "::error file=$f::duplicate vector name(s): $dupes" fail=$((fail+1)) fi done < <(find smart-byte/conformance -type f -name '*_vectors.json' -print0) if [ "$fail" -ne 0 ]; then exit 1 fi # The pack's values come from the Rust reference implementation, and # standards-build checks them against it under `cargo test`. This job checks # them against a second implementation, written separately in another # language, so a mistake shared by the generator and its own verifier still # fails here. Standard library only: nothing to install beyond python3. crosscheck: needs: relevant if: needs.relevant.outputs.run == 'true' name: Second implementation reproduces every vector runs-on: linux steps: - name: Checkout uses: actions/checkout@v4 - name: Ensure python3 is available shell: bash run: | if ! command -v python3 >/dev/null 2>&1; then SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo" $SUDO apt-get update -qq $SUDO apt-get install -y -qq python3 fi python3 --version - name: Cross-check v1 run: python3 smart-byte/conformance/crosscheck/verify_v1.py smart-byte/conformance/v1 spec-vector-coupling: needs: relevant if: needs.relevant.outputs.run == 'true' && (github.event_name == 'pull_request') name: Spec changes require vector-pack updates runs-on: linux steps: - name: Checkout (with full history for diff) uses: actions/checkout@v4 with: fetch-depth: 0 - name: Determine the PR base id: base run: | base="${{ github.event.pull_request.base.sha }}" if [ -z "$base" ]; then base="origin/main" fi echo "base=$base" >> "$GITHUB_OUTPUT" - name: Enforce coupling between spec and conformance changes run: | set -euo pipefail base="${{ steps.base.outputs.base }}" head="${{ github.event.pull_request.head.sha }}" if [ -z "$head" ]; then head="HEAD" fi changed=$(git diff --name-only "$base" "$head") echo "Files changed in this PR:" echo "$changed" spec_touched=0 conformance_touched=0 if echo "$changed" | grep -qE '^smart-byte/spec/'; then spec_touched=1 fi if echo "$changed" | grep -qE '^smart-byte/conformance/'; then conformance_touched=1 fi if [ "$spec_touched" -eq 1 ] && [ "$conformance_touched" -eq 0 ]; then echo "::error::This PR modifies smart-byte/spec/ without modifying smart-byte/conformance/." echo "::error::Per the conformance social contract, every spec change must be accompanied by a vector-pack update." echo "::error::If the spec change is editorial and the wire format is unchanged, record the editorial reason in smart-byte/conformance/v/regeneration.md and include that file in the PR." exit 1 fi echo "coupling check passed (spec_touched=$spec_touched, conformance_touched=$conformance_touched)" versions-present: needs: relevant if: needs.relevant.outputs.run == 'true' name: VERSIONS.md and README.md must exist runs-on: linux steps: - name: Checkout uses: actions/checkout@v4 - name: Required pack metadata files run: | set -euo pipefail missing=0 for f in smart-byte/conformance/README.md smart-byte/conformance/VERSIONS.md; do if [ ! -f "$f" ]; then echo "::error file=$f::missing required pack metadata file" missing=$((missing+1)) fi done for d in smart-byte/conformance/v*/ ; do if [ ! -f "$d/regeneration.md" ]; then echo "::error file=$d/regeneration.md::missing regeneration.md for $d" missing=$((missing+1)) fi done if [ "$missing" -ne 0 ]; then exit 1 fi