cliProcessMain.ts 13.2 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6
import { localize } from 'vs/nls';
7 8
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
9
import * as path from 'path';
S
Sandeep Somavarapu 已提交
10
import * as semver from 'semver';
J
Joao Moreno 已提交
11

12
import { sequence } from 'vs/base/common/async';
J
Joao Moreno 已提交
13 14 15 16
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
J
Joao Moreno 已提交
17
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
18
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
19
import { IExtensionManagementService, IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
20
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
J
Joao Moreno 已提交
21
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
22 23
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
24 25
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
J
Joao Moreno 已提交
26
import { IRequestService } from 'vs/platform/request/node/request';
J
Joao Moreno 已提交
27
import { RequestService } from 'vs/platform/request/node/requestService';
J
Joao Moreno 已提交
28
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
29
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
30
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
31
import { mkdirp, writeFile } from 'vs/base/node/pfs';
B
Benjamin Pasero 已提交
32
import { getBaseLabel } from 'vs/base/common/labels';
33 34
import { IStateService } from 'vs/platform/state/common/state';
import { StateService } from 'vs/platform/state/node/stateService';
35
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
S
Sandeep Somavarapu 已提交
36
import { ILogService, getLogLevel } from 'vs/platform/log/common/log';
37
import { isPromiseCanceledError } from 'vs/base/common/errors';
S
Sandeep Somavarapu 已提交
38
import { areSameExtensions, getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
39
import { URI } from 'vs/base/common/uri';
40
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
41
import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions';
J
Joao Moreno 已提交
42

43 44
const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id);
const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id);
J
Joao Moreno 已提交
45
const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, eg: {0}", 'ms-vscode.csharp');
J
Joao Moreno 已提交
46

G
greams 已提交
47 48
function getId(manifest: IExtensionManifest, withVersion?: boolean): string {
	if (withVersion) {
J
Joao Moreno 已提交
49
		return `${manifest.publisher}.${manifest.name}@${manifest.version}`;
G
greams 已提交
50
	} else {
51
		return `${manifest.publisher}.${manifest.name}`;
G
greams 已提交
52
	}
J
Joao Moreno 已提交
53 54
}

55 56
const EXTENSION_ID_REGEX = /^([^.]+\..+)@(\d+\.\d+\.\d+(-.*)?)$/;

M
Matt Bierner 已提交
57
export function getIdAndVersion(id: string): [string, string | undefined] {
58 59 60 61
	const matches = EXTENSION_ID_REGEX.exec(id);
	if (matches && matches[1]) {
		return [adoptToGalleryExtensionId(matches[1]), matches[2]];
	}
R
Rob Lourens 已提交
62
	return [adoptToGalleryExtensionId(id), undefined];
63 64 65
}


J
Joao Moreno 已提交
66 67 68
class Main {

	constructor(
69 70 71
		@IEnvironmentService private readonly environmentService: IEnvironmentService,
		@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
		@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService
J
Johannes Rieken 已提交
72
	) { }
J
Joao Moreno 已提交
73

J
Joao Moreno 已提交
74
	async run(argv: ParsedArgs): Promise<any> {
75
		if (argv['install-source']) {
J
Joao Moreno 已提交
76 77
			await this.setInstallSource(argv['install-source']);

78
		} else if (argv['list-extensions']) {
M
Matt Bierner 已提交
79
			await this.listExtensions(!!argv['show-versions']);
J
Joao Moreno 已提交
80

J
Joao Moreno 已提交
81
		} else if (argv['install-extension']) {
82
			const arg = argv['install-extension'];
J
Joao Moreno 已提交
83
			const args: string[] = typeof arg === 'string' ? [arg] : arg;
S
Sandeep Somavarapu 已提交
84
			await this.installExtensions(args, argv['force']);
J
Joao Moreno 已提交
85

J
Joao Moreno 已提交
86
		} else if (argv['uninstall-extension']) {
87 88
			const arg = argv['uninstall-extension'];
			const ids: string[] = typeof arg === 'string' ? [arg] : arg;
J
Joao Moreno 已提交
89
			await this.uninstallExtension(ids);
J
Joao Moreno 已提交
90 91
		}
	}
J
Joao Moreno 已提交
92

J
Joao Moreno 已提交
93
	private setInstallSource(installSource: string): Promise<any> {
94
		return writeFile(this.environmentService.installSourcePath, installSource.slice(0, 30));
95 96
	}

J
Joao Moreno 已提交
97
	private async listExtensions(showVersions: boolean): Promise<any> {
98
		const extensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
J
Joao Moreno 已提交
99
		extensions.forEach(e => console.log(getId(e.manifest, showVersions)));
J
Joao Moreno 已提交
100 101
	}

S
Sandeep Somavarapu 已提交
102 103 104 105 106 107 108 109 110 111 112 113
	private async installExtensions(extensions: string[], force: boolean): Promise<void> {
		let failed: string[] = [];
		for (const extension of extensions) {
			try {
				await this.installExtension(extension, force);
			} catch (err) {
				console.error(err.message || err.stack || err);
				failed.push(extension);
			}
		}
		return failed.length ? Promise.reject(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))) : Promise.resolve();
	}
J
Joao Moreno 已提交
114

S
Sandeep Somavarapu 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127
	private installExtension(extension: string, force: boolean): Promise<any> {
		if (/\.vsix$/i.test(extension)) {
			extension = path.isAbsolute(extension) ? extension : path.join(process.cwd(), extension);

			return this.validate(extension, force)
				.then(valid => {
					if (valid) {
						return this.extensionManagementService.install(URI.file(extension)).then(() => {
							console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", getBaseLabel(extension)));
						}, error => {
							if (isPromiseCanceledError(error)) {
								console.log(localize('cancelVsixInstall', "Cancelled installing Extension '{0}'.", getBaseLabel(extension)));
								return null;
128
							} else {
S
Sandeep Somavarapu 已提交
129
								return Promise.reject(error);
130
							}
S
Sandeep Somavarapu 已提交
131 132 133 134 135
						});
					}
					return null;
				});
		}
J
Joao Moreno 已提交
136

S
Sandeep Somavarapu 已提交
137
		const [id, version] = getIdAndVersion(extension);
138
		return this.extensionManagementService.getInstalled(ExtensionType.User)
S
Sandeep Somavarapu 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
			.then(installed => this.extensionGalleryService.getExtension({ id }, version)
				.then<IGalleryExtension>(null, err => {
					if (err.responseText) {
						try {
							const response = JSON.parse(err.responseText);
							return Promise.reject(response.message);
						} catch (e) {
							// noop
						}
					}
					return Promise.reject(err);
				})
				.then(extension => {
					if (!extension) {
						return Promise.reject(new Error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`));
					}

					const [installedExtension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id }));
					if (installedExtension) {
						if (extension.version !== installedExtension.manifest.version) {
							if (version || force) {
								console.log(localize('updateMessage', "Updating the Extension '{0}' to the version {1}", id, extension.version));
								return this.installFromGallery(id, extension);
							} else {
								console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, extension.version));
								return Promise.resolve(null);
							}
						} else {
							console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id));
							return Promise.resolve(null);
						}
					} else {
						console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id));
						return this.installFromGallery(id, extension);
					}
174

S
Sandeep Somavarapu 已提交
175
				}));
J
Joao Moreno 已提交
176
	}
J
Joao Moreno 已提交
177

S
Sandeep Somavarapu 已提交
178 179


J
Joao Moreno 已提交
180 181 182 183 184 185 186 187
	private async validate(vsix: string, force: boolean): Promise<boolean> {
		const manifest = await getManifest(vsix);

		if (!manifest) {
			throw new Error('Invalid vsix');
		}

		const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
188
		const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
J
Joao Moreno 已提交
189 190 191 192 193 194 195 196
		const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, { id: getGalleryExtensionIdFromLocal(local) }) && semver.gt(local.manifest.version, manifest.version))[0];

		if (newer && !force) {
			console.log(localize('forceDowngrade', "A newer version of this extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", newer.galleryIdentifier.id, newer.manifest.version, manifest.version));
			return false;
		}

		return true;
S
Sandeep Somavarapu 已提交
197 198
	}

J
Joao Moreno 已提交
199
	private async installFromGallery(id: string, extension: IGalleryExtension): Promise<void> {
200
		console.log(localize('installing', "Installing..."));
J
Joao Moreno 已提交
201 202 203 204 205 206 207 208 209 210 211

		try {
			await this.extensionManagementService.installFromGallery(extension);
			console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version));
		} catch (error) {
			if (isPromiseCanceledError(error)) {
				console.log(localize('cancelVsixInstall', "Cancelled installing Extension '{0}'.", id));
			} else {
				throw error;
			}
		}
212 213
	}

J
Johannes Rieken 已提交
214
	private uninstallExtension(extensions: string[]): Promise<any> {
J
Joao Moreno 已提交
215
		async function getExtensionId(extensionDescription: string): Promise<string> {
216 217
			if (!/\.vsix$/i.test(extensionDescription)) {
				return extensionDescription;
218
			}
J
Joao Moreno 已提交
219

220
			const zipPath = path.isAbsolute(extensionDescription) ? extensionDescription : path.join(process.cwd(), extensionDescription);
221
			const manifest = await getManifest(zipPath);
222
			return getId(manifest);
223
		}
J
Joao Moreno 已提交
224

225
		return sequence(extensions.map(extension => () => {
226
			return getExtensionId(extension).then(id => {
227
				return this.extensionManagementService.getInstalled(ExtensionType.User).then(installed => {
S
Sandeep Somavarapu 已提交
228
					const [extension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id }));
J
Joao Moreno 已提交
229

230
					if (!extension) {
J
Joao Moreno 已提交
231
						return Promise.reject(new Error(`${notInstalled(id)}\n${useId}`));
232
					}
J
Joao Moreno 已提交
233

234
					console.log(localize('uninstalling', "Uninstalling {0}...", id));
J
Joao Moreno 已提交
235

236 237 238
					return this.extensionManagementService.uninstall(extension, true)
						.then(() => console.log(localize('successUninstall', "Extension '{0}' was successfully uninstalled!", id)));
				});
J
Joao Moreno 已提交
239
			});
240
		}));
J
Joao Moreno 已提交
241
	}
J
Joao Moreno 已提交
242 243
}

244 245
const eventPrefix = 'monacoworkbench';

J
Joao Moreno 已提交
246
export function main(argv: ParsedArgs): Promise<void> {
J
Joao Moreno 已提交
247
	const services = new ServiceCollection();
J
Joao Moreno 已提交
248 249

	const environmentService = new EnvironmentService(argv, process.execPath);
S
Sandeep Somavarapu 已提交
250
	const logService = createSpdLogService('cli', getLogLevel(environmentService), environmentService.logsPath);
251
	process.once('exit', () => logService.dispose());
J
Joao Moreno 已提交
252 253 254 255 256

	logService.info('main', argv);

	services.set(IEnvironmentService, environmentService);
	services.set(ILogService, logService);
257
	services.set(IStateService, new SyncDescriptor(StateService));
J
Joao Moreno 已提交
258 259

	const instantiationService: IInstantiationService = new InstantiationService(services);
260 261

	return instantiationService.invokeFunction(accessor => {
J
Joao Moreno 已提交
262
		const envService = accessor.get(IEnvironmentService);
263
		const stateService = accessor.get(IStateService);
264

J
Joao Moreno 已提交
265
		return Promise.all([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => {
266
			const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService;
267

J
Joao Moreno 已提交
268
			const services = new ServiceCollection();
269
			services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
J
Joao Moreno 已提交
270
			services.set(IRequestService, new SyncDescriptor(RequestService));
J
Joao Moreno 已提交
271 272
			services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
			services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
273

274
			const appenders: AppInsightsAppender[] = [];
275
			if (isBuilt && !extensionDevelopmentLocationURI && !envService.args['disable-telemetry'] && product.enableTelemetry) {
276

J
Joao Moreno 已提交
277
				if (product.aiConfig && product.aiConfig.asimovKey) {
278
					appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, logService));
J
Joao Moreno 已提交
279
				}
280

J
Joao Moreno 已提交
281 282
				const config: ITelemetryServiceConfig = {
					appender: combinedAppender(...appenders),
283
					commonProperties: resolveCommonProperties(product.commit, pkg.version, stateService.getItem('telemetry.machineId'), installSourcePath),
J
Joao Moreno 已提交
284 285
					piiPaths: [appRoot, extensionsPath]
				};
286

287
				services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
J
Joao Moreno 已提交
288 289 290
			} else {
				services.set(ITelemetryService, NullTelemetryService);
			}
291

J
Joao Moreno 已提交
292 293
			const instantiationService2 = instantiationService.createChild(services);
			const main = instantiationService2.createInstance(Main);
294

295 296 297 298
			return main.run(argv).then(() => {
				// Dispose the AI adapter so that remaining data gets flushed.
				return combinedAppender(...appenders).dispose();
			});
J
Joao Moreno 已提交
299
		});
300
	});
J
Joao Moreno 已提交
301
}