@@ -144,75 +144,61 @@ export async function runMain(): Promise<void> {
144
144
const digestsObj : Record < string , string > = { } ;
145
145
146
146
if ( platform ) {
147
+ // Extract digest from OCI tarball
147
148
for ( const tag of imageTagArray ) {
148
149
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 }
157
155
) ;
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
194
164
}
195
- } else {
196
- core . warning ( `Failed to inspect image : ${ inspectCmd . stderr } ` ) ;
165
+ } catch ( error ) {
166
+ core . warning ( `Failed to parse OCI archive info : ${ error } ` ) ;
197
167
}
168
+ } else {
169
+ core . warning ( `Failed to inspect OCI tarball for ${ tag } ` ) ;
198
170
}
199
171
}
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
+ }
200
179
} 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
202
181
const inspectCmd = await exec (
203
182
'docker' ,
204
183
[
205
184
'inspect' ,
206
185
`${ imageName } :${ imageTagArray [ 0 ] } ` ,
207
186
'--format' ,
208
- '{{.Id }}' ,
187
+ '{{index .RepoDigests 0 }}' ,
209
188
] ,
210
189
{ silent : true } ,
211
190
) ;
212
191
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 ( / s h a 2 5 6 : [ a - f 0 - 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 } ` ) ;
216
202
}
217
203
}
218
204
@@ -352,7 +338,6 @@ export async function runPost(): Promise<void> {
352
338
if ( platform ) {
353
339
// Create a digests object to track digests for each platform
354
340
const digestsObj : Record < string , string > = { } ;
355
- const platforms = platform . split ( / \s * , \s * / ) ;
356
341
357
342
for ( const tag of imageTagArray ) {
358
343
core . info ( `Copying multiplatform image '${ imageName } :${ tag } '...` ) ;
@@ -361,42 +346,54 @@ export async function runPost(): Promise<void> {
361
346
362
347
await copyImage ( true , imageSource , imageDest ) ;
363
348
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' ,
367
353
[
368
- 'buildx' ,
369
- 'imagetools' ,
370
354
'inspect' ,
371
- `${ imageName } :${ tag } ` ,
372
- '--format' ,
373
- '{{json .}}' ,
355
+ imageSource ,
374
356
] ,
375
357
{ silent : true } ,
376
358
) ;
377
- if ( inspectCmd . exitCode === 0 ) {
359
+
360
+ if ( inspectOCICmd . exitCode === 0 ) {
378
361
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 ;
397
366
}
398
367
} 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
+ }
400
397
}
401
398
}
402
399
}
@@ -445,3 +442,5 @@ function emptyStringAsUndefined(value: string): string | undefined {
445
442
}
446
443
return value ;
447
444
}
445
+
446
+
0 commit comments