integration-test-env: add start (brings up the stack only, no tests)#2091
integration-test-env: add start (brings up the stack only, no tests)#2091shrugs wants to merge 3 commits into
start (brings up the stack only, no tests)#2091Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
📝 WalkthroughWalkthroughThis 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: ChangesLifecycle Refactoring and Entrypoint Split
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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. Comment |
There was a problem hiding this comment.
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.tsshared by both entrypoints. - Adds a new
startentrypoint that brings up the full stack and blocks until Ctrl+C, and renames the prior behavior tostart:ci. - Updates root
test:integration:cito call the newstart:ciscript 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 startflow (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.
| 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 | ||
| ``` |
| "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 SummaryThis PR splits the integration-test-env orchestrator into a shared
Confidence Score: 5/5Safe 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
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])
Reviews (3): Last reviewed commit: "fix: allow --only" | Re-trigger Greptile |
| * 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. | ||
| */ |
There was a problem hiding this comment.
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.
| * 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(). | |
| */ |
| ``` | ||
|
|
||
| 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: |
There was a problem hiding this comment.
| 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)
| * 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. |
There was a problem hiding this comment.
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>
b7eb383 to
8f0fd1f
Compare
There was a problem hiding this comment.
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:cirun; 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.
| "start": "tsx src/start.ts", | ||
| "start:ci": "CI=1 tsx src/ci.ts", |
| ```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: |
| // 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: |
There was a problem hiding this comment.
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 winDon't report every
SIGTERMshutdown as success.
SIGTERMis also what CI runners and supervisors use for cancellation/timeouts. Exiting0here can make an abortedstart:cirun look green. If you want local Ctrl+C to be clean, keepSIGINTat0but preserve a non-zero or signal-derived exit forSIGTERM.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 winFix 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, callcleanup(), 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
📒 Files selected for processing (6)
package.jsonpackages/integration-test-env/README.mdpackages/integration-test-env/package.jsonpackages/integration-test-env/src/ci.tspackages/integration-test-env/src/lifecycle.tspackages/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", |
There was a problem hiding this comment.
🧩 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 -20Repository: 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.tsRepository: 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 -A2Repository: 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.jsonRepository: 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.tsRepository: 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.
| ```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 | ||
| ``` |
There was a problem hiding this comment.
🧹 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(); |
There was a problem hiding this comment.
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.
| 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).
| /** | ||
| * 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[]); | ||
| } |
There was a problem hiding this comment.
🛠️ 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 tsRepository: 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 -A2Repository: namehash/ensnode
Length of output: 1644
🏁 Script executed:
# Check if zod is a dependency in the project
rg "\"zod\"" --type json | head -20Repository: namehash/ensnode
Length of output: 487
🏁 Script executed:
# Check for any zod imports in packages/
rg "import.*zod" packages/ --type ts --max-count 10Repository: 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 -10Repository: 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 -30Repository: 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.
Summary
lifecycle.ts) shared by two entrypointspnpm -F @ensnode/integration-test-env start— bring up the full stack (incl. seed) and block until Ctrl+C, sopnpm test:integrationcan run from another terminalstartscript is renamed tostart:ci(bring up + run tests + tear down); the roottest:integration:cialias is updated to match so CI behavior is unchanged--onlyflag:pnpm start --only devnet,ensrainbowrestricts 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),ensapiLOG_LEVEL=errorso its info/warn chatter doesn't drown out the rest of the stack outputstartexits 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'sseedDevnetphase, 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.--onlyexists 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 separatedocker composeinvocations.Testing
pnpm -F @ensnode/integration-test-env typecheck✓pnpm lint✓pnpm start/ Ctrl+C teardown not exercised locally (existing CI flow is unchanged becausestart:ciis identical to the oldstartmodulo a one-line extracted call). Worth a manual spot-check before merge.Notes for Reviewer (Optional)
lifecycle.tsis a mechanical extraction of phases 1–6 fromorchestrator.ts— diff is large only because of the move; the only logic change is oneawait waitForHealth(...)URL now usesendpoints.ensapiinstead of an inline string.lifecycle.tsso both entrypoints share them automatically.--onlygates each phase via ashould(service)check insidebringUp. ci.ts passes no options → full stack as before.Checklist
🤖 Generated with Claude Code