Skip to content

trading stevedore cutover: exploit the build-once Dockerfile (compile once, stamp 13 images) #39

Description

@blairham

Follow-up to pinpredict/trading#1967 (build-once Dockerfile, PR pinpredict/trading#1968) and part of the #38 migration. Captures the rollout plan and the findings from the 2026-07-05 verification session so they don't get lost.

Context — what's already done

  • trading Dockerfile restructured build-once (trading#1968, 3 commits): build stage restores+publishes all 12 .NET services with no build-arg references → byte-identical layers for every image; production stage stays PROJECT-parameterized and final. Verified: /app trees sha256-identical to the old per-service build (oms/reconciler/nadex-oegw); image sizes unchanged (±~25 B on ~124 MB); second image assembles in ~2 s from cache; full local stevedore build of the 13-image catalog passes with the publish layer CACHED on every image.
  • trading ci/stevedore-config branch ready (4 commits, rebased, local): includes two bugs found by actually building — missing RUNTIME_BASE (fixed by defaulting the Dockerfile to aspnet:10.0; every consumer already passed it) and settlements-csv needing context: . (its Dockerfile COPYs repo-root-relative paths).
  • Measured baseline: full 13-job docker-release matrix averages ~69 s/job ≈ ~15 runner-min and ~1.5 min wall per shared release (GHA cache warm).

Why the CI shape matters

Trading's 13 images cannot form a stevedore build group (grouping = identical image, many repos — e.g. dis's 5-way group; trading's images differ by content). So:

  • Matrix mode fan-out (plan → one runner per group): 13 cold BuildKit caches → the shared build stage compiles 13× (each a union compile) ≈ ~40–50 runner-min. Worse than today.
  • Single-job shape (the current draft stevedore-release.yml): one runner, one BuildKit; the solver dedups the shared build stage even across concurrent builds (parallel: 4 still compiles once) → ~4–5 runner-min per shared release.

Plan

  • Merge trading#1968 (byte-parity + boot-smoke verified; one full release wave on merge is expected — Dockerfile is a shared rebuild pattern).
  • Wire cross-run caching into the stevedore path: one type=gha scope now serves the shared build stage for all trading images (vs 13 per-service scopes thrashing the 10 GB repo cache limit under docker-release).
  • Clear the remaining stevedore migration readiness — blockers before docker-release runs on stevedore #38 blocker: blairham/stevedore#6 machine-readable publish summary → notify-dispatch.
  • Cut trading over on the single-job workflow shape: open the ci/stevedore-config PR, flip trading's ci.yml off docker-release.yml@pre-stevedore, drop the pin.
  • Optional stevedore feature (needed only if trading should later ride matrix mode for per-service xp-<name>-gha-push roles): cache affinity — coalesce plan groups that share (dockerfile, context) into one matrix entry with combined only: + merged pins, so same-Dockerfile images land on one runner while dis-style repos still fan out. File on blairham/stevedore if pursued.
  • Audit the other 12 repos' .stevedore.yaml for the settlements-csv bug class (context: vs Dockerfile COPY paths) — it only surfaces when stevedore actually builds, so run stevedore build locally per repo before opening each config PR.

Immediate wins that need no further work once #1968 merges

scripts/chaos-rig-*.sh and docker-compose builds (5–6 images serially from a developer checkout) drop from N full compiles to one compile + seconds per image, and builds are now hermetic from dirty checkouts (**/bin/**/obj excluded from the context).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-followupFollow-up work captured from a Claude session

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions