Skip to content

integration-test-env: add start (brings up the stack only, no tests)#2091

Open
shrugs wants to merge 3 commits into
mainfrom
fix/integration-test-env-up-down
Open

integration-test-env: add start (brings up the stack only, no tests)#2091
shrugs wants to merge 3 commits into
mainfrom
fix/integration-test-env-up-down

Conversation

@shrugs
Copy link
Copy Markdown
Member

@shrugs shrugs commented May 11, 2026

Summary

  • split the integration-test-env orchestrator into a reusable bring-up phase (lifecycle.ts) shared by two entrypoints
  • new pnpm -F @ensnode/integration-test-env start — bring up the full stack (incl. seed) and block until Ctrl+C, so pnpm test:integration can run from another terminal
  • the previous start script is renamed to start:ci (bring up + run tests + tear down); the root test:integration:ci alias is updated to match so CI behavior is unchanged
  • new --only flag: pnpm start --only devnet,ensrainbow restricts the subset of services brought up, so you can iterate on ensindexer/ensapi locally and have the rest auto-managed + auto-cleaned. valid services: devnet (incl. ensdb + seed), ensrainbow, ensindexer (incl. wait-for-indexing), ensapi
  • ensrainbow now runs at LOG_LEVEL=error so its info/warn chatter doesn't drown out the rest of the stack output
  • Ctrl+C during start exits 0 (user-initiated shutdown is not an error)

Why

Running the integration test env manually (docker compose up devnet ensrainbow + standalone ensapi) skips the orchestrator's seedDevnet phase, so resolver/primary-name records aren't on chain and 6 resolution integration tests fail. This adds a one-shot manual path that runs the same lifecycle CI does (seed included), without coupling it to a test run.

--only exists so when iterating on ensindexer or ensapi (running them yourself in dev mode), the supporting services come up and tear down with one command instead of separate docker compose invocations.

Testing

  • pnpm -F @ensnode/integration-test-env typecheck
  • pnpm lint
  • end-to-end run of pnpm start / Ctrl+C teardown not exercised locally (existing CI flow is unchanged because start:ci is identical to the old start modulo a one-line extracted call). Worth a manual spot-check before merge.

Notes for Reviewer (Optional)

  • lifecycle.ts is a mechanical extraction of phases 1–6 from orchestrator.ts — diff is large only because of the move; the only logic change is one await waitForHealth(...) URL now uses endpoints.ensapi instead of an inline string.
  • SIGINT/SIGTERM handlers are registered at module load in lifecycle.ts so both entrypoints share them automatically.
  • --only gates each phase via a should(service) check inside bringUp. ci.ts passes no options → full stack as before.

Checklist

  • This PR does not change runtime behavior or semantics
  • This PR is low-risk and safe to review quickly

🤖 Generated with Claude Code

@shrugs shrugs requested a review from a team as a code owner May 11, 2026 20:25
Copilot AI review requested due to automatic review settings May 11, 2026 20:25
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 11, 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 May 18, 2026 4:27pm
enskit-react-example.ensnode.io Ready Ready Preview, Comment May 18, 2026 4:27pm
ensnode.io Ready Ready Preview, Comment May 18, 2026 4:27pm
ensrainbow.io Ready Ready Preview, Comment May 18, 2026 4:27pm

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 11, 2026

⚠️ No Changeset found

Latest commit: 8f0fd1f

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
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors the integration test environment from a monolithic orchestrator into a composable lifecycle module. Startup logic, test execution, and cleanup become reusable exported functions, supporting two distinct entry points: pnpm start for local development and pnpm start:ci for CI automation. Root test:integration:ci script now explicitly calls the CI variant.

Changes

Lifecycle Refactoring and Entrypoint Split

Layer / File(s) Summary
Lifecycle module exports and infrastructure
packages/integration-test-env/src/lifecycle.ts
Module documentation updated to reflect lifecycle concept. New exports: endpoints constant, ALL_SERVICES allowlist, Service type, and parseOnly() utility for comma-separated service filtering. Cleanup function promoted to export. Signal handlers (SIGINT/SIGTERM) now exit with code 0 after cleanup instead of error exit.
Core orchestration and test execution
packages/integration-test-env/src/lifecycle.ts
bringUp(options) orchestrates conditional 6-phase startup: docker-compose services, devnet seeding, ENSRainbow, ENSIndexer with indexing status polling, ENSApi, with optional filtering by service selection. runIntegrationTests() runs pnpm test:integration with ENSNODE_URL pointing to endpoints.ensapi.
CI orchestrator entrypoint
packages/integration-test-env/src/ci.ts
New entrypoint orchestrates CI flow: call bringUp(), start integration tests, perform cleanup, exit with status 0 on success or 1 on error.
Local development entrypoint
packages/integration-test-env/src/start.ts
New CLI entrypoint for long-lived local development: parse optional --only filter for service selection, call bringUp() with filter, log enabled service endpoints, block indefinitely until manual interruption. Errors trigger cleanup and exit with status 1.
Script configuration and root integration
packages/integration-test-env/package.json, package.json
Integration-test-env start script now runs tsx src/start.ts (local development mode). Root package.json test:integration:ci updated to invoke integration-test-env start:ci instead of start.
README documentation
packages/integration-test-env/README.md
"How It Works" section rewritten with formatted 6-phase lifecycle list. Usage section expanded into two subsections: "Bring up the stack (manual)" for pnpm start and "Full CI pipeline" for pnpm start:ci, clarifying when ports must be available for each mode.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • namehash/ensnode#1729: Both PRs refactor integration test environment CI orchestration, adding or reshaping src/ci.ts and wiring test:integration:ci / start scripts, with this PR extracting shared logic into reusable lifecycle.ts and start.ts modules.
  • namehash/ensnode#1873: Both PRs evolve the integration-test flow; this PR refactors how the integration test environment starts and runs tests in CI, while the retrieved PR adds new ENSApi integration test suites executed by that pipeline.

Poem

🐰 Refactored the orch with care and grace,
Now tests run in CI's rightful place,
With lifecycle flows so clean and clear,
Local dev and CI, no more to fear! 🚀

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The linked issue (#39) concerns missing accounts in snapshot queries with no relation to the integration-test-env refactoring in this PR. This PR does not address the objectives of linked issue #39. Verify the correct issue is linked or that this PR is indeed addressing the intended problem.
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a start command that brings up the integration test stack without running tests.
Description check ✅ Passed The description comprehensively covers summary, motivation, testing approach, and reviewer notes following the template structure with clear explanations.
Out of Scope Changes check ✅ Passed All changes (lifecycle extraction, new start entrypoint, --only flag, logging level adjustment) are consistent with the declared objectives of refactoring the integration test orchestration.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/integration-test-env-up-down

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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
Copy Markdown
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 refactors the integration test environment orchestrator into a shared lifecycle module so the stack bring-up (including devnet seeding) can be reused by both CI and a new manual “bring up and wait” workflow.

Changes:

  • Extracts stack bring-up/cleanup/test execution logic into src/lifecycle.ts shared by both entrypoints.
  • Adds a new start entrypoint that brings up the full stack and blocks until Ctrl+C, and renames the prior behavior to start:ci.
  • Updates root test:integration:ci to call the new start:ci script to keep CI behavior consistent.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/integration-test-env/src/start.ts New manual entrypoint to bring up the stack and wait (no tests).
packages/integration-test-env/src/orchestrator.ts Simplified CI entrypoint now delegating to lifecycle.ts.
packages/integration-test-env/src/lifecycle.ts New shared bring-up/cleanup/signal-handling and integration test runner.
packages/integration-test-env/README.md Documents the split between manual start and CI start:ci flows.
packages/integration-test-env/package.json Renames start behavior and adds start:ci script.
package.json Updates test:integration:ci to call start:ci.
Comments suppressed due to low confidence (2)

packages/integration-test-env/src/lifecycle.ts:245

  • The JSDoc for bringUp() says it "runs cleanup() and rethrows" on failure, but bringUp() doesn't wrap its phases in a try/catch/finally and doesn't call cleanup() itself (callers do). Please update the comment to match the actual behavior, or add error-handling inside bringUp() if that's the intended contract.
    packages/integration-test-env/src/lifecycle.ts:117
  • handleShutdown() always exits with code 1 after SIGINT/SIGTERM. For the new manual pnpm start flow (Ctrl+C as a normal teardown), this will report failure to the shell/CI wrappers. Consider exiting 0 for SIGINT (or when CI is not set), and reserve exit code 1 for actual failures/forced termination.

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

Comment on lines +39 to +43
Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 8000, 3223, 42069, 4334). Once it's up, run integration tests from another terminal:

