diff --git a/__tests__/distributors/jetbrains-installer.test.ts b/__tests__/distributors/jetbrains-installer.test.ts index bf1dc15d..afc41c47 100644 --- a/__tests__/distributors/jetbrains-installer.test.ts +++ b/__tests__/distributors/jetbrains-installer.test.ts @@ -9,6 +9,8 @@ import * as core from '@actions/core'; describe('getAvailableVersions', () => { let spyHttpClient: jest.SpyInstance; let spyCoreError: jest.SpyInstance; + let spyCoreWarning: jest.SpyInstance; + let spyHeadRequest: jest.SpyInstance; beforeEach(() => { spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson'); @@ -21,6 +23,14 @@ describe('getAvailableVersions', () => { // Mock core.error to suppress error logs spyCoreError = jest.spyOn(core, 'error'); spyCoreError.mockImplementation(() => {}); + spyCoreWarning = jest.spyOn(core, 'warning'); + spyCoreWarning.mockImplementation(() => {}); + spyHeadRequest = jest.spyOn(HttpClient.prototype, 'head'); + spyHeadRequest.mockResolvedValue({ + message: { + statusCode: 404 + } + } as any); }); afterEach(() => { @@ -50,6 +60,27 @@ describe('getAvailableVersions', () => { os.platform() === 'win32' ? manifestData.length : manifestData.length + 2; expect(availableVersions.length).toBe(length); }, 10_000); + + it('stops pagination after 1000 pages as a safeguard', async () => { + spyHttpClient.mockResolvedValue({ + statusCode: 200, + headers: {}, + result: [{tag_name: 'jbr17-b87.7', name: 'jbr17-b87.7', prerelease: false}] + }); + + const distribution = new JetBrainsDistribution({ + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false + }); + await distribution['getAvailableVersions'](); + + expect(spyHttpClient).toHaveBeenCalledTimes(1000); + expect(spyCoreWarning).toHaveBeenCalledWith( + expect.stringContaining('Reached pagination safeguard limit (1000 pages)') + ); + }, 20_000); }); describe('findPackageForDownload', () => { diff --git a/src/distributions/jetbrains/installer.ts b/src/distributions/jetbrains/installer.ts index e3458e1e..15c4cd6f 100644 --- a/src/distributions/jetbrains/installer.ts +++ b/src/distributions/jetbrains/installer.ts @@ -16,6 +16,8 @@ import {extractJdkFile, isVersionSatisfies} from '../../util'; import {OutgoingHttpHeaders} from 'http'; import {HttpCodes} from '@actions/http-client'; +const MAX_PAGINATION_PAGES = 1000; + export class JetBrainsDistribution extends JavaBase { constructor(installerOptions: JavaInstallerOptions) { super('JetBrains', installerOptions); @@ -93,7 +95,7 @@ export class JetBrainsDistribution extends JavaBase { const rawVersions: IJetBrainsRawVersion[] = []; const bearerToken = process.env.GITHUB_TOKEN; - while (true) { + while (page_index <= MAX_PAGINATION_PAGES) { const requestArguments = `per_page=100&page=${page_index}`; const requestHeaders: OutgoingHttpHeaders = {}; @@ -129,6 +131,12 @@ export class JetBrainsDistribution extends JavaBase { page_index++; } + if (page_index > MAX_PAGINATION_PAGES) { + core.warning( + `Reached pagination safeguard limit (${MAX_PAGINATION_PAGES} pages) while listing JetBrains runtime releases.` + ); + } + if (this.stable) { // Add versions not available from the API but are downloadable const hidden = ['11_0_10b1145.115', '11_0_11b1341.60'];