diff --git a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap index f63b6d3f9fa792526192cb55f482e5c4e4c92810..241719abb8ef1bceb4d98d06f95da3b891a3df7f 100644 --- a/lib/platform/gitlab/__snapshots__/index.spec.ts.snap +++ b/lib/platform/gitlab/__snapshots__/index.spec.ts.snap @@ -2039,6 +2039,61 @@ Array [ ] `; +exports[`platform/gitlab/index initRepo should fall back respecting when GITLAB_IGNORE_REPO_URL is set 1`] = ` +Array [ + Array [ + Object { + "cloneSubmodules": undefined, + "defaultBranch": "master", + "gitAuthorEmail": undefined, + "gitAuthorName": undefined, + "ignorePrAuthor": undefined, + "mergeMethod": "merge", + "repository": "some%2Frepo%2Fproject", + "url": "http://oauth2:abc123@mycompany.com/gitlab/some/repo/project.git", + }, + ], +] +`; + +exports[`platform/gitlab/index initRepo should fall back respecting when GITLAB_IGNORE_REPO_URL is set 2`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer mytoken", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/user", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer mytoken", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/version", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/projects/some%2Frepo%2Fproject", + }, +] +`; + exports[`platform/gitlab/index initRepo should throw an error if MRs are disabled 1`] = ` Array [ Object { diff --git a/lib/platform/gitlab/index.spec.ts b/lib/platform/gitlab/index.spec.ts index 384d282da1b190cf032ac5be28d77a19d4cd278f..b7b738cf376c112ec6e53e178cc228b7dc700d58 100644 --- a/lib/platform/gitlab/index.spec.ts +++ b/lib/platform/gitlab/index.spec.ts @@ -43,6 +43,7 @@ describe(getName(), () => { hostRules.find.mockReturnValue({ token: 'abc123', }); + delete process.env.GITLAB_IGNORE_REPO_URL; }); async function initFakePlatform(version: string) { @@ -285,6 +286,38 @@ describe(getName(), () => { }); expect(httpMock.getTrace()).toMatchSnapshot(); }); + + it('should fall back respecting when GITLAB_IGNORE_REPO_URL is set', async () => { + process.env.GITLAB_IGNORE_REPO_URL = 'true'; + const selfHostedUrl = 'http://mycompany.com/gitlab'; + httpMock + .scope(selfHostedUrl) + .get('/api/v4/user') + .reply(200, { + email: 'a@b.com', + name: 'Renovate Bot', + }) + .get('/api/v4/version') + .reply(200, { + version: '13.8.0', + }); + await gitlab.initPlatform({ + endpoint: `${selfHostedUrl}/api/v4`, + token: 'mytoken', + }); + httpMock + .scope(selfHostedUrl) + .get('/api/v4/projects/some%2Frepo%2Fproject') + .reply(200, { + default_branch: 'master', + http_url_to_repo: `http://other.host.com/gitlab/some/repo/project.git`, + }); + await gitlab.initRepo({ + repository: 'some/repo/project', + }); + expect(git.initRepo.mock.calls).toMatchSnapshot(); + expect(httpMock.getTrace()).toMatchSnapshot(); + }); }); describe('getRepoForceRebase', () => { it('should return false', async () => { diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts index 87f86415f444f411b4992ee43a979fee004243e6..748b072c16b8701daa7c2f95f133e4758010bf55 100755 --- a/lib/platform/gitlab/index.ts +++ b/lib/platform/gitlab/index.ts @@ -22,7 +22,7 @@ import * as hostRules from '../../util/host-rules'; import { HttpResponse } from '../../util/http'; import { setBaseUrl } from '../../util/http/gitlab'; import { sanitize } from '../../util/sanitize'; -import { ensureTrailingSlash, getQueryString } from '../../util/url'; +import { ensureTrailingSlash, getQueryString, parseUrl } from '../../util/url'; import type { BranchStatusConfig, CreatePRConfig, @@ -225,13 +225,15 @@ export async function initRepo({ res.body.http_url_to_repo === null ) { logger.debug('no http_url_to_repo found. Falling back to old behaviour.'); - const { host, protocol } = URL.parse(defaults.endpoint); - url = git.getUrl({ - protocol: protocol.slice(0, -1) as any, + const { protocol, host, pathname } = parseUrl(defaults.endpoint); + const newPathname = pathname.slice(0, pathname.indexOf('/api')); + url = URL.format({ + protocol: protocol.slice(0, -1) || 'https', auth: 'oauth2:' + opts.token, host, - repository, + pathname: newPathname + '/' + repository + '.git', }); + logger.debug({ url }, 'using URL based on configured endpoint'); } else { logger.debug(`${repository} http URL = ${res.body.http_url_to_repo}`); const repoUrl = URL.parse(`${res.body.http_url_to_repo}`); diff --git a/lib/util/http/__snapshots__/gitlab.spec.ts.snap b/lib/util/http/__snapshots__/gitlab.spec.ts.snap index 49f1f886195b9f04edce4973c3663d8a094186c0..928048947c541cd9f734740cc4bbd37a5499c626 100644 --- a/lib/util/http/__snapshots__/gitlab.spec.ts.snap +++ b/lib/util/http/__snapshots__/gitlab.spec.ts.snap @@ -54,6 +54,44 @@ Array [ ] `; +exports[`util/http/gitlab paginates with GITLAB_IGNORE_REPO_URL set 1`] = ` +Array [ + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/some-url", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/some-url&page=2", + }, + Object { + "headers": Object { + "accept": "application/json", + "accept-encoding": "gzip, deflate, br", + "authorization": "Bearer abc123", + "host": "mycompany.com", + "user-agent": "https://github.com/renovatebot/renovate", + }, + "method": "GET", + "url": "http://mycompany.com/gitlab/api/v4/some-url&page=3", + }, +] +`; + exports[`util/http/gitlab posts 1`] = ` Array [ Object { diff --git a/lib/util/http/gitlab.spec.ts b/lib/util/http/gitlab.spec.ts index d3a653f1e3a058cdba20c4f440098b6926f97394..fff760d17da4255b92905d8e5fdd30aaf30f550c 100644 --- a/lib/util/http/gitlab.spec.ts +++ b/lib/util/http/gitlab.spec.ts @@ -11,6 +11,7 @@ hostRules.add({ }); const gitlabApiHost = 'https://gitlab.com'; +const selfHostedUrl = 'http://mycompany.com/gitlab'; describe(getName(), () => { let gitlabApi: GitlabHttp; @@ -18,6 +19,7 @@ describe(getName(), () => { beforeEach(() => { gitlabApi = new GitlabHttp(); setBaseUrl(`${gitlabApiHost}/api/v4/`); + delete process.env.GITLAB_IGNORE_REPO_URL; }); afterEach(() => { @@ -44,6 +46,29 @@ describe(getName(), () => { expect(trace).toHaveLength(3); expect(trace).toMatchSnapshot(); }); + it('paginates with GITLAB_IGNORE_REPO_URL set', async () => { + process.env.GITLAB_IGNORE_REPO_URL = 'true'; + setBaseUrl(`${selfHostedUrl}/api/v4/`); + + httpMock + .scope(selfHostedUrl) + .get('/api/v4/some-url') + .reply(200, ['a'], { + link: '<https://other.host.com/gitlab/api/v4/some-url&page=2>; rel="next", <https://other.host.com/gitlab/api/v4/some-url&page=3>; rel="last"', + }) + .get('/api/v4/some-url&page=2') + .reply(200, ['b', 'c'], { + link: '<https://other.host.com/gitlab/api/v4/some-url&page=3>; rel="next", <https://other.host.com/gitlab/api/v4/some-url&page=3>; rel="last"', + }) + .get('/api/v4/some-url&page=3') + .reply(200, ['d']); + const res = await gitlabApi.getJson('some-url', { paginate: true }); + expect(res.body).toHaveLength(4); + + const trace = httpMock.getTrace(); + expect(trace).toHaveLength(3); + expect(trace).toMatchSnapshot(); + }); it('attempts to paginate', async () => { httpMock.scope(gitlabApiHost).get('/api/v4/some-url').reply(200, ['a'], { link: '<https://gitlab.com/api/v4/some-url&page=3>; rel="last"', @@ -63,9 +88,7 @@ describe(getName(), () => { expect(httpMock.getTrace()).toMatchSnapshot(); }); it('sets baseUrl', () => { - expect(() => - setBaseUrl('https://gitlab.renovatebot.com/api/v4/') - ).not.toThrow(); + expect(() => setBaseUrl(`${selfHostedUrl}/api/v4/`)).not.toThrow(); }); describe('fails with', () => { diff --git a/lib/util/http/gitlab.ts b/lib/util/http/gitlab.ts index 13bd3028171aeadf7afffa46331eeb40929ffea1..f93df7358a45e91be5d44c0099d6244ed3203d57 100644 --- a/lib/util/http/gitlab.ts +++ b/lib/util/http/gitlab.ts @@ -2,6 +2,7 @@ import parseLinkHeader from 'parse-link-header'; import { PLATFORM_TYPE_GITLAB } from '../../constants/platforms'; import { logger } from '../../logger'; import { ExternalHostError } from '../../types/errors/external-host-error'; +import { parseUrl } from '../url'; import { Http, HttpResponse, InternalHttpOptions } from '.'; let baseUrl = 'https://gitlab.com/api/v4/'; @@ -42,8 +43,15 @@ export class GitlabHttp extends Http<GitlabHttpOptions, GitlabHttpOptions> { try { const linkHeader = parseLinkHeader(result.headers.link as string); if (linkHeader?.next) { + const nextUrl = parseUrl(linkHeader.next.url); + if (process.env.GITLAB_IGNORE_REPO_URL) { + const defaultEndpoint = new URL(baseUrl); + nextUrl.protocol = defaultEndpoint.protocol; + nextUrl.host = defaultEndpoint.host; + } + result.body = result.body.concat( - (await this.request<T>(linkHeader.next.url, opts)).body + (await this.request<T>(nextUrl, opts)).body ); } } catch (err) /* istanbul ignore next */ {