Skip to content

ci: verify the client's wire shapes against the committed openapi.json #31

ci: verify the client's wire shapes against the committed openapi.json

ci: verify the client's wire shapes against the committed openapi.json #31

Workflow file for this run

name: CI
on:
push:
branches: [main, dev]
pull_request:
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- name: fmt
run: cargo fmt --all --check
- name: clippy
run: cargo clippy --all-targets -- -D warnings
- name: build
run: cargo build --locked --verbose
# Includes tests/openapi_conformance.rs, the SHAPE gate: it loads the committed openapi.json
# and checks every serde type in src/client.rs against its schema (field set, required vs
# nullable, round trip, endpoint wiring). The spec-drift job below only compares a version
# STRING, so this is the check that fails when a struct drifts from the spec in the same
# commit. Keep `cargo test` in this workflow: it is the only thing running that gate.
- name: test
run: cargo test --locked --verbose
integration:
# BIDIRECTIONAL TESTING (matches how the plugin repos test themselves against busbar): download
# the latest RELEASED busbar, boot it, and drive EVERY busbar-admin command against it, asserting
# real effects. Catches client drift a `cargo build` never would — a renamed field, a changed
# status code, a broken auth header. (busbar core's own dev-gate runs the SAME scripts/
# integration.sh against the freshly-built engine, the reverse direction.)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build busbar-admin
run: cargo build --release --locked
- name: Download the latest released busbar (linux x86_64)
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
gh release download --repo GetBusbar/busbar \
--pattern 'busbar-x86_64-unknown-linux-gnu.tar.gz' --dir /tmp/busbar-dl
tar -xzf /tmp/busbar-dl/busbar-x86_64-unknown-linux-gnu.tar.gz -C /tmp/busbar-dl
chmod +x /tmp/busbar-dl/busbar
/tmp/busbar-dl/busbar --version
- name: Drive every command against the real gateway
run: scripts/integration.sh /tmp/busbar-dl/busbar target/release/busbar-admin
spec-mirror:
# THE SHAPE OF THE SPEC ITSELF, which nothing else in this repo checks.
#
# Three questions, three different gates, and it is worth being explicit about which is which:
# 1. "Does src/client.rs match the committed openapi.json?" -> tests/openapi_conformance.rs
# (in the build job). Rust vs its OWN copy of the spec.
# 2. "Is the committed spec's VERSION STRING current?" -> the spec-drift job below.
# 3. "Is the committed spec the same DOCUMENT as core's?" -> this job. Nothing answered it.
#
# Question 2 is not a substitute for question 3, and assuming it was is what shipped: a mirror
# can carry core's exact info.version and still be missing properties, because the version
# string is copied along with everything else and says nothing about what was left behind. So
# this job never looks at the version. It walks components.schemas property-by-property and
# paths method-by-method and names every schema and property that differs.
#
# And it FAILS CLOSED. The spec-drift job below prints ::warning:: and exits 0 when the GitHub
# API is unreachable, which means a rate-limited runner reports "no drift" without having
# looked. This job exits non-zero instead. An unknown is not a pass.
#
# REF: latest-release, not a branch. This client is built and integration-tested against the
# latest RELEASED busbar (see the integration job, which downloads exactly that), so the spec
# it commits must be the released engine's spec. Tracking a branch here would make the client
# document endpoints and fields no released busbar serves.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
# SELF-TEST FIRST, then the verdict. Fleet convention, and the whole reason this job exists:
# an assertion nobody has watched fail is indistinguishable from `exit 0`. This proves the
# gate goes RED on a missing schema, a missing property, an extra property, a changed
# required set, a changed enum, a changed type, and a core spec it could not fetch or parse.
- name: spec-mirror gate self-test (red before green)
run: python3 scripts/spec_mirror_gate.py --selftest
- name: committed openapi.json is the same document as busbar core's
env:
GH_TOKEN: ${{ github.token }}
run: python3 scripts/spec_mirror_gate.py --mirror openapi.json --busbar-ref latest-release
spec-drift:
# The admin client is hand-rolled against openapi.json (committed at the repo root).
# This job answers "is the committed spec CURRENT?" only. Whether src/client.rs still MATCHES
# that spec is a separate question, answered by tests/openapi_conformance.rs in the build job.
# Fail when the latest busbar release ships a NEWER spec version than the one committed,
# so drift is visible instead of silent. Degrades to a warning if the GitHub API is
# unavailable or rate-limited (keeps the check non-flaky).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: compare committed spec version to the latest busbar release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -u
committed=$(jq -r .info.version openapi.json)
echo "committed spec version: $committed"
resp=$(curl -sS --max-time 30 \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/GetBusbar/busbar/releases/latest) || {
echo "::warning::could not reach the GitHub API to check the latest busbar release; skipping spec-drift check"
exit 0
}
latest=$(echo "$resp" | jq -r '.tag_name // empty' | sed 's/^v//')
if [ -z "$latest" ]; then
echo "::warning::GitHub API returned no usable release tag (rate-limited?); skipping spec-drift check"
echo "$resp" | head -c 500
exit 0
fi
echo "latest busbar release: $latest"
if [ "$committed" != "$latest" ]; then
echo "::error::committed openapi.json is v$committed but the latest busbar release is v$latest — refresh the spec and re-audit src/client.rs"
exit 1
fi
echo "spec is current (v$committed)"