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';
J
Joao Moreno 已提交
19
import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension, LocalExtensionType } 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';
J
Joao Moreno 已提交
41

42 43
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 已提交
44
const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, eg: {0}", 'ms-vscode.csharp');
J
Joao Moreno 已提交
45

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

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

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


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

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

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

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

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

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

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

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

S
Sandeep Somavarapu 已提交
101 102 103 104 105 106 107 108 109 110 111 112
	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 已提交
113

S
Sandeep Somavarapu 已提交
114 115 116 117 118 119 120 121 122 123 124 125 126
	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;
127
							} else {
S
Sandeep Somavarapu 已提交
128
								return Promise.reject(error);
129
							}
S
Sandeep Somavarapu 已提交
130 131 132 133 134
						});
					}
					return null;
				});
		}
J
Joao Moreno 已提交
135

S
Sandeep Somavarapu 已提交
136 137 138 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
		const [id, version] = getIdAndVersion(extension);
		return this.extensionManagementService.getInstalled(LocalExtensionType.User)
			.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);
					}
173

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

S
Sandeep Somavarapu 已提交
177 178


J
Joao Moreno 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
	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) };
		const installedExtensions = await this.extensionManagementService.getInstalled(LocalExtensionType.User);
		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 已提交
196 197
	}

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

		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;
			}
		}
211 212
	}

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

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

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

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

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

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

243 244
const eventPrefix = 'monacoworkbench';

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

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

	logService.info('main', argv);

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

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

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

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

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

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

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

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

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

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

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