@@ -8,6 +8,17 @@ import {WriteStreams} from "./write-streams.js";
88import chalkBase from "chalk" ;
99import chalk from "chalk-template" ;
1010
11+ export function splitSemicolonEnvVars ( argv : Record < string , any > , arrayKeys : Set < string > , env : Record < string , string | undefined > ) : void {
12+ for ( const [ envKey , envValue ] of Object . entries ( env ) ) {
13+ if ( ! envKey . startsWith ( "GCL_" ) || envValue == null ) continue ;
14+ const optionName = camelCase ( envKey . slice ( 4 ) ) ;
15+ if ( ! arrayKeys . has ( optionName ) ) continue ;
16+ const currentVal = argv [ optionName ] ;
17+ if ( ! Array . isArray ( currentVal ) || currentVal . length !== 1 || currentVal [ 0 ] !== envValue ) continue ;
18+ argv [ optionName ] = envValue . split ( ";" ) ;
19+ }
20+ }
21+
1122async function isInGitRepository ( ) {
1223 try {
1324 await Utils . spawn ( [ "git" , "rev-parse" , "--is-inside-work-tree" ] ) ;
@@ -103,36 +114,43 @@ export class Argv {
103114 }
104115
105116 private injectDotenv ( potentialDotenvFilepath : string , argv : any ) {
106- if ( fs . existsSync ( potentialDotenvFilepath ) ) {
107- const config = dotenv . parse ( fs . readFileSync ( potentialDotenvFilepath ) ) ;
108- for ( const [ key , value ] of Object . entries ( config ) ) {
109- const argKey = camelCase ( key ) ;
110-
111- // Special handle KEY=VALUE variable keys
112- if ( argKey === "variable" ) {
113- let currentVal = argv [ argKey ] ;
114- if ( currentVal == null ) {
115- currentVal = [ ] ;
116- this . map . set ( argKey , currentVal ) ;
117- }
118- if ( ! Array . isArray ( currentVal ) ) {
119- continue ;
120- }
121- for ( const pair of value . split ( " " ) ) {
122- currentVal . unshift ( pair ) ;
123- }
124- } else if ( argv [ argKey ] == null ) {
125- // Work around `dotenv.parse` limitation https://github.com/motdotla/dotenv/issues/51#issuecomment-552559070
126- if ( value === "true" ) this . map . set ( argKey , true ) ;
127- else if ( value === "false" ) this . map . set ( argKey , false ) ;
128- else if ( value === "null" ) this . map . set ( argKey , null ) ;
129- else if ( ! isNaN ( Number ( value ) ) ) this . map . set ( argKey , Number ( value ) ) ;
130- else this . map . set ( argKey , value ) ;
117+ if ( ! fs . existsSync ( potentialDotenvFilepath ) ) return ;
118+
119+ const config = dotenv . parse ( fs . readFileSync ( potentialDotenvFilepath ) ) ;
120+ for ( const [ key , value ] of Object . entries ( config ) ) {
121+ const argKey = camelCase ( key ) ;
122+
123+ // variable is additive — merge dotenv values with CLI values
124+ if ( argKey === "variable" ) {
125+ let currentVal = argv [ argKey ] ;
126+ if ( currentVal == null ) {
127+ currentVal = [ ] ;
128+ this . map . set ( argKey , currentVal ) ;
129+ }
130+ if ( ! Array . isArray ( currentVal ) ) {
131+ continue ;
131132 }
133+ for ( const pair of value . split ( " " ) ) {
134+ currentVal . unshift ( pair ) ;
135+ }
136+ } else if ( argv [ argKey ] == null ) {
137+ // Work around `dotenv.parse` limitation https://github.com/motdotla/dotenv/issues/51#issuecomment-552559070
138+ if ( value === "true" ) this . map . set ( argKey , true ) ;
139+ else if ( value === "false" ) this . map . set ( argKey , false ) ;
140+ else if ( value === "null" ) this . map . set ( argKey , null ) ;
141+ else if ( ! isNaN ( Number ( value ) ) ) this . map . set ( argKey , Number ( value ) ) ;
142+ else this . map . set ( argKey , value ) ;
132143 }
133144 }
134145 }
135146
147+ private getStringArray ( key : string ) : string [ ] {
148+ const val = this . map . get ( key ) ?? [ ] ;
149+ if ( Array . isArray ( val ) ) return val ;
150+ if ( typeof val === "string" ) return val . split ( " " ) ;
151+ return [ ] ;
152+ }
153+
136154 get cwd ( ) : string {
137155 let cwd = this . map . get ( "cwd" ) ?? "." ;
138156 assert ( typeof cwd != "object" , "--cwd option cannot be an array" ) ;
@@ -163,20 +181,11 @@ export class Argv {
163181 return ( this . map . get ( "home" ) ?? process . env . HOME ?? "" ) . replace ( / \/ $ / , "" ) ;
164182 }
165183
166- get volume ( ) : string [ ] {
167- const val = this . map . get ( "volume" ) ?? [ ] ;
168- return typeof val == "string" ? val . split ( " " ) : val ;
169- }
184+ get volume ( ) : string [ ] { return this . getStringArray ( "volume" ) ; }
170185
171- get network ( ) : string [ ] {
172- const val = this . map . get ( "network" ) ?? [ ] ;
173- return typeof val == "string" ? val . split ( " " ) : val ;
174- }
186+ get network ( ) : string [ ] { return this . getStringArray ( "network" ) ; }
175187
176- get extraHost ( ) : string [ ] {
177- const val = this . map . get ( "extraHost" ) ?? [ ] ;
178- return typeof val == "string" ? val . split ( " " ) : val ;
179- }
188+ get extraHost ( ) : string [ ] { return this . getStringArray ( "extraHost" ) ; }
180189
181190 get caFile ( ) : string | null {
182191 return this . map . get ( "caFile" ) ?? null ;
@@ -194,32 +203,26 @@ export class Argv {
194203 return this . map . get ( "pullPolicy" ) ?? "if-not-present" ;
195204 }
196205
197- get remoteVariables ( ) : string [ ] {
198- const val = this . map . get ( "remoteVariables" ) ?? [ ] ;
199- return typeof val == "string" ? val . split ( " " ) : val ;
200- }
206+ get remoteVariables ( ) : string [ ] { return this . getStringArray ( "remoteVariables" ) ; }
201207
202208 get variable ( ) : { [ key : string ] : string } {
203- const val = this . map . get ( "variable" ) ;
204209 const variables : { [ key : string ] : string } = { } ;
205- const pairs = typeof val == "string" ? val . split ( " " ) : val ;
206- ( pairs ?? [ ] ) . forEach ( ( variablePair : string ) => {
207- const exec = / (?< key > \w * ?) ( = ) (?< value > ( .| \n | \r ) * ) / . exec ( variablePair ) ;
208- if ( exec ?. groups ?. key ) {
209- variables [ exec . groups . key ] = exec ?. groups ?. value ;
210+ for ( const pair of this . getStringArray ( "variable" ) ) {
211+ const eqIndex = pair . indexOf ( "=" ) ;
212+ if ( eqIndex < 1 ) continue ;
213+ const key = pair . substring ( 0 , eqIndex ) ;
214+ if ( / ^ \w + $ / . test ( key ) ) {
215+ variables [ key ] = pair . substring ( eqIndex + 1 ) ;
210216 }
211- } ) ;
217+ }
212218 return variables ;
213219 }
214220
215221 get unsetVariables ( ) : string [ ] {
216222 return this . map . get ( "unsetVariable" ) ?? [ ] ;
217223 }
218224
219- get manual ( ) : string [ ] {
220- const val = this . map . get ( "manual" ) ?? [ ] ;
221- return typeof val == "string" ? val . split ( " " ) : val ;
222- }
225+ get manual ( ) : string [ ] { return this . getStringArray ( "manual" ) ; }
223226
224227 get job ( ) : string [ ] {
225228 return this . map . get ( "job" ) ?? [ ] ;
@@ -250,10 +253,7 @@ export class Argv {
250253 return this . map . get ( "privileged" ) ?? false ;
251254 }
252255
253- get device ( ) : string [ ] {
254- const val = this . map . get ( "device" ) ?? [ ] ;
255- return typeof val == "string" ? val . split ( " " ) : val ;
256- }
256+ get device ( ) : string [ ] { return this . getStringArray ( "device" ) ; }
257257
258258 get ulimit ( ) : string | null {
259259 const ulimit = this . map . get ( "ulimit" ) ;
0 commit comments