@@ -20,7 +20,10 @@ import path from 'path';
20
20
import * as core from '@actions/core' ;
21
21
import * as io from '@actions/io' ;
22
22
23
+ import { Context } from '../context' ;
24
+ import { Cache } from '../cache' ;
23
25
import { Exec } from '../exec' ;
26
+ import { Util } from '../util' ;
24
27
25
28
import { ConfigFile } from '../types/docker' ;
26
29
@@ -73,4 +76,89 @@ export class Docker {
73
76
public static async printInfo ( ) : Promise < void > {
74
77
await Exec . exec ( 'docker' , [ 'info' ] ) ;
75
78
}
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 ] , {
126
+ ignoreReturnCode : true
127
+ } ) . then ( res => {
128
+ if ( res . stderr . length > 0 && res . exitCode != 0 ) {
129
+ core . warning ( `Failed to load image from cache: ${ res . stderr . match ( / ( .* ) \s * $ / ) ?. [ 0 ] ?. trim ( ) ?? 'unknown error' } ` ) ;
130
+ }
131
+ } ) ;
132
+ }
133
+ }
134
+
135
+ let pulled = true ;
136
+ await Exec . getExecOutput ( `docker` , [ 'pull' , image ] , {
137
+ ignoreReturnCode : true
138
+ } ) . then ( res => {
139
+ pulled = false ;
140
+ if ( res . stderr . length > 0 && res . exitCode != 0 ) {
141
+ const err = res . stderr . match ( / ( .* ) \s * $ / ) ?. [ 0 ] ?. trim ( ) ?? 'unknown error' ;
142
+ if ( cacheFoundPath ) {
143
+ core . warning ( `Failed to pull image, using one from cache: ${ err } ` ) ;
144
+ } else {
145
+ throw new Error ( err ) ;
146
+ }
147
+ }
148
+ } ) ;
149
+
150
+ if ( cache && pulled ) {
151
+ const imageTarPath = path . join ( Context . tmpDir ( ) , `${ Util . hash ( image ) } .tar` ) ;
152
+ await Exec . getExecOutput ( `docker` , [ 'save' , '-o' , imageTarPath , image ] , {
153
+ ignoreReturnCode : true
154
+ } ) . then ( async res => {
155
+ if ( res . stderr . length > 0 && res . exitCode != 0 ) {
156
+ core . warning ( `Failed to save image: ${ res . stderr . match ( / ( .* ) \s * $ / ) ?. [ 0 ] ?. trim ( ) ?? 'unknown error' } ` ) ;
157
+ } else {
158
+ const cachePath = await imageCache . save ( imageTarPath ) ;
159
+ core . info ( `Image cached to ${ cachePath } ` ) ;
160
+ }
161
+ } ) ;
162
+ }
163
+ }
76
164
}
0 commit comments