Skip to content

Commit fcc0265

Browse files
improve
Signed-off-by: Roman Nikitenko <rnikiten@redhat.com>
1 parent f5dcab2 commit fcc0265

2 files changed

Lines changed: 67 additions & 34 deletions

File tree

code/extensions/che-commands/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969
"component": {
7070
"type": "string",
7171
"description": "Target Container"
72+
},
73+
"isComposite": {
74+
"type": "boolean",
75+
"description": "Marks a composite devfile task"
7276
}
7377
},
7478
"when": "customExecutionSupported"

code/extensions/che-commands/src/taskProvider.ts

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,16 @@ interface DevfileTaskDefinition extends vscode.TaskDefinition {
1818
workdir?: string;
1919
component?: string;
2020
commandId?: string;
21+
isComposite?: boolean;
2122
}
2223

24+
type DevfileCommandEntry =
25+
| { kind: 'exec'; task: vscode.Task }
26+
| { kind: 'composite'; name: string; commandIds: string[]; parallel: boolean };
27+
2328
export class DevfileTaskProvider implements vscode.TaskProvider {
24-
private execTaskById = new Map<string, vscode.Task>();
25-
private compositeConfigById = new Map<string, { name: string; commandIds: string[]; parallel: boolean }>();
29+
private commandById = new Map<string, DevfileCommandEntry>();
30+
private tasksCache: vscode.Task[] | undefined;
2631

2732
constructor(private channel: vscode.OutputChannel, private cheAPI: any, private terminalExtAPI: any) {
2833
}
@@ -36,6 +41,9 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
3641
}
3742

3843
private async computeTasks(): Promise<vscode.Task[]> {
44+
if (this.tasksCache) {
45+
return this.tasksCache;
46+
}
3947
const devfileCommands = await this.fetchDevfileCommands();
4048

4149
const localCommands = devfileCommands!
@@ -45,7 +53,7 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
4553
})
4654
.filter(command => !/^init-ssh-agent-command-\d+$/.test(command.id));
4755

48-
this.execTaskById.clear();
56+
this.commandById.clear();
4957
const execTasks: vscode.Task[] = localCommands
5058
.filter(command => command.exec?.commandLine)
5159
.map(command => {
@@ -57,23 +65,23 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
5765
command.exec?.env,
5866
command.id
5967
);
60-
this.execTaskById.set(command.id, task);
68+
this.commandById.set(command.id, { kind: 'exec', task });
6169
return task;
6270
});
6371

64-
this.compositeConfigById.clear();
6572
const compositeTasks: vscode.Task[] = localCommands
6673
.filter(command => (command as any).composite?.commands?.length)
6774
.map(command => {
6875
const composite = (command as any).composite;
6976
const name = composite?.label || command.id;
7077
const commandIds = composite?.commands || [];
7178
const parallel = Boolean(composite?.parallel);
72-
this.compositeConfigById.set(command.id, { name, commandIds, parallel });
79+
this.commandById.set(command.id, { kind: 'composite', name, commandIds, parallel });
7380
return this.createCompositeTask(name, commandIds, parallel, command.id);
7481
});
7582

76-
return [...execTasks, ...compositeTasks];
83+
this.tasksCache = [...execTasks, ...compositeTasks];
84+
return this.tasksCache;
7785
}
7886

7987
private async fetchDevfileCommands(): Promise<V1alpha2DevWorkspaceSpecTemplateCommands[]> {
@@ -135,18 +143,19 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
135143
const kind: DevfileTaskDefinition = {
136144
type: 'devfile',
137145
command: `composite:${commandId}`,
138-
commandId
146+
commandId,
147+
isComposite: true
139148
};
140149

141150
const execution = this.createCompositeExecution(commandId);
142151
const task = new vscode.Task(kind, vscode.TaskScope.Workspace, name, 'devfile', execution, []);
143152
task.presentationOptions = {
144-
reveal: vscode.TaskRevealKind.Never,
145-
panel: vscode.TaskPanelKind.Shared,
153+
reveal: vscode.TaskRevealKind.Silent,
154+
panel: vscode.TaskPanelKind.Dedicated,
146155
focus: false,
147156
echo: false,
148157
showReuseMessage: false,
149-
close: true
158+
close: false
150159
};
151160
return task;
152161
}
@@ -155,6 +164,9 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
155164
const writeEmitter = new vscode.EventEmitter<string>();
156165
const closeEmitter = new vscode.EventEmitter<number | void>();
157166
const activeExecutions: vscode.TaskExecution[] = [];
167+
const write = (message: string): void => {
168+
writeEmitter.fire(message.endsWith('\n') ? message : `${message}\r\n`);
169+
};
158170

159171
return new vscode.CustomExecution(async (): Promise<vscode.Pseudoterminal> => {
160172
const pty: vscode.Pseudoterminal = {
@@ -163,52 +175,62 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
163175
open: async () => {
164176
let exitCode = 0;
165177
try {
166-
const result = await this.runCompositeById(commandId, activeExecutions);
178+
write(`Composite task started: ${commandId}`);
179+
const result = await this.runCompositeById(commandId, activeExecutions, write);
167180
if (result.failed) {
168181
exitCode = 1;
169182
}
170183
} catch (error) {
171184
exitCode = 1;
172185
const message = `Composite task failed: ${String(error)}`;
173-
writeEmitter.fire(`${message}\r\n`);
186+
write(message);
174187
this.channel.appendLine(message);
175188
} finally {
189+
write(`Composite task finished: ${commandId}`);
176190
closeEmitter.fire(exitCode);
177191
}
178192
},
179193
close: () => {
180194
for (const execution of activeExecutions) {
181195
execution.terminate();
182196
}
197+
write('Composite task terminated by user.');
183198
}
184199
};
185200
return pty;
186201
});
187202
}
188203

