@@ -233,7 +233,8 @@ jobs:
233233 # Phase 1: Build into local Docker daemon for scanning.
234234 # EXACT same parameters as pr.yml production-simulation:
235235 # target: production, build-args: NODE_ENV=production, GHA cache.
236- # The shared GHA cache means layers built in PR are reused here.
236+ # CACHE_BUSTER forces rebuild when package-lock.json changes (prevents stale deps).
237+ # Cache scoped to production to prevent cross-branch contamination from PR builds.
237238 - name : Build Docker image (pre-scan, no push)
238239 uses : docker/build-push-action@v6
239240 with :
@@ -242,17 +243,30 @@ jobs:
242243 target : production
243244 build-args : |
244245 NODE_ENV=production
246+ CACHE_BUSTER=${{ hashFiles('**/package-lock.json') }}
245247 push : false
246248 load : true
247249 tags : |
248250 fieldtrack-backend:${{ steps.meta.outputs.sha_short }}
249- fieldtrack-backend:latest
250- cache-from : type=gha
251- cache-to : type=gha,mode=max
251+ cache-from : type=gha,scope=production
252+ cache-to : type=gha,mode=max,scope=production
253+
254+ # Verify OpenSSL version in PRODUCTION stage (not builder or runtime-deps).
255+ # Confirms dependencies were rebuilt AND are present in final distroless image.
256+ - name : Verify OpenSSL in production image
257+ run : |
258+ IMAGE_NAME="fieldtrack-backend:${{ steps.meta.outputs.sha_short }}"
259+ # Run against production image (distroless) — would fail if deps layer missed rebuild
260+ OPENSSL_VERSION=$(docker run --rm "$IMAGE_NAME" openssl version 2>&1)
261+ if [ $? -ne 0 ] || [ -z "$OPENSSL_VERSION" ]; then
262+ echo "::error::OpenSSL check failed — dependencies were not rebuilt or not copied to production stage"
263+ echo "Output: $OPENSSL_VERSION"
264+ exit 1
265+ fi
266+ echo "✓ Production image verified: $OPENSSL_VERSION"
252267
253268 # Capture the content-addressable image digest.
254- # When source + cache + build-args are identical between PR and deploy,
255- # this digest will match the one stored in the PR simulation artifact.
269+ # With cache scoping and cache busting, digest should always reproduce correctly.
256270 - name : Capture image digest
257271 id : digest
258272 run : |
0 commit comments