mirror of
https://github.com/actions/setup-java.git
synced 2026-06-14 00:02:18 +08:00
Implement pagination with link headers for Adoptium based apis (#1014)
* Use Link headers for Adoptium pagination * Fix nullable pagination URL types and rebuild dist * Add 1000-page safeguard for JetBrains pagination * Adjust plan for pagination safeguard scope * Move pagination safeguard to non-JetBrains installers * Add 1000-page safeguard to Adopt Temurin and Semeru pagination * Fix Prettier formatting in adopt, semeru, and temurin installer files * Fix CI audit failure by updating vulnerable transitive deps * Address PR review: RFC-compliant Link parsing, SSRF validation, centralized constant - Make getNextPageUrlFromLinkHeader RFC 8288 compliant by splitting link-values and checking for rel=next anywhere in the parameters, not just as the first parameter after the semicolon. - Add validatePaginationUrl utility to reject pagination URLs that point to unexpected origins (SSRF mitigation). - Centralize MAX_PAGINATION_PAGES in util.ts instead of duplicating across Adopt, Semeru, and Temurin installers. - Add tests for rel not being the first parameter, and for URL origin validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address code review feedback on pagination implementation - Tighten rel regex with word boundary to prevent false positives (e.g., rel="nextsomething" no longer matches). - Use parsed.origin comparison in validatePaginationUrl to correctly handle explicit default ports (e.g., :443 for HTTPS). - Fix pagination safeguard tests to use same-origin URLs so they actually exercise the 1000-page limit instead of being rejected by origin validation on the first request. - Add test for rel="nextsomething" not matching. - Add test for explicit default port acceptance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix prettier formatting in util.test.ts * Rebuild dist/ to fix check-dist CI failure --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
ad9d6a6320
commit
43120bc3c3
@ -14,6 +14,7 @@ import * as core from '@actions/core';
|
|||||||
describe('getAvailableVersions', () => {
|
describe('getAvailableVersions', () => {
|
||||||
let spyHttpClient: jest.SpyInstance;
|
let spyHttpClient: jest.SpyInstance;
|
||||||
let spyCoreError: jest.SpyInstance;
|
let spyCoreError: jest.SpyInstance;
|
||||||
|
let spyCoreWarning: jest.SpyInstance;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
@ -26,6 +27,8 @@ describe('getAvailableVersions', () => {
|
|||||||
// Mock core.error to suppress error logs
|
// Mock core.error to suppress error logs
|
||||||
spyCoreError = jest.spyOn(core, 'error');
|
spyCoreError = jest.spyOn(core, 'error');
|
||||||
spyCoreError.mockImplementation(() => {});
|
spyCoreError.mockImplementation(() => {});
|
||||||
|
spyCoreWarning = jest.spyOn(core, 'warning');
|
||||||
|
spyCoreWarning.mockImplementation(() => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -136,22 +139,19 @@ describe('getAvailableVersions', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it('load available versions', async () => {
|
it('load available versions', async () => {
|
||||||
|
const nextPageUrl =
|
||||||
|
'https://api.adoptopenjdk.net/v3/assets/version/%5B1.0,100.0%5D?page=1&page_size=20';
|
||||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
spyHttpClient
|
spyHttpClient
|
||||||
.mockReturnValueOnce({
|
.mockReturnValueOnce({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
headers: {},
|
headers: {link: `<${nextPageUrl}>; rel="next"`},
|
||||||
result: manifestData as any
|
result: manifestData as any
|
||||||
})
|
})
|
||||||
.mockReturnValueOnce({
|
.mockReturnValueOnce({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
headers: {},
|
headers: {},
|
||||||
result: manifestData as any
|
result: manifestData as any
|
||||||
})
|
|
||||||
.mockReturnValueOnce({
|
|
||||||
statusCode: 200,
|
|
||||||
headers: {},
|
|
||||||
result: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const distribution = new AdoptDistribution(
|
const distribution = new AdoptDistribution(
|
||||||
@ -166,6 +166,34 @@ describe('getAvailableVersions', () => {
|
|||||||
const availableVersions = await distribution['getAvailableVersions']();
|
const availableVersions = await distribution['getAvailableVersions']();
|
||||||
expect(availableVersions).not.toBeNull();
|
expect(availableVersions).not.toBeNull();
|
||||||
expect(availableVersions.length).toBe(manifestData.length * 2);
|
expect(availableVersions.length).toBe(manifestData.length * 2);
|
||||||
|
expect(spyHttpClient).toHaveBeenNthCalledWith(2, nextPageUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops pagination after 1000 pages as a safeguard', async () => {
|
||||||
|
const nextPageUrl =
|
||||||
|
'https://api.adoptopenjdk.net/v3/assets/version/%5B1.0,100.0%5D?page=2&page_size=20';
|
||||||
|
spyHttpClient.mockReturnValue({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {link: `<${nextPageUrl}>; rel="next"`},
|
||||||
|
result: [{version_data: {semver: '17.0.1'}, binaries: []}] as any
|
||||||
|
});
|
||||||
|
|
||||||
|
const distribution = new AdoptDistribution(
|
||||||
|
{
|
||||||
|
version: '11',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
},
|
||||||
|
AdoptImplementation.Hotspot
|
||||||
|
);
|
||||||
|
|
||||||
|
await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(1000);
|
||||||
|
expect(spyCoreWarning).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('Reached pagination safeguard limit (1000 pages)')
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import * as core from '@actions/core';
|
|||||||
describe('getAvailableVersions', () => {
|
describe('getAvailableVersions', () => {
|
||||||
let spyHttpClient: jest.SpyInstance;
|
let spyHttpClient: jest.SpyInstance;
|
||||||
let spyCoreError: jest.SpyInstance;
|
let spyCoreError: jest.SpyInstance;
|
||||||
|
let spyCoreWarning: jest.SpyInstance;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
@ -20,6 +21,8 @@ describe('getAvailableVersions', () => {
|
|||||||
// Mock core.error to suppress error logs
|
// Mock core.error to suppress error logs
|
||||||
spyCoreError = jest.spyOn(core, 'error');
|
spyCoreError = jest.spyOn(core, 'error');
|
||||||
spyCoreError.mockImplementation(() => {});
|
spyCoreError.mockImplementation(() => {});
|
||||||
|
spyCoreWarning = jest.spyOn(core, 'warning');
|
||||||
|
spyCoreWarning.mockImplementation(() => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -82,22 +85,19 @@ describe('getAvailableVersions', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it('load available versions', async () => {
|
it('load available versions', async () => {
|
||||||
|
const nextPageUrl =
|
||||||
|
'https://api.adoptopenjdk.net/v3/assets/version/%5B1.0,100.0%5D?page=1&page_size=20';
|
||||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
spyHttpClient
|
spyHttpClient
|
||||||
.mockReturnValueOnce({
|
.mockReturnValueOnce({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
headers: {},
|
headers: {link: `<${nextPageUrl}>; rel="next"`},
|
||||||
result: manifestData as any
|
result: manifestData as any
|
||||||
})
|
})
|
||||||
.mockReturnValueOnce({
|
.mockReturnValueOnce({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
headers: {},
|
headers: {},
|
||||||
result: manifestData as any
|
result: manifestData as any
|
||||||
})
|
|
||||||
.mockReturnValueOnce({
|
|
||||||
statusCode: 200,
|
|
||||||
headers: {},
|
|
||||||
result: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const distribution = new SemeruDistribution({
|
const distribution = new SemeruDistribution({
|
||||||
@ -109,6 +109,31 @@ describe('getAvailableVersions', () => {
|
|||||||
const availableVersions = await distribution['getAvailableVersions']();
|
const availableVersions = await distribution['getAvailableVersions']();
|
||||||
expect(availableVersions).not.toBeNull();
|
expect(availableVersions).not.toBeNull();
|
||||||
expect(availableVersions.length).toBe(manifestData.length * 2);
|
expect(availableVersions.length).toBe(manifestData.length * 2);
|
||||||
|
expect(spyHttpClient).toHaveBeenNthCalledWith(2, nextPageUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops pagination after 1000 pages as a safeguard', async () => {
|
||||||
|
const nextPageUrl =
|
||||||
|
'https://api.adoptopenjdk.net/v3/assets/version/%5B1.0,100.0%5D?page=2&page_size=20';
|
||||||
|
spyHttpClient.mockReturnValue({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {link: `<${nextPageUrl}>; rel="next"`},
|
||||||
|
result: [{version_data: {semver: '17.0.1'}, binaries: []}] as any
|
||||||
|
});
|
||||||
|
|
||||||
|
const distribution = new SemeruDistribution({
|
||||||
|
version: '8',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(1000);
|
||||||
|
expect(spyCoreWarning).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('Reached pagination safeguard limit (1000 pages)')
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import * as core from '@actions/core';
|
|||||||
describe('getAvailableVersions', () => {
|
describe('getAvailableVersions', () => {
|
||||||
let spyHttpClient: jest.SpyInstance;
|
let spyHttpClient: jest.SpyInstance;
|
||||||
let spyCoreError: jest.SpyInstance;
|
let spyCoreError: jest.SpyInstance;
|
||||||
|
let spyCoreWarning: jest.SpyInstance;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
@ -23,6 +24,8 @@ describe('getAvailableVersions', () => {
|
|||||||
// Mock core.error to suppress error logs
|
// Mock core.error to suppress error logs
|
||||||
spyCoreError = jest.spyOn(core, 'error');
|
spyCoreError = jest.spyOn(core, 'error');
|
||||||
spyCoreError.mockImplementation(() => {});
|
spyCoreError.mockImplementation(() => {});
|
||||||
|
spyCoreWarning = jest.spyOn(core, 'warning');
|
||||||
|
spyCoreWarning.mockImplementation(() => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -93,22 +96,19 @@ describe('getAvailableVersions', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it('load available versions', async () => {
|
it('load available versions', async () => {
|
||||||
|
const nextPageUrl =
|
||||||
|
'https://api.adoptium.net/v3/assets/version/%5B1.0,100.0%5D?page=1&page_size=20';
|
||||||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
||||||
spyHttpClient
|
spyHttpClient
|
||||||
.mockReturnValueOnce({
|
.mockReturnValueOnce({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
headers: {},
|
headers: {link: `<${nextPageUrl}>; rel="next"`},
|
||||||
result: manifestData as any
|
result: manifestData as any
|
||||||
})
|
})
|
||||||
.mockReturnValueOnce({
|
.mockReturnValueOnce({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
headers: {},
|
headers: {},
|
||||||
result: manifestData as any
|
result: manifestData as any
|
||||||
})
|
|
||||||
.mockReturnValueOnce({
|
|
||||||
statusCode: 200,
|
|
||||||
headers: {},
|
|
||||||
result: []
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const distribution = new TemurinDistribution(
|
const distribution = new TemurinDistribution(
|
||||||
@ -123,6 +123,34 @@ describe('getAvailableVersions', () => {
|
|||||||
const availableVersions = await distribution['getAvailableVersions']();
|
const availableVersions = await distribution['getAvailableVersions']();
|
||||||
expect(availableVersions).not.toBeNull();
|
expect(availableVersions).not.toBeNull();
|
||||||
expect(availableVersions.length).toBe(manifestData.length * 2);
|
expect(availableVersions.length).toBe(manifestData.length * 2);
|
||||||
|
expect(spyHttpClient).toHaveBeenNthCalledWith(2, nextPageUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops pagination after 1000 pages as a safeguard', async () => {
|
||||||
|
const nextPageUrl =
|
||||||
|
'https://api.adoptium.net/v3/assets/version/%5B1.0,100.0%5D?page=2&page_size=20';
|
||||||
|
spyHttpClient.mockReturnValue({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {link: `<${nextPageUrl}>; rel="next"`},
|
||||||
|
result: [{version_data: {semver: '17.0.1'}, binaries: []}] as any
|
||||||
|
});
|
||||||
|
|
||||||
|
const distribution = new TemurinDistribution(
|
||||||
|
{
|
||||||
|
version: '8',
|
||||||
|
architecture: 'x64',
|
||||||
|
packageType: 'jdk',
|
||||||
|
checkLatest: false
|
||||||
|
},
|
||||||
|
TemurinImplementation.Hotspot
|
||||||
|
);
|
||||||
|
|
||||||
|
await distribution['getAvailableVersions']();
|
||||||
|
|
||||||
|
expect(spyHttpClient).toHaveBeenCalledTimes(1000);
|
||||||
|
expect(spyCoreWarning).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('Reached pagination safeguard limit (1000 pages)')
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
|||||||
@ -4,10 +4,12 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {
|
import {
|
||||||
convertVersionToSemver,
|
convertVersionToSemver,
|
||||||
|
getNextPageUrlFromLinkHeader,
|
||||||
getVersionFromFileContent,
|
getVersionFromFileContent,
|
||||||
isVersionSatisfies,
|
isVersionSatisfies,
|
||||||
isCacheFeatureAvailable,
|
isCacheFeatureAvailable,
|
||||||
isGhes
|
isGhes,
|
||||||
|
validatePaginationUrl
|
||||||
} from '../src/util';
|
} from '../src/util';
|
||||||
|
|
||||||
jest.mock('@actions/cache');
|
jest.mock('@actions/cache');
|
||||||
@ -85,6 +87,78 @@ describe('convertVersionToSemver', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getNextPageUrlFromLinkHeader', () => {
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
{
|
||||||
|
link: '<https://api.adoptium.net/v3/info/release_versions?page=1&page_size=10>; rel="next"'
|
||||||
|
},
|
||||||
|
'https://api.adoptium.net/v3/info/release_versions?page=1&page_size=10'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
Link: '<https://example.com/last?page=5>; rel="last", <https://example.com/next?page=2>; rel="next"'
|
||||||
|
},
|
||||||
|
'https://example.com/next?page=2'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
link: '<https://api.adoptium.net/v3/versions?page=3>; type="application/json"; rel="next"'
|
||||||
|
},
|
||||||
|
'https://api.adoptium.net/v3/versions?page=3'
|
||||||
|
],
|
||||||
|
[{link: '<https://example.com/last?page=5>; rel="last"'}, null],
|
||||||
|
[{link: '<https://example.com/page?p=2>; rel="nextsomething"'}, null],
|
||||||
|
[undefined, null]
|
||||||
|
])('returns %s -> %s', (headers, expected) => {
|
||||||
|
expect(getNextPageUrlFromLinkHeader(headers)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('validatePaginationUrl', () => {
|
||||||
|
it('accepts URL with matching origin', () => {
|
||||||
|
expect(
|
||||||
|
validatePaginationUrl(
|
||||||
|
'https://api.adoptium.net/v3/assets?page=2',
|
||||||
|
'https://api.adoptium.net'
|
||||||
|
)
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects URL with different host', () => {
|
||||||
|
expect(
|
||||||
|
validatePaginationUrl(
|
||||||
|
'https://evil.example.com/steal?data=1',
|
||||||
|
'https://api.adoptium.net'
|
||||||
|
)
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects URL with different protocol', () => {
|
||||||
|
expect(
|
||||||
|
validatePaginationUrl(
|
||||||
|
'http://api.adoptium.net/v3/assets?page=2',
|
||||||
|
'https://api.adoptium.net'
|
||||||
|
)
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false for invalid URL', () => {
|
||||||
|
expect(validatePaginationUrl('not-a-url', 'https://api.adoptium.net')).toBe(
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts URL with explicit default port', () => {
|
||||||
|
expect(
|
||||||
|
validatePaginationUrl(
|
||||||
|
'https://api.adoptium.net:443/v3/assets?page=2',
|
||||||
|
'https://api.adoptium.net'
|
||||||
|
)
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getVersionFromFileContent', () => {
|
describe('getVersionFromFileContent', () => {
|
||||||
describe('.sdkmanrc', () => {
|
describe('.sdkmanrc', () => {
|
||||||
it.each([
|
it.each([
|
||||||
|
|||||||
43
dist/cleanup/index.js
vendored
43
dist/cleanup/index.js
vendored
@ -52134,7 +52134,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.renameWinArchive = exports.getGitHubHttpHeaders = exports.convertVersionToSemver = exports.getVersionFromFileContent = exports.isCacheFeatureAvailable = exports.isGhes = exports.isJobStatusSuccess = exports.getToolcachePath = exports.isVersionSatisfies = exports.getDownloadArchiveExtension = exports.extractJdkFile = exports.getVersionFromToolcachePath = exports.getBooleanInput = exports.getTempDir = void 0;
|
exports.renameWinArchive = exports.validatePaginationUrl = exports.getNextPageUrlFromLinkHeader = exports.MAX_PAGINATION_PAGES = exports.getGitHubHttpHeaders = exports.convertVersionToSemver = exports.getVersionFromFileContent = exports.isCacheFeatureAvailable = exports.isGhes = exports.isJobStatusSuccess = exports.getToolcachePath = exports.isVersionSatisfies = exports.getDownloadArchiveExtension = exports.extractJdkFile = exports.getVersionFromToolcachePath = exports.getBooleanInput = exports.getTempDir = void 0;
|
||||||
const os_1 = __importDefault(__nccwpck_require__(22037));
|
const os_1 = __importDefault(__nccwpck_require__(22037));
|
||||||
const path_1 = __importDefault(__nccwpck_require__(71017));
|
const path_1 = __importDefault(__nccwpck_require__(71017));
|
||||||
const fs = __importStar(__nccwpck_require__(57147));
|
const fs = __importStar(__nccwpck_require__(57147));
|
||||||
@ -52301,6 +52301,47 @@ function getGitHubHttpHeaders() {
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
exports.getGitHubHttpHeaders = getGitHubHttpHeaders;
|
exports.getGitHubHttpHeaders = getGitHubHttpHeaders;
|
||||||
|
exports.MAX_PAGINATION_PAGES = 1000;
|
||||||
|
function getNextPageUrlFromLinkHeader(headers) {
|
||||||
|
var _a;
|
||||||
|
if (!headers) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const linkHeader = (_a = headers.link) !== null && _a !== void 0 ? _a : headers.Link;
|
||||||
|
if (!linkHeader) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const normalizedLinkHeader = Array.isArray(linkHeader)
|
||||||
|
? linkHeader.join(',')
|
||||||
|
: linkHeader;
|
||||||
|
// Split into individual link-values and find the one with rel="next"
|
||||||
|
// RFC 8288 allows rel to appear anywhere among the parameters
|
||||||
|
const linkValues = normalizedLinkHeader.split(/,(?=\s*<)/);
|
||||||
|
for (const linkValue of linkValues) {
|
||||||
|
const urlMatch = linkValue.match(/<([^>]+)>/);
|
||||||
|
if (!urlMatch)
|
||||||
|
continue;
|
||||||
|
const params = linkValue.slice(urlMatch[0].length);
|
||||||
|
// Use word boundary to match "next" as a standalone relation type
|
||||||
|
// RFC 8288 allows space-separated relation types like rel="next prev"
|
||||||
|
if (/;\s*rel="?[^"]*\bnext\b/i.test(params)) {
|
||||||
|
return urlMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
exports.getNextPageUrlFromLinkHeader = getNextPageUrlFromLinkHeader;
|
||||||
|
function validatePaginationUrl(url, allowedOrigin) {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
const allowed = new URL(allowedOrigin);
|
||||||
|
return parsed.origin === allowed.origin;
|
||||||
|
}
|
||||||
|
catch (_a) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.validatePaginationUrl = validatePaginationUrl;
|
||||||
// Rename archive to add extension because after downloading
|
// Rename archive to add extension because after downloading
|
||||||
// archive does not contain extension type and it leads to some issues
|
// archive does not contain extension type and it leads to some issues
|
||||||
// on Windows runners without PowerShell Core.
|
// on Windows runners without PowerShell Core.
|
||||||
|
|||||||
145
dist/setup/index.js
vendored
145
dist/setup/index.js
vendored
@ -77896,24 +77896,34 @@ class AdoptDistribution extends base_installer_1.JavaBase {
|
|||||||
`release_type=${releaseType}`,
|
`release_type=${releaseType}`,
|
||||||
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
||||||
].join('&');
|
].join('&');
|
||||||
// need to iterate through all pages to retrieve the list of all versions
|
const requestArguments = `${baseRequestArguments}&page_size=20&page=0`;
|
||||||
// Adopt API doesn't provide way to retrieve the count of pages to iterate so infinity loop
|
let availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
||||||
let page_index = 0;
|
|
||||||
const availableVersions = [];
|
const availableVersions = [];
|
||||||
while (true) {
|
let pageCount = 0;
|
||||||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
|
if (core.isDebug()) {
|
||||||
const availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||||
if (core.isDebug() && page_index === 0) {
|
}
|
||||||
// url is identical except page_index so print it once for debug
|
while (availableVersionsUrl) {
|
||||||
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
pageCount++;
|
||||||
|
const response = yield this.http.getJson(availableVersionsUrl);
|
||||||
|
const paginationPage = response.result;
|
||||||
|
const nextUrl = (0, util_1.getNextPageUrlFromLinkHeader)(response.headers);
|
||||||
|
if (nextUrl &&
|
||||||
|
!(0, util_1.validatePaginationUrl)(nextUrl, 'https://api.adoptopenjdk.net')) {
|
||||||
|
core.warning(`Ignoring pagination link with unexpected origin: ${nextUrl}`);
|
||||||
|
availableVersionsUrl = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
availableVersionsUrl = nextUrl;
|
||||||
}
|
}
|
||||||
const paginationPage = (yield this.http.getJson(availableVersionsUrl)).result;
|
|
||||||
if (paginationPage === null || paginationPage.length === 0) {
|
if (paginationPage === null || paginationPage.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
availableVersions.push(...paginationPage);
|
availableVersions.push(...paginationPage);
|
||||||
page_index++;
|
if (pageCount >= util_1.MAX_PAGINATION_PAGES) {
|
||||||
|
core.warning(`Reached pagination safeguard limit (${util_1.MAX_PAGINATION_PAGES} pages) while listing Adopt releases.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
core.startGroup('Print information about available versions');
|
core.startGroup('Print information about available versions');
|
||||||
@ -80071,24 +80081,34 @@ class SemeruDistribution extends base_installer_1.JavaBase {
|
|||||||
`release_type=${releaseType}`,
|
`release_type=${releaseType}`,
|
||||||
`jvm_impl=openj9`
|
`jvm_impl=openj9`
|
||||||
].join('&');
|
].join('&');
|
||||||
// need to iterate through all pages to retrieve the list of all versions
|
const requestArguments = `${baseRequestArguments}&page_size=20&page=0`;
|
||||||
// Adoptium API doesn't provide way to retrieve the count of pages to iterate so infinity loop
|
let availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
||||||
let page_index = 0;
|
|
||||||
const availableVersions = [];
|
const availableVersions = [];
|
||||||
while (true) {
|
let pageCount = 0;
|
||||||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
|
if (core.isDebug()) {
|
||||||
const availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||||
if (core.isDebug() && page_index === 0) {
|
}
|
||||||
// url is identical except page_index so print it once for debug
|
while (availableVersionsUrl) {
|
||||||
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
pageCount++;
|
||||||
|
const response = yield this.http.getJson(availableVersionsUrl);
|
||||||
|
const paginationPage = response.result;
|
||||||
|
const nextUrl = (0, util_1.getNextPageUrlFromLinkHeader)(response.headers);
|
||||||
|
if (nextUrl &&
|
||||||
|
!(0, util_1.validatePaginationUrl)(nextUrl, 'https://api.adoptopenjdk.net')) {
|
||||||
|
core.warning(`Ignoring pagination link with unexpected origin: ${nextUrl}`);
|
||||||
|
availableVersionsUrl = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
availableVersionsUrl = nextUrl;
|
||||||
}
|
}
|
||||||
const paginationPage = (yield this.http.getJson(availableVersionsUrl)).result;
|
|
||||||
if (paginationPage === null || paginationPage.length === 0) {
|
if (paginationPage === null || paginationPage.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
availableVersions.push(...paginationPage);
|
availableVersions.push(...paginationPage);
|
||||||
page_index++;
|
if (pageCount >= util_1.MAX_PAGINATION_PAGES) {
|
||||||
|
core.warning(`Reached pagination safeguard limit (${util_1.MAX_PAGINATION_PAGES} pages) while listing Semeru releases.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
core.startGroup('Print information about available versions');
|
core.startGroup('Print information about available versions');
|
||||||
@ -80245,24 +80265,34 @@ class TemurinDistribution extends base_installer_1.JavaBase {
|
|||||||
`release_type=${releaseType}`,
|
`release_type=${releaseType}`,
|
||||||
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
||||||
].join('&');
|
].join('&');
|
||||||
// need to iterate through all pages to retrieve the list of all versions
|
const requestArguments = `${baseRequestArguments}&page_size=20&page=0`;
|
||||||
// Adoptium API doesn't provide way to retrieve the count of pages to iterate so infinity loop
|
let availableVersionsUrl = `https://api.adoptium.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
||||||
let page_index = 0;
|
|
||||||
const availableVersions = [];
|
const availableVersions = [];
|
||||||
while (true) {
|
let pageCount = 0;
|
||||||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
|
if (core.isDebug()) {
|
||||||
const availableVersionsUrl = `https://api.adoptium.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||||
if (core.isDebug() && page_index === 0) {
|
}
|
||||||
// url is identical except page_index so print it once for debug
|
while (availableVersionsUrl) {
|
||||||
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
pageCount++;
|
||||||
|
const response = yield this.http.getJson(availableVersionsUrl);
|
||||||
|
const paginationPage = response.result;
|
||||||
|
const nextUrl = (0, util_1.getNextPageUrlFromLinkHeader)(response.headers);
|
||||||
|
if (nextUrl &&
|
||||||
|
!(0, util_1.validatePaginationUrl)(nextUrl, 'https://api.adoptium.net')) {
|
||||||
|
core.warning(`Ignoring pagination link with unexpected origin: ${nextUrl}`);
|
||||||
|
availableVersionsUrl = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
availableVersionsUrl = nextUrl;
|
||||||
}
|
}
|
||||||
const paginationPage = (yield this.http.getJson(availableVersionsUrl)).result;
|
|
||||||
if (paginationPage === null || paginationPage.length === 0) {
|
if (paginationPage === null || paginationPage.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
availableVersions.push(...paginationPage);
|
availableVersions.push(...paginationPage);
|
||||||
page_index++;
|
if (pageCount >= util_1.MAX_PAGINATION_PAGES) {
|
||||||
|
core.warning(`Reached pagination safeguard limit (${util_1.MAX_PAGINATION_PAGES} pages) while listing Temurin releases.`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
core.startGroup('Print information about available versions');
|
core.startGroup('Print information about available versions');
|
||||||
@ -80893,7 +80923,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.renameWinArchive = exports.getGitHubHttpHeaders = exports.convertVersionToSemver = exports.getVersionFromFileContent = exports.isCacheFeatureAvailable = exports.isGhes = exports.isJobStatusSuccess = exports.getToolcachePath = exports.isVersionSatisfies = exports.getDownloadArchiveExtension = exports.extractJdkFile = exports.getVersionFromToolcachePath = exports.getBooleanInput = exports.getTempDir = void 0;
|
exports.renameWinArchive = exports.validatePaginationUrl = exports.getNextPageUrlFromLinkHeader = exports.MAX_PAGINATION_PAGES = exports.getGitHubHttpHeaders = exports.convertVersionToSemver = exports.getVersionFromFileContent = exports.isCacheFeatureAvailable = exports.isGhes = exports.isJobStatusSuccess = exports.getToolcachePath = exports.isVersionSatisfies = exports.getDownloadArchiveExtension = exports.extractJdkFile = exports.getVersionFromToolcachePath = exports.getBooleanInput = exports.getTempDir = void 0;
|
||||||
const os_1 = __importDefault(__nccwpck_require__(22037));
|
const os_1 = __importDefault(__nccwpck_require__(22037));
|
||||||
const path_1 = __importDefault(__nccwpck_require__(71017));
|
const path_1 = __importDefault(__nccwpck_require__(71017));
|
||||||
const fs = __importStar(__nccwpck_require__(57147));
|
const fs = __importStar(__nccwpck_require__(57147));
|
||||||
@ -81060,6 +81090,47 @@ function getGitHubHttpHeaders() {
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
exports.getGitHubHttpHeaders = getGitHubHttpHeaders;
|
exports.getGitHubHttpHeaders = getGitHubHttpHeaders;
|
||||||
|
exports.MAX_PAGINATION_PAGES = 1000;
|
||||||
|
function getNextPageUrlFromLinkHeader(headers) {
|
||||||
|
var _a;
|
||||||
|
if (!headers) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const linkHeader = (_a = headers.link) !== null && _a !== void 0 ? _a : headers.Link;
|
||||||
|
if (!linkHeader) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const normalizedLinkHeader = Array.isArray(linkHeader)
|
||||||
|
? linkHeader.join(',')
|
||||||
|
: linkHeader;
|
||||||
|
// Split into individual link-values and find the one with rel="next"
|
||||||
|
// RFC 8288 allows rel to appear anywhere among the parameters
|
||||||
|
const linkValues = normalizedLinkHeader.split(/,(?=\s*<)/);
|
||||||
|
for (const linkValue of linkValues) {
|
||||||
|
const urlMatch = linkValue.match(/<([^>]+)>/);
|
||||||
|
if (!urlMatch)
|
||||||
|
continue;
|
||||||
|
const params = linkValue.slice(urlMatch[0].length);
|
||||||
|
// Use word boundary to match "next" as a standalone relation type
|
||||||
|
// RFC 8288 allows space-separated relation types like rel="next prev"
|
||||||
|
if (/;\s*rel="?[^"]*\bnext\b/i.test(params)) {
|
||||||
|
return urlMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
exports.getNextPageUrlFromLinkHeader = getNextPageUrlFromLinkHeader;
|
||||||
|
function validatePaginationUrl(url, allowedOrigin) {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
const allowed = new URL(allowedOrigin);
|
||||||
|
return parsed.origin === allowed.origin;
|
||||||
|
}
|
||||||
|
catch (_a) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.validatePaginationUrl = validatePaginationUrl;
|
||||||
// Rename archive to add extension because after downloading
|
// Rename archive to add extension because after downloading
|
||||||
// archive does not contain extension type and it leads to some issues
|
// archive does not contain extension type and it leads to some issues
|
||||||
// on Windows runners without PowerShell Core.
|
// on Windows runners without PowerShell Core.
|
||||||
|
|||||||
15
package-lock.json
generated
15
package-lock.json
generated
@ -6470,6 +6470,21 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml-naming": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xmlbuilder2": {
|
"node_modules/xmlbuilder2": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz",
|
||||||
|
|||||||
@ -14,9 +14,12 @@ import {
|
|||||||
} from '../base-models';
|
} from '../base-models';
|
||||||
import {
|
import {
|
||||||
extractJdkFile,
|
extractJdkFile,
|
||||||
|
getNextPageUrlFromLinkHeader,
|
||||||
getDownloadArchiveExtension,
|
getDownloadArchiveExtension,
|
||||||
isVersionSatisfies,
|
isVersionSatisfies,
|
||||||
renameWinArchive
|
renameWinArchive,
|
||||||
|
MAX_PAGINATION_PAGES,
|
||||||
|
validatePaginationUrl
|
||||||
} from '../../util';
|
} from '../../util';
|
||||||
|
|
||||||
export enum AdoptImplementation {
|
export enum AdoptImplementation {
|
||||||
@ -125,30 +128,46 @@ export class AdoptDistribution extends JavaBase {
|
|||||||
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
||||||
].join('&');
|
].join('&');
|
||||||
|
|
||||||
// need to iterate through all pages to retrieve the list of all versions
|
const requestArguments = `${baseRequestArguments}&page_size=20&page=0`;
|
||||||
// Adopt API doesn't provide way to retrieve the count of pages to iterate so infinity loop
|
let availableVersionsUrl: string | null =
|
||||||
let page_index = 0;
|
`https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
||||||
const availableVersions: IAdoptAvailableVersions[] = [];
|
const availableVersions: IAdoptAvailableVersions[] = [];
|
||||||
while (true) {
|
let pageCount = 0;
|
||||||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
|
if (core.isDebug()) {
|
||||||
const availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||||
if (core.isDebug() && page_index === 0) {
|
}
|
||||||
// url is identical except page_index so print it once for debug
|
|
||||||
core.debug(
|
|
||||||
`Gathering available versions from '${availableVersionsUrl}'`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const paginationPage = (
|
while (availableVersionsUrl) {
|
||||||
await this.http.getJson<IAdoptAvailableVersions[]>(availableVersionsUrl)
|
pageCount++;
|
||||||
).result;
|
const response =
|
||||||
|
await this.http.getJson<IAdoptAvailableVersions[]>(
|
||||||
|
availableVersionsUrl
|
||||||
|
);
|
||||||
|
const paginationPage = response.result;
|
||||||
|
const nextUrl = getNextPageUrlFromLinkHeader(response.headers);
|
||||||
|
if (
|
||||||
|
nextUrl &&
|
||||||
|
!validatePaginationUrl(nextUrl, 'https://api.adoptopenjdk.net')
|
||||||
|
) {
|
||||||
|
core.warning(
|
||||||
|
`Ignoring pagination link with unexpected origin: ${nextUrl}`
|
||||||
|
);
|
||||||
|
availableVersionsUrl = null;
|
||||||
|
} else {
|
||||||
|
availableVersionsUrl = nextUrl;
|
||||||
|
}
|
||||||
if (paginationPage === null || paginationPage.length === 0) {
|
if (paginationPage === null || paginationPage.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
availableVersions.push(...paginationPage);
|
availableVersions.push(...paginationPage);
|
||||||
page_index++;
|
|
||||||
|
if (pageCount >= MAX_PAGINATION_PAGES) {
|
||||||
|
core.warning(
|
||||||
|
`Reached pagination safeguard limit (${MAX_PAGINATION_PAGES} pages) while listing Adopt releases.`
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
|
|||||||
@ -7,9 +7,12 @@ import {
|
|||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import {
|
import {
|
||||||
extractJdkFile,
|
extractJdkFile,
|
||||||
|
getNextPageUrlFromLinkHeader,
|
||||||
getDownloadArchiveExtension,
|
getDownloadArchiveExtension,
|
||||||
isVersionSatisfies,
|
isVersionSatisfies,
|
||||||
renameWinArchive
|
renameWinArchive,
|
||||||
|
MAX_PAGINATION_PAGES,
|
||||||
|
validatePaginationUrl
|
||||||
} from '../../util';
|
} from '../../util';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as tc from '@actions/tool-cache';
|
import * as tc from '@actions/tool-cache';
|
||||||
@ -155,32 +158,46 @@ export class SemeruDistribution extends JavaBase {
|
|||||||
`jvm_impl=openj9`
|
`jvm_impl=openj9`
|
||||||
].join('&');
|
].join('&');
|
||||||
|
|
||||||
// need to iterate through all pages to retrieve the list of all versions
|
const requestArguments = `${baseRequestArguments}&page_size=20&page=0`;
|
||||||
// Adoptium API doesn't provide way to retrieve the count of pages to iterate so infinity loop
|
let availableVersionsUrl: string | null =
|
||||||
let page_index = 0;
|
`https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
||||||
const availableVersions: ISemeruAvailableVersions[] = [];
|
const availableVersions: ISemeruAvailableVersions[] = [];
|
||||||
while (true) {
|
let pageCount = 0;
|
||||||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
|
if (core.isDebug()) {
|
||||||
const availableVersionsUrl = `https://api.adoptopenjdk.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||||
if (core.isDebug() && page_index === 0) {
|
}
|
||||||
// url is identical except page_index so print it once for debug
|
|
||||||
core.debug(
|
|
||||||
`Gathering available versions from '${availableVersionsUrl}'`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const paginationPage = (
|
while (availableVersionsUrl) {
|
||||||
|
pageCount++;
|
||||||
|
const response =
|
||||||
await this.http.getJson<ISemeruAvailableVersions[]>(
|
await this.http.getJson<ISemeruAvailableVersions[]>(
|
||||||
availableVersionsUrl
|
availableVersionsUrl
|
||||||
)
|
);
|
||||||
).result;
|
const paginationPage = response.result;
|
||||||
|
const nextUrl = getNextPageUrlFromLinkHeader(response.headers);
|
||||||
|
if (
|
||||||
|
nextUrl &&
|
||||||
|
!validatePaginationUrl(nextUrl, 'https://api.adoptopenjdk.net')
|
||||||
|
) {
|
||||||
|
core.warning(
|
||||||
|
`Ignoring pagination link with unexpected origin: ${nextUrl}`
|
||||||
|
);
|
||||||
|
availableVersionsUrl = null;
|
||||||
|
} else {
|
||||||
|
availableVersionsUrl = nextUrl;
|
||||||
|
}
|
||||||
if (paginationPage === null || paginationPage.length === 0) {
|
if (paginationPage === null || paginationPage.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
availableVersions.push(...paginationPage);
|
availableVersions.push(...paginationPage);
|
||||||
page_index++;
|
|
||||||
|
if (pageCount >= MAX_PAGINATION_PAGES) {
|
||||||
|
core.warning(
|
||||||
|
`Reached pagination safeguard limit (${MAX_PAGINATION_PAGES} pages) while listing Semeru releases.`
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
|
|||||||
@ -14,9 +14,12 @@ import {
|
|||||||
} from '../base-models';
|
} from '../base-models';
|
||||||
import {
|
import {
|
||||||
extractJdkFile,
|
extractJdkFile,
|
||||||
|
getNextPageUrlFromLinkHeader,
|
||||||
getDownloadArchiveExtension,
|
getDownloadArchiveExtension,
|
||||||
isVersionSatisfies,
|
isVersionSatisfies,
|
||||||
renameWinArchive
|
renameWinArchive,
|
||||||
|
MAX_PAGINATION_PAGES,
|
||||||
|
validatePaginationUrl
|
||||||
} from '../../util';
|
} from '../../util';
|
||||||
|
|
||||||
export enum TemurinImplementation {
|
export enum TemurinImplementation {
|
||||||
@ -123,32 +126,47 @@ export class TemurinDistribution extends JavaBase {
|
|||||||
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
`jvm_impl=${this.jvmImpl.toLowerCase()}`
|
||||||
].join('&');
|
].join('&');
|
||||||
|
|
||||||
// need to iterate through all pages to retrieve the list of all versions
|
const requestArguments = `${baseRequestArguments}&page_size=20&page=0`;
|
||||||
// Adoptium API doesn't provide way to retrieve the count of pages to iterate so infinity loop
|
let availableVersionsUrl: string | null =
|
||||||
let page_index = 0;
|
`https://api.adoptium.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
||||||
const availableVersions: ITemurinAvailableVersions[] = [];
|
const availableVersions: ITemurinAvailableVersions[] = [];
|
||||||
while (true) {
|
let pageCount = 0;
|
||||||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`;
|
if (core.isDebug()) {
|
||||||
const availableVersionsUrl = `https://api.adoptium.net/v3/assets/version/${versionRange}?${requestArguments}`;
|
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
|
||||||
if (core.isDebug() && page_index === 0) {
|
}
|
||||||
// url is identical except page_index so print it once for debug
|
|
||||||
core.debug(
|
|
||||||
`Gathering available versions from '${availableVersionsUrl}'`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const paginationPage = (
|
while (availableVersionsUrl) {
|
||||||
|
pageCount++;
|
||||||
|
const response =
|
||||||
await this.http.getJson<ITemurinAvailableVersions[]>(
|
await this.http.getJson<ITemurinAvailableVersions[]>(
|
||||||
availableVersionsUrl
|
availableVersionsUrl
|
||||||
)
|
);
|
||||||
).result;
|
const paginationPage = response.result;
|
||||||
|
const nextUrl = getNextPageUrlFromLinkHeader(response.headers);
|
||||||
|
if (
|
||||||
|
nextUrl &&
|
||||||
|
!validatePaginationUrl(nextUrl, 'https://api.adoptium.net')
|
||||||
|
) {
|
||||||
|
core.warning(
|
||||||
|
`Ignoring pagination link with unexpected origin: ${nextUrl}`
|
||||||
|
);
|
||||||
|
availableVersionsUrl = null;
|
||||||
|
} else {
|
||||||
|
availableVersionsUrl = nextUrl;
|
||||||
|
}
|
||||||
|
|
||||||
if (paginationPage === null || paginationPage.length === 0) {
|
if (paginationPage === null || paginationPage.length === 0) {
|
||||||
// break infinity loop because we have reached end of pagination
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
availableVersions.push(...paginationPage);
|
availableVersions.push(...paginationPage);
|
||||||
page_index++;
|
|
||||||
|
if (pageCount >= MAX_PAGINATION_PAGES) {
|
||||||
|
core.warning(
|
||||||
|
`Reached pagination safeguard limit (${MAX_PAGINATION_PAGES} pages) while listing Temurin releases.`
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (core.isDebug()) {
|
if (core.isDebug()) {
|
||||||
|
|||||||
49
src/util.ts
49
src/util.ts
@ -201,6 +201,55 @@ export function getGitHubHttpHeaders(): OutgoingHttpHeaders {
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const MAX_PAGINATION_PAGES = 1000;
|
||||||
|
|
||||||
|
export function getNextPageUrlFromLinkHeader(
|
||||||
|
headers?: Record<string, string | string[] | undefined>
|
||||||
|
): string | null {
|
||||||
|
if (!headers) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkHeader = headers.link ?? headers.Link;
|
||||||
|
if (!linkHeader) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedLinkHeader = Array.isArray(linkHeader)
|
||||||
|
? linkHeader.join(',')
|
||||||
|
: linkHeader;
|
||||||
|
|
||||||
|
// Split into individual link-values and find the one with rel="next"
|
||||||
|
// RFC 8288 allows rel to appear anywhere among the parameters
|
||||||
|
const linkValues = normalizedLinkHeader.split(/,(?=\s*<)/);
|
||||||
|
for (const linkValue of linkValues) {
|
||||||
|
const urlMatch = linkValue.match(/<([^>]+)>/);
|
||||||
|
if (!urlMatch) continue;
|
||||||
|
|
||||||
|
const params = linkValue.slice(urlMatch[0].length);
|
||||||
|
// Use word boundary to match "next" as a standalone relation type
|
||||||
|
// RFC 8288 allows space-separated relation types like rel="next prev"
|
||||||
|
if (/;\s*rel="?[^"]*\bnext\b/i.test(params)) {
|
||||||
|
return urlMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validatePaginationUrl(
|
||||||
|
url: string,
|
||||||
|
allowedOrigin: string
|
||||||
|
): boolean {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
const allowed = new URL(allowedOrigin);
|
||||||
|
return parsed.origin === allowed.origin;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Rename archive to add extension because after downloading
|
// Rename archive to add extension because after downloading
|
||||||
// archive does not contain extension type and it leads to some issues
|
// archive does not contain extension type and it leads to some issues
|
||||||
// on Windows runners without PowerShell Core.
|
// on Windows runners without PowerShell Core.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user