@@ -85,11 +85,20 @@ jobs:
8585 - uses : docker/setup-qemu-action@v3
8686 - uses : docker/setup-buildx-action@v3
8787
88- - name : Build image (amd64) for validation
89- run : docker buildx build --load --platform linux/amd64 -t bcimg:local "$C/$V"
88+ # Build each target platform locally so Trivy can gate the EXACT images that get
89+ # published. buildx caches these builds, so the later `--push` reuses the same
90+ # layers — the pushed image is byte-for-byte what was scanned. arm64 is
91+ # best-effort: many legacy base images are amd64-only, so its build fails fast.
92+ - name : Build amd64 image
93+ run : docker buildx build --load --platform linux/amd64 -t bcimg:amd64 "$C/$V"
94+
95+ - name : Build arm64 image (best-effort)
96+ id : arm64
97+ continue-on-error : true
98+ run : docker buildx build --load --platform linux/arm64 -t bcimg:arm64 "$C/$V"
9099
91100 - name : Extract labels
92- run : docker inspect --format '{{json .Config.Labels}}' bcimg:local > labels.json
101+ run : docker inspect --format '{{json .Config.Labels}}' bcimg:amd64 > labels.json
93102
94103 - name : Check labels and compute tag
95104 id : check
@@ -107,26 +116,25 @@ jobs:
107116 cmd="${cmd%$'\r'}" # tolerate CRLF line endings
108117 case "$cmd" in ''|\#*) continue ;; esac # skip blank and comment lines
109118 echo "::group::TEST $cmd"
110- if docker run --rm bcimg:local $cmd; then echo "ok"; else echo "FAILED"; status=1; fi
119+ if docker run --rm bcimg:amd64 $cmd; then echo "ok"; else echo "FAILED"; status=1; fi
111120 echo "::endgroup::"
112121 done < "$TESTS"
113122 exit $status
114123
115- # --- Security gate: scan the locally-built image BEFORE pushing. ---
116- # A fixable HIGH/CRITICAL CVE fails the job, so nothing is pushed. Unfixable
117- # OS-level CVEs are ignored. Maintainers can accept specific CVEs by adding a
118- # `.trivyignore` (CVE IDs, one per line) in the container's version directory.
124+ # --- Security gate: scan every platform that WILL be published, before push. ---
125+ # A fixable HIGH/CRITICAL CVE fails the job so nothing is pushed. Unfixable OS
126+ # CVEs are ignored; a per-container `.trivyignore` (CVE IDs) can accept specific ones.
119127 - name : Prepare Trivy ignore file
120128 run : |
121129 if [ -f "$C/$V/.trivyignore" ]; then
122130 cp "$C/$V/.trivyignore" .trivyignore
123131 echo "Using per-container .trivyignore:"; cat .trivyignore
124132 fi
125133
126- - name : Trivy security scan ( gate on fixable HIGH/CRITICAL )
134+ - name : Trivy gate (amd64 )
127135 uses : aquasecurity/trivy-action@0.35.0
128136 with :
129- image-ref : bcimg:local
137+ image-ref : bcimg:amd64
130138 format : sarif
131139 output : trivy.sarif
132140 severity : HIGH,CRITICAL
@@ -141,30 +149,37 @@ jobs:
141149 sarif_file : trivy.sarif
142150 category : trivy-${{ env.C }}
143151
152+ - name : Trivy gate (arm64)
153+ if : ${{ steps.arm64.outcome == 'success' }}
154+ uses : aquasecurity/trivy-action@0.35.0
155+ with :
156+ image-ref : bcimg:arm64
157+ format : table
158+ severity : HIGH,CRITICAL
159+ ignore-unfixed : true
160+ exit-code : ' 1'
161+
144162 - name : Log in to DockerHub
145163 uses : docker/login-action@v3
146164 with :
147165 username : ${{ secrets.DOCKERHUB_USERNAME }}
148166 password : ${{ secrets.DOCKERHUB_TOKEN }}
149167
150- - name : Build and push (multi-arch, amd64 fallback )
168+ - name : Push scanned image(s )
151169 env :
152170 TAG : ${{ steps.check.outputs.tag }} # validated [A-Za-z0-9._-]; used only as "$TAG"
171+ ARM64_OK : ${{ steps.arm64.outcome }}
153172 run : |
154173 IMG="biocontainers/${C}:${TAG}"
155- # No --pull: reuse the exact amd64 base/layers that the Trivy gate scanned
156- # above (bcimg:local was built from the same context/cache), so the amd64
157- # image we publish is the one that passed the gate. The arm64 image is built
158- # fresh and is not independently scanned; it uses the same Dockerfile/packages.
159- # Many legacy base images are amd64-only, so the arm64 leg may fail fast at the
160- # FROM pull — fall back to amd64-only in that case.
161- if docker buildx build --platform linux/amd64,linux/arm64 --push -t "$IMG" "$C/$V"; then
162- echo "Published multi-arch (amd64+arm64): $IMG"
163- echo "- \`$IMG\` — **multi-arch** (amd64 + arm64)" >> "$GITHUB_STEP_SUMMARY"
174+ # --push reuses the buildx cache from the --load builds above, so the pushed
175+ # images are exactly the ones Trivy just gated.
176+ if [ "$ARM64_OK" = "success" ]; then
177+ docker buildx build --platform linux/amd64,linux/arm64 --push -t "$IMG" "$C/$V"
178+ echo "Published multi-arch (amd64+arm64, both scanned): $IMG"
179+ echo "- \`$IMG\` — **multi-arch** (amd64 + arm64, both scanned)" >> "$GITHUB_STEP_SUMMARY"
164180 else
165- echo "arm64 build failed (base likely amd64-only); publishing amd64-only."
166181 docker buildx build --platform linux/amd64 --push -t "$IMG" "$C/$V"
167- echo "Published amd64-only: $IMG"
182+ echo "Published amd64-only (scanned) : $IMG"
168183 echo "- \`$IMG\` — **amd64-only** (base has no arm64)" >> "$GITHUB_STEP_SUMMARY"
169184 fi
170185
0 commit comments