1919
2020permissions :
2121 contents : read
22+ id-token : write
23+ attestations : write
2224
2325jobs :
24- # Builds Container only if a new push to main branch, a tag or a pull request from a source
25- # branch within the repository
26+ # Builds and pushes the container image for branch pushes, PRs, and release tags.
27+ # On tag events, also signs the image and attests a signed SBOM to DockerHub via cosign.
2628 ci-build-container :
2729 name : ci-build-container
2830 runs-on : ubuntu-latest
2931 steps :
32+ # Check out the repository so the Dockerfile and source are available to the build.
3033 - name : Checkout
3134 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
3235
36+ # Register QEMU emulators so Docker Buildx can build non-native architectures (arm, s390x, etc).
3337 - name : Set up QEMU
3438 uses : docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
3539
40+ # Create a multi-platform-capable Buildx builder instance on the runner.
3641 - name : Set up Docker Buildx
3742 uses : docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
3843
44+ # Authenticate to DockerHub so subsequent push steps and cosign attestation pushes succeed.
3945 - name : Login to DockerHub
4046 uses : docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
4147 with :
4248 username : ${{ secrets.DOCKERHUB_USERNAME }}
4349 password : ${{ secrets.DOCKERHUB_TOKEN }}
4450
51+ # Install cosign once for all tag-based signing and attestation steps below.
52+ # Uses keyless signing — no private key required; identity is proven via the GitHub Actions
53+ # OIDC token issued to this workflow, rooted in Sigstore Fulcio and logged in Rekor.
54+ - name : Install cosign
55+ if : ${{ startsWith(github.ref, 'refs/tags/v') }}
56+ uses : sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
57+
58+ # Parse the branch name from GITHUB_REF for use as the image tag on non-tag branch pushes.
4559 - name : Extract branch from github ref - New Push
4660 if : ${{ startsWith(github.ref, 'refs/tags/v') != true && github.event_name != 'pull_request' }}
4761 shell : bash
4862 run : echo "branch=${GITHUB_REF#refs/heads/}" >> "$GITHUB_OUTPUT"
4963 id : extract_branch
5064
65+ # Parse the version tag from GITHUB_REF for use as the image tag on tag pushes.
5166 - name : Extract tag from github ref - New Release
5267 if : ${{ startsWith(github.ref, 'refs/tags/v') }}
5368 shell : bash
5469 run : echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
5570 id : extract_tag
5671
72+ # Build and push a multi-arch image tagged with the branch name on direct pushes to tracked
73+ # branches (master, v*, prep-v*). Not run on PRs or tag pushes.
5774 - name : Build and push - New Push
5875 uses : docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
5976 if : ${{ startsWith(github.ref, 'refs/tags/v') != true && github.event_name != 'pull_request' }}
@@ -71,22 +88,30 @@ jobs:
7188 RUNTIME_BASE=${{ inputs.runtime-base }}
7289 tags : cloudnativelabs/kube-router-git:${{ steps.extract_branch.outputs.branch }}
7390
91+ # Build and push a single-arch (amd64) image tagged with the PR number for pull requests.
92+ # Multi-arch is skipped here as it adds 30+ minutes to PR feedback time.
7493 - name : Build and push - New PR
7594 uses : docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
7695 if : github.event_name == 'pull_request'
7796 with :
7897 context : .
79- # Don't build multi arch images for PR as they take more than 30 min to build
8098 platforms : linux/amd64
8199 push : true
82100 build-args : |
83101 BUILDTIME_BASE=${{ inputs.buildtime-base }}
84102 RUNTIME_BASE=${{ inputs.runtime-base }}
85103 tags : cloudnativelabs/kube-router-git:PR-${{ github.event.pull_request.number }}
86104
87- # Tagging a release candidate, don't update latest
105+ # -----------------------------------------------------------------------------------------
106+ # Release Candidate tag (e.g. v2.8.0-rc1): build, sign, generate SBOM, and attest to registry.
107+ # The `latest` tag is NOT updated for release candidates.
108+ # -----------------------------------------------------------------------------------------
109+
110+ # Build and push the multi-arch release candidate image. The step id captures the digest
111+ # so subsequent signing and attestation steps can reference the exact immutable image.
88112 - name : Build and push - New Tag (Release Candidate)
89113 uses : docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
114+ id : push_rc
90115 if : ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
91116 with :
92117 context : .
@@ -103,9 +128,51 @@ jobs:
103128 tags : |
104129 cloudnativelabs/kube-router:${{ steps.extract_tag.outputs.tag }}
105130
106- # Tagging a proper release, update latest
131+ # Sign the RC image digest with cosign (keyless). The signature is pushed to DockerHub as a
132+ # sibling OCI artifact and the signing event is recorded in the Rekor transparency log.
133+ # Verify with: cosign verify --certificate-identity-regexp "https://github.com/cloudnativelabs/.*"
134+ # --certificate-oidc-issuer "https://token.actions.githubusercontent.com"
135+ # cloudnativelabs/kube-router:<rc-tag>
136+ - name : Sign container image - New Tag (Release Candidate)
137+ if : ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
138+ run : cosign sign --yes cloudnativelabs/kube-router@${{ steps.push_rc.outputs.digest }}
139+
140+ # Generate an SPDX-JSON SBOM for the RC image using Syft. The SBOM is written to a local
141+ # file for the attestation step and also uploaded as a workflow artifact.
142+ - name : Generate SBOM for container image - New Tag (Release Candidate)
143+ uses : anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
144+ id : sbom_rc
145+ if : ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
146+ with :
147+ image : cloudnativelabs/kube-router@${{ steps.push_rc.outputs.digest }}
148+ format : spdx-json
149+ artifact-name : kube-router-${{ github.ref_name }}-image-sbom.spdx.json
150+ output-file : ./container-sbom-rc.spdx.json
151+
152+ # Wrap the SBOM in a signed in-toto attestation and push it to DockerHub alongside the image.
153+ # Users can retrieve and verify it with:
154+ # cosign verify-attestation --type spdxjson
155+ # --certificate-identity-regexp "https://github.com/cloudnativelabs/.*"
156+ # --certificate-oidc-issuer "https://token.actions.githubusercontent.com"
157+ # cloudnativelabs/kube-router:<rc-tag> | jq -r '.payload' | base64 -d | jq '.predicate'
158+ - name : Attest SBOM to container image - New Tag (Release Candidate)
159+ if : ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc') }}
160+ run : |
161+ cosign attest --yes \
162+ --predicate ./container-sbom-rc.spdx.json \
163+ --type spdxjson \
164+ cloudnativelabs/kube-router@${{ steps.push_rc.outputs.digest }}
165+
166+ # -----------------------------------------------------------------------------------------
167+ # Production release tag (e.g. v2.8.0): build, sign, generate SBOM, and attest to registry.
168+ # Both the versioned tag and `latest` are updated.
169+ # -----------------------------------------------------------------------------------------
170+
171+ # Build and push the multi-arch production release image. Updates both the versioned tag and
172+ # `latest`. The step id captures the digest for signing and attestation.
107173 - name : Build and push - New Tag
108174 uses : docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
175+ id : push_release
109176 if : ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
110177 with :
111178 context : .
@@ -122,3 +189,29 @@ jobs:
122189 tags : |
123190 cloudnativelabs/kube-router:${{ steps.extract_tag.outputs.tag }}
124191 cloudnativelabs/kube-router:latest
192+
193+ # Sign the production release image digest with cosign (keyless). Same verification command
194+ # as the RC step above, using the production release tag.
195+ - name : Sign container image - New Tag
196+ if : ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
197+ run : cosign sign --yes cloudnativelabs/kube-router@${{ steps.push_release.outputs.digest }}
198+
199+ # Generate an SPDX-JSON SBOM for the production release image using Syft.
200+ - name : Generate SBOM for container image - New Tag
201+ uses : anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
202+ id : sbom_release
203+ if : ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
204+ with :
205+ image : cloudnativelabs/kube-router@${{ steps.push_release.outputs.digest }}
206+ format : spdx-json
207+ artifact-name : kube-router-${{ github.ref_name }}-image-sbom.spdx.json
208+ output-file : ./container-sbom-release.spdx.json
209+
210+ # Wrap the SBOM in a signed in-toto attestation and push it to DockerHub alongside the image.
211+ - name : Attest SBOM to container image - New Tag
212+ if : ${{ startsWith(github.ref, 'refs/tags/v') && ! contains(github.ref, '-rc') }}
213+ run : |
214+ cosign attest --yes \
215+ --predicate ./container-sbom-release.spdx.json \
216+ --type spdxjson \
217+ cloudnativelabs/kube-router@${{ steps.push_release.outputs.digest }}
0 commit comments