@@ -35,6 +35,7 @@ import {limaYamlData, dockerServiceLogsPs1, setupDockerWinPs1} from './assets';
3535import { GitHubRelease } from '../types/github' ;
3636import { HubRepository } from '../hubRepository' ;
3737import { Image } from '../types/oci/config' ;
38+ import { exec } from "child_process" ;
3839
3940export interface InstallSourceImage {
4041 type : 'image' ;
@@ -56,6 +57,7 @@ export interface InstallOpts {
5657 runDir : string ;
5758 contextName ?: string ;
5859 daemonConfig ?: string ;
60+ rootless ?: boolean ;
5961}
6062
6163interface LimaImage {
@@ -65,19 +67,21 @@ interface LimaImage {
6567}
6668
6769export class Install {
68- private readonly runDir : string ;
70+ private runDir : string ;
6971 private readonly source : InstallSource ;
7072 private readonly contextName : string ;
7173 private readonly daemonConfig ?: string ;
7274 private _version : string | undefined ;
7375 private _toolDir : string | undefined ;
76+ private rootless : boolean ;
7477
7578 private gitCommit : string | undefined ;
7679
7780 private readonly limaInstanceName = 'docker-actions-toolkit' ;
7881
7982 constructor ( opts : InstallOpts ) {
8083 this . runDir = opts . runDir ;
84+ this . rootless = opts . rootless || false ;
8185 this . source = opts . source || {
8286 type : 'archive' ,
8387 version : 'latest' ,
@@ -195,7 +199,13 @@ export class Install {
195199 if ( ! this . runDir ) {
196200 throw new Error ( 'runDir must be set' ) ;
197201 }
198- switch ( os . platform ( ) ) {
202+
203+ const platform = os . platform ( ) ;
204+ if ( this . rootless && platform != 'linux' ) {
205+ // TODO: Support on macOS (via lima)
206+ throw new Error ( `rootless is only supported on linux` ) ;
207+ }
208+ switch ( platform ) {
199209 case 'darwin' : {
200210 return await this . installDarwin ( ) ;
201211 }
@@ -312,6 +322,9 @@ export class Install {
312322 }
313323
314324 private async installLinux ( ) : Promise < string > {
325+ if ( this . rootless ) {
326+ this . runDir = os . homedir ( ) + '/' + this . runDir ;
327+ }
315328 const dockerHost = `unix://${ path . join ( this . runDir , 'docker.sock' ) } ` ;
316329 await io . mkdirP ( this . runDir ) ;
317330
@@ -339,15 +352,27 @@ export class Install {
339352 }
340353
341354 const envs = Object . assign ( { } , process . env , {
342- PATH : `${ this . toolDir } :${ process . env . PATH } `
355+ PATH : `${ this . toolDir } :${ process . env . PATH } ` ,
356+ XDG_RUNTIME_DIR : this . runDir
343357 } ) as {
344358 [ key : string ] : string ;
345359 } ;
346360
347361 await core . group ( 'Start Docker daemon' , async ( ) => {
348362 const bashPath : string = await io . which ( 'bash' , true ) ;
349- const cmd = `${ this . toolDir } /dockerd --host="${ dockerHost } " --config-file="${ daemonConfigPath } " --exec-root="${ this . runDir } /execroot" --data-root="${ this . runDir } /data" --pidfile="${ this . runDir } /docker.pid" --userland-proxy=false` ;
363+ let dockerPath = `${ this . toolDir } /dockerd` ;
364+ if ( this . rootless ) {
365+ dockerPath = `${ this . toolDir } /dockerd-rootless.sh` ;
366+ if ( fs . existsSync ( '/proc/sys/kernel/apparmor_restrict_unprivileged_userns' ) ) {
367+ await Exec . exec ( 'sudo' , [ 'sh' , '-c' , 'echo 0 > /proc/sys/kernel/apparmor_restrict_unprivileged_userns' ] ) ;
368+ }
369+ }
370+
371+ let cmd = `${ dockerPath } --host="${ dockerHost } " --config-file="${ daemonConfigPath } " --exec-root="${ this . runDir } /execroot" --data-root="${ this . runDir } /data" --pidfile="${ this . runDir } /docker.pid"` ;
350372 core . info ( `[command] ${ cmd } ` ) ; // https://github.com/actions/toolkit/blob/3d652d3133965f63309e4b2e1c8852cdbdcb3833/packages/exec/src/toolrunner.ts#L47
373+ if ( this . rootless ) {
374+ cmd = `sudo -u #1000` ;
375+ }
351376 const proc = await child_process . spawn (
352377 // We can't use Exec.exec here because we need to detach the process to
353378 // avoid killing it when the action finishes running. Even if detached,
@@ -359,19 +384,23 @@ EOF`,
359384 [ ] ,
360385 {
361386 env : envs ,
362- detached : true ,
387+ // detached: true,
363388 shell : true ,
364389 stdio : [ 'ignore' , process . stdout , process . stderr ]
365390 }
366391 ) ;
392+ core . info ( `unref` ) ;
367393 proc . unref ( ) ;
368- await Util . sleep ( 3 ) ;
394+
395+ core . info ( `sleep` ) ;
396+ await Util . sleep ( 5 ) ;
369397 const retries = 10 ;
398+ core . info ( `Waiting for Docker daemon to start (up to ${ retries } retries)...` ) ;
370399 await retry (
371400 async bail => {
372401 try {
373402 await Exec . getExecOutput ( `docker version` , undefined , {
374- silent : true ,
403+ silent : false ,
375404 env : Object . assign ( { } , envs , {
376405 DOCKER_HOST : dockerHost ,
377406 DOCKER_CONTENT_TRUST : 'false'
@@ -380,6 +409,7 @@ EOF`,
380409 }
381410 } ) ;
382411 } catch ( e ) {
412+ core . error ( e ) ;
383413 bail ( e ) ;
384414 }
385415 } ,
0 commit comments