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

Make Created At work for GitHub Registries #181

Merged
merged 6 commits into from
Aug 22, 2023
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
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>({
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
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