Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
136 changes: 136 additions & 0 deletions .github/workflows/build-docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# build-docker-image.yml
# Reusable workflow to build a Docker image and upload it as an artifact.
# Supports three build strategies:
# 1. build-docker.sh script (e.g., dsm-erchef)
# 2. Makefile with compose-build target
# 3. Standard docker build (fallback)
#
# The built image is saved as a tar and uploaded as a GitHub Actions artifact
# for downstream jobs (e.g., Wiz CLI scan, Grype scan) to consume.

name: Build Docker image

on:
workflow_call:
inputs:
skip-aws:
description: 'Skip AWS ECR login (for repos that do not need ECR base images)'
required: false
type: boolean
default: false
outputs:
image-names:
description: 'Space-separated list of built Docker image names (repository:tag)'
value: ${{ jobs.build.outputs.image-names }}

jobs:
build:
name: Build and upload Docker image
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
outputs:
image-names: ${{ steps.build-image.outputs.IMAGES }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Configure git for private repos
run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/"

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
if: ${{ !inputs.skip-aws }}
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
aws-region: us-east-2

- name: Login to Amazon ECR
id: login-ecr
if: ${{ !inputs.skip-aws }}
uses: aws-actions/amazon-ecr-login@v2

- name: Build Docker image
id: build-image
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
if [ ! -f "Dockerfile" ]; then
echo "❌ No Dockerfile found - cannot build"
exit 1
fi

echo "Building Docker image..."
REPO_NAME=$(basename $(pwd))

# Strategy 1: Check for build-docker.sh script (e.g., dsm-erchef)
if [ -f "build-docker.sh" ]; then
echo "Found build-docker.sh script - using it to build images"
chmod +x build-docker.sh
GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" ./build-docker.sh

# Detect all images built (typically repo name or repo-name-init)
IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "^<none>")

if [ -z "$IMAGES" ]; then
echo "⚠️ No images found with prefix ${REPO_NAME} after build-docker.sh"
echo "Checking for any recently built images..."
IMAGES=$(docker images --format "{{.CreatedAt}}\t{{.Repository}}:{{.Tag}}" | sort -r | head -5 | cut -f2 | grep -v "^<none>")
fi
# Strategy 2: Check for Makefile with compose-build target (e.g., chef-platform-user-accounts-service)
elif [ -f "Makefile" ] && grep -q "^compose-build:" Makefile; then
echo "Using Makefile compose-build target with GITHUB_TOKEN"
export GITHUB_TOKEN="${{ secrets.GH_TOKEN }}"
make compose-build

echo "Detecting built images..."
# Get all image names from compose, then keep only ones that exist locally (i.e., were actually built)
IMAGES=""
for img in $(docker compose config --images 2>/dev/null | sort -u); do
TAG_IMG=$(echo "$img" | grep -q ':' && echo "$img" || echo "${img}:latest")
if docker image inspect "$TAG_IMG" &>/dev/null; then
IMAGES="${IMAGES}${TAG_IMG} "
fi
done
IMAGES=$(echo "$IMAGES" | xargs)

if [ -z "$IMAGES" ]; then
echo "⚠️ Could not detect built images from compose config, falling back to repo name match"
IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${REPO_NAME}" | grep -v "^<none>")
fi
# Strategy 3: Fallback to standard docker build
else
echo "Using standard docker build with GITHUB_TOKEN build arg"
docker build --build-arg GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" -t "${REPO_NAME}:latest" .
IMAGES="${REPO_NAME}:latest"
fi

if [ -z "$IMAGES" ]; then
echo "❌ No Docker images found after build"
exit 1
fi

echo "Found images:"
echo "$IMAGES"

# Output as space-separated list for downstream jobs
echo "IMAGES=$(echo $IMAGES | tr '\n' ' ')" >> "$GITHUB_OUTPUT"

- name: Save Docker images to tar
run: |
IMAGES="${{ steps.build-image.outputs.IMAGES }}"
echo "Saving images to /tmp/docker-image.tar: $IMAGES"
docker save $IMAGES -o /tmp/docker-image.tar
ls -lh /tmp/docker-image.tar

- name: Upload Docker image artifact
uses: actions/upload-artifact@v4
with:
name: docker-image-for-scans
path: /tmp/docker-image.tar
retention-days: 1
55 changes: 50 additions & 5 deletions .github/workflows/ci-main-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ on:
required: false
type: boolean
default: false
grype-image-skip-aws:
description: 'Skip Grype image scan on AWS ECR images to avoid rate limits (assumes these images are scanned with Amazon ECR scan or Trivy)'
image-skip-aws:
description: 'Skip AWS ECR login for Docker image scans (for repos that do not need ECR base images)'
required: false
type: boolean
default: false
Expand Down Expand Up @@ -541,6 +541,26 @@ on:
# required: false
# default: 'https://polaris.blackduck.com'
# type: string
perform-wiz-scan:
description: 'Perform Wiz CLI security scan on Docker image'
required: false
type: boolean
default: false
wiz-fail-build:
description: 'Fail the build on Wiz policy violations'
required: false
type: boolean
default: true
wiz-fail-on-critical:
description: 'Fail the pipeline if Wiz finds CRITICAL vulnerabilities'
required: false
type: boolean
default: false
wiz-fail-on-high:
description: 'Fail the pipeline if Wiz finds HIGH vulnerabilities'
required: false
type: boolean
default: false

