Skip to content

ci: add schema drift check for resource JSON Schemas - #309

Merged
moonming merged 1 commit into
mainfrom
ci/schema-drift-check
May 17, 2026
Merged

ci: add schema drift check for resource JSON Schemas#309
moonming merged 1 commit into
mainfrom
ci/schema-drift-check

Conversation

@moonming

@moonming moonming commented May 17, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #308. Base will switch to main once #308 merges.

Summary

Adds a schema-drift job to .github/workflows/ci.yml that:

  1. Runs cargo run -p aisix-core --bin dump-schema
  2. Asserts git diff --exit-code schemas/ is clean

PRs that modify a resource struct in crates/aisix-core/src/models/ but forget to regenerate the schema files now fail CI with a fix instruction in the error message:

::error::Resource JSON Schemas in 'schemas/resources/' drift from the Rust types in 'crates/aisix-core/src/models/'.
::error::Fix: run 'cargo run -p aisix-core --bin dump-schema' locally and commit the diff.

Why

Refs #304 item #1. The dump-schema tool and the nine schemas/resources/*.schema.json files landed in #308. Without an enforcement mechanism the committed schemas can silently diverge from the Rust types as the resource graph evolves — especially relevant during issue #302 Phase A, which is actively mutating ProviderKey and Model. This job is that enforcement.

Job placement

Sits as a peer to lint — fast, independent, no service dependencies. Runs in parallel with lint, rust-unit, and build-bin. Not a needs: target of any downstream job, so a drift failure does not block e2e or coverage signals.

Verification

Positive path:

  • cargo run -p aisix-core --bin dump-schema on HEAD succeeds
  • git diff --exit-code schemas/ is empty (no drift in tree)

Negative path (proving the check actually catches drift):

  • Locally truncated schemas/resources/api_key.schema.json to {}
  • git diff --exit-code schemas/ returned non-zero ✓
  • Reverted with git checkout schemas/resources/api_key.schema.json
  • git diff --exit-code schemas/ clean again ✓

Workflow file:

  • python3 -c "import yaml; yaml.safe_load(open('.github/workflows/ci.yml'))" parses successfully

Diff (20 added lines)

  schema-drift:
    name: schema drift (resources)
    runs-on: ubicloud-standard-2
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: arduino/setup-protoc@v3
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: Swatinem/rust-cache@v2
      - name: regenerate schemas
        run: cargo run -p aisix-core --bin dump-schema
      - name: assert no drift
        run: |
          if ! git diff --exit-code schemas/; then
            echo "::error::Resource JSON Schemas in 'schemas/resources/' drift from the Rust types in 'crates/aisix-core/src/models/'."
            echo "::error::Fix: run 'cargo run -p aisix-core --bin dump-schema' locally and commit the diff."
            exit 1
          fi

Stack

Builds on #308 (binary + initial schemas), which builds on #307 (JsonSchema derives). Merge order: #307#308 → this PR.

Refs #304 (#1).

Summary by CodeRabbit

Release Notes

  • New Features

    • Generated canonical JSON Schema files for core resource types (ApiKey, CachePolicy, Guardrail, Model, ObservabilityExporter, ProviderKey, RateLimit, RateLimitPolicy, Routing), providing structured documentation of configuration shapes and constraints.
  • Chores

    • Added automated schema validation to CI to ensure schema definitions remain synchronized with resource definitions.

Review Change Stack

Copilot AI review requested due to automatic review settings May 17, 2026 01:07
@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@moonming has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 32 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: faff1c06-852e-47a5-b3e5-1c5730c5c88c

📥 Commits

Reviewing files that changed from the base of the PR and between 16a7ddb and ddefbd5.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml
📝 Walkthrough

Walkthrough

This PR enables automated JSON Schema generation for aisix-core resource model types. It adds a dump-schema CLI binary powered by the schemars crate, integrates schema drift checking into CI, adds JsonSchema derives to 30+ Rust types across 9 model files, and commits the resulting canonical schema files.

