mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-13 15:36:25 +00:00
feat(docker): use Docker Hub tags api (#23876)
This commit is contained in:
parent
da7fc430ed
commit
a1f79bcf39
3 changed files with 61 additions and 16 deletions
|
@ -21,6 +21,7 @@ const ecrMock = mockClient(ECRClient);
|
||||||
const baseUrl = 'https://index.docker.io/v2';
|
const baseUrl = 'https://index.docker.io/v2';
|
||||||
const authUrl = 'https://auth.docker.io';
|
const authUrl = 'https://auth.docker.io';
|
||||||
const amazonUrl = 'https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2';
|
const amazonUrl = 'https://123456789.dkr.ecr.us-east-1.amazonaws.com/v2';
|
||||||
|
const dockerHubUrl = 'https://hub.docker.com/v2/repositories';
|
||||||
|
|
||||||
function mockEcrAuthResolve(
|
function mockEcrAuthResolve(
|
||||||
res: Partial<GetAuthorizationTokenCommandOutput> = {}
|
res: Partial<GetAuthorizationTokenCommandOutput> = {}
|
||||||
|
@ -41,6 +42,7 @@ describe('modules/datasource/docker/index', () => {
|
||||||
});
|
});
|
||||||
hostRules.hosts.mockReturnValue([]);
|
hostRules.hosts.mockReturnValue([]);
|
||||||
delete process.env.RENOVATE_X_DOCKER_MAX_PAGES;
|
delete process.env.RENOVATE_X_DOCKER_MAX_PAGES;
|
||||||
|
delete process.env.RENOVATE_X_DOCKER_HUB_TAGS;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getDigest', () => {
|
describe('getDigest', () => {
|
||||||
|
@ -1520,7 +1522,12 @@ describe('modules/datasource/docker/index', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds library/ prefix for Docker Hub (implicit)', async () => {
|
it('adds library/ prefix for Docker Hub (implicit)', async () => {
|
||||||
|
process.env.RENOVATE_X_DOCKER_HUB_TAGS = 'true';
|
||||||
const tags = ['1.0.0'];
|
const tags = ['1.0.0'];
|
||||||
|
httpMock
|
||||||
|
.scope(dockerHubUrl)
|
||||||
|
.get('/library/node/tags?page_size=100')
|
||||||
|
.reply(404);
|
||||||
httpMock
|
httpMock
|
||||||
.scope(baseUrl)
|
.scope(baseUrl)
|
||||||
.get('/library/node/tags/list?n=10000')
|
.get('/library/node/tags/list?n=10000')
|
||||||
|
@ -1548,31 +1555,29 @@ describe('modules/datasource/docker/index', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds library/ prefix for Docker Hub (explicit)', async () => {
|
it('adds library/ prefix for Docker Hub (explicit)', async () => {
|
||||||
const tags = ['1.0.0'];
|
process.env.RENOVATE_X_DOCKER_HUB_TAGS = 'true';
|
||||||
|
httpMock
|
||||||
|
.scope(dockerHubUrl)
|
||||||
|
.get('/library/node/tags?page_size=100')
|
||||||
|
.reply(200, {
|
||||||
|
next: `${dockerHubUrl}/library/node/tags?page=2&page_size=100`,
|
||||||
|
results: [{ name: '1.0.0' }],
|
||||||
|
})
|
||||||
|
.get('/library/node/tags?page=2&page_size=100')
|
||||||
|
.reply(200, {
|
||||||
|
results: [{ name: '0.9.0' }],
|
||||||
|
});
|
||||||
httpMock
|
httpMock
|
||||||
.scope(baseUrl)
|
.scope(baseUrl)
|
||||||
.get('/library/node/tags/list?n=10000')
|
|
||||||
.reply(401, '', {
|
|
||||||
'www-authenticate':
|
|
||||||
'Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/node:pull"',
|
|
||||||
})
|
|
||||||
.get('/library/node/tags/list?n=10000')
|
|
||||||
.reply(200, { tags }, {})
|
|
||||||
.get('/')
|
.get('/')
|
||||||
.reply(200)
|
.reply(200)
|
||||||
.get('/library/node/manifests/1.0.0')
|
.get('/library/node/manifests/1.0.0')
|
||||||
.reply(200);
|
.reply(200);
|
||||||
httpMock
|
|
||||||
.scope(authUrl)
|
|
||||||
.get(
|
|
||||||
'/token?service=registry.docker.io&scope=repository:library/node:pull'
|
|
||||||
)
|
|
||||||
.reply(200, { token: 'test' });
|
|
||||||
const res = await getPkgReleases({
|
const res = await getPkgReleases({
|
||||||
datasource: DockerDatasource.id,
|
datasource: DockerDatasource.id,
|
||||||
packageName: 'docker.io/node',
|
packageName: 'docker.io/node',
|
||||||
});
|
});
|
||||||
expect(res?.releases).toHaveLength(1);
|
expect(res?.releases).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds no library/ prefix for other registries', async () => {
|
it('adds no library/ prefix for other registries', async () => {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {
|
||||||
} from './common';
|
} from './common';
|
||||||
import { ecrPublicRegex, ecrRegex, isECRMaxResultsError } from './ecr';
|
import { ecrPublicRegex, ecrRegex, isECRMaxResultsError } from './ecr';
|
||||||
import type { Manifest, OciImageConfig } from './schema';
|
import type { Manifest, OciImageConfig } from './schema';
|
||||||
|
import type { DockerHubTags } from './types';
|
||||||
|
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
commitMessageTopic: '{{{depName}}} Docker tag',
|
commitMessageTopic: '{{{depName}}} Docker tag',
|
||||||
|
@ -819,6 +820,36 @@ export class DockerDatasource extends Datasource {
|
||||||
return digest;
|
return digest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDockerHubTags(dockerRepository: string): Promise<string[] | null> {
|
||||||
|
if (!process.env.RENOVATE_X_DOCKER_HUB_TAGS) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let index = 0;
|
||||||
|
let tags: string[] = [];
|
||||||
|
let url:
|
||||||
|
| string
|
||||||
|
| undefined = `https://hub.docker.com/v2/repositories/${dockerRepository}/tags?page_size=100`;
|
||||||
|
do {
|
||||||
|
const res: DockerHubTags = (await this.http.getJson<DockerHubTags>(url))
|
||||||
|
.body;
|
||||||
|
tags = tags.concat(res.results.map((tag) => tag.name));
|
||||||
|
url = res.next;
|
||||||
|
index += 1;
|
||||||
|
} while (url && index < 100);
|
||||||
|
logger.debug(
|
||||||
|
`getDockerHubTags(${dockerRepository}): found ${tags.length} tags`
|
||||||
|
);
|
||||||
|
return tags;
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(
|
||||||
|
{ dockerRepository, errMessage: err.message },
|
||||||
|
`No Docker Hub tags result - falling back to docker.io api`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* docker.getReleases
|
* docker.getReleases
|
||||||
*
|
*
|
||||||
|
@ -838,7 +869,11 @@ export class DockerDatasource extends Datasource {
|
||||||
packageName,
|
packageName,
|
||||||
registryUrl!
|
registryUrl!
|
||||||
);
|
);
|
||||||
const tags = await this.getTags(registryHost, dockerRepository);
|
let tags: string[] | null = null;
|
||||||
|
if (registryHost === 'https://index.docker.io') {
|
||||||
|
tags = await this.getDockerHubTags(dockerRepository);
|
||||||
|
}
|
||||||
|
tags ??= await this.getTags(registryHost, dockerRepository);
|
||||||
if (!tags) {
|
if (!tags) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,8 @@ export interface RegistryRepository {
|
||||||
registryHost: string;
|
registryHost: string;
|
||||||
dockerRepository: string;
|
dockerRepository: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DockerHubTags {
|
||||||
|
next?: string;
|
||||||
|
results: { name: string }[];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue