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

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.

3 Skipped Deployments
Project Deployment Review Updated (UTC)
admin.ensnode.io Skipped Skipped Jan 20, 2026 7:07pm
ensnode.io Skipped Skipped Jan 20, 2026 7:07pm
ensrainbow.io Skipped Skipped Jan 20, 2026 7:07pm

@changeset-bot
Copy link

changeset-bot bot commented Jan 19, 2026

⚠️ No Changeset found

Latest commit: aaf1a73

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 OpenAPI spec sync: CI job to validate committed openapi.json, a generation script and npm task to fetch/write the spec from a running ENSApi (CI mode), documentation updates including a hidden preview page, and a minimal ENSApi config mode for CI spec generation.

Changes

Cohort / File(s) Summary
CI Validation Job
\.github/workflows/test_ci.yml
New "OpenAPI Spec Sync Check" job that runs after tests, starts ENSApi with CI-check mode, waits for http://localhost:4334/openapi.json, generates the spec, formats it, and fails if the generated docs/openapi.json differs from the committed file.
OpenAPI Generation Script
docs/docs.ensnode.io/scripts/generate-openapi.ts
New CLI script that requires an ENSApi URL, fetches /openapi.json with timeout and explicit error messages, validates presence of info and paths, writes pretty docs/openapi.json, and runs Biome formatting (non-fatal on failure).
Docs config & preview
docs/docs.ensnode.io/docs.json, docs/docs.ensnode.io/ensapi/preview.mdx
docs.json: updates API Reference URL to https://api.alpha.ensnode.io/openapi.json and adds a hidden "Preview" group referencing local ./openapi.json. Adds ensapi/preview.mdx documenting the branch-preview spec.
Documentation content & guidance
docs/docs.ensnode.io/README.md
Adds OpenAPI Spec Management, generation instructions (required URL arg), CI validation details for openapi-sync-check, updated local development flow and troubleshooting, and guidance to commit openapi.json when routes/schemas change.
Docs tooling
docs/docs.ensnode.io/package.json
Adds openapi:generate npm script (tsx scripts/generate-openapi.ts) and tsx devDependency (^4.7.1).
ENSApi CI config mode
apps/ensapi/src/config/config.schema.ts, apps/ensapi/src/config/environment.ts
Adds OPENAPI_CI_CHECK env flag to the environment type and introduces buildConfigForOpenApiCiCheck that returns a mocked/minimal config when the flag is "true", causing buildConfigFromEnvironment to short-circuit to this mocked config for CI generation.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant Script as generate-openapi.ts
    participant API as ENSApi (CI mode)
    participant FS as Filesystem

    Dev->>Script: npm run openapi:generate <ENSApi URL>
    Script->>Script: validate arg
    Script->>API: GET /openapi.json
    API-->>Script: OpenAPI JSON
    Script->>Script: validate status & JSON (info, paths)
    Script->>FS: write docs/openapi.json (pretty)
    FS-->>Script: write complete
    Script->>Dev: log version & path count
Loading
sequenceDiagram
    participant Repo as Git Repo
    participant CI as GitHub Actions
    participant API as ENSApi (CI mode)
    participant Formatter as Biome
    participant RepoFiles as Repository files

    Repo->>CI: push triggers workflow
    CI->>CI: run unit tests
    CI->>CI: start ENSApi with OPENAPI_CI_CHECK=true
    CI->>API: poll /openapi.json
    API-->>CI: OpenAPI spec
    CI->>Formatter: format generated spec
    CI->>RepoFiles: compare generated vs committed docs/openapi.json
    alt specs match
        CI-->>Repo: job passes
    else specs differ
        CI-->>Repo: job fails with guidance
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped a branch to fetch the spec,
I fetched, I checked, I wrote it back correct.
CI watches closely, tidy and spry,
Docs in sync now—hop, twirl, and sigh. ✨

