@@ -59,68 +59,124 @@ runs:
59
59
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV
60
60
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV
61
61
62
+ # TODO (@NickLarsenNZ): Make this a reusable action in .github/actions/push-and-sign/action.yml
62
63
- name : Push Image to repo.stackable.tech and sign via cosign
63
64
shell : bash
64
65
run : |
65
66
set -euo pipefail
66
- # Store the output of `docker image push` into a variable, so we can
67
- # parse it for the digest
68
- PUSH_OUTPUT=$(docker image push "$(< bake-target-tags)" 2>&1)
69
- echo "$PUSH_OUTPUT"
70
- # Obtain the digest of the pushed image from the output of `docker image
71
- # push`, because signing by tag is deprecated and will be removed from
72
- # cosign in the future
73
- DIGEST=$(echo "$PUSH_OUTPUT" | awk "/: digest: sha256:[a-f0-9]{64} size: [0-9]+$/ { print \$3 }")
74
- echo "DIGEST=$DIGEST" >> $GITHUB_ENV
67
+ TARGET_TAG="$(< bake-target-tags)"
68
+ docker image push "$TARGET_TAG"
69
+ # Obtain the digest of the image, because signing by tag is deprecated and will be removed from cosign in the future
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
+
75
98
# Refer to image via its digest (docker.stackable.tech/stackable/airflow@sha256:0a1b2c...)
76
99
# This generates a signature and publishes it to the registry, next to the image
77
100
# Uses the keyless signing flow with Github Actions as identity provider
78
- cosign sign -y "$IMAGE_NAME@$DIGEST "
101
+ cosign sign -y "${REPO_DIGEST} "
79
102
80
103
- name : Generate SBOM for the Nexus Image
81
104
shell : bash
82
105
run : |
83
106
set -euo pipefail
84
- 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#*@}
85
110
# Determine the PURL for the image
86
111
PURL="pkg:docker/stackable/${{ inputs.product }}@$DIGEST?repository_url=docker.stackable.tech";
87
112
# Get metadata from the image
88
- IMAGE_METADATA_DESCRIPTION=$(docker inspect --format='{{.Config.Labels.description}}' "$IMAGE_NAME@$DIGEST ");
89
- 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} ");
90
115
# Merge the SBOM with the metadata for the image
91
116
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;
92
117
# Attest the SBOM to the image
93
- cosign attest -y --predicate sbom.merged.json --type cyclonedx "$IMAGE_NAME@$DIGEST "
118
+ cosign attest -y --predicate sbom.merged.json --type cyclonedx "${REPO_DIGEST} "
94
119
120
+ # TODO (@NickLarsenNZ): Make this a reusable action in .github/actions/push-and-sign/action.yml
95
121
- name : Push Image to oci.stackable.tech and sign via cosign
96
122
shell : bash
97
123
run : |
98
124
set -euo pipefail
125
+ # Update the registry
99
126
IMAGE_NAME=oci.stackable.tech/sdp/${{ inputs.product }}
100
- echo "image: $IMAGE_NAME"
101
- docker tag "$(< bake-target-tags)" "$IMAGE_NAME:$TAG_NAME"
102
- # Store the output of `docker image push` into a variable, so we can parse it for the digest
103
- PUSH_OUTPUT=$(docker image push "$IMAGE_NAME:$TAG_NAME" 2>&1)
104
- echo "$PUSH_OUTPUT"
105
- # Obtain the digest of the pushed image from the output of `docker image push`, because signing by tag is deprecated and will be removed from cosign in the future
106
- DIGEST=$(echo "$PUSH_OUTPUT" | awk "/: digest: sha256:[a-f0-9]{64} size: [0-9]+$/ { print \$3 }")
107
- echo "DIGEST=$DIGEST" >> $GITHUB_ENV
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"
133
+ # Obtain the digest of the image, because signing by tag is deprecated and will be removed from cosign in the future
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
+
108
162
# Refer to image via its digest (oci.stackable.tech/sdp/airflow@sha256:0a1b2c...)
109
163
# This generates a signature and publishes it to the registry, next to the image
110
164
# Uses the keyless signing flow with Github Actions as identity provider
111
- cosign sign -y "$IMAGE_NAME@$DIGEST "
165
+ cosign sign -y "${REPO_DIGEST} "
112
166
113
167
- name : Generate SBOM for the Harbor Image
114
168
shell : bash
115
169
run : |
116
170
set -euo pipefail
117
- 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#*@}
118
174
# Determine the PURL for the image
119
175
PURL="pkg:docker/sdp/${{ inputs.product }}@$DIGEST?repository_url=oci.stackable.tech";
120
176
# Get metadata from the image
121
- IMAGE_METADATA_DESCRIPTION=$(docker inspect --format='{{.Config.Labels.description}}' "$IMAGE_NAME@$DIGEST ");
122
- 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} ");
123
179
# Merge the SBOM with the metadata for the image
124
180
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;
125
181
# Attest the SBOM to the image
126
- 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