From 82284d8fc3ec53111412d2e0850456654d61062e Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Tue, 11 Aug 2026 01:41:43 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=91=B7=20add=20optional=20docker=20at?= =?UTF-8?q?testation=20support=20to=20ci=20pipeline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds optional attest_image parameter (default true) to allow operators to disable attestation as a fallback when registry support is unavailable. Includes docker login/logout steps in both staging and dockerhub attestation jobs, environment gates for production safety, and updates conditional logic for the promotion job to handle skipped attestation. --- .github/workflows/ci-pipeline.yml | 50 +++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 84635d2..2236b90 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -13,6 +13,10 @@ on: type: boolean description: Publish the saved container image to the configured Staging registry after all gates pass. default: false + attest_image: + type: boolean + description: Publish GitHub provenance and SBOM attestations to the target registry after image publication. + default: true container_repository: type: string description: Fully qualified image repository without a tag. @@ -39,6 +43,7 @@ jobs: run-mac-tests: ${{ steps.vars.outputs.run-mac-tests }} run-privileged-jobs: ${{ steps.vars.outputs.run-privileged-jobs }} publish-image: ${{ steps.vars.outputs.publish-image }} + attest-image: ${{ steps.vars.outputs.attest-image }} container-repository: ${{ steps.vars.outputs.container-repository }} container-registry: ${{ steps.vars.outputs.container-registry }} promote-dockerhub: ${{ steps.vars.outputs.promote-dockerhub }} @@ -51,6 +56,7 @@ jobs: EVENT_NAME: ${{ github.event_name }} RUN_MAC_INPUT: ${{ inputs.run_mac_tests }} PUBLISH_IMAGE_INPUT: ${{ inputs.publish_image }} + ATTEST_IMAGE_INPUT: ${{ inputs.attest_image }} PROMOTE_DOCKERHUB_INPUT: ${{ inputs.promote_dockerhub }} CONTAINER_REPOSITORY_INPUT: ${{ inputs.container_repository }} DOCKERHUB_REPOSITORY_INPUT: ${{ inputs.dockerhub_repository }} @@ -72,6 +78,12 @@ jobs: echo "publish-image=false" >> "$GITHUB_OUTPUT" fi + if [[ "$EVENT_NAME" == "workflow_dispatch" && "$ATTEST_IMAGE_INPUT" == "false" ]]; then + echo "attest-image=false" >> "$GITHUB_OUTPUT" + else + echo "attest-image=true" >> "$GITHUB_OUTPUT" + fi + if [[ "$EVENT_NAME" == "workflow_dispatch" && "$PROMOTE_DOCKERHUB_INPUT" == "true" ]]; then if [[ "$PUBLISH_IMAGE_INPUT" != "true" ]]; then echo "::error::promote_dockerhub requires publish_image to be true." @@ -433,7 +445,7 @@ jobs: run: docker logout "$REGISTRY" container_promote_dockerhub: - if: ${{ always() && needs.init.outputs.publish-image == 'true' && needs.init.outputs.promote-dockerhub == 'true' && github.event_name != 'pull_request' && needs.container_attest.result == 'success' }} + if: ${{ always() && needs.init.outputs.publish-image == 'true' && needs.init.outputs.promote-dockerhub == 'true' && github.event_name != 'pull_request' && (needs.init.outputs.attest-image != 'true' || needs.container_attest.result == 'success') }} name: promote-container-dockerhub needs: [init, container_build, container_publish, container_attest] runs-on: ubuntu-24.04 @@ -544,10 +556,11 @@ jobs: run: docker logout "$REGISTRY" container_attest_dockerhub: - if: ${{ always() && needs.init.outputs.publish-image == 'true' && needs.init.outputs.promote-dockerhub == 'true' && github.event_name != 'pull_request' && needs.container_promote_dockerhub.result == 'success' }} + if: ${{ always() && needs.init.outputs.attest-image == 'true' && needs.init.outputs.publish-image == 'true' && needs.init.outputs.promote-dockerhub == 'true' && github.event_name != 'pull_request' && needs.container_promote_dockerhub.result == 'success' }} name: attest-container-dockerhub needs: [init, container_build, container_promote_dockerhub] runs-on: ubuntu-24.04 + environment: Production permissions: contents: read id-token: write @@ -558,6 +571,7 @@ jobs: IMAGE_REPOSITORY: docker.io/${{ needs.init.outputs.dockerhub-repository }} IMAGE_DIGEST: ${{ needs.container_promote_dockerhub.outputs.image-digest }} SBOM_ARTIFACT_NAME: ${{ needs.container_build.outputs.sbom-artifact-name }} + REGISTRY: docker.io steps: - name: Check out local actions uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -570,6 +584,13 @@ jobs: name: ${{ env.SBOM_ARTIFACT_NAME }} path: ${{ runner.temp }}/container-sbom + - name: Log in to Docker Hub for attestation + uses: ./.github/actions/docker-login + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Attest Docker Hub container provenance uses: ./.github/actions/container-attest-provenance with: @@ -583,11 +604,19 @@ jobs: subject-digest: ${{ env.IMAGE_DIGEST }} sbom-path: ${{ runner.temp }}/container-sbom/sbom.spdx.json + - name: Log out of Docker Hub after attestation + if: ${{ always() }} + shell: bash + env: + REGISTRY: ${{ env.REGISTRY }} + run: docker logout "$REGISTRY" + container_attest: - if: ${{ always() && needs.init.outputs.publish-image == 'true' && github.event_name != 'pull_request' && needs.container_publish.result == 'success' }} + if: ${{ always() && needs.init.outputs.attest-image == 'true' && needs.init.outputs.publish-image == 'true' && github.event_name != 'pull_request' && needs.container_publish.result == 'success' }} name: attest-container needs: [init, build, container_build, container_publish] runs-on: ubuntu-24.04 + environment: Staging permissions: contents: read id-token: write @@ -598,6 +627,7 @@ jobs: IMAGE_REPOSITORY: ${{ needs.init.outputs.container-repository }} IMAGE_DIGEST: ${{ needs.container_publish.outputs.image-digest }} SBOM_ARTIFACT_NAME: ${{ needs.container_build.outputs.sbom-artifact-name }} + REGISTRY: ${{ needs.init.outputs.container-registry }} steps: - name: Check out local actions uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -610,6 +640,13 @@ jobs: name: ${{ env.SBOM_ARTIFACT_NAME }} path: ${{ runner.temp }}/container-sbom + - name: Log in to image registry for attestation + uses: ./.github/actions/docker-login + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + - name: Attest container provenance uses: ./.github/actions/container-attest-provenance with: @@ -622,3 +659,10 @@ jobs: subject-name: ${{ env.IMAGE_REPOSITORY }} subject-digest: ${{ env.IMAGE_DIGEST }} sbom-path: ${{ runner.temp }}/container-sbom/sbom.spdx.json + + - name: Log out of image registry after attestation + if: ${{ always() }} + shell: bash + env: + REGISTRY: ${{ env.REGISTRY }} + run: docker logout "$REGISTRY" From 39e6aeee44fb3b6a774f3232cd215490a0bcf5ff Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Tue, 11 Aug 2026 01:41:50 +0200 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=92=AC=20document=20attestation=20fea?= =?UTF-8?q?ture=20and=20release=20fallback=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents that attestation is enabled by default and explains the attest_image fallback parameter for cases where registry attestation is unavailable. Clarifies how disabling attestation affects the release artifact and digest gates. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf227ab..d40f505 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ Use a manually dispatched run with `publish_image: true` to publish the saved ta To keep the exact same run artifact, set both `publish_image: true` and `promote_dockerhub: true` on the manual dispatch. The workflow publishes to Staging and then pauses at the protected `Production` environment, allowing you to verify JCR before approving Docker Hub publication. Configure required reviewers for `Production` and add the exact `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` secret names to that environment. `DOCKERHUB_TOKEN` must be a Docker Hub access token with permission to push to the target namespace; a normal account password is not expected. The default Docker Hub repository is `codebeltnet/web-cdn-origin`; override `dockerhub_repository` when needed. A run with `promote_dockerhub: false` intentionally stops after Staging; enabling it on a later run creates a new build artifact. -This is a promotion of one immutable build artifact: the Docker Hub job downloads the same `docker save` tarball, uses `docker load`, retags it for Docker Hub, and pushes both tags. It does not rebuild or pull a new image. The build job carries the image and SBOM artifact names to downstream jobs, so rerunning only a failed publish or attestation job reuses the artifact from the successful build attempt. Digest gates verify that the SemVer and TrunkVer tags point to the same image in each registry and that the Docker Hub digest matches Staging. Attestation jobs publish GitHub build-provenance and SBOM attestations for the pushed digest, so each target registry must accept OCI attestation artifacts. +This is a promotion of one immutable build artifact: the Docker Hub job downloads the same `docker save` tarball, uses `docker load`, retags it for Docker Hub, and pushes both tags. It does not rebuild or pull a new image. The build job carries the image and SBOM artifact names to downstream jobs, so rerunning only a failed publish or attestation job reuses the artifact from the successful build attempt. Digest gates verify that the SemVer and TrunkVer tags point to the same image in each registry and that the Docker Hub digest matches Staging. Attestation jobs publish GitHub build-provenance and SBOM attestations for the pushed digest, so each target registry must accept OCI attestation artifacts. Attestation is enabled by default; set the manual `attest_image` input to `false` only as an explicit release fallback when registry attestation is unavailable. This skips both attestation jobs and removes the attestation-success gate from Docker Hub promotion, so the resulting release has no registry-published GitHub attestations. ## Kubernetes