1+ import { field , Level , logger } from "@coder/logger"
12import * as fs from "fs-extra"
23import yaml from "js-yaml"
34import * as path from "path"
4- import { field , logger , Level } from "@coder/logger"
55import { Args as VsArgs } from "../../lib/vscode/src/vs/server/ipc"
66import { AuthType } from "./http"
7- import { paths , uxPath } from "./util"
7+ import { generatePassword , humanPath , paths } from "./util"
88
99export class Optional < T > {
1010 public constructor ( public readonly value ?: T ) { }
@@ -84,7 +84,10 @@ type Options<T> = {
8484
8585const options : Options < Required < Args > > = {
8686 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+ } ,
8891 cert : {
8992 type : OptionalString ,
9093 path : true ,
@@ -96,11 +99,14 @@ const options: Options<Required<Args>> = {
9699 json : { type : "boolean" } ,
97100 open : { type : "boolean" , description : "Open in browser on startup. Does not work remotely." } ,
98101
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+ } ,
100106
101107 config : {
102108 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." ,
104110 } ,
105111
106112 // These two have been deprecated by bindAddr.
@@ -145,7 +151,19 @@ export const optionDescriptions = (): string[] => {
145151 )
146152}
147153
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+
149167 const args : Args = { _ : [ ] }
150168 let ended = false
151169
@@ -175,7 +193,11 @@ export const parse = (argv: string[]): Args => {
175193 }
176194
177195 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" )
179201 }
180202
181203 const option = options [ key ]
@@ -194,7 +216,11 @@ export const parse = (argv: string[]): Args => {
194216 ; ( args [ key ] as OptionalString ) = new OptionalString ( value )
195217 continue
196218 } 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
198224 }
199225
200226 if ( option . path ) {
@@ -214,15 +240,15 @@ export const parse = (argv: string[]): Args => {
214240 case "number" :
215241 ; ( args [ key ] as number ) = parseInt ( value , 10 )
216242 if ( isNaN ( args [ key ] as number ) ) {
217- throw new Error ( `--${ key } must be a number` )
243+ throw error ( `--${ key } must be a number` )
218244 }
219245 break
220246 case OptionalString :
221247 ; ( args [ key ] as OptionalString ) = new OptionalString ( value )
222248 break
223249 default : {
224250 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 ( ", " ) } ]` )
226252 }
227253 ; ( args [ key ] as string ) = value
228254 break
@@ -284,53 +310,93 @@ export const parse = (argv: string[]): Args => {
284310 return args
285311}
286312
287- const defaultConfigFile = `
313+ async function defaultConfigFile ( ) : Promise < string > {
314+ return `bind-addr: 127.0.0.1:8080
288315auth: 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+ }
301333
302334 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 ) } ` )
305337 }
306338
307- logger . info ( `Using config file from ${ uxPath ( configPath ) } ` )
339+ logger . info ( `Using config file from ${ humanPath ( configPath ) } ` )
308340
309341 const configFile = await fs . readFile ( configPath )
310342 const config = yaml . safeLoad ( configFile . toString ( ) , {
311- filename : args . config ,
343+ filename : configPath ,
312344 } )
313345
314346 // We convert the config file into a set of flags.
315347 // This is a temporary measure until we add a proper CLI library.
316348 const configFileArgv = Object . entries ( config ) . map ( ( [ optName , opt ] ) => {
317- if ( opt === null ) {
349+ if ( opt === true ) {
318350 return `--${ optName } `
319351 }
320352 return `--${ optName } =${ opt } `
321353 } )
322- const configFileArgs = parse ( configFileArgv )
354+ const args = parse ( configFileArgv , {
355+ configFile : configPath ,
356+ } )
357+ return {
358+ ...args ,
359+ config : configPath ,
360+ }
361+ }
323362
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 ) ]
326366}
327367
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" ] )
331377 }
332- if ( process . env . CODE_SERVER_CONFIG !== undefined ) {
333- return process . env . CODE_SERVER_CONFIG
378+ if ( args . host ) {
379+ addr . host = args . host
334380 }
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 ]
336402}
0 commit comments