Skip to content

Commit 1da4d67

Browse files
committed
Split some entry methods into main
This is so they can be unit tested.
1 parent 1e2aac2 commit 1da4d67

File tree

3 files changed

+166
-166
lines changed

3 files changed

+166
-166
lines changed

src/node/entry.ts

+4-166
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,17 @@
1-
import { field, logger } from "@coder/logger"
2-
import * as cp from "child_process"
3-
import http from "http"
4-
import * as path from "path"
5-
import { CliMessage, OpenCommandPipeArgs } from "../../typings/ipc"
6-
import { plural } from "../common/util"
7-
import { createApp, ensureAddress } from "./app"
1+
import { logger } from "@coder/logger"
82
import {
9-
AuthType,
10-
DefaultedArgs,
11-
Feature,
123
optionDescriptions,
134
parse,
145
readConfigFile,
156
setDefaults,
167
shouldOpenInExistingInstance,
17-
shouldRunVsCodeCli,
8+
shouldRunVsCodeCli
189
} from "./cli"
19-
import { coderCloudBind } from "./coder_cloud"
2010
import { commit, version } from "./constants"
11+
import { openInExistingInstance, runCodeServer, runVsCodeCli } from "./main"
2112
import * as proxyAgent from "./proxy_agent"
22-
import { register } from "./routes"
23-
import { humanPath, isFile, open } from "./util"
2413
import { isChild, wrapper } from "./wrapper"
2514

26-
export const runVsCodeCli = (args: DefaultedArgs): void => {
27-
logger.debug("forking vs code cli...")
28-
const vscode = cp.fork(path.resolve(__dirname, "../../lib/vscode/out/vs/server/fork"), [], {
29-
env: {
30-
...process.env,
31-
CODE_SERVER_PARENT_PID: process.pid.toString(),
32-
},
33-
})
34-
vscode.once("message", (message: any) => {
35-
logger.debug("got message from VS Code", field("message", message))
36-
if (message.type !== "ready") {
37-
logger.error("Unexpected response waiting for ready response", field("type", message.type))
38-
process.exit(1)
39-
}
40-
const send: CliMessage = { type: "cli", args }
41-
vscode.send(send)
42-
})
43-
vscode.once("error", (error) => {
44-
logger.error("Got error from VS Code", field("error", error))
45-
process.exit(1)
46-
})
47-
vscode.on("exit", (code) => process.exit(code || 0))
48-
}
49-
50-
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
51-
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = {
52-
type: "open",
53-
folderURIs: [],
54-
fileURIs: [],
55-
forceReuseWindow: args["reuse-window"],
56-
forceNewWindow: args["new-window"],
57-
}
58-
59-
for (let i = 0; i < args._.length; i++) {
60-
const fp = path.resolve(args._[i])
61-
if (await isFile(fp)) {
62-
pipeArgs.fileURIs.push(fp)
63-
} else {
64-
pipeArgs.folderURIs.push(fp)
65-
}
66-
}
67-
68-
if (pipeArgs.forceNewWindow && pipeArgs.fileURIs.length > 0) {
69-
logger.error("--new-window can only be used with folder paths")
70-
process.exit(1)
71-
}
72-
73-
if (pipeArgs.folderURIs.length === 0 && pipeArgs.fileURIs.length === 0) {
74-
logger.error("Please specify at least one file or folder")
75-
process.exit(1)
76-
}
77-
78-
const vscode = http.request(
79-
{
80-
path: "/",
81-
method: "POST",
82-
socketPath,
83-
},
84-
(response) => {
85-
response.on("data", (message) => {
86-
logger.debug("got message from VS Code", field("message", message.toString()))
87-
})
88-
},
89-
)
90-
vscode.on("error", (error: unknown) => {
91-
logger.error("got error from VS Code", field("error", error))
92-
})
93-
vscode.write(JSON.stringify(pipeArgs))
94-
vscode.end()
95-
}
96-
97-
const main = async (args: DefaultedArgs): Promise<void> => {
98-
logger.info(`code-server ${version} ${commit}`)
99-
100-
logger.info(`Using user-data-dir ${humanPath(args["user-data-dir"])}`)
101-
logger.trace(`Using extensions-dir ${humanPath(args["extensions-dir"])}`)
102-
103-
if (args.auth === AuthType.Password && !args.password && !args["hashed-password"]) {
104-
throw new Error(
105-
"Please pass in a password via the config file or environment variable ($PASSWORD or $HASHED_PASSWORD)",
106-
)
107-
}
108-
109-
const [app, wsApp, server] = await createApp(args)
110-
const serverAddress = ensureAddress(server)
111-
await register(app, wsApp, server, args)
112-
113-
logger.info(`Using config file ${humanPath(args.config)}`)
114-
logger.info(`HTTP server listening on ${serverAddress} ${args.link ? "(randomized by --link)" : ""}`)
115-
116-
if (args.auth === AuthType.Password) {
117-
logger.info(" - Authentication is enabled")
118-
if (args.usingEnvPassword) {
119-
logger.info(" - Using password from $PASSWORD")
120-
} else if (args.usingEnvHashedPassword) {
121-
logger.info(" - Using password from $HASHED_PASSWORD")
122-
} else {
123-
logger.info(` - Using password from ${humanPath(args.config)}`)
124-
}
125-
} else {
126-
logger.info(` - Authentication is disabled ${args.link ? "(disabled by --link)" : ""}`)
127-
}
128-
129-
if (args.cert) {
130-
logger.info(` - Using certificate for HTTPS: ${humanPath(args.cert.value)}`)
131-
} else {
132-
logger.info(` - Not serving HTTPS ${args.link ? "(disabled by --link)" : ""}`)
133-
}
134-
135-
if (args["proxy-domain"].length > 0) {
136-
logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`)
137-
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`))
138-
}
139-
140-
if (args.link) {
141-
try {
142-
await coderCloudBind(serverAddress.replace(/^https?:\/\//, ""), args.link.value)
143-
logger.info(" - Connected to cloud agent")
144-
} catch (err) {
145-
logger.error(err.message)
146-
wrapper.exit(1)
147-
}
148-
}
149-
150-
if (args.enable && args.enable.length > 0) {
151-
logger.info("Enabling the following experimental features:")
152-
args.enable.forEach((feature) => {
153-
if (Object.values(Feature).includes(feature as Feature)) {
154-
logger.info(` - "${feature}"`)
155-
} else {
156-
logger.error(` X "${feature}" (unknown feature)`)
157-
}
158-
})
159-
// TODO: Could be nice to add wrapping to the logger?
160-
logger.info(
161-
" The code-server project does not provide stability guarantees or commit to fixing bugs relating to these experimental features. When filing bug reports, please ensure that you can reproduce the bug with all experimental features turned off.",
162-
)
163-
}
164-
165-
if (!args.socket && args.open) {
166-
// The web socket doesn't seem to work if browsing with 0.0.0.0.
167-
const openAddress = serverAddress.replace("://0.0.0.0", "://localhost")
168-
try {
169-
await open(openAddress)
170-
logger.info(`Opened ${openAddress}`)
171-
} catch (error) {
172-
logger.error("Failed to open", field("address", openAddress), field("error", error))
173-
}
174-
}
175-
}
176-
17715
async function entry(): Promise<void> {
17816
proxyAgent.monkeyPatch(false)
17917

@@ -186,7 +24,7 @@ async function entry(): Promise<void> {
18624
if (isChild(wrapper)) {
18725
const args = await wrapper.handshake()
18826
wrapper.preventExit()
189-
return main(args)
27+
return runCodeServer(args)
19028
}
19129

19230
const cliArgs = parse(process.argv.slice(2))

src/node/main.ts

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { field, logger } from "@coder/logger"
2+
import * as cp from "child_process"
3+
import http from "http"
4+
import * as path from "path"
5+
import { CliMessage, OpenCommandPipeArgs } from "../../typings/ipc"
6+
import { plural } from "../common/util"
7+
import { createApp, ensureAddress } from "./app"
8+
import { AuthType, DefaultedArgs, Feature } from "./cli"
9+
import { coderCloudBind } from "./coder_cloud"
10+
import { commit, version } from "./constants"
11+
import { register } from "./routes"
12+
import { humanPath, isFile, open } from "./util"
13+
14+
export const runVsCodeCli = (args: DefaultedArgs): void => {
15+
logger.debug("forking vs code cli...")
16+
const vscode = cp.fork(path.resolve(__dirname, "../../lib/vscode/out/vs/server/fork"), [], {
17+
env: {
18+
...process.env,
19+
CODE_SERVER_PARENT_PID: process.pid.toString(),
20+
},
21+
})
22+
vscode.once("message", (message: any) => {
23+
logger.debug("got message from VS Code", field("message", message))
24+
if (message.type !== "ready") {
25+
logger.error("Unexpected response waiting for ready response", field("type", message.type))
26+
process.exit(1)
27+
}
28+
const send: CliMessage = { type: "cli", args }
29+
vscode.send(send)
30+
})
31+
vscode.once("error", (error) => {
32+
logger.error("Got error from VS Code", field("error", error))
33+
process.exit(1)
34+
})
35+
vscode.on("exit", (code) => process.exit(code || 0))
36+
}
37+
38+
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
39+
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = {
40+
type: "open",
41+
folderURIs: [],
42+
fileURIs: [],
43+
forceReuseWindow: args["reuse-window"],
44+
forceNewWindow: args["new-window"],
45+
}
46+
47+
for (let i = 0; i < args._.length; i++) {
48+
const fp = path.resolve(args._[i])
49+
if (await isFile(fp)) {
50+
pipeArgs.fileURIs.push(fp)
51+
} else {
52+
pipeArgs.folderURIs.push(fp)
53+
}
54+
}
55+
56+
if (pipeArgs.forceNewWindow && pipeArgs.fileURIs.length > 0) {
57+
logger.error("--new-window can only be used with folder paths")
58+
process.exit(1)
59+
}
60+
61+
if (pipeArgs.folderURIs.length === 0 && pipeArgs.fileURIs.length === 0) {
62+
logger.error("Please specify at least one file or folder")
63+
process.exit(1)
64+
}
65+
66+
const vscode = http.request(
67+
{
68+
path: "/",
69+
method: "POST",
70+
socketPath,
71+
},
72+
(response) => {
73+
response.on("data", (message) => {
74+
logger.debug("got message from VS Code", field("message", message.toString()))
75+
})
76+
},
77+
)
78+
vscode.on("error", (error: unknown) => {
79+
logger.error("got error from VS Code", field("error", error))
80+
})
81+
vscode.write(JSON.stringify(pipeArgs))
82+
vscode.end()
83+
}
84+
85+
export const runCodeServer = async (args: DefaultedArgs): Promise<void> => {
86+
logger.info(`code-server ${version} ${commit}`)
87+
88+
logger.info(`Using user-data-dir ${humanPath(args["user-data-dir"])}`)
89+
logger.trace(`Using extensions-dir ${humanPath(args["extensions-dir"])}`)
90+
91+
if (args.auth === AuthType.Password && !args.password && !args["hashed-password"]) {
92+
throw new Error(
93+
"Please pass in a password via the config file or environment variable ($PASSWORD or $HASHED_PASSWORD)",
94+
)
95+
}
96+
97+
const [app, wsApp, server] = await createApp(args)
98+
const serverAddress = ensureAddress(server)
99+
await register(app, wsApp, server, args)
100+
101+
logger.info(`Using config file ${humanPath(args.config)}`)
102+
logger.info(`HTTP server listening on ${serverAddress} ${args.link ? "(randomized by --link)" : ""}`)
103+
104+
if (args.auth === AuthType.Password) {
105+
logger.info(" - Authentication is enabled")
106+
if (args.usingEnvPassword) {
107+
logger.info(" - Using password from $PASSWORD")
108+
} else if (args.usingEnvHashedPassword) {
109+
logger.info(" - Using password from $HASHED_PASSWORD")
110+
} else {
111+
logger.info(` - Using password from ${humanPath(args.config)}`)
112+
}
113+
} else {
114+
logger.info(` - Authentication is disabled ${args.link ? "(disabled by --link)" : ""}`)
115+
}
116+
117+
if (args.cert) {
118+
logger.info(` - Using certificate for HTTPS: ${humanPath(args.cert.value)}`)
119+
} else {
120+
logger.info(` - Not serving HTTPS ${args.link ? "(disabled by --link)" : ""}`)
121+
}
122+
123+
if (args["proxy-domain"].length > 0) {
124+
logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`)
125+
args["proxy-domain"].forEach((domain) => logger.info(` - *.${domain}`))
126+
}
127+
128+
if (args.link) {
129+
await coderCloudBind(serverAddress.replace(/^https?:\/\//, ""), args.link.value)
130+
logger.info(" - Connected to cloud agent")
131+
}
132+
133+
if (args.enable && args.enable.length > 0) {
134+
logger.info("Enabling the following experimental features:")
135+
args.enable.forEach((feature) => {
136+
if (Object.values(Feature).includes(feature as Feature)) {
137+
logger.info(` - "${feature}"`)
138+
} else {
139+
logger.error(` X "${feature}" (unknown feature)`)
140+
}
141+
})
142+
// TODO: Could be nice to add wrapping to the logger?
143+
logger.info(
144+
" The code-server project does not provide stability guarantees or commit to fixing bugs relating to these experimental features. When filing bug reports, please ensure that you can reproduce the bug with all experimental features turned off.",
145+
)
146+
}
147+
148+
if (!args.socket && args.open) {
149+
// The web socket doesn't seem to work if browsing with 0.0.0.0.
150+
const openAddress = serverAddress.replace("://0.0.0.0", "://localhost")
151+
try {
152+
await open(openAddress)
153+
logger.info(`Opened ${openAddress}`)
154+
} catch (error) {
155+
logger.error("Failed to open", field("address", openAddress), field("error", error))
156+
}
157+
}
158+
}

test/unit/entry.test.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
describe("entry", () => {
2+
it("should work", () => {
3+
})
4+
})

0 commit comments

Comments
 (0)