From e9bc921eecc36766e11a56f4ac8ab45fef7ed5d3 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Wed, 15 Jan 2025 10:40:17 +0100 Subject: [PATCH] fix(gitea): handle null PR as temporary error (#33623) --- lib/modules/platform/gitea/index.spec.ts | 13 +++++++++++++ lib/modules/platform/gitea/pr-cache.ts | 13 +++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/modules/platform/gitea/index.spec.ts b/lib/modules/platform/gitea/index.spec.ts index 84031f86974876..23494f1ed8f1f9 100644 --- a/lib/modules/platform/gitea/index.spec.ts +++ b/lib/modules/platform/gitea/index.spec.ts @@ -9,6 +9,7 @@ import { REPOSITORY_CHANGED, REPOSITORY_EMPTY, REPOSITORY_MIRRORED, + TEMPORARY_ERROR, } from '../../../constants/error-messages'; import type { logger as _logger } from '../../../logger'; import type * as _git from '../../../util/git'; @@ -1309,6 +1310,18 @@ describe('modules/platform/gitea/index', () => { expect(res).toBeNull(); }); + + it('should throw temporary error for null pull request', async () => { + const scope = httpMock + .scope('https://gitea.com/api/v1') + .get('/repos/some/repo/pulls') + .query({ state: 'all', sort: 'recentupdate' }) + .reply(200, [null]); // TODO: 404 should be handled + await initFakePlatform(scope); + await initFakeRepo(scope); + + await expect(gitea.getPr(42)).rejects.toThrow(TEMPORARY_ERROR); + }); }); describe('findPr', () => { diff --git a/lib/modules/platform/gitea/pr-cache.ts b/lib/modules/platform/gitea/pr-cache.ts index e6f363500dd03e..936689ef14810c 100644 --- a/lib/modules/platform/gitea/pr-cache.ts +++ b/lib/modules/platform/gitea/pr-cache.ts @@ -1,5 +1,7 @@ import { dequal } from 'dequal'; import { DateTime } from 'luxon'; +import { TEMPORARY_ERROR } from '../../../constants/error-messages'; +import { logger } from '../../../logger'; import * as memCache from '../../../util/cache/memory'; import { getCache } from '../../../util/cache/repository'; import type { GiteaHttp } from '../../../util/http/gitea'; @@ -83,7 +85,7 @@ export class GiteaPrCache { prCache.setPr(item); } - private reconcile(rawItems: PR[]): boolean { + private reconcile(rawItems: (PR | null)[]): boolean { const { items } = this.cache; let { updated_at } = this.cache; const cacheTime = updated_at ? DateTime.fromISO(updated_at) : null; @@ -91,6 +93,12 @@ export class GiteaPrCache { let needNextPage = true; for (const rawItem of rawItems) { + if (!rawItem) { + logger.warn('Gitea PR is empty, throwing temporary error'); + // Gitea API sometimes returns empty PRs, so we throw a temporary error + // https://github.com/go-gitea/gitea/blob/fcd096231ac2deaefbca10a7db1b9b01f1da93d7/services/convert/pull.go#L34-L52 + throw new Error(TEMPORARY_ERROR); + } const id = rawItem.number; const newItem = toRenovatePR(rawItem, this.author); @@ -127,7 +135,8 @@ export class GiteaPrCache { `${API_PATH}/repos/${this.repo}/pulls?${query}`; while (url) { - const res: HttpResponse = await http.getJson(url, { + // TODO: use zod, typescript can't infer the type of the response #22198 + const res: HttpResponse<(PR | null)[]> = await http.getJson(url, { memCache: false, paginate: false, });