Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/npm_and_yarn/vscode/test-electron…
Browse files Browse the repository at this point in the history
…-2.4.1
  • Loading branch information
matifali authored Feb 5, 2025
2 parents 650f5c8 + 71f5ba5 commit 5ba3f76
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 80 deletions.
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,46 @@

## Unreleased

## [v1.4.0](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2025-02-04)

- Recreate REST client after starting a workspace to ensure fresh TLS certificates.
- Use `coder ssh` subcommand in place of `coder vscodessh`.

## [v1.3.10](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2025-01-17)

- Fix bug where checking for overridden properties incorrectly converted host name pattern to regular expression.

## [v1.3.9](https://github.com/coder/vscode-coder/releases/tag/v1.3.9) (2024-12-12)

- Only show a login failure dialog for explicit logins (and not autologins).

## [v1.3.8](https://github.com/coder/vscode-coder/releases/tag/v1.3.8) (2024-12-06)

- When starting a workspace, shell out to the Coder binary instead of making an
API call. This reduces drift between what the plugin does and the CLI does. As
part of this, the `session_token` file was renamed to `session` since that is
what the CLI expects.

## [v1.3.7](https://github.com/coder/vscode-coder/releases/tag/v1.3.7) (2024-11-04)

### Added

- New setting `coder.tlsAltHost` to configure an alternative hostname to use for
TLS verification. This is useful when the hostname in the certificate does not
match the hostname used to connect.

## [v1.3.6](https://github.com/coder/vscode-coder/releases/tag/v1.3.6) (2024-11-04)

### Added

- Default URL setting that takes precedence over CODER_URL.
- Autologin setting that automatically initiates login when the extension
activates using either the default URL or CODER_URL.

### Changed

- When a client certificate and/or key is configured, skip token authentication.

## [v1.3.5](https://github.com/coder/vscode-coder/releases/tag/v1.3.5) (2024-10-16)

### Fixed
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contains the `coder-vscode` prefix, and if so we delay activation to:

