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

Commit

Permalink
Use component for task configuration instead of container name
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Nikitenko <[email protected]>
  • Loading branch information
RomanNikitenko committed Oct 24, 2019
1 parent 7b31b3c commit 1c6b3eb
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 45 deletions.
2 changes: 1 addition & 1 deletion plugins/task-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The format of a Che task is the following:
"command": "",
"target": {
"workspaceId": "",
"containerName": "",
"component": "",
"workingDir": ""
},
"previewUrl": ""
Expand Down
2 changes: 2 additions & 0 deletions plugins/task-plugin/src/che-task-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ConfigFileTasksExtractor } from './extract/config-file-task-configs-ext
import { VsCodeLaunchConfigsExtractor } from './extract/vscode-launch-configs-extractor';
import { VsCodeTaskConfigsExtractor } from './extract/vscode-task-configs-extractor';
import { PreviewUrlVariableResolver } from './variable/preview-url-variable-resolver';
import { BackwardCompatibilityResolver } from './task/backward-compatibility';

const container = new Container();
container.bind(CheTaskProvider).toSelf().inSingletonScope();
Expand All @@ -54,6 +55,7 @@ container.bind(ConfigFileTasksExtractor).toSelf().inSingletonScope();
container.bind(ConfigFileLaunchConfigsExtractor).toSelf().inSingletonScope();
container.bind(VsCodeLaunchConfigsExtractor).toSelf().inSingletonScope();
container.bind(VsCodeTaskConfigsExtractor).toSelf().inSingletonScope();
container.bind(BackwardCompatibilityResolver).toSelf().inSingletonScope();