```sh
pnpm test:integration
```
Comment on lines 8 to 12
"scripts": {
"start": "CI=1 tsx src/orchestrator.ts",
"start": "tsx src/start.ts",
"start:ci": "CI=1 tsx src/ci.ts",
"typecheck": "tsc --noEmit"
},
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 11, 2026

Greptile Summary

This PR splits the integration-test-env orchestrator into a shared lifecycle.ts module consumed by two entrypoints: start.ts (bring up the stack and block until Ctrl+C) and ci.ts (bring up + run tests + tear down), preserving identical CI behaviour while enabling a new one-shot manual developer flow with an optional --only flag for subset service startup.

  • lifecycle.ts is a near-mechanical extraction of orchestrator.ts's bring-up phases into exportable functions (bringUp, cleanup, runIntegrationTests), with endpoints and parseOnly added for the new --only feature.
  • start.ts parks in an unresolvable Promise after bringUp; SIGINT/SIGTERM handlers registered in lifecycle.ts at import time drive cleanup and exit 0 for clean shutdowns.
  • Root test:integration:ci and the package start:ci script are updated to invoke the renamed CI entrypoint; no runtime behaviour change for CI.

Confidence Score: 5/5

Safe to merge — this is a mechanical extraction and script rename with no changes to the underlying bring-up or test-execution logic.

The orchestrator logic is unchanged; only its packaging changed. The new bringUp phases are guarded by the same should() predicate pattern used throughout. start.ts correctly delegates signal handling to lifecycle.ts handlers. ci.ts preserves the original error-propagation and cleanup contract. The only notable runtime behaviour change is the handleShutdown exit code moving from 1 to 0, which is intentional and documented.

No files require special attention.

Important Files Changed

Filename Overview
packages/integration-test-env/src/lifecycle.ts Mechanical extraction of the former orchestrator.ts into a reusable module; exports bringUp, cleanup, runIntegrationTests, endpoints, and parseOnly. One minor URL inconsistency remains (ENSIndexer health check still uses a raw interpolated URL instead of endpoints.ensindexer).
packages/integration-test-env/src/ci.ts New CI entrypoint replacing the old orchestrator.ts main(); calls bringUp then runIntegrationTests (synchronous execaSync, no await needed) then cleanup then exit 0. Error path correctly delegates to .catch handler.
packages/integration-test-env/src/start.ts New interactive entrypoint; parses --only flag, calls bringUp, prints endpoint URLs, then parks in an unresolvable Promise. Ctrl+C is handled by lifecycle.ts signal handlers.
packages/integration-test-env/package.json start renamed to start:ci (with CI=1), new start points to start.ts without CI=1; straightforward script rename.
package.json Root test:integration:ci alias updated from start to start:ci to match renamed script; CI behaviour is unchanged.
packages/integration-test-env/README.md Documentation updated to reflect the two-entrypoint design and new --only flag; accurate and clear.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([pnpm start]) --> S[start.ts]
    B([pnpm start:ci]) --> C[ci.ts]

    S --> BU[lifecycle.bringUp]
    C --> BU

    BU --> D{only?}
    D -- devnet --> P1[Start ENSDb + Devnet\ndocker-compose]
    P1 --> P2[Seed Devnet]
    D -- ensrainbow --> P3[Start ENSRainbow\nwait /ready]
    D -- ensindexer --> P4[Start ENSIndexer\nwait /health]
    P4 --> P5[Poll indexing status]
    D -- ensapi --> P6[Start ENSApi\nwait /health]

    BU --> UP[Stack healthy]

    UP --> S2[start.ts: block\nawait Promise never]
    UP --> C2[ci.ts: runIntegrationTests\nexecaSync pnpm test:integration]

    C2 --> CL[lifecycle.cleanup]
    CL --> EXIT0([exit 0])

    S2 -- Ctrl+C SIGINT --> SH[handleShutdown]
    SH --> CL2[lifecycle.cleanup]
    CL2 --> EXIT0b([exit 0])

    C2 -- test failure --> ERR[.catch handler]
    ERR --> CL3[lifecycle.cleanup]
    CL3 --> EXIT1([exit 1])
