contain gallery code inside GalleryService

related to #10180
This commit is contained in:
Joao Moreno 2016-08-16 16:23:20 +02:00
parent 416fe53a13
commit c8caac361f
8 changed files with 189 additions and 196 deletions

View file

@ -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)));
});
});
});

View file

@ -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 => {

View file

@ -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[]>;
}

View file

@ -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> {

View file

@ -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 };
}
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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> {