@theia/remote 1.52.0 → 1.53.0-next.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -61
- package/lib/electron-node/setup/remote-setup-script-service.js +11 -11
- package/package.json +5 -5
- package/src/electron-browser/port-forwarding/port-forwading-contribution.ts +33 -33
- package/src/electron-browser/port-forwarding/port-forwarding-service.ts +92 -92
- package/src/electron-browser/port-forwarding/port-forwarding-widget.tsx +140 -140
- package/src/electron-browser/remote-electron-file-dialog-service.ts +47 -47
- package/src/electron-browser/remote-frontend-contribution.ts +143 -143
- package/src/electron-browser/remote-frontend-module.ts +68 -68
- package/src/electron-browser/remote-preferences.ts +62 -62
- package/src/electron-browser/remote-registry-contribution.ts +73 -73
- package/src/electron-browser/remote-service.ts +31 -31
- package/src/electron-browser/remote-ssh-contribution.ts +102 -102
- package/src/electron-browser/style/port-forwarding-widget.css +44 -44
- package/src/electron-common/remote-port-forwarding-provider.ts +30 -30
- package/src/electron-common/remote-ssh-connection-provider.ts +29 -29
- package/src/electron-common/remote-status-service.ts +35 -35
- package/src/electron-node/backend-remote-service-impl.ts +45 -45
- package/src/electron-node/remote-backend-module.ts +87 -87
- package/src/electron-node/remote-connection-service.ts +56 -56
- package/src/electron-node/remote-connection-socket-provider.ts +34 -34
- package/src/electron-node/remote-port-forwarding-provider.ts +66 -66
- package/src/electron-node/remote-proxy-server-provider.ts +37 -37
- package/src/electron-node/remote-status-service.ts +41 -41
- package/src/electron-node/remote-types.ts +64 -64
- package/src/electron-node/setup/app-native-dependency-contribution.ts +48 -48
- package/src/electron-node/setup/main-copy-contribution.ts +28 -28
- package/src/electron-node/setup/remote-copy-contribution.ts +74 -74
- package/src/electron-node/setup/remote-copy-service.ts +116 -116
- package/src/electron-node/setup/remote-native-dependency-contribution.ts +63 -63
- package/src/electron-node/setup/remote-native-dependency-service.ts +111 -111
- package/src/electron-node/setup/remote-node-setup-service.ts +123 -123
- package/src/electron-node/setup/remote-setup-script-service.ts +146 -146
- package/src/electron-node/setup/remote-setup-service.ts +220 -220
- package/src/electron-node/ssh/remote-ssh-connection-provider.ts +358 -358
- package/src/electron-node/ssh/ssh-identity-file-collector.ts +137 -137
- package/src/package.spec.ts +29 -29
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2023 TypeFox and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
18
|
-
import { RemoteConnection, RemoteExecResult, RemoteStatusReport } from '../remote-types';
|
|
19
|
-
import { RemoteCliContext, RemoteCliContribution, RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution';
|
|
20
|
-
import { ApplicationPackage } from '@theia/core/shared/@theia/application-package';
|
|
21
|
-
import { RemoteCopyService } from './remote-copy-service';
|
|
22
|
-
import { RemoteNativeDependencyService } from './remote-native-dependency-service';
|
|
23
|
-
import { ContributionProvider, OS, THEIA_VERSION } from '@theia/core';
|
|
24
|
-
import { RemoteNodeSetupService } from './remote-node-setup-service';
|
|
25
|
-
import { RemoteSetupScriptService } from './remote-setup-script-service';
|
|
26
|
-
|
|
27
|
-
export interface RemoteSetupOptions {
|
|
28
|
-
connection: RemoteConnection;
|
|
29
|
-
report: RemoteStatusReport;
|
|
30
|
-
nodeDownloadTemplate?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface RemoteSetupResult {
|
|
34
|
-
applicationDirectory: string;
|
|
35
|
-
nodeDirectory: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@injectable()
|
|
39
|
-
export class RemoteSetupService {
|
|
40
|
-
|
|
41
|
-
@inject(RemoteCopyService)
|
|
42
|
-
protected readonly copyService: RemoteCopyService;
|
|
43
|
-
|
|
44
|
-
@inject(RemoteNativeDependencyService)
|
|
45
|
-
protected readonly nativeDependencyService: RemoteNativeDependencyService;
|
|
46
|
-
|
|
47
|
-
@inject(RemoteNodeSetupService)
|
|
48
|
-
protected readonly nodeSetupService: RemoteNodeSetupService;
|
|
49
|
-
|
|
50
|
-
@inject(RemoteSetupScriptService)
|
|
51
|
-
protected readonly scriptService: RemoteSetupScriptService;
|
|
52
|
-
|
|
53
|
-
@inject(ApplicationPackage)
|
|
54
|
-
protected readonly applicationPackage: ApplicationPackage;
|
|
55
|
-
|
|
56
|
-
@inject(ContributionProvider) @named(RemoteCliContribution)
|
|
57
|
-
protected readonly cliContributions: ContributionProvider<RemoteCliContribution>;
|
|
58
|
-
|
|
59
|
-
async setup(options: RemoteSetupOptions): Promise<RemoteSetupResult> {
|
|
60
|
-
const {
|
|
61
|
-
connection,
|
|
62
|
-
report,
|
|
63
|
-
nodeDownloadTemplate
|
|
64
|
-
} = options;
|
|
65
|
-
report('Identifying remote system...');
|
|
66
|
-
// 1. Identify remote platform
|
|
67
|
-
const platform = await this.detectRemotePlatform(connection);
|
|
68
|
-
// 2. Setup home directory
|
|
69
|
-
const remoteHome = await this.getRemoteHomeDirectory(connection, platform);
|
|
70
|
-
const applicationDirectory = this.scriptService.joinPath(platform, remoteHome, `.${this.getRemoteAppName()}`);
|
|
71
|
-
await this.mkdirRemote(connection, platform, applicationDirectory);
|
|
72
|
-
// 3. Download+copy node for that platform
|
|
73
|
-
const nodeFileName = this.nodeSetupService.getNodeFileName(platform);
|
|
74
|
-
const nodeDirName = this.nodeSetupService.getNodeDirectoryName(platform);
|
|
75
|
-
const remoteNodeDirectory = this.scriptService.joinPath(platform, applicationDirectory, nodeDirName);
|
|
76
|
-
const nodeDirExists = await this.dirExistsRemote(connection, remoteNodeDirectory);
|
|
77
|
-
if (!nodeDirExists) {
|
|
78
|
-
report('Downloading and installing Node.js on remote...');
|
|
79
|
-
// Download the binaries locally and move it via SSH
|
|
80
|
-
const nodeArchive = await this.nodeSetupService.downloadNode(platform, nodeDownloadTemplate);
|
|
81
|
-
const remoteNodeZip = this.scriptService.joinPath(platform, applicationDirectory, nodeFileName);
|
|
82
|
-
await connection.copy(nodeArchive, remoteNodeZip);
|
|
83
|
-
await this.unzipRemote(connection, platform, remoteNodeZip, applicationDirectory);
|
|
84
|
-
}
|
|
85
|
-
// 4. Copy backend to remote system
|
|
86
|
-
const libDir = this.scriptService.joinPath(platform, applicationDirectory, 'lib');
|
|
87
|
-
const libDirExists = await this.dirExistsRemote(connection, libDir);
|
|
88
|
-
if (!libDirExists) {
|
|
89
|
-
report('Installing application on remote...');
|
|
90
|
-
const applicationZipFile = this.scriptService.joinPath(platform, applicationDirectory, `${this.getRemoteAppName()}.tar`);
|
|
91
|
-
await this.copyService.copyToRemote(connection, platform, applicationZipFile);
|
|
92
|
-
await this.unzipRemote(connection, platform, applicationZipFile, applicationDirectory);
|
|
93
|
-
}
|
|
94
|
-
// 5. start remote backend
|
|
95
|
-
report('Starting application on remote...');
|
|
96
|
-
const port = await this.startApplication(connection, platform, applicationDirectory, remoteNodeDirectory);
|
|
97
|
-
connection.remotePort = port;
|
|
98
|
-
return {
|
|
99
|
-
applicationDirectory: libDir,
|
|
100
|
-
nodeDirectory: remoteNodeDirectory
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
protected async startApplication(connection: RemoteConnection, platform: RemotePlatform, remotePath: string, nodeDir: string): Promise<number> {
|
|
105
|
-
const nodeExecutable = this.scriptService.joinPath(platform, nodeDir, ...(platform.os === OS.Type.Windows ? ['node.exe'] : ['bin', 'node']));
|
|
106
|
-
const mainJsFile = this.scriptService.joinPath(platform, remotePath, 'lib', 'backend', 'main.js');
|
|
107
|
-
const localAddressRegex = /listening on http:\/\/0.0.0.0:(\d+)/;
|
|
108
|
-
let prefix = '';
|
|
109
|
-
if (platform.os === OS.Type.Windows) {
|
|
110
|
-
// We might to switch to PowerShell beforehand on Windows
|
|
111
|
-
prefix = this.scriptService.exec(platform) + ' ';
|
|
112
|
-
}
|
|
113
|
-
const remoteContext: RemoteCliContext = {
|
|
114
|
-
platform,
|
|
115
|
-
directory: remotePath
|
|
116
|
-
};
|
|
117
|
-
const args: string[] = ['--hostname=0.0.0.0', `--port=${connection.remotePort ?? 0}`, '--remote'];
|
|
118
|
-
for (const cli of this.cliContributions.getContributions()) {
|
|
119
|
-
if (cli.enhanceArgs) {
|
|
120
|
-
args.push(...await cli.enhanceArgs(remoteContext));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
// Change to the remote application path and start a node process with the copied main.js file
|
|
124
|
-
// This way, our current working directory is set as expected
|
|
125
|
-
const result = await connection.execPartial(`${prefix}cd "${remotePath}";${nodeExecutable}`,
|
|
126
|
-
stdout => localAddressRegex.test(stdout),
|
|
127
|
-
[mainJsFile, ...args]);
|
|
128
|
-
|
|
129
|
-
const match = localAddressRegex.exec(result.stdout);
|
|
130
|
-
if (!match) {
|
|
131
|
-
throw new Error('Could not start remote system: ' + result.stdout);
|
|
132
|
-
} else {
|
|
133
|
-
return Number(match[1]);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
protected async detectRemotePlatform(connection: RemoteConnection): Promise<RemotePlatform> {
|
|
138
|
-
const osResult = await connection.exec('uname -s');
|
|
139
|
-
|
|
140
|
-
let os: OS.Type | undefined;
|
|
141
|
-
if (osResult.stderr) {
|
|
142
|
-
// Only Windows systems return an error stream here
|
|
143
|
-
os = OS.Type.Windows;
|
|
144
|
-
} else if (osResult.stdout) {
|
|
145
|
-
if (osResult.stdout.includes('windows32') || osResult.stdout.includes('MINGW64')) {
|
|
146
|
-
os = OS.Type.Windows;
|
|
147
|
-
} else if (osResult.stdout.includes('Linux')) {
|
|
148
|
-
os = OS.Type.Linux;
|
|
149
|
-
} else if (osResult.stdout.includes('Darwin')) {
|
|
150
|
-
os = OS.Type.OSX;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (!os) {
|
|
154
|
-
throw new Error('Failed to identify remote system: ' + osResult.stdout + '\n' + osResult.stderr);
|
|
155
|
-
}
|
|
156
|
-
let arch: string | undefined;
|
|
157
|
-
if (os === OS.Type.Windows) {
|
|
158
|
-
const wmicResult = await connection.exec('wmic OS get OSArchitecture');
|
|
159
|
-
if (wmicResult.stdout.includes('64-bit')) {
|
|
160
|
-
arch = 'x64';
|
|
161
|
-
} else if (wmicResult.stdout.includes('32-bit')) {
|
|
162
|
-
arch = 'x86';
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
const archResult = (await connection.exec('uname -m')).stdout;
|
|
166
|
-
if (archResult.includes('x86_64')) {
|
|
167
|
-
arch = 'x64';
|
|
168
|
-
} else if (archResult.match(/i\d83/)) { // i386, i483, i683
|
|
169
|
-
arch = 'x86';
|
|
170
|
-
} else {
|
|
171
|
-
arch = archResult.trim();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (!arch) {
|
|
175
|
-
throw new Error('Could not identify remote system architecture');
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
os,
|
|
179
|
-
arch
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
protected async getRemoteHomeDirectory(connection: RemoteConnection, platform: RemotePlatform): Promise<string> {
|
|
184
|
-
const result = await connection.exec(this.scriptService.home(platform));
|
|
185
|
-
return result.stdout.trim();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
protected getRemoteAppName(): string {
|
|
189
|
-
const appName = this.applicationPackage.pck.name || 'theia';
|
|
190
|
-
const appVersion = this.applicationPackage.pck.version || THEIA_VERSION;
|
|
191
|
-
return `${this.cleanupDirectoryName(`${appName}-${appVersion}`)}-remote`;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
protected cleanupDirectoryName(name: string): string {
|
|
195
|
-
return name.replace(/[@<>:"\\|?*]/g, '').replace(/\//g, '-');
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
protected async mkdirRemote(connection: RemoteConnection, platform: RemotePlatform, remotePath: string): Promise<void> {
|
|
199
|
-
const result = await connection.exec(this.scriptService.mkdir(platform, remotePath));
|
|
200
|
-
if (result.stderr) {
|
|
201
|
-
throw new Error('Failed to create directory: ' + result.stderr);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
protected async dirExistsRemote(connection: RemoteConnection, remotePath: string): Promise<boolean> {
|
|
206
|
-
const cdResult = await connection.exec(`cd "${remotePath}"`);
|
|
207
|
-
return !Boolean(cdResult.stderr);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
protected async unzipRemote(connection: RemoteConnection, platform: RemotePlatform, remoteFile: string, remoteDirectory: string): Promise<void> {
|
|
211
|
-
const result = await connection.exec(this.scriptService.unzip(platform, remoteFile, remoteDirectory));
|
|
212
|
-
if (result.stderr) {
|
|
213
|
-
throw new Error('Failed to unzip: ' + result.stderr);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
protected async executeScriptRemote(connection: RemoteConnection, platform: RemotePlatform, script: string): Promise<RemoteExecResult> {
|
|
218
|
-
return connection.exec(this.scriptService.exec(platform), [script]);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
18
|
+
import { RemoteConnection, RemoteExecResult, RemoteStatusReport } from '../remote-types';
|
|
19
|
+
import { RemoteCliContext, RemoteCliContribution, RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution';
|
|
20
|
+
import { ApplicationPackage } from '@theia/core/shared/@theia/application-package';
|
|
21
|
+
import { RemoteCopyService } from './remote-copy-service';
|
|
22
|
+
import { RemoteNativeDependencyService } from './remote-native-dependency-service';
|
|
23
|
+
import { ContributionProvider, OS, THEIA_VERSION } from '@theia/core';
|
|
24
|
+
import { RemoteNodeSetupService } from './remote-node-setup-service';
|
|
25
|
+
import { RemoteSetupScriptService } from './remote-setup-script-service';
|
|
26
|
+
|
|
27
|
+
export interface RemoteSetupOptions {
|
|
28
|
+
connection: RemoteConnection;
|
|
29
|
+
report: RemoteStatusReport;
|
|
30
|
+
nodeDownloadTemplate?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RemoteSetupResult {
|
|
34
|
+
applicationDirectory: string;
|
|
35
|
+
nodeDirectory: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@injectable()
|
|
39
|
+
export class RemoteSetupService {
|
|
40
|
+
|
|
41
|
+
@inject(RemoteCopyService)
|
|
42
|
+
protected readonly copyService: RemoteCopyService;
|
|
43
|
+
|
|
44
|
+
@inject(RemoteNativeDependencyService)
|
|
45
|
+
protected readonly nativeDependencyService: RemoteNativeDependencyService;
|
|
46
|
+
|
|
47
|
+
@inject(RemoteNodeSetupService)
|
|
48
|
+
protected readonly nodeSetupService: RemoteNodeSetupService;
|
|
49
|
+
|
|
50
|
+
@inject(RemoteSetupScriptService)
|
|
51
|
+
protected readonly scriptService: RemoteSetupScriptService;
|
|
52
|
+
|
|
53
|
+
@inject(ApplicationPackage)
|
|
54
|
+
protected readonly applicationPackage: ApplicationPackage;
|
|
55
|
+
|
|
56
|
+
@inject(ContributionProvider) @named(RemoteCliContribution)
|
|
57
|
+
protected readonly cliContributions: ContributionProvider<RemoteCliContribution>;
|
|
58
|
+
|
|
59
|
+
async setup(options: RemoteSetupOptions): Promise<RemoteSetupResult> {
|
|
60
|
+
const {
|
|
61
|
+
connection,
|
|
62
|
+
report,
|
|
63
|
+
nodeDownloadTemplate
|
|
64
|
+
} = options;
|
|
65
|
+
report('Identifying remote system...');
|
|
66
|
+
// 1. Identify remote platform
|
|
67
|
+
const platform = await this.detectRemotePlatform(connection);
|
|
68
|
+
// 2. Setup home directory
|
|
69
|
+
const remoteHome = await this.getRemoteHomeDirectory(connection, platform);
|
|
70
|
+
const applicationDirectory = this.scriptService.joinPath(platform, remoteHome, `.${this.getRemoteAppName()}`);
|
|
71
|
+
await this.mkdirRemote(connection, platform, applicationDirectory);
|
|
72
|
+
// 3. Download+copy node for that platform
|
|
73
|
+
const nodeFileName = this.nodeSetupService.getNodeFileName(platform);
|
|
74
|
+
const nodeDirName = this.nodeSetupService.getNodeDirectoryName(platform);
|
|
75
|
+
const remoteNodeDirectory = this.scriptService.joinPath(platform, applicationDirectory, nodeDirName);
|
|
76
|
+
const nodeDirExists = await this.dirExistsRemote(connection, remoteNodeDirectory);
|
|
77
|
+
if (!nodeDirExists) {
|
|
78
|
+
report('Downloading and installing Node.js on remote...');
|
|
79
|
+
// Download the binaries locally and move it via SSH
|
|
80
|
+
const nodeArchive = await this.nodeSetupService.downloadNode(platform, nodeDownloadTemplate);
|
|
81
|
+
const remoteNodeZip = this.scriptService.joinPath(platform, applicationDirectory, nodeFileName);
|
|
82
|
+
await connection.copy(nodeArchive, remoteNodeZip);
|
|
83
|
+
await this.unzipRemote(connection, platform, remoteNodeZip, applicationDirectory);
|
|
84
|
+
}
|
|
85
|
+
// 4. Copy backend to remote system
|
|
86
|
+
const libDir = this.scriptService.joinPath(platform, applicationDirectory, 'lib');
|
|
87
|
+
const libDirExists = await this.dirExistsRemote(connection, libDir);
|
|
88
|
+
if (!libDirExists) {
|
|
89
|
+
report('Installing application on remote...');
|
|
90
|
+
const applicationZipFile = this.scriptService.joinPath(platform, applicationDirectory, `${this.getRemoteAppName()}.tar`);
|
|
91
|
+
await this.copyService.copyToRemote(connection, platform, applicationZipFile);
|
|
92
|
+
await this.unzipRemote(connection, platform, applicationZipFile, applicationDirectory);
|
|
93
|
+
}
|
|
94
|
+
// 5. start remote backend
|
|
95
|
+
report('Starting application on remote...');
|
|
96
|
+
const port = await this.startApplication(connection, platform, applicationDirectory, remoteNodeDirectory);
|
|
97
|
+
connection.remotePort = port;
|
|
98
|
+
return {
|
|
99
|
+
applicationDirectory: libDir,
|
|
100
|
+
nodeDirectory: remoteNodeDirectory
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
protected async startApplication(connection: RemoteConnection, platform: RemotePlatform, remotePath: string, nodeDir: string): Promise<number> {
|
|
105
|
+
const nodeExecutable = this.scriptService.joinPath(platform, nodeDir, ...(platform.os === OS.Type.Windows ? ['node.exe'] : ['bin', 'node']));
|
|
106
|
+
const mainJsFile = this.scriptService.joinPath(platform, remotePath, 'lib', 'backend', 'main.js');
|
|
107
|
+
const localAddressRegex = /listening on http:\/\/0.0.0.0:(\d+)/;
|
|
108
|
+
let prefix = '';
|
|
109
|
+
if (platform.os === OS.Type.Windows) {
|
|
110
|
+
// We might to switch to PowerShell beforehand on Windows
|
|
111
|
+
prefix = this.scriptService.exec(platform) + ' ';
|
|
112
|
+
}
|
|
113
|
+
const remoteContext: RemoteCliContext = {
|
|
114
|
+
platform,
|
|
115
|
+
directory: remotePath
|
|
116
|
+
};
|
|
117
|
+
const args: string[] = ['--hostname=0.0.0.0', `--port=${connection.remotePort ?? 0}`, '--remote'];
|
|
118
|
+
for (const cli of this.cliContributions.getContributions()) {
|
|
119
|
+
if (cli.enhanceArgs) {
|
|
120
|
+
args.push(...await cli.enhanceArgs(remoteContext));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Change to the remote application path and start a node process with the copied main.js file
|
|
124
|
+
// This way, our current working directory is set as expected
|
|
125
|
+
const result = await connection.execPartial(`${prefix}cd "${remotePath}";${nodeExecutable}`,
|
|
126
|
+
stdout => localAddressRegex.test(stdout),
|
|
127
|
+
[mainJsFile, ...args]);
|
|
128
|
+
|
|
129
|
+
const match = localAddressRegex.exec(result.stdout);
|
|
130
|
+
if (!match) {
|
|
131
|
+
throw new Error('Could not start remote system: ' + result.stdout);
|
|
132
|
+
} else {
|
|
133
|
+
return Number(match[1]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
protected async detectRemotePlatform(connection: RemoteConnection): Promise<RemotePlatform> {
|
|
138
|
+
const osResult = await connection.exec('uname -s');
|
|
139
|
+
|
|
140
|
+
let os: OS.Type | undefined;
|
|
141
|
+
if (osResult.stderr) {
|
|
142
|
+
// Only Windows systems return an error stream here
|
|
143
|
+
os = OS.Type.Windows;
|
|
144
|
+
} else if (osResult.stdout) {
|
|
145
|
+
if (osResult.stdout.includes('windows32') || osResult.stdout.includes('MINGW64')) {
|
|
146
|
+
os = OS.Type.Windows;
|
|
147
|
+
} else if (osResult.stdout.includes('Linux')) {
|
|
148
|
+
os = OS.Type.Linux;
|
|
149
|
+
} else if (osResult.stdout.includes('Darwin')) {
|
|
150
|
+
os = OS.Type.OSX;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!os) {
|
|
154
|
+
throw new Error('Failed to identify remote system: ' + osResult.stdout + '\n' + osResult.stderr);
|
|
155
|
+
}
|
|
156
|
+
let arch: string | undefined;
|
|
157
|
+
if (os === OS.Type.Windows) {
|
|
158
|
+
const wmicResult = await connection.exec('wmic OS get OSArchitecture');
|
|
159
|
+
if (wmicResult.stdout.includes('64-bit')) {
|
|
160
|
+
arch = 'x64';
|
|
161
|
+
} else if (wmicResult.stdout.includes('32-bit')) {
|
|
162
|
+
arch = 'x86';
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
const archResult = (await connection.exec('uname -m')).stdout;
|
|
166
|
+
if (archResult.includes('x86_64')) {
|
|
167
|
+
arch = 'x64';
|
|
168
|
+
} else if (archResult.match(/i\d83/)) { // i386, i483, i683
|
|
169
|
+
arch = 'x86';
|
|
170
|
+
} else {
|
|
171
|
+
arch = archResult.trim();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (!arch) {
|
|
175
|
+
throw new Error('Could not identify remote system architecture');
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
os,
|
|
179
|
+
arch
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
protected async getRemoteHomeDirectory(connection: RemoteConnection, platform: RemotePlatform): Promise<string> {
|
|
184
|
+
const result = await connection.exec(this.scriptService.home(platform));
|
|
185
|
+
return result.stdout.trim();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
protected getRemoteAppName(): string {
|
|
189
|
+
const appName = this.applicationPackage.pck.name || 'theia';
|
|
190
|
+
const appVersion = this.applicationPackage.pck.version || THEIA_VERSION;
|
|
191
|
+
return `${this.cleanupDirectoryName(`${appName}-${appVersion}`)}-remote`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
protected cleanupDirectoryName(name: string): string {
|
|
195
|
+
return name.replace(/[@<>:"\\|?*]/g, '').replace(/\//g, '-');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected async mkdirRemote(connection: RemoteConnection, platform: RemotePlatform, remotePath: string): Promise<void> {
|
|
199
|
+
const result = await connection.exec(this.scriptService.mkdir(platform, remotePath));
|
|
200
|
+
if (result.stderr) {
|
|
201
|
+
throw new Error('Failed to create directory: ' + result.stderr);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
protected async dirExistsRemote(connection: RemoteConnection, remotePath: string): Promise<boolean> {
|
|
206
|
+
const cdResult = await connection.exec(`cd "${remotePath}"`);
|
|
207
|
+
return !Boolean(cdResult.stderr);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
protected async unzipRemote(connection: RemoteConnection, platform: RemotePlatform, remoteFile: string, remoteDirectory: string): Promise<void> {
|
|
211
|
+
const result = await connection.exec(this.scriptService.unzip(platform, remoteFile, remoteDirectory));
|
|
212
|
+
if (result.stderr) {
|
|
213
|
+
throw new Error('Failed to unzip: ' + result.stderr);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
protected async executeScriptRemote(connection: RemoteConnection, platform: RemotePlatform, script: string): Promise<RemoteExecResult> {
|
|
218
|
+
return connection.exec(this.scriptService.exec(platform), [script]);
|
|
219
|
+
}
|
|
220
|
+
}
|