Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Added

- Support for `CODER_BINARY_DESTINATION` environment variable to set CLI download location (overridden by extension setting `coder.binaryDestination` if configured).

## [v1.11.0](https://github.com/coder/vscode-coder/releases/tag/v1.11.0) 2025-09-24

### Changed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"default": ""
},
"coder.binaryDestination": {
"markdownDescription": "The full path of the directory into which the Coder CLI will be downloaded. Defaults to the extension's global storage directory.",
"markdownDescription": "The full path of the directory into which the Coder CLI will be downloaded. Defaults to the value of `CODER_BINARY_DESTINATION` if not set, otherwise the extension's global storage directory.",
"type": "string",
"default": ""
},
Expand Down
9 changes: 6 additions & 3 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,13 @@ export class Commands {
* CODER_URL or enter a new one. Undefined means the user aborted.
*/
private async askURL(selection?: string): Promise<string | undefined> {
const defaultURL =
vscode.workspace.getConfiguration().get<string>("coder.defaultUrl") ?? "";
const defaultURL = vscode.workspace
.getConfiguration()
.get<string>("coder.defaultUrl")
?.trim();
const quickPick = vscode.window.createQuickPick();
quickPick.value = selection || defaultURL || process.env.CODER_URL || "";
quickPick.value =
selection || defaultURL || process.env.CODER_URL?.trim() || "";
quickPick.placeholder = "https://example.coder.com";
quickPick.title = "Enter the URL of your Coder deployment.";

Expand Down
27 changes: 25 additions & 2 deletions src/core/pathResolver.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from "path";
import { describe, it, expect, beforeEach } from "vitest";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { MockConfigurationProvider } from "../__mocks__/testHelpers";
import { PathResolver } from "./pathResolver";

Expand All @@ -11,6 +11,7 @@ describe("PathResolver", () => {
let mockConfig: MockConfigurationProvider;

beforeEach(() => {
vi.unstubAllEnvs();
pathResolver = new PathResolver(basePath, codeLogPath);
mockConfig = new MockConfigurationProvider();
});
Expand All @@ -32,6 +33,7 @@ describe("PathResolver", () => {
});

it("should use default path when custom destination is empty or whitespace", () => {
vi.stubEnv("CODER_BINARY_DESTINATION", " ");
mockConfig.set("coder.binaryDestination", " ");
expect(pathResolver.getBinaryCachePath("deployment")).toBe(
path.join(basePath, "deployment", "bin"),
Expand All @@ -41,7 +43,28 @@ describe("PathResolver", () => {
it("should normalize custom paths", () => {
mockConfig.set("coder.binaryDestination", "/custom/../binary/./path");
expect(pathResolver.getBinaryCachePath("deployment")).toBe(
path.normalize("/custom/../binary/./path"),
"/binary/path",
);
});

it("should use CODER_BINARY_DESTINATION environment variable with proper precedence", () => {
// Use the global storage when the environment variable and setting are unset/blank
vi.stubEnv("CODER_BINARY_DESTINATION", "");
mockConfig.set("coder.binaryDestination", "");
expect(pathResolver.getBinaryCachePath("deployment")).toBe(
path.join(basePath, "deployment", "bin"),
);

// Test environment variable takes precedence over global storage
vi.stubEnv("CODER_BINARY_DESTINATION", " /env/binary/path ");
expect(pathResolver.getBinaryCachePath("deployment")).toBe(
"/env/binary/path",
);

// Test setting takes precedence over environment variable
mockConfig.set("coder.binaryDestination", " /setting/path ");
expect(pathResolver.getBinaryCachePath("deployment")).toBe(
"/setting/path",
);
});
});
Expand Down
11 changes: 7 additions & 4 deletions src/core/pathResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ export class PathResolver {
* The caller must ensure this directory exists before use.
*/
public getBinaryCachePath(label: string): string {
const configPath = vscode.workspace
const settingPath = vscode.workspace
.getConfiguration()
.get<string>("coder.binaryDestination");
return configPath && configPath.trim().length > 0
? path.normalize(configPath)
.get<string>("coder.binaryDestination")
?.trim();
const binaryPath =
settingPath || process.env.CODER_BINARY_DESTINATION?.trim();
return binaryPath
? path.normalize(binaryPath)
: path.join(this.getGlobalConfigDir(label), "bin");
}

Expand Down
4 changes: 3 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,9 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
// Handle autologin, if not already logged in.
const cfg = vscode.workspace.getConfiguration();
if (cfg.get("coder.autologin") === true) {
const defaultUrl = cfg.get("coder.defaultUrl") || process.env.CODER_URL;
const defaultUrl =
cfg.get<string>("coder.defaultUrl")?.trim() ||
process.env.CODER_URL?.trim();
if (defaultUrl) {
vscode.commands.executeCommand(
"coder.login",
Expand Down
4 changes: 2 additions & 2 deletions src/headers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,9 @@ describe("getHeaderCommand", () => {
expect(getHeaderCommand(config)).toBeUndefined();
});

it("should return undefined if coder.headerCommand is not a string", () => {
it("should return undefined if coder.headerCommand is a blank string", () => {
const config = {
get: () => 1234,
get: () => " ",
} as unknown as WorkspaceConfiguration;

expect(getHeaderCommand(config)).toBeUndefined();
Expand Down
9 changes: 4 additions & 5 deletions src/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ export function getHeaderCommand(
config: WorkspaceConfiguration,
): string | undefined {
const cmd =
config.get("coder.headerCommand") || process.env.CODER_HEADER_COMMAND;
if (!cmd || typeof cmd !== "string") {
return undefined;
}
return cmd;
config.get<string>("coder.headerCommand")?.trim() ||
process.env.CODER_HEADER_COMMAND?.trim();

return cmd ? cmd : undefined;
}

export function getHeaderArgs(config: WorkspaceConfiguration): string[] {
Expand Down