name: Docker
on:
push:
branches: [main]
tags:
- "v*"
pull_request:
branches: [main]
paths:
- ".github/workflows/docker.yml"
- "Dockerfile"
- ".dockerignore"
- "Cargo.toml"
- "Cargo.lock"
- "src/**"
- "crates/**"
schedule:
- cron: "0 4 * * 1"
workflow_dispatch:
permissions:
contents: read
packages: read
env:
REGISTRY: ghcr.io
DOCKERHUB_REGISTRY: docker.io
DOCKERHUB_NAMESPACE: ${{ vars.RUST_TEMPLATE_DOCKERHUB_NAMESPACE || 'threatflux' }}
PUBLISH_DOCKERHUB: ${{ vars.RUST_TEMPLATE_PUBLISH_DOCKERHUB || 'false' }}
DOCKER_BUILDKIT: 1
BINARY_NAME: ${{ vars.RUST_TEMPLATE_BINARY_NAME || 'github-actions-maintainer' }}
BINARY_PACKAGE: ${{ vars.RUST_TEMPLATE_BINARY_PACKAGE || '' }}
SBOM_MANIFEST_PATH: ${{ vars.RUST_TEMPLATE_SBOM_MANIFEST_PATH || 'Cargo.toml' }}
jobs:
build:
name: Build Docker Image
runs-on: ${{ vars.RUST_TEMPLATE_RUNNER_UBUNTU || 'ubuntu-latest' }}
permissions:
contents: read
packages: write
security-events: write
outputs:
digest: ${{ steps.build.outputs.digest }}
image_name: ${{ steps.lowercase.outputs.image_name }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with:
persist-credentials: false
- name: Lowercase image name
id: lowercase
run: echo "image_name=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
- name: Lowercase repo name
id: dockerhub_repo
run: |
REPO_NAME="${GITHUB_REPOSITORY#*/}"
echo "name=${REPO_NAME,,}" >> "$GITHUB_OUTPUT"
- name: Detect Rust toolchain version
id: rust_toolchain
run: |
TOOLCHAIN_VERSION="$(awk -F '"' '/^channel[[:space:]]*=/ { print $2; exit }' rust-toolchain.toml)"
if [[ -z "${TOOLCHAIN_VERSION}" ]]; then
echo "Unable to detect Rust toolchain version from rust-toolchain.toml" >&2
exit 1
fi
echo "version=${TOOLCHAIN_VERSION}" >> "$GITHUB_OUTPUT"
- name: Compose image list
id: images
run: |
GHCR_IMAGE="${REGISTRY}/${{ steps.lowercase.outputs.image_name }}"
DOCKERHUB_IMAGE="${DOCKERHUB_REGISTRY}/${DOCKERHUB_NAMESPACE}/${{ steps.dockerhub_repo.outputs.name }}"
{
echo "images<<EOF"
echo "${GHCR_IMAGE}"
if [[ "${PUBLISH_DOCKERHUB}" == "true" ]]; then
echo "${DOCKERHUB_IMAGE}"
fi
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Select build platforms
id: platforms
run: |
echo "value=linux/amd64,linux/arm64" >> "$GITHUB_OUTPUT"
echo "needs_qemu=true" >> "$GITHUB_OUTPUT"
- name: Set up QEMU
if: steps.platforms.outputs.needs_qemu == 'true'
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to Docker Hub
if: github.event_name != 'pull_request' && env.PUBLISH_DOCKERHUB == 'true'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f with:
registry: ${{ env.DOCKERHUB_REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract runtime metadata
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 with:
images: ${{ steps.images.outputs.images }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=
type=raw,value=latest,enable={{is_default_branch}}
- name: Extract base image metadata
id: base_meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 with:
images: ${{ steps.images.outputs.images }}
tags: |
type=raw,value=base-rust-${{ steps.rust_toolchain.outputs.version }}
type=raw,value=base-rust-latest,enable={{is_default_branch}}
- name: Build base image
if: github.event_name != 'pull_request'
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a with:
context: .
target: rust-base
platforms: ${{ steps.platforms.outputs.value }}
push: true
tags: ${{ steps.base_meta.outputs.tags }}
labels: ${{ steps.base_meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
BINARY_NAME=${{ env.BINARY_NAME }}
BINARY_PACKAGE=${{ env.BINARY_PACKAGE }}
SBOM_MANIFEST_PATH=${{ env.SBOM_MANIFEST_PATH }}
- name: Build and export (PRs)
if: github.event_name == 'pull_request'
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a with:
context: .
platforms: linux/amd64
tags: ${{ steps.lowercase.outputs.image_name }}:scan
labels: ${{ steps.meta.outputs.labels }}
outputs: type=docker,dest=/tmp/image.tar
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
BINARY_NAME=${{ env.BINARY_NAME }}
BINARY_PACKAGE=${{ env.BINARY_PACKAGE }}
SBOM_MANIFEST_PATH=${{ env.SBOM_MANIFEST_PATH }}
- name: Build and push runtime image (non-PR)
if: github.event_name != 'pull_request'
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a with:
context: .
platforms: ${{ steps.platforms.outputs.value }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
BINARY_NAME=${{ env.BINARY_NAME }}
BINARY_PACKAGE=${{ env.BINARY_PACKAGE }}
SBOM_MANIFEST_PATH=${{ env.SBOM_MANIFEST_PATH }}
- name: Upload image artifact
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with:
name: docker-image
path: /tmp/image.tar
retention-days: 1
scan:
name: Security Scan
needs: build
runs-on: ${{ vars.RUST_TEMPLATE_RUNNER_UBUNTU || 'ubuntu-latest' }}
permissions:
contents: read
packages: read
security-events: write
steps:
- name: Download image (PRs)
if: github.event_name == 'pull_request'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
name: docker-image
path: /tmp
- name: Load image (PRs)
if: github.event_name == 'pull_request'
run: docker load -i /tmp/image.tar
- name: Get short SHA
id: sha
run: echo "short=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
- name: Log in to registry
if: github.event_name != 'pull_request'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull image
if: github.event_name != 'pull_request'
run: docker pull ${{ env.REGISTRY }}/${{ needs.build.outputs.image_name }}:${{ steps.sha.outputs.short }}
- name: Run Trivy scanner
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 with:
image-ref: ${{ github.event_name == 'pull_request' && format('{0}:scan', needs.build.outputs.image_name) || format('{0}/{1}:{2}', env.REGISTRY, needs.build.outputs.image_name, steps.sha.outputs.short) }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
vuln-type: os,library
continue-on-error: true
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@3d8036cf7fe7433e4a725cf513a6ea56c7fd0f14 with:
sarif_file: trivy-results.sarif
if: always() && hashFiles('trivy-results.sarif') != ''
sign:
name: Sign Container
needs: [build, scan]
runs-on: ${{ vars.RUST_TEMPLATE_RUNNER_UBUNTU || 'ubuntu-latest' }}
if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Install cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6
- name: Log in to registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Sign container image
run: cosign sign --yes ${{ env.REGISTRY }}/${{ needs.build.outputs.image_name }}@${{ needs.build.outputs.digest }}
env:
COSIGN_EXPERIMENTAL: 1
sbom:
name: Generate SBOM
needs: build
runs-on: ${{ vars.RUST_TEMPLATE_RUNNER_UBUNTU || 'ubuntu-latest' }}
if: github.event_name != 'pull_request'
steps:
- name: Get short SHA
id: sha
run: echo "short=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
- name: Log in to registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate SBOM
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 with:
image: ${{ env.REGISTRY }}/${{ needs.build.outputs.image_name }}:${{ steps.sha.outputs.short }}
artifact-name: sbom.spdx.json
format: spdx-json
continue-on-error: true