🚥 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 'poc' (proof of concept) without clearly conveying the main objective or change. Use a more descriptive title that clarifies the purpose, such as 'Add OpenAPI spec generation and sync validation for docs' or 'Implement OpenAPI spec sync check and documentation preview'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description follows the template with complete Summary, Why, Testing, and Notes for Reviewer sections, providing clear context and actionable information.

✏️ 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.

Comment on lines +32 to 35
"openapi": "./openapi.json",
"hidden": true
}
]
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

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

The "hidden" property may not be a valid field in the Mintlify docs.json schema. According to Mintlify documentation, there is no "hidden" property for navigation groups. If you want to hide this section, you should verify this property is supported or use a different approach.

Suggested change
"openapi": "./openapi.json",
"hidden": true
}
]
"openapi": "./openapi.json"
}
]
]

Copilot uses AI. Check for mistakes.
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 20, 2026 16:01 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 6 out of 7 changed files in this pull request and generated 2 comments.


💡 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 `@docs/docs.ensnode.io/scripts/generate-openapi.ts`:
- Around line 14-18: Replace the use of import.meta.dirname when computing
OUTPUT_PATH with the project-wide file URL pattern: convert import.meta.url to a
filesystem path via fileURLToPath(import.meta.url) and then resolve the relative
"../openapi.json"; update the const OUTPUT_PATH initialization (refer to
OUTPUT_PATH) and add the necessary import for fileURLToPath from "node:url" so
the resolve call uses the converted directory path instead of
import.meta.dirname.
♻️ Duplicate comments (2)
docs/docs.ensnode.io/README.md (2)

20-25: Align CI validation description with production comparison.

This bullet conflicts with the later statement that CI compares against production. It should reflect the production source.

📝 Suggested wording
-- 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.
+- Each branch has its own `openapi.json`, validated by `openapi-sync-check` against the production ENSApi `/openapi.json`.

29-31: Clarify how to run the generator from repo root.

The command as written only works if you’re already in the docs package. Consider using a filtered command so it’s correct from the repo root.

📝 Suggested update
-pnpm openapi:generate http://localhost:3223
+pnpm --filter `@docs/mintlify` openapi:generate http://localhost:3223

Comment on lines +14 to +18
import { writeFileSync } from "node:fs";
import { resolve } from "node:path";

const OUTPUT_PATH = resolve(import.meta.dirname, "../openapi.json");
const TIMEOUT_MS = 30000;
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for Node version constraint files and package.json node field
echo "=== Searching for Node version constraint files ==="
fd -a '\.nvmrc$|\.node-version$|\.tool-versions$' 2>/dev/null || echo "No version files found"

echo -e "\n=== Checking package.json for node version field ==="
fd -a 'package\.json$' | head -5 | while read f; do
  echo "File: $f"
  rg -A 2 '"engines".*node' "$f" 2>/dev/null || true
done

echo -e "\n=== Checking GitHub workflows for Node version ==="
fd -a '\.github/workflows' -type d 2>/dev/null && rg -n 'node-version|NODE_VERSION' .github/workflows -g '*.yml' -g '*.yaml' 2>/dev/null || echo "No workflows found or no node version specified"

Repository: namehash/ensnode

Length of output: 534


🏁 Script executed:

#!/bin/bash
# Search for other instances of import.meta.dirname in the codebase
echo "=== Searching for import.meta.dirname usage ==="
rg 'import\.meta\.dirname' -n

echo -e "\n=== Searching for fileURLToPath usage (to see if pattern is already used) ==="
rg 'fileURLToPath' -n

Repository: namehash/ensnode

Length of output: 674


🌐 Web query:

import.meta.dirname Node.js support version history ESM

💡 Result:

Short timeline for import.meta.dirname / import.meta.filename in Node.js (ESM):

  • Introduced (added): Node.js v20.11.0 and v21.2.0. [1][2]
  • Stability / no longer experimental: documented as stable in Node.js v22.16.0 and v24.0.0. [1]
  • Caveat: properties exist only for file: modules (i.e., local filesystem modules). [2][3]