Loading

Reviews (3): Last reviewed commit: "fix: allow --only" | Re-trigger Greptile

Comment on lines +241 to +245
* Bring up the integration test environment: ENSDb + Devnet, seed, ENSRainbow, ENSIndexer,
* wait for indexing to complete, ENSApi. Returns once every service is healthy.
*
* On failure, runs cleanup() and rethrows.
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The JSDoc for bringUp() says "On failure, runs cleanup() and rethrows", but bringUp itself never calls cleanup() — it simply throws and leaves cleanup to the caller. Both ci.ts and start.ts do handle this correctly in their .catch() blocks, so runtime behaviour is fine, but the comment will mislead future readers about where cleanup responsibility sits.

Suggested change
* Bring up the integration test environment: ENSDb + Devnet, seed, ENSRainbow, ENSIndexer,
* wait for indexing to complete, ENSApi. Returns once every service is healthy.
*
* On failure, runs cleanup() and rethrows.
*/
* Bring up the integration test environment: ENSDb + Devnet, seed, ENSRainbow, ENSIndexer,
* wait for indexing to complete, ENSApi. Returns once every service is healthy.
*
* On failure, throws callers are responsible for calling cleanup().
*/

@vercel vercel Bot temporarily deployed to Preview – ensnode.io May 11, 2026 20:44 Inactive
@vercel vercel Bot temporarily deployed to Preview – ensrainbow.io May 11, 2026 20:44 Inactive
@vercel vercel Bot temporarily deployed to Preview – admin.ensnode.io May 11, 2026 20:44 Inactive
```

Works both in CI and locally — just make sure the required ports are available (8545, 8000, 3223, 42069, 4334).
Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 8000, 3223, 42069, 4334). Once it's up, run integration tests from another terminal:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 8000, 3223, 42069, 4334). Once it's up, run integration tests from another terminal:
Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 3223, 42069, 4334, 5433). Once it's up, run integration tests from another terminal:

README lists incorrect ports (includes non-existent 8000, omits PostgreSQL port 5433)

Fix on Vercel

* Note: `devnet` includes ensdb (coupled via docker-compose) and seeding. `ensindexer` includes
* waiting for indexing to reach following/completed.
*
* On failure, runs cleanup() and rethrows.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
* On failure, runs cleanup() and rethrows.
* On failure, throws callers are responsible for calling cleanup().

JSDoc for bringUp() incorrectly claims the function calls cleanup() on failure, but it doesn't—cleanup is the caller's responsibility

Fix on Vercel

shrugs and others added 3 commits May 18, 2026 11:25
Splits the integration-test-env orchestrator into a reusable bring-up
phase (`lifecycle.ts`) shared by two entrypoints:

- `pnpm start` — bring up the full stack (incl. seed) and block until
  Ctrl+C, so `pnpm test:integration` can run from another terminal.
- `pnpm start:ci` — renamed from `start`; bring up + run tests + tear
  down. CI flow is unchanged via the `test:integration:ci` root alias.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The file no longer orchestrates — lifecycle.ts does. ci.ts is now the
CI entrypoint that wires bringUp + runIntegrationTests + cleanup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
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 6 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (2)

packages/integration-test-env/src/lifecycle.ts:140

  • Because lifecycle.ts is shared by start:ci, this handler now exits 0 for SIGTERM/SIGINT during the CI test flow too. A termination before the integration tests finish (for example a timeout or service-manager SIGTERM) can therefore be reported as a successful test:integration:ci run; keep the zero exit behavior scoped to the manual start entrypoint or distinguish SIGINT from CI termination.
    packages/integration-test-env/src/lifecycle.ts:275
  • This exported function's documentation says it runs cleanup on failure, but the implementation does not catch errors or call cleanup; cleanup currently happens only in the entrypoint catch blocks. This mismatch can mislead future callers into leaking started containers/processes if they rely on the documented contract.

Comment on lines +9 to +10
"start": "tsx src/start.ts",
"start:ci": "CI=1 tsx src/ci.ts",
Comment on lines +41 to +43
```sh
pnpm test:integration
```
```