```text
Host coder-vscode.dev.coder.com--*
ProxyCommand "/tmp/coder" vscodessh --network-info-dir "/home/kyle/.config/Code/User/globalStorage/coder.coder-remote/net" --session-token-file "/home/kyle/.config/Code/User/globalStorage/coder.coder-remote/dev.coder.com/session_token" --url-file "/home/kyle/.config/Code/User/globalStorage/coder.coder-remote/dev.coder.com/url" %h
ProxyCommand "/tmp/coder" --global-config "/home/kyle/.config/Code/User/globalStorage/coder.coder-remote/dev.coder.com" ssh --stdio --network-info-dir "/home/kyle/.config/Code/User/globalStorage/coder.coder-remote/net" --ssh-host-prefix coder-vscode.dev.coder.com-- %h
ConnectTimeout 0
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
Expand All @@ -50,8 +50,8 @@ specified port. This port is printed to the `Remote - SSH` log file in the VS
Code Output panel in the format `-> socksPort <port> ->`. We use this port to
find the SSH process ID that is being used by the remote session.

The `vscodessh` subcommand on the `coder` binary periodically flushes its
network information to `network-info-dir + "/" + process.ppid`. SSH executes
The `ssh` subcommand on the `coder` binary periodically flushes its network
information to `network-info-dir + "/" + process.ppid`. SSH executes
`ProxyCommand`, which means the `process.ppid` will always be the matching SSH
command.

Expand Down
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"displayName": "Coder",
"description": "Open any workspace with a single click.",
"repository": "https://github.com/coder/vscode-coder",
"version": "1.3.5",
"version": "1.4.0",
"engines": {
"vscode": "^1.73.0"
},
Expand Down Expand Up @@ -74,17 +74,22 @@
"default": ""
},
"coder.tlsCertFile": {
"markdownDescription": "Path to file for TLS client cert. When specified, token authorization will be skipped.",
"markdownDescription": "Path to file for TLS client cert. When specified, token authorization will be skipped. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsKeyFile": {
"markdownDescription": "Path to file for TLS client key. When specified, token authorization will be skipped.",
"markdownDescription": "Path to file for TLS client key. When specified, token authorization will be skipped. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsCaFile": {
"markdownDescription": "Path to file for TLS certificate authority.",
"markdownDescription": "Path to file for TLS certificate authority. `http.proxySupport` must be set to `on` or `off`, otherwise VS Code will override the proxy agent set by the plugin.",
"type": "string",
"default": ""
},
"coder.tlsAltHost": {
"markdownDescription": "Alternative hostname to use for TLS verification. This is useful when the hostname in the certificate does not match the hostname used to connect.",
"type": "string",
"default": ""
},
Expand Down
73 changes: 57 additions & 16 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { spawn } from "child_process"
import { Api } from "coder/site/src/api/api"
import { ProvisionerJobLog, Workspace } from "coder/site/src/api/typesGenerated"
import fs from "fs/promises"
Expand Down Expand Up @@ -31,6 +32,7 @@ async function createHttpAgent(): Promise<ProxyAgent> {
const certFile = expandPath(String(cfg.get("coder.tlsCertFile") ?? "").trim())
const keyFile = expandPath(String(cfg.get("coder.tlsKeyFile") ?? "").trim())
const caFile = expandPath(String(cfg.get("coder.tlsCaFile") ?? "").trim())
const altHost = expandPath(String(cfg.get("coder.tlsAltHost") ?? "").trim())

return new ProxyAgent({
// Called each time a request is made.
Expand All @@ -41,6 +43,7 @@ async function createHttpAgent(): Promise<ProxyAgent> {
cert: certFile === "" ? undefined : await fs.readFile(certFile),
key: keyFile === "" ? undefined : await fs.readFile(keyFile),
ca: caFile === "" ? undefined : await fs.readFile(caFile),
servername: altHost === "" ? undefined : altHost,
// rejectUnauthorized defaults to true, so we need to explicitly set it to
// false if we want to allow self-signed certificates.
rejectUnauthorized: !insecure,
Expand All @@ -66,7 +69,8 @@ async function getHttpAgent(): Promise<ProxyAgent> {
e.affectsConfiguration("coder.insecure") ||
e.affectsConfiguration("coder.tlsCertFile") ||
e.affectsConfiguration("coder.tlsKeyFile") ||
e.affectsConfiguration("coder.tlsCaFile")
e.affectsConfiguration("coder.tlsCaFile") ||
e.affectsConfiguration("coder.tlsAltHost")
) {
agent = createHttpAgent()
}
Expand Down Expand Up @@ -119,29 +123,66 @@ export async function makeCoderSdk(baseUrl: string, token: string | undefined, s
/**
* Start or update a workspace and return the updated workspace.
*/
export async function startWorkspaceIfStoppedOrFailed(restClient: Api, workspace: Workspace): Promise<Workspace> {
// If the workspace requires the latest active template version, we should attempt
// to update that here.
// TODO: If param set changes, what do we do??
const versionID = workspace.template_require_active_version
? // Use the latest template version
workspace.template_active_version_id
: // Default to not updating the workspace if not required.
workspace.latest_build.template_version_id

export async function startWorkspaceIfStoppedOrFailed(
restClient: Api,
globalConfigDir: string,
binPath: string,
workspace: Workspace,
writeEmitter: vscode.EventEmitter<string>,
): Promise<Workspace> {
// Before we start a workspace, we make an initial request to check it's not already started
const updatedWorkspace = await restClient.getWorkspace(workspace.id)

if (!["stopped", "failed"].includes(updatedWorkspace.latest_build.status)) {
return updatedWorkspace
}

const latestBuild = await restClient.startWorkspace(updatedWorkspace.id, versionID)
return new Promise((resolve, reject) => {
const startArgs = [
"--global-config",
globalConfigDir,
"start",
"--yes",
workspace.owner_name + "/" + workspace.name,
]
const startProcess = spawn(binPath, startArgs)

startProcess.stdout.on("data", (data: Buffer) => {
data
.toString()
.split(/\r*\n/)
.forEach((line: string) => {
if (line !== "") {
writeEmitter.fire(line.toString() + "\r\n")
}
})
})

let capturedStderr = ""
startProcess.stderr.on("data", (data: Buffer) => {
data
.toString()
.split(/\r*\n/)
.forEach((line: string) => {
if (line !== "") {
writeEmitter.fire(line.toString() + "\r\n")
capturedStderr += line.toString() + "\n"
}
})
})

return {
...updatedWorkspace,
latest_build: latestBuild,
}
startProcess.on("close", (code: number) => {
if (code === 0) {
resolve(restClient.getWorkspace(workspace.id))
} else {
let errorText = `"${startArgs.join(" ")}" exited with code ${code}`
if (capturedStderr !== "") {
errorText += `: ${capturedStderr}`
}
reject(new Error(errorText))
}
})
})
}

/**
Expand Down
25 changes: 17 additions & 8 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export class Commands {
const inputUrl = args[0]
const inputToken = args[1]
const inputLabel = args[2]
const isAutologin = typeof args[3] === "undefined" ? false : Boolean(args[3])

const url = await this.maybeAskUrl(inputUrl)
if (!url) {
Expand All @@ -155,7 +156,7 @@ export class Commands {
const label = typeof inputLabel === "undefined" ? toSafeHost(url) : inputLabel

// Try to get a token from the user, if we need one, and their user.
const res = await this.maybeAskToken(url, inputToken)
const res = await this.maybeAskToken(url, inputToken, isAutologin)
if (!res) {
return // The user aborted, or unable to auth.
}
Expand Down Expand Up @@ -202,7 +203,11 @@ export class Commands {
* token. Null means the user aborted or we were unable to authenticate with
* mTLS (in the latter case, an error notification will have been displayed).
*/
private async maybeAskToken(url: string, token: string): Promise<{ user: User; token: string } | null> {
private async maybeAskToken(
url: string,
token: string,
isAutologin: boolean,
): Promise<{ user: User; token: string } | null> {
const restClient = await makeCoderSdk(url, token, this.storage)
if (!needToken()) {
try {
Expand All @@ -212,11 +217,15 @@ export class Commands {
return { token: "", user }
} catch (err) {
const message = getErrorMessage(err, "no response from the server")
this.vscodeProposed.window.showErrorMessage("Failed to log in", {
detail: message,
modal: true,
useCustom: true,
})
if (isAutologin) {
this.storage.writeToCoderOutputChannel(`Failed to log in to Coder server: ${message}`)
} else {
this.vscodeProposed.window.showErrorMessage("Failed to log in to Coder server", {
detail: message,
modal: true,
useCustom: true,
})
}
// Invalid certificate, most likely.
return null
}
Expand Down Expand Up @@ -529,7 +538,7 @@ async function openWorkspace(
// when opening a workspace unless explicitly specified.
let remoteAuthority = `ssh-remote+${AuthorityPrefix}.${toSafeHost(baseUrl)}--${workspaceOwner}--${workspaceName}`
if (workspaceAgent) {
remoteAuthority += `--${workspaceAgent}`
remoteAuthority += `.${workspaceAgent}`
}

let newWindow = true
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
if (cfg.get("coder.autologin") === true) {
const defaultUrl = cfg.get("coder.defaultUrl") || process.env.CODER_URL
if (defaultUrl) {
vscode.commands.executeCommand("coder.login", defaultUrl)
vscode.commands.executeCommand("coder.login", defaultUrl, undefined, undefined, "true")
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/featureSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as semver from "semver"
export type FeatureSet = {
vscodessh: boolean
proxyLogDirectory: boolean
wildcardSSH: boolean
}

/**
Expand All @@ -21,5 +22,6 @@ export function featureSetForVersion(version: semver.SemVer | null): FeatureSet
// If this check didn't exist, VS Code connections would fail on
// older versions because of an unknown CLI argument.
proxyLogDirectory: (version?.compare("2.3.3") || 0) > 0 || version?.prerelease[0] === "devel",
wildcardSSH: (version?.compare("2.19.0") || 0) > 0 || version?.prerelease[0] === "devel",
}
}
Loading

0 comments on commit 5ba3f76

Please sign in to comment.