@@ -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+
2328export 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 => ! / ^ i n i t - s s h - a g e n t - c o m m a n d - \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