Skip to content

Commit 09d7e10

Browse files
Copilotneilime
andcommitted
Eliminate untagged versions for single-platform builds
Co-authored-by: neilime <314088+neilime@users.noreply.github.com>
1 parent 7c2e965 commit 09d7e10

4 files changed

Lines changed: 28 additions & 7 deletions

File tree

.github/workflows/__test-workflow-docker-build-images.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,9 @@ jobs:
164164
const expectedTaggedVersions = isSigned ? 2 : 1;
165165
166166
// Expected untagged versions:
167-
// - For single platform: 1 (the digest-only push from build step)
167+
// - For single platform: 0 (no untagged versions when optimized)
168168
// - For multi platform: number of platforms (one per platform digest-only push)
169-
// Note: The build step uses push-by-digest which creates untagged versions
170-
const expectedUntaggedVersions = platforms.length;
169+
const expectedUntaggedVersions = isSinglePlatform ? 0 : platforms.length;
171170
172171
assert.equal(
173172
taggedVersions.length,

.github/workflows/docker-build-images.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ jobs:
310310
...image,
311311
platform: platformName,
312312
"runs-on": platformRunsOn,
313+
// For single-platform builds, don't use push-by-digest to avoid untagged versions
314+
"push-by-digest": platforms.length > 1,
313315
};
314316
imagesByPlatform.push(imageByPlatform);
315317
}
@@ -425,6 +427,7 @@ jobs:
425427
secret-envs: ${{ steps.prepare-secret-envs.outputs.secret-envs }}
426428
secrets: ${{ secrets.build-secrets }}
427429
cache-type: ${{ inputs.cache-type }}
430+
push-by-digest: ${{ matrix.image.push-by-digest }}
428431

429432
# FIXME: Set built images infos in file to be uploaded as artifacts, because github action does not handle job outputs for matrix
430433
# https://github.com/orgs/community/discussions/26639

actions/docker/build-image/action.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ inputs:
8787
See https://docs.docker.com/build/cache/backends.
8888
default: "gha"
8989
required: false
90+
push-by-digest:
91+
description: |
92+
Whether to push by digest only (without tags).
93+
Set to false for single-platform builds to avoid creating untagged versions.
94+
Set to true for multi-platform builds (required for manifest creation).
95+
default: "true"
96+
required: false
9097

9198
outputs:
9299
built-image:
@@ -297,7 +304,7 @@ runs:
297304
# FIXME: Remove 'inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-to ||'
298305
# when https://github.com/int128/docker-build-cache-config-action/pull/1213 is merged
299306
cache-to: ${{ inputs.cache-type == 'gha' && steps.transform-cache-gha.outputs.cache-to || steps.cache-arguments.outputs.cache-to }}
300-
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
307+
outputs: ${{ inputs.push-by-digest == 'true' && 'type=image,push-by-digest=true,name-canonical=true,push=true' || 'type=image,push=true' }}
301308
labels: ${{ steps.metadata.outputs.labels }}
302309
annotations: ${{ steps.metadata.outputs.annotations }}
303310
tags: ${{ steps.metadata.outputs.image }}

actions/docker/create-images-manifests/action.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,15 @@ runs:
125125
126126
// Helper function to tag single-platform image
127127
async function tagSinglePlatformImage(builtImage, imagesWithTags) {
128-
core.info(`Skipping multiarch manifest creation for "${builtImage.name}" (single platform: ${builtImage.platforms[0] || 'none'})`);
128+
core.info(`Tagging single-platform image "${builtImage.name}" (skipping multiarch manifest creation)`);
129129
130130
validateImage(builtImage);
131131
132132
const sourceImage = builtImage.images[0];
133133
for (const targetImage of imagesWithTags) {
134134
const tagCommand = `docker buildx imagetools create --tag ${targetImage} ${sourceImage}`;
135135
await exec.exec(tagCommand);
136-
core.debug(`Tag single-platform image "${builtImage.name}" ("${tagCommand}") executed`);
136+
core.debug(`Tagged single-platform image "${builtImage.name}"`);
137137
}
138138
139139
builtImage.images = imagesWithTags;
@@ -173,7 +173,19 @@ runs:
173173
return new Promise(async (resolve, reject) => {
174174
try {
175175
if (builtImage.platforms.length <= 1) {
176-
await tagSinglePlatformImage(builtImage, imagesWithTags);
176+
// For single-platform builds pushed with tags (not by digest),
177+
// the image reference already includes the tag, so we just need to update the images array
178+
const sourceImage = builtImage.images[0];
179+
const isPushedByDigest = sourceImage.match(/@sha256:[a-f0-9]{64}$/) && !sourceImage.match(/:[^:@]+@sha256:/);
180+
181+
if (isPushedByDigest) {
182+
// Image was pushed by digest only, need to tag it
183+
await tagSinglePlatformImage(builtImage, imagesWithTags);
184+
} else {
185+
// Image was already pushed with tags, just ensure images array is updated
186+
core.info(`Single-platform image "${builtImage.name}" was already pushed with tags`);
187+
builtImage.images = imagesWithTags;
188+
}
177189
} else {
178190
await createMultiarchManifest(builtImage, imagesWithTags);
179191
}

0 commit comments

Comments
 (0)