Skip to content
This repository has been archived by the owner on Apr 4, 2023. It is now read-only.

WIP Make Che use multi root workspaces by default #408

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions che-theia-init-sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ sources:
- extensions/eclipse-che-theia-dashboard
- extensions/eclipse-che-theia-activity-tracker
- extensions/eclipse-che-theia-about
- extensions/eclipse-che-theia-workspace
- extensions/eclipse-che-theia-preferences-provider-extension
plugins:
- plugins/containers-plugin
Expand Down
2 changes: 2 additions & 0 deletions dockerfiles/theia/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ RUN adduser -D -S -u 1001 -G root -h ${HOME} -s /bin/sh theia \
&& find ${HOME} -exec sh -c "chgrp 0 {}; chmod g+rwX {}" \;

COPY --chown=theia:root --from=builder /home/theia-dev/theia-source-code/production /home/theia
RUN mkdir -p ${HOME}/.theia && chmod -R 777 ${HOME}/.theia
COPY ["./projects.theia-workspace", "/home/theia/.theia"]
USER theia
WORKDIR /projects
ADD src/entrypoint.sh /entrypoint.sh
Expand Down
4 changes: 4 additions & 0 deletions dockerfiles/theia/projects.theia-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"folders": [],
"settings": {}
}
2 changes: 1 addition & 1 deletion dockerfiles/theia/src/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ fi
shopt -u nocasematch

# run che
node src-gen/backend/main.js /projects --hostname=0.0.0.0 --port=${THEIA_PORT} &
node src-gen/backend/main.js /home/theia/.theia/projects.theia-workspace --hostname=0.0.0.0 --port=${THEIA_PORT} &

PID=$!

