Skip to content

Commit

Permalink
feat(gitlab): add branch status check attemps (#32692)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
  • Loading branch information
3 people authored Jan 31, 2025
1 parent 476d11c commit 78f389a
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 19 deletions.
9 changes: 9 additions & 0 deletions docs/usage/self-hosted-experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ The formula for the delay between attempts is `RENOVATE_X_GITLAB_MERGE_REQUEST_D

Default value: `5` (attempts results in max. 13.75 seconds timeout).

## `RENOVATE_X_GITLAB_BRANCH_STATUS_CHECK_ATTEMPTS`

If set to a positive integer, Renovate will use this as the number of attempts to check branch status before trying to add a status check.
The delay between attempts is `RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY` milliseconds.

Default value: `2` (attempts results in maximum 2 seconds timeout).

!!! warning Increasing this value too much penalizes projects that do not have defined pipelines, Renovate will systematically wait `RENOVATE_X_GITLAB_BRANCH_STATUS_CHECK_ATTEMPTS * RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY` milliseconds on these projects and slow down the Renovate analyzes.

## `RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY`

Adjust default time (in milliseconds) given to GitLab to create pipelines for a commit pushed by Renovate.
Expand Down
106 changes: 101 additions & 5 deletions lib/modules/platform/gitlab/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('modules/platform/gitlab/index', () => {
token: '123test',
});
delete process.env.GITLAB_IGNORE_REPO_URL;
delete process.env.RENOVATE_X_GITLAB_BRANCH_STATUS_CHECK_ATTEMPTS;
delete process.env.RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY;
delete process.env.RENOVATE_X_GITLAB_AUTO_MERGEABLE_CHECK_ATTEMPS;
delete process.env.RENOVATE_X_GITLAB_MERGE_REQUEST_DELAY;
Expand Down Expand Up @@ -1058,6 +1059,51 @@ describe('modules/platform/gitlab/index', () => {
describe('setBranchStatus', () => {
const states: BranchStatus[] = ['green', 'yellow', 'red'];

it('should log message that branch commit SHA not found', async () => {
git.getBranchCommit.mockReturnValue(null);
await gitlab.setBranchStatus({
branchName: 'some-branch',
context: 'some-context',
description: 'some-description',
state: 'green',
url: 'some-url',
});
expect(logger.warn).toHaveBeenCalledWith(
'Failed to get the branch commit SHA',
);
});

it('should log message that failed to retrieve commit pipeline', async () => {
const scope = await initRepo();
scope
.post(
'/api/v4/projects/some%2Frepo/statuses/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, {})
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses',
)
.reply(200, [])
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, {});

timers.setTimeout.mockImplementation(() => {
throw new Error();
});
await gitlab.setBranchStatus({
branchName: 'some-branch',
context: 'some-context',
description: 'some-description',
state: 'green',
url: 'some-url',
});
expect(logger.warn).toHaveBeenCalledWith(
'Failed to retrieve commit pipeline',
);
});

it.each(states)('sets branch status %s', async (state) => {
const scope = await initRepo();
scope
Expand All @@ -1072,7 +1118,8 @@ describe('modules/platform/gitlab/index', () => {
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, []);
.times(3)
.reply(200, {});

await expect(
gitlab.setBranchStatus({
Expand All @@ -1099,7 +1146,8 @@ describe('modules/platform/gitlab/index', () => {
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, []);
.times(3)
.reply(200, {});

await gitlab.setBranchStatus({
branchName: 'some-branch',
Expand All @@ -1109,7 +1157,7 @@ describe('modules/platform/gitlab/index', () => {
url: 'some-url',
});

expect(timers.setTimeout.mock.calls).toHaveLength(1);
expect(timers.setTimeout.mock.calls).toHaveLength(3);
expect(timers.setTimeout.mock.calls[0][0]).toBe(1000);
});

Expand All @@ -1131,6 +1179,10 @@ describe('modules/platform/gitlab/index', () => {
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, {})
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, { last_pipeline: { id: 123 } });

await expect(
Expand All @@ -1146,6 +1198,7 @@ describe('modules/platform/gitlab/index', () => {

it('waits for RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY ms when set', async () => {
const delay = 5000;
const retry = 2;
process.env.RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY = String(delay);

const scope = await initRepo();
Expand All @@ -1161,7 +1214,50 @@ describe('modules/platform/gitlab/index', () => {
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, []);
.times(3)
.reply(200, {});

await gitlab.setBranchStatus({
branchName: 'some-branch',
context: 'some-context',
description: 'some-description',
state: 'green',
url: 'some-url',
});

expect(timers.setTimeout.mock.calls).toHaveLength(retry + 1);
expect(timers.setTimeout.mock.calls[0][0]).toBe(delay);
expect(logger.debug).toHaveBeenCalledWith(
`Pipeline not yet created. Retrying 1`,
);
expect(logger.debug).toHaveBeenCalledWith(
`Pipeline not yet created. Retrying 2`,
);
expect(logger.debug).toHaveBeenCalledWith(
`Pipeline not yet created after 3 attempts`,
);
});

it('do RENOVATE_X_GITLAB_BRANCH_STATUS_CHECK_ATTEMPTS attemps when set', async () => {
const delay = 1000;
const retry = 5;
process.env.RENOVATE_X_GITLAB_BRANCH_STATUS_CHECK_ATTEMPTS = `${retry}`;

const scope = await initRepo();
scope
.post(
'/api/v4/projects/some%2Frepo/statuses/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.reply(200, {})
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e/statuses',
)
.reply(200, [])
.get(
'/api/v4/projects/some%2Frepo/repository/commits/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
)
.times(retry + 1)
.reply(200, {});

await gitlab.setBranchStatus({
branchName: 'some-branch',
Expand All @@ -1171,7 +1267,7 @@ describe('modules/platform/gitlab/index', () => {
url: 'some-url',
});

expect(timers.setTimeout.mock.calls).toHaveLength(1);
expect(timers.setTimeout.mock.calls).toHaveLength(retry + 1);
expect(timers.setTimeout.mock.calls[0][0]).toBe(delay);
});
});
Expand Down
48 changes: 34 additions & 14 deletions lib/modules/platform/gitlab/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -994,9 +994,12 @@ export async function setBranchStatus({
}: BranchStatusConfig): Promise<void> {
// First, get the branch commit SHA
const branchSha = git.getBranchCommit(branchName);
if (!branchSha) {
logger.warn('Failed to get the branch commit SHA');
return;
}
// Now, check the statuses for that commit
// TODO: types (#22198)
const url = `projects/${config.repository}/statuses/${branchSha!}`;
const url = `projects/${config.repository}/statuses/${branchSha}`;
let state = 'success';
if (renovateState === 'yellow') {
state = 'pending';
Expand All @@ -1013,21 +1016,38 @@ export async function setBranchStatus({
options.target_url = targetUrl;
}

if (branchSha) {
const commitUrl = `projects/${config.repository}/repository/commits/${branchSha}`;
await gitlabApi
.getJsonSafe(commitUrl, LastPipelineId)
.onValue((pipelineId) => {
options.pipeline_id = pipelineId;
});
}
const retryTimes = parseInteger(
process.env.RENOVATE_X_GITLAB_BRANCH_STATUS_CHECK_ATTEMPTS,
2,
);

try {
// give gitlab some time to create pipelines for the sha
await setTimeout(
parseInteger(process.env.RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY, 1000),
);
for (let attempt = 1; attempt <= retryTimes + 1; attempt += 1) {
const commitUrl = `projects/${config.repository}/repository/commits/${branchSha}`;
await gitlabApi
.getJsonSafe(commitUrl, { memCache: false }, LastPipelineId)
.onValue((pipelineId) => {
options.pipeline_id = pipelineId;
});
if (options.pipeline_id !== undefined) {
break;
}
if (attempt >= retryTimes + 1) {
logger.debug(`Pipeline not yet created after ${attempt} attempts`);
} else {
logger.debug(`Pipeline not yet created. Retrying ${attempt}`);
}
// give gitlab some time to create pipelines for the sha
await setTimeout(
parseInteger(process.env.RENOVATE_X_GITLAB_BRANCH_STATUS_DELAY, 1000),
);
}
} catch (err) {
logger.debug({ err });
logger.warn('Failed to retrieve commit pipeline');
}

try {
await gitlabApi.postJson(url, { body: options });

// update status cache
Expand Down

0 comments on commit 78f389a

Please sign in to comment.