Skip to content

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

Closes #1532
Closes #1475


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.
  • MINTLIFY_API_KEY secret and MINTLIFY_PROJECT_ID variable need added to to GitHub for the environment switch workflow
  • 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

vercel bot commented Jan 19, 2026

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

Project Deployment Review Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Jan 21, 2026 4:48pm
ensnode.io Ready Ready Preview, Comment Jan 21, 2026 4:48pm
ensrainbow.io Ready Ready Preview, Comment Jan 21, 2026 4:48pm

@changeset-bot
Copy link

changeset-bot bot commented Jan 19, 2026

⚠️ No Changeset found

Latest commit: fac4754

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds an OpenAPI spec sync CI check, a CLI script to fetch/generate the OpenAPI JSON from a running ENSApi, documentation updates (preview page and README), and a small deploy workflow step to trigger a Mintlify docs rebuild.

Changes

Cohort / File(s) Summary
CI Workflow & ENSApi CI mode
​.github/workflows/test_ci.yml, apps/ensapi/src/config/config.schema.ts, apps/ensapi/src/config/environment.ts
New GitHub Actions job "OpenAPI Spec Sync Check" that launches ENSApi in CI-check mode and compares generated /openapi.json to the committed file. Adds OPENAPI_CI_CHECK env var, a mocked config builder for CI-check mode, and extends the environment type.
Docs site scripts & config
docs/docs.ensnode.io/package.json, docs/docs.ensnode.io/scripts/generate-openapi.ts, docs/docs.ensnode.io/openapi.json, docs/docs.ensnode.io/docs.json
Adds openapi:generate npm script and tsx devDependency. New generate-openapi.ts fetches, validates, pretty-prints, and writes openapi.json from a provided ENSApi URL; attempts Biome formatting. Docs config now points API Reference to production endpoint and adds a hidden "Preview" group that references the local openapi.json.
Docs content & guidance
docs/docs.ensnode.io/README.md, docs/docs.ensnode.io/ensapi/preview.mdx
Rewrites README to document OpenAPI spec management, CI validation, and generation instructions. Adds an MDX preview page describing branch-specific unreleased OpenAPI changes.
Deploy workflow docs rebuild
.github/workflows/deploy_switch_ensnode_environment.yml
Adds a step to POST to Mintlify to trigger a documentation rebuild as part of the deployment workflow.

Sequence Diagram(s)

sequenceDiagram
  participant GH as GitHub Actions
  participant ENS as ENSApi (CI mode)
  participant Script as generate-openapi.ts
  participant FS as Repo filesystem
  participant Comparator as CI comparison step

  GH->>ENS: start ENSApi with OPENAPI_CI_CHECK=true
  GH->>ENS: wait for /openapi.json to be available
  Script->>ENS: fetch /openapi.json (URL arg)
  ENS-->>Script: return JSON
  Script->>FS: write formatted openapi.json
  Comparator->>FS: read committed openapi.json
  Comparator->>FS: read generated openapi.json
  Comparator-->>GH: pass/fail (fail prints guidance)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I fetched a spec straight from the tree,
I wrote it out neat, then hopped with glee.
CI checks the files, bellies full of lore,
Docs stay in sync — hop, sync, explore! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'openapi spec / docs poc' is vague and generic, using abbreviations (poc) without context and not clearly conveying the main changes or purpose to someone scanning the commit history. Consider a more descriptive title like 'Add OpenAPI spec sync validation and PR preview support' or 'Implement OpenAPI spec generation and CI sync check' that clearly summarizes the primary changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description follows the required template with clear summary, why section, testing details, reviewer notes, and a pre-review checklist.

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

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

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
Copilot AI review requested due to automatic review settings January 21, 2026 15:57
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 21, 2026 15:57 Inactive
@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io January 21, 2026 15:57 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io January 21, 2026 15:57 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 21, 2026 15:58 Inactive
@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io January 21, 2026 15:58 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io January 21, 2026 15:58 Inactive
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 9 out of 11 changed files in this pull request and generated 2 comments.

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

💡 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: 1

🤖 Fix all issues with AI agents
In `@apps/ensapi/src/handlers/ensanalytics-api.ts`:
- Around line 55-57: The OpenAPI tag in the describeRoute call is misspelled as
"ENSAwardsssss" causing fragmented docs; update the tags array in the
describeRoute invocation (the object with tags/summary, used to register the
route in ensanalytics-api.ts) to use the correct tag "ENSAwards" (or the
existing canonical EN S tag) so it matches other routes and consolidates the
documentation group.

@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io January 21, 2026 16:03 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io January 21, 2026 16:03 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 21, 2026 16:03 Inactive
Copilot AI review requested due to automatic review settings January 21, 2026 16:24
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 21, 2026 16:24 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io January 21, 2026 16:24 Inactive
@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io January 21, 2026 16:24 Inactive
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 8 out of 10 changed files in this pull request and generated 4 comments.

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

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

* Generate OpenAPI spec from a running ENSApi instance.
*
* Usage:
* pnpm --filter docs.ensnode.io openapi:generate http://localhost:3223
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

The usage example in the comment shows port 3223, but the actual default port for ENSApi is 4334 (as shown in the CI workflow and README). This could confuse users trying to run the script locally.

Copilot uses AI. Check for mistakes.
if (!ensapiUrl) {
console.error("Error: ENSApi URL is required.");
console.error("Usage: pnpm openapi:generate <url>");
console.error("Example: pnpm openapi:generate http://localhost:3223");
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

The usage example in the error message shows port 3223, which doesn't match the default ENSApi port (4334). This should be updated to use 4334 to match the actual default port used in the CI workflow and README.

Copilot uses AI. Check for mistakes.

return EnsApiConfigSchemaForOpenApiCiCheck.parse({
port: env.PORT || ENSApi_DEFAULT_PORT,
databaseUrl: "postgresql://openapi:openapi@localhost:5432/openapi",
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

The mock database URL contains credentials in plaintext. While this is for CI-only mock config and won't connect to a real database, consider using a more obviously fake format like "postgresql://mock:mock@localhost:5432/mock" to make it clearer this is not a real connection string.

Suggested change
databaseUrl: "postgresql://openapi:openapi@localhost:5432/openapi",
databaseUrl: "postgresql://mock:mock@localhost:5432/mock",

Copilot uses AI. Check for mistakes.
echo ""
git diff --color docs/docs.ensnode.io/openapi.json
echo ""
echo "Run 'OPENAPI_CI_CHECK=true pnpm --filter ensapi start' then 'pnpm --filter @docs/mintlify openapi:generate http://localhost:4334' and commit the changes."
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

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

The error message suggests running the command from the repository root, but based on the package.json structure, the command should be run with the correct filter. The suggested command should be: 'pnpm --filter @docs/mintlify openapi:generate http://localhost:4334' to match the package name in package.json.

Copilot uses AI. Check for mistakes.
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 `@docs/docs.ensnode.io/README.md`:
- Line 25: Add terminal punctuation to the final bullet "PR previews show
upcoming API changes before merge" by appending a period (.) so the sentence
ends consistently with the other list items; update the README.md bullet text
accordingly.

- Production API docs match the production deployment, even when production lags behind `main`
- Non-API docs stay in sync with `main` through normal Git flow
- Each branch has its own `openapi.json`, validated by the CI to be in sync with the `openapi.json` that would actually be returned by the code for ENSApi in the same branch.
- PR previews show upcoming API changes before merge
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add terminal punctuation to the bullet list.

The last bullet in the list should end with punctuation for consistency and to satisfy the grammar check.

🧰 Tools
🪛 LanguageTool

[grammar] ~25-~25: Please add a punctuation mark at the end of paragraph.
Context: ...eviews show upcoming API changes before merge ### Generating the Spec To generate t...

(PUNCTUATION_PARAGRAPH_END)

🤖 Prompt for AI Agents
In `@docs/docs.ensnode.io/README.md` at line 25, Add terminal punctuation to the
final bullet "PR previews show upcoming API changes before merge" by appending a
period (.) so the sentence ends consistently with the other list items; update
the README.md bullet text accordingly.

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: 2

🤖 Fix all issues with AI agents
In @.github/workflows/deploy_switch_ensnode_environment.yml:
- Around line 84-89: Modify the "Trigger Mintlify Docs Rebuild" job step so that
a transient Mintlify API failure won't fail the entire workflow: add
continue-on-error: true to the step (or append a non-failing fallback like "||
true" to the curl run command) and ensure the step still logs the curl response
for debugging; also verify and correct the secret name consistency by using the
actual configured secret (replace MINTLIFY_API_TOKEN with MINTLIFY_API_KEY if
that is the real secret name referenced in your repo settings) so the workflow
uses the correct credential.

In `@docs/docs.ensnode.io/scripts/generate-openapi.ts`:
- Line 103: The top-level call to main() can produce an unhandled promise
rejection; update the invocation of main (the main function in
generate-openapi.ts) to handle errors by appending a .catch(...) to the returned
promise (or wrapping the call in an async IIFE with try/catch) and ensure the
catch logs the error and exits/non-zero as appropriate so all rejections from
main are handled.
♻️ Duplicate comments (1)
docs/docs.ensnode.io/scripts/generate-openapi.ts (1)

17-17: Use fileURLToPath for consistency with codebase patterns.

Previous review comments have already flagged this — import.meta.dirname requires Node.js v20.11.0+ and the codebase uses fileURLToPath(import.meta.url) elsewhere (e.g., apps/ensrainbow/src/cli.ts). Apply the same pattern for consistency.

Comment on lines +84 to +89
- name: Trigger Mintlify Docs Rebuild
run: |
curl --request POST \
--url "https://api.mintlify.com/v1/project/update/${{ secrets.MINTLIFY_PROJECT_ID }}" \
--header "Authorization: Bearer ${{ secrets.MINTLIFY_API_TOKEN }}" \
--fail
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider allowing Mintlify rebuild failures to not block the workflow.

If the Mintlify API is temporarily unavailable, this step will fail the entire workflow and prevent the Slack notification from being sent. Since the docs rebuild is not critical to the environment switch operation itself, consider using continue-on-error: true or adding a fallback notification.

Also, the PR description mentions MINTLIFY_API_KEY but the workflow uses MINTLIFY_API_TOKEN — verify the secret name is consistent with what's configured in GitHub.

♻️ Suggested improvement
       - name: Trigger Mintlify Docs Rebuild
+        continue-on-error: true
         run: |
           curl --request POST \
             --url "https://api.mintlify.com/v1/project/update/${{ secrets.MINTLIFY_PROJECT_ID }}" \
             --header "Authorization: Bearer ${{ secrets.MINTLIFY_API_TOKEN }}" \
             --fail
🤖 Prompt for AI Agents
In @.github/workflows/deploy_switch_ensnode_environment.yml around lines 84 -
89, Modify the "Trigger Mintlify Docs Rebuild" job step so that a transient
Mintlify API failure won't fail the entire workflow: add continue-on-error: true
to the step (or append a non-failing fallback like "|| true" to the curl run
command) and ensure the step still logs the curl response for debugging; also
verify and correct the secret name consistency by using the actual configured
secret (replace MINTLIFY_API_TOKEN with MINTLIFY_API_KEY if that is the real
secret name referenced in your repo settings) so the workflow uses the correct
credential.

}
}

main();
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Handle potential unhandled promise rejection.

If an unexpected error occurs outside the explicit try-catch blocks, the promise rejection will be unhandled. Add a .catch() handler for robustness.

♻️ Suggested fix
-main();
+main().catch((error) => {
+  console.error("Unexpected error:", error);
+  process.exit(1);
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
main();
main().catch((error) => {
console.error("Unexpected error:", error);
process.exit(1);
});
🤖 Prompt for AI Agents
In `@docs/docs.ensnode.io/scripts/generate-openapi.ts` at line 103, The top-level
call to main() can produce an unhandled promise rejection; update the invocation
of main (the main function in generate-openapi.ts) to handle errors by appending
a .catch(...) to the returned promise (or wrapping the call in an async IIFE
with try/catch) and ensure the catch logs the error and exits/non-zero as
appropriate so all rejections from main are handled.

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.

Generate Mintlify Preview Environments on PRs Epic: ENSApi Docs

3 participants