diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 6b461d4..c5b4352 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -178,14 +178,19 @@ jobs: done built=0 for i in 1 2 3 4 5; do - if docker compose build; then built=1; break; fi + # --pull refreshes base images; the build args carry the new commit + # so the cache is invalidated per release. + if docker compose build --pull; then built=1; break; fi echo "docker compose build failed (attempt $i); retrying in 30s" sleep 30 done [ "$built" = "1" ] || { echo "::error::compose build failed after retries"; exit 1; } - # Bring the new stack up and wait for healthchecks. No pre-`down`, so - # the old containers keep serving until the new ones are healthy. - if ! docker compose up -d --remove-orphans --wait --wait-timeout 300; then + # --force-recreate guarantees the containers are replaced with the + # freshly built images. Without it a config-identical service can be + # left running the previous image (stale UI after a deploy). + # No pre-`down`, so the old containers keep serving until the new ones + # are healthy. + if ! docker compose up -d --remove-orphans --force-recreate --wait --wait-timeout 300; then echo "::error::New stack is unhealthy; rolling back to previous images" docker compose down --remove-orphans || true for img in crypto_trading-backend crypto_trading-frontend; do @@ -193,7 +198,7 @@ jobs: docker tag "${img}:prev" "$img" fi done - docker compose up -d --remove-orphans --wait --wait-timeout 300 || true + docker compose up -d --remove-orphans --force-recreate --wait --wait-timeout 300 || true exit 1 fi @@ -202,6 +207,19 @@ jobs: docker compose ps docker compose logs --tail=80 + - name: Verify the deployed frontend matches this commit + run: | + # The container must serve the build stamped with this commit's SHA. + # Without this, a config-identical service could keep an old image and + # serve a stale UI even though the deploy "succeeded". + want="${CRYPTO_BUILD_VERSION:-unknown}" + got="$(docker compose exec -T frontend cat /usr/share/nginx/html/.build-version 2>/dev/null | tr -d '[:space:]' || true)" + echo "expected=${want} served=${got:-}" + if [ "$want" != "unknown" ] && [ "$got" != "$want" ]; then + echo "::error::frontend container is serving a stale build; failing deploy" + exit 1 + fi + - name: Health check (HTTP) run: | probe_backend() { diff --git a/frontend/Dockerfile b/frontend/Dockerfile index f9633d1..83404ec 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -19,4 +19,9 @@ ENV API_TOKEN= COPY --from=build /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/templates/default.conf.template + +# Marker so the deploy can verify the served build matches the commit. +ARG BUILD_VERSION=unknown +RUN printf '%s' "${BUILD_VERSION}" > /usr/share/nginx/html/.build-version + EXPOSE 80