99 DevContainerCliUpArgs ,
1010} from '../../common/src/dev-container-cli' ;
1111
12- import { isDockerBuildXInstalled , pushImage } from './docker' ;
12+ import { isDockerBuildXInstalled , pushImage , createManifest } from './docker' ;
1313import { isSkopeoInstalled , copyImage } from './skopeo' ;
1414import { populateDefaults } from '../../common/src/envvars' ;
1515
@@ -26,6 +26,14 @@ export async function runMain(): Promise<void> {
2626 try {
2727 core . info ( 'Starting...' ) ;
2828 core . saveState ( 'hasRunMain' , 'true' ) ;
29+
30+ const mergeTag = emptyStringAsUndefined ( core . getInput ( 'mergeTag' ) ) ;
31+ if ( mergeTag ) {
32+ core . info ( 'mergeTag is set - skipping build (manifest merge will run in post step)' ) ;
33+ core . saveState ( 'mergeTag' , mergeTag ) ;
34+ return ;
35+ }
36+
2937 const buildXInstalled = await isDockerBuildXInstalled ( ) ;
3038 if ( ! buildXInstalled ) {
3139 core . warning (
@@ -47,6 +55,7 @@ export async function runMain(): Promise<void> {
4755 const imageName = emptyStringAsUndefined ( core . getInput ( 'imageName' ) ) ;
4856 const imageTag = emptyStringAsUndefined ( core . getInput ( 'imageTag' ) ) ;
4957 const platform = emptyStringAsUndefined ( core . getInput ( 'platform' ) ) ;
58+ const platformTag = emptyStringAsUndefined ( core . getInput ( 'platformTag' ) ) ;
5059 const subFolder : string = core . getInput ( 'subFolder' ) ;
5160 const relativeConfigFile = emptyStringAsUndefined (
5261 core . getInput ( 'configFile' ) ,
@@ -64,7 +73,7 @@ export async function runMain(): Promise<void> {
6473 const userDataFolder : string = core . getInput ( 'userDataFolder' ) ;
6574 const mounts : string [ ] = core . getMultilineInput ( 'mounts' ) ;
6675
67- if ( platform ) {
76+ if ( platform && ! platformTag ) {
6877 const skopeoInstalled = await isSkopeoInstalled ( ) ;
6978 if ( ! skopeoInstalled ) {
7079 core . warning (
@@ -73,7 +82,11 @@ export async function runMain(): Promise<void> {
7382 return ;
7483 }
7584 }
76- const buildxOutput = platform ? 'type=oci,dest=/tmp/output.tar' : undefined ;
85+ const buildxOutput = platform && ! platformTag ? 'type=oci,dest=/tmp/output.tar' : undefined ;
86+
87+ if ( platformTag ) {
88+ core . saveState ( 'platformTag' , platformTag ) ;
89+ }
7790
7891 const log = ( message : string ) : void => core . info ( message ) ;
7992 const workspaceFolder = path . resolve ( checkoutPath , subFolder ) ;
@@ -84,23 +97,21 @@ export async function runMain(): Promise<void> {
8497 const imageTagArray = resolvedImageTag . split ( / \s * , \s * / ) ;
8598 const fullImageNameArray : string [ ] = [ ] ;
8699 for ( const tag of imageTagArray ) {
87- fullImageNameArray . push ( `${ imageName } :${ tag } ` ) ;
100+ if ( platformTag ) {
101+ fullImageNameArray . push ( `${ imageName } :${ tag } -${ platformTag } ` ) ;
102+ } else {
103+ fullImageNameArray . push ( `${ imageName } :${ tag } ` ) ;
104+ }
88105 }
89106 if ( imageName ) {
90107 if ( fullImageNameArray . length === 1 ) {
91108 if ( ! noCache && ! cacheFrom . includes ( fullImageNameArray [ 0 ] ) ) {
92- // If the cacheFrom options don't include the fullImageName, add it here
93- // This ensures that when building a PR where the image specified in the action
94- // isn't included in devcontainer.json (or docker-compose.yml), the action still
95- // resolves a previous image for the tag as a layer cache (if pushed to a registry)
96-
97109 core . info (
98110 `Adding --cache-from ${ fullImageNameArray [ 0 ] } to build args` ,
99111 ) ;
100112 cacheFrom . splice ( 0 , 0 , fullImageNameArray [ 0 ] ) ;
101113 }
102114 } else {
103- // Don't automatically add --cache-from if multiple image tags are specified
104115 core . info (
105116 'Not adding --cache-from automatically since multiple image tags were supplied' ,
106117 ) ;
@@ -217,18 +228,37 @@ export async function runPost(): Promise<void> {
217228 const eventFilterForPush : string [ ] =
218229 core . getMultilineInput ( 'eventFilterForPush' ) ;
219230
220- // default to 'never' if not set and no imageName
231+ const mergeTag = emptyStringAsUndefined ( core . getState ( 'mergeTag' ) ) ;
232+ if ( mergeTag ) {
233+ if ( ! imageName ) {
234+ core . setFailed ( 'imageName is required for manifest merge' ) ;
235+ return ;
236+ }
237+ const imageTag =
238+ emptyStringAsUndefined ( core . getInput ( 'imageTag' ) ) ?? 'latest' ;
239+ const imageTagArray = imageTag . split ( / \s * , \s * / ) ;
240+ const platformTags = mergeTag . split ( / \s * , \s * / ) ;
241+ for ( const tag of imageTagArray ) {
242+ core . info ( `Creating multi-arch manifest for '${ imageName } :${ tag } '...` ) ;
243+ const success = await createManifest ( imageName , tag , platformTags ) ;
244+ if ( ! success ) {
245+ return ;
246+ }
247+ }
248+ return ;
249+ }
250+
251+ const platformTag = emptyStringAsUndefined ( core . getState ( 'platformTag' ) ) ;
252+
221253 if ( pushOption === 'never' || ( ! pushOption && ! imageName ) ) {
222254 core . info ( `Image push skipped because 'push' is set to '${ pushOption } '` ) ;
223255 return ;
224256 }
225257
226- // default to 'filter' if not set and imageName is set
227258 if ( pushOption === 'filter' || ( ! pushOption && imageName ) ) {
228- // https://docs.github.com/en/actions/reference/environment-variables#default-environment-variables
229259 const ref = process . env . GITHUB_REF ;
230260 if (
231- refFilterForPush . length !== 0 && // empty filter allows all
261+ refFilterForPush . length !== 0 &&
232262 ! refFilterForPush . some ( s => s === ref )
233263 ) {
234264 core . info (
@@ -238,7 +268,7 @@ export async function runPost(): Promise<void> {
238268 }
239269 const eventName = process . env . GITHUB_EVENT_NAME ;
240270 if (
241- eventFilterForPush . length !== 0 && // empty filter allows all
271+ eventFilterForPush . length !== 0 &&
242272 ! eventFilterForPush . some ( s => s === eventName )
243273 ) {
244274 core . info (
@@ -256,14 +286,18 @@ export async function runPost(): Promise<void> {
256286 const imageTagArray = imageTag . split ( / \s * , \s * / ) ;
257287 if ( ! imageName ) {
258288 if ( pushOption ) {
259- // pushOption was set (and not to "never") - give an error that imageName is required
260289 core . error ( 'imageName is required to push images' ) ;
261290 }
262291 return ;
263292 }
264293
265294 const platform = emptyStringAsUndefined ( core . getInput ( 'platform' ) ) ;
266- if ( platform ) {
295+ if ( platformTag ) {
296+ for ( const tag of imageTagArray ) {
297+ core . info ( `Pushing platform image '${ imageName } :${ tag } -${ platformTag } '...` ) ;
298+ await pushImage ( imageName , `${ tag } -${ platformTag } ` ) ;
299+ }
300+ } else if ( platform ) {
267301 for ( const tag of imageTagArray ) {
268302 core . info ( `Copying multiplatform image '${ imageName } :${ tag } '...` ) ;
269303 const imageSource = `oci-archive:/tmp/output.tar:${ tag } ` ;
0 commit comments