From 0ffcee90e423d86f6e871ac5ac1f066953f36aa0 Mon Sep 17 00:00:00 2001 From: Vitaliy Gulyy Date: Fri, 8 Sep 2023 01:43:32 +0300 Subject: [PATCH 1/5] first steps Signed-off-by: Vitaliy Gulyy --- .che/che-editor.yaml | 95 +++++++++++++++++++++++++++++++++++++++ .che/che-editor.yaml.orig | 1 + 2 files changed, 96 insertions(+) create mode 100644 .che/che-editor.yaml.orig diff --git a/.che/che-editor.yaml b/.che/che-editor.yaml index b096f7f98bf..6b1dca1b10f 100644 --- a/.che/che-editor.yaml +++ b/.che/che-editor.yaml @@ -1 +1,96 @@ id: che-incubator/che-code/insiders +inline: + schemaVersion: 2.1.0 + metadata: + name: che-code + displayName: VS Code - Open Source + description: Microsoft Visual Studio Code - Open Source IDE for Eclipse Che + - Insiders build + icon: https://raw.githubusercontent.com/che-incubator/che-code/main/code/resources/server/code-512.png?sanitize=true + attributes: + publisher: che-incubator + version: insiders + title: Microsoft Visual Studio Code - Open Source IDE for Eclipse Che - Insiders + build + repository: https://github.com/che-incubator/che-code + firstPublicationDate: '2021-10-31' + skipMetaYaml: true + commands: + - id: init-container-command + apply: + component: che-code-injector + - id: init-che-code-command + exec: + component: che-code-runtime-description + commandLine: nohup /checode/entrypoint-volume.sh > /checode/entrypoint-logs.txt 2>&1 & + events: + preStart: + - init-container-command + postStart: + - init-che-code-command + components: + - name: che-code-runtime-description + container: + # image: quay.io/devfile/universal-developer-image@sha256:d4434a3944861c7a4de4570b1f56b3014c4ab1de05f5a9cf59d30d5fa4fc7154 + image: quay.io/devfile/universal-developer-image@sha256:3200c95ee97555f1a64cee9155f22e369b5b64f93f3fa4f50aa9ae3507a8279e + volumeMounts: + - name: checode + path: /checode + memoryLimit: 1024Mi + memoryRequest: 256Mi + cpuLimit: 500m + cpuRequest: 30m + env: + - name: WEBVIEW_LOCAL_RESOURCES + value: 'true' + endpoints: + - name: che-code + attributes: + type: main + cookiesAuthEnabled: true + discoverable: false + urlRewriteSupported: true + targetPort: 3100 + exposure: public + secure: false + protocol: https + - name: code-redirect-1 + attributes: + discoverable: false + urlRewriteSupported: false + targetPort: 13131 + exposure: public + protocol: http + - name: code-redirect-2 + attributes: + discoverable: false + urlRewriteSupported: false + targetPort: 13132 + exposure: public + protocol: http + - name: code-redirect-3 + attributes: + discoverable: false + urlRewriteSupported: false + targetPort: 13133 + exposure: public + protocol: http + attributes: + app.kubernetes.io/component: che-code-runtime + app.kubernetes.io/part-of: che-code.eclipse.org + controller.devfile.io/container-contribution: true + - name: checode + volume: {} + - name: che-code-injector + container: + # image: quay.io/che-incubator/che-code:insiders + image: quay.io/vgulyy/che-code:test-browser-storage-identifier + command: + - /entrypoint-init-container.sh + volumeMounts: + - name: checode + path: /checode + memoryLimit: 256Mi + memoryRequest: 32Mi + cpuLimit: 500m + cpuRequest: 30m diff --git a/.che/che-editor.yaml.orig b/.che/che-editor.yaml.orig new file mode 100644 index 00000000000..b096f7f98bf --- /dev/null +++ b/.che/che-editor.yaml.orig @@ -0,0 +1 @@ +id: che-incubator/che-code/insiders From b65d26810b18a683a8ac3fd92f82ec9a4ace09cc Mon Sep 17 00:00:00 2001 From: Vitaliy Gulyy Date: Fri, 8 Sep 2023 11:19:09 +0300 Subject: [PATCH 2/5] feat: use DevWorkspace ID to form browser database identifier Signed-off-by: Vitaliy Gulyy --- .../workspaces/browser/che/devWorkspaceId.ts | 24 +++++++ .../services/workspaces/browser/workspaces.ts | 3 +- launcher/src/devworkspace-id.ts | 45 +++++++++++++ launcher/src/files.ts | 14 +++++ launcher/src/fs-extra.ts | 2 +- launcher/src/main.ts | 2 + launcher/src/openvsix-registry.ts | 3 +- launcher/src/vscode-launcher.ts | 2 - launcher/src/webview-resources.ts | 4 +- launcher/tests/devworkspace-id.spec.ts | 63 +++++++++++++++++++ launcher/tests/main.spec.ts | 8 +++ 11 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts create mode 100644 launcher/src/devworkspace-id.ts create mode 100644 launcher/src/files.ts create mode 100644 launcher/tests/devworkspace-id.spec.ts diff --git a/code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts b/code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts new file mode 100644 index 00000000000..0224374591f --- /dev/null +++ b/code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ +/* eslint-disable header/header */ + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// NOTE: DO NOT CHANGE. Launcher updates this constant on workspace startup +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +const DEVWORKSPACE = "https://{{che-cluster}}.{{host}}/{{namespace}}/{{workspace-name}}/{{port}}/"; + +export function getDevWorkspceId(): string | undefined { + // checking for "https://" guarantees we apply here the ID set by che-code launcher + if (DEVWORKSPACE && !DEVWORKSPACE.startsWith("https://")) { + return DEVWORKSPACE; + } + + return undefined; +} diff --git a/code/src/vs/workbench/services/workspaces/browser/workspaces.ts b/code/src/vs/workbench/services/workspaces/browser/workspaces.ts index 8bfc7e0e3f3..5b069cfc463 100644 --- a/code/src/vs/workbench/services/workspaces/browser/workspaces.ts +++ b/code/src/vs/workbench/services/workspaces/browser/workspaces.ts @@ -6,6 +6,7 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { hash } from 'vs/base/common/hash'; +import { getDevWorkspceId } from 'vs/workbench/services/workspaces/browser/che/devWorkspaceId' // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // NOTE: DO NOT CHANGE. IDENTIFIERS HAVE TO REMAIN STABLE @@ -30,5 +31,5 @@ export function getSingleFolderWorkspaceIdentifier(folderUri: URI): ISingleFolde } function getWorkspaceId(uri: URI): string { - return hash(window.location.href + uri.toString()).toString(16); + return getDevWorkspceId() || hash(uri.toString()).toString(16); } diff --git a/launcher/src/devworkspace-id.ts b/launcher/src/devworkspace-id.ts new file mode 100644 index 00000000000..5a8b1097c1c --- /dev/null +++ b/launcher/src/devworkspace-id.ts @@ -0,0 +1,45 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { env } from "process"; +import { FILE_WORKBENCH_WEB_MAIN } from "./files"; +import * as fs from "./fs-extra"; + +const DEVWORKSPACE_ID_MASK = + "https://{{che-cluster}}.{{host}}/{{namespace}}/{{workspace-name}}/{{port}}/"; + +export class DevWorkspaceId { + async configure(): Promise { + console.log("# Setting curent DevWorkspace ID to che-code..."); + + if (!env.DEVWORKSPACE_ID) { + console.log("> env.DEVWORKSPACE_ID is not set, skip this step"); + return; + } + + console.log(` > apply DevWorkspace ID [${env.DEVWORKSPACE_ID}]`); + + try { + await this.update( + FILE_WORKBENCH_WEB_MAIN, + DEVWORKSPACE_ID_MASK, + env.DEVWORKSPACE_ID + ); + } catch (err) { + console.error(`${err.message} Webviews will not work if CDN disabled.`); + } + } + + async update(file: string, text: string, newText: string): Promise { + const content = await fs.readFile(file); + const newContent = content.replace(text, newText); + await fs.writeFile(file, newContent); + } +} diff --git a/launcher/src/files.ts b/launcher/src/files.ts new file mode 100644 index 00000000000..09e880dcb7a --- /dev/null +++ b/launcher/src/files.ts @@ -0,0 +1,14 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +export const FILE_WORKBENCH_WEB_MAIN = "out/vs/workbench/workbench.web.main.js"; + +export const FILE_EXTENSION_HOST_PROCESS = + "out/vs/workbench/api/node/extensionHostProcess.js"; diff --git a/launcher/src/fs-extra.ts b/launcher/src/fs-extra.ts index e1ebb9a102e..225f64e543e 100644 --- a/launcher/src/fs-extra.ts +++ b/launcher/src/fs-extra.ts @@ -13,7 +13,7 @@ * Why do we need to add the wrapper: * - it allows to get rid of using `fs-extra` package * - it allows to use common `node_modules` directory for launcher and VS Code - * - it simplifies writing tests and allows to easily mock of this module + * - it simplifies writing tests and allows to easily mock this module */ import * as fs from "fs"; diff --git a/launcher/src/main.ts b/launcher/src/main.ts index 85565d83163..bc6458dd7cd 100644 --- a/launcher/src/main.ts +++ b/launcher/src/main.ts @@ -9,6 +9,7 @@ ***********************************************************************/ import { CodeWorkspace } from "./code-workspace"; +import { DevWorkspaceId } from "./devworkspace-id"; import { NodeExtraCertificate } from "./node-extra-certificate"; import { OpenVSIXRegistry } from "./openvsix-registry"; import { VSCodeLauncher } from "./vscode-launcher"; @@ -22,6 +23,7 @@ import { WebviewResources } from "./webview-resources"; */ export class Main { async start(): Promise { + await new DevWorkspaceId().configure(); await new OpenVSIXRegistry().configure(); await new WebviewResources().configure(); await new NodeExtraCertificate().configure(); diff --git a/launcher/src/openvsix-registry.ts b/launcher/src/openvsix-registry.ts index b805525e39b..4ae3e7cb5cc 100644 --- a/launcher/src/openvsix-registry.ts +++ b/launcher/src/openvsix-registry.ts @@ -9,6 +9,7 @@ ***********************************************************************/ import { env } from "process"; +import { FILE_WORKBENCH_WEB_MAIN } from "./files"; import * as fs from "./fs-extra"; import { ProductJSON } from "./product-json"; @@ -74,7 +75,7 @@ export class OpenVSIXRegistry { await productJSON.save(); await this.update( - "out/vs/workbench/workbench.web.main.js", + FILE_WORKBENCH_WEB_MAIN, serviceURL, newServiceURL, itemURL, diff --git a/launcher/src/vscode-launcher.ts b/launcher/src/vscode-launcher.ts index c5402d769ed..f2c6d786495 100644 --- a/launcher/src/vscode-launcher.ts +++ b/launcher/src/vscode-launcher.ts @@ -16,8 +16,6 @@ import * as child_process from "child_process"; import { NODE_EXTRA_CERTIFICATE } from "./node-extra-certificate"; import { workspaceFilePath } from "./code-workspace"; -export const DEFAULT_PROJECTS_DIRECTORY = "/projects"; - export class VSCodeLauncher { async launch(): Promise { console.log("# Launching VS Code..."); diff --git a/launcher/src/webview-resources.ts b/launcher/src/webview-resources.ts index 511538f5382..f42480f30b6 100644 --- a/launcher/src/webview-resources.ts +++ b/launcher/src/webview-resources.ts @@ -13,9 +13,7 @@ import * as fs from "./fs-extra"; import { FlattenedDevfile } from "./flattened-devfile"; import { ProductJSON } from "./product-json"; -const FILE_WORKBENCH_WEB_MAIN = "out/vs/workbench/workbench.web.main.js"; -const FILE_EXTENSION_HOST_PROCESS = - "out/vs/workbench/api/node/extensionHostProcess.js"; +import { FILE_EXTENSION_HOST_PROCESS, FILE_WORKBENCH_WEB_MAIN } from "./files"; export class WebviewResources { /***************************************************************************************************************** diff --git a/launcher/tests/devworkspace-id.spec.ts b/launcher/tests/devworkspace-id.spec.ts new file mode 100644 index 00000000000..5ba06e68803 --- /dev/null +++ b/launcher/tests/devworkspace-id.spec.ts @@ -0,0 +1,63 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import * as fs from "../src/fs-extra"; +import { env } from "process"; + +import { DevWorkspaceId } from "../src/devworkspace-id"; + +const ORIGIN_WORKBENCH_WEB_MAIN = ` +some code, some code, a mask to be replaced https://{{che-cluster}}.{{host}}/{{namespace}}/{{workspace-name}}/{{port}}/, some code +`; + +const NEW_WORKBENCH_WEB_MAIN = ` +some code, some code, a mask to be replaced test-workspace-1234567890, some code +`; + +describe("Test setting DevWorkspace ID to VS Code", () => { + beforeEach(() => { + delete env.DEVWORKSPACE_ID; + }); + + test("should return if env.DEVWORKSPACE_ID is not set", async () => { + const readFileMock = jest.fn(); + Object.assign(fs, { + readFile: readFileMock, + }); + + const devWorkspaceId = new DevWorkspaceId(); + await devWorkspaceId.configure(); + + expect(readFileMock).toBeCalledTimes(0); + }); + + test("should apply env.DEVWORKSPACE_ID", async () => { + env.DEVWORKSPACE_ID = "test-workspace-1234567890"; + + const readFileMock = jest.fn(); + const writeFileMock = jest.fn(); + + Object.assign(fs, { + readFile: readFileMock, + writeFile: writeFileMock, + }); + + readFileMock.mockImplementation(() => ORIGIN_WORKBENCH_WEB_MAIN); + + const devWorkspaceId = new DevWorkspaceId(); + await devWorkspaceId.configure(); + + expect(readFileMock).toBeCalledTimes(1); + expect(writeFileMock).toBeCalledWith( + "out/vs/workbench/workbench.web.main.js", + NEW_WORKBENCH_WEB_MAIN + ); + }); +}); diff --git a/launcher/tests/main.spec.ts b/launcher/tests/main.spec.ts index 8855304805f..edd61e6c696 100644 --- a/launcher/tests/main.spec.ts +++ b/launcher/tests/main.spec.ts @@ -10,6 +10,13 @@ import { Main } from "../src/main"; +const setDevWorkspaceIdMock = jest.fn(); +jest.mock("../src/devworkspace-id", () => ({ + DevWorkspaceId: function () { + return { configure: setDevWorkspaceIdMock }; + }, +})); + const configureOpenVSIXRegistryMock = jest.fn(); jest.mock("../src/openvsix-registry", () => ({ OpenVSIXRegistry: function () { @@ -49,6 +56,7 @@ describe("Test main flow:", () => { test("should configure all the stuff", async () => { await new Main().start(); + expect(setDevWorkspaceIdMock).toBeCalled(); expect(configureOpenVSIXRegistryMock).toBeCalled(); expect(configureWebviewResourcesMock).toBeCalled(); expect(configureNodeExtraCertificate).toBeCalled(); From 24e63ffdce3180c19849d2e5a510b1c2a8de46fd Mon Sep 17 00:00:00 2001 From: Vitaliy Gulyy Date: Fri, 8 Sep 2023 11:24:32 +0300 Subject: [PATCH 3/5] chore: revert back che-editor.yaml Signed-off-by: Vitaliy Gulyy --- .che/che-editor.yaml | 95 --------------------------------------- .che/che-editor.yaml.orig | 1 - 2 files changed, 96 deletions(-) delete mode 100644 .che/che-editor.yaml.orig diff --git a/.che/che-editor.yaml b/.che/che-editor.yaml index 6b1dca1b10f..b096f7f98bf 100644 --- a/.che/che-editor.yaml +++ b/.che/che-editor.yaml @@ -1,96 +1 @@ id: che-incubator/che-code/insiders -inline: - schemaVersion: 2.1.0 - metadata: - name: che-code - displayName: VS Code - Open Source - description: Microsoft Visual Studio Code - Open Source IDE for Eclipse Che - - Insiders build - icon: https://raw.githubusercontent.com/che-incubator/che-code/main/code/resources/server/code-512.png?sanitize=true - attributes: - publisher: che-incubator - version: insiders - title: Microsoft Visual Studio Code - Open Source IDE for Eclipse Che - Insiders - build - repository: https://github.com/che-incubator/che-code - firstPublicationDate: '2021-10-31' - skipMetaYaml: true - commands: - - id: init-container-command - apply: - component: che-code-injector - - id: init-che-code-command - exec: - component: che-code-runtime-description - commandLine: nohup /checode/entrypoint-volume.sh > /checode/entrypoint-logs.txt 2>&1 & - events: - preStart: - - init-container-command - postStart: - - init-che-code-command - components: - - name: che-code-runtime-description - container: - # image: quay.io/devfile/universal-developer-image@sha256:d4434a3944861c7a4de4570b1f56b3014c4ab1de05f5a9cf59d30d5fa4fc7154 - image: quay.io/devfile/universal-developer-image@sha256:3200c95ee97555f1a64cee9155f22e369b5b64f93f3fa4f50aa9ae3507a8279e - volumeMounts: - - name: checode - path: /checode - memoryLimit: 1024Mi - memoryRequest: 256Mi - cpuLimit: 500m - cpuRequest: 30m - env: - - name: WEBVIEW_LOCAL_RESOURCES - value: 'true' - endpoints: - - name: che-code - attributes: - type: main - cookiesAuthEnabled: true - discoverable: false - urlRewriteSupported: true - targetPort: 3100 - exposure: public - secure: false - protocol: https - - name: code-redirect-1 - attributes: - discoverable: false - urlRewriteSupported: false - targetPort: 13131 - exposure: public - protocol: http - - name: code-redirect-2 - attributes: - discoverable: false - urlRewriteSupported: false - targetPort: 13132 - exposure: public - protocol: http - - name: code-redirect-3 - attributes: - discoverable: false - urlRewriteSupported: false - targetPort: 13133 - exposure: public - protocol: http - attributes: - app.kubernetes.io/component: che-code-runtime - app.kubernetes.io/part-of: che-code.eclipse.org - controller.devfile.io/container-contribution: true - - name: checode - volume: {} - - name: che-code-injector - container: - # image: quay.io/che-incubator/che-code:insiders - image: quay.io/vgulyy/che-code:test-browser-storage-identifier - command: - - /entrypoint-init-container.sh - volumeMounts: - - name: checode - path: /checode - memoryLimit: 256Mi - memoryRequest: 32Mi - cpuLimit: 500m - cpuRequest: 30m diff --git a/.che/che-editor.yaml.orig b/.che/che-editor.yaml.orig deleted file mode 100644 index b096f7f98bf..00000000000 --- a/.che/che-editor.yaml.orig +++ /dev/null @@ -1 +0,0 @@ -id: che-incubator/che-code/insiders From f9469ec5715de324f2e00ba7e82d919017064f38 Mon Sep 17 00:00:00 2001 From: Vitaliy Gulyy Date: Mon, 11 Sep 2023 13:20:38 +0300 Subject: [PATCH 4/5] chore: fix typo Signed-off-by: Vitaliy Gulyy --- launcher/src/devworkspace-id.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/src/devworkspace-id.ts b/launcher/src/devworkspace-id.ts index 5a8b1097c1c..c4162519b46 100644 --- a/launcher/src/devworkspace-id.ts +++ b/launcher/src/devworkspace-id.ts @@ -20,7 +20,7 @@ export class DevWorkspaceId { console.log("# Setting curent DevWorkspace ID to che-code..."); if (!env.DEVWORKSPACE_ID) { - console.log("> env.DEVWORKSPACE_ID is not set, skip this step"); + console.log(" > env.DEVWORKSPACE_ID is not set, skip this step"); return; } From a8a5664654757fd67cf80034695f9f557e14cb53 Mon Sep 17 00:00:00 2001 From: Vitaliy Gulyy Date: Wed, 13 Sep 2023 18:41:07 +0300 Subject: [PATCH 5/5] chore: fix typo Signed-off-by: Vitaliy Gulyy --- .../services/workspaces/browser/che/devWorkspaceId.ts | 2 +- .../vs/workbench/services/workspaces/browser/workspaces.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts b/code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts index 0224374591f..530ec5c764d 100644 --- a/code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts +++ b/code/src/vs/workbench/services/workspaces/browser/che/devWorkspaceId.ts @@ -14,7 +14,7 @@ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! const DEVWORKSPACE = "https://{{che-cluster}}.{{host}}/{{namespace}}/{{workspace-name}}/{{port}}/"; -export function getDevWorkspceId(): string | undefined { +export function getDevWorkspaceId(): string | undefined { // checking for "https://" guarantees we apply here the ID set by che-code launcher if (DEVWORKSPACE && !DEVWORKSPACE.startsWith("https://")) { return DEVWORKSPACE; diff --git a/code/src/vs/workbench/services/workspaces/browser/workspaces.ts b/code/src/vs/workbench/services/workspaces/browser/workspaces.ts index 5b069cfc463..275c9bd5da3 100644 --- a/code/src/vs/workbench/services/workspaces/browser/workspaces.ts +++ b/code/src/vs/workbench/services/workspaces/browser/workspaces.ts @@ -6,7 +6,7 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { hash } from 'vs/base/common/hash'; -import { getDevWorkspceId } from 'vs/workbench/services/workspaces/browser/che/devWorkspaceId' +import { getDevWorkspaceId } from 'vs/workbench/services/workspaces/browser/che/devWorkspaceId' // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // NOTE: DO NOT CHANGE. IDENTIFIERS HAVE TO REMAIN STABLE @@ -31,5 +31,5 @@ export function getSingleFolderWorkspaceIdentifier(folderUri: URI): ISingleFolde } function getWorkspaceId(uri: URI): string { - return getDevWorkspceId() || hash(uri.toString()).toString(16); + return hash(getDevWorkspaceId() + uri.toString()).toString(16); }