Skip to content

feat(docs): OpenAPI spec sync with CI validation and PR previews#1550

Open
notrab wants to merge 70 commits intomainfrom
openapi-spec-generator
Open

feat(docs): OpenAPI spec sync with CI validation and PR previews#1550
notrab wants to merge 70 commits intomainfrom
openapi-spec-generator

Conversation

@notrab
Copy link
Member

@notrab notrab commented Jan 19, 2026

Lite PR

Summary

  1. Added openapi:generate script to fetch and save OpenAPI spec from ENSApi
  2. Updated Mintlify docs to use production URL for live API docs, committed file for PR previews
  3. Added CI job to validate committed openapi.json stays in sync with production

Why

Production API docs were getting out of sync with deployed API, and there was no way to preview API doc changes in PRs.

This setup ensures:

  • Production docs always reflect the live API (via runtime URL)
  • PR previews show upcoming API changes (via committed file)
  • CI catches drift between committed spec and production

Testing

  • Ran pnpm --filter ensapi openapi:generate locally to verify spec generation
  • Verified Biome formatting works on generated output
  • CI workflow syntax validated

Notes for Reviewer (Optional)

  • Requires curl step inside the switch environment to trigger production build using the Mintlify API.
  • The hidden "Preview" page in docs is for internal use during PR reviews
  • CI check will fail on PRs where API changes haven't been reflected in the committed `openapi.json

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

Copilot AI review requested due to automatic review settings January 19, 2026 13:32
@vercel
Copy link
Contributor

vercel bot commented Jan 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Feb 3, 2026 7:30pm
ensnode.io Ready Ready Preview, Comment Feb 3, 2026 7:30pm
ensrainbow.io Ready Ready Preview, Comment Feb 3, 2026 7:30pm

@changeset-bot
Copy link

changeset-bot bot commented Jan 19, 2026

🦋 Changeset detected

Latest commit: c69b37a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 18 packages
Name Type
@docs/mintlify Patch
ensapi Patch
ensindexer Patch
ensadmin Patch
ensrainbow Patch
fallback-ensapi Patch
@ensnode/datasources Patch
@ensnode/ensrainbow-sdk Patch
@ensnode/ponder-metadata Patch
@ensnode/ensnode-schema Patch
@ensnode/ensnode-react Patch
@ensnode/ponder-subgraph Patch
@ensnode/ensnode-sdk Patch
@ensnode/shared-configs Patch
@docs/ensnode Patch
@docs/ensrainbow Patch
@namehash/ens-referrals Patch
@namehash/namehash-ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Warning

Rate limit exceeded

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

⌛ 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.

📝 Walkthrough

Walkthrough

Adds OPENAPI_GENERATE_MODE to start ENSApi with a mock config exposing /openapi.json; a TypeScript CLI fetches, validates, formats and writes the spec; CI runs ENSApi in generate mode, polls /openapi.json, diffs generated vs committed spec and validates with Mintlify; docs include a hidden Preview page and README updates.

Changes

Cohort / File(s) Summary
CI / Workflows
​.github/workflows/test_ci.yml, ​.github/workflows/deploy_switch_ensnode_environment.yml
Adds openapi-sync-check job that starts ENSApi with OPENAPI_GENERATE_MODE, polls /openapi.json, runs generator, diffs against committed spec, validates with Mintlify; adds Mintlify rebuild trigger and a conditional Slack failure notifier in deploy workflow.
OpenAPI generator CLI & docs package
docs/docs.ensnode.io/scripts/generate-openapi.ts, docs/docs.ensnode.io/package.json
New tsx-based CLI to fetch/validate /openapi.json, write/format docs/docs.ensnode.io/openapi.json; adds openapi:generate script and dev dependencies (tsx, types).
Documentation content & config
docs/docs.ensnode.io/docs.json, docs/docs.ensnode.io/ensapi/preview.mdx, docs/docs.ensnode.io/README.md
Repoints production API Reference URL, adds hidden "Preview" group referencing local /openapi.json, adds preview MDX page and README guidance for generating/validating the OpenAPI spec.
ENSApi config, env & mock
apps/ensapi/src/config/config.schema.ts, apps/ensapi/src/config/openapi-mock-config.ts, apps/ensapi/src/config/environment.ts, apps/ensapi/src/config/config.schema.test.ts, apps/ensapi/src/config/openapi-mock-config.test.ts, apps/ensapi/.env.local.example
Introduces OPENAPI_GENERATE_MODE, adds buildOpenApiMockConfig() returning a self-contained mock EnsApiConfigInput, early-return in config builder to use mock config, type updates, env example entries, and tests for generate-mode behavior.
Workspace & metadata
pnpm-workspace.yaml, .changeset/bright-waves-flow.md, .changeset/gentle-clouds-dance.md
Minor pnpm workspace quoting/order tweak; adds changesets documenting docs/OpenAPI addition and new generate mode for a patch release.

