kube-router/.github/workflows/ci-container.yml

219 lines
10 KiB
YAML

name: ci-container
on:
workflow_call:
inputs:
buildtime-base:
description: The Docker image to use as the Go build base
required: true
type: string
runtime-base:
description: The Docker image to use as the container runtime base
required: true
type: string
secrets:
DOCKERHUB_USERNAME:
required: true
DOCKERHUB_TOKEN:
required: true
permissions:
contents: read
id-token: write
attestations: write
jobs:
# Builds and pushes the container image for branch pushes, PRs, and release tags.
# On tag events, also signs the image and attests a signed SBOM to DockerHub via cosign.
ci-build-container:
name: ci-build-container
runs-on: ubuntu-latest
steps:
# Check out the repository so the Dockerfile and source are available to the build.
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Register QEMU emulators so Docker Buildx can build non-native architectures (arm, s390x, etc).
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
# Create a multi-platform-capable Buildx builder instance on the runner.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
# Authenticate to DockerHub so subsequent push steps and cosign attestation pushes succeed.
- name: Login to DockerHub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Install cosign once for all tag-based signing and attestation steps below.
# Uses keyless signing — no private key required; identity is proven via the GitHub Actions
# OIDC token issued to this workflow, rooted in Sigstore Fulcio and logged in Rekor.
- name: Install cosign
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
# Parse the branch name from GITHUB_REF for use as the image tag on non-tag branch pushes.
- name: Extract branch from github ref - New Push
if: ${{ startsWith(github.ref, 'refs/tags/v') != true && github.event_name != 'pull_request' }}
shell: bash
run: echo "branch=${GITHUB_REF#refs/heads/}" >> "$GITHUB_OUTPUT"
id: extract_branch
# Parse the version tag from GITHUB_REF for use as the image tag on tag pushes.
- name: Extract tag from github ref - New Release
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
shell: bash
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
id: extract_tag
# Build and push a multi-arch image tagged with the branch name on direct pushes to tracked
# branches (master, v*, prep-v*). Not run on PRs or tag pushes.
- name: Build and push - New Push
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
if: ${{ startsWith(github.ref, 'refs/tags/v') != true && github.event_name != 'pull_request' }}
with:
context: .
platforms: |
linux/amd64
linux/arm64
linux/arm/v7
linux/s390x
linux/ppc64le
push: true
build-args: |
BUILDTIME_BASE=${{ inputs.buildtime-base }}
RUNTIME_BASE=${{ inputs.runtime-base }}
tags: cloudnativelabs/kube-router-git:${{ steps.extract_branch.outputs.branch }}
# Build and push a single-arch (amd64) image tagged with the PR number for pull requests.
# Multi-arch is skipped here as it adds 30+ minutes to PR feedback time.
- name: Build and push - New PR
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
if: github.event_name == 'pull_request'
with:
context: .
platforms: linux/amd64
push: true
provenance: false
build-args: |
BUILDTIME_BASE=${{ inputs.buildtime-base }}
RUNTIME_BASE=${{ inputs.runtime-base }}
tags: cloudnativelabs/kube-router-git:PR-${{ github.event.pull_request.number }}
# -----------------------------------------------------------------------------------------
# Release Candidate tag (e.g. v2.8.0-rc1): build, sign, generate SBOM, and attest to registry.
# The `latest` tag is NOT updated for release candidates.
# -----------------------------------------------------------------------------------------
# Build and push the multi-arch release candidate image. The step id captures the digest
# so subsequent signing and attestation steps can reference the exact immutable image.
- name: Build and push - New Tag (Release Candidate)
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
id: push_rc
if: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
with:
context: .
platforms: |
linux/amd64
linux/arm64
linux/arm/v7
linux/s390x
linux/ppc64le
push: true
build-args: |
BUILDTIME_BASE=${{ inputs.buildtime-base }}
RUNTIME_BASE=${{ inputs.runtime-base }}
tags: |
cloudnativelabs/kube-router:${{ steps.extract_tag.outputs.tag }}
# Sign the RC image digest with cosign (keyless). The signature is pushed to DockerHub as a
# sibling OCI artifact and the signing event is recorded in the Rekor transparency log.
# Verify with: cosign verify --certificate-identity-regexp "https://github.com/cloudnativelabs/.*"
# --certificate-oidc-issuer "https://token.actions.githubusercontent.com"
# cloudnativelabs/kube-router:<rc-tag>
- name: Sign container image - New Tag (Release Candidate)
if: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
run: cosign sign --yes cloudnativelabs/kube-router@${{ steps.push_rc.outputs.digest }}
# Generate an SPDX-JSON SBOM for the RC image using Syft. The SBOM is written to a local
# file for the attestation step and also uploaded as a workflow artifact.
- name: Generate SBOM for container image - New Tag (Release Candidate)
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
id: sbom_rc
if: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
with:
image: cloudnativelabs/kube-router@${{ steps.push_rc.outputs.digest }}
format: spdx-json
artifact-name: kube-router-${{ github.ref_name }}-image-sbom.spdx.json
output-file: ./container-sbom-rc.spdx.json
# Wrap the SBOM in a signed in-toto attestation and push it to DockerHub alongside the image.
# Users can retrieve and verify it with:
# cosign verify-attestation --type spdxjson
# --certificate-identity-regexp "https://github.com/cloudnativelabs/.*"
# --certificate-oidc-issuer "https://token.actions.githubusercontent.com"
# cloudnativelabs/kube-router:<rc-tag> | jq -r '.payload' | base64 -d | jq '.predicate'
- name: Attest SBOM to container image - New Tag (Release Candidate)
if: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
run: |
cosign attest --yes \
--predicate ./container-sbom-rc.spdx.json \
--type spdxjson \
cloudnativelabs/kube-router@${{ steps.push_rc.outputs.digest }}
# -----------------------------------------------------------------------------------------
# Production release tag (e.g. v2.8.0): build, sign, generate SBOM, and attest to registry.
# Both the versioned tag and `latest` are updated.
# -----------------------------------------------------------------------------------------
# Build and push the multi-arch production release image. Updates both the versioned tag and
# `latest`. The step id captures the digest for signing and attestation.
- name: Build and push - New Tag
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
id: push_release
if: ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
with:
context: .
platforms: |
linux/amd64
linux/arm64
linux/arm/v7
linux/s390x
linux/ppc64le
push: true
build-args: |
BUILDTIME_BASE=${{ inputs.buildtime-base }}
RUNTIME_BASE=${{ inputs.runtime-base }}
tags: |
cloudnativelabs/kube-router:${{ steps.extract_tag.outputs.tag }}
cloudnativelabs/kube-router:latest
# Sign the production release image digest with cosign (keyless). Same verification command
# as the RC step above, using the production release tag.
- name: Sign container image - New Tag
if: ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
run: cosign sign --yes cloudnativelabs/kube-router@${{ steps.push_release.outputs.digest }}
# Generate an SPDX-JSON SBOM for the production release image using Syft.
- name: Generate SBOM for container image - New Tag
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
id: sbom_release
if: ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
with:
image: cloudnativelabs/kube-router@${{ steps.push_release.outputs.digest }}
format: spdx-json
artifact-name: kube-router-${{ github.ref_name }}-image-sbom.spdx.json
output-file: ./container-sbom-release.spdx.json
# Wrap the SBOM in a signed in-toto attestation and push it to DockerHub alongside the image.
- name: Attest SBOM to container image - New Tag
if: ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
run: |
cosign attest --yes \
--predicate ./container-sbom-release.spdx.json \
--type spdxjson \
cloudnativelabs/kube-router@${{ steps.push_release.outputs.digest }}