Works both in CI and locally — just make sure the required ports are available (8545, 8000, 3223, 42069, 4334).
Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 8000, 3223, 42069, 4334). Once it's up, run integration tests from another terminal:
Comment on lines +54 to +55
// Block forever — SIGINT/SIGTERM handlers in lifecycle.ts call cleanup() and exit.
await new Promise<never>(() => {});
```

Works both in CI and locally — just make sure the required ports are available (8545, 8000, 3223, 42069, 4334).
Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 8000, 3223, 42069, 4334). Once it's up, run integration tests from another terminal:
Copy link
Copy Markdown
Contributor

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/integration-test-env/src/lifecycle.ts (1)

134-144: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't report every SIGTERM shutdown as success.

SIGTERM is also what CI runners and supervisors use for cancellation/timeouts. Exiting 0 here can make an aborted start:ci run look green. If you want local Ctrl+C to be clean, keep SIGINT at 0 but preserve a non-zero or signal-derived exit for SIGTERM.

Suggested fix
-async function handleShutdown() {
+async function handleShutdown(signal: NodeJS.Signals) {
   if (cleanupInProgress) return;
   cleanupInProgress = true;
   log("Shutting down...");
   await cleanup();
-  // SIGINT/SIGTERM is a user-initiated shutdown, not an error — exit 0.
-  process.exit(0);
+  process.exit(signal === "SIGINT" ? 0 : 128 + 15);
 }
 
-process.on("SIGINT", handleShutdown);
-process.on("SIGTERM", handleShutdown);
+process.on("SIGINT", () => void handleShutdown("SIGINT"));
+process.on("SIGTERM", () => void handleShutdown("SIGTERM"));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/integration-test-env/src/lifecycle.ts` around lines 134 - 144, The
current handleShutdown always calls process.exit(0) which hides CI/supervisor
cancellations; change the shutdown wiring so SIGINT still exits 0 but SIGTERM
yields a non-zero/signal-derived exit code: update handleShutdown to accept an
optional signal name (e.g., function handleShutdown(signal?: string)) and after
await cleanup() call process.exit(0) when signal === "SIGINT" but
process.exit(1) or process.exit(128 + <signalNumber>) for other signals (e.g.,
"SIGTERM"); register handlers with process.on("SIGINT", () =>
handleShutdown("SIGINT")) and process.on("SIGTERM", () =>
handleShutdown("SIGTERM")) while preserving cleanupInProgress and the cleanup()
call.
♻️ Duplicate comments (2)
packages/integration-test-env/README.md (1)

39-39: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the incorrect port list.

This line still lists incorrect ports. Port 8000 does not exist in the stack, and PostgreSQL port 5433 is missing. The correct required ports are: 8545, 3223, 42069, 4334, 5433.

📝 Proposed fix
-Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 8000, 3223, 42069, 4334). Once it's up, run integration tests from another terminal:
+Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 3223, 42069, 4334, 5433). Once it's up, run integration tests from another terminal:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/integration-test-env/README.md` at line 39, Update the port list in
the README line that starts "Brings up the full stack and blocks until Ctrl+C."
to show the correct required ports: 8545, 3223, 42069, 4334, 5433 (remove 8000
and add 5433). Edit that sentence in packages/integration-test-env/README.md so
the parentheses contain the corrected port set.
packages/integration-test-env/src/lifecycle.ts (1)

275-383: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

bringUp() still leaks partial startup on failure.

After phase 1/3/4 acquires resources, any later throw here rejects without tearing them down. That leaves detached processes and compose services running on startup errors, and Line 275 currently promises the opposite. Wrap the phase body in try/catch, call cleanup(), then rethrow so the exported lifecycle contract matches its behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/integration-test-env/src/lifecycle.ts` around lines 275 - 383,
bringUp() can leak started resources if a later phase throws; wrap the main
phase sequence inside a try/catch in the bringUp function, and in the catch call
await cleanup() (which will stop composeEnvironment and spawned services like
those started via spawnService and any started in phases
"devnet"/"ensrainbow"/"ensindexer"/"ensapi"), then rethrow the original error so
the function's contract holds; ensure you reference and use the existing
cleanup() helper and do not swallow the error when rethrowing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/integration-test-env/package.json`:
- Line 10: The package.json "start:ci" script exports CI=1 but that env var is
never used; either remove "CI=1" from the "start:ci" entry in package.json, or
update the code to consume it (e.g., check process.env.CI in src/ci.ts or
src/lifecycle.ts and gate CI-specific behavior such as tighter timeouts, quieter
logging, or alternate resource allocation). Ensure any new behavior is clearly
named and documented in the script and that process.env.CI is parsed as a
boolean where used.