Sequence Diagram(s)

sequenceDiagram
    participant GH as GitHub Actions
    participant ENS as ENSApi (OPENAPI_GENERATE_MODE)
    participant CLI as generate-openapi.ts
    participant Repo as Repo (committed openapi.json)
    participant Mint as Mintlify API

    GH->>ENS: start ENSApi with OPENAPI_GENERATE_MODE=true
    activate ENS
    ENS->>ENS: load mock config, expose /openapi.json
    GH->>CLI: run readiness loop (poll /openapi.json)
    loop poll until ready or timeout
        CLI->>ENS: GET /openapi.json
        ENS-->>CLI: 200 + JSON
    end
    CLI->>CLI: validate JSON, format, write `docs/docs.ensnode.io/openapi.json`
    CLI->>Repo: produce generated spec (in-memory)
    GH->>GH: diff generated spec vs committed `docs/docs.ensnode.io/openapi.json`
    alt specs match
        GH->>Mint: submit spec for Mintlify validation
        Mint-->>GH: validation result
    else specs differ
        GH->>GH: fail job
    end
    GH->>ENS: stop ENSApi
    deactivate ENS
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped to a server in the night,

Mocked a tiny world to show each route,
I fetched the spec, tidied every line,
CI checked the match and Mintlify gave light,
Docs now bloom—previews ready, crisp, and bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: OpenAPI spec synchronization with CI validation and PR preview capability.
Linked Issues check ✅ Passed The PR comprehensively addresses both linked issues: implements CI validation for spec sync [#1532], enables PR previews via committed openapi.json [#1532, #1475], and supports runtime URL for production docs [#1475].
Out of Scope Changes check ✅ Passed All changes are directly scoped to OpenAPI spec generation and integration: added scripts, CI workflow, documentation updates, configuration for mock mode, and package dependencies align with stated objectives.
Description check ✅ Passed The pull request description follows the required template structure with all key sections completed: Summary (3 bullets), Why (motivation and intended outcomes), Testing (methods used), Notes for Reviewer, and Pre-Review Checklist fully marked.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch openapi-spec-generator

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds OpenAPI specification generation and documentation capabilities to the ENSNode project. It introduces a script to fetch and save the OpenAPI spec from a running ENSApi instance, integrates it with Mintlify documentation, and adds CI checks to ensure the spec stays in sync with production.

Changes:

  • Added a 5,106-line OpenAPI specification file documenting all ENSApi endpoints
  • Created a generation script to fetch and update the spec from a running instance
  • Configured Mintlify docs to reference both production and local OpenAPI specs
  • Added CI workflow to validate spec synchronization on the main branch

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
docs/docs.ensnode.io/openapi.json Complete OpenAPI 3.1.0 specification documenting all ENSApi endpoints including resolution, meta, explore, and ENSAwards APIs
apps/ensapi/scripts/generate-openapi.ts TypeScript script to fetch OpenAPI spec from running instance and save to docs directory
apps/ensapi/package.json Added npm script openapi:generate to run the generation script
docs/docs.ensnode.io/docs.json Updated configuration to reference production API spec and added hidden preview section for local spec
.github/workflows/test_ci.yml Added CI job to verify OpenAPI spec stays in sync with production on main branch

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @.github/workflows/test_ci.yml:
- Around line 51-55: The openapi-sync-check job currently only runs when
github.ref == 'refs/heads/main', which lets PRs merge with out-of-sync specs;
update the condition on the openapi-sync-check job (the if: line) to also run
for pull requests (e.g., include github.event_name == 'pull_request' or
github.head_ref checks) and mark it non-blocking for PRs by using
continue-on-error: true (or alternatively remove the if entirely and rely on
branch protection or add contributor documentation). Target the job named
openapi-sync-check and the existing if: github.ref == 'refs/heads/main'
condition when making the change.

In `@apps/ensapi/scripts/generate-openapi.ts`:
- Around line 23-24: ensapiUrl may end with a trailing slash causing openapiUrl
to become "//openapi.json"; normalize ensapiUrl before composing openapiUrl (the
variables to change are ensapiUrl and openapiUrl in generate-openapi.ts) by
trimming any trailing '/' from ensapiUrl (or using a URL-safe join) so
openapiUrl is built as `${normalizedEnsapiUrl}/openapi.json` even when
ENSAPI_URL or process.argv[2] includes a trailing slash; ensure
DEFAULT_ENSAPI_URL remains fallback and normalization runs after selecting the
value.
- Line 12: Update the header comment string that reads "Writes openapi.json to
the docs directory for Mintilify" and correct the product name typo to
"Mintlify" so the comment reads "Writes openapi.json to the docs directory for
Mintlify"; locate this exact comment text in generate-openapi.ts and make the
single-word change.
- Around line 28-33: Add a timeout to the fetch in generate-openapi.ts by
creating an AbortSignal via AbortSignal.timeout(ms) and passing it as the signal
option to the fetch(openapiUrl) call; handle the abort case by catching the
thrown error (check for AbortError or error.name === 'AbortError') and log a
clear timeout message before exiting, and ensure the existing non-ok response
handling remains unchanged.

In `@docs/docs.ensnode.io/docs.json`:
- Around line 29-34: The docs.json "Preview" group references a non-existent
page "ensapi/preview"; fix by either adding a new page file named preview.mdx
under the docs/docs.ensnode.io/ensapi/ folder (so the path matches
"ensapi/preview") or remove the entire Preview group entry (the object with
"group": "Preview", "pages": ["ensapi/preview"], "openapi": "./openapi.json",
"hidden": true) from docs.json; update whichever you choose and ensure the
"pages" array references only existing MDX/MD files.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/workflows/test_ci.yml:
- Around line 58-70: The CI step runs the openapi:generate npm script which
currently relies on a hardcoded production default; make the behavior explicit
by passing the production URL via the ENSAPI_URL env in the workflow step or by
updating the openapi:generate script to accept an ENSAPI_URL argument and
default it to https://api.alpha.ensnode.io; update the step that invokes
openapi:generate to export ENSAPI_URL or call the script with the URL so the
action is explicit and maintainable.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 20, 2026 16:01 Inactive
Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@notrab Thanks for updates. Reviewed and shared feedback 👍

# ENS_HOLIDAY_AWARDS_END="2025-12-31T23:59:59Z"

# OpenAPI Generate Mode
# Optional. When set to "true", ENSApi starts with a minimal mock configuration,
Copy link
Member

Choose a reason for hiding this comment

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

Feedback:

  1. Please document specific references to each of the other environment variables that are ignored when this is set to true. Or, alternatively, document something like: causes all other environment variable configurations to be ignored except for X, Y, and Z as it might be easier to say what isn't ignored, than to make a big list of everything that is ignored.
  2. I believe it's important that when this environment variable is set, ENSApi should refuse to serve any route other than the route for the openapi.json file. I understand we can achieve this as follows:
    1. Introduce some new "global" middleware, such as can be seen here.
    2. This new "global" middleware can have some simple logic:
      1. If this environment variable has been set and the route is not the route for the openapi.json file: return a HTTP 503 service unavailable error with some little message about how ENSApi was started in this OpenAPI generate mode and therefore only route X is available.
      2. Else: pass through the request.
    3. Update the docs for this environment variable to note how when it is set, all APIs return HTTP 503 service unavailable with the exception of route X to get the openapi.json file.
    4. The logic for how the "config" object in ENSApi is built should be refined. More specifically: EnsApiConfig / EnsApiConfigSchema as defined in apps/ensapi/src/config/config.schema.ts should be updated to include a new boolean field named something like inOpenApiGenerateMode. This should be implemented such that the value will be false unless the related environment variable is explicitly set to "true". The logic in the new "global" middleware should then read from this variable to decide its behaviour.

Copy link
Member Author

Choose a reason for hiding this comment

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

@lightwalker-eth great idea about the middleware. Makes things a bit simpler and more isolated, thanks!

/**
* Environment variables for OpenAPI generation mode.
*
* When enabled, ENSApi starts with a minimal mock configuration,
Copy link
Member

Choose a reason for hiding this comment

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

Please see my related comment for how activating this flag should not only impact how ENSApi starts, but also how it handles all requests to all routes except for the route for the openapi.json file.

* Mock configuration used exclusively for OpenAPI spec generation.
*
* When OPENAPI_GENERATE_MODE is enabled, ENSApi uses this mock config
* to start without requiring real database or indexer connections.
Copy link
Member

Choose a reason for hiding this comment

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

Please see my related comment for how activating this flag should not only impact how ENSApi starts, but also how it handles all requests to all routes except for the route for the openapi.json file.

with:
slack_webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
slack_title: "⚠️ Mintlify Docs Rebuild Failed"
slack_message: "Failed to trigger Mintlify docs rebuild during environment switch to ${{ inputs.target }}"
Copy link
Member

Choose a reason for hiding this comment

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

@notrab This is a nice suggestion from Copilot. Appreciate your advice.

slack_webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
slack_title: "⚠️ Mintlify Docs Rebuild Failed"
slack_message: "Failed to trigger Mintlify docs rebuild during environment switch to ${{ inputs.target }}"

Copy link
Member

Choose a reason for hiding this comment

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

@notrab I'd also like to see an additional separate Slack notification sent to notify us of successfully rebuilt Mintlify docs. This notification ideally should also include a reference to the specific URL to the OpenAPI.json that the new production Mintlify API docs were successfully built from.

Goal: Help everyone in our team build a stronger mental model for how all these pieces are working together.


- name: Validate OpenAPI spec with Mintlify
run: pnpm dlx mintlify openapi-check docs/docs.ensnode.io/openapi.json

Copy link
Member

Choose a reason for hiding this comment

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

@notrab I like the idea of following this best practice as suggested by Copilot.

- uses: actions/checkout@v4
- uses: ./.github/actions/setup_node_environment

- name: Start ENSApi in OpenAPI generate mode
Copy link
Member

Choose a reason for hiding this comment

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

@notrab There's currently many ideas in this file (.github/workflows/test_ci.yml) for coordinating the whole process of updating the committed openapi spec. Could we move those ideas into some bash script or something that could not only be used in CI, but also be used locally in development? The goal here is to make it so that the instructions in the "Generating the Spec" section of docs/docs.ensnode.io/README.md could be made more simple so that it doesn't require multiple commands across separate terminals.

--retry 2 \
--retry-delay 5 \
--request POST \
--url "https://api.mintlify.com/v1/project/update/${{ vars.MINTLIFY_PROJECT_ID }}" \
Copy link
Member

Choose a reason for hiding this comment

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

We need to implement this operation more defensively.

Specifically:

Copy link
Member Author

Choose a reason for hiding this comment

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

I love this thinking! Great idea

Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@notrab One other suggestion.

"group": "Preview",
"pages": ["ensapi/preview"],
"openapi": "/openapi.json",
"hidden": true
Copy link
Member

Choose a reason for hiding this comment

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

Please update the description of this PR to include a section for post-merge follow-up actions. This needs to include the following goal:

  • Ensure that the "hidden" preview API docs remain truly hidden from search engines once we merge this PR. It's possible they are only hidden in preview environments but may not be hidden in production.

Co-authored-by: lightwalker.eth <126201998+lightwalker-eth@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 3, 2026 19:08
Co-authored-by: lightwalker.eth <126201998+lightwalker-eth@users.noreply.github.com>
@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io February 3, 2026 19:09 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io February 3, 2026 19:09 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io February 3, 2026 19:09 Inactive
Co-authored-by: lightwalker.eth <126201998+lightwalker-eth@users.noreply.github.com>
Co-authored-by: lightwalker.eth <126201998+lightwalker-eth@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 17 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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