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

Improve running task process #742

Merged
merged 6 commits into from
May 19, 2020
Merged
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
2 changes: 1 addition & 1 deletion build.include
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ set -u
IMAGE_TAG="next"
THEIA_VERSION="master"
THEIA_BRANCH="master"
THEIA_COMMIT_SHA="3f28503e754bbb4fa6534612af3d1ed6da3ed66a"
THEIA_COMMIT_SHA=
THEIA_GIT_REFS="refs\\/heads\\/master"
THEIA_DOCKER_IMAGE_VERSION=

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ import { PluginFrontendViewContribution } from '@theia/plugin-ext/lib/main/brows
import { OauthUtils } from './oauth-utils';
import { TaskService } from '@theia/task/lib/browser';
import { TaskConfigurationsService } from './task-config-service';
import { CheTaskResolver } from './che-task-resolver';
import { CheTaskTerminalWidgetManager } from './che-task-terminal-widget-manager';
import { TaskTerminalWidgetManager } from '@theia/task/lib/browser/task-terminal-widget-manager';
import { ContainerPicker } from './container-picker';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(CheApiProvider).toSelf().inSingletonScope();
Expand Down Expand Up @@ -111,5 +115,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(OauthUtils).toSelf().inSingletonScope();

bind(TaskConfigurationsService).toSelf().inSingletonScope();
rebind(TaskService).to(TaskConfigurationsService).inSingletonScope();
rebind(TaskService).toService(TaskConfigurationsService);

bind(CheTaskResolver).toSelf().inSingletonScope();
bind(ContainerPicker).toSelf().inSingletonScope();

bind(CheTaskTerminalWidgetManager).toSelf().inSingletonScope();
rebind(TaskTerminalWidgetManager).toService(CheTaskTerminalWidgetManager);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*********************************************************************
* Copyright (c) 2020 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 { che as cheApi } from '@eclipse-che/api';
import { TaskResolver, TaskResolverRegistry } from '@theia/task/lib/browser';
import { TaskConfiguration } from '@theia/task/lib/common';
import { VariableResolverService } from '@theia/variable-resolver/lib/browser';
import { inject, injectable, postConstruct } from 'inversify';
import { CheApiService } from '../common/che-protocol';
import { ContainerPicker } from './container-picker';

const COMPONENT_ATTRIBUTE: string = 'component';

@injectable()
export class CheTaskResolver implements TaskResolver {

@inject(CheApiService)
protected readonly cheApi: CheApiService;

@inject(VariableResolverService)
protected readonly variableResolverService: VariableResolverService;

@inject(ContainerPicker)
protected readonly containerPicker: ContainerPicker;

@inject(TaskResolverRegistry)
protected readonly taskResolverRegistry: TaskResolverRegistry;

private workspaceId: string | undefined;
private containers: { name: string, container: cheApi.workspace.Machine }[] = [];

@postConstruct()
protected init(): void {
this.taskResolverRegistry.register('che', this);

this.getWorkspaceId();
this.getWorkspaceContainers();
}

async resolveTask(taskConfig: TaskConfiguration): Promise<TaskConfiguration> {
const taskType = taskConfig.type;
if (taskType !== 'che') {
throw new Error(`Unsupported task type: ${taskType}`);
}

const target = taskConfig.target;
const resultTarget: { [key: string]: string | undefined } = {};

resultTarget.workspaceId = target && target.workspaceId ? target.workspaceId : await this.getWorkspaceId();
resultTarget.containerName = await this.getContainerName(target);

if (target && target.workingDir) {
resultTarget.workingDir = await this.variableResolverService.resolve(target.workingDir);
}

let commandLine = undefined;
const command = taskConfig.command;
if (command) {
commandLine = await this.variableResolverService.resolve(command) || command;
}

return { ...taskConfig, command: commandLine, target: resultTarget };
}

private async getContainerName(target?: { containerName?: string, component?: string }): Promise<string> {
if (!target) {
return this.containerPicker.pick();
}

const containers = await this.getWorkspaceContainers();

const containerName = target && target.containerName;
if (containerName && containers.find(container => container.name === containerName)) {
return containerName;
}

return await this.getContainerNameByComponent(target && target.component) || this.containerPicker.pick();
}

private async getContainerNameByComponent(targetComponent: string | undefined): Promise<string | undefined> {
if (!targetComponent) {
return undefined;
}

const containers = await this.getWorkspaceContainers();
const names = [];
for (const containerEntity of containers) {
const container = containerEntity.container;
const component = getAttribute(COMPONENT_ATTRIBUTE, container.attributes);
if (component && component === targetComponent) {
names.push(containerEntity.name);
}
}

if (names.length === 1) {
return names[0];
}

if (names.length > 1) {
return this.containerPicker.pick(names);
}
return undefined;
}

private async getWorkspaceId(): Promise<string | undefined> {
if (this.workspaceId) {
return this.workspaceId;
}

this.workspaceId = await this.cheApi.getCurrentWorkspaceId();
return this.workspaceId;
}

private async getWorkspaceContainers(): Promise<{ name: string, container: cheApi.workspace.Machine }[]> {
if (this.containers.length > 0) {
return this.containers;
}

this.containers = [];
try {
const containersList = await this.cheApi.getCurrentWorkspacesContainers();
for (const containerName in containersList) {
if (!containersList.hasOwnProperty(containerName)) {
continue;
}
const container = { name: containerName, container: containersList[containerName] };
this.containers.push(container);
}
} catch (e) {
throw new Error('Unable to get list workspace containers. Cause: ' + e);
}

return this.containers;
}
}

export function getAttribute(attributeName: string, attributes?: { [key: string]: string; }): string | undefined {
if (!attributes) {
return undefined;
}

for (const attribute in attributes) {
if (attribute === attributeName) {
return attributes[attribute];
}
}
return undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*********************************************************************
* Copyright (c) 2020 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 { TaskTerminalWidgetManager, TaskTerminalWidgetOpenerOptions, TaskTerminalWidget } from '@theia/task/lib/browser/task-terminal-widget-manager';
import { TerminalWidget, TerminalWidgetOptions } from '@theia/terminal/lib/browser/base/terminal-widget';
import { TerminalWidgetFactoryOptions } from '@theia/terminal/lib/browser/terminal-widget-impl';
import { injectable } from 'inversify';

export const CHE_TASK_TYPE: string = 'che';
export const TASK_KIND: string = 'task';
export const REMOTE_TASK_KIND: string = 'remote-task';

export interface RemoteTaskTerminalWidget extends TerminalWidget {
readonly kind: 'remote-task';
}
export namespace RemoteTaskTerminalWidget {
export function is(widget: TerminalWidget): widget is RemoteTaskTerminalWidget {
return widget.kind === REMOTE_TASK_KIND;
}
}

export namespace RemoteTerminalOptions {
export function isRemoteTerminal(options: TerminalWidgetOptions): boolean {
const attributes = options.attributes;
if (!attributes) {
return false;
}

const containerName = attributes['CHE_MACHINE_NAME'];
if (containerName) {
return true;
}

const isRemoteValue = attributes['remote'];
if (isRemoteValue) {
return isRemoteValue.toLowerCase() === 'true' ? true : false;
}
return false;
}
}

@injectable()
export class CheTaskTerminalWidgetManager extends TaskTerminalWidgetManager {

async newTaskTerminal(factoryOptions: TerminalWidgetFactoryOptions): Promise<TerminalWidget> {
const attributes = factoryOptions.attributes || {};
if (!RemoteTerminalOptions.isRemoteTerminal(factoryOptions)) {
attributes['remote'] = 'false';
}

return this.terminalService.newTerminal({ ...factoryOptions, attributes });
}

async open(factoryOptions: TerminalWidgetFactoryOptions, openerOptions: TaskTerminalWidgetOpenerOptions): Promise<TerminalWidget> {
if (RemoteTerminalOptions.isRemoteTerminal(factoryOptions)) {
const terminal = await this.newTaskTerminal(factoryOptions);
this.terminalService.open(terminal, openerOptions);
return terminal;
}

return super.open(factoryOptions, openerOptions);
}

getTaskTerminals(): TerminalWidget[] {
return this.terminalService.all.filter(terminal => this.isTaskTerminal(terminal));
}

isTaskTerminal(terminal: TerminalWidget): boolean {
return TaskTerminalWidget.is(terminal) || RemoteTaskTerminalWidget.is(terminal);
}
}
Loading