@@ -20,7 +20,10 @@ import path from 'path';
2020import * as core from '@actions/core' ;
2121import * as io from '@actions/io' ;
2222
23+ import { Context } from '../context' ;
24+ import { Cache } from '../cache' ;
2325import { Exec } from '../exec' ;
26+ import { Util } from '../util' ;
2427
2528import { ConfigFile } from '../types/docker' ;
2629
@@ -73,4 +76,78 @@ export class Docker {
7376 public static async printInfo ( ) : Promise < void > {
7477 await Exec . exec ( 'docker' , [ 'info' ] ) ;
7578 }
79+
80+ public static parseRepoTag ( image : string ) : { repository : string ; tag : string } {
81+ let sepPos : number ;
82+ const digestPos = image . indexOf ( '@' ) ;
83+ const colonPos = image . lastIndexOf ( ':' ) ;
84+ if ( digestPos >= 0 ) {
85+ // priority on digest
86+ sepPos = digestPos ;
87+ } else if ( colonPos >= 0 ) {
88+ sepPos = colonPos ;
89+ } else {
90+ return {
91+ repository : image ,
92+ tag : 'latest'
93+ } ;
94+ }
95+ const tag = image . slice ( sepPos + 1 ) ;
96+ if ( tag . indexOf ( '/' ) === - 1 ) {
97+ return {
98+ repository : image . slice ( 0 , sepPos ) ,
99+ tag : tag
100+ } ;
101+ }
102+ return {
103+ repository : image ,
104+ tag : 'latest'
105+ } ;
106+ }
107+
108+ public static async pull ( image : string , cache ?: boolean ) : Promise < void > {
109+ const parsedImage = Docker . parseRepoTag ( image ) ;
110+ const repoSanitized = parsedImage . repository . replace ( / [ ^ a - z A - Z 0 - 9 . ] + / g, '--' ) ;
111+ const tagSanitized = parsedImage . tag . replace ( / [ ^ a - z A - Z 0 - 9 . ] + / g, '--' ) ;
112+
113+ const imageCache = new Cache ( {
114+ htcName : repoSanitized ,
115+ htcVersion : tagSanitized ,
116+ baseCacheDir : path . join ( Docker . configDir , '.cache' , 'images' , repoSanitized ) ,
117+ cacheFile : 'image.tar'
118+ } ) ;
119+
120+ let cacheFoundPath : string | undefined ;
121+ if ( cache ) {
122+ cacheFoundPath = await imageCache . find ( ) ;
123+ if ( cacheFoundPath ) {
124+ core . info ( `Image found from cache in ${ cacheFoundPath } ` ) ;
125+ await Exec . getExecOutput ( `docker` , [ 'load' , '-i' , cacheFoundPath ] ) . catch ( e => {
126+ core . warning ( `Failed to load image from cache: ${ e } ` ) ;
127+ } ) ;
128+ }
129+ }
130+
131+ let pulled = true ;
132+ await Exec . getExecOutput ( `docker` , [ 'pull' , image ] ) . catch ( e => {
133+ pulled = false ;
134+ if ( cacheFoundPath ) {
135+ core . warning ( `Failed to pull image, using one from cache: ${ e } ` ) ;
136+ } else {
137+ throw new Error ( e ) ;
138+ }
139+ } ) ;
140+
141+ if ( cache && pulled ) {
142+ const imageTarPath = path . join ( Context . tmpDir ( ) , `${ Util . hash ( image ) } .tar` ) ;
143+ await Exec . getExecOutput ( `docker` , [ 'save' , '-o' , imageTarPath , image ] )
144+ . then ( async ( ) => {
145+ const cachePath = await imageCache . save ( imageTarPath ) ;
146+ core . info ( `Image cached to ${ cachePath } ` ) ;
147+ } )
148+ . catch ( e => {
149+ core . warning ( `Failed to save image: ${ e } ` ) ;
150+ } ) ;
151+ }
152+ }
76153}
0 commit comments