From 649985af8e3b73f251f3e8ea4770b02a2a0a8a03 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 28 Oct 2022 09:59:24 -0700 Subject: [PATCH] feat: customize getting started page (#5707) * feat: add getting-started patch This modifies the text on the Getting Started page to promote coder/coder. * feat: add --disable-getting-started-override This adds a new CLI flag to code-server called `--disable-getting-started` which will be used in Code to not use Coder's custom Getting Started text. * refactor: conditionally show coder getting started This modifies the getting started patch changes to work with the new `--disable-getting-started-override`. The flag is false by default meaning the Coder getting started is shown. By passing the flag to code-server, it will not be shown. * docs: update faq for getting started override * docs: update getting-started patch description * fixup!: update patch * fixup!: unit test * feat: add more tests * fixup!: use correct env var in tests Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com> --- docs/FAQ.md | 5 + patches/getting-started.diff | 178 +++++++++++++++++++++++++++++++++++ patches/series | 1 + src/node/cli.ts | 9 ++ test/unit/node/cli.test.ts | 28 ++++++ 5 files changed, 221 insertions(+) create mode 100644 patches/getting-started.diff diff --git a/docs/FAQ.md b/docs/FAQ.md index abc520232..2858f97b9 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -32,6 +32,7 @@ - [Does code-server have any security login validation?](#does-code-server-have-any-security-login-validation) - [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server) - [How do I change the port?](#how-do-i-change-the-port) +- [How do I hide the coder/coder promotion?](#how-do-i-hide-the-codercoder-promotion) @@ -418,3 +419,7 @@ There are two ways to change the port on which code-server runs: 1. with an environment variable e.g. `PORT=3000 code-server` 2. using the flag `--bind-addr` e.g. `code-server --bind-addr localhost:3000` + +## How do I hide the coder/coder promotion? + +You can pass the flag `--disable-getting-started-override` to `code-server`. diff --git a/patches/getting-started.diff b/patches/getting-started.diff new file mode 100644 index 000000000..247c3a311 --- /dev/null +++ b/patches/getting-started.diff @@ -0,0 +1,178 @@ +Modify Help: Getting Started + +This modifies some text on the Getting Started page and adds text about using +code-server on a team. + +It is enabled by default but can be overriden using the cli flag +`--disable-getting-started-override`. + +Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts ++++ code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +@@ -62,7 +62,7 @@ import { GettingStartedIndexList } from + import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; + import { KeyCode } from 'vs/base/common/keyCodes'; + import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils'; +-import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; ++import { IsEnabledCoderGettingStarted, WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; + import { OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; + import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions'; + import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; +@@ -753,11 +753,24 @@ export class GettingStartedPage extends + onShowOnStartupChanged(); + })); + +- const header = $('.header', {}, ++ let header = $('.header', {}, + $('h1.product-name.caption', {}, this.productService.nameLong), + $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved")) + ); + ++ if (this.contextService.contextMatchesRules(IsEnabledCoderGettingStarted)) { ++ header = $('.header', {}, ++ $('h1.product-name.caption', {}, this.productService.nameLong), ++ $('p.subtitle.description.coder', {}, ++ "Using code-server on a team?", ++ ), ++ $('p.subtitle.description.coder-coder', {}, ++ "Check out: ", ++ $('a', { href: "https://github.com/coder/coder" }, "coder/coder") ++ ), ++ ); ++ } ++ + + const leftColumn = $('.categories-column.categories-column-left', {},); + const rightColumn = $('.categories-column.categories-column-right', {},); +Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css +=================================================================== +--- code-server.orig/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css ++++ code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css +@@ -60,6 +60,15 @@ + display: block; + } + ++.monaco-workbench .part.editor > .content .gettingStartedContainer .coder { ++ margin-bottom: 0.2em; ++} ++ ++.monaco-workbench .part.editor>.content .gettingStartedContainer .coder-coder { ++ font-size: 1em; ++ margin-top: 0.2em; ++} ++ + .monaco-workbench.hc-black .part.editor>.content .gettingStartedContainer .subtitle, + .monaco-workbench.hc-light .part.editor>.content .gettingStartedContainer .subtitle { + font-weight: 200; +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 +@@ -276,6 +276,11 @@ export interface IWorkbenchConstructionO + */ + readonly isEnabledFileDownloads?: boolean + ++ /** ++ * Whether to use Coder's custom Getting Started text. ++ */ ++ readonly isEnabledCoderGettingStarted?: 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 +@@ -36,6 +36,11 @@ export interface IBrowserWorkbenchEnviro + * Enable downloading files via menu actions. + */ + readonly isEnabledFileDownloads?: boolean; ++ ++ /** ++ * Enable Coder's custom getting started text. ++ */ ++ readonly isEnabledCoderGettingStarted?: boolean; + } + + export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvironmentService { +@@ -74,6 +79,13 @@ export class BrowserWorkbenchEnvironment + return this.options.isEnabledFileDownloads; + } + ++ get isEnabledCoderGettingStarted(): boolean { ++ if (typeof this.options.isEnabledCoderGettingStarted === "undefined") { ++ throw new Error('isEnabledCoderGettingStarted was not provided to the browser'); ++ } ++ return this.options.isEnabledCoderGettingStarted; ++ } ++ + @memoize + get argvResource(): URI { return joinPath(this.userRoamingDataHome, 'argv.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 +@@ -16,6 +16,7 @@ export const serverOptions: OptionDescri + 'auth': { type: 'string' }, + 'disable-file-downloads': { type: 'boolean' }, + 'locale': { type: 'string' }, ++ 'disable-getting-started-override': { type: 'boolean' }, + + /* ----- server setup ----- */ + +@@ -98,6 +99,7 @@ export interface ServerParsedArgs { + 'auth'?: string + 'disable-file-downloads'?: boolean; + 'locale'?: string ++ 'disable-getting-started-override'?: 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 +@@ -308,6 +308,7 @@ export class WebClientServer { + webviewEndpoint: vscodeBase + this._staticRoute + '/out/vs/workbench/contrib/webview/browser/pre', + userDataPath: this._environmentService.userDataPath, + isEnabledFileDownloads: !this._environmentService.args['disable-file-downloads'], ++ isEnabledCoderGettingStarted: !this._environmentService.args['disable-getting-started-override'], + _wrapWebWorkerExtHostInIframe, + developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined, logLevel: this._logService.getLevel() }, + settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, +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, ProductQualityContext, IsMobileContext } 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, TemporaryWorkspaceContext, IsEnabledFileDownloads } 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, TemporaryWorkspaceContext, IsEnabledFileDownloads, IsEnabledCoderGettingStarted } 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'; +@@ -204,6 +204,7 @@ export class WorkbenchContextKeysHandler + + // code-server + IsEnabledFileDownloads.bindTo(this.contextKeyService).set(this.environmentService.isEnabledFileDownloads ?? true) ++ IsEnabledCoderGettingStarted.bindTo(this.contextKeyService).set(this.environmentService.isEnabledCoderGettingStarted ?? true) + + this.registerListeners(); + } +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 +@@ -33,6 +33,7 @@ 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); ++export const IsEnabledCoderGettingStarted = new RawContextKey('isEnabledCoderGettingStarted', true, true); + + //#endregion + diff --git a/patches/series b/patches/series index 66df7f905..adab5b615 100644 --- a/patches/series +++ b/patches/series @@ -19,3 +19,4 @@ telemetry.diff display-language.diff cli-window-open.diff exec-argv.diff +getting-started.diff diff --git a/src/node/cli.ts b/src/node/cli.ts index 2d87bd461..f34cb2a4f 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -51,6 +51,7 @@ export interface UserProvidedCodeArgs { "disable-update-check"?: boolean "disable-file-downloads"?: boolean "disable-workspace-trust"?: boolean + "disable-getting-started-override"?: boolean } /** @@ -170,6 +171,10 @@ export const options: Options> = { type: "boolean", description: "Disable Workspace Trust feature. This switch only affects the current session.", }, + "disable-getting-started-override": { + type: "boolean", + description: "Disable the coder/coder override in the Help: Getting Started page.", + }, // --enable can be used to enable experimental features. These features // provide no guarantees. enable: { type: "string[]" }, @@ -563,6 +568,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config args["disable-file-downloads"] = true } + if (process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE?.match(/^(1|true)$/)) { + args["disable-getting-started-override"] = true + } + const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD if (process.env.HASHED_PASSWORD) { args["hashed-password"] = process.env.HASHED_PASSWORD diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index 4afa404c0..afa8edefb 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -43,6 +43,7 @@ describe("parser", () => { delete process.env.LOG_LEVEL delete process.env.PASSWORD delete process.env.CS_DISABLE_FILE_DOWNLOADS + delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE console.log = jest.fn() }) @@ -97,6 +98,8 @@ describe("parser", () => { "--disable-file-downloads", + "--disable-getting-started-override", + ["--host", "0.0.0.0"], "4", "--", @@ -114,6 +117,7 @@ describe("parser", () => { value: path.resolve("path/to/cert"), }, "disable-file-downloads": true, + "disable-getting-started-override": true, enable: ["feature1", "feature2"], help: true, host: "0.0.0.0", @@ -378,6 +382,30 @@ describe("parser", () => { }) }) + it("should use env var CS_DISABLE_GETTING_STARTED_OVERRIDE", async () => { + process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE = "1" + const args = parse([]) + expect(args).toEqual({}) + + const defaultArgs = await setDefaults(args) + expect(defaultArgs).toEqual({ + ...defaults, + "disable-getting-started-override": true, + }) + }) + + it("should use env var CS_DISABLE_GETTING_STARTED_OVERRIDE set to true", async () => { + process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE = "true" + const args = parse([]) + expect(args).toEqual({}) + + const defaultArgs = await setDefaults(args) + expect(defaultArgs).toEqual({ + ...defaults, + "disable-getting-started-override": 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",