Skip to content

Proposal: Commit the drf-spectacular generated OpenAPI spec in git #950

@ChristopherChudzicki

Description

@ChristopherChudzicki

Description

tldr: I propose that Open5e-api commit its drf-spectacular generated OpenAPI spec in repo to improve visibility in PRs and enable tracking in git. A simple CI check + git attribute can ensure it does not get out of sync.

Current Spec Tooling: DRF-Spectacular

Open5e uses drf-spectacular to generate its OpenAPI spec. The OpenAPI spec is served at

Generally, drf-spectacular generates the spec:

  1. By introspecting models/serializers/viewsets/filtersets (~90% of spec)
  2. With custom overrides provided by @extend_schema_field and other decorators. (extra ~10% manual work)

Where drf-spectacular warns, or manual work has an error/inaccuracy, the spec can be wrong.

Proposal

I propose that Open5e commit the YAML version of its OpenAPI spec. Provided that the spec stays in-sync with the code, making the spec visible in Git history is very useful:

  1. Changes over time, particularly in PRs, are visible to reviewers
    • YAML version of spec is fairly easy to read.
    • This is, IMO, a big win. It's not always obvious how a serializer/model change will affect the spec. (E.g., this simple looking edit changed weapon.properties from a properly typed Array<WeaponPropertyAssignmentSerializer> to a string.1)
    • And it makes reviewing a spec-fix PR very easy. Contributor adds decorator or makes trivial refactor to fix spec -> you see the change.
    • Very easy to see how a drf-spectacular upgrade will affect your spec.
  2. Becomes easy to add a CI check that detects breaking changes, e.g., by using a tool like https://github.com/oasdiff/oasdiff to compare the spec on staging vs PR branch and detect if fields were removed, or changed type, etc.
    • In practice, I'm not sure I would recommend doing this, at least not as a CI check that can fail. It would treat a lot of spec bugfixes as breaking changes. (Like the fix above, or many places where the open5e code has a comment "this property should be nullable in spec")
  3. You can generate frontend API clients against different branches, if you use generated clients for developing your frontend.

Keeping the spec in-sync

Committing the spec safely requires a way to ensure it stays up-to-date.

Concern 1: Author forgot to regenerate the spec.

Simple solution:

  1. Github Action: Generate the spec in a github action. Does the committed spec match? If yes, then it's completely up-to-date at the moment CI checks run.
    • Note: Github's pull_request event provides a merge ref containing the PR branch already merged into the base branch; actions/checkout uses that ref by default, so spec is truly up-to-date at this moment.
    • Freshness action can show the diff plus message nudging you to regenerate.

Concern 2: Stale CI Check, 2 branches modify spec: However, there is still the possibility that two branches A and B both change the spec and both have up to date specs at the moment CI runs, and A is merged into staging first. Now B's spec is out-of-date.

Another simple solution:

  1. Git attribute: Mark the spec as openapi-schema.yml text eol=lf merge=binary in git attributes:
    • merge=binary forces a conflict if pr-branch and main have both modified the spec. It prevents textual merges, so the file must be regenerated afresh.
    • eol=lf ensures cross-platform stability of the generated spec, which CI relies on (so that devs generating on Windows match CI)

This eliminates the "Stale CI Check, 2 branches modify spec" concern.

Concern 3: Spec not changed, but affected by base branch: Again, the CI check guarantees the branch is completely up-to-date at the time CI runs. There is one remaining concern that since the check ran, base (staging) has (A) not changed the spec itself, but (B) changed in some way that affects spec generation.

This can be prevented by enabling merge queue or "Require branch up to date before merging" in github. Both of these checks have their own pros/cons.

However: In practice, this seems quite rare. We've used the CI check + git attribute for ~ 2 years in https://github.com/mitodl/mit-learn 2 and never run into this.

Additionally, any spec drift caused by Concern 3 is self-healing. At worst, the next CI check that runs against staging, on any PR, would detect the drift.

Recommendation: Commit the spec, do CI check + git attribute. Skip merge queue / require-up-to-date-before-merging unless you want it for other reasons.

Demonstration

I thought this feature would be easiest to understand / see with some PRs demonstrating it, so:

Footnotes

  1. It needs an @extend_schema_field(WeaponPropertyAssignmentSerializer(many=True)) decorator.

  2. The above proposal is based on what we do at my work, mit-learn is one of our repos. You can see our spec check at https://github.com/mitodl/mit-learn/blob/main/.github/workflows/openapi-diff.yml and our git attributes at https://github.com/mitodl/mit-learn/blob/main/.gitattributes#L9.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions