@@ -23,6 +23,7 @@ import { RemoteConnection, RemoteExecResult, RemotePlatform, RemoteStatusReport
23
23
import { ApplicationPackage } from '@theia/core/shared/@theia/application-package' ;
24
24
import { RemoteCopyService } from './remote-copy-service' ;
25
25
import { RemoteNativeDependencyService } from './remote-native-dependency-service' ;
26
+ import { THEIA_VERSION } from '@theia/core' ;
26
27
27
28
/**
28
29
* The current node version that Theia recommends.
@@ -50,14 +51,14 @@ export class RemoteSetupService {
50
51
report ( 'Identifying remote system...' ) ;
51
52
// 1. Identify remote platform
52
53
const platform = await this . detectRemotePlatform ( connection ) ;
53
- // Build a few remote system paths
54
+ // 2. Setup home directory
54
55
const remoteHome = await this . getRemoteHomeDirectory ( connection , platform ) ;
55
56
const applicationDirectory = this . joinRemotePath ( platform , remoteHome , `.${ this . getRemoteAppName ( ) } ` ) ;
56
57
await this . mkdirRemote ( connection , platform , applicationDirectory ) ;
58
+ // 3. Download+copy node for that platform
57
59
const nodeFileName = this . getNodeFileName ( platform ) ;
58
60
const nodeDirName = this . getNodeDirectoryName ( platform ) ;
59
61
const remoteNodeDirectory = this . joinRemotePath ( platform , applicationDirectory , nodeDirName ) ;
60
- // 2. Download+copy node for that platform
61
62
const nodeDirExists = await this . dirExistsRemote ( connection , remoteNodeDirectory ) ;
62
63
if ( ! nodeDirExists ) {
63
64
report ( 'Downloading and installing Node.js on remote...' ) ;
@@ -66,12 +67,16 @@ export class RemoteSetupService {
66
67
await connection . copy ( nodeArchive , remoteNodeZip ) ;
67
68
await this . unzipRemote ( connection , remoteNodeZip , applicationDirectory ) ;
68
69
}
69
- // 3 . Copy backend to remote system
70
+ // 4 . Copy backend to remote system
70
71
report ( 'Copying application to remote...' ) ;
71
- const applicationZipFile = this . joinRemotePath ( platform , applicationDirectory , `${ this . getRemoteAppName ( ) } .tar` ) ;
72
- await this . copyService . copyToRemote ( connection , platform , applicationZipFile ) ;
73
- await this . unzipRemote ( connection , applicationZipFile , applicationDirectory ) ;
74
- // 4. start remote backend
72
+ const libDir = this . joinRemotePath ( platform , applicationDirectory , 'lib' ) ;
73
+ const libDirExists = await this . dirExistsRemote ( connection , libDir ) ;
74
+ if ( ! libDirExists ) {
75
+ const applicationZipFile = this . joinRemotePath ( platform , applicationDirectory , `${ this . getRemoteAppName ( ) } .tar` ) ;
76
+ await this . copyService . copyToRemote ( connection , platform , applicationZipFile ) ;
77
+ await this . unzipRemote ( connection , applicationZipFile , applicationDirectory ) ;
78
+ }
79
+ // 5. start remote backend
75
80
report ( 'Starting application on remote...' ) ;
76
81
const port = await this . startApplication ( connection , platform , applicationDirectory , remoteNodeDirectory ) ;
77
82
connection . remotePort = port ;
@@ -162,7 +167,9 @@ export class RemoteSetupService {
162
167
}
163
168
164
169
protected getRemoteAppName ( ) : string {
165
- return `${ this . cleanupDirectoryName ( this . applicationPackage . pck . name || 'theia' ) } -remote` ;
170
+ const appName = this . applicationPackage . pck . name || 'theia' ;
171
+ const appVersion = this . applicationPackage . pck . version || THEIA_VERSION ;
172
+ return `${ this . cleanupDirectoryName ( `${ appName } -${ appVersion } ` ) } -remote` ;
166
173
}
167
174
168
175
protected cleanupDirectoryName ( name : string ) : string {
@@ -176,15 +183,15 @@ export class RemoteSetupService {
176
183
177
184
protected async mkdirRemote ( connection : RemoteConnection , platform : RemotePlatform , remotePath : string ) : Promise < void > {
178
185
const recursive = platform !== 'windows' ? ' -p' : '' ;
179
- const result = await connection . exec ( `mkdir${ recursive } ` , [ remotePath ] ) ;
186
+ const result = await this . retry ( ( ) => connection . exec ( `mkdir${ recursive } " ${ remotePath } ";echo "Success"` ) ) ;
180
187
if ( result . stderr ) {
181
188
throw new Error ( 'Failed to create directory: ' + result . stderr ) ;
182
189
}
183
190
}
184
191
185
192
protected async dirExistsRemote ( connection : RemoteConnection , remotePath : string ) : Promise < boolean > {
186
- const cdResult = await connection . exec ( 'cd' , [ remotePath ] ) ;
187
- return ! Boolean ( cdResult . stderr ) ;
193
+ const cdResult = await this . retry ( ( ) => connection . exec ( `cd " ${ remotePath } ";echo "Success"` ) ) ;
194
+ return Boolean ( cdResult . stdout ) ;
188
195
}
189
196
190
197
protected async unzipRemote ( connection : RemoteConnection , remoteFile : string , remoteDirectory : string ) : Promise < void > {
@@ -194,7 +201,12 @@ export class RemoteSetupService {
194
201
}
195
202
}
196
203
197
- protected async retry ( action : ( ) => Promise < RemoteExecResult > , times = 10 ) : Promise < RemoteExecResult > {
204
+ /**
205
+ * Sometimes, ssh2.exec will not execute and retrieve any data.
206
+ * For this case, we just perform the exec call multiple times until we get something back.
207
+ * See also https://github.com/mscdex/ssh2/issues/48
208
+ */
209
+ protected async retry ( action : ( ) => Promise < RemoteExecResult > , times = 20 ) : Promise < RemoteExecResult > {
198
210
let result : RemoteExecResult = { stderr : '' , stdout : '' } ;
199
211
while ( times -- > 0 ) {
200
212
result = await action ( ) ;
0 commit comments