# Unified CI image for Harb services (ponder, webapp, landing, txnBot). # Parameterized via build args — eliminates per-service Dockerfile duplication. # # Usage: # docker build -f docker/Dockerfile.service-ci \ # --build-arg SERVICE_DIR=services/ponder \ # --build-arg SERVICE_PORT=42069 \ # --build-arg ENTRYPOINT_SCRIPT=containers/ponder-entrypoint.sh \ # -t ponder-ci . # ── Build args (declared early for builder stage) ────────────────── ARG SERVICE_DIR ARG NPM_INSTALL_CMD=ci # ── Builder stage ────────────────────────────────────────────────── FROM node:20-alpine AS builder RUN apk add --no-cache git bash WORKDIR /app # Copy root package files COPY package.json package-lock.json ./ # Copy kraiken-lib package files COPY kraiken-lib/package.json kraiken-lib/package-lock.json ./kraiken-lib/ # Copy ABI files needed by kraiken-lib COPY onchain/out/Kraiken.sol/Kraiken.json ./onchain/out/Kraiken.sol/ COPY onchain/out/Stake.sol/Stake.json ./onchain/out/Stake.sol/ # Copy Stake.sol for sync-tax-rates + the script itself COPY onchain/src/Stake.sol ./onchain/src/ COPY scripts/sync-tax-rates.mjs ./scripts/ # Install kraiken-lib dependencies, run sync-tax-rates, and build WORKDIR /app/kraiken-lib RUN npm ci --ignore-scripts COPY kraiken-lib/ ./ RUN node ../scripts/sync-tax-rates.mjs && ./node_modules/.bin/tsc # Install service dependencies ARG SERVICE_DIR ARG NPM_INSTALL_CMD WORKDIR /app/${SERVICE_DIR} COPY ${SERVICE_DIR}/package.json ./ # Use glob pattern to optionally copy package-lock.json (txnBot has none) COPY ${SERVICE_DIR}/package-lock.jso[n] ./ RUN if [ "$NPM_INSTALL_CMD" = "install" ]; then npm install; else npm ci; fi # Copy service source COPY ${SERVICE_DIR}/ ./ # Copy onchain deployment artifacts (glob handles missing files) WORKDIR /app COPY onchain/deployments*.jso[n] ./onchain/ # ── Runtime stage ────────────────────────────────────────────────── FROM node:20-alpine RUN apk add --no-cache dumb-init wget bash WORKDIR /app # Copy kraiken-lib (src for Vite alias, dist for runtime, package.json for resolution) COPY --from=builder /app/kraiken-lib/src ./kraiken-lib/src COPY --from=builder /app/kraiken-lib/dist ./kraiken-lib/dist COPY --from=builder /app/kraiken-lib/package.json ./kraiken-lib/ # Copy service with all node_modules ARG SERVICE_DIR COPY --from=builder /app/${SERVICE_DIR} ./${SERVICE_DIR} # Copy onchain artifacts COPY --from=builder /app/onchain ./onchain # Create placeholder deployments-local.json if not present RUN test -f /app/onchain/deployments-local.json || \ (mkdir -p /app/onchain && echo '{"contracts":{}}' > /app/onchain/deployments-local.json) # Conditionally create symlinks for Vite path resolution (webapp only) ARG NEEDS_SYMLINKS=false RUN if [ "$NEEDS_SYMLINKS" = "true" ]; then \ ln -sf /app/web-app /web-app && \ ln -sf /app/kraiken-lib /kraiken-lib && \ ln -sf /app/onchain /onchain; \ fi # Copy entrypoint script # For services with entrypoints (ponder, webapp, txnbot): pass the actual entrypoint # For landing (no entrypoint): defaults to entrypoint-common.sh which is just helpers ARG ENTRYPOINT_SCRIPT=containers/entrypoint-common.sh COPY ${ENTRYPOINT_SCRIPT} /entrypoint.sh RUN chmod +x /entrypoint.sh # Set working directory to service WORKDIR /app/${SERVICE_DIR} ARG NODE_ENV=production ENV NODE_ENV=${NODE_ENV} ENV HOST=0.0.0.0 ARG SERVICE_PORT=8080 ENV PORT=${SERVICE_PORT} EXPOSE ${SERVICE_PORT} ARG HEALTHCHECK_PATH=/ ARG HEALTHCHECK_RETRIES=12 ARG HEALTHCHECK_START=20s HEALTHCHECK --interval=5s --timeout=3s --retries=${HEALTHCHECK_RETRIES} --start-period=${HEALTHCHECK_START} \ CMD wget --spider -q http://127.0.0.1:${PORT}${HEALTHCHECK_PATH} || exit 1 ENTRYPOINT ["dumb-init", "--", "/entrypoint.sh"]