Checkout v6.0.1 was released yesterday (Dec 2, 2025) and is causing CI failures across all workflows. The v6 release requires minimum GitHub Actions Runner v2.329.0 for Docker container scenarios and likely has edge cases causing failures. Downgrading to v4.2.2 (stable release from Oct 2024) to restore CI stability. Can re-evaluate v6 after it matures. Affects 16 checkout action references across 12 workflow files: - quality-checks.yml - waf-integration.yml - docker-publish.yml - codecov-upload.yml - codeql.yml - benchmark.yml - docs.yml - release-goreleaser.yml - auto-versioning.yml - docker-lint.yml - auto-changelog.yml - renovate.yml
277 lines
11 KiB
YAML
277 lines
11 KiB
YAML
name: Docker Build, Publish & Test
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
- development
|
|
- feature/beta-release
|
|
# Note: Tags are handled by release-goreleaser.yml to avoid duplicate builds
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
- development
|
|
- feature/beta-release
|
|
workflow_dispatch:
|
|
workflow_call:
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
IMAGE_NAME: ${{ github.repository_owner }}/charon
|
|
|
|
jobs:
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
security-events: write
|
|
|
|
outputs:
|
|
skip_build: ${{ steps.skip.outputs.skip_build }}
|
|
digest: ${{ steps.build-and-push.outputs.digest }}
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
|
|
- name: Normalize image name
|
|
run: |
|
|
echo "IMAGE_NAME=$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
|
|
|
- name: Determine skip condition
|
|
id: skip
|
|
env:
|
|
ACTOR: ${{ github.actor }}
|
|
EVENT: ${{ github.event_name }}
|
|
HEAD_MSG: ${{ github.event.head_commit.message }}
|
|
REF: ${{ github.ref }}
|
|
run: |
|
|
should_skip=false
|
|
pr_title=""
|
|
if [ "$EVENT" = "pull_request" ]; then
|
|
pr_title=$(jq -r '.pull_request.title' "$GITHUB_EVENT_PATH" 2>/dev/null || echo '')
|
|
fi
|
|
if [ "$ACTOR" = "renovate[bot]" ]; then should_skip=true; fi
|
|
if echo "$HEAD_MSG" | grep -Ei '^chore\(deps' >/dev/null 2>&1; then should_skip=true; fi
|
|
if echo "$HEAD_MSG" | grep -Ei '^chore:' >/dev/null 2>&1; then should_skip=true; fi
|
|
if echo "$pr_title" | grep -Ei '^chore\(deps' >/dev/null 2>&1; then should_skip=true; fi
|
|
if echo "$pr_title" | grep -Ei '^chore:' >/dev/null 2>&1; then should_skip=true; fi
|
|
|
|
# Always build on beta-release branch to ensure artifacts for testing
|
|
if [[ "$REF" == "refs/heads/feature/beta-release" ]]; then
|
|
should_skip=false
|
|
echo "Force building on beta-release branch"
|
|
fi
|
|
|
|
echo "skip_build=$should_skip" >> $GITHUB_OUTPUT
|
|
|
|
- name: Set up QEMU
|
|
if: steps.skip.outputs.skip_build != 'true'
|
|
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
|
|
|
- name: Set up Docker Buildx
|
|
if: steps.skip.outputs.skip_build != 'true'
|
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
|
|
|
- name: Resolve Caddy base digest
|
|
if: steps.skip.outputs.skip_build != 'true'
|
|
id: caddy
|
|
run: |
|
|
docker pull caddy:2-alpine
|
|
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' caddy:2-alpine)
|
|
echo "image=$DIGEST" >> $GITHUB_OUTPUT
|
|
|
|
- name: Log in to Container Registry
|
|
if: github.event_name != 'pull_request' && steps.skip.outputs.skip_build != 'true'
|
|
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Extract metadata (tags, labels)
|
|
if: steps.skip.outputs.skip_build != 'true'
|
|
id: meta
|
|
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
|
with:
|
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
tags: |
|
|
type=raw,value=latest,enable={{is_default_branch}}
|
|
type=raw,value=dev,enable=${{ github.ref == 'refs/heads/development' }}
|
|
type=raw,value=beta,enable=${{ github.ref == 'refs/heads/feature/beta-release' }}
|
|
type=raw,value=pr-${{ github.ref_name }},enable=${{ github.event_name == 'pull_request' }}
|
|
type=sha,format=short,enable=${{ github.event_name != 'pull_request' }}
|
|
|
|
- name: Build and push Docker image
|
|
if: steps.skip.outputs.skip_build != 'true'
|
|
id: build-and-push
|
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
|
|
with:
|
|
context: .
|
|
platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
|
|
push: ${{ github.event_name != 'pull_request' }}
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
build-args: |
|
|
VERSION=${{ steps.meta.outputs.version }}
|
|
BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
|
|
VCS_REF=${{ github.sha }}
|
|
CADDY_IMAGE=${{ steps.caddy.outputs.image }}
|
|
|
|
- name: Run Trivy scan (table output)
|
|
if: github.event_name != 'pull_request' && steps.skip.outputs.skip_build != 'true'
|
|
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
|
|
with:
|
|
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}
|
|
format: 'table'
|
|
severity: 'CRITICAL,HIGH'
|
|
exit-code: '0'
|
|
continue-on-error: true
|
|
|
|
- name: Run Trivy vulnerability scanner (SARIF)
|
|
if: github.event_name != 'pull_request' && steps.skip.outputs.skip_build != 'true'
|
|
id: trivy
|
|
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
|
|
with:
|
|
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}
|
|
format: 'sarif'
|
|
output: 'trivy-results.sarif'
|
|
severity: 'CRITICAL,HIGH'
|
|
continue-on-error: true
|
|
|
|
- name: Check Trivy SARIF exists
|
|
if: github.event_name != 'pull_request' && steps.skip.outputs.skip_build != 'true'
|
|
id: trivy-check
|
|
run: |
|
|
if [ -f trivy-results.sarif ]; then
|
|
echo "exists=true" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "exists=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Upload Trivy results
|
|
if: github.event_name != 'pull_request' && steps.skip.outputs.skip_build != 'true' && steps.trivy-check.outputs.exists == 'true'
|
|
uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6
|
|
with:
|
|
sarif_file: 'trivy-results.sarif'
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Create summary
|
|
if: steps.skip.outputs.skip_build != 'true'
|
|
run: |
|
|
echo "## 🎉 Docker Image Built Successfully!" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "### 📦 Image Details" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Registry**: GitHub Container Registry (ghcr.io)" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Repository**: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Tags**: " >> $GITHUB_STEP_SUMMARY
|
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
|
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
|
|
test-image:
|
|
name: Test Docker Image
|
|
needs: build-and-push
|
|
runs-on: ubuntu-latest
|
|
if: needs.build-and-push.outputs.skip_build != 'true' && github.event_name != 'pull_request'
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
|
|
- name: Normalize image name
|
|
run: |
|
|
raw="${{ github.repository_owner }}/${{ github.event.repository.name }}"
|
|
echo "IMAGE_NAME=$(echo "$raw" | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
|
|
|
- name: Determine image tag
|
|
id: tag
|
|
run: |
|
|
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
|
echo "tag=latest" >> $GITHUB_OUTPUT
|
|
elif [[ "${{ github.ref }}" == "refs/heads/development" ]]; then
|
|
echo "tag=dev" >> $GITHUB_OUTPUT
|
|
elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
|
echo "tag=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "tag=sha-$(echo ${{ github.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Pull Docker image
|
|
run: docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }}
|
|
|
|
- name: Create Docker Network
|
|
run: docker network create charon-test-net
|
|
|
|
- name: Run Upstream Service (whoami)
|
|
run: |
|
|
docker run -d \
|
|
--name whoami \
|
|
--network charon-test-net \
|
|
traefik/whoami
|
|
|
|
- name: Run Charon Container
|
|
run: |
|
|
docker run -d \
|
|
--name test-container \
|
|
--network charon-test-net \
|
|
-p 8080:8080 \
|
|
-p 80:80 \
|
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }}
|
|
|
|
- name: Run Integration Test
|
|
run: ./scripts/integration-test.sh
|
|
|
|
- name: Check container logs
|
|
if: always()
|
|
run: docker logs test-container
|
|
|
|
- name: Stop container
|
|
if: always()
|
|
run: |
|
|
docker stop test-container whoami || true
|
|
docker rm test-container whoami || true
|
|
docker network rm charon-test-net || true
|
|
|
|
- name: Create test summary
|
|
if: always()
|
|
run: |
|
|
echo "## 🧪 Docker Image Test Results" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Image**: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Integration Test**: ${{ job.status == 'success' && '✅ Passed' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY
|
|
|
|
trivy-pr-app-only:
|
|
name: Trivy (PR) - App-only
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'pull_request'
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
|
|
- name: Build image locally for PR
|
|
run: |
|
|
docker build -t charon:pr-${{ github.sha }} .
|
|
|
|
- name: Extract `charon` binary from image
|
|
run: |
|
|
CONTAINER=$(docker create charon:pr-${{ github.sha }})
|
|
docker cp ${CONTAINER}:/app/charon ./charon_binary || true
|
|
docker rm ${CONTAINER} || true
|
|
|
|
- name: Run Trivy filesystem scan on `charon` (fail PR on HIGH/CRITICAL)
|
|
run: |
|
|
docker run --rm -v $HOME/.cache/trivy:/root/.cache/trivy -v $PWD:/workdir aquasec/trivy:latest fs --exit-code 1 --severity CRITICAL,HIGH /workdir/charon_binary
|
|
shell: bash
|