Skip to content
Snippets Groups Projects
Unverified Commit 32ee265c authored by Bastian's avatar Bastian Committed by GitHub
Browse files

feat(gitlab): support getDigest for gitlab repositories (#13361)


* feat(gitlab): support getDigest for gitlab repositories

* typehint and remove paging

* add gitlab to go.getDigest tests

* remove obsolete expectations

* add gitlab with a specific branch to go.getDigest tests

* exception handling and test for invalid/nonexisting commits

Co-authored-by: default avatarMichael Kriese <michael.kriese@visualon.de>
parent 2e258d81
No related branches found
No related tags found
No related merge requests found
import { getPkgReleases } from '..'; import { getDigest, getPkgReleases } from '..';
import * as httpMock from '../../../test/http-mock'; import * as httpMock from '../../../test/http-mock';
import { id as datasource } from '.'; import { id as datasource } from '.';
...@@ -76,4 +76,75 @@ describe('datasource/gitlab-tags/index', () => { ...@@ -76,4 +76,75 @@ describe('datasource/gitlab-tags/index', () => {
expect(res.releases).toHaveLength(2); expect(res.releases).toHaveLength(2);
}); });
}); });
describe('getDigest', () => {
it('returns commits from gitlab installation', async () => {
const digest = 'abcd00001234';
const body = [
{
id: digest,
},
];
httpMock
.scope('https://gitlab.company.com')
.get('/api/v4/projects/some%2Fdep2/repository/commits?per_page=1')
.reply(200, body);
const res = await getDigest({
datasource,
registryUrls: ['https://gitlab.company.com/api/v4/'],
depName: 'some/dep2',
});
expect(res).toBe(digest);
});
it('returns commits from gitlab installation for a specific branch', async () => {
const digest = 'abcd00001234';
const body = {
id: digest,
};
httpMock
.scope('https://gitlab.company.com')
.get('/api/v4/projects/some%2Fdep2/repository/commits/branch')
.reply(200, body);
const res = await getDigest(
{
datasource,
registryUrls: ['https://gitlab.company.com/api/v4/'],
depName: 'some/dep2',
},
'branch'
);
expect(res).toBe(digest);
});
it('returns null from gitlab installation with no commits', async () => {
const body = [];
httpMock
.scope('https://gitlab.company.com')
.get('/api/v4/projects/some%2Fdep2/repository/commits?per_page=1')
.reply(200, body);
const res = await getDigest({
datasource,
registryUrls: ['https://gitlab.company.com/api/v4/'],
depName: 'some/dep2',
});
expect(res).toBeNull();
});
it('returns null from gitlab installation with unknown branch', async () => {
httpMock
.scope('https://gitlab.company.com')
.get('/api/v4/projects/some%2Fdep2/repository/commits/unknown-branch')
.reply(404, null);
const res = await getDigest(
{
datasource,
registryUrls: ['https://gitlab.company.com/api/v4/'],
depName: 'some/dep2',
},
'unknown-branch'
);
expect(res).toBeNull();
});
});
}); });
import { logger } from '../../logger';
import * as packageCache from '../../util/cache/package'; import * as packageCache from '../../util/cache/package';
import { GitlabHttp } from '../../util/http/gitlab'; import { GitlabHttp } from '../../util/http/gitlab';
import { joinUrlParts } from '../../util/url'; import { joinUrlParts } from '../../util/url';
import type { GetReleasesConfig, ReleaseResult } from '../types'; import type { DigestConfig, GetReleasesConfig, ReleaseResult } from '../types';
import type { GitlabTag } from './types'; import type { GitlabCommit, GitlabTag } from './types';
import { defaultRegistryUrl, getDepHost, getSourceUrl } from './util'; import { defaultRegistryUrl, getDepHost, getSourceUrl } from './util';
export const id = 'gitlab-tags'; export const id = 'gitlab-tags';
...@@ -14,8 +15,7 @@ export const registryStrategy = 'first'; ...@@ -14,8 +15,7 @@ export const registryStrategy = 'first';
const cacheNamespace = 'datasource-gitlab'; const cacheNamespace = 'datasource-gitlab';
function getCacheKey(depHost: string, repo: string): string { function getCacheKey(depHost: string, repo: string, type = 'tags'): string {
const type = 'tags';
return `${depHost}:${repo}:${type}`; return `${depHost}:${repo}:${type}`;
} }
...@@ -69,3 +69,68 @@ export async function getReleases({ ...@@ -69,3 +69,68 @@ export async function getReleases({
); );
return dependency; return dependency;
} }
/**
* gitlab.getDigest
*
* This function will simply return the latest commit hash for the configured repository.
*/
export async function getDigest(
{ lookupName: repo, registryUrl }: Partial<DigestConfig>,
newValue?: string
): Promise<string | null> {
const depHost = getDepHost(registryUrl);
const cachedResult = await packageCache.get<string>(
cacheNamespace,
getCacheKey(depHost, repo, 'commit')
);
// istanbul ignore if
if (cachedResult) {
return cachedResult;
}
const urlEncodedRepo = encodeURIComponent(repo);
let digest: string;
try {
if (newValue) {
const url = joinUrlParts(
depHost,
`api/v4/projects`,
urlEncodedRepo,
`repository/commits/`,
newValue
);
const gitlabCommits = await gitlabApi.getJson<GitlabCommit>(url);
digest = gitlabCommits.body.id;
} else {
const url = joinUrlParts(
depHost,
`api/v4/projects`,
urlEncodedRepo,
`repository/commits?per_page=1`
);
const gitlabCommits = await gitlabApi.getJson<GitlabCommit[]>(url);
digest = gitlabCommits.body[0].id;
}
} catch (err) {
logger.debug(
{ gitlabRepo: repo, err, registryUrl },
'Error getting latest commit from Gitlab repo'
);
}
if (!digest) {
return null;
}
const cacheMinutes = 10;
await packageCache.set(
cacheNamespace,
getCacheKey(registryUrl, repo, 'commit'),
digest,
cacheMinutes
);
return digest;
}
...@@ -4,3 +4,7 @@ export type GitlabTag = { ...@@ -4,3 +4,7 @@ export type GitlabTag = {
created_at?: string; created_at?: string;
}; };
}; };
export type GitlabCommit = {
id: string;
};
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`datasource/go/digest getDigest gitlab digest is not supported at the moment 1`] = `
Array [
Object {
"headers": Object {
"accept-encoding": "gzip, deflate, br",
"host": "gitlab.com",
"user-agent": "RenovateBot/0.0.0-semantic-release (https://github.com/renovatebot/renovate)",
},
"method": "GET",
"url": "https://gitlab.com/golang/text?go-get=1",
},
]
`;
exports[`datasource/go/digest getDigest returns digest 1`] = ` exports[`datasource/go/digest getDigest returns digest 1`] = `
Array [ Array [
Object { Object {
......
import * as httpMock from '../../../test/http-mock'; import * as httpMock from '../../../test/http-mock';
import { mocked } from '../../../test/util'; import { loadFixture, mocked } from '../../../test/util';
import * as _hostRules from '../../util/host-rules'; import * as _hostRules from '../../util/host-rules';
import { getDigest } from '.'; import { getDigest } from '.';
...@@ -49,17 +49,36 @@ describe('datasource/go/digest', () => { ...@@ -49,17 +49,36 @@ describe('datasource/go/digest', () => {
expect(res).toBeNull(); expect(res).toBeNull();
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('gitlab digest is not supported at the moment', async () => { it('supports gitlab digest', async () => {
httpMock httpMock
.scope('https://gitlab.com/') .scope('https://gitlab.com/')
.get('/golang/text?go-get=1') .get('/group/subgroup?go-get=1')
.reply(200, ''); .reply(200, loadFixture('go-get-gitlab.html'));
httpMock
.scope('https://gitlab.com/')
.get('/api/v4/projects/group%2Fsubgroup/repository/commits?per_page=1')
.reply(200, [{ id: 'abcdefabcdefabcdefabcdef' }]);
const res = await getDigest( const res = await getDigest(
{ lookupName: 'gitlab.com/golang/text' }, { lookupName: 'gitlab.com/group/subgroup' },
null null
); );
expect(res).toBeNull(); expect(res).toBe('abcdefabcdefabcdefabcdef');
expect(httpMock.getTrace()).toMatchSnapshot(); });
it('supports gitlab digest with a specific branch', async () => {
const branch = 'some-branch';
httpMock
.scope('https://gitlab.com/')
.get('/group/subgroup?go-get=1')
.reply(200, loadFixture('go-get-gitlab.html'));
httpMock
.scope('https://gitlab.com/')
.get(`/api/v4/projects/group%2Fsubgroup/repository/commits/${branch}`)
.reply(200, { id: 'abcdefabcdefabcdefabcdef' });
const res = await getDigest(
{ lookupName: 'gitlab.com/group/subgroup' },
branch
);
expect(res).toBe('abcdefabcdefabcdefabcdef');
}); });
it('returns digest', async () => { it('returns digest', async () => {
httpMock httpMock
......
import * as github from '../github-tags'; import * as github from '../github-tags';
import * as gitlab from '../gitlab-tags';
import type { DigestConfig } from '../types'; import type { DigestConfig } from '../types';
import { bitbucket } from './common'; import { bitbucket } from './common';
import { getDatasource } from './get-datasource'; import { getDatasource } from './get-datasource';
...@@ -32,6 +33,9 @@ export async function getDigest( ...@@ -32,6 +33,9 @@ export async function getDigest(
case bitbucket.id: { case bitbucket.id: {
return bitbucket.getDigest(source, tag); return bitbucket.getDigest(source, tag);
} }
case gitlab.id: {
return gitlab.getDigest(source, tag);
}
/* istanbul ignore next: can never happen, makes lint happy */ /* istanbul ignore next: can never happen, makes lint happy */
default: { default: {
return null; return null;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment