@@ -59,62 +59,124 @@ runs:
5959 echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV
6060 echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV
6161
62+ # TODO (@NickLarsenNZ): Make this a reusable action in .github/actions/push-and-sign/action.yml
6263 - name : Push Image to repo.stackable.tech and sign via cosign
6364 shell : bash
6465 run : |
6566 set -euo pipefail
66- docker image push "$(< bake-target-tags)"
67+ TARGET_TAG="$(< bake-target-tags)"
68+ docker image push "$TARGET_TAG"
6769 # Obtain the digest of the image, because signing by tag is deprecated and will be removed from cosign in the future
68- DIGEST=$(docker images --digests "$(< bake-target-tags)" --format '{{.Digest}}')
69- echo "DIGEST=$DIGEST" >> $GITHUB_ENV
70+ # This must be done after a push, otherwise the repo digest is empty.
71+ # Find the digest for the image. Notice the architecture at the end of TAG
72+ # IMAGE_VERSION does not contain the architecture (otherwise we could have used ${IMAGE_NAME}:${IMAGE_VERSION})
73+ # Given:
74+ # IMAGE_NAME: docker.stackable.tech/stackable/hello-world
75+ # TARGET_TAG: docker.stackable.tech/stackable/hello-world:0.0.1-SNAPSHOT-stackable0.0.0-dev-arm64
76+ # Expect:
77+ # STDOUT: docker.stackable.tech/stackable/hello-world@sha256:917f800259ef4915f976e93987b752fd64debf347568610d7f685d20220fc88a
78+ REPO_DIGEST=$(
79+ docker inspect "$TARGET_TAG" --format json | \
80+ jq -r \
81+ --arg IMAGE_NAME "$IMAGE_NAME" \
82+ --arg TARGET_TAG "$TARGET_TAG" \
83+ '
84+ map(select(.RepoTags[] | contains($TARGET_TAG)))[0]
85+ | .RepoDigests[]
86+ | select(. | startswith($IMAGE_NAME))
87+ '
88+ )
89+ # Ensure REPO_DIGEST is not empty
90+ if [[ -z "$REPO_DIGEST" ]]; then
91+ >&2 echo "Repo Digest is empty, but is required for signing"
92+ exit 1
93+ fi
94+
95+ # Needed by future steps
96+ echo "REPO_DIGEST=$REPO_DIGEST" | tee -a $GITHUB_ENV
97+
7098 # Refer to image via its digest (docker.stackable.tech/stackable/airflow@sha256:0a1b2c...)
7199 # This generates a signature and publishes it to the registry, next to the image
72100 # Uses the keyless signing flow with Github Actions as identity provider
73- cosign sign -y "$IMAGE_NAME@$DIGEST "
101+ cosign sign -y "${REPO_DIGEST} "
74102
75103 - name : Generate SBOM for the Nexus Image
76104 shell : bash
77105 run : |
78106 set -euo pipefail
79- syft scan --output cyclonedx-json=sbom.json --select-catalogers "-cargo-auditable-binary-cataloger" --scope all-layers --source-name "${{ inputs.product }}" --source-version "$TAG_NAME" "$IMAGE_NAME@$DIGEST";
107+ syft scan --output cyclonedx-json=sbom.json --select-catalogers "-cargo-auditable-binary-cataloger" --scope all-layers --source-name "${{ inputs.product }}" --source-version "$TAG_NAME" "${REPO_DIGEST}";
108+ # The DIGEST is the right side of `REPO_DIGEST` (split by '@')
109+ DIGEST=${REPO_DIGEST#*@}
80110 # Determine the PURL for the image
81111 PURL="pkg:docker/stackable/${{ inputs.product }}@$DIGEST?repository_url=docker.stackable.tech";
82112 # Get metadata from the image
83- IMAGE_METADATA_DESCRIPTION=$(docker inspect --format='{{.Config.Labels.description}}' "$IMAGE_NAME@$DIGEST ");
84- IMAGE_METADATA_NAME=$(docker inspect --format='{{.Config.Labels.name}}' "$IMAGE_NAME@$DIGEST ");
113+ IMAGE_METADATA_DESCRIPTION=$(docker inspect --format='{{.Config.Labels.description}}' "${REPO_DIGEST} ");
114+ IMAGE_METADATA_NAME=$(docker inspect --format='{{.Config.Labels.name}}' "${REPO_DIGEST} ");
85115 # Merge the SBOM with the metadata for the image
86116 jq -s '{"metadata":{"component":{"description":"'"$IMAGE_METADATA_NAME. $IMAGE_METADATA_DESCRIPTION"'","supplier":{"name":"Stackable GmbH","url":["https://stackable.tech/"]},"author":"Stackable GmbH","purl":"'"$PURL"'","publisher":"Stackable GmbH"}}} * .[0]' sbom.json > sbom.merged.json;
87117 # Attest the SBOM to the image
88- cosign attest -y --predicate sbom.merged.json --type cyclonedx "$IMAGE_NAME@$DIGEST "
118+ cosign attest -y --predicate sbom.merged.json --type cyclonedx "${REPO_DIGEST} "
89119
120+ # TODO (@NickLarsenNZ): Make this a reusable action in .github/actions/push-and-sign/action.yml
90121 - name : Push Image to oci.stackable.tech and sign via cosign
91122 shell : bash
92123 run : |
93124 set -euo pipefail
125+ # Update the registry
94126 IMAGE_NAME=oci.stackable.tech/sdp/${{ inputs.product }}
95- echo "image: $IMAGE_NAME"
96- docker tag "$(< bake-target-tags)" "$IMAGE_NAME:$TAG_NAME"
97- docker image push "$(< bake-target-tags)"
127+ # IMAGE_NAME is needed by future steps
128+ echo "IMAGE_NAME=$IMAGE_NAME" | tee -a $GITHUB_ENV
129+ OLD_TARGET_TAG="$(< bake-target-tags)"
130+ TARGET_TAG="$IMAGE_NAME:$TAG_NAME"
131+ docker tag "$OLD_TARGET_TAG" "$TARGET_TAG"
132+ docker image push "$TARGET_TAG"
98133 # Obtain the digest of the image, because signing by tag is deprecated and will be removed from cosign in the future
99- DIGEST=$(docker images --digests "$(< bake-target-tags)" --format '{{.Digest}}')
100- echo "DIGEST=$DIGEST" >> $GITHUB_ENV
134+ # This must be done after a push, otherwise the repo digest is empty.
135+ # Find the digest for the image. Notice the architecture at the end of TAG
136+ # IMAGE_VERSION does not contain the architecture (otherwise we could have used ${IMAGE_NAME}:${IMAGE_VERSION})
137+ # Given:
138+ # IMAGE_NAME: oci.stackable.tech/sdp/hello-world
139+ # TARGET_TAG: oci.stackable.tech/sdp/hello-world:0.0.1-SNAPSHOT-stackable0.0.0-dev-arm64
140+ # Expect:
141+ # STDOUT: oci.stackable.tech/sdp/hello-world@sha256:917f800259ef4915f976e93987b752fd64debf347568610d7f685d20220fc88a
142+ REPO_DIGEST=$(
143+ docker inspect "$TARGET_TAG" --format json | \
144+ jq -r \
145+ --arg IMAGE_NAME "$IMAGE_NAME" \
146+ --arg TARGET_TAG "$TARGET_TAG" \
147+ '
148+ map(select(.RepoTags[] | contains($TARGET_TAG)))[0]
149+ | .RepoDigests[]
150+ | select(. | startswith($IMAGE_NAME))
151+ '
152+ )
153+ # Ensure REPO_DIGEST is not empty
154+ if [[ -z "$REPO_DIGEST" ]]; then
155+ >&2 echo "Repo Digest is empty, but is required for signing"
156+ exit 1
157+ fi
158+
159+ # REPO_DIGEST is needed by future steps
160+ echo "REPO_DIGEST=$REPO_DIGEST" | tee -a $GITHUB_ENV
161+
101162 # Refer to image via its digest (oci.stackable.tech/sdp/airflow@sha256:0a1b2c...)
102163 # This generates a signature and publishes it to the registry, next to the image
103164 # Uses the keyless signing flow with Github Actions as identity provider
104- cosign sign -y "$IMAGE_NAME@$DIGEST "
165+ cosign sign -y "${REPO_DIGEST} "
105166
106167 - name : Generate SBOM for the Harbor Image
107168 shell : bash
108169 run : |
109170 set -euo pipefail
110- IMAGE_NAME=oci.stackable.tech/sdp/${{ inputs.product }}
111- syft scan --output cyclonedx-json=sbom.json --select-catalogers "-cargo-auditable-binary-cataloger" --scope all-layers --source-name "${{ inputs.product }}" --source-version "$TAG_NAME" "$IMAGE_NAME@$DIGEST";
171+ syft scan --output cyclonedx-json=sbom.json --select-catalogers "-cargo-auditable-binary-cataloger" --scope all-layers --source-name "${{ inputs.product }}" --source-version "$TAG_NAME" "${REPO_DIGEST}";
172+ # The DIGEST is the right side of `REPO_DIGEST` (split by '@')
173+ DIGEST=${REPO_DIGEST#*@}
112174 # Determine the PURL for the image
113175 PURL="pkg:docker/sdp/${{ inputs.product }}@$DIGEST?repository_url=oci.stackable.tech";
114176 # Get metadata from the image
115- IMAGE_METADATA_DESCRIPTION=$(docker inspect --format='{{.Config.Labels.description}}' "$IMAGE_NAME@$DIGEST ");
116- IMAGE_METADATA_NAME=$(docker inspect --format='{{.Config.Labels.name}}' "$IMAGE_NAME@$DIGEST ");
177+ IMAGE_METADATA_DESCRIPTION=$(docker inspect --format='{{.Config.Labels.description}}' "${REPO_DIGEST} ");
178+ IMAGE_METADATA_NAME=$(docker inspect --format='{{.Config.Labels.name}}' "${REPO_DIGEST} ");
117179 # Merge the SBOM with the metadata for the image
118180 jq -s '{"metadata":{"component":{"description":"'"$IMAGE_METADATA_NAME. $IMAGE_METADATA_DESCRIPTION"'","supplier":{"name":"Stackable GmbH","url":["https://stackable.tech/"]},"author":"Stackable GmbH","purl":"'"$PURL"'","publisher":"Stackable GmbH"}}} * .[0]' sbom.json > sbom.merged.json;
119181 # Attest the SBOM to the image
120- cosign attest -y --predicate sbom.merged.json --type cyclonedx "$IMAGE_NAME@$DIGEST "
182+ cosign attest -y --predicate sbom.merged.json --type cyclonedx "${REPO_DIGEST} "
0 commit comments