chore: harden release CD verification #15
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |