From 7eac1c3eeda5939fc5b8583bdf9518d59859ac49 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Jul 2026 16:47:50 +0200 Subject: [PATCH 1/5] feat(ensnode-sdk): `git-ref` module Add indernaal functionality to build an optional git commit reference from environment variables. --- .../ensnode-sdk/src/ensapi/config/types.ts | 7 ++++ .../src/ensapi/config/zod-schemas.ts | 1 + .../src/ensindexer/config/types.ts | 7 ++++ .../src/ensindexer/config/zod-schemas.ts | 1 + packages/ensnode-sdk/src/ensrainbow/config.ts | 7 ++++ .../src/ensrainbow/zod-schemas/config.ts | 5 ++- packages/ensnode-sdk/src/internal.ts | 1 + .../src/shared/config/environments.ts | 9 +++++ .../src/shared/config/git-ref.test.ts | 39 +++++++++++++++++++ .../ensnode-sdk/src/shared/config/git-ref.ts | 22 +++++++++++ .../src/shared/config/zod-schemas.ts | 4 ++ 11 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 packages/ensnode-sdk/src/shared/config/git-ref.test.ts create mode 100644 packages/ensnode-sdk/src/shared/config/git-ref.ts diff --git a/packages/ensnode-sdk/src/ensapi/config/types.ts b/packages/ensnode-sdk/src/ensapi/config/types.ts index 66ae798e4c..45a8650685 100644 --- a/packages/ensnode-sdk/src/ensapi/config/types.ts +++ b/packages/ensnode-sdk/src/ensapi/config/types.ts @@ -7,6 +7,13 @@ export type { TheGraphCannotFallbackReason, TheGraphFallback }; * Version info about ENSApi and its dependencies. */ export interface EnsApiVersionInfo { + /** + * ENSApi codebase commit hash + * + * Optional, as it may not be available in all environments. + */ + commit?: string; + /** * ENSApi service version * diff --git a/packages/ensnode-sdk/src/ensapi/config/zod-schemas.ts b/packages/ensnode-sdk/src/ensapi/config/zod-schemas.ts index 19e5eff2dd..0d4271ecf3 100644 --- a/packages/ensnode-sdk/src/ensapi/config/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensapi/config/zod-schemas.ts @@ -13,6 +13,7 @@ export { TheGraphCannotFallbackReasonSchema, TheGraphFallbackSchema }; const makeEnsApiVersionInfoSchema = (valueLabel: string = "ENS API version info") => z.object({ + commit: z.optional(z.string().nonempty(`${valueLabel}.commit must be a non-empty string`)), ensApi: z.string().nonempty(`${valueLabel}.ensApi must be a non-empty string`), ensNormalize: z.string().nonempty(`${valueLabel}.ensNormalize must be a non-empty string`), }); diff --git a/packages/ensnode-sdk/src/ensindexer/config/types.ts b/packages/ensnode-sdk/src/ensindexer/config/types.ts index 2583dea676..3bc44bc355 100644 --- a/packages/ensnode-sdk/src/ensindexer/config/types.ts +++ b/packages/ensnode-sdk/src/ensindexer/config/types.ts @@ -31,6 +31,13 @@ export interface EnsIndexerVersionInfo { **/ ponder: string; + /** + * ENSIndexer codebase commit hash + * + * Optional, as it may not be available in all environments. + */ + commit?: string; + /** * ENSDb service version * diff --git a/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.ts b/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.ts index a4d4d47606..15d3e1f496 100644 --- a/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.ts +++ b/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.ts @@ -99,6 +99,7 @@ export const makeEnsIndexerVersionInfoSchema = (valueLabel: string = "Value") => .object( { ponder: makeNonEmptyStringSchema(), + commit: z.optional(makeNonEmptyStringSchema()), ensDb: makeNonEmptyStringSchema(), ensIndexer: makeNonEmptyStringSchema(), ensNormalize: makeNonEmptyStringSchema(), diff --git a/packages/ensnode-sdk/src/ensrainbow/config.ts b/packages/ensnode-sdk/src/ensrainbow/config.ts index e271a0dd82..96600774db 100644 --- a/packages/ensnode-sdk/src/ensrainbow/config.ts +++ b/packages/ensnode-sdk/src/ensrainbow/config.ts @@ -4,6 +4,13 @@ import type { EnsRainbowServerLabelSet } from "./types"; * Version info about ENSRainbow and its dependencies. */ export interface EnsRainbowVersionInfo { + /** + * ENSRainbow codebase commit hash + * + * Optional, as it may not be available in all environments. + */ + commit?: string; + /** * ENSRainbow service version * diff --git a/packages/ensnode-sdk/src/ensrainbow/zod-schemas/config.ts b/packages/ensnode-sdk/src/ensrainbow/zod-schemas/config.ts index c49a2e26b2..f504b781bc 100644 --- a/packages/ensnode-sdk/src/ensrainbow/zod-schemas/config.ts +++ b/packages/ensnode-sdk/src/ensrainbow/zod-schemas/config.ts @@ -53,8 +53,11 @@ export const makeEnsRainbowPublicConfigSchema = (valueLabel: string = "EnsRainbo }), versionInfo: z.object({ + commit: z.optional( + z.string().nonempty(`${valueLabel}.versionInfo.commit must be a non-empty string.`), + ), ensRainbow: z .string() - .nonempty({ error: `${valueLabel}.versionInfo.ensRainbow must be a non-empty string.` }), + .nonempty(`${valueLabel}.versionInfo.ensRainbow must be a non-empty string.`), }), }); diff --git a/packages/ensnode-sdk/src/internal.ts b/packages/ensnode-sdk/src/internal.ts index 7d553054ae..24f81d7d04 100644 --- a/packages/ensnode-sdk/src/internal.ts +++ b/packages/ensnode-sdk/src/internal.ts @@ -30,6 +30,7 @@ export * from "./registrars/zod-schemas"; export * from "./rpc"; export * from "./shared/config/build-rpc-urls"; export * from "./shared/config/environments"; +export * from "./shared/config/git-ref"; export * from "./shared/config/pretty-printing"; export * from "./shared/config/redacting"; export * from "./shared/config/rpc-configs-from-env"; diff --git a/packages/ensnode-sdk/src/shared/config/environments.ts b/packages/ensnode-sdk/src/shared/config/environments.ts index 7d79d3b03b..f7b8a9cb1f 100644 --- a/packages/ensnode-sdk/src/shared/config/environments.ts +++ b/packages/ensnode-sdk/src/shared/config/environments.ts @@ -1,3 +1,12 @@ +export interface GitEnvironment { + /** + * A 40-character commit hash of the codebase. + * + * This can be used to identify the exact version of the code running in a particular environment. + */ + GIT_COMMIT?: string; +} + /** * Environment variables for ENSDb configuration. */ diff --git a/packages/ensnode-sdk/src/shared/config/git-ref.test.ts b/packages/ensnode-sdk/src/shared/config/git-ref.test.ts new file mode 100644 index 0000000000..ea34df4436 --- /dev/null +++ b/packages/ensnode-sdk/src/shared/config/git-ref.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from "vitest"; + +import type { GitEnvironment } from "./environments"; +import { buildCommitRef } from "./git-ref"; + +describe("git-ref", () => { + describe("buildCommitRef", () => { + it("should return the commit reference for valid GIT_COMMIT", () => { + const envWithCommit: GitEnvironment = { + GIT_COMMIT: "1234567890abcdef1234567890abcdef12345678", + }; + const commitRef = buildCommitRef(envWithCommit); + expect(commitRef).toBe(envWithCommit.GIT_COMMIT); + }); + + it("should return undefined if GIT_COMMIT is not available", () => { + const envWithoutCommit: GitEnvironment = { + GIT_COMMIT: undefined, + }; + const commitRef = buildCommitRef(envWithoutCommit); + expect(commitRef).toBe(undefined); + }); + + it("should return undefined if GIT_COMMIT is empty", () => { + const envWithEmptyCommit: GitEnvironment = { + GIT_COMMIT: "", + }; + const commitRef = buildCommitRef(envWithEmptyCommit); + expect(commitRef).toBe(undefined); + }); + + it("should throw if GIT_COMMIT is invalid", () => { + const envWithInvalidCommit: GitEnvironment = { + GIT_COMMIT: "invalid_commit_hash", + }; + expect(() => buildCommitRef(envWithInvalidCommit)).toThrow(); + }); + }); +}); diff --git a/packages/ensnode-sdk/src/shared/config/git-ref.ts b/packages/ensnode-sdk/src/shared/config/git-ref.ts new file mode 100644 index 0000000000..fb96bb7482 --- /dev/null +++ b/packages/ensnode-sdk/src/shared/config/git-ref.ts @@ -0,0 +1,22 @@ +import { z } from "zod/v4"; + +import type { GitEnvironment } from "./environments"; +import { GitCommitShaSchema } from "./zod-schemas"; + +/** + * Build the commit reference from the given environment. + * @param env The environment containing the GIT_COMMIT variable. + * @returns The commit reference or undefined if not available + * @throws Will throw if the GIT_COMMIT variable is invalid. + */ +export function buildCommitRef(env: GitEnvironment): string | undefined { + const result = z + .preprocess((val) => (val === "" ? undefined : val), z.optional(GitCommitShaSchema)) + .safeParse(env.GIT_COMMIT); + + if (!result.success) { + throw new Error("Cannot build commit reference: invalid GIT_COMMIT value"); + } + + return result.data; +} diff --git a/packages/ensnode-sdk/src/shared/config/zod-schemas.ts b/packages/ensnode-sdk/src/shared/config/zod-schemas.ts index df193019b2..ec74840696 100644 --- a/packages/ensnode-sdk/src/shared/config/zod-schemas.ts +++ b/packages/ensnode-sdk/src/shared/config/zod-schemas.ts @@ -55,4 +55,8 @@ export const PortNumberSchema = z.coerce export const OptionalPortNumberSchema = PortNumberSchema.optional(); +export const GitCommitShaSchema = z.string().length(40, { + error: "Git commit SHA must be a 40-character string.", +}); + export const TheGraphApiKeySchema = z.string().optional(); From ff62e112454e1971bb952e2317643c57d2bcfe9c Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Jul 2026 16:49:39 +0200 Subject: [PATCH 2/5] Add field to apps' version info data model This change will be reflected in the stack info data models as well. --- apps/ensapi/src/config/environment.ts | 4 +++- apps/ensapi/src/lib/version-info.ts | 11 ++++++++++ apps/ensindexer/src/config/environment.ts | 9 +++++++-- .../public-config-builder.test.ts | 13 +++++++++++- .../public-config-builder.ts | 7 ++++++- apps/ensindexer/src/lib/version-info.ts | 10 ++++++++++ .../src/config/config.schema.test.ts | 2 +- apps/ensrainbow/src/config/environment.ts | 9 +++++++-- apps/ensrainbow/src/config/public.ts | 20 ++++++++++++++++++- 9 files changed, 76 insertions(+), 9 deletions(-) diff --git a/apps/ensapi/src/config/environment.ts b/apps/ensapi/src/config/environment.ts index 1b191a73c6..bcfd9f75a9 100644 --- a/apps/ensapi/src/config/environment.ts +++ b/apps/ensapi/src/config/environment.ts @@ -1,5 +1,6 @@ import type { EnsDbEnvironment, + GitEnvironment, LogLevelEnvironment, PortEnvironment, ReferralProgramEditionsEnvironment, @@ -14,7 +15,8 @@ import type { * their state in `process.env`. This interface is intended to be the source type which then gets * mapped/parsed into a structured configuration object like `EnsApiConfig`. */ -export type EnsApiEnvironment = EnsDbEnvironment & +export type EnsApiEnvironment = GitEnvironment & + EnsDbEnvironment & RpcEnvironment & PortEnvironment & LogLevelEnvironment & diff --git a/apps/ensapi/src/lib/version-info.ts b/apps/ensapi/src/lib/version-info.ts index 7867f46ab6..df7881d50a 100644 --- a/apps/ensapi/src/lib/version-info.ts +++ b/apps/ensapi/src/lib/version-info.ts @@ -5,6 +5,16 @@ import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import type { EnsApiVersionInfo } from "@ensnode/ensnode-sdk"; +import { buildCommitRef } from "@ensnode/ensnode-sdk/internal"; + +import type { EnsApiEnvironment } from "@/config/environment"; + +/** + * Get ENSApi codebase commit reference + */ +export function getEnsApiCommitRef(): string | undefined { + return buildCommitRef(process.env satisfies EnsApiEnvironment); +} /** * Get ENS API version @@ -101,6 +111,7 @@ function getPackageVersionFromPnpmStore(pnpmDir: string, packageName: string): s } export const ensApiVersionInfo = { + commit: getEnsApiCommitRef(), ensApi: getEnsApiVersion(), ensNormalize: getPackageVersion("@adraffy/ens-normalize"), } as const satisfies EnsApiVersionInfo; diff --git a/apps/ensindexer/src/config/environment.ts b/apps/ensindexer/src/config/environment.ts index c3c070f155..d5b1fe9127 100644 --- a/apps/ensindexer/src/config/environment.ts +++ b/apps/ensindexer/src/config/environment.ts @@ -1,4 +1,8 @@ -import type { EnsDbEnvironment, RpcEnvironment } from "@ensnode/ensnode-sdk/internal"; +import type { + EnsDbEnvironment, + GitEnvironment, + RpcEnvironment, +} from "@ensnode/ensnode-sdk/internal"; /** * Represents the raw, unvalidated environment variables for the ENSIndexer application. @@ -7,7 +11,8 @@ import type { EnsDbEnvironment, RpcEnvironment } from "@ensnode/ensnode-sdk/inte * their state in `process.env`. This interface is intended to be the source type which then gets * mapped/parsed into a structured configuration object like `ENSIndexerConfig`. */ -export type ENSIndexerEnvironment = EnsDbEnvironment & +export type ENSIndexerEnvironment = GitEnvironment & + EnsDbEnvironment & RpcEnvironment & { NAMESPACE?: string; PLUGINS?: string; diff --git a/apps/ensindexer/src/lib/public-config-builder/public-config-builder.test.ts b/apps/ensindexer/src/lib/public-config-builder/public-config-builder.test.ts index c52966478f..902e5852f2 100644 --- a/apps/ensindexer/src/lib/public-config-builder/public-config-builder.test.ts +++ b/apps/ensindexer/src/lib/public-config-builder/public-config-builder.test.ts @@ -27,6 +27,7 @@ vi.mock("@/config", () => ({ vi.mock("@/lib/version-info", () => ({ getEnsIndexerVersion: vi.fn(), getPackageVersion: vi.fn(), + getEnsIndexerCommitRef: vi.fn(), })); // Mock the SDK validation functions @@ -47,18 +48,24 @@ import { validateEnsIndexerVersionInfo, } from "@ensnode/ensnode-sdk"; -import { getEnsIndexerVersion, getPackageVersion } from "@/lib/version-info"; +import { + getEnsIndexerCommitRef, + getEnsIndexerVersion, + getPackageVersion, +} from "@/lib/version-info"; // Test fixtures const mockEnsRainbowConfig: EnsRainbowPublicConfig = { serverLabelSet: { labelSetId: "subgraph", highestLabelSetVersion: 0 }, versionInfo: { + commit: "a26a979f06f52ef0e40e69da1052e190240db29b", ensRainbow: "1.0.0", }, }; const mockVersionInfo: EnsIndexerVersionInfo = { ponder: "0.9.0", + commit: "a26a979f06f52ef0e40e69da1052e190240db29b", ensDb: "1.0.0", ensIndexer: "1.0.0", ensNormalize: "1.10.0", @@ -83,6 +90,7 @@ function createMockPublicConfig(overrides: Partial = {}) function setupStandardMocks() { vi.mocked(getEnsIndexerVersion).mockReturnValue("1.0.0"); vi.mocked(getPackageVersion).mockReturnValue("0.9.0"); + vi.mocked(getEnsIndexerCommitRef).mockReturnValue("a26a979f06f52ef0e40e69da1052e190240db29b"); vi.mocked(validateEnsIndexerVersionInfo).mockReturnValue(mockVersionInfo); } @@ -115,6 +123,7 @@ describe("PublicConfigBuilder", () => { expect(validateEnsIndexerVersionInfo).toHaveBeenCalledWith({ ponder: "0.9.0", + commit: "a26a979f06f52ef0e40e69da1052e190240db29b", ensDb: "1.0.0", ensIndexer: "1.0.0", ensNormalize: "0.9.0", @@ -182,6 +191,7 @@ describe("PublicConfigBuilder", () => { const customVersionInfo: EnsIndexerVersionInfo = { ponder: "1.0.0", + commit: "a26a979f06f52ef0e40e69da1052e190240db29b", ensDb: "2.0.0", ensIndexer: "2.0.0", ensNormalize: "1.10.0", @@ -210,6 +220,7 @@ describe("PublicConfigBuilder", () => { const customEnsRainbowConfig: EnsRainbowPublicConfig = { serverLabelSet: { labelSetId: "custom", highestLabelSetVersion: 1 }, versionInfo: { + commit: "a26a979f06f52ef0e40e69da1052e190240db29b", ensRainbow: "1.0.0", }, }; diff --git a/apps/ensindexer/src/lib/public-config-builder/public-config-builder.ts b/apps/ensindexer/src/lib/public-config-builder/public-config-builder.ts index 9b132ddcee..0c04a56af9 100644 --- a/apps/ensindexer/src/lib/public-config-builder/public-config-builder.ts +++ b/apps/ensindexer/src/lib/public-config-builder/public-config-builder.ts @@ -8,7 +8,11 @@ import { } from "@ensnode/ensnode-sdk"; import type { EnsRainbow } from "@ensnode/ensrainbow-sdk"; -import { getEnsIndexerVersion, getPackageVersion } from "@/lib/version-info"; +import { + getEnsIndexerCommitRef, + getEnsIndexerVersion, + getPackageVersion, +} from "@/lib/version-info"; export class PublicConfigBuilder { /** @@ -84,6 +88,7 @@ export class PublicConfigBuilder { return validateEnsIndexerVersionInfo({ ponder: getPackageVersion("ponder"), + commit: getEnsIndexerCommitRef(), ensDb: ensDbVersion, ensIndexer: ensIndexerVersion, ensNormalize: getPackageVersion("@adraffy/ens-normalize"), diff --git a/apps/ensindexer/src/lib/version-info.ts b/apps/ensindexer/src/lib/version-info.ts index 284d1ec1b8..332cb62822 100644 --- a/apps/ensindexer/src/lib/version-info.ts +++ b/apps/ensindexer/src/lib/version-info.ts @@ -4,8 +4,18 @@ import { existsSync, readdirSync, readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; +import { buildCommitRef } from "@ensnode/ensnode-sdk/internal"; + +import type { ENSIndexerEnvironment } from "@/config/environment"; import { logger } from "@/lib/logger"; +/** + * Get ENSIndexer codebase commit reference + */ +export function getEnsIndexerCommitRef(): string | undefined { + return buildCommitRef(process.env satisfies ENSIndexerEnvironment); +} + /** * Get ENSIndexer version */ diff --git a/apps/ensrainbow/src/config/config.schema.test.ts b/apps/ensrainbow/src/config/config.schema.test.ts index 6197f2e8a1..62b736ceb8 100644 --- a/apps/ensrainbow/src/config/config.schema.test.ts +++ b/apps/ensrainbow/src/config/config.schema.test.ts @@ -397,7 +397,7 @@ describe("buildEnsRainbowPublicConfig", () => { it("returns a valid ENSRainbow public config with correct structure", () => { const result = buildEnsRainbowPublicConfig(dbConfig); - expect(result).toStrictEqual({ + expect(result).toMatchObject({ serverLabelSet: dbConfig.serverLabelSet, versionInfo: { ensRainbow: packageJson.version, diff --git a/apps/ensrainbow/src/config/environment.ts b/apps/ensrainbow/src/config/environment.ts index 1cdf781af9..ef519eb4ad 100644 --- a/apps/ensrainbow/src/config/environment.ts +++ b/apps/ensrainbow/src/config/environment.ts @@ -1,9 +1,14 @@ -import type { LogLevelEnvironment, PortEnvironment } from "@ensnode/ensnode-sdk/internal"; +import type { + GitEnvironment, + LogLevelEnvironment, + PortEnvironment, +} from "@ensnode/ensnode-sdk/internal"; /** * Raw, unvalidated environment variables for ENSRainbow. */ -export type ENSRainbowEnvironment = PortEnvironment & +export type ENSRainbowEnvironment = GitEnvironment & + PortEnvironment & LogLevelEnvironment & { DATA_DIR?: string; DB_SCHEMA_VERSION?: string; diff --git a/apps/ensrainbow/src/config/public.ts b/apps/ensrainbow/src/config/public.ts index d504539c6a..d0ab0ac621 100644 --- a/apps/ensrainbow/src/config/public.ts +++ b/apps/ensrainbow/src/config/public.ts @@ -1,16 +1,34 @@ import packageJson from "@/../package.json" with { type: "json" }; import type { EnsRainbowServerLabelSet, EnsRainbowVersionInfo } from "@ensnode/ensnode-sdk"; +import { buildCommitRef } from "@ensnode/ensnode-sdk/internal"; import type { EnsRainbow } from "@ensnode/ensrainbow-sdk"; +import type { ENSRainbowEnvironment } from "@/config/environment"; + import type { DbConfig } from "./types"; +/** + * Get ENSRainbow codebase commit reference + */ +export function getEnsRainbowCommitRef(): string | undefined { + return buildCommitRef(process.env satisfies ENSRainbowEnvironment); +} + +/** + * Get ENSRainbow version + */ +function getEnsRainbowVersion(): string { + return packageJson.version; +} + /** Builds public config from a label set (CLI/env before DB open, or from DB after open). */ export function buildEnsRainbowPublicConfigFromLabelSet( serverLabelSet: EnsRainbowServerLabelSet, ): EnsRainbow.ENSRainbowPublicConfig { const versionInfo = { - ensRainbow: packageJson.version, + commit: getEnsRainbowCommitRef(), + ensRainbow: getEnsRainbowVersion(), } satisfies EnsRainbowVersionInfo; return { From 2a0ce7bb0e7a9d6b1eb760106e08782275f9b99c Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Jul 2026 16:52:20 +0200 Subject: [PATCH 3/5] Update docker images to accept the `GIT_COMMIT` build arg This update allows "baking in" the provided commit reference into each ENSNode app docker image --- apps/ensapi/Dockerfile | 6 ++++++ apps/ensindexer/Dockerfile | 6 ++++++ apps/ensrainbow/Dockerfile | 6 ++++++ docker/README.md | 18 ++++++++++++++++++ docker/services/ensapi.yml | 2 ++ docker/services/ensindexer.yml | 2 ++ docker/services/ensrainbow.yml | 2 ++ package.json | 9 ++++++--- 8 files changed, 48 insertions(+), 3 deletions(-) diff --git a/apps/ensapi/Dockerfile b/apps/ensapi/Dockerfile index 0b624ba28c..7f2c22a7ab 100644 --- a/apps/ensapi/Dockerfile +++ b/apps/ensapi/Dockerfile @@ -14,6 +14,12 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile FROM deps AS runner WORKDIR /app/apps/ensapi ENV NODE_ENV=production +# Read the build arg +ARG GIT_COMMIT +# Set the GIT_COMMIT as an environment variable for the app runtime. +ENV GIT_COMMIT=$GIT_COMMIT +# Attach the GIT_COMMIT as a label for the image, so it can be inspected later. +LABEL org.opencontainers.image.revision=$GIT_COMMIT EXPOSE 4334 CMD ["pnpm", "start"] diff --git a/apps/ensindexer/Dockerfile b/apps/ensindexer/Dockerfile index ee39f95330..ad25c1beb4 100644 --- a/apps/ensindexer/Dockerfile +++ b/apps/ensindexer/Dockerfile @@ -14,6 +14,12 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile FROM deps AS runner WORKDIR /app/apps/ensindexer ENV NODE_ENV=production +# Read the build arg +ARG GIT_COMMIT +# Set the GIT_COMMIT as an environment variable for the app runtime. +ENV GIT_COMMIT=$GIT_COMMIT +# Attach the GIT_COMMIT as a label for the image, so it can be inspected later. +LABEL org.opencontainers.image.revision=$GIT_COMMIT EXPOSE 42069 CMD ["pnpm", "start"] diff --git a/apps/ensrainbow/Dockerfile b/apps/ensrainbow/Dockerfile index 8523d8ec80..3158be2438 100644 --- a/apps/ensrainbow/Dockerfile +++ b/apps/ensrainbow/Dockerfile @@ -36,6 +36,12 @@ RUN chmod +x /app/apps/ensrainbow/scripts/download-prebuilt-database.sh # Set environment variables ENV NODE_ENV=production +# Read the build arg +ARG GIT_COMMIT +# Set the GIT_COMMIT as an environment variable for the app runtime. +ENV GIT_COMMIT=$GIT_COMMIT +# Attach the GIT_COMMIT as a label for the image, so it can be inspected later. +LABEL org.opencontainers.image.revision=$GIT_COMMIT # PORT is consumed by the entrypoint command, defaulting to 3223 if not set at runtime. # DB_SCHEMA_VERSION, LABEL_SET_ID, LABEL_SET_VERSION must be provided at runtime to the entrypoint. diff --git a/docker/README.md b/docker/README.md index 03ad9754a7..b4d6f9f954 100644 --- a/docker/README.md +++ b/docker/README.md @@ -21,6 +21,24 @@ All commands are run from the **monorepo root**. > docker compose -f docker/docker-compose.yml config > ``` +## Build-time environment + +The ENSNode service images (`ensindexer`, `ensapi`, `ensrainbow`) bake a `GIT_COMMIT` value into their public config's `versionInfo.commit`. When building these docker images, you can pass the `GIT_COMMIT` build arg in order to "bake in" the commit reference into each docker image. + +### Docker compose + +```bash +GIT_COMMIT=$(git rev-parse HEAD) docker compose -f docker/docker-compose.yml up --build +``` + +### Docker build + +```bash +docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) -f apps/ensapi/Dockerfile -t ghcr.io/namehash/ensnode/ensapi:latest . +docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) -f apps/ensindexer/Dockerfile -t ghcr.io/namehash/ensnode/ensindexer:latest . +docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) -f apps/ensrainbow/Dockerfile -t ghcr.io/namehash/ensnode/ensrainbow:latest . +``` + ## Use cases ### Mainnet / Sepolia diff --git a/docker/services/ensapi.yml b/docker/services/ensapi.yml index 48e822b568..9a2d74f7fa 100644 --- a/docker/services/ensapi.yml +++ b/docker/services/ensapi.yml @@ -5,6 +5,8 @@ services: build: dockerfile: ./apps/ensapi/Dockerfile context: ../.. + args: + GIT_COMMIT: ${GIT_COMMIT} ports: - "4334:4334" healthcheck: diff --git a/docker/services/ensindexer.yml b/docker/services/ensindexer.yml index 03f826b2ca..4551233d03 100644 --- a/docker/services/ensindexer.yml +++ b/docker/services/ensindexer.yml @@ -5,6 +5,8 @@ services: build: dockerfile: ./apps/ensindexer/Dockerfile context: ../.. + args: + GIT_COMMIT: ${GIT_COMMIT} ports: - "42069:42069" healthcheck: diff --git a/docker/services/ensrainbow.yml b/docker/services/ensrainbow.yml index 92612b008d..ced3cf025e 100644 --- a/docker/services/ensrainbow.yml +++ b/docker/services/ensrainbow.yml @@ -5,6 +5,8 @@ services: build: dockerfile: ./apps/ensrainbow/Dockerfile context: ../.. + args: + GIT_COMMIT: ${GIT_COMMIT} ports: - "3223:3223" volumes: diff --git a/package.json b/package.json index c4f2e38371..34e820e8e6 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,13 @@ "devnet": "docker compose -f docker/services/devnet.yml up", "docker:build:ensnode": "pnpm run -w --parallel \"/^docker:build:.*/\"", "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:ensindexer": "docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) -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 .", - "docker:build:ensapi": "docker build -f apps/ensapi/Dockerfile -t ghcr.io/namehash/ensnode/ensapi:latest .", + "docker:build:ensrainbow": "docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) -f apps/ensrainbow/Dockerfile -t ghcr.io/namehash/ensnode/ensrainbow:latest .", + "docker:build:ensapi": "docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) -f apps/ensapi/Dockerfile -t ghcr.io/namehash/ensnode/ensapi:latest .", + "docker:compose:build": "GIT_COMMIT=$(git rev-parse HEAD) docker compose -f docker/docker-compose.yml build", + "docker:compose:up": "docker compose -f docker/docker-compose.yml up -d", + "docker:compose:down": "docker compose -f docker/docker-compose.yml down", "otel-desktop-viewer": "docker run -p 8000:8000 -p 4317:4317 -p 4318:4318 davetron5000/otel-desktop-viewer:alpine-3", "generate": "pnpm run -w \"/^generate:.*/\"", "generate:openapi": "pnpm -r generate:openapi", From ef0149661784a95996f5e0aacca2231571d8d416 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Jul 2026 16:53:06 +0200 Subject: [PATCH 4/5] Update CI/CD to include the `GIT_COMMIT` docker build arg --- .github/workflows/release.yml | 2 ++ .github/workflows/release_preview.yml | 2 ++ .github/workflows/release_snapshot.yml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93ca632595..bb99455ddc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -155,6 +155,8 @@ jobs: type=semver,pattern={{version}},value=${{ matrix.apps.version }} type=ref,event=branch type=sha + build_args: | + GIT_COMMIT=${{ github.sha }} build-lambdas: name: ${{ matrix.lambdas.name }} ${{ matrix.lambdas.version }} diff --git a/.github/workflows/release_preview.yml b/.github/workflows/release_preview.yml index 9f0d617d29..f75ed41f92 100644 --- a/.github/workflows/release_preview.yml +++ b/.github/workflows/release_preview.yml @@ -247,6 +247,8 @@ jobs: type=raw,value=${{ needs.validate-and-prepare.outputs.docker-tag-base }} type=raw,value=${{ needs.validate-and-prepare.outputs.docker-tag-base }}-${{ needs.validate-and-prepare.outputs.commit-sha }} type=sha + build_args: | + GIT_COMMIT=${{ github.sha }} build-and-publish-lambdas: name: Build & Publish Preview Lambdas diff --git a/.github/workflows/release_snapshot.yml b/.github/workflows/release_snapshot.yml index 44040a97eb..59c01e1091 100644 --- a/.github/workflows/release_snapshot.yml +++ b/.github/workflows/release_snapshot.yml @@ -122,6 +122,8 @@ jobs: type=raw,value=next type=raw,value=${{ steps.snapshot-tag.outputs.tag }} type=sha + build_args: | + GIT_COMMIT=${{ github.sha }} build-and-publish-lambdas: name: Build & Publish Snapshot Lambdas From 6a7ee26397caf58a467b86b96d934e8d7cc1dab5 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Jul 2026 16:53:16 +0200 Subject: [PATCH 5/5] Update OpenAPI Spec --- docs/ensnode.io/ensapi-openapi.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/ensnode.io/ensapi-openapi.json b/docs/ensnode.io/ensapi-openapi.json index d12c6cdf6d..f1a16f34f0 100644 --- a/docs/ensnode.io/ensapi-openapi.json +++ b/docs/ensnode.io/ensapi-openapi.json @@ -821,6 +821,7 @@ "versionInfo": { "type": "object", "properties": { + "commit": { "type": "string", "minLength": 1 }, "ensRainbow": { "type": "string", "minLength": 1 } }, "required": ["ensRainbow"] @@ -860,6 +861,7 @@ "type": "object", "properties": { "ponder": { "type": "string", "minLength": 1 }, + "commit": { "type": "string", "minLength": 1 }, "ensDb": { "type": "string", "minLength": 1 }, "ensIndexer": { "type": "string", "minLength": 1 }, "ensNormalize": { "type": "string", "minLength": 1 } @@ -896,7 +898,10 @@ }, "versionInfo": { "type": "object", - "properties": { "ensRainbow": { "type": "string", "minLength": 1 } }, + "properties": { + "commit": { "type": "string", "minLength": 1 }, + "ensRainbow": { "type": "string", "minLength": 1 } + }, "required": ["ensRainbow"] } }, @@ -931,6 +936,7 @@ "versionInfo": { "type": "object", "properties": { + "commit": { "type": "string", "minLength": 1 }, "ensRainbow": { "type": "string", "minLength": 1 } }, "required": ["ensRainbow"] @@ -970,6 +976,7 @@ "type": "object", "properties": { "ponder": { "type": "string", "minLength": 1 }, + "commit": { "type": "string", "minLength": 1 }, "ensDb": { "type": "string", "minLength": 1 }, "ensIndexer": { "type": "string", "minLength": 1 }, "ensNormalize": { "type": "string", "minLength": 1 } @@ -1020,6 +1027,7 @@ "versionInfo": { "type": "object", "properties": { + "commit": { "type": "string", "minLength": 1 }, "ensApi": { "type": "string", "minLength": 1 }, "ensNormalize": { "type": "string", "minLength": 1 } },