From fd015bf0a38d4aee3e9648be3427f85916c546bb Mon Sep 17 00:00:00 2001 From: Mohammad Al Faiyaz Date: Fri, 23 Oct 2026 17:13:39 -0400 Subject: [PATCH] ci(docker): verify production image starts and serves health checks PR CI built the production image but never loaded or ran it, so the image runtime was never exercised. The build now targets linux/amd64 explicitly and loads the image into the runner Docker daemon. A smoke step starts both application modes from that image with TLS disabled, runs AWM and Master Express on a temporary bridge network with a DNS alias, and polls three health checks for 200: POST /ping, POST /advancedwallet/ping, and POST /ping/advancedWalletManager (MBE to AWM over the network). Non-200 or timeout fails the job; the EXIT trap prints container logs on failure and removes containers/network. Ticket: WCN-3794 --- .github/workflows/pull_request.yaml | 71 +++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index 7549f549..54c459cc 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -110,6 +110,8 @@ jobs: with: context: . push: false + platforms: linux/amd64 + load: true tags: | ghcr.io/bitgo/advanced-wallets:${{ github.sha }} build-args: | @@ -118,3 +120,72 @@ jobs: VCS_REF=${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max + + - name: Smoke test image + run: | + set -euo pipefail + + IMAGE="ghcr.io/bitgo/advanced-wallets:${GITHUB_SHA}" + NETWORK="advanced-wallets-smoke-${GITHUB_RUN_ID}" + AWM_CONTAINER="advanced-wallets-smoke-awm-${GITHUB_RUN_ID}" + MBE_CONTAINER="advanced-wallets-smoke-mbe-${GITHUB_RUN_ID}" + FAILED="false" + + cleanup() { + if [ "${FAILED}" = "true" ]; then + echo "::group::AWM container logs" + docker logs "${AWM_CONTAINER}" || true + echo "::endgroup::" + echo "::group::Master Express container logs" + docker logs "${MBE_CONTAINER}" || true + echo "::endgroup::" + fi + docker rm -f "${AWM_CONTAINER}" >/dev/null 2>&1 || true + docker rm -f "${MBE_CONTAINER}" >/dev/null 2>&1 || true + docker network rm "${NETWORK}" >/dev/null 2>&1 || true + } + trap cleanup EXIT + + docker network create "${NETWORK}" + + docker run -d --name "${AWM_CONTAINER}" \ + --network "${NETWORK}" \ + --network-alias advanced-wallet-manager \ + -p 127.0.0.1:13080:3080 \ + -e APP_MODE=advanced-wallet-manager \ + -e ADVANCED_WALLET_MANAGER_PORT=3080 \ + -e BIND=0.0.0.0 \ + -e TLS_MODE=disabled \ + -e KEY_PROVIDER_URL=https://key-provider.invalid \ + "${IMAGE}" + + docker run -d --name "${MBE_CONTAINER}" \ + --network "${NETWORK}" \ + -p 127.0.0.1:13081:3081 \ + -e APP_MODE=master-express \ + -e MASTER_EXPRESS_PORT=3081 \ + -e BIND=0.0.0.0 \ + -e TLS_MODE=disabled \ + -e ADVANCED_WALLET_MANAGER_URL=http://advanced-wallet-manager:3080 \ + "${IMAGE}" + + wait_for() { + local url="${1}" + local status="none" + for _ in $(seq 1 30); do + if status="$(curl -s -o /dev/null -w '%{http_code}' -X POST "${url}")" && [ "${status}" = "200" ]; then + echo "${url} -> 200" + return 0 + fi + sleep 1 + done + echo "Timed out waiting for POST ${url} (last status: ${status})" + FAILED="true" + return 1 + } + + wait_for "http://127.0.0.1:13080/ping" + wait_for "http://127.0.0.1:13081/advancedwallet/ping" + wait_for "http://127.0.0.1:13081/ping/advancedWalletManager" + + echo "Production image smoke checks passed for both application modes."