diff --git a/launcher/src/code-workspace.ts b/launcher/src/code-workspace.ts index bd97c3bc634c..2dce32369e79 100644 --- a/launcher/src/code-workspace.ts +++ b/launcher/src/code-workspace.ts @@ -40,6 +40,8 @@ export class CodeWorkspace { return; } + const projectsRoot = env.PROJECTS_ROOT; + let path: string | undefined; let workspace: Workspace | undefined; @@ -112,6 +114,15 @@ export class CodeWorkspace { saveRequired = true; } + // Ensure the workspace always opens at least the projects root. + // Empty workspaces (no projects, dependentProjects or starterProjects) would + // otherwise produce a `.code-workspace` file with no `folders`, causing the + // editor to open with an empty Explorer and nothing mounted. + if (!workspace!.folders || workspace!.folders.length === 0) { + workspace!.folders = [{ name: 'projects', path: projectsRoot }]; + saveRequired = true; + } + // write workspace file only if it has been changed if (saveRequired) { const json = JSON.stringify(workspace, null, '\t'); diff --git a/launcher/tests/_data/flattened.devworkspace.empty.yaml b/launcher/tests/_data/flattened.devworkspace.empty.yaml new file mode 100644 index 000000000000..959dd37742c8 --- /dev/null +++ b/launcher/tests/_data/flattened.devworkspace.empty.yaml @@ -0,0 +1,20 @@ +attributes: + controller.devfile.io/devworkspace-config: + name: devworkspace-config + namespace: dogfooding + controller.devfile.io/storage-type: per-workspace +components: +- attributes: + app.kubernetes.io/component: che-code-runtime + app.kubernetes.io/part-of: che-code.eclipse.org + controller.devfile.io/merged-contributions: editor + container: + cpuLimit: "4" + image: quay.io/che-incubator/che-code-dev:insiders + memoryLimit: 13Gi + memoryRequest: 320Mi + sourceMapping: /projects + name: dev +- name: projects + volume: + size: 3Gi diff --git a/launcher/tests/code-workspace.spec.ts b/launcher/tests/code-workspace.spec.ts index d7a54ef1cf27..7e1049330b25 100644 --- a/launcher/tests/code-workspace.spec.ts +++ b/launcher/tests/code-workspace.spec.ts @@ -78,6 +78,15 @@ const WORKSPACE_WITH_FIVE_PROJECTS = `{ \t] }`; +const WORKSPACE_WITH_PROJECTS_ROOT = `{ +\t"folders": [ +\t\t{ +\t\t\t"name": "projects", +\t\t\t"path": "/tmp/projects" +\t\t} +\t] +}`; + const WORKSPACE_WITH_DEPENDENT_PROJECTS = `{ \t"folders": [ \t\t{ @@ -462,6 +471,43 @@ describe('Test generating VS Code Workspace file:', () => { expect(writeFileMock).toBeCalledWith('/tmp/projects/.code-workspace', WORKSPACE_WITH_DEPENDENT_PROJECTS); }); + test('should add PROJECTS_ROOT as a default folder when the workspace has no projects', async () => { + env.PROJECTS_ROOT = '/tmp/projects'; + + env.DEVWORKSPACE_FLATTENED_DEVFILE = path.join(__dirname, '_data', 'flattened.devworkspace.empty.yaml'); + + const pathExistsMock = jest.fn(); + const writeFileMock = jest.fn(); + const readFileMock = jest.fn(); + + Object.assign(fs, { + pathExists: pathExistsMock, + writeFile: writeFileMock, + readFile: readFileMock, + }); + + readFileMock.mockImplementation(async (path) => { + if (path === env.DEVWORKSPACE_FLATTENED_DEVFILE) { + return originalReadFile(path); + } + + return undefined; + }); + + // no project directories exist and no default .code-workspace file is present + pathExistsMock.mockImplementation(async () => false); + + const codeWorkspace = new CodeWorkspace(); + await codeWorkspace.generate(); + + // should read only the flattened devworkspace file + expect(readFileMock).toBeCalledTimes(1); + expect(readFileMock).toBeCalledWith(env.DEVWORKSPACE_FLATTENED_DEVFILE); + + // should create a default workspace file that opens the projects root + expect(writeFileMock).toBeCalledWith('/tmp/projects/.code-workspace', WORKSPACE_WITH_PROJECTS_ROOT); + }); + test('should parse .code-workspace file if the file has extra characters', async () => { env.PROJECTS_ROOT = '/tmp/projects';