name: Docker Build, Scan & Publish on: workflow_call: inputs: images: description: > JSON array of images to build. Each item: { name, context, dockerfile, target, cache_ref } required: true type: string registry_host: required: true type: string build_args: required: false type: string default: "" trivy_severity: required: false type: string default: "CRITICAL" secrets: registry_user: required: true registry_password: required: true ci_token: required: true ssh_private_key: required: false ssh_known_hosts: required: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Start ssh-agent if: ${{ secrets.ssh_private_key != '' }} uses: webfactory/ssh-agent@v0.9.0 with: ssh-private-key: ${{ secrets.ssh_private_key }} - name: Add SSH known hosts if: ${{ secrets.ssh_known_hosts != '' }} run: | mkdir -p ~/.ssh printf '%s\n' "${{ secrets.ssh_known_hosts }}" >> ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Install Trivy run: | curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin trivy --version - name: Login to registry uses: docker/login-action@v3 with: registry: ${{ inputs.registry_host }} username: ${{ secrets.registry_user }} password: ${{ secrets.registry_password }} - name: Build, scan and push images env: IMAGES: ${{ inputs.images }} BUILD_ARGS: ${{ inputs.build_args }} CI_TOKEN: ${{ secrets.ci_token }} TRIVY_SEVERITY: ${{ inputs.trivy_severity }} run: | set -euo pipefail SSH_FLAGS="" if [ -n "${SSH_AUTH_SOCK:-}" ]; then SSH_FLAGS="--ssh default" fi echo "$IMAGES" | jq -c '.[]' | while read -r img; do IMAGE_NAME=$(echo "$img" | jq -r '.name') FULL_IMAGE="${{ inputs.registry_host }}/${IMAGE_NAME}" CACHE_REF="${{ inputs.registry_host }}/$(echo "$img" | jq -r '.cache_ref')" CONTEXT=$(echo "$img" | jq -r '.context') DOCKERFILE=$(echo "$img" | jq -r '.dockerfile') TARGET=$(echo "$img" | jq -r '.target') RAW_REF="${{ github.ref }}" SHA_FULL="${{ github.sha }}" SHA_SHORT="${SHA_FULL:0:12}" echo "==== Building $FULL_IMAGE ====" TAGS=() TAGS+=("$FULL_IMAGE:sha-$SHA_SHORT") if [[ "$RAW_REF" =~ ^refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then TAGS+=("$FULL_IMAGE:v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}") TAGS+=("$FULL_IMAGE:v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}") TAGS+=("$FULL_IMAGE:v${BASH_REMATCH[1]}") TAGS+=("$FULL_IMAGE:latest") fi TAG_ARGS=$(printf -- "--tag %s " "${TAGS[@]}") BUILD_ARG_FLAGS="" if [ -n "$BUILD_ARGS" ]; then BUILD_ARG_FLAGS=$(printf -- "--build-arg %s " $BUILD_ARGS) fi TARGET_FLAG="" if [ -n "$TARGET" ] && [ "$TARGET" != "null" ]; then TARGET_FLAG="--target $TARGET" fi docker buildx build \ --file "$DOCKERFILE" \ $TARGET_FLAG \ --cache-from "type=registry,ref=$CACHE_REF" \ --cache-to "type=registry,ref=$CACHE_REF,mode=max" \ $SSH_FLAGS \ --load \ $TAG_ARGS \ $BUILD_ARG_FLAGS \ "$CONTEXT" echo "==== Trivy scan for $FULL_IMAGE ====" trivy image \ --severity "$TRIVY_SEVERITY" \ --exit-code 1 \ "${TAGS[0]}" echo "==== Pushing $FULL_IMAGE ====" for tag in "${TAGS[@]}"; do docker push "$tag" done done