mirror of
https://gitea.com/actions/setup-java.git
synced 2026-08-07 02:31:20 +00:00
Fix JetBrains Runtime release pagination (#1218)
* Fix JetBrains release pagination Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Update setup distribution Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges <brborges@microsoft.com>
This commit is contained in:
@@ -47,6 +47,24 @@ const core = await import('@actions/core');
|
|||||||
const {JetBrainsDistribution} =
|
const {JetBrainsDistribution} =
|
||||||
await import('../../src/distributions/jetbrains/installer.js');
|
await import('../../src/distributions/jetbrains/installer.js');
|
||||||
const {RetryingHttpClient} = await import('../../src/retrying-http-client.js');
|
const {RetryingHttpClient} = await import('../../src/retrying-http-client.js');
|
||||||
|
const {MAX_PAGINATION_PAGES} = await import('../../src/util.js');
|
||||||
|
|
||||||
|
const JETBRAINS_RELEASES_URL =
|
||||||
|
'https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases?per_page=100';
|
||||||
|
|
||||||
|
function release(tagName: string, prerelease: boolean) {
|
||||||
|
return {
|
||||||
|
tag_name: tagName,
|
||||||
|
name: tagName,
|
||||||
|
prerelease
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextPageHeader(page: number) {
|
||||||
|
return {
|
||||||
|
link: `<${JETBRAINS_RELEASES_URL}&page=${page}>; rel="next"`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function response(
|
function response(
|
||||||
statusCode: number,
|
statusCode: number,
|
||||||
@@ -110,6 +128,138 @@ describe('getAvailableVersions', () => {
|
|||||||
expect(availableVersions.length).toBe(length);
|
expect(availableVersions.length).toBe(length);
|
||||||
}, 10_000);
|
}, 10_000);
|
||||||
|
|
||||||
|
it('continues a stable request after an all-prerelease page', async () => {
|
||||||
|
jest.spyOn(HttpClient.prototype, 'head').mockResolvedValue({
|
||||||
|
message: {statusCode: 200}
|
||||||
|
} as any);
|
||||||
|
spyHttpClient
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: nextPageHeader(2),
|
||||||
|
result: [release('jbr-release-26.0.0b1.1', true)]
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {},
|
||||||
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
||||||
|
});
|
||||||
|
const distribution = new JetBrainsDistribution({
|
||||||
|
version: '21',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const availableVersions = await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(availableVersions.map(version => version.tag_name)).toContain(
|
||||||
|
'jbr-release-21.0.11b1163.116'
|
||||||
|
);
|
||||||
|
expect(availableVersions.map(version => version.tag_name)).not.toContain(
|
||||||
|
'jbr-release-26.0.0b1.1'
|
||||||
|
);
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('continues an EA request after an all-stable page', async () => {
|
||||||
|
jest.spyOn(HttpClient.prototype, 'head').mockResolvedValue({
|
||||||
|
message: {statusCode: 200}
|
||||||
|
} as any);
|
||||||
|
spyHttpClient
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: nextPageHeader(2),
|
||||||
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {},
|
||||||
|
result: [release('jbr-release-26.0.0b1.1', true)]
|
||||||
|
});
|
||||||
|
const distribution = new JetBrainsDistribution({
|
||||||
|
version: '26-ea',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const availableVersions = await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(availableVersions.map(version => version.tag_name)).toEqual([
|
||||||
|
'jbr-release-26.0.0b1.1'
|
||||||
|
]);
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops pagination when a raw GitHub page is empty', async () => {
|
||||||
|
spyHttpClient
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: nextPageHeader(2),
|
||||||
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
||||||
|
})
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: nextPageHeader(3),
|
||||||
|
result: []
|
||||||
|
});
|
||||||
|
const distribution = new JetBrainsDistribution({
|
||||||
|
version: '26-ea',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops at the pagination safeguard', async () => {
|
||||||
|
spyHttpClient.mockResolvedValue({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: nextPageHeader(2),
|
||||||
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
||||||
|
});
|
||||||
|
const distribution = new JetBrainsDistribution({
|
||||||
|
version: '26-ea',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const availableVersions = await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(availableVersions).toEqual([]);
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(MAX_PAGINATION_PAGES);
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
`Reached pagination safeguard limit (${MAX_PAGINATION_PAGES} pages) while listing JetBrains Runtime releases.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores pagination links with an unexpected origin', async () => {
|
||||||
|
spyHttpClient.mockResolvedValueOnce({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
link: '<https://example.com/releases?page=2>; rel="next"'
|
||||||
|
},
|
||||||
|
result: [release('jbr-release-21.0.11b1163.116', false)]
|
||||||
|
});
|
||||||
|
const distribution = new JetBrainsDistribution({
|
||||||
|
version: '26-ea',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(1);
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
'Ignoring pagination link with unexpected origin: https://example.com/releases?page=2'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('retries a GitHub rate limit using Retry-After', async () => {
|
it('retries a GitHub rate limit using Retry-After', async () => {
|
||||||
spyHttpClient.mockRestore();
|
spyHttpClient.mockRestore();
|
||||||
const sleep = jest.fn(async () => undefined);
|
const sleep = jest.fn(async () => undefined);
|
||||||
|
|||||||
Vendored
+28
-21
@@ -25,6 +25,8 @@ export const modules = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const JETBRAINS_RELEASES_URL = 'https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases?per_page=100';
|
||||||
|
const GITHUB_API_ORIGIN = 'https://api.github.com';
|
||||||
class JetBrainsDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/* .JavaBase */ .O {
|
class JetBrainsDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/* .JavaBase */ .O {
|
||||||
constructor(installerOptions) {
|
constructor(installerOptions) {
|
||||||
super('JetBrains', installerOptions);
|
super('JetBrains', installerOptions);
|
||||||
@@ -74,34 +76,39 @@ class JetBrainsDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_
|
|||||||
if (_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .isDebug */ ._o()) {
|
if (_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .isDebug */ ._o()) {
|
||||||
console.time('Retrieving available versions for JBR took'); // eslint-disable-line no-console
|
console.time('Retrieving available versions for JBR took'); // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
// 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 = [];
|
const rawVersions = [];
|
||||||
const bearerToken = process.env.GITHUB_TOKEN;
|
const bearerToken = process.env.GITHUB_TOKEN;
|
||||||
while (true) {
|
const requestHeaders = {};
|
||||||
const requestArguments = `per_page=100&page=${page_index}`;
|
if (bearerToken) {
|
||||||
const requestHeaders = {};
|
requestHeaders['Authorization'] = `Bearer ${bearerToken}`;
|
||||||
if (bearerToken) {
|
}
|
||||||
requestHeaders['Authorization'] = `Bearer ${bearerToken}`;
|
let releasesUrl = JETBRAINS_RELEASES_URL;
|
||||||
}
|
let pageCount = 0;
|
||||||
const rawUrl = `https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases?${requestArguments}`;
|
if (_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .isDebug */ ._o()) {
|
||||||
if (_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .isDebug */ ._o() && page_index === 1) {
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .debug */ .Yz(`Gathering available versions from '${releasesUrl}'`);
|
||||||
// url is identical except page_index so print it once for debug
|
}
|
||||||
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .debug */ .Yz(`Gathering available versions from '${rawUrl}'`);
|
while (releasesUrl) {
|
||||||
}
|
pageCount++;
|
||||||
const paginationPageResult = (await this.http.getJson(rawUrl, requestHeaders)).result;
|
const response = await this.http.getJson(releasesUrl, requestHeaders);
|
||||||
|
const paginationPageResult = response.result;
|
||||||
if (!paginationPageResult || paginationPageResult.length === 0) {
|
if (!paginationPageResult || paginationPageResult.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const paginationPage = paginationPageResult.filter(version => this.stable ? !version.prerelease : version.prerelease);
|
rawVersions.push(...paginationPageResult.filter(version => this.stable ? !version.prerelease : version.prerelease));
|
||||||
if (!paginationPage || paginationPage.length === 0) {
|
const nextUrl = (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .getNextPageUrlFromLinkHeader */ .rC)(response.headers);
|
||||||
// break infinity loop because we have reached end of pagination
|
if (nextUrl && !(0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .validatePaginationUrl */ .SA)(nextUrl, GITHUB_API_ORIGIN)) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .warning */ .$e(`Ignoring pagination link with unexpected origin: ${nextUrl}`);
|
||||||
|
releasesUrl = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
releasesUrl = nextUrl;
|
||||||
|
}
|
||||||
|
if (pageCount >= _util_js__WEBPACK_IMPORTED_MODULE_5__/* .MAX_PAGINATION_PAGES */ .Tp) {
|
||||||
|
if (releasesUrl) {
|
||||||
|
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .warning */ .$e(`Reached pagination safeguard limit (${_util_js__WEBPACK_IMPORTED_MODULE_5__/* .MAX_PAGINATION_PAGES */ .Tp} pages) while listing JetBrains Runtime releases.`);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
rawVersions.push(...paginationPage);
|
|
||||||
page_index++;
|
|
||||||
}
|
}
|
||||||
if (this.stable) {
|
if (this.stable) {
|
||||||
// Add versions not available from the API but are downloadable
|
// Add versions not available from the API but are downloadable
|
||||||
|
|||||||
@@ -11,10 +11,21 @@ import {
|
|||||||
JavaInstallerOptions,
|
JavaInstallerOptions,
|
||||||
JavaInstallerResults
|
JavaInstallerResults
|
||||||
} from '../base-models.js';
|
} from '../base-models.js';
|
||||||
import {cacheJdkDir, extractJdkFile, isVersionSatisfies} from '../../util.js';
|
import {
|
||||||
|
cacheJdkDir,
|
||||||
|
extractJdkFile,
|
||||||
|
getNextPageUrlFromLinkHeader,
|
||||||
|
isVersionSatisfies,
|
||||||
|
MAX_PAGINATION_PAGES,
|
||||||
|
validatePaginationUrl
|
||||||
|
} from '../../util.js';
|
||||||
import {OutgoingHttpHeaders} from 'http';
|
import {OutgoingHttpHeaders} from 'http';
|
||||||
import {HttpCodes} from '@actions/http-client';
|
import {HttpCodes} from '@actions/http-client';
|
||||||
|
|
||||||
|
const JETBRAINS_RELEASES_URL =
|
||||||
|
'https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases?per_page=100';
|
||||||
|
const GITHUB_API_ORIGIN = 'https://api.github.com';
|
||||||
|
|
||||||
export class JetBrainsDistribution extends JavaBase {
|
export class JetBrainsDistribution extends JavaBase {
|
||||||
constructor(installerOptions: JavaInstallerOptions) {
|
constructor(installerOptions: JavaInstallerOptions) {
|
||||||
super('JetBrains', installerOptions);
|
super('JetBrains', installerOptions);
|
||||||
@@ -96,46 +107,54 @@ export class JetBrainsDistribution extends JavaBase {
|
|||||||
console.time('Retrieving available versions for JBR took'); // eslint-disable-line no-console
|
console.time('Retrieving available versions for JBR took'); // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 rawVersions: IJetBrainsRawVersion[] = [];
|
||||||
const bearerToken = process.env.GITHUB_TOKEN;
|
const bearerToken = process.env.GITHUB_TOKEN;
|
||||||
|
const requestHeaders: OutgoingHttpHeaders = {};
|
||||||
|
if (bearerToken) {
|
||||||
|
requestHeaders['Authorization'] = `Bearer ${bearerToken}`;
|
||||||
|
}
|
||||||
|
let releasesUrl: string | null = JETBRAINS_RELEASES_URL;
|
||||||
|
let pageCount = 0;
|
||||||
|
|
||||||
while (true) {
|
if (core.isDebug()) {
|
||||||
const requestArguments = `per_page=100&page=${page_index}`;
|
core.debug(`Gathering available versions from '${releasesUrl}'`);
|
||||||
const requestHeaders: OutgoingHttpHeaders = {};
|
}
|
||||||
|
|
||||||
if (bearerToken) {
|
while (releasesUrl) {
|
||||||
requestHeaders['Authorization'] = `Bearer ${bearerToken}`;
|
pageCount++;
|
||||||
}
|
const response = await this.http.getJson<IJetBrainsRawVersion[]>(
|
||||||
|
releasesUrl,
|
||||||
const rawUrl = `https://api.github.com/repos/JetBrains/JetBrainsRuntime/releases?${requestArguments}`;
|
requestHeaders
|
||||||
|
);
|
||||||
if (core.isDebug() && page_index === 1) {
|
const paginationPageResult = response.result;
|
||||||
// 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) {
|
if (!paginationPageResult || paginationPageResult.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const paginationPage: IJetBrainsRawVersion[] =
|
rawVersions.push(
|
||||||
paginationPageResult.filter(version =>
|
...paginationPageResult.filter(version =>
|
||||||
this.stable ? !version.prerelease : version.prerelease
|
this.stable ? !version.prerelease : version.prerelease
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const nextUrl = getNextPageUrlFromLinkHeader(response.headers);
|
||||||
|
if (nextUrl && !validatePaginationUrl(nextUrl, GITHUB_API_ORIGIN)) {
|
||||||
|
core.warning(
|
||||||
|
`Ignoring pagination link with unexpected origin: ${nextUrl}`
|
||||||
);
|
);
|
||||||
if (!paginationPage || paginationPage.length === 0) {
|
releasesUrl = null;
|
||||||
// break infinity loop because we have reached end of pagination
|
} else {
|
||||||
break;
|
releasesUrl = nextUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
rawVersions.push(...paginationPage);
|
if (pageCount >= MAX_PAGINATION_PAGES) {
|
||||||
page_index++;
|
if (releasesUrl) {
|
||||||
|
core.warning(
|
||||||
|
`Reached pagination safeguard limit (${MAX_PAGINATION_PAGES} pages) while listing JetBrains Runtime releases.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stable) {
|
if (this.stable) {
|
||||||
|
|||||||
Reference in New Issue
Block a user