Changes

Schema Generation Infrastructure and Output

Layer / File(s) Summary
Dependency, CLI binary, and CI integration
crates/aisix-core/Cargo.toml, crates/aisix-core/src/bin/dump-schema.rs, .github/workflows/ci.yml, schemas/README.md
Adds schemars workspace dependency, implements dump-schema binary to generate JSON schemas from Rust types using schemars::schema_for!(), creates a new CI job to detect schema drift via git diff --exit-code schemas/, and documents the auto-generation workflow and layout conventions.
Rust model types with JsonSchema derive
crates/aisix-core/src/models/apikey.rs, crates/aisix-core/src/models/cache_policy.rs, crates/aisix-core/src/models/guardrail.rs, crates/aisix-core/src/models/model.rs, crates/aisix-core/src/models/observability_exporter.rs, crates/aisix-core/src/models/provider_key.rs, crates/aisix-core/src/models/rate_limit.rs, crates/aisix-core/src/models/rate_limit_policy.rs, crates/aisix-core/src/models/routing.rs
Adds schemars::JsonSchema derive macro to 30+ public types (ApiKey, CachePolicy, Guardrail and 8 related enums/structs, Model and 6 supporting types, ObservabilityExporter, ProviderKey and 5 supporting types, RateLimit, RateLimitPolicy, Routing and 3 supporting types), enabling automatic JSON schema generation without changing serialization or logic.
Generated JSON schema files
schemas/resources/api_key.schema.json, schemas/resources/cache_policy.schema.json, schemas/resources/guardrail.schema.json, schemas/resources/model.schema.json, schemas/resources/observability_exporter.schema.json, schemas/resources/provider_key.schema.json, schemas/resources/rate_limit.schema.json, schemas/resources/rate_limit_policy.schema.json, schemas/resources/routing.schema.json
Commits canonical draft-07 JSON schemas for 9 resource types; schemas define required/optional fields, type constraints, numeric bounds, enums, nullable fields, nested object definitions, and additionalProperties: false policies derived directly from Rust model definitions.

🎯 3 (Moderate) | ⏱️ ~20 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@moonming
moonming changed the base branch from chore/dump-schema-binary to main May 17, 2026 01:49
@moonming moonming closed this May 17, 2026
@moonming moonming reopened this May 17, 2026
Adds a new `schema-drift` job to the CI workflow that runs
`cargo run -p aisix-core --bin dump-schema` and asserts
`git diff --exit-code schemas/` is clean. PRs that modify resource
struct in `crates/aisix-core/src/models/` but forget to regenerate
the schema files now fail CI with a fix instruction in the error
message.

## Why

Refs #304 item #1. The `dump-schema` tool and
`schemas/resources/*.schema.json` files were introduced in #308;
without an enforcement mechanism the committed schemas can silently
diverge from the Rust types as the resource graph evolves
(especially during issue #302 Phase A, which is actively mutating
ProviderKey / Model). This job is that enforcement.

## Job placement

Sits as a peer to `lint` — fast, independent, no service deps. Runs
in parallel with `lint` / `rust-unit` / `build-bin`. Not a `needs:`
target of any downstream job, so a drift failure does not block the
e2e or coverage signals.

## Verification

- Positive path: `cargo run -p aisix-core --bin dump-schema` on the
  HEAD of this PR succeeds and `git diff --exit-code schemas/` is
  empty (no drift in tree)
- Negative path: locally introduced a synthetic drift by truncating
  `schemas/resources/api_key.schema.json` to `{}`. `git diff
  --exit-code schemas/` returned non-zero — the check fires as
  expected. Reverted with `git checkout schemas/resources/api_key.schema.json`.
- YAML parses with `python3 -c "import yaml; yaml.safe_load(open(...))"`.

## Stack

Builds on #308 (which adds the binary + initial schemas). Base will
switch to `main` once #308 merges.