189-
private async runCompositeById(commandId: string, activeExecutions: vscode.TaskExecution[]): Promise<{ failed: boolean }> {
190-
const config = this.compositeConfigById.get(commandId);
191-
if (!config) {
192-
this.channel.appendLine(`Composite task not found: ${commandId}`);
204+
private async runCompositeById(
205+
commandId: string,
206+
activeExecutions: vscode.TaskExecution[],
207+
write: (message: string) => void
208+
): Promise<{ failed: boolean }> {
209+
const entry = this.commandById.get(commandId);
210+
if (!entry || entry.kind !== 'composite') {
211+
const message = `Composite task not found: ${commandId}`;
212+
write(message);
213+
this.channel.appendLine(message);
193214
return { failed: true };
194215
}
195-
return this.runCompositeCommands(config.commandIds, config.parallel, activeExecutions, []);
216+
return this.runCompositeCommands(entry.commandIds, entry.parallel, activeExecutions, [], write);
196217
}
197218

198219
private async runCompositeCommands(
199220
commandIds: string[],
200221
parallel: boolean,
201222
activeExecutions: vscode.TaskExecution[],
202-
stack: string[]
223+
stack: string[],
224+
write: (message: string) => void
203225
): Promise<{ failed: boolean }> {
204226
if (parallel) {
205-
const results = await Promise.all(commandIds.map(id => this.runCommandById(id, activeExecutions, stack)));
227+
const results = await Promise.all(commandIds.map(id => this.runCommandById(id, activeExecutions, stack, write)));
206228
return { failed: results.some(result => result.failed) };
207229
}
208230

209231
let failed = false;
210232
for (const id of commandIds) {
211-
const result = await this.runCommandById(id, activeExecutions, stack);
233+
const result = await this.runCommandById(id, activeExecutions, stack, write);
212234
if (result.failed) {
213235
failed = true;
214236
}
@@ -219,30 +241,37 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
219241
private async runCommandById(
220242
commandId: string,
221243
activeExecutions: vscode.TaskExecution[],
222-
stack: string[]
244+
stack: string[],
245+
write: (message: string) => void
223246
): Promise<{ failed: boolean }> {
224247
if (stack.includes(commandId)) {
225-
this.channel.appendLine(`Composite cycle detected: ${[...stack, commandId].join(' -> ')}`);
248+
const message = `Composite cycle detected: ${[...stack, commandId].join(' -> ')}`;
249+
write(message);
250+
this.channel.appendLine(message);
226251
return { failed: true };
227252
}
228-
const execTask = this.execTaskById.get(commandId);
229-
if (execTask) {
230-
const execution = await vscode.tasks.executeTask(execTask);
253+
const entry = this.commandById.get(commandId);
254+
if (entry?.kind === 'exec') {
255+
write(`Starting ${entry.task.name}`);
256+
const execution = await vscode.tasks.executeTask(entry.task);
231257
activeExecutions.push(execution);
232-
const failed = await this.waitForTaskEnd(execution);
233-
return { failed };
258+
const result = await this.waitForTaskEnd(execution);
259+
const status = result.exitCode === undefined ? 'unknown' : result.exitCode;
260+
write(`Completed ${entry.task.name} (exit code ${status})`);
261+
return { failed: result.exitCode !== undefined && result.exitCode !== 0 };
234262
}
235263

236-
const compositeConfig = this.compositeConfigById.get(commandId);
237-
if (compositeConfig) {
238-
return this.runCompositeCommands(compositeConfig.commandIds, compositeConfig.parallel, activeExecutions, [...stack, commandId]);
264+
if (entry?.kind === 'composite') {
265+
return this.runCompositeCommands(entry.commandIds, entry.parallel, activeExecutions, [...stack, commandId], write);
239266
}
240267

241-
this.channel.appendLine(`Composite dependency not found: ${commandId}`);
268+
const message = `Composite dependency not found: ${commandId}`;
269+
write(message);
270+
this.channel.appendLine(message);
242271
return { failed: true };
243272
}
244273

245-
private waitForTaskEnd(execution: vscode.TaskExecution): Promise<boolean> {
274+
private waitForTaskEnd(execution: vscode.TaskExecution): Promise<{ exitCode: number | undefined }> {
246275
return new Promise(resolve => {
247276
let exitCode: number | undefined;
248277
const processDisposable = vscode.tasks.onDidEndTaskProcess(event => {
@@ -254,7 +283,7 @@ export class DevfileTaskProvider implements vscode.TaskProvider {
254283
if (event.execution === execution) {
255284
processDisposable.dispose();
256285
disposable.dispose();
257-
resolve(exitCode !== undefined && exitCode !== 0);
286+
resolve({ exitCode });
258287
}
259288
});
260289
});

0 commit comments

Comments
 (0)