@@ -28,11 +28,13 @@ import * as tc from '@actions/tool-cache';
2828
2929import { Context } from '../context' ;
3030import { Docker } from './docker' ;
31+ import { Regctl } from '../regclient/regctl' ;
32+ import { Undock } from '../undock/undock' ;
3133import { Exec } from '../exec' ;
3234import { Util } from '../util' ;
3335import { limaYamlData , dockerServiceLogsPs1 , setupDockerWinPs1 } from './assets' ;
36+
3437import { GitHubRelease } from '../types/github' ;
35- import { HubRepository } from '../hubRepository' ;
3638import { Image } from '../types/oci/config' ;
3739
3840export interface InstallSourceImage {
@@ -57,6 +59,9 @@ export interface InstallOpts {
5759 daemonConfig ?: string ;
5860 rootless ?: boolean ;
5961 localTCPPort ?: number ;
62+
63+ regctl : Regctl ;
64+ undock : Undock ;
6065}
6166
6267interface LimaImage {
@@ -72,6 +77,8 @@ export class Install {
7277 private readonly daemonConfig ?: string ;
7378 private readonly rootless : boolean ;
7479 private readonly localTCPPort ?: number ;
80+ private readonly regctl : Regctl ;
81+ private readonly undock : Undock ;
7582
7683 private _version : string | undefined ;
7784 private _toolDir : string | undefined ;
@@ -91,76 +98,24 @@ export class Install {
9198 this . daemonConfig = opts . daemonConfig ;
9299 this . rootless = opts . rootless || false ;
93100 this . localTCPPort = opts . localTCPPort ;
101+ this . regctl = opts . regctl ;
102+ this . undock = opts . undock ;
94103 }
95104
96105 get toolDir ( ) : string {
97106 return this . _toolDir || Context . tmpDir ( ) ;
98107 }
99108
100- async downloadStaticArchive ( component : 'docker' | 'docker-rootless-extras' , src : InstallSourceArchive ) : Promise < string > {
101- const release : GitHubRelease = await Install . getRelease ( src . version ) ;
102- this . _version = release . tag_name . replace ( / ^ v + | v + $ / g, '' ) ;
103- core . debug ( `docker.Install.download version: ${ this . _version } ` ) ;
104-
105- const downloadURL = this . downloadURL ( component , this . _version , src . channel ) ;
106- core . info ( `Downloading ${ downloadURL } ` ) ;
107-
108- const downloadPath = await tc . downloadTool ( downloadURL ) ;
109- core . debug ( `docker.Install.download downloadPath: ${ downloadPath } ` ) ;
110-
111- let extractFolder ;
112- if ( os . platform ( ) == 'win32' ) {
113- extractFolder = await tc . extractZip ( downloadPath , extractFolder ) ;
114- } else {
115- extractFolder = await tc . extractTar ( downloadPath , extractFolder ) ;
116- }
117- if ( Util . isDirectory ( path . join ( extractFolder , component ) ) ) {
118- extractFolder = path . join ( extractFolder , component ) ;
119- }
120- core . debug ( `docker.Install.download extractFolder: ${ extractFolder } ` ) ;
121- return extractFolder ;
122- }
123-
124109 public async download ( ) : Promise < string > {
125110 let extractFolder : string ;
126111 let cacheKey : string ;
127112 const platform = os . platform ( ) ;
128113
129114 switch ( this . source . type ) {
130115 case 'image' : {
131- const tag = this . source . tag ;
132- this . _version = tag ;
116+ this . _version = this . source . tag ;
133117 cacheKey = `docker-image` ;
134-
135- core . info ( `Downloading docker cli from dockereng/cli-bin:${ tag } ` ) ;
136- const cli = await HubRepository . build ( 'dockereng/cli-bin' ) ;
137- extractFolder = await cli . extractImage ( tag ) ;
138-
139- const moby = await HubRepository . build ( 'moby/moby-bin' ) ;
140- if ( [ 'win32' , 'linux' ] . includes ( platform ) ) {
141- core . info ( `Downloading dockerd from moby/moby-bin:${ tag } ` ) ;
142- await moby . extractImage ( tag , extractFolder ) ;
143- } else if ( platform == 'darwin' ) {
144- // On macOS, the docker daemon binary will be downloaded inside the lima VM.
145- // However, we will get the exact git revision from the image config
146- // to get the matching systemd unit files.
147- core . info ( `Getting git revision from moby/moby-bin:${ tag } ` ) ;
148-
149- // There's no macOS image for moby/moby-bin - a linux daemon is run inside lima.
150- const manifest = await moby . getPlatformManifest ( tag , 'linux' ) ;
151-
152- const config = await moby . getJSONBlob < Image > ( manifest . config . digest ) ;
153- core . debug ( `Config ${ JSON . stringify ( config . config ) } ` ) ;
154-
155- this . gitCommit = config . config ?. Labels ?. [ 'org.opencontainers.image.revision' ] ;
156- if ( ! this . gitCommit ) {
157- core . warning ( `No git revision can be determined from the image. Will use master.` ) ;
158- this . gitCommit = 'master' ;
159- }
160- core . info ( `Git revision is ${ this . gitCommit } ` ) ;
161- } else {
162- core . warning ( `dockerd not supported on ${ platform } , only the Docker cli will be available` ) ;
163- }
118+ extractFolder = await this . downloadSourceImage ( platform ) ;
164119 break ;
165120 }
166121 case 'archive' : {
@@ -170,10 +125,10 @@ export class Install {
170125 this . _version = version ;
171126
172127 core . info ( `Downloading Docker ${ version } from ${ this . source . channel } at download.docker.com` ) ;
173- extractFolder = await this . downloadStaticArchive ( 'docker' , this . source ) ;
128+ extractFolder = await this . downloadSourceArchive ( 'docker' , this . source ) ;
174129 if ( this . rootless ) {
175130 core . info ( `Downloading Docker rootless extras ${ version } from ${ this . source . channel } at download.docker.com` ) ;
176- const extrasFolder = await this . downloadStaticArchive ( 'docker-rootless-extras' , this . source ) ;
131+ const extrasFolder = await this . downloadSourceArchive ( 'docker-rootless-extras' , this . source ) ;
177132 fs . readdirSync ( extrasFolder ) . forEach ( file => {
178133 const src = path . join ( extrasFolder , file ) ;
179134 const dest = path . join ( extractFolder , file ) ;
@@ -191,7 +146,9 @@ export class Install {
191146 }
192147 // eslint-disable-next-line @typescript-eslint/no-unused-vars
193148 files . forEach ( function ( file , index ) {
194- fs . chmodSync ( path . join ( extractFolder , file ) , '0755' ) ;
149+ if ( ! Util . isDirectory ( path . join ( extractFolder , file ) ) ) {
150+ fs . chmodSync ( path . join ( extractFolder , file ) , '0755' ) ;
151+ }
195152 } ) ;
196153 } ) ;
197154
@@ -203,6 +160,72 @@ export class Install {
203160 return tooldir ;
204161 }
205162
163+ private async downloadSourceImage ( platform : string ) : Promise < string > {
164+ const dest = path . join ( Context . tmpDir ( ) , 'docker-install-image' ) ;
165+ const cliImage = `dockereng/cli-bin:${ this . _version } ` ;
166+ const engineImage = `moby/moby-bin:${ this . _version } ` ;
167+
168+ core . info ( `Downloading Docker CLI from ${ cliImage } ` ) ;
169+ await this . undock . run ( {
170+ source : cliImage ,
171+ dist : dest
172+ } ) ;
173+
174+ if ( [ 'win32' , 'linux' ] . includes ( platform ) ) {
175+ core . info ( `Downloading Docker engine from ${ engineImage } ` ) ;
176+ await this . undock . run ( {
177+ source : engineImage ,
178+ dist : dest
179+ } ) ;
180+ } else if ( platform == 'darwin' ) {
181+ // On macOS, the docker daemon binary will be downloaded inside the lima VM.
182+ // However, we will get the exact git revision from the image config
183+ // to get the matching systemd unit files. There's no macOS image for
184+ // moby/moby-bin - a linux daemon is run inside lima.
185+ try {
186+ const engineImageConfig = await this . imageConfig ( engineImage , 'linux/arm64' ) ;
187+ core . debug ( `docker.Install.downloadSourceImage engineImageConfig: ${ JSON . stringify ( engineImageConfig ) } ` ) ;
188+ this . gitCommit = engineImageConfig . config ?. Labels ?. [ 'org.opencontainers.image.revision' ] ;
189+ if ( ! this . gitCommit ) {
190+ throw new Error ( `No git revision can be determined from the image` ) ;
191+ }
192+ } catch ( e ) {
193+ core . warning ( e ) ;
194+ this . gitCommit = 'master' ;
195+ }
196+
197+ core . debug ( `docker.Install.downloadSourceImage gitCommit: ${ this . gitCommit } ` ) ;
198+ } else {
199+ core . warning ( `Docker engine not supported on ${ platform } , only the Docker cli will be available` ) ;
200+ }
201+
202+ return dest ;
203+ }
204+
205+ private async downloadSourceArchive ( component : 'docker' | 'docker-rootless-extras' , src : InstallSourceArchive ) : Promise < string > {
206+ const release : GitHubRelease = await Install . getRelease ( src . version ) ;
207+ this . _version = release . tag_name . replace ( / ^ v + | v + $ / g, '' ) ;
208+ core . debug ( `docker.Install.downloadSourceArchive version: ${ this . _version } ` ) ;
209+
210+ const downloadURL = this . downloadURL ( component , this . _version , src . channel ) ;
211+ core . info ( `Downloading ${ downloadURL } ` ) ;
212+
213+ const downloadPath = await tc . downloadTool ( downloadURL ) ;
214+ core . debug ( `docker.Install.downloadSourceArchive downloadPath: ${ downloadPath } ` ) ;
215+
216+ let extractFolder ;
217+ if ( os . platform ( ) == 'win32' ) {
218+ extractFolder = await tc . extractZip ( downloadPath , extractFolder ) ;
219+ } else {
220+ extractFolder = await tc . extractTar ( downloadPath , extractFolder ) ;
221+ }
222+ if ( Util . isDirectory ( path . join ( extractFolder , component ) ) ) {
223+ extractFolder = path . join ( extractFolder , component ) ;
224+ }
225+ core . debug ( `docker.Install.downloadSourceArchive extractFolder: ${ extractFolder } ` ) ;
226+ return extractFolder ;
227+ }
228+
206229 public async install ( ) : Promise < string > {
207230 if ( ! this . toolDir ) {
208231 throw new Error ( 'toolDir must be set. Run download first.' ) ;
@@ -709,4 +732,20 @@ EOF`,
709732 }
710733 return res ;
711734 }
735+
736+ private async imageConfig ( image : string , platform ?: string ) : Promise < Image > {
737+ const manifest = await this . regctl . manifestGet ( {
738+ image : image ,
739+ platform : platform
740+ } ) ;
741+ const configDigest = manifest ?. config ?. digest ;
742+ if ( ! configDigest ) {
743+ throw new Error ( `No config digest found for image ${ image } ` ) ;
744+ }
745+ const blob = await this . regctl . blobGet ( {
746+ repository : image ,
747+ digest : configDigest
748+ } ) ;
749+ return < Image > JSON . parse ( blob ) ;
750+ }
712751}
0 commit comments