Expand Down
2 changes: 2 additions & 0 deletions extensions/eclipse-che-theia-workspace/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
conf
lib
39 changes: 39 additions & 0 deletions extensions/eclipse-che-theia-workspace/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@eclipse-che/theia-workspace",
"keywords": [
"theia-extension",
"workspace"
],
"version": "0.0.1",
"description": "Eclipse Che - Workspace Menu",
"dependencies": {
"@theia/core": "^0.8.0",
"@theia/workspace": "^0.8.0"
},
"publishConfig": {
"access": "public"
},
"theiaExtensions": [
{
"frontend": "lib/browser/workspace-frontend-module"
}
],
"license": "EPL-2.0",
"files": [
"lib",
"src",
"scripts",
"conf"
],
"scripts": {
"prepare": "yarn run clean && yarn run build",
"clean": "rimraf lib",
"format": "tsfmt -r --useTsfmt ../../configs/tsfmt.json",
"lint": "tslint -c ../../configs/tslint.json --project tsconfig.json",
"compile": "tsc",
"build": "concurrently -n \"format,lint,compile\" -c \"red,green,blue\" \"yarn format\" \"yarn lint\" \"yarn compile\"",
"watch": "tsc -w",
"publish:next": "yarn publish --registry=https://registry.npmjs.org/ --no-git-tag-version --new-version 0.0.1-\"$(date +%s)\""
},
"devDependencies": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/********************************************************************************
* Copyright (C) 2017 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, inject } from 'inversify';
import { CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry } from '@theia/core/lib/common';
import {
CommonMenus, LabelProvider, KeybindingRegistry, KeybindingContribution
} from '@theia/core/lib/browser';
import { WorkspaceCommands, WorkspaceService } from '@theia/workspace/lib/browser';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
import URI from '@theia/core/lib/common/uri';

@injectable()
export class WorkspaceFrontendContribution implements CommandContribution, KeybindingContribution, MenuContribution {

@inject(LabelProvider) protected readonly labelProvider: LabelProvider;
@inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;

@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;

registerCommands(commands: CommandRegistry): void {
// Not visible/enabled on Windows/Linux in electron.
commands.unregisterCommand(WorkspaceCommands.OPEN);
// Visible/enabled only on Windows/Linux in electron.
commands.unregisterCommand(WorkspaceCommands.OPEN_FOLDER);
commands.unregisterCommand(WorkspaceCommands.OPEN_RECENT_WORKSPACE);
commands.unregisterCommand(WorkspaceCommands.CLOSE);
commands.unregisterCommand(WorkspaceCommands.OPEN_RECENT_WORKSPACE);
commands.unregisterCommand(WorkspaceCommands.SAVE_WORKSPACE_AS);

commands.registerCommand({
id: 'che.workspace.addFolder'
}, {
execute: async (uris: URI[]) => await this.workspaceService.spliceRoots(0, 0, ...uris)
});
}

registerMenus(menus: MenuModelRegistry): void {
menus.unregisterMenuAction({
commandId: WorkspaceCommands.OPEN.id,
}, CommonMenus.FILE_OPEN);
menus.unregisterMenuAction({
commandId: WorkspaceCommands.OPEN_FOLDER.id
});
menus.unregisterMenuAction({
commandId: WorkspaceCommands.OPEN_WORKSPACE.id
});
menus.unregisterMenuAction({
commandId: WorkspaceCommands.OPEN_RECENT_WORKSPACE.id
});
menus.unregisterMenuAction({
commandId: WorkspaceCommands.SAVE_WORKSPACE_AS.id
});
menus.unregisterMenuAction({
commandId: WorkspaceCommands.CLOSE.id
});
menus.registerMenuAction(CommonMenus.FILE_OPEN, {
commandId: WorkspaceCommands.OPEN_FILE.id,
label: `${WorkspaceCommands.OPEN_FILE.dialogLabel}...`,
order: 'a01'
});
}

registerKeybindings(keybindings: KeybindingRegistry): void {
keybindings.unregisterKeybinding({
command: WorkspaceCommands.OPEN.id,
keybinding: 'ctrlcmd+alt+o',
});
keybindings.unregisterKeybinding({
command: WorkspaceCommands.OPEN_FOLDER.id,
keybinding: 'ctrl+k ctrl+o',
});
keybindings.unregisterKeybinding({
command: WorkspaceCommands.OPEN_WORKSPACE.id,
keybinding: 'ctrlcmd+alt+w',
});
keybindings.unregisterKeybinding({
command: WorkspaceCommands.OPEN_RECENT_WORKSPACE.id,
keybinding: 'ctrlcmd+alt+r',
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*********************************************************************
* Copyright (c) 2019 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 { ContainerModule } from 'inversify';
import { WorkspaceFrontendContribution } from './workspace-frontend-contribution';
import { CommandContribution } from '@theia/core/lib/common/command';
import { KeybindingContribution } from '@theia/core/lib/browser/keybinding';
import { MenuContribution } from '@theia/core/lib/common/menu';

export default new ContainerModule((bind, unbind, isBound, rebind) => {

bind(WorkspaceFrontendContribution).toSelf().inSingletonScope();
for (const identifier of [CommandContribution, KeybindingContribution, MenuContribution]) {
bind(identifier).toService(WorkspaceFrontendContribution);
}

});
14 changes: 14 additions & 0 deletions extensions/eclipse-che-theia-workspace/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "../../configs/base.tsconfig.json",
"compilerOptions": {
"lib": [
"es6",
"dom"
],
"rootDir": "src",
"outDir": "lib"
},
"include": [
"src"
]
}
9 changes: 8 additions & 1 deletion plugins/factory-plugin/src/factory-initializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,16 @@ export class FactoryInitializer {
return;
}

const workspaceFolders: theia.Uri[] = [];
await Promise.all(
cloneCommands.map(command => command.execute())
cloneCommands.map(cloneCommand => {
if (cloneCommand.isInTheiaWorkspace()) {
workspaceFolders.push(theia.Uri.file(cloneCommand.folder));
}
cloneCommand.clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cloneCommand.execute ?

Copy link
Contributor

@sunix sunix Jan 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not in favor of having logic implementations here. It should go to the command implementation.

})
);
await theia.commands.executeCommand('che.workspace.addFolder', workspaceFolders);

theia.window.showInformationMessage('Che Factory: Finished cloning projects.');
}
Expand Down
42 changes: 30 additions & 12 deletions plugins/factory-plugin/src/theia-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as theia from '@theia/plugin';
import { che as cheApi } from '@eclipse-che/api';
import * as fileuri from './file-uri';
import * as git from './git';
import * as fs from 'fs';

const CHE_TASK_TYPE = 'che';

Expand All @@ -31,7 +32,7 @@ function isDevfileProjectConfig(project: cheApi.workspace.ProjectConfig | cheApi
export class TheiaCloneCommand {

private locationURI: string | undefined;
private folder: string;
private _folder: string;
private checkoutBranch?: string | undefined;
private checkoutTag?: string | undefined;
private checkoutStartPoint?: string | undefined;
Expand All @@ -46,7 +47,7 @@ export class TheiaCloneCommand {
}

this.locationURI = source.location;
this.folder = project.clonePath ? path.join(projectsRoot, project.clonePath) : path.join(projectsRoot, project.name);
this._folder = project.clonePath ? path.join(projectsRoot, project.clonePath) : path.join(projectsRoot, project.name);
this.checkoutBranch = source.branch;
this.checkoutStartPoint = source.startPoint;
this.checkoutTag = source.tag;
Expand All @@ -59,7 +60,7 @@ export class TheiaCloneCommand {
const parameters = project.source.parameters;

this.locationURI = project.source.location;
this.folder = projectsRoot + project.path;
this._folder = projectsRoot + project.path;
this.checkoutBranch = parameters['branch'];
this.checkoutStartPoint = parameters['startPoint'];
this.checkoutTag = project.source.parameters['tag'];
Expand All @@ -68,13 +69,13 @@ export class TheiaCloneCommand {
this.projectsRoot = projectsRoot;
}

execute(): PromiseLike<void> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't see any good reasons to rename execute to clone.
This is a command. At the moment we tell Che-theia to clone (running clone commands) at start up but we may chain other commands that are not about cloning.

clone(): PromiseLike<void> {
if (!this.locationURI) {
return new Promise(() => { });
}

const clone = async (progress: theia.Progress<{ message?: string; increment?: number }>, token: theia.CancellationToken): Promise<void> => {
const args: string[] = ['clone', this.locationURI, this.folder];
const args: string[] = ['clone', this.locationURI, this._folder];
if (this.checkoutBranch) {
args.push('--branch');
args.push(this.checkoutBranch);
Expand All @@ -90,15 +91,15 @@ export class TheiaCloneCommand {
: (this.checkoutTag ? this.checkoutTag : this.checkoutCommitId);

const branch = this.checkoutBranch ? this.checkoutBranch : 'default branch';
const messageStart = `Project ${this.locationURI} cloned to ${this.folder} and checked out ${branch}`;
const messageStart = `Project ${this.locationURI} cloned to ${this._folder} and checked out ${branch}`;

if (treeish) {
git.execGit(this.folder, 'reset', '--hard', treeish)
git.execGit(this._folder, 'reset', '--hard', treeish)
.then(_ => {
theia.window.showInformationMessage(`${messageStart} which has been reset to ${treeish}.`);
}, e => {
theia.window.showErrorMessage(`${messageStart} but resetting to ${treeish} failed with ${e.message}.`);
console.log(`Couldn't reset to ${treeish} of ${this.folder} cloned from ${this.locationURI} and checked out to ${branch}.`, e);
console.log(`Couldn't reset to ${treeish} of ${this._folder} cloned from ${this.locationURI} and checked out to ${branch}.`, e);
});
} else {
theia.window.showInformationMessage(`${messageStart}.`);
Expand All @@ -109,12 +110,29 @@ export class TheiaCloneCommand {
}
};

return theia.window.withProgress({
location: theia.ProgressLocation.Notification,
title: `Cloning ${this.locationURI} ...`
}, (progress, token) => clone(progress, token));
if (!fs.existsSync(this._folder)) {
return theia.window.withProgress({
location: theia.ProgressLocation.Notification,
title: `Cloning ${this.locationURI} ...`
}, (progress, token) => clone(progress, token));
}
return Promise.resolve();
}

isInTheiaWorkspace(): boolean {
for (let i = 0; i < theia.workspace.workspaceFolders.length; i++) {
const wsFolder = theia.workspace.workspaceFolders[i];

if (wsFolder && fs.realpathSync(wsFolder.uri.fsPath) === fs.realpathSync(this._folder)) {
return true;
}
}
return false;
}

get folder(): string {
return this._folder;
}
}

export class TheiaCommand {
Expand Down
21 changes: 8 additions & 13 deletions plugins/factory-plugin/src/workspace-projects-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/

import * as path from 'path';
import * as fs from 'fs';
import { TheiaCloneCommand } from './theia-commands';
import * as git from './git';
import * as projectsHelper from './projects';
Expand Down Expand Up @@ -41,11 +39,6 @@ abstract class WorkspaceProjectsManager {
abstract deleteProject(workspace: cheApi.workspace.Workspace, projectFolderURI: string): void;

async run(workspace?: cheApi.workspace.Workspace) {
if (!theia.workspace.name) {
// no workspace opened, so nothing to clone / watch
return;
}

if (!workspace) {
workspace = await che.workspace.getCurrentWorkspace();
}
Expand All @@ -61,9 +54,16 @@ abstract class WorkspaceProjectsManager {
}

theia.window.showInformationMessage('Che Workspace: Starting cloning projects.');
const workspaceFolders: theia.Uri[] = [];
await Promise.all(
cloneCommandList.map(cloneCommand => cloneCommand.execute())
cloneCommandList.map(cloneCommand => {
if (!cloneCommand.isInTheiaWorkspace()) {
workspaceFolders.push(theia.Uri.file(cloneCommand.folder));
}
return cloneCommand.clone();
})
);
await theia.commands.executeCommand('che.workspace.addFolder', workspaceFolders);
Copy link
Contributor

@sunix sunix Jan 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comments i had for factory-initializer

I am not in favor of having logic implementations here. It should go to the command implementation.

You see here you are duplicating code.
Also, workspace root folders may be handle differently in the future see: eclipse-che/che#15347

theia.window.showInformationMessage('Che Workspace: Finished cloning projects.');
}

Expand Down Expand Up @@ -126,10 +126,6 @@ export class DevfileProjectsManager extends WorkspaceProjectsManager {
}

return projects
.filter(project => {
const projectPath = project.clonePath ? path.join(instance.projectsRoot, project.clonePath) : path.join(instance.projectsRoot, project.name);
return !fs.existsSync(projectPath);
})
.map(project => new TheiaCloneCommand(project, instance.projectsRoot));
}

Expand Down Expand Up @@ -170,7 +166,6 @@ export class WorkspaceConfigProjectsManager extends WorkspaceProjectsManager {
}

return projects
.filter(project => !fs.existsSync(instance.projectsRoot + project.path))
.map(project => new TheiaCloneCommand(project, instance.projectsRoot));
}

Expand Down