container.bind(PreviewUrlsWidget).toSelf().inTransientScope();
container.bind(PreviewUrlsWidgetFactory).toDynamicValue(ctx => ({
Expand Down
17 changes: 17 additions & 0 deletions plugins/task-plugin/src/che-workspace-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@ export class CheWorkspaceClient {
return workspace.links;
}

/** Returns array of containers' names for the current workspace. */
async getContainersNames(): Promise<string[]> {
const containerNames: string[] = [];

try {
const containers = await this.getMachines();
for (const containerName in containers) {
if (containers.hasOwnProperty(containerName)) {
containerNames.push(containerName);
}
}
} catch (error) {
} finally {
return containerNames;
}
}

async getMachines(): Promise<{ [attrName: string]: cheApi.workspace.Machine }> {
const workspace = await this.getCurrentWorkspace();
const runtime = workspace.runtime;
Expand Down
2 changes: 1 addition & 1 deletion plugins/task-plugin/src/export/export-configs-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface ConfigurationsExporter {
* @param workspaceFolder workspace folder for exporting configs in the config file
* @param commands commands with configurations for export
*/
export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): void;
export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): Promise<void>;
}
/** Contains configurations as array of object and as raw content and is used at getting configurations from config file for example */
export interface Configurations<T> {
Expand Down
2 changes: 1 addition & 1 deletion plugins/task-plugin/src/export/launch-configs-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class LaunchConfigurationsExporter implements ConfigurationsExporter {
@inject(VsCodeLaunchConfigsExtractor)
protected readonly vsCodeLaunchConfigsExtractor: VsCodeLaunchConfigsExtractor;

export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): void {
async export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): Promise<void> {
const launchConfigFileUri = this.getConfigFileUri(workspaceFolder.uri.path);
const configFileConfigs = this.configFileLaunchConfigsExtractor.extract(launchConfigFileUri);
const vsCodeConfigs = this.vsCodeLaunchConfigsExtractor.extract(commands);
Expand Down
9 changes: 7 additions & 2 deletions plugins/task-plugin/src/export/task-configs-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CheTaskConfigsExtractor } from '../extract/che-task-configs-extractor';
import { VsCodeTaskConfigsExtractor } from '../extract/vscode-task-configs-extractor';
import { ConfigurationsExporter } from './export-configs-manager';
import { ConfigFileTasksExtractor } from '../extract/config-file-task-configs-extractor';
import { BackwardCompatibilityResolver } from '../task/backward-compatibility';

const CONFIG_DIR = '.theia';
const TASK_CONFIG_FILE = 'tasks.json';
Expand All @@ -39,17 +40,21 @@ export class TaskConfigurationsExporter implements ConfigurationsExporter {
@inject(VsCodeTaskConfigsExtractor)
protected readonly vsCodeTaskConfigsExtractor: VsCodeTaskConfigsExtractor;

export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): void {
@inject(BackwardCompatibilityResolver)
protected readonly backwardCompatibilityResolver: BackwardCompatibilityResolver;

async export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): Promise<void> {
const tasksConfigFileUri = this.getConfigFileUri(workspaceFolder.uri.path);
const configFileTasks = this.configFileTasksExtractor.extract(tasksConfigFileUri);

const cheTasks = this.cheTaskConfigsExtractor.extract(commands);
const vsCodeTasks = this.vsCodeTaskConfigsExtractor.extract(commands);
const devfileConfigs = this.merge(cheTasks, vsCodeTasks.configs, this.getOutputChannelConflictLogger());
const configFileConfigs = await this.backwardCompatibilityResolver.resolveComponent(configFileTasks.configs);

const configFileContent = configFileTasks.content;
if (configFileContent) {
this.saveConfigs(tasksConfigFileUri, configFileContent, this.merge(configFileTasks.configs, devfileConfigs, this.getConsoleConflictLogger()));
this.saveConfigs(tasksConfigFileUri, configFileContent, this.merge(configFileConfigs, devfileConfigs, this.getConsoleConflictLogger()));
return;
}

Expand Down
39 changes: 12 additions & 27 deletions plugins/task-plugin/src/machine/machines-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { injectable, inject } from 'inversify';
import * as theia from '@theia/plugin';
import { CheWorkspaceClient } from '../che-workspace-client';

const MACHINES_PLACE_HOLDER = 'Pick a machine to run the task';
const MACHINES_PLACE_HOLDER = 'Pick a container to run the task';
export const COMPONENT_ATTRIBUTE: string = 'component';

@injectable()
export class MachinesPicker {
Expand All @@ -21,36 +22,20 @@ export class MachinesPicker {
protected readonly cheWorkspaceClient!: CheWorkspaceClient;

/**
* Returns a machine name if there's just one machine in the current workspace.
* Shows a quick open widget allows to pick a machine if there are several ones.
* Returns a container name if there's just one container in the current workspace.
* Shows a quick open widget and allows to pick a container if there are several ones.
* @param containerNames containers for displaying in quick open widget,
* all containers of current workspace will be displayed if the parametr is absent
*/
async pick(): Promise<string> {
const machines = await this.getMachines();
if (machines.length === 1) {
return Promise.resolve(machines[0]);
async pick(containerNames?: string[]): Promise<string> {
if (!containerNames) {
containerNames = await this.cheWorkspaceClient.getContainersNames();
}

const items: string[] = [];
for (const machineName of machines) {
items.push(machineName);
if (containerNames.length === 1) {
return Promise.resolve(containerNames[0]);
}

return this.showMachineQuickPick(items);
}

protected async getMachines(): Promise<string[]> {
const machineNames: string[] = [];
const machines = await this.cheWorkspaceClient.getMachines();
if (!machines) {
return machineNames;
}

for (const machineName in machines) {
if (machines.hasOwnProperty(machineName)) {
machineNames.push(machineName);
}
}
return machineNames;
return this.showMachineQuickPick(containerNames);
}

private showMachineQuickPick(items: string[]): Promise<string> {
Expand Down
90 changes: 90 additions & 0 deletions plugins/task-plugin/src/task/backward-compatibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*********************************************************************
* 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 { injectable, inject } from 'inversify';
import * as che from '@eclipse-che/plugin';
import { CHE_TASK_TYPE } from './task-protocol';
import { CheWorkspaceClient } from '../che-workspace-client';
import { getAttribute } from '../utils';
import { COMPONENT_ATTRIBUTE } from '../machine/machines-picker';

/** Contains logic to provide backward compatibility. */
@injectable()
export class BackwardCompatibilityResolver {

@inject(CheWorkspaceClient)
protected readonly cheWorkspaceClient!: CheWorkspaceClient;

/**
* Provides backward compatibility for `containerName` field of task configuration.
* `containerName` was used to indicate which container should be used for running task configuration
* `component` is used instead of `containerName` for this goal.
*
* So, the following configuration:
* {
* "type": "che",
* "label": "theia:build",
* "command": "yarn",
* "target": {
* "workingDir": "/projects/theia",
* "containerName": "che-dev"
* }
* }
*
* should be replaced by:
*
* {
* "type": "che",
* "label": "theia:build",
* "command": "yarn",
* "target": {
* "workingDir": "/projects/theia",
* "component": "che-dev"
* }
* }
*
* Note: `containerName` is replaced by empty `component` field if the corresponding component is not found.
* List of containers is displayed at running task for this case and user has opportunity to select a container for running.
*
* @param configs task configurations for resolving
*/
async resolveComponent(configs: che.TaskConfiguration[]): Promise<che.TaskConfiguration[]> {
if (configs.length === 0) {
return configs;
}

const containers = await this.cheWorkspaceClient.getMachines();
for (const config of configs) {
if (config.type !== CHE_TASK_TYPE) {
continue;
}

const target = config.target;
if (!target || !target.containerName) {
continue;
}

const containerName = target.containerName;
target.containerName = undefined;
target.component = '';

if (!containers.hasOwnProperty(containerName)) {
continue;
}

const container = containers[containerName];
const component = getAttribute(COMPONENT_ATTRIBUTE, container.attributes);
if (component) {
target.component = component;
}
}
return configs;
}
}
53 changes: 47 additions & 6 deletions plugins/task-plugin/src/task/che-task-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@

import { injectable, inject } from 'inversify';
import * as che from '@eclipse-che/plugin';
import { che as cheApi } from '@eclipse-che/api';
import { Task, ShellExecution } from '@theia/plugin';
import { CHE_TASK_TYPE, CheTaskDefinition, Target } from './task-protocol';
import { MachinesPicker } from '../machine/machines-picker';
import { MachinesPicker, COMPONENT_ATTRIBUTE } from '../machine/machines-picker';
import { CheWorkspaceClient } from '../che-workspace-client';
import { getAttribute } from '../utils';

/** Reads the commands from the current Che workspace and provides it as Task Configurations. */
@injectable()
Expand Down Expand Up @@ -45,11 +47,7 @@ export class CheTaskProvider {
resultTarget.workspaceId = await this.cheWorkspaceClient.getWorkspaceId();
}

if (target && target.containerName) {
resultTarget.containerName = target.containerName;
} else {
resultTarget.containerName = await this.machinePicker.pick();
}
resultTarget.containerName = await this.getContainerName(target);

if (target && target.workingDir) {
resultTarget.workingDir = await che.variables.resolve(target.workingDir);
Expand All @@ -71,4 +69,47 @@ export class CheTaskProvider {
execution: execution
};
}

private async getContainerName(target?: Target): Promise<string> {
if (!target) {
return this.machinePicker.pick();
}

const containers = await this.cheWorkspaceClient.getMachines();

const containerName = target.containerName;
if (containerName && containers.hasOwnProperty(containerName)) {
return containerName;
}

return await this.getContainerNameByComponent(target.component, containers) || this.machinePicker.pick();
}

private async getContainerNameByComponent(targetComponent: string | undefined, containers: { [attrName: string]: cheApi.workspace.Machine }): Promise<string | undefined> {
if (!targetComponent) {
return undefined;
}

const names = [];
for (const containerName in containers) {
if (!containers.hasOwnProperty(containerName)) {
continue;
}

const container = containers[containerName];
const component = getAttribute(COMPONENT_ATTRIBUTE, container.attributes);
if (component && component === targetComponent) {
names.push(containerName);
}
}

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

if (names.length > 1) {
return this.machinePicker.pick(names);
}
return undefined;
}
}
15 changes: 8 additions & 7 deletions plugins/task-plugin/src/task/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import { che as cheApi } from '@eclipse-che/api';
import { Task } from '@theia/plugin';
import { TaskConfiguration } from '@eclipse-che/plugin';
import { CHE_TASK_TYPE, MACHINE_NAME_ATTRIBUTE, PREVIEW_URL_ATTRIBUTE, WORKING_DIR_ATTRIBUTE } from './task-protocol';
import { CHE_TASK_TYPE, PREVIEW_URL_ATTRIBUTE, WORKING_DIR_ATTRIBUTE, COMPONENT_ALIAS_ATTRIBUTE } from './task-protocol';
import { getAttribute } from '../utils';

/** Converts the Che command to Theia Task Configuration */
export function toTaskConfiguration(command: cheApi.workspace.Command): TaskConfiguration {
Expand All @@ -21,10 +22,10 @@ export function toTaskConfiguration(command: cheApi.workspace.Command): TaskConf
command: command.commandLine,
_scope: undefined, // not to put into tasks.json
target: {
workingDir: getCommandAttribute(command, WORKING_DIR_ATTRIBUTE),
containerName: getCommandAttribute(command, MACHINE_NAME_ATTRIBUTE)
workingDir: getAttribute(WORKING_DIR_ATTRIBUTE, command.attributes),
component: getAttribute(COMPONENT_ALIAS_ATTRIBUTE, command.attributes)
},
previewUrl: getCommandAttribute(command, PREVIEW_URL_ATTRIBUTE)
previewUrl: getAttribute(PREVIEW_URL_ATTRIBUTE, command.attributes)
};

return taskConfig;
Expand All @@ -37,10 +38,10 @@ export function toTask(command: cheApi.workspace.Command): Task {
type: CHE_TASK_TYPE,
command: command.commandLine,
target: {
workingDir: getCommandAttribute(command, WORKING_DIR_ATTRIBUTE),
containerName: getCommandAttribute(command, MACHINE_NAME_ATTRIBUTE)
workingDir: getAttribute(WORKING_DIR_ATTRIBUTE, command.attributes),
component: getAttribute(COMPONENT_ALIAS_ATTRIBUTE, command.attributes)
},
previewUrl: getCommandAttribute(command, PREVIEW_URL_ATTRIBUTE)
previewUrl: getAttribute(PREVIEW_URL_ATTRIBUTE, command.attributes)
},
name: `${command.name}`,
source: CHE_TASK_TYPE,
Expand Down
2 changes: 2 additions & 0 deletions plugins/task-plugin/src/task/task-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const CHE_TASK_TYPE: string = 'che';
export const MACHINE_NAME_ATTRIBUTE: string = 'machineName';
export const PREVIEW_URL_ATTRIBUTE: string = 'previewUrl';
export const WORKING_DIR_ATTRIBUTE: string = 'workingDir';
export const COMPONENT_ALIAS_ATTRIBUTE: string = 'componentAlias';

export interface CheTaskDefinition extends TaskDefinition {
readonly target?: Target,
Expand All @@ -24,4 +25,5 @@ export interface Target {
workspaceId?: string,
containerName?: string,
workingDir?: string
component?: string,
}
14 changes: 14 additions & 0 deletions plugins/task-plugin/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ import { FormattingOptions, ParseError, JSONPath } from 'jsonc-parser';

const fs = require('fs');

/** Allows to get attribute by given name, returns `undefined` if attribute is not found */
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;
}

/**
* Apply segments to the url endpoint, where are:
* @param endPointUrl - url endpoint, for example 'http://ws:/some-server/api'
Expand Down

0 comments on commit 1c6b3eb

Please sign in to comment.