In `@packages/integration-test-env/README.md`:
- Around line 35-43: Add documentation for the new --only flag in the section
that explains how to run the stack with pnpm start: state that you can pass
--only with a comma-separated list of services (valid values: devnet,
ensrainbow, ensindexer, ensapi), show a brief usage example like pnpm start
--only devnet,ensrainbow, and mention that this starts only the listed services
instead of the full stack so ports for the omitted services are not required.

In `@packages/integration-test-env/src/ci.ts`:
- Line 24: runIntegrationTests() is being invoked without awaiting, causing
cleanup() and process.exit(0) to run before tests finish; change the invocation
to await runIntegrationTests() and ensure it's inside an async context (or use
.then/.catch) so the process only calls cleanup() and process.exit(0) after
runIntegrationTests() resolves; refer to runIntegrationTests(), cleanup(), and
process.exit to locate and update the call site and surrounding control flow
(use try/finally or promise chaining to guarantee cleanup runs after the awaited
test run).

In `@packages/integration-test-env/src/lifecycle.ts`:
- Around line 59-77: Add zod to packages/integration-test-env package.json
dependencies, then replace the manual validation in parseOnly with a zod schema:
create a Zod schema that accepts a comma-separated string,
splits/trims/filter(Boolean) and validates each item is one of the allowed
values from ALL_SERVICES, then call zod.parse(...) to validate the incoming
value; use the parsed array to construct and return a Set<Service> (removing the
unchecked "as Service[]" cast) and ensure any validation errors bubble up as zod
errors from parseOnly.

---

Outside diff comments:
In `@packages/integration-test-env/src/lifecycle.ts`:
- Around line 134-144: The current handleShutdown always calls process.exit(0)
which hides CI/supervisor cancellations; change the shutdown wiring so SIGINT
still exits 0 but SIGTERM yields a non-zero/signal-derived exit code: update
handleShutdown to accept an optional signal name (e.g., function
handleShutdown(signal?: string)) and after await cleanup() call process.exit(0)
when signal === "SIGINT" but process.exit(1) or process.exit(128 +
<signalNumber>) for other signals (e.g., "SIGTERM"); register handlers with
process.on("SIGINT", () => handleShutdown("SIGINT")) and process.on("SIGTERM",
() => handleShutdown("SIGTERM")) while preserving cleanupInProgress and the
cleanup() call.

---

Duplicate comments:
In `@packages/integration-test-env/README.md`:
- Line 39: Update the port list in the README line that starts "Brings up the
full stack and blocks until Ctrl+C." to show the correct required ports: 8545,
3223, 42069, 4334, 5433 (remove 8000 and add 5433). Edit that sentence in
packages/integration-test-env/README.md so the parentheses contain the corrected
port set.

In `@packages/integration-test-env/src/lifecycle.ts`:
- Around line 275-383: bringUp() can leak started resources if a later phase
throws; wrap the main phase sequence inside a try/catch in the bringUp function,
and in the catch call await cleanup() (which will stop composeEnvironment and
spawned services like those started via spawnService and any started in phases
"devnet"/"ensrainbow"/"ensindexer"/"ensapi"), then rethrow the original error so
the function's contract holds; ensure you reference and use the existing
cleanup() helper and do not swallow the error when rethrowing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 39dbe864-f7b1-49df-b27d-6acdfbf99110

📥 Commits

Reviewing files that changed from the base of the PR and between 26dc2da and 8f0fd1f.

📒 Files selected for processing (6)
  • package.json
  • packages/integration-test-env/README.md
  • packages/integration-test-env/package.json
  • packages/integration-test-env/src/ci.ts
  • packages/integration-test-env/src/lifecycle.ts
  • packages/integration-test-env/src/start.ts

