1
- import { default as dockerComposeV1 , default as v1 , v2 as dockerComposeV2 , v2 } from "docker-compose" ;
1
+ import { default as v1 , v2 } from "docker-compose" ;
2
2
import { log , pullLog } from "../../../common" ;
3
3
import { ComposeInfo } from "../types" ;
4
4
import { defaultComposeOptions } from "./default-compose-options" ;
5
5
import { ComposeDownOptions , ComposeOptions } from "./types" ;
6
6
7
7
export interface ComposeClient {
8
- info : ComposeInfo ;
8
+ getInfo ( ) : Promise < ComposeInfo > ;
9
9
up ( options : ComposeOptions , services ?: Array < string > ) : Promise < void > ;
10
10
pull ( options : ComposeOptions , services ?: Array < string > ) : Promise < void > ;
11
11
stop ( options : ComposeOptions ) : Promise < void > ;
12
12
down ( options : ComposeOptions , downOptions : ComposeDownOptions ) : Promise < void > ;
13
13
}
14
14
15
- export async function getComposeClient ( environment : NodeJS . ProcessEnv ) : Promise < ComposeClient > {
16
- const info = await getComposeInfo ( ) ;
17
-
18
- switch ( info ?. compatability ) {
19
- case undefined :
20
- return new MissingComposeClient ( ) ;
21
- case "v1" :
22
- return new ComposeV1Client ( info , environment ) ;
23
- case "v2" :
24
- return new ComposeV2Client ( info , environment ) ;
25
- }
15
+ export function getComposeClient ( environment : NodeJS . ProcessEnv ) : ComposeClient {
16
+ return new LazyComposeClient ( environment ) ;
26
17
}
27
18
28
- async function getComposeInfo ( ) : Promise < ComposeInfo | undefined > {
29
- try {
30
- return {
31
- version : ( await dockerComposeV2 . version ( ) ) . data . version ,
32
- compatability : "v2" ,
33
- } ;
34
- } catch ( err ) {
19
+ class LazyComposeClient implements ComposeClient {
20
+ private info : ComposeInfo | undefined = undefined ;
21
+ private client : typeof v1 | typeof v2 | undefined = undefined ;
22
+ constructor ( private readonly environment : NodeJS . ProcessEnv ) { }
23
+
24
+ async getInfo ( ) : Promise < ComposeInfo | undefined > {
25
+ if ( this . info !== undefined ) {
26
+ return this . info ;
27
+ }
28
+
35
29
try {
36
- return {
37
- version : ( await dockerComposeV1 . version ( ) ) . data . version ,
38
- compatability : "v1 " ,
30
+ this . info = {
31
+ version : ( await v2 . version ( ) ) . data . version ,
32
+ compatibility : "v2 " ,
39
33
} ;
40
- } catch {
41
- return undefined ;
34
+ } catch ( err ) {
35
+ try {
36
+ this . info = {
37
+ version : ( await v1 . version ( ) ) . data . version ,
38
+ compatibility : "v1" ,
39
+ } ;
40
+ } catch {
41
+ return undefined ;
42
+ }
42
43
}
44
+
45
+ return this . info ;
43
46
}
44
- }
45
47
46
- class ComposeV1Client implements ComposeClient {
47
- constructor (
48
- public readonly info : ComposeInfo ,
49
- private readonly environment : NodeJS . ProcessEnv
50
- ) { }
48
+ private async getClient ( ) : Promise < typeof v1 | typeof v2 > {
49
+ if ( this . client !== undefined ) {
50
+ return this . client ;
51
+ }
51
52
52
- async up ( options : ComposeOptions , services : Array < string > | undefined ) : Promise < void > {
53
- try {
54
- if ( services ) {
55
- log . info ( `Upping Compose environment services ${ services . join ( ", " ) } ...` ) ;
56
- await v1 . upMany ( services , await defaultComposeOptions ( this . environment , options ) ) ;
57
- } else {
58
- log . info ( `Upping Compose environment...` ) ;
59
- await v1 . upAll ( await defaultComposeOptions ( this . environment , options ) ) ;
60
- }
61
- log . info ( `Upped Compose environment` ) ;
62
- } catch ( err ) {
63
- await handleAndRethrow ( err , async ( error : Error ) => {
64
- try {
65
- log . error ( `Failed to up Compose environment: ${ error . message } ` ) ;
66
- await this . down ( options , { removeVolumes : true , timeout : 0 } ) ;
67
- } catch {
68
- log . error ( `Failed to down Compose environment after failed up` ) ;
69
- }
70
- } ) ;
53
+ const info = await this . getInfo ( ) ;
54
+ switch ( info ?. compatibility ) {
55
+ case undefined :
56
+ throw new Error ( "Compose is not installed" ) ;
57
+ case "v1" :
58
+ this . client = v1 ;
59
+ return v1 ;
60
+ case "v2" :
61
+ this . client = v2 ;
62
+ return v2 ;
71
63
}
72
64
}
73
65
74
- async pull ( options : ComposeOptions , services : Array < string > | undefined ) : Promise < void > {
66
+ async up ( options : ComposeOptions , services : Array < string > | undefined ) : Promise < void > {
67
+ const client = await this . getClient ( ) ;
68
+
75
69
try {
76
70
if ( services ) {
77
71
log . info ( `Pulling Compose environment images "${ services . join ( '", "' ) } "...` ) ;
78
- await v1 . pullMany ( services , await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
72
+ await client . pullMany ( services , await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
79
73
} else {
80
74
log . info ( `Pulling Compose environment images...` ) ;
81
- await v1 . pullAll ( await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
75
+ await client . pullAll ( await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
82
76
}
83
77
log . info ( `Pulled Compose environment` ) ;
84
78
} catch ( err ) {
@@ -88,70 +82,16 @@ class ComposeV1Client implements ComposeClient {
88
82
}
89
83
}
90
84
91
- async stop ( options : ComposeOptions ) : Promise < void > {
92
- try {
93
- log . info ( `Stopping Compose environment...` ) ;
94
- await v1 . stop ( await defaultComposeOptions ( this . environment , options ) ) ;
95
- log . info ( `Stopped Compose environment` ) ;
96
- } catch ( err ) {
97
- await handleAndRethrow ( err , async ( error : Error ) =>
98
- log . error ( `Failed to stop Compose environment: ${ error . message } ` )
99
- ) ;
100
- }
101
- }
102
-
103
- async down ( options : ComposeOptions , downOptions : ComposeDownOptions ) : Promise < void > {
104
- try {
105
- log . info ( `Downing Compose environment...` ) ;
106
- await v1 . down ( {
107
- ...( await defaultComposeOptions ( this . environment , options ) ) ,
108
- commandOptions : composeDownCommandOptions ( downOptions ) ,
109
- } ) ;
110
- log . info ( `Downed Compose environment` ) ;
111
- } catch ( err ) {
112
- await handleAndRethrow ( err , async ( error : Error ) =>
113
- log . error ( `Failed to down Compose environment: ${ error . message } ` )
114
- ) ;
115
- }
116
- }
117
- }
118
-
119
- class ComposeV2Client implements ComposeClient {
120
- constructor (
121
- public readonly info : ComposeInfo ,
122
- private readonly environment : NodeJS . ProcessEnv
123
- ) { }
124
-
125
- async up ( options : ComposeOptions , services : Array < string > | undefined ) : Promise < void > {
126
- try {
127
- if ( services ) {
128
- log . info ( `Upping Compose environment services ${ services . join ( ", " ) } ...` ) ;
129
- await v2 . upMany ( services , await defaultComposeOptions ( this . environment , options ) ) ;
130
- } else {
131
- log . info ( `Upping Compose environment...` ) ;
132
- await v2 . upAll ( await defaultComposeOptions ( this . environment , options ) ) ;
133
- }
134
- log . info ( `Upped Compose environment` ) ;
135
- } catch ( err ) {
136
- await handleAndRethrow ( err , async ( error : Error ) => {
137
- try {
138
- log . error ( `Failed to up Compose environment: ${ error . message } ` ) ;
139
- await this . down ( options , { removeVolumes : true , timeout : 0 } ) ;
140
- } catch {
141
- log . error ( `Failed to down Compose environment after failed up` ) ;
142
- }
143
- } ) ;
144
- }
145
- }
146
-
147
85
async pull ( options : ComposeOptions , services : Array < string > | undefined ) : Promise < void > {
86
+ const client = await this . getClient ( ) ;
87
+
148
88
try {
149
89
if ( services ) {
150
90
log . info ( `Pulling Compose environment images "${ services . join ( '", "' ) } "...` ) ;
151
- await v2 . pullMany ( services , await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
91
+ await client . pullMany ( services , await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
152
92
} else {
153
93
log . info ( `Pulling Compose environment images...` ) ;
154
- await v2 . pullAll ( await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
94
+ await client . pullAll ( await defaultComposeOptions ( this . environment , { ...options , logger : pullLog } ) ) ;
155
95
}
156
96
log . info ( `Pulled Compose environment` ) ;
157
97
} catch ( err ) {
@@ -162,9 +102,11 @@ class ComposeV2Client implements ComposeClient {
162
102
}
163
103
164
104
async stop ( options : ComposeOptions ) : Promise < void > {
105
+ const client = await this . getClient ( ) ;
106
+
165
107
try {
166
108
log . info ( `Stopping Compose environment...` ) ;
167
- await v2 . stop ( await defaultComposeOptions ( this . environment , options ) ) ;
109
+ await client . stop ( await defaultComposeOptions ( this . environment , options ) ) ;
168
110
log . info ( `Stopped Compose environment` ) ;
169
111
} catch ( err ) {
170
112
await handleAndRethrow ( err , async ( error : Error ) =>
@@ -174,9 +116,10 @@ class ComposeV2Client implements ComposeClient {
174
116
}
175
117
176
118
async down ( options : ComposeOptions , downOptions : ComposeDownOptions ) : Promise < void > {
119
+ const client = await this . getClient ( ) ;
177
120
try {
178
121
log . info ( `Downing Compose environment...` ) ;
179
- await v2 . down ( {
122
+ await client . down ( {
180
123
...( await defaultComposeOptions ( this . environment , options ) ) ,
181
124
commandOptions : composeDownCommandOptions ( downOptions ) ,
182
125
} ) ;
@@ -189,26 +132,6 @@ class ComposeV2Client implements ComposeClient {
189
132
}
190
133
}
191
134
192
- class MissingComposeClient implements ComposeClient {
193
- public readonly info = undefined ;
194
-
195
- up ( ) : Promise < void > {
196
- throw new Error ( "Compose is not installed" ) ;
197
- }
198
-
199
- pull ( ) : Promise < void > {
200
- throw new Error ( "Compose is not installed" ) ;
201
- }
202
-
203
- stop ( ) : Promise < void > {
204
- throw new Error ( "Compose is not installed" ) ;
205
- }
206
-
207
- down ( ) : Promise < void > {
208
- throw new Error ( "Compose is not installed" ) ;
209
- }
210
- }
211
-
212
135
// eslint-disable-next-line @typescript-eslint/no-explicit-any
213
136
async function handleAndRethrow ( err : any , handle : ( error : Error ) => Promise < void > ) : Promise < never > {
214
137
const error = err instanceof Error ? err : new Error ( err . err . trim ( ) ) ;
0 commit comments