Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
338 changes: 111 additions & 227 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -1,215 +1,70 @@
name: Docker Build, Scan & Publish
name: Docker Scan & Promote

# Runs after Stack Tests completes on main — promotes sha-xxx → latest.
# "latest" is NEVER set during build. Only this workflow can set it,
# and only after all tests pass. If any test fails, latest stays unchanged.
on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]
workflow_run:
workflows: ["Stack Tests"]
types: [completed]
workflow_dispatch:
inputs:
sha:
description: 'Full commit SHA to promote (defaults to latest main)'
required: false

env:
REGISTRY: ghcr.io

jobs:
build-base:
name: Build Base
scan:
name: Scan ${{ matrix.image }}
if: >
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main')
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

outputs:
image-tag: ${{ steps.image-tag.outputs.tag }}

steps:
- uses: actions/checkout@v6

- name: Set lowercase image prefix
run: echo "IMAGE_PREFIX=${GITHUB_REPOSITORY_OWNER,,}/integr8scode" >> $GITHUB_ENV

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/base
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}

- name: Determine image tag for dependent builds
id: image-tag
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "tag=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
else
echo "tag=latest" >> $GITHUB_OUTPUT
fi

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./backend
file: ./backend/Dockerfile.base
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=base
cache-to: type=gha,mode=max,scope=base

build-backend:
name: Build Backend
needs: build-base
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

outputs:
image-ref: ${{ steps.image-ref.outputs.ref }}

steps:
- uses: actions/checkout@v6

- name: Set lowercase image prefix
run: echo "IMAGE_PREFIX=${GITHUB_REPOSITORY_OWNER,,}/integr8scode" >> $GITHUB_ENV

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/backend
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}

- name: Set image reference for scan
id: image-ref
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/backend:pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
else
echo "ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/backend:latest" >> $GITHUB_OUTPUT
fi

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./backend
file: ./backend/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=backend
cache-to: type=gha,mode=max,scope=backend
build-contexts: |
base=docker-image://${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/base:${{ needs.build-base.outputs.image-tag }}

build-frontend:
name: Build Frontend
needs: build-base
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

outputs:
image-ref: ${{ steps.image-ref.outputs.ref }}

security-events: write
packages: read
strategy:
fail-fast: false
matrix:
image:
- base
- backend
- frontend
- coordinator
- k8s-worker
- pod-monitor
- result-processor
- saga-orchestrator
- event-replay
- dlq-processor
- cert-generator
- zookeeper-certgen
steps:
- uses: actions/checkout@v6

- name: Set lowercase image prefix
run: echo "IMAGE_PREFIX=${GITHUB_REPOSITORY_OWNER,,}/integr8scode" >> $GITHUB_ENV

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/frontend
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}

- name: Set image reference for scan
id: image-ref
- name: Compute image ref
id: ref
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/frontend:pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
PREFIX="${GITHUB_REPOSITORY_OWNER,,}/integr8scode"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
SHA="${{ github.event.inputs.sha || github.sha }}"
else
echo "ref=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/frontend:latest" >> $GITHUB_OUTPUT
SHA="${{ github.event.workflow_run.head_sha }}"
fi

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./frontend
file: ./frontend/Dockerfile.prod
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=frontend
cache-to: type=gha,mode=max,scope=frontend

scan-backend:
name: Scan Backend
needs: build-backend
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write

steps:
- uses: actions/checkout@v6
TAG="sha-${SHA::7}"
echo "image=${{ env.REGISTRY }}/$PREFIX/${{ matrix.image }}:$TAG" >> $GITHUB_OUTPUT

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.33.1
with:
image-ref: ${{ needs.build-backend.outputs.image-ref }}
image-ref: ${{ steps.ref.outputs.image }}
format: 'sarif'
output: 'trivy-backend-results.sarif'
output: 'trivy-${{ matrix.image }}-results.sarif'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
timeout: '5m0s'
Expand All @@ -220,56 +75,85 @@ jobs:
if: always()
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: 'trivy-backend-results.sarif'
category: 'trivy-backend'

scan-frontend:
name: Scan Frontend
needs: build-frontend
sarif_file: 'trivy-${{ matrix.image }}-results.sarif'
category: 'trivy-${{ matrix.image }}'

# Promote SHA tag → latest using crane (registry-level manifest copy, no rebuild)
promote:
name: Promote to Latest
needs: [scan]
if: >
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main')
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write

packages: write
steps:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.33.1
- name: Log in to GHCR
uses: docker/login-action@v3
with:
image-ref: ${{ needs.build-frontend.outputs.image-ref }}
format: 'sarif'
output: 'trivy-frontend-results.sarif'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
timeout: '5m0s'
version: 'v0.68.2'
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Upload Trivy scan results
if: always()
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: 'trivy-frontend-results.sarif'
category: 'trivy-frontend'
- name: Install crane
uses: imjasonh/setup-crane@v0.4

- name: Promote images (SHA → latest)
run: |
PREFIX="${GITHUB_REPOSITORY_OWNER,,}/integr8scode"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
SHA="${{ github.event.inputs.sha || github.sha }}"
else
SHA="${{ github.event.workflow_run.head_sha }}"
fi
TAG="sha-${SHA::7}"

echo "Promoting tag: $TAG → latest"
echo ""

crane copy "$REGISTRY/$PREFIX/base:$TAG" "$REGISTRY/$PREFIX/base:latest"
crane copy "$REGISTRY/$PREFIX/backend:$TAG" "$REGISTRY/$PREFIX/backend:latest"
crane copy "$REGISTRY/$PREFIX/frontend:$TAG" "$REGISTRY/$PREFIX/frontend:latest"
crane copy "$REGISTRY/$PREFIX/coordinator:$TAG" "$REGISTRY/$PREFIX/coordinator:latest"
crane copy "$REGISTRY/$PREFIX/k8s-worker:$TAG" "$REGISTRY/$PREFIX/k8s-worker:latest"
crane copy "$REGISTRY/$PREFIX/pod-monitor:$TAG" "$REGISTRY/$PREFIX/pod-monitor:latest"
crane copy "$REGISTRY/$PREFIX/result-processor:$TAG" "$REGISTRY/$PREFIX/result-processor:latest"
crane copy "$REGISTRY/$PREFIX/saga-orchestrator:$TAG" "$REGISTRY/$PREFIX/saga-orchestrator:latest"
crane copy "$REGISTRY/$PREFIX/event-replay:$TAG" "$REGISTRY/$PREFIX/event-replay:latest"
crane copy "$REGISTRY/$PREFIX/dlq-processor:$TAG" "$REGISTRY/$PREFIX/dlq-processor:latest"
crane copy "$REGISTRY/$PREFIX/cert-generator:$TAG" "$REGISTRY/$PREFIX/cert-generator:latest"
crane copy "$REGISTRY/$PREFIX/zookeeper-certgen:$TAG" "$REGISTRY/$PREFIX/zookeeper-certgen:latest"
Comment thread
HardMax71 marked this conversation as resolved.

summary:
name: Summary
if: github.event_name != 'pull_request'
needs: [build-base, build-backend, build-frontend, scan-backend, scan-frontend]
needs: [promote]
runs-on: ubuntu-latest

steps:
- name: Set lowercase image prefix
run: echo "IMAGE_PREFIX=${GITHUB_REPOSITORY_OWNER,,}/integr8scode" >> $GITHUB_ENV

- name: Generate summary
run: |
echo "## Docker Images Published" >> $GITHUB_STEP_SUMMARY
PREFIX="${GITHUB_REPOSITORY_OWNER,,}/integr8scode"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
SHA="${{ github.event.inputs.sha || github.sha }}"
else
SHA="${{ github.event.workflow_run.head_sha }}"
fi
TAG="sha-${SHA::7}"

echo "## Docker Images Promoted to Latest" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
echo "Images promoted manually from \`$TAG\` to \`latest\` — Stack Tests may not have run." >> $GITHUB_STEP_SUMMARY
else
echo "All Stack Tests passed. Images promoted from \`$TAG\` to \`latest\`." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Image | Pull Command |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------------|" >> $GITHUB_STEP_SUMMARY
echo "| Base | \`docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/base:latest\` |" >> $GITHUB_STEP_SUMMARY
echo "| Backend | \`docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/backend:latest\` |" >> $GITHUB_STEP_SUMMARY
echo "| Frontend | \`docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/frontend:latest\` |" >> $GITHUB_STEP_SUMMARY
echo "| Base | \`docker pull $REGISTRY/$PREFIX/base:latest\` |" >> $GITHUB_STEP_SUMMARY
echo "| Backend | \`docker pull $REGISTRY/$PREFIX/backend:latest\` |" >> $GITHUB_STEP_SUMMARY
echo "| Frontend | \`docker pull $REGISTRY/$PREFIX/frontend:latest\` |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Scan Results" >> $GITHUB_STEP_SUMMARY
echo "- Backend scan: ✅ Passed" >> $GITHUB_STEP_SUMMARY
echo "- Frontend scan: ✅ Passed" >> $GITHUB_STEP_SUMMARY
echo "### Security Scans" >> $GITHUB_STEP_SUMMARY
echo "All 12 images scanned with Trivy (CRITICAL + HIGH, unfixed ignored)." >> $GITHUB_STEP_SUMMARY
Loading
Loading