Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use targetPlatform when installing plugin from open-vsx #13825

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,13 @@
"request": "launch",
"name": "Launch Browser Backend",
"program": "${workspaceFolder}/examples/browser/lib/backend/main.js",
"cwd": "${workspaceFolder}/examples/browser",
"args": [
"--hostname=0.0.0.0",
"--port=3000",
"--no-cluster",
"--app-project-path=${workspaceFolder}/examples/browser",
"--plugins=local-dir:plugins",
"--plugins=local-dir:../../plugins",
"--hosted-plugin-inspect=9339",
"--ovsx-router-config=${workspaceFolder}/examples/ovsx-router-config.json"
],
Expand Down
15 changes: 10 additions & 5 deletions dev-packages/cli/src/download-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

/* eslint-disable @typescript-eslint/no-explicit-any */

import { OVSXApiFilterImpl, OVSXClient } from '@theia/ovsx-client';
import { OVSXApiFilterImpl, OVSXClient, VSXTargetPlatform } from '@theia/ovsx-client';
import * as chalk from 'chalk';
import * as decompress from 'decompress';
import { promises as fs } from 'fs';
Expand Down Expand Up @@ -75,7 +75,7 @@ export default async function downloadPlugins(ovsxClient: OVSXClient, requestSer
} = options;

const rateLimiter = new RateLimiter({ tokensPerInterval: rateLimit, interval: 'second' });
const apiFilter = new OVSXApiFilterImpl(apiVersion);
const apiFilter = new OVSXApiFilterImpl(ovsxClient, apiVersion);

// Collect the list of failures to be appended at the end of the script.
const failures: string[] = [];
Expand Down Expand Up @@ -129,8 +129,11 @@ export default async function downloadPlugins(ovsxClient: OVSXClient, requestSer
await parallelOrSequence(Array.from(ids, id => async () => {
try {
await rateLimiter.removeTokens(1);
const { extensions } = await ovsxClient.query({ extensionId: id, includeAllVersions: true });
const extension = apiFilter.getLatestCompatibleExtension(extensions);
const extension = await apiFilter.findLatestCompatibleExtension({
extensionId: id,
includeAllVersions: true,
targetPlatform
});
const version = extension?.version;
const downloadUrl = extension?.files.download;
if (downloadUrl) {
Expand Down Expand Up @@ -170,8 +173,10 @@ export default async function downloadPlugins(ovsxClient: OVSXClient, requestSer
}
}

const targetPlatform = `${process.platform}-${process.arch}` as VSXTargetPlatform;

const placeholders: Record<string, string> = {
targetPlatform: `${process.platform}-${process.arch}`
targetPlatform
};
function resolveDownloadUrlPlaceholders(url: string): string {
for (const [name, value] of Object.entries(placeholders)) {
Expand Down
2 changes: 1 addition & 1 deletion dev-packages/ovsx-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

export { OVSXApiFilter, OVSXApiFilterImpl } from './ovsx-api-filter';
export { OVSXApiFilter, OVSXApiFilterImpl, OVSXApiFilterProvider } from './ovsx-api-filter';
export { OVSXHttpClient } from './ovsx-http-client';
export { OVSXMockClient } from './ovsx-mock-client';
export { OVSXRouterClient, OVSXRouterConfig, OVSXRouterFilterFactory as FilterFactory } from './ovsx-router-client';
Expand Down
47 changes: 46 additions & 1 deletion dev-packages/ovsx-client/src/ovsx-api-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
// *****************************************************************************

import * as semver from 'semver';
import { VSXAllVersions, VSXBuiltinNamespaces, VSXExtensionRaw, VSXSearchEntry } from './ovsx-types';
import { OVSXClient, VSXAllVersions, VSXBuiltinNamespaces, VSXExtensionRaw, VSXQueryOptions, VSXSearchEntry } from './ovsx-types';

export const OVSXApiFilterProvider = Symbol('OVSXApiFilterProvider');

export type OVSXApiFilterProvider = () => Promise<OVSXApiFilter>;

export const OVSXApiFilter = Symbol('OVSXApiFilter');
/**
* Filter various data types based on a pre-defined supported VS Code API version.
*/
export interface OVSXApiFilter {
supportedApiVersion: string;
findLatestCompatibleExtension(query: VSXQueryOptions): Promise<VSXExtensionRaw | undefined>;
/**
* Get the latest compatible extension version:
* - A builtin extension is fetched based on the extension version which matches the API.
Expand All @@ -38,9 +43,49 @@ export interface OVSXApiFilter {
export class OVSXApiFilterImpl implements OVSXApiFilter {

constructor(
public client: OVSXClient,
public supportedApiVersion: string
) { }

async findLatestCompatibleExtension(query: VSXQueryOptions): Promise<VSXExtensionRaw | undefined> {
const targetPlatform = query.targetPlatform;
if (!targetPlatform) {
return this.queryLatestCompatibleExtension(query);
}
const latestWithTargetPlatform = await this.queryLatestCompatibleExtension(query);
let latestUniversal: VSXExtensionRaw | undefined;
if (targetPlatform !== 'universal' && targetPlatform !== 'web') {
// Additionally query the universal version, as there might be a newer one available
latestUniversal = await this.queryLatestCompatibleExtension({ ...query, targetPlatform: 'universal' });
}
if (latestWithTargetPlatform && latestUniversal) {
// Prefer the version with the target platform if it's greater or equal to the universal version
return this.versionGreaterThanOrEqualTo(latestWithTargetPlatform.version, latestUniversal.version) ? latestWithTargetPlatform : latestUniversal;
}
return latestWithTargetPlatform ?? latestUniversal;
}

protected async queryLatestCompatibleExtension(query: VSXQueryOptions): Promise<VSXExtensionRaw | undefined> {
let offset = 0;
let loop = true;
while (loop) {
const queryOptions = {
...query,
offset
};
const results = await this.client.query(queryOptions);
const compatibleExtension = this.getLatestCompatibleExtension(results.extensions);
if (compatibleExtension) {
return compatibleExtension;
}
// Adjust offset by the amount of returned extensions
offset += results.extensions.length;
// Continue querying if there are more extensions available
loop = results.totalSize > offset;
}
return undefined;
}

getLatestCompatibleExtension(extensions: VSXExtensionRaw[]): VSXExtensionRaw | undefined {
if (extensions.length === 0) {
return;
Expand Down
5 changes: 3 additions & 2 deletions dev-packages/ovsx-client/src/ovsx-http-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ export class OVSXHttpClient implements OVSXClient {

async query(queryOptions?: VSXQueryOptions): Promise<VSXQueryResult> {
try {
return await this.requestJson(this.buildUrl('api/-/query', queryOptions));
return await this.requestJson(this.buildUrl('api/v2/-/query', queryOptions));
} catch (error) {
console.warn(error);
return {
offset: 0,
totalSize: 0,
extensions: []
};
}
Expand Down
21 changes: 13 additions & 8 deletions dev-packages/ovsx-client/src/ovsx-mock-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,26 @@ export class OVSXMockClient implements OVSXClient {
reviewsUrl: url.extensionReviewsUrl(namespace, name),
timestamp: new Date(now - ids.length + i + 1).toISOString(),
version,
description: `Mock VS Code Extension for ${id}`
description: `Mock VS Code Extension for ${id}`,
namespaceDisplayName: name,
preRelease: false
};
});
return this;
}

async query(queryOptions?: VSXQueryOptions): Promise<VSXQueryResult> {
const extensions = this.extensions
.filter(extension => typeof queryOptions === 'object' && (
this.compare(queryOptions.extensionId, this.id(extension)) &&
this.compare(queryOptions.extensionName, extension.name) &&
this.compare(queryOptions.extensionVersion, extension.version) &&
this.compare(queryOptions.namespaceName, extension.namespace)
));
return {
extensions: this.extensions
.filter(extension => typeof queryOptions === 'object' && (
this.compare(queryOptions.extensionId, this.id(extension)) &&
this.compare(queryOptions.extensionName, extension.name) &&
this.compare(queryOptions.extensionVersion, extension.version) &&
this.compare(queryOptions.namespaceName, extension.namespace)
))
offset: 0,
totalSize: extensions.length,
extensions
};
}

Expand Down
7 changes: 6 additions & 1 deletion dev-packages/ovsx-client/src/ovsx-router-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ export class OVSXRouterClient implements OVSXClient {

protected emptyQueryResult(queryOptions?: VSXQueryOptions): VSXQueryResult {
return {
offset: 0,
totalSize: 0,
extensions: []
};
}
Expand Down Expand Up @@ -183,8 +185,11 @@ export class OVSXRouterClient implements OVSXClient {
results.forEach((result, sourceUri) => {
result.extensions.forEach(extension => filtering.push(this.filterExtension(sourceUri, extension)));
});
const extensions = removeNullValues(await Promise.all(filtering));
return {
extensions: removeNullValues(await Promise.all(filtering))
offset: 0,
totalSize: extensions.length,
extensions
};
}

Expand Down
52 changes: 47 additions & 5 deletions dev-packages/ovsx-client/src/ovsx-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface OVSXClient {
*/
search(searchOptions?: VSXSearchOptions): Promise<VSXSearchResult>;
/**
* GET https://openvsx.org/api/-/query
* GET https://openvsx.org/api/v2/-/query
*
* Fetch one or all versions of an extension.
*/
Expand Down Expand Up @@ -110,8 +110,6 @@ export interface VSXSearchResult {
extensions: VSXSearchEntry[];
}

/** @deprecated since 1.31.0 use {@link VSXQueryOptions} instead */
export type VSXQueryParam = VSXQueryOptions;
/**
* The possible options when performing a search.
*
Expand All @@ -126,10 +124,25 @@ export interface VSXQueryOptions {
extensionId?: string;
extensionUuid?: string;
namespaceUuid?: string;
includeAllVersions?: boolean;
includeAllVersions?: boolean | 'links';
targetPlatform?: VSXTargetPlatform;
size?: number;
offset?: number;
}

export type VSXTargetPlatform =
'universal' | 'web' |
'win32-x64' | 'win32-ia32' | 'win32-arm64' |
'darwin-x64' | 'darwin-arm64' |
'linux-x64' | 'linux-arm64' | 'linux-armhf' |
'alpine-x64' | 'alpine-arm64' | (string & {});

export interface VSXQueryResult {
success?: string;
warning?: string;
error?: string;
offset: number;
totalSize: number;
extensions: VSXExtensionRaw[];
}

Expand Down Expand Up @@ -199,12 +212,15 @@ export interface VSXExtensionRaw {
reviewsUrl: string;
name: string;
namespace: string;
publishedBy: VSXUser
targetPlatform?: VSXTargetPlatform;
publishedBy: VSXUser;
preRelease: boolean;
namespaceAccess: VSXExtensionNamespaceAccess;
files: VSXExtensionRawFiles;
allVersions: {
[version: string]: string;
};
allVersionsUrl?: string;
averageRating?: number;
downloadCount: number;
reviewCount: number;
Expand All @@ -213,22 +229,48 @@ export interface VSXExtensionRaw {
preview?: boolean;
verified?: boolean;
displayName?: string;
namespaceDisplayName: string;
description?: string;
categories?: string[];
extensionKind?: string[];
tags?: string[];
license?: string;
homepage?: string;
repository?: string;
sponsorLink?: string;
bugs?: string;
markdown?: string;
galleryColor?: string;
galleryTheme?: string;
localizedLanguages?: string[];
qna?: string;
badges?: VSXBadge[];
dependencies?: VSXExtensionReference[];
bundledExtensions?: VSXExtensionReference[];
allTargetPlatformVersions?: VSXTargetPlatforms[];
url?: string;
engines?: {
[engine: string]: string;
};
}

export interface VSXBadge {
url?: string;
href?: string;
description?: string;
}

export interface VSXExtensionReference {
url: string;
namespace: string;
extension: string;
}

export interface VSXTargetPlatforms {
version: string;
targetPlatforms: VSXTargetPlatform[];
}

export interface VSXResponseError extends Error {
statusCode: number;
}
Expand Down
4 changes: 3 additions & 1 deletion examples/api-samples/src/node/sample-mock-open-vsx-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class SampleMockOpenVsxServer implements BackendApplicationContribution {
app.use(
this.mockServerPath + '/api',
express.Router()
.get('/-/query', async (req, res) => {
.get('/v2/-/query', async (req, res) => {
await this.ready;
res.json(await this.mockClient.query(this.sanitizeQuery(req.query)));
})
Expand Down Expand Up @@ -170,6 +170,8 @@ export class SampleMockOpenVsxServer implements BackendApplicationContribution {
reviewsUrl: url.extensionReviewsUrl(namespace, name),
timestamp: new Date().toISOString(),
version,
namespaceDisplayName: name,
preRelease: false
}
});
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const frontendOnlyApplicationModule = new ContainerModule((bind, unbind,
getExtensionsInfos: async (): Promise<ExtensionInfo[]> => [],
getApplicationInfo: async (): Promise<ApplicationInfo | undefined> => undefined,
getApplicationRoot: async (): Promise<string> => '',
getApplicationPlatform: () => Promise.resolve('web'),
getBackendOS: async (): Promise<OS.Type> => OS.Type.Linux
};
if (isBound(ApplicationServer)) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/common/application-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface ApplicationServer {
getExtensionsInfos(): Promise<ExtensionInfo[]>;
getApplicationInfo(): Promise<ApplicationInfo | undefined>;
getApplicationRoot(): Promise<string>;
getApplicationPlatform(): Promise<string>;
/**
* @deprecated since 1.25.0. Use `OS.backend.type()` instead.
*/
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/node/application-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export class ApplicationServerImpl implements ApplicationServer {
return Promise.resolve(this.applicationPackage.projectPath);
}

getApplicationPlatform(): Promise<string> {
return Promise.resolve(`${process.platform}-${process.arch}`);
}

async getBackendOS(): Promise<OS.Type> {
return OS.type();
}
Expand Down
Loading
Loading