From 1551b8eea027e0be198af35a2f4518c552685b20 Mon Sep 17 00:00:00 2001 From: "Alex Yang [MSFT]" <59073590+alexyaang@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:50:12 -0400 Subject: [PATCH] Make `Created At` work for GitHub Registries (#181) * Method determining whether a registry is generic V2 * added created at date for docker hub * added Github CreatedAt field * added override keyword * changed function name --- .../Common/CommonRegistryDataProvider.ts | 2 +- .../DockerHubRegistryDataProvider.ts | 7 ++-- .../GenericRegistryV2DataProvider.ts | 4 +++ .../GitHub/GitHubRegistryDataProvider.ts | 33 +++++++++++++++++++ .../RegistryV2/RegistryV2DataProvider.ts | 6 ++-- 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/packages/vscode-docker-registries/src/clients/Common/CommonRegistryDataProvider.ts b/packages/vscode-docker-registries/src/clients/Common/CommonRegistryDataProvider.ts index 562c9f9d..fb6e0699 100644 --- a/packages/vscode-docker-registries/src/clients/Common/CommonRegistryDataProvider.ts +++ b/packages/vscode-docker-registries/src/clients/Common/CommonRegistryDataProvider.ts @@ -63,7 +63,7 @@ export abstract class CommonRegistryDataProvider implements RegistryDataProvider collapsibleState: vscode.TreeItemCollapsibleState.None, contextValue: getContextValue(element, 'commontag'), // TODO iconPath: new vscode.ThemeIcon('bookmark'), - description: dayjs(element.createdAt).fromNow(), + description: element.createdAt ? dayjs(element.createdAt).fromNow() : undefined, }); } else if (isError(element)) { return Promise.resolve({ diff --git a/packages/vscode-docker-registries/src/clients/DockerHub/DockerHubRegistryDataProvider.ts b/packages/vscode-docker-registries/src/clients/DockerHub/DockerHubRegistryDataProvider.ts index e1c1d9de..1dd454a3 100644 --- a/packages/vscode-docker-registries/src/clients/DockerHub/DockerHubRegistryDataProvider.ts +++ b/packages/vscode-docker-registries/src/clients/DockerHub/DockerHubRegistryDataProvider.ts @@ -129,7 +129,8 @@ export class DockerHubRegistryDataProvider extends CommonRegistryDataProvider { const requestUrl = vscode.Uri.parse(DockerHubUrl) .with({ path: `v2/repositories/${repository.parent.label}/${repository.label}/tags` }); - const response = await httpRequest<{ results: [{ name: string; }] }>(requestUrl.toString(), { + // eslint-disable-next-line @typescript-eslint/naming-convention + const response = await httpRequest<{ results: [{ name: string, last_updated: string }] }>(requestUrl.toString(), { method: 'GET', headers: { Authorization: `Bearer ${(await this.authenticationProvider.getSession([], {})).accessToken}`, @@ -143,8 +144,8 @@ export class DockerHubRegistryDataProvider extends CommonRegistryDataProvider { parent: repository, label: tag.name, type: 'commontag', - additionalContextValues: ['dockerHubTag'] - // createdAt: '', // TODO: get this from the API + additionalContextValues: ['dockerHubTag'], + createdAt: new Date(tag.last_updated || ''), }); } diff --git a/packages/vscode-docker-registries/src/clients/GenericRegistryV2/GenericRegistryV2DataProvider.ts b/packages/vscode-docker-registries/src/clients/GenericRegistryV2/GenericRegistryV2DataProvider.ts index a0834851..e3dde282 100644 --- a/packages/vscode-docker-registries/src/clients/GenericRegistryV2/GenericRegistryV2DataProvider.ts +++ b/packages/vscode-docker-registries/src/clients/GenericRegistryV2/GenericRegistryV2DataProvider.ts @@ -23,6 +23,10 @@ interface GenericV2RegistryItem extends V2RegistryItem { export type GenericV2Registry = V2Registry & GenericV2RegistryItem; +export function isGenericV2Registry(item: unknown): item is GenericV2Registry { + return !!item && typeof item === 'object' && (item as GenericV2Registry).additionalContextValues?.includes('genericRegistryV2') === true; +} + export class GenericRegistryV2DataProvider extends RegistryV2DataProvider { public readonly id = 'vscode-docker.genericRegistryV2DataProvider'; public readonly label = 'Generic Registry V2'; diff --git a/packages/vscode-docker-registries/src/clients/GitHub/GitHubRegistryDataProvider.ts b/packages/vscode-docker-registries/src/clients/GitHub/GitHubRegistryDataProvider.ts index b16163d0..9d698a7c 100644 --- a/packages/vscode-docker-registries/src/clients/GitHub/GitHubRegistryDataProvider.ts +++ b/packages/vscode-docker-registries/src/clients/GitHub/GitHubRegistryDataProvider.ts @@ -16,6 +16,13 @@ import { BasicCredentials } from '../../contracts/BasicCredentials'; const GitHubContainerRegistryUri = vscode.Uri.parse('https://ghcr.io'); +interface Config { + digest: string; +} +interface Blob { + config: Config; +} + export class GitHubRegistryDataProvider extends RegistryV2DataProvider { public readonly id: string = 'vscode-docker.githubContainerRegistry'; public readonly label: string = vscode.l10n.t('GitHub'); @@ -117,6 +124,32 @@ export class GitHubRegistryDataProvider extends RegistryV2DataProvider { return this.authenticationProvider; } + protected override async getTagDetails(repository: V2Repository, tag: string): Promise { + const tagDetailResponse = await registryV2Request({ + authenticationProvider: this.getAuthenticationProvider(repository), + method: 'GET', + registryUri: repository.baseUrl, + path: ['v2', repository.label, 'manifests', tag], + scopes: [`repository:${repository.label}:pull`] + }); + + const digest = tagDetailResponse.body?.config?.digest || ''; + + if (digest) { + const configFile = await registryV2Request<{ created: string }>({ + authenticationProvider: this.getAuthenticationProvider(repository), + method: 'GET', + registryUri: repository.baseUrl, + path: ['v2', repository.label, 'blobs', digest], + scopes: [`repository:${repository.label}:pull`] + }); + + return configFile.body?.created ? new Date(configFile.body.created) : undefined; + } + + return undefined; + } + private async getOrganizations(): Promise { const results: string[] = []; diff --git a/packages/vscode-docker-registries/src/clients/RegistryV2/RegistryV2DataProvider.ts b/packages/vscode-docker-registries/src/clients/RegistryV2/RegistryV2DataProvider.ts index d05a1cd0..f6dab12e 100644 --- a/packages/vscode-docker-registries/src/clients/RegistryV2/RegistryV2DataProvider.ts +++ b/packages/vscode-docker-registries/src/clients/RegistryV2/RegistryV2DataProvider.ts @@ -101,7 +101,7 @@ export abstract class RegistryV2DataProvider extends CommonRegistryDataProvider throw new Error(vscode.l10n.t('Authentication provider {0} does not support getting login information.', authenticationProvider)); } - private async getTagDetails(repository: V2Repository, tag: string): Promise { + protected async getTagDetails(repository: V2Repository, tag: string): Promise { const tagDetailResponse = await registryV2Request({ authenticationProvider: this.getAuthenticationProvider(repository), method: 'GET', @@ -110,8 +110,8 @@ export abstract class RegistryV2DataProvider extends CommonRegistryDataProvider scopes: [`repository:${repository.label}:pull`] }); - const history = JSON.parse(tagDetailResponse.body?.history[0].v1Compatibility || '{}'); - return new Date(history.created); + const history = JSON.parse(tagDetailResponse.body?.history?.[0]?.v1Compatibility || '{}'); + return history?.created ? new Date(history.created) : undefined; } protected abstract getAuthenticationProvider(item: V2RegistryItem): AuthenticationProvider;