From f7417d10212c8de3f77991740f38355c7845be07 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 31 Mar 2022 10:33:20 -0700 Subject: [PATCH 01/15] feat(cli): add disable-file-downloads to cli --- src/node/cli.ts | 10 ++++++++++ test/unit/node/cli.test.ts | 4 ++++ test/unit/node/plugin.test.ts | 1 + 3 files changed, 15 insertions(+) diff --git a/src/node/cli.ts b/src/node/cli.ts index 6dd260c19124..717532c0695b 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -49,6 +49,7 @@ export interface UserProvidedCodeArgs { category?: string "github-auth"?: string "disable-update-check"?: boolean + "disable-file-downloads"?: boolean } /** @@ -157,6 +158,10 @@ export const options: Options> = { "Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and \n" + "then notifies you once every week that a new release is available.", }, + "disable-file-downloads": { + type: "boolean", + description: "Disable file downloads from Code. Defaults to true.", + }, // --enable can be used to enable experimental features. These features // provide no guarantees. enable: { type: "string[]" }, @@ -445,6 +450,7 @@ export interface DefaultedArgs extends ConfigArgs { usingEnvHashedPassword: boolean "extensions-dir": string "user-data-dir": string + "disable-file-downloads": boolean /* Positional arguments. */ _: string[] } @@ -556,6 +562,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, ""))) args["proxy-domain"] = Array.from(proxyDomains) + if (!args["disable-file-downloads"]) { + args["disable-file-downloads"] = false + } + if (typeof args._ === "undefined") { args._ = [] } diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index 0b04adadb4af..0945914ca42e 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -31,6 +31,7 @@ const defaults = { host: "localhost", port: 8080, "proxy-domain": [], + "disable-file-downloads": false, usingEnvPassword: false, usingEnvHashedPassword: false, "extensions-dir": path.join(paths.data, "extensions"), @@ -92,6 +93,8 @@ describe("parser", () => { "--port=8081", + "--disable-file-downloads", + ["--host", "0.0.0.0"], "4", "--", @@ -108,6 +111,7 @@ describe("parser", () => { cert: { value: path.resolve("path/to/cert"), }, + "disable-file-downloads": true, enable: ["feature1", "feature2"], help: true, host: "0.0.0.0", diff --git a/test/unit/node/plugin.test.ts b/test/unit/node/plugin.test.ts index 73923415b57e..88210fd8594a 100644 --- a/test/unit/node/plugin.test.ts +++ b/test/unit/node/plugin.test.ts @@ -38,6 +38,7 @@ describe("plugin", () => { "proxy-domain": [], config: "~/.config/code-server/config.yaml", verbose: false, + "disable-file-downloads": false, usingEnvPassword: false, usingEnvHashedPassword: false, "extensions-dir": "", From dd9a2c383b9f47cb7e184e447894938fe1921dcb Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 31 Mar 2022 13:00:28 -0700 Subject: [PATCH 02/15] feat(e2e): add download test --- test/e2e/downloads.test.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/e2e/downloads.test.ts diff --git a/test/e2e/downloads.test.ts b/test/e2e/downloads.test.ts new file mode 100644 index 000000000000..798d18ccf85e --- /dev/null +++ b/test/e2e/downloads.test.ts @@ -0,0 +1,26 @@ +import * as path from "path" +import { promises as fs } from "fs" +import { clean } from "../utils/helpers" +import { describe, test, expect } from "./baseFixture" + +describe("Downloads", true, [], {}, async () => { + const testName = "downloads" + test.beforeAll(async () => { + await clean(testName) + }) + + test("should see the 'Download...' option", async ({ codeServerPage }) => { + // Setup + const workspaceDir = await codeServerPage.workspaceDir + const tmpFilePath = path.join(workspaceDir, "unique-file.txt") + await fs.writeFile(tmpFilePath, "hello world") + + // Action + const fileInExplorer = await codeServerPage.page.waitForSelector("text=unique-file.txt") + await fileInExplorer.click({ + button: "right", + }) + + expect(await codeServerPage.page.isVisible("text=Download...")).toBe(true) + }) +}) From 05e789beb4e239aec23d308bc073402b79d8b234 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 31 Mar 2022 15:36:01 -0700 Subject: [PATCH 03/15] feat(e2e): add downloads disabled test --- test/e2e/downloads.test.ts | 2 +- test/e2e/downloadsDisabled.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 test/e2e/downloadsDisabled.test.ts diff --git a/test/e2e/downloads.test.ts b/test/e2e/downloads.test.ts index 798d18ccf85e..a6d59c532ce7 100644 --- a/test/e2e/downloads.test.ts +++ b/test/e2e/downloads.test.ts @@ -3,7 +3,7 @@ import { promises as fs } from "fs" import { clean } from "../utils/helpers" import { describe, test, expect } from "./baseFixture" -describe("Downloads", true, [], {}, async () => { +describe("Downloads (enabled)", true, [], {}, async () => { const testName = "downloads" test.beforeAll(async () => { await clean(testName) diff --git a/test/e2e/downloadsDisabled.test.ts b/test/e2e/downloadsDisabled.test.ts new file mode 100644 index 000000000000..aa3c45ad65c3 --- /dev/null +++ b/test/e2e/downloadsDisabled.test.ts @@ -0,0 +1,26 @@ +import * as path from "path" +import { promises as fs } from "fs" +import { clean } from "../utils/helpers" +import { describe, test, expect } from "./baseFixture" + +describe("Downloads (disabled)", true, ["--disable-file-downloads"], {}, async () => { + const testName = "downloads" + test.beforeAll(async () => { + await clean(testName) + }) + + test("should not see the 'Download...' option", async ({ codeServerPage }) => { + // Setup + const workspaceDir = await codeServerPage.workspaceDir + const tmpFilePath = path.join(workspaceDir, "unique-file.txt") + await fs.writeFile(tmpFilePath, "hello world") + + // Action + const fileInExplorer = await codeServerPage.page.waitForSelector("text=unique-file.txt") + await fileInExplorer.click({ + button: "right", + }) + + expect(await codeServerPage.page.isVisible("text=Download...")).toBe(false) + }) +}) From cd3f248d0ec693655521256681639fe54b8cae9c Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 1 Apr 2022 16:55:12 -0700 Subject: [PATCH 04/15] refactor(e2e): explain how to debug unexpected close --- test/e2e/models/CodeServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/models/CodeServer.ts b/test/e2e/models/CodeServer.ts index e2bbdc1d6920..53e2208d270b 100644 --- a/test/e2e/models/CodeServer.ts +++ b/test/e2e/models/CodeServer.ts @@ -134,7 +134,7 @@ export class CodeServer { }) proc.on("close", (code) => { - const error = new Error("code-server closed unexpectedly") + const error = new Error("code-server closed unexpectedly. Try running with LOG_LEVEL=debug to see more info.") if (!this.closed) { this.logger.error(error.message, field("code", code)) } From 8e61aff8e38b69e05c46ae3c22cdbfcd2c91f14c Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 1 Apr 2022 16:55:46 -0700 Subject: [PATCH 05/15] feat(patches): add disable file downloads --- patches/disable-downloads.diff | 113 +++++++++++++++++++++++++++++++++ patches/series | 1 + 2 files changed, 114 insertions(+) create mode 100644 patches/disable-downloads.diff diff --git a/patches/disable-downloads.diff b/patches/disable-downloads.diff new file mode 100644 index 000000000000..f096f9c1f17e --- /dev/null +++ b/patches/disable-downloads.diff @@ -0,0 +1,113 @@ +Add option to disable file downloads via CLI + +This patch adds support for a new CLI flag called `--disable-file-downloads` +which allows a user to remove the "Download..." option that shows up when you +right-click files in Code. The default value for this is `false`. + +To test this, start code-server with `--disable-file-downloads`, open editor, +right-click on a file (not a folder) and you should **not** see the +"Download..." option. + +Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/browser/web.api.ts ++++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +@@ -210,6 +210,11 @@ export interface IWorkbenchConstructionO + */ + readonly userDataPath?: string + ++ /** ++ * Whether the "Download..." option is enabled for files. ++ */ ++ readonly isEnabledFileDownloads?: boolean ++ + //#endregion + + +Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts ++++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts +@@ -61,6 +61,13 @@ export class BrowserWorkbenchEnvironment + return this.options.userDataPath; + } + ++ get isEnabledFileDownloads(): boolean { ++ if (typeof this.options.isEnabledFileDownloads === "undefined") { ++ throw new Error('isEnabledFileDownloads was not provided to the browser'); ++ } ++ return this.options.isEnabledFileDownloads; ++ } ++ + @memoize + get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } + +Index: code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/server/node/serverEnvironmentService.ts ++++ code-server/lib/vscode/src/vs/server/node/serverEnvironmentService.ts +@@ -15,6 +15,7 @@ export const serverOptions: OptionDescri + 'disable-update-check': { type: 'boolean' }, + 'auth': { type: 'string' }, + 'locale': { type: 'string' }, ++ 'disable-file-downloads': { type: 'boolean' }, + + /* ----- server setup ----- */ + +@@ -92,6 +93,7 @@ export interface ServerParsedArgs { + 'disable-update-check'?: boolean; + 'auth'?: string + 'locale'?: string ++ 'disable-file-downloads'?: boolean; + + /* ----- server setup ----- */ + +Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts ++++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts +@@ -290,6 +290,7 @@ export class WebClientServer { + logLevel: this._logService.getLevel(), + }, + userDataPath: this._environmentService.userDataPath, ++ isEnabledFileDownloads: this._environmentService.args['disable-file-downloads'], + settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, + productConfiguration: >{ + rootEndpoint: base, +Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts ++++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts +@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/ev + import { Disposable } from 'vs/base/common/lifecycle'; + import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; + import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext } from 'vs/platform/contextkey/common/contextkeys'; +-import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext } from 'vs/workbench/common/contextkeys'; ++import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys'; + import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; + import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; + import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +@@ -194,6 +194,9 @@ export class WorkbenchContextKeysHandler + this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService); + this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)); + ++ // code-server ++ IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads) ++ + this.registerListeners(); + } + +Index: code-server/lib/vscode/src/vs/workbench/services/environment/common/environmentService.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/services/environment/common/environmentService.ts ++++ code-server/lib/vscode/src/vs/workbench/services/environment/common/environmentService.ts +@@ -44,6 +44,9 @@ export interface IWorkbenchEnvironmentSe + readonly filesToOpenOrCreate?: IPath[] | undefined; + readonly filesToDiff?: IPath[] | undefined; + ++ // --- code-server ++ readonly isEnabledFileDownloads: boolean; ++ + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. AS SUCH: + // - PUT NON-WEB PROPERTIES INTO NATIVE WB ENV SERVICE diff --git a/patches/series b/patches/series index 87e8f3948a90..79da7efdaf7b 100644 --- a/patches/series +++ b/patches/series @@ -18,3 +18,4 @@ local-storage.diff service-worker.diff connection-type.diff sourcemaps.diff +disable-downloads.diff From 36e71916f1b569a27e7f929d8832eaf00bd97ee4 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 4 Apr 2022 13:37:43 -0700 Subject: [PATCH 06/15] wip: update diff --- patches/disable-downloads.diff | 39 +++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/patches/disable-downloads.diff b/patches/disable-downloads.diff index f096f9c1f17e..7f3c0e8113a1 100644 --- a/patches/disable-downloads.diff +++ b/patches/disable-downloads.diff @@ -70,7 +70,7 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts logLevel: this._logService.getLevel(), }, userDataPath: this._environmentService.userDataPath, -+ isEnabledFileDownloads: this._environmentService.args['disable-file-downloads'], ++ isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'], settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, productConfiguration: >{ rootEndpoint: base, @@ -111,3 +111,40 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/common/envir // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. AS SUCH: // - PUT NON-WEB PROPERTIES INTO NATIVE WB ENV SERVICE +Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts ++++ code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +@@ -21,7 +21,7 @@ import { CLOSE_SAVED_EDITORS_COMMAND_ID, + import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; + import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; + import { Schemas } from 'vs/base/common/network'; +-import { DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey } from 'vs/workbench/common/contextkeys'; ++import { DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, IsEnabledFileDownloads } from 'vs/workbench/common/contextkeys'; + import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; + import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; + import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +@@ -475,13 +475,16 @@ MenuRegistry.appendMenuItem(MenuId.Explo + id: DOWNLOAD_COMMAND_ID, + title: DOWNLOAD_LABEL + }, +- when: ContextKeyExpr.or( +- // native: for any remote resource +- ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.notEqualsTo(Schemas.file)), +- // web: for any files +- ContextKeyExpr.and(IsWebContext, ExplorerFolderContext.toNegated(), ExplorerRootContext.toNegated()), +- // web: for any folders if file system API support is provided +- ContextKeyExpr.and(IsWebContext, HasWebFileSystemAccess) ++ when: ContextKeyExpr.and( ++ IsEnabledFileDownloads, ++ ContextKeyExpr.or( ++ // native: for any remote resource ++ ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.notEqualsTo(Schemas.file)), ++ // web: for any files ++ ContextKeyExpr.and(IsWebContext, ExplorerFolderContext.toNegated(), ExplorerRootContext.toNegated()), ++ // web: for any folders if file system API support is provided ++ ContextKeyExpr.and(IsWebContext, HasWebFileSystemAccess) ++ ) + ) + })); + From 4909569c9b31bf9105c8154f097f984dbfc64a63 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 5 Apr 2022 11:30:17 -0700 Subject: [PATCH 07/15] Update src/node/cli.ts Co-authored-by: Asher --- src/node/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 717532c0695b..d06925bf8b74 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -160,7 +160,7 @@ export const options: Options> = { }, "disable-file-downloads": { type: "boolean", - description: "Disable file downloads from Code. Defaults to true.", + description: "Disable file downloads from Code.", }, // --enable can be used to enable experimental features. These features // provide no guarantees. From 491d43ad2d01368b322bc4718136e014806b4204 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 5 Apr 2022 11:36:29 -0700 Subject: [PATCH 08/15] fixup! add missing common/contextkeys file to patch --- patches/disable-downloads.diff | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/patches/disable-downloads.diff b/patches/disable-downloads.diff index 7f3c0e8113a1..3e643ec53982 100644 --- a/patches/disable-downloads.diff +++ b/patches/disable-downloads.diff @@ -148,3 +148,16 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions ) })); +Index: code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/common/contextkeys.ts ++++ code-server/lib/vscode/src/vs/workbench/common/contextkeys.ts +@@ -30,6 +30,8 @@ export const IsFullscreenContext = new R + + export const HasWebFileSystemAccess = new RawContextKey('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access) + ++export const IsEnabledFileDownloads = new RawContextKey('isEnabledFileDownloads', true, true); ++ + //#endregion + + From fd581d785080b48b39aa5d4b52c317478826ded1 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 11 Apr 2022 15:18:10 -0700 Subject: [PATCH 09/15] fixup!: update patch --- patches/disable-downloads.diff | 54 +++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/patches/disable-downloads.diff b/patches/disable-downloads.diff index 3e643ec53982..3c4b87da9102 100644 --- a/patches/disable-downloads.diff +++ b/patches/disable-downloads.diff @@ -28,7 +28,19 @@ Index: code-server/lib/vscode/src/vs/workbench/services/environment/browser/envi =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts +++ code-server/lib/vscode/src/vs/workbench/services/environment/browser/environmentService.ts -@@ -61,6 +61,13 @@ export class BrowserWorkbenchEnvironment +@@ -30,6 +30,11 @@ export interface IBrowserWorkbenchEnviro + * Options used to configure the workbench. + */ + readonly options?: IWorkbenchConstructionOptions; ++ ++ /** ++ * Enable downloading files via menu actions. ++ */ ++ readonly isEnabledFileDownloads?: boolean; + } + + export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { +@@ -61,6 +66,13 @@ export class BrowserWorkbenchEnvironment return this.options.userDataPath; } @@ -78,7 +90,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/contextkeys.ts +++ code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts -@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/ev +@@ -7,12 +7,11 @@ import { Event } from 'vs/base/common/ev import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext } from 'vs/platform/contextkey/common/contextkeys'; @@ -87,30 +99,38 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/contextkeys.ts import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; + import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +-import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; + import { IWorkbenchLayoutService, Parts, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; +@@ -24,6 +23,7 @@ import { IEditorResolverService } from ' + import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; + import { Schemas } from 'vs/base/common/network'; + import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; ++import { IBrowserWorkbenchEnvironmentService } from '../services/environment/browser/environmentService'; + + export class WorkbenchContextKeysHandler extends Disposable { + private inputFocusedContext: IContextKey; +@@ -75,7 +75,7 @@ export class WorkbenchContextKeysHandler + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IConfigurationService private readonly configurationService: IConfigurationService, +- @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, ++ @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, + @IEditorService private readonly editorService: IEditorService, + @IEditorResolverService private readonly editorResolverService: IEditorResolverService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -194,6 +194,9 @@ export class WorkbenchContextKeysHandler this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService); this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART)); + // code-server -+ IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads) ++ IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true) + this.registerListeners(); } -Index: code-server/lib/vscode/src/vs/workbench/services/environment/common/environmentService.ts -=================================================================== ---- code-server.orig/lib/vscode/src/vs/workbench/services/environment/common/environmentService.ts -+++ code-server/lib/vscode/src/vs/workbench/services/environment/common/environmentService.ts -@@ -44,6 +44,9 @@ export interface IWorkbenchEnvironmentSe - readonly filesToOpenOrCreate?: IPath[] | undefined; - readonly filesToDiff?: IPath[] | undefined; - -+ // --- code-server -+ readonly isEnabledFileDownloads: boolean; -+ - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. AS SUCH: - // - PUT NON-WEB PROPERTIES INTO NATIVE WB ENV SERVICE Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts From df8fff617eb2fd1c7d1baf4474542f157c4cf6c8 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 11 Apr 2022 15:28:10 -0700 Subject: [PATCH 10/15] fixup!: default disable-file-downloads undefined --- src/node/cli.ts | 5 ----- test/unit/node/cli.test.ts | 1 - 2 files changed, 6 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index d06925bf8b74..8dee68c476a9 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -450,7 +450,6 @@ export interface DefaultedArgs extends ConfigArgs { usingEnvHashedPassword: boolean "extensions-dir": string "user-data-dir": string - "disable-file-downloads": boolean /* Positional arguments. */ _: string[] } @@ -562,10 +561,6 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, ""))) args["proxy-domain"] = Array.from(proxyDomains) - if (!args["disable-file-downloads"]) { - args["disable-file-downloads"] = false - } - if (typeof args._ === "undefined") { args._ = [] } diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index 0945914ca42e..af00f9ce4d45 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -31,7 +31,6 @@ const defaults = { host: "localhost", port: 8080, "proxy-domain": [], - "disable-file-downloads": false, usingEnvPassword: false, usingEnvHashedPassword: false, "extensions-dir": path.join(paths.data, "extensions"), From 384c924572766d9a20c9a032be89712c14ab9d64 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 11 Apr 2022 15:42:51 -0700 Subject: [PATCH 11/15] fixup!: combine e2e tests --- test/e2e/downloads.test.ts | 22 ++++++++++++++++++++++ test/e2e/downloadsDisabled.test.ts | 26 -------------------------- 2 files changed, 22 insertions(+), 26 deletions(-) delete mode 100644 test/e2e/downloadsDisabled.test.ts diff --git a/test/e2e/downloads.test.ts b/test/e2e/downloads.test.ts index a6d59c532ce7..cee0149a080a 100644 --- a/test/e2e/downloads.test.ts +++ b/test/e2e/downloads.test.ts @@ -24,3 +24,25 @@ describe("Downloads (enabled)", true, [], {}, async () => { expect(await codeServerPage.page.isVisible("text=Download...")).toBe(true) }) }) + +describe("Downloads (disabled)", true, ["--disable-file-downloads"], {}, async () => { + const testName = "downloads" + test.beforeAll(async () => { + await clean(testName) + }) + + test("should not see the 'Download...' option", async ({ codeServerPage }) => { + // Setup + const workspaceDir = await codeServerPage.workspaceDir + const tmpFilePath = path.join(workspaceDir, "unique-file.txt") + await fs.writeFile(tmpFilePath, "hello world") + + // Action + const fileInExplorer = await codeServerPage.page.waitForSelector("text=unique-file.txt") + await fileInExplorer.click({ + button: "right", + }) + + expect(await codeServerPage.page.isVisible("text=Download...")).toBe(false) + }) +}) diff --git a/test/e2e/downloadsDisabled.test.ts b/test/e2e/downloadsDisabled.test.ts deleted file mode 100644 index aa3c45ad65c3..000000000000 --- a/test/e2e/downloadsDisabled.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as path from "path" -import { promises as fs } from "fs" -import { clean } from "../utils/helpers" -import { describe, test, expect } from "./baseFixture" - -describe("Downloads (disabled)", true, ["--disable-file-downloads"], {}, async () => { - const testName = "downloads" - test.beforeAll(async () => { - await clean(testName) - }) - - test("should not see the 'Download...' option", async ({ codeServerPage }) => { - // Setup - const workspaceDir = await codeServerPage.workspaceDir - const tmpFilePath = path.join(workspaceDir, "unique-file.txt") - await fs.writeFile(tmpFilePath, "hello world") - - // Action - const fileInExplorer = await codeServerPage.page.waitForSelector("text=unique-file.txt") - await fileInExplorer.click({ - button: "right", - }) - - expect(await codeServerPage.page.isVisible("text=Download...")).toBe(false) - }) -}) From b7c164cb64a5fe4e3a0db7b10318a8957f6e1e08 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Mon, 11 Apr 2022 15:43:21 -0700 Subject: [PATCH 12/15] fixup!: use different test names --- test/e2e/downloads.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/downloads.test.ts b/test/e2e/downloads.test.ts index cee0149a080a..4503e076ca5b 100644 --- a/test/e2e/downloads.test.ts +++ b/test/e2e/downloads.test.ts @@ -4,7 +4,7 @@ import { clean } from "../utils/helpers" import { describe, test, expect } from "./baseFixture" describe("Downloads (enabled)", true, [], {}, async () => { - const testName = "downloads" + const testName = "downloads-enabled" test.beforeAll(async () => { await clean(testName) }) @@ -26,7 +26,7 @@ describe("Downloads (enabled)", true, [], {}, async () => { }) describe("Downloads (disabled)", true, ["--disable-file-downloads"], {}, async () => { - const testName = "downloads" + const testName = "downloads-disabled" test.beforeAll(async () => { await clean(testName) }) From fcc8086138be6a8eea75eff9ca382b13f62b673e Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 12 Apr 2022 16:16:46 -0700 Subject: [PATCH 13/15] feat: add CS_DISABLE_FILE_DOWNLOADS --- src/node/cli.ts | 5 +++++ test/unit/node/cli.test.ts | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/node/cli.ts b/src/node/cli.ts index 8dee68c476a9..d88ab5e51760 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -542,6 +542,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config args.password = process.env.PASSWORD } + if (process.env.CS_DISABLE_FILE_DOWNLOADS) { + args["disable-file-downloads"] = true + } + const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD if (process.env.HASHED_PASSWORD) { args["hashed-password"] = process.env.HASHED_PASSWORD @@ -556,6 +560,7 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config delete process.env.PASSWORD delete process.env.HASHED_PASSWORD delete process.env.GITHUB_TOKEN + delete process.env.CS_DISABLE_FILE_DOWNLOADS // Filter duplicate proxy domains and remove any leading `*.`. const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, ""))) diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index af00f9ce4d45..ea2144aeafce 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -349,6 +349,18 @@ describe("parser", () => { expect(process.env.GITHUB_TOKEN).toBe(undefined) }) + it("should use env var CS_DISABLE_FILE_DOWNLOADS", async () => { + process.env.CS_DISABLE_FILE_DOWNLOADS = "0" + const args = parse([]) + expect(args).toEqual({}) + + const defaultArgs = await setDefaults(args) + expect(defaultArgs).toEqual({ + ...defaults, + "disable-file-downloads": true, + }) + }) + it("should error if password passed in", () => { expect(() => parse(["--password", "supersecret123"])).toThrowError( "--password can only be set in the config file or passed in via $PASSWORD", From d98a6ddc7af0bb1e8710c589d65a1c2947f5c1df Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 12 Apr 2022 16:30:45 -0700 Subject: [PATCH 14/15] fixup!: make explicit and cleanup test --- src/node/cli.ts | 3 +-- test/unit/node/cli.test.ts | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index d88ab5e51760..2522a6268516 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -542,7 +542,7 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config args.password = process.env.PASSWORD } - if (process.env.CS_DISABLE_FILE_DOWNLOADS) { + if (process.env.CS_DISABLE_FILE_DOWNLOADS === "1") { args["disable-file-downloads"] = true } @@ -560,7 +560,6 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config delete process.env.PASSWORD delete process.env.HASHED_PASSWORD delete process.env.GITHUB_TOKEN - delete process.env.CS_DISABLE_FILE_DOWNLOADS // Filter duplicate proxy domains and remove any leading `*.`. const proxyDomains = new Set((args["proxy-domain"] || []).map((d) => d.replace(/^\*\./, ""))) diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index ea2144aeafce..e53e26b465d3 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -350,7 +350,7 @@ describe("parser", () => { }) it("should use env var CS_DISABLE_FILE_DOWNLOADS", async () => { - process.env.CS_DISABLE_FILE_DOWNLOADS = "0" + process.env.CS_DISABLE_FILE_DOWNLOADS = "1" const args = parse([]) expect(args).toEqual({}) @@ -359,6 +359,9 @@ describe("parser", () => { ...defaults, "disable-file-downloads": true, }) + + // Cleanup + delete process.env.CS_DISABLE_FILE_DOWNLOADS }) it("should error if password passed in", () => { From 7a3b266f2d4d1c85ac10a87f824209d062296abe Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Tue, 12 Apr 2022 16:31:41 -0700 Subject: [PATCH 15/15] fixup!: use beforeEach --- test/unit/node/cli.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index e53e26b465d3..dab47b4cf65f 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -42,6 +42,7 @@ describe("parser", () => { beforeEach(() => { delete process.env.LOG_LEVEL delete process.env.PASSWORD + delete process.env.CS_DISABLE_FILE_DOWNLOADS console.log = jest.fn() }) @@ -359,9 +360,6 @@ describe("parser", () => { ...defaults, "disable-file-downloads": true, }) - - // Cleanup - delete process.env.CS_DISABLE_FILE_DOWNLOADS }) it("should error if password passed in", () => {