Skip to content

Commit 0302ead

Browse files
committed
Move path resolution from storage.ts
1 parent ac10249 commit 0302ead

File tree

6 files changed

+144
-125
lines changed

6 files changed

+144
-125
lines changed

src/commands.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import {
55
Workspace,
66
WorkspaceAgent,
77
} from "coder/site/src/api/typesGenerated";
8-
import path from "node:path";
98
import * as vscode from "vscode";
109
import { createWorkspaceIdentifier, extractAgents } from "./api/api-helper";
1110
import { CoderApi } from "./api/coderApi";
1211
import { needToken } from "./api/utils";
12+
import { PathResolver } from "./core/pathResolver";
1313
import { CertificateError } from "./error";
1414
import { getGlobalFlags } from "./globalFlags";
1515
import { Storage } from "./storage";
@@ -36,6 +36,7 @@ export class Commands {
3636
private readonly vscodeProposed: typeof vscode,
3737
private readonly restClient: Api,
3838
private readonly storage: Storage,
39+
private readonly pathResolver: PathResolver,
3940
) {}
4041

4142
/**
@@ -512,8 +513,8 @@ export class Commands {
512513
toSafeHost(url),
513514
);
514515

515-
const configDir = path.dirname(
516-
this.storage.getSessionTokenPath(toSafeHost(url)),
516+
const configDir = this.pathResolver.getGlobalConfigDir(
517+
toSafeHost(url),
517518
);
518519
const globalFlags = getGlobalFlags(
519520
vscode.workspace.getConfiguration(),

src/core/pathResolver.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import * as path from "path";
2+
import * as vscode from "vscode";
3+
4+
export class PathResolver {
5+
constructor(
6+
private readonly basePath: string,
7+
private readonly configurations: vscode.WorkspaceConfiguration,
8+
) {}
9+
10+
/**
11+
* Return the directory for the deployment with the provided label to where
12+
* the global Coder configs are stored.
13+
*
14+
* If the label is empty, read the old deployment-unaware config instead.
15+
*
16+
* The caller must ensure this directory exists before use.
17+
*/
18+
public getGlobalConfigDir(label: string): string {
19+
return label ? path.join(this.basePath, label) : this.basePath;
20+
}
21+
22+
/**
23+
* Return the directory for a deployment with the provided label to where its
24+
* binary is cached.
25+
*
26+
* If the label is empty, read the old deployment-unaware config instead.
27+
*
28+
* The caller must ensure this directory exists before use.
29+
*/
30+
public getBinaryCachePath(label: string): string {
31+
const configPath = this.configurations.get<string>(
32+
"coder.binaryDestination",
33+
);
34+
return configPath && configPath.trim().length > 0
35+
? path.normalize(configPath)
36+
: path.join(this.getGlobalConfigDir(label), "bin");
37+
}
38+
39+
/**
40+
* Return the path where network information for SSH hosts are stored.
41+
*
42+
* The CLI will write files here named after the process PID.
43+
*/
44+
public getNetworkInfoPath(): string {
45+
return path.join(this.basePath, "net");
46+
}
47+
48+
/**
49+
* Return the path where log data from the connection is stored.
50+
*
51+
* The CLI will write files here named after the process PID.
52+
*
53+
* Note: This directory is not currently used.
54+
*/
55+
public getLogPath(): string {
56+
return path.join(this.basePath, "log");
57+
}
58+
59+
/**
60+
* Get the path to the user's settings.json file.
61+
*
62+
* Going through VSCode's API should be preferred when modifying settings.
63+
*/
64+
public getUserSettingsPath(): string {
65+
return path.join(this.basePath, "..", "..", "..", "User", "settings.json");
66+
}
67+
68+
/**
69+
* Return the directory for the deployment with the provided label to where
70+
* its session token is stored.
71+
*
72+
* If the label is empty, read the old deployment-unaware config instead.
73+
*
74+
* The caller must ensure this directory exists before use.
75+
*/
76+
public getSessionTokenPath(label: string): string {
77+
return path.join(this.getGlobalConfigDir(label), "session");
78+
}
79+
80+
/**
81+
* Return the directory for the deployment with the provided label to where
82+
* its session token was stored by older code.
83+
*
84+
* If the label is empty, read the old deployment-unaware config instead.
85+
*
86+
* The caller must ensure this directory exists before use.
87+
*/
88+
public getLegacySessionTokenPath(label: string): string {
89+
return path.join(this.getGlobalConfigDir(label), "session_token");
90+
}
91+
92+
/**
93+
* Return the directory for the deployment with the provided label to where
94+
* its url is stored.
95+
*
96+
* If the label is empty, read the old deployment-unaware config instead.
97+
*
98+
* The caller must ensure this directory exists before use.
99+
*/
100+
public getUrlPath(label: string): string {
101+
return path.join(this.getGlobalConfigDir(label), "url");
102+
}
103+
}