References:
[1] Node.js ESM docs (history notes showing v20.11.0, v21.2.0 added; v22.16.0, v24.0.0 marked non-experimental).
[2] Node.js v23.11.1 ESM docs (notes on import.meta.dirname / filename; added in v21.2.0, v20.11.0).
[3] Node.js v22.x ESM docs (same caveat about file: modules).


Use fileURLToPath(import.meta.url) for consistency with the codebase pattern.

import.meta.dirname is a standard ESM feature (available since Node.js v20.11.0), but the codebase already uses the fileURLToPath(import.meta.url) pattern in apps/ensrainbow/src/cli.ts and apps/ensindexer/src/lib/version-info.ts. For consistency, apply the same approach here.

🛠️ Suggested fix
-import { resolve } from "node:path";
+import { resolve } from "node:path";
+import { fileURLToPath } from "node:url";

-const OUTPUT_PATH = resolve(import.meta.dirname, "../openapi.json");
+const OUTPUT_PATH = resolve(
+  fileURLToPath(new URL(".", import.meta.url)),
+  "../openapi.json",
+);
📝 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
import { writeFileSync } from "node:fs";
import { resolve } from "node:path";
const OUTPUT_PATH = resolve(import.meta.dirname, "../openapi.json");
const TIMEOUT_MS = 30000;
import { writeFileSync } from "node:fs";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
const OUTPUT_PATH = resolve(
fileURLToPath(new URL(".", import.meta.url)),
"../openapi.json",
);
const TIMEOUT_MS = 30000;
🤖 Prompt for AI Agents
In `@docs/docs.ensnode.io/scripts/generate-openapi.ts` around lines 14 - 18,
Replace the use of import.meta.dirname when computing OUTPUT_PATH with the
project-wide file URL pattern: convert import.meta.url to a filesystem path via
fileURLToPath(import.meta.url) and then resolve the relative "../openapi.json";
update the const OUTPUT_PATH initialization (refer to OUTPUT_PATH) and add the
necessary import for fileURLToPath from "node:url" so the resolve call uses the
converted directory path instead of import.meta.dirname.

Copilot AI review requested due to automatic review settings January 20, 2026 18:53
@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io January 20, 2026 18:53 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io January 20, 2026 18:53 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 20, 2026 18:53 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.

echo "❌ OpenAPI spec is out of sync"
echo ""
echo "The committed openapi.json differs from what ENSApi generates."
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 20, 2026

Choose a reason for hiding this comment

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

The error message instructs users to run commands with the filter @docs/mintlify, but the package.json shows the package name is actually docs.ensnode.io. The correct command should reference the package name from package.json line 2. Update the instruction to use the correct filter or package reference.

Suggested change
echo "Run 'OPENAPI_CI_CHECK=true pnpm --filter ensapi start' then 'pnpm --filter @docs/mintlify openapi:generate http://localhost:4334' and commit the changes."
echo "Run 'OPENAPI_CI_CHECK=true pnpm --filter ensapi start' then 'pnpm --filter docs.ensnode.io openapi:generate http://localhost:4334' and commit the changes."

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +17

const OUTPUT_PATH = resolve(import.meta.dirname, "../openapi.json");
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The script uses import.meta.dirname which is only available in Node.js 20.11.0+ and ESM. While tsx version 4.20.6 supports this, consider adding a comment or runtime check to ensure compatibility, or document the minimum Node.js version requirement.

