diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 9fd3caf..84635d2 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -265,6 +265,8 @@ jobs: outputs: semver: ${{ steps.tag_semver.outputs.version }} trunkver: ${{ steps.tag_trunkver.outputs.trunkver }} + image-artifact-name: ${{ steps.artifact_names.outputs.image-artifact-name }} + sbom-artifact-name: ${{ steps.artifact_names.outputs.sbom-artifact-name }} permissions: contents: read env: @@ -279,6 +281,18 @@ jobs: with: fetch-depth: 0 + - id: artifact_names + name: Calculate artifact names + shell: bash + env: + IMAGE_ARTIFACT_NAME: ${{ env.IMAGE_ARTIFACT_NAME }} + SBOM_ARTIFACT_NAME: ${{ env.SBOM_ARTIFACT_NAME }} + run: | + set -euo pipefail + + printf 'image-artifact-name=%s\n' "$IMAGE_ARTIFACT_NAME" >> "$GITHUB_OUTPUT" + printf 'sbom-artifact-name=%s\n' "$SBOM_ARTIFACT_NAME" >> "$GITHUB_OUTPUT" + - name: Build Docker image uses: ./.github/actions/docker-build with: @@ -344,7 +358,7 @@ jobs: env: SEMVER_IMAGE_REF: ${{ needs.init.outputs.container-repository }}:${{ needs.container_build.outputs.semver }} TRUNKVER_IMAGE_REF: ${{ needs.init.outputs.container-repository }}:${{ needs.container_build.outputs.trunkver }} - IMAGE_ARTIFACT_NAME: container-image-${{ github.run_id }}-${{ github.run_attempt }} + IMAGE_ARTIFACT_NAME: ${{ needs.container_build.outputs.image-artifact-name }} REGISTRY: ${{ needs.init.outputs.container-registry }} steps: - name: Check out local actions @@ -434,7 +448,7 @@ jobs: STAGING_IMAGE_DIGEST: ${{ needs.container_publish.outputs.image-digest }} DOCKERHUB_REPOSITORY: docker.io/${{ needs.init.outputs.dockerhub-repository }} DOCKERHUB_REGISTRY: docker.io - IMAGE_ARTIFACT_NAME: container-image-${{ github.run_id }}-${{ github.run_attempt }} + IMAGE_ARTIFACT_NAME: ${{ needs.container_build.outputs.image-artifact-name }} steps: - name: Check out local actions uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -543,7 +557,7 @@ jobs: env: IMAGE_REPOSITORY: docker.io/${{ needs.init.outputs.dockerhub-repository }} IMAGE_DIGEST: ${{ needs.container_promote_dockerhub.outputs.image-digest }} - SBOM_ARTIFACT_NAME: container-sbom-${{ github.run_id }}-${{ github.run_attempt }} + SBOM_ARTIFACT_NAME: ${{ needs.container_build.outputs.sbom-artifact-name }} steps: - name: Check out local actions uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -583,7 +597,7 @@ jobs: env: IMAGE_REPOSITORY: ${{ needs.init.outputs.container-repository }} IMAGE_DIGEST: ${{ needs.container_publish.outputs.image-digest }} - SBOM_ARTIFACT_NAME: container-sbom-${{ github.run_id }}-${{ github.run_attempt }} + SBOM_ARTIFACT_NAME: ${{ needs.container_build.outputs.sbom-artifact-name }} steps: - name: Check out local actions uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/README.md b/README.md index 7da784f..cf227ab 100644 --- a/README.md +++ b/README.md @@ -217,11 +217,11 @@ The saved image receives two tags: - SemVer from `needs.build.outputs.version`, with one leading `v` removed. For example, `v2.0.0` becomes `2.0.0` for compatibility with the existing Docker Hub `1.4.0` naming. - A [TrunkVer](https://trunkver.org/) generated once during the container build and reused for both registries. -Use a manually dispatched run with `publish_image: true` to publish the saved tarball to the `Staging` environment. The default staging repository is `jcr.codebelt.net/geekle/web-cdn-origin`; override `container_repository` when needed. Configure `JCR_USERNAME` and `JCR_PASSWORD` as `Staging` environment secrets. +Use a manually dispatched run with `publish_image: true` to publish the saved tarball to the `Staging` environment. The default staging repository is `jcr.codebelt.net/geekle/web-cdn-origin`; override `container_repository` when needed. Configure the exact `REGISTRY_USERNAME` and `REGISTRY_PASSWORD` secret names in the `Staging` environment. The values must be accepted by the selected container registry, so the same generic secret names work when `container_repository` is overridden. -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 `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` as its environment secrets. 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. +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. 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. ## Kubernetes