Skip to content

chore: harden release CD verification #15

chore: harden release CD verification

chore: harden release CD verification #15

Workflow file for this run

name: CD - Docker Hub Publish
on:
push:
branches: [master, main]
tags:
- 'v*.*.*'
env:
BACKEND_IMAGE: jobhunter-backend
FRONTEND_IMAGE: jobhunter-frontend
jobs:
docker-hub-check:
name: Check Docker Hub credentials
runs-on: ubuntu-latest
if: >-
github.event_name == 'push' &&
(github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' ||
startsWith(github.ref, 'refs/tags/v'))
outputs:
should_publish: ${{ steps.gate.outputs.should_publish }}
steps:
- name: Evaluate Docker Hub secrets
id: gate
env:
DH_USER: ${{ secrets.DOCKERHUB_USERNAME }}
DH_PASS: ${{ secrets.DOCKERHUB_PASSWORD }}
run: |
if [ -n "$DH_USER" ] && [ -n "$DH_PASS" ]; then
echo "should_publish=true" >> "$GITHUB_OUTPUT"
else
echo "should_publish=false" >> "$GITHUB_OUTPUT"
echo "::notice title=Docker Hub publish skipped::Add DOCKERHUB_USERNAME and DOCKERHUB_PASSWORD in repository Actions secrets to enable image publishing."
fi
publish:
name: Publish to Docker Hub
runs-on: ubuntu-latest
needs: docker-hub-check
if: >-
needs.docker-hub-check.outputs.should_publish == 'true' &&
github.event_name == 'push' &&
(github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' ||
startsWith(github.ref, 'refs/tags/v'))
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Docker metadata (backend)
id: meta-backend
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.BACKEND_IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha,prefix=
flavor: |
latest=true
labels: |
org.opencontainers.image.title=Jobhunter Backend API
org.opencontainers.image.description=Jobhunter REST API backend - Spring Boot, JWT authentication, RBAC, Flyway, MySQL, and production MVP workflows.
org.opencontainers.image.source=https://github.com/JasonTM17/JobHunter_SpringBoot_RestfulAPI_React
org.opencontainers.image.licenses=MIT
org.opencontainers.image.vendor=Jobhunter
- name: Docker metadata (frontend)
id: meta-frontend
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.FRONTEND_IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha,prefix=
flavor: |
latest=true
labels: |
org.opencontainers.image.title=Jobhunter Frontend Web
org.opencontainers.image.description=Jobhunter frontend - Next.js 16, TypeScript, responsive IT recruitment UI, E2E coverage, and visual regression.
org.opencontainers.image.source=https://github.com/JasonTM17/JobHunter_SpringBoot_RestfulAPI_React
org.opencontainers.image.licenses=MIT
org.opencontainers.image.vendor=Jobhunter
- name: Build and push backend
uses: docker/build-push-action@v6
with:
context: ./backend
push: true
tags: ${{ steps.meta-backend.outputs.tags }}
labels: ${{ steps.meta-backend.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
sbom: true
- name: Build and push frontend
uses: docker/build-push-action@v6
with:
context: ./frontend
push: true
tags: ${{ steps.meta-frontend.outputs.tags }}
labels: ${{ steps.meta-frontend.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
sbom: true
- name: Image digest (backend)
run: |
echo "Backend image: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.BACKEND_IMAGE }}"
echo "Tags: ${{ steps.meta-backend.outputs.tags }}"
- name: Image digest (frontend)
run: |
echo "Frontend image: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.FRONTEND_IMAGE }}"
echo "Tags: ${{ steps.meta-frontend.outputs.tags }}"
verify-tag-build:
name: Verify tag Docker build
runs-on: ubuntu-latest
needs: docker-hub-check
if: >-
github.event_name == 'push' &&
startsWith(github.ref, 'refs/tags/v') &&
needs.docker-hub-check.outputs.should_publish != 'true'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Verify backend build
uses: docker/build-push-action@v6
with:
context: ./backend
push: false
load: true
tags: jobhunter-backend:verify
cache-from: type=gha
- name: Verify frontend build
uses: docker/build-push-action@v6
with:
context: ./frontend
push: false
load: true
tags: jobhunter-frontend:verify
cache-from: type=gha
- name: Docker images
run: docker images | grep jobhunter
- name: Summary
run: |
echo "## Release Docker verification" >> "$GITHUB_STEP_SUMMARY"
echo "Tag: ${{ github.ref_name }}" >> "$GITHUB_STEP_SUMMARY"
echo "Docker Hub publish: skipped because credentials are not configured" >> "$GITHUB_STEP_SUMMARY"
echo "Backend image: jobhunter-backend:verify" >> "$GITHUB_STEP_SUMMARY"
echo "Frontend image: jobhunter-frontend:verify" >> "$GITHUB_STEP_SUMMARY"