Suggested change
const OUTPUT_PATH = resolve(import.meta.dirname, "../openapi.json");
import { fileURLToPath } from "node:url";
// Determine the directory of this script in a way that works both with and without import.meta.dirname (Node.js < 20.11.0).
const SCRIPT_DIR =
typeof import.meta.dirname === "string"
? import.meta.dirname
: fileURLToPath(new URL(".", import.meta.url));
const OUTPUT_PATH = resolve(SCRIPT_DIR, "../openapi.json");

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +24
- 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.
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The documentation states that PR previews show the committed openapi.json file, but there's no mechanism shown in the PR to actually trigger Mintlify builds for PR previews. The description mentions needing to "curl step inside the switch environment to trigger production build using the Mintlify API" but this workflow isn't included in the changes. Consider adding documentation or the workflow for triggering Mintlify PR preview deployments.

Copilot uses AI. Check for mistakes.
},
ensHolidayAwardsStart: getUnixTime(new Date(ENS_HOLIDAY_AWARDS_START_DATE)),
ensHolidayAwardsEnd: getUnixTime(new Date(ENS_HOLIDAY_AWARDS_END_DATE)),
}) as EnsApiConfig;
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The cast to EnsApiConfig at line 127 bypasses type safety because EnsApiConfigSchemaForOpenApiCiCheck doesn't include the invariant checks from EnsApiConfigSchema (lines 73-75). This means the mock config could theoretically violate invariants that would be checked in normal operation. Consider either applying the invariant checks to the mock config or adding a comment explaining why these checks are safely skipped in CI mode.

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +69
for i in {1..30}; do
if curl -s http://localhost:4334/openapi.json > /dev/null 2>&1; then
echo "ENSApi is ready"
break
fi
echo "Waiting for ENSApi to start... ($i/30)"
sleep 1
done
Copy link

Choose a reason for hiding this comment

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

The server startup wait loop doesn't fail if the ENSApi server never becomes ready. The CI workflow will continue to the next steps even if the server failed to start, causing misleading test results or errors.

View Details
📝 Patch Details
diff --git a/.github/workflows/test_ci.yml b/.github/workflows/test_ci.yml
index 246b53d7..6d1d0dd4 100644
--- a/.github/workflows/test_ci.yml
+++ b/.github/workflows/test_ci.yml
@@ -67,6 +67,12 @@ jobs:
             echo "Waiting for ENSApi to start... ($i/30)"
             sleep 1
           done
+          
+          # Verify server is actually running
+          if ! curl -s http://localhost:4334/openapi.json > /dev/null 2>&1; then
+            echo "❌ Failed: ENSApi did not start within 30 seconds"
+            exit 1
+          fi
 
       - name: Generate OpenAPI spec from local build
         working-directory: docs/docs.ensnode.io

Analysis

Server startup wait loop doesn't fail if ENSApi server never becomes ready

What fails: The openapi-sync-check workflow step's server startup loop completes with exit code 0 even when the ENSApi server fails to start, allowing the workflow to proceed to the next step where it will fail with a confusing error message.

How to reproduce:

  1. Introduce a failure in the pnpm --filter ensapi start command (e.g., missing dependencies)
  2. Run the workflow - the "Start ENSApi in OpenAPI CI check mode" step will complete successfully
  3. The next step "Generate OpenAPI spec from local build" will fail with "Error: fetch failed" because the server never started
  4. Developers will be confused about whether the server started or if the generation script has an issue

Result: The waiting loop completes with exit code 0 after 30 iterations regardless of server status. The workflow continues to the next step, which then fails with an unclear error message.

Expected: The step should immediately fail with a clear message indicating the server failed to start within 30 seconds, making it obvious what went wrong.

Fix applied: Added an explicit verification check after the loop to confirm the server is running. If the server is not responding within the timeout period, the step now exits with code 1 and displays a clear error message: "Failed: ENSApi did not start within 30 seconds"

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-69: The readiness loop starting the ENSApi
(OPENAPI_CI_CHECK=true pnpm --filter ensapi start &) uses curl -s which treats
non‑2xx as success and never fails the job; update the probe that hits
http://localhost:4334/openapi.json inside the for i in {1..30} loop to use
curl's fail and timeout flags (e.g., --fail and --max-time) so non‑2xx responses
are treated as failures, and after the retries expire ensure the script exits
non‑zero with an explicit error message so the CI step fails instead of
proceeding.