Refs #304 (#1).
Copilot AI review requested due to automatic review settings May 17, 2026 01:55
@moonming
moonming force-pushed the ci/schema-drift-check branch from 16a7ddb to ddefbd5 Compare May 17, 2026 01:55
moonming added a commit that referenced this pull request May 17, 2026
The hand-written OpenAPI 3.1 document in `crates/aisix-admin/src/openapi.rs`
previously inlined its own copy of every resource schema (`Model`,
`ApiKey`, `ProviderKey`, `Guardrail`, `CachePolicy`,
`ObservabilityExporter`, `RateLimit`, `Routing`, plus the nested
`ModelCost` / `BackgroundModelCheck`). That left three places to keep
in sync whenever a resource field changed: the Rust struct, the
inline OpenAPI schema, and the cp-api / dashboard side.

This PR cuts the duplication. The Rust struct is now the single
source of truth; `dump-schema` (PR #308) writes canonical
draft-07 JSON Schemas into `schemas/resources/*.schema.json`; CI
(PR #309) enforces those files match the structs. This commit:

1. Removes the ten inlined resource schemas from `OPENAPI_JSON_BASE`
   (the const formerly named `OPENAPI_JSON`).
2. Embeds the eight canonical schema files at compile time via
   `include_str!` into a new `RESOURCE_SCHEMAS` const.
3. Adds `merged_openapi()` — runs once on first request, parses the
   base spec, parses each embedded schema, hoists `definitions/*`
   into top-level `components.schemas`, rewrites
   `$ref: #/definitions/X` to `$ref: #/components/schemas/X`
   (JSON Schema draft-07 → OpenAPI 3.1), and caches the result in
   an `OnceLock<String>`.
4. Changes `openapi_json()` to serve the merged doc instead of the
   raw `OPENAPI_JSON_BASE`.
5. Updates the three openapi unit tests to parse `merged_openapi()`.

## What this means for `/admin/openapi.json`

The served document keeps the same wrapper schemas (`ModelEntry`,
`ApiKeyEntry`, `ModelStatusView`, `ModelKind`, `RuntimeStatus`,
`SystemTime`, `AdminError`) and gains 16 new top-level component
schemas hoisted from the resource definitions (`Adapter`,
`BedrockConfig`, `CacheBackend`, `CooldownConfig`,
`GuardrailHookPoint`, `KeywordPattern`, `OnAllFilteredPolicy`,
`ParamConstraints`, `Provider`, `RequestOverrides`,
`ResponseOverrides`, `RoutingStrategy`, `RoutingTarget`,
`StreamDoneMarker`, `TelemetryTags`, etc.).

The resource schemas themselves are now precise reflections of the
Rust types — e.g. `Guardrail` uses a proper `oneOf` discriminator on
`kind` instead of the previous flat `additionalProperties: true`
hand-wave; `Provider` lists its 6 variants from the actual enum;
`Adapter` lists the 5 wire-shape kebab-case values from #302
Phase A.

## Verification

- `cargo check -p aisix-admin` clean
- `cargo clippy --workspace --all-targets -- -D warnings` clean
- `cargo fmt --all -- --check` clean
- `cargo test -p aisix-admin --lib` — all 7 openapi tests pass,
  including the regression test
  `openapi_apikey_schema_excludes_max_budget_usd`
- External validation: parsed the merged doc, collected 43 `$ref`
  references across 32 distinct targets, all resolve inside
  `#/components/schemas/*` (0 unresolved)

## Why nested `if let` instead of let-chains

Workspace is on `edition = "2021"`. The merge logic uses one level
of nesting in two spots; not pretty, but `edition = "2024"` is a
separate decision not in this PR's scope.

## Stack

Builds on:
- #307 (JsonSchema derives on resource structs)
- #308 (dump-schema binary + initial schema files)
- #309 (CI drift enforcement)

Merge order: 307 → 308 → 309 → this PR. Base will switch to `main`
once #308 merges.

Refs #304 (#1).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants