From f300429fbae3ca8301ae4298bab87487a45ee00d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:12:38 -0700 Subject: [PATCH 01/57] Bump @typescript-eslint/parser from 8.48.0 to 8.61.1 (#1021) * Bump @typescript-eslint/parser from 8.48.0 to 8.61.1 Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.48.0 to 8.61.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.1/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-version: 8.61.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * run licensed and update dist --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: George Adams --- .licenses/npm/debug.dep.yml | 2 +- .licenses/npm/ms.dep.yml | 4 +- dist/cleanup/index.js | 107 ++++++++++-------- dist/setup/index.js | 107 ++++++++++-------- package-lock.json | 214 ++++++++++++++++++++++++++++++++---- package.json | 2 +- 6 files changed, 325 insertions(+), 111 deletions(-) diff --git a/.licenses/npm/debug.dep.yml b/.licenses/npm/debug.dep.yml index b9b723389..b49c69e92 100644 --- a/.licenses/npm/debug.dep.yml +++ b/.licenses/npm/debug.dep.yml @@ -1,6 +1,6 @@ --- name: debug -version: 4.3.4 +version: 4.4.3 type: npm summary: Lightweight debugging utility for Node.js and the browser homepage: diff --git a/.licenses/npm/ms.dep.yml b/.licenses/npm/ms.dep.yml index ac3c8eb15..5a303e4d6 100644 --- a/.licenses/npm/ms.dep.yml +++ b/.licenses/npm/ms.dep.yml @@ -1,6 +1,6 @@ --- name: ms -version: 2.1.2 +version: 2.1.3 type: npm summary: Tiny millisecond conversion utility homepage: @@ -10,7 +10,7 @@ licenses: text: | The MIT License (MIT) - Copyright (c) 2016 Zeit, Inc. + Copyright (c) 2020 Vercel, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index b1a86753f..4f3f4f1ae 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -18118,14 +18118,17 @@ function useColors() { return false; } + let m; + // Is webkit? http://stackoverflow.com/a/16459606/376773 // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + // eslint-disable-next-line no-return-assign return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || // Is firebug? http://stackoverflow.com/a/398120/376773 (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || // Is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + (typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31) || // Double check webkit in userAgent just in case we are in a worker (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); } @@ -18209,7 +18212,7 @@ function save(namespaces) { function load() { let r; try { - r = exports.storage.getItem('debug'); + r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG') ; } catch (error) { // Swallow // XXX (@Qix-) should we be logging these? @@ -18435,24 +18438,62 @@ function setup(env) { createDebug.names = []; createDebug.skips = []; - let i; - const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - const len = split.length; + const split = (typeof namespaces === 'string' ? namespaces : '') + .trim() + .replace(/\s+/g, ',') + .split(',') + .filter(Boolean); - for (i = 0; i < len; i++) { - if (!split[i]) { - // ignore empty strings - continue; + for (const ns of split) { + if (ns[0] === '-') { + createDebug.skips.push(ns.slice(1)); + } else { + createDebug.names.push(ns); } + } + } - namespaces = split[i].replace(/\*/g, '.*?'); - - if (namespaces[0] === '-') { - createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$')); + /** + * Checks if the given string matches a namespace template, honoring + * asterisks as wildcards. + * + * @param {String} search + * @param {String} template + * @return {Boolean} + */ + function matchesTemplate(search, template) { + let searchIndex = 0; + let templateIndex = 0; + let starIndex = -1; + let matchIndex = 0; + + while (searchIndex < search.length) { + if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) { + // Match character or proceed with wildcard + if (template[templateIndex] === '*') { + starIndex = templateIndex; + matchIndex = searchIndex; + templateIndex++; // Skip the '*' + } else { + searchIndex++; + templateIndex++; + } + } else if (starIndex !== -1) { // eslint-disable-line no-negated-condition + // Backtrack to the last '*' and try to match more characters + templateIndex = starIndex + 1; + matchIndex++; + searchIndex = matchIndex; } else { - createDebug.names.push(new RegExp('^' + namespaces + '$')); + return false; // No match } } + + // Handle trailing '*' in template + while (templateIndex < template.length && template[templateIndex] === '*') { + templateIndex++; + } + + return templateIndex === template.length; } /** @@ -18463,8 +18504,8 @@ function setup(env) { */ function disable() { const namespaces = [ - ...createDebug.names.map(toNamespace), - ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace) + ...createDebug.names, + ...createDebug.skips.map(namespace => '-' + namespace) ].join(','); createDebug.enable(''); return namespaces; @@ -18478,21 +18519,14 @@ function setup(env) { * @api public */ function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - - let i; - let len; - - for (i = 0, len = createDebug.skips.length; i < len; i++) { - if (createDebug.skips[i].test(name)) { + for (const skip of createDebug.skips) { + if (matchesTemplate(name, skip)) { return false; } } - for (i = 0, len = createDebug.names.length; i < len; i++) { - if (createDebug.names[i].test(name)) { + for (const ns of createDebug.names) { + if (matchesTemplate(name, ns)) { return true; } } @@ -18500,19 +18534,6 @@ function setup(env) { return false; } - /** - * Convert regexp to namespace - * - * @param {RegExp} regxep - * @return {String} namespace - * @api private - */ - function toNamespace(regexp) { - return regexp.toString() - .substring(2, regexp.toString().length - 2) - .replace(/\.\*\?$/, '*'); - } - /** * Coerce `val`. * @@ -18754,11 +18775,11 @@ function getDate() { } /** - * Invokes `util.format()` with the specified arguments and writes to stderr. + * Invokes `util.formatWithOptions()` with the specified arguments and writes to stderr. */ function log(...args) { - return process.stderr.write(util.format(...args) + '\n'); + return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + '\n'); } /** @@ -20338,7 +20359,7 @@ var y = d * 365.25; * @api public */ -module.exports = function(val, options) { +module.exports = function (val, options) { options = options || {}; var type = typeof val; if (type === 'string' && val.length > 0) { diff --git a/dist/setup/index.js b/dist/setup/index.js index 7a6cdebb4..f393386bb 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -39730,14 +39730,17 @@ function useColors() { return false; } + let m; + // Is webkit? http://stackoverflow.com/a/16459606/376773 // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + // eslint-disable-next-line no-return-assign return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || // Is firebug? http://stackoverflow.com/a/398120/376773 (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || // Is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + (typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31) || // Double check webkit in userAgent just in case we are in a worker (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); } @@ -39821,7 +39824,7 @@ function save(namespaces) { function load() { let r; try { - r = exports.storage.getItem('debug'); + r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG') ; } catch (error) { // Swallow // XXX (@Qix-) should we be logging these? @@ -40047,24 +40050,62 @@ function setup(env) { createDebug.names = []; createDebug.skips = []; - let i; - const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - const len = split.length; + const split = (typeof namespaces === 'string' ? namespaces : '') + .trim() + .replace(/\s+/g, ',') + .split(',') + .filter(Boolean); - for (i = 0; i < len; i++) { - if (!split[i]) { - // ignore empty strings - continue; + for (const ns of split) { + if (ns[0] === '-') { + createDebug.skips.push(ns.slice(1)); + } else { + createDebug.names.push(ns); } + } + } - namespaces = split[i].replace(/\*/g, '.*?'); - - if (namespaces[0] === '-') { - createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$')); + /** + * Checks if the given string matches a namespace template, honoring + * asterisks as wildcards. + * + * @param {String} search + * @param {String} template + * @return {Boolean} + */ + function matchesTemplate(search, template) { + let searchIndex = 0; + let templateIndex = 0; + let starIndex = -1; + let matchIndex = 0; + + while (searchIndex < search.length) { + if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) { + // Match character or proceed with wildcard + if (template[templateIndex] === '*') { + starIndex = templateIndex; + matchIndex = searchIndex; + templateIndex++; // Skip the '*' + } else { + searchIndex++; + templateIndex++; + } + } else if (starIndex !== -1) { // eslint-disable-line no-negated-condition + // Backtrack to the last '*' and try to match more characters + templateIndex = starIndex + 1; + matchIndex++; + searchIndex = matchIndex; } else { - createDebug.names.push(new RegExp('^' + namespaces + '$')); + return false; // No match } } + + // Handle trailing '*' in template + while (templateIndex < template.length && template[templateIndex] === '*') { + templateIndex++; + } + + return templateIndex === template.length; } /** @@ -40075,8 +40116,8 @@ function setup(env) { */ function disable() { const namespaces = [ - ...createDebug.names.map(toNamespace), - ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace) + ...createDebug.names, + ...createDebug.skips.map(namespace => '-' + namespace) ].join(','); createDebug.enable(''); return namespaces; @@ -40090,21 +40131,14 @@ function setup(env) { * @api public */ function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - - let i; - let len; - - for (i = 0, len = createDebug.skips.length; i < len; i++) { - if (createDebug.skips[i].test(name)) { + for (const skip of createDebug.skips) { + if (matchesTemplate(name, skip)) { return false; } } - for (i = 0, len = createDebug.names.length; i < len; i++) { - if (createDebug.names[i].test(name)) { + for (const ns of createDebug.names) { + if (matchesTemplate(name, ns)) { return true; } } @@ -40112,19 +40146,6 @@ function setup(env) { return false; } - /** - * Convert regexp to namespace - * - * @param {RegExp} regxep - * @return {String} namespace - * @api private - */ - function toNamespace(regexp) { - return regexp.toString() - .substring(2, regexp.toString().length - 2) - .replace(/\.\*\?$/, '*'); - } - /** * Coerce `val`. * @@ -40366,11 +40387,11 @@ function getDate() { } /** - * Invokes `util.format()` with the specified arguments and writes to stderr. + * Invokes `util.formatWithOptions()` with the specified arguments and writes to stderr. */ function log(...args) { - return process.stderr.write(util.format(...args) + '\n'); + return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + '\n'); } /** @@ -46064,7 +46085,7 @@ var y = d * 365.25; * @api public */ -module.exports = function(val, options) { +module.exports = function (val, options) { options = options || {}; var type = typeof val; if (type === 'string' && val.length > 0) { diff --git a/package-lock.json b/package-lock.json index c2dd5b271..491644e09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/node": "^25.9.3", "@types/semver": "^7.5.8", "@typescript-eslint/eslint-plugin": "^8.48.0", - "@typescript-eslint/parser": "^8.35.1", + "@typescript-eslint/parser": "^8.61.1", "@vercel/ncc": "^0.44.0", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8", @@ -2072,17 +2072,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.0.tgz", - "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", + "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.48.0", - "@typescript-eslint/types": "8.48.0", - "@typescript-eslint/typescript-estree": "8.48.0", - "@typescript-eslint/visitor-keys": "8.48.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2092,8 +2092,177 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", + "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.61.1", + "@typescript-eslint/types": "^8.61.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", + "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", + "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", + "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", + "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.61.1", + "@typescript-eslint/tsconfig-utils": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", + "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/project-service": { @@ -3131,11 +3300,12 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -5165,9 +5335,10 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/napi-postinstall": { "version": "0.3.4", @@ -6095,10 +6266,11 @@ "dev": true }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.12" }, diff --git a/package.json b/package.json index 89d842da6..66e0e0f71 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@types/node": "^25.9.3", "@types/semver": "^7.5.8", "@typescript-eslint/eslint-plugin": "^8.48.0", - "@typescript-eslint/parser": "^8.35.1", + "@typescript-eslint/parser": "^8.61.1", "@vercel/ncc": "^0.44.0", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8", From c9b6aee07e98596593e5709d3687092feeabe808 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:52:02 -0400 Subject: [PATCH 02/57] Fix codeql workflow permissions (#993) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7a8261238..1964b62fc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -10,5 +10,8 @@ on: jobs: call-codeQL-analysis: + permissions: + actions: read + security-events: write name: CodeQL analysis uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main From bc52a13212ed712059892c2a00fd554f25c78125 Mon Sep 17 00:00:00 2001 From: George Adams Date: Wed, 17 Jun 2026 07:58:23 -0700 Subject: [PATCH 03/57] fix CodeQL permissions (#1025) --- .github/workflows/codeql-analysis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1964b62fc..1816c150c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -12,6 +12,7 @@ jobs: call-codeQL-analysis: permissions: actions: read + contents: read security-events: write name: CodeQL analysis uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main From baa1691374336073bf9d31ab4c3ee6399dc3dcf3 Mon Sep 17 00:00:00 2001 From: Sean Proctor Date: Thu, 18 Jun 2026 05:47:02 +0200 Subject: [PATCH 04/57] fix: reject non-semver candidate versions in isVersionSatisfies (#1009) Distributions like JetBrains Runtime publish 4-segment versions such as '17.0.8.1+1080.1' that the semver package rejects. Both compareBuild and satisfies throw on these, which surfaced to users as "Error: Invalid Version: 17.0.8.1+1080.1" and aborted the whole install when any available version was non-semver. Guard with an early semver.valid check so unparseable versions are treated as a non-match. Co-authored-by: Claude Opus 4.7 (1M context) --- __tests__/util.test.ts | 6 +++++- dist/cleanup/index.js | 7 +++++++ dist/setup/index.js | 7 +++++++ src/util.ts | 8 ++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index f41d2c918..310a180ac 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -29,7 +29,11 @@ describe('isVersionSatisfies', () => { ['2.5.1+3', '2.5.1+3', true], ['2.5.1+3', '2.5.1+2', false], ['15.0.0+14', '15.0.0+14.1.202003190635', false], - ['15.0.0+14.1.202003190635', '15.0.0+14.1.202003190635', true] + ['15.0.0+14.1.202003190635', '15.0.0+14.1.202003190635', true], + // 4-segment versions (e.g. JetBrains Runtime '17.0.8.1+1080.1') are not + // valid semver — they should be rejected, not throw. + ['25.0.3+480.61', '17.0.8.1+1080.1', false], + ['17', '17.0.8.1+1080.1', false] ])( '%s, %s -> %s', (inputRange: string, inputVersion: string, expected: boolean) => { diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 4f3f4f1ae..5b4454754 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52208,6 +52208,13 @@ function getDownloadArchiveExtension() { exports.getDownloadArchiveExtension = getDownloadArchiveExtension; function isVersionSatisfies(range, version) { var _a; + // Some distributions (e.g. JetBrains Runtime) publish 4-segment versions + // like '17.0.8.1+1080.1' that semver rejects. If the candidate version + // isn't valid semver, it can't match — bail out rather than letting + // compareBuild / satisfies throw. + if (!semver.valid(version)) { + return false; + } if (semver.valid(range)) { // if full version with build digit is provided as a range (such as '1.2.3+4') // we should check for exact equal via compareBuild diff --git a/dist/setup/index.js b/dist/setup/index.js index f393386bb..8e2595766 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -81039,6 +81039,13 @@ function getDownloadArchiveExtension() { exports.getDownloadArchiveExtension = getDownloadArchiveExtension; function isVersionSatisfies(range, version) { var _a; + // Some distributions (e.g. JetBrains Runtime) publish 4-segment versions + // like '17.0.8.1+1080.1' that semver rejects. If the candidate version + // isn't valid semver, it can't match — bail out rather than letting + // compareBuild / satisfies throw. + if (!semver.valid(version)) { + return false; + } if (semver.valid(range)) { // if full version with build digit is provided as a range (such as '1.2.3+4') // we should check for exact equal via compareBuild diff --git a/src/util.ts b/src/util.ts index 5fe84c520..679c9fe34 100644 --- a/src/util.ts +++ b/src/util.ts @@ -55,6 +55,14 @@ export function getDownloadArchiveExtension() { } export function isVersionSatisfies(range: string, version: string): boolean { + // Some distributions (e.g. JetBrains Runtime) publish 4-segment versions + // like '17.0.8.1+1080.1' that semver rejects. If the candidate version + // isn't valid semver, it can't match — bail out rather than letting + // compareBuild / satisfies throw. + if (!semver.valid(version)) { + return false; + } + if (semver.valid(range)) { // if full version with build digit is provided as a range (such as '1.2.3+4') // we should check for exact equal via compareBuild From 6e9017e1258fe913cf5ad9d78c874b028aa55235 Mon Sep 17 00:00:00 2001 From: Jason Ginchereau Date: Sun, 21 Jun 2026 22:16:01 -1000 Subject: [PATCH 05/57] Bump @actions/cache to 5.1.0, handle cache write denied (#1026) --- .licenses/npm/@actions/cache.dep.yml | 2 +- __tests__/cache.test.ts | 6 ++- dist/cleanup/index.js | 57 +++++++++++++++++++++++++--- dist/setup/index.js | 57 +++++++++++++++++++++++++--- package-lock.json | 12 +++--- package.json | 4 +- src/cache.ts | 10 ++++- 7 files changed, 126 insertions(+), 22 deletions(-) diff --git a/.licenses/npm/@actions/cache.dep.yml b/.licenses/npm/@actions/cache.dep.yml index 25b3a5b13..979068018 100644 --- a/.licenses/npm/@actions/cache.dep.yml +++ b/.licenses/npm/@actions/cache.dep.yml @@ -1,6 +1,6 @@ --- name: "@actions/cache" -version: 5.0.5 +version: 5.1.0 type: npm summary: Actions cache lib homepage: https://github.com/actions/toolkit/tree/main/packages/cache diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index df7a59bd4..0fdc6344d 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -291,10 +291,12 @@ describe('dependency cache', () => { await save('maven'); expect(spyCacheSave).toHaveBeenCalled(); expect(spyWarning).not.toHaveBeenCalled(); - expect(spyInfo).toHaveBeenCalled(); - expect(spyInfo).toHaveBeenCalledWith( + expect(spyInfo).not.toHaveBeenCalledWith( expect.stringMatching(/^Cache saved with the key:.*/) ); + expect(spyDebug).toHaveBeenCalledWith( + expect.stringMatching(/^Cache was not saved for the key:.*/) + ); }); it('saves with error from toolkit, should fail workflow', async () => { diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 5b4454754..327c94701 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -49,7 +49,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.FinalizeCacheError = exports.ReserveCacheError = exports.ValidationError = void 0; +exports.FinalizeCacheError = exports.CacheWriteDeniedError = exports.CACHE_WRITE_DENIED_PREFIX = exports.ReserveCacheError = exports.ValidationError = void 0; exports.isFeatureAvailable = isFeatureAvailable; exports.restoreCache = restoreCache; exports.saveCache = saveCache; @@ -77,6 +77,26 @@ class ReserveCacheError extends Error { } } exports.ReserveCacheError = ReserveCacheError; +/** + * Stable prefix used by the cache receiver to signal that the token has + * no writable scopes (read-only cache policy). Consumers can match on + * this prefix to distinguish policy denials from ordinary contention. + */ +exports.CACHE_WRITE_DENIED_PREFIX = 'cache write denied:'; +/** + * Extends ReserveCacheError for source-compatibility: existing + * `instanceof ReserveCacheError` checks and `typedError.name === + * ReserveCacheError.name` paths keep working, while consumers that want to + * distinguish a policy denial can check for CacheWriteDeniedError.name. + */ +class CacheWriteDeniedError extends ReserveCacheError { + constructor(message) { + super(message); + this.name = 'CacheWriteDeniedError'; + Object.setPrototypeOf(this, CacheWriteDeniedError.prototype); + } +} +exports.CacheWriteDeniedError = CacheWriteDeniedError; class FinalizeCacheError extends Error { constructor(message) { super(message); @@ -387,7 +407,11 @@ function saveCacheV1(paths_1, key_1, options_1) { throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); } else { - throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); + const detailMessage = (_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message; + if (detailMessage === null || detailMessage === void 0 ? void 0 : detailMessage.startsWith(exports.CACHE_WRITE_DENIED_PREFIX)) { + throw new CacheWriteDeniedError(`Unable to reserve cache with key ${key}. More details: ${detailMessage}`); + } + throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${detailMessage}`); } core.debug(`Saving Cache (ID: ${cacheId})`); yield cacheHttpClient.saveCache(cacheId, archivePath, '', options); @@ -397,6 +421,9 @@ function saveCacheV1(paths_1, key_1, options_1) { if (typedError.name === ValidationError.name) { throw error; } + else if (typedError.name === CacheWriteDeniedError.name) { + core.warning(`Failed to save: ${typedError.message}`); + } else if (typedError.name === ReserveCacheError.name) { core.info(`Failed to save: ${typedError.message}`); } @@ -435,6 +462,7 @@ function saveCacheV1(paths_1, key_1, options_1) { */ function saveCacheV2(paths_1, key_1, options_1) { return __awaiter(this, arguments, void 0, function* (paths, key, options, enableCrossOsArchive = false) { + var _a; // Override UploadOptions to force the use of Azure // ...options goes first because we want to override the default values // set in UploadOptions with these specific figures @@ -470,7 +498,11 @@ function saveCacheV2(paths_1, key_1, options_1) { try { const response = yield twirpClient.CreateCacheEntry(request); if (!response.ok) { - if (response.message) { + // Skip the redundant inner warning when the receiver signalled a + // policy denial: the outer catch arm below will log a single + // customer-facing warning. + if (response.message && + !response.message.startsWith(exports.CACHE_WRITE_DENIED_PREFIX)) { core.warning(`Cache reservation failed: ${response.message}`); } throw new Error(response.message || 'Response was not ok'); @@ -479,6 +511,10 @@ function saveCacheV2(paths_1, key_1, options_1) { } catch (error) { core.debug(`Failed to reserve cache: ${error}`); + const errorMessage = (_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : ''; + if (errorMessage.startsWith(exports.CACHE_WRITE_DENIED_PREFIX)) { + throw new CacheWriteDeniedError(`Unable to reserve cache with key ${key}. More details: ${errorMessage}`); + } throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache.`); } core.debug(`Attempting to upload cache located at: ${archivePath}`); @@ -503,6 +539,9 @@ function saveCacheV2(paths_1, key_1, options_1) { if (typedError.name === ValidationError.name) { throw error; } + else if (typedError.name === CacheWriteDeniedError.name) { + core.warning(`Failed to save: ${typedError.message}`); + } else if (typedError.name === ReserveCacheError.name) { core.info(`Failed to save: ${typedError.message}`); } @@ -51849,7 +51888,15 @@ function save(id) { return; } try { - yield cache.saveCache(packageManager.path, primaryKey); + const cacheId = yield cache.saveCache(packageManager.path, primaryKey); + if (cacheId === -1) { + // saveCache returns -1 without throwing when the cache was not saved, + // e.g. a reserve collision or a read-only token (fork PR). @actions/cache + // has already logged the reason at the appropriate severity, so just + // trace it instead of misreporting that the cache was saved. + core.debug(`Cache was not saved for the key: ${primaryKey}`); + return; + } core.info(`Cache saved with the key: ${primaryKey}`); } catch (error) { @@ -93808,7 +93855,7 @@ function randomUUID() { /***/ ((module) => { "use strict"; -module.exports = /*#__PURE__*/JSON.parse('{"name":"@actions/cache","version":"5.0.5","preview":true,"description":"Actions cache lib","keywords":["github","actions","cache"],"homepage":"https://github.com/actions/toolkit/tree/main/packages/cache","license":"MIT","main":"lib/cache.js","types":"lib/cache.d.ts","directories":{"lib":"lib","test":"__tests__"},"files":["lib","!.DS_Store"],"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/actions/toolkit.git","directory":"packages/cache"},"scripts":{"audit-moderate":"npm install && npm audit --json --audit-level=moderate > audit.json","test":"echo \\"Error: run tests from root\\" && exit 1","tsc":"tsc"},"bugs":{"url":"https://github.com/actions/toolkit/issues"},"dependencies":{"@actions/core":"^2.0.0","@actions/exec":"^2.0.0","@actions/glob":"^0.5.1","@protobuf-ts/runtime-rpc":"^2.11.1","@actions/http-client":"^3.0.2","@actions/io":"^2.0.0","@azure/abort-controller":"^1.1.0","@azure/core-rest-pipeline":"^1.22.0","@azure/storage-blob":"^12.29.1","semver":"^6.3.1"},"devDependencies":{"@types/node":"^24.1.0","@types/semver":"^6.0.0","@protobuf-ts/plugin":"^2.9.4","typescript":"^5.2.2"},"overrides":{"uri-js":"npm:uri-js-replace@^1.0.1","node-fetch":"^3.3.2"}}'); +module.exports = /*#__PURE__*/JSON.parse('{"name":"@actions/cache","version":"5.1.0","preview":true,"description":"Actions cache lib","keywords":["github","actions","cache"],"homepage":"https://github.com/actions/toolkit/tree/main/packages/cache","license":"MIT","main":"lib/cache.js","types":"lib/cache.d.ts","directories":{"lib":"lib","test":"__tests__"},"files":["lib","!.DS_Store"],"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/actions/toolkit.git","directory":"packages/cache"},"scripts":{"audit-moderate":"npm install && npm audit --json --audit-level=moderate > audit.json","test":"echo \\"Error: run tests from root\\" && exit 1","tsc":"tsc"},"bugs":{"url":"https://github.com/actions/toolkit/issues"},"dependencies":{"@actions/core":"^2.0.0","@actions/exec":"^2.0.0","@actions/glob":"^0.5.1","@protobuf-ts/runtime-rpc":"^2.11.1","@actions/http-client":"^3.0.2","@actions/io":"^2.0.0","@azure/abort-controller":"^1.1.0","@azure/core-rest-pipeline":"^1.22.0","@azure/storage-blob":"^12.29.1","semver":"^6.3.1"},"devDependencies":{"@types/node":"^24.1.0","@types/semver":"^6.0.0","@protobuf-ts/plugin":"^2.9.4","typescript":"^5.2.2"},"overrides":{"uri-js":"npm:uri-js-replace@^1.0.1","node-fetch":"^3.3.2"}}'); /***/ }) diff --git a/dist/setup/index.js b/dist/setup/index.js index 8e2595766..f48cde261 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -49,7 +49,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.FinalizeCacheError = exports.ReserveCacheError = exports.ValidationError = void 0; +exports.FinalizeCacheError = exports.CacheWriteDeniedError = exports.CACHE_WRITE_DENIED_PREFIX = exports.ReserveCacheError = exports.ValidationError = void 0; exports.isFeatureAvailable = isFeatureAvailable; exports.restoreCache = restoreCache; exports.saveCache = saveCache; @@ -77,6 +77,26 @@ class ReserveCacheError extends Error { } } exports.ReserveCacheError = ReserveCacheError; +/** + * Stable prefix used by the cache receiver to signal that the token has + * no writable scopes (read-only cache policy). Consumers can match on + * this prefix to distinguish policy denials from ordinary contention. + */ +exports.CACHE_WRITE_DENIED_PREFIX = 'cache write denied:'; +/** + * Extends ReserveCacheError for source-compatibility: existing + * `instanceof ReserveCacheError` checks and `typedError.name === + * ReserveCacheError.name` paths keep working, while consumers that want to + * distinguish a policy denial can check for CacheWriteDeniedError.name. + */ +class CacheWriteDeniedError extends ReserveCacheError { + constructor(message) { + super(message); + this.name = 'CacheWriteDeniedError'; + Object.setPrototypeOf(this, CacheWriteDeniedError.prototype); + } +} +exports.CacheWriteDeniedError = CacheWriteDeniedError; class FinalizeCacheError extends Error { constructor(message) { super(message); @@ -387,7 +407,11 @@ function saveCacheV1(paths_1, key_1, options_1) { throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); } else { - throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); + const detailMessage = (_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message; + if (detailMessage === null || detailMessage === void 0 ? void 0 : detailMessage.startsWith(exports.CACHE_WRITE_DENIED_PREFIX)) { + throw new CacheWriteDeniedError(`Unable to reserve cache with key ${key}. More details: ${detailMessage}`); + } + throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${detailMessage}`); } core.debug(`Saving Cache (ID: ${cacheId})`); yield cacheHttpClient.saveCache(cacheId, archivePath, '', options); @@ -397,6 +421,9 @@ function saveCacheV1(paths_1, key_1, options_1) { if (typedError.name === ValidationError.name) { throw error; } + else if (typedError.name === CacheWriteDeniedError.name) { + core.warning(`Failed to save: ${typedError.message}`); + } else if (typedError.name === ReserveCacheError.name) { core.info(`Failed to save: ${typedError.message}`); } @@ -435,6 +462,7 @@ function saveCacheV1(paths_1, key_1, options_1) { */ function saveCacheV2(paths_1, key_1, options_1) { return __awaiter(this, arguments, void 0, function* (paths, key, options, enableCrossOsArchive = false) { + var _a; // Override UploadOptions to force the use of Azure // ...options goes first because we want to override the default values // set in UploadOptions with these specific figures @@ -470,7 +498,11 @@ function saveCacheV2(paths_1, key_1, options_1) { try { const response = yield twirpClient.CreateCacheEntry(request); if (!response.ok) { - if (response.message) { + // Skip the redundant inner warning when the receiver signalled a + // policy denial: the outer catch arm below will log a single + // customer-facing warning. + if (response.message && + !response.message.startsWith(exports.CACHE_WRITE_DENIED_PREFIX)) { core.warning(`Cache reservation failed: ${response.message}`); } throw new Error(response.message || 'Response was not ok'); @@ -479,6 +511,10 @@ function saveCacheV2(paths_1, key_1, options_1) { } catch (error) { core.debug(`Failed to reserve cache: ${error}`); + const errorMessage = (_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : ''; + if (errorMessage.startsWith(exports.CACHE_WRITE_DENIED_PREFIX)) { + throw new CacheWriteDeniedError(`Unable to reserve cache with key ${key}. More details: ${errorMessage}`); + } throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache.`); } core.debug(`Attempting to upload cache located at: ${archivePath}`); @@ -503,6 +539,9 @@ function saveCacheV2(paths_1, key_1, options_1) { if (typedError.name === ValidationError.name) { throw error; } + else if (typedError.name === CacheWriteDeniedError.name) { + core.warning(`Failed to save: ${typedError.message}`); + } else if (typedError.name === ReserveCacheError.name) { core.info(`Failed to save: ${typedError.message}`); } @@ -77713,7 +77752,15 @@ function save(id) { return; } try { - yield cache.saveCache(packageManager.path, primaryKey); + const cacheId = yield cache.saveCache(packageManager.path, primaryKey); + if (cacheId === -1) { + // saveCache returns -1 without throwing when the cache was not saved, + // e.g. a reserve collision or a read-only token (fork PR). @actions/cache + // has already logged the reason at the appropriate severity, so just + // trace it instead of misreporting that the cache was saved. + core.debug(`Cache was not saved for the key: ${primaryKey}`); + return; + } core.info(`Cache saved with the key: ${primaryKey}`); } catch (error) { @@ -128099,7 +128146,7 @@ Object.defineProperty(exports, "YAMLWriter", ({ enumerable: true, get: function /***/ ((module) => { "use strict"; -module.exports = /*#__PURE__*/JSON.parse('{"name":"@actions/cache","version":"5.0.5","preview":true,"description":"Actions cache lib","keywords":["github","actions","cache"],"homepage":"https://github.com/actions/toolkit/tree/main/packages/cache","license":"MIT","main":"lib/cache.js","types":"lib/cache.d.ts","directories":{"lib":"lib","test":"__tests__"},"files":["lib","!.DS_Store"],"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/actions/toolkit.git","directory":"packages/cache"},"scripts":{"audit-moderate":"npm install && npm audit --json --audit-level=moderate > audit.json","test":"echo \\"Error: run tests from root\\" && exit 1","tsc":"tsc"},"bugs":{"url":"https://github.com/actions/toolkit/issues"},"dependencies":{"@actions/core":"^2.0.0","@actions/exec":"^2.0.0","@actions/glob":"^0.5.1","@protobuf-ts/runtime-rpc":"^2.11.1","@actions/http-client":"^3.0.2","@actions/io":"^2.0.0","@azure/abort-controller":"^1.1.0","@azure/core-rest-pipeline":"^1.22.0","@azure/storage-blob":"^12.29.1","semver":"^6.3.1"},"devDependencies":{"@types/node":"^24.1.0","@types/semver":"^6.0.0","@protobuf-ts/plugin":"^2.9.4","typescript":"^5.2.2"},"overrides":{"uri-js":"npm:uri-js-replace@^1.0.1","node-fetch":"^3.3.2"}}'); +module.exports = /*#__PURE__*/JSON.parse('{"name":"@actions/cache","version":"5.1.0","preview":true,"description":"Actions cache lib","keywords":["github","actions","cache"],"homepage":"https://github.com/actions/toolkit/tree/main/packages/cache","license":"MIT","main":"lib/cache.js","types":"lib/cache.d.ts","directories":{"lib":"lib","test":"__tests__"},"files":["lib","!.DS_Store"],"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/actions/toolkit.git","directory":"packages/cache"},"scripts":{"audit-moderate":"npm install && npm audit --json --audit-level=moderate > audit.json","test":"echo \\"Error: run tests from root\\" && exit 1","tsc":"tsc"},"bugs":{"url":"https://github.com/actions/toolkit/issues"},"dependencies":{"@actions/core":"^2.0.0","@actions/exec":"^2.0.0","@actions/glob":"^0.5.1","@protobuf-ts/runtime-rpc":"^2.11.1","@actions/http-client":"^3.0.2","@actions/io":"^2.0.0","@azure/abort-controller":"^1.1.0","@azure/core-rest-pipeline":"^1.22.0","@azure/storage-blob":"^12.29.1","semver":"^6.3.1"},"devDependencies":{"@types/node":"^24.1.0","@types/semver":"^6.0.0","@protobuf-ts/plugin":"^2.9.4","typescript":"^5.2.2"},"overrides":{"uri-js":"npm:uri-js-replace@^1.0.1","node-fetch":"^3.3.2"}}'); /***/ }) diff --git a/package-lock.json b/package-lock.json index 491644e09..7bc4b8e43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "setup-java", - "version": "5.2.0", + "version": "5.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "setup-java", - "version": "5.2.0", + "version": "5.3.0", "license": "MIT", "dependencies": { - "@actions/cache": "^5.0.5", + "@actions/cache": "^5.1.0", "@actions/core": "^2.0.3", "@actions/exec": "^2.0.0", "@actions/glob": "^0.5.1", @@ -50,9 +50,9 @@ } }, "node_modules/@actions/cache": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.0.5.tgz", - "integrity": "sha512-jiQSg0gfd+C2KPgcmdCOq7dCuCIQQWQ4b1YfGIRaaA9w7PJbRwTOcCz4LiFEUnqZGf0ha/8OKL3BeNwetHzYsQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-5.1.0.tgz", + "integrity": "sha512-kTIj4YPrjjRPKSGlj7f8eq+Pijoy/SKBEbJcAwNsQTFGEF29NGqj1mqD02/PmhV6r4bRAixycexAWpmUJ2aCwg==", "license": "MIT", "dependencies": { "@actions/core": "^2.0.0", diff --git a/package.json b/package.json index 66e0e0f71..2ff28b9cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "setup-java", - "version": "5.2.0", + "version": "5.3.0", "private": true, "description": "setup java action", "main": "dist/setup/index.js", @@ -29,7 +29,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@actions/cache": "^5.0.5", + "@actions/cache": "^5.1.0", "@actions/core": "^2.0.3", "@actions/exec": "^2.0.0", "@actions/glob": "^0.5.1", diff --git a/src/cache.ts b/src/cache.ts index 7d13839ee..bb6941402 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -146,7 +146,15 @@ export async function save(id: string) { return; } try { - await cache.saveCache(packageManager.path, primaryKey); + const cacheId = await cache.saveCache(packageManager.path, primaryKey); + if (cacheId === -1) { + // saveCache returns -1 without throwing when the cache was not saved, + // e.g. a reserve collision or a read-only token (fork PR). @actions/cache + // has already logged the reason at the appropriate severity, so just + // trace it instead of misreporting that the cache was saved. + core.debug(`Cache was not saved for the key: ${primaryKey}`); + return; + } core.info(`Cache saved with the key: ${primaryKey}`); } catch (error) { const err = error as Error; From ce7f9ce6210f41b654dbe3fd4565ad91f0349eb4 Mon Sep 17 00:00:00 2001 From: mahabaleshwars <147705296+mahabaleshwars@users.noreply.github.com> Date: Mon, 22 Jun 2026 20:15:18 +0530 Subject: [PATCH 06/57] Add Maven Wrapper cache feature (#1027) * add Maven Wrapper distribution caching * update test case --------- Co-authored-by: Bruno Borges --- README.md | 2 +- __tests__/cache.test.ts | 39 ++++++++++++++++++++++++++++++++++----- dist/cleanup/index.js | 7 +++++-- dist/setup/index.js | 7 +++++-- src/cache.ts | 7 +++++-- 5 files changed, 50 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8a7392a9f..ec24882f0 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Currently, the following distributions are supported: The action has a built-in functionality for caching and restoring dependencies. It uses [toolkit/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under hood for caching dependencies but requires less configuration settings. Supported package managers are gradle, maven and sbt. The format of the used cache key is `setup-java-${{ platform }}-${{ packageManager }}-${{ fileHash }}`, where the hash is based on the following files: - gradle: `**/*.gradle*`, `**/gradle-wrapper.properties`, `buildSrc/**/Versions.kt`, `buildSrc/**/Dependencies.kt`, `gradle/*.versions.toml`, and `**/versions.properties` -- maven: `**/pom.xml` +- maven: `**/pom.xml` and `**/.mvn/wrapper/maven-wrapper.properties` - sbt: all sbt build definition files `**/*.sbt`, `**/project/build.properties`, `**/project/**.scala`, `**/project/**.sbt` When the option `cache-dependency-path` is specified, the hash is based on the matching file. This option supports wildcards and a list of file names, and is especially useful for monorepos. diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index 0fdc6344d..8a56ef608 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -96,19 +96,48 @@ describe('dependency cache', () => { }); describe('for maven', () => { - it('throws error if no pom.xml found', async () => { + it('throws error if no pom.xml or maven-wrapper.properties found', async () => { await expect(restore('maven', '')).rejects.toThrow( `No file in ${projectRoot( workspace - )} matched to [**/pom.xml], make sure you have checked out the target repository` + )} matched to [**/pom.xml,**/.mvn/wrapper/maven-wrapper.properties], make sure you have checked out the target repository` ); }); - it('downloads cache', async () => { + it('downloads cache based on pom.xml', async () => { createFile(join(workspace, 'pom.xml')); await restore('maven', ''); - expect(spyCacheRestore).toHaveBeenCalled(); - expect(spyGlobHashFiles).toHaveBeenCalledWith('**/pom.xml'); + expect(spyCacheRestore).toHaveBeenCalledWith( + [ + join(os.homedir(), '.m2', 'repository'), + join(os.homedir(), '.m2', 'wrapper', 'dists') + ], + expect.any(String) + ); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/pom.xml\n**/.mvn/wrapper/maven-wrapper.properties' + ); + expect(spyWarning).not.toHaveBeenCalled(); + expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); + }); + it('downloads cache based on maven-wrapper.properties', async () => { + createDirectory(join(workspace, '.mvn')); + createDirectory(join(workspace, '.mvn', 'wrapper')); + createFile( + join(workspace, '.mvn', 'wrapper', 'maven-wrapper.properties') + ); + + await restore('maven', ''); + expect(spyCacheRestore).toHaveBeenCalledWith( + [ + join(os.homedir(), '.m2', 'repository'), + join(os.homedir(), '.m2', 'wrapper', 'dists') + ], + expect.any(String) + ); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/pom.xml\n**/.mvn/wrapper/maven-wrapper.properties' + ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); }); diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 327c94701..5a1db9633 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -51773,9 +51773,12 @@ const CACHE_KEY_PREFIX = 'setup-java'; const supportedPackageManager = [ { id: 'maven', - path: [(0, path_1.join)(os_1.default.homedir(), '.m2', 'repository')], + path: [ + (0, path_1.join)(os_1.default.homedir(), '.m2', 'repository'), + (0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists') + ], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven - pattern: ['**/pom.xml'] + pattern: ['**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties'] }, { id: 'gradle', diff --git a/dist/setup/index.js b/dist/setup/index.js index f48cde261..434039045 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -77637,9 +77637,12 @@ const CACHE_KEY_PREFIX = 'setup-java'; const supportedPackageManager = [ { id: 'maven', - path: [(0, path_1.join)(os_1.default.homedir(), '.m2', 'repository')], + path: [ + (0, path_1.join)(os_1.default.homedir(), '.m2', 'repository'), + (0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists') + ], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven - pattern: ['**/pom.xml'] + pattern: ['**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties'] }, { id: 'gradle', diff --git a/src/cache.ts b/src/cache.ts index bb6941402..f91f90c74 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -23,9 +23,12 @@ interface PackageManager { const supportedPackageManager: PackageManager[] = [ { id: 'maven', - path: [join(os.homedir(), '.m2', 'repository')], + path: [ + join(os.homedir(), '.m2', 'repository'), + join(os.homedir(), '.m2', 'wrapper', 'dists') + ], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven - pattern: ['**/pom.xml'] + pattern: ['**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties'] }, { id: 'gradle', From 957ad8b43eeab7afa49f1adbe12cc02aa3e3fd8f Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 22 Jun 2026 10:57:54 -0400 Subject: [PATCH 07/57] Spelling (#713) * spelling: aarch Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: cannot Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: guaranteed Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: its Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: macos Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: on the fly Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: warn/fail Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: more information about ADRs Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Distribution / Official site Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: License Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --------- Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Co-authored-by: Bruno Borges --- README.md | 32 +++++++++---------- __tests__/cleanup-java.test.ts | 2 +- __tests__/data/zulu-windows.json | 2 +- .../distributors/dragonwell-installer.test.ts | 2 +- .../distributors/local-installer.test.ts | 4 +-- .../distributors/sapmachine-installer.test.ts | 2 +- docs/adrs/0000-v2-setup-java.md | 2 +- docs/adrs/README.md | 2 +- docs/advanced-usage.md | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index ec24882f0..21d4e826d 100644 --- a/README.md +++ b/README.md @@ -105,21 +105,21 @@ The `java-version` input supports an exact version or a version range using [Sem #### Supported distributions Currently, the following distributions are supported: -| Keyword | Distribution | Official site | License -|-|-|-|-| -| `temurin` | Eclipse Temurin | [Link](https://adoptium.net/) | [Link](https://adoptium.net/about.html) -| `zulu` | Azul Zulu OpenJDK | [Link](https://www.azul.com/downloads/zulu-community/?package=jdk) | [Link](https://www.azul.com/products/zulu-and-zulu-enterprise/zulu-terms-of-use/) | -| `adopt` or `adopt-hotspot` | AdoptOpenJDK Hotspot | [Link](https://adoptopenjdk.net/) | [Link](https://adoptopenjdk.net/about.html) | -| `adopt-openj9` | AdoptOpenJDK OpenJ9 | [Link](https://adoptopenjdk.net/) | [Link](https://adoptopenjdk.net/about.html) | -| `liberica` | Liberica JDK | [Link](https://bell-sw.com/) | [Link](https://bell-sw.com/liberica_eula/) | -| `microsoft` | Microsoft Build of OpenJDK | [Link](https://www.microsoft.com/openjdk) | [Link](https://docs.microsoft.com/java/openjdk/faq) -| `corretto` | Amazon Corretto Build of OpenJDK | [Link](https://aws.amazon.com/corretto/) | [Link](https://aws.amazon.com/corretto/faqs/) -| `semeru` | IBM Semeru Runtime Open Edition | [Link](https://developer.ibm.com/languages/java/semeru-runtimes/downloads/) | [Link](https://openjdk.java.net/legal/gplv2+ce.html) | -| `oracle` | Oracle JDK | [Link](https://www.oracle.com/java/technologies/downloads/) | [Link](https://java.com/freeuselicense) -| `dragonwell` | Alibaba Dragonwell JDK | [Link](https://dragonwell-jdk.io/) | [Link](https://www.aliyun.com/product/dragonwell/) -| `sapmachine` | SAP SapMachine JDK/JRE | [Link](https://sapmachine.io/) | [Link](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE) -| `graalvm` | Oracle GraalVM | [Link](https://www.graalvm.org/) | [Link](https://www.oracle.com/downloads/licenses/graal-free-license.html) -| `jetbrains` | JetBrains Runtime | [Link](https://github.com/JetBrains/JetBrainsRuntime/) | [Link](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE) +| Keyword | Distribution / Official site | License +|-|-|-| +| `temurin` | [Eclipse Temurin](https://adoptium.net/) | [`temurin` license](https://adoptium.net/about.html) +| `zulu` | [Azul Zulu OpenJDK](https://www.azul.com/downloads/zulu-community/?package=jdk) | [`zulu` license](https://www.azul.com/products/zulu-and-zulu-enterprise/zulu-terms-of-use/) | +| `adopt` or `adopt-hotspot` | [AdoptOpenJDK Hotspot](https://adoptopenjdk.net/) | [`adopt-hotspot` license](https://adoptopenjdk.net/about.html) | +| `adopt-openj9` | [AdoptOpenJDK OpenJ9](https://adoptopenjdk.net/) | [`adopt-openj9` license](https://adoptopenjdk.net/about.html) | +| `liberica` | [Liberica JDK](https://bell-sw.com/) | [`liberica` license](https://bell-sw.com/liberica_eula/) | +| `microsoft` | [Microsoft Build of OpenJDK](https://www.microsoft.com/openjdk) | [`microsoft` license](https://docs.microsoft.com/java/openjdk/faq) +| `corretto` | [Amazon Corretto Build of OpenJDK](https://aws.amazon.com/corretto/) | [`corretto` license](https://aws.amazon.com/corretto/faqs/) +| `semeru` | [IBM Semeru Runtime Open Edition](https://developer.ibm.com/languages/java/semeru-runtimes/downloads/) | [`semeru` license](https://openjdk.java.net/legal/gplv2+ce.html) | +| `oracle` | [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) | [`oracle` license](https://java.com/freeuselicense) +| `dragonwell` | [Alibaba Dragonwell JDK](https://dragonwell-jdk.io/) | [`dragonwell` license](https://www.aliyun.com/product/dragonwell/) +| `sapmachine` | [SAP SapMachine JDK/JRE](https://sapmachine.io/) | [`sapmachine` license](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE) +| `graalvm` | [Oracle GraalVM](https://www.graalvm.org/) | [`graalvm` license](https://www.oracle.com/downloads/licenses/graal-free-license.html) +| `jetbrains` | [JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/) | [`jetbrains` license](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE) **NOTE:** The different distributors can provide discrepant list of available versions / supported configurations. Please refer to the official documentation to see the list of supported versions. @@ -218,7 +218,7 @@ In the basic examples above, the `check-latest` flag defaults to `false`. When s If `check-latest` is set to `true`, the action first checks if the cached version is the latest one. If the locally cached version is not the most up-to-date, the latest version of Java will be downloaded. Set `check-latest` to `true` if you want the most up-to-date version of Java to always be used. Setting `check-latest` to `true` has performance implications as downloading versions of Java is slower than using cached versions. -For Java distributions that are not cached on Hosted images, `check-latest` always behaves as `true` and downloads Java on-flight. Check out [Hosted Tool Cache](docs/advanced-usage.md#Hosted-Tool-Cache) for more details about pre-cached Java versions. +For Java distributions that are not cached on Hosted images, `check-latest` always behaves as `true` and downloads Java on the fly. Check out [Hosted Tool Cache](docs/advanced-usage.md#Hosted-Tool-Cache) for more details about pre-cached Java versions. ```yaml diff --git a/__tests__/cleanup-java.test.ts b/__tests__/cleanup-java.test.ts index 4cd2709d8..bb988b3c9 100644 --- a/__tests__/cleanup-java.test.ts +++ b/__tests__/cleanup-java.test.ts @@ -39,7 +39,7 @@ describe('cleanup', () => { jest.restoreAllMocks(); }); - it('does not fail nor warn even when the save process throws a ReserveCacheError', async () => { + it('does not warn/fail even when the save process throws a ReserveCacheError', async () => { spyCacheSave.mockImplementation((paths: string[], key: string) => Promise.reject( new cache.ReserveCacheError( diff --git a/__tests__/data/zulu-windows.json b/__tests__/data/zulu-windows.json index e4ce99538..0ec1a0df0 100644 --- a/__tests__/data/zulu-windows.json +++ b/__tests__/data/zulu-windows.json @@ -247,7 +247,7 @@ { "id": 12446, "url": "https://cdn.azul.com/zulu/bin/zulu17.48.15-ca-jdk17.0.10-windows_aarch64.zip", - "name": "zulu17.48.15-ca-jdk17.0.10-win_aarhc4.zip", + "name": "zulu17.48.15-ca-jdk17.0.10-win_aarch4.zip", "zulu_version": [17, 48, 15, 0], "jdk_version": [17, 0, 10, 7] } diff --git a/__tests__/distributors/dragonwell-installer.test.ts b/__tests__/distributors/dragonwell-installer.test.ts index a10f75a9c..dffadde48 100644 --- a/__tests__/distributors/dragonwell-installer.test.ts +++ b/__tests__/distributors/dragonwell-installer.test.ts @@ -225,7 +225,7 @@ describe('getAvailableVersions', () => { ['11', 'macos', 'aarch64'], ['17', 'linux', 'riscv'] ])( - 'should throw when required version of JDK can not be found in the JSON', + 'should throw when required version of JDK cannot be found in the JSON', async (jdkVersion: string, platform: string, arch: string) => { const distribution = new DragonwellDistribution({ version: jdkVersion, diff --git a/__tests__/distributors/local-installer.test.ts b/__tests__/distributors/local-installer.test.ts index 201d1d254..5486ba81a 100644 --- a/__tests__/distributors/local-installer.test.ts +++ b/__tests__/distributors/local-installer.test.ts @@ -219,7 +219,7 @@ describe('setupJava', () => { ); }); - it('java is resolved from toolcache including Contents/Home on MacOS', async () => { + it('java is resolved from toolcache including Contents/Home on macOS', async () => { const inputs = { version: actualJavaVersion, architecture: 'x86', @@ -262,7 +262,7 @@ describe('setupJava', () => { }); }); - it('java is unpacked from jdkfile including Contents/Home on MacOS', async () => { + it('java is unpacked from jdkfile including Contents/Home on macOS', async () => { const inputs = { version: '11.0.289', architecture: 'x86', diff --git a/__tests__/distributors/sapmachine-installer.test.ts b/__tests__/distributors/sapmachine-installer.test.ts index f49189a70..8f1e2b27d 100644 --- a/__tests__/distributors/sapmachine-installer.test.ts +++ b/__tests__/distributors/sapmachine-installer.test.ts @@ -254,7 +254,7 @@ describe('getAvailableVersions', () => { ['21.0.3+8-ea', 'linux', 'x64', '21.0.3+8'], ['17', 'linux-muse', 'aarch64'] ])( - 'should throw when required version of JDK can not be found in the JSON', + 'should throw when required version of JDK cannot be found in the JSON', async ( version: string, platform: string, diff --git a/docs/adrs/0000-v2-setup-java.md b/docs/adrs/0000-v2-setup-java.md index 2d0c170d3..c8ea4502c 100644 --- a/docs/adrs/0000-v2-setup-java.md +++ b/docs/adrs/0000-v2-setup-java.md @@ -34,7 +34,7 @@ Requiring a default version will break users that are pinned to `@main` as they `setup-java` should be structured in such a way that will allow the open source community to easily add support for extra distributions. -Existing code will be restructured so that distribution specific code will be easily separated. Currently the core download logic is in a single file, `installer.ts`. This file will be split up and abstracted out so that there will be no vendor specified logic. Each distribution will have it's own files under `src/distributions` that will contain the core setup logic for a specific distribution. +Existing code will be restructured so that distribution specific code will be easily separated. Currently the core download logic is in a single file, `installer.ts`. This file will be split up and abstracted out so that there will be no vendor specified logic. Each distribution will have its own files under `src/distributions` that will contain the core setup logic for a specific distribution. ```yaml ∟ src/ diff --git a/docs/adrs/README.md b/docs/adrs/README.md index f23a8f723..e76d1d05e 100644 --- a/docs/adrs/README.md +++ b/docs/adrs/README.md @@ -16,4 +16,4 @@ This folder includes ADRs for the setup-java action. ADRs are proposed in the fo --- -- More information about ADRs can be found [here](https://github.com/joelparkerhenderson/architecture_decision_record). \ No newline at end of file +- See [more information about ADRs](https://github.com/joelparkerhenderson/architecture_decision_record). \ No newline at end of file diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index b4035c6e6..1b1e4feea 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -176,7 +176,7 @@ steps: **NOTE:** JetBrains is only available for LTS versions on 11 or later (11, 17, 21, etc.). -Not all minor LTS versions are guarenteed to be available, since JetBrains considers what to ship IntelliJ IDEA with, most commonly on JDK 11. +Not all minor LTS versions are guaranteed to be available, since JetBrains considers what to ship IntelliJ IDEA with, most commonly on JDK 11. For example, `11.0.24` is not available but `11.0.16` is. ```yaml From bb8b13a4a55692ebdd06b667de8f32fa5db2b994 Mon Sep 17 00:00:00 2001 From: Robert Stoll Date: Mon, 22 Jun 2026 17:13:36 +0200 Subject: [PATCH 08/57] add link to advanced configuration for JetBrains (#850) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 21d4e826d..e06c9c75b 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,7 @@ In the example above multiple JDKs are installed for the same job. The result af - [Alibaba Dragonwell](docs/advanced-usage.md#Alibaba-Dragonwell) - [SapMachine](docs/advanced-usage.md#SapMachine) - [GraalVM](docs/advanced-usage.md#GraalVM) + - [JetBrains](docs/advanced-usage.md#JetBrains) - [Installing custom Java package type](docs/advanced-usage.md#Installing-custom-Java-package-type) - [Installing custom Java architecture](docs/advanced-usage.md#Installing-custom-Java-architecture) - [Installing custom Java distribution from local file](docs/advanced-usage.md#Installing-Java-from-local-file) From 2872526dc6aa914d4002d818cd5d0df2b9de8c17 Mon Sep 17 00:00:00 2001 From: Kranthi Poturaju Date: Mon, 22 Jun 2026 20:47:16 +0530 Subject: [PATCH 09/57] docs(action): fix missing required or default fields (#1007) - Add required: false to java-version, java-version-file, job-status, and token, which had defaults or were optional but lacked the explicit flag - Add default: '' to gpg-private-key to match its stated description - Fix java-version-file description: the input accepts .java-version, .tool-versions, and .sdkmanrc, not only .java-version - Fix gpg-passphrase description: GPG_PASSPHRASE is only defaulted when gpg-private-key is provided, not unconditionally Co-authored-by: Kranthi Poturaju Co-authored-by: Panuganti Saketh Co-authored-by: Bruno Borges --- action.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/action.yml b/action.yml index 21a4269d7..d5f46bbed 100644 --- a/action.yml +++ b/action.yml @@ -5,8 +5,10 @@ author: 'GitHub' inputs: java-version: description: 'The Java version to set up. Takes a whole or semver Java version. See examples of supported syntax in README file' + required: false java-version-file: - description: 'The path to the `.java-version` file. See examples of supported syntax in README file' + description: 'The path to a file containing the Java version to set up (.java-version, .tool-versions, .sdkmanrc). Used when java-version is not set. See examples of supported syntax in README file' + required: false distribution: description: 'Java distribution. See the list of supported distributions in README file' required: true @@ -49,9 +51,9 @@ inputs: gpg-private-key: description: 'GPG private key to import. Default is empty string.' required: false + default: '' gpg-passphrase: - description: 'Environment variable name for the GPG private key passphrase. Default is - $GPG_PASSPHRASE.' + description: 'Environment variable name for the GPG private key passphrase. Defaults to GPG_PASSPHRASE when gpg-private-key is set; ignored otherwise.' required: false cache: description: 'Name of the build platform to cache dependencies. It can be "maven", "gradle" or "sbt".' @@ -61,9 +63,11 @@ inputs: required: false job-status: description: 'Workaround to pass job status to post job step. This variable is not intended for manual setting' + required: false default: ${{ job.status }} token: description: The token used to authenticate when fetching version manifests hosted on github.com, such as for the Microsoft Build of OpenJDK. When running this action on github.com, the default value is sufficient. When running on GHES, you can pass a personal access token for github.com if you are experiencing rate limiting. + required: false default: ${{ github.server_url == 'https://github.com' && github.token || '' }} mvn-toolchain-id: description: 'Name of Maven Toolchain ID if the default name of "${distribution}_${java-version}" is not wanted. See examples of supported syntax in Advanced Usage file' From 5866e121b4df186d39f97b28b494de963caf55c6 Mon Sep 17 00:00:00 2001 From: alexander <116556921+al-kau@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:56:08 +0300 Subject: [PATCH 10/57] feat: add microsoft openjdk 17.0.18 (#1002) * feat: add microsoft openjdk 17.0.18 * fix: correct url microsoft-jdk-17.0.10-macos-x64 --- __tests__/data/microsoft.json | 43 ++++++++++++++++++ .../distributors/microsoft-installer.test.ts | 19 +++++--- .../microsoft/microsoft-openjdk-versions.json | 45 ++++++++++++++++++- 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/__tests__/data/microsoft.json b/__tests__/data/microsoft.json index b2f0e68a1..b67b9447d 100644 --- a/__tests__/data/microsoft.json +++ b/__tests__/data/microsoft.json @@ -79,6 +79,49 @@ } ] }, + { + "version": "17.0.18", + "stable": true, + "release_url": "https://aka.ms/download-jdk", + "files": [ + { + "filename": "microsoft-jdk-17.0.18-macos-x64.tar.gz", + "arch": "x64", + "platform": "darwin", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-macos-x64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-linux-x64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-windows-x64.zip", + "arch": "x64", + "platform": "win32", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-windows-x64.zip" + }, + { + "filename": "microsoft-jdk-17.0.18-macos-aarch64.tar.gz", + "arch": "aarch64", + "platform": "darwin", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-macos-aarch64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-linux-aarch64.tar.gz", + "arch": "aarch64", + "platform": "linux", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-linux-aarch64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-windows-aarch64.zip", + "arch": "aarch64", + "platform": "win32", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-windows-aarch64.zip" + } + ] + }, { "version": "17.0.7", "stable": true, diff --git a/__tests__/distributors/microsoft-installer.test.ts b/__tests__/distributors/microsoft-installer.test.ts index a971dc7fb..ca5a253af 100644 --- a/__tests__/distributors/microsoft-installer.test.ts +++ b/__tests__/distributors/microsoft-installer.test.ts @@ -45,15 +45,20 @@ describe('findPackageForDownload', () => { 'https://aka.ms/download-jdk/microsoft-jdk-21.0.0-{{OS_TYPE}}-x64.{{ARCHIVE_TYPE}}' ], [ - '17.0.1', - '17.0.1+12.1', - 'https://aka.ms/download-jdk/microsoft-jdk-17.0.1.12.1-{{OS_TYPE}}-x64.{{ARCHIVE_TYPE}}' + '17.x', + '17.0.18', + 'https://aka.ms/download-jdk/microsoft-jdk-17.0.18-{{OS_TYPE}}-x64.{{ARCHIVE_TYPE}}' ], [ - '17.x', + '17.0.7', '17.0.7', 'https://aka.ms/download-jdk/microsoft-jdk-17.0.7-{{OS_TYPE}}-x64.{{ARCHIVE_TYPE}}' ], + [ + '17.0.1', + '17.0.1+12.1', + 'https://aka.ms/download-jdk/microsoft-jdk-17.0.1.12.1-{{OS_TYPE}}-x64.{{ARCHIVE_TYPE}}' + ], [ '16.0.x', '16.0.2+7.1', @@ -119,7 +124,7 @@ describe('findPackageForDownload', () => { }); const result = await distro['findPackageForDownload'](version); - const expectedUrl = `https://aka.ms/download-jdk/microsoft-jdk-17.0.7-macos-${distroArch}.tar.gz`; + const expectedUrl = `https://aka.ms/download-jdk/microsoft-jdk-17.0.18-macos-${distroArch}.tar.gz`; expect(result.url).toBe(expectedUrl); } @@ -145,7 +150,7 @@ describe('findPackageForDownload', () => { }); const result = await distro['findPackageForDownload'](version); - const expectedUrl = `https://aka.ms/download-jdk/microsoft-jdk-17.0.7-linux-${distroArch}.tar.gz`; + const expectedUrl = `https://aka.ms/download-jdk/microsoft-jdk-17.0.18-linux-${distroArch}.tar.gz`; expect(result.url).toBe(expectedUrl); } @@ -171,7 +176,7 @@ describe('findPackageForDownload', () => { }); const result = await distro['findPackageForDownload'](version); - const expectedUrl = `https://aka.ms/download-jdk/microsoft-jdk-17.0.7-windows-${distroArch}.zip`; + const expectedUrl = `https://aka.ms/download-jdk/microsoft-jdk-17.0.18-windows-${distroArch}.zip`; expect(result.url).toBe(expectedUrl); } diff --git a/src/distributions/microsoft/microsoft-openjdk-versions.json b/src/distributions/microsoft/microsoft-openjdk-versions.json index 5c11c026b..1dd4a2319 100644 --- a/src/distributions/microsoft/microsoft-openjdk-versions.json +++ b/src/distributions/microsoft/microsoft-openjdk-versions.json @@ -171,6 +171,49 @@ } ] }, + { + "version": "17.0.18", + "stable": true, + "release_url": "https://aka.ms/download-jdk", + "files": [ + { + "filename": "microsoft-jdk-17.0.18-macos-x64.tar.gz", + "arch": "x64", + "platform": "darwin", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-macos-x64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-linux-x64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-windows-x64.zip", + "arch": "x64", + "platform": "win32", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-windows-x64.zip" + }, + { + "filename": "microsoft-jdk-17.0.18-macos-aarch64.tar.gz", + "arch": "aarch64", + "platform": "darwin", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-macos-aarch64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-linux-aarch64.tar.gz", + "arch": "aarch64", + "platform": "linux", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-linux-aarch64.tar.gz" + }, + { + "filename": "microsoft-jdk-17.0.18-windows-aarch64.zip", + "arch": "aarch64", + "platform": "win32", + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.18-windows-aarch64.zip" + } + ] + }, { "version": "17.0.10", "stable": true, @@ -180,7 +223,7 @@ "filename": "microsoft-jdk-17.0.10-macos-x64.tar.gz", "arch": "x64", "platform": "darwin", - "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.7-macos-x64.tar.gz" + "download_url": "https://aka.ms/download-jdk/microsoft-jdk-17.0.10-macos-x64.tar.gz" }, { "filename": "microsoft-jdk-17.0.10-linux-x64.tar.gz", From 347226bb3be67364e9158595f5aef631dd95bac0 Mon Sep 17 00:00:00 2001 From: Markus Hoffrogge Date: Mon, 22 Jun 2026 17:58:00 +0200 Subject: [PATCH 11/57] Update README.md - use "alert syntax for Markdown" for notes (#924) --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e06c9c75b..50147b5a9 100644 --- a/README.md +++ b/README.md @@ -121,13 +121,11 @@ Currently, the following distributions are supported: | `graalvm` | [Oracle GraalVM](https://www.graalvm.org/) | [`graalvm` license](https://www.oracle.com/downloads/licenses/graal-free-license.html) | `jetbrains` | [JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/) | [`jetbrains` license](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE) -**NOTE:** The different distributors can provide discrepant list of available versions / supported configurations. Please refer to the official documentation to see the list of supported versions. - -**NOTE:** AdoptOpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from `adopt` and `adopt-openj9`, to `temurin` and `semeru` respectively, to keep receiving software and security updates. See more details in the [Good-bye AdoptOpenJDK post](https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/). - -**NOTE:** For Azul Zulu OpenJDK architectures x64 and arm64 are mapped to x86 / arm with proper hw_bitness. - -**NOTE:** To comply with the GraalVM Free Terms and Conditions (GFTC) license, it is recommended to use GraalVM JDK 17 version 17.0.12, as this is the only version of GraalVM JDK 17 available under the GFTC license. Additionally, it is encouraged to consider upgrading to GraalVM JDK 21, which offers the latest features and improvements. +> [!NOTE] +> - The different distributors can provide discrepant list of available versions / supported configurations. Please refer to the official documentation to see the list of supported versions. +> - AdoptOpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from `adopt` and `adopt-openj9`, to `temurin` and `semeru` respectively, to keep receiving software and security updates. See more details in the [Good-bye AdoptOpenJDK post](https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/). +> - For Azul Zulu OpenJDK architectures x64 and arm64 are mapped to x86 / arm with proper hw_bitness. +> - To comply with the GraalVM Free Terms and Conditions (GFTC) license, it is recommended to use GraalVM JDK 17 version 17.0.12, as this is the only version of GraalVM JDK 17 available under the GFTC license. Additionally, it is encouraged to consider upgrading to GraalVM JDK 21, which offers the latest features and improvements. **NOTE:** Oracle JDK 17 licensing varies by patch level. As shown on the [JDK 17 Archive](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) (versions up to 17.0.12 are under the [NFTC](https://www.oracle.com/downloads/licenses/no-fee-license.html) license) and the [JDK 17.0.13+ Archive](https://www.oracle.com/java/technologies/javase/jdk17-0-13-later-archive-downloads.html) (versions 17.0.13 and later are under the [OTN](https://www.oracle.com/downloads/licenses/javase-license1.html) license). To stay on the free NFTC license, use `distribution: 'oracle'` with `java-version: '17.0.12'` (or earlier) instead of the floating `'17'`. Alternatively, upgrade to Oracle JDK 21+, which remains under the NFTC license. From cefdecda466762a419c4f3279a009ea3865f86fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:07:33 -0400 Subject: [PATCH 12/57] Bump undici from 6.24.1 to 6.27.0 (#1033) Bumps [undici](https://github.com/nodejs/undici) from 6.24.1 to 6.27.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v6.24.1...v6.27.0) --- updated-dependencies: - dependency-name: undici dependency-version: 6.27.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bruno Borges --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7bc4b8e43..fba9d372c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6419,9 +6419,9 @@ } }, "node_modules/undici": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", - "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", + "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", "license": "MIT", "engines": { "node": ">=18.17" From 3d27da4ac18f738ed0edc28d90fa8fbcae543d84 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Mon, 22 Jun 2026 16:21:54 -0400 Subject: [PATCH 13/57] Update contributor guide with emoji for clarity (#1028) --- docs/contributors.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/contributors.md b/docs/contributors.md index 0d49925a0..bfbbefeb9 100644 --- a/docs/contributors.md +++ b/docs/contributors.md @@ -6,13 +6,13 @@ We have prepared a short guide so that the process of making your contribution i ## How can I contribute... -* [Contribute Documentation:green_book:](#contribute-documentation) +* [:green_book: Contribute Documentation](#contribute-documentation) -* [Contribute Code :computer:](#contribute-code) +* [:computer: Contribute Code](#contribute-code) -* [Provide Support on Issues:pencil:](#provide-support-on-issues) +* [:pencil: Provide Support on Issues](#provide-support-on-issues) -* [Review Pull Requests:mag:](#review-pull-requests) +* [:mag: Review Pull Requests](#review-pull-requests) ## Contribute documentation @@ -111,4 +111,4 @@ Another great way to contribute is is to review pull request. Please, be extra k - Make sure you're familiar with the code or documentation is updated, unless it's a minor change (spellchecking, minor formatting, etc.) - Review changes using the GitHub functionality. You can ask a clarifying question, point out an error or suggest an alternative. > Note: You may ask for minor changes - "nitpicks", but consider whether they are real blockers to merging or not -- Submit your review, which may include comments, an approval, or a changes request \ No newline at end of file +- Submit your review, which may include comments, an approval, or a changes request From dc8e16ad3778083b5f90344c067c2542b653f424 Mon Sep 17 00:00:00 2001 From: Trass3r Date: Mon, 22 Jun 2026 22:37:47 +0200 Subject: [PATCH 14/57] add javac problem matcher (#562) * add javac problemMatcher * fix spaces Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Bruno Borges Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/java.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/java.json b/.github/java.json index eda1b0cd4..baf35254e 100644 --- a/.github/java.json +++ b/.github/java.json @@ -9,6 +9,18 @@ "message": 3 } ] + }, + { + "owner": "javac", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+): (warning|error): (.+?)$", + "file": 1, + "line": 2, + "severity": 3, + "message": 4 + } + ] } ] -} \ No newline at end of file +} From c09b25f3e7c9ac50000c7a0ced06c1e4a400176c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:38:14 -0400 Subject: [PATCH 15/57] Clarify README version syntax and migration guidance (#1038) * Initial plan * Clarify README version guidance --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 50147b5a9..816e23401 100644 --- a/README.md +++ b/README.md @@ -25,13 +25,6 @@ This action allows you to work with Java and Scala projects. For more details, see the full release notes on the [releases page](https://github.com/actions/setup-java/releases/tag/v5.0.0) -## V2 vs V1 - -- V2 supports custom distributions and provides support for Azul Zulu OpenJDK, Eclipse Temurin and AdoptOpenJDK out of the box. V1 supports only Azul Zulu OpenJDK. -- V2 requires you to specify distribution along with the version. V1 defaults to Azul Zulu OpenJDK, only version input is required. Follow [the migration guide](docs/switching-to-v2.md) to switch from V1 to V2. - -For information about the latest releases, recent updates, and newly supported distributions, please refer to the `setup-java` [Releases](https://github.com/actions/setup-java/releases). - ## Usage - `java-version`: The Java version that is going to be set up. Takes a whole or [semver](#supported-version-syntax) Java version. If not specified, the action will expect `java-version-file` input to be specified. @@ -98,8 +91,8 @@ steps: ``` #### Supported version syntax -The `java-version` input supports an exact version or a version range using [SemVer](https://semver.org/) notation: -- major versions: `8`, `11`, `16`, `17`, `21`, `25` +The `java-version` input supports an exact version or a version range using [SemVer](https://semver.org/) notation. The values below are examples, not an exhaustive list: +- major versions, such as: `8`, `11`, `16`, `17`, `21`, `25` - more specific versions: `8.0.282+8`, `8.0.232`, `11.0`, `11.0.4`, `17.0` - early access (EA) versions: `15-ea`, `15.0.0-ea` @@ -292,6 +285,15 @@ In the example above multiple JDKs are installed for the same job. The result af - [Modifying Maven Toolchains](docs/advanced-usage.md#Modifying-Maven-Toolchains) - [Java Version File](docs/advanced-usage.md#Java-version-file) +## V2 vs V1 + +Examples in this README use `actions/setup-java@v5`, but the main migration note from V1 still applies to all later major versions (`v2`, `v3`, `v4`, and `v5`): + +- Starting with V2, the action supports custom distributions. V1 supports only Azul Zulu OpenJDK. +- Starting with V2, you must specify distribution along with the version. V1 defaults to Azul Zulu OpenJDK, so only version input is required. Follow [the migration guide](docs/switching-to-v2.md) to switch from V1 to V2. + +For information about the latest releases, recent updates, and newly supported distributions, please refer to the `setup-java` [Releases](https://github.com/actions/setup-java/releases). + ## Recommended permissions When using the `setup-java` action in your GitHub Actions workflow, it is recommended to set the following permissions to ensure proper functionality: From 525097081d2d8db6870b5144b26392eb94d2537c Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Mon, 22 Jun 2026 16:43:17 -0400 Subject: [PATCH 16/57] Update undici artifacts to 6.27.0 (license cache + dist) (#1040) * Update undici license cache to 6.27.0 The Licensed check failed because the cached license record for undici was pinned to 6.24.1 while the installed dependency is 6.27.0, causing "license: mit, allowed: false" / source enumeration errors. Regenerate the cached record with `licensed cache` so it matches the installed version. `licensed status` now reports 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rebuild dist with undici 6.27.0 The committed dist/ bundle was built with undici 6.24.1, but the lockfile resolves undici 6.27.0. The check-dist workflow rebuilds the bundle and detected this drift (uncommitted changes after build). Rebuild dist/setup and dist/cleanup with `npm run build` so the committed bundle matches the installed undici 6.27.0, aligning with the license cache update in this PR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .licenses/npm/undici.dep.yml | 2 +- dist/cleanup/index.js | 417 +++++++++++++++++++++++++---------- dist/setup/index.js | 417 +++++++++++++++++++++++++---------- 3 files changed, 613 insertions(+), 223 deletions(-) diff --git a/.licenses/npm/undici.dep.yml b/.licenses/npm/undici.dep.yml index 121a6e35a..c46a5c7bf 100644 --- a/.licenses/npm/undici.dep.yml +++ b/.licenses/npm/undici.dep.yml @@ -1,6 +1,6 @@ --- name: undici -version: 6.24.1 +version: 6.27.0 type: npm summary: An HTTP/1.1 client, written from scratch for Node.js homepage: https://undici.nodejs.org diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 5a1db9633..0c3278164 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -28337,8 +28337,6 @@ function defaultFactory (origin, opts) { class Agent extends DispatcherBase { constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) { - super() - if (typeof factory !== 'function') { throw new InvalidArgumentError('factory must be a function.') } @@ -28351,6 +28349,8 @@ class Agent extends DispatcherBase { throw new InvalidArgumentError('maxRedirections must be a positive number') } + super(options) + if (connect && typeof connect !== 'function') { connect = { ...connect } } @@ -28724,6 +28724,9 @@ const EMPTY_BUF = Buffer.alloc(0) const FastBuffer = Buffer[Symbol.species] const addListener = util.addListener const removeAllListeners = util.removeAllListeners +const kIdleSocketValidation = Symbol('kIdleSocketValidation') +const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout') +const kSocketUsed = Symbol('kSocketUsed') let extractBody @@ -28946,29 +28949,71 @@ class Parser { const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr - if (ret === constants.ERROR.PAUSED_UPGRADE) { - this.onUpgrade(data.slice(offset)) - } else if (ret === constants.ERROR.PAUSED) { - this.paused = true - socket.unshift(data.slice(offset)) - } else if (ret !== constants.ERROR.OK) { - const ptr = llhttp.llhttp_get_error_reason(this.ptr) - let message = '' - /* istanbul ignore else: difficult to make a test case for */ - if (ptr) { - const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0) - message = - 'Response does not match the HTTP/1.1 protocol (' + - Buffer.from(llhttp.memory.buffer, ptr, len).toString() + - ')' - } - throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset)) + if (ret !== constants.ERROR.OK) { + const body = data.subarray(offset) + + if (ret === constants.ERROR.PAUSED_UPGRADE) { + this.onUpgrade(body) + } else if (ret === constants.ERROR.PAUSED) { + this.paused = true + socket.unshift(body) + } else { + throw this.createError(ret, body) + } } } catch (err) { util.destroy(socket, err) } } + finish () { + assert(currentParser === null) + assert(this.ptr != null) + assert(!this.paused) + + const { llhttp } = this + + let ret + + try { + currentParser = this + ret = llhttp.llhttp_finish(this.ptr) + } finally { + currentParser = null + } + + if (ret === constants.ERROR.OK) { + return null + } + + if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) { + this.paused = true + return null + } + + return this.createError(ret, EMPTY_BUF) + } + + createError (ret, data) { + const { llhttp, contentLength, bytesRead } = this + + if (contentLength && bytesRead !== parseInt(contentLength, 10)) { + return new ResponseContentLengthMismatchError() + } + + const ptr = llhttp.llhttp_get_error_reason(this.ptr) + let message = '' + if (ptr) { + const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0) + message = + 'Response does not match the HTTP/1.1 protocol (' + + Buffer.from(llhttp.memory.buffer, ptr, len).toString() + + ')' + } + + return new HTTPParserError(message, constants.ERROR[ret], data) + } + destroy () { assert(this.ptr != null) assert(currentParser == null) @@ -28996,6 +29041,11 @@ class Parser { return -1 } + if (client[kRunning] === 0) { + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) + return -1 + } + const request = client[kQueue][client[kRunningIdx]] if (!request) { return -1 @@ -29099,6 +29149,11 @@ class Parser { return -1 } + if (client[kRunning] === 0) { + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) + return -1 + } + const request = client[kQueue][client[kRunningIdx]] /* istanbul ignore next: difficult to make a test case for */ @@ -29272,6 +29327,7 @@ class Parser { request.onComplete(headers) client[kQueue][client[kRunningIdx]++] = null + socket[kSocketUsed] = true if (socket[kWriting]) { assert(client[kRunning] === 0) @@ -29330,6 +29386,9 @@ async function connectH1 (client, socket) { socket[kWriting] = false socket[kReset] = false socket[kBlocking] = false + socket[kIdleSocketValidation] = 0 + socket[kIdleSocketValidationTimeout] = null + socket[kSocketUsed] = false socket[kParser] = new Parser(client, socket, llhttpInstance) addListener(socket, 'error', function (err) { @@ -29340,8 +29399,11 @@ async function connectH1 (client, socket) { // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded // to the user. if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so for as a valid response. - parser.onMessageComplete() + const parserErr = parser.finish() + if (parserErr) { + this[kError] = parserErr + this[kClient][kOnError](parserErr) + } return } @@ -29360,8 +29422,10 @@ async function connectH1 (client, socket) { const parser = this[kParser] if (parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() + const parserErr = parser.finish() + if (parserErr) { + util.destroy(this, parserErr) + } return } @@ -29371,10 +29435,11 @@ async function connectH1 (client, socket) { const client = this[kClient] const parser = this[kParser] + clearIdleSocketValidation(this) + if (parser) { if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() + this[kError] = parser.finish() || this[kError] } this[kParser].destroy() @@ -29437,7 +29502,7 @@ async function connectH1 (client, socket) { return socket.destroyed }, busy (request) { - if (socket[kWriting] || socket[kReset] || socket[kBlocking]) { + if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) { return true } @@ -29475,6 +29540,31 @@ async function connectH1 (client, socket) { } } +function clearIdleSocketValidation (socket) { + if (socket[kIdleSocketValidationTimeout]) { + clearTimeout(socket[kIdleSocketValidationTimeout]) + socket[kIdleSocketValidationTimeout] = null + } + + socket[kIdleSocketValidation] = 0 +} + +function scheduleIdleSocketValidation (client, socket) { + socket[kIdleSocketValidation] = 1 + socket[kIdleSocketValidationTimeout] = setTimeout(() => { + socket[kIdleSocketValidationTimeout] = null + socket[kIdleSocketValidation] = 2 + + if (client[kSocket] === socket && !socket.destroyed) { + client[kResume]() + } + }, 0) + socket[kIdleSocketValidationTimeout].unref?.() +} + +/** + * @param {import('./client.js')} client + */ function resumeH1 (client) { const socket = client[kSocket] @@ -29489,6 +29579,32 @@ function resumeH1 (client) { socket[kNoRef] = false } + if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) { + if (socket[kIdleSocketValidation] === 0) { + scheduleIdleSocketValidation(client, socket) + socket[kParser].readMore() + if (socket.destroyed) { + return + } + return + } + + if (socket[kIdleSocketValidation] === 1) { + socket[kParser].readMore() + if (socket.destroyed) { + return + } + return + } + } + + if (client[kRunning] === 0) { + socket[kParser].readMore() + if (socket.destroyed) { + return + } + } + if (client[kSize] === 0) { if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) { socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE) @@ -29582,6 +29698,7 @@ function writeH1 (client, request) { } const socket = client[kSocket] + clearIdleSocketValidation(socket) const abort = (err) => { if (request.aborted || request.completed) { @@ -30903,9 +31020,10 @@ class Client extends DispatcherBase { autoSelectFamilyAttemptTimeout, // h2 maxConcurrentStreams, - allowH2 + allowH2, + webSocket } = {}) { - super() + super({ webSocket }) if (keepAlive !== undefined) { throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead') @@ -31438,15 +31556,24 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nc const kOnDestroyed = Symbol('onDestroyed') const kOnClosed = Symbol('onClosed') const kInterceptedDispatch = Symbol('Intercepted Dispatch') +const kWebSocketOptions = Symbol('webSocketOptions') class DispatcherBase extends Dispatcher { - constructor () { + constructor (opts) { super() this[kDestroyed] = false this[kOnDestroyed] = null this[kClosed] = false this[kOnClosed] = [] + this[kWebSocketOptions] = opts?.webSocket ?? {} + } + + get webSocketOptions () { + return { + maxFragments: this[kWebSocketOptions].maxFragments ?? 131072, + maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 + } } get destroyed () { @@ -32010,8 +32137,8 @@ const kRemoveClient = Symbol('remove client') const kStats = Symbol('stats') class PoolBase extends DispatcherBase { - constructor () { - super() + constructor (opts) { + super(opts) this[kQueue] = new FixedQueue() this[kClients] = [] @@ -32271,8 +32398,6 @@ class Pool extends PoolBase { allowH2, ...options } = {}) { - super() - if (connections != null && (!Number.isFinite(connections) || connections < 0)) { throw new InvalidArgumentError('invalid connections') } @@ -32297,6 +32422,8 @@ class Pool extends PoolBase { }) } + super(options) + this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool) ? options.interceptors.Pool : [] @@ -37381,32 +37508,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) // If the attribute-name case-insensitively matches the string // "SameSite", the user agent MUST process the cookie-av as follows: - // 1. Let enforcement be "Default". - let enforcement = 'Default' - const attributeValueLowercase = attributeValue.toLowerCase() - // 2. If cookie-av's attribute-value is a case-insensitive match for - // "None", set enforcement to "None". - if (attributeValueLowercase.includes('none')) { - enforcement = 'None' - } - // 3. If cookie-av's attribute-value is a case-insensitive match for - // "Strict", set enforcement to "Strict". - if (attributeValueLowercase.includes('strict')) { - enforcement = 'Strict' + // 1. If cookie-av's attribute-value is a case-insensitive match for + // "None", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "None". + if (attributeValueLowercase === 'none') { + cookieAttributeList.sameSite = 'None' + } else if (attributeValueLowercase === 'strict') { + // 2. If cookie-av's attribute-value is a case-insensitive match for + // "Strict", append an attribute to the cookie-attribute-list with + // an attribute-name of "SameSite" and an attribute-value of + // "Strict". + cookieAttributeList.sameSite = 'Strict' + } else if (attributeValueLowercase === 'lax') { + // 3. If cookie-av's attribute-value is a case-insensitive match for + // "Lax", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "Lax". + cookieAttributeList.sameSite = 'Lax' } - - // 4. If cookie-av's attribute-value is a case-insensitive match for - // "Lax", set enforcement to "Lax". - if (attributeValueLowercase.includes('lax')) { - enforcement = 'Lax' - } - - // 5. Append an attribute to the cookie-attribute-list with an - // attribute-name of "SameSite" and an attribute-value of - // enforcement. - cookieAttributeList.sameSite = enforcement } else { cookieAttributeList.unparsed ??= [] @@ -50112,40 +50232,35 @@ const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]) const kBuffer = Symbol('kBuffer') const kLength = Symbol('kLength') -// Default maximum decompressed message size: 4 MB -const kDefaultMaxDecompressedSize = 4 * 1024 * 1024 - class PerMessageDeflate { /** @type {import('node:zlib').InflateRaw} */ #inflate #options = {} - /** @type {boolean} */ - #aborted = false - - /** @type {Function|null} */ - #currentCallback = null + #maxPayloadSize = 0 /** * @param {Map} extensions */ - constructor (extensions) { + constructor (extensions, options) { this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover') this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits') + + this.#maxPayloadSize = options.maxPayloadSize } + /** + * Decompress a compressed payload. + * @param {Buffer} chunk Compressed data + * @param {boolean} fin Final fragment flag + * @param {Function} callback Callback function + */ decompress (chunk, fin, callback) { // An endpoint uses the following algorithm to decompress a message. // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the // payload of the message. // 2. Decompress the resulting data using DEFLATE. - - if (this.#aborted) { - callback(new MessageSizeExceededError()) - return - } - if (!this.#inflate) { let windowBits = Z_DEFAULT_WINDOWBITS @@ -50168,23 +50283,12 @@ class PerMessageDeflate { this.#inflate[kLength] = 0 this.#inflate.on('data', (data) => { - if (this.#aborted) { - return - } - this.#inflate[kLength] += data.length - if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) { - this.#aborted = true + if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) { + callback(new MessageSizeExceededError()) this.#inflate.removeAllListeners() - this.#inflate.destroy() this.#inflate = null - - if (this.#currentCallback) { - const cb = this.#currentCallback - this.#currentCallback = null - cb(new MessageSizeExceededError()) - } return } @@ -50197,14 +50301,13 @@ class PerMessageDeflate { }) } - this.#currentCallback = callback this.#inflate.write(chunk) if (fin) { this.#inflate.write(tail) } this.#inflate.flush(() => { - if (this.#aborted || !this.#inflate) { + if (!this.#inflate) { return } @@ -50212,7 +50315,6 @@ class PerMessageDeflate { this.#inflate[kBuffer].length = 0 this.#inflate[kLength] = 0 - this.#currentCallback = null callback(null, full) }) @@ -50248,6 +50350,12 @@ const { const { WebsocketFrameSend } = __nccwpck_require__(3264) const { closeWebSocketConnection } = __nccwpck_require__(86897) const { PerMessageDeflate } = __nccwpck_require__(19469) +const { MessageSizeExceededError } = __nccwpck_require__(68707) + +function failWebsocketConnectionWithCode (ws, code, reason) { + closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason)) + failWebsocketConnection(ws, reason) +} // This code was influenced by ws released under the MIT license. // Copyright (c) 2011 Einar Otto Stangvik @@ -50256,6 +50364,7 @@ const { PerMessageDeflate } = __nccwpck_require__(19469) class ByteParser extends Writable { #buffers = [] + #fragmentsBytes = 0 #byteOffset = 0 #loop = false @@ -50267,18 +50376,27 @@ class ByteParser extends Writable { /** @type {Map} */ #extensions + /** @type {number} */ + #maxFragments + + /** @type {number} */ + #maxPayloadSize + /** * @param {import('./websocket').WebSocket} ws * @param {Map|null} extensions + * @param {{ maxFragments?: number, maxPayloadSize?: number }} [options] */ - constructor (ws, extensions) { + constructor (ws, extensions, options = {}) { super() this.ws = ws this.#extensions = extensions == null ? new Map() : extensions + this.#maxFragments = options.maxFragments ?? 0 + this.#maxPayloadSize = options.maxPayloadSize ?? 0 if (this.#extensions.has('permessage-deflate')) { - this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions)) + this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions, options)) } } @@ -50294,6 +50412,19 @@ class ByteParser extends Writable { this.run(callback) } + #validatePayloadLength () { + if ( + this.#maxPayloadSize > 0 && + !isControlFrame(this.#info.opcode) && + this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize + ) { + failWebsocketConnectionWithCode(this.ws, 1009, 'Payload size exceeds maximum allowed size') + return false + } + + return true + } + /** * Runs whenever a new chunk is received. * Callback is called whenever there are no more chunks buffering, @@ -50382,6 +50513,10 @@ class ByteParser extends Writable { if (payloadLength <= 125) { this.#info.payloadLength = payloadLength this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (payloadLength === 126) { this.#state = parserStates.PAYLOADLENGTH_16 } else if (payloadLength === 127) { @@ -50406,6 +50541,10 @@ class ByteParser extends Writable { this.#info.payloadLength = buffer.readUInt16BE(0) this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (this.#state === parserStates.PAYLOADLENGTH_64) { if (this.#byteOffset < 8) { return callback() @@ -50428,6 +50567,10 @@ class ByteParser extends Writable { this.#info.payloadLength = lower this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (this.#state === parserStates.READ_DATA) { if (this.#byteOffset < this.#info.payloadLength) { return callback() @@ -50440,42 +50583,58 @@ class ByteParser extends Writable { this.#state = parserStates.INFO } else { if (!this.#info.compressed) { - this.#fragments.push(body) + if (!this.writeFragments(body)) { + return + } + + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message) + return + } // If the frame is not fragmented, a message has been received. // If the frame is fragmented, it will terminate with a fin bit set // and an opcode of 0 (continuation), therefore we handle that when // parsing continuation frames, not here. if (!this.#info.fragmented && this.#info.fin) { - const fullMessage = Buffer.concat(this.#fragments) - websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage) - this.#fragments.length = 0 + websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments()) } this.#state = parserStates.INFO } else { - this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => { - if (error) { - failWebsocketConnection(this.ws, error.message) - return - } + this.#extensions.get('permessage-deflate').decompress( + body, + this.#info.fin, + (error, data) => { + if (error) { + const code = error instanceof MessageSizeExceededError ? 1009 : 1007 + failWebsocketConnectionWithCode(this.ws, code, error.message) + return + } - this.#fragments.push(data) + if (!this.writeFragments(data)) { + return + } + + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message) + return + } + + if (!this.#info.fin) { + this.#state = parserStates.INFO + this.#loop = true + this.run(callback) + return + } + + websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments()) - if (!this.#info.fin) { - this.#state = parserStates.INFO this.#loop = true + this.#state = parserStates.INFO this.run(callback) - return } - - websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments)) - - this.#loop = true - this.#state = parserStates.INFO - this.#fragments.length = 0 - this.run(callback) - }) + ) this.#loop = false break @@ -50527,6 +50686,35 @@ class ByteParser extends Writable { return buffer } + writeFragments (fragment) { + if ( + this.#maxFragments > 0 && + this.#fragments.length === this.#maxFragments + ) { + failWebsocketConnectionWithCode(this.ws, 1008, 'Too many message fragments') + return false + } + + this.#fragmentsBytes += fragment.length + this.#fragments.push(fragment) + return true + } + + consumeFragments () { + const fragments = this.#fragments + + if (fragments.length === 1) { + this.#fragmentsBytes = 0 + return fragments.shift() + } + + const output = Buffer.concat(fragments, this.#fragmentsBytes) + this.#fragments = [] + this.#fragmentsBytes = 0 + + return output + } + parseCloseBody (data) { assert(data.length !== 1) @@ -51562,7 +51750,14 @@ class WebSocket extends EventTarget { // once this happens, the connection is open this[kResponse] = response - const parser = new ByteParser(this, parsedExtensions) + const webSocketOptions = this[kController]?.dispatcher?.webSocketOptions + const maxFragments = webSocketOptions?.maxFragments + const maxPayloadSize = webSocketOptions?.maxPayloadSize + + const parser = new ByteParser(this, parsedExtensions, { + maxFragments, + maxPayloadSize + }) parser.on('drain', onParserDrain) parser.on('error', onParserError.bind(this)) diff --git a/dist/setup/index.js b/dist/setup/index.js index 434039045..33056027d 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -54063,8 +54063,6 @@ function defaultFactory (origin, opts) { class Agent extends DispatcherBase { constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) { - super() - if (typeof factory !== 'function') { throw new InvalidArgumentError('factory must be a function.') } @@ -54077,6 +54075,8 @@ class Agent extends DispatcherBase { throw new InvalidArgumentError('maxRedirections must be a positive number') } + super(options) + if (connect && typeof connect !== 'function') { connect = { ...connect } } @@ -54450,6 +54450,9 @@ const EMPTY_BUF = Buffer.alloc(0) const FastBuffer = Buffer[Symbol.species] const addListener = util.addListener const removeAllListeners = util.removeAllListeners +const kIdleSocketValidation = Symbol('kIdleSocketValidation') +const kIdleSocketValidationTimeout = Symbol('kIdleSocketValidationTimeout') +const kSocketUsed = Symbol('kSocketUsed') let extractBody @@ -54672,29 +54675,71 @@ class Parser { const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr - if (ret === constants.ERROR.PAUSED_UPGRADE) { - this.onUpgrade(data.slice(offset)) - } else if (ret === constants.ERROR.PAUSED) { - this.paused = true - socket.unshift(data.slice(offset)) - } else if (ret !== constants.ERROR.OK) { - const ptr = llhttp.llhttp_get_error_reason(this.ptr) - let message = '' - /* istanbul ignore else: difficult to make a test case for */ - if (ptr) { - const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0) - message = - 'Response does not match the HTTP/1.1 protocol (' + - Buffer.from(llhttp.memory.buffer, ptr, len).toString() + - ')' - } - throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset)) + if (ret !== constants.ERROR.OK) { + const body = data.subarray(offset) + + if (ret === constants.ERROR.PAUSED_UPGRADE) { + this.onUpgrade(body) + } else if (ret === constants.ERROR.PAUSED) { + this.paused = true + socket.unshift(body) + } else { + throw this.createError(ret, body) + } } } catch (err) { util.destroy(socket, err) } } + finish () { + assert(currentParser === null) + assert(this.ptr != null) + assert(!this.paused) + + const { llhttp } = this + + let ret + + try { + currentParser = this + ret = llhttp.llhttp_finish(this.ptr) + } finally { + currentParser = null + } + + if (ret === constants.ERROR.OK) { + return null + } + + if (ret === constants.ERROR.PAUSED || ret === constants.ERROR.PAUSED_UPGRADE) { + this.paused = true + return null + } + + return this.createError(ret, EMPTY_BUF) + } + + createError (ret, data) { + const { llhttp, contentLength, bytesRead } = this + + if (contentLength && bytesRead !== parseInt(contentLength, 10)) { + return new ResponseContentLengthMismatchError() + } + + const ptr = llhttp.llhttp_get_error_reason(this.ptr) + let message = '' + if (ptr) { + const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0) + message = + 'Response does not match the HTTP/1.1 protocol (' + + Buffer.from(llhttp.memory.buffer, ptr, len).toString() + + ')' + } + + return new HTTPParserError(message, constants.ERROR[ret], data) + } + destroy () { assert(this.ptr != null) assert(currentParser == null) @@ -54722,6 +54767,11 @@ class Parser { return -1 } + if (client[kRunning] === 0) { + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) + return -1 + } + const request = client[kQueue][client[kRunningIdx]] if (!request) { return -1 @@ -54825,6 +54875,11 @@ class Parser { return -1 } + if (client[kRunning] === 0) { + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) + return -1 + } + const request = client[kQueue][client[kRunningIdx]] /* istanbul ignore next: difficult to make a test case for */ @@ -54998,6 +55053,7 @@ class Parser { request.onComplete(headers) client[kQueue][client[kRunningIdx]++] = null + socket[kSocketUsed] = true if (socket[kWriting]) { assert(client[kRunning] === 0) @@ -55056,6 +55112,9 @@ async function connectH1 (client, socket) { socket[kWriting] = false socket[kReset] = false socket[kBlocking] = false + socket[kIdleSocketValidation] = 0 + socket[kIdleSocketValidationTimeout] = null + socket[kSocketUsed] = false socket[kParser] = new Parser(client, socket, llhttpInstance) addListener(socket, 'error', function (err) { @@ -55066,8 +55125,11 @@ async function connectH1 (client, socket) { // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded // to the user. if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so for as a valid response. - parser.onMessageComplete() + const parserErr = parser.finish() + if (parserErr) { + this[kError] = parserErr + this[kClient][kOnError](parserErr) + } return } @@ -55086,8 +55148,10 @@ async function connectH1 (client, socket) { const parser = this[kParser] if (parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() + const parserErr = parser.finish() + if (parserErr) { + util.destroy(this, parserErr) + } return } @@ -55097,10 +55161,11 @@ async function connectH1 (client, socket) { const client = this[kClient] const parser = this[kParser] + clearIdleSocketValidation(this) + if (parser) { if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { - // We treat all incoming data so far as a valid response. - parser.onMessageComplete() + this[kError] = parser.finish() || this[kError] } this[kParser].destroy() @@ -55163,7 +55228,7 @@ async function connectH1 (client, socket) { return socket.destroyed }, busy (request) { - if (socket[kWriting] || socket[kReset] || socket[kBlocking]) { + if (socket[kWriting] || socket[kReset] || socket[kBlocking] || socket[kIdleSocketValidation] === 1) { return true } @@ -55201,6 +55266,31 @@ async function connectH1 (client, socket) { } } +function clearIdleSocketValidation (socket) { + if (socket[kIdleSocketValidationTimeout]) { + clearTimeout(socket[kIdleSocketValidationTimeout]) + socket[kIdleSocketValidationTimeout] = null + } + + socket[kIdleSocketValidation] = 0 +} + +function scheduleIdleSocketValidation (client, socket) { + socket[kIdleSocketValidation] = 1 + socket[kIdleSocketValidationTimeout] = setTimeout(() => { + socket[kIdleSocketValidationTimeout] = null + socket[kIdleSocketValidation] = 2 + + if (client[kSocket] === socket && !socket.destroyed) { + client[kResume]() + } + }, 0) + socket[kIdleSocketValidationTimeout].unref?.() +} + +/** + * @param {import('./client.js')} client + */ function resumeH1 (client) { const socket = client[kSocket] @@ -55215,6 +55305,32 @@ function resumeH1 (client) { socket[kNoRef] = false } + if (client[kRunning] === 0 && client[kPending] > 0 && socket[kSocketUsed]) { + if (socket[kIdleSocketValidation] === 0) { + scheduleIdleSocketValidation(client, socket) + socket[kParser].readMore() + if (socket.destroyed) { + return + } + return + } + + if (socket[kIdleSocketValidation] === 1) { + socket[kParser].readMore() + if (socket.destroyed) { + return + } + return + } + } + + if (client[kRunning] === 0) { + socket[kParser].readMore() + if (socket.destroyed) { + return + } + } + if (client[kSize] === 0) { if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) { socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE) @@ -55308,6 +55424,7 @@ function writeH1 (client, request) { } const socket = client[kSocket] + clearIdleSocketValidation(socket) const abort = (err) => { if (request.aborted || request.completed) { @@ -56629,9 +56746,10 @@ class Client extends DispatcherBase { autoSelectFamilyAttemptTimeout, // h2 maxConcurrentStreams, - allowH2 + allowH2, + webSocket } = {}) { - super() + super({ webSocket }) if (keepAlive !== undefined) { throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead') @@ -57164,15 +57282,24 @@ const { kDestroy, kClose, kClosed, kDestroyed, kDispatch, kInterceptors } = __nc const kOnDestroyed = Symbol('onDestroyed') const kOnClosed = Symbol('onClosed') const kInterceptedDispatch = Symbol('Intercepted Dispatch') +const kWebSocketOptions = Symbol('webSocketOptions') class DispatcherBase extends Dispatcher { - constructor () { + constructor (opts) { super() this[kDestroyed] = false this[kOnDestroyed] = null this[kClosed] = false this[kOnClosed] = [] + this[kWebSocketOptions] = opts?.webSocket ?? {} + } + + get webSocketOptions () { + return { + maxFragments: this[kWebSocketOptions].maxFragments ?? 131072, + maxPayloadSize: this[kWebSocketOptions].maxPayloadSize ?? 128 * 1024 * 1024 + } } get destroyed () { @@ -57736,8 +57863,8 @@ const kRemoveClient = Symbol('remove client') const kStats = Symbol('stats') class PoolBase extends DispatcherBase { - constructor () { - super() + constructor (opts) { + super(opts) this[kQueue] = new FixedQueue() this[kClients] = [] @@ -57997,8 +58124,6 @@ class Pool extends PoolBase { allowH2, ...options } = {}) { - super() - if (connections != null && (!Number.isFinite(connections) || connections < 0)) { throw new InvalidArgumentError('invalid connections') } @@ -58023,6 +58148,8 @@ class Pool extends PoolBase { }) } + super(options) + this[kInterceptors] = options.interceptors?.Pool && Array.isArray(options.interceptors.Pool) ? options.interceptors.Pool : [] @@ -63107,32 +63234,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) // If the attribute-name case-insensitively matches the string // "SameSite", the user agent MUST process the cookie-av as follows: - // 1. Let enforcement be "Default". - let enforcement = 'Default' - const attributeValueLowercase = attributeValue.toLowerCase() - // 2. If cookie-av's attribute-value is a case-insensitive match for - // "None", set enforcement to "None". - if (attributeValueLowercase.includes('none')) { - enforcement = 'None' - } - // 3. If cookie-av's attribute-value is a case-insensitive match for - // "Strict", set enforcement to "Strict". - if (attributeValueLowercase.includes('strict')) { - enforcement = 'Strict' + // 1. If cookie-av's attribute-value is a case-insensitive match for + // "None", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "None". + if (attributeValueLowercase === 'none') { + cookieAttributeList.sameSite = 'None' + } else if (attributeValueLowercase === 'strict') { + // 2. If cookie-av's attribute-value is a case-insensitive match for + // "Strict", append an attribute to the cookie-attribute-list with + // an attribute-name of "SameSite" and an attribute-value of + // "Strict". + cookieAttributeList.sameSite = 'Strict' + } else if (attributeValueLowercase === 'lax') { + // 3. If cookie-av's attribute-value is a case-insensitive match for + // "Lax", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "Lax". + cookieAttributeList.sameSite = 'Lax' } - - // 4. If cookie-av's attribute-value is a case-insensitive match for - // "Lax", set enforcement to "Lax". - if (attributeValueLowercase.includes('lax')) { - enforcement = 'Lax' - } - - // 5. Append an attribute to the cookie-attribute-list with an - // attribute-name of "SameSite" and an attribute-value of - // enforcement. - cookieAttributeList.sameSite = enforcement } else { cookieAttributeList.unparsed ??= [] @@ -75838,40 +75958,35 @@ const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]) const kBuffer = Symbol('kBuffer') const kLength = Symbol('kLength') -// Default maximum decompressed message size: 4 MB -const kDefaultMaxDecompressedSize = 4 * 1024 * 1024 - class PerMessageDeflate { /** @type {import('node:zlib').InflateRaw} */ #inflate #options = {} - /** @type {boolean} */ - #aborted = false - - /** @type {Function|null} */ - #currentCallback = null + #maxPayloadSize = 0 /** * @param {Map} extensions */ - constructor (extensions) { + constructor (extensions, options) { this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover') this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits') + + this.#maxPayloadSize = options.maxPayloadSize } + /** + * Decompress a compressed payload. + * @param {Buffer} chunk Compressed data + * @param {boolean} fin Final fragment flag + * @param {Function} callback Callback function + */ decompress (chunk, fin, callback) { // An endpoint uses the following algorithm to decompress a message. // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the // payload of the message. // 2. Decompress the resulting data using DEFLATE. - - if (this.#aborted) { - callback(new MessageSizeExceededError()) - return - } - if (!this.#inflate) { let windowBits = Z_DEFAULT_WINDOWBITS @@ -75894,23 +76009,12 @@ class PerMessageDeflate { this.#inflate[kLength] = 0 this.#inflate.on('data', (data) => { - if (this.#aborted) { - return - } - this.#inflate[kLength] += data.length - if (this.#inflate[kLength] > kDefaultMaxDecompressedSize) { - this.#aborted = true + if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) { + callback(new MessageSizeExceededError()) this.#inflate.removeAllListeners() - this.#inflate.destroy() this.#inflate = null - - if (this.#currentCallback) { - const cb = this.#currentCallback - this.#currentCallback = null - cb(new MessageSizeExceededError()) - } return } @@ -75923,14 +76027,13 @@ class PerMessageDeflate { }) } - this.#currentCallback = callback this.#inflate.write(chunk) if (fin) { this.#inflate.write(tail) } this.#inflate.flush(() => { - if (this.#aborted || !this.#inflate) { + if (!this.#inflate) { return } @@ -75938,7 +76041,6 @@ class PerMessageDeflate { this.#inflate[kBuffer].length = 0 this.#inflate[kLength] = 0 - this.#currentCallback = null callback(null, full) }) @@ -75974,6 +76076,12 @@ const { const { WebsocketFrameSend } = __nccwpck_require__(3264) const { closeWebSocketConnection } = __nccwpck_require__(86897) const { PerMessageDeflate } = __nccwpck_require__(19469) +const { MessageSizeExceededError } = __nccwpck_require__(68707) + +function failWebsocketConnectionWithCode (ws, code, reason) { + closeWebSocketConnection(ws, code, reason, Buffer.byteLength(reason)) + failWebsocketConnection(ws, reason) +} // This code was influenced by ws released under the MIT license. // Copyright (c) 2011 Einar Otto Stangvik @@ -75982,6 +76090,7 @@ const { PerMessageDeflate } = __nccwpck_require__(19469) class ByteParser extends Writable { #buffers = [] + #fragmentsBytes = 0 #byteOffset = 0 #loop = false @@ -75993,18 +76102,27 @@ class ByteParser extends Writable { /** @type {Map} */ #extensions + /** @type {number} */ + #maxFragments + + /** @type {number} */ + #maxPayloadSize + /** * @param {import('./websocket').WebSocket} ws * @param {Map|null} extensions + * @param {{ maxFragments?: number, maxPayloadSize?: number }} [options] */ - constructor (ws, extensions) { + constructor (ws, extensions, options = {}) { super() this.ws = ws this.#extensions = extensions == null ? new Map() : extensions + this.#maxFragments = options.maxFragments ?? 0 + this.#maxPayloadSize = options.maxPayloadSize ?? 0 if (this.#extensions.has('permessage-deflate')) { - this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions)) + this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions, options)) } } @@ -76020,6 +76138,19 @@ class ByteParser extends Writable { this.run(callback) } + #validatePayloadLength () { + if ( + this.#maxPayloadSize > 0 && + !isControlFrame(this.#info.opcode) && + this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize + ) { + failWebsocketConnectionWithCode(this.ws, 1009, 'Payload size exceeds maximum allowed size') + return false + } + + return true + } + /** * Runs whenever a new chunk is received. * Callback is called whenever there are no more chunks buffering, @@ -76108,6 +76239,10 @@ class ByteParser extends Writable { if (payloadLength <= 125) { this.#info.payloadLength = payloadLength this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (payloadLength === 126) { this.#state = parserStates.PAYLOADLENGTH_16 } else if (payloadLength === 127) { @@ -76132,6 +76267,10 @@ class ByteParser extends Writable { this.#info.payloadLength = buffer.readUInt16BE(0) this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (this.#state === parserStates.PAYLOADLENGTH_64) { if (this.#byteOffset < 8) { return callback() @@ -76154,6 +76293,10 @@ class ByteParser extends Writable { this.#info.payloadLength = lower this.#state = parserStates.READ_DATA + + if (!this.#validatePayloadLength()) { + return + } } else if (this.#state === parserStates.READ_DATA) { if (this.#byteOffset < this.#info.payloadLength) { return callback() @@ -76166,42 +76309,58 @@ class ByteParser extends Writable { this.#state = parserStates.INFO } else { if (!this.#info.compressed) { - this.#fragments.push(body) + if (!this.writeFragments(body)) { + return + } + + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message) + return + } // If the frame is not fragmented, a message has been received. // If the frame is fragmented, it will terminate with a fin bit set // and an opcode of 0 (continuation), therefore we handle that when // parsing continuation frames, not here. if (!this.#info.fragmented && this.#info.fin) { - const fullMessage = Buffer.concat(this.#fragments) - websocketMessageReceived(this.ws, this.#info.binaryType, fullMessage) - this.#fragments.length = 0 + websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments()) } this.#state = parserStates.INFO } else { - this.#extensions.get('permessage-deflate').decompress(body, this.#info.fin, (error, data) => { - if (error) { - failWebsocketConnection(this.ws, error.message) - return - } + this.#extensions.get('permessage-deflate').decompress( + body, + this.#info.fin, + (error, data) => { + if (error) { + const code = error instanceof MessageSizeExceededError ? 1009 : 1007 + failWebsocketConnectionWithCode(this.ws, code, error.message) + return + } - this.#fragments.push(data) + if (!this.writeFragments(data)) { + return + } + + if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) { + failWebsocketConnectionWithCode(this.ws, 1009, new MessageSizeExceededError().message) + return + } + + if (!this.#info.fin) { + this.#state = parserStates.INFO + this.#loop = true + this.run(callback) + return + } + + websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments()) - if (!this.#info.fin) { - this.#state = parserStates.INFO this.#loop = true + this.#state = parserStates.INFO this.run(callback) - return } - - websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments)) - - this.#loop = true - this.#state = parserStates.INFO - this.#fragments.length = 0 - this.run(callback) - }) + ) this.#loop = false break @@ -76253,6 +76412,35 @@ class ByteParser extends Writable { return buffer } + writeFragments (fragment) { + if ( + this.#maxFragments > 0 && + this.#fragments.length === this.#maxFragments + ) { + failWebsocketConnectionWithCode(this.ws, 1008, 'Too many message fragments') + return false + } + + this.#fragmentsBytes += fragment.length + this.#fragments.push(fragment) + return true + } + + consumeFragments () { + const fragments = this.#fragments + + if (fragments.length === 1) { + this.#fragmentsBytes = 0 + return fragments.shift() + } + + const output = Buffer.concat(fragments, this.#fragmentsBytes) + this.#fragments = [] + this.#fragmentsBytes = 0 + + return output + } + parseCloseBody (data) { assert(data.length !== 1) @@ -77288,7 +77476,14 @@ class WebSocket extends EventTarget { // once this happens, the connection is open this[kResponse] = response - const parser = new ByteParser(this, parsedExtensions) + const webSocketOptions = this[kController]?.dispatcher?.webSocketOptions + const maxFragments = webSocketOptions?.maxFragments + const maxPayloadSize = webSocketOptions?.maxPayloadSize + + const parser = new ByteParser(this, parsedExtensions, { + maxFragments, + maxPayloadSize + }) parser.on('drain', onParserDrain) parser.on('error', onParserError.bind(this)) From 679e4e46a7fdb4a3b36d12e3fbd9f1e8b850ffbd Mon Sep 17 00:00:00 2001 From: Stephan Abel Date: Mon, 22 Jun 2026 23:10:15 +0200 Subject: [PATCH 17/57] docs: enhance custom jdk file installation (#996) * docs: enhance custom jdk file installation * Update jdkFile note for case sensitivity Clarify that 'distribution' must be set to 'jdkfile' in lowercase when using jdkFile input. --------- Co-authored-by: Bruno Borges Co-authored-by: Bruno Borges --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 816e23401..c4809a827 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ For more details, see the full release notes on the [releases page](https://git - `architecture`: The target architecture of the package. Possible values: `x86`, `x64`, `armv7`, `aarch64`, `ppc64le`. Default value: Derived from the runner machine. - - `jdkFile`: If a use-case requires a custom distribution setup-java uses the compressed JDK from the location pointed by this input and will take care of the installation and caching on the VM. + - `jdkFile`: If a use-case requires a custom distribution setup-java uses the compressed JDK from the location pointed by this input and will take care of the installation and caching on the VM. Note: `distribution` must be set to 'jdkfile' (case-sensitive; all lowercase) when using this option. - `check-latest`: Setting this option makes the action to check for the latest available version for the version spec. @@ -113,6 +113,7 @@ Currently, the following distributions are supported: | `sapmachine` | [SAP SapMachine JDK/JRE](https://sapmachine.io/) | [`sapmachine` license](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE) | `graalvm` | [Oracle GraalVM](https://www.graalvm.org/) | [`graalvm` license](https://www.oracle.com/downloads/licenses/graal-free-license.html) | `jetbrains` | [JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/) | [`jetbrains` license](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE) +| `jdkfile` | Custom JDK Installation | | > [!NOTE] > - The different distributors can provide discrepant list of available versions / supported configurations. Please refer to the official documentation to see the list of supported versions. From 92163d3dc6d298cd0993dad0eb086dc8d6359871 Mon Sep 17 00:00:00 2001 From: Milos Pantic <101411245+panticmilos@users.noreply.github.com> Date: Mon, 22 Jun 2026 23:11:20 +0200 Subject: [PATCH 18/57] Templates for new Java distributions (#429) * Add templates for new Java distributions * Update new pull request template * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Address PR #429 review suggestions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Bruno Borges Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../new_distribution_request.md | 22 +++++++++++++++++++ .../new_distribution_pull_request_template.md | 16 ++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/new_distribution_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/new_distribution_pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/new_distribution_request.md b/.github/ISSUE_TEMPLATE/new_distribution_request.md new file mode 100644 index 000000000..f5d7c7faf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new_distribution_request.md @@ -0,0 +1,22 @@ +--- +name: New Java distribution template +about: Suggest a new Java distribution +title: '' +labels: feature request, needs triage +assignees: '' +--- + +**Description:** +Describe your proposal. + +**Justification:** +Justification or a use case for your proposal. + +**Download URL:** +Download URL for the new distribution. + +**License:** +Link to the license for the new distribution. + +**Are you willing to submit a PR?** + \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/new_distribution_pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/new_distribution_pull_request_template.md new file mode 100644 index 000000000..4e3b44372 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/new_distribution_pull_request_template.md @@ -0,0 +1,16 @@ +**Description:** +Describe your changes. + +**Related issue:** +Add link to the related issue. + +**Download URL:** +Download URL for the new distribution. + +**License:** +Link to the license for the new distribution. + +**Check list:** +- [ ] Mark if documentation changes are required. +- [ ] Mark if tests were added or updated to cover the changes. +- [ ] Mark if new distribution is being added. \ No newline at end of file From bf0c0e6df33849b0cba166f7c5d05fb38a219383 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:15:27 -0400 Subject: [PATCH 19/57] Bump actions/checkout from 6 to 7 (#1032) Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bruno Borges --- .../workflows/e2e-cache-dependency-path.yml | 6 ++-- .github/workflows/e2e-cache.yml | 12 ++++---- .github/workflows/e2e-local-file.yml | 6 ++-- .github/workflows/e2e-publishing.yml | 8 +++--- .github/workflows/e2e-versions.yml | 28 +++++++++---------- .../workflows/publish-immutable-actions.yml | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/workflows/e2e-cache-dependency-path.yml b/.github/workflows/e2e-cache-dependency-path.yml index 8b40e99f0..298198551 100644 --- a/.github/workflows/e2e-cache-dependency-path.yml +++ b/.github/workflows/e2e-cache-dependency-path.yml @@ -24,7 +24,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -51,7 +51,7 @@ jobs: needs: gradle1-save steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -76,7 +76,7 @@ jobs: needs: gradle1-save steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for gradle uses: ./ id: setup-java diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index 6df10b578..f03132875 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -24,7 +24,7 @@ jobs: os: [macos-15-intel, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -50,7 +50,7 @@ jobs: needs: gradle-save steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -73,7 +73,7 @@ jobs: os: [macos-15-intel, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for maven uses: ./ id: setup-java @@ -97,7 +97,7 @@ jobs: needs: maven-save steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for maven uses: ./ id: setup-java @@ -124,7 +124,7 @@ jobs: os: [macos-15-intel, windows-latest, ubuntu-22.04] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for sbt uses: ./ id: setup-java @@ -174,7 +174,7 @@ jobs: needs: sbt-save steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Run setup-java with the cache for sbt uses: ./ id: setup-java diff --git a/.github/workflows/e2e-local-file.yml b/.github/workflows/e2e-local-file.yml index 92fdf7597..313453e1b 100644 --- a/.github/workflows/e2e-local-file.yml +++ b/.github/workflows/e2e-local-file.yml @@ -21,7 +21,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Download Adopt OpenJDK file run: | if ($IsLinux) { @@ -58,7 +58,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Download Zulu OpenJDK file run: | if ($IsLinux) { @@ -95,7 +95,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Download Eclipse Temurin file run: | if ($IsLinux) { diff --git a/.github/workflows/e2e-publishing.yml b/.github/workflows/e2e-publishing.yml index 0c0aaafa9..e685c43ab 100644 --- a/.github/workflows/e2e-publishing.yml +++ b/.github/workflows/e2e-publishing.yml @@ -25,7 +25,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -60,7 +60,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Create fake settings.xml run: | $xmlDirectory = Join-Path $HOME ".m2" @@ -96,7 +96,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Create fake settings.xml run: | $xmlDirectory = Join-Path $HOME ".m2" @@ -133,7 +133,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 7dc8d8a71..4cc66670f 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -73,7 +73,7 @@ jobs: version: '24-ea' steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -99,7 +99,7 @@ jobs: version: ['21', '17'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Install bash run: apk add --no-cache bash - name: setup-java @@ -149,7 +149,7 @@ jobs: version: '17.0.7' steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -184,7 +184,7 @@ jobs: os: macos-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -220,7 +220,7 @@ jobs: os: macos-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -259,7 +259,7 @@ jobs: version: ['17-ea', '15.0.0-ea.14'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -281,7 +281,7 @@ jobs: version: ['17-ea'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -303,7 +303,7 @@ jobs: version: ['17-ea', '21-ea'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -390,7 +390,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -418,7 +418,7 @@ jobs: version: ['11'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: setup-java uses: ./ id: setup-java @@ -441,7 +441,7 @@ jobs: java-version-file: ['.java-version', '.tool-versions'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Create .java-version file shell: bash run: echo "17" > .java-version @@ -470,7 +470,7 @@ jobs: java-version-file: ['.java-version', '.tool-versions'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Create .java-version file shell: bash run: echo "11" > .java-version @@ -498,7 +498,7 @@ jobs: java-version-file: ['.java-version', '.tool-versions'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Create .java-version file shell: bash run: echo "17.0.10" > .java-version @@ -526,7 +526,7 @@ jobs: java-version-file: ['.java-version', '.tool-versions', '.sdkmanrc'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Create .java-version file shell: bash run: echo "openjdk64-17.0.10" > .java-version diff --git a/.github/workflows/publish-immutable-actions.yml b/.github/workflows/publish-immutable-actions.yml index bfe592049..21d96a8d7 100644 --- a/.github/workflows/publish-immutable-actions.yml +++ b/.github/workflows/publish-immutable-actions.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checking out - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Publish id: publish uses: actions/publish-immutable-action@v0.0.4 From eab4b0854d2abdb899d5cf9e1bbff1df86044a8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:16:03 -0400 Subject: [PATCH 20/57] Bump @types/node from 25.9.3 to 26.0.0 (#1031) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.3 to 26.0.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bruno Borges Co-authored-by: Bruno Borges --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index fba9d372c..b753e6574 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.9.3", + "@types/node": "^26.0.0", "@types/semver": "^7.5.8", "@typescript-eslint/eslint-plugin": "^8.48.0", "@typescript-eslint/parser": "^8.61.1", @@ -1993,13 +1993,13 @@ } }, "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", + "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/semver": { @@ -6428,9 +6428,9 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 2ff28b9cc..757acdbc9 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.9.3", + "@types/node": "^26.0.0", "@types/semver": "^7.5.8", "@typescript-eslint/eslint-plugin": "^8.48.0", "@typescript-eslint/parser": "^8.61.1", From 4baa9b45d2bff6fcbef9619dabd0cb1ca822905b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:59:24 -0400 Subject: [PATCH 21/57] docs: replace non-existent HelloWorldApp references with java --version (#1043) * Initial plan * docs: replace HelloWorldApp references with java --version in README and advanced-usage --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- README.md | 8 ++++---- docs/advanced-usage.md | 40 ++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c4809a827..03860b372 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ steps: with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '25' -- run: java HelloWorldApp.java +- run: java --version ``` #### Azul Zulu OpenJDK @@ -87,7 +87,7 @@ steps: with: distribution: 'zulu' # See 'Supported distributions' for available options java-version: '25' -- run: java HelloWorldApp.java +- run: java --version ``` #### Supported version syntax @@ -221,7 +221,7 @@ steps: distribution: 'temurin' java-version: '25' check-latest: true -- run: java HelloWorldApp.java +- run: java --version ``` ### Testing against different Java versions @@ -240,7 +240,7 @@ jobs: with: distribution: '' java-version: ${{ matrix.java }} - - run: java HelloWorldApp.java + - run: java --version ``` ### Install multiple JDKs diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 1b1e4feea..4aeca46cc 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -36,7 +36,7 @@ steps: with: distribution: 'temurin' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Adopt @@ -49,7 +49,7 @@ steps: with: distribution: 'adopt-hotspot' java-version: '11' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Zulu @@ -62,7 +62,7 @@ steps: distribution: 'zulu' java-version: '21' java-package: jdk # optional (jdk, jre, jdk+fx or jre+fx) - defaults to jdk -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Liberica @@ -75,7 +75,7 @@ steps: distribution: 'liberica' java-version: '21' java-package: jdk # optional (jdk, jre, jdk+fx or jre+fx) - defaults to jdk -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Microsoft @@ -87,7 +87,7 @@ steps: with: distribution: 'microsoft' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Using Microsoft distribution on GHES @@ -116,7 +116,7 @@ steps: with: distribution: 'corretto' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Oracle @@ -129,7 +129,7 @@ steps: with: distribution: 'oracle' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Alibaba Dragonwell @@ -142,7 +142,7 @@ steps: with: distribution: 'dragonwell' java-version: '8' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### SapMachine @@ -154,7 +154,7 @@ steps: with: distribution: 'sapmachine' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### GraalVM @@ -168,8 +168,8 @@ steps: distribution: 'graalvm' java-version: '21' - run: | - java -cp java HelloWorldApp - native-image -cp java HelloWorldApp + java --version + native-image --version ``` ### JetBrains @@ -186,7 +186,7 @@ steps: with: distribution: 'jetbrains' java-version: '11' -- run: java -cp java HelloWorldApp +- run: java --version ``` The JetBrains installer uses the GitHub API to fetch the latest version. If you believe your project is going to be running into rate limits, you can provide a @@ -202,7 +202,7 @@ steps: java-package: 'jdk' # optional (jdk, jre, jdk+jcef, jre+jcef, jdk+ft, or jre+ft) - defaults to jdk env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -- run: java -cp java HelloWorldApp +- run: java --version ``` You can specify your package type (as shown in the [releases page](https://github.com/JetBrains/JetBrainsRuntime/releases/)) in the `java-package` parameter. @@ -225,7 +225,7 @@ steps: distribution: '' java-version: '11' java-package: jdk # optional (jdk or jre) - defaults to jdk -- run: java -cp java HelloWorldApp +- run: java --version ``` ## Installing custom Java architecture @@ -238,7 +238,7 @@ steps: distribution: '' java-version: '11' architecture: x86 # optional - default value derived from the runner machine -- run: java -cp java HelloWorldApp +- run: java --version ``` ## Installing Java from local file @@ -256,7 +256,7 @@ steps: java-version: '11.0.0' architecture: x64 -- run: java -cp java HelloWorldApp +- run: java --version ``` If your use-case requires a custom distribution (in the example, alpine-linux is used) or a version that is not provided by setup-java and you want to always install the latest version during runtime, then you can use the following code to auto-download the latest JDK, determine the semver needed for setup-java, and setup-java will take care of the installation and caching on the VM: @@ -281,7 +281,7 @@ If your use-case requires a custom distribution (in the example, alpine-linux is jdkFile: ${{ runner.temp }}/java_package.tar.gz java-version: {{ steps.fetch_latest_jdk.outputs.java_version }} architecture: x64 - - run: java -cp java HelloWorldApp + - run: java --version ``` ## Testing against different Java distributions @@ -302,7 +302,7 @@ jobs: with: distribution: ${{ matrix.distribution }} java-version: ${{ matrix.java }} - - run: java -cp java HelloWorldApp + - run: java --version ``` #### Testing against different platforms @@ -322,7 +322,7 @@ jobs: with: distribution: 'temurin' java-version: ${{ matrix.java }} - - run: java -cp java HelloWorldApp + - run: java --version ``` ## Publishing using Apache Maven @@ -580,7 +580,7 @@ steps: distribution: 'temurin' java-version: '11' mvn-toolchain-id: 'some_other_id' -- run: java -cp java HelloWorldApp +- run: java --version ``` In case you install multiple versions of Java at once you can use the same syntax as used in `java-versions`. Please note that you have to declare an ID for all Java versions that will be installed or the `mvn-toolchain-id` instruction will be skipped wholesale due to mapping ambiguities. From 5431e71f9a4e00431c1c904af57e62794b518b11 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:04:38 -0400 Subject: [PATCH 22/57] docs: add JavaFX Maven project configuration instructions (#1044) * Initial plan * docs: add JavaFX Maven project configuration instructions --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- docs/advanced-usage.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 4aeca46cc..375ea7ece 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -12,6 +12,7 @@ - [GraalVM](#GraalVM) - [JetBrains](#JetBrains) - [Installing custom Java package type](#Installing-custom-Java-package-type) + - [JavaFX Maven project](#JavaFX-Maven-project) - [Installing custom Java architecture](#Installing-custom-Java-architecture) - [Installing custom Java distribution from local file](#Installing-Java-from-local-file) - [Testing against different Java distributions](#Testing-against-different-Java-distributions) @@ -228,6 +229,30 @@ steps: - run: java --version ``` +### JavaFX Maven project + +For JavaFX projects that use Maven, use `jdk+fx` (or `jre+fx`) as the `java-package` value together with a distribution that supports it (e.g. `zulu` or `liberica`). Then include the [`javafx-maven-plugin`](https://openjfx.io/openjfx-docs/#maven) in your `pom.xml` as described in the [Getting Started with JavaFX](https://openjfx.io/openjfx-docs/#maven) guide. + +```yaml +steps: +- uses: actions/checkout@v6 +- uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: '21' + java-package: jdk+fx + cache: maven +- name: Build with Maven + run: mvn --no-transfer-progress compile +``` + +To run the JavaFX application in CI: + +```yaml +- name: Run with Maven + run: mvn --no-transfer-progress javafx:run +``` + ## Installing custom Java architecture ```yaml From a9a46fbe0996878a5673db759d29dbc5f470320e Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Mon, 22 Jun 2026 21:51:01 -0400 Subject: [PATCH 23/57] docs: document self-signed certificate / internal CA handling for GitHub Enterprise (#1050) Adds an advanced-usage section explaining the 'self signed certificate in certificate chain' error seen on GitHub Enterprise Server and behind TLS-inspecting proxies. Recommends the secure fix of trusting the internal CA via NODE_EXTRA_CA_CERTS (or the OS trust store on self-hosted runners), with a GitHub Enterprise callout, and warns against disabling TLS verification since the JDK download has no checksum fallback. Refs #640 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/advanced-usage.md | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 375ea7ece..4b81c8547 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -22,6 +22,7 @@ - [Hosted Tool Cache](#Hosted-Tool-Cache) - [Modifying Maven Toolchains](#Modifying-Maven-Toolchains) - [Java-version file](#Java-version-file) +- [Self-signed certificates and internal CAs (GitHub Enterprise)](#Self-signed-certificates-and-internal-CAs-GitHub-Enterprise) See [action.yml](../action.yml) for more details on task inputs. @@ -660,3 +661,56 @@ If the file contains multiple versions, only the first one will be recognized. ***NOTE***: For the tool-version file, ensure that you use standard semantic versioning (semver) formats, as non-standard formats (such as jetbrains-21b212.1) may not be parsed correctly. Additionally, for complex version strings containing multiple version-like segments (for example, java semeru-openj9-11.0.15+10_openj9-0.32.0), the extraction logic may incorrectly capture the last segment (0.32.0) instead of the main version (11.0.15+10). + +## Self-signed certificates and internal CAs (GitHub Enterprise) + +When `setup-java` dynamically downloads a JDK, it makes HTTPS requests both to fetch the available version metadata and to download the JDK archive. If your runners sit behind a **TLS-inspecting corporate proxy**, or you are on **GitHub Enterprise Server (GHES)** with an internal certificate authority, those requests can fail with an error such as: + +``` +Error: self signed certificate in certificate chain +``` + +This happens because the certificate presented to the runner is signed by an **internal or self-signed CA** that is not part of the runner's default trust store. The download itself is fine — the runner simply cannot verify the certificate chain. + +### Recommended fix: trust your internal CA + +The secure way to resolve this is to make the runner trust your organization's CA, which keeps TLS verification fully enabled. `setup-java` runs on Node.js, which honors the [`NODE_EXTRA_CA_CERTS`](https://nodejs.org/api/cli.html#node_extra_ca_certsfile) environment variable. Point it at your CA bundle (in PEM format) **before** the `actions/setup-java` step: + +```yaml +steps: + # The CA bundle is already present on the runner image in this example. + # Alternatively, write it from a secret in a previous step. + - name: Trust the internal CA + run: echo "NODE_EXTRA_CA_CERTS=/etc/ssl/certs/internal-ca.pem" >> "$GITHUB_ENV" + + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' +``` + +If you keep the certificate in a secret rather than on the runner image, write it to disk first: + +```yaml +steps: + - name: Write and trust the internal CA + run: | + echo "${{ secrets.INTERNAL_CA_PEM }}" > "${RUNNER_TEMP}/internal-ca.pem" + echo "NODE_EXTRA_CA_CERTS=${RUNNER_TEMP}/internal-ca.pem" >> "$GITHUB_ENV" + + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' +``` + +For **self-hosted runners**, you can instead install your CA into the operating system's trust store (for example, `update-ca-certificates` on Debian/Ubuntu or `update-ca-trust` on RHEL). This makes the certificate trusted for all tooling on the runner, not just `setup-java`. + +### GitHub Enterprise customers + +On **GitHub Enterprise Server**, traffic from your runners frequently passes through an organization-managed proxy or terminates TLS at an appliance using a certificate from an internal CA. If your workflows hit the error above, set `NODE_EXTRA_CA_CERTS` to your enterprise CA bundle (or bake the CA into your self-hosted runner image) as shown above. Coordinate with your platform team to obtain the correct PEM bundle for your appliance and proxy chain. + +### Security warning: do not disable certificate verification + +Do **not** work around this error by disabling TLS verification (for example, by setting `NODE_TLS_REJECT_UNAUTHORIZED=0`). `setup-java` does not verify a pinned checksum or signature of the downloaded archive, so **TLS is effectively the only integrity guarantee** on the JDK download. Disabling verification would expose your workflow to a man-in-the-middle attacker who could serve a tampered JDK — which then becomes the `java` used by the rest of your pipeline, with access to your secrets and credentials. Always extend trust to your CA instead of turning verification off. + From 668c1ea991737ea087e51dbf0bcf7fd41cbc20ee Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Mon, 22 Jun 2026 21:59:01 -0400 Subject: [PATCH 24/57] docs: add post-install keytool import for the JDK cacerts trust store (#1051) Document how to make the installed JDK trust an internal CA at application runtime by importing it into $JAVA_HOME/lib/security/cacerts with keytool after setup-java runs. Clarifies this is the runtime trust layer, distinct from the download/transport layer (NODE_EXTRA_CA_CERTS), and notes hosted vs self-hosted persistence caveats. Refs #640 #1035 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/advanced-usage.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 4b81c8547..f4838b349 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -714,3 +714,41 @@ On **GitHub Enterprise Server**, traffic from your runners frequently passes thr Do **not** work around this error by disabling TLS verification (for example, by setting `NODE_TLS_REJECT_UNAUTHORIZED=0`). `setup-java` does not verify a pinned checksum or signature of the downloaded archive, so **TLS is effectively the only integrity guarantee** on the JDK download. Disabling verification would expose your workflow to a man-in-the-middle attacker who could serve a tampered JDK — which then becomes the `java` used by the rest of your pipeline, with access to your secrets and credentials. Always extend trust to your CA instead of turning verification off. +### Trusting an internal CA inside the installed JDK + +The guidance above makes the **runner** trust your CA so that the JDK can be *downloaded*. That is a separate layer from making the **installed JDK** trust your CA at *application runtime*. If your build steps (Maven/Gradle dependency resolution, integration tests, HTTPS calls from your app, etc.) connect to internal services that present a certificate from your internal CA, the JDK will reject them with errors such as: + +``` +PKIX path building failed: unable to find valid certification path to requested target +``` + +The JDK keeps its own trust store — a keystore named `cacerts` under `$JAVA_HOME/lib/security/cacerts` — which is independent of the operating system and Node trust stores. After `setup-java` has run (so that `JAVA_HOME` points at the freshly installed JDK), import your CA into that keystore with `keytool`: + +```yaml +steps: + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + + - name: Import internal CA into the JDK trust store + shell: bash + run: | + # Write the CA from a secret (or reference a file already on the runner) + echo "${{ secrets.INTERNAL_CA_PEM }}" > "${RUNNER_TEMP}/internal-ca.pem" + keytool -importcert -noprompt \ + -alias internal-ca \ + -file "${RUNNER_TEMP}/internal-ca.pem" \ + -keystore "${JAVA_HOME}/lib/security/cacerts" \ + -storepass changeit +``` + +Notes and caveats: + +- The default keystore password for `cacerts` is `changeit` unless your distribution overrides it. +- On **hosted runners** the change applies only to the current job's JDK and is discarded when the job ends, so include the import step in every job that needs it. +- On **self-hosted runners**, importing into a tool-cache JDK persists for as long as that cached version remains on the runner; if you want it to survive JDK reinstalls, pre-seed the CA into your runner image or re-run the import step each time. +- Prefer giving the certificate a stable, descriptive `-alias` so re-runs are idempotent (re-importing the same alias will fail; add `keytool -delete -alias internal-ca ...` first if you re-run within a long-lived runner). + +This documents the post-install workflow; there is no dedicated action input for supplying a custom `cacerts` file. + From 1d252528046b5ceb47839b03a115e0ea04f026cc Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 23 Jun 2026 13:10:17 -0400 Subject: [PATCH 25/57] chore: Harden workflows: least-privilege permissions + zizmor integration (#1039) * Harden workflows with least-privilege permissions and zizmor Apply GitHub Actions security best practices to the action's own workflows and integrate zizmor to catch regressions. - Add explicit least-privilege `permissions:` to every workflow (contents: read for read-only workflows; default-deny `{}` with job-scoped grants for codeql, publish-immutable-actions and update-config-files). - Set `persist-credentials: false` on all checkout steps that don't need the GITHUB_TOKEN afterwards. - Move `${{ ... }}` expansions out of `run:` blocks into `env:` vars to avoid template injection. - Pin the alpine container image (alpine:latest -> alpine:3.21). - Add a zizmor CI workflow that uploads SARIF to code scanning, plus a `.github/zizmor.yml` pinning policy (ref-pin for actions/* and github/*, hash-pin for third-party actions). zizmor now reports no findings (offline and online). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix indentation of if: in zizmor SARIF upload step The `if:` key on the "Upload SARIF results to code scanning" step had no indentation, producing invalid YAML ("Nested mappings are not allowed in compact mappings"). This broke `npm run format-check` (prettier) in Basic validation. Indent `if:` to 8 spaces so it nests under the step alongside uses/with. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/basic-validation.yml | 3 + .github/workflows/check-dist.yml | 3 + .github/workflows/codeql-analysis.yml | 2 + .../workflows/e2e-cache-dependency-path.yml | 9 ++ .github/workflows/e2e-cache.yml | 15 +++ .github/workflows/e2e-local-file.yml | 21 +++- .github/workflows/e2e-publishing.yml | 11 +++ .github/workflows/e2e-versions.yml | 99 ++++++++++++++++--- .github/workflows/licensed.yml | 3 + .../workflows/publish-immutable-actions.yml | 4 + .github/workflows/update-config-files.yml | 5 + .github/workflows/zizmor.yml | 48 +++++++++ .github/zizmor.yml | 11 +++ 13 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/zizmor.yml create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/basic-validation.yml b/.github/workflows/basic-validation.yml index e93e58009..ea70e0577 100644 --- a/.github/workflows/basic-validation.yml +++ b/.github/workflows/basic-validation.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + jobs: call-basic-validation: name: Basic validation diff --git a/.github/workflows/check-dist.yml b/.github/workflows/check-dist.yml index 90ef986ad..5592249e7 100644 --- a/.github/workflows/check-dist.yml +++ b/.github/workflows/check-dist.yml @@ -11,6 +11,9 @@ on: - '**.md' workflow_dispatch: +permissions: + contents: read + jobs: call-check-dist: name: Check dist/ diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1816c150c..598f7de5d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -8,6 +8,8 @@ on: schedule: - cron: '0 3 * * 0' +permissions: {} + jobs: call-codeQL-analysis: permissions: diff --git a/.github/workflows/e2e-cache-dependency-path.yml b/.github/workflows/e2e-cache-dependency-path.yml index 298198551..1c79c0814 100644 --- a/.github/workflows/e2e-cache-dependency-path.yml +++ b/.github/workflows/e2e-cache-dependency-path.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + defaults: run: shell: bash @@ -25,6 +28,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -52,6 +57,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -77,6 +84,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index f03132875..c76cf50b9 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + defaults: run: shell: bash @@ -25,6 +28,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -51,6 +56,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -74,6 +81,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for maven uses: ./ id: setup-java @@ -98,6 +107,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for maven uses: ./ id: setup-java @@ -125,6 +136,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for sbt uses: ./ id: setup-java @@ -175,6 +188,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for sbt uses: ./ id: setup-java diff --git a/.github/workflows/e2e-local-file.yml b/.github/workflows/e2e-local-file.yml index 313453e1b..1b9c48641 100644 --- a/.github/workflows/e2e-local-file.yml +++ b/.github/workflows/e2e-local-file.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + jobs: setup-java-local-file-adopt: name: Validate installation from local file Adopt @@ -22,6 +25,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Download Adopt OpenJDK file run: | if ($IsLinux) { @@ -46,7 +51,9 @@ jobs: java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version - run: bash __tests__/verify-java.sh "11.0.10" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11.0.10" "$JAVA_PATH" shell: bash setup-java-local-file-zulu: @@ -59,6 +66,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Download Zulu OpenJDK file run: | if ($IsLinux) { @@ -83,7 +92,9 @@ jobs: java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version - run: bash __tests__/verify-java.sh "11.0" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11.0" "$JAVA_PATH" shell: bash setup-java-local-file-temurin: @@ -96,6 +107,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Download Eclipse Temurin file run: | if ($IsLinux) { @@ -120,5 +133,7 @@ jobs: java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version - run: bash __tests__/verify-java.sh "11.0.12" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11.0.12" "$JAVA_PATH" shell: bash diff --git a/.github/workflows/e2e-publishing.yml b/.github/workflows/e2e-publishing.yml index e685c43ab..02a6b2596 100644 --- a/.github/workflows/e2e-publishing.yml +++ b/.github/workflows/e2e-publishing.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + defaults: run: shell: pwsh @@ -26,6 +29,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -61,6 +66,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create fake settings.xml run: | $xmlDirectory = Join-Path $HOME ".m2" @@ -97,6 +104,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create fake settings.xml run: | $xmlDirectory = Join-Path $HOME ".m2" @@ -134,6 +143,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 4cc66670f..c384d4023 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -13,6 +13,10 @@ on: schedule: - cron: '0 */12 * * *' workflow_dispatch: + +permissions: + contents: read + jobs: setup-java-major-versions: name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} @@ -74,6 +78,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -83,14 +89,17 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-alpine-linux: name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - alpine-linux - ${{ matrix.os }} runs-on: ${{ matrix.os }} container: - image: alpine:latest + image: alpine:3.21 strategy: fail-fast: false matrix: @@ -100,6 +109,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Install bash run: apk add --no-cache bash - name: setup-java @@ -109,7 +120,10 @@ jobs: java-version: ${{ matrix.version }} distribution: ${{ matrix.distribution }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-major-minor-versions: @@ -150,6 +164,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -157,10 +173,12 @@ jobs: java-version: ${{ matrix.version }} distribution: ${{ matrix.distribution }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" - shell: bash env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" + shell: bash setup-java-check-latest: name: ${{ matrix.distribution }} ${{ matrix.version }} - check-latest flag - ${{ matrix.os }} @@ -185,6 +203,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -195,7 +215,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Verify Java - run: bash __tests__/verify-java.sh "11" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11" "$JAVA_PATH" shell: bash setup-java-multiple-jdks: @@ -221,6 +243,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -245,7 +269,9 @@ jobs: } shell: pwsh - name: Verify Java - run: bash __tests__/verify-java.sh "17" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "17" "$JAVA_PATH" shell: bash setup-java-ea-versions-zulu: @@ -260,6 +286,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -267,7 +295,10 @@ jobs: java-version: ${{ matrix.version }} distribution: zulu - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-ea-versions-temurin: @@ -282,6 +313,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -289,7 +322,10 @@ jobs: java-version: ${{ matrix.version }} distribution: temurin - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-ea-versions-sapmachine: @@ -304,6 +340,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -311,7 +349,10 @@ jobs: java-version: ${{ matrix.version }} distribution: sapmachine - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-custom-package-type: @@ -391,6 +432,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -401,7 +444,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash # Only Liberica and Zulu provide x86 @@ -419,6 +465,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -427,7 +475,10 @@ jobs: java-version: ${{ matrix.version }} architecture: 'x86' - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-version-both-version-inputs-presents: @@ -442,6 +493,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "17" > .java-version @@ -456,7 +509,9 @@ jobs: java-version: 11 java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "11" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11" "$JAVA_PATH" shell: bash setup-java-version-from-file-major-notation: @@ -471,6 +526,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "11" > .java-version @@ -484,7 +541,9 @@ jobs: distribution: ${{ matrix.distribution }} java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "11" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11" "$JAVA_PATH" shell: bash setup-java-version-from-file-major-minor-patch-notation: @@ -499,6 +558,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "17.0.10" > .java-version @@ -512,7 +573,9 @@ jobs: distribution: ${{ matrix.distribution }} java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "17.0.10" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "17.0.10" "$JAVA_PATH" shell: bash setup-java-version-from-file-major-minor-patch-with-dist: @@ -527,6 +590,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "openjdk64-17.0.10" > .java-version @@ -543,5 +608,7 @@ jobs: distribution: ${{ matrix.distribution }} java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "17.0.10" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "17.0.10" "$JAVA_PATH" shell: bash diff --git a/.github/workflows/licensed.yml b/.github/workflows/licensed.yml index 37f1560c3..b5d009cb5 100644 --- a/.github/workflows/licensed.yml +++ b/.github/workflows/licensed.yml @@ -9,6 +9,9 @@ on: - main workflow_dispatch: +permissions: + contents: read + jobs: call-licensed: name: Licensed diff --git a/.github/workflows/publish-immutable-actions.yml b/.github/workflows/publish-immutable-actions.yml index 21d96a8d7..3888f9a85 100644 --- a/.github/workflows/publish-immutable-actions.yml +++ b/.github/workflows/publish-immutable-actions.yml @@ -5,6 +5,8 @@ on: types: [released] workflow_dispatch: +permissions: {} + jobs: publish: runs-on: ubuntu-latest @@ -16,6 +18,8 @@ jobs: steps: - name: Checking out uses: actions/checkout@v7 + with: + persist-credentials: false - name: Publish id: publish uses: actions/publish-immutable-action@v0.0.4 diff --git a/.github/workflows/update-config-files.yml b/.github/workflows/update-config-files.yml index 87af50042..bacdc74ec 100644 --- a/.github/workflows/update-config-files.yml +++ b/.github/workflows/update-config-files.yml @@ -5,7 +5,12 @@ on: - cron: '0 3 * * 0' workflow_dispatch: +permissions: {} + jobs: call-update-configuration-files: name: Update configuration files + permissions: + contents: write # to push the branch with updated configuration files + pull-requests: write # to open/update the configuration update PR uses: actions/reusable-workflows/.github/workflows/update-config-files.yml@main diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000..11a0f96a6 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,48 @@ +name: Security analysis with zizmor + +on: + push: + branches: + - main + - releases/* + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + workflow_dispatch: + +permissions: {} + +jobs: + zizmor: + name: Analyze workflows with zizmor + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # to upload SARIF results to code scanning + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install zizmor + run: pip install zizmor + + - name: Run zizmor + run: zizmor --format sarif .github/workflows/ > zizmor.sarif + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload SARIF results to code scanning + if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: zizmor.sarif + category: zizmor diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 000000000..38309ec47 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,11 @@ +# Configuration for zizmor (https://docs.zizmor.sh) +rules: + unpinned-uses: + config: + # First-party GitHub-maintained actions are trusted and referenced by + # major-version tags (the convention used across the actions org). + # Any third-party action must be pinned to a full commit SHA. + policies: + actions/*: ref-pin + github/*: ref-pin + '*': hash-pin From 1d56e31dbb83904d53629e4e0bd2d956e011c1c2 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 13:19:27 -0400 Subject: [PATCH 26/57] dist: Add GraalVM Community distribution support (#1042) * Initial plan * feat: add graalvm community distribution support * build: update bundled dist for graalvm community support * chore: address GraalVM community review feedback * fix: tidy graalvm community validation follow-ups * refactor: simplify GraalVM Community release resolution * refactor: address review feedback on Community resolver * refactor: rename pagination index for clarity * test: fix graalvm installer test formatting --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- README.md | 2 + .../distributors/graalvm-installer.test.ts | 128 ++++++++++- dist/setup/index.js | 156 +++++++++++-- docs/advanced-usage.md | 16 ++ src/distributions/distribution-factory.ts | 8 +- src/distributions/graalvm/installer.ts | 217 ++++++++++++++++-- 6 files changed, 482 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 03860b372..b79760e94 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ Currently, the following distributions are supported: | `dragonwell` | [Alibaba Dragonwell JDK](https://dragonwell-jdk.io/) | [`dragonwell` license](https://www.aliyun.com/product/dragonwell/) | `sapmachine` | [SAP SapMachine JDK/JRE](https://sapmachine.io/) | [`sapmachine` license](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE) | `graalvm` | [Oracle GraalVM](https://www.graalvm.org/) | [`graalvm` license](https://www.oracle.com/downloads/licenses/graal-free-license.html) +| `graalvm-community` | [GraalVM Community](https://github.com/graalvm/graalvm-ce-builds/releases) | [`graalvm-community` license](https://github.com/oracle/graal/blob/master/LICENSE) | `jetbrains` | [JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/) | [`jetbrains` license](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE) | `jdkfile` | Custom JDK Installation | | @@ -120,6 +121,7 @@ Currently, the following distributions are supported: > - AdoptOpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from `adopt` and `adopt-openj9`, to `temurin` and `semeru` respectively, to keep receiving software and security updates. See more details in the [Good-bye AdoptOpenJDK post](https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/). > - For Azul Zulu OpenJDK architectures x64 and arm64 are mapped to x86 / arm with proper hw_bitness. > - To comply with the GraalVM Free Terms and Conditions (GFTC) license, it is recommended to use GraalVM JDK 17 version 17.0.12, as this is the only version of GraalVM JDK 17 available under the GFTC license. Additionally, it is encouraged to consider upgrading to GraalVM JDK 21, which offers the latest features and improvements. +> - GraalVM Community is available as `distribution: 'graalvm-community'` for stable JDK 17 and later releases published on GitHub. **NOTE:** Oracle JDK 17 licensing varies by patch level. As shown on the [JDK 17 Archive](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) (versions up to 17.0.12 are under the [NFTC](https://www.oracle.com/downloads/licenses/no-fee-license.html) license) and the [JDK 17.0.13+ Archive](https://www.oracle.com/java/technologies/javase/jdk17-0-13-later-archive-downloads.html) (versions 17.0.13 and later are under the [OTN](https://www.oracle.com/downloads/licenses/javase-license1.html) license). To stay on the free NFTC license, use `distribution: 'oracle'` with `java-version: '17.0.12'` (or earlier) instead of the floating `'17'`. Alternatively, upgrade to Oracle JDK 21+, which remains under the NFTC license. diff --git a/__tests__/distributors/graalvm-installer.test.ts b/__tests__/distributors/graalvm-installer.test.ts index 23f90b884..155e3d9ee 100644 --- a/__tests__/distributors/graalvm-installer.test.ts +++ b/__tests__/distributors/graalvm-installer.test.ts @@ -3,7 +3,11 @@ import * as tc from '@actions/tool-cache'; import * as http from '@actions/http-client'; import fs from 'fs'; import path from 'path'; -import {GraalVMDistribution} from '../../src/distributions/graalvm/installer'; +import { + GraalVMCommunityDistribution, + GraalVMDistribution +} from '../../src/distributions/graalvm/installer'; +import {getJavaDistribution} from '../../src/distributions/distribution-factory'; import {JavaInstallerOptions} from '../../src/distributions/base-models'; import * as util from '../../src/util'; @@ -41,6 +45,7 @@ beforeAll(() => { describe('GraalVMDistribution', () => { let distribution: GraalVMDistribution; + let communityDistribution: GraalVMCommunityDistribution; let mockHttpClient: jest.Mocked; let spyCoreError: jest.SpyInstance; @@ -55,9 +60,11 @@ describe('GraalVMDistribution', () => { jest.clearAllMocks(); distribution = new GraalVMDistribution(defaultOptions); + communityDistribution = new GraalVMCommunityDistribution(defaultOptions); mockHttpClient = new http.HttpClient() as jest.Mocked; (distribution as any).http = mockHttpClient; + (communityDistribution as any).http = mockHttpClient; (util.getDownloadArchiveExtension as jest.Mock).mockReturnValue('tar.gz'); @@ -242,6 +249,23 @@ describe('GraalVMDistribution', () => { path: '/cached/java/path' }); }); + + it('should use a dedicated toolcache folder for GraalVM Community', async () => { + const result = await (communityDistribution as any).downloadTool( + javaRelease + ); + + expect(tc.cacheDir).toHaveBeenCalledWith( + path.join('/tmp/extracted', 'graalvm-jdk-17.0.5'), + 'Java_GraalVM_Community_jdk', + '17.0.5', + 'x64' + ); + expect(result).toEqual({ + version: '17.0.5', + path: '/cached/java/path' + }); + }); }); describe('findPackageForDownload', () => { @@ -948,5 +972,107 @@ describe('GraalVMDistribution', () => { configurable: true }); }); + + describe('GraalVMCommunityDistribution', () => { + beforeEach(() => { + jest + .spyOn(communityDistribution, 'getPlatform') + .mockReturnValue('linux'); + }); + + it('should resolve an exact GraalVM Community version from GitHub releases', async () => { + mockHttpClient.getJson.mockResolvedValue({ + result: [ + { + draft: false, + prerelease: false, + assets: [ + { + name: 'graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + browser_download_url: + 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz' + } + ] + } + ], + statusCode: 200, + headers: {} + }); + + const result = await ( + communityDistribution as any + ).findPackageForDownload('21.0.2'); + + expect(result).toEqual({ + url: 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + version: '21.0.2' + }); + }); + + it('should resolve the latest GraalVM Community release for a major version', async () => { + mockHttpClient.getJson.mockResolvedValue({ + result: [ + { + draft: false, + prerelease: false, + assets: [ + { + name: 'graalvm-community-jdk-21.0.1_linux-x64_bin.tar.gz', + browser_download_url: + 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.1/graalvm-community-jdk-21.0.1_linux-x64_bin.tar.gz' + } + ] + }, + { + draft: false, + prerelease: false, + assets: [ + { + name: 'graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + browser_download_url: + 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz' + } + ] + } + ], + statusCode: 200, + headers: {} + }); + + const result = await ( + communityDistribution as any + ).findPackageForDownload('21'); + + expect(result).toEqual({ + url: 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + version: '21.0.2' + }); + }); + + it('should reject GraalVM Community early access requests', async () => { + (communityDistribution as any).stable = false; + + await expect( + (communityDistribution as any).findPackageForDownload('23') + ).rejects.toThrow( + 'GraalVM Community does not provide early access builds' + ); + }); + }); + }); +}); + +describe('distribution factory', () => { + const defaultOptions: JavaInstallerOptions = { + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false + }; + + it('should map graalvm-community to the community installer', () => { + const community = getJavaDistribution('graalvm-community', defaultOptions); + + expect(community).toBeInstanceOf(GraalVMCommunityDistribution); }); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index 33056027d..007b48497 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78771,6 +78771,7 @@ var JavaDistribution; JavaDistribution["Dragonwell"] = "dragonwell"; JavaDistribution["SapMachine"] = "sapmachine"; JavaDistribution["GraalVM"] = "graalvm"; + JavaDistribution["GraalVMCommunity"] = "graalvm-community"; JavaDistribution["JetBrains"] = "jetbrains"; })(JavaDistribution || (JavaDistribution = {})); function getJavaDistribution(distributionName, installerOptions, jdkFile) { @@ -78802,6 +78803,8 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) { return new installer_11.SapMachineDistribution(installerOptions); case JavaDistribution.GraalVM: return new installer_12.GraalVMDistribution(installerOptions); + case JavaDistribution.GraalVMCommunity: + return new installer_12.GraalVMCommunityDistribution(installerOptions); case JavaDistribution.JetBrains: return new installer_13.JetBrainsDistribution(installerOptions); default: @@ -79069,23 +79072,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.GraalVMDistribution = void 0; +exports.GraalVMCommunityDistribution = exports.GraalVMDistribution = void 0; const core = __importStar(__nccwpck_require__(37484)); const tc = __importStar(__nccwpck_require__(33472)); const fs_1 = __importDefault(__nccwpck_require__(79896)); const path_1 = __importDefault(__nccwpck_require__(16928)); +const semver_1 = __importDefault(__nccwpck_require__(62088)); const base_installer_1 = __nccwpck_require__(79935); const http_client_1 = __nccwpck_require__(54844); const util_1 = __nccwpck_require__(54527); const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; const GRAALVM_DOWNLOAD_URL = 'https://www.graalvm.org/downloads/'; +const GRAALVM_COMMUNITY_RELEASES_URL = 'https://api.github.com/repos/graalvm/graalvm-ce-builds/releases?per_page=100'; +const GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN = 'https://api.github.com'; +const GRAALVM_COMMUNITY_DOWNLOAD_URL = 'https://github.com/graalvm/graalvm-ce-builds/releases'; +const GRAALVM_COMMUNITY_ASSET_PREFIX = 'graalvm-community-jdk-'; +const GRAALVM_COMMUNITY_VERSION_PATTERN = /^\d+(?:\.\d+)*$/; const IS_WINDOWS = process.platform === 'win32'; const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; const GRAALVM_MIN_VERSION = 17; const SUPPORTED_ARCHITECTURES = ['x64', 'aarch64']; class GraalVMDistribution extends base_installer_1.JavaBase { - constructor(installerOptions) { - super('GraalVM', installerOptions); + constructor(installerOptions, distributionName = 'GraalVM') { + super(distributionName, installerOptions); } downloadTool(javaRelease) { return __awaiter(this, void 0, void 0, function* () { @@ -79119,36 +79128,50 @@ class GraalVMDistribution extends base_installer_1.JavaBase { } findPackageForDownload(range) { return __awaiter(this, void 0, void 0, function* () { - // Add input validation - if (!range || typeof range !== 'string') { - throw new Error('Version range is required and must be a string'); - } - const arch = this.distributionArchitecture(); - if (!SUPPORTED_ARCHITECTURES.includes(arch)) { - throw new Error(`Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}`); - } + this.validateVersionRange(range); + const arch = this.getSupportedArchitecture(); if (!this.stable) { return this.findEABuildDownloadUrl(`${range}-ea`); } - if (this.packageType !== 'jdk') { - throw new Error('GraalVM provides only the `jdk` package type'); - } - const platform = this.getPlatform(); - const extension = (0, util_1.getDownloadArchiveExtension)(); - const major = range.includes('.') ? range.split('.')[0] : range; - const majorVersion = parseInt(major); - if (isNaN(majorVersion)) { - throw new Error(`Invalid version format: ${range}`); - } - if (majorVersion < GRAALVM_MIN_VERSION) { - throw new Error(`GraalVM is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}`); - } + const { platform, extension, major } = this.validateStableBuildRequest(range); const fileUrl = this.constructFileUrl(range, major, platform, arch, extension); const response = yield this.http.head(fileUrl); this.handleHttpResponse(response, range); return { url: fileUrl, version: range }; }); } + validateVersionRange(range) { + if (!range || typeof range !== 'string') { + throw new Error('Version range is required and must be a string'); + } + } + getSupportedArchitecture() { + const arch = this.distributionArchitecture(); + if (!SUPPORTED_ARCHITECTURES.includes(arch)) { + throw new Error(`Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}`); + } + return arch; + } + validateStableBuildRequest(range) { + if (this.packageType !== 'jdk') { + throw new Error(`${this.distribution} provides only the \`jdk\` package type`); + } + const platform = this.getPlatform(); + const extension = (0, util_1.getDownloadArchiveExtension)(); + const major = range.includes('.') ? range.split('.')[0] : range; + const majorVersion = parseInt(major); + if (isNaN(majorVersion)) { + throw new Error(`Invalid version format: ${range}`); + } + if (majorVersion < GRAALVM_MIN_VERSION) { + throw new Error(`${this.distribution} is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}`); + } + return { + platform, + major, + extension + }; + } constructFileUrl(range, major, platform, arch, extension) { return range.includes('.') ? `${GRAALVM_DL_BASE}/${major}/archive/graalvm-jdk-${range}_${platform}-${arch}_bin.${extension}` @@ -79239,6 +79262,91 @@ class GraalVMDistribution extends base_installer_1.JavaBase { } } exports.GraalVMDistribution = GraalVMDistribution; +class GraalVMCommunityDistribution extends GraalVMDistribution { + constructor(installerOptions) { + super(installerOptions, 'GraalVM Community'); + } + get toolcacheFolderName() { + return `Java_GraalVM_Community_${this.packageType}`; + } + findPackageForDownload(range) { + return __awaiter(this, void 0, void 0, function* () { + this.validateVersionRange(range); + if (!this.stable) { + throw new Error('GraalVM Community does not provide early access builds'); + } + const arch = this.getSupportedArchitecture(); + const { platform, extension } = this.validateStableBuildRequest(range); + // GraalVM Community asset names embed the platform, architecture and + // archive type, e.g. `graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz`. + const assetSuffix = `_${platform}-${arch}_bin.${extension}`; + const availableVersions = yield this.getAvailableVersions(assetSuffix); + const satisfiedVersion = availableVersions + .filter(item => (0, util_1.isVersionSatisfies)(range, item.version)) + .sort((a, b) => -semver_1.default.compareBuild(a.version, b.version))[0]; + if (!satisfiedVersion) { + const error = this.createVersionNotFoundError(range, availableVersions.map(item => item.version), `Platform: ${platform}`); + error.message += `\nPlease check if this version is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.`; + throw error; + } + return satisfiedVersion; + }); + } + getAvailableVersions(assetSuffix) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const headers = (0, util_1.getGitHubHttpHeaders)(); + const versions = new Map(); + let releasesUrl = GRAALVM_COMMUNITY_RELEASES_URL; + for (let pageIndex = 0; releasesUrl && pageIndex < util_1.MAX_PAGINATION_PAGES; pageIndex++) { + const response = yield this.http.getJson(releasesUrl, headers); + const releases = Array.isArray(response.result) ? response.result : []; + if (releases.length === 0) { + break; + } + for (const release of releases) { + if (release.draft || release.prerelease) { + continue; + } + for (const asset of (_a = release.assets) !== null && _a !== void 0 ? _a : []) { + const version = this.extractAssetVersion(asset.name, assetSuffix); + if (version) { + versions.set(version, { + version, + url: asset.browser_download_url + }); + } + } + } + releasesUrl = this.getNextReleasesUrl(response.headers); + } + return [...versions.values()]; + }); + } + // Returns the GraalVM JDK version encoded in a release asset name when it + // matches the requested platform/architecture/archive suffix, otherwise null. + extractAssetVersion(assetName, assetSuffix) { + if (!assetName.startsWith(GRAALVM_COMMUNITY_ASSET_PREFIX) || + !assetName.endsWith(assetSuffix)) { + return null; + } + const rawVersion = assetName.slice(GRAALVM_COMMUNITY_ASSET_PREFIX.length, -assetSuffix.length); + if (!GRAALVM_COMMUNITY_VERSION_PATTERN.test(rawVersion)) { + return null; + } + return (0, util_1.convertVersionToSemver)(rawVersion); + } + getNextReleasesUrl(headers) { + const nextUrl = (0, util_1.getNextPageUrlFromLinkHeader)(headers); + if (nextUrl && + !(0, util_1.validatePaginationUrl)(nextUrl, GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN)) { + core.warning(`Ignoring pagination link with unexpected origin: ${nextUrl}`); + return null; + } + return nextUrl; + } +} +exports.GraalVMCommunityDistribution = GraalVMCommunityDistribution; /***/ }), diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index f4838b349..d3a8e3128 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -10,6 +10,7 @@ - [Alibaba Dragonwell](#Alibaba-Dragonwell) - [SapMachine](#SapMachine) - [GraalVM](#GraalVM) + - [GraalVM Community](#GraalVM-Community) - [JetBrains](#JetBrains) - [Installing custom Java package type](#Installing-custom-Java-package-type) - [JavaFX Maven project](#JavaFX-Maven-project) @@ -174,6 +175,21 @@ steps: native-image --version ``` +### GraalVM Community +**NOTE:** GraalVM Community is available for stable JDK 17 and later releases. + +```yaml +steps: +- uses: actions/checkout@v6 +- uses: actions/setup-java@v5 + with: + distribution: 'graalvm-community' + java-version: '21' +- run: | + java -cp java HelloWorldApp + native-image -cp java HelloWorldApp +``` + ### JetBrains **NOTE:** JetBrains is only available for LTS versions on 11 or later (11, 17, 21, etc.). diff --git a/src/distributions/distribution-factory.ts b/src/distributions/distribution-factory.ts index 9cd459e65..0ff6597e1 100644 --- a/src/distributions/distribution-factory.ts +++ b/src/distributions/distribution-factory.ts @@ -11,7 +11,10 @@ import {CorrettoDistribution} from './corretto/installer'; import {OracleDistribution} from './oracle/installer'; import {DragonwellDistribution} from './dragonwell/installer'; import {SapMachineDistribution} from './sapmachine/installer'; -import {GraalVMDistribution} from './graalvm/installer'; +import { + GraalVMCommunityDistribution, + GraalVMDistribution +} from './graalvm/installer'; import {JetBrainsDistribution} from './jetbrains/installer'; enum JavaDistribution { @@ -29,6 +32,7 @@ enum JavaDistribution { Dragonwell = 'dragonwell', SapMachine = 'sapmachine', GraalVM = 'graalvm', + GraalVMCommunity = 'graalvm-community', JetBrains = 'jetbrains' } @@ -74,6 +78,8 @@ export function getJavaDistribution( return new SapMachineDistribution(installerOptions); case JavaDistribution.GraalVM: return new GraalVMDistribution(installerOptions); + case JavaDistribution.GraalVMCommunity: + return new GraalVMCommunityDistribution(installerOptions); case JavaDistribution.JetBrains: return new JetBrainsDistribution(installerOptions); default: diff --git a/src/distributions/graalvm/installer.ts b/src/distributions/graalvm/installer.ts index fea3b8f1e..2b2442f80 100644 --- a/src/distributions/graalvm/installer.ts +++ b/src/distributions/graalvm/installer.ts @@ -2,6 +2,7 @@ import * as core from '@actions/core'; import * as tc from '@actions/tool-cache'; import fs from 'fs'; import path from 'path'; +import semver from 'semver'; import {JavaBase} from '../base-installer'; import {HttpCodes} from '@actions/http-client'; import {GraalVMEAVersion} from './models'; @@ -11,14 +12,26 @@ import { JavaInstallerResults } from '../base-models'; import { + convertVersionToSemver, extractJdkFile, getDownloadArchiveExtension, getGitHubHttpHeaders, - renameWinArchive + getNextPageUrlFromLinkHeader, + isVersionSatisfies, + MAX_PAGINATION_PAGES, + renameWinArchive, + validatePaginationUrl } from '../../util'; const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; const GRAALVM_DOWNLOAD_URL = 'https://www.graalvm.org/downloads/'; +const GRAALVM_COMMUNITY_RELEASES_URL = + 'https://api.github.com/repos/graalvm/graalvm-ce-builds/releases?per_page=100'; +const GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN = 'https://api.github.com'; +const GRAALVM_COMMUNITY_DOWNLOAD_URL = + 'https://github.com/graalvm/graalvm-ce-builds/releases'; +const GRAALVM_COMMUNITY_ASSET_PREFIX = 'graalvm-community-jdk-'; +const GRAALVM_COMMUNITY_VERSION_PATTERN = /^\d+(?:\.\d+)*$/; const IS_WINDOWS = process.platform === 'win32'; const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; const GRAALVM_MIN_VERSION = 17; @@ -26,9 +39,23 @@ const SUPPORTED_ARCHITECTURES = ['x64', 'aarch64'] as const; type SupportedArchitecture = (typeof SUPPORTED_ARCHITECTURES)[number]; type OsVersions = 'linux' | 'macos' | 'windows'; +interface GraalVMCommunityAsset { + name: string; + browser_download_url: string; +} + +interface GraalVMCommunityRelease { + draft: boolean; + prerelease: boolean; + assets: GraalVMCommunityAsset[]; +} + export class GraalVMDistribution extends JavaBase { - constructor(installerOptions: JavaInstallerOptions) { - super('GraalVM', installerOptions); + constructor( + installerOptions: JavaInstallerOptions, + distributionName = 'GraalVM' + ) { + super(distributionName, installerOptions); } protected async downloadTool( @@ -85,11 +112,36 @@ export class GraalVMDistribution extends JavaBase { protected async findPackageForDownload( range: string ): Promise { - // Add input validation + this.validateVersionRange(range); + const arch = this.getSupportedArchitecture(); + + if (!this.stable) { + return this.findEABuildDownloadUrl(`${range}-ea`); + } + + const {platform, extension, major} = this.validateStableBuildRequest(range); + + const fileUrl = this.constructFileUrl( + range, + major, + platform, + arch, + extension + ); + + const response = await this.http.head(fileUrl); + this.handleHttpResponse(response, range); + + return {url: fileUrl, version: range}; + } + + protected validateVersionRange(range: string): void { if (!range || typeof range !== 'string') { throw new Error('Version range is required and must be a string'); } + } + protected getSupportedArchitecture(): SupportedArchitecture { const arch = this.distributionArchitecture(); if (!SUPPORTED_ARCHITECTURES.includes(arch as SupportedArchitecture)) { throw new Error( @@ -97,12 +149,18 @@ export class GraalVMDistribution extends JavaBase { ); } - if (!this.stable) { - return this.findEABuildDownloadUrl(`${range}-ea`); - } + return arch as SupportedArchitecture; + } + protected validateStableBuildRequest(range: string): { + platform: OsVersions; + extension: string; + major: string; + } { if (this.packageType !== 'jdk') { - throw new Error('GraalVM provides only the `jdk` package type'); + throw new Error( + `${this.distribution} provides only the \`jdk\` package type` + ); } const platform = this.getPlatform(); @@ -116,22 +174,15 @@ export class GraalVMDistribution extends JavaBase { if (majorVersion < GRAALVM_MIN_VERSION) { throw new Error( - `GraalVM is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}` + `${this.distribution} is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}` ); } - const fileUrl = this.constructFileUrl( - range, - major, + return { platform, - arch, + major, extension - ); - - const response = await this.http.head(fileUrl); - this.handleHttpResponse(response, range); - - return {url: fileUrl, version: range}; + }; } private constructFileUrl( @@ -280,3 +331,131 @@ export class GraalVMDistribution extends JavaBase { return result; } } + +export class GraalVMCommunityDistribution extends GraalVMDistribution { + constructor(installerOptions: JavaInstallerOptions) { + super(installerOptions, 'GraalVM Community'); + } + + protected get toolcacheFolderName(): string { + return `Java_GraalVM_Community_${this.packageType}`; + } + + protected async findPackageForDownload( + range: string + ): Promise { + this.validateVersionRange(range); + + if (!this.stable) { + throw new Error('GraalVM Community does not provide early access builds'); + } + + const arch = this.getSupportedArchitecture(); + const {platform, extension} = this.validateStableBuildRequest(range); + // GraalVM Community asset names embed the platform, architecture and + // archive type, e.g. `graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz`. + const assetSuffix = `_${platform}-${arch}_bin.${extension}`; + const availableVersions = await this.getAvailableVersions(assetSuffix); + + const satisfiedVersion = availableVersions + .filter(item => isVersionSatisfies(range, item.version)) + .sort((a, b) => -semver.compareBuild(a.version, b.version))[0]; + + if (!satisfiedVersion) { + const error = this.createVersionNotFoundError( + range, + availableVersions.map(item => item.version), + `Platform: ${platform}` + ); + error.message += `\nPlease check if this version is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.`; + throw error; + } + + return satisfiedVersion; + } + + private async getAvailableVersions( + assetSuffix: string + ): Promise { + const headers = getGitHubHttpHeaders(); + const versions = new Map(); + let releasesUrl: string | null = GRAALVM_COMMUNITY_RELEASES_URL; + + for ( + let pageIndex = 0; + releasesUrl && pageIndex < MAX_PAGINATION_PAGES; + pageIndex++ + ) { + const response = await this.http.getJson( + releasesUrl, + headers + ); + + const releases = Array.isArray(response.result) ? response.result : []; + if (releases.length === 0) { + break; + } + + for (const release of releases) { + if (release.draft || release.prerelease) { + continue; + } + + for (const asset of release.assets ?? []) { + const version = this.extractAssetVersion(asset.name, assetSuffix); + if (version) { + versions.set(version, { + version, + url: asset.browser_download_url + }); + } + } + } + + releasesUrl = this.getNextReleasesUrl(response.headers); + } + + return [...versions.values()]; + } + + // Returns the GraalVM JDK version encoded in a release asset name when it + // matches the requested platform/architecture/archive suffix, otherwise null. + private extractAssetVersion( + assetName: string, + assetSuffix: string + ): string | null { + if ( + !assetName.startsWith(GRAALVM_COMMUNITY_ASSET_PREFIX) || + !assetName.endsWith(assetSuffix) + ) { + return null; + } + + const rawVersion = assetName.slice( + GRAALVM_COMMUNITY_ASSET_PREFIX.length, + -assetSuffix.length + ); + + if (!GRAALVM_COMMUNITY_VERSION_PATTERN.test(rawVersion)) { + return null; + } + + return convertVersionToSemver(rawVersion); + } + + private getNextReleasesUrl( + headers: Record + ): string | null { + const nextUrl = getNextPageUrlFromLinkHeader(headers); + if ( + nextUrl && + !validatePaginationUrl(nextUrl, GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN) + ) { + core.warning( + `Ignoring pagination link with unexpected origin: ${nextUrl}` + ); + return null; + } + return nextUrl; + } +} From fa2c6508d1036292a5efdf642f98d1c695974c72 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 23 Jun 2026 13:23:45 -0400 Subject: [PATCH 27/57] docs: note jdkfile approach for Early Access / unreleased JDK builds (#1058) * docs: note jdkfile approach for Early Access / unreleased JDK builds Clarify in advanced-usage that the existing 'jdkfile' distribution can be used to install Early Access (EA) or other unreleased JDK builds not provided directly by setup-java, by downloading the archive in a prior step and pointing jdkFile at it. Adds a concrete EA example. Addresses #612. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/advanced-usage.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index d3a8e3128..b12a49b9e 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -286,6 +286,9 @@ steps: ## Installing Java from local file If your use-case requires a custom distribution or a version that is not provided by setup-java, you can download it manually and setup-java will take care of the installation and caching on the VM: +> [!NOTE] +> This approach also lets you use builds that setup-java does not provide directly, such as **Early Access (EA)** or other unreleased JDK builds (for example, an upcoming feature release or a Loom/Valhalla preview build). Download the desired archive in a prior step and point `jdkFile` at it; setup-java will extract, install, and cache it just like a supported distribution. When targeting multiple architectures, select the correct binary per architecture in your workflow (for example, with a build matrix). + ```yaml steps: - run: | @@ -301,6 +304,23 @@ steps: - run: java --version ``` +For example, to use an **Early Access** build from [jdk.java.net](https://jdk.java.net/), download the archive for your runner OS/architecture and install it via `distribution: 'jdkfile'` (example below assumes Linux x64): + +```yaml +steps: +- run: | + download_url="https://download.java.net/java/early_access/jdk25/36/GPL/openjdk-25-ea+36_linux-x64_bin.tar.gz" + wget -O $RUNNER_TEMP/java_package.tar.gz $download_url +- uses: actions/setup-java@v5 + with: + distribution: 'jdkfile' + jdkFile: ${{ runner.temp }}/java_package.tar.gz + java-version: '25.0.0-ea.36' + architecture: x64 + +- run: java --version +``` + If your use-case requires a custom distribution (in the example, alpine-linux is used) or a version that is not provided by setup-java and you want to always install the latest version during runtime, then you can use the following code to auto-download the latest JDK, determine the semver needed for setup-java, and setup-java will take care of the installation and caching on the VM: ```yaml From 1bcf9fb12cf4aa7d266a90ae39939e61372fe520 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 23 Jun 2026 13:37:44 -0400 Subject: [PATCH 28/57] dist: Address Copilot review suggestions from PR #1042 (GraalVM Community) (#1059) - installer: surface a clear error when the GraalVM Community releases listing is not a JSON array, instead of silently treating an error payload (rate limit, auth failure, etc.) as "no releases" which later surfaced as a misleading "version not found" error. - docs: fix the GraalVM Community advanced-usage example to check the installed binary versions (java/native-image --version) rather than running a non-existent HelloWorldApp classpath that fails when copied. - tests: cover the new non-array release listing error path. Rebuilt dist bundle. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- __tests__/distributors/graalvm-installer.test.ts | 14 ++++++++++++++ dist/setup/index.js | 12 +++++++++++- docs/advanced-usage.md | 4 ++-- src/distributions/graalvm/installer.ts | 15 ++++++++++++++- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/__tests__/distributors/graalvm-installer.test.ts b/__tests__/distributors/graalvm-installer.test.ts index 155e3d9ee..beefbd13f 100644 --- a/__tests__/distributors/graalvm-installer.test.ts +++ b/__tests__/distributors/graalvm-installer.test.ts @@ -1058,6 +1058,20 @@ describe('GraalVMDistribution', () => { 'GraalVM Community does not provide early access builds' ); }); + + it('should surface an error when the releases listing is not an array', async () => { + mockHttpClient.getJson.mockResolvedValue({ + result: {message: 'API rate limit exceeded'}, + statusCode: 403, + headers: {} + }); + + await expect( + (communityDistribution as any).findPackageForDownload('21') + ).rejects.toThrow( + /Unexpected response while listing GraalVM Community releases.*HTTP status code: 403/s + ); + }); }); }); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index 007b48497..bbf320ebb 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -79300,7 +79300,17 @@ class GraalVMCommunityDistribution extends GraalVMDistribution { let releasesUrl = GRAALVM_COMMUNITY_RELEASES_URL; for (let pageIndex = 0; releasesUrl && pageIndex < util_1.MAX_PAGINATION_PAGES; pageIndex++) { const response = yield this.http.getJson(releasesUrl, headers); - const releases = Array.isArray(response.result) ? response.result : []; + // A successful GitHub releases listing is always a JSON array (possibly + // empty). Anything else indicates an unexpected/error payload (rate + // limiting, auth failure, etc.) that must be surfaced instead of being + // silently treated as "no releases", which would later look like a + // misleading "version not found" error. + if (!Array.isArray(response.result)) { + throw new Error(`Unexpected response while listing GraalVM Community releases from ${releasesUrl} ` + + `(HTTP status code: ${response.statusCode}). Expected a JSON array of releases. ` + + `Please check if the service is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.`); + } + const releases = response.result; if (releases.length === 0) { break; } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index b12a49b9e..58301be99 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -186,8 +186,8 @@ steps: distribution: 'graalvm-community' java-version: '21' - run: | - java -cp java HelloWorldApp - native-image -cp java HelloWorldApp + java --version + native-image --version ``` ### JetBrains diff --git a/src/distributions/graalvm/installer.ts b/src/distributions/graalvm/installer.ts index 2b2442f80..861cf12a8 100644 --- a/src/distributions/graalvm/installer.ts +++ b/src/distributions/graalvm/installer.ts @@ -391,7 +391,20 @@ export class GraalVMCommunityDistribution extends GraalVMDistribution { headers ); - const releases = Array.isArray(response.result) ? response.result : []; + // A successful GitHub releases listing is always a JSON array (possibly + // empty). Anything else indicates an unexpected/error payload (rate + // limiting, auth failure, etc.) that must be surfaced instead of being + // silently treated as "no releases", which would later look like a + // misleading "version not found" error. + if (!Array.isArray(response.result)) { + throw new Error( + `Unexpected response while listing GraalVM Community releases from ${releasesUrl} ` + + `(HTTP status code: ${response.statusCode}). Expected a JSON array of releases. ` + + `Please check if the service is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.` + ); + } + + const releases = response.result; if (releases.length === 0) { break; } From 623c707d77e926f0f5a50f82e56da1a94d0326f0 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Fri, 26 Jun 2026 03:07:16 -0400 Subject: [PATCH 29/57] chore: enforce pre-PR validation (aggregate scripts, git hooks, PR checklist) (#1061) * chore: enforce pre-PR validation with aggregate scripts, git hooks, and PR checklist Add tooling to help contributors run the same checks as CI before submitting a pull request, reducing avoidable format/lint/build failures. - Add aggregate npm scripts: - `npm run check` runs format-check + lint + build + test (mirrors CI) - `npm run fix` runs format + lint:fix + build - Add husky + lint-staged git hooks (installed via `npm install`): - pre-commit formats and lints staged files - pre-push rebuilds dist/ and runs the test suite - Add a checklist item to the PR template prompting contributors to run `npm run check` locally - Document the aggregate scripts and hooks in docs/contributors.md dist/ is intentionally not auto-committed by CI to avoid pwn-request security risks; the existing `Check dist/` workflow continues to verify it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/pull_request_template.md | 1 + .husky/pre-commit | 4 + .husky/pre-push | 4 + docs/contributors.md | 11 + package-lock.json | 565 +++++++++++++++++++++++++++++++ package.json | 14 + 6 files changed, 599 insertions(+) create mode 100755 .husky/pre-commit create mode 100755 .husky/pre-push diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ef54acadd..dcf4beaaf 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,5 +5,6 @@ Describe your changes. Add link to the related issue. **Check list:** +- [ ] Ran `npm run check` locally (format, lint, build, test) and all checks pass. - [ ] Mark if documentation changes are required. - [ ] Mark if tests were added or updated to cover the changes. \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..d24fdfc60 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx lint-staged diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 000000000..7d6707f95 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run build && npm test diff --git a/docs/contributors.md b/docs/contributors.md index bfbbefeb9..771e3b7bf 100644 --- a/docs/contributors.md +++ b/docs/contributors.md @@ -63,6 +63,17 @@ Pull requests are the easiest way to contribute changes to git repos at GitHub. - To lint the code, **you need to run the `lint:fix` script** - To transpile source code to `javascript` we use [NCC](https://github.com/vercel/ncc). **It is very important to run the `build` script after making changes**, otherwise your changes will not get into the final `javascript` build +> [!TIP] +> Instead of running each script individually, you can run the aggregate scripts: +> +> - `npm run fix` — formats, lints (with autofix) and rebuilds the `dist/` bundle. +> - `npm run check` — runs the same validation as CI: `format-check`, `lint`, `build` and `test`. **Run this before pushing a pull request** to make sure it will pass the required checks. +> +> Git hooks are installed automatically when you run `npm install` (via [husky](https://typicode.github.io/husky/)): +> +> - a **pre-commit** hook formats and lints staged files with [lint-staged](https://github.com/lint-staged/lint-staged); +> - a **pre-push** hook rebuilds `dist/` and runs the test suite. + **Learn more about how to implement tests:** Adding or changing tests is an integral part of making a change to the code. diff --git a/package-lock.json b/package-lock.json index b753e6574..a831d2e34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,8 +30,10 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-jest": "^29.0.1", "eslint-plugin-node": "^11.1.0", + "husky": "^9.1.7", "jest": "^30.4.2", "jest-circus": "^30.4.2", + "lint-staged": "^17.0.8", "prettier": "^3.6.2", "ts-jest": "^29.4.11", "typescript": "^5.3.3" @@ -3222,6 +3224,85 @@ "dev": true, "license": "MIT" }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3403,6 +3484,19 @@ "dev": true, "license": "MIT" }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -3722,6 +3816,13 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -3983,6 +4084,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -4151,6 +4265,22 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -5218,6 +5348,138 @@ "dev": true, "license": "MIT" }, + "node_modules/lint-staged": { + "version": "17.0.8", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.8.tgz", + "integrity": "sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "listr2": "^10.2.1", + "picomatch": "^4.0.4", + "string-argv": "^0.3.2", + "tinyexec": "^1.2.4" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=22.22.1" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + }, + "optionalDependencies": { + "yaml": "^2.9.0" + } + }, + "node_modules/lint-staged/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/listr2": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.2.tgz", + "integrity": "sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.2.0", + "eventemitter3": "^5.0.4", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^10.0.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-10.0.0.tgz", + "integrity": "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "string-width": "^8.2.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5245,6 +5507,160 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -5302,6 +5718,19 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -5885,6 +6314,52 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5895,6 +6370,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -5981,6 +6463,52 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6032,6 +6560,16 @@ "node": ">=8" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6211,6 +6749,16 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -6675,6 +7223,23 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 757acdbc9..146ff1ff0 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,22 @@ "format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"", "lint": "eslint --config ./.eslintrc.js \"**/*.ts\"", "lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix", + "check": "npm run format-check && npm run lint && npm run build && npm test", + "fix": "npm run format && npm run lint:fix && npm run build", + "prepare": "husky install", "prerelease": "npm run-script build", "release": "git add -f dist/setup/index.js dist/cleanup/index.js", "test": "jest" }, + "lint-staged": { + "*.ts": [ + "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write", + "eslint --config ./.eslintrc.js --fix" + ], + "*.{yml,yaml}": [ + "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write" + ] + }, "repository": { "type": "git", "url": "git+https://github.com/actions/setup-java.git" @@ -50,8 +62,10 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-jest": "^29.0.1", "eslint-plugin-node": "^11.1.0", + "husky": "^9.1.7", "jest": "^30.4.2", "jest-circus": "^30.4.2", + "lint-staged": "^17.0.8", "prettier": "^3.6.2", "ts-jest": "^29.4.11", "typescript": "^5.3.3" From c5f2f2ea960c17e4f9a7e5f06303cb104dcfdeaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 09:12:17 +0100 Subject: [PATCH 30/57] Bump github/codeql-action from 3 to 4 (#1069) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 11a0f96a6..e329917af 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -42,7 +42,7 @@ jobs: - name: Upload SARIF results to code scanning if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: zizmor.sarif category: zizmor From aff09c223000f470d258a6c6f74385de76dedc9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 09:12:58 +0100 Subject: [PATCH 31/57] Bump actions/checkout from 6 to 7 (#1068) Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index e329917af..3bdb7a378 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -23,7 +23,7 @@ jobs: security-events: write # to upload SARIF results to code scanning steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false From bf1fac860b818bcfb803ea3b95f532774aea3f4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 09:13:25 +0100 Subject: [PATCH 32/57] Bump actions/setup-python from 5 to 6 (#1067) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 3bdb7a378..79e470f62 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -28,7 +28,7 @@ jobs: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' From e9339ddc843e9dbd34bf59acac2d761ea4cf6bcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 09:13:56 +0100 Subject: [PATCH 33/57] Bump @typescript-eslint/parser from 8.61.1 to 8.62.0 (#1062) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.61.1 to 8.62.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-version: 8.62.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 70 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index a831d2e34..3c1577bab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/node": "^26.0.0", "@types/semver": "^7.5.8", "@typescript-eslint/eslint-plugin": "^8.48.0", - "@typescript-eslint/parser": "^8.61.1", + "@typescript-eslint/parser": "^8.62.0", "@vercel/ncc": "^0.44.0", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8", @@ -2074,16 +2074,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", - "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.0.tgz", + "integrity": "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/scope-manager": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/typescript-estree": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", "debug": "^4.4.3" }, "engines": { @@ -2099,14 +2099,14 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", - "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz", + "integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.61.1", - "@typescript-eslint/types": "^8.61.1", + "@typescript-eslint/tsconfig-utils": "^8.62.0", + "@typescript-eslint/types": "^8.62.0", "debug": "^4.4.3" }, "engines": { @@ -2121,14 +2121,14 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", - "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz", + "integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1" + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2139,9 +2139,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", - "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz", + "integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==", "dev": true, "license": "MIT", "engines": { @@ -2156,9 +2156,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", - "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz", + "integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==", "dev": true, "license": "MIT", "engines": { @@ -2170,16 +2170,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", - "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz", + "integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.61.1", - "@typescript-eslint/tsconfig-utils": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/project-service": "8.62.0", + "@typescript-eslint/tsconfig-utils": "8.62.0", + "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/visitor-keys": "8.62.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2198,13 +2198,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", - "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", + "version": "8.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz", + "integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/types": "8.62.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { diff --git a/package.json b/package.json index 146ff1ff0..64f2bf53a 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@types/node": "^26.0.0", "@types/semver": "^7.5.8", "@typescript-eslint/eslint-plugin": "^8.48.0", - "@typescript-eslint/parser": "^8.61.1", + "@typescript-eslint/parser": "^8.62.0", "@vercel/ncc": "^0.44.0", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8", From b150355f04079948b130c89635f82eb738274afe Mon Sep 17 00:00:00 2001 From: John <1615532+johnoliver@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:19:49 +0100 Subject: [PATCH 34/57] feat: Add verify-signature plumbing and Temurin+Microsoft verification support (#1060) * Add verify-signature plumbing and Temurin verification support * Rebuild dist after signature verification changes * Refine signature verification errors and regenerate dist * refactor: make gpg.ts generic, move Adoptium-specific constant to temurin distribution * fix: mock renameWinArchive in temurin tests and add signature e2e job * refactor: bundle Adoptium public key, replace keyserver lookup with local import * feat: add verify-signature-public-key input to allow custom GPG key override * refactor: extract Adoptium public key to adoptium-key.ts; tighten gpg.ts cleanup scope * Add verify-signature plumbing and Temurin verification support * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Add Microsoft signature verification support * Regenerate dist bundles for Microsoft signature checks * Harden Microsoft signature URL handling * Add setup-java-microsoft-signature-verification e2e job * chore: regenerate dist files * Fix e2e-versions: remove duplicate job, update signature jobs to checkout@v7 with env vars * Fix Prettier formatting in test files * fix: mock renameWinArchive in microsoft-installer tests to fix Windows CI failure * fix: use --homedir flag instead of GNUPGHOME env var for Windows GPG compatibility The Git-bundled GPG on Windows (MSYS2-based) does not automatically convert Windows-style paths in environment variables like GNUPGHOME. This caused GPG to fail with exit code 2 when verifying Microsoft JDK signatures on Windows, because the GNUPGHOME path (D:\a\_temp\...) was not recognized as a valid POSIX path. Fix: pass --homedir as an explicit command-line argument to both gpg --import and gpg --verify. MSYS2 does correctly convert Windows paths in command-line arguments, so this approach works reliably on Windows, Linux, and macOS. * fix: convert Windows paths to POSIX format for MSYS2 GPG on Windows The Git-bundled GPG on Windows (C:\Program Files\Git\usr\bin\gpg.exe) is an MSYS2-based binary that uses POSIX path conventions internally. When Windows-style paths with backslashes and drive letters (D:\a\_temp\...) are passed as arguments, GPG may fail to resolve them correctly, resulting in a fatal error (exit code 2). Fix: add a toGpgPath() helper that converts Windows paths to MSYS2 POSIX format (/d/a/_temp/...) before passing them to any gpg command. On Linux and macOS the helper is a no-op. Applied to all four paths used in verifyPackageSignature: - gpgHome (--homedir argument) - publicKeyFile (--import argument) - signaturePath (--verify signature argument) - archivePath (--verify data argument) * Fix gpg test formatting --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- .github/workflows/e2e-versions.yml | 56 ++++ README.md | 4 + __tests__/data/temurin.json | 297 ++++++++++++------ __tests__/distributors/base-installer.test.ts | 18 ++ .../distributors/microsoft-installer.test.ts | 159 +++++++++- .../distributors/temurin-installer.test.ts | 114 ++++++- __tests__/gpg.test.ts | 78 +++++ action.yml | 7 + dist/cleanup/index.js | 62 +++- dist/setup/index.js | 208 +++++++++++- src/constants.ts | 2 + src/distributions/base-installer.ts | 14 + src/distributions/base-models.ts | 3 + src/distributions/microsoft/installer.ts | 37 ++- src/distributions/microsoft/microsoft-key.ts | 21 ++ src/distributions/temurin/adoptium-key.ts | 33 ++ src/distributions/temurin/installer.ts | 33 +- src/distributions/temurin/models.ts | 1 + src/gpg.ts | 68 ++++ src/setup-java.ts | 14 + 20 files changed, 1117 insertions(+), 112 deletions(-) create mode 100644 src/distributions/microsoft/microsoft-key.ts create mode 100644 src/distributions/temurin/adoptium-key.ts diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index c384d4023..4ea83d8b8 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -328,6 +328,62 @@ jobs: run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash + setup-java-temurin-signature-verification: + name: temurin ${{ matrix.version }} signature verification - ${{ matrix.os }} + needs: setup-java-major-minor-versions + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + version: ['21', '17'] + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + persist-credentials: false + - name: setup-java with signature verification + uses: ./ + id: setup-java + with: + java-version: ${{ matrix.version }} + distribution: temurin + verify-signature: true + - name: Verify Java + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" + shell: bash + + setup-java-microsoft-signature-verification: + name: microsoft ${{ matrix.version }} signature verification - ${{ matrix.os }} + needs: setup-java-major-minor-versions + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + version: ['21', '17'] + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + persist-credentials: false + - name: setup-java with signature verification + uses: ./ + id: setup-java + with: + java-version: ${{ matrix.version }} + distribution: microsoft + verify-signature: true + - name: Verify Java + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" + shell: bash + setup-java-ea-versions-sapmachine: name: sapmachine ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} needs: setup-java-major-minor-versions diff --git a/README.md b/README.md index b79760e94..53f7f70ad 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,10 @@ For more details, see the full release notes on the [releases page](https://git - `check-latest`: Setting this option makes the action to check for the latest available version for the version spec. + - `verify-signature`: Verifies downloaded Java package signatures when supported by the selected distribution. Currently supported for `temurin` and `microsoft`. If set to `true` for unsupported distributions, the action fails. + + - `verify-signature-public-key`: ASCII-armored GPG public key used to verify the downloaded package signature. Overrides the default bundled key for the selected distribution. + - `cache`: Quick [setup caching](#caching-packages-dependencies) for the dependencies managed through one of the predefined package managers. It can be one of "maven", "gradle" or "sbt". - `cache-dependency-path`: The path to a dependency file: pom.xml, build.gradle, build.sbt, etc. This option can be used with the `cache` option. If this option is omitted, the action searches for the dependency file in the entire repository. This option supports wildcards and a list of file names for caching multiple dependencies. diff --git a/__tests__/data/temurin.json b/__tests__/data/temurin.json index 7ce00d2c7..cec2c631a 100644 --- a/__tests__/data/temurin.json +++ b/__tests__/data/temurin.json @@ -15,7 +15,8 @@ "link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_linux_hotspot_16.0.2_7.tar.gz", "metadata_link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_linux_hotspot_16.0.2_7.tar.gz.json", "name": "OpenJDK16U-jdk_x64_linux_hotspot_16.0.2_7.tar.gz", - "size": 205463525 + "size": 205463525, + "signature_link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_linux_hotspot_16.0.2_7.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-16.0.2+7_adopt", @@ -44,7 +45,8 @@ "link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_mac_hotspot_16.0.2_7.tar.gz", "metadata_link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_mac_hotspot_16.0.2_7.tar.gz.json", "name": "OpenJDK16U-jdk_x64_mac_hotspot_16.0.2_7.tar.gz", - "size": 206621395 + "size": 206621395, + "signature_link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_mac_hotspot_16.0.2_7.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-16.0.2+7_adopt", @@ -73,7 +75,8 @@ "link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_windows_hotspot_16.0.2_7.zip", "metadata_link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_windows_hotspot_16.0.2_7.zip.json", "name": "OpenJDK16U-jdk_x64_windows_hotspot_16.0.2_7.zip", - "size": 203448494 + "size": 203448494, + "signature_link": "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_windows_hotspot_16.0.2_7.zip.sig" }, "project": "jdk", "scm_ref": "jdk-16.0.2+7_adopt", @@ -113,7 +116,8 @@ "link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u302b08.tar.gz", "metadata_link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u302b08.tar.gz.json", "name": "OpenJDK8U-jdk_x64_linux_hotspot_8u302b08.tar.gz", - "size": 102954777 + "size": 102954777, + "signature_link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u302b08.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk8u302-b08", @@ -142,7 +146,8 @@ "link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_mac_hotspot_8u302b08.tar.gz", "metadata_link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_mac_hotspot_8u302b08.tar.gz.json", "name": "OpenJDK8U-jdk_x64_mac_hotspot_8u302b08.tar.gz", - "size": 107303398 + "size": 107303398, + "signature_link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_mac_hotspot_8u302b08.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk8u302-b08", @@ -171,7 +176,8 @@ "link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_windows_hotspot_8u302b08.zip", "metadata_link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_windows_hotspot_8u302b08.zip.json", "name": "OpenJDK8U-jdk_x64_windows_hotspot_8u302b08.zip", - "size": 104297671 + "size": 104297671, + "signature_link": "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_windows_hotspot_8u302b08.zip.sig" }, "project": "jdk", "scm_ref": "jdk8u302-b08", @@ -211,7 +217,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-31-00-07.tar.gz", - "size": 188909250 + "size": 188909250, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-16-ge39bf269d60", @@ -240,7 +247,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-31-00-07.tar.gz", - "size": 192952713 + "size": 192952713, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-219-ge39bf269d60", @@ -260,7 +268,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-31-00-07.tar.gz", - "size": 188816971 + "size": 188816971, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-16-ge39bf269d60", @@ -280,7 +289,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-31-00-07.tar.gz", - "size": 182299353 + "size": 182299353, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-219-ge39bf269d60", @@ -300,7 +310,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-31-00-07.tar.gz", - "size": 187674392 + "size": 187674392, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-46-gea8d2c72e83", @@ -320,7 +331,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-31-00-07.tar.gz", - "size": 179501342 + "size": 179501342, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-21-ge39bf269d60", @@ -340,7 +352,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-29-23-34.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-29-23-34.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-29-23-34.tar.gz", - "size": 192126971 + "size": 192126971, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-29-23-34.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-219-ge39bf269d60", @@ -360,7 +373,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-31-00-07.tar.gz", - "size": 192015878 + "size": 192015878, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-21-ge39bf269d60", @@ -389,7 +403,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-31-00-07.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-31-00-07.tar.gz.json", "name": "OpenJDK17-jdk_x64_mac_hotspot_2021-07-31-00-07.tar.gz", - "size": 192422068 + "size": 192422068, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-31-00-07.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-219-ge39bf269d60", @@ -418,7 +433,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-31-00-07.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-31-00-07.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-31-00-07.zip", - "size": 188694175 + "size": 188694175, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-31-00-07.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-219-ge39bf269d60", @@ -447,7 +463,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-31-00-07.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-31-00-07.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-31-00-07.zip", - "size": 184618115 + "size": 184618115, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-31-00-07.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+33_adopt-219-ge39bf269d60", @@ -489,7 +506,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-27-23-34.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-27-23-34.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-27-23-34.tar.gz", - "size": 192125161 + "size": 192125161, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-31-00-07-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-27-23-34.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-242-gce1857dd7a1", @@ -531,7 +549,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-23-03-09.tar.gz", - "size": 188911467 + "size": 188911467, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-42-g4596b4e9d4e", @@ -560,7 +579,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-23-03-09.tar.gz", - "size": 192950510 + "size": 192950510, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-217-g4596b4e9d4e", @@ -580,7 +600,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-23-03-09.tar.gz", - "size": 188815685 + "size": 188815685, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-36-g4596b4e9d4e", @@ -600,7 +621,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-23-03-09.tar.gz", - "size": 182307654 + "size": 182307654, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-217-g4596b4e9d4e", @@ -620,7 +642,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-23-03-09.tar.gz", - "size": 187698851 + "size": 187698851, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-36-g4596b4e9d4e", @@ -640,7 +663,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-23-03-09.tar.gz", - "size": 179501218 + "size": 179501218, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-40-g4596b4e9d4e", @@ -660,7 +684,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-22-23-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-22-23-30.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-22-23-30.tar.gz", - "size": 192124471 + "size": 192124471, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-22-23-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-217-g4596b4e9d4e", @@ -680,7 +705,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-23-03-09.tar.gz", - "size": 192015026 + "size": 192015026, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-40-g4596b4e9d4e", @@ -709,7 +735,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-23-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-23-03-09.tar.gz.json", "name": "OpenJDK17-jdk_x64_mac_hotspot_2021-07-23-03-09.tar.gz", - "size": 193003513 + "size": 193003513, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-23-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-217-g4596b4e9d4e", @@ -738,7 +765,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-23-03-09.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-23-03-09.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-23-03-09.zip", - "size": 188694996 + "size": 188694996, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-23-03-09.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-217-g4596b4e9d4e", @@ -767,7 +795,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-23-03-09.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-23-03-09.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-23-03-09.zip", - "size": 184626937 + "size": 184626937, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-23-03-09-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-23-03-09.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+32_adopt-217-g4596b4e9d4e", @@ -809,7 +838,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-21-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-21-03-09.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-21-03-09.tar.gz", - "size": 188891565 + "size": 188891565, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-21-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "a342f28aaec", @@ -829,7 +859,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-21-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-21-03-09.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-21-03-09.tar.gz", - "size": 188790907 + "size": 188790907, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-21-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "a342f28aaec", @@ -849,7 +880,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-21-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-21-03-09.tar.gz.json", "name": "OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-21-03-09.tar.gz", - "size": 182276594 + "size": 182276594, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-21-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-191-ga342f28aaec", @@ -869,7 +901,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-21-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-21-03-09.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-21-03-09.tar.gz", - "size": 187678422 + "size": 187678422, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-21-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-26-ga342f28aaec", @@ -889,7 +922,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-21-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-21-03-09.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-21-03-09.tar.gz", - "size": 179475721 + "size": 179475721, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-21-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-26-ga342f28aaec", @@ -909,7 +943,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-20-23-34.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-20-23-34.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-20-23-34.tar.gz", - "size": 192104689 + "size": 192104689, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-20-23-34.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-191-ga342f28aaec", @@ -929,7 +964,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-21-03-09.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-21-03-09.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-21-03-09.tar.gz", - "size": 191982821 + "size": 191982821, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-21-03-09.tar.gz.sig" }, "project": "jdk", "scm_ref": "a342f28aaec", @@ -958,7 +994,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-21-03-09.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-21-03-09.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-21-03-09.zip", - "size": 188685330 + "size": 188685330, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-21-03-09.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-191-ga342f28aaec", @@ -987,7 +1024,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-21-03-09.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-21-03-09.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-21-03-09.zip", - "size": 184607457 + "size": 184607457, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-21-03-09-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-21-03-09.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-191-ga342f28aaec", @@ -1029,7 +1067,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-16-10-58.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-16-10-58.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-16-10-58.tar.gz", - "size": 188896773 + "size": 188896773, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-16-10-58.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-14-g20418a26958", @@ -1058,7 +1097,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-16-10-58.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-16-10-58.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-16-10-58.tar.gz", - "size": 192948484 + "size": 192948484, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-16-10-58.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-179-g20418a26958", @@ -1078,7 +1118,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-16-10-58.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-16-10-58.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-16-10-58.tar.gz", - "size": 188791964 + "size": 188791964, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-16-10-58.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-14-g20418a26958", @@ -1098,7 +1139,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-16-10-58.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-16-10-58.tar.gz.json", "name": "OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-16-10-58.tar.gz", - "size": 182281944 + "size": 182281944, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-16-10-58.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-179-g20418a26958", @@ -1118,7 +1160,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-16-10-58.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-16-10-58.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-16-10-58.tar.gz", - "size": 187650365 + "size": 187650365, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-16-10-58.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-23-g20418a26958", @@ -1138,7 +1181,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-16-10-58.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-16-10-58.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-16-10-58.tar.gz", - "size": 179483622 + "size": 179483622, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-16-10-58.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-55-g20418a26958", @@ -1158,7 +1202,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-15-23-34.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-15-23-34.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-15-23-34.tar.gz", - "size": 192108731 + "size": 192108731, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-15-23-34.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-179-g20418a26958", @@ -1178,7 +1223,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-16-10-58.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-16-10-58.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-16-10-58.tar.gz", - "size": 191985123 + "size": 191985123, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-16-10-58.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-14-g20418a26958", @@ -1207,7 +1253,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-16-10-58.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-16-10-58.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-16-10-58.zip", - "size": 188688539 + "size": 188688539, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-16-10-58.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-179-g20418a26958", @@ -1236,7 +1283,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-16-10-58.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-16-10-58.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-16-10-58.zip", - "size": 184612045 + "size": 184612045, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-16-10-58-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-16-10-58.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+31_adopt-179-g20418a26958", @@ -1278,7 +1326,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-14-11-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-14-11-30.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-14-11-30.tar.gz", - "size": 188899456 + "size": 188899456, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-14-11-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "ce22197617d", @@ -1307,7 +1356,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-14-11-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-14-11-30.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-14-11-30.tar.gz", - "size": 192953428 + "size": 192953428, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-14-11-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-200-gce22197617d", @@ -1327,7 +1377,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-14-11-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-14-11-30.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-14-11-30.tar.gz", - "size": 188792852 + "size": 188792852, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-14-11-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-66-gce22197617d", @@ -1347,7 +1398,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-14-11-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-14-11-30.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-14-11-30.tar.gz", - "size": 187678600 + "size": 187678600, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-14-11-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "ce22197617d", @@ -1367,7 +1419,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-14-11-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-14-11-30.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-14-11-30.tar.gz", - "size": 179480996 + "size": 179480996, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-14-11-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "ce22197617d", @@ -1387,7 +1440,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-13-23-34.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-13-23-34.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-13-23-34.tar.gz", - "size": 192105446 + "size": 192105446, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-13-23-34.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-200-gce22197617d", @@ -1407,7 +1461,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-14-11-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-14-11-30.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-14-11-30.tar.gz", - "size": 191986856 + "size": 191986856, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-14-11-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "ce22197617d", @@ -1436,7 +1491,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-14-11-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-14-11-30.tar.gz.json", "name": "OpenJDK17-jdk_x64_mac_hotspot_2021-07-14-11-30.tar.gz", - "size": 192995067 + "size": 192995067, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-14-11-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-200-gce22197617d", @@ -1465,7 +1521,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-14-11-30.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-14-11-30.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-14-11-30.zip", - "size": 188686556 + "size": 188686556, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-14-11-30.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-200-gce22197617d", @@ -1494,7 +1551,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-14-11-30.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-14-11-30.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-14-11-30.zip", - "size": 184620492 + "size": 184620492, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-14-11-30-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-14-11-30.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-200-gce22197617d", @@ -1536,7 +1594,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-09-12-54.tar.gz", - "size": 188896299 + "size": 188896299, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-164-g3e7e5bc2003", @@ -1565,7 +1624,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-09-12-54.tar.gz", - "size": 192941671 + "size": 192941671, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-164-g3e7e5bc2003", @@ -1585,7 +1645,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-09-12-54.tar.gz", - "size": 188838708 + "size": 188838708, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-161-g3e7e5bc2003", @@ -1605,7 +1666,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-09-12-54.tar.gz", - "size": 182274073 + "size": 182274073, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-40-g3e7e5bc2003", @@ -1625,7 +1687,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-09-12-54.tar.gz", - "size": 187666608 + "size": 187666608, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-40-g3e7e5bc2003", @@ -1645,7 +1708,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-09-12-54.tar.gz", - "size": 179472325 + "size": 179472325, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-30-g3e7e5bc2003", @@ -1665,7 +1729,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-08-23-35.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-08-23-35.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-08-23-35.tar.gz", - "size": 192098387 + "size": 192098387, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-08-23-35.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-164-g3e7e5bc2003", @@ -1685,7 +1750,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-09-12-54.tar.gz", - "size": 191983708 + "size": 191983708, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-164-g3e7e5bc2003", @@ -1714,7 +1780,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-09-12-54.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-09-12-54.tar.gz.json", "name": "OpenJDK17-jdk_x64_mac_hotspot_2021-07-09-12-54.tar.gz", - "size": 193004476 + "size": 193004476, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-09-12-54.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-164-g3e7e5bc2003", @@ -1743,7 +1810,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-09-12-54.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-09-12-54.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-09-12-54.zip", - "size": 188681640 + "size": 188681640, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-09-12-54.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-164-g3e7e5bc2003", @@ -1772,7 +1840,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-09-12-54.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-09-12-54.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-09-12-54.zip", - "size": 184605514 + "size": 184605514, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-09-12-54-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-09-12-54.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+30_adopt-164-g3e7e5bc2003", @@ -1823,7 +1892,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-07-11-35.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-07-11-35.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-07-11-35.tar.gz", - "size": 192962903 + "size": 192962903, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-07-11-35.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-172-g06428c22b61", @@ -1843,7 +1913,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-07-11-35.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-07-11-35.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-07-11-35.tar.gz", - "size": 188800217 + "size": 188800217, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-07-11-35.tar.gz.sig" }, "project": "jdk", "scm_ref": "06428c22b61", @@ -1863,7 +1934,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-07-11-35.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-07-11-35.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-07-11-35.tar.gz", - "size": 187663978 + "size": 187663978, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-07-11-35.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-78-g06428c22b61", @@ -1883,7 +1955,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-07-11-35.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-07-11-35.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-07-11-35.tar.gz", - "size": 179496669 + "size": 179496669, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-07-11-35.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-78-g06428c22b61", @@ -1903,7 +1976,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-06-23-34.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-06-23-34.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-06-23-34.tar.gz", - "size": 192113242 + "size": 192113242, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-06-23-34.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-172-g06428c22b61", @@ -1923,7 +1997,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-07-11-35.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-07-11-35.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-07-11-35.tar.gz", - "size": 192021951 + "size": 192021951, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-07-11-35.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-172-g06428c22b61", @@ -1952,7 +2027,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-07-11-35.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-07-11-35.tar.gz.json", "name": "OpenJDK17-jdk_x64_mac_hotspot_2021-07-07-11-35.tar.gz", - "size": 193018554 + "size": 193018554, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-07-11-35.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-172-g06428c22b61", @@ -1981,7 +2057,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-07-11-35.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-07-11-35.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-07-11-35.zip", - "size": 188702463 + "size": 188702463, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-07-11-35-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-07-11-35.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-172-g06428c22b61", @@ -2023,7 +2100,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-02-12-00.tar.gz", - "size": 188953178 + "size": 188953178, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2052,7 +2130,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-02-12-00.tar.gz", - "size": 192957934 + "size": 192957934, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2072,7 +2151,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-07-02-12-00.tar.gz", - "size": 188797466 + "size": 188797466, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-43-g6d3debb5c12", @@ -2092,7 +2172,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-02-12-00.tar.gz", - "size": 182292580 + "size": 182292580, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2112,7 +2193,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-02-12-00.tar.gz", - "size": 187684930 + "size": 187684930, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-64-g6d3debb5c12", @@ -2132,7 +2214,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-07-02-12-00.tar.gz", - "size": 179484390 + "size": 179484390, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-54-g6d3debb5c12", @@ -2152,7 +2235,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-01-23-30.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-01-23-30.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-01-23-30.tar.gz", - "size": 192114561 + "size": 192114561, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-07-01-23-30.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2172,7 +2256,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-07-02-12-00.tar.gz", - "size": 192014644 + "size": 192014644, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2201,7 +2286,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-02-12-00.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-02-12-00.tar.gz.json", "name": "OpenJDK17-jdk_x64_mac_hotspot_2021-07-02-12-00.tar.gz", - "size": 192425033 + "size": 192425033, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-07-02-12-00.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2230,7 +2316,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-02-12-00.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-02-12-00.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-07-02-12-00.zip", - "size": 188697393 + "size": 188697393, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-07-02-12-00.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2259,7 +2346,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-02-12-00.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-02-12-00.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-02-12-00.zip", - "size": 184618232 + "size": 184618232, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-07-02-12-00-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-07-02-12-00.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+29_adopt-138-g6d3debb5c12", @@ -2301,7 +2389,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_linux_hotspot_2021-06-30-09-16.tar.gz", - "size": 188940191 + "size": 188940191, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_aarch64_linux_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+28_adopt-130-g0fe0d0825e7", @@ -2330,7 +2419,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_aarch64_mac_hotspot_2021-06-30-09-16.tar.gz", - "size": 192953718 + "size": 192953718, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_aarch64_mac_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+28_adopt-130-g0fe0d0825e7", @@ -2350,7 +2440,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_arm_linux_hotspot_2021-06-30-09-16.tar.gz", - "size": 188795343 + "size": 188795343, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_arm_linux_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "0fe0d0825e7", @@ -2370,7 +2461,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_ppc64_aix_hotspot_2021-06-30-09-16.tar.gz", - "size": 182285782 + "size": 182285782, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_ppc64_aix_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "0fe0d0825e7", @@ -2390,7 +2482,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_ppc64le_linux_hotspot_2021-06-30-09-16.tar.gz", - "size": 187652430 + "size": 187652430, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_ppc64le_linux_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "0fe0d0825e7", @@ -2410,7 +2503,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_s390x_linux_hotspot_2021-06-30-09-16.tar.gz", - "size": 179489547 + "size": 179489547, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_s390x_linux_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "0fe0d0825e7", @@ -2430,7 +2524,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-06-29-23-33.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-06-29-23-33.tar.gz.json", "name": "OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-06-29-23-33.tar.gz", - "size": 192109453 + "size": 192109453, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_alpine-linux_hotspot_2021-06-29-23-33.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+28_adopt-130-g0fe0d0825e7", @@ -2450,7 +2545,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_x64_linux_hotspot_2021-06-30-09-16.tar.gz", - "size": 192013559 + "size": 192013559, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_linux_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+28_adopt-130-g0fe0d0825e7", @@ -2479,7 +2575,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-06-30-09-16.tar.gz", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-06-30-09-16.tar.gz.json", "name": "OpenJDK17-jdk_x64_mac_hotspot_2021-06-30-09-16.tar.gz", - "size": 192419518 + "size": 192419518, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_mac_hotspot_2021-06-30-09-16.tar.gz.sig" }, "project": "jdk", "scm_ref": "jdk-17+28_adopt-130-g0fe0d0825e7", @@ -2508,7 +2605,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-06-30-09-16.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-06-30-09-16.zip.json", "name": "OpenJDK17-jdk_x64_windows_hotspot_2021-06-30-09-16.zip", - "size": 188672489 + "size": 188672489, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x64_windows_hotspot_2021-06-30-09-16.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+28_adopt-130-g0fe0d0825e7", @@ -2537,7 +2635,8 @@ "link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-06-30-09-16.zip", "metadata_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-06-30-09-16.zip.json", "name": "OpenJDK17-jdk_x86-32_windows_hotspot_2021-06-30-09-16.zip", - "size": 184626094 + "size": 184626094, + "signature_link": "https://github.com/adoptium/temurin17-binaries/releases/download/jdk17-2021-06-30-09-16-beta/OpenJDK17-jdk_x86-32_windows_hotspot_2021-06-30-09-16.zip.sig" }, "project": "jdk", "scm_ref": "jdk-17+28_adopt-132-g23fbf51b850", diff --git a/__tests__/distributors/base-installer.test.ts b/__tests__/distributors/base-installer.test.ts index 2a0b13ab1..7497b06a3 100644 --- a/__tests__/distributors/base-installer.test.ts +++ b/__tests__/distributors/base-installer.test.ts @@ -464,6 +464,24 @@ describe('setupJava', () => { } ); + it('should fail when verify-signature is enabled for unsupported distributions', async () => { + mockJavaBase = new EmptyJavaBase({ + version: '11', + architecture: 'x86', + packageType: 'jdk', + checkLatest: false, + verifySignature: true + }); + + await expect(mockJavaBase.setupJava()).rejects.toThrow( + "Input 'verify-signature' is not supported for distribution 'Empty'." + ); + expect(spyTcFindAllVersions).not.toHaveBeenCalled(); + expect(spyCoreAddPath).not.toHaveBeenCalled(); + expect(spyCoreExportVariable).not.toHaveBeenCalled(); + expect(spyCoreSetOutput).not.toHaveBeenCalled(); + }); + it.each([ [ { diff --git a/__tests__/distributors/microsoft-installer.test.ts b/__tests__/distributors/microsoft-installer.test.ts index ca5a253af..d2527f6f7 100644 --- a/__tests__/distributors/microsoft-installer.test.ts +++ b/__tests__/distributors/microsoft-installer.test.ts @@ -1,8 +1,15 @@ -import {MicrosoftDistributions} from '../../src/distributions/microsoft/installer'; +import { + MicrosoftDistributions, + MICROSOFT_PUBLIC_KEY +} from '../../src/distributions/microsoft/installer'; import os from 'os'; import data from '../data/microsoft.json'; import * as httpm from '@actions/http-client'; import * as core from '@actions/core'; +import * as tc from '@actions/tool-cache'; +import * as gpg from '../../src/gpg'; +import * as util from '../../src/util'; +import fs from 'fs'; describe('findPackageForDownload', () => { let distribution: MicrosoftDistributions; @@ -102,6 +109,7 @@ describe('findPackageForDownload', () => { .replace('{{OS_TYPE}}', os) .replace('{{ARCHIVE_TYPE}}', archive); expect(result.url).toBe(url); + expect(result.signatureUrl).toBe(`${url}.sig`); }); it.each([ @@ -187,4 +195,153 @@ describe('findPackageForDownload', () => { /No matching version found for SemVer */ ); }); + + it('uses manifest-provided signature URL when available', async () => { + spyGetManifestFromRepo.mockReturnValue({ + result: [ + { + version: '17.0.10', + stable: true, + release_url: 'https://example.test', + files: [ + { + filename: 'microsoft-jdk-17.0.10-linux-x64.tar.gz', + arch: 'x64', + platform: 'linux', + download_url: 'https://example.test/jdk.tar.gz', + signature_url: 'https://example.test/jdk.tar.gz.custom.sig' + } + ] + } + ], + statusCode: 200, + headers: {} + }); + jest.spyOn(os, 'platform').mockReturnValue('linux'); + + const result = await distribution['findPackageForDownload']('17.0.10'); + + expect(result.signatureUrl).toBe( + 'https://example.test/jdk.tar.gz.custom.sig' + ); + }); +}); + +describe('downloadTool', () => { + let spyDownloadTool: jest.SpyInstance; + let spyExtractJdkFile: jest.SpyInstance; + let spyCacheDir: jest.SpyInstance; + let spyVerifySignature: jest.SpyInstance; + let distribution: MicrosoftDistributions; + + beforeEach(() => { + jest + .spyOn(os, 'platform') + .mockReturnValue(process.platform as ReturnType); + + distribution = new MicrosoftDistributions({ + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false + }); + + spyDownloadTool = jest.spyOn(tc, 'downloadTool'); + spyDownloadTool.mockImplementation(async () => { + return '/tmp/jdk.tar.gz'; + }); + + spyExtractJdkFile = jest.spyOn(util, 'extractJdkFile'); + spyExtractJdkFile.mockImplementation(async () => { + return '/tmp/unpacked'; + }); + + jest.spyOn(fs, 'readdirSync').mockReturnValue(['jdk'] as any); + spyCacheDir = jest.spyOn(tc, 'cacheDir'); + spyCacheDir.mockImplementation(async () => { + return '/tmp/cached'; + }); + + jest + .spyOn(util, 'renameWinArchive') + .mockImplementation((archivePath: string) => `${archivePath}.zip`); + + spyVerifySignature = jest.spyOn(gpg, 'verifyPackageSignature'); + spyVerifySignature.mockImplementation(async () => {}); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('verifies signature when enabled', async () => { + const signedDistribution = new MicrosoftDistributions({ + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false, + verifySignature: true + }); + + await signedDistribution['downloadTool']({ + version: '17.0.14+7', + url: 'https://example.com/jdk.tar.gz', + signatureUrl: 'https://example.com/jdk.tar.gz.sig' + }); + + expect(spyVerifySignature).toHaveBeenCalledWith( + '/tmp/jdk.tar.gz', + 'https://example.com/jdk.tar.gz.sig', + MICROSOFT_PUBLIC_KEY + ); + }); + + it('uses custom public key when verifySignaturePublicKey is provided', async () => { + const customKey = + '-----BEGIN PGP PUBLIC KEY BLOCK-----\ncustom\n-----END PGP PUBLIC KEY BLOCK-----'; + const signedDistribution = new MicrosoftDistributions({ + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false, + verifySignature: true, + verifySignaturePublicKey: customKey + }); + + await signedDistribution['downloadTool']({ + version: '17.0.14+7', + url: 'https://example.com/jdk.tar.gz', + signatureUrl: 'https://example.com/jdk.tar.gz.sig' + }); + + expect(spyVerifySignature).toHaveBeenCalledWith( + '/tmp/jdk.tar.gz', + 'https://example.com/jdk.tar.gz.sig', + customKey + ); + }); + + it('fails when signature is missing and verification is enabled', async () => { + const signedDistribution = new MicrosoftDistributions({ + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false, + verifySignature: true + }); + + await expect( + signedDistribution['downloadTool']({ + version: '17.0.14+7', + url: 'https://example.com/jdk.tar.gz' + }) + ).rejects.toThrow( + "Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version 17.0.14+7." + ); + expect(spyVerifySignature).not.toHaveBeenCalled(); + }); + + it('supports signature verification', () => { + expect(distribution['supportsSignatureVerification']()).toBe(true); + }); }); diff --git a/__tests__/distributors/temurin-installer.test.ts b/__tests__/distributors/temurin-installer.test.ts index 161a2d087..ddf707eb5 100644 --- a/__tests__/distributors/temurin-installer.test.ts +++ b/__tests__/distributors/temurin-installer.test.ts @@ -1,10 +1,15 @@ import {HttpClient} from '@actions/http-client'; +import * as tc from '@actions/tool-cache'; +import fs from 'fs'; import os from 'os'; import { TemurinDistribution, - TemurinImplementation + TemurinImplementation, + ADOPTIUM_PUBLIC_KEY } from '../../src/distributions/temurin/installer'; import {JavaInstallerOptions} from '../../src/distributions/base-models'; +import * as util from '../../src/util'; +import * as gpg from '../../src/gpg'; import manifestData from '../data/temurin.json'; import * as core from '@actions/core'; @@ -231,6 +236,7 @@ describe('findPackageForDownload', () => { distribution['getAvailableVersions'] = async () => manifestData as any; const resolvedVersion = await distribution['findPackageForDownload'](input); expect(resolvedVersion.version).toBe(expected); + expect(resolvedVersion.signatureUrl).toBeDefined(); }); it('version is found but binaries list is empty', async () => { @@ -281,3 +287,109 @@ describe('findPackageForDownload', () => { ); }); }); + +describe('downloadTool', () => { + let spyDownloadTool: jest.SpyInstance; + let spyVerifySignature: jest.SpyInstance; + let spyExtractJdkFile: jest.SpyInstance; + let spyCacheDir: jest.SpyInstance; + let spyReadDirSync: jest.SpyInstance; + let spyRenameWinArchive: jest.SpyInstance; + + beforeEach(() => { + spyDownloadTool = jest.spyOn(tc, 'downloadTool'); + spyDownloadTool.mockResolvedValue('/tmp/jdk.tar.gz'); + spyVerifySignature = jest.spyOn(gpg, 'verifyPackageSignature'); + spyVerifySignature.mockResolvedValue(undefined); + spyExtractJdkFile = jest.spyOn(util, 'extractJdkFile'); + spyExtractJdkFile.mockResolvedValue('/tmp/extracted'); + spyCacheDir = jest.spyOn(tc, 'cacheDir'); + spyCacheDir.mockResolvedValue('/tmp/toolcache'); + spyReadDirSync = jest.spyOn(fs, 'readdirSync'); + spyReadDirSync.mockReturnValue(['jdk-17'] as any); + spyRenameWinArchive = jest.spyOn(util, 'renameWinArchive'); + spyRenameWinArchive.mockReturnValue('/tmp/jdk.tar.gz.zip'); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + it('verifies signature when enabled', async () => { + const distribution = new TemurinDistribution( + { + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false, + verifySignature: true + }, + TemurinImplementation.Hotspot + ); + + await distribution['downloadTool']({ + version: '17.0.14+7', + url: 'https://example.com/jdk.tar.gz', + signatureUrl: 'https://example.com/jdk.tar.gz.sig' + }); + + expect(spyVerifySignature).toHaveBeenCalledWith( + '/tmp/jdk.tar.gz', + 'https://example.com/jdk.tar.gz.sig', + ADOPTIUM_PUBLIC_KEY + ); + }); + + it('fails when signature is missing and verification is enabled', async () => { + const distribution = new TemurinDistribution( + { + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false, + verifySignature: true + }, + TemurinImplementation.Hotspot + ); + + await expect( + distribution['downloadTool']({ + version: '17.0.14+7', + url: 'https://example.com/jdk.tar.gz' + }) + ).rejects.toThrow( + "Input 'verify-signature' is enabled, but no signature URL was found" + ); + expect(spyVerifySignature).not.toHaveBeenCalled(); + }); + + it('uses custom public key when verifySignaturePublicKey is provided', async () => { + const customKey = + '-----BEGIN PGP PUBLIC KEY BLOCK-----\ncustom\n-----END PGP PUBLIC KEY BLOCK-----'; + const distribution = new TemurinDistribution( + { + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false, + verifySignature: true, + verifySignaturePublicKey: customKey + }, + TemurinImplementation.Hotspot + ); + + await distribution['downloadTool']({ + version: '17.0.14+7', + url: 'https://example.com/jdk.tar.gz', + signatureUrl: 'https://example.com/jdk.tar.gz.sig' + }); + + expect(spyVerifySignature).toHaveBeenCalledWith( + '/tmp/jdk.tar.gz', + 'https://example.com/jdk.tar.gz.sig', + customKey + ); + }); +}); diff --git a/__tests__/gpg.test.ts b/__tests__/gpg.test.ts index 1c981b3f5..bfc5be741 100644 --- a/__tests__/gpg.test.ts +++ b/__tests__/gpg.test.ts @@ -1,6 +1,7 @@ import * as path from 'path'; import * as io from '@actions/io'; import * as exec from '@actions/exec'; +import * as tc from '@actions/tool-cache'; import * as gpg from '../src/gpg'; jest.mock('@actions/exec', () => { @@ -9,6 +10,12 @@ jest.mock('@actions/exec', () => { }; }); +jest.mock('@actions/tool-cache', () => { + return { + downloadTool: jest.fn() + }; +}); + const tempDir = path.join(__dirname, 'runner', 'temp'); process.env['RUNNER_TEMP'] = tempDir; @@ -25,6 +32,35 @@ describe('gpg tests', () => { } }); + describe('toGpgPath', () => { + const originalPlatform = process.platform; + + afterEach(() => { + Object.defineProperty(process, 'platform', {value: originalPlatform}); + }); + + it('returns path unchanged on non-Windows platforms', () => { + Object.defineProperty(process, 'platform', {value: 'linux'}); + expect(gpg.toGpgPath('/tmp/some/path')).toBe('/tmp/some/path'); + expect(gpg.toGpgPath('D:\\a\\_temp\\file')).toBe('D:\\a\\_temp\\file'); + }); + + it('converts Windows backslashes and drive letter to POSIX path on Windows', () => { + Object.defineProperty(process, 'platform', {value: 'win32'}); + expect(gpg.toGpgPath('D:\\a\\_temp\\gpg-home')).toBe( + '/d/a/_temp/gpg-home' + ); + expect( + gpg.toGpgPath('C:\\Users\\runner\\AppData\\Local\\Temp\\key.asc') + ).toBe('/c/Users/runner/AppData/Local/Temp/key.asc'); + }); + + it('handles uppercase and lowercase drive letters on Windows', () => { + Object.defineProperty(process, 'platform', {value: 'win32'}); + expect(gpg.toGpgPath('d:\\a\\_temp\\file')).toBe('/d/a/_temp/file'); + }); + }); + describe('importKey', () => { it('attempts to import private key and returns null key id on failure', async () => { const privateKey = 'KEY CONTENTS'; @@ -51,5 +87,47 @@ describe('gpg tests', () => { expect.anything() ); }); + + describe('verifyPackageSignature', () => { + it('imports bundled key and verifies package', async () => { + const publicKeyContent = + '-----BEGIN PGP PUBLIC KEY BLOCK-----\ntest\n-----END PGP PUBLIC KEY BLOCK-----'; + (tc.downloadTool as jest.Mock).mockResolvedValue('/tmp/jdk.tar.gz.sig'); + await gpg.verifyPackageSignature( + '/tmp/jdk.tar.gz', + 'https://example.com/jdk.tar.gz.sig', + publicKeyContent + ); + + expect(tc.downloadTool).toHaveBeenCalledWith( + 'https://example.com/jdk.tar.gz.sig' + ); + expect(exec.exec).toHaveBeenNthCalledWith( + 1, + 'gpg', + [ + '--homedir', + expect.any(String), + '--batch', + '--import', + expect.stringContaining('public-key.asc') + ], + expect.objectContaining({silent: true}) + ); + expect(exec.exec).toHaveBeenNthCalledWith( + 2, + 'gpg', + [ + '--homedir', + expect.any(String), + '--batch', + '--verify', + '/tmp/jdk.tar.gz.sig', + '/tmp/jdk.tar.gz' + ], + expect.objectContaining({silent: true}) + ); + }); + }); }); }); diff --git a/action.yml b/action.yml index d5f46bbed..cc1a541b2 100644 --- a/action.yml +++ b/action.yml @@ -26,6 +26,13 @@ inputs: description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec' required: false default: false + verify-signature: + description: 'Verify downloaded Java package signatures when supported by the selected distribution' + required: false + default: false + verify-signature-public-key: + description: 'ASCII-armored GPG public key used to verify the downloaded package signature. Overrides the default bundled key for the selected distribution.' + required: false server-id: description: 'ID of the distributionManagement repository in the pom.xml file. Default is `github`' diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 0c3278164..d962852b5 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52241,7 +52241,7 @@ else { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -52250,6 +52250,8 @@ exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; exports.INPUT_JDK_FILE = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; +exports.INPUT_VERIFY_SIGNATURE = 'verify-signature'; +exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; exports.INPUT_SERVER_ID = 'server-id'; exports.INPUT_SERVER_USERNAME = 'server-username'; exports.INPUT_SERVER_PASSWORD = 'server-password'; @@ -52311,14 +52313,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.deleteKey = exports.importKey = exports.PRIVATE_KEY_FILE = void 0; +exports.verifyPackageSignature = exports.deleteKey = exports.importKey = exports.toGpgPath = exports.PRIVATE_KEY_FILE = void 0; const fs = __importStar(__nccwpck_require__(79896)); const path = __importStar(__nccwpck_require__(16928)); const io = __importStar(__nccwpck_require__(94994)); const exec = __importStar(__nccwpck_require__(95236)); +const tc = __importStar(__nccwpck_require__(33472)); const util = __importStar(__nccwpck_require__(54527)); exports.PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc'); const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/; +// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...). +// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions +// internally. Passing Windows paths with backslashes can cause fatal GPG errors +// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows. +function toGpgPath(p) { + if (process.platform !== 'win32') + return p; + return p + .replace(/\\/g, '/') + .replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`); +} +exports.toGpgPath = toGpgPath; function importKey(privateKey) { return __awaiter(this, void 0, void 0, function* () { fs.writeFileSync(exports.PRIVATE_KEY_FILE, privateKey, { @@ -52355,6 +52370,49 @@ function deleteKey(keyFingerprint) { }); } exports.deleteKey = deleteKey; +function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) { + return __awaiter(this, void 0, void 0, function* () { + const signaturePath = yield tc.downloadTool(signatureUrl); + let gpgHome; + try { + gpgHome = fs.mkdtempSync(path.join(util.getTempDir(), 'verify-signature-gpg-home-')); + } + catch (error) { + try { + yield io.rmRF(signaturePath); + } + catch (_a) { + // ignore cleanup failures + } + throw new Error(`Failed to create temporary GPG home directory for signature verification: ${error.message}`); + } + try { + const publicKeyFile = path.join(gpgHome, 'public-key.asc'); + fs.writeFileSync(publicKeyFile, publicKeyContent, { encoding: 'utf-8' }); + const options = { silent: true }; + yield exec.exec('gpg', [ + '--homedir', + toGpgPath(gpgHome), + '--batch', + '--import', + toGpgPath(publicKeyFile) + ], options); + yield exec.exec('gpg', [ + '--homedir', + toGpgPath(gpgHome), + '--batch', + '--verify', + toGpgPath(signaturePath), + toGpgPath(archivePath) + ], options); + } + finally { + yield io.rmRF(signaturePath); + yield io.rmRF(gpgHome); + } + }); +} +exports.verifyPackageSignature = verifyPackageSignature; /***/ }), diff --git a/dist/setup/index.js b/dist/setup/index.js index bbf320ebb..54ced2b5f 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78000,7 +78000,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -78009,6 +78009,8 @@ exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; exports.INPUT_JDK_FILE = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; +exports.INPUT_VERIFY_SIGNATURE = 'verify-signature'; +exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; exports.INPUT_SERVER_ID = 'server-id'; exports.INPUT_SERVER_USERNAME = 'server-username'; exports.INPUT_SERVER_PASSWORD = 'server-password'; @@ -78308,6 +78310,7 @@ const constants_1 = __nccwpck_require__(27242); const os_1 = __importDefault(__nccwpck_require__(70857)); class JavaBase { constructor(distribution, installerOptions) { + var _a; this.distribution = distribution; this.http = new httpm.HttpClient('actions/setup-java', undefined, { allowRetries: true, @@ -78317,10 +78320,15 @@ class JavaBase { this.architecture = installerOptions.architecture || os_1.default.arch(); this.packageType = installerOptions.packageType; this.checkLatest = installerOptions.checkLatest; + this.verifySignature = (_a = installerOptions.verifySignature) !== null && _a !== void 0 ? _a : false; + this.verifySignaturePublicKey = installerOptions.verifySignaturePublicKey; } setupJava() { var _a, _b; return __awaiter(this, void 0, void 0, function* () { + if (this.verifySignature && !this.supportsSignatureVerification()) { + throw new Error(`Input 'verify-signature' is not supported for distribution '${this.distribution}'.`); + } let foundJava = this.findInToolcache(); if (foundJava && !this.checkLatest) { core.info(`Resolved Java ${foundJava.version} from tool-cache`); @@ -78440,6 +78448,9 @@ class JavaBase { get toolcacheFolderName() { return `Java_${this.distribution}_${this.packageType}`; } + supportsSignatureVerification() { + return false; + } getToolcacheVersionName(version) { if (!this.stable) { if (version.includes('+')) { @@ -79912,21 +79923,38 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.MicrosoftDistributions = void 0; +exports.MicrosoftDistributions = exports.MICROSOFT_PUBLIC_KEY = void 0; const base_installer_1 = __nccwpck_require__(79935); const util_1 = __nccwpck_require__(54527); +const gpg = __importStar(__nccwpck_require__(88343)); +const microsoft_key_1 = __nccwpck_require__(56286); const core = __importStar(__nccwpck_require__(37484)); const tc = __importStar(__nccwpck_require__(33472)); const fs_1 = __importDefault(__nccwpck_require__(79896)); const path_1 = __importDefault(__nccwpck_require__(16928)); +var microsoft_key_2 = __nccwpck_require__(56286); +Object.defineProperty(exports, "MICROSOFT_PUBLIC_KEY", ({ enumerable: true, get: function () { return microsoft_key_2.MICROSOFT_PUBLIC_KEY; } })); class MicrosoftDistributions extends base_installer_1.JavaBase { constructor(installerOptions) { super('Microsoft', installerOptions); } downloadTool(javaRelease) { + var _a; return __awaiter(this, void 0, void 0, function* () { core.info(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`); let javaArchivePath = yield tc.downloadTool(javaRelease.url); + if (this.verifySignature) { + if (!javaRelease.signatureUrl) { + throw new Error(`Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version ${javaRelease.version}.`); + } + core.info(`Verifying Java package signature...`); + try { + yield gpg.verifyPackageSignature(javaArchivePath, javaRelease.signatureUrl, (_a = this.verifySignaturePublicKey) !== null && _a !== void 0 ? _a : microsoft_key_1.MICROSOFT_PUBLIC_KEY); + } + catch (error) { + throw new Error(`Failed to verify signature for Microsoft Build of OpenJDK version ${javaRelease.version}. Signature URL: ${javaRelease.signatureUrl}. Error: ${error.message}`); + } + } core.info(`Extracting Java archive...`); const extension = (0, util_1.getDownloadArchiveExtension)(); if (process.platform === 'win32') { @@ -79940,6 +79968,7 @@ class MicrosoftDistributions extends base_installer_1.JavaBase { }); } findPackageForDownload(range) { + var _a; return __awaiter(this, void 0, void 0, function* () { const arch = this.distributionArchitecture(); if (arch !== 'x64' && arch !== 'aarch64') { @@ -79960,12 +79989,18 @@ class MicrosoftDistributions extends base_installer_1.JavaBase { const availableVersionStrings = manifest.map(item => item.version); throw this.createVersionNotFoundError(range, availableVersionStrings); } + const file = foundRelease.files[0]; + const signatureUrl = (_a = file.signature_url) !== null && _a !== void 0 ? _a : `${file.download_url}.sig`; return { - url: foundRelease.files[0].download_url, + url: file.download_url, + signatureUrl, version: foundRelease.version }; }); } + supportsSignatureVerification() { + return true; + } getAvailableVersions() { return __awaiter(this, void 0, void 0, function* () { // TODO get these dynamically! @@ -80008,6 +80043,38 @@ class MicrosoftDistributions extends base_installer_1.JavaBase { exports.MicrosoftDistributions = MicrosoftDistributions; +/***/ }), + +/***/ 56286: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.MICROSOFT_PUBLIC_KEY = void 0; +// Microsoft Build of OpenJDK GPG signing key +// Retrieved from: https://download.visualstudio.microsoft.com/download/pr/b90071e2-e0cf-4411-98be-dbeb09d67bf0/8622862bcd54206e158c5abca0582c9b/464279_464280_aoc_20210208.asc +exports.MICROSOFT_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: BSN Pgp v1.1.0.0 + +mQENBGAhlWcBCADCQjj6huLTenvZSLej35e9YKEHm4lix2uvPOONexMaU8V2v7KL +RGdoXF7jwHci7efnPZ+9zpS2+g3rhvv8M7yWy9E/1psEtGzvmp1IL/qIabMEQqi+ +UlhPGh7MQ/BkXAlic8Dyl3XYqr0EXS11iCiTr6Zkxs9Ee4V54gxL4gogRn4wk9sl +/nrjgDzMsUwla0pynoQQvYpqCdiAr3gKKllT1skCDqgVOMMyZxsx9HjZxg/3AJz6 +r5i512L2R+3Hkv+XmxT+mnGBCFcny0DM7PjNXEmIK3ZSkro1tQML90zx3Fyh5esx +fpVvuIXGFV75o35VVCBZoiD3hcfOnIJsPQ9nABEBAAG0OE1pY3Jvc29mdCBKYXZh +IEVuZ2luZWVyaW5nIDxqYXZhcGxhdGluZnJhQG1pY3Jvc29mdC5jb20+iQE4BBMB +CAAiBQJgIZVnAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRA1Ux0xWyHB +icwTCACJO2FGNocNvdUtAb+eDKuGwt0chAJdCES2ZtgBScwrwDyWpxpRznoXWBHL +MJeLyxJoKsCG3vVlY4uh48psCzVm3OKvi7MCPT955t8W6TzfSBxTpjR8zRgJkjPJ +EGhHTlusUfz7TtM5etJF0qscSJH1grcNsgtee97mk4QyEzT8Di83NQmYxKcBrliq +yK/SWWt8VkTyYAEO6L5PoB4L9r8ka27uQs+jgCw+/Z0JMtNmmhyNGY3+a1YtPeoy +JdQaI9LphfKGbVaz6SK2aol7vj+c2TG3TLUYdOYGMH1OZlri2GTkCVjwna2GC7p4 +Fa133tP85xzJEq1XeXm8WeLFo2wV +=rHCS +-----END PGP PUBLIC KEY BLOCK-----`; + + /***/ }), /***/ 11182: @@ -80558,6 +80625,50 @@ class SemeruDistribution extends base_installer_1.JavaBase { exports.SemeruDistribution = SemeruDistribution; +/***/ }), + +/***/ 80877: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ADOPTIUM_PUBLIC_KEY = void 0; +// Adoptium GPG signing key (fingerprint: 3B04D753C9050D9A5D343F39843C48A565F8F04B) +// Retrieved from: https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3B04D753C9050D9A5D343F39843C48A565F8F04B +exports.ADOPTIUM_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBGGTvTQBCAC6ey144n7CG8foafF6mwgIBN1fIm1ILZDuGS4tMr0/XI8pgJnT +QvsPxZWEvtSm7bEMObzEoZJcXwjBcJl1B0ui8k5kHMTI75gCmZPsoKLFWIEpuRBQ +PBocusw80apDmLnNDQLVQvDFtEua5gaNa/fRw9YsmBoXBqvgrjFUIdGyWoQvH5+a +9OYlWD9n5VV0gnVMb+aclwVzB/zJw3kHGSgzuMtlAHeQiah7Y8yomQn/UIX8yqDf ++11sP3+c87YcjkRqImRTtmKEDcEtGPAIXC6SYA+uEEkbYE0Fy0chkvtnVWJ597fa +Epai4rnICU8zoJ6X5z3v1aM2WerhX9oq9X8PABEBAAHNQEFkb3B0aXVtIEdQRyBL +ZXkgKERFQi9SUE0gU2lnbmluZyBLZXkpIDx0ZW11cmluLWRldkBlY2xpcHNlLm9y +Zz7CwJIEEwEIADwWIQQ7BNdTyQUNml00PzmEPEilZfjwSwUCYZO9NAIbAwULCQgH +AgMiAgEGFQoJCAsCBBYCAwECHgcCF4AACgkQhDxIpWX48Et4AggAjjJzYWuKV3nG +7ngInngl8G/m9JoHr7BmwgcQXYhdy5hVkMcUx5JLeXz2LMBUH/F2nD595hgjMabk +kVib20X8lq9RsNbdfc2hBcWU6qyHKxsIqT4boI2/XDyEzzMyyZWWNGo/27Ci7Xmj +pWu31nh0pDdPqdyWDIKojbVVnxlCRY8as8Sm+1ufi709KCi4MuwHNsUlCSwb/fju +NKeHkrHbLcHKUUIEcmTSKRWrpMYBzm1HYOGBz4xPuELwUfUp71ehfoyBZlp6RDRf +l5TYI1FmCyHuvjNhrJgWv7bOTcf8yObGY+TEUhzc4xQqCrF4ur9d3opvsuPBQsv+ +Klqi5KSZgs7ATQRhk700AQgAq14okly8cFrpYVenEQPiB75AUZfKRpMduiR6IxAj +SKcH7aSoFZ9AubUEBVpZsyT5svxoEPe1i4TdbF+m9FGy42EcOlLa3ArLTj5H8FRl +UdGZB9I5mk4GptOzPM+aHMMu92vW/ZwjuS8DvOiQSp+cUmG1EqOMJSM7e/4BM71z +E+OKaVJCj79pEzhG3SK/IC/OlxxyETT66NSfYJd7Sw5R6Vr19am/uNU690W0CJ+q +VQeFpmDMr7LnfdFRIh+lJe05+PvWXeidkGjox5cbG52wf8aRIR/FgkfcFvqRMN1f +B+dVOWueloUeVAnzcUznOKmUEs7LP9ObJhYHHgup4IAU2wARAQABwsB2BBgBCAAg +FiEEOwTXU8kFDZpdND85hDxIpWX48EsFAmGTvTQCGwwACgkQhDxIpWX48EvXHQf/ +Q0nZsGDXnZHiBoojeSdpkO7WBjMIP3w1GdLvRpPQrS8TfOPbZuoevzCNh38Y3gwF +yelJspvzDQrBXhgkzAGlucYg8Y7KHa5Ebm7iDgMzc37L1hYSZTYCqwd7aowfgy34 +hOk3B67LffkJpIh738Oa9CtlwxQ9xcytmBmQ1fBBOwm/9IhAwHPQuydYIs4DxWbj +0MGSP4fDntU7e4UjsHNmhudDcYol0FaqdHHIIB9C/G4CzetRwHFOn3b4JwXMU7YU +6aJA3mXhi3hggMC3wkT2HHZ/TquuOdNc02fypWOCDOHz0alBBJNqoVUNFNqU3tfJ +wI4qF/KKq9BfyfucAs0ykA== +=XLag +-----END PGP PUBLIC KEY BLOCK-----`; + + /***/ }), /***/ 91986: @@ -80601,14 +80712,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.TemurinDistribution = exports.TemurinImplementation = void 0; +exports.TemurinDistribution = exports.TemurinImplementation = exports.ADOPTIUM_PUBLIC_KEY = void 0; const core = __importStar(__nccwpck_require__(37484)); const tc = __importStar(__nccwpck_require__(33472)); const fs_1 = __importDefault(__nccwpck_require__(79896)); const path_1 = __importDefault(__nccwpck_require__(16928)); const semver_1 = __importDefault(__nccwpck_require__(62088)); +const gpg = __importStar(__nccwpck_require__(88343)); +const adoptium_key_1 = __nccwpck_require__(80877); const base_installer_1 = __nccwpck_require__(79935); const util_1 = __nccwpck_require__(54527); +var adoptium_key_2 = __nccwpck_require__(80877); +Object.defineProperty(exports, "ADOPTIUM_PUBLIC_KEY", ({ enumerable: true, get: function () { return adoptium_key_2.ADOPTIUM_PUBLIC_KEY; } })); var TemurinImplementation; (function (TemurinImplementation) { TemurinImplementation["Hotspot"] = "Hotspot"; @@ -80633,7 +80748,8 @@ class TemurinDistribution extends base_installer_1.JavaBase { : item.version_data.semver.replace('-beta+', '+'); return { version: formattedVersion, - url: item.binaries[0].package.link + url: item.binaries[0].package.link, + signatureUrl: item.binaries[0].package.signature_link }; }); const satisfiedVersions = availableVersionsWithBinaries @@ -80650,9 +80766,22 @@ class TemurinDistribution extends base_installer_1.JavaBase { }); } downloadTool(javaRelease) { + var _a; return __awaiter(this, void 0, void 0, function* () { core.info(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`); let javaArchivePath = yield tc.downloadTool(javaRelease.url); + if (this.verifySignature) { + if (!javaRelease.signatureUrl) { + throw new Error(`Input 'verify-signature' is enabled, but no signature URL was found for Temurin version ${javaRelease.version}.`); + } + core.info(`Verifying Java package signature...`); + try { + yield gpg.verifyPackageSignature(javaArchivePath, javaRelease.signatureUrl, (_a = this.verifySignaturePublicKey) !== null && _a !== void 0 ? _a : adoptium_key_1.ADOPTIUM_PUBLIC_KEY); + } + catch (error) { + throw new Error(`Failed to verify signature for Temurin version ${javaRelease.version} from ${javaRelease.signatureUrl}: ${error.message}`); + } + } core.info(`Extracting Java archive...`); const extension = (0, util_1.getDownloadArchiveExtension)(); if (process.platform === 'win32') { @@ -80669,6 +80798,9 @@ class TemurinDistribution extends base_installer_1.JavaBase { get toolcacheFolderName() { return super.toolcacheFolderName; } + supportsSignatureVerification() { + return true; + } getAvailableVersions() { return __awaiter(this, void 0, void 0, function* () { const platform = this.getPlatformOption(); @@ -80961,14 +81093,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.deleteKey = exports.importKey = exports.PRIVATE_KEY_FILE = void 0; +exports.verifyPackageSignature = exports.deleteKey = exports.importKey = exports.toGpgPath = exports.PRIVATE_KEY_FILE = void 0; const fs = __importStar(__nccwpck_require__(79896)); const path = __importStar(__nccwpck_require__(16928)); const io = __importStar(__nccwpck_require__(94994)); const exec = __importStar(__nccwpck_require__(95236)); +const tc = __importStar(__nccwpck_require__(33472)); const util = __importStar(__nccwpck_require__(54527)); exports.PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc'); const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/; +// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...). +// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions +// internally. Passing Windows paths with backslashes can cause fatal GPG errors +// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows. +function toGpgPath(p) { + if (process.platform !== 'win32') + return p; + return p + .replace(/\\/g, '/') + .replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`); +} +exports.toGpgPath = toGpgPath; function importKey(privateKey) { return __awaiter(this, void 0, void 0, function* () { fs.writeFileSync(exports.PRIVATE_KEY_FILE, privateKey, { @@ -81005,6 +81150,49 @@ function deleteKey(keyFingerprint) { }); } exports.deleteKey = deleteKey; +function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) { + return __awaiter(this, void 0, void 0, function* () { + const signaturePath = yield tc.downloadTool(signatureUrl); + let gpgHome; + try { + gpgHome = fs.mkdtempSync(path.join(util.getTempDir(), 'verify-signature-gpg-home-')); + } + catch (error) { + try { + yield io.rmRF(signaturePath); + } + catch (_a) { + // ignore cleanup failures + } + throw new Error(`Failed to create temporary GPG home directory for signature verification: ${error.message}`); + } + try { + const publicKeyFile = path.join(gpgHome, 'public-key.asc'); + fs.writeFileSync(publicKeyFile, publicKeyContent, { encoding: 'utf-8' }); + const options = { silent: true }; + yield exec.exec('gpg', [ + '--homedir', + toGpgPath(gpgHome), + '--batch', + '--import', + toGpgPath(publicKeyFile) + ], options); + yield exec.exec('gpg', [ + '--homedir', + toGpgPath(gpgHome), + '--batch', + '--verify', + toGpgPath(signaturePath), + toGpgPath(archivePath) + ], options); + } + finally { + yield io.rmRF(signaturePath); + yield io.rmRF(gpgHome); + } + }); +} +exports.verifyPackageSignature = verifyPackageSignature; /***/ }), @@ -81073,6 +81261,8 @@ function run() { const cache = core.getInput(constants.INPUT_CACHE); const cacheDependencyPath = core.getInput(constants.INPUT_CACHE_DEPENDENCY_PATH); const checkLatest = (0, util_1.getBooleanInput)(constants.INPUT_CHECK_LATEST, false); + const verifySignature = (0, util_1.getBooleanInput)(constants.INPUT_VERIFY_SIGNATURE, false); + const verifySignaturePublicKey = core.getInput(constants.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY) || undefined; let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); core.startGroup('Installed distributions'); if (versions.length !== toolchainIds.length) { @@ -81085,6 +81275,8 @@ function run() { architecture, packageType, checkLatest, + verifySignature, + verifySignaturePublicKey, distributionName, jdkFile, toolchainIds @@ -81118,11 +81310,13 @@ function run() { run(); function installVersion(version, options, toolchainId = 0) { return __awaiter(this, void 0, void 0, function* () { - const { distributionName, jdkFile, architecture, packageType, checkLatest, toolchainIds } = options; + const { distributionName, jdkFile, architecture, packageType, checkLatest, verifySignature, verifySignaturePublicKey, toolchainIds } = options; const installerOptions = { architecture, packageType, checkLatest, + verifySignature, + verifySignaturePublicKey, version }; const distribution = (0, distribution_factory_1.getJavaDistribution)(distributionName, installerOptions, jdkFile); diff --git a/src/constants.ts b/src/constants.ts index 93af286f8..5b0188965 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,6 +6,8 @@ export const INPUT_JAVA_PACKAGE = 'java-package'; export const INPUT_DISTRIBUTION = 'distribution'; export const INPUT_JDK_FILE = 'jdkFile'; export const INPUT_CHECK_LATEST = 'check-latest'; +export const INPUT_VERIFY_SIGNATURE = 'verify-signature'; +export const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; export const INPUT_SERVER_ID = 'server-id'; export const INPUT_SERVER_USERNAME = 'server-username'; export const INPUT_SERVER_PASSWORD = 'server-password'; diff --git a/src/distributions/base-installer.ts b/src/distributions/base-installer.ts index 5d9f3c82a..4494019cd 100644 --- a/src/distributions/base-installer.ts +++ b/src/distributions/base-installer.ts @@ -20,6 +20,8 @@ export abstract class JavaBase { protected packageType: string; protected stable: boolean; protected checkLatest: boolean; + protected verifySignature: boolean; + protected verifySignaturePublicKey: string | undefined; constructor( protected distribution: string, @@ -36,6 +38,8 @@ export abstract class JavaBase { this.architecture = installerOptions.architecture || os.arch(); this.packageType = installerOptions.packageType; this.checkLatest = installerOptions.checkLatest; + this.verifySignature = installerOptions.verifySignature ?? false; + this.verifySignaturePublicKey = installerOptions.verifySignaturePublicKey; } protected abstract downloadTool( @@ -46,6 +50,12 @@ export abstract class JavaBase { ): Promise; public async setupJava(): Promise { + if (this.verifySignature && !this.supportsSignatureVerification()) { + throw new Error( + `Input 'verify-signature' is not supported for distribution '${this.distribution}'.` + ); + } + let foundJava = this.findInToolcache(); if (foundJava && !this.checkLatest) { core.info(`Resolved Java ${foundJava.version} from tool-cache`); @@ -179,6 +189,10 @@ export abstract class JavaBase { return `Java_${this.distribution}_${this.packageType}`; } + protected supportsSignatureVerification(): boolean { + return false; + } + protected getToolcacheVersionName(version: string): string { if (!this.stable) { if (version.includes('+')) { diff --git a/src/distributions/base-models.ts b/src/distributions/base-models.ts index 82344d585..867a058af 100644 --- a/src/distributions/base-models.ts +++ b/src/distributions/base-models.ts @@ -3,6 +3,8 @@ export interface JavaInstallerOptions { architecture: string; packageType: string; checkLatest: boolean; + verifySignature?: boolean; + verifySignaturePublicKey?: string; } export interface JavaInstallerResults { @@ -13,4 +15,5 @@ export interface JavaInstallerResults { export interface JavaDownloadRelease { version: string; url: string; + signatureUrl?: string; } diff --git a/src/distributions/microsoft/installer.ts b/src/distributions/microsoft/installer.ts index a48a3c2a3..9e8e03388 100644 --- a/src/distributions/microsoft/installer.ts +++ b/src/distributions/microsoft/installer.ts @@ -10,12 +10,16 @@ import { getGitHubHttpHeaders, renameWinArchive } from '../../util'; +import * as gpg from '../../gpg'; +import {MICROSOFT_PUBLIC_KEY} from './microsoft-key'; import * as core from '@actions/core'; import * as tc from '@actions/tool-cache'; import fs from 'fs'; import path from 'path'; import {TypedResponse} from '@actions/http-client/lib/interfaces'; +export {MICROSOFT_PUBLIC_KEY} from './microsoft-key'; + export class MicrosoftDistributions extends JavaBase { constructor(installerOptions: JavaInstallerOptions) { super('Microsoft', installerOptions); @@ -29,6 +33,26 @@ export class MicrosoftDistributions extends JavaBase { ); let javaArchivePath = await tc.downloadTool(javaRelease.url); + if (this.verifySignature) { + if (!javaRelease.signatureUrl) { + throw new Error( + `Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version ${javaRelease.version}.` + ); + } + core.info(`Verifying Java package signature...`); + try { + await gpg.verifyPackageSignature( + javaArchivePath, + javaRelease.signatureUrl, + this.verifySignaturePublicKey ?? MICROSOFT_PUBLIC_KEY + ); + } catch (error) { + throw new Error( + `Failed to verify signature for Microsoft Build of OpenJDK version ${javaRelease.version}. Signature URL: ${javaRelease.signatureUrl}. Error: ${(error as Error).message}` + ); + } + } + core.info(`Extracting Java archive...`); const extension = getDownloadArchiveExtension(); if (process.platform === 'win32') { @@ -80,12 +104,23 @@ export class MicrosoftDistributions extends JavaBase { throw this.createVersionNotFoundError(range, availableVersionStrings); } + const file = foundRelease.files[0] as { + download_url: string; + signature_url?: string; + }; + const signatureUrl = file.signature_url ?? `${file.download_url}.sig`; + return { - url: foundRelease.files[0].download_url, + url: file.download_url, + signatureUrl, version: foundRelease.version }; } + protected supportsSignatureVerification(): boolean { + return true; + } + private async getAvailableVersions(): Promise { // TODO get these dynamically! // We will need Microsoft to add an endpoint where we can query for versions. diff --git a/src/distributions/microsoft/microsoft-key.ts b/src/distributions/microsoft/microsoft-key.ts new file mode 100644 index 000000000..3cb1f42c4 --- /dev/null +++ b/src/distributions/microsoft/microsoft-key.ts @@ -0,0 +1,21 @@ +// Microsoft Build of OpenJDK GPG signing key +// Retrieved from: https://download.visualstudio.microsoft.com/download/pr/b90071e2-e0cf-4411-98be-dbeb09d67bf0/8622862bcd54206e158c5abca0582c9b/464279_464280_aoc_20210208.asc +export const MICROSOFT_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: BSN Pgp v1.1.0.0 + +mQENBGAhlWcBCADCQjj6huLTenvZSLej35e9YKEHm4lix2uvPOONexMaU8V2v7KL +RGdoXF7jwHci7efnPZ+9zpS2+g3rhvv8M7yWy9E/1psEtGzvmp1IL/qIabMEQqi+ +UlhPGh7MQ/BkXAlic8Dyl3XYqr0EXS11iCiTr6Zkxs9Ee4V54gxL4gogRn4wk9sl +/nrjgDzMsUwla0pynoQQvYpqCdiAr3gKKllT1skCDqgVOMMyZxsx9HjZxg/3AJz6 +r5i512L2R+3Hkv+XmxT+mnGBCFcny0DM7PjNXEmIK3ZSkro1tQML90zx3Fyh5esx +fpVvuIXGFV75o35VVCBZoiD3hcfOnIJsPQ9nABEBAAG0OE1pY3Jvc29mdCBKYXZh +IEVuZ2luZWVyaW5nIDxqYXZhcGxhdGluZnJhQG1pY3Jvc29mdC5jb20+iQE4BBMB +CAAiBQJgIZVnAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRA1Ux0xWyHB +icwTCACJO2FGNocNvdUtAb+eDKuGwt0chAJdCES2ZtgBScwrwDyWpxpRznoXWBHL +MJeLyxJoKsCG3vVlY4uh48psCzVm3OKvi7MCPT955t8W6TzfSBxTpjR8zRgJkjPJ +EGhHTlusUfz7TtM5etJF0qscSJH1grcNsgtee97mk4QyEzT8Di83NQmYxKcBrliq +yK/SWWt8VkTyYAEO6L5PoB4L9r8ka27uQs+jgCw+/Z0JMtNmmhyNGY3+a1YtPeoy +JdQaI9LphfKGbVaz6SK2aol7vj+c2TG3TLUYdOYGMH1OZlri2GTkCVjwna2GC7p4 +Fa133tP85xzJEq1XeXm8WeLFo2wV +=rHCS +-----END PGP PUBLIC KEY BLOCK-----`; diff --git a/src/distributions/temurin/adoptium-key.ts b/src/distributions/temurin/adoptium-key.ts new file mode 100644 index 000000000..f7466e5e2 --- /dev/null +++ b/src/distributions/temurin/adoptium-key.ts @@ -0,0 +1,33 @@ +// Adoptium GPG signing key (fingerprint: 3B04D753C9050D9A5D343F39843C48A565F8F04B) +// Retrieved from: https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3B04D753C9050D9A5D343F39843C48A565F8F04B +export const ADOPTIUM_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBGGTvTQBCAC6ey144n7CG8foafF6mwgIBN1fIm1ILZDuGS4tMr0/XI8pgJnT +QvsPxZWEvtSm7bEMObzEoZJcXwjBcJl1B0ui8k5kHMTI75gCmZPsoKLFWIEpuRBQ +PBocusw80apDmLnNDQLVQvDFtEua5gaNa/fRw9YsmBoXBqvgrjFUIdGyWoQvH5+a +9OYlWD9n5VV0gnVMb+aclwVzB/zJw3kHGSgzuMtlAHeQiah7Y8yomQn/UIX8yqDf ++11sP3+c87YcjkRqImRTtmKEDcEtGPAIXC6SYA+uEEkbYE0Fy0chkvtnVWJ597fa +Epai4rnICU8zoJ6X5z3v1aM2WerhX9oq9X8PABEBAAHNQEFkb3B0aXVtIEdQRyBL +ZXkgKERFQi9SUE0gU2lnbmluZyBLZXkpIDx0ZW11cmluLWRldkBlY2xpcHNlLm9y +Zz7CwJIEEwEIADwWIQQ7BNdTyQUNml00PzmEPEilZfjwSwUCYZO9NAIbAwULCQgH +AgMiAgEGFQoJCAsCBBYCAwECHgcCF4AACgkQhDxIpWX48Et4AggAjjJzYWuKV3nG +7ngInngl8G/m9JoHr7BmwgcQXYhdy5hVkMcUx5JLeXz2LMBUH/F2nD595hgjMabk +kVib20X8lq9RsNbdfc2hBcWU6qyHKxsIqT4boI2/XDyEzzMyyZWWNGo/27Ci7Xmj +pWu31nh0pDdPqdyWDIKojbVVnxlCRY8as8Sm+1ufi709KCi4MuwHNsUlCSwb/fju +NKeHkrHbLcHKUUIEcmTSKRWrpMYBzm1HYOGBz4xPuELwUfUp71ehfoyBZlp6RDRf +l5TYI1FmCyHuvjNhrJgWv7bOTcf8yObGY+TEUhzc4xQqCrF4ur9d3opvsuPBQsv+ +Klqi5KSZgs7ATQRhk700AQgAq14okly8cFrpYVenEQPiB75AUZfKRpMduiR6IxAj +SKcH7aSoFZ9AubUEBVpZsyT5svxoEPe1i4TdbF+m9FGy42EcOlLa3ArLTj5H8FRl +UdGZB9I5mk4GptOzPM+aHMMu92vW/ZwjuS8DvOiQSp+cUmG1EqOMJSM7e/4BM71z +E+OKaVJCj79pEzhG3SK/IC/OlxxyETT66NSfYJd7Sw5R6Vr19am/uNU690W0CJ+q +VQeFpmDMr7LnfdFRIh+lJe05+PvWXeidkGjox5cbG52wf8aRIR/FgkfcFvqRMN1f +B+dVOWueloUeVAnzcUznOKmUEs7LP9ObJhYHHgup4IAU2wARAQABwsB2BBgBCAAg +FiEEOwTXU8kFDZpdND85hDxIpWX48EsFAmGTvTQCGwwACgkQhDxIpWX48EvXHQf/ +Q0nZsGDXnZHiBoojeSdpkO7WBjMIP3w1GdLvRpPQrS8TfOPbZuoevzCNh38Y3gwF +yelJspvzDQrBXhgkzAGlucYg8Y7KHa5Ebm7iDgMzc37L1hYSZTYCqwd7aowfgy34 +hOk3B67LffkJpIh738Oa9CtlwxQ9xcytmBmQ1fBBOwm/9IhAwHPQuydYIs4DxWbj +0MGSP4fDntU7e4UjsHNmhudDcYol0FaqdHHIIB9C/G4CzetRwHFOn3b4JwXMU7YU +6aJA3mXhi3hggMC3wkT2HHZ/TquuOdNc02fypWOCDOHz0alBBJNqoVUNFNqU3tfJ +wI4qF/KKq9BfyfucAs0ykA== +=XLag +-----END PGP PUBLIC KEY BLOCK-----`; diff --git a/src/distributions/temurin/installer.ts b/src/distributions/temurin/installer.ts index 109c2d413..16a896f60 100644 --- a/src/distributions/temurin/installer.ts +++ b/src/distributions/temurin/installer.ts @@ -4,7 +4,9 @@ import * as tc from '@actions/tool-cache'; import fs from 'fs'; import path from 'path'; import semver from 'semver'; +import * as gpg from '../../gpg'; +import {ADOPTIUM_PUBLIC_KEY} from './adoptium-key'; import {JavaBase} from '../base-installer'; import {ITemurinAvailableVersions} from './models'; import { @@ -22,6 +24,8 @@ import { validatePaginationUrl } from '../../util'; +export {ADOPTIUM_PUBLIC_KEY} from './adoptium-key'; + export enum TemurinImplementation { Hotspot = 'Hotspot' } @@ -50,7 +54,8 @@ export class TemurinDistribution extends JavaBase { : item.version_data.semver.replace('-beta+', '+'); return { version: formattedVersion, - url: item.binaries[0].package.link + url: item.binaries[0].package.link, + signatureUrl: item.binaries[0].package.signature_link } as JavaDownloadRelease; }); @@ -80,6 +85,28 @@ export class TemurinDistribution extends JavaBase { ); let javaArchivePath = await tc.downloadTool(javaRelease.url); + if (this.verifySignature) { + if (!javaRelease.signatureUrl) { + throw new Error( + `Input 'verify-signature' is enabled, but no signature URL was found for Temurin version ${javaRelease.version}.` + ); + } + core.info(`Verifying Java package signature...`); + try { + await gpg.verifyPackageSignature( + javaArchivePath, + javaRelease.signatureUrl, + this.verifySignaturePublicKey ?? ADOPTIUM_PUBLIC_KEY + ); + } catch (error) { + throw new Error( + `Failed to verify signature for Temurin version ${javaRelease.version} from ${javaRelease.signatureUrl}: ${ + (error as Error).message + }` + ); + } + } + core.info(`Extracting Java archive...`); const extension = getDownloadArchiveExtension(); if (process.platform === 'win32') { @@ -105,6 +132,10 @@ export class TemurinDistribution extends JavaBase { return super.toolcacheFolderName; } + protected supportsSignatureVerification(): boolean { + return true; + } + private async getAvailableVersions(): Promise { const platform = this.getPlatformOption(); const arch = this.distributionArchitecture(); diff --git a/src/distributions/temurin/models.ts b/src/distributions/temurin/models.ts index eb4b49b7b..c5bff9020 100644 --- a/src/distributions/temurin/models.ts +++ b/src/distributions/temurin/models.ts @@ -11,6 +11,7 @@ export interface ITemurinAvailableVersions { package: { checksum: string; checksum_link: string; + signature_link?: string; download_count: number; link: string; metadata_link: string; diff --git a/src/gpg.ts b/src/gpg.ts index 3b74c1516..3472e3b96 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -2,6 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as io from '@actions/io'; import * as exec from '@actions/exec'; +import * as tc from '@actions/tool-cache'; import * as util from './util'; import {ExecOptions} from '@actions/exec/lib/interfaces'; @@ -9,6 +10,17 @@ export const PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc'); const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/; +// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...). +// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions +// internally. Passing Windows paths with backslashes can cause fatal GPG errors +// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows. +export function toGpgPath(p: string): string { + if (process.platform !== 'win32') return p; + return p + .replace(/\\/g, '/') + .replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`); +} + export async function importKey(privateKey: string) { fs.writeFileSync(PRIVATE_KEY_FILE, privateKey, { encoding: 'utf-8', @@ -53,3 +65,59 @@ export async function deleteKey(keyFingerprint: string) { } ); } + +export async function verifyPackageSignature( + archivePath: string, + signatureUrl: string, + publicKeyContent: string +) { + const signaturePath = await tc.downloadTool(signatureUrl); + let gpgHome: string; + try { + gpgHome = fs.mkdtempSync( + path.join(util.getTempDir(), 'verify-signature-gpg-home-') + ); + } catch (error) { + try { + await io.rmRF(signaturePath); + } catch { + // ignore cleanup failures + } + throw new Error( + `Failed to create temporary GPG home directory for signature verification: ${ + (error as Error).message + }` + ); + } + try { + const publicKeyFile = path.join(gpgHome, 'public-key.asc'); + fs.writeFileSync(publicKeyFile, publicKeyContent, {encoding: 'utf-8'}); + const options: ExecOptions = {silent: true}; + await exec.exec( + 'gpg', + [ + '--homedir', + toGpgPath(gpgHome), + '--batch', + '--import', + toGpgPath(publicKeyFile) + ], + options + ); + await exec.exec( + 'gpg', + [ + '--homedir', + toGpgPath(gpgHome), + '--batch', + '--verify', + toGpgPath(signaturePath), + toGpgPath(archivePath) + ], + options + ); + } finally { + await io.rmRF(signaturePath); + await io.rmRF(gpgHome); + } +} diff --git a/src/setup-java.ts b/src/setup-java.ts index 73baf33a7..e4ec400b2 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -28,6 +28,12 @@ async function run() { constants.INPUT_CACHE_DEPENDENCY_PATH ); const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false); + const verifySignature = getBooleanInput( + constants.INPUT_VERIFY_SIGNATURE, + false + ); + const verifySignaturePublicKey = + core.getInput(constants.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY) || undefined; let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); core.startGroup('Installed distributions'); @@ -44,6 +50,8 @@ async function run() { architecture, packageType, checkLatest, + verifySignature, + verifySignaturePublicKey, distributionName, jdkFile, toolchainIds @@ -100,6 +108,8 @@ async function installVersion( architecture, packageType, checkLatest, + verifySignature, + verifySignaturePublicKey, toolchainIds } = options; @@ -107,6 +117,8 @@ async function installVersion( architecture, packageType, checkLatest, + verifySignature, + verifySignaturePublicKey, version }; @@ -141,6 +153,8 @@ interface installerInputsOptions { architecture: string; packageType: string; checkLatest: boolean; + verifySignature: boolean; + verifySignaturePublicKey: string | undefined; distributionName: string; jdkFile: string; toolchainIds: Array; From 2e73c8f8cdda42e27cf4a72a4ff643f5864e0cbf Mon Sep 17 00:00:00 2001 From: jmjaffe37 <111303274+jmjaffe37@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:01:00 -0700 Subject: [PATCH 35/57] Updated jetbrains test: https.request() now catches errors. This fixes leaking tests as well (#1070) * Updated jetbrains https.request command to catch errors. This fixes leaking tests as well * Removed deprecated lines from pre-commit and pre-push * added suggestion from PR feedback --- .husky/pre-commit | 3 --- .husky/pre-push | 3 --- .../distributors/jetbrains-installer.test.ts | 24 +++++++++++++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index d24fdfc60..2312dc587 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx lint-staged diff --git a/.husky/pre-push b/.husky/pre-push index 7d6707f95..192cb67eb 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npm run build && npm test diff --git a/__tests__/distributors/jetbrains-installer.test.ts b/__tests__/distributors/jetbrains-installer.test.ts index bf1dc15da..2d32e7e63 100644 --- a/__tests__/distributors/jetbrains-installer.test.ts +++ b/__tests__/distributors/jetbrains-installer.test.ts @@ -87,10 +87,26 @@ describe('findPackageForDownload', () => { const url = resolvedVersion.url; const options = {method: 'HEAD'}; - https.request(url, options, res => { - // JetBrains uses 403 for inexistent packages - expect(res.statusCode).not.toBe(403); - res.resume(); + await new Promise((resolve, reject) => { + const request = https.request(url, options, res => { + let assertionError: unknown; + + try { + // JetBrains uses 403 for non-existent packages + expect(res.statusCode).not.toBe(403); + } catch (error) { + assertionError = error; + } + + res.resume(); + res.once('error', reject); + res.once('end', () => + assertionError ? reject(assertionError as Error) : resolve() + ); + }); + + request.on('error', reject); + request.end(); }); } ); From 324b33387d37f5aa8878ea44bd7144864a316dee Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:30:53 -0400 Subject: [PATCH 36/57] Fix arm64 e2e workflow tests mislabeled as x64 (#1073) * Initial plan * Fix mislabeled arch in e2e workflow job names for Apple silicon runners --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- .github/workflows/e2e-versions.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 4ea83d8b8..2bcf4883b 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -19,7 +19,7 @@ permissions: jobs: setup-java-major-versions: - name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} + name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-${{ contains(matrix.os, 'macos') && !contains(matrix.os, 'intel') && 'arm64' || 'x64' }}) - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -96,7 +96,7 @@ jobs: shell: bash setup-java-alpine-linux: - name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - alpine-linux - ${{ matrix.os }} + name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-${{ contains(matrix.os, 'macos') && !contains(matrix.os, 'intel') && 'arm64' || 'x64' }}) - alpine-linux - ${{ matrix.os }} runs-on: ${{ matrix.os }} container: image: alpine:3.21 @@ -127,7 +127,7 @@ jobs: shell: bash setup-java-major-minor-versions: - name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} + name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-${{ contains(matrix.os, 'macos') && !contains(matrix.os, 'intel') && 'arm64' || 'x64' }}) - ${{ matrix.os }} needs: setup-java-major-versions runs-on: ${{ matrix.os }} strategy: @@ -275,7 +275,7 @@ jobs: shell: bash setup-java-ea-versions-zulu: - name: zulu ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} + name: zulu ${{ matrix.version }} (jdk-${{ contains(matrix.os, 'macos') && !contains(matrix.os, 'intel') && 'arm64' || 'x64' }}) - ${{ matrix.os }} needs: setup-java-major-minor-versions runs-on: ${{ matrix.os }} strategy: @@ -302,7 +302,7 @@ jobs: shell: bash setup-java-ea-versions-temurin: - name: temurin ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} + name: temurin ${{ matrix.version }} (jdk-${{ contains(matrix.os, 'macos') && !contains(matrix.os, 'intel') && 'arm64' || 'x64' }}) - ${{ matrix.os }} needs: setup-java-major-minor-versions runs-on: ${{ matrix.os }} strategy: @@ -385,7 +385,7 @@ jobs: shell: bash setup-java-ea-versions-sapmachine: - name: sapmachine ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} + name: sapmachine ${{ matrix.version }} (jdk-${{ contains(matrix.os, 'macos') && !contains(matrix.os, 'intel') && 'arm64' || 'x64' }}) - ${{ matrix.os }} needs: setup-java-major-minor-versions runs-on: ${{ matrix.os }} strategy: From 6c4d4a5025dca6ad7cc86c839fadbcb66762ed3b Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Thu, 2 Jul 2026 17:12:50 -0400 Subject: [PATCH 37/57] feat: suppress Maven transfer progress via MAVEN_ARGS by default (add show-download-progress input) (#1053) * feat: suppress Maven transfer progress via MAVEN_ARGS by default Set MAVEN_ARGS to include -ntp (--no-transfer-progress) so Maven invocations in the job produce cleaner CI logs without download/transfer progress noise. Add a new optional 'show-download-progress' input (default false); set it to true to keep the progress output. The change preserves any existing MAVEN_ARGS value (the flag is appended, not overwritten) and is idempotent (it won't add the flag twice if -ntp or --no-transfer-progress is already present). Applies on all platforms; honored by Maven 3.9.0+ and the Maven Wrapper, and is a no-op for non-Maven builds. - action.yml: add show-download-progress input - src/constants.ts: add input + MAVEN_ARGS constants - src/maven-args.ts: new configureMavenArgs() - src/setup-java.ts: invoke configureMavenArgs() during setup - __tests__/maven-args.test.ts: unit tests - docs/advanced-usage.md: document the behavior and input - dist: rebuild bundled action Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Update generated dist for Maven args log change --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- __tests__/maven-args.test.ts | 104 +++++++++++++++++++++++++++++++++++ action.yml | 4 ++ dist/cleanup/index.js | 6 +- dist/setup/index.js | 89 +++++++++++++++++++++++++++++- docs/advanced-usage.md | 31 +++++++++++ src/constants.ts | 5 ++ src/maven-args.ts | 69 +++++++++++++++++++++++ src/setup-java.ts | 2 + 8 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 __tests__/maven-args.test.ts create mode 100644 src/maven-args.ts diff --git a/__tests__/maven-args.test.ts b/__tests__/maven-args.test.ts new file mode 100644 index 000000000..8a45a0ea9 --- /dev/null +++ b/__tests__/maven-args.test.ts @@ -0,0 +1,104 @@ +import * as core from '@actions/core'; + +import {configureMavenArgs} from '../src/maven-args'; +import { + INPUT_SHOW_DOWNLOAD_PROGRESS, + MAVEN_ARGS_ENV, + MAVEN_NO_TRANSFER_PROGRESS_FLAG +} from '../src/constants'; + +describe('configureMavenArgs', () => { + let inputs: Record; + let spyGetInput: jest.SpyInstance; + let spyExportVariable: jest.SpyInstance; + let spyInfo: jest.SpyInstance; + let spyDebug: jest.SpyInstance; + const originalMavenArgs = process.env[MAVEN_ARGS_ENV]; + + beforeEach(() => { + inputs = {}; + + spyGetInput = jest.spyOn(core, 'getInput'); + spyGetInput.mockImplementation((name: string) => inputs[name] ?? ''); + + spyExportVariable = jest.spyOn(core, 'exportVariable'); + spyExportVariable.mockImplementation((name: string, value: string) => { + process.env[name] = value; + }); + + spyInfo = jest.spyOn(core, 'info'); + spyInfo.mockImplementation(() => undefined); + + spyDebug = jest.spyOn(core, 'debug'); + spyDebug.mockImplementation(() => undefined); + + delete process.env[MAVEN_ARGS_ENV]; + }); + + afterEach(() => { + jest.restoreAllMocks(); + if (originalMavenArgs === undefined) { + delete process.env[MAVEN_ARGS_ENV]; + } else { + process.env[MAVEN_ARGS_ENV] = originalMavenArgs; + } + }); + + it('sets MAVEN_ARGS with -ntp by default', () => { + configureMavenArgs(); + + expect(spyExportVariable).toHaveBeenCalledWith( + MAVEN_ARGS_ENV, + MAVEN_NO_TRANSFER_PROGRESS_FLAG + ); + expect(process.env[MAVEN_ARGS_ENV]).toBe(MAVEN_NO_TRANSFER_PROGRESS_FLAG); + }); + + it('does not modify MAVEN_ARGS when show-download-progress is true', () => { + inputs[INPUT_SHOW_DOWNLOAD_PROGRESS] = 'true'; + + configureMavenArgs(); + + expect(spyExportVariable).not.toHaveBeenCalled(); + expect(process.env[MAVEN_ARGS_ENV]).toBeUndefined(); + }); + + it('preserves an existing MAVEN_ARGS value and appends -ntp', () => { + process.env[MAVEN_ARGS_ENV] = '-B -Dstyle.color=always'; + + configureMavenArgs(); + + expect(spyExportVariable).toHaveBeenCalledWith( + MAVEN_ARGS_ENV, + `-B -Dstyle.color=always ${MAVEN_NO_TRANSFER_PROGRESS_FLAG}` + ); + }); + + it('does not duplicate the flag when -ntp is already present', () => { + process.env[MAVEN_ARGS_ENV] = '-B -ntp'; + + configureMavenArgs(); + + expect(spyExportVariable).not.toHaveBeenCalled(); + expect(process.env[MAVEN_ARGS_ENV]).toBe('-B -ntp'); + }); + + it('does not duplicate the flag when --no-transfer-progress is already present', () => { + process.env[MAVEN_ARGS_ENV] = '--no-transfer-progress -B'; + + configureMavenArgs(); + + expect(spyExportVariable).not.toHaveBeenCalled(); + expect(process.env[MAVEN_ARGS_ENV]).toBe('--no-transfer-progress -B'); + }); + + it('keeps the existing MAVEN_ARGS when show-download-progress is true', () => { + inputs[INPUT_SHOW_DOWNLOAD_PROGRESS] = 'true'; + process.env[MAVEN_ARGS_ENV] = '-B'; + + configureMavenArgs(); + + expect(spyExportVariable).not.toHaveBeenCalled(); + expect(process.env[MAVEN_ARGS_ENV]).toBe('-B'); + }); +}); diff --git a/action.yml b/action.yml index cc1a541b2..a5b673a8f 100644 --- a/action.yml +++ b/action.yml @@ -82,6 +82,10 @@ inputs: mvn-toolchain-vendor: description: 'Name of Maven Toolchain Vendor if the default name of "${distribution}" is not wanted. See examples of supported syntax in Advanced Usage file' required: false + show-download-progress: + description: 'Whether Maven should print artifact download/transfer progress to the build log. When "false" (default) the action sets "-ntp" (--no-transfer-progress) in MAVEN_ARGS to produce cleaner logs. Set to "true" to keep the progress output. Has no effect on non-Maven builds.' + required: false + default: false outputs: distribution: description: 'Distribution of Java that has been installed' diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index d962852b5..ef4fed3b3 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52241,7 +52241,7 @@ else { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -52270,6 +52270,10 @@ exports.MVN_SETTINGS_FILE = 'settings.xml'; exports.MVN_TOOLCHAINS_FILE = 'toolchains.xml'; exports.INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id'; exports.INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor'; +exports.INPUT_SHOW_DOWNLOAD_PROGRESS = 'show-download-progress'; +exports.MAVEN_ARGS_ENV = 'MAVEN_ARGS'; +exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; +exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = ['corretto']; diff --git a/dist/setup/index.js b/dist/setup/index.js index 54ced2b5f..59116af79 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78000,7 +78000,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -78029,6 +78029,10 @@ exports.MVN_SETTINGS_FILE = 'settings.xml'; exports.MVN_TOOLCHAINS_FILE = 'toolchains.xml'; exports.INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id'; exports.INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor'; +exports.INPUT_SHOW_DOWNLOAD_PROGRESS = 'show-download-progress'; +exports.MAVEN_ARGS_ENV = 'MAVEN_ARGS'; +exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; +exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = ['corretto']; @@ -81195,6 +81199,87 @@ function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) { exports.verifyPackageSignature = verifyPackageSignature; +/***/ }), + +/***/ 38172: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.configureMavenArgs = void 0; +const core = __importStar(__nccwpck_require__(37484)); +const util_1 = __nccwpck_require__(54527); +const constants_1 = __nccwpck_require__(27242); +/** + * Configures the MAVEN_ARGS environment variable so that Maven suppresses + * artifact transfer/download progress output by default, producing cleaner + * CI logs. + * + * Behavior: + * - When `show-download-progress` is `false` (the default), `-ntp` + * (`--no-transfer-progress`) is appended to any existing MAVEN_ARGS value. + * - When `show-download-progress` is `true`, MAVEN_ARGS is left untouched so + * the user's own configuration (and Maven's default progress output) is + * preserved. + * + * The change is idempotent: if MAVEN_ARGS already disables transfer progress + * (via `-ntp` or `--no-transfer-progress`) nothing is added. Any pre-existing + * MAVEN_ARGS value is preserved. + * + * MAVEN_ARGS is honored by Maven 3.9.0+ and the Maven Wrapper; older Maven + * versions ignore it, so this is a no-op there. It has no effect on non-Maven + * builds such as Gradle or sbt. + */ +function configureMavenArgs() { + var _a; + const showDownloadProgress = (0, util_1.getBooleanInput)(constants_1.INPUT_SHOW_DOWNLOAD_PROGRESS, false); + if (showDownloadProgress) { + core.debug(`${constants_1.INPUT_SHOW_DOWNLOAD_PROGRESS} is true; leaving ${constants_1.MAVEN_ARGS_ENV} unchanged`); + return; + } + const existingArgs = ((_a = process.env[constants_1.MAVEN_ARGS_ENV]) !== null && _a !== void 0 ? _a : '').trim(); + const alreadyDisabled = existingArgs + .split(/\s+/) + .some(arg => arg === constants_1.MAVEN_NO_TRANSFER_PROGRESS_FLAG || + arg === constants_1.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG); + if (alreadyDisabled) { + core.debug(`${constants_1.MAVEN_ARGS_ENV} already disables transfer progress; leaving it unchanged`); + return; + } + const updatedArgs = existingArgs + ? `${existingArgs} ${constants_1.MAVEN_NO_TRANSFER_PROGRESS_FLAG}` + : constants_1.MAVEN_NO_TRANSFER_PROGRESS_FLAG; + core.exportVariable(constants_1.MAVEN_ARGS_ENV, updatedArgs); + core.info(`Configured ${constants_1.MAVEN_ARGS_ENV} to include ${constants_1.MAVEN_NO_TRANSFER_PROGRESS_FLAG} to suppress Maven transfer progress logs. ` + + `Set '${constants_1.INPUT_SHOW_DOWNLOAD_PROGRESS}: true' to keep the download progress output.`); +} +exports.configureMavenArgs = configureMavenArgs; + + /***/ }), /***/ 90471: @@ -81247,6 +81332,7 @@ const constants = __importStar(__nccwpck_require__(27242)); const cache_1 = __nccwpck_require__(97377); const path = __importStar(__nccwpck_require__(16928)); const distribution_factory_1 = __nccwpck_require__(2970); +const maven_args_1 = __nccwpck_require__(38172); function run() { return __awaiter(this, void 0, void 0, function* () { try { @@ -81298,6 +81384,7 @@ function run() { const matchersPath = path.join(__dirname, '..', '..', '.github'); core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`); yield auth.configureAuthentication(); + (0, maven_args_1.configureMavenArgs)(); if (cache && (0, util_1.isCacheFeatureAvailable)()) { yield (0, cache_1.restore)(cache, cacheDependencyPath); } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 58301be99..7e18a278f 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -19,6 +19,7 @@ - [Testing against different Java distributions](#Testing-against-different-Java-distributions) - [Testing against different platforms](#Testing-against-different-platforms) - [Publishing using Apache Maven](#Publishing-using-Apache-Maven) +- [Maven transfer progress (download logs)](#Maven-transfer-progress-download-logs) - [Publishing using Gradle](#Publishing-using-Gradle) - [Hosted Tool Cache](#Hosted-Tool-Cache) - [Modifying Maven Toolchains](#Modifying-Maven-Toolchains) @@ -526,6 +527,36 @@ jobs: GITHUB_TOKEN: ${{ github.token }} ``` +## Maven transfer progress (download logs) + +By default, Maven prints a line for every artifact it downloads, which can add hundreds of noisy lines to CI logs. To keep logs clean, `setup-java` sets the [`MAVEN_ARGS`](https://maven.apache.org/configure.html#maven_args-environment-variable) environment variable to include `-ntp` (`--no-transfer-progress`) so that subsequent Maven invocations in the job suppress this transfer progress output. + +This is enabled by default. Any existing `MAVEN_ARGS` value is preserved (the flag is appended, not overwritten), and the flag is not added twice if you already set it yourself. + +If you want to keep the download/transfer progress in your logs, set `show-download-progress: true`: + +```yaml +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 + with: + distribution: '' + java-version: '21' + show-download-progress: true # keep Maven download/transfer progress in the logs + + - name: Build with Maven + run: mvn -B package --file pom.xml +``` + +***NOTES***: +- `MAVEN_ARGS` is honored by Maven 3.9.0+ and the Maven Wrapper (`mvnw`). Older Maven versions ignore it, so on those you can pass `--no-transfer-progress` on the command line instead. +- This setting only affects Maven. It has no effect on Gradle, sbt, or other build tools. +- `-ntp` only controls transfer/progress output; it does not change whether Maven runs in batch mode. Use `-B`/`--batch-mode` (or `false` in `settings.xml`) if you also want non-interactive runs. + ## Publishing using Gradle ```yaml jobs: diff --git a/src/constants.ts b/src/constants.ts index 5b0188965..641714c45 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -30,5 +30,10 @@ export const MVN_SETTINGS_FILE = 'settings.xml'; export const MVN_TOOLCHAINS_FILE = 'toolchains.xml'; export const INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id'; export const INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor'; +export const INPUT_SHOW_DOWNLOAD_PROGRESS = 'show-download-progress'; + +export const MAVEN_ARGS_ENV = 'MAVEN_ARGS'; +export const MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; +export const MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; export const DISTRIBUTIONS_ONLY_MAJOR_VERSION = ['corretto']; diff --git a/src/maven-args.ts b/src/maven-args.ts new file mode 100644 index 000000000..654bed27b --- /dev/null +++ b/src/maven-args.ts @@ -0,0 +1,69 @@ +import * as core from '@actions/core'; +import {getBooleanInput} from './util'; +import { + INPUT_SHOW_DOWNLOAD_PROGRESS, + MAVEN_ARGS_ENV, + MAVEN_NO_TRANSFER_PROGRESS_FLAG, + MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG +} from './constants'; + +/** + * Configures the MAVEN_ARGS environment variable so that Maven suppresses + * artifact transfer/download progress output by default, producing cleaner + * CI logs. + * + * Behavior: + * - When `show-download-progress` is `false` (the default), `-ntp` + * (`--no-transfer-progress`) is appended to any existing MAVEN_ARGS value. + * - When `show-download-progress` is `true`, MAVEN_ARGS is left untouched so + * the user's own configuration (and Maven's default progress output) is + * preserved. + * + * The change is idempotent: if MAVEN_ARGS already disables transfer progress + * (via `-ntp` or `--no-transfer-progress`) nothing is added. Any pre-existing + * MAVEN_ARGS value is preserved. + * + * MAVEN_ARGS is honored by Maven 3.9.0+ and the Maven Wrapper; older Maven + * versions ignore it, so this is a no-op there. It has no effect on non-Maven + * builds such as Gradle or sbt. + */ +export function configureMavenArgs(): void { + const showDownloadProgress = getBooleanInput( + INPUT_SHOW_DOWNLOAD_PROGRESS, + false + ); + + if (showDownloadProgress) { + core.debug( + `${INPUT_SHOW_DOWNLOAD_PROGRESS} is true; leaving ${MAVEN_ARGS_ENV} unchanged` + ); + return; + } + + const existingArgs = (process.env[MAVEN_ARGS_ENV] ?? '').trim(); + + const alreadyDisabled = existingArgs + .split(/\s+/) + .some( + arg => + arg === MAVEN_NO_TRANSFER_PROGRESS_FLAG || + arg === MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG + ); + + if (alreadyDisabled) { + core.debug( + `${MAVEN_ARGS_ENV} already disables transfer progress; leaving it unchanged` + ); + return; + } + + const updatedArgs = existingArgs + ? `${existingArgs} ${MAVEN_NO_TRANSFER_PROGRESS_FLAG}` + : MAVEN_NO_TRANSFER_PROGRESS_FLAG; + + core.exportVariable(MAVEN_ARGS_ENV, updatedArgs); + core.info( + `Configured ${MAVEN_ARGS_ENV} to include ${MAVEN_NO_TRANSFER_PROGRESS_FLAG} to suppress Maven transfer progress logs. ` + + `Set '${INPUT_SHOW_DOWNLOAD_PROGRESS}: true' to keep the download progress output.` + ); +} diff --git a/src/setup-java.ts b/src/setup-java.ts index e4ec400b2..d7a84fab0 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -12,6 +12,7 @@ import {restore} from './cache'; import * as path from 'path'; import {getJavaDistribution} from './distributions/distribution-factory'; import {JavaInstallerOptions} from './distributions/base-models'; +import {configureMavenArgs} from './maven-args'; async function run() { try { @@ -87,6 +88,7 @@ async function run() { core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`); await auth.configureAuthentication(); + configureMavenArgs(); if (cache && isCacheFeatureAvailable()) { await restore(cache, cacheDependencyPath); } From 733efaeaca653493004ffb895a11b2c6c316558e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:22:38 -0400 Subject: [PATCH 38/57] feat: Disable interactiveMode in generated Maven settings.xml (#1052) * Initial plan * Add interactiveMode false to generated maven settings.xml --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- __tests__/auth.test.ts | 2 ++ dist/setup/index.js | 1 + docs/advanced-usage.md | 2 ++ src/auth.ts | 1 + 4 files changed, 6 insertions(+) diff --git a/__tests__/auth.test.ts b/__tests__/auth.test.ts index 06591da7a..eca837a56 100644 --- a/__tests__/auth.test.ts +++ b/__tests__/auth.test.ts @@ -160,6 +160,7 @@ describe('auth tests', () => { const expectedSettings = ` + false ${id} @@ -181,6 +182,7 @@ describe('auth tests', () => { const expectedSettings = ` + false ${id} diff --git a/dist/setup/index.js b/dist/setup/index.js index 59116af79..0e40385cb 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -77727,6 +77727,7 @@ function generate(id, username, password, gpgPassphrase) { '@xmlns': 'http://maven.apache.org/SETTINGS/1.0.0', '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', '@xsi:schemaLocation': 'http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd', + interactiveMode: false, servers: { server: [ { diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 7e18a278f..58a6ca5d7 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -437,6 +437,7 @@ The two `settings.xml` files created from the above example look like the follow + false github @@ -456,6 +457,7 @@ The two `settings.xml` files created from the above example look like the follow + false maven diff --git a/src/auth.ts b/src/auth.ts index c8ea6291c..52ee3ede9 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -80,6 +80,7 @@ export function generate( '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', '@xsi:schemaLocation': 'http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd', + interactiveMode: false, servers: { server: [ { From c712b2fb55a8deb2d59f204bd7aa86ac6f938c98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:35:02 -0400 Subject: [PATCH 39/57] Bump prettier from 3.6.2 to 3.9.1 (#1066) * Bump prettier from 3.6.2 to 3.9.1 Bumps [prettier](https://github.com/prettier/prettier) from 3.6.2 to 3.9.1. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.6.2...3.9.1) --- updated-dependencies: - dependency-name: prettier dependency-version: 3.9.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Format files for Prettier 3.9.1 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bruno Borges Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- src/distributions/corretto/installer.ts | 3 +-- src/distributions/liberica/models.ts | 6 +----- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c1577bab..aaae36b4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "jest": "^30.4.2", "jest-circus": "^30.4.2", "lint-staged": "^17.0.8", - "prettier": "^3.6.2", + "prettier": "^3.9.1", "ts-jest": "^29.4.11", "typescript": "^5.3.3" }, @@ -6137,9 +6137,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.1.tgz", + "integrity": "sha512-ppiDo2CSwexck1eyZUwJHg/N3nf1+6IRCv7W/VJ5vaLnVCmB7+3CdRfMwoCHBBX6xTrREDTksZ4OZl5SSf4zXA==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index 64f2bf53a..88b11df58 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "jest": "^30.4.2", "jest-circus": "^30.4.2", "lint-staged": "^17.0.8", - "prettier": "^3.6.2", + "prettier": "^3.9.1", "ts-jest": "^29.4.11", "typescript": "^5.3.3" }, diff --git a/src/distributions/corretto/installer.ts b/src/distributions/corretto/installer.ts index 52af2dd96..fd732899c 100644 --- a/src/distributions/corretto/installer.ts +++ b/src/distributions/corretto/installer.ts @@ -127,8 +127,7 @@ export class CorrettoDistribution extends JavaBase { private getAvailableVersionsForPlatform( eligibleVersions: - | ICorrettoAllAvailableVersions['os']['arch']['imageType'] - | undefined + ICorrettoAllAvailableVersions['os']['arch']['imageType'] | undefined ): ICorrettoAvailableVersions[] { const availableVersions: ICorrettoAvailableVersions[] = []; diff --git a/src/distributions/liberica/models.ts b/src/distributions/liberica/models.ts index c9285fb7f..6f5a1de1e 100644 --- a/src/distributions/liberica/models.ts +++ b/src/distributions/liberica/models.ts @@ -4,11 +4,7 @@ export type Bitness = '32' | '64'; export type ArchType = 'arm' | 'ppc' | 'sparc' | 'x86'; export type OsVersions = - | 'linux' - | 'linux-musl' - | 'macos' - | 'solaris' - | 'windows'; + 'linux' | 'linux-musl' | 'macos' | 'solaris' | 'windows'; export interface ArchitectureOptions { bitness: Bitness; From 0765b158bfd14bd15c56facab447d138e1161193 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:45:36 -0400 Subject: [PATCH 40/57] chore(deps-dev): bump eslint-plugin-jest from 29.0.1 to 29.15.4 (#1074) Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 29.0.1 to 29.15.4. - [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases) - [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v29.0.1...v29.15.4) --- updated-dependencies: - dependency-name: eslint-plugin-jest dependency-version: 29.15.4 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 17 +++++++++++------ package.json | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index aaae36b4a..e94683d4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "@vercel/ncc": "^0.44.0", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jest": "^29.0.1", + "eslint-plugin-jest": "^29.15.4", "eslint-plugin-node": "^11.1.0", "husky": "^9.1.7", "jest": "^30.4.2", @@ -3620,10 +3620,11 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.0.1.tgz", - "integrity": "sha512-EE44T0OSMCeXhDrrdsbKAhprobKkPtJTbQz5yEktysNpHeDZTAL1SfDTNKmcFfJkY6yrQLtTKZALrD3j/Gpmiw==", + "version": "29.15.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.15.4.tgz", + "integrity": "sha512-6ln5i9Nkrb27X4w91ZPt/xHDsVQnvxTS2ntgq6r32u+8gymdUrp88TdcBXSveZW0Dl+M5v2H6K75kJhMvUGhjg==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/utils": "^8.0.0" }, @@ -3632,8 +3633,9 @@ }, "peerDependencies": { "@typescript-eslint/eslint-plugin": "^8.0.0", - "eslint": "^8.57.0 || ^9.0.0", - "jest": "*" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "jest": "*", + "typescript": ">=4.8.4 <7.0.0" }, "peerDependenciesMeta": { "@typescript-eslint/eslint-plugin": { @@ -3641,6 +3643,9 @@ }, "jest": { "optional": true + }, + "typescript": { + "optional": true } } }, diff --git a/package.json b/package.json index 88b11df58..e235cb95c 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@vercel/ncc": "^0.44.0", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jest": "^29.0.1", + "eslint-plugin-jest": "^29.15.4", "eslint-plugin-node": "^11.1.0", "husky": "^9.1.7", "jest": "^30.4.2", From 77ee41d00e246422933200d520a0334a299f26e4 Mon Sep 17 00:00:00 2001 From: Nikolas Grottendieck Date: Tue, 7 Jul 2026 06:01:31 +0200 Subject: [PATCH 41/57] fix: Maven Toolchains grows unexpectedly (#534) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Maven Toolchains grows unexpectedly On self-hosted runners toolchains.xml may survive multiple runs and unexpectedly grow as a result of the toolchains setup simply appending the JDK definition even if one with the same `type` and `provides.id` already exists. Restructuring the parsing step and filtering the potentially existing list of toolchain definitions prevents this and also fixes toolchain.xml files that already contain duplicates. Fixes #530 * fix: guard toolchain dedup and preserve existing root attributes Address reviewer feedback on the Maven toolchains dedup logic: - Treat jdk toolchains without a string `provides.id` as non-deduplicatable and use optional access when comparing ids, so partially-formed or nonstandard toolchains.xml files no longer crash setup. - Preserve the existing `` root attributes (xmlns, schemaLocation, …) when present, falling back to the 1.1.0 defaults only for attributes the existing file is missing. This avoids silently rewriting user-managed metadata or changing the effective namespace. Adds tests covering custom root attributes and id-less jdk toolchains, and rebuilds dist/. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Bruno Borges Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- __tests__/toolchains.test.ts | 615 ++++++++++++++++++++++++++++++++++- dist/setup/index.js | 88 +++-- src/toolchains.ts | 122 +++++-- 3 files changed, 754 insertions(+), 71 deletions(-) diff --git a/__tests__/toolchains.test.ts b/__tests__/toolchains.test.ts index 483077dc1..38cac18ca 100644 --- a/__tests__/toolchains.test.ts +++ b/__tests__/toolchains.test.ts @@ -143,7 +143,551 @@ describe('toolchains tests', () => { `; const result = ` - + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + + + jdk + + 1.6 + Sun + sun_1.6 + + + /opt/jdk/sun/1.6 + + +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('does not discard custom elements in existing toolchain definitions', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ` + + jdk + + 1.6 + Sun + sun_1.6 + foo + + + /opt/jdk/sun/1.6 + /usr/local/bin/bash + + + `; + const result = ` + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + + + jdk + + 1.6 + Sun + sun_1.6 + foo + + + /opt/jdk/sun/1.6 + /usr/local/bin/bash + + +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('does not discard existing, custom toolchain definitions', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ` + + foo + + baz + + + /usr/local/bin/foo + + + `; + const result = ` + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + + + foo + + baz + + + /usr/local/bin/foo + + +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('does not duplicate existing toolchain definitions', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ` + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + + `; + const result = ` + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('does not duplicate existing toolchain definitions if multiple exist', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ` + + jdk + + 1.6 + Sun + sun_1.6 + + + /opt/jdk/sun/1.6 + + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + + `; + const result = ` + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + + + jdk + + 1.6 + Sun + sun_1.6 + + + /opt/jdk/sun/1.6 + + +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('handles an empty list of existing toolchains correctly', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ` + `; + const result = ` + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('handles an empty existing toolchains.xml correctly', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ``; + const result = ` + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('preserves custom root attributes on existing toolchains.xml', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ` + + jdk + + 1.6 + Sun + sun_1.6 + + + /opt/jdk/sun/1.6 + + + `; + const result = ` + + + jdk + + 17 + Eclipse Temurin + temurin_17 + + + /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + jdk @@ -155,6 +699,65 @@ describe('toolchains tests', () => { /opt/jdk/sun/1.6 +`; + + fs.mkdirSync(m2Dir, {recursive: true}); + fs.writeFileSync(toolchainsFile, originalFile); + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + + await toolchains.createToolchainsSettings({ + jdkInfo, + settingsDirectory: m2Dir, + overwriteSettings: true + }); + + expect(fs.existsSync(m2Dir)).toBe(true); + expect(fs.existsSync(toolchainsFile)).toBe(true); + expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ); + expect( + toolchains.generateToolchainDefinition( + originalFile, + jdkInfo.version, + jdkInfo.vendor, + jdkInfo.id, + jdkInfo.jdkHome + ) + ).toEqual(result); + }, 100000); + + it('keeps partially-formed jdk toolchains without an id instead of crashing', async () => { + const jdkInfo = { + version: '17', + vendor: 'Eclipse Temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + }; + + const originalFile = ` + + jdk + + 1.6 + Sun + + + /opt/jdk/sun/1.6 + + + `; + const result = ` + jdk @@ -166,6 +769,16 @@ describe('toolchains tests', () => { /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64 + + jdk + + 1.6 + Sun + + + /opt/jdk/sun/1.6 + + `; fs.mkdirSync(m2Dir, {recursive: true}); diff --git a/dist/setup/index.js b/dist/setup/index.js index 0e40385cb..af4581917 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -81506,46 +81506,66 @@ function createToolchainsSettings({ jdkInfo, settingsDirectory, overwriteSetting exports.createToolchainsSettings = createToolchainsSettings; // only exported for testing purposes function generateToolchainDefinition(original, version, vendor, id, jdkHome) { - let xmlObj; + let jsToolchains = [ + { + type: 'jdk', + provides: { + version: `${version}`, + vendor: `${vendor}`, + id: `${id}` + }, + configuration: { + jdkHome: `${jdkHome}` + } + } + ]; + // default root attributes, used when the existing file does not declare its own + let rootAttributes = { + '@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0', + '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', + '@xsi:schemaLocation': 'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd' + }; if (original === null || original === void 0 ? void 0 : original.length) { - xmlObj = (0, xmlbuilder2_1.create)(original) + // convert existing toolchains into TS native objects for better handling + // xmlbuilder2 will convert the document into a `{toolchains: { toolchain: [] | {} }}` structure + // instead of the desired `toolchains: [{}]` one or simply `[{}]` + const jsObj = (0, xmlbuilder2_1.create)(original) .root() - .ele({ - toolchain: { - type: 'jdk', - provides: { - version: `${version}`, - vendor: `${vendor}`, - id: `${id}` - }, - configuration: { - jdkHome: `${jdkHome}` + .toObject(); + if (jsObj.toolchains) { + // preserve the existing root attributes (xmlns, schemaLocation, …) so we don't + // silently rewrite user-managed metadata or change the effective XML namespace; + // xmlbuilder2 exposes attributes as `@`-prefixed keys on the element object + const existingAttributes = Object.fromEntries(Object.entries(jsObj.toolchains).filter(([key]) => key.startsWith('@'))); + // fall back to the defaults only for attributes the existing file is missing + rootAttributes = Object.assign(Object.assign({}, rootAttributes), existingAttributes); + if (jsObj.toolchains.toolchain) { + // in case only a single child exists xmlbuilder2 will not create an array and using verbose = true equally doesn't work here + // See https://oozcitak.github.io/xmlbuilder2/serialization.html#js-object-and-map-serializers for details + if (Array.isArray(jsObj.toolchains.toolchain)) { + jsToolchains.push(...jsObj.toolchains.toolchain); + } + else { + jsToolchains.push(jsObj.toolchains.toolchain); } } + } + // remove potential duplicates based on type & id (which should be a unique combination); + // self.findIndex will only return the first occurrence, ensuring duplicates are skipped + jsToolchains = jsToolchains.filter((value, index, self) => { + var _a; + // ensure non-jdk toolchains are kept in the results, we must not touch them because they belong to the user + return value.type !== 'jdk' || + // keep toolchains that lack a usable string id (e.g. partially-formed user files); + // we cannot safely deduplicate them and must not crash while reading them + typeof ((_a = value.provides) === null || _a === void 0 ? void 0 : _a.id) !== 'string' || + index === + self.findIndex(t => { var _a, _b; return t.type === value.type && ((_a = t.provides) === null || _a === void 0 ? void 0 : _a.id) === ((_b = value.provides) === null || _b === void 0 ? void 0 : _b.id); }); }); } - else - xmlObj = (0, xmlbuilder2_1.create)({ - toolchains: { - '@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0', - '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', - '@xsi:schemaLocation': 'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd', - toolchain: [ - { - type: 'jdk', - provides: { - version: `${version}`, - vendor: `${vendor}`, - id: `${id}` - }, - configuration: { - jdkHome: `${jdkHome}` - } - } - ] - } - }); - return xmlObj.end({ + return (0, xmlbuilder2_1.create)({ + toolchains: Object.assign(Object.assign({}, rootAttributes), { toolchain: jsToolchains }) + }).end({ format: 'xml', wellFormed: false, headless: false, diff --git a/src/toolchains.ts b/src/toolchains.ts index 77cae83f7..a94a56302 100644 --- a/src/toolchains.ts +++ b/src/toolchains.ts @@ -83,47 +83,76 @@ export function generateToolchainDefinition( id: string, jdkHome: string ) { - let xmlObj; + let jsToolchains: Toolchain[] = [ + { + type: 'jdk', + provides: { + version: `${version}`, + vendor: `${vendor}`, + id: `${id}` + }, + configuration: { + jdkHome: `${jdkHome}` + } + } + ]; + // default root attributes, used when the existing file does not declare its own + let rootAttributes: Record = { + '@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0', + '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', + '@xsi:schemaLocation': + 'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd' + }; if (original?.length) { - xmlObj = xmlCreate(original) + // convert existing toolchains into TS native objects for better handling + // xmlbuilder2 will convert the document into a `{toolchains: { toolchain: [] | {} }}` structure + // instead of the desired `toolchains: [{}]` one or simply `[{}]` + const jsObj = xmlCreate(original) .root() - .ele({ - toolchain: { - type: 'jdk', - provides: { - version: `${version}`, - vendor: `${vendor}`, - id: `${id}` - }, - configuration: { - jdkHome: `${jdkHome}` - } + .toObject() as unknown as ExtractedToolchains; + if (jsObj.toolchains) { + // preserve the existing root attributes (xmlns, schemaLocation, …) so we don't + // silently rewrite user-managed metadata or change the effective XML namespace; + // xmlbuilder2 exposes attributes as `@`-prefixed keys on the element object + const existingAttributes = Object.fromEntries( + Object.entries(jsObj.toolchains).filter(([key]) => key.startsWith('@')) + ) as Record; + // fall back to the defaults only for attributes the existing file is missing + rootAttributes = {...rootAttributes, ...existingAttributes}; + + if (jsObj.toolchains.toolchain) { + // in case only a single child exists xmlbuilder2 will not create an array and using verbose = true equally doesn't work here + // See https://oozcitak.github.io/xmlbuilder2/serialization.html#js-object-and-map-serializers for details + if (Array.isArray(jsObj.toolchains.toolchain)) { + jsToolchains.push(...jsObj.toolchains.toolchain); + } else { + jsToolchains.push(jsObj.toolchains.toolchain); } - }); - } else - xmlObj = xmlCreate({ - toolchains: { - '@xmlns': 'http://maven.apache.org/TOOLCHAINS/1.1.0', - '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', - '@xsi:schemaLocation': - 'http://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd', - toolchain: [ - { - type: 'jdk', - provides: { - version: `${version}`, - vendor: `${vendor}`, - id: `${id}` - }, - configuration: { - jdkHome: `${jdkHome}` - } - } - ] } - }); + } + + // remove potential duplicates based on type & id (which should be a unique combination); + // self.findIndex will only return the first occurrence, ensuring duplicates are skipped + jsToolchains = jsToolchains.filter( + (value, index, self) => + // ensure non-jdk toolchains are kept in the results, we must not touch them because they belong to the user + value.type !== 'jdk' || + // keep toolchains that lack a usable string id (e.g. partially-formed user files); + // we cannot safely deduplicate them and must not crash while reading them + typeof value.provides?.id !== 'string' || + index === + self.findIndex( + t => t.type === value.type && t.provides?.id === value.provides?.id + ) + ); + } - return xmlObj.end({ + return xmlCreate({ + toolchains: { + ...rootAttributes, + toolchain: jsToolchains + } + }).end({ format: 'xml', wellFormed: false, headless: false, @@ -166,3 +195,24 @@ async function writeToolchainsFileToDisk( flag: 'w' }); } + +interface ExtractedToolchains { + toolchains: { + // root attributes such as xmlns / schemaLocation are exposed as `@`-prefixed keys + [attribute: `@${string}`]: string; + toolchain?: Toolchain[] | Toolchain; + }; +} + +// Toolchain type definition according to Maven Toolchains XSD 1.1.0 +interface Toolchain { + type: string; + provides: + | { + version: string; + vendor: string; + id: string; + } + | any; + configuration: any; +} From a50fdccef19f861401a6f00b7caa2abf98504acb Mon Sep 17 00:00:00 2001 From: John Jiang Date: Tue, 7 Jul 2026 23:00:40 +0800 Subject: [PATCH 42/57] dist: Support Tencent Kona JDK (#672) * Support Tencent Kona JDK (#672) Signed-off-by: John Jiang * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Address Copilot review feedback for Kona distribution - Sort matching releases by semver descending so range versions (e.g. >=17) resolve to the newest matching Kona JDK instead of the lowest - Rename downloaded archive on Windows before extraction (renameWinArchive) to avoid extraction failures - Import semver for version sorting Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Signed-off-by: John Jiang Co-authored-by: Bruno Borges Co-authored-by: Bruno Borges Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/e2e-versions.yml | 3 +- README.md | 2 + __tests__/data/kona.json | 162 +++++++++++++ __tests__/distributors/kona-installer.test.ts | 223 ++++++++++++++++++ dist/setup/index.js | 184 +++++++++++++++ docs/advanced-usage.md | 13 + src/distributions/distribution-factory.ts | 6 +- src/distributions/kona/installer.ts | 191 +++++++++++++++ src/distributions/kona/models.ts | 25 ++ 9 files changed, 807 insertions(+), 2 deletions(-) create mode 100644 __tests__/data/kona.json create mode 100644 __tests__/distributors/kona-installer.test.ts create mode 100644 src/distributions/kona/installer.ts create mode 100644 src/distributions/kona/models.ts diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 2bcf4883b..6dc713e95 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -36,7 +36,8 @@ jobs: 'corretto', 'dragonwell', 'sapmachine', - 'jetbrains' + 'jetbrains', + 'kona' ] # internally 'adopt-hotspot' is the same as 'adopt' version: ['21', '11', '17'] exclude: diff --git a/README.md b/README.md index 53f7f70ad..ac471c667 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ Currently, the following distributions are supported: | `graalvm` | [Oracle GraalVM](https://www.graalvm.org/) | [`graalvm` license](https://www.oracle.com/downloads/licenses/graal-free-license.html) | `graalvm-community` | [GraalVM Community](https://github.com/graalvm/graalvm-ce-builds/releases) | [`graalvm-community` license](https://github.com/oracle/graal/blob/master/LICENSE) | `jetbrains` | [JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/) | [`jetbrains` license](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE) +| `kona` | [Tencent Kona JDK](https://tencent.github.io/konajdk/) | [`kona` license](https://tencent.github.io/konajdk/LICENSE.txt) | `jdkfile` | Custom JDK Installation | | > [!NOTE] @@ -281,6 +282,7 @@ In the example above multiple JDKs are installed for the same job. The result af - [SapMachine](docs/advanced-usage.md#SapMachine) - [GraalVM](docs/advanced-usage.md#GraalVM) - [JetBrains](docs/advanced-usage.md#JetBrains) + - [Tencent Kona](docs/advanced-usage.md#Tencent-Kona) - [Installing custom Java package type](docs/advanced-usage.md#Installing-custom-Java-package-type) - [Installing custom Java architecture](docs/advanced-usage.md#Installing-custom-Java-architecture) - [Installing custom Java distribution from local file](docs/advanced-usage.md#Installing-Java-from-local-file) diff --git a/__tests__/data/kona.json b/__tests__/data/kona.json new file mode 100644 index 000000000..1f250ebe6 --- /dev/null +++ b/__tests__/data/kona.json @@ -0,0 +1,162 @@ +{ + "8": [ + { + "version": "8.0.20", + "jdkVersion": "8u432", + "latest": true, + "baseUrl": "https://github.com/Tencent/TencentKona-8/releases/download/8.0.20-GA/", + "files": [ + { + "os": "linux", + "arch": "aarch64", + "filename": "TencentKona8.0.20.b1_jdk_linux-aarch64_8u432.tar.gz", + "checksum": "8e6ab38b17f98d7ba727037cb49bbd174f3103a6ddacafb1fb7c0231006a80a7" + }, + { + "os": "linux", + "arch": "x86_64", + "filename": "TencentKona8.0.20.b1_jdk_linux-x86_64_8u432.tar.gz", + "checksum": "384cdb36b38993f4b7292682a5dfd8d5d33ba7bdbca2d95018d1341c792d2823" + }, + { + "os": "macos", + "arch": "aarch64", + "filename": "TencentKona8.0.20.b1_jdk_macosx-aarch64_8u432_notarized.tar.gz", + "checksum": "829c46691a4b519f14fedfcdca32a94d7793d3570c4a51b3a5072cc394619f25" + }, + { + "os": "macos", + "arch": "x86_64", + "filename": "TencentKona8.0.20.b1_jdk_macosx-x86_64_8u432_notarized.tar.gz", + "checksum": "" + }, + { + "os": "windows", + "arch": "x86_64", + "filename": "TencentKona8.0.20.b1_jdk_windows-x86_64_8u432_signed.zip", + "checksum": "339646817254dbcb5c17904807bbfdeafaa8e4bac9f2aae25434870cdeaba296" + } + ] + } + ], + "11": [ + { + "version": "11.0.25", + "jdkVersion": "11.0.25", + "latest": true, + "baseUrl": "https://github.com/Tencent/TencentKona-11/releases/download/kona11.0.25/", + "files": [ + { + "os": "linux", + "arch": "aarch64", + "filename": "TencentKona-11.0.25.b1-jdk_linux-aarch64.tar.gz", + "checksum": "887ca5eeb675dd9b9d22833a8b0c1031ee1a031227d6cf2d8c1920cc585d2b71" + }, + { + "os": "linux", + "arch": "x86_64", + "filename": "TencentKona-11.0.25.b1-jdk_linux-x86_64.tar.gz", + "checksum": "6642d7cccf98f33b3ec55cdbf77979c614f0d3cbbd282e8d0df52233edc52f9a" + }, + { + "os": "macos", + "arch": "aarch64", + "filename": "TencentKona-11.0.25.b1_jdk_macosx-aarch64_notarized.tar.gz", + "checksum": "8f07242d3191a35c3b2fca1122f315518c7312f0155e98ac7ae39ea083f93e21" + }, + { + "os": "macos", + "arch": "x86_64", + "filename": "TencentKona-11.0.25.b1_jdk_macosx-x86_64_notarized.tar.gz", + "checksum": "0832b93d8d8122cb72db85321ddd85c9a6086e0f28327733fc2da3c0ecc9c455" + }, + { + "os": "windows", + "arch": "x86_64", + "filename": "TencentKona-11.0.25.b1_jdk_windows-x86_64_signed.zip", + "checksum": "05c470c5da4b3bc1844117f611ecd241d3ae9e5d01c17ffafffde0f23825aad7" + } + ] + } + ], + "17": [ + { + "version": "17.0.13", + "jdkVersion": "17.0.13", + "latest": true, + "baseUrl": "https://github.com/Tencent/TencentKona-17/releases/download/TencentKona-17.0.13/", + "files": [ + { + "os": "linux", + "arch": "aarch64", + "filename": "TencentKona-17.0.13.b1-jdk_linux-aarch64.tar.gz", + "checksum": "372411dff5b42f6e419f1dd40772d98141a15c3d180ed1af6f2b49bdbbd32d52" + }, + { + "os": "linux", + "arch": "x86_64", + "filename": "TencentKona-17.0.13.b1-jdk_linux-x86_64.tar.gz", + "checksum": "b54bb023d1187737b23ca34d0857d2d40822b14e38d28c7948c8ff6b5927e523" + }, + { + "os": "macos", + "arch": "aarch64", + "filename": "TencentKona-17.0.13.b1_jdk_macosx-aarch64_notarized.tar.gz", + "checksum": "22f5d296c407fc137e6af9ce275e662346ca82f1a5acfc407247efd8cedf5256" + }, + { + "os": "macos", + "arch": "x86_64", + "filename": "TencentKona-17.0.13.b1_jdk_macosx-x86_64_notarized.tar.gz", + "checksum": "87ace41ac9718f2a9512b24bad0f735bc5ac61b8198b4cd5634199f124883b36" + }, + { + "os": "windows", + "arch": "x86_64", + "filename": "TencentKona-17.0.13.b1_jdk_windows-x86_64_signed.zip", + "checksum": "616089018151e8e5daf8e88276063633a2cb28a718a2afce49bb8fc10541e83d" + } + ] + } + ], + "21": [ + { + "version": "21.0.5", + "jdkVersion": "21.0.5", + "latest": true, + "baseUrl": "https://github.com/Tencent/TencentKona-21/releases/download/TencentKona-21.0.5/", + "files": [ + { + "os": "linux", + "arch": "aarch64", + "filename": "TencentKona-21.0.5.b1-jdk_linux-aarch64.tar.gz", + "checksum": "d8ca108147db3f19134d7aa995bac14e1fb3d124b0300a9a7893266a8f028104" + }, + { + "os": "linux", + "arch": "x86_64", + "filename": "TencentKona-21.0.5.b1-jdk_linux-x86_64.tar.gz", + "checksum": "afae039d9666fadcb84940c5350b29cd061019b0cc43700f0bf0342320892adf" + }, + { + "os": "macos", + "arch": "aarch64", + "filename": "TencentKona-21.0.5.b1_jdk_macosx-aarch64_notarized.tar.gz", + "checksum": "7621a218767bfbd3023b176dc6d9dd019677f8efec0d48a4eb2b2ed2b50bd1fb" + }, + { + "os": "macos", + "arch": "x86_64", + "filename": "TencentKona-21.0.5.b1_jdk_macosx-x86_64_notarized.tar.gz", + "checksum": "6c54d46f979ad998b708f664c5aeeeef855660ef527d584a7c2930951cca9999" + }, + { + "os": "windows", + "arch": "x86_64", + "filename": "TencentKona-21.0.5.b1_jdk_windows-x86_64_signed.zip", + "checksum": "ee1ee730fc5e02268d91b9df602b65122dad25b3d3898069331ebc8338005da1" + } + ] + } + ] +} diff --git a/__tests__/distributors/kona-installer.test.ts b/__tests__/distributors/kona-installer.test.ts new file mode 100644 index 000000000..d90fdd943 --- /dev/null +++ b/__tests__/distributors/kona-installer.test.ts @@ -0,0 +1,223 @@ +import {KonaDistribution} from '../../src/distributions/kona/installer'; + +import manifestData from '../data/kona.json'; + +function mockDistr( + version: string, + os: string, + arch: string, + packageType: string +): KonaDistribution { + const distribution = new KonaDistribution({ + version: version, + architecture: arch, + packageType: packageType, + checkLatest: false + }); + + distribution['getOs'] = () => os; + distribution['fetchReleaseInfo'] = async () => manifestData; + + return distribution; +} + +describe('Check getAvailableReleases', () => { + it.each([ + ['8', 'linux', 'aarch64', 'linux-aarch64'], + ['8.0.20', 'macos', 'x86_64', 'macosx-x86_64'], + ['11', 'linux', 'x86_64', 'linux-x86_64'], + ['11.0.25', 'macos', 'aarch64', 'macosx-aarch64'], + ['17.0.13', 'windows', 'x86_64', 'windows-x86_64'], + ['21.0.5', 'linux', 'x86_64', 'linux-x86_64'] + ])( + 'should get releases with the specified version "%s", OS "%s" and arch "%s"', + async ( + version: string, + os: string, + arch: string, + expectedPattern: string + ) => { + const distribution = mockDistr(version, os, arch, 'jdk'); + + const releases = await distribution['getAvailableReleases'](); + expect(releases).not.toBeNull(); + expect(releases.length).toBe(4); + releases.forEach(release => + expect(release.downloadUrl).toContain(expectedPattern) + ); + } + ); +}); + +describe('Check findPackageForDownload', () => { + it.each([ + [ + '8', + 'linux', + 'aarch64', + 'https://github.com/Tencent/TencentKona-8/releases/download/8.0.20-GA/TencentKona8.0.20.b1_jdk_linux-aarch64_8u432.tar.gz' + ], + [ + '8.0.20', + 'linux', + 'x86_64', + 'https://github.com/Tencent/TencentKona-8/releases/download/8.0.20-GA/TencentKona8.0.20.b1_jdk_linux-x86_64_8u432.tar.gz' + ], + [ + '8.0.20', + 'macos', + 'aarch64', + 'https://github.com/Tencent/TencentKona-8/releases/download/8.0.20-GA/TencentKona8.0.20.b1_jdk_macosx-aarch64_8u432_notarized.tar.gz' + ], + [ + '8.0.20', + 'macos', + 'x86_64', + 'https://github.com/Tencent/TencentKona-8/releases/download/8.0.20-GA/TencentKona8.0.20.b1_jdk_macosx-x86_64_8u432_notarized.tar.gz' + ], + [ + '8.0.20', + 'windows', + 'x86_64', + 'https://github.com/Tencent/TencentKona-8/releases/download/8.0.20-GA/TencentKona8.0.20.b1_jdk_windows-x86_64_8u432_signed.zip' + ], + + [ + '11', + 'linux', + 'aarch64', + 'https://github.com/Tencent/TencentKona-11/releases/download/kona11.0.25/TencentKona-11.0.25.b1-jdk_linux-aarch64.tar.gz' + ], + [ + '11.0.25', + 'linux', + 'x86_64', + 'https://github.com/Tencent/TencentKona-11/releases/download/kona11.0.25/TencentKona-11.0.25.b1-jdk_linux-x86_64.tar.gz' + ], + [ + '11.0.25', + 'macos', + 'aarch64', + 'https://github.com/Tencent/TencentKona-11/releases/download/kona11.0.25/TencentKona-11.0.25.b1_jdk_macosx-aarch64_notarized.tar.gz' + ], + [ + '11.0.25', + 'macos', + 'x86_64', + 'https://github.com/Tencent/TencentKona-11/releases/download/kona11.0.25/TencentKona-11.0.25.b1_jdk_macosx-x86_64_notarized.tar.gz' + ], + [ + '11.0.25', + 'windows', + 'x86_64', + 'https://github.com/Tencent/TencentKona-11/releases/download/kona11.0.25/TencentKona-11.0.25.b1_jdk_windows-x86_64_signed.zip' + ], + + [ + '17', + 'linux', + 'aarch64', + 'https://github.com/Tencent/TencentKona-17/releases/download/TencentKona-17.0.13/TencentKona-17.0.13.b1-jdk_linux-aarch64.tar.gz' + ], + [ + '17.0.13', + 'linux', + 'x86_64', + 'https://github.com/Tencent/TencentKona-17/releases/download/TencentKona-17.0.13/TencentKona-17.0.13.b1-jdk_linux-x86_64.tar.gz' + ], + [ + '17.0.13', + 'macos', + 'aarch64', + 'https://github.com/Tencent/TencentKona-17/releases/download/TencentKona-17.0.13/TencentKona-17.0.13.b1_jdk_macosx-aarch64_notarized.tar.gz' + ], + [ + '17.0.13', + 'macos', + 'x86_64', + 'https://github.com/Tencent/TencentKona-17/releases/download/TencentKona-17.0.13/TencentKona-17.0.13.b1_jdk_macosx-x86_64_notarized.tar.gz' + ], + [ + '17.0.13', + 'windows', + 'x86_64', + 'https://github.com/Tencent/TencentKona-17/releases/download/TencentKona-17.0.13/TencentKona-17.0.13.b1_jdk_windows-x86_64_signed.zip' + ], + + [ + '21', + 'linux', + 'aarch64', + 'https://github.com/Tencent/TencentKona-21/releases/download/TencentKona-21.0.5/TencentKona-21.0.5.b1-jdk_linux-aarch64.tar.gz' + ], + [ + '21.0.5', + 'linux', + 'x86_64', + 'https://github.com/Tencent/TencentKona-21/releases/download/TencentKona-21.0.5/TencentKona-21.0.5.b1-jdk_linux-x86_64.tar.gz' + ], + [ + '21.0.5', + 'macos', + 'aarch64', + 'https://github.com/Tencent/TencentKona-21/releases/download/TencentKona-21.0.5/TencentKona-21.0.5.b1_jdk_macosx-aarch64_notarized.tar.gz' + ], + [ + '21.0.5', + 'macos', + 'x86_64', + 'https://github.com/Tencent/TencentKona-21/releases/download/TencentKona-21.0.5/TencentKona-21.0.5.b1_jdk_macosx-x86_64_notarized.tar.gz' + ], + [ + '21.0.5', + 'windows', + 'x86_64', + 'https://github.com/Tencent/TencentKona-21/releases/download/TencentKona-21.0.5/TencentKona-21.0.5.b1_jdk_windows-x86_64_signed.zip' + ] + ])( + 'should return the download URL with the specified version "%s", OS "%s" and arch "%s"', + async (version: string, os: string, arch: string, expectedUrl: string) => { + const distribution = mockDistr(version, os, arch, 'jdk'); + + const availableRelease = + await distribution['findPackageForDownload'](version); + expect(availableRelease).not.toBeNull(); + expect(availableRelease.url).toBe(expectedUrl); + } + ); +}); + +describe('No release is found', () => { + it.each([ + ['8', 'linux', 'x86'], + ['8.0.0', 'linux', 'x86_64'], + ['11', 'linux', 'ppc64'], + ['17', 'solaris', 'x86_64'], + ['17', 'windows', 'aarch64'], + ['22', 'macos', 'x86_64'] + ])( + `should throw an error due to no release with the specified version "%s", os "%s" and arch "%s"`, + async (version: string, os: string, arch: string) => { + const distribution = mockDistr(version, os, arch, 'jdk'); + + await expect( + distribution['findPackageForDownload'](version) + ).rejects.toThrow( + `No Kona release for the specified version "${version}" on OS "${os}" and arch "${arch}".` + ); + } + ); +}); + +describe('The package type must be jdk', () => { + it('should throw an error due to the specified package type is not jdk', async () => { + const version = '8.0.20'; + const os = 'linux'; + const arch = 'x86_64'; + const distribution = mockDistr(version, os, arch, 'jre'); + + await expect( + distribution['findPackageForDownload'](version) + ).rejects.toThrow('Kona provides jdk only'); + }); +}); diff --git a/dist/setup/index.js b/dist/setup/index.js index af4581917..5fb690f20 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78771,6 +78771,7 @@ const installer_10 = __nccwpck_require__(37675); const installer_11 = __nccwpck_require__(17557); const installer_12 = __nccwpck_require__(26968); const installer_13 = __nccwpck_require__(62282); +const installer_14 = __nccwpck_require__(48151); var JavaDistribution; (function (JavaDistribution) { JavaDistribution["Adopt"] = "adopt"; @@ -78789,6 +78790,7 @@ var JavaDistribution; JavaDistribution["GraalVM"] = "graalvm"; JavaDistribution["GraalVMCommunity"] = "graalvm-community"; JavaDistribution["JetBrains"] = "jetbrains"; + JavaDistribution["Kona"] = "kona"; })(JavaDistribution || (JavaDistribution = {})); function getJavaDistribution(distributionName, installerOptions, jdkFile) { switch (distributionName) { @@ -78823,6 +78825,8 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) { return new installer_12.GraalVMCommunityDistribution(installerOptions); case JavaDistribution.JetBrains: return new installer_13.JetBrainsDistribution(installerOptions); + case JavaDistribution.Kona: + return new installer_14.KonaDistribution(installerOptions); default: return null; } @@ -79602,6 +79606,186 @@ class JetBrainsDistribution extends base_installer_1.JavaBase { exports.JetBrainsDistribution = JetBrainsDistribution; +/***/ }), + +/***/ 48151: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.KonaDistribution = void 0; +const core = __importStar(__nccwpck_require__(37484)); +const tc = __importStar(__nccwpck_require__(33472)); +const semver_1 = __importDefault(__nccwpck_require__(62088)); +const fs_1 = __importDefault(__nccwpck_require__(79896)); +const path_1 = __importDefault(__nccwpck_require__(16928)); +const base_installer_1 = __nccwpck_require__(79935); +const util_1 = __nccwpck_require__(54527); +class KonaDistribution extends base_installer_1.JavaBase { + constructor(installerOptions) { + super('Kona', installerOptions); + } + downloadTool(javaRelease) { + return __awaiter(this, void 0, void 0, function* () { + core.info(`Downloading Kona JDK ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`); + const javaArchivePath = yield tc.downloadTool(javaRelease.url); + core.info(`Extracting Java archive...`); + const extension = (0, util_1.getDownloadArchiveExtension)(); + const archivePath = process.platform === 'win32' + ? (0, util_1.renameWinArchive)(javaArchivePath) + : javaArchivePath; + const extractedJavaPath = yield (0, util_1.extractJdkFile)(archivePath, extension); + const archiveName = fs_1.default.readdirSync(extractedJavaPath)[0]; + const jdkDirectory = path_1.default.join(extractedJavaPath, archiveName); + const version = this.getToolcacheVersionName(javaRelease.version); + const javaPath = yield tc.cacheDir(jdkDirectory, this.toolcacheFolderName, version, this.architecture); + return { version: javaRelease.version, path: javaPath }; + }); + } + findPackageForDownload(version) { + return __awaiter(this, void 0, void 0, function* () { + if (!this.stable) { + throw new Error('Kona provides stable releases only'); + } + if (this.packageType !== 'jdk') { + throw new Error('Kona provides jdk only'); + } + const availableReleases = yield this.getAvailableReleases(); + const releases = availableReleases + .filter(item => { + return (0, util_1.isVersionSatisfies)(version, item.version); + }) + .map(item => { + return { + version: item.version, + url: item.downloadUrl + }; + }) + .sort((a, b) => -semver_1.default.compareBuild(a.version, b.version)); + if (!releases.length) { + throw new Error(`No Kona release for the specified version "${version}" on OS "${this.getOs()}" and arch "${this.getArch()}".`); + } + return releases[0]; + }); + } + getAvailableReleases() { + return __awaiter(this, void 0, void 0, function* () { + if (core.isDebug()) { + console.time('Retrieving available releases for Kona took'); // eslint-disable-line no-console + } + const releaseInfo = yield this.fetchReleaseInfo(); + if (!releaseInfo) { + throw new Error(`Couldn't fetch Kona release information`); + } + const availableReleases = this.chooseReleases(this.getOs(), this.getArch(), releaseInfo); + if (core.isDebug()) { + core.startGroup('Print information about available releases'); + console.timeEnd('Retrieving available releases for Kona took'); // eslint-disable-line no-console + core.debug(availableReleases.map(item => item.version).join(', ')); + core.endGroup(); + } + return availableReleases; + }); + } + fetchReleaseInfo() { + return __awaiter(this, void 0, void 0, function* () { + const releasesInfoUrl = 'https://tencent.github.io/konajdk/releases/kona-v1.json'; + try { + core.debug(`Fetching Kona release info from URL: ${releasesInfoUrl}`); + return (yield this.http.getJson(releasesInfoUrl)) + .result; + } + catch (err) { + core.debug(`Fetching Kona release info from the URL: ${releasesInfoUrl} failed with the error: ${err.message}`); + return null; + } + }); + } + chooseReleases(os, arch, releaseInfo) { + const releases = []; + for (const majorVersion in releaseInfo) { + const versions = releaseInfo[majorVersion]; + for (const version of versions) { + if (!version.latest) { + continue; + } + for (const file of version.files) { + if (file.os === os && file.arch === arch) { + releases.push({ + version: version.version, + jdkVersion: version.jdkVersion, + os: os, + arch: arch, + downloadUrl: version.baseUrl + file.filename, + checksum: file.checksum + }); + break; + } + } + } + } + return releases; + } + getOs() { + switch (process.platform) { + case 'darwin': + return 'macos'; + case 'win32': + return 'windows'; + default: + return process.platform; + } + } + getArch() { + switch (this.architecture) { + case 'arm64': + return 'aarch64'; + case 'x64': + return 'x86_64'; + default: + return this.architecture; + } + } +} +exports.KonaDistribution = KonaDistribution; + + /***/ }), /***/ 32063: diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 58a6ca5d7..41899cb87 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -12,6 +12,7 @@ - [GraalVM](#GraalVM) - [GraalVM Community](#GraalVM-Community) - [JetBrains](#JetBrains) + - [Tencent Kona](#Tencent-Kona) - [Installing custom Java package type](#Installing-custom-Java-package-type) - [JavaFX Maven project](#JavaFX-Maven-project) - [Installing custom Java architecture](#Installing-custom-Java-architecture) @@ -234,6 +235,18 @@ The available package types are: - `jdk+ft` - JBRSDK (FreeType) - `jre+ft` - JBR (FreeType) +### Tencent Kona +**NOTE:** Tencent Kona supports major versions 8, 11, 17 and 21, and provides jdk only. + +```yaml +steps: +- uses: actions/checkout@v6 +- uses: actions/setup-java@v5 + with: + distribution: 'kona' + java-version: '21' +- run: java --version +``` ## Installing custom Java package type ```yaml diff --git a/src/distributions/distribution-factory.ts b/src/distributions/distribution-factory.ts index 0ff6597e1..22b48aee2 100644 --- a/src/distributions/distribution-factory.ts +++ b/src/distributions/distribution-factory.ts @@ -16,6 +16,7 @@ import { GraalVMDistribution } from './graalvm/installer'; import {JetBrainsDistribution} from './jetbrains/installer'; +import {KonaDistribution} from './kona/installer'; enum JavaDistribution { Adopt = 'adopt', @@ -33,7 +34,8 @@ enum JavaDistribution { SapMachine = 'sapmachine', GraalVM = 'graalvm', GraalVMCommunity = 'graalvm-community', - JetBrains = 'jetbrains' + JetBrains = 'jetbrains', + Kona = 'kona' } export function getJavaDistribution( @@ -82,6 +84,8 @@ export function getJavaDistribution( return new GraalVMCommunityDistribution(installerOptions); case JavaDistribution.JetBrains: return new JetBrainsDistribution(installerOptions); + case JavaDistribution.Kona: + return new KonaDistribution(installerOptions); default: return null; } diff --git a/src/distributions/kona/installer.ts b/src/distributions/kona/installer.ts new file mode 100644 index 000000000..e2fb5de94 --- /dev/null +++ b/src/distributions/kona/installer.ts @@ -0,0 +1,191 @@ +import * as core from '@actions/core'; +import * as tc from '@actions/tool-cache'; +import semver from 'semver'; + +import fs from 'fs'; +import path from 'path'; + +import {JavaBase} from '../base-installer'; +import {IKonaReleaseInfo, IKonaRelease} from './models'; +import { + JavaDownloadRelease, + JavaInstallerOptions, + JavaInstallerResults +} from '../base-models'; +import { + extractJdkFile, + getDownloadArchiveExtension, + isVersionSatisfies, + renameWinArchive +} from '../../util'; + +export class KonaDistribution extends JavaBase { + constructor(installerOptions: JavaInstallerOptions) { + super('Kona', installerOptions); + } + + protected async downloadTool( + javaRelease: JavaDownloadRelease + ): Promise { + core.info( + `Downloading Kona JDK ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...` + ); + const javaArchivePath = await tc.downloadTool(javaRelease.url); + + core.info(`Extracting Java archive...`); + + const extension = getDownloadArchiveExtension(); + const archivePath = + process.platform === 'win32' + ? renameWinArchive(javaArchivePath) + : javaArchivePath; + const extractedJavaPath = await extractJdkFile(archivePath, extension); + + const archiveName = fs.readdirSync(extractedJavaPath)[0]; + const jdkDirectory = path.join(extractedJavaPath, archiveName); + const version = this.getToolcacheVersionName(javaRelease.version); + + const javaPath = await tc.cacheDir( + jdkDirectory, + this.toolcacheFolderName, + version, + this.architecture + ); + + return {version: javaRelease.version, path: javaPath}; + } + + protected async findPackageForDownload( + version: string + ): Promise { + if (!this.stable) { + throw new Error('Kona provides stable releases only'); + } + + if (this.packageType !== 'jdk') { + throw new Error('Kona provides jdk only'); + } + + const availableReleases = await this.getAvailableReleases(); + const releases = availableReleases + .filter(item => { + return isVersionSatisfies(version, item.version); + }) + .map(item => { + return { + version: item.version, + url: item.downloadUrl + } as JavaDownloadRelease; + }) + .sort((a, b) => -semver.compareBuild(a.version, b.version)); + + if (!releases.length) { + throw new Error( + `No Kona release for the specified version "${version}" on OS "${this.getOs()}" and arch "${this.getArch()}".` + ); + } + + return releases[0]; + } + + private async getAvailableReleases(): Promise { + if (core.isDebug()) { + console.time('Retrieving available releases for Kona took'); // eslint-disable-line no-console + } + + const releaseInfo = await this.fetchReleaseInfo(); + if (!releaseInfo) { + throw new Error(`Couldn't fetch Kona release information`); + } + + const availableReleases = this.chooseReleases( + this.getOs(), + this.getArch(), + releaseInfo + ); + + if (core.isDebug()) { + core.startGroup('Print information about available releases'); + console.timeEnd('Retrieving available releases for Kona took'); // eslint-disable-line no-console + core.debug(availableReleases.map(item => item.version).join(', ')); + core.endGroup(); + } + + return availableReleases; + } + + private async fetchReleaseInfo(): Promise { + const releasesInfoUrl = + 'https://tencent.github.io/konajdk/releases/kona-v1.json'; + + try { + core.debug(`Fetching Kona release info from URL: ${releasesInfoUrl}`); + return (await this.http.getJson(releasesInfoUrl)) + .result; + } catch (err) { + core.debug( + `Fetching Kona release info from the URL: ${releasesInfoUrl} failed with the error: ${ + (err as Error).message + }` + ); + return null; + } + } + + private chooseReleases( + os: string, + arch: string, + releaseInfo: IKonaReleaseInfo + ): IKonaRelease[] { + const releases: IKonaRelease[] = []; + + for (const majorVersion in releaseInfo) { + const versions = releaseInfo[majorVersion]; + + for (const version of versions) { + if (!version.latest) { + continue; + } + + for (const file of version.files) { + if (file.os === os && file.arch === arch) { + releases.push({ + version: version.version, + jdkVersion: version.jdkVersion, + os: os, + arch: arch, + downloadUrl: version.baseUrl + file.filename, + checksum: file.checksum + }); + + break; + } + } + } + } + + return releases; + } + + private getOs(): string { + switch (process.platform) { + case 'darwin': + return 'macos'; + case 'win32': + return 'windows'; + default: + return process.platform; + } + } + + private getArch(): string { + switch (this.architecture) { + case 'arm64': + return 'aarch64'; + case 'x64': + return 'x86_64'; + default: + return this.architecture; + } + } +} diff --git a/src/distributions/kona/models.ts b/src/distributions/kona/models.ts new file mode 100644 index 000000000..196ab693e --- /dev/null +++ b/src/distributions/kona/models.ts @@ -0,0 +1,25 @@ +export interface IKonaReleaseInfo { + [majorVersion: string]: { + version: string; + jdkVersion: string; + latest: boolean; + + baseUrl: string; + files: { + os: string; // linux, macos, windows + arch: string; // x86_64, aarch64 + + filename: string; + checksum: string; + }[]; + }[]; +} + +export interface IKonaRelease { + version: string; + jdkVersion: string; + os: string; // linux, macos, windows + arch: string; // x86_64, aarch64 + downloadUrl: string; + checksum: string; // SHA-256 digest +} From 6657b993409da921e0c021d82aa9c159601e7a27 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 7 Jul 2026 18:38:57 +0200 Subject: [PATCH 43/57] feat: Add set-default option (#1017) * Add set-default option This option allows to install an additional JDK without making it the default one. I have wanted this for quite a long time as I'm running custom GitHub Actions with Java, which might require a specific JDK and I don't want to pollute the JDK that is used by the overall workflow calling the action. And I'm apparently not alone as there was a preexisting issue. Fixes #560 * Dedupe setJavaDefault and document multi-version/toolchain behavior - Refactor setJavaDefault to delegate shared output/env logic to setJavaEnvironment, avoiding duplication between the two. - Document that set-default applies to all JDKs in a multiline java-version, and that installed JDKs remain registered in Maven toolchains regardless of set-default. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: fix Prettier formatting in base-installer test Resolves the failing 'Basic validation / build' format-check on __tests__/distributors/base-installer.test.ts (line exceeded print width). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Bruno Borges Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/e2e-versions.yml | 74 +++++++++++++ README.md | 2 + __tests__/distributors/base-installer.test.ts | 104 ++++++++++++++++++ action.yml | 4 + dist/cleanup/index.js | 3 +- dist/setup/index.js | 37 +++++-- docs/advanced-usage.md | 29 +++++ src/constants.ts | 1 + src/distributions/base-installer.ts | 22 +++- src/distributions/base-models.ts | 1 + src/distributions/local/installer.ts | 12 +- src/setup-java.ts | 5 + 12 files changed, 280 insertions(+), 14 deletions(-) diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 6dc713e95..3080fd702 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -669,3 +669,77 @@ jobs: JAVA_PATH: ${{ steps.setup-java.outputs.path }} run: bash __tests__/verify-java.sh "17.0.10" "$JAVA_PATH" shell: bash + + setup-java-set-default: + name: set-default option - ${{ matrix.os }} + needs: setup-java-major-versions + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Setup Java 17 as default + uses: ./ + id: setup-java-17 + with: + distribution: 'temurin' + java-version: '17' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Setup Java 21 without setting as default + uses: ./ + id: setup-java-21 + with: + distribution: 'temurin' + java-version: '21' + set-default: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Verify JAVA_HOME still points to Java 17 + run: | + echo "JAVA_HOME=$JAVA_HOME" + echo "Java 17 path=${{ steps.setup-java-17.outputs.path }}" + if [ "$JAVA_HOME" != "${{ steps.setup-java-17.outputs.path }}" ]; then + echo "JAVA_HOME should still point to Java 17" + exit 1 + fi + shell: bash + - name: Verify java -version reports Java 17 + run: | + JAVA_VERSION=$(java -version 2>&1 | head -1) + echo "java -version: $JAVA_VERSION" + if ! echo "$JAVA_VERSION" | grep -q "17"; then + echo "Default java should still be version 17" + exit 1 + fi + shell: bash + - name: Verify JAVA_HOME_21 env var is set + run: | + $envName = "JAVA_HOME_21_${env:RUNNER_ARCH}" + $JavaVersionPath = [Environment]::GetEnvironmentVariable($envName) + if (-not $JavaVersionPath) { + Write-Host "$envName is not set" + exit 1 + } + if (-not (Test-Path "$JavaVersionPath")) { + Write-Host "$envName path does not exist: $JavaVersionPath" + exit 1 + } + Write-Host "$envName=$JavaVersionPath" + shell: pwsh + - name: Verify Java 21 outputs are set + run: | + echo "Java 21 path=${{ steps.setup-java-21.outputs.path }}" + echo "Java 21 version=${{ steps.setup-java-21.outputs.version }}" + if [ -z "${{ steps.setup-java-21.outputs.path }}" ]; then + echo "Java 21 path output should be set" + exit 1 + fi + if [ -z "${{ steps.setup-java-21.outputs.version }}" ]; then + echo "Java 21 version output should be set" + exit 1 + fi + shell: bash diff --git a/README.md b/README.md index ac471c667..d11e1855d 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ For more details, see the full release notes on the [releases page](https://git - `check-latest`: Setting this option makes the action to check for the latest available version for the version spec. + - `set-default`: Set to `false` to install a JDK without making it the default. When `false`, `JAVA_HOME` and `PATH` are not updated, but `JAVA_HOME__` is still set so the JDK remains discoverable. Default value: `true`. See [Installing JDK without setting as default](docs/advanced-usage.md#Installing-JDK-without-setting-as-default) for more details. + - `verify-signature`: Verifies downloaded Java package signatures when supported by the selected distribution. Currently supported for `temurin` and `microsoft`. If set to `true` for unsupported distributions, the action fails. - `verify-signature-public-key`: ASCII-armored GPG public key used to verify the downloaded package signature. Overrides the default bundled key for the selected distribution. diff --git a/__tests__/distributors/base-installer.test.ts b/__tests__/distributors/base-installer.test.ts index 7497b06a3..1ee703cd2 100644 --- a/__tests__/distributors/base-installer.test.ts +++ b/__tests__/distributors/base-installer.test.ts @@ -563,6 +563,110 @@ describe('setupJava', () => { expect(spyCoreExportVariable).not.toHaveBeenCalled(); expect(spyCoreSetOutput).not.toHaveBeenCalled(); }); + + it('should not set JAVA_HOME and PATH when setDefault is false', async () => { + mockJavaBase = new EmptyJavaBase({ + version: '11', + architecture: 'x86', + packageType: 'jdk', + checkLatest: false, + setDefault: false + }); + await expect(mockJavaBase.setupJava()).resolves.toEqual({ + version: installedJavaVersion, + path: javaPath + }); + expect(spyCoreExportVariable).not.toHaveBeenCalledWith( + 'JAVA_HOME', + expect.anything() + ); + expect(spyCoreAddPath).not.toHaveBeenCalled(); + expect(spyCoreExportVariable).toHaveBeenCalledWith( + 'JAVA_HOME_11_X86', + javaPath + ); + expect(spyCoreSetOutput).toHaveBeenCalledWith( + 'version', + installedJavaVersion + ); + expect(spyCoreSetOutput).toHaveBeenCalledWith('path', javaPath); + expect(spyCoreSetOutput).toHaveBeenCalledWith('distribution', 'Empty'); + expect(spyCoreInfo).toHaveBeenCalledWith( + `Installing Java ${installedJavaVersion} (not setting as default)` + ); + }); + + it('should set JAVA_HOME and PATH when setDefault is true', async () => { + mockJavaBase = new EmptyJavaBase({ + version: '11', + architecture: 'x86', + packageType: 'jdk', + checkLatest: false, + setDefault: true + }); + await expect(mockJavaBase.setupJava()).resolves.toEqual({ + version: installedJavaVersion, + path: javaPath + }); + expect(spyCoreExportVariable).toHaveBeenCalledWith('JAVA_HOME', javaPath); + expect(spyCoreAddPath).toHaveBeenCalledWith(path.join(javaPath, 'bin')); + expect(spyCoreExportVariable).toHaveBeenCalledWith( + 'JAVA_HOME_11_X86', + javaPath + ); + expect(spyCoreInfo).toHaveBeenCalledWith( + `Setting Java ${installedJavaVersion} as the default` + ); + }); + + it('should default to setting as default when setDefault is not specified', async () => { + mockJavaBase = new EmptyJavaBase({ + version: '11', + architecture: 'x86', + packageType: 'jdk', + checkLatest: false + }); + await expect(mockJavaBase.setupJava()).resolves.toEqual({ + version: installedJavaVersion, + path: javaPath + }); + expect(spyCoreExportVariable).toHaveBeenCalledWith('JAVA_HOME', javaPath); + expect(spyCoreAddPath).toHaveBeenCalledWith(path.join(javaPath, 'bin')); + expect(spyCoreInfo).toHaveBeenCalledWith( + `Setting Java ${installedJavaVersion} as the default` + ); + }); + + it('should download and not set default when setDefault is false', async () => { + mockJavaBase = new EmptyJavaBase({ + version: '11', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false, + setDefault: false + }); + await expect(mockJavaBase.setupJava()).resolves.toEqual({ + version: '11.0.9', + path: path.join('toolcache', 'Java_Empty_jdk', '11.0.9', 'x64') + }); + expect(spyCoreExportVariable).not.toHaveBeenCalledWith( + 'JAVA_HOME', + expect.anything() + ); + expect(spyCoreAddPath).not.toHaveBeenCalled(); + expect(spyCoreExportVariable).toHaveBeenCalledWith( + 'JAVA_HOME_11_X64', + path.join('toolcache', 'Java_Empty_jdk', '11.0.9', 'x64') + ); + expect(spyCoreSetOutput).toHaveBeenCalledWith('version', '11.0.9'); + expect(spyCoreSetOutput).toHaveBeenCalledWith( + 'path', + path.join('toolcache', 'Java_Empty_jdk', '11.0.9', 'x64') + ); + expect(spyCoreInfo).toHaveBeenCalledWith( + 'Installing Java 11.0.9 (not setting as default)' + ); + }); }); describe('normalizeVersion', () => { diff --git a/action.yml b/action.yml index a5b673a8f..0edef47d4 100644 --- a/action.yml +++ b/action.yml @@ -26,6 +26,10 @@ inputs: description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec' required: false default: false + set-default: + description: 'Set this option to false if you want to install a JDK but not make it the default. When false, JAVA_HOME and PATH are not updated, but JAVA_HOME__ is still set.' + required: false + default: true verify-signature: description: 'Verify downloaded Java package signatures when supported by the selected distribution' required: false diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index ef4fed3b3..96ea04eb9 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52241,7 +52241,7 @@ else { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -52250,6 +52250,7 @@ exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; exports.INPUT_JDK_FILE = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; +exports.INPUT_SET_DEFAULT = 'set-default'; exports.INPUT_VERIFY_SIGNATURE = 'verify-signature'; exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; exports.INPUT_SERVER_ID = 'server-id'; diff --git a/dist/setup/index.js b/dist/setup/index.js index 5fb690f20..8db71b132 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78001,7 +78001,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -78010,6 +78010,7 @@ exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; exports.INPUT_JDK_FILE = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; +exports.INPUT_SET_DEFAULT = 'set-default'; exports.INPUT_VERIFY_SIGNATURE = 'verify-signature'; exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; exports.INPUT_SERVER_ID = 'server-id'; @@ -78325,6 +78326,10 @@ class JavaBase { this.architecture = installerOptions.architecture || os_1.default.arch(); this.packageType = installerOptions.packageType; this.checkLatest = installerOptions.checkLatest; + this.setDefault = + installerOptions.setDefault !== undefined + ? installerOptions.setDefault + : true; this.verifySignature = (_a = installerOptions.verifySignature) !== null && _a !== void 0 ? _a : false; this.verifySignaturePublicKey = installerOptions.verifySignaturePublicKey; } @@ -78445,8 +78450,14 @@ class JavaBase { if (process.platform === 'darwin' && fs.existsSync(macOSPostfixPath)) { foundJava.path = macOSPostfixPath; } - core.info(`Setting Java ${foundJava.version} as the default`); - this.setJavaDefault(foundJava.version, foundJava.path); + if (this.setDefault) { + core.info(`Setting Java ${foundJava.version} as the default`); + this.setJavaDefault(foundJava.version, foundJava.path); + } + else { + core.info(`Installing Java ${foundJava.version} (not setting as default)`); + this.setJavaEnvironment(foundJava.version, foundJava.path); + } return foundJava; }); } @@ -78547,9 +78558,12 @@ class JavaBase { return error; } setJavaDefault(version, toolPath) { - const majorVersion = version.split('.')[0]; core.exportVariable('JAVA_HOME', toolPath); core.addPath(path_1.default.join(toolPath, 'bin')); + this.setJavaEnvironment(version, toolPath); + } + setJavaEnvironment(version, toolPath) { + const majorVersion = version.split('.')[0]; core.setOutput('distribution', this.distribution); core.setOutput('path', toolPath); core.setOutput('version', version); @@ -80048,8 +80062,14 @@ class LocalDistribution extends base_installer_1.JavaBase { if (process.platform === 'darwin' && fs_1.default.existsSync(macOSPostfixPath)) { foundJava.path = macOSPostfixPath; } - core.info(`Setting Java ${foundJava.version} as default`); - this.setJavaDefault(foundJava.version, foundJava.path); + if (this.setDefault) { + core.info(`Setting Java ${foundJava.version} as the default`); + this.setJavaDefault(foundJava.version, foundJava.path); + } + else { + core.info(`Installing Java ${foundJava.version} (not setting as default)`); + this.setJavaEnvironment(foundJava.version, foundJava.path); + } return foundJava; }); } @@ -81532,6 +81552,7 @@ function run() { const cache = core.getInput(constants.INPUT_CACHE); const cacheDependencyPath = core.getInput(constants.INPUT_CACHE_DEPENDENCY_PATH); const checkLatest = (0, util_1.getBooleanInput)(constants.INPUT_CHECK_LATEST, false); + const setDefault = (0, util_1.getBooleanInput)(constants.INPUT_SET_DEFAULT, true); const verifySignature = (0, util_1.getBooleanInput)(constants.INPUT_VERIFY_SIGNATURE, false); const verifySignaturePublicKey = core.getInput(constants.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY) || undefined; let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); @@ -81546,6 +81567,7 @@ function run() { architecture, packageType, checkLatest, + setDefault, verifySignature, verifySignaturePublicKey, distributionName, @@ -81582,11 +81604,12 @@ function run() { run(); function installVersion(version, options, toolchainId = 0) { return __awaiter(this, void 0, void 0, function* () { - const { distributionName, jdkFile, architecture, packageType, checkLatest, verifySignature, verifySignaturePublicKey, toolchainIds } = options; + const { distributionName, jdkFile, architecture, packageType, checkLatest, setDefault, verifySignature, verifySignaturePublicKey, toolchainIds } = options; const installerOptions = { architecture, packageType, checkLatest, + setDefault, verifySignature, verifySignaturePublicKey, version diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 41899cb87..2b769abde 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -16,6 +16,7 @@ - [Installing custom Java package type](#Installing-custom-Java-package-type) - [JavaFX Maven project](#JavaFX-Maven-project) - [Installing custom Java architecture](#Installing-custom-Java-architecture) +- [Installing JDK without setting as default](#Installing-JDK-without-setting-as-default) - [Installing custom Java distribution from local file](#Installing-Java-from-local-file) - [Testing against different Java distributions](#Testing-against-different-Java-distributions) - [Testing against different platforms](#Testing-against-different-platforms) @@ -297,6 +298,34 @@ steps: - run: java --version ``` +## Installing JDK without setting as default + +When installing multiple JDKs, the last one installed becomes the default (`JAVA_HOME`, `PATH`). Use the `set-default` option to install a JDK without overriding the default. The installed JDK is still discoverable via the `JAVA_HOME__` environment variable (e.g. `JAVA_HOME_21_X64`). + +```yaml +steps: +- uses: actions/checkout@v6 +- uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '17' +- uses: actions/setup-java@v5 + id: setup-java-21 + with: + distribution: 'temurin' + java-version: '21' + set-default: false +- run: | + echo "Default java:" + java -version + echo "Java 21 home: $JAVA_HOME_21_X64" + echo "Java 21 path from output: ${{ steps.setup-java-21.outputs.path }}" +``` + +In this example, `JAVA_HOME` and `java` on `PATH` point to Java 17, while Java 21 is available via `JAVA_HOME_21_X64` or the step output `path`. + +> **Note:** When a single step installs multiple JDKs via a multiline `java-version`, the `set-default` value applies to all of them. With `set-default: false`, none of those JDKs become the default; each remains discoverable through its `JAVA_HOME__` variable. Regardless of `set-default`, installed JDKs are still registered in the Maven toolchains file, so they can be selected via Maven toolchains. + ## Installing Java from local file If your use-case requires a custom distribution or a version that is not provided by setup-java, you can download it manually and setup-java will take care of the installation and caching on the VM: diff --git a/src/constants.ts b/src/constants.ts index 641714c45..67a5d7ea6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,6 +6,7 @@ export const INPUT_JAVA_PACKAGE = 'java-package'; export const INPUT_DISTRIBUTION = 'distribution'; export const INPUT_JDK_FILE = 'jdkFile'; export const INPUT_CHECK_LATEST = 'check-latest'; +export const INPUT_SET_DEFAULT = 'set-default'; export const INPUT_VERIFY_SIGNATURE = 'verify-signature'; export const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; export const INPUT_SERVER_ID = 'server-id'; diff --git a/src/distributions/base-installer.ts b/src/distributions/base-installer.ts index 4494019cd..bf8d9d3b7 100644 --- a/src/distributions/base-installer.ts +++ b/src/distributions/base-installer.ts @@ -20,6 +20,7 @@ export abstract class JavaBase { protected packageType: string; protected stable: boolean; protected checkLatest: boolean; + protected setDefault: boolean; protected verifySignature: boolean; protected verifySignaturePublicKey: string | undefined; @@ -38,6 +39,10 @@ export abstract class JavaBase { this.architecture = installerOptions.architecture || os.arch(); this.packageType = installerOptions.packageType; this.checkLatest = installerOptions.checkLatest; + this.setDefault = + installerOptions.setDefault !== undefined + ? installerOptions.setDefault + : true; this.verifySignature = installerOptions.verifySignature ?? false; this.verifySignaturePublicKey = installerOptions.verifySignaturePublicKey; } @@ -179,8 +184,15 @@ export abstract class JavaBase { foundJava.path = macOSPostfixPath; } - core.info(`Setting Java ${foundJava.version} as the default`); - this.setJavaDefault(foundJava.version, foundJava.path); + if (this.setDefault) { + core.info(`Setting Java ${foundJava.version} as the default`); + this.setJavaDefault(foundJava.version, foundJava.path); + } else { + core.info( + `Installing Java ${foundJava.version} (not setting as default)` + ); + this.setJavaEnvironment(foundJava.version, foundJava.path); + } return foundJava; } @@ -312,9 +324,13 @@ export abstract class JavaBase { } protected setJavaDefault(version: string, toolPath: string) { - const majorVersion = version.split('.')[0]; core.exportVariable('JAVA_HOME', toolPath); core.addPath(path.join(toolPath, 'bin')); + this.setJavaEnvironment(version, toolPath); + } + + protected setJavaEnvironment(version: string, toolPath: string) { + const majorVersion = version.split('.')[0]; core.setOutput('distribution', this.distribution); core.setOutput('path', toolPath); core.setOutput('version', version); diff --git a/src/distributions/base-models.ts b/src/distributions/base-models.ts index 867a058af..b2ca3f0ff 100644 --- a/src/distributions/base-models.ts +++ b/src/distributions/base-models.ts @@ -3,6 +3,7 @@ export interface JavaInstallerOptions { architecture: string; packageType: string; checkLatest: boolean; + setDefault?: boolean; verifySignature?: boolean; verifySignaturePublicKey?: string; } diff --git a/src/distributions/local/installer.ts b/src/distributions/local/installer.ts index adfac4998..cf422bc6a 100644 --- a/src/distributions/local/installer.ts +++ b/src/distributions/local/installer.ts @@ -69,9 +69,15 @@ export class LocalDistribution extends JavaBase { foundJava.path = macOSPostfixPath; } - core.info(`Setting Java ${foundJava.version} as default`); - - this.setJavaDefault(foundJava.version, foundJava.path); + if (this.setDefault) { + core.info(`Setting Java ${foundJava.version} as the default`); + this.setJavaDefault(foundJava.version, foundJava.path); + } else { + core.info( + `Installing Java ${foundJava.version} (not setting as default)` + ); + this.setJavaEnvironment(foundJava.version, foundJava.path); + } return foundJava; } diff --git a/src/setup-java.ts b/src/setup-java.ts index d7a84fab0..d7e479880 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -29,6 +29,7 @@ async function run() { constants.INPUT_CACHE_DEPENDENCY_PATH ); const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false); + const setDefault = getBooleanInput(constants.INPUT_SET_DEFAULT, true); const verifySignature = getBooleanInput( constants.INPUT_VERIFY_SIGNATURE, false @@ -51,6 +52,7 @@ async function run() { architecture, packageType, checkLatest, + setDefault, verifySignature, verifySignaturePublicKey, distributionName, @@ -110,6 +112,7 @@ async function installVersion( architecture, packageType, checkLatest, + setDefault, verifySignature, verifySignaturePublicKey, toolchainIds @@ -119,6 +122,7 @@ async function installVersion( architecture, packageType, checkLatest, + setDefault, verifySignature, verifySignaturePublicKey, version @@ -155,6 +159,7 @@ interface installerInputsOptions { architecture: string; packageType: string; checkLatest: boolean; + setDefault: boolean; verifySignature: boolean; verifySignaturePublicKey: string | undefined; distributionName: string; From c4922bf8099a153c2ad2fa6c869b9e6fa8d21017 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 7 Jul 2026 14:14:25 -0400 Subject: [PATCH 44/57] docs: document problem matcher (and how to disable it), Maven Wrapper caching, and generated interactiveMode (#1075) * docs: document the Java problem matcher and how to disable it Add an advanced-usage section explaining the javac/java problem matcher that setup-java registers, and how to turn it off for a job using the built-in ::remove-matcher:: workflow command (owners javac and java). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: document Maven Wrapper caching and generated interactiveMode - README: note that cache: 'maven' also caches/restores the Maven Wrapper distribution (~/.m2/wrapper/dists), not just the local repository. - advanced-usage: note that the generated settings.xml sets interactiveMode=false for non-interactive CI runs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify Java problem matcher annotations Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 2 ++ docs/advanced-usage.md | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/README.md b/README.md index d11e1855d..fa3f83944 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,8 @@ The workflow output `cache-hit` is set to indicate if an exact match was found f The cache input is optional, and caching is turned off by default. +**Maven Wrapper:** when `cache: 'maven'` is enabled, the action also caches and restores the Maven Wrapper distribution downloaded to `~/.m2/wrapper/dists` (in addition to the local repository), so wrapper-based (`./mvnw`) builds don't re-download the wrapper on every run. This is keyed on `**/.mvn/wrapper/maven-wrapper.properties` as shown above. + #### Caching gradle dependencies ```yaml steps: diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 2b769abde..65193c4b1 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -516,6 +516,8 @@ The two `settings.xml` files created from the above example look like the follow ***NOTE***: The `settings.xml` file is created in the Actions `$HOME/.m2` directory. If you have an existing `settings.xml` file at that location, it will be overwritten. See [below](#apache-maven-with-a-settings-path) for using the `settings-path` to change your `settings.xml` file location. +***NOTE***: The generated `settings.xml` sets `false` so that Maven never blocks a CI run waiting on an interactive prompt. This is applied automatically whenever the action generates `settings.xml`. + If you don't want to overwrite the `settings.xml` file, you can set `overwrite-settings: false` ### Extra setup for pom.xml: @@ -601,6 +603,44 @@ jobs: - This setting only affects Maven. It has no effect on Gradle, sbt, or other build tools. - `-ntp` only controls transfer/progress output; it does not change whether Maven runs in batch mode. Use `-B`/`--batch-mode` (or `false` in `settings.xml`) if you also want non-interactive runs. +## Java problem matcher (compiler annotations) + +`setup-java` registers a [problem matcher](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md) for Java after installing the JDK. It scans the log output of subsequent steps and turns `javac` diagnostics into GitHub [annotations](https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-a-warning-message) that appear in the run summary and inline on the affected files. It matches two kinds of lines: + +- Compiler errors and warnings, e.g. `App.java:12: error: cannot find symbol` (owner `javac`). +- Uncaught-exception header lines, e.g. `Exception in thread "main" ...`; because these lines have no file or line captures, they appear as log/run-level annotations rather than inline file annotations (owner `java`). + +This is enabled by default and requires no configuration. + +### Disabling the problem matcher + +There is no action input to turn the matcher off, but you can disable it for the rest of the job with the built-in [`remove-matcher`](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md#remove-a-problem-matcher) workflow command. Pass the matcher **owner** (not a file name); the Java matcher defines two owners, `javac` and `java`, so remove both to fully suppress it: + +```yaml +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 + with: + distribution: '' + java-version: '21' + + - name: Disable the Java problem matcher + run: | + echo "::remove-matcher owner=javac::" + echo "::remove-matcher owner=java::" + + - name: Build with Maven + run: mvn -B package --file pom.xml +``` + +***NOTES***: +- `remove-matcher` only stops annotations from being created; the underlying compiler output is unchanged, so a failing `javac`/build still fails the step. +- The command is scoped to the job, so add the step right after `setup-java` (and before your build) in every job where you want the matcher disabled. + ## Publishing using Gradle ```yaml jobs: From 0f481fcb613427c0f801b606911222b5b6f3083a Mon Sep 17 00:00:00 2001 From: Lukasz <106734180+lukaszgyg@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:49:12 +0200 Subject: [PATCH 45/57] feat: Add distribution detection support to .sdkmanrc file (#975) * feat: Add distribution detection support to .sdkmanrc file Extends .sdkmanrc support to automatically detect Java distribution from SDKMAN identifiers (e.g., java=21.0.5-tem maps to temurin distribution). Makes distribution input optional when using .sdkmanrc with distribution suffix. * fix: align SDKMAN sem identifier mapping * fix: support SDKMAN albba identifier * docs: clarify sdkmanrc distribution inference scope Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add Tencent Kona SDKMAN mapping and format sdkmanrc docs as a table - Map SDKMAN 'kona' identifier to the 'kona' distribution (added in #672) - Add a .sdkmanrc test case for the kona suffix - Convert the inline SDKMAN suffix mapping in advanced-usage.md to a table - Rebuild dist bundles Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Bruno Borges Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- README.md | 4 +- __tests__/data/sdkman-java-versions.csv | 20 ++-- __tests__/util.test.ts | 65 +++++++++++-- action.yml | 4 +- dist/cleanup/index.js | 53 +++++++++-- dist/setup/index.js | 116 ++++++++++++++++++------ docs/advanced-usage.md | 22 ++++- src/setup-java.ts | 71 ++++++++++----- src/util.ts | 67 ++++++++++++-- 9 files changed, 333 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index fa3f83944..fead4a69f 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ For more details, see the full release notes on the [releases page](https://git - `java-version`: The Java version that is going to be set up. Takes a whole or [semver](#supported-version-syntax) Java version. If not specified, the action will expect `java-version-file` input to be specified. - - `java-version-file`: The path to a file containing java version. Supported file types are `.java-version` and `.tool-versions`. See more details in [about .java-version-file](docs/advanced-usage.md#Java-version-file). + - `java-version-file`: The path to a file containing java version. Supported file types are `.java-version`, `.tool-versions`, and `.sdkmanrc`. See more details in [about .java-version-file](docs/advanced-usage.md#Java-version-file). - - `distribution`: _(required)_ Java [distribution](#supported-distributions). + - `distribution`: Java [distribution](#supported-distributions). Required unless `java-version-file` points to `.sdkmanrc` with a recognized distribution suffix (for example `java=21.0.5-tem`). - `java-package`: The packaging variant of the chosen distribution. Possible values: `jdk`, `jre`, `jdk+fx`, `jre+fx`. Default value: `jdk`. diff --git a/__tests__/data/sdkman-java-versions.csv b/__tests__/data/sdkman-java-versions.csv index 5c75a44c9..edbdc265c 100644 --- a/__tests__/data/sdkman-java-versions.csv +++ b/__tests__/data/sdkman-java-versions.csv @@ -2,7 +2,7 @@ 7.0.352-zulu, 7.0.352 8.0.282-trava, 8.0.282 8.0.432-albba, 8.0.432 -8.0.432-amzn, 8.0.432 +8.0.432-amzn, 8 8.0.432-kona, 8.0.432 8.0.432-librca, 8.0.432 8.0.432-sem, 8.0.432 @@ -10,7 +10,7 @@ 8.0.432-zulu, 8.0.432 8.0.432.fx-librca, 8.0.432 8.0.432.fx-zulu, 8.0.432 -8.0.442-amzn, 8.0.442 +8.0.442-amzn, 8 8.0.442-librca, 8.0.442 8.0.442-tem, 8.0.442 8.0.442-zulu, 8.0.442 @@ -19,7 +19,7 @@ 11.0.14.1-jbr, 11.0.14 11.0.15-trava, 11.0.15 11.0.25-albba, 11.0.25 -11.0.25-amzn, 11.0.25 +11.0.25-amzn, 11 11.0.25-kona, 11.0.25 11.0.25-librca, 11.0.25 11.0.25-ms, 11.0.25 @@ -29,7 +29,7 @@ 11.0.25-zulu, 11.0.25 11.0.25.fx-librca, 11.0.25 11.0.25.fx-zulu, 11.0.25 -11.0.26-amzn, 11.0.26 +11.0.26-amzn, 11 11.0.26-librca, 11.0.26 11.0.26-ms, 11.0.26 11.0.26-sapmchn, 11.0.26 @@ -40,7 +40,7 @@ 17.0.12-jbr, 17.0.12 17.0.12-oracle, 17.0.12 17.0.13-albba, 17.0.13 -17.0.13-amzn, 17.0.13 +17.0.13-amzn, 17 17.0.13-kona, 17.0.13 17.0.13-librca, 17.0.13 17.0.13-ms, 17.0.13 @@ -52,7 +52,7 @@ 17.0.13.crac-zulu, 17.0.13 17.0.13.fx-librca, 17.0.13 17.0.13.fx-zulu, 17.0.13 -17.0.14-amzn, 17.0.14 +17.0.14-amzn, 17 17.0.14-librca, 17.0.14 17.0.14-ms, 17.0.14 17.0.14-sapmchn, 17.0.14 @@ -62,7 +62,7 @@ 17.0.9-graalce, 17.0.9 21.0.2-graalce, 21.0.2 21.0.2-open, 21.0.2 -21.0.5-amzn, 21.0.5 +21.0.5-amzn, 21 21.0.5-graal, 21.0.5 21.0.5-jbr, 21.0.5 21.0.5-kona, 21.0.5 @@ -77,7 +77,7 @@ 21.0.5.crac-zulu, 21.0.5 21.0.5.fx-librca, 21.0.5 21.0.5.fx-zulu, 21.0.5 -21.0.6-amzn, 21.0.6 +21.0.6-amzn, 21 21.0.6-graal, 21.0.6 21.0.6-librca, 21.0.6 21.0.6-ms, 21.0.6 @@ -94,7 +94,7 @@ 22.3.5.r17-mandrel, 22.3.5 22.3.5.r17-nik, 22.3.5 23-open, 23 -23.0.1-amzn, 23.0.1 +23.0.1-amzn, 23 23.0.1-graal, 23.0.1 23.0.1-graalce, 23.0.1 23.0.1-librca, 23.0.1 @@ -106,7 +106,7 @@ 23.0.1.crac-zulu, 23.0.1 23.0.1.fx-librca, 23.0.1 23.0.1.fx-zulu, 23.0.1 -23.0.2-amzn, 23.0.2 +23.0.2-amzn, 23 23.0.2-graal, 23.0.2 23.0.2-graalce, 23.0.2 23.0.2-librca, 23.0.2 diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 310a180ac..6782e6870 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -166,15 +166,60 @@ describe('validatePaginationUrl', () => { describe('getVersionFromFileContent', () => { describe('.sdkmanrc', () => { it.each([ - ['java=11.0.20.1-tem', '11.0.20'], - ['java = 11.0.20.1-tem', '11.0.20'], - ['java=11.0.20.1-tem # a comment in sdkmanrc', '11.0.20'], - ['java=11.0.20.1-tem\n#java=21.0.20.1-tem\n', '11.0.20'], // choose first match - ['java=11.0.20.1-tem\njava=21.0.20.1-tem\n', '11.0.20'], // choose first match - ['#java=11.0.20.1-tem\njava=21.0.20.1-tem\n', '21.0.20'] // first one is 'commented' in .sdkmanrc - ])('parsing %s should return %s', (content: string, expected: string) => { - const actual = getVersionFromFileContent(content, 'openjdk', '.sdkmanrc'); - expect(actual).toBe(expected); + ['java=11.0.20.1-tem', '11.0.20', 'temurin'], + ['java = 11.0.20.1-tem', '11.0.20', 'temurin'], + ['java=11.0.20.1-tem # a comment in sdkmanrc', '11.0.20', 'temurin'], + ['java=11.0.20.1-tem\n#java=21.0.20.1-tem\n', '11.0.20', 'temurin'], // choose first match + ['java=11.0.20.1-tem\njava=21.0.20.1-tem\n', '11.0.20', 'temurin'], // choose first match + ['#java=11.0.20.1-tem\njava=21.0.20.1-tem\n', '21.0.20', 'temurin'], // first one is 'commented' in .sdkmanrc + ['java=21.0.5-zulu', '21.0.5', 'zulu'], + ['java=17.0.13-albba', '17.0.13', 'dragonwell'], + ['java=17.0.13-amzn', '17', 'corretto'], + ['java=21.0.5-graal', '21.0.5', 'graalvm'], + ['java=17.0.9-graalce', '17.0.9', 'graalvm'], + ['java=11.0.25-librca', '11.0.25', 'liberica'], + ['java=11.0.25-ms', '11.0.25', 'microsoft'], + ['java=21.0.5-oracle', '21.0.5', 'oracle'], + ['java=11.0.25-sapmchn', '11.0.25', 'sapmachine'], + ['java=21.0.5-jbr', '21.0.5', 'jetbrains'], + ['java=11.0.25-sem', '11.0.25', 'semeru'], + ['java=17.0.13-dragonwell', '17.0.13', 'dragonwell'], + ['java=21.0.5-kona', '21.0.5', 'kona'] + ])( + 'parsing %s should return version %s and distribution %s', + (content: string, expectedVersion: string, expectedDist: string) => { + const actual = getVersionFromFileContent( + content, + 'openjdk', + '.sdkmanrc' + ); + expect(actual?.version).toBe(expectedVersion); + expect(actual?.distribution).toBe(expectedDist); + } + ); + + it('should warn and return undefined distribution for unknown identifier', () => { + const warnSpy = jest.spyOn(core, 'warning'); + const actual = getVersionFromFileContent( + 'java=21.0.5-unknown', + 'temurin', + '.sdkmanrc' + ); + expect(actual?.version).toBe('21.0.5'); + expect(actual?.distribution).toBeUndefined(); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('Unknown SDKMAN distribution identifier') + ); + }); + + it('should return version without distribution when no suffix provided', () => { + const actual = getVersionFromFileContent( + 'java=11.0.20', + 'temurin', + '.sdkmanrc' + ); + expect(actual?.version).toBe('11.0.20'); + expect(actual?.distribution).toBeUndefined(); }); describe('known versions', () => { @@ -193,7 +238,7 @@ describe('getVersionFromFileContent', () => { 'openjdk', '.sdkmanrc' ); - expect(actual).toBe(expected); + expect(actual?.version).toBe(expected); } ); }); diff --git a/action.yml b/action.yml index 0edef47d4..e42fb05cb 100644 --- a/action.yml +++ b/action.yml @@ -10,8 +10,8 @@ inputs: description: 'The path to a file containing the Java version to set up (.java-version, .tool-versions, .sdkmanrc). Used when java-version is not set. See examples of supported syntax in README file' required: false distribution: - description: 'Java distribution. See the list of supported distributions in README file' - required: true + description: 'Java distribution. See the list of supported distributions in README file. This input is required except when java-version-file points to .sdkmanrc with a recognized distribution suffix (e.g., java=21.0.5-tem).' + required: false java-package: description: 'The package type (jdk, jre, jdk+fx, jre+fx)' required: false diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 96ea04eb9..7b0733452 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52572,8 +52572,9 @@ function isCacheFeatureAvailable() { } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; function getVersionFromFileContent(content, distributionName, versionFile) { - var _a, _b, _c, _d, _e; + var _a, _b, _c; let javaVersionRegExp; + let extractedDistribution; function getFileName(versionFile) { return path_1.default.basename(versionFile); } @@ -52583,14 +52584,23 @@ function getVersionFromFileContent(content, distributionName, versionFile) { /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { - javaVersionRegExp = /^java\s*=\s*(?[^-]+)/m; + // Match both version and optional distribution identifier + javaVersionRegExp = + /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; } else { javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; } - const capturedVersion = ((_b = (_a = content.match(javaVersionRegExp)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.version) - ? (_d = (_c = content.match(javaVersionRegExp)) === null || _c === void 0 ? void 0 : _c.groups) === null || _d === void 0 ? void 0 : _d.version + const match = content.match(javaVersionRegExp); + const capturedVersion = ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.version) + ? match.groups.version : ''; + // Extract distribution from .sdkmanrc file + if (versionFileName == '.sdkmanrc' && ((_b = match === null || match === void 0 ? void 0 : match.groups) === null || _b === void 0 ? void 0 : _b.distribution)) { + const sdkmanDist = match.groups.distribution; + extractedDistribution = mapSdkmanDistribution(sdkmanDist); + core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); + } core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); if (!capturedVersion) { return null; @@ -52604,13 +52614,42 @@ function getVersionFromFileContent(content, distributionName, versionFile) { if (!version) { return null; } - if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(distributionName)) { - const coerceVersion = (_e = semver.coerce(version)) !== null && _e !== void 0 ? _e : version; + // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution + // (either explicitly provided or extracted from the version file) is in the list. + if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { + const coerceVersion = (_c = semver.coerce(version)) !== null && _c !== void 0 ? _c : version; version = semver.major(coerceVersion).toString(); } - return version.toString(); + return { + version: version.toString(), + distribution: extractedDistribution + }; } exports.getVersionFromFileContent = getVersionFromFileContent; +// Map SDKMAN distribution identifiers to setup-java distribution names +function mapSdkmanDistribution(sdkmanDist) { + const distributionMap = { + tem: 'temurin', + sem: 'semeru', + albba: 'dragonwell', + zulu: 'zulu', + amzn: 'corretto', + graal: 'graalvm', + graalce: 'graalvm', + librca: 'liberica', + ms: 'microsoft', + oracle: 'oracle', + sapmchn: 'sapmachine', + jbr: 'jetbrains', + dragonwell: 'dragonwell', + kona: 'kona' + }; + const mapped = distributionMap[sdkmanDist.toLowerCase()]; + if (!mapped) { + core.warning(`Unknown SDKMAN distribution identifier '${sdkmanDist}'. Please specify the distribution explicitly.`); + } + return mapped; +} // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content) { return content.startsWith('1.') ? content.substring(2) : content; diff --git a/dist/setup/index.js b/dist/setup/index.js index 8db71b132..871b71de7 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -81542,9 +81542,7 @@ function run() { return __awaiter(this, void 0, void 0, function* () { try { const versions = core.getMultilineInput(constants.INPUT_JAVA_VERSION); - const distributionName = core.getInput(constants.INPUT_DISTRIBUTION, { - required: true - }); + let distributionName = core.getInput(constants.INPUT_DISTRIBUTION); const versionFile = core.getInput(constants.INPUT_JAVA_VERSION_FILE); const architecture = core.getInput(constants.INPUT_ARCHITECTURE); const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE); @@ -81563,29 +81561,54 @@ function run() { if (!versions.length && !versionFile) { throw new Error('java-version or java-version-file input expected'); } - const installerInputsOptions = { - architecture, - packageType, - checkLatest, - setDefault, - verifySignature, - verifySignaturePublicKey, - distributionName, - jdkFile, - toolchainIds - }; if (!versions.length) { core.debug('java-version input is empty, looking for java-version-file input'); const content = fs_1.default.readFileSync(versionFile).toString().trim(); - const version = (0, util_1.getVersionFromFileContent)(content, distributionName, versionFile); - core.debug(`Parsed version from file '${version}'`); - if (!version) { + const versionInfo = (0, util_1.getVersionFromFileContent)(content, distributionName, versionFile); + core.debug(`Parsed version from file '${versionInfo === null || versionInfo === void 0 ? void 0 : versionInfo.version}'`); + if (!versionInfo) { throw new Error(`No supported version was found in file ${versionFile}`); } - yield installVersion(version, installerInputsOptions); + // Use distribution from file if available, otherwise use the input + if (versionInfo.distribution) { + core.info(`Using distribution '${versionInfo.distribution}' from ${versionFile}`); + distributionName = versionInfo.distribution; + } + else if (!distributionName) { + throw new Error('distribution input is required when not specified in the version file'); + } + const installerInputsOptions = { + architecture, + packageType, + checkLatest, + setDefault, + verifySignature, + verifySignaturePublicKey, + distributionName, + jdkFile, + toolchainIds + }; + yield installVersion(versionInfo.version, installerInputsOptions); } - for (const [index, version] of versions.entries()) { - yield installVersion(version, installerInputsOptions, index); + else { + // When using java-version input, distribution is still required + if (!distributionName) { + throw new Error('distribution input is required'); + } + const installerInputsOptions = { + architecture, + packageType, + checkLatest, + setDefault, + verifySignature, + verifySignaturePublicKey, + distributionName, + jdkFile, + toolchainIds + }; + for (const [index, version] of versions.entries()) { + yield installVersion(version, installerInputsOptions, index); + } } core.endGroup(); const matchersPath = path.join(__dirname, '..', '..', '.github'); @@ -81967,8 +81990,9 @@ function isCacheFeatureAvailable() { } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; function getVersionFromFileContent(content, distributionName, versionFile) { - var _a, _b, _c, _d, _e; + var _a, _b, _c; let javaVersionRegExp; + let extractedDistribution; function getFileName(versionFile) { return path_1.default.basename(versionFile); } @@ -81978,14 +82002,23 @@ function getVersionFromFileContent(content, distributionName, versionFile) { /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { - javaVersionRegExp = /^java\s*=\s*(?[^-]+)/m; + // Match both version and optional distribution identifier + javaVersionRegExp = + /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; } else { javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; } - const capturedVersion = ((_b = (_a = content.match(javaVersionRegExp)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.version) - ? (_d = (_c = content.match(javaVersionRegExp)) === null || _c === void 0 ? void 0 : _c.groups) === null || _d === void 0 ? void 0 : _d.version + const match = content.match(javaVersionRegExp); + const capturedVersion = ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.version) + ? match.groups.version : ''; + // Extract distribution from .sdkmanrc file + if (versionFileName == '.sdkmanrc' && ((_b = match === null || match === void 0 ? void 0 : match.groups) === null || _b === void 0 ? void 0 : _b.distribution)) { + const sdkmanDist = match.groups.distribution; + extractedDistribution = mapSdkmanDistribution(sdkmanDist); + core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); + } core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); if (!capturedVersion) { return null; @@ -81999,13 +82032,42 @@ function getVersionFromFileContent(content, distributionName, versionFile) { if (!version) { return null; } - if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(distributionName)) { - const coerceVersion = (_e = semver.coerce(version)) !== null && _e !== void 0 ? _e : version; + // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution + // (either explicitly provided or extracted from the version file) is in the list. + if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { + const coerceVersion = (_c = semver.coerce(version)) !== null && _c !== void 0 ? _c : version; version = semver.major(coerceVersion).toString(); } - return version.toString(); + return { + version: version.toString(), + distribution: extractedDistribution + }; } exports.getVersionFromFileContent = getVersionFromFileContent; +// Map SDKMAN distribution identifiers to setup-java distribution names +function mapSdkmanDistribution(sdkmanDist) { + const distributionMap = { + tem: 'temurin', + sem: 'semeru', + albba: 'dragonwell', + zulu: 'zulu', + amzn: 'corretto', + graal: 'graalvm', + graalce: 'graalvm', + librca: 'liberica', + ms: 'microsoft', + oracle: 'oracle', + sapmchn: 'sapmachine', + jbr: 'jetbrains', + dragonwell: 'dragonwell', + kona: 'kona' + }; + const mapped = distributionMap[sdkmanDist.toLowerCase()]; + if (!mapped) { + core.warning(`Unknown SDKMAN distribution identifier '${sdkmanDist}'. Please specify the distribution explicitly.`); + } + return mapped; +} // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content) { return content.startsWith('1.') ? content.substring(2) : content; diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 65193c4b1..18fa4e626 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -781,18 +781,34 @@ steps: Supported files are `.java-version`, `.tool-versions` and `.sdkmanrc`. * In `.java-version` file, only the version should be specified (e.g., 17.0.7). The `.java-version` file recognizes all variants of the version description according to [jenv](https://github.com/jenv/jenv). * In `.tool-versions` file, java version should be preceded by the java keyword (e.g., java 17.0.7). The `.tool-versions` file supports version specifications in accordance with [asdf](https://github.com/asdf-vm/asdf) standards, adhering to Semantic Versioning ([semver](https://semver.org/)). - * In `.sdkmanrc` file, java version should be preceded by the `java=` prefix (e.g., java=17.0.7-tem) and include the distribution. The `.sdkmanrc` file supports version specifications in accordance with [file format](https://sdkman.io/usage#env-command), see [Sdkman! documentation](https://sdkman.io/jdks) for more information. + * In `.sdkmanrc` file, java version should be preceded by the `java=` prefix (e.g., `java=17.0.7-tem`). When a recognized SDKMAN distribution suffix is present, setup-java can infer the `distribution` input automatically. Unrecognized suffixes require setting `distribution` explicitly. The `.sdkmanrc` file supports version specifications in accordance with [file format](https://sdkman.io/usage#env-command), see [Sdkman! documentation](https://sdkman.io/jdks) for more information. + + Supported SDKMAN suffix mappings: + + | SDKMAN suffix | setup-java distribution | + | ------------- | ----------------------- | + | `tem` | `temurin` | + | `sem` | `semeru` | + | `albba`, `dragonwell` | `dragonwell` | + | `zulu` | `zulu` | + | `amzn` | `corretto` | + | `graal`, `graalce` | `graalvm` | + | `librca` | `liberica` | + | `ms` | `microsoft` | + | `oracle` | `oracle` | + | `sapmchn` | `sapmachine` | + | `jbr` | `jetbrains` | + | `kona` | `kona` | If both `java-version` and `java-version-file` **inputs** are provided, the `java-version` input will be used. -**Example step using `Sdkman!`**: +**Example step using `Sdkman!`** (distribution inferred from `.sdkmanrc`): ```yml - name: Setup java uses: actions/setup-java@v5 with: java-version-file: '.sdkmanrc' - distribution: 'temurin' ``` **Example `.sdkmanrc`**: diff --git a/src/setup-java.ts b/src/setup-java.ts index d7e479880..6ecb49011 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -17,9 +17,7 @@ import {configureMavenArgs} from './maven-args'; async function run() { try { const versions = core.getMultilineInput(constants.INPUT_JAVA_VERSION); - const distributionName = core.getInput(constants.INPUT_DISTRIBUTION, { - required: true - }); + let distributionName = core.getInput(constants.INPUT_DISTRIBUTION); const versionFile = core.getInput(constants.INPUT_JAVA_VERSION_FILE); const architecture = core.getInput(constants.INPUT_ARCHITECTURE); const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE); @@ -48,42 +46,71 @@ async function run() { throw new Error('java-version or java-version-file input expected'); } - const installerInputsOptions: installerInputsOptions = { - architecture, - packageType, - checkLatest, - setDefault, - verifySignature, - verifySignaturePublicKey, - distributionName, - jdkFile, - toolchainIds - }; - if (!versions.length) { core.debug( 'java-version input is empty, looking for java-version-file input' ); const content = fs.readFileSync(versionFile).toString().trim(); - const version = getVersionFromFileContent( + const versionInfo = getVersionFromFileContent( content, distributionName, versionFile ); - core.debug(`Parsed version from file '${version}'`); + core.debug(`Parsed version from file '${versionInfo?.version}'`); - if (!version) { + if (!versionInfo) { throw new Error( `No supported version was found in file ${versionFile}` ); } - await installVersion(version, installerInputsOptions); - } + // Use distribution from file if available, otherwise use the input + if (versionInfo.distribution) { + core.info( + `Using distribution '${versionInfo.distribution}' from ${versionFile}` + ); + distributionName = versionInfo.distribution; + } else if (!distributionName) { + throw new Error( + 'distribution input is required when not specified in the version file' + ); + } - for (const [index, version] of versions.entries()) { - await installVersion(version, installerInputsOptions, index); + const installerInputsOptions: installerInputsOptions = { + architecture, + packageType, + checkLatest, + setDefault, + verifySignature, + verifySignaturePublicKey, + distributionName, + jdkFile, + toolchainIds + }; + + await installVersion(versionInfo.version, installerInputsOptions); + } else { + // When using java-version input, distribution is still required + if (!distributionName) { + throw new Error('distribution input is required'); + } + + const installerInputsOptions: installerInputsOptions = { + architecture, + packageType, + checkLatest, + setDefault, + verifySignature, + verifySignaturePublicKey, + distributionName, + jdkFile, + toolchainIds + }; + + for (const [index, version] of versions.entries()) { + await installVersion(version, installerInputsOptions, index); + } } core.endGroup(); const matchersPath = path.join(__dirname, '..', '..', '.github'); diff --git a/src/util.ts b/src/util.ts index 679c9fe34..32cb6bc16 100644 --- a/src/util.ts +++ b/src/util.ts @@ -127,12 +127,18 @@ export function isCacheFeatureAvailable(): boolean { return false; } +export interface VersionInfo { + version: string; + distribution?: string; +} + export function getVersionFromFileContent( content: string, distributionName: string, versionFile: string -): string | null { +): VersionInfo | null { let javaVersionRegExp: RegExp; + let extractedDistribution: string | undefined; function getFileName(versionFile: string) { return path.basename(versionFile); @@ -143,15 +149,27 @@ export function getVersionFromFileContent( javaVersionRegExp = /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { - javaVersionRegExp = /^java\s*=\s*(?[^-]+)/m; + // Match both version and optional distribution identifier + javaVersionRegExp = + /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; } else { javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; } - const capturedVersion = content.match(javaVersionRegExp)?.groups?.version - ? (content.match(javaVersionRegExp)?.groups?.version as string) + const match = content.match(javaVersionRegExp); + const capturedVersion = match?.groups?.version + ? (match.groups.version as string) : ''; + // Extract distribution from .sdkmanrc file + if (versionFileName == '.sdkmanrc' && match?.groups?.distribution) { + const sdkmanDist = match.groups.distribution; + extractedDistribution = mapSdkmanDistribution(sdkmanDist); + core.debug( + `Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'` + ); + } + core.debug( `Parsed version '${capturedVersion}' from file '${versionFileName}'` ); @@ -172,12 +190,49 @@ export function getVersionFromFileContent( return null; } - if (DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(distributionName)) { + // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution + // (either explicitly provided or extracted from the version file) is in the list. + if ( + DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes( + extractedDistribution || distributionName + ) + ) { const coerceVersion = semver.coerce(version) ?? version; version = semver.major(coerceVersion).toString(); } - return version.toString(); + return { + version: version.toString(), + distribution: extractedDistribution + }; +} + +// Map SDKMAN distribution identifiers to setup-java distribution names +function mapSdkmanDistribution(sdkmanDist: string): string | undefined { + const distributionMap: Record = { + tem: 'temurin', + sem: 'semeru', + albba: 'dragonwell', + zulu: 'zulu', + amzn: 'corretto', + graal: 'graalvm', + graalce: 'graalvm', + librca: 'liberica', + ms: 'microsoft', + oracle: 'oracle', + sapmchn: 'sapmachine', + jbr: 'jetbrains', + dragonwell: 'dragonwell', + kona: 'kona' + }; + + const mapped = distributionMap[sdkmanDist.toLowerCase()]; + if (!mapped) { + core.warning( + `Unknown SDKMAN distribution identifier '${sdkmanDist}'. Please specify the distribution explicitly.` + ); + } + return mapped; } // By convention, action expects version 8 in the format `8.*` instead of `1.8` From 3be16b57e9c9a41ee225eb6b88a69f6f4d87460b Mon Sep 17 00:00:00 2001 From: James Wald Date: Wed, 8 Jul 2026 05:00:43 -0400 Subject: [PATCH 46/57] dist: Migrate from Zulu Discovery API to Azul Metadata API (#1010) * Use Azul metadata API * Document arm64 -> aarch64 mapping in README.md * Paginate through all available versions * Fix typo: win_aarhc4 * Only query for linux_glibc packages * Add Zulu CRaC package support to metadata migration Fold CRaC-related work into the Zulu metadata API migration by wiring crac_supported query handling, extending Zulu package docs, and updating installer tests for jdk+crac/jre+crac behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Harden Zulu metadata pagination with safety cap - Stop paginating on a short page to avoid an extra empty request - Guard against undefined results (not just null) - Cap iterations at 100 pages and warn if the limit is hit to prevent a runaway loop if the API misbehaves Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Preserve JDK build number from Azul Metadata API The Azul Metadata API returns java_version as a 3-element array (e.g. [17,0,7]) and reports the build number separately in openjdk_build_number. The migration mapped version directly from java_version, dropping the build and breaking exact-version lookups like 17.0.7+7 (e2e failure: "No matching version found for SemVer"). Add openjdk_build_number to IZuluVersions and append it to java_version before converting to semver so resolved versions retain the build (e.g. 17.0.7+7). Update the zulu test fixtures to mirror the real API shape (3-element java_version plus openjdk_build_number) so unit tests exercise the actual response format, and rebuild dist. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Bruno Borges Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit d3530c1eb4acf0514f6f9598366c2139fe198de9) --- README.md | 4 +- __tests__/data/zulu-linux.json | 864 ++++++++++++----- __tests__/data/zulu-releases-default.json | 836 ++++++++++++----- __tests__/data/zulu-windows.json | 866 +++++++++++++----- __tests__/distributors/zulu-installer.test.ts | 64 +- .../distributors/zulu-linux-installer.test.ts | 68 +- .../zulu-windows-installer.test.ts | 64 +- action.yml | 2 +- dist/setup/index.js | 81 +- docs/advanced-usage.md | 26 +- src/distributions/zulu/installer.ts | 98 +- src/distributions/zulu/models.ts | 13 +- 12 files changed, 2199 insertions(+), 787 deletions(-) diff --git a/README.md b/README.md index fead4a69f..97194d197 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ For more details, see the full release notes on the [releases page](https://git - `distribution`: Java [distribution](#supported-distributions). Required unless `java-version-file` points to `.sdkmanrc` with a recognized distribution suffix (for example `java=21.0.5-tem`). - - `java-package`: The packaging variant of the chosen distribution. Possible values: `jdk`, `jre`, `jdk+fx`, `jre+fx`. Default value: `jdk`. + - `java-package`: The packaging variant of the chosen distribution. Possible values: `jdk`, `jre`, `jdk+fx`, `jre+fx`. For Azul Zulu, `jdk+crac` and `jre+crac` are also supported. Default value: `jdk`. - `architecture`: The target architecture of the package. Possible values: `x86`, `x64`, `armv7`, `aarch64`, `ppc64le`. Default value: Derived from the runner machine. @@ -126,7 +126,7 @@ Currently, the following distributions are supported: > [!NOTE] > - The different distributors can provide discrepant list of available versions / supported configurations. Please refer to the official documentation to see the list of supported versions. > - AdoptOpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from `adopt` and `adopt-openj9`, to `temurin` and `semeru` respectively, to keep receiving software and security updates. See more details in the [Good-bye AdoptOpenJDK post](https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/). -> - For Azul Zulu OpenJDK architectures x64 and arm64 are mapped to x86 / arm with proper hw_bitness. +> - For Azul Zulu OpenJDK, architecture `arm64` is mapped to `aarch64` when querying the Azul Metadata API. > - To comply with the GraalVM Free Terms and Conditions (GFTC) license, it is recommended to use GraalVM JDK 17 version 17.0.12, as this is the only version of GraalVM JDK 17 available under the GFTC license. Additionally, it is encouraged to consider upgrading to GraalVM JDK 21, which offers the latest features and improvements. > - GraalVM Community is available as `distribution: 'graalvm-community'` for stable JDK 17 and later releases published on GitHub. diff --git a/__tests__/data/zulu-linux.json b/__tests__/data/zulu-linux.json index 1f2fa2716..c74487e2c 100644 --- a/__tests__/data/zulu-linux.json +++ b/__tests__/data/zulu-linux.json @@ -1,254 +1,686 @@ -[ +[ { - "id": 10996, - "url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_05-8.1.0.10-linux.tar.gz", + "package_uuid": "test-uuid-10996", "name": "zulu1.8.0_05-8.1.0.10-linux.tar.gz", - "zulu_version": [8, 1, 0, 10], - "jdk_version": [8, 0, 5, 13] - }, - { - "id": 10997, - "url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_11-8.2.0.1-linux.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_05-8.1.0.10-linux.tar.gz", + "java_version": [ + 8, + 0, + 5 + ], + "openjdk_build_number": 13, + "distro_version": [ + 8, + 1, + 0, + 10 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10997", "name": "zulu1.8.0_11-8.2.0.1-linux.tar.gz", - "zulu_version": [8, 2, 0, 1], - "jdk_version": [8, 0, 11, 12] - }, - { - "id": 10346, - "url": "https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_11-8.2.0.1-linux.tar.gz", + "java_version": [ + 8, + 0, + 11 + ], + "openjdk_build_number": 12, + "distro_version": [ + 8, + 2, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10346", "name": "zulu8.21.0.1-jdk8.0.131-linux_x64.tar.gz", - "zulu_version": [8, 21, 0, 1], - "jdk_version": [8, 0, 131, 11] - }, - { - "id": 10362, - "url": "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 131 + ], + "openjdk_build_number": 11, + "distro_version": [ + 8, + 21, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10362", "name": "zulu8.23.0.3-jdk8.0.144-linux_x64.tar.gz", - "zulu_version": [8, 23, 0, 3], - "jdk_version": [8, 0, 144, 1] - }, - { - "id": 10399, - "url": "https://cdn.azul.com/zulu/bin/zulu8.25.0.1-jdk8.0.152-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 144 + ], + "openjdk_build_number": 1, + "distro_version": [ + 8, + 23, + 0, + 3 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10399", "name": "zulu8.25.0.1-jdk8.0.152-linux_x64.tar.gz", - "zulu_version": [8, 25, 0, 1], - "jdk_version": [8, 0, 152, 16] - }, - { - "id": 11355, - "url": "https://cdn.azul.com/zulu/bin/zulu8.46.0.19-ca-jdk8.0.252-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.25.0.1-jdk8.0.152-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 152 + ], + "openjdk_build_number": 16, + "distro_version": [ + 8, + 25, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11355", "name": "zulu8.46.0.19-ca-jdk8.0.252-linux_x64.tar.gz", - "zulu_version": [8, 46, 0, 19], - "jdk_version": [8, 0, 252, 14] - }, - { - "id": 11481, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.47-ca-jdk8.0.262-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.46.0.19-ca-jdk8.0.252-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 252 + ], + "openjdk_build_number": 14, + "distro_version": [ + 8, + 46, + 0, + 19 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11481", "name": "zulu8.48.0.47-ca-jdk8.0.262-linux_x64.tar.gz", - "zulu_version": [8, 48, 0, 47], - "jdk_version": [8, 0, 262, 17] - }, - { - "id": 11622, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.51-ca-jdk8.0.262-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.47-ca-jdk8.0.262-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 17, + "distro_version": [ + 8, + 48, + 0, + 47 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11622", "name": "zulu8.48.0.51-ca-jdk8.0.262-linux_x64.tar.gz", - "zulu_version": [8, 48, 0, 51], - "jdk_version": [8, 0, 262, 19] - }, - { - "id": 11535, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.49-ca-jdk8.0.262-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.51-ca-jdk8.0.262-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 19, + "distro_version": [ + 8, + 48, + 0, + 51 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11535", "name": "zulu8.48.0.49-ca-jdk8.0.262-linux_x64.tar.gz", - "zulu_version": [8, 48, 0, 49], - "jdk_version": [8, 0, 262, 18] - }, - { - "id": 12424, - "url": "https://cdn.azul.com/zulu/bin/zulu8.52.0.23-ca-jdk8.0.282-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.49-ca-jdk8.0.262-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 18, + "distro_version": [ + 8, + 48, + 0, + 49 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12424", "name": "zulu8.52.0.23-ca-jdk8.0.282-linux_x64.tar.gz", - "zulu_version": [8, 52, 0, 23], - "jdk_version": [8, 0, 282, 8] - }, - { - "id": 10383, - "url": "https://cdn.azul.com/zulu/bin/zulu9.0.0.15-jdk9.0.0-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.52.0.23-ca-jdk8.0.282-linux_x64.tar.gz", + "java_version": [ + 8, + 0, + 282 + ], + "openjdk_build_number": 8, + "distro_version": [ + 8, + 52, + 0, + 23 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10383", "name": "zulu9.0.0.15-jdk9.0.0-linux_x64.tar.gz", - "zulu_version": [9, 0, 0, 15], - "jdk_version": [9, 0, 0, 0] - }, - { - "id": 10413, - "url": "https://cdn.azul.com/zulu/bin/zulu9.0.1.3-jdk9.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu9.0.0.15-jdk9.0.0-linux_x64.tar.gz", + "java_version": [ + 9, + 0, + 0 + ], + "openjdk_build_number": 0, + "distro_version": [ + 9, + 0, + 0, + 15 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10413", "name": "zulu9.0.1.3-jdk9.0.1-linux_x64.tar.gz", - "zulu_version": [9, 0, 1, 3], - "jdk_version": [9, 0, 1, 0] - }, - { - "id": 10503, - "url": "https://cdn.azul.com/zulu/bin/zulu10.2+3-jdk10.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu9.0.1.3-jdk9.0.1-linux_x64.tar.gz", + "java_version": [ + 9, + 0, + 1 + ], + "openjdk_build_number": 0, + "distro_version": [ + 9, + 0, + 1, + 3 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10503", "name": "zulu10.2+3-jdk10.0.1-linux_x64.tar.gz", - "zulu_version": [10, 2, 3, 0], - "jdk_version": [10, 0, 1, 9] - }, - { - "id": 10541, - "url": "https://cdn.azul.com/zulu/bin/zulu10.3+5-jdk10.0.2-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu10.2+3-jdk10.0.1-linux_x64.tar.gz", + "java_version": [ + 10, + 0, + 1 + ], + "openjdk_build_number": 9, + "distro_version": [ + 10, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10541", "name": "zulu10.3+5-jdk10.0.2-linux_x64.tar.gz", - "zulu_version": [10, 3, 5, 0], - "jdk_version": [10, 0, 2, 13] - }, - { - "id": 10576, - "url": "https://cdn.azul.com/zulu/bin/zulu11.2.3-jdk11.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu10.3+5-jdk10.0.2-linux_x64.tar.gz", + "java_version": [ + 10, + 0, + 2 + ], + "openjdk_build_number": 13, + "distro_version": [ + 10, + 3, + 5, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10576", "name": "zulu11.2.3-jdk11.0.1-linux_x64.tar.gz", - "zulu_version": [11, 2, 3, 0], - "jdk_version": [11, 0, 1, 13] - }, - { - "id": 10604, - "url": "https://cdn.azul.com/zulu/bin/zulu11.29.3-ca-jdk11.0.2-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.2.3-jdk11.0.1-linux_x64.tar.gz", + "java_version": [ + 11, + 0, + 1 + ], + "openjdk_build_number": 13, + "distro_version": [ + 11, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10604", "name": "zulu11.29.3-ca-jdk11.0.2-linux_x64.tar.gz", - "zulu_version": [11, 29, 3, 0], - "jdk_version": [11, 0, 2, 7] - }, - { - "id": 10687, - "url": "https://cdn.azul.com/zulu/bin/zulu11.31.11-ca-jdk11.0.3-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.29.3-ca-jdk11.0.2-linux_x64.tar.gz", + "java_version": [ + 11, + 0, + 2 + ], + "openjdk_build_number": 7, + "distro_version": [ + 11, + 29, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10687", "name": "zulu11.31.11-ca-jdk11.0.3-linux_x64.tar.gz", - "zulu_version": [11, 31, 11, 0], - "jdk_version": [11, 0, 3, 7] - }, - { - "id": 10856, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.13-ca-jdk11.0.5-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.31.11-ca-jdk11.0.3-linux_x64.tar.gz", + "java_version": [ + 11, + 0, + 3 + ], + "openjdk_build_number": 7, + "distro_version": [ + 11, + 31, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10856", "name": "zulu11.35.13-ca-jdk11.0.5-linux_x64.tar.gz", - "zulu_version": [11, 35, 13, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 10933, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.15-ca-jdk11.0.5-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.13-ca-jdk11.0.5-linux_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 13, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10933", "name": "zulu11.35.15-ca-jdk11.0.5-linux_x64.tar.gz", - "zulu_version": [11, 35, 15, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 10933, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.11-ca-jdk11.0.5-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.15-ca-jdk11.0.5-linux_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 15, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10933", "name": "zulu11.35.15-ca-jdk11.0.5-linux_x64.tar.gz", - "zulu_version": [11, 35, 11, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 12397, - "url": "https://cdn.azul.com/zulu/bin/zulu11.45.27-ca-jdk11.0.10-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.11-ca-jdk11.0.5-linux_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12397", "name": "zulu11.45.27-ca-jdk11.0.10-linux_x64.tar.gz", - "zulu_version": [11, 45, 27, 0], - "jdk_version": [11, 0, 10, 9] - }, - { - "id": 10667, - "url": "https://cdn.azul.com/zulu/bin/zulu12.1.3-ca-jdk12.0.0-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.45.27-ca-jdk11.0.10-linux_x64.tar.gz", + "java_version": [ + 11, + 0, + 10 + ], + "openjdk_build_number": 9, + "distro_version": [ + 11, + 45, + 27, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10667", "name": "zulu12.1.3-ca-jdk12.0.0-linux_x64.tar.gz", - "zulu_version": [12, 1, 3, 0], - "jdk_version": [12, 0, 0, 33] - }, - { - "id": 10710, - "url": "https://cdn.azul.com/zulu/bin/zulu12.2.3-ca-jdk12.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.1.3-ca-jdk12.0.0-linux_x64.tar.gz", + "java_version": [ + 12, + 0, + 0 + ], + "openjdk_build_number": 33, + "distro_version": [ + 12, + 1, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10710", "name": "zulu12.2.3-ca-jdk12.0.1-linux_x64.tar.gz", - "zulu_version": [12, 2, 3, 0], - "jdk_version": [12, 0, 1, 12] - }, - { - "id": 10780, - "url": "https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.2.3-ca-jdk12.0.1-linux_x64.tar.gz", + "java_version": [ + 12, + 0, + 1 + ], + "openjdk_build_number": 12, + "distro_version": [ + 12, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10780", "name": "zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz", - "zulu_version": [12, 3, 11, 0], - "jdk_version": [12, 0, 2, 3] - }, - { - "id": 10846, - "url": "https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13.0.0-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-linux_x64.tar.gz", + "java_version": [ + 12, + 0, + 2 + ], + "openjdk_build_number": 3, + "distro_version": [ + 12, + 3, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10846", "name": "zulu13.27.9-ca-jdk13.0.0-linux_x64.tar.gz", - "zulu_version": [13, 27, 9, 0], - "jdk_version": [13, 0, 0, 33] - }, - { - "id": 10888, - "url": "https://cdn.azul.com/zulu/bin/zulu13.28.11-ca-jdk13.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13.0.0-linux_x64.tar.gz", + "java_version": [ + 13, + 0, + 0 + ], + "openjdk_build_number": 33, + "distro_version": [ + 13, + 27, + 9, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10888", "name": "zulu13.28.11-ca-jdk13.0.1-linux_x64.tar.gz", - "zulu_version": [13, 28, 11, 0], - "jdk_version": [13, 0, 1, 10] - }, - { - "id": 11073, - "url": "https://cdn.azul.com/zulu/bin/zulu13.29.9-ca-jdk13.0.2-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.28.11-ca-jdk13.0.1-linux_x64.tar.gz", + "java_version": [ + 13, + 0, + 1 + ], + "openjdk_build_number": 10, + "distro_version": [ + 13, + 28, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11073", "name": "zulu13.29.9-ca-jdk13.0.2-linux_x64.tar.gz", - "zulu_version": [13, 29, 9, 0], - "jdk_version": [13, 0, 2, 6] - }, - { - "id": 12408, - "url": "https://cdn.azul.com/zulu/bin/zulu13.37.21-ca-jdk13.0.6-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.29.9-ca-jdk13.0.2-linux_x64.tar.gz", + "java_version": [ + 13, + 0, + 2 + ], + "openjdk_build_number": 6, + "distro_version": [ + 13, + 29, + 9, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12408", "name": "zulu13.37.21-ca-jdk13.0.6-linux_x64.tar.gz", - "zulu_version": [13, 37, 21, 0], - "jdk_version": [13, 0, 6, 5] - }, - { - "id": 11236, - "url": "https://cdn.azul.com/zulu/bin/zulu14.27.1-ca-jdk14.0.0-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.37.21-ca-jdk13.0.6-linux_x64.tar.gz", + "java_version": [ + 13, + 0, + 6 + ], + "openjdk_build_number": 5, + "distro_version": [ + 13, + 37, + 21, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11236", "name": "zulu14.27.1-ca-jdk14.0.0-linux_x64.tar.gz", - "zulu_version": [14, 27, 1, 0], - "jdk_version": [14, 0, 0, 36] - }, - { - "id": 11349, - "url": "https://cdn.azul.com/zulu/bin/zulu14.28.21-ca-jdk14.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.27.1-ca-jdk14.0.0-linux_x64.tar.gz", + "java_version": [ + 14, + 0, + 0 + ], + "openjdk_build_number": 36, + "distro_version": [ + 14, + 27, + 1, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11349", "name": "zulu14.28.21-ca-jdk14.0.1-linux_x64.tar.gz", - "zulu_version": [14, 28, 21, 0], - "jdk_version": [14, 0, 1, 8] - }, - { - "id": 11513, - "url": "https://cdn.azul.com/zulu/bin/zulu14.29.23-ca-jdk14.0.2-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.28.21-ca-jdk14.0.1-linux_x64.tar.gz", + "java_version": [ + 14, + 0, + 1 + ], + "openjdk_build_number": 8, + "distro_version": [ + 14, + 28, + 21, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11513", "name": "zulu14.29.23-ca-jdk14.0.2-linux_x64.tar.gz", - "zulu_version": [14, 29, 23, 0], - "jdk_version": [14, 0, 2, 12] - }, - { - "id": 11780, - "url": "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.29.23-ca-jdk14.0.2-linux_x64.tar.gz", + "java_version": [ + 14, + 0, + 2 + ], + "openjdk_build_number": 12, + "distro_version": [ + 14, + 29, + 23, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11780", "name": "zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz", - "zulu_version": [15, 27, 17, 0], - "jdk_version": [15, 0, 0, 36] - }, - { - "id": 11924, - "url": "https://cdn.azul.com/zulu/bin/zulu15.28.13-ca-jdk15.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-linux_x64.tar.gz", + "java_version": [ + 15, + 0, + 0 + ], + "openjdk_build_number": 36, + "distro_version": [ + 15, + 27, + 17, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11924", "name": "zulu15.28.13-ca-jdk15.0.1-linux_x64.tar.gz", - "zulu_version": [15, 28, 13, 0], - "jdk_version": [15, 0, 1, 8] - }, - { - "id": 12101, - "url": "https://cdn.azul.com/zulu/bin/zulu15.28.51-ca-jdk15.0.1-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.28.13-ca-jdk15.0.1-linux_x64.tar.gz", + "java_version": [ + 15, + 0, + 1 + ], + "openjdk_build_number": 8, + "distro_version": [ + 15, + 28, + 13, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12101", "name": "zulu15.28.51-ca-jdk15.0.1-linux_x64.tar.gz", - "zulu_version": [15, 28, 51, 0], - "jdk_version": [15, 0, 1, 9] - }, - { - "id": 12445, - "url": "https://cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-linux_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.28.51-ca-jdk15.0.1-linux_x64.tar.gz", + "java_version": [ + 15, + 0, + 1 + ], + "openjdk_build_number": 9, + "distro_version": [ + 15, + 28, + 51, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12445", "name": "zulu15.29.15-ca-jdk15.0.2-linux_x64.tar.gz", - "zulu_version": [15, 29, 15, 0], - "jdk_version": [15, 0, 2, 7] - }, - { - "id": 12447, - "url": "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-linux_x64.tar.gz", + "java_version": [ + 15, + 0, + 2 + ], + "openjdk_build_number": 7, + "distro_version": [ + 15, + 29, + 15, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12447", "name": "zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz", - "zulu_version": [21, 32, 17, 0], - "jdk_version": [21, 0, 2, 6] + "download_url": "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz", + "java_version": [ + 21, + 0, + 2 + ], + "openjdk_build_number": 6, + "distro_version": [ + 21, + 32, + 17, + 0 + ], + "latest": false, + "availability_type": "ca" } -] \ No newline at end of file +] diff --git a/__tests__/data/zulu-releases-default.json b/__tests__/data/zulu-releases-default.json index f23a87d43..16ed97772 100644 --- a/__tests__/data/zulu-releases-default.json +++ b/__tests__/data/zulu-releases-default.json @@ -1,247 +1,667 @@ [ { - "id": 10996, - "url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_05-8.1.0.10-macosx.tar.gz", + "package_uuid": "test-uuid-10996", "name": "zulu1.8.0_05-8.1.0.10-macosx.tar.gz", - "zulu_version": [8, 1, 0, 10], - "jdk_version": [8, 0, 5, 13] - }, - { - "id": 10997, - "url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_11-8.2.0.1-macosx.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_05-8.1.0.10-macosx.tar.gz", + "java_version": [ + 8, + 0, + 5 + ], + "openjdk_build_number": 13, + "distro_version": [ + 8, + 1, + 0, + 10 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10997", "name": "zulu1.8.0_11-8.2.0.1-macosx.tar.gz", - "zulu_version": [8, 2, 0, 1], - "jdk_version": [8, 0, 11, 12] - }, - { - "id": 10346, - "url": "https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_11-8.2.0.1-macosx.tar.gz", + "java_version": [ + 8, + 0, + 11 + ], + "openjdk_build_number": 12, + "distro_version": [ + 8, + 2, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10346", "name": "zulu8.21.0.1-jdk8.0.131-macosx_x64.tar.gz", - "zulu_version": [8, 21, 0, 1], - "jdk_version": [8, 0, 131, 11] - }, - { - "id": 10362, - "url": "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 131 + ], + "openjdk_build_number": 11, + "distro_version": [ + 8, + 21, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10362", "name": "zulu8.23.0.3-jdk8.0.144-macosx_x64.tar.gz", - "zulu_version": [8, 23, 0, 3], - "jdk_version": [8, 0, 144, 1] - }, - { - "id": 10399, - "url": "https://cdn.azul.com/zulu/bin/zulu8.25.0.1-jdk8.0.152-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 144 + ], + "openjdk_build_number": 1, + "distro_version": [ + 8, + 23, + 0, + 3 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10399", "name": "zulu8.25.0.1-jdk8.0.152-macosx_x64.tar.gz", - "zulu_version": [8, 25, 0, 1], - "jdk_version": [8, 0, 152, 16] - }, - { - "id": 11355, - "url": "https://cdn.azul.com/zulu/bin/zulu8.46.0.19-ca-jdk8.0.252-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.25.0.1-jdk8.0.152-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 152 + ], + "openjdk_build_number": 16, + "distro_version": [ + 8, + 25, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11355", "name": "zulu8.46.0.19-ca-jdk8.0.252-macosx_x64.tar.gz", - "zulu_version": [8, 46, 0, 19], - "jdk_version": [8, 0, 252, 14] - }, - { - "id": 11481, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.47-ca-jdk8.0.262-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.46.0.19-ca-jdk8.0.252-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 252 + ], + "openjdk_build_number": 14, + "distro_version": [ + 8, + 46, + 0, + 19 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11481", "name": "zulu8.48.0.47-ca-jdk8.0.262-macosx_x64.tar.gz", - "zulu_version": [8, 48, 0, 47], - "jdk_version": [8, 0, 262, 17] - }, - { - "id": 11622, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.51-ca-jdk8.0.262-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.47-ca-jdk8.0.262-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 17, + "distro_version": [ + 8, + 48, + 0, + 47 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11622", "name": "zulu8.48.0.51-ca-jdk8.0.262-macosx_x64.tar.gz", - "zulu_version": [8, 48, 0, 51], - "jdk_version": [8, 0, 262, 19] - }, - { - "id": 11535, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.49-ca-jdk8.0.262-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.51-ca-jdk8.0.262-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 19, + "distro_version": [ + 8, + 48, + 0, + 51 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11535", "name": "zulu8.48.0.49-ca-jdk8.0.262-macosx_x64.tar.gz", - "zulu_version": [8, 48, 0, 49], - "jdk_version": [8, 0, 262, 18] - }, - { - "id": 12424, - "url": "https://cdn.azul.com/zulu/bin/zulu8.52.0.23-ca-jdk8.0.282-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.49-ca-jdk8.0.262-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 18, + "distro_version": [ + 8, + 48, + 0, + 49 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12424", "name": "zulu8.52.0.23-ca-jdk8.0.282-macosx_x64.tar.gz", - "zulu_version": [8, 52, 0, 23], - "jdk_version": [8, 0, 282, 8] - }, - { - "id": 10383, - "url": "https://cdn.azul.com/zulu/bin/zulu9.0.0.15-jdk9.0.0-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.52.0.23-ca-jdk8.0.282-macosx_x64.tar.gz", + "java_version": [ + 8, + 0, + 282 + ], + "openjdk_build_number": 8, + "distro_version": [ + 8, + 52, + 0, + 23 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10383", "name": "zulu9.0.0.15-jdk9.0.0-macosx_x64.tar.gz", - "zulu_version": [9, 0, 0, 15], - "jdk_version": [9, 0, 0, 0] - }, - { - "id": 10413, - "url": "https://cdn.azul.com/zulu/bin/zulu9.0.1.3-jdk9.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu9.0.0.15-jdk9.0.0-macosx_x64.tar.gz", + "java_version": [ + 9, + 0, + 0 + ], + "openjdk_build_number": 0, + "distro_version": [ + 9, + 0, + 0, + 15 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10413", "name": "zulu9.0.1.3-jdk9.0.1-macosx_x64.tar.gz", - "zulu_version": [9, 0, 1, 3], - "jdk_version": [9, 0, 1, 0] - }, - { - "id": 10503, - "url": "https://cdn.azul.com/zulu/bin/zulu10.2+3-jdk10.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu9.0.1.3-jdk9.0.1-macosx_x64.tar.gz", + "java_version": [ + 9, + 0, + 1 + ], + "openjdk_build_number": 0, + "distro_version": [ + 9, + 0, + 1, + 3 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10503", "name": "zulu10.2+3-jdk10.0.1-macosx_x64.tar.gz", - "zulu_version": [10, 2, 3, 0], - "jdk_version": [10, 0, 1, 9] - }, - { - "id": 10541, - "url": "https://cdn.azul.com/zulu/bin/zulu10.3+5-jdk10.0.2-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu10.2+3-jdk10.0.1-macosx_x64.tar.gz", + "java_version": [ + 10, + 0, + 1 + ], + "openjdk_build_number": 9, + "distro_version": [ + 10, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10541", "name": "zulu10.3+5-jdk10.0.2-macosx_x64.tar.gz", - "zulu_version": [10, 3, 5, 0], - "jdk_version": [10, 0, 2, 13] - }, - { - "id": 10576, - "url": "https://cdn.azul.com/zulu/bin/zulu11.2.3-jdk11.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu10.3+5-jdk10.0.2-macosx_x64.tar.gz", + "java_version": [ + 10, + 0, + 2 + ], + "openjdk_build_number": 13, + "distro_version": [ + 10, + 3, + 5, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10576", "name": "zulu11.2.3-jdk11.0.1-macosx_x64.tar.gz", - "zulu_version": [11, 2, 3, 0], - "jdk_version": [11, 0, 1, 13] - }, - { - "id": 10604, - "url": "https://cdn.azul.com/zulu/bin/zulu11.29.3-ca-jdk11.0.2-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.2.3-jdk11.0.1-macosx_x64.tar.gz", + "java_version": [ + 11, + 0, + 1 + ], + "openjdk_build_number": 13, + "distro_version": [ + 11, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10604", "name": "zulu11.29.3-ca-jdk11.0.2-macosx_x64.tar.gz", - "zulu_version": [11, 29, 3, 0], - "jdk_version": [11, 0, 2, 7] - }, - { - "id": 10687, - "url": "https://cdn.azul.com/zulu/bin/zulu11.31.11-ca-jdk11.0.3-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.29.3-ca-jdk11.0.2-macosx_x64.tar.gz", + "java_version": [ + 11, + 0, + 2 + ], + "openjdk_build_number": 7, + "distro_version": [ + 11, + 29, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10687", "name": "zulu11.31.11-ca-jdk11.0.3-macosx_x64.tar.gz", - "zulu_version": [11, 31, 11, 0], - "jdk_version": [11, 0, 3, 7] - }, - { - "id": 10856, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.13-ca-jdk11.0.5-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.31.11-ca-jdk11.0.3-macosx_x64.tar.gz", + "java_version": [ + 11, + 0, + 3 + ], + "openjdk_build_number": 7, + "distro_version": [ + 11, + 31, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10856", "name": "zulu11.35.13-ca-jdk11.0.5-macosx_x64.tar.gz", - "zulu_version": [11, 35, 13, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 10933, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.15-ca-jdk11.0.5-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.13-ca-jdk11.0.5-macosx_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 13, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10933", "name": "zulu11.35.15-ca-jdk11.0.5-macosx_x64.tar.gz", - "zulu_version": [11, 35, 15, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 10933, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.11-ca-jdk11.0.5-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.15-ca-jdk11.0.5-macosx_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 15, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10933", "name": "zulu11.35.15-ca-jdk11.0.5-macosx_x64.tar.gz", - "zulu_version": [11, 35, 11, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 12397, - "url": "https://cdn.azul.com/zulu/bin/zulu11.45.27-ca-jdk11.0.10-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.11-ca-jdk11.0.5-macosx_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12397", "name": "zulu11.45.27-ca-jdk11.0.10-macosx_x64.tar.gz", - "zulu_version": [11, 45, 27, 0], - "jdk_version": [11, 0, 10, 9] - }, - { - "id": 10667, - "url": "https://cdn.azul.com/zulu/bin/zulu12.1.3-ca-jdk12.0.0-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.45.27-ca-jdk11.0.10-macosx_x64.tar.gz", + "java_version": [ + 11, + 0, + 10 + ], + "openjdk_build_number": 9, + "distro_version": [ + 11, + 45, + 27, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10667", "name": "zulu12.1.3-ca-jdk12.0.0-macosx_x64.tar.gz", - "zulu_version": [12, 1, 3, 0], - "jdk_version": [12, 0, 0, 33] - }, - { - "id": 10710, - "url": "https://cdn.azul.com/zulu/bin/zulu12.2.3-ca-jdk12.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.1.3-ca-jdk12.0.0-macosx_x64.tar.gz", + "java_version": [ + 12, + 0, + 0 + ], + "openjdk_build_number": 33, + "distro_version": [ + 12, + 1, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10710", "name": "zulu12.2.3-ca-jdk12.0.1-macosx_x64.tar.gz", - "zulu_version": [12, 2, 3, 0], - "jdk_version": [12, 0, 1, 12] - }, - { - "id": 10780, - "url": "https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.2.3-ca-jdk12.0.1-macosx_x64.tar.gz", + "java_version": [ + 12, + 0, + 1 + ], + "openjdk_build_number": 12, + "distro_version": [ + 12, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10780", "name": "zulu12.3.11-ca-jdk12.0.2-macosx_x64.tar.gz", - "zulu_version": [12, 3, 11, 0], - "jdk_version": [12, 0, 2, 3] - }, - { - "id": 10846, - "url": "https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13.0.0-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-macosx_x64.tar.gz", + "java_version": [ + 12, + 0, + 2 + ], + "openjdk_build_number": 3, + "distro_version": [ + 12, + 3, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10846", "name": "zulu13.27.9-ca-jdk13.0.0-macosx_x64.tar.gz", - "zulu_version": [13, 27, 9, 0], - "jdk_version": [13, 0, 0, 33] - }, - { - "id": 10888, - "url": "https://cdn.azul.com/zulu/bin/zulu13.28.11-ca-jdk13.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13.0.0-macosx_x64.tar.gz", + "java_version": [ + 13, + 0, + 0 + ], + "openjdk_build_number": 33, + "distro_version": [ + 13, + 27, + 9, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10888", "name": "zulu13.28.11-ca-jdk13.0.1-macosx_x64.tar.gz", - "zulu_version": [13, 28, 11, 0], - "jdk_version": [13, 0, 1, 10] - }, - { - "id": 11073, - "url": "https://cdn.azul.com/zulu/bin/zulu13.29.9-ca-jdk13.0.2-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.28.11-ca-jdk13.0.1-macosx_x64.tar.gz", + "java_version": [ + 13, + 0, + 1 + ], + "openjdk_build_number": 10, + "distro_version": [ + 13, + 28, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11073", "name": "zulu13.29.9-ca-jdk13.0.2-macosx_x64.tar.gz", - "zulu_version": [13, 29, 9, 0], - "jdk_version": [13, 0, 2, 6] - }, - { - "id": 12408, - "url": "https://cdn.azul.com/zulu/bin/zulu13.37.21-ca-jdk13.0.6-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.29.9-ca-jdk13.0.2-macosx_x64.tar.gz", + "java_version": [ + 13, + 0, + 2 + ], + "openjdk_build_number": 6, + "distro_version": [ + 13, + 29, + 9, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12408", "name": "zulu13.37.21-ca-jdk13.0.6-macosx_x64.tar.gz", - "zulu_version": [13, 37, 21, 0], - "jdk_version": [13, 0, 6, 5] - }, - { - "id": 11236, - "url": "https://cdn.azul.com/zulu/bin/zulu14.27.1-ca-jdk14.0.0-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.37.21-ca-jdk13.0.6-macosx_x64.tar.gz", + "java_version": [ + 13, + 0, + 6 + ], + "openjdk_build_number": 5, + "distro_version": [ + 13, + 37, + 21, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11236", "name": "zulu14.27.1-ca-jdk14.0.0-macosx_x64.tar.gz", - "zulu_version": [14, 27, 1, 0], - "jdk_version": [14, 0, 0, 36] - }, - { - "id": 11349, - "url": "https://cdn.azul.com/zulu/bin/zulu14.28.21-ca-jdk14.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.27.1-ca-jdk14.0.0-macosx_x64.tar.gz", + "java_version": [ + 14, + 0, + 0 + ], + "openjdk_build_number": 36, + "distro_version": [ + 14, + 27, + 1, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11349", "name": "zulu14.28.21-ca-jdk14.0.1-macosx_x64.tar.gz", - "zulu_version": [14, 28, 21, 0], - "jdk_version": [14, 0, 1, 8] - }, - { - "id": 11513, - "url": "https://cdn.azul.com/zulu/bin/zulu14.29.23-ca-jdk14.0.2-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.28.21-ca-jdk14.0.1-macosx_x64.tar.gz", + "java_version": [ + 14, + 0, + 1 + ], + "openjdk_build_number": 8, + "distro_version": [ + 14, + 28, + 21, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11513", "name": "zulu14.29.23-ca-jdk14.0.2-macosx_x64.tar.gz", - "zulu_version": [14, 29, 23, 0], - "jdk_version": [14, 0, 2, 12] - }, - { - "id": 11780, - "url": "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.29.23-ca-jdk14.0.2-macosx_x64.tar.gz", + "java_version": [ + 14, + 0, + 2 + ], + "openjdk_build_number": 12, + "distro_version": [ + 14, + 29, + 23, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11780", "name": "zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz", - "zulu_version": [15, 27, 17, 0], - "jdk_version": [15, 0, 0, 36] - }, - { - "id": 11924, - "url": "https://cdn.azul.com/zulu/bin/zulu15.28.13-ca-jdk15.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-macosx_x64.tar.gz", + "java_version": [ + 15, + 0, + 0 + ], + "openjdk_build_number": 36, + "distro_version": [ + 15, + 27, + 17, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11924", "name": "zulu15.28.13-ca-jdk15.0.1-macosx_x64.tar.gz", - "zulu_version": [15, 28, 13, 0], - "jdk_version": [15, 0, 1, 8] - }, - { - "id": 12101, - "url": "https://cdn.azul.com/zulu/bin/zulu15.28.51-ca-jdk15.0.1-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.28.13-ca-jdk15.0.1-macosx_x64.tar.gz", + "java_version": [ + 15, + 0, + 1 + ], + "openjdk_build_number": 8, + "distro_version": [ + 15, + 28, + 13, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12101", "name": "zulu15.28.51-ca-jdk15.0.1-macosx_x64.tar.gz", - "zulu_version": [15, 28, 51, 0], - "jdk_version": [15, 0, 1, 9] - }, - { - "id": 12445, - "url": "https://cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-macosx_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.28.51-ca-jdk15.0.1-macosx_x64.tar.gz", + "java_version": [ + 15, + 0, + 1 + ], + "openjdk_build_number": 9, + "distro_version": [ + 15, + 28, + 51, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12445", "name": "zulu15.29.15-ca-jdk15.0.2-macosx_x64.tar.gz", - "zulu_version": [15, 29, 15, 0], - "jdk_version": [15, 0, 2, 7] + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-macosx_x64.tar.gz", + "java_version": [ + 15, + 0, + 2 + ], + "openjdk_build_number": 7, + "distro_version": [ + 15, + 29, + 15, + 0 + ], + "latest": false, + "availability_type": "ca" } ] diff --git a/__tests__/data/zulu-windows.json b/__tests__/data/zulu-windows.json index 0ec1a0df0..dcfb9462f 100644 --- a/__tests__/data/zulu-windows.json +++ b/__tests__/data/zulu-windows.json @@ -1,254 +1,686 @@ -[ +[ { - "id": 10996, - "url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_05-8.1.0.10-windows.tar.gz", + "package_uuid": "test-uuid-10996", "name": "zulu1.8.0_05-8.1.0.10-windows.tar.gz", - "zulu_version": [8, 1, 0, 10], - "jdk_version": [8, 0, 5, 13] - }, - { - "id": 10997, - "url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_11-8.2.0.1-windows.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_05-8.1.0.10-windows.tar.gz", + "java_version": [ + 8, + 0, + 5 + ], + "openjdk_build_number": 13, + "distro_version": [ + 8, + 1, + 0, + 10 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10997", "name": "zulu1.8.0_11-8.2.0.1-windows.tar.gz", - "zulu_version": [8, 2, 0, 1], - "jdk_version": [8, 0, 11, 12] - }, - { - "id": 10346, - "url": "https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu1.8.0_11-8.2.0.1-windows.tar.gz", + "java_version": [ + 8, + 0, + 11 + ], + "openjdk_build_number": 12, + "distro_version": [ + 8, + 2, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10346", "name": "zulu8.21.0.1-jdk8.0.131-windows_x64.tar.gz", - "zulu_version": [8, 21, 0, 1], - "jdk_version": [8, 0, 131, 11] - }, - { - "id": 10362, - "url": "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.21.0.1-jdk8.0.131-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 131 + ], + "openjdk_build_number": 11, + "distro_version": [ + 8, + 21, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10362", "name": "zulu8.23.0.3-jdk8.0.144-windows_x64.tar.gz", - "zulu_version": [8, 23, 0, 3], - "jdk_version": [8, 0, 144, 1] - }, - { - "id": 10399, - "url": "https://cdn.azul.com/zulu/bin/zulu8.25.0.1-jdk8.0.152-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 144 + ], + "openjdk_build_number": 1, + "distro_version": [ + 8, + 23, + 0, + 3 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10399", "name": "zulu8.25.0.1-jdk8.0.152-windows_x64.tar.gz", - "zulu_version": [8, 25, 0, 1], - "jdk_version": [8, 0, 152, 16] - }, - { - "id": 11355, - "url": "https://cdn.azul.com/zulu/bin/zulu8.46.0.19-ca-jdk8.0.252-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.25.0.1-jdk8.0.152-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 152 + ], + "openjdk_build_number": 16, + "distro_version": [ + 8, + 25, + 0, + 1 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11355", "name": "zulu8.46.0.19-ca-jdk8.0.252-windows_x64.tar.gz", - "zulu_version": [8, 46, 0, 19], - "jdk_version": [8, 0, 252, 14] - }, - { - "id": 11481, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.47-ca-jdk8.0.262-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.46.0.19-ca-jdk8.0.252-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 252 + ], + "openjdk_build_number": 14, + "distro_version": [ + 8, + 46, + 0, + 19 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11481", "name": "zulu8.48.0.47-ca-jdk8.0.262-windows_x64.tar.gz", - "zulu_version": [8, 48, 0, 47], - "jdk_version": [8, 0, 262, 17] - }, - { - "id": 11622, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.51-ca-jdk8.0.262-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.47-ca-jdk8.0.262-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 17, + "distro_version": [ + 8, + 48, + 0, + 47 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11622", "name": "zulu8.48.0.51-ca-jdk8.0.262-windows_x64.tar.gz", - "zulu_version": [8, 48, 0, 51], - "jdk_version": [8, 0, 262, 19] - }, - { - "id": 11535, - "url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.49-ca-jdk8.0.262-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.51-ca-jdk8.0.262-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 19, + "distro_version": [ + 8, + 48, + 0, + 51 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11535", "name": "zulu8.48.0.49-ca-jdk8.0.262-windows_x64.tar.gz", - "zulu_version": [8, 48, 0, 49], - "jdk_version": [8, 0, 262, 18] - }, - { - "id": 12424, - "url": "https://cdn.azul.com/zulu/bin/zulu8.52.0.23-ca-jdk8.0.282-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.48.0.49-ca-jdk8.0.262-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 262 + ], + "openjdk_build_number": 18, + "distro_version": [ + 8, + 48, + 0, + 49 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12424", "name": "zulu8.52.0.23-ca-jdk8.0.282-windows_x64.tar.gz", - "zulu_version": [8, 52, 0, 23], - "jdk_version": [8, 0, 282, 8] - }, - { - "id": 10383, - "url": "https://cdn.azul.com/zulu/bin/zulu9.0.0.15-jdk9.0.0-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu8.52.0.23-ca-jdk8.0.282-windows_x64.tar.gz", + "java_version": [ + 8, + 0, + 282 + ], + "openjdk_build_number": 8, + "distro_version": [ + 8, + 52, + 0, + 23 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10383", "name": "zulu9.0.0.15-jdk9.0.0-windows_x64.tar.gz", - "zulu_version": [9, 0, 0, 15], - "jdk_version": [9, 0, 0, 0] - }, - { - "id": 10413, - "url": "https://cdn.azul.com/zulu/bin/zulu9.0.1.3-jdk9.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu9.0.0.15-jdk9.0.0-windows_x64.tar.gz", + "java_version": [ + 9, + 0, + 0 + ], + "openjdk_build_number": 0, + "distro_version": [ + 9, + 0, + 0, + 15 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10413", "name": "zulu9.0.1.3-jdk9.0.1-windows_x64.tar.gz", - "zulu_version": [9, 0, 1, 3], - "jdk_version": [9, 0, 1, 0] - }, - { - "id": 10503, - "url": "https://cdn.azul.com/zulu/bin/zulu10.2+3-jdk10.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu9.0.1.3-jdk9.0.1-windows_x64.tar.gz", + "java_version": [ + 9, + 0, + 1 + ], + "openjdk_build_number": 0, + "distro_version": [ + 9, + 0, + 1, + 3 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10503", "name": "zulu10.2+3-jdk10.0.1-windows_x64.tar.gz", - "zulu_version": [10, 2, 3, 0], - "jdk_version": [10, 0, 1, 9] - }, - { - "id": 10541, - "url": "https://cdn.azul.com/zulu/bin/zulu10.3+5-jdk10.0.2-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu10.2+3-jdk10.0.1-windows_x64.tar.gz", + "java_version": [ + 10, + 0, + 1 + ], + "openjdk_build_number": 9, + "distro_version": [ + 10, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10541", "name": "zulu10.3+5-jdk10.0.2-windows_x64.tar.gz", - "zulu_version": [10, 3, 5, 0], - "jdk_version": [10, 0, 2, 13] - }, - { - "id": 10576, - "url": "https://cdn.azul.com/zulu/bin/zulu11.2.3-jdk11.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu10.3+5-jdk10.0.2-windows_x64.tar.gz", + "java_version": [ + 10, + 0, + 2 + ], + "openjdk_build_number": 13, + "distro_version": [ + 10, + 3, + 5, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10576", "name": "zulu11.2.3-jdk11.0.1-windows_x64.tar.gz", - "zulu_version": [11, 2, 3, 0], - "jdk_version": [11, 0, 1, 13] - }, - { - "id": 10604, - "url": "https://cdn.azul.com/zulu/bin/zulu11.29.3-ca-jdk11.0.2-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.2.3-jdk11.0.1-windows_x64.tar.gz", + "java_version": [ + 11, + 0, + 1 + ], + "openjdk_build_number": 13, + "distro_version": [ + 11, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10604", "name": "zulu11.29.3-ca-jdk11.0.2-windows_x64.tar.gz", - "zulu_version": [11, 29, 3, 0], - "jdk_version": [11, 0, 2, 7] - }, - { - "id": 10687, - "url": "https://cdn.azul.com/zulu/bin/zulu11.31.11-ca-jdk11.0.3-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.29.3-ca-jdk11.0.2-windows_x64.tar.gz", + "java_version": [ + 11, + 0, + 2 + ], + "openjdk_build_number": 7, + "distro_version": [ + 11, + 29, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10687", "name": "zulu11.31.11-ca-jdk11.0.3-windows_x64.tar.gz", - "zulu_version": [11, 31, 11, 0], - "jdk_version": [11, 0, 3, 7] - }, - { - "id": 10856, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.13-ca-jdk11.0.5-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.31.11-ca-jdk11.0.3-windows_x64.tar.gz", + "java_version": [ + 11, + 0, + 3 + ], + "openjdk_build_number": 7, + "distro_version": [ + 11, + 31, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10856", "name": "zulu11.35.13-ca-jdk11.0.5-windows_x64.tar.gz", - "zulu_version": [11, 35, 13, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 10933, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.15-ca-jdk11.0.5-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.13-ca-jdk11.0.5-windows_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 13, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10933", "name": "zulu11.35.15-ca-jdk11.0.5-windows_x64.tar.gz", - "zulu_version": [11, 35, 15, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 10933, - "url": "https://cdn.azul.com/zulu/bin/zulu11.35.11-ca-jdk11.0.5-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.15-ca-jdk11.0.5-windows_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 15, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10933", "name": "zulu11.35.15-ca-jdk11.0.5-windows_x64.tar.gz", - "zulu_version": [11, 35, 11, 0], - "jdk_version": [11, 0, 5, 10] - }, - { - "id": 12397, - "url": "https://cdn.azul.com/zulu/bin/zulu11.45.27-ca-jdk11.0.10-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.35.11-ca-jdk11.0.5-windows_x64.tar.gz", + "java_version": [ + 11, + 0, + 5 + ], + "openjdk_build_number": 10, + "distro_version": [ + 11, + 35, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12397", "name": "zulu11.45.27-ca-jdk11.0.10-windows_x64.tar.gz", - "zulu_version": [11, 45, 27, 0], - "jdk_version": [11, 0, 10, 9] - }, - { - "id": 10667, - "url": "https://cdn.azul.com/zulu/bin/zulu12.1.3-ca-jdk12.0.0-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu11.45.27-ca-jdk11.0.10-windows_x64.tar.gz", + "java_version": [ + 11, + 0, + 10 + ], + "openjdk_build_number": 9, + "distro_version": [ + 11, + 45, + 27, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10667", "name": "zulu12.1.3-ca-jdk12.0.0-windows_x64.tar.gz", - "zulu_version": [12, 1, 3, 0], - "jdk_version": [12, 0, 0, 33] - }, - { - "id": 10710, - "url": "https://cdn.azul.com/zulu/bin/zulu12.2.3-ca-jdk12.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.1.3-ca-jdk12.0.0-windows_x64.tar.gz", + "java_version": [ + 12, + 0, + 0 + ], + "openjdk_build_number": 33, + "distro_version": [ + 12, + 1, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10710", "name": "zulu12.2.3-ca-jdk12.0.1-windows_x64.tar.gz", - "zulu_version": [12, 2, 3, 0], - "jdk_version": [12, 0, 1, 12] - }, - { - "id": 10780, - "url": "https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.2.3-ca-jdk12.0.1-windows_x64.tar.gz", + "java_version": [ + 12, + 0, + 1 + ], + "openjdk_build_number": 12, + "distro_version": [ + 12, + 2, + 3, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10780", "name": "zulu12.3.11-ca-jdk12.0.2-windows_x64.tar.gz", - "zulu_version": [12, 3, 11, 0], - "jdk_version": [12, 0, 2, 3] - }, - { - "id": 10846, - "url": "https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13.0.0-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu12.3.11-ca-jdk12.0.2-windows_x64.tar.gz", + "java_version": [ + 12, + 0, + 2 + ], + "openjdk_build_number": 3, + "distro_version": [ + 12, + 3, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10846", "name": "zulu13.27.9-ca-jdk13.0.0-windows_x64.tar.gz", - "zulu_version": [13, 27, 9, 0], - "jdk_version": [13, 0, 0, 33] - }, - { - "id": 10888, - "url": "https://cdn.azul.com/zulu/bin/zulu13.28.11-ca-jdk13.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.27.9-ca-jdk13.0.0-windows_x64.tar.gz", + "java_version": [ + 13, + 0, + 0 + ], + "openjdk_build_number": 33, + "distro_version": [ + 13, + 27, + 9, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-10888", "name": "zulu13.28.11-ca-jdk13.0.1-windows_x64.tar.gz", - "zulu_version": [13, 28, 11, 0], - "jdk_version": [13, 0, 1, 10] - }, - { - "id": 11073, - "url": "https://cdn.azul.com/zulu/bin/zulu13.29.9-ca-jdk13.0.2-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.28.11-ca-jdk13.0.1-windows_x64.tar.gz", + "java_version": [ + 13, + 0, + 1 + ], + "openjdk_build_number": 10, + "distro_version": [ + 13, + 28, + 11, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11073", "name": "zulu13.29.9-ca-jdk13.0.2-windows_x64.tar.gz", - "zulu_version": [13, 29, 9, 0], - "jdk_version": [13, 0, 2, 6] - }, - { - "id": 12408, - "url": "https://cdn.azul.com/zulu/bin/zulu13.37.21-ca-jdk13.0.6-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.29.9-ca-jdk13.0.2-windows_x64.tar.gz", + "java_version": [ + 13, + 0, + 2 + ], + "openjdk_build_number": 6, + "distro_version": [ + 13, + 29, + 9, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12408", "name": "zulu13.37.21-ca-jdk13.0.6-windows_x64.tar.gz", - "zulu_version": [13, 37, 21, 0], - "jdk_version": [13, 0, 6, 5] - }, - { - "id": 11236, - "url": "https://cdn.azul.com/zulu/bin/zulu14.27.1-ca-jdk14.0.0-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu13.37.21-ca-jdk13.0.6-windows_x64.tar.gz", + "java_version": [ + 13, + 0, + 6 + ], + "openjdk_build_number": 5, + "distro_version": [ + 13, + 37, + 21, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11236", "name": "zulu14.27.1-ca-jdk14.0.0-windows_x64.tar.gz", - "zulu_version": [14, 27, 1, 0], - "jdk_version": [14, 0, 0, 36] - }, - { - "id": 11349, - "url": "https://cdn.azul.com/zulu/bin/zulu14.28.21-ca-jdk14.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.27.1-ca-jdk14.0.0-windows_x64.tar.gz", + "java_version": [ + 14, + 0, + 0 + ], + "openjdk_build_number": 36, + "distro_version": [ + 14, + 27, + 1, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11349", "name": "zulu14.28.21-ca-jdk14.0.1-windows_x64.tar.gz", - "zulu_version": [14, 28, 21, 0], - "jdk_version": [14, 0, 1, 8] - }, - { - "id": 11513, - "url": "https://cdn.azul.com/zulu/bin/zulu14.29.23-ca-jdk14.0.2-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.28.21-ca-jdk14.0.1-windows_x64.tar.gz", + "java_version": [ + 14, + 0, + 1 + ], + "openjdk_build_number": 8, + "distro_version": [ + 14, + 28, + 21, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11513", "name": "zulu14.29.23-ca-jdk14.0.2-windows_x64.tar.gz", - "zulu_version": [14, 29, 23, 0], - "jdk_version": [14, 0, 2, 12] - }, - { - "id": 11780, - "url": "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu14.29.23-ca-jdk14.0.2-windows_x64.tar.gz", + "java_version": [ + 14, + 0, + 2 + ], + "openjdk_build_number": 12, + "distro_version": [ + 14, + 29, + 23, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11780", "name": "zulu15.27.17-ca-jdk15.0.0-windows_x64.tar.gz", - "zulu_version": [15, 27, 17, 0], - "jdk_version": [15, 0, 0, 36] - }, - { - "id": 11924, - "url": "https://cdn.azul.com/zulu/bin/zulu15.28.13-ca-jdk15.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.27.17-ca-jdk15.0.0-windows_x64.tar.gz", + "java_version": [ + 15, + 0, + 0 + ], + "openjdk_build_number": 36, + "distro_version": [ + 15, + 27, + 17, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-11924", "name": "zulu15.28.13-ca-jdk15.0.1-windows_x64.tar.gz", - "zulu_version": [15, 28, 13, 0], - "jdk_version": [15, 0, 1, 8] - }, - { - "id": 12101, - "url": "https://cdn.azul.com/zulu/bin/zulu15.28.51-ca-jdk15.0.1-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.28.13-ca-jdk15.0.1-windows_x64.tar.gz", + "java_version": [ + 15, + 0, + 1 + ], + "openjdk_build_number": 8, + "distro_version": [ + 15, + 28, + 13, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12101", "name": "zulu15.28.51-ca-jdk15.0.1-windows_x64.tar.gz", - "zulu_version": [15, 28, 51, 0], - "jdk_version": [15, 0, 1, 9] - }, - { - "id": 12445, - "url": "https://cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-windows_x64.tar.gz", + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.28.51-ca-jdk15.0.1-windows_x64.tar.gz", + "java_version": [ + 15, + 0, + 1 + ], + "openjdk_build_number": 9, + "distro_version": [ + 15, + 28, + 51, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12445", "name": "zulu15.29.15-ca-jdk15.0.2-windows_x64.tar.gz", - "zulu_version": [15, 29, 15, 0], - "jdk_version": [15, 0, 2, 7] - }, - { - "id": 12446, - "url": "https://cdn.azul.com/zulu/bin/zulu17.48.15-ca-jdk17.0.10-windows_aarch64.zip", - "name": "zulu17.48.15-ca-jdk17.0.10-win_aarch4.zip", - "zulu_version": [17, 48, 15, 0], - "jdk_version": [17, 0, 10, 7] + "download_url": "https://cdn.azul.com/zulu/bin/zulu15.29.15-ca-jdk15.0.2-windows_x64.tar.gz", + "java_version": [ + 15, + 0, + 2 + ], + "openjdk_build_number": 7, + "distro_version": [ + 15, + 29, + 15, + 0 + ], + "latest": false, + "availability_type": "ca" + }, + { + "package_uuid": "test-uuid-12446", + "name": "zulu17.48.15-ca-jdk17.0.10-win_aarch64.zip", + "download_url": "https://cdn.azul.com/zulu/bin/zulu17.48.15-ca-jdk17.0.10-windows_aarch64.zip", + "java_version": [ + 17, + 0, + 10 + ], + "openjdk_build_number": 7, + "distro_version": [ + 17, + 48, + 15, + 0 + ], + "latest": false, + "availability_type": "ca" } -] \ No newline at end of file +] diff --git a/__tests__/distributors/zulu-installer.test.ts b/__tests__/distributors/zulu-installer.test.ts index 0a46983b2..16f4c63f2 100644 --- a/__tests__/distributors/zulu-installer.test.ts +++ b/__tests__/distributors/zulu-installer.test.ts @@ -17,7 +17,7 @@ describe('getAvailableVersions', () => { spyHttpClient.mockReturnValue({ statusCode: 200, headers: {}, - result: manifestData as IZuluVersions[] + result: [] as IZuluVersions[] }); spyUtilGetDownloadArchiveExtension = jest.spyOn( @@ -45,7 +45,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=32&release_status=ga' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -54,7 +54,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=32&release_status=ea' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ea&availability_types=ca&page=1&page_size=100' ], [ { @@ -63,7 +63,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=64&release_status=ga' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -72,7 +72,7 @@ describe('getAvailableVersions', () => { packageType: 'jre', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jre&javafx=false&arch=x86&hw_bitness=64&release_status=ga' + '?os=macos&archive_type=tar.gz&java_package_type=jre&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -81,7 +81,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk+fx', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jdk&javafx=true&arch=x86&hw_bitness=64&release_status=ga&features=fx' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -90,7 +90,16 @@ describe('getAvailableVersions', () => { packageType: 'jre+fx', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jre&javafx=true&arch=x86&hw_bitness=64&release_status=ga&features=fx' + '?os=macos&archive_type=tar.gz&java_package_type=jre&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' + ], + [ + { + version: '8', + architecture: 'x64', + packageType: 'jdk+crac', + checkLatest: false + }, + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=true&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -99,7 +108,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jdk&javafx=false&arch=arm&hw_bitness=64&release_status=ga' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=aarch64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -108,12 +117,12 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=macos&ext=tar.gz&bundle_type=jdk&javafx=false&arch=arm&hw_bitness=&release_status=ga' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=arm&release_status=ga&availability_types=ca&page=1&page_size=100' ] ])('build correct url for %s -> %s', async (input, parsedUrl) => { const distribution = new ZuluDistribution(input); distribution['getPlatformOption'] = () => 'macos'; - const buildUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/${parsedUrl}`; + const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/${parsedUrl}`; await distribution['getAvailableVersions'](); @@ -121,16 +130,12 @@ describe('getAvailableVersions', () => { expect(spyHttpClient.mock.calls[0][0]).toBe(buildUrl); }); - type DistroArch = { - bitness: string; - arch: string; - }; it.each([ - ['amd64', {bitness: '64', arch: 'x86'}], - ['arm64', {bitness: '64', arch: 'arm'}] + ['amd64', 'x64'], + ['arm64', 'aarch64'] ])( 'defaults to os.arch(): %s mapped to distro arch: %s', - async (osArch: string, distroArch: DistroArch) => { + async (osArch: string, distroArch: string) => { jest .spyOn(os, 'arch') .mockReturnValue(osArch as ReturnType); @@ -142,7 +147,7 @@ describe('getAvailableVersions', () => { checkLatest: false }); distribution['getPlatformOption'] = () => 'macos'; - const buildUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/?os=macos&ext=tar.gz&bundle_type=jdk&javafx=false&arch=${distroArch.arch}&hw_bitness=${distroArch.bitness}&release_status=ga`; + const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=${distroArch}&release_status=ga&availability_types=ca&page=1&page_size=100`; await distribution['getAvailableVersions'](); @@ -152,6 +157,18 @@ describe('getAvailableVersions', () => { ); it('load available versions', async () => { + spyHttpClient + .mockReturnValueOnce({ + statusCode: 200, + headers: {}, + result: manifestData as IZuluVersions[] + }) + .mockReturnValueOnce({ + statusCode: 200, + headers: {}, + result: [] as IZuluVersions[] + }); + const distribution = new ZuluDistribution({ version: '11', architecture: 'x86', @@ -165,10 +182,11 @@ describe('getAvailableVersions', () => { describe('getArchitectureOptions', () => { it.each([ - [{architecture: 'x64'}, {arch: 'x86', hw_bitness: '64', abi: ''}], - [{architecture: 'x86'}, {arch: 'x86', hw_bitness: '32', abi: ''}], - [{architecture: 'x32'}, {arch: 'x32', hw_bitness: '', abi: ''}], - [{architecture: 'arm'}, {arch: 'arm', hw_bitness: '', abi: ''}] + [{architecture: 'x64'}, 'x64'], + [{architecture: 'x86'}, 'x86'], + [{architecture: 'aarch64'}, 'aarch64'], + [{architecture: 'arm64'}, 'aarch64'], + [{architecture: 'arm'}, 'arm'] ])('%s -> %s', (input, expected) => { const distribution = new ZuluDistribution({ version: '11', @@ -176,7 +194,7 @@ describe('getArchitectureOptions', () => { packageType: 'jdk', checkLatest: false }); - expect(distribution['getArchitectureOptions']()).toEqual(expected); + expect(distribution['getArchitectureOptions']()).toBe(expected); }); }); diff --git a/__tests__/distributors/zulu-linux-installer.test.ts b/__tests__/distributors/zulu-linux-installer.test.ts index b3ca6fa17..470de81ef 100644 --- a/__tests__/distributors/zulu-linux-installer.test.ts +++ b/__tests__/distributors/zulu-linux-installer.test.ts @@ -18,7 +18,7 @@ describe('getAvailableVersions', () => { spyHttpClient.mockReturnValue({ statusCode: 200, headers: {}, - result: manifestData as IZuluVersions[] + result: [] as IZuluVersions[] }); spyUtilGetDownloadArchiveExtension = jest.spyOn( @@ -46,7 +46,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=32&release_status=ga' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -55,7 +55,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=32&release_status=ea' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ea&availability_types=ca&page=1&page_size=100' ], [ { @@ -64,7 +64,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=64&release_status=ga' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -73,7 +73,7 @@ describe('getAvailableVersions', () => { packageType: 'jre', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jre&javafx=false&arch=x86&hw_bitness=64&release_status=ga' + '?os=linux_glibc&archive_type=zip&java_package_type=jre&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -82,7 +82,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk+fx', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jdk&javafx=true&arch=x86&hw_bitness=64&release_status=ga&features=fx' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -91,7 +91,16 @@ describe('getAvailableVersions', () => { packageType: 'jre+fx', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jre&javafx=true&arch=x86&hw_bitness=64&release_status=ga&features=fx' + '?os=linux_glibc&archive_type=zip&java_package_type=jre&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' + ], + [ + { + version: '8', + architecture: 'x64', + packageType: 'jdk+crac', + checkLatest: false + }, + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=true&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -100,7 +109,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jdk&javafx=false&arch=arm&hw_bitness=64&release_status=ga' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=aarch64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -109,12 +118,12 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=linux&ext=zip&bundle_type=jdk&javafx=false&arch=arm&hw_bitness=&release_status=ga' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=arm&release_status=ga&availability_types=ca&page=1&page_size=100' ] ])('build correct url for %s -> %s', async (input, parsedUrl) => { const distribution = new ZuluDistribution(input); - distribution['getPlatformOption'] = () => 'linux'; - const buildUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/${parsedUrl}`; + distribution['getPlatformOption'] = () => 'linux_glibc'; + const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/${parsedUrl}`; await distribution['getAvailableVersions'](); @@ -122,16 +131,12 @@ describe('getAvailableVersions', () => { expect(spyHttpClient.mock.calls[0][0]).toBe(buildUrl); }); - type DistroArch = { - bitness: string; - arch: string; - }; it.each([ - ['amd64', {bitness: '64', arch: 'x86'}], - ['arm64', {bitness: '64', arch: 'arm'}] + ['amd64', 'x64'], + ['arm64', 'aarch64'] ])( 'defaults to os.arch(): %s mapped to distro arch: %s', - async (osArch: string, distroArch: DistroArch) => { + async (osArch: string, distroArch: string) => { jest .spyOn(os, 'arch') .mockReturnValue(osArch as ReturnType); @@ -142,10 +147,10 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }); - distribution['getPlatformOption'] = () => 'linux'; + distribution['getPlatformOption'] = () => 'linux_glibc'; // Override extension for linux default arch case to match util behavior spyUtilGetDownloadArchiveExtension.mockReturnValue('tar.gz'); - const buildUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/?os=linux&ext=tar.gz&bundle_type=jdk&javafx=false&arch=${distroArch.arch}&hw_bitness=${distroArch.bitness}&release_status=ga`; + const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/?os=linux_glibc&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=${distroArch}&release_status=ga&availability_types=ca&page=1&page_size=100`; await distribution['getAvailableVersions'](); @@ -155,6 +160,18 @@ describe('getAvailableVersions', () => { ); it('load available versions', async () => { + spyHttpClient + .mockReturnValueOnce({ + statusCode: 200, + headers: {}, + result: manifestData as IZuluVersions[] + }) + .mockReturnValueOnce({ + statusCode: 200, + headers: {}, + result: [] as IZuluVersions[] + }); + const distribution = new ZuluDistribution({ version: '11', architecture: 'x86', @@ -168,10 +185,11 @@ describe('getAvailableVersions', () => { describe('getArchitectureOptions', () => { it.each([ - [{architecture: 'x64'}, {arch: 'x86', hw_bitness: '64', abi: ''}], - [{architecture: 'x86'}, {arch: 'x86', hw_bitness: '32', abi: ''}], - [{architecture: 'x32'}, {arch: 'x32', hw_bitness: '', abi: ''}], - [{architecture: 'arm'}, {arch: 'arm', hw_bitness: '', abi: ''}] + [{architecture: 'x64'}, 'x64'], + [{architecture: 'x86'}, 'x86'], + [{architecture: 'aarch64'}, 'aarch64'], + [{architecture: 'arm64'}, 'aarch64'], + [{architecture: 'arm'}, 'arm'] ])('%s -> %s', (input, expected) => { const distribution = new ZuluDistribution({ version: '11', @@ -179,7 +197,7 @@ describe('getArchitectureOptions', () => { packageType: 'jdk', checkLatest: false }); - expect(distribution['getArchitectureOptions']()).toEqual(expected); + expect(distribution['getArchitectureOptions']()).toBe(expected); }); }); diff --git a/__tests__/distributors/zulu-windows-installer.test.ts b/__tests__/distributors/zulu-windows-installer.test.ts index 37435e8db..a54f55fe1 100644 --- a/__tests__/distributors/zulu-windows-installer.test.ts +++ b/__tests__/distributors/zulu-windows-installer.test.ts @@ -18,7 +18,7 @@ describe('getAvailableVersions', () => { spyHttpClient.mockReturnValue({ statusCode: 200, headers: {}, - result: manifestData as IZuluVersions[] + result: [] as IZuluVersions[] }); spyUtilGetDownloadArchiveExtension = jest.spyOn( @@ -46,7 +46,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=32&release_status=ga' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -55,7 +55,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=32&release_status=ea' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ea&availability_types=ca&page=1&page_size=100' ], [ { @@ -64,7 +64,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jdk&javafx=false&arch=x86&hw_bitness=64&release_status=ga' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -73,7 +73,7 @@ describe('getAvailableVersions', () => { packageType: 'jre', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jre&javafx=false&arch=x86&hw_bitness=64&release_status=ga' + '?os=windows&archive_type=zip&java_package_type=jre&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -82,7 +82,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk+fx', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jdk&javafx=true&arch=x86&hw_bitness=64&release_status=ga&features=fx' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -91,7 +91,16 @@ describe('getAvailableVersions', () => { packageType: 'jre+fx', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jre&javafx=true&arch=x86&hw_bitness=64&release_status=ga&features=fx' + '?os=windows&archive_type=zip&java_package_type=jre&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' + ], + [ + { + version: '8', + architecture: 'x64', + packageType: 'jdk+crac', + checkLatest: false + }, + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=true&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -100,7 +109,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jdk&javafx=false&arch=arm&hw_bitness=64&release_status=ga' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=aarch64&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -109,12 +118,12 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=windows&ext=zip&bundle_type=jdk&javafx=false&arch=arm&hw_bitness=&release_status=ga' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=arm&release_status=ga&availability_types=ca&page=1&page_size=100' ] ])('build correct url for %s -> %s', async (input, parsedUrl) => { const distribution = new ZuluDistribution(input); distribution['getPlatformOption'] = () => 'windows'; - const buildUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/${parsedUrl}`; + const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/${parsedUrl}`; await distribution['getAvailableVersions'](); @@ -122,16 +131,12 @@ describe('getAvailableVersions', () => { expect(spyHttpClient.mock.calls[0][0]).toBe(buildUrl); }); - type DistroArch = { - bitness: string; - arch: string; - }; it.each([ - ['amd64', {bitness: '64', arch: 'x86'}], - ['arm64', {bitness: '64', arch: 'arm'}] + ['amd64', 'x64'], + ['arm64', 'aarch64'] ])( 'defaults to os.arch(): %s mapped to distro arch: %s', - async (osArch: string, distroArch: DistroArch) => { + async (osArch: string, distroArch: string) => { jest .spyOn(os, 'arch') .mockReturnValue(osArch as ReturnType); @@ -143,7 +148,7 @@ describe('getAvailableVersions', () => { checkLatest: false }); distribution['getPlatformOption'] = () => 'windows'; - const buildUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/?os=windows&ext=zip&bundle_type=jdk&javafx=false&arch=${distroArch.arch}&hw_bitness=${distroArch.bitness}&release_status=ga`; + const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=${distroArch}&release_status=ga&availability_types=ca&page=1&page_size=100`; await distribution['getAvailableVersions'](); @@ -153,6 +158,18 @@ describe('getAvailableVersions', () => { ); it('load available versions', async () => { + spyHttpClient + .mockReturnValueOnce({ + statusCode: 200, + headers: {}, + result: manifestData as IZuluVersions[] + }) + .mockReturnValueOnce({ + statusCode: 200, + headers: {}, + result: [] as IZuluVersions[] + }); + const distribution = new ZuluDistribution({ version: '11', architecture: 'x86', @@ -166,10 +183,11 @@ describe('getAvailableVersions', () => { describe('getArchitectureOptions', () => { it.each([ - [{architecture: 'x64'}, {arch: 'x86', hw_bitness: '64', abi: ''}], - [{architecture: 'x86'}, {arch: 'x86', hw_bitness: '32', abi: ''}], - [{architecture: 'x32'}, {arch: 'x32', hw_bitness: '', abi: ''}], - [{architecture: 'arm'}, {arch: 'arm', hw_bitness: '', abi: ''}] + [{architecture: 'x64'}, 'x64'], + [{architecture: 'x86'}, 'x86'], + [{architecture: 'aarch64'}, 'aarch64'], + [{architecture: 'arm64'}, 'aarch64'], + [{architecture: 'arm'}, 'arm'] ])('%s -> %s', (input, expected) => { const distribution = new ZuluDistribution({ version: '11', @@ -177,7 +195,7 @@ describe('getArchitectureOptions', () => { packageType: 'jdk', checkLatest: false }); - expect(distribution['getArchitectureOptions']()).toEqual(expected); + expect(distribution['getArchitectureOptions']()).toBe(expected); }); }); diff --git a/action.yml b/action.yml index e42fb05cb..f4747c1a4 100644 --- a/action.yml +++ b/action.yml @@ -13,7 +13,7 @@ inputs: description: 'Java distribution. See the list of supported distributions in README file. This input is required except when java-version-file points to .sdkmanrc with a recognized distribution suffix (e.g., java=21.0.5-tem).' required: false java-package: - description: 'The package type (jdk, jre, jdk+fx, jre+fx)' + description: 'The package type (jdk, jre, jdk+fx, jre+fx, jdk+crac, jre+crac)' required: false default: 'jdk' architecture: diff --git a/dist/setup/index.js b/dist/setup/index.js index 871b71de7..50e9c3e05 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -81150,16 +81150,22 @@ class ZuluDistribution extends base_installer_1.JavaBase { return __awaiter(this, void 0, void 0, function* () { const availableVersionsRaw = yield this.getAvailableVersions(); const availableVersions = availableVersionsRaw.map(item => { + // The Azul Metadata API reports the JDK build number separately from + // java_version (e.g. java_version=[17,0,7], openjdk_build_number=7). + // Append it so the resulting semver retains the build (e.g. 17.0.7+7). + const javaVersion = item.openjdk_build_number != null + ? [...item.java_version, item.openjdk_build_number] + : item.java_version; return { - version: (0, util_1.convertVersionToSemver)(item.jdk_version), - url: item.url, - zuluVersion: (0, util_1.convertVersionToSemver)(item.zulu_version) + version: (0, util_1.convertVersionToSemver)(javaVersion), + url: item.download_url, + zuluVersion: (0, util_1.convertVersionToSemver)(item.distro_version) }; }); const satisfiedVersions = availableVersions .filter(item => (0, util_1.isVersionSatisfies)(version, item.version)) .sort((a, b) => { - // Azul provides two versions: jdk_version and azul_version + // Azul provides two versions: java_version and distro_version // we should sort by both fields by descending return (-semver_1.default.compareBuild(a.version, b.version) || -semver_1.default.compareBuild(a.zuluVersion, b.zuluVersion)); @@ -81197,37 +81203,60 @@ class ZuluDistribution extends base_installer_1.JavaBase { getAvailableVersions() { var _a, _b; return __awaiter(this, void 0, void 0, function* () { - const { arch, hw_bitness, abi } = this.getArchitectureOptions(); + const arch = this.getArchitectureOptions(); const [bundleType, features] = this.packageType.split('+'); const platform = this.getPlatformOption(); const extension = (0, util_1.getDownloadArchiveExtension)(); const javafx = (_a = features === null || features === void 0 ? void 0 : features.includes('fx')) !== null && _a !== void 0 ? _a : false; + const crac = (_b = features === null || features === void 0 ? void 0 : features.includes('crac')) !== null && _b !== void 0 ? _b : false; const releaseStatus = this.stable ? 'ga' : 'ea'; if (core.isDebug()) { console.time('Retrieving available versions for Zulu took'); // eslint-disable-line no-console } - const requestArguments = [ + const baseRequestArguments = [ `os=${platform}`, - `ext=${extension}`, - `bundle_type=${bundleType}`, - `javafx=${javafx}`, + `archive_type=${extension}`, + `java_package_type=${bundleType}`, + `javafx_bundled=${javafx}`, + `crac_supported=${crac}`, `arch=${arch}`, - `hw_bitness=${hw_bitness}`, `release_status=${releaseStatus}`, - abi ? `abi=${abi}` : null, - features ? `features=${features}` : null - ] - .filter(Boolean) - .join('&'); - const availableVersionsUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/?${requestArguments}`; - core.debug(`Gathering available versions from '${availableVersionsUrl}'`); - const availableVersions = (_b = (yield this.http.getJson(availableVersionsUrl)) - .result) !== null && _b !== void 0 ? _b : []; + `availability_types=ca` + ].join('&'); + // Need to iterate through all pages to retrieve the list of all versions. + // The Azul API doesn't return a total page count, so paginate until a page + // comes back empty (or short), guarding against a runaway loop with a cap. + const pageSize = 100; + const maxPages = 100; + let pageIndex = 1; + const availableVersions = []; + while (pageIndex <= maxPages) { + const requestArguments = `${baseRequestArguments}&page=${pageIndex}&page_size=${pageSize}`; + const availableVersionsUrl = `https://api.azul.com/metadata/v1/zulu/packages/?${requestArguments}`; + if (core.isDebug() && pageIndex === 1) { + // the url is identical except for the page number, so print it once for debug + core.debug(`Gathering available versions from '${availableVersionsUrl}'`); + } + const paginationPage = (yield this.http.getJson(availableVersionsUrl)).result; + if (!paginationPage || paginationPage.length === 0) { + // stop paginating because we have reached the end of the results + break; + } + availableVersions.push(...paginationPage); + if (paginationPage.length < pageSize) { + // a short page means this was the last one; avoid an extra empty request + break; + } + pageIndex++; + } + if (pageIndex > maxPages) { + core.warning(`Reached the maximum of ${maxPages} pages while listing Zulu versions; results may be truncated.`); + } if (core.isDebug()) { core.startGroup('Print information about available versions'); console.timeEnd('Retrieving available versions for Zulu took'); // eslint-disable-line no-console core.debug(`Available versions: [${availableVersions.length}]`); - core.debug(availableVersions.map(item => item.jdk_version.join('.')).join(', ')); + core.debug(availableVersions.map(item => item.java_version.join('.')).join(', ')); core.endGroup(); } return availableVersions; @@ -81237,14 +81266,14 @@ class ZuluDistribution extends base_installer_1.JavaBase { const arch = this.distributionArchitecture(); switch (arch) { case 'x64': - return { arch: 'x86', hw_bitness: '64', abi: '' }; + return 'x64'; case 'x86': - return { arch: 'x86', hw_bitness: '32', abi: '' }; + return 'x86'; case 'aarch64': case 'arm64': - return { arch: 'arm', hw_bitness: '64', abi: '' }; + return 'aarch64'; default: - return { arch: arch, hw_bitness: '', abi: '' }; + return arch; } } getPlatformOption() { @@ -81254,6 +81283,10 @@ class ZuluDistribution extends base_installer_1.JavaBase { return 'macos'; case 'win32': return 'windows'; + case 'linux': + // The new Metadata API's "linux" value returns both glibc and musl packages; + // use "linux_glibc" to target only glibc, which is what standard runners use. + return 'linux_glibc'; default: return process.platform; } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 18fa4e626..c46db1e78 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -41,7 +41,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '21' + java-version: '25' - run: java --version ``` @@ -66,8 +66,8 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: '21' - java-package: jdk # optional (jdk, jre, jdk+fx or jre+fx) - defaults to jdk + java-version: '25' + java-package: jdk # optional (jdk, jre, jdk+fx, jre+fx, jdk+crac, or jre+crac) - defaults to jdk - run: java --version ``` @@ -79,7 +79,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'liberica' - java-version: '21' + java-version: '25' java-package: jdk # optional (jdk, jre, jdk+fx or jre+fx) - defaults to jdk - run: java --version ``` @@ -92,7 +92,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'microsoft' - java-version: '21' + java-version: '25' - run: java --version ``` @@ -107,7 +107,7 @@ uses: actions/setup-java@v5 with: token: ${{ secrets.GH_DOTCOM_TOKEN }} distribution: 'microsoft' - java-version: '21' + java-version: '25' ``` If the runner is not able to access github.com, any Java versions requested during a workflow run must come from the runner's tool cache. See "[Setting up the tool cache on self-hosted runners without internet access](https://docs.github.com/en/enterprise-server@3.2/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)" for more information. @@ -121,7 +121,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'corretto' - java-version: '21' + java-version: '25' - run: java --version ``` @@ -134,7 +134,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'oracle' - java-version: '21' + java-version: '25' - run: java --version ``` @@ -159,7 +159,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'sapmachine' - java-version: '21' + java-version: '25' - run: java --version ``` @@ -172,7 +172,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'graalvm' - java-version: '21' + java-version: '25' - run: | java --version native-image --version @@ -256,7 +256,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: '' - java-version: '11' + java-version: '25' java-package: jdk # optional (jdk or jre) - defaults to jdk - run: java --version ``` @@ -271,7 +271,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: '21' + java-version: '25' java-package: jdk+fx cache: maven - name: Build with Maven @@ -293,7 +293,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: '' - java-version: '11' + java-version: '25' architecture: x86 # optional - default value derived from the runner machine - run: java --version ``` diff --git a/src/distributions/zulu/installer.ts b/src/distributions/zulu/installer.ts index 83f879461..caf039105 100644 --- a/src/distributions/zulu/installer.ts +++ b/src/distributions/zulu/installer.ts @@ -30,17 +30,24 @@ export class ZuluDistribution extends JavaBase { ): Promise { const availableVersionsRaw = await this.getAvailableVersions(); const availableVersions = availableVersionsRaw.map(item => { + // The Azul Metadata API reports the JDK build number separately from + // java_version (e.g. java_version=[17,0,7], openjdk_build_number=7). + // Append it so the resulting semver retains the build (e.g. 17.0.7+7). + const javaVersion = + item.openjdk_build_number != null + ? [...item.java_version, item.openjdk_build_number] + : item.java_version; return { - version: convertVersionToSemver(item.jdk_version), - url: item.url, - zuluVersion: convertVersionToSemver(item.zulu_version) + version: convertVersionToSemver(javaVersion), + url: item.download_url, + zuluVersion: convertVersionToSemver(item.distro_version) }; }); const satisfiedVersions = availableVersions .filter(item => isVersionSatisfies(version, item.version)) .sort((a, b) => { - // Azul provides two versions: jdk_version and azul_version + // Azul provides two versions: java_version and distro_version // we should sort by both fields by descending return ( -semver.compareBuild(a.version, b.version) || @@ -95,45 +102,76 @@ export class ZuluDistribution extends JavaBase { } private async getAvailableVersions(): Promise { - const {arch, hw_bitness, abi} = this.getArchitectureOptions(); + const arch = this.getArchitectureOptions(); const [bundleType, features] = this.packageType.split('+'); const platform = this.getPlatformOption(); const extension = getDownloadArchiveExtension(); const javafx = features?.includes('fx') ?? false; + const crac = features?.includes('crac') ?? false; const releaseStatus = this.stable ? 'ga' : 'ea'; if (core.isDebug()) { console.time('Retrieving available versions for Zulu took'); // eslint-disable-line no-console } - const requestArguments = [ + const baseRequestArguments = [ `os=${platform}`, - `ext=${extension}`, - `bundle_type=${bundleType}`, - `javafx=${javafx}`, + `archive_type=${extension}`, + `java_package_type=${bundleType}`, + `javafx_bundled=${javafx}`, + `crac_supported=${crac}`, `arch=${arch}`, - `hw_bitness=${hw_bitness}`, `release_status=${releaseStatus}`, - abi ? `abi=${abi}` : null, - features ? `features=${features}` : null - ] - .filter(Boolean) - .join('&'); + `availability_types=ca` + ].join('&'); + + // Need to iterate through all pages to retrieve the list of all versions. + // The Azul API doesn't return a total page count, so paginate until a page + // comes back empty (or short), guarding against a runaway loop with a cap. + const pageSize = 100; + const maxPages = 100; + let pageIndex = 1; + const availableVersions: IZuluVersions[] = []; + while (pageIndex <= maxPages) { + const requestArguments = `${baseRequestArguments}&page=${pageIndex}&page_size=${pageSize}`; + const availableVersionsUrl = `https://api.azul.com/metadata/v1/zulu/packages/?${requestArguments}`; + if (core.isDebug() && pageIndex === 1) { + // the url is identical except for the page number, so print it once for debug + core.debug( + `Gathering available versions from '${availableVersionsUrl}'` + ); + } + + const paginationPage = ( + await this.http.getJson(availableVersionsUrl) + ).result; + if (!paginationPage || paginationPage.length === 0) { + // stop paginating because we have reached the end of the results + break; + } + + availableVersions.push(...paginationPage); - const availableVersionsUrl = `https://api.azul.com/zulu/download/community/v1.0/bundles/?${requestArguments}`; + if (paginationPage.length < pageSize) { + // a short page means this was the last one; avoid an extra empty request + break; + } - core.debug(`Gathering available versions from '${availableVersionsUrl}'`); + pageIndex++; + } - const availableVersions = - (await this.http.getJson>(availableVersionsUrl)) - .result ?? []; + if (pageIndex > maxPages) { + core.warning( + `Reached the maximum of ${maxPages} pages while listing Zulu versions; results may be truncated.` + ); + } if (core.isDebug()) { core.startGroup('Print information about available versions'); console.timeEnd('Retrieving available versions for Zulu took'); // eslint-disable-line no-console core.debug(`Available versions: [${availableVersions.length}]`); core.debug( - availableVersions.map(item => item.jdk_version.join('.')).join(', ') + availableVersions.map(item => item.java_version.join('.')).join(', ') ); core.endGroup(); } @@ -141,22 +179,18 @@ export class ZuluDistribution extends JavaBase { return availableVersions; } - private getArchitectureOptions(): { - arch: string; - hw_bitness: string; - abi: string; - } { + private getArchitectureOptions(): string { const arch = this.distributionArchitecture(); switch (arch) { case 'x64': - return {arch: 'x86', hw_bitness: '64', abi: ''}; + return 'x64'; case 'x86': - return {arch: 'x86', hw_bitness: '32', abi: ''}; + return 'x86'; case 'aarch64': case 'arm64': - return {arch: 'arm', hw_bitness: '64', abi: ''}; + return 'aarch64'; default: - return {arch: arch, hw_bitness: '', abi: ''}; + return arch; } } @@ -167,6 +201,10 @@ export class ZuluDistribution extends JavaBase { return 'macos'; case 'win32': return 'windows'; + case 'linux': + // The new Metadata API's "linux" value returns both glibc and musl packages; + // use "linux_glibc" to target only glibc, which is what standard runners use. + return 'linux_glibc'; default: return process.platform; } diff --git a/src/distributions/zulu/models.ts b/src/distributions/zulu/models.ts index a97406f09..36d369e05 100644 --- a/src/distributions/zulu/models.ts +++ b/src/distributions/zulu/models.ts @@ -1,9 +1,12 @@ -// Models from https://app.swaggerhub.com/apis-docs/azul/zulu-download-community/1.0 +// Models from https://app.swaggerhub.com/apis/azul/metadata/1.0 export interface IZuluVersions { - id: number; + package_uuid: string; name: string; - url: string; - jdk_version: Array; - zulu_version: Array; + download_url: string; + java_version: Array; + distro_version: Array; + openjdk_build_number: number; + latest: boolean; + availability_type: string; } From 78efe031a6f7abef4b5f22a7d45c2d8dcbd1b72b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Jul 2026 10:07:54 +0100 Subject: [PATCH 47/57] feat: add .mvn/extensions.xml to Maven cache key pattern (#1041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * feat: add .mvn/extensions.xml to Maven cache key pattern Closes #990 Maven build extensions declared in `.mvn/extensions.xml` can introduce additional plugin dependencies (e.g. lifecycle participants, custom packaging types). Including this file in the cache key hash ensures that changes to extensions — which affect what plugin JARs Maven downloads — properly invalidate the cache, preventing stale caches from missing newly-required plugin dependencies. Changes: - src/cache.ts: add `**/.mvn/extensions.xml` to Maven pattern array - __tests__/cache.test.ts: update pattern expectations; add new test - README.md: document the new file in the Maven cache key hash list * test: update maven cache error test name for extensions.xml --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges (cherry picked from commit 2a07c83aea2846617db2dfe16d4493d04c1ee0d7) --- README.md | 2 +- __tests__/cache.test.ts | 26 ++++++++++++++++++++++---- dist/cleanup/index.js | 6 +++++- dist/setup/index.js | 6 +++++- src/cache.ts | 6 +++++- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 97194d197..562f9ff55 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,7 @@ Currently, the following distributions are supported: The action has a built-in functionality for caching and restoring dependencies. It uses [toolkit/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under hood for caching dependencies but requires less configuration settings. Supported package managers are gradle, maven and sbt. The format of the used cache key is `setup-java-${{ platform }}-${{ packageManager }}-${{ fileHash }}`, where the hash is based on the following files: - gradle: `**/*.gradle*`, `**/gradle-wrapper.properties`, `buildSrc/**/Versions.kt`, `buildSrc/**/Dependencies.kt`, `gradle/*.versions.toml`, and `**/versions.properties` -- maven: `**/pom.xml` and `**/.mvn/wrapper/maven-wrapper.properties` +- maven: `**/pom.xml`, `**/.mvn/wrapper/maven-wrapper.properties`, and `**/.mvn/extensions.xml` - sbt: all sbt build definition files `**/*.sbt`, `**/project/build.properties`, `**/project/**.scala`, `**/project/**.sbt` When the option `cache-dependency-path` is specified, the hash is based on the matching file. This option supports wildcards and a list of file names, and is especially useful for monorepos. diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index 8a56ef608..690568706 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -96,11 +96,11 @@ describe('dependency cache', () => { }); describe('for maven', () => { - it('throws error if no pom.xml or maven-wrapper.properties found', async () => { + it('throws error if no pom.xml, maven-wrapper.properties, or extensions.xml found', async () => { await expect(restore('maven', '')).rejects.toThrow( `No file in ${projectRoot( workspace - )} matched to [**/pom.xml,**/.mvn/wrapper/maven-wrapper.properties], make sure you have checked out the target repository` + )} matched to [**/pom.xml,**/.mvn/wrapper/maven-wrapper.properties,**/.mvn/extensions.xml], make sure you have checked out the target repository` ); }); it('downloads cache based on pom.xml', async () => { @@ -115,7 +115,7 @@ describe('dependency cache', () => { expect.any(String) ); expect(spyGlobHashFiles).toHaveBeenCalledWith( - '**/pom.xml\n**/.mvn/wrapper/maven-wrapper.properties' + '**/pom.xml\n**/.mvn/wrapper/maven-wrapper.properties\n**/.mvn/extensions.xml' ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); @@ -136,7 +136,25 @@ describe('dependency cache', () => { expect.any(String) ); expect(spyGlobHashFiles).toHaveBeenCalledWith( - '**/pom.xml\n**/.mvn/wrapper/maven-wrapper.properties' + '**/pom.xml\n**/.mvn/wrapper/maven-wrapper.properties\n**/.mvn/extensions.xml' + ); + expect(spyWarning).not.toHaveBeenCalled(); + expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); + }); + it('downloads cache based on extensions.xml', async () => { + createDirectory(join(workspace, '.mvn')); + createFile(join(workspace, '.mvn', 'extensions.xml')); + + await restore('maven', ''); + expect(spyCacheRestore).toHaveBeenCalledWith( + [ + join(os.homedir(), '.m2', 'repository'), + join(os.homedir(), '.m2', 'wrapper', 'dists') + ], + expect.any(String) + ); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/pom.xml\n**/.mvn/wrapper/maven-wrapper.properties\n**/.mvn/extensions.xml' ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 7b0733452..4228a6839 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -51973,7 +51973,11 @@ const supportedPackageManager = [ (0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists') ], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven - pattern: ['**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties'] + pattern: [ + '**/pom.xml', + '**/.mvn/wrapper/maven-wrapper.properties', + '**/.mvn/extensions.xml' + ] }, { id: 'gradle', diff --git a/dist/setup/index.js b/dist/setup/index.js index 50e9c3e05..b1d565c06 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -77838,7 +77838,11 @@ const supportedPackageManager = [ (0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists') ], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven - pattern: ['**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties'] + pattern: [ + '**/pom.xml', + '**/.mvn/wrapper/maven-wrapper.properties', + '**/.mvn/extensions.xml' + ] }, { id: 'gradle', diff --git a/src/cache.ts b/src/cache.ts index f91f90c74..42c086c13 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -28,7 +28,11 @@ const supportedPackageManager: PackageManager[] = [ join(os.homedir(), '.m2', 'wrapper', 'dists') ], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven - pattern: ['**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties'] + pattern: [ + '**/pom.xml', + '**/.mvn/wrapper/maven-wrapper.properties', + '**/.mvn/extensions.xml' + ] }, { id: 'gradle', From e2863ad49937c063e5a23922d1971a105f4f0140 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 8 Jul 2026 09:46:49 -0400 Subject: [PATCH 48/57] Map Zulu x86 architecture to i686 for Azul Metadata API (#1079) The Azul Metadata API's `arch=x86` returns both 32-bit (i686) and 64-bit (x64) packages. Because the two variants share identical java_version and distro_version, setup-java cannot distinguish them and may resolve an explicit `architecture: x86` request to a 64-bit JDK (and for Java 21+, where 32-bit is dropped, x86 silently returns x64 instead of failing). The legacy Zulu Discovery API used `arch=x86&hw_bitness=32` to target only 32-bit builds. The Metadata API exposes the equivalent via `arch=i686`, which returns only genuine 32-bit builds with full version parity to the old behavior. Map x86 -> i686 to restore correct 32-bit resolution. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 8a3e8157a1d49c9366f4fe872db095ab790eb3b0) --- __tests__/distributors/zulu-installer.test.ts | 6 +++--- __tests__/distributors/zulu-linux-installer.test.ts | 6 +++--- __tests__/distributors/zulu-windows-installer.test.ts | 6 +++--- src/distributions/zulu/installer.ts | 6 +++++- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/__tests__/distributors/zulu-installer.test.ts b/__tests__/distributors/zulu-installer.test.ts index 16f4c63f2..25f11be08 100644 --- a/__tests__/distributors/zulu-installer.test.ts +++ b/__tests__/distributors/zulu-installer.test.ts @@ -45,7 +45,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ga&availability_types=ca&page=1&page_size=100' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -54,7 +54,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ea&availability_types=ca&page=1&page_size=100' + '?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ea&availability_types=ca&page=1&page_size=100' ], [ { @@ -183,7 +183,7 @@ describe('getAvailableVersions', () => { describe('getArchitectureOptions', () => { it.each([ [{architecture: 'x64'}, 'x64'], - [{architecture: 'x86'}, 'x86'], + [{architecture: 'x86'}, 'i686'], [{architecture: 'aarch64'}, 'aarch64'], [{architecture: 'arm64'}, 'aarch64'], [{architecture: 'arm'}, 'arm'] diff --git a/__tests__/distributors/zulu-linux-installer.test.ts b/__tests__/distributors/zulu-linux-installer.test.ts index 470de81ef..6caed3f47 100644 --- a/__tests__/distributors/zulu-linux-installer.test.ts +++ b/__tests__/distributors/zulu-linux-installer.test.ts @@ -46,7 +46,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ga&availability_types=ca&page=1&page_size=100' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -55,7 +55,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ea&availability_types=ca&page=1&page_size=100' + '?os=linux_glibc&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ea&availability_types=ca&page=1&page_size=100' ], [ { @@ -186,7 +186,7 @@ describe('getAvailableVersions', () => { describe('getArchitectureOptions', () => { it.each([ [{architecture: 'x64'}, 'x64'], - [{architecture: 'x86'}, 'x86'], + [{architecture: 'x86'}, 'i686'], [{architecture: 'aarch64'}, 'aarch64'], [{architecture: 'arm64'}, 'aarch64'], [{architecture: 'arm'}, 'arm'] diff --git a/__tests__/distributors/zulu-windows-installer.test.ts b/__tests__/distributors/zulu-windows-installer.test.ts index a54f55fe1..0d3fdb933 100644 --- a/__tests__/distributors/zulu-windows-installer.test.ts +++ b/__tests__/distributors/zulu-windows-installer.test.ts @@ -46,7 +46,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ga&availability_types=ca&page=1&page_size=100' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ga&availability_types=ca&page=1&page_size=100' ], [ { @@ -55,7 +55,7 @@ describe('getAvailableVersions', () => { packageType: 'jdk', checkLatest: false }, - '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x86&release_status=ea&availability_types=ca&page=1&page_size=100' + '?os=windows&archive_type=zip&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ea&availability_types=ca&page=1&page_size=100' ], [ { @@ -184,7 +184,7 @@ describe('getAvailableVersions', () => { describe('getArchitectureOptions', () => { it.each([ [{architecture: 'x64'}, 'x64'], - [{architecture: 'x86'}, 'x86'], + [{architecture: 'x86'}, 'i686'], [{architecture: 'aarch64'}, 'aarch64'], [{architecture: 'arm64'}, 'aarch64'], [{architecture: 'arm'}, 'arm'] diff --git a/src/distributions/zulu/installer.ts b/src/distributions/zulu/installer.ts index caf039105..9ae3ec615 100644 --- a/src/distributions/zulu/installer.ts +++ b/src/distributions/zulu/installer.ts @@ -185,7 +185,11 @@ export class ZuluDistribution extends JavaBase { case 'x64': return 'x64'; case 'x86': - return 'x86'; + // The Azul Metadata API's "x86" value returns both 32-bit (i686) and + // 64-bit (x64) packages, which are indistinguishable by version and + // would let a 32-bit request resolve to a 64-bit JDK. Use "i686" to + // target only genuine 32-bit builds, matching the legacy API behavior. + return 'i686'; case 'aarch64': case 'arm64': return 'aarch64'; From f45cd82b67042e9e5c24cef950ea0c61736241c6 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 8 Jul 2026 12:44:58 -0400 Subject: [PATCH 49/57] Rename jdkFile input to jdk-file with deprecated alias (#1083) * Rename jdkFile input to jdk-file with deprecated alias Add a standardized `jdk-file` input to match the lowercase-dash naming used by every other action input. The camelCase `jdkFile` input is kept as a deprecated alias: it still works, but emits a deprecation warning and may be removed in a future release. `jdk-file` takes precedence when both are provided. Updates docs and the local-file e2e workflow (one case intentionally keeps using the deprecated alias for coverage) and regenerates the dist bundles. Fixes #1077 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: keep jdkFile in switching-to-v2 guide The switching-to-v2 migration guide uses actions/setup-java@v2, which only supports the camelCase jdkFile input. Keep the new jdk-file spelling in current-version docs only. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 7dbccc66eed5f22216da97c4e9764d613b80642b) --- .github/workflows/e2e-local-file.yml | 5 +++-- README.md | 2 +- action.yml | 6 +++++- docs/advanced-usage.md | 12 ++++++------ src/constants.ts | 3 ++- src/setup-java.ts | 15 ++++++++++++++- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/.github/workflows/e2e-local-file.yml b/.github/workflows/e2e-local-file.yml index 1b9c48641..0a81182e4 100644 --- a/.github/workflows/e2e-local-file.yml +++ b/.github/workflows/e2e-local-file.yml @@ -47,7 +47,7 @@ jobs: id: setup-java with: distribution: 'jdkfile' - jdkFile: ${{ runner.temp }}/${{ env.LocalFilename }} + jdk-file: ${{ runner.temp }}/${{ env.LocalFilename }} java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version @@ -88,7 +88,7 @@ jobs: id: setup-java with: distribution: 'jdkfile' - jdkFile: ${{ runner.temp }}/${{ env.LocalFilename }} + jdk-file: ${{ runner.temp }}/${{ env.LocalFilename }} java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version @@ -129,6 +129,7 @@ jobs: id: setup-java with: distribution: 'jdkfile' + # Intentionally uses the deprecated `jdkFile` alias to keep it covered. jdkFile: ${{ runner.temp }}/${{ env.LocalFilename }} java-version: '11.0.0-ea' architecture: x64 diff --git a/README.md b/README.md index 562f9ff55..f8a0b5727 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ For more details, see the full release notes on the [releases page](https://git - `architecture`: The target architecture of the package. Possible values: `x86`, `x64`, `armv7`, `aarch64`, `ppc64le`. Default value: Derived from the runner machine. - - `jdkFile`: If a use-case requires a custom distribution setup-java uses the compressed JDK from the location pointed by this input and will take care of the installation and caching on the VM. Note: `distribution` must be set to 'jdkfile' (case-sensitive; all lowercase) when using this option. + - `jdk-file`: If a use-case requires a custom distribution setup-java uses the compressed JDK from the location pointed by this input and will take care of the installation and caching on the VM. Note: `distribution` must be set to 'jdkfile' (case-sensitive; all lowercase) when using this option. (The camelCase `jdkFile` input is still accepted as a deprecated alias and may be removed in a future release.) - `check-latest`: Setting this option makes the action to check for the latest available version for the version spec. diff --git a/action.yml b/action.yml index f4747c1a4..781c82456 100644 --- a/action.yml +++ b/action.yml @@ -19,9 +19,13 @@ inputs: architecture: description: "The architecture of the package (defaults to the action runner's architecture)" required: false - jdkFile: + jdk-file: description: 'Path to where the compressed JDK is located' required: false + jdkFile: + description: 'Deprecated alias for `jdk-file`. Path to where the compressed JDK is located. Use `jdk-file` instead; this alias may be removed in a future release.' + required: false + deprecationMessage: 'The `jdkFile` input is deprecated. Use `jdk-file` instead.' check-latest: description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec' required: false diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index c46db1e78..1df02a608 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -330,7 +330,7 @@ In this example, `JAVA_HOME` and `java` on `PATH` point to Java 17, while Java 2 If your use-case requires a custom distribution or a version that is not provided by setup-java, you can download it manually and setup-java will take care of the installation and caching on the VM: > [!NOTE] -> This approach also lets you use builds that setup-java does not provide directly, such as **Early Access (EA)** or other unreleased JDK builds (for example, an upcoming feature release or a Loom/Valhalla preview build). Download the desired archive in a prior step and point `jdkFile` at it; setup-java will extract, install, and cache it just like a supported distribution. When targeting multiple architectures, select the correct binary per architecture in your workflow (for example, with a build matrix). +> This approach also lets you use builds that setup-java does not provide directly, such as **Early Access (EA)** or other unreleased JDK builds (for example, an upcoming feature release or a Loom/Valhalla preview build). Download the desired archive in a prior step and point `jdk-file` at it; setup-java will extract, install, and cache it just like a supported distribution. When targeting multiple architectures, select the correct binary per architecture in your workflow (for example, with a build matrix). ```yaml steps: @@ -340,7 +340,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'jdkfile' - jdkFile: ${{ runner.temp }}/java_package.tar.gz + jdk-file: ${{ runner.temp }}/java_package.tar.gz java-version: '11.0.0' architecture: x64 @@ -357,7 +357,7 @@ steps: - uses: actions/setup-java@v5 with: distribution: 'jdkfile' - jdkFile: ${{ runner.temp }}/java_package.tar.gz + jdk-file: ${{ runner.temp }}/java_package.tar.gz java-version: '25.0.0-ea.36' architecture: x64 @@ -383,7 +383,7 @@ If your use-case requires a custom distribution (in the example, alpine-linux is - uses: actions/setup-java@v5 with: distribution: 'jdkfile' - jdkFile: ${{ runner.temp }}/java_package.tar.gz + jdk-file: ${{ runner.temp }}/java_package.tar.gz java-version: {{ steps.fetch_latest_jdk.outputs.java_version }} architecture: x64 - run: java --version @@ -708,7 +708,7 @@ The result is a Toolchain with entries for JDKs 8, 11 and 15. You can even combi - uses: actions/setup-java@v5 with: distribution: 'jdkfile' - jdkFile: ${{ runner.temp }}/java_package.tar.gz + jdk-file: ${{ runner.temp }}/java_package.tar.gz java-version: '1.6' architecture: x64 ``` @@ -725,7 +725,7 @@ Each JDK provider will receive a default `vendor` using the `distribution` input - uses: actions/setup-java@v5 with: distribution: 'jdkfile' - jdkFile: ${{ runner.temp }}/java_package.tar.gz + jdk-file: ${{ runner.temp }}/java_package.tar.gz java-version: '1.6' architecture: x64 mvn-toolchain-vendor: 'Oracle' diff --git a/src/constants.ts b/src/constants.ts index 67a5d7ea6..2f7362b05 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,7 +4,8 @@ export const INPUT_JAVA_VERSION_FILE = 'java-version-file'; export const INPUT_ARCHITECTURE = 'architecture'; export const INPUT_JAVA_PACKAGE = 'java-package'; export const INPUT_DISTRIBUTION = 'distribution'; -export const INPUT_JDK_FILE = 'jdkFile'; +export const INPUT_JDK_FILE = 'jdk-file'; +export const INPUT_JDK_FILE_DEPRECATED = 'jdkFile'; export const INPUT_CHECK_LATEST = 'check-latest'; export const INPUT_SET_DEFAULT = 'set-default'; export const INPUT_VERIFY_SIGNATURE = 'verify-signature'; diff --git a/src/setup-java.ts b/src/setup-java.ts index 6ecb49011..1d12b2906 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -21,7 +21,7 @@ async function run() { const versionFile = core.getInput(constants.INPUT_JAVA_VERSION_FILE); const architecture = core.getInput(constants.INPUT_ARCHITECTURE); const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE); - const jdkFile = core.getInput(constants.INPUT_JDK_FILE); + const jdkFile = getJdkFileInput(); const cache = core.getInput(constants.INPUT_CACHE); const cacheDependencyPath = core.getInput( constants.INPUT_CACHE_DEPENDENCY_PATH @@ -128,6 +128,19 @@ async function run() { run(); +function getJdkFileInput(): string { + const jdkFile = core.getInput(constants.INPUT_JDK_FILE); + const deprecatedJdkFile = core.getInput(constants.INPUT_JDK_FILE_DEPRECATED); + + if (deprecatedJdkFile) { + core.warning( + `The '${constants.INPUT_JDK_FILE_DEPRECATED}' input is deprecated and may be removed in a future release. Please use '${constants.INPUT_JDK_FILE}' instead.` + ); + } + + return jdkFile || deprecatedJdkFile; +} + async function installVersion( version: string, options: installerInputsOptions, From 0173e6dd1b6e53ac3f6d68d220fa24cce79ae77c Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 8 Jul 2026 12:57:52 -0400 Subject: [PATCH 50/57] Infer distribution from asdf .tool-versions vendor prefix (#1084) * Infer distribution from asdf .tool-versions vendor prefix asdf-java encodes the JDK vendor as a prefix on the version string in .tool-versions (e.g. `java temurin-17.0.3+7`). Capture that prefix and map it to a setup-java distribution, mirroring the existing .sdkmanrc behavior. Unknown prefixes warn and fall back to the distribution input. Fixes #1081 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> (cherry picked from commit 4db08ef6bf1fe5256ab8252dec94d7b4d5f8d42f) --- __tests__/util.test.ts | 66 ++++++++++++++++++++++++++++++++++++++++++ docs/advanced-usage.md | 35 +++++++++++++++++++++- src/util.ts | 58 ++++++++++++++++++++++++++++++++++++- 3 files changed, 157 insertions(+), 2 deletions(-) diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 6782e6870..7a3515b60 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -243,6 +243,72 @@ describe('getVersionFromFileContent', () => { ); }); }); + + describe('.tool-versions', () => { + it.each([ + ['java temurin-17.0.3+7', '17.0.3+7', 'temurin'], + ['java temurin-jre-17.0.3+7', '17.0.3+7', 'temurin'], + ['java adoptopenjdk-11.0.16+8', '11.0.16+8', 'temurin'], + ['java adoptopenjdk-openj9-11.0.16+8', '11.0.16+8', 'temurin'], + ['java zulu-11.56.19', '11.56.19', 'zulu'], + ['java corretto-17.0.13.11.1', '17', 'corretto'], // corretto -> major only + ['java liberica-11.0.15+10', '11.0.15+10', 'liberica'], + ['java microsoft-11.0.13.8.1', '11.0.13', 'microsoft'], + ['java semeru-openj9-11.0.25+9', '11.0.25+9', 'semeru'], + ['java ibm-openj9-11.0.25+9', '11.0.25+9', 'semeru'], + ['java dragonwell-17.0.13.0.13+11', '17.0.13', 'dragonwell'], + ['java graalvm-22.3.0+java17', '22.3.0+java17', 'graalvm'], + ['java graalvm-community-22.3.0', '22.3.0', 'graalvm-community'], + ['java oracle-graalvm-21.0.5', '21.0.5', 'graalvm'], + ['java oracle-21.0.5', '21.0.5', 'oracle'], + ['java sapmachine-21.0.5', '21.0.5', 'sapmachine'], + ['java kona-17.0.13', '17.0.13', 'kona'], + ['java jetbrains-21.0.5', '21.0.5', 'jetbrains'] + ])( + 'parsing %s should return version %s and distribution %s', + (content: string, expectedVersion: string, expectedDist: string) => { + const actual = getVersionFromFileContent( + content, + 'openjdk', + '.tool-versions' + ); + expect(actual?.version).toBe(expectedVersion); + expect(actual?.distribution).toBe(expectedDist); + } + ); + + it.each([ + ['java 17.0.7', '17.0.7'], + ['java 17', '17'], + ['java 1.8', '8'], + ['java 21-ea', '21-ea'] + ])( + 'parsing prefix-less %s should return version %s and no distribution', + (content: string, expectedVersion: string) => { + const actual = getVersionFromFileContent( + content, + 'temurin', + '.tool-versions' + ); + expect(actual?.version).toBe(expectedVersion); + expect(actual?.distribution).toBeUndefined(); + } + ); + + it('should warn and return undefined distribution for unsupported vendor', () => { + const warnSpy = jest.spyOn(core, 'warning'); + const actual = getVersionFromFileContent( + 'java openjdk-17.0.7', + 'temurin', + '.tool-versions' + ); + expect(actual?.version).toBe('17.0.7'); + expect(actual?.distribution).toBeUndefined(); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('Unknown asdf distribution identifier') + ); + }); + }); }); describe('isGhes', () => { diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 1df02a608..b2b87eb35 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -780,7 +780,27 @@ steps: Supported files are `.java-version`, `.tool-versions` and `.sdkmanrc`. * In `.java-version` file, only the version should be specified (e.g., 17.0.7). The `.java-version` file recognizes all variants of the version description according to [jenv](https://github.com/jenv/jenv). - * In `.tool-versions` file, java version should be preceded by the java keyword (e.g., java 17.0.7). The `.tool-versions` file supports version specifications in accordance with [asdf](https://github.com/asdf-vm/asdf) standards, adhering to Semantic Versioning ([semver](https://semver.org/)). + * In `.tool-versions` file, java version should be preceded by the java keyword (e.g., java 17.0.7). The `.tool-versions` file supports version specifications in accordance with [asdf](https://github.com/asdf-vm/asdf) standards, adhering to Semantic Versioning ([semver](https://semver.org/)). When the entry includes an [asdf-java](https://github.com/halcyon/asdf-java) vendor prefix (e.g. `java temurin-17.0.3+7`), setup-java can infer the `distribution` input automatically. Unrecognized vendor prefixes require setting `distribution` explicitly. + + Supported asdf-java vendor prefix mappings (packaging variants such as `-jre`, `-musl`, `-openj9`, `-crac`, `-javafx` are collapsed onto the base vendor): + + | asdf-java vendor prefix | setup-java distribution | + | ----------------------- | ----------------------- | + | `temurin` | `temurin` | + | `adoptopenjdk` | `temurin` | + | `zulu` | `zulu` | + | `corretto` | `corretto` | + | `liberica` | `liberica` | + | `microsoft` | `microsoft` | + | `semeru`, `ibm` | `semeru` | + | `dragonwell` | `dragonwell` | + | `graalvm`, `oracle-graalvm` | `graalvm` | + | `graalvm-community` | `graalvm-community` | + | `oracle` | `oracle` | + | `sapmachine` | `sapmachine` | + | `kona` | `kona` | + | `jetbrains` | `jetbrains` | + * In `.sdkmanrc` file, java version should be preceded by the `java=` prefix (e.g., `java=17.0.7-tem`). When a recognized SDKMAN distribution suffix is present, setup-java can infer the `distribution` input automatically. Unrecognized suffixes require setting `distribution` explicitly. The `.sdkmanrc` file supports version specifications in accordance with [file format](https://sdkman.io/usage#env-command), see [Sdkman! documentation](https://sdkman.io/jdks) for more information. Supported SDKMAN suffix mappings: @@ -816,6 +836,19 @@ steps: java=17.0.7-tem ``` +**Example step using `asdf`** (distribution inferred from `.tool-versions`): +```yml + - name: Setup java + uses: actions/setup-java@v5 + with: + java-version-file: '.tool-versions' +``` + +**Example `.tool-versions`**: +``` +java temurin-17.0.7+7 +``` + Valid entry options (does not apply to `.sdkmanrc`): ``` major versions: 8, 11, 16, 17, 21 diff --git a/src/util.ts b/src/util.ts index 32cb6bc16..22121ee58 100644 --- a/src/util.ts +++ b/src/util.ts @@ -146,8 +146,10 @@ export function getVersionFromFileContent( const versionFileName = getFileName(versionFile); if (versionFileName == '.tool-versions') { + // Capture an optional asdf-java vendor prefix (e.g. `temurin-`, `corretto-`) + // in the `distribution` group so it can be mapped to a setup-java distribution. javaVersionRegExp = - /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; + /^java\s+(?:(?\S*)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { // Match both version and optional distribution identifier javaVersionRegExp = @@ -170,6 +172,17 @@ export function getVersionFromFileContent( ); } + // Extract distribution from asdf .tool-versions file + if (versionFileName == '.tool-versions' && match?.groups?.distribution) { + const asdfDist = match.groups.distribution; + extractedDistribution = mapAsdfDistribution(asdfDist); + if (extractedDistribution) { + core.debug( + `Parsed distribution '${extractedDistribution}' from asdf identifier '${asdfDist}'` + ); + } + } + core.debug( `Parsed version '${capturedVersion}' from file '${versionFileName}'` ); @@ -235,6 +248,49 @@ function mapSdkmanDistribution(sdkmanDist: string): string | undefined { return mapped; } +// Map asdf-java (.tool-versions) vendor identifiers to setup-java distribution names. +// asdf-java encodes the vendor as a prefix on the version string, e.g. +// `java temurin-17.0.3+7` or `java semeru-openj9-11.0.25+9`. Packaging variants +// (`-jre`, `-musl`, `-openj9`, `-crac`, `-javafx`, ...) are collapsed onto the +// base vendor since setup-java does not distinguish them here. +function mapAsdfDistribution(asdfDist: string): string | undefined { + const normalized = asdfDist.toLowerCase(); + + // Multi-segment vendors that map to a distinct setup-java distribution. + if (normalized.startsWith('graalvm-community')) { + return 'graalvm-community'; + } + if (normalized.startsWith('oracle-graalvm')) { + return 'graalvm'; + } + + const baseVendor = normalized.split('-')[0]; + const distributionMap: Record = { + temurin: 'temurin', + adoptopenjdk: 'temurin', + zulu: 'zulu', + corretto: 'corretto', + liberica: 'liberica', + microsoft: 'microsoft', + semeru: 'semeru', + ibm: 'semeru', + dragonwell: 'dragonwell', + graalvm: 'graalvm', + oracle: 'oracle', + sapmachine: 'sapmachine', + kona: 'kona', + jetbrains: 'jetbrains' + }; + + const mapped = distributionMap[baseVendor]; + if (!mapped) { + core.warning( + `Unknown asdf distribution identifier '${asdfDist}'. Please specify the distribution explicitly.` + ); + } + return mapped; +} + // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content: string): string { return content.startsWith('1.') ? content.substring(2) : content; From bf7b8deac240b9cee05eb15ccdb1d2f424a54b9f Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 8 Jul 2026 13:27:01 -0400 Subject: [PATCH 51/57] build: rebuild dist for backported changes (#1079, #1083, #1084) Regenerate the CommonJS bundle so the compiled dist/ reflects the source changes backported from main, without the ESM migration (#1078). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- dist/cleanup/index.js | 58 ++++++++++++++++++++++++++++++--- dist/setup/index.js | 74 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 12 deletions(-) diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 4228a6839..01fb85055 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52245,14 +52245,15 @@ else { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE_DEPRECATED = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; exports.INPUT_ARCHITECTURE = 'architecture'; exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; -exports.INPUT_JDK_FILE = 'jdkFile'; +exports.INPUT_JDK_FILE = 'jdk-file'; +exports.INPUT_JDK_FILE_DEPRECATED = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; exports.INPUT_SET_DEFAULT = 'set-default'; exports.INPUT_VERIFY_SIGNATURE = 'verify-signature'; @@ -52576,7 +52577,7 @@ function isCacheFeatureAvailable() { } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; function getVersionFromFileContent(content, distributionName, versionFile) { - var _a, _b, _c; + var _a, _b, _c, _d; let javaVersionRegExp; let extractedDistribution; function getFileName(versionFile) { @@ -52584,8 +52585,10 @@ function getVersionFromFileContent(content, distributionName, versionFile) { } const versionFileName = getFileName(versionFile); if (versionFileName == '.tool-versions') { + // Capture an optional asdf-java vendor prefix (e.g. `temurin-`, `corretto-`) + // in the `distribution` group so it can be mapped to a setup-java distribution. javaVersionRegExp = - /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; + /^java\s+(?:(?\S*)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { // Match both version and optional distribution identifier @@ -52605,6 +52608,14 @@ function getVersionFromFileContent(content, distributionName, versionFile) { extractedDistribution = mapSdkmanDistribution(sdkmanDist); core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); } + // Extract distribution from asdf .tool-versions file + if (versionFileName == '.tool-versions' && ((_c = match === null || match === void 0 ? void 0 : match.groups) === null || _c === void 0 ? void 0 : _c.distribution)) { + const asdfDist = match.groups.distribution; + extractedDistribution = mapAsdfDistribution(asdfDist); + if (extractedDistribution) { + core.debug(`Parsed distribution '${extractedDistribution}' from asdf identifier '${asdfDist}'`); + } + } core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); if (!capturedVersion) { return null; @@ -52621,7 +52632,7 @@ function getVersionFromFileContent(content, distributionName, versionFile) { // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution // (either explicitly provided or extracted from the version file) is in the list. if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { - const coerceVersion = (_c = semver.coerce(version)) !== null && _c !== void 0 ? _c : version; + const coerceVersion = (_d = semver.coerce(version)) !== null && _d !== void 0 ? _d : version; version = semver.major(coerceVersion).toString(); } return { @@ -52654,6 +52665,43 @@ function mapSdkmanDistribution(sdkmanDist) { } return mapped; } +// Map asdf-java (.tool-versions) vendor identifiers to setup-java distribution names. +// asdf-java encodes the vendor as a prefix on the version string, e.g. +// `java temurin-17.0.3+7` or `java semeru-openj9-11.0.25+9`. Packaging variants +// (`-jre`, `-musl`, `-openj9`, `-crac`, `-javafx`, ...) are collapsed onto the +// base vendor since setup-java does not distinguish them here. +function mapAsdfDistribution(asdfDist) { + const normalized = asdfDist.toLowerCase(); + // Multi-segment vendors that map to a distinct setup-java distribution. + if (normalized.startsWith('graalvm-community')) { + return 'graalvm-community'; + } + if (normalized.startsWith('oracle-graalvm')) { + return 'graalvm'; + } + const baseVendor = normalized.split('-')[0]; + const distributionMap = { + temurin: 'temurin', + adoptopenjdk: 'temurin', + zulu: 'zulu', + corretto: 'corretto', + liberica: 'liberica', + microsoft: 'microsoft', + semeru: 'semeru', + ibm: 'semeru', + dragonwell: 'dragonwell', + graalvm: 'graalvm', + oracle: 'oracle', + sapmachine: 'sapmachine', + kona: 'kona', + jetbrains: 'jetbrains' + }; + const mapped = distributionMap[baseVendor]; + if (!mapped) { + core.warning(`Unknown asdf distribution identifier '${asdfDist}'. Please specify the distribution explicitly.`); + } + return mapped; +} // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content) { return content.startsWith('1.') ? content.substring(2) : content; diff --git a/dist/setup/index.js b/dist/setup/index.js index b1d565c06..30395de2d 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78005,14 +78005,15 @@ function isProbablyGradleDaemonProblem(packageManager, error) { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE_DEPRECATED = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; exports.INPUT_ARCHITECTURE = 'architecture'; exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; -exports.INPUT_JDK_FILE = 'jdkFile'; +exports.INPUT_JDK_FILE = 'jdk-file'; +exports.INPUT_JDK_FILE_DEPRECATED = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; exports.INPUT_SET_DEFAULT = 'set-default'; exports.INPUT_VERIFY_SIGNATURE = 'verify-signature'; @@ -81272,7 +81273,11 @@ class ZuluDistribution extends base_installer_1.JavaBase { case 'x64': return 'x64'; case 'x86': - return 'x86'; + // The Azul Metadata API's "x86" value returns both 32-bit (i686) and + // 64-bit (x64) packages, which are indistinguishable by version and + // would let a 32-bit request resolve to a 64-bit JDK. Use "i686" to + // target only genuine 32-bit builds, matching the legacy API behavior. + return 'i686'; case 'aarch64': case 'arm64': return 'aarch64'; @@ -81583,7 +81588,7 @@ function run() { const versionFile = core.getInput(constants.INPUT_JAVA_VERSION_FILE); const architecture = core.getInput(constants.INPUT_ARCHITECTURE); const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE); - const jdkFile = core.getInput(constants.INPUT_JDK_FILE); + const jdkFile = getJdkFileInput(); const cache = core.getInput(constants.INPUT_CACHE); const cacheDependencyPath = core.getInput(constants.INPUT_CACHE_DEPENDENCY_PATH); const checkLatest = (0, util_1.getBooleanInput)(constants.INPUT_CHECK_LATEST, false); @@ -81662,6 +81667,14 @@ function run() { }); } run(); +function getJdkFileInput() { + const jdkFile = core.getInput(constants.INPUT_JDK_FILE); + const deprecatedJdkFile = core.getInput(constants.INPUT_JDK_FILE_DEPRECATED); + if (deprecatedJdkFile) { + core.warning(`The '${constants.INPUT_JDK_FILE_DEPRECATED}' input is deprecated and may be removed in a future release. Please use '${constants.INPUT_JDK_FILE}' instead.`); + } + return jdkFile || deprecatedJdkFile; +} function installVersion(version, options, toolchainId = 0) { return __awaiter(this, void 0, void 0, function* () { const { distributionName, jdkFile, architecture, packageType, checkLatest, setDefault, verifySignature, verifySignaturePublicKey, toolchainIds } = options; @@ -82027,7 +82040,7 @@ function isCacheFeatureAvailable() { } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; function getVersionFromFileContent(content, distributionName, versionFile) { - var _a, _b, _c; + var _a, _b, _c, _d; let javaVersionRegExp; let extractedDistribution; function getFileName(versionFile) { @@ -82035,8 +82048,10 @@ function getVersionFromFileContent(content, distributionName, versionFile) { } const versionFileName = getFileName(versionFile); if (versionFileName == '.tool-versions') { + // Capture an optional asdf-java vendor prefix (e.g. `temurin-`, `corretto-`) + // in the `distribution` group so it can be mapped to a setup-java distribution. javaVersionRegExp = - /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; + /^java\s+(?:(?\S*)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { // Match both version and optional distribution identifier @@ -82056,6 +82071,14 @@ function getVersionFromFileContent(content, distributionName, versionFile) { extractedDistribution = mapSdkmanDistribution(sdkmanDist); core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); } + // Extract distribution from asdf .tool-versions file + if (versionFileName == '.tool-versions' && ((_c = match === null || match === void 0 ? void 0 : match.groups) === null || _c === void 0 ? void 0 : _c.distribution)) { + const asdfDist = match.groups.distribution; + extractedDistribution = mapAsdfDistribution(asdfDist); + if (extractedDistribution) { + core.debug(`Parsed distribution '${extractedDistribution}' from asdf identifier '${asdfDist}'`); + } + } core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); if (!capturedVersion) { return null; @@ -82072,7 +82095,7 @@ function getVersionFromFileContent(content, distributionName, versionFile) { // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution // (either explicitly provided or extracted from the version file) is in the list. if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { - const coerceVersion = (_c = semver.coerce(version)) !== null && _c !== void 0 ? _c : version; + const coerceVersion = (_d = semver.coerce(version)) !== null && _d !== void 0 ? _d : version; version = semver.major(coerceVersion).toString(); } return { @@ -82105,6 +82128,43 @@ function mapSdkmanDistribution(sdkmanDist) { } return mapped; } +// Map asdf-java (.tool-versions) vendor identifiers to setup-java distribution names. +// asdf-java encodes the vendor as a prefix on the version string, e.g. +// `java temurin-17.0.3+7` or `java semeru-openj9-11.0.25+9`. Packaging variants +// (`-jre`, `-musl`, `-openj9`, `-crac`, `-javafx`, ...) are collapsed onto the +// base vendor since setup-java does not distinguish them here. +function mapAsdfDistribution(asdfDist) { + const normalized = asdfDist.toLowerCase(); + // Multi-segment vendors that map to a distinct setup-java distribution. + if (normalized.startsWith('graalvm-community')) { + return 'graalvm-community'; + } + if (normalized.startsWith('oracle-graalvm')) { + return 'graalvm'; + } + const baseVendor = normalized.split('-')[0]; + const distributionMap = { + temurin: 'temurin', + adoptopenjdk: 'temurin', + zulu: 'zulu', + corretto: 'corretto', + liberica: 'liberica', + microsoft: 'microsoft', + semeru: 'semeru', + ibm: 'semeru', + dragonwell: 'dragonwell', + graalvm: 'graalvm', + oracle: 'oracle', + sapmachine: 'sapmachine', + kona: 'kona', + jetbrains: 'jetbrains' + }; + const mapped = distributionMap[baseVendor]; + if (!mapped) { + core.warning(`Unknown asdf distribution identifier '${asdfDist}'. Please specify the distribution explicitly.`); + } + return mapped; +} // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content) { return content.startsWith('1.') ? content.substring(2) : content; From 176156a187714aaf460b0a3c8f21e8b4f784b978 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 8 Jul 2026 13:29:18 -0400 Subject: [PATCH 52/57] chore: bump version to 5.6.0 for v5 release line Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e94683d4e..c5938e234 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "setup-java", - "version": "5.3.0", + "version": "5.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "setup-java", - "version": "5.3.0", + "version": "5.6.0", "license": "MIT", "dependencies": { "@actions/cache": "^5.1.0", diff --git a/package.json b/package.json index e235cb95c..6f5d50247 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "setup-java", - "version": "5.3.0", + "version": "5.6.0", "private": true, "description": "setup java action", "main": "dist/setup/index.js", From 62df799a9c6e3022bb466697c66c36e9a2dbf347 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 8 Jul 2026 14:27:50 -0400 Subject: [PATCH 53/57] Add Maven compiler problem matcher for javac diagnostics (#1087) The javac problem matcher only recognized javac's native diagnostic format (File.java:12: warning: msg), which works for plain javac and Gradle but not Maven. The maven-compiler-plugin reformats diagnostics to [WARNING] /path/File.java:[12,5] msg, so Maven builds produced zero annotations. Add a new maven-javac matcher owner that recognizes the Maven format, capturing severity, file, line, column, and message. Maven builds now annotate consistently with Gradle and plain javac. Fixes: #1085 (cherry picked from commit cf9e5e7084bdc49e1fe01d5fbcf394839a954eb3) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/java.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/java.json b/.github/java.json index baf35254e..5e52ab819 100644 --- a/.github/java.json +++ b/.github/java.json @@ -21,6 +21,19 @@ "message": 4 } ] + }, + { + "owner": "maven-javac", + "pattern": [ + { + "regexp": "^\\[(WARNING|ERROR)\\]\\s+(.+?\\.java):\\[(\\d+),(\\d+)\\]\\s+(.+)$", + "severity": 1, + "file": 2, + "line": 3, + "column": 4, + "message": 5 + } + ] } ] } From 513edc4f8710565e4ad696f3b7d8e3bda584a46c Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 8 Jul 2026 16:04:08 -0400 Subject: [PATCH 54/57] feat: expose cache-primary-key output (#597) [v5 backport] (#1089) * feat: expose cache-primary-key output (#597) Backport of the cache-primary-key output to the v5 release line. Expose the primary cache key computed by the caching logic as a new `cache-primary-key` action output, so workflows can compose the built-in setup-java cache key with actions/cache or actions/cache/restore across steps and dependent jobs. - src/cache.ts: set the `cache-primary-key` output in restore() - action.yml: declare the new output - README.md: document the new output - __tests__/cache.test.ts: assert the output is set - dist: rebuild (CommonJS) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Apply PR #1088 review suggestions to v5 backport Port the review feedback from the main-line PR (#1088) into the v5 backport: - src/cache.ts: use the STATE_CACHE_PRIMARY_KEY constant for the output name instead of a duplicated string literal - action.yml / README.md: clarify that the output is also empty when caching is skipped (e.g. the cache service is unavailable) - dist: rebuild Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 2 ++ __tests__/cache.test.ts | 14 ++++++++++++++ action.yml | 2 ++ dist/cleanup/index.js | 1 + dist/setup/index.js | 1 + src/cache.ts | 1 + 6 files changed, 21 insertions(+) diff --git a/README.md b/README.md index f8a0b5727..24ffb3394 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,8 @@ When the option `cache-dependency-path` is specified, the hash is based on the m The workflow output `cache-hit` is set to indicate if an exact match was found for the key [as actions/cache does](https://github.com/actions/cache/tree/main#outputs). +The workflow output `cache-primary-key` exposes the primary cache key computed by the action for the configured build tool. It is useful for composing with [`actions/cache`](https://github.com/actions/cache) or [`actions/cache/restore`](https://github.com/actions/cache/tree/main/restore) in later steps or dependent jobs that need to reuse the exact same key. It is empty when caching is not enabled or when caching is skipped (for example, when the cache service is unavailable). + The cache input is optional, and caching is turned off by default. **Maven Wrapper:** when `cache: 'maven'` is enabled, the action also caches and restores the Maven Wrapper distribution downloaded to `~/.m2/wrapper/dists` (in addition to the local repository), so wrapper-based (`./mvnw`) builds don't re-download the wrapper on every run. This is keyed on `**/.mvn/wrapper/maven-wrapper.properties` as shown above. diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index 690568706..90f68efe1 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -78,6 +78,10 @@ describe('dependency cache', () => { ReturnType, Parameters >; + let spySetOutput: jest.SpyInstance< + ReturnType, + Parameters + >; beforeEach(() => { spyCacheRestore = jest @@ -86,6 +90,7 @@ describe('dependency cache', () => { Promise.resolve(undefined) ); spyGlobHashFiles = jest.spyOn(glob, 'hashFiles'); + spySetOutput = jest.spyOn(core, 'setOutput').mockImplementation(() => {}); spyWarning.mockImplementation(() => null); }); @@ -120,6 +125,15 @@ describe('dependency cache', () => { expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); }); + it('sets the cache-primary-key output', async () => { + createFile(join(workspace, 'pom.xml')); + + await restore('maven', ''); + expect(spySetOutput).toHaveBeenCalledWith( + 'cache-primary-key', + expect.stringContaining('setup-java-') + ); + }); it('downloads cache based on maven-wrapper.properties', async () => { createDirectory(join(workspace, '.mvn')); createDirectory(join(workspace, '.mvn', 'wrapper')); diff --git a/action.yml b/action.yml index 781c82456..5237f1a8f 100644 --- a/action.yml +++ b/action.yml @@ -103,6 +103,8 @@ outputs: description: 'Path to where the java environment has been installed (same as $JAVA_HOME)' cache-hit: description: 'A boolean value to indicate an exact match was found for the primary key' + cache-primary-key: + description: 'The primary cache key computed by the action for the configured build tool. Empty when caching is not enabled or when caching is skipped (e.g. cache service unavailable). Useful for composing with actions/cache or actions/cache/restore across jobs.' runs: using: 'node24' main: 'dist/setup/index.js' diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 01fb85055..1154a0bc6 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52056,6 +52056,7 @@ function restore(id, cacheDependencyPath) { const primaryKey = yield computeCacheKey(packageManager, cacheDependencyPath); core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); + core.setOutput(STATE_CACHE_PRIMARY_KEY, primaryKey); // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) const matchedKey = yield cache.restoreCache(packageManager.path, primaryKey); if (matchedKey) { diff --git a/dist/setup/index.js b/dist/setup/index.js index 30395de2d..7b098a526 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -77921,6 +77921,7 @@ function restore(id, cacheDependencyPath) { const primaryKey = yield computeCacheKey(packageManager, cacheDependencyPath); core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); + core.setOutput(STATE_CACHE_PRIMARY_KEY, primaryKey); // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) const matchedKey = yield cache.restoreCache(packageManager.path, primaryKey); if (matchedKey) { diff --git a/src/cache.ts b/src/cache.ts index 42c086c13..263cc0d8a 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -118,6 +118,7 @@ export async function restore(id: string, cacheDependencyPath: string) { const primaryKey = await computeCacheKey(packageManager, cacheDependencyPath); core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); + core.setOutput(STATE_CACHE_PRIMARY_KEY, primaryKey); // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) const matchedKey = await cache.restoreCache(packageManager.path, primaryKey); From bbf0f6967066506f72571a96d5d6c67ca42ab460 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Mon, 13 Jul 2026 14:08:17 -0400 Subject: [PATCH 55/57] dist: Cover Tencent Kona JDK 25 (#1110) - Update Tencent Kona documentation to list JDK 25 as supported - Add Kona 25 to the e2e verification matrix (ubuntu, windows, macos) - Extend Kona test fixture with real Kona 25.0.3 release entries - Add unit tests covering Kona 25 selection across all platforms Signed-off-by: John Jiang Co-authored-by: johnsjiang --- .github/workflows/e2e-versions.yml | 9 +++++ __tests__/data/kona.json | 40 +++++++++++++++++++ __tests__/distributors/kona-installer.test.ts | 37 ++++++++++++++++- docs/advanced-usage.md | 2 +- 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 3080fd702..8a7322b69 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -55,6 +55,15 @@ jobs: - distribution: microsoft os: macos-latest version: 25 + - distribution: kona + os: windows-latest + version: 25 + - distribution: kona + os: ubuntu-latest + version: 25 + - distribution: kona + os: macos-latest + version: 25 - distribution: oracle os: macos-15-intel version: 17 diff --git a/__tests__/data/kona.json b/__tests__/data/kona.json index 1f250ebe6..7dc24c38d 100644 --- a/__tests__/data/kona.json +++ b/__tests__/data/kona.json @@ -158,5 +158,45 @@ } ] } + ], + "25": [ + { + "version": "25.0.3", + "jdkVersion": "25.0.3", + "latest": true, + "baseUrl": "https://github.com/Tencent/TencentKona-25/releases/download/TencentKona-25.0.3/", + "files": [ + { + "os": "linux", + "arch": "aarch64", + "filename": "TencentKona-25.0.3.b1-jdk_linux-aarch64.tar.gz", + "checksum": "2fb77e3ba9c00045ca497ea22210f18dae4bf7df4c87029f5abadd42a5daf8c7" + }, + { + "os": "linux", + "arch": "x86_64", + "filename": "TencentKona-25.0.3.b1-jdk_linux-x86_64.tar.gz", + "checksum": "47445e6fad020e834055a705bb48fa3cd0727a2c57ad6f1c5206a45f4efb2d67" + }, + { + "os": "macos", + "arch": "aarch64", + "filename": "TencentKona-25.0.3.b1_jdk_macosx-aarch64_notarized.tar.gz", + "checksum": "e29405eff95da412ed7ecc890e6ef5ebbfcd9ab334005b3d32d1700bbe4204d2" + }, + { + "os": "macos", + "arch": "x86_64", + "filename": "TencentKona-25.0.3.b1_jdk_macosx-x86_64_notarized.tar.gz", + "checksum": "d512db5c079db16bd3a9c86d9cb979808994e90e4de29e17d27e5219fe488659" + }, + { + "os": "windows", + "arch": "x86_64", + "filename": "TencentKona-25.0.3.b1_jdk_windows-x86_64_signed.zip", + "checksum": "865bce92ccbbca75115076e618a98baa1d0f30fcccdce954c0a22bee33d6ae83" + } + ] + } ] } diff --git a/__tests__/distributors/kona-installer.test.ts b/__tests__/distributors/kona-installer.test.ts index d90fdd943..2aaaca79d 100644 --- a/__tests__/distributors/kona-installer.test.ts +++ b/__tests__/distributors/kona-installer.test.ts @@ -28,7 +28,9 @@ describe('Check getAvailableReleases', () => { ['11', 'linux', 'x86_64', 'linux-x86_64'], ['11.0.25', 'macos', 'aarch64', 'macosx-aarch64'], ['17.0.13', 'windows', 'x86_64', 'windows-x86_64'], - ['21.0.5', 'linux', 'x86_64', 'linux-x86_64'] + ['21.0.5', 'linux', 'x86_64', 'linux-x86_64'], + ['25', 'linux', 'aarch64', 'linux-aarch64'], + ['25.0.3', 'macos', 'x86_64', 'macosx-x86_64'] ])( 'should get releases with the specified version "%s", OS "%s" and arch "%s"', async ( @@ -41,7 +43,7 @@ describe('Check getAvailableReleases', () => { const releases = await distribution['getAvailableReleases'](); expect(releases).not.toBeNull(); - expect(releases.length).toBe(4); + expect(releases.length).toBe(5); releases.forEach(release => expect(release.downloadUrl).toContain(expectedPattern) ); @@ -173,6 +175,37 @@ describe('Check findPackageForDownload', () => { 'windows', 'x86_64', 'https://github.com/Tencent/TencentKona-21/releases/download/TencentKona-21.0.5/TencentKona-21.0.5.b1_jdk_windows-x86_64_signed.zip' + ], + + [ + '25', + 'linux', + 'aarch64', + 'https://github.com/Tencent/TencentKona-25/releases/download/TencentKona-25.0.3/TencentKona-25.0.3.b1-jdk_linux-aarch64.tar.gz' + ], + [ + '25.0.3', + 'linux', + 'x86_64', + 'https://github.com/Tencent/TencentKona-25/releases/download/TencentKona-25.0.3/TencentKona-25.0.3.b1-jdk_linux-x86_64.tar.gz' + ], + [ + '25.0.3', + 'macos', + 'aarch64', + 'https://github.com/Tencent/TencentKona-25/releases/download/TencentKona-25.0.3/TencentKona-25.0.3.b1_jdk_macosx-aarch64_notarized.tar.gz' + ], + [ + '25.0.3', + 'macos', + 'x86_64', + 'https://github.com/Tencent/TencentKona-25/releases/download/TencentKona-25.0.3/TencentKona-25.0.3.b1_jdk_macosx-x86_64_notarized.tar.gz' + ], + [ + '25.0.3', + 'windows', + 'x86_64', + 'https://github.com/Tencent/TencentKona-25/releases/download/TencentKona-25.0.3/TencentKona-25.0.3.b1_jdk_windows-x86_64_signed.zip' ] ])( 'should return the download URL with the specified version "%s", OS "%s" and arch "%s"', diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index b2b87eb35..633ff45bb 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -237,7 +237,7 @@ The available package types are: - `jre+ft` - JBR (FreeType) ### Tencent Kona -**NOTE:** Tencent Kona supports major versions 8, 11, 17 and 21, and provides jdk only. +**NOTE:** Tencent Kona supports major versions 8, 11, 17, 21 and 25, and provides jdk only. ```yaml steps: From d229d2e858d9137cc0b3f118fa5184b9f0a44ac4 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 14 Jul 2026 14:37:10 -0400 Subject: [PATCH 56/57] Backport #1111: Preserve Maven toolchains across repeated setup-java runs (#1099) (#1113) * Preserve Maven toolchains across repeated setup-java runs (#1099) Backport of #1111 to releases/v5. Toolchain generation was gated behind the `overwrite-settings` input, which is documented to control only regeneration of `settings.xml`. Because `generateToolchainDefinition` already performs a non-destructive merge (existing JDK, custom, and user-managed toolchains are preserved, and only an entry with the same `type` + `provides.id` is replaced), skipping the write when `overwrite-settings: false` caused later setup-java executions to drop toolchain entries registered by earlier runs. Decouple toolchains generation from `overwrite-settings`: the toolchains file is now always written, so consecutive runs accumulate every JDK. `settings.xml` behavior (auth.ts) is unchanged. - src/toolchains.ts: drop overwriteSettings from configureToolchains / createToolchainsSettings / writeToolchainsFileToDisk; always write. - __tests__/toolchains.test.ts: update call sites, rewrite the "does not overwrite" test to assert non-destructive extension, and add a regression test for consecutive configureToolchains executions. - docs/advanced-usage.md: clarify merge is non-destructive and independent of overwrite-settings. - dist/setup/index.js: rebuilt. Fixes #1099 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- __tests__/toolchains.test.ts | 99 ++++++++++++++++++++++++++---------- dist/setup/index.js | 27 +++++----- docs/advanced-usage.md | 2 + src/toolchains.ts | 41 +++++---------- 4 files changed, 101 insertions(+), 68 deletions(-) diff --git a/__tests__/toolchains.test.ts b/__tests__/toolchains.test.ts index 38cac18ca..01b2ca595 100644 --- a/__tests__/toolchains.test.ts +++ b/__tests__/toolchains.test.ts @@ -46,8 +46,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: altHome, - overwriteSettings: true + settingsDirectory: altHome }); expect(fs.existsSync(m2Dir)).toBe(false); @@ -95,8 +94,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -177,8 +175,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -263,8 +260,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -341,8 +337,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -412,8 +407,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -505,8 +499,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -565,8 +558,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -624,8 +616,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -708,8 +699,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -788,8 +778,7 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: true + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); @@ -814,7 +803,7 @@ describe('toolchains tests', () => { ).toEqual(result); }, 100000); - it('does not overwrite existing toolchains.xml files', async () => { + it('extends existing toolchains.xml files instead of overwriting them', async () => { const jdkInfo = { version: '17', vendor: 'Eclipse Temurin', @@ -843,13 +832,20 @@ describe('toolchains tests', () => { await toolchains.createToolchainsSettings({ jdkInfo, - settingsDirectory: m2Dir, - overwriteSettings: false + settingsDirectory: m2Dir }); expect(fs.existsSync(m2Dir)).toBe(true); expect(fs.existsSync(toolchainsFile)).toBe(true); - expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(originalFile); + + const updated = fs.readFileSync(toolchainsFile, 'utf-8'); + // The pre-existing (Sun 1.6) toolchain must be preserved ... + expect(updated).toContain('sun_1.6'); + expect(updated).toContain('/opt/jdk/sun/1.6'); + // ... and the newly installed JDK must be included in the merged result. + expect(updated).toContain('temurin_17'); + expect(updated).toContain('Eclipse Temurin'); + expect(updated).toContain(`${jdkInfo.jdkHome}`); }, 100000); it('generates valid toolchains.xml with minimal configuration', () => { @@ -914,4 +910,55 @@ describe('toolchains tests', () => { ) ); }, 100000); + + it('preserves toolchains from previous executions across multiple setup-java runs', async () => { + // Regression test for https://github.com/actions/setup-java/issues/1099 + // Running setup-java several times in the same job (e.g. multiple steps / multiple + // java-version entries) must accumulate every JDK in toolchains.xml rather + // than replacing previously registered entries. + jest.spyOn(core, 'getInput').mockImplementation((name: string) => { + if (name === 'settings-path') return m2Dir; + return ''; + }); + + const runs = [ + { + version: '8', + distributionName: 'temurin', + id: 'temurin_8', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/8.0.1-12/x64' + }, + { + version: '11', + distributionName: 'temurin', + id: 'temurin_11', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/11.0.1-12/x64' + }, + { + version: '17', + distributionName: 'temurin', + id: 'temurin_17', + jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64' + } + ]; + + for (const run of runs) { + await toolchains.configureToolchains( + run.version, + run.distributionName, + run.jdkHome, + undefined + ); + } + + expect(fs.existsSync(toolchainsFile)).toBe(true); + const contents = fs.readFileSync(toolchainsFile, 'utf-8'); + + for (const run of runs) { + expect(contents).toContain(`${run.id}`); + expect(contents).toContain(`${run.jdkHome}`); + } + // Exactly one entry per run – no duplicates, none dropped. + expect((contents.match(//g) || []).length).toBe(runs.length); + }, 100000); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index 7b098a526..fcd1d062c 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -81751,7 +81751,6 @@ const path = __importStar(__nccwpck_require__(16928)); const core = __importStar(__nccwpck_require__(37484)); const io = __importStar(__nccwpck_require__(94994)); const constants = __importStar(__nccwpck_require__(27242)); -const util_1 = __nccwpck_require__(54527); const xmlbuilder2_1 = __nccwpck_require__(54697); function configureToolchains(version, distributionName, jdkHome, toolchainId) { return __awaiter(this, void 0, void 0, function* () { @@ -81759,7 +81758,6 @@ function configureToolchains(version, distributionName, jdkHome, toolchainId) { const id = toolchainId || `${vendor}_${version}`; const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR); - const overwriteSettings = (0, util_1.getBooleanInput)(constants.INPUT_OVERWRITE_SETTINGS, true); yield createToolchainsSettings({ jdkInfo: { version, @@ -81767,13 +81765,12 @@ function configureToolchains(version, distributionName, jdkHome, toolchainId) { id, jdkHome }, - settingsDirectory, - overwriteSettings + settingsDirectory }); }); } exports.configureToolchains = configureToolchains; -function createToolchainsSettings({ jdkInfo, settingsDirectory, overwriteSettings }) { +function createToolchainsSettings({ jdkInfo, settingsDirectory }) { return __awaiter(this, void 0, void 0, function* () { core.info(`Creating ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}`); // when an alternate m2 location is specified use only that location (no .m2 directory) @@ -81781,7 +81778,7 @@ function createToolchainsSettings({ jdkInfo, settingsDirectory, overwriteSetting yield io.mkdirP(settingsDirectory); const originalToolchains = yield readExistingToolchainsFile(settingsDirectory); const updatedToolchains = generateToolchainDefinition(originalToolchains, jdkInfo.version, jdkInfo.vendor, jdkInfo.id, jdkInfo.jdkHome); - yield writeToolchainsFileToDisk(settingsDirectory, updatedToolchains, overwriteSettings); + yield writeToolchainsFileToDisk(settingsDirectory, updatedToolchains); }); } exports.createToolchainsSettings = createToolchainsSettings; @@ -81867,19 +81864,21 @@ function readExistingToolchainsFile(directory) { return ''; }); } -function writeToolchainsFileToDisk(directory, settings, overwriteSettings) { +function writeToolchainsFileToDisk(directory, settings) { return __awaiter(this, void 0, void 0, function* () { const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE); const settingsExists = fs.existsSync(location); - if (settingsExists && overwriteSettings) { - core.info(`Overwriting existing file ${location}`); - } - else if (!settingsExists) { - core.info(`Writing to ${location}`); + // The toolchains file is produced by a non-destructive merge (existing JDK, + // custom, and non-jdk toolchains are preserved – see generateToolchainDefinition), + // so it is always safe to write it. Unlike settings.xml, it is therefore not + // gated behind the `overwrite-settings` input; that would prevent subsequent + // setup-java runs from registering additional JDKs and silently drop the + // toolchain entries created by earlier runs. + if (settingsExists) { + core.info(`Updating existing file ${location}`); } else { - core.info(`Skipping generation of ${location} because file already exists and overwriting is not enabled`); - return; + core.info(`Writing to ${location}`); } return fs.writeFileSync(location, settings, { encoding: 'utf-8', diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 633ff45bb..bbd6a8903 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -684,6 +684,8 @@ The `setup-java` action generates a basic [Maven Toolchains declaration](https:/ ### Installing Multiple JDKs With Toolchains Subsequent calls to `setup-java` with distinct distribution and version parameters will continue to extend the toolchains declaration and make all specified Java versions available. +Toolchain entries are always merged non-destructively: existing JDK, custom, and user-managed toolchains are preserved, and only an entry with the exact same `type` and `provides.id` is replaced. This behavior is independent of the `overwrite-settings` input, which only controls regeneration of `settings.xml`. As a result, running `setup-java` several times in the same job (for example in multiple steps or with multiple `java-version` values) accumulates every JDK in `toolchains.xml` instead of dropping previously registered entries. + ```yaml steps: - uses: actions/setup-java@v5 diff --git a/src/toolchains.ts b/src/toolchains.ts index a94a56302..103173e3c 100644 --- a/src/toolchains.ts +++ b/src/toolchains.ts @@ -5,7 +5,6 @@ import * as core from '@actions/core'; import * as io from '@actions/io'; import * as constants from './constants'; -import {getBooleanInput} from './util'; import {create as xmlCreate} from 'xmlbuilder2'; interface JdkInfo { @@ -27,10 +26,6 @@ export async function configureToolchains( const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR); - const overwriteSettings = getBooleanInput( - constants.INPUT_OVERWRITE_SETTINGS, - true - ); await createToolchainsSettings({ jdkInfo: { @@ -39,19 +34,16 @@ export async function configureToolchains( id, jdkHome }, - settingsDirectory, - overwriteSettings + settingsDirectory }); } export async function createToolchainsSettings({ jdkInfo, - settingsDirectory, - overwriteSettings + settingsDirectory }: { jdkInfo: JdkInfo; settingsDirectory: string; - overwriteSettings: boolean; }) { core.info( `Creating ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}` @@ -68,11 +60,7 @@ export async function createToolchainsSettings({ jdkInfo.id, jdkInfo.jdkHome ); - await writeToolchainsFileToDisk( - settingsDirectory, - updatedToolchains, - overwriteSettings - ); + await writeToolchainsFileToDisk(settingsDirectory, updatedToolchains); } // only exported for testing purposes @@ -172,22 +160,19 @@ async function readExistingToolchainsFile(directory: string) { return ''; } -async function writeToolchainsFileToDisk( - directory: string, - settings: string, - overwriteSettings: boolean -) { +async function writeToolchainsFileToDisk(directory: string, settings: string) { const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE); const settingsExists = fs.existsSync(location); - if (settingsExists && overwriteSettings) { - core.info(`Overwriting existing file ${location}`); - } else if (!settingsExists) { - core.info(`Writing to ${location}`); + // The toolchains file is produced by a non-destructive merge (existing JDK, + // custom, and non-jdk toolchains are preserved – see generateToolchainDefinition), + // so it is always safe to write it. Unlike settings.xml, it is therefore not + // gated behind the `overwrite-settings` input; that would prevent subsequent + // setup-java runs from registering additional JDKs and silently drop the + // toolchain entries created by earlier runs. + if (settingsExists) { + core.info(`Updating existing file ${location}`); } else { - core.info( - `Skipping generation of ${location} because file already exists and overwriting is not enabled` - ); - return; + core.info(`Writing to ${location}`); } return fs.writeFileSync(location, settings, { From 03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 14 Jul 2026 17:14:30 -0400 Subject: [PATCH 57/57] Backport #1097/#1098: cache Maven and Gradle wrapper distributions separately (#1122) Backports the wrapper caching fix to the v5 release line. The Maven wrapper distribution (~/.m2/wrapper/dists) and Gradle wrapper distribution (~/.gradle/wrapper) were cached in the same entry as the dependency cache, keyed on the volatile dependency-file hashes (**/pom.xml, **/*.gradle*). With no restoreKeys fallback (#269), almost every dependency change was a full cache miss, forcing ./mvnw and ./gradlew to re-download the build tool distribution and hitting intermittent rate-limit failures. Each wrapper distribution now lives in its own additional cache entry keyed only on the rarely-changing wrapper properties file (**/.mvn/wrapper/maven-wrapper.properties, **/gradle-wrapper.properties), so it survives dependency changes. Optional-cache saves swallow ValidationError and ReserveCacheError so a project without a wrapper never fails the post step. Fixes: #1095 Copilot-Session: ea015caa-980a-4e0a-af20-3fc9f39c77fd Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 4 +- __tests__/cache.test.ts | 149 +++++++++++++++++++++++++++--- dist/cleanup/index.js | 137 ++++++++++++++++++++++++++-- dist/setup/index.js | 137 ++++++++++++++++++++++++++-- src/cache.ts | 194 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 581 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 24ffb3394..78c595801 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ The workflow output `cache-primary-key` exposes the primary cache key computed b The cache input is optional, and caching is turned off by default. -**Maven Wrapper:** when `cache: 'maven'` is enabled, the action also caches and restores the Maven Wrapper distribution downloaded to `~/.m2/wrapper/dists` (in addition to the local repository), so wrapper-based (`./mvnw`) builds don't re-download the wrapper on every run. This is keyed on `**/.mvn/wrapper/maven-wrapper.properties` as shown above. +**Maven Wrapper:** when `cache: 'maven'` is enabled, the action also caches and restores the Maven Wrapper distribution downloaded to `~/.m2/wrapper/dists` (in addition to the local repository), so wrapper-based (`./mvnw`) builds don't re-download the Maven distribution. The wrapper distribution is stored in a **separate** cache entry keyed only on `**/.mvn/wrapper/maven-wrapper.properties`, so it stays cached across the frequent `pom.xml` changes that rotate the main dependency cache key. #### Caching gradle dependencies ```yaml @@ -167,6 +167,8 @@ steps: ``` Using the `cache: gradle` provides a simple and effective way to cache Gradle dependencies with minimal configuration. +**Gradle Wrapper:** when `cache: 'gradle'` is enabled, the action also caches and restores the Gradle Wrapper distribution downloaded to `~/.gradle/wrapper` (in addition to the Gradle caches), so wrapper-based (`./gradlew`) builds don't re-download the Gradle distribution. The wrapper distribution is stored in a **separate** cache entry keyed only on `**/gradle-wrapper.properties`, so it stays cached across the frequent `*.gradle*` changes that rotate the main dependency cache key. + For projects that require more advanced `Gradle` caching features, such as caching build outputs, support for Gradle configuration cache, encrypted cache storage, fine-grained cache control (including options to enable or disable the cache, set it to read-only or write-only, perform automated cleanup, and define custom cache rules), or optimized performance for complex CI workflows, consider using [`gradle/actions/setup-gradle`](https://github.com/gradle/actions/tree/main/setup-gradle). For setup details and a comprehensive overview of all available features, visit the [setup-gradle documentation](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md). diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index 90f68efe1..88660ae65 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -113,10 +113,7 @@ describe('dependency cache', () => { await restore('maven', ''); expect(spyCacheRestore).toHaveBeenCalledWith( - [ - join(os.homedir(), '.m2', 'repository'), - join(os.homedir(), '.m2', 'wrapper', 'dists') - ], + [join(os.homedir(), '.m2', 'repository')], expect.any(String) ); expect(spyGlobHashFiles).toHaveBeenCalledWith( @@ -143,10 +140,7 @@ describe('dependency cache', () => { await restore('maven', ''); expect(spyCacheRestore).toHaveBeenCalledWith( - [ - join(os.homedir(), '.m2', 'repository'), - join(os.homedir(), '.m2', 'wrapper', 'dists') - ], + [join(os.homedir(), '.m2', 'repository')], expect.any(String) ); expect(spyGlobHashFiles).toHaveBeenCalledWith( @@ -161,10 +155,7 @@ describe('dependency cache', () => { await restore('maven', ''); expect(spyCacheRestore).toHaveBeenCalledWith( - [ - join(os.homedir(), '.m2', 'repository'), - join(os.homedir(), '.m2', 'wrapper', 'dists') - ], + [join(os.homedir(), '.m2', 'repository')], expect.any(String) ); expect(spyGlobHashFiles).toHaveBeenCalledWith( @@ -173,6 +164,39 @@ describe('dependency cache', () => { expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); }); + it('restores the maven wrapper distribution cache independently of the main cache', async () => { + createFile(join(workspace, 'pom.xml')); + createDirectory(join(workspace, '.mvn')); + createDirectory(join(workspace, '.mvn', 'wrapper')); + createFile( + join(workspace, '.mvn', 'wrapper', 'maven-wrapper.properties') + ); + + await restore('maven', ''); + // Main cache no longer includes the wrapper distribution. + expect(spyCacheRestore).toHaveBeenCalledWith( + [join(os.homedir(), '.m2', 'repository')], + expect.any(String) + ); + // Wrapper distribution is restored on its own, keyed only on the + // maven-wrapper.properties hash. + expect(spyCacheRestore).toHaveBeenCalledWith( + [join(os.homedir(), '.m2', 'wrapper', 'dists')], + expect.any(String) + ); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/.mvn/wrapper/maven-wrapper.properties' + ); + }); + it('skips the maven wrapper cache when no maven-wrapper.properties exists', async () => { + createFile(join(workspace, 'pom.xml')); + + await restore('maven', ''); + expect(spyCacheRestore).not.toHaveBeenCalledWith( + [join(os.homedir(), '.m2', 'wrapper', 'dists')], + expect.any(String) + ); + }); }); describe('for gradle', () => { it('throws error if no build.gradle found', async () => { @@ -228,6 +252,30 @@ describe('dependency cache', () => { expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); }); + it('restores the gradle wrapper distribution cache independently of the main cache', async () => { + createFile(join(workspace, 'build.gradle')); + createDirectory(join(workspace, 'gradle')); + createDirectory(join(workspace, 'gradle', 'wrapper')); + createFile( + join(workspace, 'gradle', 'wrapper', 'gradle-wrapper.properties') + ); + + await restore('gradle', ''); + // Main cache no longer includes the wrapper distribution. + expect(spyCacheRestore).toHaveBeenCalledWith( + [join(os.homedir(), '.gradle', 'caches')], + expect.any(String) + ); + // Wrapper distribution is restored on its own, keyed only on the + // gradle-wrapper.properties hash. + expect(spyCacheRestore).toHaveBeenCalledWith( + [join(os.homedir(), '.gradle', 'wrapper')], + expect.any(String) + ); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/gradle-wrapper.properties' + ); + }); }); describe('for sbt', () => { it('throws error if no build.sbt found', async () => { @@ -399,6 +447,40 @@ describe('dependency cache', () => { expect.stringMatching(/^Cache saved with the key:.*/) ); }); + it('saves the maven wrapper distribution cache under its own key', async () => { + createFile(join(workspace, 'pom.xml')); + createStateForWrapperRestore('maven-wrapper', false); + + await save('maven'); + expect(spyCacheSave).toHaveBeenCalledWith( + [join(os.homedir(), '.m2', 'wrapper', 'dists')], + 'setup-java-maven-wrapper-primary-key' + ); + }); + it('does not save the maven wrapper cache on an exact wrapper hit', async () => { + createFile(join(workspace, 'pom.xml')); + createStateForWrapperRestore('maven-wrapper', true); + + await save('maven'); + expect(spyCacheSave).not.toHaveBeenCalledWith( + [join(os.homedir(), '.m2', 'wrapper', 'dists')], + expect.any(String) + ); + }); + it('does not fail the post step when the wrapper distribution path is missing', async () => { + createFile(join(workspace, 'pom.xml')); + createStateForWrapperRestore('maven-wrapper', false); + spyCacheSave.mockImplementation((paths: string[], key: string) => { + if (paths.includes(join(os.homedir(), '.m2', 'wrapper', 'dists'))) { + return Promise.reject( + new cache.ValidationError('Path Validation Error') + ); + } + return Promise.resolve(0); + }); + + await expect(save('maven')).resolves.not.toThrow(); + }); }); describe('for gradle', () => { it('uploads cache even if no build.gradle found', async () => { @@ -451,6 +533,26 @@ describe('dependency cache', () => { expect.stringMatching(/^Cache saved with the key:.*/) ); }); + it('saves the gradle wrapper distribution cache under its own key', async () => { + createFile(join(workspace, 'build.gradle')); + createStateForWrapperRestore('gradle-wrapper', false); + + await save('gradle'); + expect(spyCacheSave).toHaveBeenCalledWith( + [join(os.homedir(), '.gradle', 'wrapper')], + 'setup-java-gradle-wrapper-primary-key' + ); + }); + it('does not save the gradle wrapper cache on an exact wrapper hit', async () => { + createFile(join(workspace, 'build.gradle')); + createStateForWrapperRestore('gradle-wrapper', true); + + await save('gradle'); + expect(spyCacheSave).not.toHaveBeenCalledWith( + [join(os.homedir(), '.gradle', 'wrapper')], + expect.any(String) + ); + }); }); describe('for sbt', () => { it('uploads cache even if no build.sbt found', async () => { @@ -528,6 +630,29 @@ function createStateForSuccessfulRestore() { }); } +/** + * Create states to emulate a restore process where an additional (wrapper) + * cache was restored. When `hit` is true the matched key equals the primary + * key, emulating an exact wrapper cache hit. + */ +function createStateForWrapperRestore(wrapperName: string, hit: boolean) { + const primaryKey = `setup-java-${wrapperName}-primary-key`; + jest.spyOn(core, 'getState').mockImplementation(name => { + switch (name) { + case 'cache-primary-key': + return 'setup-java-cache-primary-key'; + case 'cache-matched-key': + return 'setup-java-cache-matched-key'; + case `cache-primary-key-${wrapperName}`: + return primaryKey; + case `cache-matched-key-${wrapperName}`: + return hit ? primaryKey : ''; + default: + return ''; + } + }); +} + function createFile(path: string) { core.info(`created a file at ${path}`); fs.writeFileSync(path, ''); diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 1154a0bc6..111c1a00c 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -51968,23 +51968,28 @@ const CACHE_KEY_PREFIX = 'setup-java'; const supportedPackageManager = [ { id: 'maven', - path: [ - (0, path_1.join)(os_1.default.homedir(), '.m2', 'repository'), - (0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists') - ], + path: [(0, path_1.join)(os_1.default.homedir(), '.m2', 'repository')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven pattern: [ '**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties', '**/.mvn/extensions.xml' + ], + // The Maven wrapper distribution only depends on the wrapper properties, + // which change very rarely, so it is cached separately from the local + // repository. This keeps it available across the frequent pom.xml changes + // that rotate the main cache key. See issue #1095. + additionalCaches: [ + { + name: 'maven-wrapper', + path: [(0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists')], + pattern: ['**/.mvn/wrapper/maven-wrapper.properties'] + } ] }, { id: 'gradle', - path: [ - (0, path_1.join)(os_1.default.homedir(), '.gradle', 'caches'), - (0, path_1.join)(os_1.default.homedir(), '.gradle', 'wrapper') - ], + path: [(0, path_1.join)(os_1.default.homedir(), '.gradle', 'caches')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle pattern: [ '**/*.gradle*', @@ -51993,6 +51998,17 @@ const supportedPackageManager = [ 'buildSrc/**/Dependencies.kt', 'gradle/*.versions.toml', '**/versions.properties' + ], + // The Gradle wrapper distribution only depends on the wrapper properties, + // which change very rarely, so it is cached separately from the Gradle + // caches. This keeps it available across the frequent *.gradle* changes + // that rotate the main cache key. See issue #269. + additionalCaches: [ + { + name: 'gradle-wrapper', + path: [(0, path_1.join)(os_1.default.homedir(), '.gradle', 'wrapper')], + pattern: ['**/gradle-wrapper.properties'] + } ] }, { @@ -52028,6 +52044,19 @@ function findPackageManager(id) { } return packageManager; } +/** + * State keys used to carry an additional cache's restore-time information over + * to the post (save) action, scoped by the additional cache name. + */ +function additionalCachePrimaryKeyState(name) { + return `${STATE_CACHE_PRIMARY_KEY}-${name}`; +} +function additionalCacheMatchedKeyState(name) { + return `${CACHE_MATCHED_KEY}-${name}`; +} +function buildCacheKey(id, fileHash) { + return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${id}-${fileHash}`; +} /** * A function that generates a cache key to use. * Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"". @@ -52042,7 +52071,21 @@ function computeCacheKey(packageManager, cacheDependencyPath) { if (!fileHash) { throw new Error(`No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository`); } - return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${packageManager.id}-${fileHash}`; + return buildCacheKey(packageManager.id, fileHash); + }); +} +/** + * Computes the cache key for an additional cache. Unlike {@link computeCacheKey} + * this returns undefined (instead of throwing) when no file matches the pattern, + * because additional caches are optional features that many projects do not use. + */ +function computeAdditionalCacheKey(additionalCache) { + return __awaiter(this, void 0, void 0, function* () { + const fileHash = yield glob.hashFiles(additionalCache.pattern.join('\n')); + if (!fileHash) { + return undefined; + } + return buildCacheKey(additionalCache.name, fileHash); }); } /** @@ -52051,6 +52094,7 @@ function computeCacheKey(packageManager, cacheDependencyPath) { * @param cacheDependencyPath The path to a dependency file */ function restore(id, cacheDependencyPath) { + var _a; return __awaiter(this, void 0, void 0, function* () { const packageManager = findPackageManager(id); const primaryKey = yield computeCacheKey(packageManager, cacheDependencyPath); @@ -52068,19 +52112,48 @@ function restore(id, cacheDependencyPath) { core.setOutput('cache-hit', false); core.info(`${packageManager.id} cache is not found`); } + for (const additionalCache of (_a = packageManager.additionalCaches) !== null && _a !== void 0 ? _a : []) { + yield restoreAdditionalCache(additionalCache); + } }); } exports.restore = restore; +/** + * Restore an additional cache (e.g. a build-tool wrapper distribution) that is + * keyed independently of the main dependency cache so that it survives changes + * to volatile dependency files. Skips silently when the project does not use + * the corresponding feature. + */ +function restoreAdditionalCache(additionalCache) { + return __awaiter(this, void 0, void 0, function* () { + const primaryKey = yield computeAdditionalCacheKey(additionalCache); + if (!primaryKey) { + core.debug(`No file matched [${additionalCache.pattern}] for the ${additionalCache.name} cache, skipping.`); + return; + } + core.debug(`${additionalCache.name} primary key is ${primaryKey}`); + core.saveState(additionalCachePrimaryKeyState(additionalCache.name), primaryKey); + const matchedKey = yield cache.restoreCache(additionalCache.path, primaryKey); + if (matchedKey) { + core.saveState(additionalCacheMatchedKeyState(additionalCache.name), matchedKey); + core.info(`${additionalCache.name} cache restored from key: ${matchedKey}`); + } + }); +} /** * Save the dependency cache * @param id ID of the package manager, should be "maven" or "gradle" */ function save(id) { + var _a; return __awaiter(this, void 0, void 0, function* () { const packageManager = findPackageManager(id); const matchedKey = core.getState(CACHE_MATCHED_KEY); // Inputs are re-evaluated before the post action, so we want the original key used for restore const primaryKey = core.getState(STATE_CACHE_PRIMARY_KEY); + for (const additionalCache of (_a = packageManager.additionalCaches) !== null && _a !== void 0 ? _a : []) { + yield saveAdditionalCache(packageManager, additionalCache); + } if (!primaryKey) { core.warning('Error retrieving key from state.'); return; @@ -52117,6 +52190,52 @@ function save(id) { }); } exports.save = save; +/** + * Save an additional cache under its own key. Skips when no key was recorded at + * restore time (feature unused) or when the exact key was already restored. + */ +function saveAdditionalCache(packageManager, additionalCache) { + return __awaiter(this, void 0, void 0, function* () { + const primaryKey = core.getState(additionalCachePrimaryKeyState(additionalCache.name)); + const matchedKey = core.getState(additionalCacheMatchedKeyState(additionalCache.name)); + if (!primaryKey) { + // The feature is not used by this project, nothing to save. + core.debug(`No primary key for the ${additionalCache.name} cache, not saving cache.`); + return; + } + else if (matchedKey === primaryKey) { + core.info(`Cache hit occurred on the ${additionalCache.name} primary key ${primaryKey}, not saving cache.`); + return; + } + try { + const cacheId = yield cache.saveCache(additionalCache.path, primaryKey); + if (cacheId === -1) { + core.debug(`${additionalCache.name} cache was not saved for the key: ${primaryKey}`); + return; + } + core.info(`${additionalCache.name} cache saved with the key: ${primaryKey}`); + } + catch (error) { + const err = error; + if (err.name === cache.ValidationError.name) { + // The cache paths did not resolve, e.g. the wrapper distribution was + // never downloaded because a system build tool was used or the download + // failed. Optional wrapper caches must not fail the post step, so skip. + core.debug(`${additionalCache.name} cache paths do not exist, not saving cache: ${err.message}`); + return; + } + if (err.name === cache.ReserveCacheError.name) { + core.info(err.message); + } + else { + if (isProbablyGradleDaemonProblem(packageManager, err)) { + core.warning('Failed to save Gradle cache on Windows. If tar.exe reported "Permission denied", try to run Gradle with `--no-daemon` option. Refer to https://github.com/actions/cache/issues/454 for details.'); + } + throw error; + } + } + }); +} /** * @param packageManager the specified package manager by user * @param error the error thrown by the saveCache diff --git a/dist/setup/index.js b/dist/setup/index.js index fcd1d062c..d4b76ff92 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -77833,23 +77833,28 @@ const CACHE_KEY_PREFIX = 'setup-java'; const supportedPackageManager = [ { id: 'maven', - path: [ - (0, path_1.join)(os_1.default.homedir(), '.m2', 'repository'), - (0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists') - ], + path: [(0, path_1.join)(os_1.default.homedir(), '.m2', 'repository')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven pattern: [ '**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties', '**/.mvn/extensions.xml' + ], + // The Maven wrapper distribution only depends on the wrapper properties, + // which change very rarely, so it is cached separately from the local + // repository. This keeps it available across the frequent pom.xml changes + // that rotate the main cache key. See issue #1095. + additionalCaches: [ + { + name: 'maven-wrapper', + path: [(0, path_1.join)(os_1.default.homedir(), '.m2', 'wrapper', 'dists')], + pattern: ['**/.mvn/wrapper/maven-wrapper.properties'] + } ] }, { id: 'gradle', - path: [ - (0, path_1.join)(os_1.default.homedir(), '.gradle', 'caches'), - (0, path_1.join)(os_1.default.homedir(), '.gradle', 'wrapper') - ], + path: [(0, path_1.join)(os_1.default.homedir(), '.gradle', 'caches')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle pattern: [ '**/*.gradle*', @@ -77858,6 +77863,17 @@ const supportedPackageManager = [ 'buildSrc/**/Dependencies.kt', 'gradle/*.versions.toml', '**/versions.properties' + ], + // The Gradle wrapper distribution only depends on the wrapper properties, + // which change very rarely, so it is cached separately from the Gradle + // caches. This keeps it available across the frequent *.gradle* changes + // that rotate the main cache key. See issue #269. + additionalCaches: [ + { + name: 'gradle-wrapper', + path: [(0, path_1.join)(os_1.default.homedir(), '.gradle', 'wrapper')], + pattern: ['**/gradle-wrapper.properties'] + } ] }, { @@ -77893,6 +77909,19 @@ function findPackageManager(id) { } return packageManager; } +/** + * State keys used to carry an additional cache's restore-time information over + * to the post (save) action, scoped by the additional cache name. + */ +function additionalCachePrimaryKeyState(name) { + return `${STATE_CACHE_PRIMARY_KEY}-${name}`; +} +function additionalCacheMatchedKeyState(name) { + return `${CACHE_MATCHED_KEY}-${name}`; +} +function buildCacheKey(id, fileHash) { + return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${id}-${fileHash}`; +} /** * A function that generates a cache key to use. * Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"". @@ -77907,7 +77936,21 @@ function computeCacheKey(packageManager, cacheDependencyPath) { if (!fileHash) { throw new Error(`No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository`); } - return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${packageManager.id}-${fileHash}`; + return buildCacheKey(packageManager.id, fileHash); + }); +} +/** + * Computes the cache key for an additional cache. Unlike {@link computeCacheKey} + * this returns undefined (instead of throwing) when no file matches the pattern, + * because additional caches are optional features that many projects do not use. + */ +function computeAdditionalCacheKey(additionalCache) { + return __awaiter(this, void 0, void 0, function* () { + const fileHash = yield glob.hashFiles(additionalCache.pattern.join('\n')); + if (!fileHash) { + return undefined; + } + return buildCacheKey(additionalCache.name, fileHash); }); } /** @@ -77916,6 +77959,7 @@ function computeCacheKey(packageManager, cacheDependencyPath) { * @param cacheDependencyPath The path to a dependency file */ function restore(id, cacheDependencyPath) { + var _a; return __awaiter(this, void 0, void 0, function* () { const packageManager = findPackageManager(id); const primaryKey = yield computeCacheKey(packageManager, cacheDependencyPath); @@ -77933,19 +77977,48 @@ function restore(id, cacheDependencyPath) { core.setOutput('cache-hit', false); core.info(`${packageManager.id} cache is not found`); } + for (const additionalCache of (_a = packageManager.additionalCaches) !== null && _a !== void 0 ? _a : []) { + yield restoreAdditionalCache(additionalCache); + } }); } exports.restore = restore; +/** + * Restore an additional cache (e.g. a build-tool wrapper distribution) that is + * keyed independently of the main dependency cache so that it survives changes + * to volatile dependency files. Skips silently when the project does not use + * the corresponding feature. + */ +function restoreAdditionalCache(additionalCache) { + return __awaiter(this, void 0, void 0, function* () { + const primaryKey = yield computeAdditionalCacheKey(additionalCache); + if (!primaryKey) { + core.debug(`No file matched [${additionalCache.pattern}] for the ${additionalCache.name} cache, skipping.`); + return; + } + core.debug(`${additionalCache.name} primary key is ${primaryKey}`); + core.saveState(additionalCachePrimaryKeyState(additionalCache.name), primaryKey); + const matchedKey = yield cache.restoreCache(additionalCache.path, primaryKey); + if (matchedKey) { + core.saveState(additionalCacheMatchedKeyState(additionalCache.name), matchedKey); + core.info(`${additionalCache.name} cache restored from key: ${matchedKey}`); + } + }); +} /** * Save the dependency cache * @param id ID of the package manager, should be "maven" or "gradle" */ function save(id) { + var _a; return __awaiter(this, void 0, void 0, function* () { const packageManager = findPackageManager(id); const matchedKey = core.getState(CACHE_MATCHED_KEY); // Inputs are re-evaluated before the post action, so we want the original key used for restore const primaryKey = core.getState(STATE_CACHE_PRIMARY_KEY); + for (const additionalCache of (_a = packageManager.additionalCaches) !== null && _a !== void 0 ? _a : []) { + yield saveAdditionalCache(packageManager, additionalCache); + } if (!primaryKey) { core.warning('Error retrieving key from state.'); return; @@ -77982,6 +78055,52 @@ function save(id) { }); } exports.save = save; +/** + * Save an additional cache under its own key. Skips when no key was recorded at + * restore time (feature unused) or when the exact key was already restored. + */ +function saveAdditionalCache(packageManager, additionalCache) { + return __awaiter(this, void 0, void 0, function* () { + const primaryKey = core.getState(additionalCachePrimaryKeyState(additionalCache.name)); + const matchedKey = core.getState(additionalCacheMatchedKeyState(additionalCache.name)); + if (!primaryKey) { + // The feature is not used by this project, nothing to save. + core.debug(`No primary key for the ${additionalCache.name} cache, not saving cache.`); + return; + } + else if (matchedKey === primaryKey) { + core.info(`Cache hit occurred on the ${additionalCache.name} primary key ${primaryKey}, not saving cache.`); + return; + } + try { + const cacheId = yield cache.saveCache(additionalCache.path, primaryKey); + if (cacheId === -1) { + core.debug(`${additionalCache.name} cache was not saved for the key: ${primaryKey}`); + return; + } + core.info(`${additionalCache.name} cache saved with the key: ${primaryKey}`); + } + catch (error) { + const err = error; + if (err.name === cache.ValidationError.name) { + // The cache paths did not resolve, e.g. the wrapper distribution was + // never downloaded because a system build tool was used or the download + // failed. Optional wrapper caches must not fail the post step, so skip. + core.debug(`${additionalCache.name} cache paths do not exist, not saving cache: ${err.message}`); + return; + } + if (err.name === cache.ReserveCacheError.name) { + core.info(err.message); + } + else { + if (isProbablyGradleDaemonProblem(packageManager, err)) { + core.warning('Failed to save Gradle cache on Windows. If tar.exe reported "Permission denied", try to run Gradle with `--no-daemon` option. Refer to https://github.com/actions/cache/issues/454 for details.'); + } + throw error; + } + } + }); +} /** * @param packageManager the specified package manager by user * @param error the error thrown by the saveCache diff --git a/src/cache.ts b/src/cache.ts index 263cc0d8a..1ca7e7628 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -12,6 +12,30 @@ const STATE_CACHE_PRIMARY_KEY = 'cache-primary-key'; const CACHE_MATCHED_KEY = 'cache-matched-key'; const CACHE_KEY_PREFIX = 'setup-java'; +/** + * An additional cache entry that is restored and saved independently of the + * main dependency cache. Used for build-tool wrapper distributions that rarely + * change (e.g. the Maven wrapper distribution) so that they are not evicted + * every time a volatile dependency file such as pom.xml changes. See + * https://github.com/actions/setup-java/issues/1095. + */ +interface AdditionalCache { + /** + * Short identifier for the cache, used to build its cache key and to scope + * the state keys that carry information from restore to save. + */ + name: string; + /** + * Paths that make up this cache entry. + */ + path: string[]; + /** + * Glob patterns whose hash forms the cache key. If no file matches, the + * cache is skipped silently (the project simply does not use this feature). + */ + pattern: string[]; +} + interface PackageManager { id: 'maven' | 'gradle' | 'sbt'; /** @@ -19,27 +43,36 @@ interface PackageManager { */ path: string[]; pattern: string[]; + /** + * Additional caches keyed independently of the main dependency cache. + */ + additionalCaches?: AdditionalCache[]; } const supportedPackageManager: PackageManager[] = [ { id: 'maven', - path: [ - join(os.homedir(), '.m2', 'repository'), - join(os.homedir(), '.m2', 'wrapper', 'dists') - ], + path: [join(os.homedir(), '.m2', 'repository')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---maven pattern: [ '**/pom.xml', '**/.mvn/wrapper/maven-wrapper.properties', '**/.mvn/extensions.xml' + ], + // The Maven wrapper distribution only depends on the wrapper properties, + // which change very rarely, so it is cached separately from the local + // repository. This keeps it available across the frequent pom.xml changes + // that rotate the main cache key. See issue #1095. + additionalCaches: [ + { + name: 'maven-wrapper', + path: [join(os.homedir(), '.m2', 'wrapper', 'dists')], + pattern: ['**/.mvn/wrapper/maven-wrapper.properties'] + } ] }, { id: 'gradle', - path: [ - join(os.homedir(), '.gradle', 'caches'), - join(os.homedir(), '.gradle', 'wrapper') - ], + path: [join(os.homedir(), '.gradle', 'caches')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle pattern: [ '**/*.gradle*', @@ -48,6 +81,17 @@ const supportedPackageManager: PackageManager[] = [ 'buildSrc/**/Dependencies.kt', 'gradle/*.versions.toml', '**/versions.properties' + ], + // The Gradle wrapper distribution only depends on the wrapper properties, + // which change very rarely, so it is cached separately from the Gradle + // caches. This keeps it available across the frequent *.gradle* changes + // that rotate the main cache key. See issue #269. + additionalCaches: [ + { + name: 'gradle-wrapper', + path: [join(os.homedir(), '.gradle', 'wrapper')], + pattern: ['**/gradle-wrapper.properties'] + } ] }, { @@ -87,6 +131,21 @@ function findPackageManager(id: string): PackageManager { return packageManager; } +/** + * State keys used to carry an additional cache's restore-time information over + * to the post (save) action, scoped by the additional cache name. + */ +function additionalCachePrimaryKeyState(name: string): string { + return `${STATE_CACHE_PRIMARY_KEY}-${name}`; +} +function additionalCacheMatchedKeyState(name: string): string { + return `${CACHE_MATCHED_KEY}-${name}`; +} + +function buildCacheKey(id: string, fileHash: string): string { + return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${id}-${fileHash}`; +} + /** * A function that generates a cache key to use. * Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"". @@ -105,7 +164,22 @@ async function computeCacheKey( `No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository` ); } - return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${packageManager.id}-${fileHash}`; + return buildCacheKey(packageManager.id, fileHash); +} + +/** + * Computes the cache key for an additional cache. Unlike {@link computeCacheKey} + * this returns undefined (instead of throwing) when no file matches the pattern, + * because additional caches are optional features that many projects do not use. + */ +async function computeAdditionalCacheKey( + additionalCache: AdditionalCache +): Promise { + const fileHash = await glob.hashFiles(additionalCache.pattern.join('\n')); + if (!fileHash) { + return undefined; + } + return buildCacheKey(additionalCache.name, fileHash); } /** @@ -130,6 +204,40 @@ export async function restore(id: string, cacheDependencyPath: string) { core.setOutput('cache-hit', false); core.info(`${packageManager.id} cache is not found`); } + + for (const additionalCache of packageManager.additionalCaches ?? []) { + await restoreAdditionalCache(additionalCache); + } +} + +/** + * Restore an additional cache (e.g. a build-tool wrapper distribution) that is + * keyed independently of the main dependency cache so that it survives changes + * to volatile dependency files. Skips silently when the project does not use + * the corresponding feature. + */ +async function restoreAdditionalCache(additionalCache: AdditionalCache) { + const primaryKey = await computeAdditionalCacheKey(additionalCache); + if (!primaryKey) { + core.debug( + `No file matched [${additionalCache.pattern}] for the ${additionalCache.name} cache, skipping.` + ); + return; + } + core.debug(`${additionalCache.name} primary key is ${primaryKey}`); + core.saveState( + additionalCachePrimaryKeyState(additionalCache.name), + primaryKey + ); + + const matchedKey = await cache.restoreCache(additionalCache.path, primaryKey); + if (matchedKey) { + core.saveState( + additionalCacheMatchedKeyState(additionalCache.name), + matchedKey + ); + core.info(`${additionalCache.name} cache restored from key: ${matchedKey}`); + } } /** @@ -143,6 +251,10 @@ export async function save(id: string) { // Inputs are re-evaluated before the post action, so we want the original key used for restore const primaryKey = core.getState(STATE_CACHE_PRIMARY_KEY); + for (const additionalCache of packageManager.additionalCaches ?? []) { + await saveAdditionalCache(packageManager, additionalCache); + } + if (!primaryKey) { core.warning('Error retrieving key from state.'); return; @@ -180,6 +292,70 @@ export async function save(id: string) { } } +/** + * Save an additional cache under its own key. Skips when no key was recorded at + * restore time (feature unused) or when the exact key was already restored. + */ +async function saveAdditionalCache( + packageManager: PackageManager, + additionalCache: AdditionalCache +) { + const primaryKey = core.getState( + additionalCachePrimaryKeyState(additionalCache.name) + ); + const matchedKey = core.getState( + additionalCacheMatchedKeyState(additionalCache.name) + ); + + if (!primaryKey) { + // The feature is not used by this project, nothing to save. + core.debug( + `No primary key for the ${additionalCache.name} cache, not saving cache.` + ); + return; + } else if (matchedKey === primaryKey) { + core.info( + `Cache hit occurred on the ${additionalCache.name} primary key ${primaryKey}, not saving cache.` + ); + return; + } + try { + const cacheId = await cache.saveCache(additionalCache.path, primaryKey); + if (cacheId === -1) { + core.debug( + `${additionalCache.name} cache was not saved for the key: ${primaryKey}` + ); + return; + } + core.info( + `${additionalCache.name} cache saved with the key: ${primaryKey}` + ); + } catch (error) { + const err = error as Error; + + if (err.name === cache.ValidationError.name) { + // The cache paths did not resolve, e.g. the wrapper distribution was + // never downloaded because a system build tool was used or the download + // failed. Optional wrapper caches must not fail the post step, so skip. + core.debug( + `${additionalCache.name} cache paths do not exist, not saving cache: ${err.message}` + ); + return; + } + + if (err.name === cache.ReserveCacheError.name) { + core.info(err.message); + } else { + if (isProbablyGradleDaemonProblem(packageManager, err)) { + core.warning( + 'Failed to save Gradle cache on Windows. If tar.exe reported "Permission denied", try to run Gradle with `--no-daemon` option. Refer to https://github.com/actions/cache/issues/454 for details.' + ); + } + throw error; + } + } +} + /** * @param packageManager the specified package manager by user * @param error the error thrown by the saveCache