src/extension.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { errToStr } from "./api/api-helper";
77
import { CoderApi } from "./api/coderApi";
88
import { needToken } from "./api/utils";
99
import { Commands } from "./commands";
10+
import { PathResolver } from "./core/pathResolver";
1011
import { CertificateError, getErrorDetail } from "./error";
1112
import { Remote } from "./remote";
1213
import { Storage } from "./storage";
@@ -48,14 +49,18 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
4849
);
4950
}
5051

52+
const pathResolver = new PathResolver(
53+
ctx.globalStorageUri.fsPath,
54+
vscode.workspace.getConfiguration(),
55+
);
5156
const output = vscode.window.createOutputChannel("Coder", { log: true });
5257
const storage = new Storage(
5358
vscodeProposed,
5459
output,
5560
ctx.globalState,
5661
ctx.secrets,
57-
ctx.globalStorageUri,
5862
ctx.logUri,
63+
pathResolver,
5964
);
6065

6166
// Try to clear this flag ASAP then pass it around if needed
@@ -253,7 +258,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
253258

254259
// Register globally available commands. Many of these have visibility
255260
// controlled by contexts, see `when` in the package.json.
256-
const commands = new Commands(vscodeProposed, client, storage);
261+
const commands = new Commands(vscodeProposed, client, storage, pathResolver);
257262
vscode.commands.registerCommand("coder.login", commands.login.bind(commands));
258263
vscode.commands.registerCommand(
259264
"coder.logout",
@@ -312,6 +317,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
312317
storage,
313318
commands,
314319
ctx.extensionMode,
320+
pathResolver,
315321
);
316322
try {
317323
const details = await remote.setup(

src/pgp.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { createReadStream, promises as fs } from "fs";
22
import * as openpgp from "openpgp";
33
import * as path from "path";
44
import { Readable } from "stream";
5-
import * as vscode from "vscode";
65
import { errToStr } from "./api/api-helper";
6+
import { Logger } from "./logging/logger";
77

88
export type Key = openpgp.Key;
99

@@ -35,9 +35,7 @@ export class VerificationError extends Error {
3535
/**
3636
* Return the public keys bundled with the plugin.
3737
*/
38-
export async function readPublicKeys(
39-
logger?: vscode.LogOutputChannel,
40-
): Promise<Key[]> {
38+
export async function readPublicKeys(logger?: Logger): Promise<Key[]> {
4139
const keyFile = path.join(__dirname, "../pgp-public.key");
4240
logger?.info("Reading public key", keyFile);
4341
const armoredKeys = await fs.readFile(keyFile, "utf8");
@@ -53,7 +51,7 @@ export async function verifySignature(
5351
publicKeys: openpgp.Key[],
5452
cliPath: string,
5553
signaturePath: string,
56-
logger?: vscode.LogOutputChannel,
54+
logger?: Logger,
5755
): Promise<void> {
5856
try {
5957
logger?.info("Reading signature", signaturePath);

src/remote.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { needToken } from "./api/utils";
2121
import { startWorkspaceIfStoppedOrFailed, waitForBuild } from "./api/workspace";
2222
import * as cli from "./cliManager";
2323
import { Commands } from "./commands";
24+
import { PathResolver } from "./core/pathResolver";
2425
import { featureSetForVersion, FeatureSet } from "./featureSet";
2526
import { getGlobalFlags } from "./globalFlags";
2627
import { Inbox } from "./inbox";
@@ -48,6 +49,7 @@ export class Remote {
4849
private readonly storage: Storage,
4950
private readonly commands: Commands,
5051
private readonly mode: vscode.ExtensionMode,
52+
private readonly pathResolver: PathResolver,
5153
) {}
5254

5355
private async confirmStart(workspaceName: string): Promise<boolean> {
@@ -111,9 +113,7 @@ export class Remote {
111113
title: "Waiting for workspace build...",
112114
},
113115
async () => {
114-
const globalConfigDir = path.dirname(
115-
this.storage.getSessionTokenPath(label),
116-
);
116+
const globalConfigDir = this.pathResolver.getGlobalConfigDir(label);
117117
while (workspace.latest_build.status !== "running") {
118118
++attempts;
119119
switch (workspace.latest_build.status) {
@@ -437,7 +437,7 @@ export class Remote {
437437
let settingsContent = "{}";
438438
try {
439439
settingsContent = await fs.readFile(
440-
this.storage.getUserSettingsPath(),
440+
this.pathResolver.getUserSettingsPath(),
441441
"utf8",
442442
);
443443
} catch (ex) {
@@ -486,7 +486,10 @@ export class Remote {
486486

487487
if (mungedPlatforms || mungedConnTimeout) {
488488
try {
489-
await fs.writeFile(this.storage.getUserSettingsPath(), settingsContent);
489+
await fs.writeFile(
490+
this.pathResolver.getUserSettingsPath(),
491+
settingsContent,
492+
);
490493
} catch (ex) {
491494
// This could be because the user's settings.json is read-only. This is
492495
// the case when using home-manager on NixOS, for example. Failure to
@@ -765,11 +768,11 @@ export class Remote {
765768
const globalConfigs = this.globalConfigs(label);
766769

767770
const proxyCommand = featureSet.wildcardSSH
768-
? `${escapeCommandArg(binaryPath)}${globalConfigs} ssh --stdio --usage-app=vscode --disable-autostart --network-info-dir ${escapeCommandArg(this.storage.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
771+
? `${escapeCommandArg(binaryPath)}${globalConfigs} ssh --stdio --usage-app=vscode --disable-autostart --network-info-dir ${escapeCommandArg(this.pathResolver.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
769772
: `${escapeCommandArg(binaryPath)}${globalConfigs} vscodessh --network-info-dir ${escapeCommandArg(
770-
this.storage.getNetworkInfoPath(),
771-
)}${await this.formatLogArg(logDir)} --session-token-file ${escapeCommandArg(this.storage.getSessionTokenPath(label))} --url-file ${escapeCommandArg(
772-
this.storage.getUrlPath(label),
773+
this.pathResolver.getNetworkInfoPath(),
774+
)}${await this.formatLogArg(logDir)} --session-token-file ${escapeCommandArg(this.pathResolver.getSessionTokenPath(label))} --url-file ${escapeCommandArg(
775+
this.pathResolver.getUrlPath(label),
773776
)} %h`;
774777

775778
const sshValues: SSHValues = {
@@ -828,7 +831,7 @@ export class Remote {
828831
const vscodeConfig = vscode.workspace.getConfiguration();
829832
const args = getGlobalFlags(
830833
vscodeConfig,
831-
path.dirname(this.storage.getSessionTokenPath(label)),
834+
this.pathResolver.getGlobalConfigDir(label),
832835
);
833836
return ` ${args.join(" ")}`;
834837
}
@@ -841,7 +844,7 @@ export class Remote {
841844
1000,
842845
);
843846
const networkInfoFile = path.join(
844-
this.storage.getNetworkInfoPath(),
847+
this.pathResolver.getNetworkInfoPath(),
845848
`${sshPid}.json`,
846849
);
847850

0 commit comments

Comments
 (0)