parent
416fe53a13
commit
c8caac361f
|
@ -113,9 +113,8 @@ class Main {
|
|||
console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id));
|
||||
console.log(localize('installing', "Installing..."));
|
||||
|
||||
return this.extensionManagementService.install(extension).then(() => {
|
||||
console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.versions[0].version));
|
||||
});
|
||||
return this.extensionManagementService.installFromGallery(extension)
|
||||
.then(() => console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed!", id, extension.version)));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,8 +17,9 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ
|
|||
import { IEventService } from 'vs/platform/event/common/event';
|
||||
import { EventService } from 'vs/platform/event/common/eventService';
|
||||
import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
|
||||
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
|
||||
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { NodeConfigurationService } from 'vs/platform/configuration/node/nodeConfigurationService';
|
||||
import { ITelemetryService, combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
@ -55,7 +56,6 @@ function main(server: Server): void {
|
|||
|
||||
services.set(IEventService, new SyncDescriptor(EventService));
|
||||
services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService));
|
||||
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
services.set(IConfigurationService, new SyncDescriptor(NodeConfigurationService));
|
||||
|
||||
const instantiationService = new InstantiationService(services);
|
||||
|
@ -93,6 +93,9 @@ function main(server: Server): void {
|
|||
services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
|
||||
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||
|
||||
const instantiationService2 = instantiationService.createChild(services);
|
||||
|
||||
instantiationService2.invokeFunction(accessor => {
|
||||
|
|
|
@ -22,25 +22,24 @@ export interface IExtensionManifest {
|
|||
icon?: string;
|
||||
}
|
||||
|
||||
export interface IGalleryVersion {
|
||||
version: string;
|
||||
date: string;
|
||||
manifestUrl: string;
|
||||
readmeUrl: string;
|
||||
downloadUrl: string;
|
||||
iconUrl: string;
|
||||
licenseUrl: string;
|
||||
downloadHeaders: { [key: string]: string; };
|
||||
}
|
||||
|
||||
export interface IExtensionIdentity {
|
||||
name: string;
|
||||
publisher: string;
|
||||
}
|
||||
|
||||
export interface IGalleryExtensionAssets {
|
||||
manifest: string;
|
||||
readme: string;
|
||||
download: string;
|
||||
icon: string;
|
||||
license: string;
|
||||
}
|
||||
|
||||
export interface IGalleryExtension {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
date: string;
|
||||
displayName: string;
|
||||
publisherId: string;
|
||||
publisher: string;
|
||||
|
@ -49,7 +48,8 @@ export interface IGalleryExtension {
|
|||
installCount: number;
|
||||
rating: number;
|
||||
ratingCount: number;
|
||||
versions: IGalleryVersion[];
|
||||
assets: IGalleryExtensionAssets;
|
||||
downloadHeaders: { [key: string]: string; };
|
||||
}
|
||||
|
||||
export interface IGalleryMetadata {
|
||||
|
@ -98,6 +98,7 @@ export interface IExtensionGalleryService {
|
|||
_serviceBrand: any;
|
||||
isEnabled(): boolean;
|
||||
query(options?: IQueryOptions): TPromise<IPager<IGalleryExtension>>;
|
||||
download(extension: IGalleryExtension): TPromise<string>;
|
||||
}
|
||||
|
||||
export type InstallExtensionEvent = { id: string; gallery?: IGalleryExtension; };
|
||||
|
@ -111,8 +112,8 @@ export interface IExtensionManagementService {
|
|||
onUninstallExtension: Event<string>;
|
||||
onDidUninstallExtension: Event<string>;
|
||||
|
||||
install(extension: IGalleryExtension): TPromise<void>;
|
||||
install(zipPath: string): TPromise<void>;
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void>;
|
||||
uninstall(extension: ILocalExtension): TPromise<void>;
|
||||
getInstalled(): TPromise<ILocalExtension[]>;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent } from './extensionManagement';
|
||||
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension } from './extensionManagement';
|
||||
import Event from 'vs/base/common/event';
|
||||
|
||||
export interface IExtensionManagementChannel extends IChannel {
|
||||
|
@ -15,7 +15,8 @@ export interface IExtensionManagementChannel extends IChannel {
|
|||
call(command: 'event:onDidInstallExtension'): TPromise<void>;
|
||||
call(command: 'event:onUninstallExtension'): TPromise<void>;
|
||||
call(command: 'event:onDidUninstallExtension'): TPromise<void>;
|
||||
call(command: 'install', extensionOrPath: ILocalExtension | string): TPromise<ILocalExtension>;
|
||||
call(command: 'install', path: string): TPromise<void>;
|
||||
call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<void>;
|
||||
call(command: 'uninstall', extension: ILocalExtension): TPromise<void>;
|
||||
call(command: 'getInstalled'): TPromise<ILocalExtension[]>;
|
||||
call(command: string, arg: any): TPromise<any>;
|
||||
|
@ -32,6 +33,7 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel {
|
|||
case 'event:onUninstallExtension': return eventToCall(this.service.onUninstallExtension);
|
||||
case 'event:onDidUninstallExtension': return eventToCall(this.service.onDidUninstallExtension);
|
||||
case 'install': return this.service.install(arg);
|
||||
case 'installFromGallery': return this.service.installFromGallery(arg);
|
||||
case 'uninstall': return this.service.uninstall(arg);
|
||||
case 'getInstalled': return this.service.getInstalled();
|
||||
}
|
||||
|
@ -56,10 +58,12 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
|
|||
private _onDidUninstallExtension = eventFromCall<string>(this.channel, 'event:onDidUninstallExtension');
|
||||
get onDidUninstallExtension(): Event<string> { return this._onDidUninstallExtension; }
|
||||
|
||||
install(extension: IGalleryExtension): TPromise<void>;
|
||||
install(zipPath: string): TPromise<void>;
|
||||
install(arg: any): TPromise<void> {
|
||||
return this.channel.call('install', arg);
|
||||
install(zipPath: string): TPromise<void> {
|
||||
return this.channel.call('install', zipPath);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
return this.channel.call('installFromGallery', extension);
|
||||
}
|
||||
|
||||
uninstall(extension: ILocalExtension): TPromise<void> {
|
||||
|
|
|
@ -3,15 +3,22 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { tmpdir } from 'os';
|
||||
import * as path from 'path';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IGalleryExtension, IExtensionGalleryService, IGalleryVersion, IQueryOptions, SortBy, SortOrder } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IGalleryExtension, IExtensionGalleryService, IQueryOptions, SortBy, SortOrder, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { isUndefined } from 'vs/base/common/types';
|
||||
import { assign, getOrDefault } from 'vs/base/common/objects';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { download, json, IRequestOptions } from 'vs/base/node/request';
|
||||
import { getProxyAgent } from 'vs/base/node/proxy';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import pkg from 'vs/platform/package';
|
||||
import product from 'vs/platform/product';
|
||||
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
|
||||
|
||||
interface IRawGalleryExtensionFile {
|
||||
assetType: string;
|
||||
|
@ -158,21 +165,21 @@ function getAssetSource(files: IRawGalleryExtensionFile[], type: string): string
|
|||
return result && result.source;
|
||||
}
|
||||
|
||||
function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, downloadHeaders: any): IGalleryExtension {
|
||||
const versions = galleryExtension.versions.map<IGalleryVersion>(v => ({
|
||||
version: v.version,
|
||||
date: v.lastUpdated,
|
||||
downloadHeaders,
|
||||
downloadUrl: `${ v.assetUri }/${ AssetType.VSIX }?install=true`,
|
||||
manifestUrl: `${ v.assetUri }/${ AssetType.Manifest }`,
|
||||
readmeUrl: `${ v.assetUri }/${ AssetType.Details }`,
|
||||
iconUrl: getAssetSource(v.files, AssetType.Icon) || require.toUrl('./media/defaultIcon.png'),
|
||||
licenseUrl: getAssetSource(v.files, AssetType.License)
|
||||
}));
|
||||
function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, downloadHeaders: { [key: string]: string; }): IGalleryExtension {
|
||||
const [version] = galleryExtension.versions;
|
||||
const assets = {
|
||||
manifest: getAssetSource(version.files, AssetType.Manifest),
|
||||
readme: getAssetSource(version.files, AssetType.Details),
|
||||
download: `${ getAssetSource(version.files, AssetType.VSIX) }?install=true`,
|
||||
icon: getAssetSource(version.files, AssetType.Icon) || require.toUrl('./media/defaultIcon.png'),
|
||||
license: getAssetSource(version.files, AssetType.License)
|
||||
};
|
||||
|
||||
return {
|
||||
id: galleryExtension.extensionId,
|
||||
name: galleryExtension.extensionName,
|
||||
version: version.version,
|
||||
date: version.lastUpdated,
|
||||
displayName: galleryExtension.displayName,
|
||||
publisherId: galleryExtension.publisher.publisherId,
|
||||
publisher: galleryExtension.publisher.publisherName,
|
||||
|
@ -181,7 +188,8 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
|
|||
installCount: getStatistic(galleryExtension.statistics, 'install'),
|
||||
rating: getStatistic(galleryExtension.statistics, 'averagerating'),
|
||||
ratingCount: getStatistic(galleryExtension.statistics, 'ratingcount'),
|
||||
versions
|
||||
assets,
|
||||
downloadHeaders
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -190,15 +198,29 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
_serviceBrand: any;
|
||||
|
||||
private extensionsGalleryUrl: string;
|
||||
private machineId: TPromise<string>;
|
||||
|
||||
private getCommonHeaders(): TPromise<{ [key: string]: string; }> {
|
||||
return this.telemetryService.getTelemetryInfo().then(({ machineId }) => {
|
||||
const result: { [key: string]: string; } = {
|
||||
'X-Market-Client-Id': `VSCode ${ pkg.version }`,
|
||||
'User-Agent': `VSCode ${ pkg.version }`
|
||||
};
|
||||
|
||||
if (machineId) {
|
||||
result['X-Market-User-Id'] = machineId;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
constructor(
|
||||
@IRequestService private requestService: IRequestService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IConfigurationService private configurationService: IConfigurationService
|
||||
) {
|
||||
const config = product.extensionsGallery;
|
||||
this.extensionsGalleryUrl = config && config.serviceUrl;
|
||||
this.machineId = telemetryService.getTelemetryInfo().then(({ machineId }) => machineId);
|
||||
}
|
||||
|
||||
private api(path = ''): string {
|
||||
|
@ -221,10 +243,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
this.telemetryService.publicLog('galleryService:query', { type, text });
|
||||
|
||||
let query = new Query()
|
||||
.withFlags(Flags.IncludeVersions, Flags.IncludeCategoryAndTags, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles)
|
||||
.withFlags(Flags.IncludeLatestVersionOnly, Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles)
|
||||
.withPage(1, pageSize)
|
||||
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
|
||||
.withAssetTypes(AssetType.Icon, AssetType.License);
|
||||
.withAssetTypes(AssetType.Icon, AssetType.License, AssetType.Details, AssetType.Manifest, AssetType.VSIX);
|
||||
|
||||
if (text) {
|
||||
query = query.withFilter(FilterType.SearchText, text).withSortBy(SortBy.NoneOrRelevance);
|
||||
|
@ -245,7 +267,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
}
|
||||
|
||||
return this.queryGallery(query).then(({ galleryExtensions, total }) => {
|
||||
return this.getRequestHeaders().then(downloadHeaders => {
|
||||
return this.getCommonHeaders().then(downloadHeaders => {
|
||||
const extensions = galleryExtensions.map(e => toExtension(e, this.extensionsGalleryUrl, downloadHeaders));
|
||||
const pageSize = query.pageSize;
|
||||
const getPage = pageIndex => this.queryGallery(query.withPage(pageIndex + 1))
|
||||
|
@ -258,27 +280,19 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
|
||||
private queryGallery(query: Query): TPromise<{ galleryExtensions: IRawGalleryExtension[], total: number; }> {
|
||||
const data = JSON.stringify(query.raw);
|
||||
const request = this.request(this.api('/extensionquery'));
|
||||
|
||||
return this.getRequestHeaders()
|
||||
.then(headers => {
|
||||
headers = assign(headers, {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json;api-version=3.0-preview.1',
|
||||
'Accept-Encoding': 'gzip',
|
||||
'Content-Length': data.length
|
||||
});
|
||||
|
||||
const request = {
|
||||
type: 'POST',
|
||||
url: this.api('/extensionquery'),
|
||||
data,
|
||||
headers
|
||||
};
|
||||
|
||||
return this.requestService.makeRequest(request);
|
||||
})
|
||||
.then(r => JSON.parse(r.responseText).results[0])
|
||||
.then(r => {
|
||||
return this.getCommonHeaders()
|
||||
.then(headers => assign(headers, {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json;api-version=3.0-preview.1',
|
||||
'Accept-Encoding': 'gzip',
|
||||
'Content-Length': data.length
|
||||
}))
|
||||
.then(headers => assign(request, { type: 'POST', data, headers }))
|
||||
.then(() => json<any>(request))
|
||||
.then(result => {
|
||||
const r = result.results[0];
|
||||
const galleryExtensions = r.extensions;
|
||||
const resultCount = r.resultMetadata && r.resultMetadata.filter(m => m.metadataType === 'ResultCount')[0];
|
||||
const total = resultCount && resultCount.metadataItems.filter(i => i.name === 'TotalCount')[0].count || 0;
|
||||
|
@ -287,18 +301,67 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||
});
|
||||
}
|
||||
|
||||
private getRequestHeaders(): TPromise<any> {
|
||||
return this.machineId.then(machineId => {
|
||||
const result = {
|
||||
'X-Market-Client-Id': `VSCode ${ pkg.version }`,
|
||||
'User-Agent': `VSCode ${ pkg.version }`
|
||||
};
|
||||
download(extension: IGalleryExtension): TPromise<string> {
|
||||
const query = new Query()
|
||||
.withFlags(Flags.IncludeVersions, Flags.IncludeFiles)
|
||||
.withPage(1, 1)
|
||||
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
|
||||
.withAssetTypes(AssetType.Manifest, AssetType.VSIX)
|
||||
.withFilter(FilterType.ExtensionId, extension.id);
|
||||
|
||||
if (machineId) {
|
||||
result['X-Market-User-Id'] = machineId;
|
||||
return this.queryGallery(query).then(({ galleryExtensions }) => {
|
||||
const [rawExtension] = galleryExtensions;
|
||||
|
||||
if (!rawExtension) {
|
||||
return TPromise.wrapError(new Error(localize('notFound', "Extension not found")));
|
||||
}
|
||||
|
||||
return result;
|
||||
return this.getLastValidExtensionVersion(rawExtension, rawExtension.versions).then(rawVersion => {
|
||||
const url = `${ getAssetSource(rawVersion.files, AssetType.VSIX) }?install=true`;
|
||||
const zipPath = path.join(tmpdir(), extension.id);
|
||||
const request = this.request(url);
|
||||
|
||||
return this.getCommonHeaders()
|
||||
.then(headers => assign(request, { headers }))
|
||||
.then(() => download(zipPath, request))
|
||||
.then(() => zipPath);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getLastValidExtensionVersion(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): TPromise<IRawGalleryExtensionVersion> {
|
||||
if (!versions.length) {
|
||||
return TPromise.wrapError(new Error(localize('noCompatible', "Couldn't find a compatible version of {0} with this version of Code.", extension.displayName || extension.extensionName)));
|
||||
}
|
||||
|
||||
const version = versions[0];
|
||||
const url = getAssetSource(version.files, AssetType.Manifest);
|
||||
let request = this.request(url);
|
||||
request = assign(request, { headers: { 'accept-encoding': 'gzip' } });
|
||||
|
||||
return json<IExtensionManifest>(request).then(manifest => {
|
||||
const desc = {
|
||||
isBuiltin: false,
|
||||
engines: { vscode: manifest.engines.vscode },
|
||||
main: manifest.main
|
||||
};
|
||||
|
||||
if (!isValidExtensionVersion(pkg.version, desc, [])) {
|
||||
return this.getLastValidExtensionVersion(extension, versions.slice(1));
|
||||
}
|
||||
|
||||
return version;
|
||||
});
|
||||
}
|
||||
|
||||
// Helper for proxy business... shameful.
|
||||
// This should be pushed down and not rely on the context service
|
||||
private request(url: string): IRequestOptions {
|
||||
const httpConfig = this.configurationService.getConfiguration<any>('http') || {};
|
||||
const proxyUrl = httpConfig.proxy as string;
|
||||
const strictSSL = httpConfig.proxyStrictSSL as boolean;
|
||||
const agent = getProxyAgent(url, { proxyUrl, strictSSL });
|
||||
|
||||
return { url, agent, strictSSL };
|
||||
}
|
||||
}
|
|
@ -6,26 +6,19 @@
|
|||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import { tmpdir } from 'os';
|
||||
import * as path from 'path';
|
||||
import types = require('vs/base/common/types');
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { extract, buffer } from 'vs/base/node/zip';
|
||||
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionIdentity, IExtensionManifest, IGalleryVersion, IGalleryMetadata, InstallExtensionEvent, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { download, json, IRequestOptions } from 'vs/base/node/request';
|
||||
import { getProxyAgent } from 'vs/base/node/proxy';
|
||||
import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IExtensionIdentity, IExtensionManifest, IGalleryMetadata, InstallExtensionEvent, DidInstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { Limiter } from 'vs/base/common/async';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { UserSettings } from 'vs/base/node/userSettings';
|
||||
import * as semver from 'semver';
|
||||
import { groupBy, values } from 'vs/base/common/collections';
|
||||
import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import pkg from 'vs/platform/package';
|
||||
|
||||
function parseManifest(raw: string): TPromise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> {
|
||||
return new Promise((c, e) => {
|
||||
|
@ -88,100 +81,60 @@ export class ExtensionManagementService implements IExtensionManagementService {
|
|||
onDidUninstallExtension: Event<string> = this._onDidUninstallExtension.event;
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IExtensionGalleryService private galleryService: IExtensionGalleryService
|
||||
) {
|
||||
this.extensionsPath = environmentService.extensionsPath;
|
||||
this.obsoletePath = path.join(this.extensionsPath, '.obsolete');
|
||||
this.obsoleteFileLimiter = new Limiter(1);
|
||||
}
|
||||
|
||||
install(extension: IGalleryExtension): TPromise<void>;
|
||||
install(zipPath: string): TPromise<void>;
|
||||
install(arg: any): TPromise<void> {
|
||||
let id: string;
|
||||
let result: TPromise<ILocalExtension>;
|
||||
install(zipPath: string): TPromise<void> {
|
||||
return validate(zipPath).then<void>(manifest => {
|
||||
const id = getExtensionId(manifest, manifest.version);
|
||||
|
||||
if (types.isString(arg)) {
|
||||
const zipPath = arg as string;
|
||||
|
||||
result = validate(zipPath).then(manifest => {
|
||||
id = getExtensionId(manifest, manifest.version);
|
||||
this._onInstallExtension.fire({ id });
|
||||
|
||||
return this.installValidExtension(zipPath, id);
|
||||
});
|
||||
} else {
|
||||
const extension = arg as IGalleryExtension;
|
||||
id = getExtensionId(extension, extension.versions[0].version);
|
||||
this._onInstallExtension.fire({ id, gallery: extension });
|
||||
|
||||
result = this.isObsolete(id).then(obsolete => {
|
||||
if (obsolete) {
|
||||
return TPromise.wrapError<ILocalExtension>(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", extension.displayName || extension.name)));
|
||||
return this.isObsolete(id).then(isObsolete => {
|
||||
if (isObsolete) {
|
||||
return TPromise.wrapError(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", manifest.displayName || manifest.name)));
|
||||
}
|
||||
|
||||
return this.installFromGallery(arg);
|
||||
this._onInstallExtension.fire({ id });
|
||||
|
||||
return this.installExtension(zipPath, id)
|
||||
.then(
|
||||
local => this._onDidInstallExtension.fire({ id, local }),
|
||||
error => { this._onDidInstallExtension.fire({ id, error }); return TPromise.wrapError(error); }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return result.then(
|
||||
local => this._onDidInstallExtension.fire({ id, local }),
|
||||
error => { this._onDidInstallExtension.fire({ id, error }); return TPromise.wrapError(error); }
|
||||
);
|
||||
}
|
||||
|
||||
private installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
|
||||
return this.getLastValidExtensionVersion(extension).then(versionInfo => {
|
||||
const version = versionInfo.version;
|
||||
const url = versionInfo.downloadUrl;
|
||||
const headers = versionInfo.downloadHeaders;
|
||||
const zipPath = path.join(tmpdir(), extension.id);
|
||||
const id = getExtensionId(extension, version);
|
||||
const metadata = {
|
||||
id: extension.id,
|
||||
publisherId: extension.publisherId,
|
||||
publisherDisplayName: extension.publisherDisplayName
|
||||
};
|
||||
|
||||
return this.request(url)
|
||||
.then(opts => assign(opts, { headers }))
|
||||
.then(opts => download(zipPath, opts))
|
||||
.then(() => validate(zipPath, extension, version))
|
||||
.then(() => this.installValidExtension(zipPath, id, metadata));
|
||||
});
|
||||
}
|
||||
|
||||
private getLastValidExtensionVersion(extension: IGalleryExtension): TPromise<IGalleryVersion> {
|
||||
return this._getLastValidExtensionVersion(extension, extension.versions);
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
const id = getExtensionId(extension, extension.version);
|
||||
|
||||
return this.isObsolete(id).then(isObsolete => {
|
||||
if (isObsolete) {
|
||||
return TPromise.wrapError<void>(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", extension.displayName || extension.name)));
|
||||
}
|
||||
|
||||
this._onInstallExtension.fire({ id, gallery: extension });
|
||||
|
||||
const metadata = {
|
||||
id: extension.id,
|
||||
publisherId: extension.publisherId,
|
||||
publisherDisplayName: extension.publisherDisplayName
|
||||
};
|
||||
|
||||
return this.galleryService.download(extension)
|
||||
.then(zipPath => this.installExtension(zipPath, id, metadata))
|
||||
.then(
|
||||
local => this._onDidInstallExtension.fire({ id, local }),
|
||||
error => { this._onDidInstallExtension.fire({ id, error }); return TPromise.wrapError(error); }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private _getLastValidExtensionVersion(extension: IGalleryExtension, versions: IGalleryVersion[]): TPromise<IGalleryVersion> {
|
||||
if (!versions.length) {
|
||||
return TPromise.wrapError(new Error(nls.localize('noCompatible', "Couldn't find a compatible version of {0} with this version of Code.", extension.displayName || extension.name)));
|
||||
}
|
||||
|
||||
const headers = { 'accept-encoding': 'gzip' };
|
||||
const version = versions[0];
|
||||
|
||||
return this.request(version.manifestUrl)
|
||||
.then(opts => assign(opts, { headers }))
|
||||
.then(opts => json<IExtensionManifest>(opts))
|
||||
.then(manifest => {
|
||||
const desc = {
|
||||
isBuiltin: false,
|
||||
engines: { vscode: manifest.engines.vscode },
|
||||
main: manifest.main
|
||||
};
|
||||
|
||||
if (!isValidExtensionVersion(pkg.version, desc, [])) {
|
||||
return this._getLastValidExtensionVersion(extension, versions.slice(1));
|
||||
}
|
||||
|
||||
return version;
|
||||
});
|
||||
}
|
||||
|
||||
private installValidExtension(zipPath: string, id: string, metadata: IGalleryMetadata = null): TPromise<ILocalExtension> {
|
||||
private installExtension(zipPath: string, id: string, metadata: IGalleryMetadata = null): TPromise<ILocalExtension> {
|
||||
const extensionPath = path.join(this.extensionsPath, id);
|
||||
const manifestPath = path.join(extensionPath, 'package.json');
|
||||
|
||||
|
@ -314,24 +267,6 @@ export class ExtensionManagementService implements IExtensionManagementService {
|
|||
});
|
||||
}
|
||||
|
||||
// Helper for proxy business... shameful.
|
||||
// This should be pushed down and not rely on the context service
|
||||
private request(url: string): TPromise<IRequestOptions> {
|
||||
const settings = TPromise.join([
|
||||
// TODO@Joao we need a nice configuration service here!
|
||||
UserSettings.getValue(this.environmentService.userDataPath, 'http.proxy'),
|
||||
UserSettings.getValue(this.environmentService.userDataPath, 'http.proxyStrictSSL')
|
||||
]);
|
||||
|
||||
return settings.then(settings => {
|
||||
const proxyUrl: string = settings[0];
|
||||
const strictSSL: boolean = settings[1];
|
||||
const agent = getProxyAgent(url, { proxyUrl, strictSSL });
|
||||
|
||||
return { url, agent, strictSSL };
|
||||
});
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export function getOutdatedExtensions(extensionsService: IExtensionManagementSer
|
|||
|
||||
return available.map(extension => {
|
||||
const local = installed.filter(local => extensionEquals(local.manifest, extension))[0];
|
||||
if (local && semver.lt(local.manifest.version, extension.versions[0].version)) {
|
||||
if (local && semver.lt(local.manifest.version, extension.version)) {
|
||||
return local;
|
||||
} else {
|
||||
return null;
|
||||
|
|
|
@ -63,11 +63,11 @@ class Extension implements IExtension {
|
|||
}
|
||||
|
||||
get version(): string {
|
||||
return this.local ? this.local.manifest.version : this.gallery.versions[0].version;
|
||||
return this.local ? this.local.manifest.version : this.gallery.version;
|
||||
}
|
||||
|
||||
get latestVersion(): string {
|
||||
return this.gallery ? this.gallery.versions[0].version : this.local.manifest.version;
|
||||
return this.gallery ? this.gallery.version : this.local.manifest.version;
|
||||
}
|
||||
|
||||
get description(): string {
|
||||
|
@ -79,11 +79,7 @@ class Extension implements IExtension {
|
|||
return this.local.readmeUrl;
|
||||
}
|
||||
|
||||
if (this.gallery && this.gallery.versions[0].readmeUrl) {
|
||||
return this.gallery.versions[0].readmeUrl;
|
||||
}
|
||||
|
||||
return null;
|
||||
return this.gallery && this.gallery.assets.readme;
|
||||
}
|
||||
|
||||
get iconUrl(): string {
|
||||
|
@ -91,19 +87,11 @@ class Extension implements IExtension {
|
|||
return URI.file(path.join(this.local.path, this.local.manifest.icon)).toString();
|
||||
}
|
||||
|
||||
if (this.gallery) {
|
||||
return this.gallery.versions[0].iconUrl;
|
||||
}
|
||||
|
||||
return require.toUrl('./media/defaultIcon.png');
|
||||
return this.gallery ? this.gallery.assets.icon : require.toUrl('./media/defaultIcon.png');
|
||||
}
|
||||
|
||||
get licenseUrl(): string {
|
||||
if (this.gallery) {
|
||||
return this.gallery.versions[0].licenseUrl;
|
||||
}
|
||||
|
||||
return null;
|
||||
return this.gallery && this.gallery.assets.license;
|
||||
}
|
||||
|
||||
get state(): ExtensionState {
|
||||
|
@ -283,7 +271,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService {
|
|||
return TPromise.wrapError<void>(new Error('Missing gallery'));
|
||||
}
|
||||
|
||||
return this.extensionService.install(gallery);
|
||||
return this.extensionService.installFromGallery(gallery);
|
||||
}
|
||||
|
||||
uninstall(extension: IExtension): TPromise<void> {
|
||||
|
|
Loading…
Reference in a new issue