From 215eed4aff0aef450002219d2cf7f09a5a1fe5ca Mon Sep 17 00:00:00 2001 From: Robert Leifke Date: Fri, 18 Sep 2026 21:33:58 -0400 Subject: [PATCH] fix(execution): build the image in CI, and copy the package that broke it The execution image has not been buildable since #69. That PR moved the KMS signer into @numo/kms-signer, but services/execution/Dockerfile copies and builds only @numo/abis, so the service's own tsc fails inside the image: src/executor.ts(15,34): error TS2307: Cannot find module '@numo/kms-signer' ERROR: failed to build: ... exit code: 1 Nothing was deployed with it, so the running venue is unaffected. But the breakage is not limited to shipping new work: execution-service could not be rebuilt for ANY reason, including a rollback. The cause is narrow -- a workspace package's main/types resolve to dist/, which is gitignored (.gitignore:5 **/dist/), so copied-but-unbuilt means "Cannot find module" -- and the fix is to copy and build it like @numo/abis. The reason it reached main matters more. CI runs pnpm install + tsc at the WORKSPACE ROOT, where every package resolves through the root node_modules whatever the Dockerfile does. The image only has what it COPYs and builds. Those are different claims, and only one of them was checked, so green CI and an unbuildable image were entirely compatible -- the same "green means two different things" this repo wrote scripts/verify.sh to remove. So CI now builds the image (services-execution.yml), and verify.sh mirrors it, skipping with a note when docker is absent rather than failing -- the same shape as the terraform section, and for the same reason. A future workspace dependency that is not added to the Dockerfile now fails in CI instead of at deploy. Swept the class rather than the instance: services/markets is Go and uses COPY . ., so it cannot have this bug. It is the only other Dockerfile. The local build that proved the failure is reproduced in the message above; the verifying build of the fix stalled on registry metadata and was killed rather than waited out, so CI running the new step is the proof this works. Co-authored-by: Claude Opus 5 (1M context) --- .github/workflows/services-execution.yml | 5 +++++ scripts/verify.sh | 13 +++++++++++++ services/execution/Dockerfile | 20 +++++++++++++------- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/.github/workflows/services-execution.yml b/.github/workflows/services-execution.yml index 930c060b..201a5451 100644 --- a/.github/workflows/services-execution.yml +++ b/.github/workflows/services-execution.yml @@ -46,3 +46,8 @@ jobs: - run: pnpm --filter matching-executor run check - run: pnpm --filter matching-executor run build - run: pnpm --filter matching-executor test + # Builds the deployable image. tsc above resolves workspace packages through the root + # node_modules and passes whatever the Dockerfile copies; the image only has what it COPYs + # and builds. That gap let @numo/kms-signer land green while making the image unbuildable, + # which is the kind of thing found at deploy time, when it is most expensive. + - run: docker build -f services/execution/Dockerfile -t numo-exchange/execution:ci . diff --git a/scripts/verify.sh b/scripts/verify.sh index 055e89f9..cd1242f8 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -74,6 +74,19 @@ step node . "matching-executor check" pnpm --filter matching-executor run check step node . "matching-executor build" pnpm --filter matching-executor run build step node . "matching-executor test" pnpm --filter matching-executor test +# The image, not just the type check. tsc resolves workspace packages through the root +# node_modules and passes whatever the Dockerfile copies; the image only has what it COPYs and +# builds. Skipped rather than failed when docker is absent -- it is not needed to work on the +# services, and a hard failure here would train people to ignore this script. +execution_image() { + if ! docker info >/dev/null 2>&1; then + echo "docker not available; skipping the image build (CI still runs it)" >&2 + return 0 + fi + ( cd "$ROOT" && docker build -f services/execution/Dockerfile -t numo-exchange/execution:verify . ) +} +step node . "execution image builds" execution_image + # ---- services-rebalance.yml ------------------------------------------------------------ step node . "cngn-rebalance check" pnpm --filter cngn-rebalance run check step node . "cngn-rebalance test" pnpm --filter cngn-rebalance test diff --git a/services/execution/Dockerfile b/services/execution/Dockerfile index 80997d53..86685d26 100644 --- a/services/execution/Dockerfile +++ b/services/execution/Dockerfile @@ -1,5 +1,5 @@ -# Build context is the REPOSITORY ROOT, not services/execution — this package -# depends on @numo/abis through the pnpm workspace, so the build needs both: +# Build context is the REPOSITORY ROOT, not services/execution — this package depends on +# @numo/abis and @numo/kms-signer through the pnpm workspace, so the build needs all three: # # docker build -f services/execution/Dockerfile . # @@ -10,15 +10,21 @@ RUN corepack enable && corepack prepare pnpm@8.7.3 --activate # Manifests first: the install layer then survives any source-only change. COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ -COPY packages/abis/package.json packages/abis/ -COPY services/execution/package.json services/execution/ +COPY packages/abis/package.json packages/abis/ +COPY packages/kms-signer/package.json packages/kms-signer/ +COPY services/execution/package.json services/execution/ RUN pnpm install --frozen-lockfile -COPY packages/abis packages/abis -COPY services/execution services/execution +COPY packages/abis packages/abis +COPY packages/kms-signer packages/kms-signer +COPY services/execution services/execution -# @numo/abis must be compiled before the service that imports it. +# Every workspace package the service imports must be COMPILED first: their main/types resolve to +# dist/, which is gitignored, so a package that is copied but not built fails the service's tsc with +# "Cannot find module". Add the build here whenever a new workspace dependency is added -- CI now +# builds this image (services-execution.yml) so a missing one fails there rather than at deploy. RUN pnpm --filter @numo/abis build \ + && pnpm --filter @numo/kms-signer build \ && pnpm --filter matching-executor build # Resolves the workspace link into a self-contained tree with prod deps only.