env:
PRIMARY_APPLICATION: ${{ inputs.application }} # was 'default' # Custom repo property [primaryApplication]: chef360, automate, infra-server, habitat, supermarket, licensing, downloads, chef-client, inspec, chef-workstation (or derivatives like habitat-builder)
Expand Down Expand Up @@ -980,12 +1000,37 @@ jobs:
name: 'Grype Docker image scan'
if: ${{ inputs.perform-grype-image-scan }}
uses: chef/common-github-actions/.github/workflows/grype.yml@main
needs: checkout
needs: [checkout, build-docker-image]
secrets: inherit
with:
fail-grype-on-high: ${{ inputs.grype-image-fail-on-high }}
fail-grype-on-critical: ${{ inputs.grype-image-fail-on-critical }}
grype-image-skip-aws: ${{ inputs.grype-image-skip-aws }}
grype-image-skip-aws: ${{ inputs.image-skip-aws }}
prebuilt-image-artifact: docker-image-for-scans
prebuilt-image-names: ${{ needs.build-docker-image.outputs.image-names }}

build-docker-image:
name: 'Build Docker image for security scans'
if: ${{ inputs.perform-grype-image-scan == true || inputs.perform-wiz-scan == true }}
uses: chef/common-github-actions/.github/workflows/build-docker-image.yml@main
needs: checkout
secrets: inherit
with:
skip-aws: ${{ inputs.image-skip-aws }}

run-wiz-scan:
name: 'Wiz CLI security scan'
if: ${{ inputs.perform-wiz-scan == true }}
uses: chef/common-github-actions/.github/workflows/wiz.yml@main
needs: [checkout, build-docker-image]
with:
fail-build: ${{ inputs.wiz-fail-build }}
fail-on-critical: ${{ inputs.wiz-fail-on-critical }}
fail-on-high: ${{ inputs.wiz-fail-on-high }}
wiz-image-skip-aws: ${{ inputs.image-skip-aws }}
prebuilt-image-artifact: docker-image-for-scans
prebuilt-image-names: ${{ needs.build-docker-image.outputs.image-names }}
secrets: inherit

run-grype-hab-package-scan:
name: 'Grype scan Habitat packages from bldr.habitat.sh'
Expand Down Expand Up @@ -1128,7 +1173,7 @@ jobs:
# VER=$(cat VERSION)
# echo "VERSION=$VER" >> $GITHUB_ENV
# then ${{ env.VERSION }}

set-application-version:
runs-on: ubuntu-latest
name: 'Detect SBOM version for application'
Expand Down
72 changes: 63 additions & 9 deletions .github/workflows/grype.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ on:
required: false
type: boolean
default: false
prebuilt-image-artifact:
description: 'Name of uploaded artifact containing a Docker image tar (skip Docker build if provided)'
required: false
type: string
default: ''
prebuilt-image-names:
description: 'Space-separated list of Docker image:tag names inside the prebuilt artifact tar'
required: false
type: string
default: ''

jobs:
grype-scan:
Expand Down Expand Up @@ -65,13 +75,29 @@ jobs:
if: ${{ !inputs.grype-image-skip-aws }}
uses: aws-actions/amazon-ecr-login@v2

- name: Scan with Grype
id: grype-scan
- name: Download prebuilt Docker image
if: ${{ inputs.prebuilt-image-artifact != '' }}
uses: actions/download-artifact@v4
with:
name: ${{ inputs.prebuilt-image-artifact }}
path: /tmp

- name: Load prebuilt Docker image
id: load-image
if: ${{ inputs.prebuilt-image-artifact != '' }}
run: |
echo "Loading prebuilt images from artifact..."
docker load -i /tmp/docker-image.tar
echo "IMAGES=${{ inputs.prebuilt-image-names }}" >> "$GITHUB_OUTPUT"
echo "Loaded images: ${{ inputs.prebuilt-image-names }}"
docker images

- name: Build Docker image
id: build-image
if: ${{ inputs.prebuilt-image-artifact == '' }}
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
SCAN_NAME="${{ github.repository }}"

if [ ! -f "Dockerfile" ]; then
echo "❌ No Dockerfile found - this workflow requires a Dockerfile to scan Docker image"
exit 1
Expand Down Expand Up @@ -101,13 +127,19 @@ jobs:
make compose-build

echo "Detecting built images..."
docker compose images

IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${REPO_NAME}" | grep -v "^<none>")
# Get all image names from compose, then keep only ones that exist locally (i.e., were actually built)
IMAGES=""
for img in $(docker compose config --images 2>/dev/null | sort -u); do
TAG_IMG=$(echo "$img" | grep -q ':' && echo "$img" || echo "${img}:latest")
if docker image inspect "$TAG_IMG" &>/dev/null; then
IMAGES="${IMAGES}${TAG_IMG} "
fi
done
IMAGES=$(echo "$IMAGES" | xargs)

if [ -z "$IMAGES" ]; then
echo "No images found with prefix ${REPO_NAME}, scanning all recent images"
IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "^<none>" | head -5)
echo "⚠️ Could not detect built images from compose config, falling back to repo name match"
IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${REPO_NAME}" | grep -v "^<none>")
fi
# Strategy 3: Fallback to standard docker build
else
Expand All @@ -121,6 +153,28 @@ jobs:
exit 1
fi

echo "IMAGES=$(echo $IMAGES | tr '\n' ' ')" >> "$GITHUB_OUTPUT"
echo "Found images: $IMAGES"

- name: Determine scan targets
id: scan-target
run: |
IMAGES="${{ steps.load-image.outputs.IMAGES || steps.build-image.outputs.IMAGES }}"
if [ -z "$IMAGES" ]; then
echo "❌ No images available to scan"
exit 1
fi
echo "IMAGES=$IMAGES" >> "$GITHUB_OUTPUT"
echo "Scan targets: $IMAGES"

- name: Scan with Grype
id: grype-scan
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
SCAN_NAME="${{ github.repository }}"
IMAGES="${{ steps.scan-target.outputs.IMAGES }}"

echo "Found images to scan:"
echo "$IMAGES"

Expand Down
Loading