From 4bd745674ec5b1af4fc13dc418339d5525a8abda Mon Sep 17 00:00:00 2001 From: sevenzing Date: Mon, 27 Apr 2026 18:31:16 +0400 Subject: [PATCH 1/9] update docker compose logic --- .github/workflows/release.yml | 4 +- .github/workflows/release_snapshot.yml | 2 +- apps/ensapi/package.json | 2 +- docker/README.md | 10 +-- docker/docker-compose.devnet.yml | 30 ++------- docker/docker-compose.orchestrator.yml | 10 +-- docker/docker-compose.yml | 12 ++-- docker/envs/.env.docker.common | 6 +- docker/envs/.env.docker.devnet | 16 +++++ docker/services/ensadmin.yml | 2 +- docker/services/ensapi.yml | 2 +- docker/services/{postgres.yml => ensdb.yml} | 6 +- docker/services/ensindexer.yml | 2 +- docker/services/ensrainbow.yml | 2 +- .../content/docs/docs/deploying/docker.mdx | 2 +- package.json | 8 ++- scripts/sync-docker-services-tags.mjs | 64 +++++++++++++++++++ 17 files changed, 124 insertions(+), 56 deletions(-) rename docker/services/{postgres.yml => ensdb.yml} (74%) create mode 100644 scripts/sync-docker-services-tags.mjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70e646ddb..fe8f833ff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,8 +67,8 @@ jobs: with: commit: "chore(release): version apps" title: "Release New Version" - version: pnpm changeset:version - publish: pnpm changeset-publish + version: pnpm changeset:version && pnpm release:postversion + publish: pnpm changeset:publish createGithubReleases: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release_snapshot.yml b/.github/workflows/release_snapshot.yml index 1842c00a0..44040a97e 100644 --- a/.github/workflows/release_snapshot.yml +++ b/.github/workflows/release_snapshot.yml @@ -83,7 +83,7 @@ jobs: - name: Publish snapshot packages to NPM if: steps.snapshot.outputs.hasChanges == 'true' - run: pnpm changeset-publish:next + run: pnpm changeset:publish:next env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/apps/ensapi/package.json b/apps/ensapi/package.json index d20b10c19..f102ecff8 100644 --- a/apps/ensapi/package.json +++ b/apps/ensapi/package.json @@ -1,6 +1,6 @@ { "name": "ensapi", - "version": "1.10.1", + "version": "1.10.2", "private": true, "type": "module", "description": "ENSNode's ENS API", diff --git a/docker/README.md b/docker/README.md index ac8e8f6ba..61c19a9fb 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,11 +6,11 @@ All commands are run from the **monorepo root**. | File | Purpose | | ---------------------------------------- | -------------------------------------------------------------------------------------- | -| `docker/docker-compose.yml` | Base stack — ensindexer, ensapi, ensrainbow, ensadmin, postgres. For mainnet/sepolia. | +| `docker/docker-compose.yml` | Base stack — ensindexer, ensapi, ensrainbow, ensadmin, ensdb. For mainnet/sepolia. | | `docker/docker-compose.devnet.yml` | Full stack against local devnet (`ens-test-env`). Includes all base services + devnet. | -| `docker/docker-compose.orchestrator.yml` | Minimal infra for CI — devnet + postgres only. Used by `orchestrator.ts`. | +| `docker/docker-compose.orchestrator.yml` | Minimal infra for CI — devnet + ensdb only. Used by `orchestrator.ts`. | | `docker/services/*.yml` | Individual service definitions. Extended by the compose files above. | -| `docker/envs/.env.docker.common` | Shared env defaults (postgres credentials, internal service URLs). Committed. | +| `docker/envs/.env.docker.common` | Shared env defaults (ensdb credentials, internal service URLs). Committed. | | `docker/envs/.env.docker.devnet` | Devnet defaults (PLUGINS, etc.). Committed. Works out of the box. | | `docker/envs/.env.docker.example` | Example for user-specific config. Copy to `.env.docker.local` for mainnet/sepolia. | | `docker/envs/.env.docker.local` | User config (gitignored). Required for base stack, optional for devnet overrides. | @@ -57,7 +57,7 @@ To override defaults (e.g. change `PLUGINS`), create `docker/envs/.env.docker.lo docker compose -f docker/docker-compose.devnet.yml up -d # Start only devnet + core services (no ensadmin) -docker compose -f docker/docker-compose.devnet.yml up -d devnet postgres ensrainbow ensindexer ensapi +docker compose -f docker/docker-compose.devnet.yml up -d ensindexer ensapi # Start only devnet (quick local EVM node, also shows data information about devnet) docker compose -f docker/docker-compose.devnet.yml up devnet @@ -83,7 +83,7 @@ pnpm docker:build:ensadmin ### CI / integration tests -Used internally by `orchestrator.ts` via testcontainers. Starts devnet + postgres only. +Used internally by `orchestrator.ts` via testcontainers. Starts devnet + ensdb only. ```bash pnpm test:integration:ci diff --git a/docker/docker-compose.devnet.yml b/docker/docker-compose.devnet.yml index 03bfb243c..99e3c5510 100644 --- a/docker/docker-compose.devnet.yml +++ b/docker/docker-compose.devnet.yml @@ -3,14 +3,6 @@ services: extends: file: services/ensindexer.yml service: ensindexer - environment: - # TODO: in future we will migrate devnet to chain_id=1 - # need to remove `RPC_URL_15658733` in that case - RPC_URL_15658733: http://devnet:8545 - RPC_URL_1: http://devnet:8545 - ENSINDEXER_SCHEMA_NAME: docker_devnet_v1 - LABEL_SET_ID: ens-test-env - NAMESPACE: ens-test-env env_file: - path: envs/.env.docker.common required: true @@ -21,7 +13,7 @@ services: depends_on: ensrainbow: condition: service_healthy - postgres: + ensdb: condition: service_healthy devnet: condition: service_healthy @@ -30,14 +22,8 @@ services: extends: file: services/ensapi.yml service: ensapi - environment: - # TODO: in future we will migrate devnet to chain_id=1 - # need to remove `RPC_URL_15658733` in that case - RPC_URL_15658733: http://devnet:8545 - RPC_URL_1: http://devnet:8545 - ENSINDEXER_SCHEMA_NAME: docker_devnet_v1 depends_on: - postgres: + ensdb: condition: service_healthy env_file: - path: envs/.env.docker.common @@ -51,8 +37,6 @@ services: extends: file: services/ensrainbow.yml service: ensrainbow - environment: - LABEL_SET_ID: ens-test-env env_file: - path: envs/.env.docker.common required: true @@ -76,10 +60,10 @@ services: - path: envs/.env.docker.local required: false - postgres: + ensdb: extends: - file: services/postgres.yml - service: postgres + file: services/ensdb.yml + service: ensdb env_file: - path: ./envs/.env.docker.common required: true @@ -94,8 +78,8 @@ volumes: # compose file that references them — they cannot be inherited via `extends`. # Explicit `name:` prevents collision with the base stack's volumes when both # are run from the same directory (same project name). - postgres_data: - name: ensnode_devnet_postgres_data + ensdb_data: + name: ensnode_devnet_ensdb_data driver: local ensrainbow_data: name: ensnode_devnet_ensrainbow_data diff --git a/docker/docker-compose.orchestrator.yml b/docker/docker-compose.orchestrator.yml index e813e27f6..1e3196a37 100644 --- a/docker/docker-compose.orchestrator.yml +++ b/docker/docker-compose.orchestrator.yml @@ -1,16 +1,16 @@ # Minimal compose for CI integration tests. # Provides only the infrastructure services needed by orchestrator.ts: -# devnet (local EVM) and postgres (database). +# devnet (local EVM) and ensdb (database). services: devnet: extends: file: services/devnet.yml service: devnet - postgres: + ensdb: extends: - file: services/postgres.yml - service: postgres + file: services/ensdb.yml + service: ensdb env_file: - path: ./envs/.env.docker.common required: true @@ -18,5 +18,5 @@ services: volumes: # Docker Compose requires volumes used by services to be declared in each # compose file that references them — they cannot be inherited via `extends`. - postgres_data: + ensdb_data: driver: local diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 32bddab5d..ff095b3b6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -8,7 +8,7 @@ services: depends_on: ensrainbow: condition: service_healthy - postgres: + ensdb: condition: service_healthy env_file: - path: envs/.env.docker.common @@ -24,7 +24,7 @@ services: environment: ENSINDEXER_SCHEMA_NAME: docker_ensindexer_v1 depends_on: - postgres: + ensdb: condition: service_healthy env_file: - path: envs/.env.docker.common @@ -55,10 +55,10 @@ services: - path: envs/.env.docker.local required: true - postgres: + ensdb: extends: - file: services/postgres.yml - service: postgres + file: services/ensdb.yml + service: ensdb env_file: - path: envs/.env.docker.common required: true @@ -66,7 +66,7 @@ services: volumes: # Docker Compose requires volumes used by services to be declared in each # compose file that references them — they cannot be inherited via `extends`. - postgres_data: + ensdb_data: driver: local ensrainbow_data: driver: local diff --git a/docker/envs/.env.docker.common b/docker/envs/.env.docker.common index d8c682539..d075d9fd4 100644 --- a/docker/envs/.env.docker.common +++ b/docker/envs/.env.docker.common @@ -1,6 +1,8 @@ # Shared Docker Compose environment variables # Used by docker/docker-compose.yml and its variants +ENSNODE_TAG=1.9.0 + # Postgres POSTGRES_DB=postgres @@ -8,9 +10,9 @@ POSTGRES_USER=postgres POSTGRES_PASSWORD=password # Internal service URLs (container-to-container) -ENSDB_URL=postgresql://postgres:password@postgres:5432/postgres +ENSDB_URL=postgresql://postgres:password@ensdb:5432/postgres ENSRAINBOW_URL=http://ensrainbow:3223 -# ENS Admin +# ENSAdmin ENSADMIN_PUBLIC_URL=http://localhost:4173 NEXT_PUBLIC_SERVER_CONNECTION_LIBRARY=http://localhost:4334 diff --git a/docker/envs/.env.docker.devnet b/docker/envs/.env.docker.devnet index 7c63beef8..6efab20e2 100644 --- a/docker/envs/.env.docker.devnet +++ b/docker/envs/.env.docker.devnet @@ -1,6 +1,22 @@ # Default configuration for devnet docker-compose stack. # These values work out of the box — override by creating .env.docker.local. +# ENSIndexer PLUGINS=subgraph,ensv2 +# ENSIndexer and ENSRainbow +NAMESPACE=ens-test-env +# ENSIndexer and ENSApi +ENSINDEXER_SCHEMA_NAME=docker_devnet_v1 +# ENSIndexer and ENSApi +RPC_URL_1=http://devnet:8545 +# ENSIndexer and ENSRainbow LABEL_SET_VERSION=0 +# ENSIndexer and ENSRainbow +LABEL_SET_ID=ens-test-env +# ENSRainbow DB_SCHEMA_VERSION=3 + + +# TODO: in future we will migrate devnet to chain_id=1 +# need to remove `RPC_URL_15658733` in that case +RPC_URL_15658733=http://devnet:8545 diff --git a/docker/services/ensadmin.yml b/docker/services/ensadmin.yml index fbc0fd542..8b7030d78 100644 --- a/docker/services/ensadmin.yml +++ b/docker/services/ensadmin.yml @@ -1,7 +1,7 @@ services: ensadmin: container_name: ensadmin - image: ghcr.io/namehash/ensnode/ensadmin:latest + image: ghcr.io/namehash/ensnode/ensadmin:${ENSNODE_TAG:-1.10.1} build: dockerfile: ./apps/ensadmin/Dockerfile context: ../.. diff --git a/docker/services/ensapi.yml b/docker/services/ensapi.yml index 71ce5c7d3..8a28eedb4 100644 --- a/docker/services/ensapi.yml +++ b/docker/services/ensapi.yml @@ -1,7 +1,7 @@ services: ensapi: container_name: ensapi - image: ghcr.io/namehash/ensnode/ensapi:latest + image: ghcr.io/namehash/ensnode/ensapi:${ENSNODE_TAG:-1.10.1} build: dockerfile: ./apps/ensapi/Dockerfile context: ../.. diff --git a/docker/services/postgres.yml b/docker/services/ensdb.yml similarity index 74% rename from docker/services/postgres.yml rename to docker/services/ensdb.yml index 85f0f12b2..78554a630 100644 --- a/docker/services/postgres.yml +++ b/docker/services/ensdb.yml @@ -1,11 +1,11 @@ services: - postgres: - container_name: postgres + ensdb: + container_name: ensdb image: postgres:17 ports: - "5432:5432" volumes: - - postgres_data:/var/lib/postgresql/data + - ensdb_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s diff --git a/docker/services/ensindexer.yml b/docker/services/ensindexer.yml index ac472ddaf..360fe3ecf 100644 --- a/docker/services/ensindexer.yml +++ b/docker/services/ensindexer.yml @@ -1,7 +1,7 @@ services: ensindexer: container_name: ensindexer - image: ghcr.io/namehash/ensnode/ensindexer:latest + image: ghcr.io/namehash/ensnode/ensindexer:${ENSNODE_TAG:-1.10.1} build: dockerfile: ./apps/ensindexer/Dockerfile context: ../.. diff --git a/docker/services/ensrainbow.yml b/docker/services/ensrainbow.yml index 5e1c3c726..ae4eebc18 100644 --- a/docker/services/ensrainbow.yml +++ b/docker/services/ensrainbow.yml @@ -1,7 +1,7 @@ services: ensrainbow: container_name: ensrainbow - image: ghcr.io/namehash/ensnode/ensrainbow:latest + image: ghcr.io/namehash/ensnode/ensrainbow:${ENSNODE_TAG:-1.10.1} build: dockerfile: ./apps/ensrainbow/Dockerfile context: ../.. diff --git a/docs/ensnode.io/src/content/docs/docs/deploying/docker.mdx b/docs/ensnode.io/src/content/docs/docs/deploying/docker.mdx index 46196b1e2..cd3595e32 100644 --- a/docs/ensnode.io/src/content/docs/docs/deploying/docker.mdx +++ b/docs/ensnode.io/src/content/docs/docs/deploying/docker.mdx @@ -20,7 +20,7 @@ ENSIndexer runs `CREATE EXTENSION IF NOT EXISTS pg_trgm` at startup to back part ENSNode provides several [Docker Compose](https://docs.docker.com/compose/) files for different use cases: -- **`docker/docker-compose.yml`** — base stack for mainnet/sepolia: ensindexer, ensapi, ensrainbow, ensadmin, postgres +- **`docker/docker-compose.yml`** — base stack for mainnet/sepolia: ensindexer, ensapi, ensrainbow, ensadmin, ensdb (postgres) - **`docker/docker-compose.devnet.yml`** — full stack against local devnet (ens-test-env), works out of the box with no configuration required ### Mainnet / Sepolia diff --git a/package.json b/package.json index 7b643b296..1ddeeb546 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,15 @@ "audit:osv": "osv-scanner scan source --lockfile pnpm-lock.yaml", "typecheck": "pnpm -r --parallel --aggregate-output typecheck", "changeset": "changeset", - "changeset:version": "changeset version && pnpm generate:openapi", + "changeset:version": "changeset version", "changeset:next": "changeset version --snapshot next && pnpm generate:openapi", - "changeset-publish": "changeset publish", - "changeset-publish:next": "changeset publish --no-git-tag --snapshot --tag next", + "changeset:publish": "changeset publish", + "changeset:publish:next": "changeset publish --no-git-tag --snapshot --tag next", + "release:postversion": "pnpm docker:tag:sync && pnpm generate:openapi", "packages:prepublish": "pnpm -r prepublish", "devnet": "docker compose -f docker/docker-compose.devnet.yml up devnet", "docker:build:ensnode": "pnpm run -w --parallel \"/^docker:build:.*/\"", + "docker:tag:sync": "node ./scripts/sync-docker-services-tags.mjs", "docker:build:ensindexer": "docker build -f apps/ensindexer/Dockerfile -t ghcr.io/namehash/ensnode/ensindexer:latest .", "docker:build:ensadmin": "docker build -f apps/ensadmin/Dockerfile -t ghcr.io/namehash/ensnode/ensadmin:latest .", "docker:build:ensrainbow": "docker build -f apps/ensrainbow/Dockerfile -t ghcr.io/namehash/ensnode/ensrainbow:latest .", diff --git a/scripts/sync-docker-services-tags.mjs b/scripts/sync-docker-services-tags.mjs new file mode 100644 index 000000000..117a8b71f --- /dev/null +++ b/scripts/sync-docker-services-tags.mjs @@ -0,0 +1,64 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; + +const rootDir = resolve(import.meta.dirname, ".."); + +const serviceFiles = [ + "docker/services/ensadmin.yml", + "docker/services/ensapi.yml", + "docker/services/ensindexer.yml", + "docker/services/ensrainbow.yml", +]; + +const semverRegex = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/; + +async function getVersionFromEnsapi() { + const ensapiPackageJsonPath = resolve(rootDir, "apps/ensapi/package.json"); + const packageJsonContent = await readFile(ensapiPackageJsonPath, "utf8"); + const packageJson = JSON.parse(packageJsonContent); + + if (typeof packageJson.version !== "string" || packageJson.version.length === 0) { + throw new Error("Could not read version from apps/ensapi/package.json"); + } + + return packageJson.version; +} + +function validateVersion(version) { + if (!semverRegex.test(version)) { + throw new Error( + `Invalid version "${version}". Expected SemVer-like value such as 1.10.1`, + ); + } +} + +async function updateServiceDefaultTag(version) { + const pattern = /\$\{ENSNODE_TAG:-[^}]+\}/g; + const replacement = `\${ENSNODE_TAG:-${version}}`; + + for (const relativePath of serviceFiles) { + const absolutePath = resolve(rootDir, relativePath); + const content = await readFile(absolutePath, "utf8"); + + if (!pattern.test(content)) { + throw new Error(`Could not find ENSNODE_TAG default expression in ${relativePath}`); + } + + const updated = content.replace(pattern, replacement); + await writeFile(absolutePath, updated, "utf8"); + console.log(`Updated ${relativePath} -> ${version}`); + } +} + +async function main() { + const versionFromArg = process.argv[2]; + const version = versionFromArg ?? (await getVersionFromEnsapi()); + + validateVersion(version); + await updateServiceDefaultTag(version); +} + +main().catch((error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); +}); From 6934f56d2cc482d80e5f035384fe2d1ac040c1ad Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 28 Apr 2026 17:23:51 +0400 Subject: [PATCH 2/9] fix prev issues --- docker/docker-compose.devnet.yml | 2 +- docker/docker-compose.orchestrator.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.devnet.yml b/docker/docker-compose.devnet.yml index 99e3c5510..e3b431485 100644 --- a/docker/docker-compose.devnet.yml +++ b/docker/docker-compose.devnet.yml @@ -65,7 +65,7 @@ services: file: services/ensdb.yml service: ensdb env_file: - - path: ./envs/.env.docker.common + - path: envs/.env.docker.common required: true devnet: diff --git a/docker/docker-compose.orchestrator.yml b/docker/docker-compose.orchestrator.yml index 1e3196a37..82290a79e 100644 --- a/docker/docker-compose.orchestrator.yml +++ b/docker/docker-compose.orchestrator.yml @@ -12,7 +12,7 @@ services: file: services/ensdb.yml service: ensdb env_file: - - path: ./envs/.env.docker.common + - path: envs/.env.docker.common required: true volumes: From 3bd509943c2bc0b3fe95c49f9d6ff6944a9ee21a Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 28 Apr 2026 18:41:23 +0400 Subject: [PATCH 3/9] fix PR comments --- apps/ensapi/package.json | 2 +- docker/envs/.env.docker.common | 3 --- scripts/sync-docker-services-tags.mjs | 9 +++++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/ensapi/package.json b/apps/ensapi/package.json index f102ecff8..d20b10c19 100644 --- a/apps/ensapi/package.json +++ b/apps/ensapi/package.json @@ -1,6 +1,6 @@ { "name": "ensapi", - "version": "1.10.2", + "version": "1.10.1", "private": true, "type": "module", "description": "ENSNode's ENS API", diff --git a/docker/envs/.env.docker.common b/docker/envs/.env.docker.common index d075d9fd4..1d8713c9e 100644 --- a/docker/envs/.env.docker.common +++ b/docker/envs/.env.docker.common @@ -1,9 +1,6 @@ # Shared Docker Compose environment variables # Used by docker/docker-compose.yml and its variants -ENSNODE_TAG=1.9.0 - - # Postgres POSTGRES_DB=postgres POSTGRES_USER=postgres diff --git a/scripts/sync-docker-services-tags.mjs b/scripts/sync-docker-services-tags.mjs index 117a8b71f..56879bd80 100644 --- a/scripts/sync-docker-services-tags.mjs +++ b/scripts/sync-docker-services-tags.mjs @@ -10,7 +10,7 @@ const serviceFiles = [ "docker/services/ensrainbow.yml", ]; -const semverRegex = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/; +const semverRegex = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; async function getVersionFromEnsapi() { const ensapiPackageJsonPath = resolve(rootDir, "apps/ensapi/package.json"); @@ -33,18 +33,19 @@ function validateVersion(version) { } async function updateServiceDefaultTag(version) { - const pattern = /\$\{ENSNODE_TAG:-[^}]+\}/g; + const testPattern = /\$\{ENSNODE_TAG:-[^}]+\}/; + const replacePattern = /\$\{ENSNODE_TAG:-[^}]+\}/g; const replacement = `\${ENSNODE_TAG:-${version}}`; for (const relativePath of serviceFiles) { const absolutePath = resolve(rootDir, relativePath); const content = await readFile(absolutePath, "utf8"); - if (!pattern.test(content)) { + if (!testPattern.test(content)) { throw new Error(`Could not find ENSNODE_TAG default expression in ${relativePath}`); } - const updated = content.replace(pattern, replacement); + const updated = content.replace(replacePattern, replacement); await writeFile(absolutePath, updated, "utf8"); console.log(`Updated ${relativePath} -> ${version}`); } From afe2674e073b0c01d3d599e3075777afe62605c2 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 28 Apr 2026 19:09:07 +0400 Subject: [PATCH 4/9] fix lint and tests --- .../integration-test-env/src/orchestrator.ts | 22 +++++++++---------- scripts/sync-docker-services-tags.mjs | 4 +--- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/integration-test-env/src/orchestrator.ts b/packages/integration-test-env/src/orchestrator.ts index 17e2a72d5..9b177eaae 100644 --- a/packages/integration-test-env/src/orchestrator.ts +++ b/packages/integration-test-env/src/orchestrator.ts @@ -5,14 +5,14 @@ * monorepo-level integration tests, then tears everything down. * * Phases: - * 1. Postgres + devnet via docker-compose (testcontainers DockerComposeEnvironment) + * 1. ENSDb (postgres) + devnet via docker-compose (testcontainers DockerComposeEnvironment) * 2. Download pre-built ENSRainbow LevelDB, extract, start ENSRainbow from source * 3. Start ENSIndexer, wait for omnichain-following / omnichain-completed * 4. Start ENSApi * 5. Run `pnpm test:integration` at the monorepo root * * Design decisions: - * - Postgres and devnet are started from docker/docker-compose.orchestrator.yml via + * - ENSDb (postgres) and devnet are started from docker/docker-compose.orchestrator.yml via * testcontainers DockerComposeEnvironment, ensuring the orchestrator always * uses the same images and configuration defined there. * - execa for child process management — automatic cleanup on parent exit, @@ -22,7 +22,7 @@ * - ENSRainbow database is downloaded via the existing shell script and * extracted with tar, mirroring the Docker entrypoint behavior. * - Cleanup stops processes in reverse order (ensapi → ensindexer → ensrainbow) - * so DB consumers close connections before Postgres is stopped. + * so DB consumers close connections before ensdb is stopped. * - Abort flag pattern: if a background service crashes during polling/health * checks, the orchestrator fails fast instead of waiting for a timeout. * - SIGINT/SIGTERM handler is guarded against re-entrance (repeated Ctrl-C). @@ -237,21 +237,21 @@ async function main() { log("Starting integration test environment..."); logVersions(); - // Phase 1: Start Postgres + Devnet via docker-compose - log("Starting Postgres and devnet..."); + // Phase 1: Start ENSDb + Devnet via docker-compose + log("Starting ENSDb and Devnet..."); composeEnvironment = await new DockerComposeEnvironment( DOCKER_DIR, "docker-compose.orchestrator.yml", ) .withWaitStrategy("devnet", Wait.forHealthCheck()) - .withWaitStrategy("postgres", Wait.forListeningPorts()) + .withWaitStrategy("ensdb", Wait.forListeningPorts()) .withStartupTimeout(120_000) - .up(["postgres", "devnet"]); + .up(["ensdb", "devnet"]); - const postgresContainer = composeEnvironment.getContainer("postgres"); - const postgresPort = postgresContainer.getMappedPort(5432); - const ENSDB_URL = `postgresql://postgres:password@localhost:${postgresPort}/postgres`; - log(`Postgres is ready (port ${postgresPort})`); + const ensdbContainer = composeEnvironment.getContainer("ensdb"); + const ensdbPort = ensdbContainer.getMappedPort(5432); + const ENSDB_URL = `postgresql://postgres:password@localhost:${ensdbPort}/postgres`; + log(`ENSDb is ready (port ${ensdbPort})`); log("Devnet is ready"); // Phase 2: Download ENSRainbow database and start from source diff --git a/scripts/sync-docker-services-tags.mjs b/scripts/sync-docker-services-tags.mjs index 56879bd80..a2ec80d52 100644 --- a/scripts/sync-docker-services-tags.mjs +++ b/scripts/sync-docker-services-tags.mjs @@ -26,9 +26,7 @@ async function getVersionFromEnsapi() { function validateVersion(version) { if (!semverRegex.test(version)) { - throw new Error( - `Invalid version "${version}". Expected SemVer-like value such as 1.10.1`, - ); + throw new Error(`Invalid version "${version}". Expected SemVer-like value such as 1.10.1`); } } From e41d950fccff09f90ad9c17115bd2833879d3852 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Tue, 28 Apr 2026 19:44:20 +0400 Subject: [PATCH 5/9] revert `environment` NAMESPACE just for docker devnet --- docker/docker-compose.devnet.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/docker-compose.devnet.yml b/docker/docker-compose.devnet.yml index e3b431485..5f2e60852 100644 --- a/docker/docker-compose.devnet.yml +++ b/docker/docker-compose.devnet.yml @@ -3,6 +3,8 @@ services: extends: file: services/ensindexer.yml service: ensindexer + environment: + NAMESPACE: ens-test-env env_file: - path: envs/.env.docker.common required: true From 826d54a91854424baf00f9d3e8d547ebdb8a8866 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Wed, 29 Apr 2026 14:59:39 +0400 Subject: [PATCH 6/9] fix PR comments --- .github/workflows/release.yml | 2 +- apps/ensapi/package.json | 3 ++- docker/docker-compose.devnet.yml | 2 ++ docker/envs/.env.docker.devnet | 4 ---- package.json | 6 +++--- scripts/sync-docker-services-tags.mjs | 25 +++++++++---------------- 6 files changed, 17 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fe8f833ff..7822968ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: with: commit: "chore(release): version apps" title: "Release New Version" - version: pnpm changeset:version && pnpm release:postversion + version: pnpm changeset:version publish: pnpm changeset:publish createGithubReleases: false env: diff --git a/apps/ensapi/package.json b/apps/ensapi/package.json index d20b10c19..e872968ed 100644 --- a/apps/ensapi/package.json +++ b/apps/ensapi/package.json @@ -18,7 +18,8 @@ "lint": "biome check --write .", "lint:ci": "biome ci", "typecheck": "tsgo --noEmit", - "generate:gqlschema": "tsx src/omnigraph-api/lib/write-graphql-schema.ts" + "generate:gqlschema": "tsx src/omnigraph-api/lib/write-graphql-schema.ts", + "version:current": "node -p \"require('./package.json').version\"" }, "dependencies": { "@ensdomains/ensjs": "^4.0.2", diff --git a/docker/docker-compose.devnet.yml b/docker/docker-compose.devnet.yml index 5f2e60852..baddddeef 100644 --- a/docker/docker-compose.devnet.yml +++ b/docker/docker-compose.devnet.yml @@ -39,6 +39,8 @@ services: extends: file: services/ensrainbow.yml service: ensrainbow + environment: + NAMESPACE: ens-test-env env_file: - path: envs/.env.docker.common required: true diff --git a/docker/envs/.env.docker.devnet b/docker/envs/.env.docker.devnet index 6efab20e2..3b2c6c1b2 100644 --- a/docker/envs/.env.docker.devnet +++ b/docker/envs/.env.docker.devnet @@ -3,8 +3,6 @@ # ENSIndexer PLUGINS=subgraph,ensv2 -# ENSIndexer and ENSRainbow -NAMESPACE=ens-test-env # ENSIndexer and ENSApi ENSINDEXER_SCHEMA_NAME=docker_devnet_v1 # ENSIndexer and ENSApi @@ -15,8 +13,6 @@ LABEL_SET_VERSION=0 LABEL_SET_ID=ens-test-env # ENSRainbow DB_SCHEMA_VERSION=3 - - # TODO: in future we will migrate devnet to chain_id=1 # need to remove `RPC_URL_15658733` in that case RPC_URL_15658733=http://devnet:8545 diff --git a/package.json b/package.json index 1ddeeb546..8d8c78c4b 100644 --- a/package.json +++ b/package.json @@ -12,15 +12,15 @@ "audit:osv": "osv-scanner scan source --lockfile pnpm-lock.yaml", "typecheck": "pnpm -r --parallel --aggregate-output typecheck", "changeset": "changeset", - "changeset:version": "changeset version", - "changeset:next": "changeset version --snapshot next && pnpm generate:openapi", + "changeset:version": "changeset version && pnpm release:postversion", + "changeset:next": "changeset version --snapshot next && pnpm release:postversion", "changeset:publish": "changeset publish", "changeset:publish:next": "changeset publish --no-git-tag --snapshot --tag next", "release:postversion": "pnpm docker:tag:sync && pnpm generate:openapi", "packages:prepublish": "pnpm -r prepublish", "devnet": "docker compose -f docker/docker-compose.devnet.yml up devnet", "docker:build:ensnode": "pnpm run -w --parallel \"/^docker:build:.*/\"", - "docker:tag:sync": "node ./scripts/sync-docker-services-tags.mjs", + "docker:tag:sync": "node ./scripts/sync-docker-services-tags.mjs \"$(pnpm -F ensapi -s version:current)\"", "docker:build:ensindexer": "docker build -f apps/ensindexer/Dockerfile -t ghcr.io/namehash/ensnode/ensindexer:latest .", "docker:build:ensadmin": "docker build -f apps/ensadmin/Dockerfile -t ghcr.io/namehash/ensnode/ensadmin:latest .", "docker:build:ensrainbow": "docker build -f apps/ensrainbow/Dockerfile -t ghcr.io/namehash/ensnode/ensrainbow:latest .", diff --git a/scripts/sync-docker-services-tags.mjs b/scripts/sync-docker-services-tags.mjs index a2ec80d52..2e45cdbfb 100644 --- a/scripts/sync-docker-services-tags.mjs +++ b/scripts/sync-docker-services-tags.mjs @@ -12,18 +12,6 @@ const serviceFiles = [ const semverRegex = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; -async function getVersionFromEnsapi() { - const ensapiPackageJsonPath = resolve(rootDir, "apps/ensapi/package.json"); - const packageJsonContent = await readFile(ensapiPackageJsonPath, "utf8"); - const packageJson = JSON.parse(packageJsonContent); - - if (typeof packageJson.version !== "string" || packageJson.version.length === 0) { - throw new Error("Could not read version from apps/ensapi/package.json"); - } - - return packageJson.version; -} - function validateVersion(version) { if (!semverRegex.test(version)) { throw new Error(`Invalid version "${version}". Expected SemVer-like value such as 1.10.1`); @@ -35,6 +23,8 @@ async function updateServiceDefaultTag(version) { const replacePattern = /\$\{ENSNODE_TAG:-[^}]+\}/g; const replacement = `\${ENSNODE_TAG:-${version}}`; + console.log(`Updating service default tag to ${version}\n`); + for (const relativePath of serviceFiles) { const absolutePath = resolve(rootDir, relativePath); const content = await readFile(absolutePath, "utf8"); @@ -43,18 +33,21 @@ async function updateServiceDefaultTag(version) { throw new Error(`Could not find ENSNODE_TAG default expression in ${relativePath}`); } + const previous = content.match(testPattern)[0]; const updated = content.replace(replacePattern, replacement); await writeFile(absolutePath, updated, "utf8"); - console.log(`Updated ${relativePath} -> ${version}`); + console.log(`Updated ${relativePath}:\t"${previous}" -> "${replacement}"`); } } async function main() { const versionFromArg = process.argv[2]; - const version = versionFromArg ?? (await getVersionFromEnsapi()); + if (typeof versionFromArg !== "string" || versionFromArg.length === 0) { + throw new Error("Version argument is required. Usage: node scripts/sync-docker-services-tags.mjs "); + } - validateVersion(version); - await updateServiceDefaultTag(version); + validateVersion(versionFromArg); + await updateServiceDefaultTag(versionFromArg); } main().catch((error) => { From 5864c96341ed29b0cb88841f59d17f4125db9f4b Mon Sep 17 00:00:00 2001 From: sevenzing Date: Wed, 29 Apr 2026 15:02:02 +0400 Subject: [PATCH 7/9] forgot lint. maybe precommit hooks..? --- scripts/sync-docker-services-tags.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/sync-docker-services-tags.mjs b/scripts/sync-docker-services-tags.mjs index 2e45cdbfb..9851877fd 100644 --- a/scripts/sync-docker-services-tags.mjs +++ b/scripts/sync-docker-services-tags.mjs @@ -43,7 +43,9 @@ async function updateServiceDefaultTag(version) { async function main() { const versionFromArg = process.argv[2]; if (typeof versionFromArg !== "string" || versionFromArg.length === 0) { - throw new Error("Version argument is required. Usage: node scripts/sync-docker-services-tags.mjs "); + throw new Error( + "Version argument is required. Usage: node scripts/sync-docker-services-tags.mjs ", + ); } validateVersion(versionFromArg); From f2e1c369d5c3be9e2653d786cbb454fc665d3e9f Mon Sep 17 00:00:00 2001 From: sevenzing Date: Wed, 29 Apr 2026 17:26:46 +0400 Subject: [PATCH 8/9] fix comments --- docker/docker-compose.devnet.yml | 2 -- docker/envs/.env.docker.devnet | 4 ++-- docker/services/ensadmin.yml | 2 +- docker/services/ensapi.yml | 2 +- docker/services/ensindexer.yml | 2 +- docker/services/ensrainbow.yml | 2 +- package.json | 4 ++-- scripts/sync-docker-services-tags.mjs | 8 ++++---- 8 files changed, 12 insertions(+), 14 deletions(-) diff --git a/docker/docker-compose.devnet.yml b/docker/docker-compose.devnet.yml index baddddeef..5f2e60852 100644 --- a/docker/docker-compose.devnet.yml +++ b/docker/docker-compose.devnet.yml @@ -39,8 +39,6 @@ services: extends: file: services/ensrainbow.yml service: ensrainbow - environment: - NAMESPACE: ens-test-env env_file: - path: envs/.env.docker.common required: true diff --git a/docker/envs/.env.docker.devnet b/docker/envs/.env.docker.devnet index 3b2c6c1b2..20eb13b77 100644 --- a/docker/envs/.env.docker.devnet +++ b/docker/envs/.env.docker.devnet @@ -13,6 +13,6 @@ LABEL_SET_VERSION=0 LABEL_SET_ID=ens-test-env # ENSRainbow DB_SCHEMA_VERSION=3 -# TODO: in future we will migrate devnet to chain_id=1 -# need to remove `RPC_URL_15658733` in that case +# TODO: in future we might migrate devnet to different chain_id like 1337 or 31337 +# need to update this `RPC_URL_15658733` in that case RPC_URL_15658733=http://devnet:8545 diff --git a/docker/services/ensadmin.yml b/docker/services/ensadmin.yml index 8b7030d78..a47f8b568 100644 --- a/docker/services/ensadmin.yml +++ b/docker/services/ensadmin.yml @@ -1,7 +1,7 @@ services: ensadmin: container_name: ensadmin - image: ghcr.io/namehash/ensnode/ensadmin:${ENSNODE_TAG:-1.10.1} + image: ghcr.io/namehash/ensnode/ensadmin:${ENSNODE_VERSION:-1.10.1} build: dockerfile: ./apps/ensadmin/Dockerfile context: ../.. diff --git a/docker/services/ensapi.yml b/docker/services/ensapi.yml index 8a28eedb4..0d7eafa36 100644 --- a/docker/services/ensapi.yml +++ b/docker/services/ensapi.yml @@ -1,7 +1,7 @@ services: ensapi: container_name: ensapi - image: ghcr.io/namehash/ensnode/ensapi:${ENSNODE_TAG:-1.10.1} + image: ghcr.io/namehash/ensnode/ensapi:${ENSNODE_VERSION:-1.10.1} build: dockerfile: ./apps/ensapi/Dockerfile context: ../.. diff --git a/docker/services/ensindexer.yml b/docker/services/ensindexer.yml index 360fe3ecf..6f5e08088 100644 --- a/docker/services/ensindexer.yml +++ b/docker/services/ensindexer.yml @@ -1,7 +1,7 @@ services: ensindexer: container_name: ensindexer - image: ghcr.io/namehash/ensnode/ensindexer:${ENSNODE_TAG:-1.10.1} + image: ghcr.io/namehash/ensnode/ensindexer:${ENSNODE_VERSION:-1.10.1} build: dockerfile: ./apps/ensindexer/Dockerfile context: ../.. diff --git a/docker/services/ensrainbow.yml b/docker/services/ensrainbow.yml index ae4eebc18..d6b8f3073 100644 --- a/docker/services/ensrainbow.yml +++ b/docker/services/ensrainbow.yml @@ -1,7 +1,7 @@ services: ensrainbow: container_name: ensrainbow - image: ghcr.io/namehash/ensnode/ensrainbow:${ENSNODE_TAG:-1.10.1} + image: ghcr.io/namehash/ensnode/ensrainbow:${ENSNODE_VERSION:-1.10.1} build: dockerfile: ./apps/ensrainbow/Dockerfile context: ../.. diff --git a/package.json b/package.json index 8d8c78c4b..cb3f8d5a6 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,11 @@ "changeset:next": "changeset version --snapshot next && pnpm release:postversion", "changeset:publish": "changeset publish", "changeset:publish:next": "changeset publish --no-git-tag --snapshot --tag next", - "release:postversion": "pnpm docker:tag:sync && pnpm generate:openapi", + "release:postversion": "pnpm docker:version:sync && pnpm generate:openapi", "packages:prepublish": "pnpm -r prepublish", "devnet": "docker compose -f docker/docker-compose.devnet.yml up devnet", "docker:build:ensnode": "pnpm run -w --parallel \"/^docker:build:.*/\"", - "docker:tag:sync": "node ./scripts/sync-docker-services-tags.mjs \"$(pnpm -F ensapi -s version:current)\"", + "docker:version:sync": "node ./scripts/sync-docker-services-tags.mjs \"$(pnpm -F ensapi -s version:current)\"", "docker:build:ensindexer": "docker build -f apps/ensindexer/Dockerfile -t ghcr.io/namehash/ensnode/ensindexer:latest .", "docker:build:ensadmin": "docker build -f apps/ensadmin/Dockerfile -t ghcr.io/namehash/ensnode/ensadmin:latest .", "docker:build:ensrainbow": "docker build -f apps/ensrainbow/Dockerfile -t ghcr.io/namehash/ensnode/ensrainbow:latest .", diff --git a/scripts/sync-docker-services-tags.mjs b/scripts/sync-docker-services-tags.mjs index 9851877fd..aaccc31f9 100644 --- a/scripts/sync-docker-services-tags.mjs +++ b/scripts/sync-docker-services-tags.mjs @@ -19,9 +19,9 @@ function validateVersion(version) { } async function updateServiceDefaultTag(version) { - const testPattern = /\$\{ENSNODE_TAG:-[^}]+\}/; - const replacePattern = /\$\{ENSNODE_TAG:-[^}]+\}/g; - const replacement = `\${ENSNODE_TAG:-${version}}`; + const testPattern = /\$\{ENSNODE_VERSION:-[^}]+\}/; + const replacePattern = /\$\{ENSNODE_VERSION:-[^}]+\}/g; + const replacement = `\${ENSNODE_VERSION:-${version}}`; console.log(`Updating service default tag to ${version}\n`); @@ -30,7 +30,7 @@ async function updateServiceDefaultTag(version) { const content = await readFile(absolutePath, "utf8"); if (!testPattern.test(content)) { - throw new Error(`Could not find ENSNODE_TAG default expression in ${relativePath}`); + throw new Error(`Could not find ENSNODE_VERSION default expression in ${relativePath}`); } const previous = content.match(testPattern)[0]; From 34ec5ccd42ce7f9535f1a7b100e08950b12f1f76 Mon Sep 17 00:00:00 2001 From: sevenzing Date: Wed, 29 Apr 2026 17:41:07 +0400 Subject: [PATCH 9/9] fix pr comments --- docker/docker-compose.yml | 5 +++++ docker/services/ensrainbow.yml | 1 - scripts/sync-docker-services-tags.mjs | 12 ++++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ff095b3b6..65b348d8d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -3,6 +3,7 @@ services: extends: file: services/ensindexer.yml service: ensindexer + restart: unless-stopped environment: ENSINDEXER_SCHEMA_NAME: docker_ensindexer_v1 depends_on: @@ -21,6 +22,7 @@ services: extends: file: services/ensapi.yml service: ensapi + restart: unless-stopped environment: ENSINDEXER_SCHEMA_NAME: docker_ensindexer_v1 depends_on: @@ -36,6 +38,7 @@ services: extends: file: services/ensrainbow.yml service: ensrainbow + restart: unless-stopped env_file: - path: envs/.env.docker.common required: true @@ -46,6 +49,7 @@ services: extends: file: services/ensadmin.yml service: ensadmin + restart: unless-stopped depends_on: ensapi: condition: service_started @@ -59,6 +63,7 @@ services: extends: file: services/ensdb.yml service: ensdb + restart: unless-stopped env_file: - path: envs/.env.docker.common required: true diff --git a/docker/services/ensrainbow.yml b/docker/services/ensrainbow.yml index d6b8f3073..66be616d7 100644 --- a/docker/services/ensrainbow.yml +++ b/docker/services/ensrainbow.yml @@ -9,7 +9,6 @@ services: - "3223:3223" volumes: - ensrainbow_data:/app/apps/ensrainbow/data - restart: unless-stopped healthcheck: test: ["CMD", "wget", "-q", "--spider", "http://localhost:3223/health"] interval: 30s diff --git a/scripts/sync-docker-services-tags.mjs b/scripts/sync-docker-services-tags.mjs index aaccc31f9..22f13f797 100644 --- a/scripts/sync-docker-services-tags.mjs +++ b/scripts/sync-docker-services-tags.mjs @@ -1,3 +1,11 @@ +// Keeps Docker compose service defaults aligned with the current ENSNode release version. +// Responsibility: +// - update `${ENSNODE_VERSION:-...}` fallbacks in docker service definitions +// - validate that the provided version looks like SemVer before writing files + +// Usage: +// node ./scripts/sync-docker-services-tags.mjs 1.10.1-beta.1 + import { readFile, writeFile } from "node:fs/promises"; import { resolve } from "node:path"; @@ -18,7 +26,7 @@ function validateVersion(version) { } } -async function updateServiceDefaultTag(version) { +async function updateServiceDefaultVersion(version) { const testPattern = /\$\{ENSNODE_VERSION:-[^}]+\}/; const replacePattern = /\$\{ENSNODE_VERSION:-[^}]+\}/g; const replacement = `\${ENSNODE_VERSION:-${version}}`; @@ -49,7 +57,7 @@ async function main() { } validateVersion(versionFromArg); - await updateServiceDefaultTag(versionFromArg); + await updateServiceDefaultVersion(versionFromArg); } main().catch((error) => {