Comment on lines +58 to +69
- name: Start ENSApi in OpenAPI CI check mode
run: |
OPENAPI_CI_CHECK=true pnpm --filter ensapi start &
# Wait for server to be ready
for i in {1..30}; do
if curl -s http://localhost:4334/openapi.json > /dev/null 2>&1; then
echo "ENSApi is ready"
break
fi
echo "Waiting for ENSApi to start... ($i/30)"
sleep 1
done
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

Make the readiness probe fail on non‑2xx and timeout.

curl -s returns success even on 404/500, and the loop never fails explicitly. This can cause false readiness or unclear failures later.

✅ Suggested fix
       - name: Start ENSApi in OpenAPI CI check mode
         run: |
           OPENAPI_CI_CHECK=true pnpm --filter ensapi start &
           # Wait for server to be ready
-          for i in {1..30}; do
-            if curl -s http://localhost:4334/openapi.json > /dev/null 2>&1; then
+          ready=false
+          for i in {1..30}; do
+            if curl -fsS http://localhost:4334/openapi.json > /dev/null; then
               echo "ENSApi is ready"
+              ready=true
               break
             fi
             echo "Waiting for ENSApi to start... ($i/30)"
             sleep 1
           done
+          if [ "$ready" != "true" ]; then
+            echo "ENSApi failed to become ready in time"
+            exit 1
+          fi
🤖 Prompt for AI Agents
In @.github/workflows/test_ci.yml around lines 58 - 69, The readiness loop
starting the ENSApi (OPENAPI_CI_CHECK=true pnpm --filter ensapi start &) uses
curl -s which treats non‑2xx as success and never fails the job; update the
probe that hits http://localhost:4334/openapi.json inside the for i in {1..30}
loop to use curl's fail and timeout flags (e.g., --fail and --max-time) so
non‑2xx responses are treated as failures, and after the retries expire ensure
the script exits non‑zero with an explicit error message so the CI step fails
instead of proceeding.

Copilot AI review requested due to automatic review settings January 20, 2026 19:07
@vercel vercel bot temporarily deployed to Preview – ensnode.io January 20, 2026 19:07 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 20, 2026 19:07 Inactive
@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io January 20, 2026 19:07 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 3 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.

Comment on lines +95 to +97
execSync(`pnpm biome format --write ${OUTPUT_PATH}`, {
stdio: "inherit",
});
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The Biome formatting command uses string interpolation without proper escaping, which could be a security issue if OUTPUT_PATH contains special shell characters. Use the array form of execSync or properly escape the path to prevent command injection vulnerabilities.

Copilot uses AI. Check for mistakes.
echo "❌ OpenAPI spec is out of sync"
echo ""
echo "The committed openapi.json differs from what ENSApi generates."
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 20, 2026

Choose a reason for hiding this comment

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

The filter name in the error message shows "@docs/mintlify" but the package.json shows the workspace name as "docs.ensnode.io". Update the error message to use the correct filter name for consistency.

Suggested change
echo "Run 'OPENAPI_CI_CHECK=true pnpm --filter ensapi start' then 'pnpm --filter @docs/mintlify openapi:generate http://localhost:4334' and commit the changes."
echo "Run 'OPENAPI_CI_CHECK=true pnpm --filter ensapi start' then 'pnpm --filter docs.ensnode.io openapi:generate http://localhost:4334' and commit the changes."

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

Copilot AI Jan 20, 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 a different port (3223) than what's used in the README and CI workflow (4334). This inconsistency could confuse developers. Consider updating the example port to match the default port used elsewhere in the codebase.

Copilot uses AI. Check for mistakes.
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.

3 participants