Skip to content

Commit da01d0c

Browse files
authored
feat: add option to disable all deployment notifications (#853)
Add a `coder.disableNotifications` setting that suppresses all notification prompts delivered by the Coder deployment (workspace updates, scheduling reminders, resource alerts). Extract notification and header settings into `src/settings/` and add comprehensive tests for WorkspaceMonitor and Inbox notification filtering. Closes #852
1 parent 9f481ff commit da01d0c

29 files changed

+728
-164
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@
121121
"type": "boolean",
122122
"default": false
123123
},
124+
"coder.disableNotifications": {
125+
"markdownDescription": "Disable all notification prompts from the Coder deployment (workspace updates, scheduling reminders, resource alerts, etc.). Notifications are delivered by your Coder server and displayed by this extension.",
126+
"type": "boolean",
127+
"default": false
128+
},
124129
"coder.disableUpdateNotifications": {
125130
"markdownDescription": "Disable notifications when workspace template updates are available.",
126131
"type": "boolean",

src/api/coderApi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { watchConfigurationChanges } from "../configWatcher";
2121
import { ClientCertificateError } from "../error/clientCertificateError";
2222
import { toError } from "../error/errorUtils";
2323
import { ServerCertificateError } from "../error/serverCertificateError";
24-
import { getHeaderCommand, getHeaders } from "../headers";
24+
import { getHeaders } from "../headers";
2525
import { EventStreamLogger } from "../logging/eventStreamLogger";
2626
import {
2727
createRequestMeta,
@@ -35,6 +35,7 @@ import {
3535
HttpClientLogLevel,
3636
} from "../logging/types";
3737
import { sizeOf } from "../logging/utils";
38+
import { getHeaderCommand } from "../settings/headers";
3839
import { HttpStatusCode, WebSocketCloseCode } from "../websocket/codes";
3940
import {
4041
type UnidirectionalStream,

src/api/workspace.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
import { spawn } from "node:child_process";
88
import * as vscode from "vscode";
99

10-
import { type CliAuth, getGlobalFlags } from "../cliConfig";
1110
import { type FeatureSet } from "../featureSet";
11+
import { type CliAuth, getGlobalFlags } from "../settings/cli";
1212
import { escapeCommandArg } from "../util";
1313
import { type UnidirectionalStream } from "../websocket/eventStreamConnection";
1414

src/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import * as vscode from "vscode";
99

1010
import { createWorkspaceIdentifier, extractAgents } from "./api/api-helper";
1111
import { type CoderApi } from "./api/coderApi";
12-
import { getGlobalFlags, resolveCliAuth } from "./cliConfig";
1312
import { type CliManager } from "./core/cliManager";
1413
import * as cliUtils from "./core/cliUtils";
1514
import { type ServiceContainer } from "./core/container";
@@ -28,6 +27,7 @@ import {
2827
RECOMMENDED_SSH_SETTINGS,
2928
applySettingOverrides,
3029
} from "./remote/userSettings";
30+
import { getGlobalFlags, resolveCliAuth } from "./settings/cli";
3131
import { escapeCommandArg, toRemoteAuthority, toSafeHost } from "./util";
3232
import { vscodeProposed } from "./vscodeProposed";
3333
import {

src/core/cliCredentialManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import path from "node:path";
55
import { promisify } from "node:util";
66
import * as semver from "semver";
77

8-
import { isKeyringEnabled } from "../cliConfig";
98
import { isAbortError } from "../error/errorUtils";
109
import { featureSetForVersion } from "../featureSet";
11-
import { getHeaderArgs } from "../headers";
10+
import { isKeyringEnabled } from "../settings/cli";
11+
import { getHeaderArgs } from "../settings/headers";
1212
import { renameWithRetry, tempFilePath, toSafeHost } from "../util";
1313

1414
import * as cliUtils from "./cliUtils";

src/core/cliManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import * as semver from "semver";
1010
import * as vscode from "vscode";
1111

1212
import { errToStr } from "../api/api-helper";
13-
import { isKeyringEnabled } from "../cliConfig";
1413
import * as pgp from "../pgp";
1514
import { withCancellableProgress, withOptionalProgress } from "../progress";
15+
import { isKeyringEnabled } from "../settings/cli";
1616
import { tempFilePath, toSafeHost } from "../util";
1717
import { vscodeProposed } from "../vscodeProposed";
1818

src/headers.ts

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,10 @@
1-
import * as os from "node:os";
2-
31
import { execCommand } from "./command/exec";
42
import { type Logger } from "./logging/logger";
5-
import { escapeCommandArg } from "./util";
6-
7-
import type { WorkspaceConfiguration } from "vscode";
8-
9-
export function getHeaderCommand(
10-
config: Pick<WorkspaceConfiguration, "get">,
11-
): string | undefined {
12-
const cmd =
13-
config.get<string>("coder.headerCommand")?.trim() ||
14-
process.env.CODER_HEADER_COMMAND?.trim();
15-
16-
return cmd || undefined;
17-
}
18-
19-
export function getHeaderArgs(
20-
config: Pick<WorkspaceConfiguration, "get">,
21-
): string[] {
22-
// Escape a command line to be executed by the Coder binary, so ssh doesn't substitute variables.
23-
const escapeSubcommand: (str: string) => string =
24-
os.platform() === "win32"
25-
? // On Windows variables are %VAR%, and we need to use double quotes.
26-
(str) => escapeCommandArg(str).replace(/%/g, "%%")
27-
: // On *nix we can use single quotes to escape $VARS.
28-
// Note single quotes cannot be escaped inside single quotes.
29-
(str) => `'${str.replace(/'/g, "'\\''")}'`;
30-
31-
const command = getHeaderCommand(config);
32-
if (!command) {
33-
return [];
34-
}
35-
return ["--header-command", escapeSubcommand(command)];
36-
}
373

384
/**
39-
* getHeaders executes the header command and parses the headers from stdout.
40-
* Both stdout and stderr are logged on error but stderr is otherwise ignored.
41-
* Throws an error if the process exits with non-zero or the JSON is invalid.
42-
* Returns undefined if there is no header command set. No effort is made to
43-
* validate the JSON other than making sure it can be parsed.
5+
* Executes the header command and parses headers from stdout.
6+
* Throws on non-zero exit or malformed output. Returns empty headers if no
7+
* command is set.
448
*/
459
export async function getHeaders(
4610
url: string | undefined,

src/inbox.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as vscode from "vscode";
22

3+
import { areNotificationsDisabled } from "./settings/notifications";
4+
35
import type {
46
Workspace,
57
GetInboxNotificationResponse,
@@ -53,7 +55,9 @@ export class Inbox implements vscode.Disposable {
5355
socket.addEventListener("message", (data) => {
5456
if (data.parseError) {
5557
logger.error("Failed to parse inbox message", data.parseError);
56-
} else {
58+
} else if (
59+
!areNotificationsDisabled(vscode.workspace.getConfiguration())
60+
) {
5761
vscode.window.showInformationMessage(
5862
data.parsedMessage.notification.title,
5963
);

src/login/loginCoordinator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import * as vscode from "vscode";
44

55
import { CoderApi } from "../api/coderApi";
66
import { needToken } from "../api/utils";
7-
import { isKeyringEnabled } from "../cliConfig";
87
import { CertificateError } from "../error/certificateError";
98
import { OAuthAuthorizer } from "../oauth/authorizer";
109
import { buildOAuthTokenData } from "../oauth/utils";
1110
import { withOptionalProgress } from "../progress";
1211
import { maybeAskAuthMethod, maybeAskUrl } from "../promptUtils";
12+
import { isKeyringEnabled } from "../settings/cli";
1313
import { vscodeProposed } from "../vscodeProposed";
1414

1515
import type { User } from "coder/site/src/api/typesGenerated";

src/remote/remote.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@ import { extractAgents } from "../api/api-helper";
2020
import { AuthInterceptor } from "../api/authInterceptor";
2121
import { CoderApi } from "../api/coderApi";
2222
import { needToken } from "../api/utils";
23-
import {
24-
type CliAuth,
25-
getGlobalFlags,
26-
getGlobalFlagsRaw,
27-
getSshFlags,
28-
resolveCliAuth,
29-
} from "../cliConfig";
3023
import { type Commands } from "../commands";
3124
import { watchConfigurationChanges } from "../configWatcher";
3225
import { type CliManager } from "../core/cliManager";
@@ -37,11 +30,18 @@ import { type PathResolver } from "../core/pathResolver";
3730
import { type SecretsManager } from "../core/secretsManager";
3831
import { toError } from "../error/errorUtils";
3932
import { featureSetForVersion, type FeatureSet } from "../featureSet";
40-
import { getHeaderCommand } from "../headers";
4133
import { Inbox } from "../inbox";
4234
import { type Logger } from "../logging/logger";
4335
import { type LoginCoordinator } from "../login/loginCoordinator";
4436
import { OAuthSessionManager } from "../oauth/sessionManager";
37+
import {
38+
type CliAuth,
39+
getGlobalFlags,
40+
getGlobalFlagsRaw,
41+
getSshFlags,
42+
resolveCliAuth,
43+
} from "../settings/cli";
44+
import { getHeaderCommand } from "../settings/headers";
4545
import {
4646
AuthorityPrefix,
4747
escapeCommandArg,

0 commit comments

Comments
 (0)