actions-container-scan-matrix.yml
# Container Security Scan - Parallel Matrix Example
#
# This workflow demonstrates parallel container scanning using matrixed jobs.
# Each container+scanner combination runs as a separate job, providing:
# - Maximum parallelism and faster scan times
# - Per-container, per-scanner configuration from config file
# - Unified summary with deduplicated vulnerabilities
#
# GHES Compatible: Uses only composite actions (no reusable workflows)
#
# Usage:
# 1. Copy this file to .github/workflows/container-scan.yml in your repo
# 2. Create a container-config.yml file (see example below)
# 3. Configure secrets as needed for private registries
name: Container Security Scan (Parallel)
on:
push:
branches: [main]
paths:
- 'container-config.yml'
- '**/Dockerfile*'
pull_request:
branches: [main]
paths:
- 'container-config.yml'
- '**/Dockerfile*'
workflow_dispatch:
schedule:
# Scan weekly on Mondays at 6am UTC
- cron: '0 6 * * 1'
permissions:
contents: read
pull-requests: write
security-events: write
actions: read
checks: write
id-token: write
packages: read
jobs:
# ============================================
# Parse config and generate scan matrix
# ============================================
setup:
name: Parse Config
runs-on: ubuntu-latest
outputs:
scan_matrix: ${{ steps.parse.outputs.scan_matrix }}
has_containers: ${{ steps.parse.outputs.has_containers }}
container_count: ${{ steps.parse.outputs.container_count }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Parse container config
id: parse
uses: huntridge-labs/argus/.github/actions/parse-container-config@1.1.0
with:
config_file: container-config.yml
- name: Display scan matrix
run: |
echo "📦 Containers to scan: ${{ steps.parse.outputs.container_count }}"
echo "🔍 Scan matrix:"
echo '${{ steps.parse.outputs.scan_matrix }}' | jq '.'
# ============================================
# Parallel container scanning
# Each container+scanner combo runs as separate job
# ============================================
scan:
name: Scan ${{ matrix.name }} (${{ matrix.scanner }})
needs: setup
if: needs.setup.outputs.has_containers == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.scan_matrix) }}
steps:
- name: Run ${{ matrix.scanner }} scanner
uses: huntridge-labs/argus/.github/actions/scanner-container@1.1.0
with:
image_ref: ${{ matrix.image }}
container_name: ${{ matrix.name }}
scanners: ${{ matrix.scanner }}
fail_on_severity: ${{ matrix.fail_on_severity }}
enable_code_security: ${{ matrix.enable_code_security }}
registry_username: ${{ matrix.registry_username }}
registry_password: ${{ secrets[matrix.registry_auth_secret] }}
# ============================================
# Combine results and generate summary
# ============================================
summary:
name: Scan Summary
needs: [setup, scan]
if: always() && needs.setup.outputs.has_containers == 'true'
runs-on: ubuntu-latest
steps:
- name: Generate combined summary
uses: huntridge-labs/argus/.github/actions/scanner-container-summary@1.1.0
with:
post_pr_comment: 'true'
# ============================================
# Example container-config.yml
# ============================================
# Place this file in your repository root:
#
# containers:
# - name: my-app
# image: ghcr.io/myorg/my-app:latest
# scanners:
# - trivy
# - grype
# - syft
# fail_on_severity: high
# registry:
# username: ${{ github.actor }}
# auth_secret: GITHUB_TOKEN
#
# - name: database
# image:
# repository: myorg
# name: postgres-custom
# tag: "15"
# registry:
# host: ghcr.io
# username: ${{ github.actor }}
# auth_secret: GITHUB_TOKEN
# scanners:
# - trivy
# fail_on_severity: critical
# allow_failure: true
#
# - name: third-party
# image: nginx:alpine
# scanners:
# - trivy
# - grype
# fail_on_severity: none # Don't fail, just report