Skip to content

Commit

Permalink
Make Created At work for GitHub Registries (#181)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
alexyaang authored Aug 22, 2023
1 parent c34d30e commit 1551b8e
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
Expand All @@ -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 || ''),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -117,6 +124,32 @@ export class GitHubRegistryDataProvider extends RegistryV2DataProvider {
return this.authenticationProvider;
}

protected override async getTagDetails(repository: V2Repository, tag: string): Promise<Date | undefined> {
const tagDetailResponse = await registryV2Request<Blob>({
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<string[]> {
const results: string[] = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Date> {
protected async getTagDetails(repository: V2Repository, tag: string): Promise<Date | undefined> {
const tagDetailResponse = await registryV2Request<Manifest>({
authenticationProvider: this.getAuthenticationProvider(repository),
method: 'GET',
Expand All @@ -110,8 +110,8 @@ export abstract class RegistryV2DataProvider extends CommonRegistryDataProvider
scopes: [`repository:${repository.label}:pull`]
});

const history = <ManifestHistoryV1Compatibility>JSON.parse(tagDetailResponse.body?.history[0].v1Compatibility || '{}');
return new Date(history.created);
const history = <ManifestHistoryV1Compatibility>JSON.parse(tagDetailResponse.body?.history?.[0]?.v1Compatibility || '{}');
return history?.created ? new Date(history.created) : undefined;
}

protected abstract getAuthenticationProvider(item: V2RegistryItem): AuthenticationProvider<never>;
Expand Down

0 comments on commit 1551b8e

Please sign in to comment.