Description:
JetBrainsDistribution.getAvailableVersions() stops paginating when the current GitHub releases page contains no releases matching the requested stability class.
The implementation first checks whether the raw page is empty, then filters that page by prerelease, and incorrectly treats an empty filtered result as end of pagination:
- Pagination loop:
|
// need to iterate through all pages to retrieve the list of all versions |
|
// GitHub API doesn't provide way to retrieve the count of pages to iterate so infinity loop |
|
let page_index = 1; |
|
const rawVersions: IJetBrainsRawVersion[] = []; |
|
const bearerToken = process.env.GITHUB_TOKEN; |
|
|
|
while (true) { |
|
const requestArguments = `per_page=100&page=${page_index}`; |
|
const requestHeaders: OutgoingHttpHeaders = {}; |
|
|
|
if (bearerToken) { |
|
requestHeaders['Authorization'] = `Bearer ${bearerToken}`; |
|
} |
|
|
|
const rawUrl = `https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases?${requestArguments}`; |
|
|
|
if (core.isDebug() && page_index === 1) { |
|
// url is identical except page_index so print it once for debug |
|
core.debug(`Gathering available versions from '${rawUrl}'`); |
|
} |
|
|
|
const paginationPageResult = ( |
|
await this.http.getJson<IJetBrainsRawVersion[]>(rawUrl, requestHeaders) |
|
).result; |
|
if (!paginationPageResult || paginationPageResult.length === 0) { |
|
// break infinity loop because we have reached end of pagination |
|
break; |
|
} |
|
|
|
const paginationPage: IJetBrainsRawVersion[] = |
|
paginationPageResult.filter(version => |
|
this.stable ? !version.prerelease : version.prerelease |
|
); |
|
if (!paginationPage || paginationPage.length === 0) { |
|
// break infinity loop because we have reached end of pagination |
|
break; |
|
} |
|
|
|
rawVersions.push(...paginationPage); |
|
page_index++; |
|
} |
- Stability filter and early termination:
|
const paginationPage: IJetBrainsRawVersion[] = |
|
paginationPageResult.filter(version => |
|
this.stable ? !version.prerelease : version.prerelease |
|
); |
|
if (!paginationPage || paginationPage.length === 0) { |
|
// break infinity loop because we have reached end of pagination |
|
break; |
|
} |
GitHub pagination is independent of that client-side filter. A non-empty page containing only prereleases is not the end of stable releases, and a non-empty page containing only stable releases is not the end of EA releases. Valid matching releases on later pages are therefore hidden.
Task version:
actions/setup-java at ab597f914a6894678251803485c09f413263b889 (main on 2026-08-05)
Platform:
This occurs before platform-specific asset selection.
Runner type:
Repro steps:
Use deterministic mocked GitHub releases pages for GET /repos/JetBrains/JetBrainsRuntime/releases:
- For a stable request (
java-version: 21, no -ea), return page 1 as:
[{"tag_name":"jbr-release-26-ea","name":"jbr-release-26-ea","prerelease":true}]
and page 2 as:
[{"tag_name":"jbr-release-21.0.11b1163.116","name":"jbr-release-21.0.11b1163.116","prerelease":false}]
End pagination after page 2.
- Call
JetBrainsDistribution.getAvailableVersions().
- Observe that only page 1 is requested. Filtering page 1 produces
[], so the loop breaks at lines 132-135 and never requests page 2.
- The reciprocal case reproduces for an EA request when page 1 contains only stable releases and page 2 contains prereleases.
These mocked page sequences are deterministic and isolate the pagination contract from the changing contents of the live JetBrains releases repository.
Expected behavior:
Pagination termination depends only on the raw GitHub page or GitHub's end-of-pagination signal. A non-empty raw page whose filtered result is empty must not prevent later pages from being fetched.
Actual behavior:
Pagination terminates when the filtered page is empty, so matching stable or EA releases on later pages are omitted and version resolution can fail with a misleading "No matching version found" error.
Acceptance criteria:
- Continue pagination after a non-empty raw page with zero matching stable/EA releases.
- Preserve stable/EA filtering and all existing JetBrains version and asset handling.
- Terminate on GitHub's actual end-of-pagination signal.
- Bound pagination with the repository's
MAX_PAGINATION_PAGES safeguard.
- Use the existing GitHub pagination/header and origin-validation helpers.
- Add regression coverage for stable-after-prerelease and EA-after-stable page sequences, normal termination, and the pagination safeguard.
Description:
JetBrainsDistribution.getAvailableVersions()stops paginating when the current GitHub releases page contains no releases matching the requested stability class.The implementation first checks whether the raw page is empty, then filters that page by
prerelease, and incorrectly treats an empty filtered result as end of pagination:setup-java/src/distributions/jetbrains/installer.ts
Lines 99 to 139 in ab597f9
setup-java/src/distributions/jetbrains/installer.ts
Lines 128 to 135 in ab597f9
GitHub pagination is independent of that client-side filter. A non-empty page containing only prereleases is not the end of stable releases, and a non-empty page containing only stable releases is not the end of EA releases. Valid matching releases on later pages are therefore hidden.
Task version:
actions/setup-javaatab597f914a6894678251803485c09f413263b889(mainon 2026-08-05)Platform:
This occurs before platform-specific asset selection.
Runner type:
Repro steps:
Use deterministic mocked GitHub releases pages for
GET /repos/JetBrains/JetBrainsRuntime/releases:java-version: 21, no-ea), return page 1 as:[{"tag_name":"jbr-release-26-ea","name":"jbr-release-26-ea","prerelease":true}][{"tag_name":"jbr-release-21.0.11b1163.116","name":"jbr-release-21.0.11b1163.116","prerelease":false}]JetBrainsDistribution.getAvailableVersions().[], so the loop breaks at lines 132-135 and never requests page 2.These mocked page sequences are deterministic and isolate the pagination contract from the changing contents of the live JetBrains releases repository.
Expected behavior:
Pagination termination depends only on the raw GitHub page or GitHub's end-of-pagination signal. A non-empty raw page whose filtered result is empty must not prevent later pages from being fetched.
Actual behavior:
Pagination terminates when the filtered page is empty, so matching stable or EA releases on later pages are omitted and version resolution can fail with a misleading "No matching version found" error.
Acceptance criteria:
MAX_PAGINATION_PAGESsafeguard.