Skip to content

Commit baeaceb

Browse files
committed
fix: read image digest from oci tarball
1 parent 5081620 commit baeaceb

File tree

1 file changed

+79
-80
lines changed

1 file changed

+79
-80
lines changed

github-action/src/main.ts

Lines changed: 79 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -144,75 +144,61 @@ export async function runMain(): Promise<void> {
144144
const digestsObj: Record<string, string> = {};
145145

146146
if (platform) {
147+
// Extract digest from OCI tarball
147148
for (const tag of imageTagArray) {
148149
const imageSource = `oci-archive:/tmp/output.tar:${tag}`;
149-
const imageDest = `docker://${imageName}:${tag}`;
150-
await copyImage(true, imageSource, imageDest);
151-
}
152-
153-
// Extract the image digest from the build output
154-
if (buildResult.imageDigests) {
155-
core.info(
156-
`Image digest for ${platform}: ${buildResult.imageDigests}`,
150+
151+
const inspectOCICmd = await exec(
152+
'skopeo',
153+
['inspect', imageSource],
154+
{silent: true}
157155
);
158-
digestsObj[platform] = buildResult.imageDigests[platform];
159-
} else {
160-
// If buildResult doesn't have imageDigest, try to get it from the built image
161-
if (imageName) {
162-
// sleep for 5 seconds
163-
await new Promise(resolve => setTimeout(resolve, 5000));
164-
// list images
165-
const listCmd = await exec(
166-
'docker',
167-
['images', '--format', '{{.Repository}}:{{.Tag}}'],
168-
{silent: true},
169-
);
170-
core.info(`Images: ${listCmd.stdout}`);
171-
// get the digest of the image
172-
const inspectCmd = await exec(
173-
'docker',
174-
[
175-
'buildx',
176-
'imagetools',
177-
'inspect',
178-
`${imageName}:${imageTagArray[0]}`,
179-
'--format',
180-
'{{json .}}',
181-
],
182-
{silent: true},
183-
);
184-
if (inspectCmd.exitCode === 0) {
185-
try {
186-
const imageInfo = JSON.parse(inspectCmd.stdout);
187-
if (imageInfo.manifest && imageInfo.manifest.digest) {
188-
const digest = imageInfo.manifest.digest;
189-
core.info(`Image digest for ${platform}: ${digest}`);
190-
digestsObj[platform] = digest;
191-
}
192-
} catch (error) {
193-
core.warning(`Failed to parse image digest: ${error.message}`);
156+
157+
if (inspectOCICmd.exitCode === 0) {
158+
try {
159+
const imageInfo = JSON.parse(inspectOCICmd.stdout);
160+
if (imageInfo.Digest) {
161+
core.info(`Image digest for ${platform}: ${imageInfo.Digest}`);
162+
digestsObj[platform] = imageInfo.Digest;
163+
break; // Found digest, stop looking
194164
}
195-
} else {
196-
core.warning(`Failed to inspect image: ${inspectCmd.stderr}`);
165+
} catch (error) {
166+
core.warning(`Failed to parse OCI archive info: ${error}`);
197167
}
168+
} else {
169+
core.warning(`Failed to inspect OCI tarball for ${tag}`);
198170
}
199171
}
172+
173+
// Copy image to registry AFTER extracting digest locally
174+
for (const tag of imageTagArray) {
175+
const imageSource = `oci-archive:/tmp/output.tar:${tag}`;
176+
const imageDest = `docker://${imageName}:${tag}`;
177+
await copyImage(true, imageSource, imageDest);
178+
}
200179
} else if (imageName) {
201-
// For non-platform specific builds, still try to get the digest
180+
// For non-platform specific builds, use local docker inspect
202181
const inspectCmd = await exec(
203182
'docker',
204183
[
205184
'inspect',
206185
`${imageName}:${imageTagArray[0]}`,
207186
'--format',
208-
'{{.Id}}',
187+
'{{index .RepoDigests 0}}',
209188
],
210189
{silent: true},
211190
);
212191
if (inspectCmd.exitCode === 0) {
213-
const digest = inspectCmd.stdout.trim();
214-
core.info(`Image digest: ${digest}`);
215-
digestsObj['default'] = digest;
192+
const digestLine = inspectCmd.stdout.trim();
193+
// Extract just the digest part (sha256:...)
194+
const digestMatch = digestLine.match(/sha256:[a-f0-9]+/);
195+
if (digestMatch) {
196+
const digest = digestMatch[0];
197+
core.info(`Image digest: ${digest}`);
198+
digestsObj['default'] = digest;
199+
}
200+
} else {
201+
core.warning(`Failed to get image digest: ${inspectCmd.stderr}`);
216202
}
217203
}
218204

@@ -352,7 +338,6 @@ export async function runPost(): Promise<void> {
352338
if (platform) {
353339
// Create a digests object to track digests for each platform
354340
const digestsObj: Record<string, string> = {};
355-
const platforms = platform.split(/\s*,\s*/);
356341

357342
for (const tag of imageTagArray) {
358343
core.info(`Copying multiplatform image '${imageName}:${tag}'...`);
@@ -361,42 +346,54 @@ export async function runPost(): Promise<void> {
361346

362347
await copyImage(true, imageSource, imageDest);
363348

364-
// After pushing, get and set digest
365-
const inspectCmd = await exec(
366-
'docker',
349+
// Try to inspect local OCI archive first to get the correct digest
350+
// This avoids race conditions with parallel multi-platform builds
351+
const inspectOCICmd = await exec(
352+
'skopeo',
367353
[
368-
'buildx',
369-
'imagetools',
370354
'inspect',
371-
`${imageName}:${tag}`,
372-
'--format',
373-
'{{json .}}',
355+
imageSource,
374356
],
375357
{silent: true},
376358
);
377-
if (inspectCmd.exitCode === 0) {
359+
360+
if (inspectOCICmd.exitCode === 0) {
378361
try {
379-
const imageInfo = JSON.parse(inspectCmd.stdout);
380-
381-
// If it's a manifest list, extract digests for each platform
382-
if (imageInfo.manifests) {
383-
for (const manifest of imageInfo.manifests) {
384-
if (manifest.platform && manifest.digest) {
385-
const platformStr = `${manifest.platform.os}/${manifest.platform.architecture}${manifest.platform.variant ? `/${manifest.platform.variant}` : ''}`;
386-
core.info(
387-
`Image digest for ${imageName}:${tag} (${platformStr}): ${manifest.digest}`,
388-
);
389-
digestsObj[platformStr] = manifest.digest;
390-
}
391-
}
392-
} else if (imageInfo.manifest && imageInfo.manifest.digest) {
393-
// Single platform image
394-
const digest = imageInfo.manifest.digest;
395-
core.info(`Image digest for ${imageName}:${tag}: ${digest}`);
396-
digestsObj[platforms[0] || 'default'] = digest;
362+
const imageInfo = JSON.parse(inspectOCICmd.stdout);
363+
if (imageInfo.Digest) {
364+
core.info(`Image digest for ${imageName}:${tag} (${platform}): ${imageInfo.Digest}`);
365+
digestsObj[platform] = imageInfo.Digest;
397366
}
398367
} catch (error) {
399-
core.warning(`Failed to parse image digest: ${error.message}`);
368+
core.warning(`Failed to parse local OCI image info: ${error.message}`);
369+
}
370+
} else {
371+
// Fallback to registry inspection (with race condition risk)
372+
core.info('Local OCI inspection failed, trying registry...');
373+
const inspectCmd = await exec(
374+
'docker',
375+
[
376+
'buildx',
377+
'imagetools',
378+
'inspect',
379+
`${imageName}:${tag}`,
380+
'--format',
381+
'{{json .}}',
382+
],
383+
{silent: true},
384+
);
385+
if (inspectCmd.exitCode === 0) {
386+
try {
387+
const imageInfo = JSON.parse(inspectCmd.stdout);
388+
if (imageInfo.manifest && imageInfo.manifest.digest) {
389+
// Single platform image
390+
const digest = imageInfo.manifest.digest;
391+
core.info(`Image digest for ${imageName}:${tag}: ${digest}`);
392+
digestsObj[platform] = digest;
393+
}
394+
} catch (error) {
395+
core.warning(`Failed to parse registry image info: ${error.message}`);
396+
}
400397
}
401398
}
402399
}
@@ -445,3 +442,5 @@ function emptyStringAsUndefined(value: string): string | undefined {
445442
}
446443
return value;
447444
}
445+
446+

0 commit comments

Comments
 (0)