"scripts": {
"start": "CI=1 tsx src/orchestrator.ts",
"start": "tsx src/start.ts",
"start:ci": "CI=1 tsx src/ci.ts",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for references to process.env.CI in the integration-test-env package.

rg -nP --type=ts -g 'packages/integration-test-env/**' '\bprocess\.env\.CI\b'

Repository: namehash/ensnode

Length of output: 42


🏁 Script executed:

# Check the structure of the integration-test-env package
find packages/integration-test-env -type f -name "*.ts" -o -name "*.js" | head -20

Repository: namehash/ensnode

Length of output: 561


🏁 Script executed:

# Look at the ci.ts file to understand what it does
cat packages/integration-test-env/src/ci.ts

Repository: namehash/ensnode

Length of output: 1180


🏁 Script executed:

# Check if lifecycle.ts exists and where
find packages/integration-test-env -name "lifecycle.ts" -o -name "lifecycle.js"

Repository: namehash/ensnode

Length of output: 107


🏁 Script executed:

# Search for any CI environment variable usage in broader context (not just process.env.CI)
rg -n 'CI' packages/integration-test-env/src --type=ts -B2 -A2

Repository: namehash/ensnode

Length of output: 1379


🏁 Script executed:

# Check the package.json dependencies to understand what lifecycle.ts might be from
cat packages/integration-test-env/package.json

Repository: namehash/ensnode

Length of output: 738


🏁 Script executed:

# Examine the full lifecycle.ts to check for CI environment variable usage
cat packages/integration-test-env/src/lifecycle.ts

Repository: namehash/ensnode

Length of output: 12435


Remove or use the unused CI=1 environment variable.

The start:ci script sets CI=1, but this variable is never referenced in ci.ts, lifecycle.ts, or any other module in the integration-test-env package. Either remove this environment variable from the script or update the code to consume it for conditional behavior (e.g., logging, timeouts, or resource allocation).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/integration-test-env/package.json` at line 10, The package.json
"start:ci" script exports CI=1 but that env var is never used; either remove
"CI=1" from the "start:ci" entry in package.json, or update the code to consume
it (e.g., check process.env.CI in src/ci.ts or src/lifecycle.ts and gate
CI-specific behavior such as tighter timeouts, quieter logging, or alternate
resource allocation). Ensure any new behavior is clearly named and documented in
the script and that process.env.CI is parsed as a boolean where used.

Comment on lines 35 to +43
```sh
pnpm start
```

Works both in CI and locally — just make sure the required ports are available (8545, 8000, 3223, 42069, 4334).
Brings up the full stack and blocks until Ctrl+C. The required ports must be available (8545, 8000, 3223, 42069, 4334). Once it's up, run integration tests from another terminal:

```sh
pnpm test:integration
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider documenting the --only flag for partial stack bring-up.

The PR introduces an --only flag to bring up a subset of services (valid values: devnet, ensrainbow, ensindexer, ensapi). This would be useful for developers who want to run only part of the stack. Consider adding a brief example after line 43, such as:

To bring up only specific services, use the `--only` flag:

```sh
pnpm start --only devnet,ensrainbow

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @packages/integration-test-env/README.md around lines 35 - 43, Add
documentation for the new --only flag in the section that explains how to run
the stack with pnpm start: state that you can pass --only with a comma-separated
list of services (valid values: devnet, ensrainbow, ensindexer, ensapi), show a
brief usage example like pnpm start --only devnet,ensrainbow, and mention that
this starts only the listed services instead of the full stack so ports for the
omitted services are not required.


</details>

<!-- fingerprinting:phantom:triton:puma -->

<!-- This is an auto-generated comment by CodeRabbit -->


async function main() {
await bringUp();
runIntegrationTests();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing await causes premature cleanup and exit.

runIntegrationTests() is not awaited, so cleanup() on line 26 and process.exit(0) on line 27 execute immediately without waiting for the test run to complete. This will tear down the stack while tests are still running, causing test failures or incomplete results. The file header (lines 6, 17) and PR objectives confirm tests must finish before teardown.

🐛 Proposed fix
   await bringUp();
-  runIntegrationTests();
+  await runIntegrationTests();
 
   await cleanup();
📝 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
runIntegrationTests();
await runIntegrationTests();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/integration-test-env/src/ci.ts` at line 24, runIntegrationTests() is
being invoked without awaiting, causing cleanup() and process.exit(0) to run
before tests finish; change the invocation to await runIntegrationTests() and
ensure it's inside an async context (or use .then/.catch) so the process only
calls cleanup() and process.exit(0) after runIntegrationTests() resolves; refer
to runIntegrationTests(), cleanup(), and process.exit to locate and update the
call site and surrounding control flow (use try/finally or promise chaining to
guarantee cleanup runs after the awaited test run).

Comment on lines +59 to +77
/**
* Parse a comma-separated `--only` value (e.g. "devnet,ensrainbow") into a Set of services.
* Throws on unknown service names.
*/
export function parseOnly(value: string): Set<Service> {
const items = value
.split(",")
.map((s) => s.trim())
.filter(Boolean);
if (items.length === 0) {
throw new Error(`--only requires at least one service. Valid: ${ALL_SERVICES.join(", ")}`);
}
for (const item of items) {
if (!(ALL_SERVICES as readonly string[]).includes(item)) {
throw new Error(`Unknown service: "${item}". Valid: ${ALL_SERVICES.join(", ")}`);
}
}
return new Set(items as Service[]);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and view the relevant section
fd lifecycle.ts packages/

Repository: namehash/ensnode

Length of output: 169


🏁 Script executed:

# Read the file and examine lines 59-77
cat -n packages/integration-test-env/src/lifecycle.ts | sed -n '50,90p'

Repository: namehash/ensnode

Length of output: 1627


🏁 Script executed:

# Check if zod is available in the project
rg "import.*zod" packages/integration-test-env/ --type ts

Repository: namehash/ensnode

Length of output: 42


🏁 Script executed:

# Check how Service is defined
rg "type Service|interface Service|export.*Service" packages/integration-test-env/src/ --type ts -B2 -A2

Repository: namehash/ensnode

Length of output: 1644


🏁 Script executed:

# Check if zod is a dependency in the project
rg "\"zod\"" --type json | head -20

Repository: namehash/ensnode

Length of output: 487


🏁 Script executed:

# Check for any zod imports in packages/
rg "import.*zod" packages/ --type ts --max-count 10

Repository: namehash/ensnode

Length of output: 14875


🏁 Script executed:

# Check integration-test-env package.json for dependencies
cat packages/integration-test-env/package.json 2>/dev/null || echo "File not found"

Repository: namehash/ensnode

Length of output: 738


🏁 Script executed:

# Check if any of the imported packages expose zod or validation utilities
rg "export.*zod|export.*parse" packages/ensnode-sdk/src/index.ts packages/ensdb-sdk/src/ packages/shared-configs/ --type ts 2>/dev/null | head -10

Repository: namehash/ensnode

Length of output: 192


🏁 Script executed:

# Check the actual imports in the integration-test-env to see structure
cat -n packages/integration-test-env/src/start.ts | head -30

Repository: namehash/ensnode

Length of output: 1363


🏁 Script executed:

# Check if integration-test-env package should have zod as direct dep
# by looking at whether other test/script packages have it
find packages -name "package.json" -exec grep -l "\"tsx\"\|\"testcontainers\"" {} \; | xargs grep -l "\"zod\"" 2>/dev/null || echo "No test packages with zod found"

Repository: namehash/ensnode

Length of output: 92


Add zod dependency and use zod.parse() for --only validation.

Per coding guidelines, non-API code in packages/**/*.ts must use zod.parse(...) for fail-fast validation on invalid input. This requires adding zod to packages/integration-test-env/package.json dependencies, then replacing the handwritten validation with a zod schema that validates the comma-separated service names. This also eliminates the unchecked as Service[] cast.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/integration-test-env/src/lifecycle.ts` around lines 59 - 77, Add zod
to packages/integration-test-env package.json dependencies, then replace the
manual validation in parseOnly with a zod schema: create a Zod schema that
accepts a comma-separated string, splits/trims/filter(Boolean) and validates
each item is one of the allowed values from ALL_SERVICES, then call
zod.parse(...) to validate the incoming value; use the parsed array to construct
and return a Set<Service> (removing the unchecked "as Service[]" cast) and
ensure any validation errors bubble up as zod errors from parseOnly.

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