parent
aa87270148
commit
8053ec6872
|
@ -152,12 +152,12 @@ export const optionDescriptions = (): string[] => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parse = async (
|
export const parse = (
|
||||||
argv: string[],
|
argv: string[],
|
||||||
opts?: {
|
opts?: {
|
||||||
configFile: string
|
configFile: string
|
||||||
},
|
},
|
||||||
): Promise<Args> => {
|
): Args => {
|
||||||
const error = (msg: string): Error => {
|
const error = (msg: string): Error => {
|
||||||
if (opts?.configFile) {
|
if (opts?.configFile) {
|
||||||
msg = `error reading ${opts.configFile}: ${msg}`
|
msg = `error reading ${opts.configFile}: ${msg}`
|
||||||
|
@ -288,18 +288,28 @@ export const parse = async (
|
||||||
break
|
break
|
||||||
case LogLevel.Debug:
|
case LogLevel.Debug:
|
||||||
logger.level = Level.Debug
|
logger.level = Level.Debug
|
||||||
|
args.verbose = false
|
||||||
break
|
break
|
||||||
case LogLevel.Info:
|
case LogLevel.Info:
|
||||||
logger.level = Level.Info
|
logger.level = Level.Info
|
||||||
|
args.verbose = false
|
||||||
break
|
break
|
||||||
case LogLevel.Warn:
|
case LogLevel.Warn:
|
||||||
logger.level = Level.Warning
|
logger.level = Level.Warning
|
||||||
|
args.verbose = false
|
||||||
break
|
break
|
||||||
case LogLevel.Error:
|
case LogLevel.Error:
|
||||||
logger.level = Level.Error
|
logger.level = Level.Error
|
||||||
|
args.verbose = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setDefaults(args: Args): Promise<Args> {
|
||||||
|
args = { ...args }
|
||||||
|
|
||||||
if (!args["user-data-dir"]) {
|
if (!args["user-data-dir"]) {
|
||||||
await copyOldMacOSDataDir()
|
await copyOldMacOSDataDir()
|
||||||
args["user-data-dir"] = paths.data
|
args["user-data-dir"] = paths.data
|
||||||
|
@ -353,7 +363,7 @@ export async function readConfigFile(configPath?: string): Promise<Args> {
|
||||||
}
|
}
|
||||||
return `--${optName}=${opt}`
|
return `--${optName}=${opt}`
|
||||||
})
|
})
|
||||||
const args = await parse(configFileArgv, {
|
const args = parse(configFileArgv, {
|
||||||
configFile: configPath,
|
configFile: configPath,
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { ProxyHttpProvider } from "./app/proxy"
|
||||||
import { StaticHttpProvider } from "./app/static"
|
import { StaticHttpProvider } from "./app/static"
|
||||||
import { UpdateHttpProvider } from "./app/update"
|
import { UpdateHttpProvider } from "./app/update"
|
||||||
import { VscodeHttpProvider } from "./app/vscode"
|
import { VscodeHttpProvider } from "./app/vscode"
|
||||||
import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile } from "./cli"
|
import { Args, bindAddrFromAllSources, optionDescriptions, parse, readConfigFile, setDefaults } from "./cli"
|
||||||
import { AuthType, HttpServer, HttpServerOptions } from "./http"
|
import { AuthType, HttpServer, HttpServerOptions } from "./http"
|
||||||
import { generateCertificate, hash, open, humanPath } from "./util"
|
import { generateCertificate, hash, open, humanPath } from "./util"
|
||||||
import { ipcMain, wrap } from "./wrapper"
|
import { ipcMain, wrap } from "./wrapper"
|
||||||
|
@ -43,6 +43,8 @@ const main = async (cliArgs: Args): Promise<void> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args = await setDefaults(args)
|
||||||
|
|
||||||
logger.info(`Using user-data-dir ${humanPath(args["user-data-dir"])}`)
|
logger.info(`Using user-data-dir ${humanPath(args["user-data-dir"])}`)
|
||||||
|
|
||||||
logger.trace(`Using extensions-dir ${humanPath(args["extensions-dir"])}`)
|
logger.trace(`Using extensions-dir ${humanPath(args["extensions-dir"])}`)
|
||||||
|
@ -127,9 +129,9 @@ const main = async (cliArgs: Args): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function entry(): Promise<void> {
|
async function entry(): Promise<void> {
|
||||||
const tryParse = async (): Promise<Args> => {
|
const tryParse = (): Args => {
|
||||||
try {
|
try {
|
||||||
return await parse(process.argv.slice(2))
|
return parse(process.argv.slice(2))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error.message)
|
console.error(error.message)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { logger, Level } from "@coder/logger"
|
||||||
import * as assert from "assert"
|
import * as assert from "assert"
|
||||||
import * as path from "path"
|
import * as path from "path"
|
||||||
import { parse } from "../src/node/cli"
|
import { parse } from "../src/node/cli"
|
||||||
import { paths } from "../src/node/util"
|
|
||||||
|
|
||||||
describe("cli", () => {
|
describe("cli", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -12,17 +11,15 @@ describe("cli", () => {
|
||||||
// The parser will always fill these out.
|
// The parser will always fill these out.
|
||||||
const defaults = {
|
const defaults = {
|
||||||
_: [],
|
_: [],
|
||||||
"extensions-dir": path.join(paths.data, "extensions"),
|
|
||||||
"user-data-dir": paths.data,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should set defaults", async () => {
|
it("should set defaults", () => {
|
||||||
assert.deepEqual(await await parse([]), defaults)
|
assert.deepEqual(parse([]), defaults)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should parse all available options", async () => {
|
it("should parse all available options", () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
await await parse([
|
parse([
|
||||||
"--bind-addr=192.169.0.1:8080",
|
"--bind-addr=192.169.0.1:8080",
|
||||||
"--auth",
|
"--auth",
|
||||||
"none",
|
"none",
|
||||||
|
@ -84,8 +81,8 @@ describe("cli", () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should work with short options", async () => {
|
it("should work with short options", () => {
|
||||||
assert.deepEqual(await parse(["-vvv", "-v"]), {
|
assert.deepEqual(parse(["-vvv", "-v"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
log: "trace",
|
log: "trace",
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
@ -95,17 +92,18 @@ describe("cli", () => {
|
||||||
assert.equal(logger.level, Level.Trace)
|
assert.equal(logger.level, Level.Trace)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should use log level env var", async () => {
|
it("should use log level env var", () => {
|
||||||
process.env.LOG_LEVEL = "debug"
|
process.env.LOG_LEVEL = "debug"
|
||||||
assert.deepEqual(await parse([]), {
|
assert.deepEqual(parse([]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
log: "debug",
|
log: "debug",
|
||||||
|
verbose: false,
|
||||||
})
|
})
|
||||||
assert.equal(process.env.LOG_LEVEL, "debug")
|
assert.equal(process.env.LOG_LEVEL, "debug")
|
||||||
assert.equal(logger.level, Level.Debug)
|
assert.equal(logger.level, Level.Debug)
|
||||||
|
|
||||||
process.env.LOG_LEVEL = "trace"
|
process.env.LOG_LEVEL = "trace"
|
||||||
assert.deepEqual(await parse([]), {
|
assert.deepEqual(parse([]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
log: "trace",
|
log: "trace",
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
@ -116,23 +114,25 @@ describe("cli", () => {
|
||||||
|
|
||||||
it("should prefer --log to env var and --verbose to --log", async () => {
|
it("should prefer --log to env var and --verbose to --log", async () => {
|
||||||
process.env.LOG_LEVEL = "debug"
|
process.env.LOG_LEVEL = "debug"
|
||||||
assert.deepEqual(await parse(["--log", "info"]), {
|
assert.deepEqual(parse(["--log", "info"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
log: "info",
|
log: "info",
|
||||||
|
verbose: false,
|
||||||
})
|
})
|
||||||
assert.equal(process.env.LOG_LEVEL, "info")
|
assert.equal(process.env.LOG_LEVEL, "info")
|
||||||
assert.equal(logger.level, Level.Info)
|
assert.equal(logger.level, Level.Info)
|
||||||
|
|
||||||
process.env.LOG_LEVEL = "trace"
|
process.env.LOG_LEVEL = "trace"
|
||||||
assert.deepEqual(await parse(["--log", "info"]), {
|
assert.deepEqual(parse(["--log", "info"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
log: "info",
|
log: "info",
|
||||||
|
verbose: false,
|
||||||
})
|
})
|
||||||
assert.equal(process.env.LOG_LEVEL, "info")
|
assert.equal(process.env.LOG_LEVEL, "info")
|
||||||
assert.equal(logger.level, Level.Info)
|
assert.equal(logger.level, Level.Info)
|
||||||
|
|
||||||
process.env.LOG_LEVEL = "warn"
|
process.env.LOG_LEVEL = "warn"
|
||||||
assert.deepEqual(await parse(["--log", "info", "--verbose"]), {
|
assert.deepEqual(parse(["--log", "info", "--verbose"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
log: "trace",
|
log: "trace",
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
@ -141,34 +141,31 @@ describe("cli", () => {
|
||||||
assert.equal(logger.level, Level.Trace)
|
assert.equal(logger.level, Level.Trace)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should ignore invalid log level env var", async () => {
|
it("should ignore invalid log level env var", () => {
|
||||||
process.env.LOG_LEVEL = "bogus"
|
process.env.LOG_LEVEL = "bogus"
|
||||||
assert.deepEqual(await parse([]), defaults)
|
assert.deepEqual(parse([]), defaults)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should error if value isn't provided", async () => {
|
it("should error if value isn't provided", () => {
|
||||||
await assert.rejects(async () => await parse(["--auth"]), /--auth requires a value/)
|
assert.throws(() => parse(["--auth"]), /--auth requires a value/)
|
||||||
await assert.rejects(async () => await parse(["--auth=", "--log=debug"]), /--auth requires a value/)
|
assert.throws(() => parse(["--auth=", "--log=debug"]), /--auth requires a value/)
|
||||||
await assert.rejects(async () => await parse(["--auth", "--log"]), /--auth requires a value/)
|
assert.throws(() => parse(["--auth", "--log"]), /--auth requires a value/)
|
||||||
await assert.rejects(async () => await parse(["--auth", "--invalid"]), /--auth requires a value/)
|
assert.throws(() => parse(["--auth", "--invalid"]), /--auth requires a value/)
|
||||||
await assert.rejects(async () => await parse(["--bind-addr"]), /--bind-addr requires a value/)
|
assert.throws(() => parse(["--bind-addr"]), /--bind-addr requires a value/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should error if value is invalid", async () => {
|
it("should error if value is invalid", () => {
|
||||||
await assert.rejects(async () => await parse(["--port", "foo"]), /--port must be a number/)
|
assert.throws(() => parse(["--port", "foo"]), /--port must be a number/)
|
||||||
await assert.rejects(async () => await parse(["--auth", "invalid"]), /--auth valid values: \[password, none\]/)
|
assert.throws(() => parse(["--auth", "invalid"]), /--auth valid values: \[password, none\]/)
|
||||||
await assert.rejects(
|
assert.throws(() => parse(["--log", "invalid"]), /--log valid values: \[trace, debug, info, warn, error\]/)
|
||||||
async () => await parse(["--log", "invalid"]),
|
|
||||||
/--log valid values: \[trace, debug, info, warn, error\]/,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should error if the option doesn't exist", async () => {
|
it("should error if the option doesn't exist", () => {
|
||||||
await assert.rejects(async () => await parse(["--foo"]), /Unknown option --foo/)
|
assert.throws(() => parse(["--foo"]), /Unknown option --foo/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not error if the value is optional", async () => {
|
it("should not error if the value is optional", () => {
|
||||||
assert.deepEqual(await parse(["--cert"]), {
|
assert.deepEqual(parse(["--cert"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
cert: {
|
cert: {
|
||||||
value: undefined,
|
value: undefined,
|
||||||
|
@ -176,33 +173,30 @@ describe("cli", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not allow option-like values", async () => {
|
it("should not allow option-like values", () => {
|
||||||
await assert.rejects(async () => await parse(["--socket", "--socket-path-value"]), /--socket requires a value/)
|
assert.throws(() => parse(["--socket", "--socket-path-value"]), /--socket requires a value/)
|
||||||
// If you actually had a path like this you would do this instead:
|
// If you actually had a path like this you would do this instead:
|
||||||
assert.deepEqual(await parse(["--socket", "./--socket-path-value"]), {
|
assert.deepEqual(parse(["--socket", "./--socket-path-value"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
socket: path.resolve("--socket-path-value"),
|
socket: path.resolve("--socket-path-value"),
|
||||||
})
|
})
|
||||||
await assert.rejects(
|
assert.throws(() => parse(["--cert", "--socket-path-value"]), /Unknown option --socket-path-value/)
|
||||||
async () => await parse(["--cert", "--socket-path-value"]),
|
|
||||||
/Unknown option --socket-path-value/,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should allow positional arguments before options", async () => {
|
it("should allow positional arguments before options", () => {
|
||||||
assert.deepEqual(await parse(["foo", "test", "--auth", "none"]), {
|
assert.deepEqual(parse(["foo", "test", "--auth", "none"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
_: ["foo", "test"],
|
_: ["foo", "test"],
|
||||||
auth: "none",
|
auth: "none",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should support repeatable flags", async () => {
|
it("should support repeatable flags", () => {
|
||||||
assert.deepEqual(await parse(["--proxy-domain", "*.coder.com"]), {
|
assert.deepEqual(parse(["--proxy-domain", "*.coder.com"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
"proxy-domain": ["*.coder.com"],
|
"proxy-domain": ["*.coder.com"],
|
||||||
})
|
})
|
||||||
assert.deepEqual(await parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "test.com"]), {
|
assert.deepEqual(parse(["--proxy-domain", "*.coder.com", "--proxy-domain", "test.com"]), {
|
||||||
...defaults,
|
...defaults,
|
||||||
"proxy-domain": ["*.coder.com", "test.com"],
|
"proxy-domain": ["*.coder.com", "test.com"],
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue