1
+ import { field , Level , logger } from "@coder/logger"
1
2
import * as fs from "fs-extra"
2
3
import yaml from "js-yaml"
3
4
import * as path from "path"
4
- import { field , logger , Level } from "@coder/logger"
5
5
import { Args as VsArgs } from "../../lib/vscode/src/vs/server/ipc"
6
6
import { AuthType } from "./http"
7
- import { paths , uxPath } from "./util"
7
+ import { generatePassword , humanPath , paths } from "./util"
8
8
9
9
export class Optional < T > {
10
10
public constructor ( public readonly value ?: T ) { }
@@ -84,7 +84,10 @@ type Options<T> = {
84
84
85
85
const options : Options < Required < Args > > = {
86
86
auth : { type : AuthType , description : "The type of authentication to use." } ,
87
- password : { type : "string" , description : "The password for password authentication." } ,
87
+ password : {
88
+ type : "string" ,
89
+ description : "The password for password authentication (can only be passed in via $PASSWORD or the config file)." ,
90
+ } ,
88
91
cert : {
89
92
type : OptionalString ,
90
93
path : true ,
@@ -96,11 +99,14 @@ const options: Options<Required<Args>> = {
96
99
json : { type : "boolean" } ,
97
100
open : { type : "boolean" , description : "Open in browser on startup. Does not work remotely." } ,
98
101
99
- "bind-addr" : { type : "string" , description : "Address to bind to in host:port." } ,
102
+ "bind-addr" : {
103
+ type : "string" ,
104
+ description : "Address to bind to in host:port. You can also use $PORT to override the port." ,
105
+ } ,
100
106
101
107
config : {
102
108
type : "string" ,
103
- description : "Path to yaml config file. Every flag maps directory to a key in the config file." ,
109
+ description : "Path to yaml config file. Every flag maps directly to a key in the config file." ,
104
110
} ,
105
111
106
112
// These two have been deprecated by bindAddr.
@@ -145,7 +151,19 @@ export const optionDescriptions = (): string[] => {
145
151
)
146
152
}
147
153
148
- export const parse = ( argv : string [ ] ) : Args => {
154
+ export const parse = (
155
+ argv : string [ ] ,
156
+ opts ?: {
157
+ configFile : string
158
+ } ,
159
+ ) : Args => {
160
+ const error = ( msg : string ) : Error => {
161
+ if ( opts ?. configFile ) {
162
+ msg = `error reading ${ opts . configFile } : ${ msg } `
163
+ }
164
+ return new Error ( msg )
165
+ }
166
+
149
167
const args : Args = { _ : [ ] }
150
168
let ended = false
151
169
@@ -175,7 +193,11 @@ export const parse = (argv: string[]): Args => {
175
193
}
176
194
177
195
if ( ! key || ! options [ key ] ) {
178
- throw new Error ( `Unknown option ${ arg } ` )
196
+ throw error ( `Unknown option ${ arg } ` )
197
+ }
198
+
199
+ if ( key === "password" && ! opts ?. configFile ) {
200
+ throw new Error ( "--password can only be set in the config file or passed in via $PASSWORD" )
179
201
}
180
202
181
203
const option = options [ key ]
@@ -194,7 +216,11 @@ export const parse = (argv: string[]): Args => {
194
216
; ( args [ key ] as OptionalString ) = new OptionalString ( value )
195
217
continue
196
218
} else if ( ! value ) {
197
- throw new Error ( `--${ key } requires a value` )
219
+ throw error ( `--${ key } requires a value` )
220
+ }
221
+
222
+ if ( option . type == OptionalString && value == "false" ) {
223
+ continue
198
224
}
199
225
200
226
if ( option . path ) {
@@ -214,15 +240,15 @@ export const parse = (argv: string[]): Args => {
214
240
case "number" :
215
241
; ( args [ key ] as number ) = parseInt ( value , 10 )
216
242
if ( isNaN ( args [ key ] as number ) ) {
217
- throw new Error ( `--${ key } must be a number` )
243
+ throw error ( `--${ key } must be a number` )
218
244
}
219
245
break
220
246
case OptionalString :
221
247
; ( args [ key ] as OptionalString ) = new OptionalString ( value )
222
248
break
223
249
default : {
224
250
if ( ! Object . values ( option . type ) . includes ( value ) ) {
225
- throw new Error ( `--${ key } valid values: [${ Object . values ( option . type ) . join ( ", " ) } ]` )
251
+ throw error ( `--${ key } valid values: [${ Object . values ( option . type ) . join ( ", " ) } ]` )
226
252
}
227
253
; ( args [ key ] as string ) = value
228
254
break
@@ -284,53 +310,93 @@ export const parse = (argv: string[]): Args => {
284
310
return args
285
311
}
286
312
287
- const defaultConfigFile = `
313
+ async function defaultConfigFile ( ) : Promise < string > {
314
+ return `bind-addr: 127.0.0.1:8080
288
315
auth: password
289
- bind-addr: 127.0.0.1:8080
290
- ` . trimLeft ( )
291
-
292
- // readConfigFile reads the config file specified in the config flag
293
- // and loads it's configuration.
294
- //
295
- // Flags set on the CLI take priority.
296
- //
297
- // The config file can also be passed via $CODE_SERVER_CONFIG and defaults
298
- // to ~/.config/code-server/config.yaml.
299
- export async function readConfigFile ( args : Args ) : Promise < Args > {
300
- const configPath = getConfigPath ( args )
316
+ password: ${ await generatePassword ( ) }
317
+ cert: false
318
+ `
319
+ }
320
+
321
+ /**
322
+ * Reads the code-server yaml config file and returns it as Args.
323
+ *
324
+ * @param configPath Read the config from configPath instead of $CODE_SERVER_CONFIG or the default.
325
+ */
326
+ export async function readConfigFile ( configPath ?: string ) : Promise < Args > {
327
+ if ( ! configPath ) {
328
+ configPath = process . env . CODE_SERVER_CONFIG
329
+ if ( ! configPath ) {
330
+ configPath = path . join ( paths . config , "config.yaml" )
331
+ }
332
+ }
301
333
302
334
if ( ! ( await fs . pathExists ( configPath ) ) ) {
303
- await fs . outputFile ( configPath , defaultConfigFile )
304
- logger . info ( `Wrote default config file to ${ uxPath ( configPath ) } ` )
335
+ await fs . outputFile ( configPath , await defaultConfigFile ( ) )
336
+ logger . info ( `Wrote default config file to ${ humanPath ( configPath ) } ` )
305
337
}
306
338
307
- logger . info ( `Using config file from ${ uxPath ( configPath ) } ` )
339
+ logger . info ( `Using config file from ${ humanPath ( configPath ) } ` )
308
340
309
341
const configFile = await fs . readFile ( configPath )
310
342
const config = yaml . safeLoad ( configFile . toString ( ) , {
311
- filename : args . config ,
343
+ filename : configPath ,
312
344
} )
313
345
314
346
// We convert the config file into a set of flags.
315
347
// This is a temporary measure until we add a proper CLI library.
316
348
const configFileArgv = Object . entries ( config ) . map ( ( [ optName , opt ] ) => {
317
- if ( opt === null ) {
349
+ if ( opt === true ) {
318
350
return `--${ optName } `
319
351
}
320
352
return `--${ optName } =${ opt } `
321
353
} )
322
- const configFileArgs = parse ( configFileArgv )
354
+ const args = parse ( configFileArgv , {
355
+ configFile : configPath ,
356
+ } )
357
+ return {
358
+ ...args ,
359
+ config : configPath ,
360
+ }
361
+ }
323
362
324
- // This prioritizes the flags set in args over the ones in the config file.
325
- return Object . assign ( configFileArgs , args )
363
+ function parseBindAddr ( bindAddr : string ) : [ string , number ] {
364
+ const u = new URL ( `http://${ bindAddr } ` )
365
+ return [ u . hostname , parseInt ( u . port , 10 ) ]
326
366
}
327
367
328
- function getConfigPath ( args : Args ) : string {
329
- if ( args . config !== undefined ) {
330
- return args . config
368
+ interface Addr {
369
+ host : string
370
+ port : number
371
+ }
372
+
373
+ function bindAddrFromArgs ( addr : Addr , args : Args ) : Addr {
374
+ addr = { ...addr }
375
+ if ( args [ "bind-addr" ] ) {
376
+ ; [ addr . host , addr . port ] = parseBindAddr ( args [ "bind-addr" ] )
331
377
}
332
- if ( process . env . CODE_SERVER_CONFIG !== undefined ) {
333
- return process . env . CODE_SERVER_CONFIG
378
+ if ( args . host ) {
379
+ addr . host = args . host
334
380
}
335
- return path . join ( paths . config , "config.yaml" )
381
+ if ( args . port !== undefined ) {
382
+ addr . port = args . port
383
+ }
384
+ return addr
385
+ }
386
+
387
+ export function bindAddrFromAllSources ( cliArgs : Args , configArgs : Args ) : [ string , number ] {
388
+ let addr : Addr = {
389
+ host : "localhost" ,
390
+ port : 8080 ,
391
+ }
392
+
393
+ addr = bindAddrFromArgs ( addr , configArgs )
394
+
395
+ if ( process . env . PORT ) {
396
+ addr . port = parseInt ( process . env . PORT , 10 )
397
+ }
398
+
399
+ addr = bindAddrFromArgs ( addr , cliArgs )
400
+
401
+ return [ addr . host , addr . port ]
336
402
}
0 commit comments