33 * Licensed under the MIT License. See LICENSE file in the project root for license information.
44 *-----------------------------------------------------------------------------------------------*/
55
6- import { EventEmitter , Terminal , Uri , commands , window } from 'vscode' ;
7- import { ClusterVersion , FunctionObject } from './types' ;
8- import { ServerlessCommand , Utils } from './commands' ;
9- import { Platform } from '../util/platform' ;
6+ import { commands , Uri , window } from 'vscode' ;
107import { OdoImpl } from '../odo' ;
11- import { CliChannel } from '../cli' ;
12- import { ChildProcess , SpawnOptions } from 'child_process' ;
8+ import { Platform } from '../util/platform' ;
9+ import { OpenShiftTerminalApi , OpenShiftTerminalManager } from '../webview/openshift-terminal/openShiftTerminal' ;
10+ import { ServerlessCommand , Utils } from './commands' ;
11+ import { ClusterVersion , FunctionObject } from './types' ;
1312import { ServerlessFunctionView } from './view' ;
1413
1514export class BuildAndDeploy {
1615
1716 private static instance : BuildAndDeploy ;
1817
19- private buildTerminalMap : Map < string , Terminal > = new Map < string , Terminal > ( ) ;
20- public runTerminalMap : Map < string , Terminal > = new Map < string , Terminal > ( ) ;
21- private buildEmiterMap : Map < string , EventEmitter < string > > = new Map < string , EventEmitter < string > > ( ) ;
22- private buildPrcessMap : Map < Terminal , ChildProcess > = new Map < Terminal , ChildProcess > ( ) ;
18+ private buildTerminalMap = new Map < string , OpenShiftTerminalApi > ( ) ;
19+ public runTerminalMap = new Map < string , OpenShiftTerminalApi > ( ) ;
2320
2421 public static imageRegex = RegExp ( '[^/]+\\.[^/.]+\\/([^/.]+)(?:\\/[\\w\\s._-]*([\\w\\s._-]))*(?::[a-z0-9\\.-]+)?$' ) ;
2522
@@ -46,38 +43,33 @@ export class BuildAndDeploy {
4643 }
4744 }
4845
46+ private pollForBuildTerminalDead ( resolve : ( ) => void , functionUri : Uri , timeout : number ) {
47+ return ( ) => {
48+ if ( ! this . buildTerminalMap . get ( `build-${ functionUri . fsPath } ` ) ) {
49+ resolve ( ) ;
50+ } else {
51+ setTimeout ( this . pollForBuildTerminalDead ( resolve , functionUri , timeout * 2 ) , timeout * 2 ) ;
52+ }
53+ }
54+ }
55+
4956 public async buildFunction ( functionName : string , functionUri : Uri ) : Promise < void > {
50- const exisitingTerminal = this . buildTerminalMap . get ( `build-${ functionUri . fsPath } ` ) ;
51- const outputEmitter = this . buildEmiterMap . get ( `build-${ functionUri . fsPath } ` ) ;
52- if ( exisitingTerminal ) {
53- let exisitingProcess = this . buildPrcessMap . get ( exisitingTerminal ) ;
57+ const existingTerminal = this . buildTerminalMap . get ( `build-${ functionUri . fsPath } ` ) ;
58+
59+ if ( existingTerminal ) {
5460 void window . showWarningMessage ( `Do you want to restart ${ functionName } build ?` , 'Yes' , 'No' ) . then ( async ( value : string ) => {
5561 if ( value === 'Yes' ) {
56- exisitingTerminal . show ( true ) ;
57- await commands . executeCommand ( 'workbench.action.terminal.clear' )
58- outputEmitter . fire ( `Start Building ${ functionName } \r\n` ) ;
59- exisitingProcess . kill ( 'SIGINT' )
60- this . buildPrcessMap . delete ( exisitingTerminal ) ;
61- const clusterVersion : ClusterVersion | null = await this . checkOpenShiftCluster ( ) ;
62- const buildImage = await this . getImage ( functionUri ) ;
63- const opt : SpawnOptions = { cwd : functionUri . fsPath } ;
64- void CliChannel . getInstance ( ) . spawnTool ( ServerlessCommand . buildFunction ( functionUri . fsPath , buildImage , clusterVersion ) , opt ) . then ( ( cp ) => {
65- exisitingProcess = cp ;
66- this . buildPrcessMap . set ( exisitingTerminal , cp ) ;
67- exisitingProcess . on ( 'error' , ( err ) => {
68- void window . showErrorMessage ( err . message ) ;
69- } ) ;
70- exisitingProcess . stdout . on ( 'data' , ( chunk ) => {
71- outputEmitter . fire ( `${ chunk as string } ` . replaceAll ( '\n' , '\r\n' ) ) ;
72- } ) ;
73- exisitingProcess . stderr . on ( 'data' , ( errChunk ) => {
74- outputEmitter . fire ( `\x1b[31m${ errChunk as string } \x1b[0m` . replaceAll ( '\n' , '\r\n' ) ) ;
75- void window . showErrorMessage ( `${ errChunk as string } ` ) ;
76- } ) ;
77- exisitingProcess . on ( 'exit' , ( ) => {
78- outputEmitter . fire ( '\r\nPress any key to close this terminal\r\n' ) ;
79- } ) ;
62+ existingTerminal . focusTerminal ( ) ;
63+ existingTerminal . sendText ( '\u0003' ) ;
64+
65+ // wait for old build to exit using polling with a back off
66+ await new Promise < void > ( ( resolve ) => {
67+ const INIT_TIMEOUT = 100 ;
68+ setTimeout ( this . pollForBuildTerminalDead ( resolve , functionUri , INIT_TIMEOUT ) , INIT_TIMEOUT ) ;
8069 } ) ;
70+
71+ // start new build
72+ await this . buildProcess ( functionUri , functionName ) ;
8173 }
8274 } ) ;
8375 } else {
@@ -88,115 +80,48 @@ export class BuildAndDeploy {
8880 private async buildProcess ( functionUri : Uri , functionName : string ) {
8981 const clusterVersion : ClusterVersion | null = await this . checkOpenShiftCluster ( ) ;
9082 const buildImage = await this . getImage ( functionUri ) ;
91- const outputEmitter = new EventEmitter < string > ( ) ;
92- let devProcess : ChildProcess ;
93- let terminal = window . createTerminal ( {
94- name : `Build ${ functionName } ` ,
95- pty : {
96- onDidWrite : outputEmitter . event ,
97- open : ( ) => {
98- outputEmitter . fire ( `Start Building ${ functionName } \r\n` ) ;
99- const opt : SpawnOptions = { cwd : functionUri . fsPath } ;
100- void CliChannel . getInstance ( ) . spawnTool ( ServerlessCommand . buildFunction ( functionUri . fsPath , buildImage , clusterVersion ) , opt ) . then ( ( cp ) => {
101- this . buildPrcessMap . set ( terminal , cp ) ;
102- devProcess = cp ;
103- devProcess . on ( 'spawn' , ( ) => {
104- terminal . show ( ) ;
105- } ) ;
106- devProcess . on ( 'error' , ( err ) => {
107- void window . showErrorMessage ( err . message ) ;
108- } ) ;
109- devProcess . stdout . on ( 'data' , ( chunk ) => {
110- outputEmitter . fire ( `${ chunk as string } ` . replaceAll ( '\n' , '\r\n' ) ) ;
111- } ) ;
112- devProcess . stderr . on ( 'data' , ( errChunk ) => {
113- outputEmitter . fire ( `\x1b[31m${ errChunk as string } \x1b[0m` . replaceAll ( '\n' , '\r\n' ) ) ;
114- } ) ;
115- devProcess . on ( 'exit' , ( ) => {
116- outputEmitter . fire ( '\r\nPress any key to close this terminal\r\n' ) ;
117- } ) ;
118- } ) ;
119- } ,
120- close : ( ) => {
121- if ( devProcess && devProcess . exitCode === null ) { // if process is still running and user closed terminal
122- devProcess . kill ( 'SIGINT' ) ;
123- }
124- this . buildTerminalMap . delete ( `build-${ functionUri . fsPath } ` ) ;
125- this . buildEmiterMap . delete ( `build-${ functionUri . fsPath } ` ) ;
126- this . buildPrcessMap . delete ( terminal ) ;
127- terminal = undefined ;
128- } ,
129- handleInput : ( ( data : string ) => {
130- if ( ! devProcess ) { // if any key pressed after process ends
131- terminal . dispose ( ) ;
132- } else { // ctrl+C processed only once when there is no cleaning process
133- outputEmitter . fire ( '^C\r\n' ) ;
134- devProcess . kill ( 'SIGINT' ) ;
135- terminal . dispose ( ) ;
136- }
137- } )
138- } ,
139- } ) ;
140- this . buildTerminalMap . set ( `build-${ functionUri . fsPath } ` , terminal ) ;
141- this . buildEmiterMap . set ( `build-${ functionUri . fsPath } ` , outputEmitter ) ;
83+ const terminalKey = `build-${ functionUri . fsPath } ` ;
84+
85+ const terminal = await OpenShiftTerminalManager . getInstance ( ) . createTerminal (
86+ ServerlessCommand . buildFunction ( functionUri . fsPath , buildImage , clusterVersion ) ,
87+ `Build ${ functionName } ` ,
88+ functionUri . fsPath ,
89+ process . env ,
90+ true ,
91+ {
92+ onExit : ( ) => {
93+ this . buildTerminalMap . delete ( terminalKey )
94+ }
95+ }
96+ ) ;
97+
98+ this . buildTerminalMap . set ( terminalKey , terminal ) ;
14299 }
143100
144- public runFunction ( context : FunctionObject , runBuild = false ) {
145- const outputEmitter = new EventEmitter < string > ( ) ;
146- let runProcess : ChildProcess ;
147- let terminal = window . createTerminal ( {
148- name : `Run ${ context . name } ` ,
149- pty : {
150- onDidWrite : outputEmitter . event ,
151- open : ( ) => {
152- outputEmitter . fire ( `Running ${ context . name } \r\n` ) ;
153- const opt : SpawnOptions = { cwd : context . folderURI . fsPath } ;
154- void CliChannel . getInstance ( ) . spawnTool ( ServerlessCommand . runFunction ( context . folderURI . fsPath , runBuild ) , opt ) . then ( ( cp ) => {
155- runProcess = cp ;
156- runProcess . on ( 'spawn' , ( ) => {
157- terminal . show ( ) ;
158- } ) ;
159- runProcess . on ( 'error' , ( err ) => {
160- void window . showErrorMessage ( err . message ) ;
161- } ) ;
162- runProcess . stdout . on ( 'data' , ( chunk ) => {
163- outputEmitter . fire ( `${ chunk as string } ` . replaceAll ( '\n' , '\r\n' ) ) ;
164- void commands . executeCommand ( 'openshift.Serverless.refresh' , context ) ;
165- } ) ;
166- runProcess . stderr . on ( 'data' , ( errChunk ) => {
167- outputEmitter . fire ( `\x1b[31m${ errChunk as string } \x1b[0m` . replaceAll ( '\n' , '\r\n' ) ) ;
168- } ) ;
169- runProcess . on ( 'exit' , ( ) => {
170- outputEmitter . fire ( '\r\nPress any key to close this terminal\r\n' ) ;
171- } ) ;
172- } ) ;
101+ public async runFunction ( context : FunctionObject , runBuild = false ) {
102+ const terminal = await OpenShiftTerminalManager . getInstance ( ) . createTerminal (
103+ ServerlessCommand . runFunction ( context . folderURI . fsPath , runBuild ) ,
104+ `${ runBuild ? 'Build and ' : '' } Run ${ context . name } ` ,
105+ context . folderURI . fsPath ,
106+ process . env ,
107+ true ,
108+ {
109+ onSpawn : ( ) => {
110+ void commands . executeCommand ( 'openshift.Serverless.refresh' , context ) ;
173111 } ,
174- close : ( ) => {
175- if ( runProcess && runProcess . exitCode === null ) { // if process is still running and user closed terminal
176- runProcess . kill ( 'SIGINT' ) ;
177- }
112+ onExit : ( ) => {
178113 this . runTerminalMap . delete ( `run-${ context . folderURI . fsPath } ` ) ;
179- terminal = undefined ;
180114 void commands . executeCommand ( 'openshift.Serverless.refresh' , context ) ;
181- } ,
182- handleInput : ( ( _data : string ) => {
183- if ( ! runProcess ) {
184- terminal . dispose ( ) ;
185- } else {
186- outputEmitter . fire ( '^C\r\n' ) ;
187- runProcess . kill ( 'SIGINT' ) ;
188- terminal . dispose ( )
189- }
190- } )
191- } ,
192- } ) ;
115+ }
116+ }
117+ ) ;
193118 this . runTerminalMap . set ( `run-${ context . folderURI . fsPath } ` , terminal ) ;
194119 }
195120
196121 public stopFunction ( context : FunctionObject ) {
197122 const terminal = this . runTerminalMap . get ( `run-${ context . folderURI . fsPath } ` ) ;
198123 if ( terminal ) {
199- terminal . sendText ( '^C\r\n ' ) ;
124+ terminal . sendText ( '\u0003 ' ) ;
200125 }
201126 }
202127
0 commit comments