|
| 1 | +# build-docker-image.yml |
| 2 | +# Reusable workflow to build a Docker image and upload it as an artifact. |
| 3 | +# Supports three build strategies: |
| 4 | +# 1. build-docker.sh script (e.g., dsm-erchef) |
| 5 | +# 2. Makefile with compose-build target |
| 6 | +# 3. Standard docker build (fallback) |
| 7 | +# |
| 8 | +# The built image is saved as a tar and uploaded as a GitHub Actions artifact |
| 9 | +# for downstream jobs (e.g., Wiz CLI scan, Grype scan) to consume. |
| 10 | + |
| 11 | +name: Build Docker image |
| 12 | + |
| 13 | +on: |
| 14 | + workflow_call: |
| 15 | + inputs: |
| 16 | + skip-aws: |
| 17 | + description: 'Skip AWS ECR login (for repos that do not need ECR base images)' |
| 18 | + required: false |
| 19 | + type: boolean |
| 20 | + default: false |
| 21 | + outputs: |
| 22 | + image-names: |
| 23 | + description: 'Space-separated list of built Docker image names (repository:tag)' |
| 24 | + value: ${{ jobs.build.outputs.image-names }} |
| 25 | + |
| 26 | +jobs: |
| 27 | + build: |
| 28 | + name: Build and upload Docker image |
| 29 | + runs-on: ubuntu-latest |
| 30 | + permissions: |
| 31 | + id-token: write |
| 32 | + contents: read |
| 33 | + outputs: |
| 34 | + image-names: ${{ steps.build-image.outputs.IMAGES }} |
| 35 | + steps: |
| 36 | + - name: Checkout repository |
| 37 | + uses: actions/checkout@v6 |
| 38 | + with: |
| 39 | + fetch-depth: 0 |
| 40 | + |
| 41 | + - name: Configure git for private repos |
| 42 | + run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" |
| 43 | + |
| 44 | + - name: Configure AWS credentials |
| 45 | + uses: aws-actions/configure-aws-credentials@v4 |
| 46 | + if: ${{ !inputs.skip-aws }} |
| 47 | + with: |
| 48 | + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} |
| 49 | + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} |
| 50 | + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} |
| 51 | + aws-region: us-east-2 |
| 52 | + |
| 53 | + - name: Login to Amazon ECR |
| 54 | + id: login-ecr |
| 55 | + if: ${{ !inputs.skip-aws }} |
| 56 | + uses: aws-actions/amazon-ecr-login@v2 |
| 57 | + |
| 58 | + - name: Build Docker image |
| 59 | + id: build-image |
| 60 | + env: |
| 61 | + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} |
| 62 | + run: | |
| 63 | + if [ ! -f "Dockerfile" ]; then |
| 64 | + echo "❌ No Dockerfile found - cannot build" |
| 65 | + exit 1 |
| 66 | + fi |
| 67 | +
|
| 68 | + echo "Building Docker image..." |
| 69 | + REPO_NAME=$(basename $(pwd)) |
| 70 | +
|
| 71 | + # Strategy 1: Check for build-docker.sh script (e.g., dsm-erchef) |
| 72 | + if [ -f "build-docker.sh" ]; then |
| 73 | + echo "Found build-docker.sh script - using it to build images" |
| 74 | + chmod +x build-docker.sh |
| 75 | + GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" ./build-docker.sh |
| 76 | +
|
| 77 | + # Detect all images built (typically repo name or repo-name-init) |
| 78 | + IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "^<none>") |
| 79 | +
|
| 80 | + if [ -z "$IMAGES" ]; then |
| 81 | + echo "⚠️ No images found with prefix ${REPO_NAME} after build-docker.sh" |
| 82 | + echo "Checking for any recently built images..." |
| 83 | + IMAGES=$(docker images --format "{{.CreatedAt}}\t{{.Repository}}:{{.Tag}}" | sort -r | head -5 | cut -f2 | grep -v "^<none>") |
| 84 | + fi |
| 85 | + # Strategy 2: Check for Makefile with compose-build target (e.g., chef-platform-user-accounts-service) |
| 86 | + elif [ -f "Makefile" ] && grep -q "^compose-build:" Makefile; then |
| 87 | + echo "Using Makefile compose-build target with GITHUB_TOKEN" |
| 88 | + export GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" |
| 89 | + make compose-build |
| 90 | +
|
| 91 | + echo "Detecting built images..." |
| 92 | + # Get all image names from compose, then keep only ones that exist locally (i.e., were actually built) |
| 93 | + IMAGES="" |
| 94 | + for img in $(docker compose config --images 2>/dev/null | sort -u); do |
| 95 | + TAG_IMG=$(echo "$img" | grep -q ':' && echo "$img" || echo "${img}:latest") |
| 96 | + if docker image inspect "$TAG_IMG" &>/dev/null; then |
| 97 | + IMAGES="${IMAGES}${TAG_IMG} " |
| 98 | + fi |
| 99 | + done |
| 100 | + IMAGES=$(echo "$IMAGES" | xargs) |
| 101 | +
|
| 102 | + if [ -z "$IMAGES" ]; then |
| 103 | + echo "⚠️ Could not detect built images from compose config, falling back to repo name match" |
| 104 | + IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${REPO_NAME}" | grep -v "^<none>") |
| 105 | + fi |
| 106 | + # Strategy 3: Fallback to standard docker build |
| 107 | + else |
| 108 | + echo "Using standard docker build with GITHUB_TOKEN build arg" |
| 109 | + docker build --build-arg GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" -t "${REPO_NAME}:latest" . |
| 110 | + IMAGES="${REPO_NAME}:latest" |
| 111 | + fi |
| 112 | +
|
| 113 | + if [ -z "$IMAGES" ]; then |
| 114 | + echo "❌ No Docker images found after build" |
| 115 | + exit 1 |
| 116 | + fi |
| 117 | +
|
| 118 | + echo "Found images:" |
| 119 | + echo "$IMAGES" |
| 120 | +
|
| 121 | + # Output as space-separated list for downstream jobs |
| 122 | + echo "IMAGES=$(echo $IMAGES | tr '\n' ' ')" >> "$GITHUB_OUTPUT" |
| 123 | +
|
| 124 | + - name: Save Docker images to tar |
| 125 | + run: | |
| 126 | + IMAGES="${{ steps.build-image.outputs.IMAGES }}" |
| 127 | + echo "Saving images to /tmp/docker-image.tar: $IMAGES" |
| 128 | + docker save $IMAGES -o /tmp/docker-image.tar |
| 129 | + ls -lh /tmp/docker-image.tar |
| 130 | +
|
| 131 | + - name: Upload Docker image artifact |
| 132 | + uses: actions/upload-artifact@v4 |
| 133 | + with: |
| 134 | + name: docker-image-for-scans |
| 135 | + path: /tmp/docker-image.tar |
| 136 | + retention-days: 1 |
0 commit comments