From 4277a4bdfd4dabea3aa293b7829770db2eaef351 Mon Sep 17 00:00:00 2001 From: RomanNikitenko Date: Thu, 18 Jul 2019 16:39:12 +0200 Subject: [PATCH] Export configurations of Che tasks to config file (#355) * Export configurations of Che tasks to config file Signed-off-by: Roman Nikitenko * Use 'projects' as root when workspace folder is not found Signed-off-by: Roman Nikitenko --- .../src/containers-service.ts | 3 +- .../src/containers-tree-data-provider.ts | 10 +- .../src/che-task-backend-module.ts | 12 ++- .../src/export/export-configs-manager.ts | 47 +++----- .../src/export/launch-configs-exporter.ts | 86 ++++++++++----- .../src/export/task-configs-exporter.ts | 102 ++++++++++++++---- .../src/extract/che-task-configs-extractor.ts | 34 ++++++ .../config-file-launch-configs-extractor.ts | 29 +++++ .../config-file-task-configs-extractor.ts | 29 +++++ .../vscode-launch-configs-extractor.ts | 48 +++++++++ .../extract/vscode-task-configs-extractor.ts | 48 +++++++++ .../task-plugin/src/task-plugin-backend.ts | 10 ++ .../task-plugin/src/task/che-task-provider.ts | 40 +------ plugins/task-plugin/src/task/converter.ts | 59 ++++++++++ 14 files changed, 438 insertions(+), 119 deletions(-) create mode 100644 plugins/task-plugin/src/extract/che-task-configs-extractor.ts create mode 100644 plugins/task-plugin/src/extract/config-file-launch-configs-extractor.ts create mode 100644 plugins/task-plugin/src/extract/config-file-task-configs-extractor.ts create mode 100644 plugins/task-plugin/src/extract/vscode-launch-configs-extractor.ts create mode 100644 plugins/task-plugin/src/extract/vscode-task-configs-extractor.ts create mode 100644 plugins/task-plugin/src/task/converter.ts diff --git a/plugins/containers-plugin/src/containers-service.ts b/plugins/containers-plugin/src/containers-service.ts index ab9c7b9d7..fb795a316 100644 --- a/plugins/containers-plugin/src/containers-service.ts +++ b/plugins/containers-plugin/src/containers-service.ts @@ -91,7 +91,8 @@ export class ContainersService { } if (runtime.commands) { container.commands = []; - runtime.commands.forEach(command => { + const cheCommands = runtime.commands.filter(command => command.type === 'exec'); + cheCommands.forEach(command => { if (command.attributes && command.attributes.machineName && command.attributes.machineName !== name) { return; } diff --git a/plugins/containers-plugin/src/containers-tree-data-provider.ts b/plugins/containers-plugin/src/containers-tree-data-provider.ts index 11e929c73..d5d9f4753 100644 --- a/plugins/containers-plugin/src/containers-tree-data-provider.ts +++ b/plugins/containers-plugin/src/containers-tree-data-provider.ts @@ -106,7 +106,7 @@ export class ContainersTreeDataProvider implements theia.TreeDataProvider(ConfigurationsExporter).to(TaskConfigurationsExporter).inSingletonScope(); container.bind(ConfigurationsExporter).to(LaunchConfigurationsExporter).inSingletonScope(); container.bind(ExportConfigurationsManager).toSelf().inSingletonScope(); +container.bind(CheTaskConfigsExtractor).toSelf().inSingletonScope(); +container.bind(ConfigFileTasksExtractor).toSelf().inSingletonScope(); +container.bind(ConfigFileLaunchConfigsExtractor).toSelf().inSingletonScope(); +container.bind(VsCodeLaunchConfigsExtractor).toSelf().inSingletonScope(); +container.bind(VsCodeTaskConfigsExtractor).toSelf().inSingletonScope(); container.bind(PreviewUrlsWidget).toSelf().inTransientScope(); container.bind(PreviewUrlsWidgetFactory).toDynamicValue(ctx => ({ diff --git a/plugins/task-plugin/src/export/export-configs-manager.ts b/plugins/task-plugin/src/export/export-configs-manager.ts index f9efaf2bb..f6c195ad0 100644 --- a/plugins/task-plugin/src/export/export-configs-manager.ts +++ b/plugins/task-plugin/src/export/export-configs-manager.ts @@ -18,15 +18,21 @@ export const ConfigurationsExporter = Symbol('ConfigurationsExporter'); /** Exports content with configurations in the config file */ export interface ConfigurationsExporter { - /** Type of the exporter corresponds to type of command which brings content with configs */ - readonly type: string; - /** - * Exports given content with configurations in the config file of given workspace folder - * @param configsContent content with configurations for export + * Exports configurations in the config file of given workspace folder * @param workspaceFolder workspace folder for exporting configs in the config file + * @param commands commands with configurations for export */ - export(configsContent: string, workspaceFolder: theia.WorkspaceFolder): void; + export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): 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 { + + /** Raw content with configurations from config file */ + content: string; + + /** Configurations as array of objects */ + configs: T[]; } /** Reads the commands from the current Che workspace and exports task and launch configurations in the config files. */ @@ -47,36 +53,13 @@ export class ExportConfigurationsManager { const cheCommands = await this.cheWorkspaceClient.getCommands(); for (const exporter of this.exporters) { - const configsContent = this.extractConfigsContent(exporter.type, cheCommands); - if (!configsContent) { - continue; - } - - this.exportContent(configsContent, exporter, workspaceFolders); + this.doExport(workspaceFolders, cheCommands, exporter); } } - private exportContent(configsContent: string, exporter: ConfigurationsExporter, workspaceFolders: theia.WorkspaceFolder[]) { + private doExport(workspaceFolders: theia.WorkspaceFolder[], cheCommands: cheApi.workspace.Command[], exporter: ConfigurationsExporter) { for (const workspaceFolder of workspaceFolders) { - exporter.export(configsContent, workspaceFolder); + exporter.export(workspaceFolder, cheCommands); } } - - private extractConfigsContent(type: string, commands: cheApi.workspace.Command[]): string { - const configCommands = commands.filter(command => command.type === type); - if (configCommands.length === 0) { - return ''; - } - - if (configCommands.length > 1) { - console.warn(`Found duplicate entry for type ${type}`); - } - - const configCommand = configCommands[0]; - if (!configCommand || !configCommand.attributes || !configCommand.attributes.actionReferenceContent) { - return ''; - } - - return configCommand.attributes.actionReferenceContent; - } } diff --git a/plugins/task-plugin/src/export/launch-configs-exporter.ts b/plugins/task-plugin/src/export/launch-configs-exporter.ts index caac93e81..076f7b098 100644 --- a/plugins/task-plugin/src/export/launch-configs-exporter.ts +++ b/plugins/task-plugin/src/export/launch-configs-exporter.ts @@ -8,57 +8,93 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -import { injectable } from 'inversify'; +import { injectable, inject } from 'inversify'; import * as theia from '@theia/plugin'; +import { che as cheApi } from '@eclipse-che/api'; import { resolve } from 'path'; -import { readFileSync, writeFileSync, format, modify, parse } from '../utils'; +import { writeFileSync, modify } from '../utils'; import { ConfigurationsExporter } from './export-configs-manager'; +import { ConfigFileLaunchConfigsExtractor } from '../extract/config-file-launch-configs-extractor'; +import { VsCodeLaunchConfigsExtractor } from '../extract/vscode-launch-configs-extractor'; const CONFIG_DIR = '.theia'; const LAUNCH_CONFIG_FILE = 'launch.json'; const formattingOptions = { tabSize: 4, insertSpaces: true, eol: '' }; -export const VSCODE_LAUNCH_TYPE = 'vscode-launch'; - /** Exports content with launch configurations in the config file. */ @injectable() export class LaunchConfigurationsExporter implements ConfigurationsExporter { - readonly type: string = VSCODE_LAUNCH_TYPE; - export(configsContent: string, workspaceFolder: theia.WorkspaceFolder): void { + @inject(ConfigFileLaunchConfigsExtractor) + protected readonly configFileLaunchConfigsExtractor: ConfigFileLaunchConfigsExtractor; + + @inject(VsCodeLaunchConfigsExtractor) + protected readonly vsCodeLaunchConfigsExtractor: VsCodeLaunchConfigsExtractor; + + export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): void { const launchConfigFileUri = this.getConfigFileUri(workspaceFolder.uri.path); - const existingContent = readFileSync(launchConfigFileUri); - if (configsContent === existingContent) { - return; - } + const configFileConfigs = this.configFileLaunchConfigsExtractor.extract(launchConfigFileUri); + const vsCodeConfigs = this.vsCodeLaunchConfigsExtractor.extract(commands); - const configsJson = parse(configsContent); - if (!configsJson || !configsJson.configurations) { + const configFileContent = configFileConfigs.content; + if (configFileContent) { + this.saveConfigs(launchConfigFileUri, configFileContent, this.merge(configFileConfigs.configs, vsCodeConfigs.configs, this.getConsoleConflictLogger())); return; } - const existingJson = parse(existingContent); - if (!existingJson || !existingJson.configurations) { - writeFileSync(launchConfigFileUri, format(configsContent, formattingOptions)); - return; + const vsCodeConfigsContent = vsCodeConfigs.content; + if (vsCodeConfigsContent) { + this.saveConfigs(launchConfigFileUri, vsCodeConfigsContent, vsCodeConfigs.configs); } - - const mergedConfigs = this.merge(existingJson.configurations, configsJson.configurations); - const result = modify(configsContent, ['configurations'], mergedConfigs, formattingOptions); - writeFileSync(launchConfigFileUri, result); } - private merge(existingConfigs: theia.DebugConfiguration[], newConfigs: theia.DebugConfiguration[]): theia.DebugConfiguration[] { - const result: theia.DebugConfiguration[] = Object.assign([], newConfigs); - for (const existing of existingConfigs) { - if (!newConfigs.some(config => config.name === existing.name)) { - result.push(existing); + private merge(configurations1: theia.DebugConfiguration[], + configurations2: theia.DebugConfiguration[], + conflictHandler: (config1: theia.DebugConfiguration, config2: theia.DebugConfiguration) => void): theia.DebugConfiguration[] { + + const result: theia.DebugConfiguration[] = Object.assign([], configurations1); + + for (const config2 of configurations2) { + const conflict = configurations1.find(config1 => config1.name === config2.name); + if (!conflict) { + result.push(config2); + continue; + } + + if (this.areEqual(config2, conflict)) { + continue; } + + conflictHandler(conflict, config2); } + return result; } + private areEqual(config1: theia.DebugConfiguration, config2: theia.DebugConfiguration): boolean { + const { type: type1, name: name1, request: request1, ...properties1 } = config1; + const { type: type2, name: name2, request: request2, ...properties2 } = config2; + + if (type1 !== type2 || name1 !== name2 || request1 !== request2) { + return false; + } + + return JSON.stringify(properties1) === JSON.stringify(properties2); + } + private getConfigFileUri(rootDir: string): string { return resolve(rootDir.toString(), CONFIG_DIR, LAUNCH_CONFIG_FILE); } + + private saveConfigs(launchConfigFileUri: string, content: string, configurations: theia.DebugConfiguration[]) { + const result = modify(content, ['configurations'], configurations, formattingOptions); + writeFileSync(launchConfigFileUri, result); + } + + private getConsoleConflictLogger(): (config1: theia.DebugConfiguration, config2: theia.DebugConfiguration) => void { + return (config1: theia.DebugConfiguration, config2: theia.DebugConfiguration) => { + console.warn(`Conflict at exporting launch configurations: ${JSON.stringify(config1)} and ${JSON.stringify(config2)}`, + `The configuration: ${JSON.stringify(config2)} is ignored`); + }; + } } diff --git a/plugins/task-plugin/src/export/task-configs-exporter.ts b/plugins/task-plugin/src/export/task-configs-exporter.ts index 55891d187..d625695a7 100644 --- a/plugins/task-plugin/src/export/task-configs-exporter.ts +++ b/plugins/task-plugin/src/export/task-configs-exporter.ts @@ -8,12 +8,17 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ -import { injectable } from 'inversify'; +import { injectable, inject } from 'inversify'; import * as theia from '@theia/plugin'; +import * as startPoint from '../task-plugin-backend'; +import { che as cheApi } from '@eclipse-che/api'; import { TaskConfiguration } from '@eclipse-che/plugin'; import { resolve } from 'path'; -import { readFileSync, writeFileSync, format, modify, parse } from '../utils'; +import { writeFileSync, modify } from '../utils'; +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'; const CONFIG_DIR = '.theia'; const TASK_CONFIG_FILE = 'tasks.json'; @@ -24,42 +29,97 @@ export const VSCODE_TASK_TYPE = 'vscode-task'; /** Exports configurations of tasks in the config file. */ @injectable() export class TaskConfigurationsExporter implements ConfigurationsExporter { - readonly type: string = VSCODE_TASK_TYPE; - export(tasksContent: string, workspaceFolder: theia.WorkspaceFolder): void { + @inject(ConfigFileTasksExtractor) + protected readonly configFileTasksExtractor: ConfigFileTasksExtractor; + + @inject(CheTaskConfigsExtractor) + protected readonly cheTaskConfigsExtractor: CheTaskConfigsExtractor; + + @inject(VsCodeTaskConfigsExtractor) + protected readonly vsCodeTaskConfigsExtractor: VsCodeTaskConfigsExtractor; + + export(workspaceFolder: theia.WorkspaceFolder, commands: cheApi.workspace.Command[]): void { const tasksConfigFileUri = this.getConfigFileUri(workspaceFolder.uri.path); - const existingContent = readFileSync(tasksConfigFileUri); - if (tasksContent === existingContent) { - return; - } + 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 tasksJson = parse(tasksContent); - if (!tasksJson || !tasksJson.tasks) { + const configFileContent = configFileTasks.content; + if (configFileContent) { + this.saveConfigs(tasksConfigFileUri, configFileContent, this.merge(configFileTasks.configs, devfileConfigs, this.getConsoleConflictLogger())); return; } - const existingJson = parse(existingContent); - if (!existingJson || !existingJson.tasks) { - writeFileSync(tasksConfigFileUri, format(tasksContent, formattingOptions)); + const vsCodeTasksContent = vsCodeTasks.content; + if (vsCodeTasksContent) { + this.saveConfigs(tasksConfigFileUri, vsCodeTasksContent, devfileConfigs); return; } - const mergedConfigs = this.merge(existingJson.tasks, tasksJson.tasks); - const result = modify(tasksContent, ['tasks'], mergedConfigs, formattingOptions); - writeFileSync(tasksConfigFileUri, result); + if (cheTasks) { + this.saveConfigs(tasksConfigFileUri, '', cheTasks); + } } - private merge(existingConfigs: TaskConfiguration[], newConfigs: TaskConfiguration[]): TaskConfiguration[] { - const result: TaskConfiguration[] = Object.assign([], newConfigs); - for (const existing of existingConfigs) { - if (!newConfigs.some(config => config.label === existing.label)) { - result.push(existing); + private merge(configurations1: TaskConfiguration[], + configurations2: TaskConfiguration[], + conflictHandler: (config1: TaskConfiguration, config2: TaskConfiguration) => void): TaskConfiguration[] { + + const result: TaskConfiguration[] = Object.assign([], configurations1); + + for (const config2 of configurations2) { + const conflict = configurations1.find(config1 => config1.label === config2.label); + if (!conflict) { + result.push(config2); + continue; } + + if (this.areEqual(config2, conflict)) { + continue; + } + + conflictHandler(conflict, config2); } + return result; } + private areEqual(config1: TaskConfiguration, config2: TaskConfiguration): boolean { + const { type: type1, label: label1, ...properties1 } = config1; + const { type: type2, label: label2, ...properties2 } = config2; + + if (type1 !== type2 || label1 !== label2) { + return false; + } + + return JSON.stringify(properties1) === JSON.stringify(properties2); + } + private getConfigFileUri(rootDir: string): string { return resolve(rootDir.toString(), CONFIG_DIR, TASK_CONFIG_FILE); } + + private saveConfigs(tasksConfigFileUri: string, content: string, configurations: TaskConfiguration[]) { + const result = modify(content, ['tasks'], configurations, formattingOptions); + writeFileSync(tasksConfigFileUri, result); + } + + private getOutputChannelConflictLogger(): (config1: TaskConfiguration, config2: TaskConfiguration) => void { + return (config1: TaskConfiguration, config2: TaskConfiguration) => { + const outputChannel = startPoint.getOutputChannel(); + outputChannel.show(); + outputChannel.appendLine(`Conflict at exporting task configurations: ${JSON.stringify(config1)} and ${JSON.stringify(config2)}`); + outputChannel.appendLine(`The configuration: ${JSON.stringify(config2)} is ignored`); + }; + } + + private getConsoleConflictLogger(): (config1: TaskConfiguration, config2: TaskConfiguration) => void { + return (config1: TaskConfiguration, config2: TaskConfiguration) => { + console.warn(`Conflict at exporting task configurations: ${JSON.stringify(config1)} and ${JSON.stringify(config2)}`, + `The configuration: ${JSON.stringify(config2)} is ignored`); + }; + } } diff --git a/plugins/task-plugin/src/extract/che-task-configs-extractor.ts b/plugins/task-plugin/src/extract/che-task-configs-extractor.ts new file mode 100644 index 000000000..1ab2c9706 --- /dev/null +++ b/plugins/task-plugin/src/extract/che-task-configs-extractor.ts @@ -0,0 +1,34 @@ +/********************************************************************* + * 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 } from 'inversify'; +import { che as cheApi } from '@eclipse-che/api'; +import { TaskConfiguration } from '@eclipse-che/plugin'; +import { toTaskConfiguration } from '../task/converter'; +import { VSCODE_TASK_TYPE } from './vscode-task-configs-extractor'; +import { VSCODE_LAUNCH_TYPE } from './vscode-launch-configs-extractor'; + +/** Extracts CHE configurations of tasks. */ +@injectable() +export class CheTaskConfigsExtractor { + + extract(commands: cheApi.workspace.Command[]): TaskConfiguration[] { + // TODO filter should be changed according to task type after resolving https://github.com/eclipse/che/issues/12710 + const filteredCommands = commands.filter(command => + command.type !== VSCODE_TASK_TYPE && + command.type !== VSCODE_LAUNCH_TYPE); + + if (filteredCommands.length === 0) { + return []; + } + + return filteredCommands.map(command => toTaskConfiguration(command)); + } +} diff --git a/plugins/task-plugin/src/extract/config-file-launch-configs-extractor.ts b/plugins/task-plugin/src/extract/config-file-launch-configs-extractor.ts new file mode 100644 index 000000000..fcedd3206 --- /dev/null +++ b/plugins/task-plugin/src/extract/config-file-launch-configs-extractor.ts @@ -0,0 +1,29 @@ +/********************************************************************* + * 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 } from 'inversify'; +import * as theia from '@theia/plugin'; +import { parse, readFileSync } from '../utils'; +import { Configurations } from '../export/export-configs-manager'; + +/** Extracts launch configurations from config file by given uri. */ +@injectable() +export class ConfigFileLaunchConfigsExtractor { + + extract(launchConfigFileUri: string): Configurations { + const configsContent = readFileSync(launchConfigFileUri); + const configsJson = parse(configsContent); + if (!configsJson || !configsJson.configurations) { + return { content: '', configs: [] }; + } + + return { content: configsContent, configs: configsJson.configurations }; + } +} diff --git a/plugins/task-plugin/src/extract/config-file-task-configs-extractor.ts b/plugins/task-plugin/src/extract/config-file-task-configs-extractor.ts new file mode 100644 index 000000000..a8b54e528 --- /dev/null +++ b/plugins/task-plugin/src/extract/config-file-task-configs-extractor.ts @@ -0,0 +1,29 @@ +/********************************************************************* + * 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 } from 'inversify'; +import { parse, readFileSync } from '../utils'; +import { Configurations } from '../export/export-configs-manager'; +import { TaskConfiguration } from '@eclipse-che/plugin'; + +/** Extracts configurations of tasks from config file by given uri. */ +@injectable() +export class ConfigFileTasksExtractor { + + extract(tasksConfigFileUri: string): Configurations { + const tasksContent = readFileSync(tasksConfigFileUri); + const tasksJson = parse(tasksContent); + if (!tasksJson || !tasksJson.tasks) { + return { content: '', configs: [] }; + } + + return { content: tasksContent, configs: tasksJson.tasks }; + } +} diff --git a/plugins/task-plugin/src/extract/vscode-launch-configs-extractor.ts b/plugins/task-plugin/src/extract/vscode-launch-configs-extractor.ts new file mode 100644 index 000000000..a97824897 --- /dev/null +++ b/plugins/task-plugin/src/extract/vscode-launch-configs-extractor.ts @@ -0,0 +1,48 @@ +/********************************************************************* + * 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 } from 'inversify'; +import * as theia from '@theia/plugin'; +import { che as cheApi } from '@eclipse-che/api'; +import { parse } from '../utils'; +import { Configurations } from '../export/export-configs-manager'; + +export const VSCODE_LAUNCH_TYPE = 'vscode-launch'; + +/** Extracts vscode launch configurations. */ +@injectable() +export class VsCodeLaunchConfigsExtractor { + + extract(commands: cheApi.workspace.Command[]): Configurations { + const emptyContent: Configurations = { content: '', configs: [] }; + + const configCommands = commands.filter(command => command.type === VSCODE_LAUNCH_TYPE); + if (configCommands.length === 0) { + return emptyContent; + } + + if (configCommands.length > 1) { + console.warn(`Found duplicate entry with configurations for type ${VSCODE_LAUNCH_TYPE}`); + } + + const configCommand = configCommands[0]; + if (!configCommand || !configCommand.attributes || !configCommand.attributes.actionReferenceContent) { + return emptyContent; + } + + const launchConfigsContent = configCommand.attributes.actionReferenceContent; + const configsJson = parse(launchConfigsContent); + if (!configsJson || !configsJson.configurations) { + return emptyContent; + } + + return { content: launchConfigsContent, configs: configsJson.configurations }; + } +} diff --git a/plugins/task-plugin/src/extract/vscode-task-configs-extractor.ts b/plugins/task-plugin/src/extract/vscode-task-configs-extractor.ts new file mode 100644 index 000000000..fef1d5875 --- /dev/null +++ b/plugins/task-plugin/src/extract/vscode-task-configs-extractor.ts @@ -0,0 +1,48 @@ +/********************************************************************* + * 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 } from 'inversify'; +import { che as cheApi } from '@eclipse-che/api'; +import { parse } from '../utils'; +import { Configurations } from '../export/export-configs-manager'; +import { TaskConfiguration } from '@eclipse-che/plugin'; + +export const VSCODE_TASK_TYPE = 'vscode-task'; + +/** Extracts vscode configurations of tasks. */ +@injectable() +export class VsCodeTaskConfigsExtractor { + + extract(commands: cheApi.workspace.Command[]): Configurations { + const emptyContent = { content: '', configs: [] } as Configurations; + + const configCommands = commands.filter(command => command.type === VSCODE_TASK_TYPE); + if (configCommands.length === 0) { + return emptyContent; + } + + if (configCommands.length > 1) { + console.warn(`Found duplicate entry with configurations for type ${VSCODE_TASK_TYPE}`); + } + + const configCommand = configCommands[0]; + if (!configCommand || !configCommand.attributes || !configCommand.attributes.actionReferenceContent) { + return emptyContent; + } + + const tasksContent = configCommand.attributes.actionReferenceContent; + const tasksJson = parse(tasksContent); + if (!tasksJson || !tasksJson.tasks) { + return emptyContent; + } + + return { content: tasksContent, configs: tasksJson.tasks }; + } +} diff --git a/plugins/task-plugin/src/task-plugin-backend.ts b/plugins/task-plugin/src/task-plugin-backend.ts index ed052b9b1..b51b302fb 100644 --- a/plugins/task-plugin/src/task-plugin-backend.ts +++ b/plugins/task-plugin/src/task-plugin-backend.ts @@ -22,6 +22,7 @@ import { TasksPreviewManager } from './preview/tasks-preview-manager'; import { ExportConfigurationsManager } from './export/export-configs-manager'; let pluginContext: theia.PluginContext; +let outputChannel: theia.OutputChannel | undefined; export async function start(context: theia.PluginContext) { pluginContext = context; @@ -60,3 +61,12 @@ export function getContext(): theia.PluginContext { export function getSubscriptions(): { dispose(): any }[] { return pluginContext.subscriptions; } + +export function getOutputChannel(): theia.OutputChannel { + if (outputChannel) { + return outputChannel; + } + + outputChannel = theia.window.createOutputChannel('task-plugin-log'); + return outputChannel; +} diff --git a/plugins/task-plugin/src/task/che-task-provider.ts b/plugins/task-plugin/src/task/che-task-provider.ts index 4276af215..c81d1c4b2 100644 --- a/plugins/task-plugin/src/task/che-task-provider.ts +++ b/plugins/task-plugin/src/task/che-task-provider.ts @@ -10,13 +10,10 @@ import { injectable, inject } from 'inversify'; import * as che from '@eclipse-che/plugin'; -import { che as cheApi } from '@eclipse-che/api'; import { Task } from '@theia/plugin'; -import { CHE_TASK_TYPE, MACHINE_NAME_ATTRIBUTE, PREVIEW_URL_ATTRIBUTE, WORKING_DIR_ATTRIBUTE, CheTaskDefinition, Target } from './task-protocol'; +import { CHE_TASK_TYPE, CheTaskDefinition, Target } from './task-protocol'; import { MachinesPicker } from '../machine/machines-picker'; import { CheWorkspaceClient } from '../che-workspace-client'; -import { VSCODE_LAUNCH_TYPE } from '../export/launch-configs-exporter'; -import { VSCODE_TASK_TYPE } from '../export/task-configs-exporter'; /** Reads the commands from the current Che workspace and provides it as Task Configurations. */ @injectable() @@ -28,11 +25,7 @@ export class CheTaskProvider { protected readonly cheWorkspaceClient!: CheWorkspaceClient; async provideTasks(): Promise { - const commands = await this.cheWorkspaceClient.getCommands(); - const filteredCommands = commands.filter(command => - command.type !== VSCODE_TASK_TYPE && - command.type !== VSCODE_LAUNCH_TYPE); - return filteredCommands.map(command => this.toTask(command)); + return []; } async resolveTask(task: Task): Promise { @@ -75,33 +68,4 @@ export class CheTaskProvider { execution: task.execution }; } - - private toTask(command: cheApi.workspace.Command): Task { - return { - definition: { - type: CHE_TASK_TYPE, - command: command.commandLine, - target: { - workingDir: this.getCommandAttribute(command, WORKING_DIR_ATTRIBUTE), - machineName: this.getCommandAttribute(command, MACHINE_NAME_ATTRIBUTE) - }, - previewUrl: this.getCommandAttribute(command, PREVIEW_URL_ATTRIBUTE) - }, - name: `${command.name}`, - source: CHE_TASK_TYPE, - }; - } - - private getCommandAttribute(command: cheApi.workspace.Command, attrName: string): string | undefined { - if (!command.attributes) { - return undefined; - } - - for (const attr in command.attributes) { - if (attr === attrName) { - return command.attributes[attr]; - } - } - return undefined; - } } diff --git a/plugins/task-plugin/src/task/converter.ts b/plugins/task-plugin/src/task/converter.ts new file mode 100644 index 000000000..3b6960e22 --- /dev/null +++ b/plugins/task-plugin/src/task/converter.ts @@ -0,0 +1,59 @@ +/********************************************************************* + * 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 { 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'; + +/** Converts the Che command to Theia Task Configuration */ +export function toTaskConfiguration(command: cheApi.workspace.Command): TaskConfiguration { + const taskConfig: TaskConfiguration = { + type: CHE_TASK_TYPE, + label: command.name, + command: command.commandLine, + target: { + workingDir: this.getCommandAttribute(command, WORKING_DIR_ATTRIBUTE), + machineName: this.getCommandAttribute(command, MACHINE_NAME_ATTRIBUTE) + }, + previewUrl: this.getCommandAttribute(command, PREVIEW_URL_ATTRIBUTE) + }; + return taskConfig; +} + +/** Converts the Che command to Task API object */ +export function toTask(command: cheApi.workspace.Command): Task { + return { + definition: { + type: CHE_TASK_TYPE, + command: command.commandLine, + target: { + workingDir: this.getCommandAttribute(command, WORKING_DIR_ATTRIBUTE), + machineName: this.getCommandAttribute(command, MACHINE_NAME_ATTRIBUTE) + }, + previewUrl: this.getCommandAttribute(command, PREVIEW_URL_ATTRIBUTE) + }, + name: `${command.name}`, + source: CHE_TASK_TYPE, + }; +} + +export function getCommandAttribute(command: cheApi.workspace.Command, attrName: string): string | undefined { + if (!command.attributes) { + return undefined; + } + + for (const attr in command.attributes) { + if (attr === attrName) { + return command.attributes[attr]; + } + } + return undefined; +}