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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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 From 6a3384db745932178632d0e22b2bd28cad1678e6 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 28 Jul 2026 21:05:41 -0400 Subject: [PATCH 58/60] Fix npm audit failures on releases/v5 (#1154) * Fix npm audit failures on v5 Override minimatch with the patched release and refresh transitive dependencies and bundled action output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 61439b48-bcf1-4855-94e9-31c4da3e8314 * Fix Windows lint file discovery Use ESLint's directory and extension arguments so file enumeration does not depend on minimatch handling absolute Windows paths after the security override. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pin minimatch security override Keep the transitive override at the validated patched release to avoid unrelated changes during future lockfile refreshes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 61439b48-bcf1-4855-94e9-31c4da3e8314 --- dist/cleanup/index.js | 3893 +++++++++++++------- dist/setup/index.js | 8119 ++++++++++++++++++++++++----------------- package-lock.json | 207 +- package.json | 9 +- 4 files changed, 7309 insertions(+), 4919 deletions(-) diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 111c1a00c..7ff509c06 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -7975,7 +7975,7 @@ const os = __importStar(__nccwpck_require__(70857)); const path = __importStar(__nccwpck_require__(16928)); const pathHelper = __importStar(__nccwpck_require__(84138)); const assert_1 = __importDefault(__nccwpck_require__(42613)); -const minimatch_1 = __nccwpck_require__(43772); +const minimatch_1 = __nccwpck_require__(46507); const internal_match_kind_1 = __nccwpck_require__(62644); const internal_path_1 = __nccwpck_require__(76617); const IS_WINDOWS = process.platform === 'win32'; @@ -17728,303 +17728,6 @@ class Agent extends http.Agent { exports.Agent = Agent; //# sourceMappingURL=index.js.map -/***/ }), - -/***/ 59380: -/***/ ((module) => { - -"use strict"; - -module.exports = balanced; -function balanced(a, b, str) { - if (a instanceof RegExp) a = maybeMatch(a, str); - if (b instanceof RegExp) b = maybeMatch(b, str); - - var r = range(a, b, str); - - return r && { - start: r[0], - end: r[1], - pre: str.slice(0, r[0]), - body: str.slice(r[0] + a.length, r[1]), - post: str.slice(r[1] + b.length) - }; -} - -function maybeMatch(reg, str) { - var m = str.match(reg); - return m ? m[0] : null; -} - -balanced.range = range; -function range(a, b, str) { - var begs, beg, left, right, result; - var ai = str.indexOf(a); - var bi = str.indexOf(b, ai + 1); - var i = ai; - - if (ai >= 0 && bi > 0) { - if(a===b) { - return [ai, bi]; - } - begs = []; - left = str.length; - - while (i >= 0 && !result) { - if (i == ai) { - begs.push(i); - ai = str.indexOf(a, i + 1); - } else if (begs.length == 1) { - result = [ begs.pop(), bi ]; - } else { - beg = begs.pop(); - if (beg < left) { - left = beg; - right = bi; - } - - bi = str.indexOf(b, i + 1); - } - - i = ai < bi && ai >= 0 ? ai : bi; - } - - if (begs.length) { - result = [ left, right ]; - } - } - - return result; -} - - -/***/ }), - -/***/ 94691: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -var concatMap = __nccwpck_require__(97087); -var balanced = __nccwpck_require__(59380); - -module.exports = expandTop; - -var escSlash = '\0SLASH'+Math.random()+'\0'; -var escOpen = '\0OPEN'+Math.random()+'\0'; -var escClose = '\0CLOSE'+Math.random()+'\0'; -var escComma = '\0COMMA'+Math.random()+'\0'; -var escPeriod = '\0PERIOD'+Math.random()+'\0'; - -function numeric(str) { - return parseInt(str, 10) == str - ? parseInt(str, 10) - : str.charCodeAt(0); -} - -function escapeBraces(str) { - return str.split('\\\\').join(escSlash) - .split('\\{').join(escOpen) - .split('\\}').join(escClose) - .split('\\,').join(escComma) - .split('\\.').join(escPeriod); -} - -function unescapeBraces(str) { - return str.split(escSlash).join('\\') - .split(escOpen).join('{') - .split(escClose).join('}') - .split(escComma).join(',') - .split(escPeriod).join('.'); -} - - -// Basically just str.split(","), but handling cases -// where we have nested braced sections, which should be -// treated as individual members, like {a,{b,c},d} -function parseCommaParts(str) { - if (!str) - return ['']; - - var parts = []; - var m = balanced('{', '}', str); - - if (!m) - return str.split(','); - - var pre = m.pre; - var body = m.body; - var post = m.post; - var p = pre.split(','); - - p[p.length-1] += '{' + body + '}'; - var postParts = parseCommaParts(post); - if (post.length) { - p[p.length-1] += postParts.shift(); - p.push.apply(p, postParts); - } - - parts.push.apply(parts, p); - - return parts; -} - -function expandTop(str) { - if (!str) - return []; - - // I don't know why Bash 4.3 does this, but it does. - // Anything starting with {} will have the first two bytes preserved - // but *only* at the top level, so {},a}b will not expand to anything, - // but a{},b}c will be expanded to [a}c,abc]. - // One could argue that this is a bug in Bash, but since the goal of - // this module is to match Bash's rules, we escape a leading {} - if (str.substr(0, 2) === '{}') { - str = '\\{\\}' + str.substr(2); - } - - return expand(escapeBraces(str), true).map(unescapeBraces); -} - -function identity(e) { - return e; -} - -function embrace(str) { - return '{' + str + '}'; -} -function isPadded(el) { - return /^-?0\d/.test(el); -} - -function lte(i, y) { - return i <= y; -} -function gte(i, y) { - return i >= y; -} - -function expand(str, isTop) { - var expansions = []; - - var m = balanced('{', '}', str); - if (!m || /\$$/.test(m.pre)) return [str]; - - var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); - var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); - var isSequence = isNumericSequence || isAlphaSequence; - var isOptions = m.body.indexOf(',') >= 0; - if (!isSequence && !isOptions) { - // {a},b} - if (m.post.match(/,(?!,).*\}/)) { - str = m.pre + '{' + m.body + escClose + m.post; - return expand(str); - } - return [str]; - } - - var n; - if (isSequence) { - n = m.body.split(/\.\./); - } else { - n = parseCommaParts(m.body); - if (n.length === 1) { - // x{{a,b}}y ==> x{a}y x{b}y - n = expand(n[0], false).map(embrace); - if (n.length === 1) { - var post = m.post.length - ? expand(m.post, false) - : ['']; - return post.map(function(p) { - return m.pre + n[0] + p; - }); - } - } - } - - // at this point, n is the parts, and we know it's not a comma set - // with a single entry. - - // no need to expand pre, since it is guaranteed to be free of brace-sets - var pre = m.pre; - var post = m.post.length - ? expand(m.post, false) - : ['']; - - var N; - - if (isSequence) { - var x = numeric(n[0]); - var y = numeric(n[1]); - var width = Math.max(n[0].length, n[1].length) - var incr = n.length == 3 - ? Math.max(Math.abs(numeric(n[2])), 1) - : 1; - var test = lte; - var reverse = y < x; - if (reverse) { - incr *= -1; - test = gte; - } - var pad = n.some(isPadded); - - N = []; - - for (var i = x; test(i, y); i += incr) { - var c; - if (isAlphaSequence) { - c = String.fromCharCode(i); - if (c === '\\') - c = ''; - } else { - c = String(i); - if (pad) { - var need = width - c.length; - if (need > 0) { - var z = new Array(need + 1).join('0'); - if (i < 0) - c = '-' + z + c.slice(1); - else - c = z + c; - } - } - } - N.push(c); - } - } else { - N = concatMap(n, function(el) { return expand(el, false) }); - } - - for (var j = 0; j < N.length; j++) { - for (var k = 0; k < post.length; k++) { - var expansion = pre + N[j] + post[k]; - if (!isTop || isSequence || expansion) - expansions.push(expansion); - } - } - - return expansions; -} - - -/***/ }), - -/***/ 97087: -/***/ ((module) => { - -module.exports = function (xs, fn) { - var res = []; - for (var i = 0; i < xs.length; i++) { - var x = fn(xs[i], i); - if (isArray(x)) res.push.apply(res, x); - else res.push(x); - } - return res; -}; - -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; -}; - - /***/ }), /***/ 6110: @@ -19356,1018 +19059,6 @@ function parseProxyResponse(socket) { exports.parseProxyResponse = parseProxyResponse; //# sourceMappingURL=parse-proxy-response.js.map -/***/ }), - -/***/ 43772: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -module.exports = minimatch -minimatch.Minimatch = Minimatch - -var path = (function () { try { return __nccwpck_require__(16928) } catch (e) {}}()) || { - sep: '/' -} -minimatch.sep = path.sep - -var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} -var expand = __nccwpck_require__(94691) - -var plTypes = { - '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, - '?': { open: '(?:', close: ')?' }, - '+': { open: '(?:', close: ')+' }, - '*': { open: '(?:', close: ')*' }, - '@': { open: '(?:', close: ')' } -} - -// any single thing other than / -// don't need to escape / when using new RegExp() -var qmark = '[^/]' - -// * => any number of characters -var star = qmark + '*?' - -// ** when dots are allowed. Anything goes, except .. and . -// not (^ or / followed by one or two dots followed by $ or /), -// followed by anything, any number of times. -var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' - -// not a ^ or / followed by a dot, -// followed by anything, any number of times. -var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' - -// characters that need to be escaped in RegExp. -var reSpecials = charSet('().*{}+?[]^$\\!') - -// "abc" -> { a:true, b:true, c:true } -function charSet (s) { - return s.split('').reduce(function (set, c) { - set[c] = true - return set - }, {}) -} - -// normalizes slashes. -var slashSplit = /\/+/ - -minimatch.filter = filter -function filter (pattern, options) { - options = options || {} - return function (p, i, list) { - return minimatch(p, pattern, options) - } -} - -function ext (a, b) { - b = b || {} - var t = {} - Object.keys(a).forEach(function (k) { - t[k] = a[k] - }) - Object.keys(b).forEach(function (k) { - t[k] = b[k] - }) - return t -} - -minimatch.defaults = function (def) { - if (!def || typeof def !== 'object' || !Object.keys(def).length) { - return minimatch - } - - var orig = minimatch - - var m = function minimatch (p, pattern, options) { - return orig(p, pattern, ext(def, options)) - } - - m.Minimatch = function Minimatch (pattern, options) { - return new orig.Minimatch(pattern, ext(def, options)) - } - m.Minimatch.defaults = function defaults (options) { - return orig.defaults(ext(def, options)).Minimatch - } - - m.filter = function filter (pattern, options) { - return orig.filter(pattern, ext(def, options)) - } - - m.defaults = function defaults (options) { - return orig.defaults(ext(def, options)) - } - - m.makeRe = function makeRe (pattern, options) { - return orig.makeRe(pattern, ext(def, options)) - } - - m.braceExpand = function braceExpand (pattern, options) { - return orig.braceExpand(pattern, ext(def, options)) - } - - m.match = function (list, pattern, options) { - return orig.match(list, pattern, ext(def, options)) - } - - return m -} - -Minimatch.defaults = function (def) { - return minimatch.defaults(def).Minimatch -} - -function minimatch (p, pattern, options) { - assertValidPattern(pattern) - - if (!options) options = {} - - // shortcut: comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - return false - } - - return new Minimatch(pattern, options).match(p) -} - -function Minimatch (pattern, options) { - if (!(this instanceof Minimatch)) { - return new Minimatch(pattern, options) - } - - assertValidPattern(pattern) - - if (!options) options = {} - - pattern = pattern.trim() - - // windows support: need to use /, not \ - if (!options.allowWindowsEscape && path.sep !== '/') { - pattern = pattern.split(path.sep).join('/') - } - - this.options = options - this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined - ? options.maxGlobstarRecursion : 200 - this.set = [] - this.pattern = pattern - this.regexp = null - this.negate = false - this.comment = false - this.empty = false - this.partial = !!options.partial - - // make the set of regexps etc. - this.make() -} - -Minimatch.prototype.debug = function () {} - -Minimatch.prototype.make = make -function make () { - var pattern = this.pattern - var options = this.options - - // empty patterns and comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - this.comment = true - return - } - if (!pattern) { - this.empty = true - return - } - - // step 1: figure out negation, etc. - this.parseNegate() - - // step 2: expand braces - var set = this.globSet = this.braceExpand() - - if (options.debug) this.debug = function debug() { console.error.apply(console, arguments) } - - this.debug(this.pattern, set) - - // step 3: now we have a set, so turn each one into a series of path-portion - // matching patterns. - // These will be regexps, except in the case of "**", which is - // set to the GLOBSTAR object for globstar behavior, - // and will not contain any / characters - set = this.globParts = set.map(function (s) { - return s.split(slashSplit) - }) - - this.debug(this.pattern, set) - - // glob --> regexps - set = set.map(function (s, si, set) { - return s.map(this.parse, this) - }, this) - - this.debug(this.pattern, set) - - // filter out everything that didn't compile properly. - set = set.filter(function (s) { - return s.indexOf(false) === -1 - }) - - this.debug(this.pattern, set) - - this.set = set -} - -Minimatch.prototype.parseNegate = parseNegate -function parseNegate () { - var pattern = this.pattern - var negate = false - var options = this.options - var negateOffset = 0 - - if (options.nonegate) return - - for (var i = 0, l = pattern.length - ; i < l && pattern.charAt(i) === '!' - ; i++) { - negate = !negate - negateOffset++ - } - - if (negateOffset) this.pattern = pattern.substr(negateOffset) - this.negate = negate -} - -// Brace expansion: -// a{b,c}d -> abd acd -// a{b,}c -> abc ac -// a{0..3}d -> a0d a1d a2d a3d -// a{b,c{d,e}f}g -> abg acdfg acefg -// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg -// -// Invalid sets are not expanded. -// a{2..}b -> a{2..}b -// a{b}c -> a{b}c -minimatch.braceExpand = function (pattern, options) { - return braceExpand(pattern, options) -} - -Minimatch.prototype.braceExpand = braceExpand - -function braceExpand (pattern, options) { - if (!options) { - if (this instanceof Minimatch) { - options = this.options - } else { - options = {} - } - } - - pattern = typeof pattern === 'undefined' - ? this.pattern : pattern - - assertValidPattern(pattern) - - // Thanks to Yeting Li for - // improving this regexp to avoid a ReDOS vulnerability. - if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { - // shortcut. no need to expand. - return [pattern] - } - - return expand(pattern) -} - -var MAX_PATTERN_LENGTH = 1024 * 64 -var assertValidPattern = function (pattern) { - if (typeof pattern !== 'string') { - throw new TypeError('invalid pattern') - } - - if (pattern.length > MAX_PATTERN_LENGTH) { - throw new TypeError('pattern is too long') - } -} - -// parse a component of the expanded set. -// At this point, no pattern may contain "/" in it -// so we're going to return a 2d array, where each entry is the full -// pattern, split on '/', and then turned into a regular expression. -// A regexp is made at the end which joins each array with an -// escaped /, and another full one which joins each regexp with |. -// -// Following the lead of Bash 4.1, note that "**" only has special meaning -// when it is the *only* thing in a path portion. Otherwise, any series -// of * is equivalent to a single *. Globstar behavior is enabled by -// default, and can be disabled by setting options.noglobstar. -Minimatch.prototype.parse = parse -var SUBPARSE = {} -function parse (pattern, isSub) { - assertValidPattern(pattern) - - var options = this.options - - // shortcuts - if (pattern === '**') { - if (!options.noglobstar) - return GLOBSTAR - else - pattern = '*' - } - if (pattern === '') return '' - - var re = '' - var hasMagic = !!options.nocase - var escaping = false - // ? => one single character - var patternListStack = [] - var negativeLists = [] - var stateChar - var inClass = false - var reClassStart = -1 - var classStart = -1 - // . and .. never match anything that doesn't start with ., - // even when options.dot is set. - var patternStart = pattern.charAt(0) === '.' ? '' // anything - // not (start or / followed by . or .. followed by / or end) - : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' - : '(?!\\.)' - var self = this - - function clearStateChar () { - if (stateChar) { - // we had some state-tracking character - // that wasn't consumed by this pass. - switch (stateChar) { - case '*': - re += star - hasMagic = true - break - case '?': - re += qmark - hasMagic = true - break - default: - re += '\\' + stateChar - break - } - self.debug('clearStateChar %j %j', stateChar, re) - stateChar = false - } - } - - for (var i = 0, len = pattern.length, c - ; (i < len) && (c = pattern.charAt(i)) - ; i++) { - this.debug('%s\t%s %s %j', pattern, i, re, c) - - // skip over any that are escaped. - if (escaping && reSpecials[c]) { - re += '\\' + c - escaping = false - continue - } - - switch (c) { - /* istanbul ignore next */ - case '/': { - // completely not allowed, even escaped. - // Should already be path-split by now. - return false - } - - case '\\': - clearStateChar() - escaping = true - continue - - // the various stateChar values - // for the "extglob" stuff. - case '?': - case '*': - case '+': - case '@': - case '!': - this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) - - // all of those are literals inside a class, except that - // the glob [!a] means [^a] in regexp - if (inClass) { - this.debug(' in class') - if (c === '!' && i === classStart + 1) c = '^' - re += c - continue - } - - // coalesce consecutive non-globstar * characters - if (c === '*' && stateChar === '*') continue - - // if we already have a stateChar, then it means - // that there was something like ** or +? in there. - // Handle the stateChar, then proceed with this one. - self.debug('call clearStateChar %j', stateChar) - clearStateChar() - stateChar = c - // if extglob is disabled, then +(asdf|foo) isn't a thing. - // just clear the statechar *now*, rather than even diving into - // the patternList stuff. - if (options.noext) clearStateChar() - continue - - case '(': - if (inClass) { - re += '(' - continue - } - - if (!stateChar) { - re += '\\(' - continue - } - - patternListStack.push({ - type: stateChar, - start: i - 1, - reStart: re.length, - open: plTypes[stateChar].open, - close: plTypes[stateChar].close - }) - // negation is (?:(?!js)[^/]*) - re += stateChar === '!' ? '(?:(?!(?:' : '(?:' - this.debug('plType %j %j', stateChar, re) - stateChar = false - continue - - case ')': - if (inClass || !patternListStack.length) { - re += '\\)' - continue - } - - clearStateChar() - hasMagic = true - var pl = patternListStack.pop() - // negation is (?:(?!js)[^/]*) - // The others are (?:) - re += pl.close - if (pl.type === '!') { - negativeLists.push(pl) - } - pl.reEnd = re.length - continue - - case '|': - if (inClass || !patternListStack.length || escaping) { - re += '\\|' - escaping = false - continue - } - - clearStateChar() - re += '|' - continue - - // these are mostly the same in regexp and glob - case '[': - // swallow any state-tracking char before the [ - clearStateChar() - - if (inClass) { - re += '\\' + c - continue - } - - inClass = true - classStart = i - reClassStart = re.length - re += c - continue - - case ']': - // a right bracket shall lose its special - // meaning and represent itself in - // a bracket expression if it occurs - // first in the list. -- POSIX.2 2.8.3.2 - if (i === classStart + 1 || !inClass) { - re += '\\' + c - escaping = false - continue - } - - // handle the case where we left a class open. - // "[z-a]" is valid, equivalent to "\[z-a\]" - // split where the last [ was, make sure we don't have - // an invalid re. if so, re-walk the contents of the - // would-be class to re-translate any characters that - // were passed through as-is - // TODO: It would probably be faster to determine this - // without a try/catch and a new RegExp, but it's tricky - // to do safely. For now, this is safe and works. - var cs = pattern.substring(classStart + 1, i) - try { - RegExp('[' + cs + ']') - } catch (er) { - // not a valid class! - var sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' - hasMagic = hasMagic || sp[1] - inClass = false - continue - } - - // finish up the class. - hasMagic = true - inClass = false - re += c - continue - - default: - // swallow any state char that wasn't consumed - clearStateChar() - - if (escaping) { - // no need - escaping = false - } else if (reSpecials[c] - && !(c === '^' && inClass)) { - re += '\\' - } - - re += c - - } // switch - } // for - - // handle the case where we left a class open. - // "[abc" is valid, equivalent to "\[abc" - if (inClass) { - // split where the last [ was, and escape it - // this is a huge pita. We now have to re-walk - // the contents of the would-be class to re-translate - // any characters that were passed through as-is - cs = pattern.substr(classStart + 1) - sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] - hasMagic = hasMagic || sp[1] - } - - // handle the case where we had a +( thing at the *end* - // of the pattern. - // each pattern list stack adds 3 chars, and we need to go through - // and escape any | chars that were passed through as-is for the regexp. - // Go through and escape them, taking care not to double-escape any - // | chars that were already escaped. - for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { - var tail = re.slice(pl.reStart + pl.open.length) - this.debug('setting tail', re, pl) - // maybe some even number of \, then maybe 1 \, followed by a | - tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { - if (!$2) { - // the | isn't already escaped, so escape it. - $2 = '\\' - } - - // need to escape all those slashes *again*, without escaping the - // one that we need for escaping the | character. As it works out, - // escaping an even number of slashes can be done by simply repeating - // it exactly after itself. That's why this trick works. - // - // I am sorry that you have to see this. - return $1 + $1 + $2 + '|' - }) - - this.debug('tail=%j\n %s', tail, tail, pl, re) - var t = pl.type === '*' ? star - : pl.type === '?' ? qmark - : '\\' + pl.type - - hasMagic = true - re = re.slice(0, pl.reStart) + t + '\\(' + tail - } - - // handle trailing things that only matter at the very end. - clearStateChar() - if (escaping) { - // trailing \\ - re += '\\\\' - } - - // only need to apply the nodot start if the re starts with - // something that could conceivably capture a dot - var addPatternStart = false - switch (re.charAt(0)) { - case '[': case '.': case '(': addPatternStart = true - } - - // Hack to work around lack of negative lookbehind in JS - // A pattern like: *.!(x).!(y|z) needs to ensure that a name - // like 'a.xyz.yz' doesn't match. So, the first negative - // lookahead, has to look ALL the way ahead, to the end of - // the pattern. - for (var n = negativeLists.length - 1; n > -1; n--) { - var nl = negativeLists[n] - - var nlBefore = re.slice(0, nl.reStart) - var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) - var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) - var nlAfter = re.slice(nl.reEnd) - - nlLast += nlAfter - - // Handle nested stuff like *(*.js|!(*.json)), where open parens - // mean that we should *not* include the ) in the bit that is considered - // "after" the negated section. - var openParensBefore = nlBefore.split('(').length - 1 - var cleanAfter = nlAfter - for (i = 0; i < openParensBefore; i++) { - cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') - } - nlAfter = cleanAfter - - var dollar = '' - if (nlAfter === '' && isSub !== SUBPARSE) { - dollar = '$' - } - var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast - re = newRe - } - - // if the re is not "" at this point, then we need to make sure - // it doesn't match against an empty path part. - // Otherwise a/* will match a/, which it should not. - if (re !== '' && hasMagic) { - re = '(?=.)' + re - } - - if (addPatternStart) { - re = patternStart + re - } - - // parsing just a piece of a larger pattern. - if (isSub === SUBPARSE) { - return [re, hasMagic] - } - - // skip the regexp for non-magical patterns - // unescape anything in it, though, so that it'll be - // an exact match against a file etc. - if (!hasMagic) { - return globUnescape(pattern) - } - - var flags = options.nocase ? 'i' : '' - try { - var regExp = new RegExp('^' + re + '$', flags) - } catch (er) /* istanbul ignore next - should be impossible */ { - // If it was an invalid regular expression, then it can't match - // anything. This trick looks for a character after the end of - // the string, which is of course impossible, except in multi-line - // mode, but it's not a /m regex. - return new RegExp('$.') - } - - regExp._glob = pattern - regExp._src = re - - return regExp -} - -minimatch.makeRe = function (pattern, options) { - return new Minimatch(pattern, options || {}).makeRe() -} - -Minimatch.prototype.makeRe = makeRe -function makeRe () { - if (this.regexp || this.regexp === false) return this.regexp - - // at this point, this.set is a 2d array of partial - // pattern strings, or "**". - // - // It's better to use .match(). This function shouldn't - // be used, really, but it's pretty convenient sometimes, - // when you just want to work with a regex. - var set = this.set - - if (!set.length) { - this.regexp = false - return this.regexp - } - var options = this.options - - var twoStar = options.noglobstar ? star - : options.dot ? twoStarDot - : twoStarNoDot - var flags = options.nocase ? 'i' : '' - - var re = set.map(function (pattern) { - return pattern.map(function (p) { - return (p === GLOBSTAR) ? twoStar - : (typeof p === 'string') ? regExpEscape(p) - : p._src - }).join('\\\/') - }).join('|') - - // must match entire pattern - // ending in a * or ** will make it less strict. - re = '^(?:' + re + ')$' - - // can match anything, as long as it's not this. - if (this.negate) re = '^(?!' + re + ').*$' - - try { - this.regexp = new RegExp(re, flags) - } catch (ex) /* istanbul ignore next - should be impossible */ { - this.regexp = false - } - return this.regexp -} - -minimatch.match = function (list, pattern, options) { - options = options || {} - var mm = new Minimatch(pattern, options) - list = list.filter(function (f) { - return mm.match(f) - }) - if (mm.options.nonull && !list.length) { - list.push(pattern) - } - return list -} - -Minimatch.prototype.match = function match (f, partial) { - if (typeof partial === 'undefined') partial = this.partial - this.debug('match', f, this.pattern) - // short-circuit in the case of busted things. - // comments, etc. - if (this.comment) return false - if (this.empty) return f === '' - - if (f === '/' && partial) return true - - var options = this.options - - // windows: need to use /, not \ - if (path.sep !== '/') { - f = f.split(path.sep).join('/') - } - - // treat the test path as a set of pathparts. - f = f.split(slashSplit) - this.debug(this.pattern, 'split', f) - - // just ONE of the pattern sets in this.set needs to match - // in order for it to be valid. If negating, then just one - // match means that we have failed. - // Either way, return on the first hit. - - var set = this.set - this.debug(this.pattern, 'set', set) - - // Find the basename of the path by looking for the last non-empty segment - var filename - var i - for (i = f.length - 1; i >= 0; i--) { - filename = f[i] - if (filename) break - } - - for (i = 0; i < set.length; i++) { - var pattern = set[i] - var file = f - if (options.matchBase && pattern.length === 1) { - file = [filename] - } - var hit = this.matchOne(file, pattern, partial) - if (hit) { - if (options.flipNegate) return true - return !this.negate - } - } - - // didn't get any hits. this is success if it's a negative - // pattern, failure otherwise. - if (options.flipNegate) return false - return this.negate -} - -// set partial to true to test if, for example, -// "/a/b" matches the start of "/*/b/*/d" -// Partial means, if you run out of file before you run -// out of pattern, then that's fine, as long as all -// the parts match. -Minimatch.prototype.matchOne = function (file, pattern, partial) { - if (pattern.indexOf(GLOBSTAR) !== -1) { - return this._matchGlobstar(file, pattern, partial, 0, 0) - } - return this._matchOne(file, pattern, partial, 0, 0) -} - -Minimatch.prototype._matchGlobstar = function (file, pattern, partial, fileIndex, patternIndex) { - var i - - // find first globstar from patternIndex - var firstgs = -1 - for (i = patternIndex; i < pattern.length; i++) { - if (pattern[i] === GLOBSTAR) { firstgs = i; break } - } - - // find last globstar - var lastgs = -1 - for (i = pattern.length - 1; i >= 0; i--) { - if (pattern[i] === GLOBSTAR) { lastgs = i; break } - } - - var head = pattern.slice(patternIndex, firstgs) - var body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs) - var tail = partial ? [] : pattern.slice(lastgs + 1) - - // check the head - if (head.length) { - var fileHead = file.slice(fileIndex, fileIndex + head.length) - if (!this._matchOne(fileHead, head, partial, 0, 0)) { - return false - } - fileIndex += head.length - } - - // check the tail - var fileTailMatch = 0 - if (tail.length) { - if (tail.length + fileIndex > file.length) return false - - var tailStart = file.length - tail.length - if (this._matchOne(file, tail, partial, tailStart, 0)) { - fileTailMatch = tail.length - } else { - // affordance for stuff like a/**/* matching a/b/ - if (file[file.length - 1] !== '' || - fileIndex + tail.length === file.length) { - return false - } - tailStart-- - if (!this._matchOne(file, tail, partial, tailStart, 0)) { - return false - } - fileTailMatch = tail.length + 1 - } - } - - // if body is empty (single ** between head and tail) - if (!body.length) { - var sawSome = !!fileTailMatch - for (i = fileIndex; i < file.length - fileTailMatch; i++) { - var f = String(file[i]) - sawSome = true - if (f === '.' || f === '..' || - (!this.options.dot && f.charAt(0) === '.')) { - return false - } - } - return partial || sawSome - } - - // split body into segments at each GLOBSTAR - var bodySegments = [[[], 0]] - var currentBody = bodySegments[0] - var nonGsParts = 0 - var nonGsPartsSums = [0] - for (var bi = 0; bi < body.length; bi++) { - var b = body[bi] - if (b === GLOBSTAR) { - nonGsPartsSums.push(nonGsParts) - currentBody = [[], 0] - bodySegments.push(currentBody) - } else { - currentBody[0].push(b) - nonGsParts++ - } - } - - var idx = bodySegments.length - 1 - var fileLength = file.length - fileTailMatch - for (var si = 0; si < bodySegments.length; si++) { - bodySegments[si][1] = fileLength - - (nonGsPartsSums[idx--] + bodySegments[si][0].length) - } - - return !!this._matchGlobStarBodySections( - file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch - ) -} - -// return false for "nope, not matching" -// return null for "not matching, cannot keep trying" -Minimatch.prototype._matchGlobStarBodySections = function ( - file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail -) { - var bs = bodySegments[bodyIndex] - if (!bs) { - // just make sure there are no bad dots - for (var i = fileIndex; i < file.length; i++) { - sawTail = true - var f = file[i] - if (f === '.' || f === '..' || - (!this.options.dot && f.charAt(0) === '.')) { - return false - } - } - return sawTail - } - - var body = bs[0] - var after = bs[1] - while (fileIndex <= after) { - var m = this._matchOne( - file.slice(0, fileIndex + body.length), - body, - partial, - fileIndex, - 0 - ) - // if limit exceeded, no match. intentional false negative, - // acceptable break in correctness for security. - if (m && globStarDepth < this.maxGlobstarRecursion) { - var sub = this._matchGlobStarBodySections( - file, bodySegments, - fileIndex + body.length, bodyIndex + 1, - partial, globStarDepth + 1, sawTail - ) - if (sub !== false) { - return sub - } - } - var f = file[fileIndex] - if (f === '.' || f === '..' || - (!this.options.dot && f.charAt(0) === '.')) { - return false - } - fileIndex++ - } - return partial || null -} - -Minimatch.prototype._matchOne = function (file, pattern, partial, fileIndex, patternIndex) { - var fi, pi, fl, pl - for ( - fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length - ; (fi < fl) && (pi < pl) - ; fi++, pi++ - ) { - this.debug('matchOne loop') - var p = pattern[pi] - var f = file[fi] - - this.debug(pattern, p, f) - - // should be impossible. - // some invalid regexp stuff in the set. - /* istanbul ignore if */ - if (p === false || p === GLOBSTAR) return false - - // something other than ** - // non-magic patterns just have to match exactly - // patterns with magic have been turned into regexps. - var hit - if (typeof p === 'string') { - hit = f === p - this.debug('string match', p, f, hit) - } else { - hit = f.match(p) - this.debug('pattern match', p, f, hit) - } - - if (!hit) return false - } - - // now either we fell off the end of the pattern, or we're done. - if (fi === fl && pi === pl) { - // ran out of pattern and filename at the same time. - // an exact hit! - return true - } else if (fi === fl) { - // ran out of file, but still had pattern left. - // this is ok if we're doing the match as part of - // a glob fs traversal. - return partial - } else /* istanbul ignore else */ if (pi === pl) { - // ran out of pattern, still have file left. - // this is only acceptable if we're on the very last - // empty segment of a file with a trailing slash. - // a/* should match a/b/ - return (fi === fl - 1) && (file[fi] === '') - } - - // should be unreachable. - /* istanbul ignore next */ - throw new Error('wtf?') -} - -// replace stuff like \* with * -function globUnescape (s) { - return s.replace(/\\(.)/g, '$1') -} - -function regExpEscape (s) { - return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') -} - - /***/ }), /***/ 70744: @@ -94316,6 +93007,2588 @@ function randomUUID() { /***/ }), +/***/ 62649: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.range = exports.balanced = void 0; +const balanced = (a, b, str) => { + const ma = a instanceof RegExp ? maybeMatch(a, str) : a; + const mb = b instanceof RegExp ? maybeMatch(b, str) : b; + const r = ma !== null && mb != null && (0, exports.range)(ma, mb, str); + return (r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + ma.length, r[1]), + post: str.slice(r[1] + mb.length), + }); +}; +exports.balanced = balanced; +const maybeMatch = (reg, str) => { + const m = str.match(reg); + return m ? m[0] : null; +}; +const range = (a, b, str) => { + let begs, beg, left, right = undefined, result; + let ai = str.indexOf(a); + let bi = str.indexOf(b, ai + 1); + let i = ai; + if (ai >= 0 && bi > 0) { + if (a === b) { + return [ai, bi]; + } + begs = []; + left = str.length; + while (i >= 0 && !result) { + if (i === ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } + else if (begs.length === 1) { + const r = begs.pop(); + if (r !== undefined) + result = [r, bi]; + } + else { + beg = begs.pop(); + if (beg !== undefined && beg < left) { + left = beg; + right = bi; + } + bi = str.indexOf(b, i + 1); + } + i = ai < bi && ai >= 0 ? ai : bi; + } + if (begs.length && right !== undefined) { + result = [left, right]; + } + } + return result; +}; +exports.range = range; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 38968: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.EXPANSION_MAX_LENGTH = exports.EXPANSION_MAX = void 0; +exports.expand = expand; +const balanced_match_1 = __nccwpck_require__(62649); +const escSlash = '\0SLASH' + Math.random() + '\0'; +const escOpen = '\0OPEN' + Math.random() + '\0'; +const escClose = '\0CLOSE' + Math.random() + '\0'; +const escComma = '\0COMMA' + Math.random() + '\0'; +const escPeriod = '\0PERIOD' + Math.random() + '\0'; +const escSlashPattern = new RegExp(escSlash, 'g'); +const escOpenPattern = new RegExp(escOpen, 'g'); +const escClosePattern = new RegExp(escClose, 'g'); +const escCommaPattern = new RegExp(escComma, 'g'); +const escPeriodPattern = new RegExp(escPeriod, 'g'); +const slashPattern = /\\\\/g; +const openPattern = /\\{/g; +const closePattern = /\\}/g; +const commaPattern = /\\,/g; +const periodPattern = /\\\./g; +exports.EXPANSION_MAX = 100_000; +// `EXPANSION_MAX` caps the *number* of expansions, but not their length. An +// input like `'{a,b}'.repeat(1500)` stays under that count - its output is +// truncated to 100k results - while making every result ~1500 characters +// long. The result set, and the intermediate arrays built while combining +// brace sets, then grow large enough to exhaust memory and crash the process +// (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of +// characters the accumulator may hold at any point, so memory stays flat no +// matter how many brace groups are chained. The limit sits well above any +// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M +// characters) so legitimate input is unaffected. +exports.EXPANSION_MAX_LENGTH = 4_000_000; +function numeric(str) { + return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0); +} +function escapeBraces(str) { + return str + .replace(slashPattern, escSlash) + .replace(openPattern, escOpen) + .replace(closePattern, escClose) + .replace(commaPattern, escComma) + .replace(periodPattern, escPeriod); +} +function unescapeBraces(str) { + return str + .replace(escSlashPattern, '\\') + .replace(escOpenPattern, '{') + .replace(escClosePattern, '}') + .replace(escCommaPattern, ',') + .replace(escPeriodPattern, '.'); +} +/** + * Basically just str.split(","), but handling cases + * where we have nested braced sections, which should be + * treated as individual members, like {a,{b,c},d} + */ +function parseCommaParts(str) { + if (!str) { + return ['']; + } + const parts = []; + const m = (0, balanced_match_1.balanced)('{', '}', str); + if (!m) { + return str.split(','); + } + const { pre, body, post } = m; + const p = pre.split(','); + p[p.length - 1] += '{' + body + '}'; + const postParts = parseCommaParts(post); + if (post.length) { + ; + p[p.length - 1] += postParts.shift(); + p.push.apply(p, postParts); + } + parts.push.apply(parts, p); + return parts; +} +function expand(str, options = {}) { + if (!str) { + return []; + } + const { max = exports.EXPANSION_MAX, maxLength = exports.EXPANSION_MAX_LENGTH } = options; + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.slice(0, 2) === '{}') { + str = '\\{\\}' + str.slice(2); + } + return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces); +} +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} +// Build `{ acc[a] + pre + values[v] }` for every combination, capping the +// number of results at `max` and the total number of characters at `maxLength`. +// This is the one place output grows, so bounding it here keeps the single +// accumulator - and therefore memory - flat regardless of how many brace groups +// are combined (CVE-2026-14257). +function combine(acc, pre, values, max, maxLength, dropEmpties) { + const out = []; + let length = 0; + for (let a = 0; a < acc.length; a++) { + for (let v = 0; v < values.length; v++) { + if (out.length >= max) + return out; + const expansion = acc[a] + pre + values[v]; + // Bash drops empty results at the top level. Skip them before they count + // against `max`, so `max` bounds the number of *kept* results. + if (dropEmpties && !expansion) + continue; + if (length + expansion.length > maxLength) + return out; + out.push(expansion); + length += expansion.length; + } + } + return out; +} +// The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`) +// sequence body. +function expandSequence(body, isAlphaSequence, max) { + const n = body.split(/\.\./); + const N = []; + // A sequence body always splits into two or three parts, but the compiler + // can't know that. + /* c8 ignore start */ + if (n[0] === undefined || n[1] === undefined) { + return N; + } + /* c8 ignore stop */ + const x = numeric(n[0]); + const y = numeric(n[1]); + const width = Math.max(n[0].length, n[1].length); + let incr = n.length === 3 && n[2] !== undefined ? + Math.max(Math.abs(numeric(n[2])), 1) + : 1; + let test = lte; + const reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + const pad = n.some(isPadded); + for (let i = x; test(i, y) && N.length < max; i += incr) { + let c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') { + c = ''; + } + } + else { + c = String(i); + if (pad) { + const need = width - c.length; + if (need > 0) { + const z = new Array(need + 1).join('0'); + if (i < 0) { + c = '-' + z + c.slice(1); + } + else { + c = z + c; + } + } + } + } + N.push(c); + } + return N; +} +function expand_(str, max, maxLength, isTop) { + // Consume the string's top-level brace groups left to right, threading a + // running set of combined prefixes (`acc`). Expanding the tail iteratively - + // rather than recursing on `m.post` once per group - keeps the native stack + // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no + // longer overflow the stack, and leaves a single accumulator whose size + // `maxLength` bounds directly (CVE-2026-14257). + let acc = ['']; + // Bash drops empty results, but only when the *first* top-level group is a + // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop + // is on the final strings, so it is applied to whichever `combine` produces + // them (the one with no brace set left in the tail). + let dropEmpties = false; + let firstGroup = true; + for (;;) { + const m = (0, balanced_match_1.balanced)('{', '}', str); + // No brace set left: the rest of the string is literal. + if (!m) { + return combine(acc, str, [''], max, maxLength, dropEmpties); + } + // no need to expand pre, since it is guaranteed to be free of brace-sets + const pre = m.pre; + if (/\$$/.test(pre)) { + acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length); + firstGroup = false; + if (!m.post.length) + break; + str = m.post; + continue; + } + const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + const isSequence = isNumericSequence || isAlphaSequence; + const isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,(?!,).*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + isTop = true; + continue; + } + // Nothing here expands, so the whole remaining string is literal. + return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties); + } + if (firstGroup) { + dropEmpties = isTop && !isSequence; + firstGroup = false; + } + let values; + if (isSequence) { + values = expandSequence(m.body, isAlphaSequence, max); + } + else { + let n = parseCommaParts(m.body); + if (n.length === 1 && n[0] !== undefined) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand_(n[0], max, maxLength, false).map(embrace); + //XXX is this necessary? Can't seem to hit it in tests. + /* c8 ignore start */ + if (n.length === 1) { + acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length); + if (!m.post.length) + break; + str = m.post; + continue; + } + /* c8 ignore stop */ + } + values = []; + for (let j = 0; j < n.length; j++) { + values.push.apply(values, expand_(n[j], max, maxLength, false)); + } + } + acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length); + if (!m.post.length) + break; + str = m.post; + } + return acc; +} +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 57305: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.assertValidPattern = void 0; +const MAX_PATTERN_LENGTH = 1024 * 64; +const assertValidPattern = (pattern) => { + if (typeof pattern !== 'string') { + throw new TypeError('invalid pattern'); + } + if (pattern.length > MAX_PATTERN_LENGTH) { + throw new TypeError('pattern is too long'); + } +}; +exports.assertValidPattern = assertValidPattern; +//# sourceMappingURL=assert-valid-pattern.js.map + +/***/ }), + +/***/ 31803: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +// parse a single path portion +var _a; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.AST = void 0; +const brace_expressions_js_1 = __nccwpck_require__(11090); +const unescape_js_1 = __nccwpck_require__(70851); +const types = new Set(['!', '?', '+', '*', '@']); +const isExtglobType = (c) => types.has(c); +const isExtglobAST = (c) => isExtglobType(c.type); +// Map of which extglob types can adopt the children of a nested extglob +// +// anything but ! can adopt a matching type: +// +(a|+(b|c)|d) => +(a|b|c|d) +// *(a|*(b|c)|d) => *(a|b|c|d) +// @(a|@(b|c)|d) => @(a|b|c|d) +// ?(a|?(b|c)|d) => ?(a|b|c|d) +// +// * can adopt anything, because 0 or repetition is allowed +// *(a|?(b|c)|d) => *(a|b|c|d) +// *(a|+(b|c)|d) => *(a|b|c|d) +// *(a|@(b|c)|d) => *(a|b|c|d) +// +// + can adopt @, because 1 or repetition is allowed +// +(a|@(b|c)|d) => +(a|b|c|d) +// +// + and @ CANNOT adopt *, because 0 would be allowed +// +(a|*(b|c)|d) => would match "", on *(b|c) +// @(a|*(b|c)|d) => would match "", on *(b|c) +// +// + and @ CANNOT adopt ?, because 0 would be allowed +// +(a|?(b|c)|d) => would match "", on ?(b|c) +// @(a|?(b|c)|d) => would match "", on ?(b|c) +// +// ? can adopt @, because 0 or 1 is allowed +// ?(a|@(b|c)|d) => ?(a|b|c|d) +// +// ? and @ CANNOT adopt * or +, because >1 would be allowed +// ?(a|*(b|c)|d) => would match bbb on *(b|c) +// @(a|*(b|c)|d) => would match bbb on *(b|c) +// ?(a|+(b|c)|d) => would match bbb on +(b|c) +// @(a|+(b|c)|d) => would match bbb on +(b|c) +// +// ! CANNOT adopt ! (nothing else can either) +// !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c) +// +// ! can adopt @ +// !(a|@(b|c)|d) => !(a|b|c|d) +// +// ! CANNOT adopt * +// !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed +// +// ! CANNOT adopt + +// !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed +// +// ! CANNOT adopt ? +// x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x" +const adoptionMap = new Map([ + ['!', ['@']], + ['?', ['?', '@']], + ['@', ['@']], + ['*', ['*', '+', '?', '@']], + ['+', ['+', '@']], +]); +// nested extglobs that can be adopted in, but with the addition of +// a blank '' element. +const adoptionWithSpaceMap = new Map([ + ['!', ['?']], + ['@', ['?']], + ['+', ['?', '*']], +]); +// union of the previous two maps +const adoptionAnyMap = new Map([ + ['!', ['?', '@']], + ['?', ['?', '@']], + ['@', ['?', '@']], + ['*', ['*', '+', '?', '@']], + ['+', ['+', '@', '?', '*']], +]); +// Extglobs that can take over their parent if they are the only child +// the key is parent, value maps child to resulting extglob parent type +// '@' is omitted because it's a special case. An `@` extglob with a single +// member can always be usurped by that subpattern. +const usurpMap = new Map([ + ['!', new Map([['!', '@']])], + [ + '?', + new Map([ + ['*', '*'], + ['+', '*'], + ]), + ], + [ + '@', + new Map([ + ['!', '!'], + ['?', '?'], + ['@', '@'], + ['*', '*'], + ['+', '+'], + ]), + ], + [ + '+', + new Map([ + ['?', '*'], + ['*', '*'], + ]), + ], +]); +// Patterns that get prepended to bind to the start of either the +// entire string, or just a single path portion, to prevent dots +// and/or traversal patterns, when needed. +// Exts don't need the ^ or / bit, because the root binds that already. +const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))'; +const startNoDot = '(?!\\.)'; +// characters that indicate a start of pattern needs the "no dots" bit, +// because a dot *might* be matched. ( is not in the list, because in +// the case of a child extglob, it will handle the prevention itself. +const addPatternStart = new Set(['[', '.']); +// cases where traversal is A-OK, no dot prevention needed +const justDots = new Set(['..', '.']); +const reSpecials = new Set('().*{}+?[]^$\\!'); +const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); +// any single thing other than / +const qmark = '[^/]'; +// * => any number of characters +const star = qmark + '*?'; +// use + when we need to ensure that *something* matches, because the * is +// the only thing in the path portion. +const starNoEmpty = qmark + '+?'; +// remove the \ chars that we added if we end up doing a nonmagic compare +// const deslash = (s: string) => s.replace(/\\(.)/g, '$1') +let ID = 0; +class AST { + type; + #root; + #hasMagic; + #uflag = false; + #parts = []; + #parent; + #parentIndex; + #negs; + #filledNegs = false; + #options; + #toString; + // set to true if it's an extglob with no children + // (which really means one child of '') + #emptyExt = false; + id = ++ID; + get depth() { + return (this.#parent?.depth ?? -1) + 1; + } + [Symbol.for('nodejs.util.inspect.custom')]() { + return { + '@@type': 'AST', + id: this.id, + type: this.type, + root: this.#root.id, + parent: this.#parent?.id, + depth: this.depth, + partsLength: this.#parts.length, + parts: this.#parts, + }; + } + constructor(type, parent, options = {}) { + this.type = type; + // extglobs are inherently magical + if (type) + this.#hasMagic = true; + this.#parent = parent; + this.#root = this.#parent ? this.#parent.#root : this; + this.#options = this.#root === this ? options : this.#root.#options; + this.#negs = this.#root === this ? [] : this.#root.#negs; + if (type === '!' && !this.#root.#filledNegs) + this.#negs.push(this); + this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0; + } + get hasMagic() { + /* c8 ignore start */ + if (this.#hasMagic !== undefined) + return this.#hasMagic; + /* c8 ignore stop */ + for (const p of this.#parts) { + if (typeof p === 'string') + continue; + if (p.type || p.hasMagic) + return (this.#hasMagic = true); + } + // note: will be undefined until we generate the regexp src and find out + return this.#hasMagic; + } + // reconstructs the pattern + toString() { + return (this.#toString !== undefined ? this.#toString + : !this.type ? + (this.#toString = this.#parts.map(p => String(p)).join('')) + : (this.#toString = + this.type + + '(' + + this.#parts.map(p => String(p)).join('|') + + ')')); + } + #fillNegs() { + /* c8 ignore start */ + if (this !== this.#root) + throw new Error('should only call on root'); + if (this.#filledNegs) + return this; + /* c8 ignore stop */ + // call toString() once to fill this out + this.toString(); + this.#filledNegs = true; + let n; + while ((n = this.#negs.pop())) { + if (n.type !== '!') + continue; + // walk up the tree, appending everthing that comes AFTER parentIndex + let p = n; + let pp = p.#parent; + while (pp) { + for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) { + for (const part of n.#parts) { + /* c8 ignore start */ + if (typeof part === 'string') { + throw new Error('string part in extglob AST??'); + } + /* c8 ignore stop */ + part.copyIn(pp.#parts[i]); + } + } + p = pp; + pp = p.#parent; + } + } + return this; + } + push(...parts) { + for (const p of parts) { + if (p === '') + continue; + /* c8 ignore start */ + if (typeof p !== 'string' && + !(p instanceof _a && p.#parent === this)) { + throw new Error('invalid part: ' + p); + } + /* c8 ignore stop */ + this.#parts.push(p); + } + } + toJSON() { + const ret = this.type === null ? + this.#parts + .slice() + .map(p => (typeof p === 'string' ? p : p.toJSON())) + : [this.type, ...this.#parts.map(p => p.toJSON())]; + if (this.isStart() && !this.type) + ret.unshift([]); + if (this.isEnd() && + (this === this.#root || + (this.#root.#filledNegs && this.#parent?.type === '!'))) { + ret.push({}); + } + return ret; + } + isStart() { + if (this.#root === this) + return true; + // if (this.type) return !!this.#parent?.isStart() + if (!this.#parent?.isStart()) + return false; + if (this.#parentIndex === 0) + return true; + // if everything AHEAD of this is a negation, then it's still the "start" + const p = this.#parent; + for (let i = 0; i < this.#parentIndex; i++) { + const pp = p.#parts[i]; + if (!(pp instanceof _a && pp.type === '!')) { + return false; + } + } + return true; + } + isEnd() { + if (this.#root === this) + return true; + if (this.#parent?.type === '!') + return true; + if (!this.#parent?.isEnd()) + return false; + if (!this.type) + return this.#parent?.isEnd(); + // if not root, it'll always have a parent + /* c8 ignore start */ + const pl = this.#parent ? this.#parent.#parts.length : 0; + /* c8 ignore stop */ + return this.#parentIndex === pl - 1; + } + copyIn(part) { + if (typeof part === 'string') + this.push(part); + else + this.push(part.clone(this)); + } + clone(parent) { + const c = new _a(this.type, parent); + for (const p of this.#parts) { + c.copyIn(p); + } + return c; + } + static #parseAST(str, ast, pos, opt, extDepth) { + const maxDepth = opt.maxExtglobRecursion ?? 2; + let escaping = false; + let inBrace = false; + let braceStart = -1; + let braceNeg = false; + if (ast.type === null) { + // outside of a extglob, append until we find a start + let i = pos; + let acc = ''; + while (i < str.length) { + const c = str.charAt(i++); + // still accumulate escapes at this point, but we do ignore + // starts that are escaped + if (escaping || c === '\\') { + escaping = !escaping; + acc += c; + continue; + } + if (inBrace) { + if (i === braceStart + 1) { + if (c === '^' || c === '!') { + braceNeg = true; + } + } + else if (c === ']' && !(i === braceStart + 2 && braceNeg)) { + inBrace = false; + } + acc += c; + continue; + } + else if (c === '[') { + inBrace = true; + braceStart = i; + braceNeg = false; + acc += c; + continue; + } + // we don't have to check for adoption here, because that's + // done at the other recursion point. + const doRecurse = !opt.noext && + isExtglobType(c) && + str.charAt(i) === '(' && + extDepth <= maxDepth; + if (doRecurse) { + ast.push(acc); + acc = ''; + const ext = new _a(c, ast); + i = _a.#parseAST(str, ext, i, opt, extDepth + 1); + ast.push(ext); + continue; + } + acc += c; + } + ast.push(acc); + return i; + } + // some kind of extglob, pos is at the ( + // find the next | or ) + let i = pos + 1; + let part = new _a(null, ast); + const parts = []; + let acc = ''; + while (i < str.length) { + const c = str.charAt(i++); + // still accumulate escapes at this point, but we do ignore + // starts that are escaped + if (escaping || c === '\\') { + escaping = !escaping; + acc += c; + continue; + } + if (inBrace) { + if (i === braceStart + 1) { + if (c === '^' || c === '!') { + braceNeg = true; + } + } + else if (c === ']' && !(i === braceStart + 2 && braceNeg)) { + inBrace = false; + } + acc += c; + continue; + } + else if (c === '[') { + inBrace = true; + braceStart = i; + braceNeg = false; + acc += c; + continue; + } + const doRecurse = !opt.noext && + isExtglobType(c) && + str.charAt(i) === '(' && + /* c8 ignore start - the maxDepth is sufficient here */ + (extDepth <= maxDepth || (ast && ast.#canAdoptType(c))); + /* c8 ignore stop */ + if (doRecurse) { + const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1; + part.push(acc); + acc = ''; + const ext = new _a(c, part); + part.push(ext); + i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd); + continue; + } + if (c === '|') { + part.push(acc); + acc = ''; + parts.push(part); + part = new _a(null, ast); + continue; + } + if (c === ')') { + if (acc === '' && ast.#parts.length === 0) { + ast.#emptyExt = true; + } + part.push(acc); + acc = ''; + ast.push(...parts, part); + return i; + } + acc += c; + } + // unfinished extglob + // if we got here, it was a malformed extglob! not an extglob, but + // maybe something else in there. + ast.type = null; + ast.#hasMagic = undefined; + ast.#parts = [str.substring(pos - 1)]; + return i; + } + #canAdoptWithSpace(child) { + return this.#canAdopt(child, adoptionWithSpaceMap); + } + #canAdopt(child, map = adoptionMap) { + if (!child || + typeof child !== 'object' || + child.type !== null || + child.#parts.length !== 1 || + this.type === null) { + return false; + } + const gc = child.#parts[0]; + if (!gc || typeof gc !== 'object' || gc.type === null) { + return false; + } + return this.#canAdoptType(gc.type, map); + } + #canAdoptType(c, map = adoptionAnyMap) { + return !!map.get(this.type)?.includes(c); + } + #adoptWithSpace(child, index) { + const gc = child.#parts[0]; + const blank = new _a(null, gc, this.options); + blank.#parts.push(''); + gc.push(blank); + this.#adopt(child, index); + } + #adopt(child, index) { + const gc = child.#parts[0]; + this.#parts.splice(index, 1, ...gc.#parts); + for (const p of gc.#parts) { + if (typeof p === 'object') + p.#parent = this; + } + this.#toString = undefined; + } + #canUsurpType(c) { + const m = usurpMap.get(this.type); + return !!m?.has(c); + } + #canUsurp(child) { + if (!child || + typeof child !== 'object' || + child.type !== null || + child.#parts.length !== 1 || + this.type === null || + this.#parts.length !== 1) { + return false; + } + const gc = child.#parts[0]; + if (!gc || typeof gc !== 'object' || gc.type === null) { + return false; + } + return this.#canUsurpType(gc.type); + } + #usurp(child) { + const m = usurpMap.get(this.type); + const gc = child.#parts[0]; + const nt = m?.get(gc.type); + /* c8 ignore start - impossible */ + if (!nt) + return false; + /* c8 ignore stop */ + this.#parts = gc.#parts; + for (const p of this.#parts) { + if (typeof p === 'object') { + p.#parent = this; + } + } + this.type = nt; + this.#toString = undefined; + this.#emptyExt = false; + } + static fromGlob(pattern, options = {}) { + const ast = new _a(null, undefined, options); + _a.#parseAST(pattern, ast, 0, options, 0); + return ast; + } + // returns the regular expression if there's magic, or the unescaped + // string if not. + toMMPattern() { + // should only be called on root + /* c8 ignore start */ + if (this !== this.#root) + return this.#root.toMMPattern(); + /* c8 ignore stop */ + const glob = this.toString(); + const [re, body, hasMagic, uflag] = this.toRegExpSource(); + // if we're in nocase mode, and not nocaseMagicOnly, then we do + // still need a regular expression if we have to case-insensitively + // match capital/lowercase characters. + const anyMagic = hasMagic || + this.#hasMagic || + (this.#options.nocase && + !this.#options.nocaseMagicOnly && + glob.toUpperCase() !== glob.toLowerCase()); + if (!anyMagic) { + return body; + } + const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : ''); + return Object.assign(new RegExp(`^${re}$`, flags), { + _src: re, + _glob: glob, + }); + } + get options() { + return this.#options; + } + // returns the string match, the regexp source, whether there's magic + // in the regexp (so a regular expression is required) and whether or + // not the uflag is needed for the regular expression (for posix classes) + // TODO: instead of injecting the start/end at this point, just return + // the BODY of the regexp, along with the start/end portions suitable + // for binding the start/end in either a joined full-path makeRe context + // (where we bind to (^|/), or a standalone matchPart context (where + // we bind to ^, and not /). Otherwise slashes get duped! + // + // In part-matching mode, the start is: + // - if not isStart: nothing + // - if traversal possible, but not allowed: ^(?!\.\.?$) + // - if dots allowed or not possible: ^ + // - if dots possible and not allowed: ^(?!\.) + // end is: + // - if not isEnd(): nothing + // - else: $ + // + // In full-path matching mode, we put the slash at the START of the + // pattern, so start is: + // - if first pattern: same as part-matching mode + // - if not isStart(): nothing + // - if traversal possible, but not allowed: /(?!\.\.?(?:$|/)) + // - if dots allowed or not possible: / + // - if dots possible and not allowed: /(?!\.) + // end is: + // - if last pattern, same as part-matching mode + // - else nothing + // + // Always put the (?:$|/) on negated tails, though, because that has to be + // there to bind the end of the negated pattern portion, and it's easier to + // just stick it in now rather than try to inject it later in the middle of + // the pattern. + // + // We can just always return the same end, and leave it up to the caller + // to know whether it's going to be used joined or in parts. + // And, if the start is adjusted slightly, can do the same there: + // - if not isStart: nothing + // - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$) + // - if dots allowed or not possible: (?:/|^) + // - if dots possible and not allowed: (?:/|^)(?!\.) + // + // But it's better to have a simpler binding without a conditional, for + // performance, so probably better to return both start options. + // + // Then the caller just ignores the end if it's not the first pattern, + // and the start always gets applied. + // + // But that's always going to be $ if it's the ending pattern, or nothing, + // so the caller can just attach $ at the end of the pattern when building. + // + // So the todo is: + // - better detect what kind of start is needed + // - return both flavors of starting pattern + // - attach $ at the end of the pattern when creating the actual RegExp + // + // Ah, but wait, no, that all only applies to the root when the first pattern + // is not an extglob. If the first pattern IS an extglob, then we need all + // that dot prevention biz to live in the extglob portions, because eg + // +(*|.x*) can match .xy but not .yx. + // + // So, return the two flavors if it's #root and the first child is not an + // AST, otherwise leave it to the child AST to handle it, and there, + // use the (?:^|/) style of start binding. + // + // Even simplified further: + // - Since the start for a join is eg /(?!\.) and the start for a part + // is ^(?!\.), we can just prepend (?!\.) to the pattern (either root + // or start or whatever) and prepend ^ or / at the Regexp construction. + toRegExpSource(allowDot) { + const dot = allowDot ?? !!this.#options.dot; + if (this.#root === this) { + this.#flatten(); + this.#fillNegs(); + } + if (!isExtglobAST(this)) { + const noEmpty = this.isStart() && + this.isEnd() && + !this.#parts.some(s => typeof s !== 'string'); + const src = this.#parts + .map(p => { + const [re, _, hasMagic, uflag] = typeof p === 'string' ? + _a.#parseGlob(p, this.#hasMagic, noEmpty) + : p.toRegExpSource(allowDot); + this.#hasMagic = this.#hasMagic || hasMagic; + this.#uflag = this.#uflag || uflag; + return re; + }) + .join(''); + let start = ''; + if (this.isStart()) { + if (typeof this.#parts[0] === 'string') { + // this is the string that will match the start of the pattern, + // so we need to protect against dots and such. + // '.' and '..' cannot match unless the pattern is that exactly, + // even if it starts with . or dot:true is set. + const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]); + if (!dotTravAllowed) { + const aps = addPatternStart; + // check if we have a possibility of matching . or .., + // and prevent that. + const needNoTrav = + // dots are allowed, and the pattern starts with [ or . + (dot && aps.has(src.charAt(0))) || + // the pattern starts with \., and then [ or . + (src.startsWith('\\.') && aps.has(src.charAt(2))) || + // the pattern starts with \.\., and then [ or . + (src.startsWith('\\.\\.') && aps.has(src.charAt(4))); + // no need to prevent dots if it can't match a dot, or if a + // sub-pattern will be preventing it anyway. + const needNoDot = !dot && !allowDot && aps.has(src.charAt(0)); + start = + needNoTrav ? startNoTraversal + : needNoDot ? startNoDot + : ''; + } + } + } + // append the "end of path portion" pattern to negation tails + let end = ''; + if (this.isEnd() && + this.#root.#filledNegs && + this.#parent?.type === '!') { + end = '(?:$|\\/)'; + } + const final = start + src + end; + return [ + final, + (0, unescape_js_1.unescape)(src), + (this.#hasMagic = !!this.#hasMagic), + this.#uflag, + ]; + } + // We need to calculate the body *twice* if it's a repeat pattern + // at the start, once in nodot mode, then again in dot mode, so a + // pattern like *(?) can match 'x.y' + const repeated = this.type === '*' || this.type === '+'; + // some kind of extglob + const start = this.type === '!' ? '(?:(?!(?:' : '(?:'; + let body = this.#partsToRegExp(dot); + if (this.isStart() && this.isEnd() && !body && this.type !== '!') { + // invalid extglob, has to at least be *something* present, if it's + // the entire path portion. + const s = this.toString(); + const me = this; + me.#parts = [s]; + me.type = null; + me.#hasMagic = undefined; + return [s, (0, unescape_js_1.unescape)(this.toString()), false, false]; + } + let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ? + '' + : this.#partsToRegExp(true); + if (bodyDotAllowed === body) { + bodyDotAllowed = ''; + } + if (bodyDotAllowed) { + body = `(?:${body})(?:${bodyDotAllowed})*?`; + } + // an empty !() is exactly equivalent to a starNoEmpty + let final = ''; + if (this.type === '!' && this.#emptyExt) { + final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty; + } + else { + const close = this.type === '!' ? + // !() must match something,but !(x) can match '' + '))' + + (this.isStart() && !dot && !allowDot ? startNoDot : '') + + star + + ')' + : this.type === '@' ? ')' + : this.type === '?' ? ')?' + : this.type === '+' && bodyDotAllowed ? ')' + : this.type === '*' && bodyDotAllowed ? `)?` + : `)${this.type}`; + final = start + body + close; + } + return [ + final, + (0, unescape_js_1.unescape)(body), + (this.#hasMagic = !!this.#hasMagic), + this.#uflag, + ]; + } + #flatten() { + if (!isExtglobAST(this)) { + for (const p of this.#parts) { + if (typeof p === 'object') { + p.#flatten(); + } + } + } + else { + // do up to 10 passes to flatten as much as possible + let iterations = 0; + let done = false; + do { + done = true; + for (let i = 0; i < this.#parts.length; i++) { + const c = this.#parts[i]; + if (typeof c === 'object') { + c.#flatten(); + if (this.#canAdopt(c)) { + done = false; + this.#adopt(c, i); + } + else if (this.#canAdoptWithSpace(c)) { + done = false; + this.#adoptWithSpace(c, i); + } + else if (this.#canUsurp(c)) { + done = false; + this.#usurp(c); + } + } + } + } while (!done && ++iterations < 10); + } + this.#toString = undefined; + } + #partsToRegExp(dot) { + return this.#parts + .map(p => { + // extglob ASTs should only contain parent ASTs + /* c8 ignore start */ + if (typeof p === 'string') { + throw new Error('string type in extglob ast??'); + } + /* c8 ignore stop */ + // can ignore hasMagic, because extglobs are already always magic + const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot); + this.#uflag = this.#uflag || uflag; + return re; + }) + .filter(p => !(this.isStart() && this.isEnd()) || !!p) + .join('|'); + } + static #parseGlob(glob, hasMagic, noEmpty = false) { + let escaping = false; + let re = ''; + let uflag = false; + // multiple stars that aren't globstars coalesce into one * + let inStar = false; + for (let i = 0; i < glob.length; i++) { + const c = glob.charAt(i); + if (escaping) { + escaping = false; + re += (reSpecials.has(c) ? '\\' : '') + c; + continue; + } + if (c === '*') { + if (inStar) + continue; + inStar = true; + re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star; + hasMagic = true; + continue; + } + else { + inStar = false; + } + if (c === '\\') { + if (i === glob.length - 1) { + re += '\\\\'; + } + else { + escaping = true; + } + continue; + } + if (c === '[') { + const [src, needUflag, consumed, magic] = (0, brace_expressions_js_1.parseClass)(glob, i); + if (consumed) { + re += src; + uflag = uflag || needUflag; + i += consumed - 1; + hasMagic = hasMagic || magic; + continue; + } + } + if (c === '?') { + re += qmark; + hasMagic = true; + continue; + } + re += regExpEscape(c); + } + return [re, (0, unescape_js_1.unescape)(glob), !!hasMagic, uflag]; + } +} +exports.AST = AST; +_a = AST; +//# sourceMappingURL=ast.js.map + +/***/ }), + +/***/ 11090: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +// translate the various posix character classes into unicode properties +// this works across all unicode locales +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.parseClass = void 0; +// { : [, /u flag required, negated] +const posixClasses = { + '[:alnum:]': ['\\p{L}\\p{Nl}\\p{Nd}', true], + '[:alpha:]': ['\\p{L}\\p{Nl}', true], + '[:ascii:]': ['\\x' + '00-\\x' + '7f', false], + '[:blank:]': ['\\p{Zs}\\t', true], + '[:cntrl:]': ['\\p{Cc}', true], + '[:digit:]': ['\\p{Nd}', true], + '[:graph:]': ['\\p{Z}\\p{C}', true, true], + '[:lower:]': ['\\p{Ll}', true], + '[:print:]': ['\\p{C}', true], + '[:punct:]': ['\\p{P}', true], + '[:space:]': ['\\p{Z}\\t\\r\\n\\v\\f', true], + '[:upper:]': ['\\p{Lu}', true], + '[:word:]': ['\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}', true], + '[:xdigit:]': ['A-Fa-f0-9', false], +}; +// only need to escape a few things inside of brace expressions +// escapes: [ \ ] - +const braceEscape = (s) => s.replace(/[[\]\\-]/g, '\\$&'); +// escape all regexp magic characters +const regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); +// everything has already been escaped, we just have to join +const rangesToString = (ranges) => ranges.join(''); +// takes a glob string at a posix brace expression, and returns +// an equivalent regular expression source, and boolean indicating +// whether the /u flag needs to be applied, and the number of chars +// consumed to parse the character class. +// This also removes out of order ranges, and returns ($.) if the +// entire class just no good. +const parseClass = (glob, position) => { + const pos = position; + /* c8 ignore start */ + if (glob.charAt(pos) !== '[') { + throw new Error('not in a brace expression'); + } + /* c8 ignore stop */ + const ranges = []; + const negs = []; + let i = pos + 1; + let sawStart = false; + let uflag = false; + let escaping = false; + let negate = false; + let endPos = pos; + let rangeStart = ''; + WHILE: while (i < glob.length) { + const c = glob.charAt(i); + if ((c === '!' || c === '^') && i === pos + 1) { + negate = true; + i++; + continue; + } + if (c === ']' && sawStart && !escaping) { + endPos = i + 1; + break; + } + sawStart = true; + if (c === '\\') { + if (!escaping) { + escaping = true; + i++; + continue; + } + // escaped \ char, fall through and treat like normal char + } + if (c === '[' && !escaping) { + // either a posix class, a collation equivalent, or just a [ + for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) { + if (glob.startsWith(cls, i)) { + // invalid, [a-[] is fine, but not [a-[:alpha]] + if (rangeStart) { + return ['$.', false, glob.length - pos, true]; + } + i += cls.length; + if (neg) + negs.push(unip); + else + ranges.push(unip); + uflag = uflag || u; + continue WHILE; + } + } + } + // now it's just a normal character, effectively + escaping = false; + if (rangeStart) { + // throw this range away if it's not valid, but others + // can still match. + if (c > rangeStart) { + ranges.push(braceEscape(rangeStart) + '-' + braceEscape(c)); + } + else if (c === rangeStart) { + ranges.push(braceEscape(c)); + } + rangeStart = ''; + i++; + continue; + } + // now might be the start of a range. + // can be either c-d or c-] or c] or c] at this point + if (glob.startsWith('-]', i + 1)) { + ranges.push(braceEscape(c + '-')); + i += 2; + continue; + } + if (glob.startsWith('-', i + 1)) { + rangeStart = c; + i += 2; + continue; + } + // not the start of a range, just a single character + ranges.push(braceEscape(c)); + i++; + } + if (endPos < i) { + // didn't see the end of the class, not a valid class, + // but might still be valid as a literal match. + return ['', false, 0, false]; + } + // if we got no ranges and no negates, then we have a range that + // cannot possibly match anything, and that poisons the whole glob + if (!ranges.length && !negs.length) { + return ['$.', false, glob.length - pos, true]; + } + // if we got one positive range, and it's a single character, then that's + // not actually a magic pattern, it's just that one literal character. + // we should not treat that as "magic", we should just return the literal + // character. [_] is a perfectly valid way to escape glob magic chars. + if (negs.length === 0 && + ranges.length === 1 && + /^\\?.$/.test(ranges[0]) && + !negate) { + const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0]; + return [regexpEscape(r), false, endPos - pos, false]; + } + const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']'; + const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']'; + const comb = ranges.length && negs.length ? '(' + sranges + '|' + snegs + ')' + : ranges.length ? sranges + : snegs; + return [comb, uflag, endPos - pos, true]; +}; +exports.parseClass = parseClass; +//# sourceMappingURL=brace-expressions.js.map + +/***/ }), + +/***/ 10800: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.escape = void 0; +/** + * Escape all magic characters in a glob pattern. + * + * If the {@link MinimatchOptions.windowsPathsNoEscape} + * option is used, then characters are escaped by wrapping in `[]`, because + * a magic character wrapped in a character class can only be satisfied by + * that exact character. In this mode, `\` is _not_ escaped, because it is + * not interpreted as a magic character, but instead as a path separator. + * + * If the {@link MinimatchOptions.magicalBraces} option is used, + * then braces (`{` and `}`) will be escaped. + */ +const escape = (s, { windowsPathsNoEscape = false, magicalBraces = false, } = {}) => { + // don't need to escape +@! because we escape the parens + // that make those magic, and escaping ! as [!] isn't valid, + // because [!]] is a valid glob class meaning not ']'. + if (magicalBraces) { + return windowsPathsNoEscape ? + s.replace(/[?*()[\]{}]/g, '[$&]') + : s.replace(/[?*()[\]\\{}]/g, '\\$&'); + } + return windowsPathsNoEscape ? + s.replace(/[?*()[\]]/g, '[$&]') + : s.replace(/[?*()[\]\\]/g, '\\$&'); +}; +exports.escape = escape; +//# sourceMappingURL=escape.js.map + +/***/ }), + +/***/ 46507: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.unescape = exports.escape = exports.AST = exports.Minimatch = exports.match = exports.makeRe = exports.braceExpand = exports.defaults = exports.filter = exports.GLOBSTAR = exports.sep = exports.minimatch = void 0; +const brace_expansion_1 = __nccwpck_require__(38968); +const assert_valid_pattern_js_1 = __nccwpck_require__(57305); +const ast_js_1 = __nccwpck_require__(31803); +const escape_js_1 = __nccwpck_require__(10800); +const unescape_js_1 = __nccwpck_require__(70851); +const minimatch = (p, pattern, options = {}) => { + (0, assert_valid_pattern_js_1.assertValidPattern)(pattern); + // shortcut: comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + return false; + } + return new Minimatch(pattern, options).match(p); +}; +exports.minimatch = minimatch; +// Optimized checking for the most common glob patterns. +const starDotExtRE = /^\*+([^+@!?*[(]*)$/; +const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext); +const starDotExtTestDot = (ext) => (f) => f.endsWith(ext); +const starDotExtTestNocase = (ext) => { + ext = ext.toLowerCase(); + return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext); +}; +const starDotExtTestNocaseDot = (ext) => { + ext = ext.toLowerCase(); + return (f) => f.toLowerCase().endsWith(ext); +}; +const starDotStarRE = /^\*+\.\*+$/; +const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.'); +const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.'); +const dotStarRE = /^\.\*+$/; +const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.'); +const starRE = /^\*+$/; +const starTest = (f) => f.length !== 0 && !f.startsWith('.'); +const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..'; +const qmarksRE = /^\?+([^+@!?*[(]*)?$/; +const qmarksTestNocase = ([$0, ext = '']) => { + const noext = qmarksTestNoExt([$0]); + if (!ext) + return noext; + ext = ext.toLowerCase(); + return (f) => noext(f) && f.toLowerCase().endsWith(ext); +}; +const qmarksTestNocaseDot = ([$0, ext = '']) => { + const noext = qmarksTestNoExtDot([$0]); + if (!ext) + return noext; + ext = ext.toLowerCase(); + return (f) => noext(f) && f.toLowerCase().endsWith(ext); +}; +const qmarksTestDot = ([$0, ext = '']) => { + const noext = qmarksTestNoExtDot([$0]); + return !ext ? noext : (f) => noext(f) && f.endsWith(ext); +}; +const qmarksTest = ([$0, ext = '']) => { + const noext = qmarksTestNoExt([$0]); + return !ext ? noext : (f) => noext(f) && f.endsWith(ext); +}; +const qmarksTestNoExt = ([$0]) => { + const len = $0.length; + return (f) => f.length === len && !f.startsWith('.'); +}; +const qmarksTestNoExtDot = ([$0]) => { + const len = $0.length; + return (f) => f.length === len && f !== '.' && f !== '..'; +}; +/* c8 ignore start */ +const defaultPlatform = (typeof process === 'object' && process ? + (typeof process.env === 'object' && + process.env && + process.env.__MINIMATCH_TESTING_PLATFORM__) || + process.platform + : 'posix'); +const path = { + win32: { sep: '\\' }, + posix: { sep: '/' }, +}; +/* c8 ignore stop */ +exports.sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep; +exports.minimatch.sep = exports.sep; +exports.GLOBSTAR = Symbol('globstar **'); +exports.minimatch.GLOBSTAR = exports.GLOBSTAR; +// any single thing other than / +// don't need to escape / when using new RegExp() +const qmark = '[^/]'; +// * => any number of characters +const star = qmark + '*?'; +// ** when dots are allowed. Anything goes, except .. and . +// not (^ or / followed by one or two dots followed by $ or /), +// followed by anything, any number of times. +const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?'; +// not a ^ or / followed by a dot, +// followed by anything, any number of times. +const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?'; +const filter = (pattern, options = {}) => (p) => (0, exports.minimatch)(p, pattern, options); +exports.filter = filter; +exports.minimatch.filter = exports.filter; +const ext = (a, b = {}) => Object.assign({}, a, b); +const defaults = (def) => { + if (!def || typeof def !== 'object' || !Object.keys(def).length) { + return exports.minimatch; + } + const orig = exports.minimatch; + const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options)); + return Object.assign(m, { + Minimatch: class Minimatch extends orig.Minimatch { + constructor(pattern, options = {}) { + super(pattern, ext(def, options)); + } + static defaults(options) { + return orig.defaults(ext(def, options)).Minimatch; + } + }, + AST: class AST extends orig.AST { + /* c8 ignore start */ + constructor(type, parent, options = {}) { + super(type, parent, ext(def, options)); + } + /* c8 ignore stop */ + static fromGlob(pattern, options = {}) { + return orig.AST.fromGlob(pattern, ext(def, options)); + } + }, + unescape: (s, options = {}) => orig.unescape(s, ext(def, options)), + escape: (s, options = {}) => orig.escape(s, ext(def, options)), + filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)), + defaults: (options) => orig.defaults(ext(def, options)), + makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)), + braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)), + match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)), + sep: orig.sep, + GLOBSTAR: exports.GLOBSTAR, + }); +}; +exports.defaults = defaults; +exports.minimatch.defaults = exports.defaults; +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +const braceExpand = (pattern, options = {}) => { + (0, assert_valid_pattern_js_1.assertValidPattern)(pattern); + // Thanks to Yeting Li for + // improving this regexp to avoid a ReDOS vulnerability. + if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { + // shortcut. no need to expand. + return [pattern]; + } + return (0, brace_expansion_1.expand)(pattern, { max: options.braceExpandMax }); +}; +exports.braceExpand = braceExpand; +exports.minimatch.braceExpand = exports.braceExpand; +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe(); +exports.makeRe = makeRe; +exports.minimatch.makeRe = exports.makeRe; +const match = (list, pattern, options = {}) => { + const mm = new Minimatch(pattern, options); + list = list.filter(f => mm.match(f)); + if (mm.options.nonull && !list.length) { + list.push(pattern); + } + return list; +}; +exports.match = match; +exports.minimatch.match = exports.match; +// replace stuff like \* with * +const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/; +const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); +class Minimatch { + options; + set; + pattern; + windowsPathsNoEscape; + nonegate; + negate; + comment; + empty; + preserveMultipleSlashes; + partial; + globSet; + globParts; + nocase; + isWindows; + platform; + windowsNoMagicRoot; + maxGlobstarRecursion; + regexp; + constructor(pattern, options = {}) { + (0, assert_valid_pattern_js_1.assertValidPattern)(pattern); + options = options || {}; + this.options = options; + this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200; + this.pattern = pattern; + this.platform = options.platform || defaultPlatform; + this.isWindows = this.platform === 'win32'; + // avoid the annoying deprecation flag lol + const awe = ('allowWindow' + 'sEscape'); + this.windowsPathsNoEscape = + !!options.windowsPathsNoEscape || options[awe] === false; + if (this.windowsPathsNoEscape) { + this.pattern = this.pattern.replace(/\\/g, '/'); + } + this.preserveMultipleSlashes = !!options.preserveMultipleSlashes; + this.regexp = null; + this.negate = false; + this.nonegate = !!options.nonegate; + this.comment = false; + this.empty = false; + this.partial = !!options.partial; + this.nocase = !!this.options.nocase; + this.windowsNoMagicRoot = + options.windowsNoMagicRoot !== undefined ? + options.windowsNoMagicRoot + : !!(this.isWindows && this.nocase); + this.globSet = []; + this.globParts = []; + this.set = []; + // make the set of regexps etc. + this.make(); + } + hasMagic() { + if (this.options.magicalBraces && this.set.length > 1) { + return true; + } + for (const pattern of this.set) { + for (const part of pattern) { + if (typeof part !== 'string') + return true; + } + } + return false; + } + debug(..._) { } + make() { + const pattern = this.pattern; + const options = this.options; + // empty patterns and comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + this.comment = true; + return; + } + if (!pattern) { + this.empty = true; + return; + } + // step 1: figure out negation, etc. + this.parseNegate(); + // step 2: expand braces + this.globSet = [...new Set(this.braceExpand())]; + if (options.debug) { + //oxlint-disable-next-line no-console + this.debug = (...args) => console.error(...args); + } + this.debug(this.pattern, this.globSet); + // step 3: now we have a set, so turn each one into a series of + // path-portion matching patterns. + // These will be regexps, except in the case of "**", which is + // set to the GLOBSTAR object for globstar behavior, + // and will not contain any / characters + // + // First, we preprocess to make the glob pattern sets a bit simpler + // and deduped. There are some perf-killing patterns that can cause + // problems with a glob walk, but we can simplify them down a bit. + const rawGlobParts = this.globSet.map(s => this.slashSplit(s)); + this.globParts = this.preprocess(rawGlobParts); + this.debug(this.pattern, this.globParts); + // glob --> regexps + let set = this.globParts.map((s, _, __) => { + if (this.isWindows && this.windowsNoMagicRoot) { + // check if it's a drive or unc path. + const isUNC = s[0] === '' && + s[1] === '' && + (s[2] === '?' || !globMagic.test(s[2])) && + !globMagic.test(s[3]); + const isDrive = /^[a-z]:/i.test(s[0]); + if (isUNC) { + return [ + ...s.slice(0, 4), + ...s.slice(4).map(ss => this.parse(ss)), + ]; + } + else if (isDrive) { + return [s[0], ...s.slice(1).map(ss => this.parse(ss))]; + } + } + return s.map(ss => this.parse(ss)); + }); + this.debug(this.pattern, set); + // filter out everything that didn't compile properly. + this.set = set.filter(s => s.indexOf(false) === -1); + // do not treat the ? in UNC paths as magic + if (this.isWindows) { + for (let i = 0; i < this.set.length; i++) { + const p = this.set[i]; + if (p[0] === '' && + p[1] === '' && + this.globParts[i][2] === '?' && + typeof p[3] === 'string' && + /^[a-z]:$/i.test(p[3])) { + p[2] = '?'; + } + } + } + this.debug(this.pattern, this.set); + } + // various transforms to equivalent pattern sets that are + // faster to process in a filesystem walk. The goal is to + // eliminate what we can, and push all ** patterns as far + // to the right as possible, even if it increases the number + // of patterns that we have to process. + preprocess(globParts) { + // if we're not in globstar mode, then turn ** into * + if (this.options.noglobstar) { + for (const partset of globParts) { + for (let j = 0; j < partset.length; j++) { + if (partset[j] === '**') { + partset[j] = '*'; + } + } + } + } + const { optimizationLevel = 1 } = this.options; + if (optimizationLevel >= 2) { + // aggressive optimization for the purpose of fs walking + globParts = this.firstPhasePreProcess(globParts); + globParts = this.secondPhasePreProcess(globParts); + } + else if (optimizationLevel >= 1) { + // just basic optimizations to remove some .. parts + globParts = this.levelOneOptimize(globParts); + } + else { + // just collapse multiple ** portions into one + globParts = this.adjascentGlobstarOptimize(globParts); + } + return globParts; + } + // just get rid of adjascent ** portions + adjascentGlobstarOptimize(globParts) { + return globParts.map(parts => { + let gs = -1; + while (-1 !== (gs = parts.indexOf('**', gs + 1))) { + let i = gs; + while (parts[i + 1] === '**') { + i++; + } + if (i !== gs) { + parts.splice(gs, i - gs); + } + } + return parts; + }); + } + // get rid of adjascent ** and resolve .. portions + levelOneOptimize(globParts) { + return globParts.map(parts => { + parts = parts.reduce((set, part) => { + const prev = set[set.length - 1]; + if (part === '**' && prev === '**') { + return set; + } + if (part === '..') { + if (prev && prev !== '..' && prev !== '.' && prev !== '**') { + set.pop(); + return set; + } + } + set.push(part); + return set; + }, []); + return parts.length === 0 ? [''] : parts; + }); + } + levelTwoFileOptimize(parts) { + if (!Array.isArray(parts)) { + parts = this.slashSplit(parts); + } + let didSomething = false; + do { + didSomething = false; + //
// -> 
/
+            if (!this.preserveMultipleSlashes) {
+                for (let i = 1; i < parts.length - 1; i++) {
+                    const p = parts[i];
+                    // don't squeeze out UNC patterns
+                    if (i === 1 && p === '' && parts[0] === '')
+                        continue;
+                    if (p === '.' || p === '') {
+                        didSomething = true;
+                        parts.splice(i, 1);
+                        i--;
+                    }
+                }
+                if (parts[0] === '.' &&
+                    parts.length === 2 &&
+                    (parts[1] === '.' || parts[1] === '')) {
+                    didSomething = true;
+                    parts.pop();
+                }
+            }
+            // 
/

/../ ->

/
+            let dd = 0;
+            while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
+                const p = parts[dd - 1];
+                if (p &&
+                    p !== '.' &&
+                    p !== '..' &&
+                    p !== '**' &&
+                    !(this.isWindows && /^[a-z]:$/i.test(p))) {
+                    didSomething = true;
+                    parts.splice(dd - 1, 2);
+                    dd -= 2;
+                }
+            }
+        } while (didSomething);
+        return parts.length === 0 ? [''] : parts;
+    }
+    // First phase: single-pattern processing
+    // 
 is 1 or more portions
+    //  is 1 or more portions
+    // 

is any portion other than ., .., '', or ** + // is . or '' + // + // **/.. is *brutal* for filesystem walking performance, because + // it effectively resets the recursive walk each time it occurs, + // and ** cannot be reduced out by a .. pattern part like a regexp + // or most strings (other than .., ., and '') can be. + // + //

/**/../

/

/ -> {

/../

/

/,

/**/

/

/} + //

// -> 
/
+    // 
/

/../ ->

/
+    // **/**/ -> **/
+    //
+    // **/*/ -> */**/ <== not valid because ** doesn't follow
+    // this WOULD be allowed if ** did follow symlinks, or * didn't
+    firstPhasePreProcess(globParts) {
+        let didSomething = false;
+        do {
+            didSomething = false;
+            // 
/**/../

/

/ -> {

/../

/

/,

/**/

/

/} + for (let parts of globParts) { + let gs = -1; + while (-1 !== (gs = parts.indexOf('**', gs + 1))) { + let gss = gs; + while (parts[gss + 1] === '**') { + //

/**/**/ -> 
/**/
+                        gss++;
+                    }
+                    // eg, if gs is 2 and gss is 4, that means we have 3 **
+                    // parts, and can remove 2 of them.
+                    if (gss > gs) {
+                        parts.splice(gs + 1, gss - gs);
+                    }
+                    let next = parts[gs + 1];
+                    const p = parts[gs + 2];
+                    const p2 = parts[gs + 3];
+                    if (next !== '..')
+                        continue;
+                    if (!p ||
+                        p === '.' ||
+                        p === '..' ||
+                        !p2 ||
+                        p2 === '.' ||
+                        p2 === '..') {
+                        continue;
+                    }
+                    didSomething = true;
+                    // edit parts in place, and push the new one
+                    parts.splice(gs, 1);
+                    const other = parts.slice(0);
+                    other[gs] = '**';
+                    globParts.push(other);
+                    gs--;
+                }
+                // 
// -> 
/
+                if (!this.preserveMultipleSlashes) {
+                    for (let i = 1; i < parts.length - 1; i++) {
+                        const p = parts[i];
+                        // don't squeeze out UNC patterns
+                        if (i === 1 && p === '' && parts[0] === '')
+                            continue;
+                        if (p === '.' || p === '') {
+                            didSomething = true;
+                            parts.splice(i, 1);
+                            i--;
+                        }
+                    }
+                    if (parts[0] === '.' &&
+                        parts.length === 2 &&
+                        (parts[1] === '.' || parts[1] === '')) {
+                        didSomething = true;
+                        parts.pop();
+                    }
+                }
+                // 
/

/../ ->

/
+                let dd = 0;
+                while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
+                    const p = parts[dd - 1];
+                    if (p && p !== '.' && p !== '..' && p !== '**') {
+                        didSomething = true;
+                        const needDot = dd === 1 && parts[dd + 1] === '**';
+                        const splin = needDot ? ['.'] : [];
+                        parts.splice(dd - 1, 2, ...splin);
+                        if (parts.length === 0)
+                            parts.push('');
+                        dd -= 2;
+                    }
+                }
+            }
+        } while (didSomething);
+        return globParts;
+    }
+    // second phase: multi-pattern dedupes
+    // {
/*/,
/

/} ->

/*/
+    // {
/,
/} -> 
/
+    // {
/**/,
/} -> 
/**/
+    //
+    // {
/**/,
/**/

/} ->

/**/
+    // ^-- not valid because ** doens't follow symlinks
+    secondPhasePreProcess(globParts) {
+        for (let i = 0; i < globParts.length - 1; i++) {
+            for (let j = i + 1; j < globParts.length; j++) {
+                const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
+                if (matched) {
+                    globParts[i] = [];
+                    globParts[j] = matched;
+                    break;
+                }
+            }
+        }
+        return globParts.filter(gs => gs.length);
+    }
+    partsMatch(a, b, emptyGSMatch = false) {
+        let ai = 0;
+        let bi = 0;
+        let result = [];
+        let which = '';
+        while (ai < a.length && bi < b.length) {
+            if (a[ai] === b[bi]) {
+                result.push(which === 'b' ? b[bi] : a[ai]);
+                ai++;
+                bi++;
+            }
+            else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {
+                result.push(a[ai]);
+                ai++;
+            }
+            else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {
+                result.push(b[bi]);
+                bi++;
+            }
+            else if (a[ai] === '*' &&
+                b[bi] &&
+                (this.options.dot || !b[bi].startsWith('.')) &&
+                b[bi] !== '**') {
+                if (which === 'b')
+                    return false;
+                which = 'a';
+                result.push(a[ai]);
+                ai++;
+                bi++;
+            }
+            else if (b[bi] === '*' &&
+                a[ai] &&
+                (this.options.dot || !a[ai].startsWith('.')) &&
+                a[ai] !== '**') {
+                if (which === 'a')
+                    return false;
+                which = 'b';
+                result.push(b[bi]);
+                ai++;
+                bi++;
+            }
+            else {
+                return false;
+            }
+        }
+        // if we fall out of the loop, it means they two are identical
+        // as long as their lengths match
+        return a.length === b.length && result;
+    }
+    parseNegate() {
+        if (this.nonegate)
+            return;
+        const pattern = this.pattern;
+        let negate = false;
+        let negateOffset = 0;
+        for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
+            negate = !negate;
+            negateOffset++;
+        }
+        if (negateOffset)
+            this.pattern = pattern.slice(negateOffset);
+        this.negate = negate;
+    }
+    // set partial to true to test if, for example,
+    // "/a/b" matches the start of "/*/b/*/d"
+    // Partial means, if you run out of file before you run
+    // out of pattern, then that's fine, as long as all
+    // the parts match.
+    matchOne(file, pattern, partial = false) {
+        let fileStartIndex = 0;
+        let patternStartIndex = 0;
+        // UNC paths like //?/X:/... can match X:/... and vice versa
+        // Drive letters in absolute drive or unc paths are always compared
+        // case-insensitively.
+        if (this.isWindows) {
+            const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
+            const fileUNC = !fileDrive &&
+                file[0] === '' &&
+                file[1] === '' &&
+                file[2] === '?' &&
+                /^[a-z]:$/i.test(file[3]);
+            const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]);
+            const patternUNC = !patternDrive &&
+                pattern[0] === '' &&
+                pattern[1] === '' &&
+                pattern[2] === '?' &&
+                typeof pattern[3] === 'string' &&
+                /^[a-z]:$/i.test(pattern[3]);
+            const fdi = fileUNC ? 3
+                : fileDrive ? 0
+                    : undefined;
+            const pdi = patternUNC ? 3
+                : patternDrive ? 0
+                    : undefined;
+            if (typeof fdi === 'number' && typeof pdi === 'number') {
+                const [fd, pd] = [
+                    file[fdi],
+                    pattern[pdi],
+                ];
+                // start matching at the drive letter index of each
+                if (fd.toLowerCase() === pd.toLowerCase()) {
+                    pattern[pdi] = fd;
+                    patternStartIndex = pdi;
+                    fileStartIndex = fdi;
+                }
+            }
+        }
+        // resolve and reduce . and .. portions in the file as well.
+        // don't need to do the second phase, because it's only one string[]
+        const { optimizationLevel = 1 } = this.options;
+        if (optimizationLevel >= 2) {
+            file = this.levelTwoFileOptimize(file);
+        }
+        if (pattern.includes(exports.GLOBSTAR)) {
+            return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
+        }
+        return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
+    }
+    #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
+        // split the pattern into head, tail, and middle of ** delimited parts
+        const firstgs = pattern.indexOf(exports.GLOBSTAR, patternIndex);
+        const lastgs = pattern.lastIndexOf(exports.GLOBSTAR);
+        // split the pattern up into globstar-delimited sections
+        // the tail has to be at the end, and the others just have
+        // to be found in order from the head.
+        const [head, body, tail] = partial ?
+            [
+                pattern.slice(patternIndex, firstgs),
+                pattern.slice(firstgs + 1),
+                [],
+            ]
+            : [
+                pattern.slice(patternIndex, firstgs),
+                pattern.slice(firstgs + 1, lastgs),
+                pattern.slice(lastgs + 1),
+            ];
+        // check the head, from the current file/pattern index.
+        if (head.length) {
+            const fileHead = file.slice(fileIndex, fileIndex + head.length);
+            if (!this.#matchOne(fileHead, head, partial, 0, 0)) {
+                return false;
+            }
+            fileIndex += head.length;
+            patternIndex += head.length;
+        }
+        // now we know the head matches!
+        // if the last portion is not empty, it MUST match the end
+        // check the tail
+        let fileTailMatch = 0;
+        if (tail.length) {
+            // if head + tail > file, then we cannot possibly match
+            if (tail.length + fileIndex > file.length)
+                return false;
+            // try to match the tail
+            let tailStart = file.length - tail.length;
+            if (this.#matchOne(file, tail, partial, tailStart, 0)) {
+                fileTailMatch = tail.length;
+            }
+            else {
+                // affordance for stuff like a/**/* matching a/b/
+                // if the last file portion is '', and there's more to the pattern
+                // then try without the '' bit.
+                if (file[file.length - 1] !== '' ||
+                    fileIndex + tail.length === file.length) {
+                    return false;
+                }
+                tailStart--;
+                if (!this.#matchOne(file, tail, partial, tailStart, 0)) {
+                    return false;
+                }
+                fileTailMatch = tail.length + 1;
+            }
+        }
+        // now we know the tail matches!
+        // the middle is zero or more portions wrapped in **, possibly
+        // containing more ** sections.
+        // so a/**/b/**/c/**/d has become **/b/**/c/**
+        // if it's empty, it means a/**/b, just verify we have no bad dots
+        // if there's no tail, so it ends on /**, then we must have *something*
+        // after the head, or it's not a matc
+        if (!body.length) {
+            let sawSome = !!fileTailMatch;
+            for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
+                const f = String(file[i]);
+                sawSome = true;
+                if (f === '.' ||
+                    f === '..' ||
+                    (!this.options.dot && f.startsWith('.'))) {
+                    return false;
+                }
+            }
+            // in partial mode, we just need to get past all file parts
+            return partial || sawSome;
+        }
+        // now we know that there's one or more body sections, which can
+        // be matched anywhere from the 0 index (because the head was pruned)
+        // through to the length-fileTailMatch index.
+        // split the body up into sections, and note the minimum index it can
+        // be found at (start with the length of all previous segments)
+        // [section, before, after]
+        const bodySegments = [[[], 0]];
+        let currentBody = bodySegments[0];
+        let nonGsParts = 0;
+        const nonGsPartsSums = [0];
+        for (const b of body) {
+            if (b === exports.GLOBSTAR) {
+                nonGsPartsSums.push(nonGsParts);
+                currentBody = [[], 0];
+                bodySegments.push(currentBody);
+            }
+            else {
+                currentBody[0].push(b);
+                nonGsParts++;
+            }
+        }
+        let i = bodySegments.length - 1;
+        const fileLength = file.length - fileTailMatch;
+        for (const b of bodySegments) {
+            b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
+        }
+        return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
+    }
+    // return false for "nope, not matching"
+    // return null for "not matching, cannot keep trying"
+    #matchGlobStarBodySections(file, 
+    // pattern section, last possible position for it
+    bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
+        // take the first body segment, and walk from fileIndex to its "after"
+        // value at the end
+        // If it doesn't match at that position, we increment, until we hit
+        // that final possible position, and give up.
+        // If it does match, then advance and try to rest.
+        // If any of them fail we keep walking forward.
+        // this is still a bit recursively painful, but it's more constrained
+        // than previous implementations, because we never test something that
+        // can't possibly be a valid matching condition.
+        const bs = bodySegments[bodyIndex];
+        if (!bs) {
+            // just make sure that there's no bad dots
+            for (let i = fileIndex; i < file.length; i++) {
+                sawTail = true;
+                const f = file[i];
+                if (f === '.' ||
+                    f === '..' ||
+                    (!this.options.dot && f.startsWith('.'))) {
+                    return false;
+                }
+            }
+            return sawTail;
+        }
+        // have a non-globstar body section to test
+        const [body, after] = bs;
+        while (fileIndex <= after) {
+            const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
+            // if limit exceeded, no match. intentional false negative,
+            // acceptable break in correctness for security.
+            if (m && globStarDepth < this.maxGlobstarRecursion) {
+                // match! see if the rest match. if so, we're done!
+                const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
+                if (sub !== false) {
+                    return sub;
+                }
+            }
+            const f = file[fileIndex];
+            if (f === '.' ||
+                f === '..' ||
+                (!this.options.dot && f.startsWith('.'))) {
+                return false;
+            }
+            fileIndex++;
+        }
+        // walked off. no point continuing
+        return partial || null;
+    }
+    #matchOne(file, pattern, partial, fileIndex, patternIndex) {
+        let fi;
+        let pi;
+        let pl;
+        let fl;
+        for (fi = fileIndex,
+            pi = patternIndex,
+            fl = file.length,
+            pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
+            this.debug('matchOne loop');
+            let p = pattern[pi];
+            let f = file[fi];
+            this.debug(pattern, p, f);
+            // should be impossible.
+            // some invalid regexp stuff in the set.
+            /* c8 ignore start */
+            if (p === false || p === exports.GLOBSTAR) {
+                return false;
+            }
+            /* c8 ignore stop */
+            // something other than **
+            // non-magic patterns just have to match exactly
+            // patterns with magic have been turned into regexps.
+            let hit;
+            if (typeof p === 'string') {
+                hit = f === p;
+                this.debug('string match', p, f, hit);
+            }
+            else {
+                hit = p.test(f);
+                this.debug('pattern match', p, f, hit);
+            }
+            if (!hit)
+                return false;
+        }
+        // Note: ending in / means that we'll get a final ""
+        // at the end of the pattern.  This can only match a
+        // corresponding "" at the end of the file.
+        // If the file ends in /, then it can only match a
+        // a pattern that ends in /, unless the pattern just
+        // doesn't have any more for it. But, a/b/ should *not*
+        // match "a/b/*", even though "" matches against the
+        // [^/]*? pattern, except in partial mode, where it might
+        // simply not be reached yet.
+        // However, a/b/ should still satisfy a/*
+        // now either we fell off the end of the pattern, or we're done.
+        if (fi === fl && pi === pl) {
+            // ran out of pattern and filename at the same time.
+            // an exact hit!
+            return true;
+        }
+        else if (fi === fl) {
+            // ran out of file, but still had pattern left.
+            // this is ok if we're doing the match as part of
+            // a glob fs traversal.
+            return partial;
+        }
+        else if (pi === pl) {
+            // ran out of pattern, still have file left.
+            // this is only acceptable if we're on the very last
+            // empty segment of a file with a trailing slash.
+            // a/* should match a/b/
+            return fi === fl - 1 && file[fi] === '';
+            /* c8 ignore start */
+        }
+        else {
+            // should be unreachable.
+            throw new Error('wtf?');
+        }
+        /* c8 ignore stop */
+    }
+    braceExpand() {
+        return (0, exports.braceExpand)(this.pattern, this.options);
+    }
+    parse(pattern) {
+        (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
+        const options = this.options;
+        // shortcuts
+        if (pattern === '**')
+            return exports.GLOBSTAR;
+        if (pattern === '')
+            return '';
+        // far and away, the most common glob pattern parts are
+        // *, *.*, and *.  Add a fast check method for those.
+        let m;
+        let fastTest = null;
+        if ((m = pattern.match(starRE))) {
+            fastTest = options.dot ? starTestDot : starTest;
+        }
+        else if ((m = pattern.match(starDotExtRE))) {
+            fastTest = (options.nocase ?
+                options.dot ?
+                    starDotExtTestNocaseDot
+                    : starDotExtTestNocase
+                : options.dot ? starDotExtTestDot
+                    : starDotExtTest)(m[1]);
+        }
+        else if ((m = pattern.match(qmarksRE))) {
+            fastTest = (options.nocase ?
+                options.dot ?
+                    qmarksTestNocaseDot
+                    : qmarksTestNocase
+                : options.dot ? qmarksTestDot
+                    : qmarksTest)(m);
+        }
+        else if ((m = pattern.match(starDotStarRE))) {
+            fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
+        }
+        else if ((m = pattern.match(dotStarRE))) {
+            fastTest = dotStarTest;
+        }
+        const re = ast_js_1.AST.fromGlob(pattern, this.options).toMMPattern();
+        if (fastTest && typeof re === 'object') {
+            // Avoids overriding in frozen environments
+            Reflect.defineProperty(re, 'test', { value: fastTest });
+        }
+        return re;
+    }
+    makeRe() {
+        if (this.regexp || this.regexp === false)
+            return this.regexp;
+        // at this point, this.set is a 2d array of partial
+        // pattern strings, or "**".
+        //
+        // It's better to use .match().  This function shouldn't
+        // be used, really, but it's pretty convenient sometimes,
+        // when you just want to work with a regex.
+        const set = this.set;
+        if (!set.length) {
+            this.regexp = false;
+            return this.regexp;
+        }
+        const options = this.options;
+        const twoStar = options.noglobstar ? star
+            : options.dot ? twoStarDot
+                : twoStarNoDot;
+        const flags = new Set(options.nocase ? ['i'] : []);
+        // regexpify non-globstar patterns
+        // if ** is only item, then we just do one twoStar
+        // if ** is first, and there are more, prepend (\/|twoStar\/)? to next
+        // if ** is last, append (\/twoStar|) to previous
+        // if ** is in the middle, append (\/|\/twoStar\/) to previous
+        // then filter out GLOBSTAR symbols
+        let re = set
+            .map(pattern => {
+            const pp = pattern.map(p => {
+                if (p instanceof RegExp) {
+                    for (const f of p.flags.split(''))
+                        flags.add(f);
+                }
+                return (typeof p === 'string' ? regExpEscape(p)
+                    : p === exports.GLOBSTAR ? exports.GLOBSTAR
+                        : p._src);
+            });
+            pp.forEach((p, i) => {
+                const next = pp[i + 1];
+                const prev = pp[i - 1];
+                if (p !== exports.GLOBSTAR || prev === exports.GLOBSTAR) {
+                    return;
+                }
+                if (prev === undefined) {
+                    if (next !== undefined && next !== exports.GLOBSTAR) {
+                        pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
+                    }
+                    else {
+                        pp[i] = twoStar;
+                    }
+                }
+                else if (next === undefined) {
+                    pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + ')?';
+                }
+                else if (next !== exports.GLOBSTAR) {
+                    pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
+                    pp[i + 1] = exports.GLOBSTAR;
+                }
+            });
+            const filtered = pp.filter(p => p !== exports.GLOBSTAR);
+            // For partial matches, we need to make the pattern match
+            // any prefix of the full path. We do this by generating
+            // alternative patterns that match progressively longer prefixes.
+            if (this.partial && filtered.length >= 1) {
+                const prefixes = [];
+                for (let i = 1; i <= filtered.length; i++) {
+                    prefixes.push(filtered.slice(0, i).join('/'));
+                }
+                return '(?:' + prefixes.join('|') + ')';
+            }
+            return filtered.join('/');
+        })
+            .join('|');
+        // need to wrap in parens if we had more than one thing with |,
+        // otherwise only the first will be anchored to ^ and the last to $
+        const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
+        // must match entire pattern
+        // ending in a * or ** will make it less strict.
+        re = '^' + open + re + close + '$';
+        // In partial mode, '/' should always match as it's a valid prefix for any pattern
+        if (this.partial) {
+            re = '^(?:\\/|' + open + re.slice(1, -1) + close + ')$';
+        }
+        // can match anything, as long as it's not this.
+        if (this.negate)
+            re = '^(?!' + re + ').+$';
+        try {
+            this.regexp = new RegExp(re, [...flags].join(''));
+            /* c8 ignore start */
+        }
+        catch {
+            // should be impossible
+            this.regexp = false;
+        }
+        /* c8 ignore stop */
+        return this.regexp;
+    }
+    slashSplit(p) {
+        // if p starts with // on windows, we preserve that
+        // so that UNC paths aren't broken.  Otherwise, any number of
+        // / characters are coalesced into one, unless
+        // preserveMultipleSlashes is set to true.
+        if (this.preserveMultipleSlashes) {
+            return p.split('/');
+        }
+        else if (this.isWindows && /^\/\/[^/]+/.test(p)) {
+            // add an extra '' for the one we lose
+            return ['', ...p.split(/\/+/)];
+        }
+        else {
+            return p.split(/\/+/);
+        }
+    }
+    match(f, partial = this.partial) {
+        this.debug('match', f, this.pattern);
+        // short-circuit in the case of busted things.
+        // comments, etc.
+        if (this.comment) {
+            return false;
+        }
+        if (this.empty) {
+            return f === '';
+        }
+        if (f === '/' && partial) {
+            return true;
+        }
+        const options = this.options;
+        // windows: need to use /, not \
+        if (this.isWindows) {
+            f = f.split('\\').join('/');
+        }
+        // treat the test path as a set of pathparts.
+        const ff = this.slashSplit(f);
+        this.debug(this.pattern, 'split', ff);
+        // just ONE of the pattern sets in this.set needs to match
+        // in order for it to be valid.  If negating, then just one
+        // match means that we have failed.
+        // Either way, return on the first hit.
+        const set = this.set;
+        this.debug(this.pattern, 'set', set);
+        // Find the basename of the path by looking for the last non-empty segment
+        let filename = ff[ff.length - 1];
+        if (!filename) {
+            for (let i = ff.length - 2; !filename && i >= 0; i--) {
+                filename = ff[i];
+            }
+        }
+        for (const pattern of set) {
+            let file = ff;
+            if (options.matchBase && pattern.length === 1) {
+                file = [filename];
+            }
+            const hit = this.matchOne(file, pattern, partial);
+            if (hit) {
+                if (options.flipNegate) {
+                    return true;
+                }
+                return !this.negate;
+            }
+        }
+        // didn't get any hits.  this is success if it's a negative
+        // pattern, failure otherwise.
+        if (options.flipNegate) {
+            return false;
+        }
+        return this.negate;
+    }
+    static defaults(def) {
+        return exports.minimatch.defaults(def).Minimatch;
+    }
+}
+exports.Minimatch = Minimatch;
+/* c8 ignore start */
+var ast_js_2 = __nccwpck_require__(31803);
+Object.defineProperty(exports, "AST", ({ enumerable: true, get: function () { return ast_js_2.AST; } }));
+var escape_js_2 = __nccwpck_require__(10800);
+Object.defineProperty(exports, "escape", ({ enumerable: true, get: function () { return escape_js_2.escape; } }));
+var unescape_js_2 = __nccwpck_require__(70851);
+Object.defineProperty(exports, "unescape", ({ enumerable: true, get: function () { return unescape_js_2.unescape; } }));
+/* c8 ignore stop */
+exports.minimatch.AST = ast_js_1.AST;
+exports.minimatch.Minimatch = Minimatch;
+exports.minimatch.escape = escape_js_1.escape;
+exports.minimatch.unescape = unescape_js_1.unescape;
+//# sourceMappingURL=index.js.map
+
+/***/ }),
+
+/***/ 70851:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.unescape = void 0;
+/**
+ * Un-escape a string that has been escaped with {@link escape}.
+ *
+ * If the {@link MinimatchOptions.windowsPathsNoEscape} option is used, then
+ * square-bracket escapes are removed, but not backslash escapes.
+ *
+ * For example, it will turn the string `'[*]'` into `*`, but it will not
+ * turn `'\\*'` into `'*'`, because `\` is a path separator in
+ * `windowsPathsNoEscape` mode.
+ *
+ * When `windowsPathsNoEscape` is not set, then both square-bracket escapes and
+ * backslash escapes are removed.
+ *
+ * Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
+ * or unescaped.
+ *
+ * When `magicalBraces` is not set, escapes of braces (`{` and `}`) will not be
+ * unescaped.
+ */
+const unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true, } = {}) => {
+    if (magicalBraces) {
+        return windowsPathsNoEscape ?
+            s.replace(/\[([^/\\])\]/g, '$1')
+            : s
+                .replace(/((?!\\).|^)\[([^/\\])\]/g, '$1$2')
+                .replace(/\\([^/])/g, '$1');
+    }
+    return windowsPathsNoEscape ?
+        s.replace(/\[([^/\\{}])\]/g, '$1')
+        : s
+            .replace(/((?!\\).|^)\[([^/\\{}])\]/g, '$1$2')
+            .replace(/\\([^/{}])/g, '$1');
+};
+exports.unescape = unescape;
+//# sourceMappingURL=unescape.js.map
+
+/***/ }),
+
 /***/ 50591:
 /***/ ((module) => {
 
diff --git a/dist/setup/index.js b/dist/setup/index.js
index d4b76ff92..f99f5cb71 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -7975,7 +7975,7 @@ const os = __importStar(__nccwpck_require__(70857));
 const path = __importStar(__nccwpck_require__(16928));
 const pathHelper = __importStar(__nccwpck_require__(84138));
 const assert_1 = __importDefault(__nccwpck_require__(42613));
-const minimatch_1 = __nccwpck_require__(43772);
+const minimatch_1 = __nccwpck_require__(46507);
 const internal_match_kind_1 = __nccwpck_require__(62644);
 const internal_path_1 = __nccwpck_require__(76617);
 const IS_WINDOWS = process.platform === 'win32';
@@ -39340,303 +39340,6 @@ class Agent extends http.Agent {
 exports.Agent = Agent;
 //# sourceMappingURL=index.js.map
 
-/***/ }),
-
-/***/ 59380:
-/***/ ((module) => {
-
-"use strict";
-
-module.exports = balanced;
-function balanced(a, b, str) {
-  if (a instanceof RegExp) a = maybeMatch(a, str);
-  if (b instanceof RegExp) b = maybeMatch(b, str);
-
-  var r = range(a, b, str);
-
-  return r && {
-    start: r[0],
-    end: r[1],
-    pre: str.slice(0, r[0]),
-    body: str.slice(r[0] + a.length, r[1]),
-    post: str.slice(r[1] + b.length)
-  };
-}
-
-function maybeMatch(reg, str) {
-  var m = str.match(reg);
-  return m ? m[0] : null;
-}
-
-balanced.range = range;
-function range(a, b, str) {
-  var begs, beg, left, right, result;
-  var ai = str.indexOf(a);
-  var bi = str.indexOf(b, ai + 1);
-  var i = ai;
-
-  if (ai >= 0 && bi > 0) {
-    if(a===b) {
-      return [ai, bi];
-    }
-    begs = [];
-    left = str.length;
-
-    while (i >= 0 && !result) {
-      if (i == ai) {
-        begs.push(i);
-        ai = str.indexOf(a, i + 1);
-      } else if (begs.length == 1) {
-        result = [ begs.pop(), bi ];
-      } else {
-        beg = begs.pop();
-        if (beg < left) {
-          left = beg;
-          right = bi;
-        }
-
-        bi = str.indexOf(b, i + 1);
-      }
-
-      i = ai < bi && ai >= 0 ? ai : bi;
-    }
-
-    if (begs.length) {
-      result = [ left, right ];
-    }
-  }
-
-  return result;
-}
-
-
-/***/ }),
-
-/***/ 94691:
-/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
-
-var concatMap = __nccwpck_require__(97087);
-var balanced = __nccwpck_require__(59380);
-
-module.exports = expandTop;
-
-var escSlash = '\0SLASH'+Math.random()+'\0';
-var escOpen = '\0OPEN'+Math.random()+'\0';
-var escClose = '\0CLOSE'+Math.random()+'\0';
-var escComma = '\0COMMA'+Math.random()+'\0';
-var escPeriod = '\0PERIOD'+Math.random()+'\0';
-
-function numeric(str) {
-  return parseInt(str, 10) == str
-    ? parseInt(str, 10)
-    : str.charCodeAt(0);
-}
-
-function escapeBraces(str) {
-  return str.split('\\\\').join(escSlash)
-            .split('\\{').join(escOpen)
-            .split('\\}').join(escClose)
-            .split('\\,').join(escComma)
-            .split('\\.').join(escPeriod);
-}
-
-function unescapeBraces(str) {
-  return str.split(escSlash).join('\\')
-            .split(escOpen).join('{')
-            .split(escClose).join('}')
-            .split(escComma).join(',')
-            .split(escPeriod).join('.');
-}
-
-
-// Basically just str.split(","), but handling cases
-// where we have nested braced sections, which should be
-// treated as individual members, like {a,{b,c},d}
-function parseCommaParts(str) {
-  if (!str)
-    return [''];
-
-  var parts = [];
-  var m = balanced('{', '}', str);
-
-  if (!m)
-    return str.split(',');
-
-  var pre = m.pre;
-  var body = m.body;
-  var post = m.post;
-  var p = pre.split(',');
-
-  p[p.length-1] += '{' + body + '}';
-  var postParts = parseCommaParts(post);
-  if (post.length) {
-    p[p.length-1] += postParts.shift();
-    p.push.apply(p, postParts);
-  }
-
-  parts.push.apply(parts, p);
-
-  return parts;
-}
-
-function expandTop(str) {
-  if (!str)
-    return [];
-
-  // I don't know why Bash 4.3 does this, but it does.
-  // Anything starting with {} will have the first two bytes preserved
-  // but *only* at the top level, so {},a}b will not expand to anything,
-  // but a{},b}c will be expanded to [a}c,abc].
-  // One could argue that this is a bug in Bash, but since the goal of
-  // this module is to match Bash's rules, we escape a leading {}
-  if (str.substr(0, 2) === '{}') {
-    str = '\\{\\}' + str.substr(2);
-  }
-
-  return expand(escapeBraces(str), true).map(unescapeBraces);
-}
-
-function identity(e) {
-  return e;
-}
-
-function embrace(str) {
-  return '{' + str + '}';
-}
-function isPadded(el) {
-  return /^-?0\d/.test(el);
-}
-
-function lte(i, y) {
-  return i <= y;
-}
-function gte(i, y) {
-  return i >= y;
-}
-
-function expand(str, isTop) {
-  var expansions = [];
-
-  var m = balanced('{', '}', str);
-  if (!m || /\$$/.test(m.pre)) return [str];
-
-  var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
-  var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
-  var isSequence = isNumericSequence || isAlphaSequence;
-  var isOptions = m.body.indexOf(',') >= 0;
-  if (!isSequence && !isOptions) {
-    // {a},b}
-    if (m.post.match(/,(?!,).*\}/)) {
-      str = m.pre + '{' + m.body + escClose + m.post;
-      return expand(str);
-    }
-    return [str];
-  }
-
-  var n;
-  if (isSequence) {
-    n = m.body.split(/\.\./);
-  } else {
-    n = parseCommaParts(m.body);
-    if (n.length === 1) {
-      // x{{a,b}}y ==> x{a}y x{b}y
-      n = expand(n[0], false).map(embrace);
-      if (n.length === 1) {
-        var post = m.post.length
-          ? expand(m.post, false)
-          : [''];
-        return post.map(function(p) {
-          return m.pre + n[0] + p;
-        });
-      }
-    }
-  }
-
-  // at this point, n is the parts, and we know it's not a comma set
-  // with a single entry.
-
-  // no need to expand pre, since it is guaranteed to be free of brace-sets
-  var pre = m.pre;
-  var post = m.post.length
-    ? expand(m.post, false)
-    : [''];
-
-  var N;
-
-  if (isSequence) {
-    var x = numeric(n[0]);
-    var y = numeric(n[1]);
-    var width = Math.max(n[0].length, n[1].length)
-    var incr = n.length == 3
-      ? Math.max(Math.abs(numeric(n[2])), 1)
-      : 1;
-    var test = lte;
-    var reverse = y < x;
-    if (reverse) {
-      incr *= -1;
-      test = gte;
-    }
-    var pad = n.some(isPadded);
-
-    N = [];
-
-    for (var i = x; test(i, y); i += incr) {
-      var c;
-      if (isAlphaSequence) {
-        c = String.fromCharCode(i);
-        if (c === '\\')
-          c = '';
-      } else {
-        c = String(i);
-        if (pad) {
-          var need = width - c.length;
-          if (need > 0) {
-            var z = new Array(need + 1).join('0');
-            if (i < 0)
-              c = '-' + z + c.slice(1);
-            else
-              c = z + c;
-          }
-        }
-      }
-      N.push(c);
-    }
-  } else {
-    N = concatMap(n, function(el) { return expand(el, false) });
-  }
-
-  for (var j = 0; j < N.length; j++) {
-    for (var k = 0; k < post.length; k++) {
-      var expansion = pre + N[j] + post[k];
-      if (!isTop || isSequence || expansion)
-        expansions.push(expansion);
-    }
-  }
-
-  return expansions;
-}
-
-
-/***/ }),
-
-/***/ 97087:
-/***/ ((module) => {
-
-module.exports = function (xs, fn) {
-    var res = [];
-    for (var i = 0; i < xs.length; i++) {
-        var x = fn(xs[i], i);
-        if (isArray(x)) res.push.apply(res, x);
-        else res.push(x);
-    }
-    return res;
-};
-
-var isArray = Array.isArray || function (xs) {
-    return Object.prototype.toString.call(xs) === '[object Array]';
-};
-
-
 /***/ }),
 
 /***/ 6110:
@@ -40976,51 +40679,48 @@ exports.parseProxyResponse = parseProxyResponse;
 "use strict";
 
 
+const loader = __nccwpck_require__(91950)
+const dumper = __nccwpck_require__(59980)
 
-var loader = __nccwpck_require__(91950);
-var dumper = __nccwpck_require__(59980);
-
-
-function renamed(from, to) {
+function renamed (from, to) {
   return function () {
     throw new Error('Function yaml.' + from + ' is removed in js-yaml 4. ' +
-      'Use yaml.' + to + ' instead, which is now safe by default.');
-  };
+      'Use yaml.' + to + ' instead, which is now safe by default.')
+  }
 }
 
-
-module.exports.Type = __nccwpck_require__(9557);
-module.exports.Schema = __nccwpck_require__(62046);
-module.exports.FAILSAFE_SCHEMA = __nccwpck_require__(69832);
-module.exports.JSON_SCHEMA = __nccwpck_require__(58927);
-module.exports.CORE_SCHEMA = __nccwpck_require__(55746);
-module.exports.DEFAULT_SCHEMA = __nccwpck_require__(97336);
-module.exports.load                = loader.load;
-module.exports.loadAll             = loader.loadAll;
-module.exports.dump                = dumper.dump;
-module.exports.YAMLException = __nccwpck_require__(41248);
+module.exports.Type = __nccwpck_require__(9557)
+module.exports.Schema = __nccwpck_require__(62046)
+module.exports.FAILSAFE_SCHEMA = __nccwpck_require__(69832)
+module.exports.JSON_SCHEMA = __nccwpck_require__(58927)
+module.exports.CORE_SCHEMA = __nccwpck_require__(55746)
+module.exports.DEFAULT_SCHEMA = __nccwpck_require__(97336)
+module.exports.load = loader.load
+module.exports.loadAll = loader.loadAll
+module.exports.dump = dumper.dump
+module.exports.YAMLException = __nccwpck_require__(41248)
 
 // Re-export all types in case user wants to create custom schema
 module.exports.types = {
-  binary:    __nccwpck_require__(8149),
-  float:     __nccwpck_require__(57584),
-  map:       __nccwpck_require__(47316),
-  null:      __nccwpck_require__(4333),
-  pairs:     __nccwpck_require__(16267),
-  set:       __nccwpck_require__(78758),
+  binary: __nccwpck_require__(8149),
+  float: __nccwpck_require__(57584),
+  map: __nccwpck_require__(47316),
+  null: __nccwpck_require__(4333),
+  pairs: __nccwpck_require__(16267),
+  set: __nccwpck_require__(78758),
   timestamp: __nccwpck_require__(28966),
-  bool:      __nccwpck_require__(67296),
-  int:       __nccwpck_require__(62271),
-  merge:     __nccwpck_require__(76854),
-  omap:      __nccwpck_require__(58649),
-  seq:       __nccwpck_require__(77161),
-  str:       __nccwpck_require__(53929)
-};
+  bool: __nccwpck_require__(67296),
+  int: __nccwpck_require__(62271),
+  merge: __nccwpck_require__(76854),
+  omap: __nccwpck_require__(58649),
+  seq: __nccwpck_require__(77161),
+  str: __nccwpck_require__(53929)
+}
 
 // Removed functions from JS-YAML 3.0.x
-module.exports.safeLoad            = renamed('safeLoad', 'load');
-module.exports.safeLoadAll         = renamed('safeLoadAll', 'loadAll');
-module.exports.safeDump            = renamed('safeDump', 'dump');
+module.exports.safeLoad = renamed('safeLoad', 'load')
+module.exports.safeLoadAll = renamed('safeLoadAll', 'loadAll')
+module.exports.safeDump = renamed('safeDump', 'dump')
 
 
 /***/ }),
@@ -41031,63 +40731,54 @@ module.exports.safeDump            = renamed('safeDump', 'dump');
 "use strict";
 
 
-
-function isNothing(subject) {
-  return (typeof subject === 'undefined') || (subject === null);
+function isNothing (subject) {
+  return (typeof subject === 'undefined') || (subject === null)
 }
 
-
-function isObject(subject) {
-  return (typeof subject === 'object') && (subject !== null);
+function isObject (subject) {
+  return (typeof subject === 'object') && (subject !== null)
 }
 
+function toArray (sequence) {
+  if (Array.isArray(sequence)) return sequence
+  else if (isNothing(sequence)) return []
 
-function toArray(sequence) {
-  if (Array.isArray(sequence)) return sequence;
-  else if (isNothing(sequence)) return [];
-
-  return [ sequence ];
+  return [sequence]
 }
 
-
-function extend(target, source) {
-  var index, length, key, sourceKeys;
-
+function extend (target, source) {
   if (source) {
-    sourceKeys = Object.keys(source);
+    const sourceKeys = Object.keys(source)
 
-    for (index = 0, length = sourceKeys.length; index < length; index += 1) {
-      key = sourceKeys[index];
-      target[key] = source[key];
+    for (let index = 0, length = sourceKeys.length; index < length; index += 1) {
+      const key = sourceKeys[index]
+      target[key] = source[key]
     }
   }
 
-  return target;
+  return target
 }
 
+function repeat (string, count) {
+  let result = ''
 
-function repeat(string, count) {
-  var result = '', cycle;
-
-  for (cycle = 0; cycle < count; cycle += 1) {
-    result += string;
+  for (let cycle = 0; cycle < count; cycle += 1) {
+    result += string
   }
 
-  return result;
+  return result
 }
 
-
-function isNegativeZero(number) {
-  return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number);
+function isNegativeZero (number) {
+  return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number)
 }
 
-
-module.exports.isNothing      = isNothing;
-module.exports.isObject       = isObject;
-module.exports.toArray        = toArray;
-module.exports.repeat         = repeat;
-module.exports.isNegativeZero = isNegativeZero;
-module.exports.extend         = extend;
+module.exports.isNothing = isNothing
+module.exports.isObject = isObject
+module.exports.toArray = toArray
+module.exports.repeat = repeat
+module.exports.isNegativeZero = isNegativeZero
+module.exports.extend = extend
 
 
 /***/ }),
@@ -41098,203 +40789,196 @@ module.exports.extend         = extend;
 "use strict";
 
 
-/*eslint-disable no-use-before-define*/
-
-var common              = __nccwpck_require__(19816);
-var YAMLException       = __nccwpck_require__(41248);
-var DEFAULT_SCHEMA      = __nccwpck_require__(97336);
-
-var _toString       = Object.prototype.toString;
-var _hasOwnProperty = Object.prototype.hasOwnProperty;
-
-var CHAR_BOM                  = 0xFEFF;
-var CHAR_TAB                  = 0x09; /* Tab */
-var CHAR_LINE_FEED            = 0x0A; /* LF */
-var CHAR_CARRIAGE_RETURN      = 0x0D; /* CR */
-var CHAR_SPACE                = 0x20; /* Space */
-var CHAR_EXCLAMATION          = 0x21; /* ! */
-var CHAR_DOUBLE_QUOTE         = 0x22; /* " */
-var CHAR_SHARP                = 0x23; /* # */
-var CHAR_PERCENT              = 0x25; /* % */
-var CHAR_AMPERSAND            = 0x26; /* & */
-var CHAR_SINGLE_QUOTE         = 0x27; /* ' */
-var CHAR_ASTERISK             = 0x2A; /* * */
-var CHAR_COMMA                = 0x2C; /* , */
-var CHAR_MINUS                = 0x2D; /* - */
-var CHAR_COLON                = 0x3A; /* : */
-var CHAR_EQUALS               = 0x3D; /* = */
-var CHAR_GREATER_THAN         = 0x3E; /* > */
-var CHAR_QUESTION             = 0x3F; /* ? */
-var CHAR_COMMERCIAL_AT        = 0x40; /* @ */
-var CHAR_LEFT_SQUARE_BRACKET  = 0x5B; /* [ */
-var CHAR_RIGHT_SQUARE_BRACKET = 0x5D; /* ] */
-var CHAR_GRAVE_ACCENT         = 0x60; /* ` */
-var CHAR_LEFT_CURLY_BRACKET   = 0x7B; /* { */
-var CHAR_VERTICAL_LINE        = 0x7C; /* | */
-var CHAR_RIGHT_CURLY_BRACKET  = 0x7D; /* } */
-
-var ESCAPE_SEQUENCES = {};
-
-ESCAPE_SEQUENCES[0x00]   = '\\0';
-ESCAPE_SEQUENCES[0x07]   = '\\a';
-ESCAPE_SEQUENCES[0x08]   = '\\b';
-ESCAPE_SEQUENCES[0x09]   = '\\t';
-ESCAPE_SEQUENCES[0x0A]   = '\\n';
-ESCAPE_SEQUENCES[0x0B]   = '\\v';
-ESCAPE_SEQUENCES[0x0C]   = '\\f';
-ESCAPE_SEQUENCES[0x0D]   = '\\r';
-ESCAPE_SEQUENCES[0x1B]   = '\\e';
-ESCAPE_SEQUENCES[0x22]   = '\\"';
-ESCAPE_SEQUENCES[0x5C]   = '\\\\';
-ESCAPE_SEQUENCES[0x85]   = '\\N';
-ESCAPE_SEQUENCES[0xA0]   = '\\_';
-ESCAPE_SEQUENCES[0x2028] = '\\L';
-ESCAPE_SEQUENCES[0x2029] = '\\P';
-
-var DEPRECATED_BOOLEANS_SYNTAX = [
+const common = __nccwpck_require__(19816)
+const YAMLException = __nccwpck_require__(41248)
+const DEFAULT_SCHEMA = __nccwpck_require__(97336)
+
+const _toString = Object.prototype.toString
+const _hasOwnProperty = Object.prototype.hasOwnProperty
+
+const CHAR_BOM = 0xFEFF
+const CHAR_TAB = 0x09 /* Tab */
+const CHAR_LINE_FEED = 0x0A /* LF */
+const CHAR_CARRIAGE_RETURN = 0x0D /* CR */
+const CHAR_SPACE = 0x20 /* Space */
+const CHAR_EXCLAMATION = 0x21 /* ! */
+const CHAR_DOUBLE_QUOTE = 0x22 /* " */
+const CHAR_SHARP = 0x23 /* # */
+const CHAR_PERCENT = 0x25 /* % */
+const CHAR_AMPERSAND = 0x26 /* & */
+const CHAR_SINGLE_QUOTE = 0x27 /* ' */
+const CHAR_ASTERISK = 0x2A /* * */
+const CHAR_COMMA = 0x2C /* , */
+const CHAR_MINUS = 0x2D /* - */
+const CHAR_COLON = 0x3A /* : */
+const CHAR_EQUALS = 0x3D /* = */
+const CHAR_GREATER_THAN = 0x3E /* > */
+const CHAR_QUESTION = 0x3F /* ? */
+const CHAR_COMMERCIAL_AT = 0x40 /* @ */
+const CHAR_LEFT_SQUARE_BRACKET = 0x5B /* [ */
+const CHAR_RIGHT_SQUARE_BRACKET = 0x5D /* ] */
+const CHAR_GRAVE_ACCENT = 0x60 /* ` */
+const CHAR_LEFT_CURLY_BRACKET = 0x7B /* { */
+const CHAR_VERTICAL_LINE = 0x7C /* | */
+const CHAR_RIGHT_CURLY_BRACKET = 0x7D /* } */
+
+const ESCAPE_SEQUENCES = {}
+
+ESCAPE_SEQUENCES[0x00] = '\\0'
+ESCAPE_SEQUENCES[0x07] = '\\a'
+ESCAPE_SEQUENCES[0x08] = '\\b'
+ESCAPE_SEQUENCES[0x09] = '\\t'
+ESCAPE_SEQUENCES[0x0A] = '\\n'
+ESCAPE_SEQUENCES[0x0B] = '\\v'
+ESCAPE_SEQUENCES[0x0C] = '\\f'
+ESCAPE_SEQUENCES[0x0D] = '\\r'
+ESCAPE_SEQUENCES[0x1B] = '\\e'
+ESCAPE_SEQUENCES[0x22] = '\\"'
+ESCAPE_SEQUENCES[0x5C] = '\\\\'
+ESCAPE_SEQUENCES[0x85] = '\\N'
+ESCAPE_SEQUENCES[0xA0] = '\\_'
+ESCAPE_SEQUENCES[0x2028] = '\\L'
+ESCAPE_SEQUENCES[0x2029] = '\\P'
+
+const DEPRECATED_BOOLEANS_SYNTAX = [
   'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON',
   'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF'
-];
-
-var DEPRECATED_BASE60_SYNTAX = /^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/;
+]
 
-function compileStyleMap(schema, map) {
-  var result, keys, index, length, tag, style, type;
+const DEPRECATED_BASE60_SYNTAX = /^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/
 
-  if (map === null) return {};
+function compileStyleMap (schema, map) {
+  if (map === null) return {}
 
-  result = {};
-  keys = Object.keys(map);
+  const result = {}
+  const keys = Object.keys(map)
 
-  for (index = 0, length = keys.length; index < length; index += 1) {
-    tag = keys[index];
-    style = String(map[tag]);
+  for (let index = 0, length = keys.length; index < length; index += 1) {
+    let tag = keys[index]
+    let style = String(map[tag])
 
     if (tag.slice(0, 2) === '!!') {
-      tag = 'tag:yaml.org,2002:' + tag.slice(2);
+      tag = 'tag:yaml.org,2002:' + tag.slice(2)
     }
-    type = schema.compiledTypeMap['fallback'][tag];
+    const type = schema.compiledTypeMap['fallback'][tag]
 
     if (type && _hasOwnProperty.call(type.styleAliases, style)) {
-      style = type.styleAliases[style];
+      style = type.styleAliases[style]
     }
 
-    result[tag] = style;
+    result[tag] = style
   }
 
-  return result;
+  return result
 }
 
-function encodeHex(character) {
-  var string, handle, length;
+function encodeHex (character) {
+  let handle
+  let length
 
-  string = character.toString(16).toUpperCase();
+  const string = character.toString(16).toUpperCase()
 
   if (character <= 0xFF) {
-    handle = 'x';
-    length = 2;
+    handle = 'x'
+    length = 2
   } else if (character <= 0xFFFF) {
-    handle = 'u';
-    length = 4;
+    handle = 'u'
+    length = 4
   } else if (character <= 0xFFFFFFFF) {
-    handle = 'U';
-    length = 8;
+    handle = 'U'
+    length = 8
   } else {
-    throw new YAMLException('code point within a string may not be greater than 0xFFFFFFFF');
+    throw new YAMLException('code point within a string may not be greater than 0xFFFFFFFF')
   }
 
-  return '\\' + handle + common.repeat('0', length - string.length) + string;
+  return '\\' + handle + common.repeat('0', length - string.length) + string
 }
 
+const QUOTING_TYPE_SINGLE = 1
+const QUOTING_TYPE_DOUBLE = 2
 
-var QUOTING_TYPE_SINGLE = 1,
-    QUOTING_TYPE_DOUBLE = 2;
-
-function State(options) {
-  this.schema        = options['schema'] || DEFAULT_SCHEMA;
-  this.indent        = Math.max(1, (options['indent'] || 2));
-  this.noArrayIndent = options['noArrayIndent'] || false;
-  this.skipInvalid   = options['skipInvalid'] || false;
-  this.flowLevel     = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel']);
-  this.styleMap      = compileStyleMap(this.schema, options['styles'] || null);
-  this.sortKeys      = options['sortKeys'] || false;
-  this.lineWidth     = options['lineWidth'] || 80;
-  this.noRefs        = options['noRefs'] || false;
-  this.noCompatMode  = options['noCompatMode'] || false;
-  this.condenseFlow  = options['condenseFlow'] || false;
-  this.quotingType   = options['quotingType'] === '"' ? QUOTING_TYPE_DOUBLE : QUOTING_TYPE_SINGLE;
-  this.forceQuotes   = options['forceQuotes'] || false;
-  this.replacer      = typeof options['replacer'] === 'function' ? options['replacer'] : null;
+function State (options) {
+  this.schema = options['schema'] || DEFAULT_SCHEMA
+  this.indent = Math.max(1, (options['indent'] || 2))
+  this.noArrayIndent = options['noArrayIndent'] || false
+  this.skipInvalid = options['skipInvalid'] || false
+  this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel'])
+  this.styleMap = compileStyleMap(this.schema, options['styles'] || null)
+  this.sortKeys = options['sortKeys'] || false
+  this.lineWidth = options['lineWidth'] || 80
+  this.noRefs = options['noRefs'] || false
+  this.noCompatMode = options['noCompatMode'] || false
+  this.condenseFlow = options['condenseFlow'] || false
+  this.quotingType = options['quotingType'] === '"' ? QUOTING_TYPE_DOUBLE : QUOTING_TYPE_SINGLE
+  this.forceQuotes = options['forceQuotes'] || false
+  this.replacer = typeof options['replacer'] === 'function' ? options['replacer'] : null
 
-  this.implicitTypes = this.schema.compiledImplicit;
-  this.explicitTypes = this.schema.compiledExplicit;
+  this.implicitTypes = this.schema.compiledImplicit
+  this.explicitTypes = this.schema.compiledExplicit
 
-  this.tag = null;
-  this.result = '';
+  this.tag = null
+  this.result = ''
 
-  this.duplicates = [];
-  this.usedDuplicates = null;
+  this.duplicates = []
+  this.usedDuplicates = null
 }
 
 // Indents every line in a string. Empty lines (\n only) are not indented.
-function indentString(string, spaces) {
-  var ind = common.repeat(' ', spaces),
-      position = 0,
-      next = -1,
-      result = '',
-      line,
-      length = string.length;
+function indentString (string, spaces) {
+  const ind = common.repeat(' ', spaces)
+  let position = 0
+  let result = ''
+  const length = string.length
 
   while (position < length) {
-    next = string.indexOf('\n', position);
+    let line
+    const next = string.indexOf('\n', position)
     if (next === -1) {
-      line = string.slice(position);
-      position = length;
+      line = string.slice(position)
+      position = length
     } else {
-      line = string.slice(position, next + 1);
-      position = next + 1;
+      line = string.slice(position, next + 1)
+      position = next + 1
     }
 
-    if (line.length && line !== '\n') result += ind;
+    if (line.length && line !== '\n') result += ind
 
-    result += line;
+    result += line
   }
 
-  return result;
+  return result
 }
 
-function generateNextLine(state, level) {
-  return '\n' + common.repeat(' ', state.indent * level);
+function generateNextLine (state, level) {
+  return '\n' + common.repeat(' ', state.indent * level)
 }
 
-function testImplicitResolving(state, str) {
-  var index, length, type;
-
-  for (index = 0, length = state.implicitTypes.length; index < length; index += 1) {
-    type = state.implicitTypes[index];
+function testImplicitResolving (state, str) {
+  for (let index = 0, length = state.implicitTypes.length; index < length; index += 1) {
+    const type = state.implicitTypes[index]
 
     if (type.resolve(str)) {
-      return true;
+      return true
     }
   }
 
-  return false;
+  return false
 }
 
 // [33] s-white ::= s-space | s-tab
-function isWhitespace(c) {
-  return c === CHAR_SPACE || c === CHAR_TAB;
+function isWhitespace (c) {
+  return c === CHAR_SPACE || c === CHAR_TAB
 }
 
 // Returns true if the character can be printed without escaping.
 // From YAML 1.2: "any allowed characters known to be non-printable
 // should also be escaped. [However,] This isn’t mandatory"
 // Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029.
-function isPrintable(c) {
-  return  (0x00020 <= c && c <= 0x00007E)
-      || ((0x000A1 <= c && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029)
-      || ((0x0E000 <= c && c <= 0x00FFFD) && c !== CHAR_BOM)
-      ||  (0x10000 <= c && c <= 0x10FFFF);
+function isPrintable (c) {
+  return (c >= 0x00020 && c <= 0x00007E) ||
+    ((c >= 0x000A1 && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029) ||
+    ((c >= 0x0E000 && c <= 0x00FFFD) && c !== CHAR_BOM) ||
+    (c >= 0x10000 && c <= 0x10FFFF)
 }
 
 // [34] ns-char ::= nb-char - s-white
@@ -41302,12 +40986,12 @@ function isPrintable(c) {
 // [26] b-char  ::= b-line-feed | b-carriage-return
 // Including s-white (for some reason, examples doesn't match specs in this aspect)
 // ns-char ::= c-printable - b-line-feed - b-carriage-return - c-byte-order-mark
-function isNsCharOrWhitespace(c) {
-  return isPrintable(c)
-    && c !== CHAR_BOM
+function isNsCharOrWhitespace (c) {
+  return isPrintable(c) &&
+    c !== CHAR_BOM &&
     // - b-char
-    && c !== CHAR_CARRIAGE_RETURN
-    && c !== CHAR_LINE_FEED;
+    c !== CHAR_CARRIAGE_RETURN &&
+    c !== CHAR_LINE_FEED
 }
 
 // [127]  ns-plain-safe(c) ::= c = flow-out  ⇒ ns-plain-safe-out
@@ -41319,91 +41003,96 @@ function isNsCharOrWhitespace(c) {
 // [130]  ns-plain-char(c) ::=  ( ns-plain-safe(c) - “:” - “#” )
 //                            | ( /* An ns-char preceding */ “#” )
 //                            | ( “:” /* Followed by an ns-plain-safe(c) */ )
-function isPlainSafe(c, prev, inblock) {
-  var cIsNsCharOrWhitespace = isNsCharOrWhitespace(c);
-  var cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c);
+function isPlainSafe (c, prev, inblock) {
+  const cIsNsCharOrWhitespace = isNsCharOrWhitespace(c)
+  const cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c)
   return (
-    // ns-plain-safe
-    inblock ? // c = flow-in
-      cIsNsCharOrWhitespace
-      : cIsNsCharOrWhitespace
-        // - c-flow-indicator
-        && c !== CHAR_COMMA
-        && c !== CHAR_LEFT_SQUARE_BRACKET
-        && c !== CHAR_RIGHT_SQUARE_BRACKET
-        && c !== CHAR_LEFT_CURLY_BRACKET
-        && c !== CHAR_RIGHT_CURLY_BRACKET
-  )
+    (
+      // ns-plain-safe
+      inblock // c = flow-in
+        ? cIsNsCharOrWhitespace
+        : cIsNsCharOrWhitespace &&
+          // - c-flow-indicator
+          c !== CHAR_COMMA &&
+          c !== CHAR_LEFT_SQUARE_BRACKET &&
+          c !== CHAR_RIGHT_SQUARE_BRACKET &&
+          c !== CHAR_LEFT_CURLY_BRACKET &&
+          c !== CHAR_RIGHT_CURLY_BRACKET
+    ) &&
     // ns-plain-char
-    && c !== CHAR_SHARP // false on '#'
-    && !(prev === CHAR_COLON && !cIsNsChar) // false on ': '
-    || (isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) // change to true on '[^ ]#'
-    || (prev === CHAR_COLON && cIsNsChar); // change to true on ':[^ ]'
+    c !== CHAR_SHARP && // false on '#'
+    !(prev === CHAR_COLON && !cIsNsChar)
+  ) || // false on ': '
+  (isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) || // change to true on '[^ ]#'
+  (prev === CHAR_COLON && cIsNsChar) // change to true on ':[^ ]'
 }
 
 // Simplified test for values allowed as the first character in plain style.
-function isPlainSafeFirst(c) {
+function isPlainSafeFirst (c) {
   // Uses a subset of ns-char - c-indicator
   // where ns-char = nb-char - s-white.
   // No support of ( ( “?” | “:” | “-” ) /* Followed by an ns-plain-safe(c)) */ ) part
-  return isPrintable(c) && c !== CHAR_BOM
-    && !isWhitespace(c) // - s-white
+  return isPrintable(c) &&
+    c !== CHAR_BOM &&
+    !isWhitespace(c) && // - s-white
     // - (c-indicator ::=
     // “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}”
-    && c !== CHAR_MINUS
-    && c !== CHAR_QUESTION
-    && c !== CHAR_COLON
-    && c !== CHAR_COMMA
-    && c !== CHAR_LEFT_SQUARE_BRACKET
-    && c !== CHAR_RIGHT_SQUARE_BRACKET
-    && c !== CHAR_LEFT_CURLY_BRACKET
-    && c !== CHAR_RIGHT_CURLY_BRACKET
+    c !== CHAR_MINUS &&
+    c !== CHAR_QUESTION &&
+    c !== CHAR_COLON &&
+    c !== CHAR_COMMA &&
+    c !== CHAR_LEFT_SQUARE_BRACKET &&
+    c !== CHAR_RIGHT_SQUARE_BRACKET &&
+    c !== CHAR_LEFT_CURLY_BRACKET &&
+    c !== CHAR_RIGHT_CURLY_BRACKET &&
     // | “#” | “&” | “*” | “!” | “|” | “=” | “>” | “'” | “"”
-    && c !== CHAR_SHARP
-    && c !== CHAR_AMPERSAND
-    && c !== CHAR_ASTERISK
-    && c !== CHAR_EXCLAMATION
-    && c !== CHAR_VERTICAL_LINE
-    && c !== CHAR_EQUALS
-    && c !== CHAR_GREATER_THAN
-    && c !== CHAR_SINGLE_QUOTE
-    && c !== CHAR_DOUBLE_QUOTE
+    c !== CHAR_SHARP &&
+    c !== CHAR_AMPERSAND &&
+    c !== CHAR_ASTERISK &&
+    c !== CHAR_EXCLAMATION &&
+    c !== CHAR_VERTICAL_LINE &&
+    c !== CHAR_EQUALS &&
+    c !== CHAR_GREATER_THAN &&
+    c !== CHAR_SINGLE_QUOTE &&
+    c !== CHAR_DOUBLE_QUOTE &&
     // | “%” | “@” | “`”)
-    && c !== CHAR_PERCENT
-    && c !== CHAR_COMMERCIAL_AT
-    && c !== CHAR_GRAVE_ACCENT;
+    c !== CHAR_PERCENT &&
+    c !== CHAR_COMMERCIAL_AT &&
+    c !== CHAR_GRAVE_ACCENT
 }
 
 // Simplified test for values allowed as the last character in plain style.
-function isPlainSafeLast(c) {
+function isPlainSafeLast (c) {
   // just not whitespace or colon, it will be checked to be plain character later
-  return !isWhitespace(c) && c !== CHAR_COLON;
+  return !isWhitespace(c) && c !== CHAR_COLON
 }
 
 // Same as 'string'.codePointAt(pos), but works in older browsers.
-function codePointAt(string, pos) {
-  var first = string.charCodeAt(pos), second;
+function codePointAt (string, pos) {
+  const first = string.charCodeAt(pos)
+  let second
+
   if (first >= 0xD800 && first <= 0xDBFF && pos + 1 < string.length) {
-    second = string.charCodeAt(pos + 1);
+    second = string.charCodeAt(pos + 1)
     if (second >= 0xDC00 && second <= 0xDFFF) {
       // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
-      return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
+      return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000
     }
   }
-  return first;
+  return first
 }
 
 // Determines whether block indentation indicator is required.
-function needIndentIndicator(string) {
-  var leadingSpaceRe = /^\n* /;
-  return leadingSpaceRe.test(string);
+function needIndentIndicator (string) {
+  const leadingSpaceRe = /^\n* /
+  return leadingSpaceRe.test(string)
 }
 
-var STYLE_PLAIN   = 1,
-    STYLE_SINGLE  = 2,
-    STYLE_LITERAL = 3,
-    STYLE_FOLDED  = 4,
-    STYLE_DOUBLE  = 5;
+const STYLE_PLAIN = 1
+const STYLE_SINGLE = 2
+const STYLE_LITERAL = 3
+const STYLE_FOLDED = 4
+const STYLE_DOUBLE = 5
 
 // Determines which scalar styles are possible and returns the preferred style.
 // lineWidth = -1 => no limit.
@@ -41412,54 +41101,53 @@ var STYLE_PLAIN   = 1,
 //    STYLE_PLAIN or STYLE_SINGLE => no \n are in the string.
 //    STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1).
 //    STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1).
-function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth,
+function chooseScalarStyle (string, singleLineOnly, indentPerLevel, lineWidth,
   testAmbiguousType, quotingType, forceQuotes, inblock) {
-
-  var i;
-  var char = 0;
-  var prevChar = null;
-  var hasLineBreak = false;
-  var hasFoldableLine = false; // only checked if shouldTrackWidth
-  var shouldTrackWidth = lineWidth !== -1;
-  var previousLineBreak = -1; // count the first line correctly
-  var plain = isPlainSafeFirst(codePointAt(string, 0))
-          && isPlainSafeLast(codePointAt(string, string.length - 1));
+  let i
+  let char = 0
+  let prevChar = null
+  let hasLineBreak = false
+  let hasFoldableLine = false // only checked if shouldTrackWidth
+  const shouldTrackWidth = lineWidth !== -1
+  let previousLineBreak = -1 // count the first line correctly
+  let plain = isPlainSafeFirst(codePointAt(string, 0)) &&
+    isPlainSafeLast(codePointAt(string, string.length - 1))
 
   if (singleLineOnly || forceQuotes) {
     // Case: no block styles.
     // Check for disallowed characters to rule out plain and single.
     for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
-      char = codePointAt(string, i);
+      char = codePointAt(string, i)
       if (!isPrintable(char)) {
-        return STYLE_DOUBLE;
+        return STYLE_DOUBLE
       }
-      plain = plain && isPlainSafe(char, prevChar, inblock);
-      prevChar = char;
+      plain = plain && isPlainSafe(char, prevChar, inblock)
+      prevChar = char
     }
   } else {
     // Case: block styles permitted.
     for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
-      char = codePointAt(string, i);
+      char = codePointAt(string, i)
       if (char === CHAR_LINE_FEED) {
-        hasLineBreak = true;
+        hasLineBreak = true
         // Check if any line can be folded.
         if (shouldTrackWidth) {
           hasFoldableLine = hasFoldableLine ||
             // Foldable line = too long, and not more-indented.
             (i - previousLineBreak - 1 > lineWidth &&
-             string[previousLineBreak + 1] !== ' ');
-          previousLineBreak = i;
+             string[previousLineBreak + 1] !== ' ')
+          previousLineBreak = i
         }
       } else if (!isPrintable(char)) {
-        return STYLE_DOUBLE;
+        return STYLE_DOUBLE
       }
-      plain = plain && isPlainSafe(char, prevChar, inblock);
-      prevChar = char;
+      plain = plain && isPlainSafe(char, prevChar, inblock)
+      prevChar = char
     }
     // in case the end is missing a \n
     hasFoldableLine = hasFoldableLine || (shouldTrackWidth &&
       (i - previousLineBreak - 1 > lineWidth &&
-       string[previousLineBreak + 1] !== ' '));
+       string[previousLineBreak + 1] !== ' '))
   }
   // Although every style can represent \n without escaping, prefer block styles
   // for multiline, since they're more readable and they don't add empty lines.
@@ -41468,20 +41156,20 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth,
     // Strings interpretable as another type have to be quoted;
     // e.g. the string 'true' vs. the boolean true.
     if (plain && !forceQuotes && !testAmbiguousType(string)) {
-      return STYLE_PLAIN;
+      return STYLE_PLAIN
     }
-    return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE;
+    return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE
   }
   // Edge case: block indentation indicator can only have one digit.
   if (indentPerLevel > 9 && needIndentIndicator(string)) {
-    return STYLE_DOUBLE;
+    return STYLE_DOUBLE
   }
   // At this point we know block styles are valid.
   // Prefer literal style unless we want to fold.
   if (!forceQuotes) {
-    return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL;
+    return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL
   }
-  return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE;
+  return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE
 }
 
 // Note: line breaking/folding is implemented for only the folded style.
@@ -41490,18 +41178,18 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth,
 //    • No ending newline => unaffected; already using strip "-" chomping.
 //    • Ending newline    => removed then restored.
 //  Importantly, this keeps the "+" chomp indicator from gaining an extra line.
-function writeScalar(state, string, level, iskey, inblock) {
+function writeScalar (state, string, level, iskey, inblock) {
   state.dump = (function () {
     if (string.length === 0) {
-      return state.quotingType === QUOTING_TYPE_DOUBLE ? '""' : "''";
+      return state.quotingType === QUOTING_TYPE_DOUBLE ? '""' : "''"
     }
     if (!state.noCompatMode) {
       if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string)) {
-        return state.quotingType === QUOTING_TYPE_DOUBLE ? ('"' + string + '"') : ("'" + string + "'");
+        return state.quotingType === QUOTING_TYPE_DOUBLE ? ('"' + string + '"') : ("'" + string + "'")
       }
     }
 
-    var indent = state.indent * Math.max(1, level); // no 0-indent scalars
+    const indent = state.indent * Math.max(1, level) // no 0-indent scalars
     // As indentation gets deeper, let the width decrease monotonically
     // to the lower bound min(state.lineWidth, 40).
     // Note that this implies
@@ -41509,461 +41197,441 @@ function writeScalar(state, string, level, iskey, inblock) {
     //  state.lineWidth > 40 + state.indent: width decreases until the lower bound.
     // This behaves better than a constant minimum width which disallows narrower options,
     // or an indent threshold which causes the width to suddenly increase.
-    var lineWidth = state.lineWidth === -1
-      ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent);
+    const lineWidth = (state.lineWidth === -1)
+      ? -1
+      : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent)
 
     // Without knowing if keys are implicit/explicit, assume implicit for safety.
-    var singleLineOnly = iskey
+    const singleLineOnly = iskey ||
       // No block styles in flow mode.
-      || (state.flowLevel > -1 && level >= state.flowLevel);
-    function testAmbiguity(string) {
-      return testImplicitResolving(state, string);
+      (state.flowLevel > -1 && level >= state.flowLevel)
+    function testAmbiguity (string) {
+      return testImplicitResolving(state, string)
     }
 
     switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth,
       testAmbiguity, state.quotingType, state.forceQuotes && !iskey, inblock)) {
-
       case STYLE_PLAIN:
-        return string;
+        return string
       case STYLE_SINGLE:
-        return "'" + string.replace(/'/g, "''") + "'";
+        return "'" + string.replace(/'/g, "''") + "'"
       case STYLE_LITERAL:
-        return '|' + blockHeader(string, state.indent)
-          + dropEndingNewline(indentString(string, indent));
+        return '|' + blockHeader(string, state.indent) +
+          dropEndingNewline(indentString(string, indent))
       case STYLE_FOLDED:
-        return '>' + blockHeader(string, state.indent)
-          + dropEndingNewline(indentString(foldString(string, lineWidth), indent));
+        return '>' + blockHeader(string, state.indent) +
+          dropEndingNewline(indentString(foldString(string, lineWidth), indent))
       case STYLE_DOUBLE:
-        return '"' + escapeString(string, lineWidth) + '"';
+        return '"' + escapeString(string, lineWidth) + '"'
       default:
-        throw new YAMLException('impossible error: invalid scalar style');
+        throw new YAMLException('impossible error: invalid scalar style')
     }
-  }());
+  }())
 }
 
 // Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.
-function blockHeader(string, indentPerLevel) {
-  var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : '';
+function blockHeader (string, indentPerLevel) {
+  const indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ''
 
   // note the special case: the string '\n' counts as a "trailing" empty line.
-  var clip =          string[string.length - 1] === '\n';
-  var keep = clip && (string[string.length - 2] === '\n' || string === '\n');
-  var chomp = keep ? '+' : (clip ? '' : '-');
+  const clip = string[string.length - 1] === '\n'
+  const keep = clip && (string[string.length - 2] === '\n' || string === '\n')
+  const chomp = keep ? '+' : (clip ? '' : '-')
 
-  return indentIndicator + chomp + '\n';
+  return indentIndicator + chomp + '\n'
 }
 
 // (See the note for writeScalar.)
-function dropEndingNewline(string) {
-  return string[string.length - 1] === '\n' ? string.slice(0, -1) : string;
+function dropEndingNewline (string) {
+  return string[string.length - 1] === '\n' ? string.slice(0, -1) : string
 }
 
 // Note: a long line without a suitable break point will exceed the width limit.
 // Pre-conditions: every char in str isPrintable, str.length > 0, width > 0.
-function foldString(string, width) {
+function foldString (string, width) {
   // In folded style, $k$ consecutive newlines output as $k+1$ newlines—
   // unless they're before or after a more-indented line, or at the very
   // beginning or end, in which case $k$ maps to $k$.
   // Therefore, parse each chunk as newline(s) followed by a content line.
-  var lineRe = /(\n+)([^\n]*)/g;
+  const lineRe = /(\n+)([^\n]*)/g
 
   // first line (possibly an empty line)
-  var result = (function () {
-    var nextLF = string.indexOf('\n');
-    nextLF = nextLF !== -1 ? nextLF : string.length;
-    lineRe.lastIndex = nextLF;
-    return foldLine(string.slice(0, nextLF), width);
-  }());
+  let result = (function () {
+    let nextLF = string.indexOf('\n')
+    nextLF = nextLF !== -1 ? nextLF : string.length
+    lineRe.lastIndex = nextLF
+    return foldLine(string.slice(0, nextLF), width)
+  }())
   // If we haven't reached the first content line yet, don't add an extra \n.
-  var prevMoreIndented = string[0] === '\n' || string[0] === ' ';
-  var moreIndented;
+  let prevMoreIndented = string[0] === '\n' || string[0] === ' '
+  let moreIndented
 
   // rest of the lines
-  var match;
+  let match
   while ((match = lineRe.exec(string))) {
-    var prefix = match[1], line = match[2];
-    moreIndented = (line[0] === ' ');
-    result += prefix
-      + (!prevMoreIndented && !moreIndented && line !== ''
-        ? '\n' : '')
-      + foldLine(line, width);
-    prevMoreIndented = moreIndented;
+    const prefix = match[1]
+    const line = match[2]
+
+    moreIndented = (line[0] === ' ')
+    result += prefix +
+      ((!prevMoreIndented && !moreIndented && line !== '') ? '\n' : '') +
+      foldLine(line, width)
+    prevMoreIndented = moreIndented
   }
 
-  return result;
+  return result
 }
 
 // Greedy line breaking.
 // Picks the longest line under the limit each time,
 // otherwise settles for the shortest line over the limit.
 // NB. More-indented lines *cannot* be folded, as that would add an extra \n.
-function foldLine(line, width) {
-  if (line === '' || line[0] === ' ') return line;
+function foldLine (line, width) {
+  if (line === '' || line[0] === ' ') return line
 
   // Since a more-indented line adds a \n, breaks can't be followed by a space.
-  var breakRe = / [^ ]/g; // note: the match index will always be <= length-2.
-  var match;
+  const breakRe = / [^ ]/g // note: the match index will always be <= length-2.
+  let match
   // start is an inclusive index. end, curr, and next are exclusive.
-  var start = 0, end, curr = 0, next = 0;
-  var result = '';
+  let start = 0
+  let end
+  let curr = 0
+  let next = 0
+  let result = ''
 
   // Invariants: 0 <= start <= length-1.
   //   0 <= curr <= next <= max(0, length-2). curr - start <= width.
   // Inside the loop:
   //   A match implies length >= 2, so curr and next are <= length-2.
   while ((match = breakRe.exec(line))) {
-    next = match.index;
+    next = match.index
     // maintain invariant: curr - start <= width
     if (next - start > width) {
-      end = (curr > start) ? curr : next; // derive end <= length-2
-      result += '\n' + line.slice(start, end);
+      end = (curr > start) ? curr : next // derive end <= length-2
+      result += '\n' + line.slice(start, end)
       // skip the space that was output as \n
-      start = end + 1;                    // derive start <= length-1
+      start = end + 1                    // derive start <= length-1
     }
-    curr = next;
+    curr = next
   }
 
   // By the invariants, start <= length-1, so there is something left over.
   // It is either the whole string or a part starting from non-whitespace.
-  result += '\n';
+  result += '\n'
   // Insert a break if the remainder is too long and there is a break available.
   if (line.length - start > width && curr > start) {
-    result += line.slice(start, curr) + '\n' + line.slice(curr + 1);
+    result += line.slice(start, curr) + '\n' + line.slice(curr + 1)
   } else {
-    result += line.slice(start);
+    result += line.slice(start)
   }
 
-  return result.slice(1); // drop extra \n joiner
+  return result.slice(1) // drop extra \n joiner
 }
 
 // Escapes a double-quoted string.
-function escapeString(string) {
-  var result = '';
-  var char = 0;
-  var escapeSeq;
+function escapeString (string) {
+  let result = ''
+  let char = 0
 
-  for (var i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
-    char = codePointAt(string, i);
-    escapeSeq = ESCAPE_SEQUENCES[char];
+  for (let i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
+    char = codePointAt(string, i)
+    const escapeSeq = ESCAPE_SEQUENCES[char]
 
     if (!escapeSeq && isPrintable(char)) {
-      result += string[i];
-      if (char >= 0x10000) result += string[i + 1];
+      result += string[i]
+      if (char >= 0x10000) result += string[i + 1]
     } else {
-      result += escapeSeq || encodeHex(char);
+      result += escapeSeq || encodeHex(char)
     }
   }
 
-  return result;
+  return result
 }
 
-function writeFlowSequence(state, level, object) {
-  var _result = '',
-      _tag    = state.tag,
-      index,
-      length,
-      value;
+function writeFlowSequence (state, level, object) {
+  let _result = ''
+  const _tag = state.tag
 
-  for (index = 0, length = object.length; index < length; index += 1) {
-    value = object[index];
+  for (let index = 0, length = object.length; index < length; index += 1) {
+    let value = object[index]
 
     if (state.replacer) {
-      value = state.replacer.call(object, String(index), value);
+      value = state.replacer.call(object, String(index), value)
     }
 
     // Write only valid elements, put null instead of invalid elements.
     if (writeNode(state, level, value, false, false) ||
         (typeof value === 'undefined' &&
          writeNode(state, level, null, false, false))) {
-
-      if (_result !== '') _result += ',' + (!state.condenseFlow ? ' ' : '');
-      _result += state.dump;
+      if (_result !== '') _result += ',' + (!state.condenseFlow ? ' ' : '')
+      _result += state.dump
     }
   }
 
-  state.tag = _tag;
-  state.dump = '[' + _result + ']';
+  state.tag = _tag
+  state.dump = '[' + _result + ']'
 }
 
-function writeBlockSequence(state, level, object, compact) {
-  var _result = '',
-      _tag    = state.tag,
-      index,
-      length,
-      value;
+function writeBlockSequence (state, level, object, compact) {
+  let _result = ''
+  const _tag = state.tag
 
-  for (index = 0, length = object.length; index < length; index += 1) {
-    value = object[index];
+  for (let index = 0, length = object.length; index < length; index += 1) {
+    let value = object[index]
 
     if (state.replacer) {
-      value = state.replacer.call(object, String(index), value);
+      value = state.replacer.call(object, String(index), value)
     }
 
     // Write only valid elements, put null instead of invalid elements.
     if (writeNode(state, level + 1, value, true, true, false, true) ||
         (typeof value === 'undefined' &&
          writeNode(state, level + 1, null, true, true, false, true))) {
-
       if (!compact || _result !== '') {
-        _result += generateNextLine(state, level);
+        _result += generateNextLine(state, level)
       }
 
       if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
-        _result += '-';
+        _result += '-'
       } else {
-        _result += '- ';
+        _result += '- '
       }
 
-      _result += state.dump;
+      _result += state.dump
     }
   }
 
-  state.tag = _tag;
-  state.dump = _result || '[]'; // Empty sequence if no valid values.
+  state.tag = _tag
+  state.dump = _result || '[]' // Empty sequence if no valid values.
 }
 
-function writeFlowMapping(state, level, object) {
-  var _result       = '',
-      _tag          = state.tag,
-      objectKeyList = Object.keys(object),
-      index,
-      length,
-      objectKey,
-      objectValue,
-      pairBuffer;
-
-  for (index = 0, length = objectKeyList.length; index < length; index += 1) {
+function writeFlowMapping (state, level, object) {
+  let _result = ''
+  const _tag = state.tag
+  const objectKeyList = Object.keys(object)
 
-    pairBuffer = '';
-    if (_result !== '') pairBuffer += ', ';
+  for (let index = 0, length = objectKeyList.length; index < length; index += 1) {
+    let pairBuffer = ''
+    if (_result !== '') pairBuffer += ', '
 
-    if (state.condenseFlow) pairBuffer += '"';
+    if (state.condenseFlow) pairBuffer += '"'
 
-    objectKey = objectKeyList[index];
-    objectValue = object[objectKey];
+    const objectKey = objectKeyList[index]
+    let objectValue = object[objectKey]
 
     if (state.replacer) {
-      objectValue = state.replacer.call(object, objectKey, objectValue);
+      objectValue = state.replacer.call(object, objectKey, objectValue)
     }
 
     if (!writeNode(state, level, objectKey, false, false)) {
-      continue; // Skip this pair because of invalid key;
+      continue // Skip this pair because of invalid key;
     }
 
-    if (state.dump.length > 1024) pairBuffer += '? ';
+    if (state.dump.length > 1024) pairBuffer += '? '
 
-    pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' ');
+    pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' ')
 
     if (!writeNode(state, level, objectValue, false, false)) {
-      continue; // Skip this pair because of invalid value.
+      continue // Skip this pair because of invalid value.
     }
 
-    pairBuffer += state.dump;
+    pairBuffer += state.dump
 
     // Both key and value are valid.
-    _result += pairBuffer;
+    _result += pairBuffer
   }
 
-  state.tag = _tag;
-  state.dump = '{' + _result + '}';
+  state.tag = _tag
+  state.dump = '{' + _result + '}'
 }
 
-function writeBlockMapping(state, level, object, compact) {
-  var _result       = '',
-      _tag          = state.tag,
-      objectKeyList = Object.keys(object),
-      index,
-      length,
-      objectKey,
-      objectValue,
-      explicitPair,
-      pairBuffer;
+function writeBlockMapping (state, level, object, compact) {
+  let _result = ''
+  const _tag = state.tag
+  const objectKeyList = Object.keys(object)
 
   // Allow sorting keys so that the output file is deterministic
   if (state.sortKeys === true) {
     // Default sorting
-    objectKeyList.sort();
+    objectKeyList.sort()
   } else if (typeof state.sortKeys === 'function') {
     // Custom sort function
-    objectKeyList.sort(state.sortKeys);
+    objectKeyList.sort(state.sortKeys)
   } else if (state.sortKeys) {
     // Something is wrong
-    throw new YAMLException('sortKeys must be a boolean or a function');
+    throw new YAMLException('sortKeys must be a boolean or a function')
   }
 
-  for (index = 0, length = objectKeyList.length; index < length; index += 1) {
-    pairBuffer = '';
+  for (let index = 0, length = objectKeyList.length; index < length; index += 1) {
+    let pairBuffer = ''
 
     if (!compact || _result !== '') {
-      pairBuffer += generateNextLine(state, level);
+      pairBuffer += generateNextLine(state, level)
     }
 
-    objectKey = objectKeyList[index];
-    objectValue = object[objectKey];
+    const objectKey = objectKeyList[index]
+    let objectValue = object[objectKey]
 
     if (state.replacer) {
-      objectValue = state.replacer.call(object, objectKey, objectValue);
+      objectValue = state.replacer.call(object, objectKey, objectValue)
     }
 
     if (!writeNode(state, level + 1, objectKey, true, true, true)) {
-      continue; // Skip this pair because of invalid key.
+      continue // Skip this pair because of invalid key.
     }
 
-    explicitPair = (state.tag !== null && state.tag !== '?') ||
-                   (state.dump && state.dump.length > 1024);
+    const explicitPair = (state.tag !== null && state.tag !== '?') ||
+                   (state.dump && state.dump.length > 1024)
 
     if (explicitPair) {
       if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
-        pairBuffer += '?';
+        pairBuffer += '?'
       } else {
-        pairBuffer += '? ';
+        pairBuffer += '? '
       }
     }
 
-    pairBuffer += state.dump;
+    pairBuffer += state.dump
 
     if (explicitPair) {
-      pairBuffer += generateNextLine(state, level);
+      pairBuffer += generateNextLine(state, level)
     }
 
     if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {
-      continue; // Skip this pair because of invalid value.
+      continue // Skip this pair because of invalid value.
     }
 
     if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
-      pairBuffer += ':';
+      pairBuffer += ':'
     } else {
-      pairBuffer += ': ';
+      pairBuffer += ': '
     }
 
-    pairBuffer += state.dump;
+    pairBuffer += state.dump
 
     // Both key and value are valid.
-    _result += pairBuffer;
+    _result += pairBuffer
   }
 
-  state.tag = _tag;
-  state.dump = _result || '{}'; // Empty mapping if no valid pairs.
+  state.tag = _tag
+  state.dump = _result || '{}' // Empty mapping if no valid pairs.
 }
 
-function detectType(state, object, explicit) {
-  var _result, typeList, index, length, type, style;
-
-  typeList = explicit ? state.explicitTypes : state.implicitTypes;
+function detectType (state, object, explicit) {
+  const typeList = explicit ? state.explicitTypes : state.implicitTypes
 
-  for (index = 0, length = typeList.length; index < length; index += 1) {
-    type = typeList[index];
+  for (let index = 0, length = typeList.length; index < length; index += 1) {
+    const type = typeList[index]
 
-    if ((type.instanceOf  || type.predicate) &&
+    if ((type.instanceOf || type.predicate) &&
         (!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) &&
-        (!type.predicate  || type.predicate(object))) {
-
+        (!type.predicate || type.predicate(object))) {
       if (explicit) {
         if (type.multi && type.representName) {
-          state.tag = type.representName(object);
+          state.tag = type.representName(object)
         } else {
-          state.tag = type.tag;
+          state.tag = type.tag
         }
       } else {
-        state.tag = '?';
+        state.tag = '?'
       }
 
       if (type.represent) {
-        style = state.styleMap[type.tag] || type.defaultStyle;
+        const style = state.styleMap[type.tag] || type.defaultStyle
 
+        let _result
         if (_toString.call(type.represent) === '[object Function]') {
-          _result = type.represent(object, style);
+          _result = type.represent(object, style)
         } else if (_hasOwnProperty.call(type.represent, style)) {
-          _result = type.represent[style](object, style);
+          _result = type.represent[style](object, style)
         } else {
-          throw new YAMLException('!<' + type.tag + '> tag resolver accepts not "' + style + '" style');
+          throw new YAMLException('!<' + type.tag + '> tag resolver accepts not "' + style + '" style')
         }
 
-        state.dump = _result;
+        state.dump = _result
       }
 
-      return true;
+      return true
     }
   }
 
-  return false;
+  return false
 }
 
 // Serializes `object` and writes it to global `result`.
 // Returns true on success, or false on invalid object.
 //
-function writeNode(state, level, object, block, compact, iskey, isblockseq) {
-  state.tag = null;
-  state.dump = object;
+function writeNode (state, level, object, block, compact, iskey, isblockseq) {
+  state.tag = null
+  state.dump = object
 
   if (!detectType(state, object, false)) {
-    detectType(state, object, true);
+    detectType(state, object, true)
   }
 
-  var type = _toString.call(state.dump);
-  var inblock = block;
-  var tagStr;
+  const type = _toString.call(state.dump)
+  const inblock = block
 
   if (block) {
-    block = (state.flowLevel < 0 || state.flowLevel > level);
+    block = (state.flowLevel < 0 || state.flowLevel > level)
   }
 
-  var objectOrArray = type === '[object Object]' || type === '[object Array]',
-      duplicateIndex,
-      duplicate;
+  const objectOrArray = type === '[object Object]' || type === '[object Array]'
+  let duplicateIndex
+  let duplicate
 
   if (objectOrArray) {
-    duplicateIndex = state.duplicates.indexOf(object);
-    duplicate = duplicateIndex !== -1;
+    duplicateIndex = state.duplicates.indexOf(object)
+    duplicate = duplicateIndex !== -1
   }
 
   if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) {
-    compact = false;
+    compact = false
   }
 
   if (duplicate && state.usedDuplicates[duplicateIndex]) {
-    state.dump = '*ref_' + duplicateIndex;
+    state.dump = '*ref_' + duplicateIndex
   } else {
     if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {
-      state.usedDuplicates[duplicateIndex] = true;
+      state.usedDuplicates[duplicateIndex] = true
     }
     if (type === '[object Object]') {
       if (block && (Object.keys(state.dump).length !== 0)) {
-        writeBlockMapping(state, level, state.dump, compact);
+        writeBlockMapping(state, level, state.dump, compact)
         if (duplicate) {
-          state.dump = '&ref_' + duplicateIndex + state.dump;
+          state.dump = '&ref_' + duplicateIndex + state.dump
         }
       } else {
-        writeFlowMapping(state, level, state.dump);
+        writeFlowMapping(state, level, state.dump)
         if (duplicate) {
-          state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;
+          state.dump = '&ref_' + duplicateIndex + ' ' + state.dump
         }
       }
     } else if (type === '[object Array]') {
       if (block && (state.dump.length !== 0)) {
         if (state.noArrayIndent && !isblockseq && level > 0) {
-          writeBlockSequence(state, level - 1, state.dump, compact);
+          writeBlockSequence(state, level - 1, state.dump, compact)
         } else {
-          writeBlockSequence(state, level, state.dump, compact);
+          writeBlockSequence(state, level, state.dump, compact)
         }
         if (duplicate) {
-          state.dump = '&ref_' + duplicateIndex + state.dump;
+          state.dump = '&ref_' + duplicateIndex + state.dump
         }
       } else {
-        writeFlowSequence(state, level, state.dump);
+        writeFlowSequence(state, level, state.dump)
         if (duplicate) {
-          state.dump = '&ref_' + duplicateIndex + ' ' + state.dump;
+          state.dump = '&ref_' + duplicateIndex + ' ' + state.dump
         }
       }
     } else if (type === '[object String]') {
       if (state.tag !== '?') {
-        writeScalar(state, state.dump, level, iskey, inblock);
+        writeScalar(state, state.dump, level, iskey, inblock)
       }
     } else if (type === '[object Undefined]') {
-      return false;
+      return false
     } else {
-      if (state.skipInvalid) return false;
-      throw new YAMLException('unacceptable kind of an object to dump ' + type);
+      if (state.skipInvalid) return false
+      throw new YAMLException('unacceptable kind of an object to dump ' + type)
     }
 
     if (state.tag !== null && state.tag !== '?') {
@@ -41980,87 +41648,82 @@ function writeNode(state, level, object, block, compact, iskey, isblockseq) {
       //
       // Also need to encode '!' because it has special meaning (end of tag prefix).
       //
-      tagStr = encodeURI(
+      let tagStr = encodeURI(
         state.tag[0] === '!' ? state.tag.slice(1) : state.tag
-      ).replace(/!/g, '%21');
+      ).replace(/!/g, '%21')
 
       if (state.tag[0] === '!') {
-        tagStr = '!' + tagStr;
+        tagStr = '!' + tagStr
       } else if (tagStr.slice(0, 18) === 'tag:yaml.org,2002:') {
-        tagStr = '!!' + tagStr.slice(18);
+        tagStr = '!!' + tagStr.slice(18)
       } else {
-        tagStr = '!<' + tagStr + '>';
+        tagStr = '!<' + tagStr + '>'
       }
 
-      state.dump = tagStr + ' ' + state.dump;
+      state.dump = tagStr + ' ' + state.dump
     }
   }
 
-  return true;
+  return true
 }
 
-function getDuplicateReferences(object, state) {
-  var objects = [],
-      duplicatesIndexes = [],
-      index,
-      length;
+function getDuplicateReferences (object, state) {
+  const objects = []
+  const duplicatesIndexes = []
 
-  inspectNode(object, objects, duplicatesIndexes);
+  inspectNode(object, objects, duplicatesIndexes)
 
-  for (index = 0, length = duplicatesIndexes.length; index < length; index += 1) {
-    state.duplicates.push(objects[duplicatesIndexes[index]]);
+  const length = duplicatesIndexes.length
+  for (let index = 0; index < length; index += 1) {
+    state.duplicates.push(objects[duplicatesIndexes[index]])
   }
-  state.usedDuplicates = new Array(length);
+  state.usedDuplicates = new Array(length)
 }
 
-function inspectNode(object, objects, duplicatesIndexes) {
-  var objectKeyList,
-      index,
-      length;
-
+function inspectNode (object, objects, duplicatesIndexes) {
   if (object !== null && typeof object === 'object') {
-    index = objects.indexOf(object);
+    const index = objects.indexOf(object)
     if (index !== -1) {
       if (duplicatesIndexes.indexOf(index) === -1) {
-        duplicatesIndexes.push(index);
+        duplicatesIndexes.push(index)
       }
     } else {
-      objects.push(object);
+      objects.push(object)
 
       if (Array.isArray(object)) {
-        for (index = 0, length = object.length; index < length; index += 1) {
-          inspectNode(object[index], objects, duplicatesIndexes);
+        for (let i = 0, length = object.length; i < length; i += 1) {
+          inspectNode(object[i], objects, duplicatesIndexes)
         }
       } else {
-        objectKeyList = Object.keys(object);
+        const objectKeyList = Object.keys(object)
 
-        for (index = 0, length = objectKeyList.length; index < length; index += 1) {
-          inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes);
+        for (let i = 0, length = objectKeyList.length; i < length; i += 1) {
+          inspectNode(object[objectKeyList[i]], objects, duplicatesIndexes)
         }
       }
     }
   }
 }
 
-function dump(input, options) {
-  options = options || {};
+function dump (input, options) {
+  options = options || {}
 
-  var state = new State(options);
+  const state = new State(options)
 
-  if (!state.noRefs) getDuplicateReferences(input, state);
+  if (!state.noRefs) getDuplicateReferences(input, state)
 
-  var value = input;
+  let value = input
 
   if (state.replacer) {
-    value = state.replacer.call({ '': value }, '', value);
+    value = state.replacer.call({ '': value }, '', value)
   }
 
-  if (writeNode(state, 0, value, true, true)) return state.dump + '\n';
+  if (writeNode(state, 0, value, true, true)) return state.dump + '\n'
 
-  return '';
+  return ''
 }
 
-module.exports.dump = dump;
+module.exports.dump = dump
 
 
 /***/ }),
@@ -42073,57 +41736,53 @@ module.exports.dump = dump;
 //
 
 
+function formatError (exception, compact) {
+  let where = ''
+  const message = exception.reason || '(unknown reason)'
 
-function formatError(exception, compact) {
-  var where = '', message = exception.reason || '(unknown reason)';
-
-  if (!exception.mark) return message;
+  if (!exception.mark) return message
 
   if (exception.mark.name) {
-    where += 'in "' + exception.mark.name + '" ';
+    where += 'in "' + exception.mark.name + '" '
   }
 
-  where += '(' + (exception.mark.line + 1) + ':' + (exception.mark.column + 1) + ')';
+  where += '(' + (exception.mark.line + 1) + ':' + (exception.mark.column + 1) + ')'
 
   if (!compact && exception.mark.snippet) {
-    where += '\n\n' + exception.mark.snippet;
+    where += '\n\n' + exception.mark.snippet
   }
 
-  return message + ' ' + where;
+  return message + ' ' + where
 }
 
-
-function YAMLException(reason, mark) {
+function YAMLException (reason, mark) {
   // Super constructor
-  Error.call(this);
+  Error.call(this)
 
-  this.name = 'YAMLException';
-  this.reason = reason;
-  this.mark = mark;
-  this.message = formatError(this, false);
+  this.name = 'YAMLException'
+  this.reason = reason
+  this.mark = mark
+  this.message = formatError(this, false)
 
   // Include stack trace in error object
   if (Error.captureStackTrace) {
     // Chrome and NodeJS
-    Error.captureStackTrace(this, this.constructor);
+    Error.captureStackTrace(this, this.constructor)
   } else {
     // FF, IE 10+ and Safari 6+. Fallback for others
-    this.stack = (new Error()).stack || '';
+    this.stack = (new Error()).stack || ''
   }
 }
 
-
 // Inherit from Error
-YAMLException.prototype = Object.create(Error.prototype);
-YAMLException.prototype.constructor = YAMLException;
-
-
-YAMLException.prototype.toString = function toString(compact) {
-  return this.name + ': ' + formatError(this, compact);
-};
+YAMLException.prototype = Object.create(Error.prototype)
+YAMLException.prototype.constructor = YAMLException
 
+YAMLException.prototype.toString = function toString (compact) {
+  return this.name + ': ' + formatError(this, compact)
+}
 
-module.exports = YAMLException;
+module.exports = YAMLException
 
 
 /***/ }),
@@ -42134,129 +41793,125 @@ module.exports = YAMLException;
 "use strict";
 
 
-/*eslint-disable max-len,no-use-before-define*/
-
-var common              = __nccwpck_require__(19816);
-var YAMLException       = __nccwpck_require__(41248);
-var makeSnippet         = __nccwpck_require__(9440);
-var DEFAULT_SCHEMA      = __nccwpck_require__(97336);
-
-
-var _hasOwnProperty = Object.prototype.hasOwnProperty;
+const common = __nccwpck_require__(19816)
+const YAMLException = __nccwpck_require__(41248)
+const makeSnippet = __nccwpck_require__(9440)
+const DEFAULT_SCHEMA = __nccwpck_require__(97336)
 
+const _hasOwnProperty = Object.prototype.hasOwnProperty
 
-var CONTEXT_FLOW_IN   = 1;
-var CONTEXT_FLOW_OUT  = 2;
-var CONTEXT_BLOCK_IN  = 3;
-var CONTEXT_BLOCK_OUT = 4;
+const CONTEXT_FLOW_IN = 1
+const CONTEXT_FLOW_OUT = 2
+const CONTEXT_BLOCK_IN = 3
+const CONTEXT_BLOCK_OUT = 4
 
+const CHOMPING_CLIP = 1
+const CHOMPING_STRIP = 2
+const CHOMPING_KEEP = 3
 
-var CHOMPING_CLIP  = 1;
-var CHOMPING_STRIP = 2;
-var CHOMPING_KEEP  = 3;
+// eslint-disable-next-line no-control-regex
+const PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/
+const PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/
+// eslint-disable-next-line no-useless-escape
+const PATTERN_FLOW_INDICATORS = /[,\[\]{}]/
+// eslint-disable-next-line no-useless-escape
+const PATTERN_TAG_HANDLE = /^(?:!|!!|![0-9A-Za-z-]+!)$/
+// eslint-disable-next-line no-useless-escape
+const PATTERN_TAG_URI = /^(?:!|[^,\[\]{}])(?:%[0-9a-f]{2}|[0-9a-z\-#;/?:@&=+$,_.!~*'()\[\]])*$/i
 
+function _class (obj) { return Object.prototype.toString.call(obj) }
 
-var PATTERN_NON_PRINTABLE         = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/;
-var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/;
-var PATTERN_FLOW_INDICATORS       = /[,\[\]\{\}]/;
-var PATTERN_TAG_HANDLE            = /^(?:!|!!|![a-z\-]+!)$/i;
-var PATTERN_TAG_URI               = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;
-
-
-function _class(obj) { return Object.prototype.toString.call(obj); }
-
-function is_EOL(c) {
-  return (c === 0x0A/* LF */) || (c === 0x0D/* CR */);
+function isEol (c) {
+  return (c === 0x0A/* LF */) || (c === 0x0D/* CR */)
 }
 
-function is_WHITE_SPACE(c) {
-  return (c === 0x09/* Tab */) || (c === 0x20/* Space */);
+function isWhiteSpace (c) {
+  return (c === 0x09/* Tab */) || (c === 0x20/* Space */)
 }
 
-function is_WS_OR_EOL(c) {
+function isWsOrEol (c) {
   return (c === 0x09/* Tab */) ||
          (c === 0x20/* Space */) ||
          (c === 0x0A/* LF */) ||
-         (c === 0x0D/* CR */);
+         (c === 0x0D/* CR */)
 }
 
-function is_FLOW_INDICATOR(c) {
+function isFlowIndicator (c) {
   return c === 0x2C/* , */ ||
          c === 0x5B/* [ */ ||
          c === 0x5D/* ] */ ||
          c === 0x7B/* { */ ||
-         c === 0x7D/* } */;
+         c === 0x7D/* } */
 }
 
-function fromHexCode(c) {
-  var lc;
-
-  if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
-    return c - 0x30;
+function fromHexCode (c) {
+  if ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */)) {
+    return c - 0x30
   }
 
-  /*eslint-disable no-bitwise*/
-  lc = c | 0x20;
+  const lc = c | 0x20
 
-  if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) {
-    return lc - 0x61 + 10;
+  if ((lc >= 0x61/* a */) && (lc <= 0x66/* f */)) {
+    return lc - 0x61 + 10
   }
 
-  return -1;
+  return -1
 }
 
-function escapedHexLen(c) {
-  if (c === 0x78/* x */) { return 2; }
-  if (c === 0x75/* u */) { return 4; }
-  if (c === 0x55/* U */) { return 8; }
-  return 0;
+function escapedHexLen (c) {
+  if (c === 0x78/* x */) { return 2 }
+  if (c === 0x75/* u */) { return 4 }
+  if (c === 0x55/* U */) { return 8 }
+  return 0
 }
 
-function fromDecimalCode(c) {
-  if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) {
-    return c - 0x30;
+function fromDecimalCode (c) {
+  if ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */)) {
+    return c - 0x30
   }
 
-  return -1;
+  return -1
 }
 
-function simpleEscapeSequence(c) {
-  /* eslint-disable indent */
-  return (c === 0x30/* 0 */) ? '\x00' :
-        (c === 0x61/* a */) ? '\x07' :
-        (c === 0x62/* b */) ? '\x08' :
-        (c === 0x74/* t */) ? '\x09' :
-        (c === 0x09/* Tab */) ? '\x09' :
-        (c === 0x6E/* n */) ? '\x0A' :
-        (c === 0x76/* v */) ? '\x0B' :
-        (c === 0x66/* f */) ? '\x0C' :
-        (c === 0x72/* r */) ? '\x0D' :
-        (c === 0x65/* e */) ? '\x1B' :
-        (c === 0x20/* Space */) ? ' ' :
-        (c === 0x22/* " */) ? '\x22' :
-        (c === 0x2F/* / */) ? '/' :
-        (c === 0x5C/* \ */) ? '\x5C' :
-        (c === 0x4E/* N */) ? '\x85' :
-        (c === 0x5F/* _ */) ? '\xA0' :
-        (c === 0x4C/* L */) ? '\u2028' :
-        (c === 0x50/* P */) ? '\u2029' : '';
-}
-
-function charFromCodepoint(c) {
+function simpleEscapeSequence (c) {
+  switch (c) {
+    case 0x30/* 0 */: return '\x00'
+    case 0x61/* a */: return '\x07'
+    case 0x62/* b */: return '\x08'
+    case 0x74/* t */: return '\x09'
+    case 0x09/* Tab */: return '\x09'
+    case 0x6E/* n */: return '\x0A'
+    case 0x76/* v */: return '\x0B'
+    case 0x66/* f */: return '\x0C'
+    case 0x72/* r */: return '\x0D'
+    case 0x65/* e */: return '\x1B'
+    case 0x20/* Space */: return ' '
+    case 0x22/* " */: return '\x22'
+    case 0x2F/* / */: return '/'
+    case 0x5C/* \ */: return '\x5C'
+    case 0x4E/* N */: return '\x85'
+    case 0x5F/* _ */: return '\xA0'
+    case 0x4C/* L */: return '\u2028'
+    case 0x50/* P */: return '\u2029'
+    default: return ''
+  }
+}
+
+function charFromCodepoint (c) {
   if (c <= 0xFFFF) {
-    return String.fromCharCode(c);
+    return String.fromCharCode(c)
   }
   // Encode UTF-16 surrogate pair
   // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF
   return String.fromCharCode(
     ((c - 0x010000) >> 10) + 0xD800,
     ((c - 0x010000) & 0x03FF) + 0xDC00
-  );
+  )
 }
 
 // set a property of a literal object, while protecting against prototype pollution,
 // see https://github.com/nodeca/js-yaml/issues/164 for more details
-function setProperty(object, key, value) {
+function setProperty (object, key, value) {
   // used for this specific key only because Object.defineProperty is slow
   if (key === '__proto__') {
     Object.defineProperty(object, key, {
@@ -42264,47 +41919,51 @@ function setProperty(object, key, value) {
       enumerable: true,
       writable: true,
       value: value
-    });
+    })
   } else {
-    object[key] = value;
+    object[key] = value
   }
 }
 
-var simpleEscapeCheck = new Array(256); // integer, for fast access
-var simpleEscapeMap = new Array(256);
-for (var i = 0; i < 256; i++) {
-  simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0;
-  simpleEscapeMap[i] = simpleEscapeSequence(i);
+const simpleEscapeCheck = new Array(256) // integer, for fast access
+const simpleEscapeMap = new Array(256)
+for (let i = 0; i < 256; i++) {
+  simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0
+  simpleEscapeMap[i] = simpleEscapeSequence(i)
 }
 
+function State (input, options) {
+  this.input = input
 
-function State(input, options) {
-  this.input = input;
-
-  this.filename  = options['filename']  || null;
-  this.schema    = options['schema']    || DEFAULT_SCHEMA;
-  this.onWarning = options['onWarning'] || null;
+  this.filename = options['filename'] || null
+  this.schema = options['schema'] || DEFAULT_SCHEMA
+  this.onWarning = options['onWarning'] || null
   // (Hidden) Remove? makes the loader to expect YAML 1.1 documents
   // if such documents have no explicit %YAML directive
-  this.legacy    = options['legacy']    || false;
+  this.legacy = options['legacy'] || false
 
-  this.json      = options['json']      || false;
-  this.listener  = options['listener']  || null;
+  this.json = options['json'] || false
+  this.listener = options['listener'] || null
+  this.maxDepth = typeof options['maxDepth'] === 'number' ? options['maxDepth'] : 100
+  this.maxTotalMergeKeys = typeof options['maxTotalMergeKeys'] === 'number' ? options['maxTotalMergeKeys'] : 10000
 
-  this.implicitTypes = this.schema.compiledImplicit;
-  this.typeMap       = this.schema.compiledTypeMap;
+  this.implicitTypes = this.schema.compiledImplicit
+  this.typeMap = this.schema.compiledTypeMap
 
-  this.length     = input.length;
-  this.position   = 0;
-  this.line       = 0;
-  this.lineStart  = 0;
-  this.lineIndent = 0;
+  this.length = input.length
+  this.position = 0
+  this.line = 0
+  this.lineStart = 0
+  this.lineIndent = 0
+  this.depth = 0
+  this.totalMergeKeys = 0
 
   // position of first leading tab in the current line,
   // used to make sure there are no tabs in the indentation
-  this.firstTabInLine = -1;
+  this.firstTabInLine = -1
 
-  this.documents = [];
+  this.documents = []
+  this.anchorMapTransactions = []
 
   /*
   this.version;
@@ -42314,164 +41973,233 @@ function State(input, options) {
   this.tag;
   this.anchor;
   this.kind;
-  this.result;*/
-
+  this.result; */
 }
 
-
-function generateError(state, message) {
-  var mark = {
-    name:     state.filename,
-    buffer:   state.input.slice(0, -1), // omit trailing \0
+function generateError (state, message) {
+  const mark = {
+    name: state.filename,
+    buffer: state.input.slice(0, -1), // omit trailing \0
     position: state.position,
-    line:     state.line,
-    column:   state.position - state.lineStart
-  };
+    line: state.line,
+    column: state.position - state.lineStart
+  }
 
-  mark.snippet = makeSnippet(mark);
+  mark.snippet = makeSnippet(mark)
 
-  return new YAMLException(message, mark);
+  return new YAMLException(message, mark)
 }
 
-function throwError(state, message) {
-  throw generateError(state, message);
+function throwError (state, message) {
+  throw generateError(state, message)
 }
 
-function throwWarning(state, message) {
+function throwWarning (state, message) {
   if (state.onWarning) {
-    state.onWarning.call(null, generateError(state, message));
+    state.onWarning.call(null, generateError(state, message))
   }
 }
 
+function storeAnchor (state, name, value) {
+  const transactions = state.anchorMapTransactions
 
-var directiveHandlers = {
+  if (transactions.length !== 0) {
+    const transaction = transactions[transactions.length - 1]
 
-  YAML: function handleYamlDirective(state, name, args) {
+    if (!_hasOwnProperty.call(transaction, name)) {
+      transaction[name] = {
+        existed: _hasOwnProperty.call(state.anchorMap, name),
+        value: state.anchorMap[name]
+      }
+    }
+  }
+
+  state.anchorMap[name] = value
+}
 
-    var match, major, minor;
+function beginAnchorTransaction (state) {
+  state.anchorMapTransactions.push(Object.create(null))
+}
+
+function commitAnchorTransaction (state) {
+  const transaction = state.anchorMapTransactions.pop()
+  const transactions = state.anchorMapTransactions
+
+  if (transactions.length === 0) return
+
+  const parent = transactions[transactions.length - 1]
+  const names = Object.keys(transaction)
+
+  for (let index = 0, length = names.length; index < length; index += 1) {
+    const name = names[index]
+
+    if (!_hasOwnProperty.call(parent, name)) {
+      parent[name] = transaction[name]
+    }
+  }
+}
 
+function rollbackAnchorTransaction (state) {
+  const transaction = state.anchorMapTransactions.pop()
+  const names = Object.keys(transaction)
+
+  for (let index = names.length - 1; index >= 0; index -= 1) {
+    const entry = transaction[names[index]]
+
+    if (entry.existed) {
+      state.anchorMap[names[index]] = entry.value
+    } else {
+      delete state.anchorMap[names[index]]
+    }
+  }
+}
+
+function snapshotState (state) {
+  return {
+    position: state.position,
+    line: state.line,
+    lineStart: state.lineStart,
+    lineIndent: state.lineIndent,
+    firstTabInLine: state.firstTabInLine,
+    tag: state.tag,
+    anchor: state.anchor,
+    kind: state.kind,
+    result: state.result
+  }
+}
+
+function restoreState (state, snapshot) {
+  state.position = snapshot.position
+  state.line = snapshot.line
+  state.lineStart = snapshot.lineStart
+  state.lineIndent = snapshot.lineIndent
+  state.firstTabInLine = snapshot.firstTabInLine
+  state.tag = snapshot.tag
+  state.anchor = snapshot.anchor
+  state.kind = snapshot.kind
+  state.result = snapshot.result
+}
+
+const directiveHandlers = {
+
+  YAML: function handleYamlDirective (state, name, args) {
     if (state.version !== null) {
-      throwError(state, 'duplication of %YAML directive');
+      throwError(state, 'duplication of %YAML directive')
     }
 
     if (args.length !== 1) {
-      throwError(state, 'YAML directive accepts exactly one argument');
+      throwError(state, 'YAML directive accepts exactly one argument')
     }
 
-    match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]);
+    const match = /^([0-9]+)\.([0-9]+)$/.exec(args[0])
 
     if (match === null) {
-      throwError(state, 'ill-formed argument of the YAML directive');
+      throwError(state, 'ill-formed argument of the YAML directive')
     }
 
-    major = parseInt(match[1], 10);
-    minor = parseInt(match[2], 10);
+    const major = parseInt(match[1], 10)
+    const minor = parseInt(match[2], 10)
 
     if (major !== 1) {
-      throwError(state, 'unacceptable YAML version of the document');
+      throwError(state, 'unacceptable YAML version of the document')
     }
 
-    state.version = args[0];
-    state.checkLineBreaks = (minor < 2);
+    state.version = args[0]
+    state.checkLineBreaks = (minor < 2)
 
     if (minor !== 1 && minor !== 2) {
-      throwWarning(state, 'unsupported YAML version of the document');
+      throwWarning(state, 'unsupported YAML version of the document')
     }
   },
 
-  TAG: function handleTagDirective(state, name, args) {
-
-    var handle, prefix;
+  TAG: function handleTagDirective (state, name, args) {
+    let prefix
 
     if (args.length !== 2) {
-      throwError(state, 'TAG directive accepts exactly two arguments');
+      throwError(state, 'TAG directive accepts exactly two arguments')
     }
 
-    handle = args[0];
-    prefix = args[1];
+    const handle = args[0]
+    prefix = args[1]
 
     if (!PATTERN_TAG_HANDLE.test(handle)) {
-      throwError(state, 'ill-formed tag handle (first argument) of the TAG directive');
+      throwError(state, 'ill-formed tag handle (first argument) of the TAG directive')
     }
 
     if (_hasOwnProperty.call(state.tagMap, handle)) {
-      throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle');
+      throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle')
     }
 
     if (!PATTERN_TAG_URI.test(prefix)) {
-      throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive');
+      throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive')
     }
 
     try {
-      prefix = decodeURIComponent(prefix);
+      prefix = decodeURIComponent(prefix)
     } catch (err) {
-      throwError(state, 'tag prefix is malformed: ' + prefix);
+      throwError(state, 'tag prefix is malformed: ' + prefix)
     }
 
-    state.tagMap[handle] = prefix;
+    state.tagMap[handle] = prefix
   }
-};
-
-
-function captureSegment(state, start, end, checkJson) {
-  var _position, _length, _character, _result;
+}
 
+function captureSegment (state, start, end, checkJson) {
   if (start < end) {
-    _result = state.input.slice(start, end);
+    const _result = state.input.slice(start, end)
 
     if (checkJson) {
-      for (_position = 0, _length = _result.length; _position < _length; _position += 1) {
-        _character = _result.charCodeAt(_position);
+      for (let _position = 0, _length = _result.length; _position < _length; _position += 1) {
+        const _character = _result.charCodeAt(_position)
         if (!(_character === 0x09 ||
-              (0x20 <= _character && _character <= 0x10FFFF))) {
-          throwError(state, 'expected valid JSON character');
+              (_character >= 0x20 && _character <= 0x10FFFF))) {
+          throwError(state, 'expected valid JSON character')
         }
       }
     } else if (PATTERN_NON_PRINTABLE.test(_result)) {
-      throwError(state, 'the stream contains non-printable characters');
+      throwError(state, 'the stream contains non-printable characters')
     }
 
-    state.result += _result;
+    state.result += _result
   }
 }
 
-function mergeMappings(state, destination, source, overridableKeys) {
-  var sourceKeys, key, index, quantity;
-
+function mergeMappings (state, destination, source, overridableKeys) {
   if (!common.isObject(source)) {
-    throwError(state, 'cannot merge mappings; the provided source object is unacceptable');
+    throwError(state, 'cannot merge mappings; the provided source object is unacceptable')
   }
 
-  sourceKeys = Object.keys(source);
+  const sourceKeys = Object.keys(source)
 
-  for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
-    key = sourceKeys[index];
+  for (let index = 0, quantity = sourceKeys.length; index < quantity; index += 1) {
+    const key = sourceKeys[index]
+
+    if (state.maxTotalMergeKeys !== -1 && ++state.totalMergeKeys > state.maxTotalMergeKeys) {
+      throwError(state, 'merge keys exceeded maxTotalMergeKeys (' + state.maxTotalMergeKeys + ')')
+    }
 
     if (!_hasOwnProperty.call(destination, key)) {
-      setProperty(destination, key, source[key]);
-      overridableKeys[key] = true;
+      setProperty(destination, key, source[key])
+      overridableKeys[key] = true
     }
   }
 }
 
-function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode,
+function storeMappingPair (state, _result, overridableKeys, keyTag, keyNode, valueNode,
   startLine, startLineStart, startPos) {
-
-  var index, quantity;
-
   // The output is a plain object here, so keys can only be strings.
   // We need to convert keyNode to a string, but doing so can hang the process
   // (deeply nested arrays that explode exponentially using aliases).
   if (Array.isArray(keyNode)) {
-    keyNode = Array.prototype.slice.call(keyNode);
+    keyNode = Array.prototype.slice.call(keyNode)
 
-    for (index = 0, quantity = keyNode.length; index < quantity; index += 1) {
+    for (let index = 0, quantity = keyNode.length; index < quantity; index += 1) {
       if (Array.isArray(keyNode[index])) {
-        throwError(state, 'nested arrays are not supported inside keys');
+        throwError(state, 'nested arrays are not supported inside keys')
       }
 
       if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') {
-        keyNode[index] = '[object Object]';
+        keyNode[index] = '[object Object]'
       }
     }
   }
@@ -42480,814 +42208,776 @@ function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valu
   // (still use its own toString for arrays, timestamps,
   // and whatever user schema extensions happen to have @@toStringTag)
   if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') {
-    keyNode = '[object Object]';
+    keyNode = '[object Object]'
   }
 
-
-  keyNode = String(keyNode);
+  keyNode = String(keyNode)
 
   if (_result === null) {
-    _result = {};
+    _result = {}
   }
 
   if (keyTag === 'tag:yaml.org,2002:merge') {
     if (Array.isArray(valueNode)) {
-      for (index = 0, quantity = valueNode.length; index < quantity; index += 1) {
-        mergeMappings(state, _result, valueNode[index], overridableKeys);
+      for (let index = 0, quantity = valueNode.length; index < quantity; index += 1) {
+        mergeMappings(state, _result, valueNode[index], overridableKeys)
       }
     } else {
-      mergeMappings(state, _result, valueNode, overridableKeys);
+      mergeMappings(state, _result, valueNode, overridableKeys)
     }
   } else {
     if (!state.json &&
         !_hasOwnProperty.call(overridableKeys, keyNode) &&
         _hasOwnProperty.call(_result, keyNode)) {
-      state.line = startLine || state.line;
-      state.lineStart = startLineStart || state.lineStart;
-      state.position = startPos || state.position;
-      throwError(state, 'duplicated mapping key');
+      state.line = startLine || state.line
+      state.lineStart = startLineStart || state.lineStart
+      state.position = startPos || state.position
+      throwError(state, 'duplicated mapping key')
     }
 
-    setProperty(_result, keyNode, valueNode);
-    delete overridableKeys[keyNode];
+    setProperty(_result, keyNode, valueNode)
+    delete overridableKeys[keyNode]
   }
 
-  return _result;
+  return _result
 }
 
-function readLineBreak(state) {
-  var ch;
-
-  ch = state.input.charCodeAt(state.position);
+function readLineBreak (state) {
+  const ch = state.input.charCodeAt(state.position)
 
   if (ch === 0x0A/* LF */) {
-    state.position++;
+    state.position++
   } else if (ch === 0x0D/* CR */) {
-    state.position++;
+    state.position++
     if (state.input.charCodeAt(state.position) === 0x0A/* LF */) {
-      state.position++;
+      state.position++
     }
   } else {
-    throwError(state, 'a line break is expected');
+    throwError(state, 'a line break is expected')
   }
 
-  state.line += 1;
-  state.lineStart = state.position;
-  state.firstTabInLine = -1;
+  state.line += 1
+  state.lineStart = state.position
+  state.firstTabInLine = -1
 }
 
-function skipSeparationSpace(state, allowComments, checkIndent) {
-  var lineBreaks = 0,
-      ch = state.input.charCodeAt(state.position);
+function skipSeparationSpace (state, allowComments, checkIndent) {
+  let lineBreaks = 0
+  let ch = state.input.charCodeAt(state.position)
 
   while (ch !== 0) {
-    while (is_WHITE_SPACE(ch)) {
+    while (isWhiteSpace(ch)) {
       if (ch === 0x09/* Tab */ && state.firstTabInLine === -1) {
-        state.firstTabInLine = state.position;
+        state.firstTabInLine = state.position
       }
-      ch = state.input.charCodeAt(++state.position);
+      ch = state.input.charCodeAt(++state.position)
     }
 
     if (allowComments && ch === 0x23/* # */) {
       do {
-        ch = state.input.charCodeAt(++state.position);
-      } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0);
+        ch = state.input.charCodeAt(++state.position)
+      } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0)
     }
 
-    if (is_EOL(ch)) {
-      readLineBreak(state);
+    if (isEol(ch)) {
+      readLineBreak(state)
 
-      ch = state.input.charCodeAt(state.position);
-      lineBreaks++;
-      state.lineIndent = 0;
+      ch = state.input.charCodeAt(state.position)
+      lineBreaks++
+      state.lineIndent = 0
 
       while (ch === 0x20/* Space */) {
-        state.lineIndent++;
-        ch = state.input.charCodeAt(++state.position);
+        state.lineIndent++
+        ch = state.input.charCodeAt(++state.position)
       }
     } else {
-      break;
+      break
     }
   }
 
   if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) {
-    throwWarning(state, 'deficient indentation');
+    throwWarning(state, 'deficient indentation')
   }
 
-  return lineBreaks;
+  return lineBreaks
 }
 
-function testDocumentSeparator(state) {
-  var _position = state.position,
-      ch;
-
-  ch = state.input.charCodeAt(_position);
+function testDocumentSeparator (state) {
+  let _position = state.position
+  let ch = state.input.charCodeAt(_position)
 
   // Condition state.position === state.lineStart is tested
   // in parent on each call, for efficiency. No needs to test here again.
   if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) &&
       ch === state.input.charCodeAt(_position + 1) &&
       ch === state.input.charCodeAt(_position + 2)) {
+    _position += 3
 
-    _position += 3;
-
-    ch = state.input.charCodeAt(_position);
+    ch = state.input.charCodeAt(_position)
 
-    if (ch === 0 || is_WS_OR_EOL(ch)) {
-      return true;
+    if (ch === 0 || isWsOrEol(ch)) {
+      return true
     }
   }
 
-  return false;
+  return false
 }
 
-function writeFoldedLines(state, count) {
+function writeFoldedLines (state, count) {
   if (count === 1) {
-    state.result += ' ';
+    state.result += ' '
   } else if (count > 1) {
-    state.result += common.repeat('\n', count - 1);
-  }
-}
-
-
-function readPlainScalar(state, nodeIndent, withinFlowCollection) {
-  var preceding,
-      following,
-      captureStart,
-      captureEnd,
-      hasPendingContent,
-      _line,
-      _lineStart,
-      _lineIndent,
-      _kind = state.kind,
-      _result = state.result,
-      ch;
-
-  ch = state.input.charCodeAt(state.position);
-
-  if (is_WS_OR_EOL(ch)      ||
-      is_FLOW_INDICATOR(ch) ||
-      ch === 0x23/* # */    ||
-      ch === 0x26/* & */    ||
-      ch === 0x2A/* * */    ||
-      ch === 0x21/* ! */    ||
-      ch === 0x7C/* | */    ||
-      ch === 0x3E/* > */    ||
-      ch === 0x27/* ' */    ||
-      ch === 0x22/* " */    ||
-      ch === 0x25/* % */    ||
-      ch === 0x40/* @ */    ||
+    state.result += common.repeat('\n', count - 1)
+  }
+}
+
+function readPlainScalar (state, nodeIndent, withinFlowCollection) {
+  let captureStart
+  let captureEnd
+  let hasPendingContent
+  let _line
+  let _lineStart
+  let _lineIndent
+  const _kind = state.kind
+  const _result = state.result
+
+  let ch = state.input.charCodeAt(state.position)
+
+  if (isWsOrEol(ch) ||
+      isFlowIndicator(ch) ||
+      ch === 0x23/* # */ ||
+      ch === 0x26/* & */ ||
+      ch === 0x2A/* * */ ||
+      ch === 0x21/* ! */ ||
+      ch === 0x7C/* | */ ||
+      ch === 0x3E/* > */ ||
+      ch === 0x27/* ' */ ||
+      ch === 0x22/* " */ ||
+      ch === 0x25/* % */ ||
+      ch === 0x40/* @ */ ||
       ch === 0x60/* ` */) {
-    return false;
+    return false
   }
 
   if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) {
-    following = state.input.charCodeAt(state.position + 1);
+    const following = state.input.charCodeAt(state.position + 1)
 
-    if (is_WS_OR_EOL(following) ||
-        withinFlowCollection && is_FLOW_INDICATOR(following)) {
-      return false;
+    if (isWsOrEol(following) ||
+        (withinFlowCollection && isFlowIndicator(following))) {
+      return false
     }
   }
 
-  state.kind = 'scalar';
-  state.result = '';
-  captureStart = captureEnd = state.position;
-  hasPendingContent = false;
+  state.kind = 'scalar'
+  state.result = ''
+  captureStart = captureEnd = state.position
+  hasPendingContent = false
 
   while (ch !== 0) {
     if (ch === 0x3A/* : */) {
-      following = state.input.charCodeAt(state.position + 1);
+      const following = state.input.charCodeAt(state.position + 1)
 
-      if (is_WS_OR_EOL(following) ||
-          withinFlowCollection && is_FLOW_INDICATOR(following)) {
-        break;
+      if (isWsOrEol(following) ||
+          (withinFlowCollection && isFlowIndicator(following))) {
+        break
       }
-
     } else if (ch === 0x23/* # */) {
-      preceding = state.input.charCodeAt(state.position - 1);
+      const preceding = state.input.charCodeAt(state.position - 1)
 
-      if (is_WS_OR_EOL(preceding)) {
-        break;
+      if (isWsOrEol(preceding)) {
+        break
       }
-
     } else if ((state.position === state.lineStart && testDocumentSeparator(state)) ||
-               withinFlowCollection && is_FLOW_INDICATOR(ch)) {
-      break;
-
-    } else if (is_EOL(ch)) {
-      _line = state.line;
-      _lineStart = state.lineStart;
-      _lineIndent = state.lineIndent;
-      skipSeparationSpace(state, false, -1);
+               (withinFlowCollection && isFlowIndicator(ch))) {
+      break
+    } else if (isEol(ch)) {
+      _line = state.line
+      _lineStart = state.lineStart
+      _lineIndent = state.lineIndent
+      skipSeparationSpace(state, false, -1)
 
       if (state.lineIndent >= nodeIndent) {
-        hasPendingContent = true;
-        ch = state.input.charCodeAt(state.position);
-        continue;
+        hasPendingContent = true
+        ch = state.input.charCodeAt(state.position)
+        continue
       } else {
-        state.position = captureEnd;
-        state.line = _line;
-        state.lineStart = _lineStart;
-        state.lineIndent = _lineIndent;
-        break;
+        state.position = captureEnd
+        state.line = _line
+        state.lineStart = _lineStart
+        state.lineIndent = _lineIndent
+        break
       }
     }
 
     if (hasPendingContent) {
-      captureSegment(state, captureStart, captureEnd, false);
-      writeFoldedLines(state, state.line - _line);
-      captureStart = captureEnd = state.position;
-      hasPendingContent = false;
+      captureSegment(state, captureStart, captureEnd, false)
+      writeFoldedLines(state, state.line - _line)
+      captureStart = captureEnd = state.position
+      hasPendingContent = false
     }
 
-    if (!is_WHITE_SPACE(ch)) {
-      captureEnd = state.position + 1;
+    if (!isWhiteSpace(ch)) {
+      captureEnd = state.position + 1
     }
 
-    ch = state.input.charCodeAt(++state.position);
+    ch = state.input.charCodeAt(++state.position)
   }
 
-  captureSegment(state, captureStart, captureEnd, false);
+  captureSegment(state, captureStart, captureEnd, false)
 
   if (state.result) {
-    return true;
+    return true
   }
 
-  state.kind = _kind;
-  state.result = _result;
-  return false;
+  state.kind = _kind
+  state.result = _result
+  return false
 }
 
-function readSingleQuotedScalar(state, nodeIndent) {
-  var ch,
-      captureStart, captureEnd;
+function readSingleQuotedScalar (state, nodeIndent) {
+  let captureStart
+  let captureEnd
 
-  ch = state.input.charCodeAt(state.position);
+  let ch = state.input.charCodeAt(state.position)
 
   if (ch !== 0x27/* ' */) {
-    return false;
+    return false
   }
 
-  state.kind = 'scalar';
-  state.result = '';
-  state.position++;
-  captureStart = captureEnd = state.position;
+  state.kind = 'scalar'
+  state.result = ''
+  state.position++
+  captureStart = captureEnd = state.position
 
   while ((ch = state.input.charCodeAt(state.position)) !== 0) {
     if (ch === 0x27/* ' */) {
-      captureSegment(state, captureStart, state.position, true);
-      ch = state.input.charCodeAt(++state.position);
+      captureSegment(state, captureStart, state.position, true)
+      ch = state.input.charCodeAt(++state.position)
 
       if (ch === 0x27/* ' */) {
-        captureStart = state.position;
-        state.position++;
-        captureEnd = state.position;
+        captureStart = state.position
+        state.position++
+        captureEnd = state.position
       } else {
-        return true;
+        return true
       }
-
-    } else if (is_EOL(ch)) {
-      captureSegment(state, captureStart, captureEnd, true);
-      writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
-      captureStart = captureEnd = state.position;
-
+    } else if (isEol(ch)) {
+      captureSegment(state, captureStart, captureEnd, true)
+      writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent))
+      captureStart = captureEnd = state.position
     } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
-      throwError(state, 'unexpected end of the document within a single quoted scalar');
-
+      throwError(state, 'unexpected end of the document within a single quoted scalar')
     } else {
-      state.position++;
-      captureEnd = state.position;
+      state.position++
+      if (!isWhiteSpace(ch)) {
+        captureEnd = state.position
+      }
     }
   }
 
-  throwError(state, 'unexpected end of the stream within a single quoted scalar');
+  throwError(state, 'unexpected end of the stream within a single quoted scalar')
 }
 
-function readDoubleQuotedScalar(state, nodeIndent) {
-  var captureStart,
-      captureEnd,
-      hexLength,
-      hexResult,
-      tmp,
-      ch;
+function readDoubleQuotedScalar (state, nodeIndent) {
+  let captureStart
+  let captureEnd
+  let tmp
 
-  ch = state.input.charCodeAt(state.position);
+  let ch = state.input.charCodeAt(state.position)
 
   if (ch !== 0x22/* " */) {
-    return false;
+    return false
   }
 
-  state.kind = 'scalar';
-  state.result = '';
-  state.position++;
-  captureStart = captureEnd = state.position;
+  state.kind = 'scalar'
+  state.result = ''
+  state.position++
+  captureStart = captureEnd = state.position
 
   while ((ch = state.input.charCodeAt(state.position)) !== 0) {
     if (ch === 0x22/* " */) {
-      captureSegment(state, captureStart, state.position, true);
-      state.position++;
-      return true;
-
+      captureSegment(state, captureStart, state.position, true)
+      state.position++
+      return true
     } else if (ch === 0x5C/* \ */) {
-      captureSegment(state, captureStart, state.position, true);
-      ch = state.input.charCodeAt(++state.position);
+      captureSegment(state, captureStart, state.position, true)
+      ch = state.input.charCodeAt(++state.position)
 
-      if (is_EOL(ch)) {
-        skipSeparationSpace(state, false, nodeIndent);
+      if (isEol(ch)) {
+        skipSeparationSpace(state, false, nodeIndent)
 
         // TODO: rework to inline fn with no type cast?
       } else if (ch < 256 && simpleEscapeCheck[ch]) {
-        state.result += simpleEscapeMap[ch];
-        state.position++;
-
+        state.result += simpleEscapeMap[ch]
+        state.position++
       } else if ((tmp = escapedHexLen(ch)) > 0) {
-        hexLength = tmp;
-        hexResult = 0;
+        let hexLength = tmp
+        let hexResult = 0
 
         for (; hexLength > 0; hexLength--) {
-          ch = state.input.charCodeAt(++state.position);
+          ch = state.input.charCodeAt(++state.position)
 
           if ((tmp = fromHexCode(ch)) >= 0) {
-            hexResult = (hexResult << 4) + tmp;
-
+            hexResult = (hexResult << 4) + tmp
           } else {
-            throwError(state, 'expected hexadecimal character');
+            throwError(state, 'expected hexadecimal character')
           }
         }
 
-        state.result += charFromCodepoint(hexResult);
-
-        state.position++;
+        state.result += charFromCodepoint(hexResult)
 
+        state.position++
       } else {
-        throwError(state, 'unknown escape sequence');
+        throwError(state, 'unknown escape sequence')
       }
 
-      captureStart = captureEnd = state.position;
-
-    } else if (is_EOL(ch)) {
-      captureSegment(state, captureStart, captureEnd, true);
-      writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent));
-      captureStart = captureEnd = state.position;
-
+      captureStart = captureEnd = state.position
+    } else if (isEol(ch)) {
+      captureSegment(state, captureStart, captureEnd, true)
+      writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent))
+      captureStart = captureEnd = state.position
     } else if (state.position === state.lineStart && testDocumentSeparator(state)) {
-      throwError(state, 'unexpected end of the document within a double quoted scalar');
-
+      throwError(state, 'unexpected end of the document within a double quoted scalar')
     } else {
-      state.position++;
-      captureEnd = state.position;
+      state.position++
+      if (!isWhiteSpace(ch)) {
+        captureEnd = state.position
+      }
     }
   }
 
-  throwError(state, 'unexpected end of the stream within a double quoted scalar');
+  throwError(state, 'unexpected end of the stream within a double quoted scalar')
 }
 
-function readFlowCollection(state, nodeIndent) {
-  var readNext = true,
-      _line,
-      _lineStart,
-      _pos,
-      _tag     = state.tag,
-      _result,
-      _anchor  = state.anchor,
-      following,
-      terminator,
-      isPair,
-      isExplicitPair,
-      isMapping,
-      overridableKeys = Object.create(null),
-      keyNode,
-      keyTag,
-      valueNode,
-      ch;
+function readFlowCollection (state, nodeIndent) {
+  let readNext = true
+  let _line
+  let _lineStart
+  let _pos
+  const _tag = state.tag
+  let _result
+  const _anchor = state.anchor
+  let terminator
+  let isPair
+  let isExplicitPair
+  let isMapping
+  const overridableKeys = Object.create(null)
+  let keyNode
+  let keyTag
+  let valueNode
 
-  ch = state.input.charCodeAt(state.position);
+  let ch = state.input.charCodeAt(state.position)
 
   if (ch === 0x5B/* [ */) {
-    terminator = 0x5D;/* ] */
-    isMapping = false;
-    _result = [];
+    terminator = 0x5D/* ] */
+    isMapping = false
+    _result = []
   } else if (ch === 0x7B/* { */) {
-    terminator = 0x7D;/* } */
-    isMapping = true;
-    _result = {};
+    terminator = 0x7D/* } */
+    isMapping = true
+    _result = {}
   } else {
-    return false;
+    return false
   }
 
   if (state.anchor !== null) {
-    state.anchorMap[state.anchor] = _result;
+    storeAnchor(state, state.anchor, _result)
   }
 
-  ch = state.input.charCodeAt(++state.position);
+  ch = state.input.charCodeAt(++state.position)
 
   while (ch !== 0) {
-    skipSeparationSpace(state, true, nodeIndent);
+    skipSeparationSpace(state, true, nodeIndent)
 
-    ch = state.input.charCodeAt(state.position);
+    ch = state.input.charCodeAt(state.position)
 
     if (ch === terminator) {
-      state.position++;
-      state.tag = _tag;
-      state.anchor = _anchor;
-      state.kind = isMapping ? 'mapping' : 'sequence';
-      state.result = _result;
-      return true;
+      state.position++
+      state.tag = _tag
+      state.anchor = _anchor
+      state.kind = isMapping ? 'mapping' : 'sequence'
+      state.result = _result
+      return true
     } else if (!readNext) {
-      throwError(state, 'missed comma between flow collection entries');
+      throwError(state, 'missed comma between flow collection entries')
     } else if (ch === 0x2C/* , */) {
       // "flow collection entries can never be completely empty", as per YAML 1.2, section 7.4
-      throwError(state, "expected the node content, but found ','");
+      throwError(state, "expected the node content, but found ','")
     }
 
-    keyTag = keyNode = valueNode = null;
-    isPair = isExplicitPair = false;
+    keyTag = keyNode = valueNode = null
+    isPair = isExplicitPair = false
 
     if (ch === 0x3F/* ? */) {
-      following = state.input.charCodeAt(state.position + 1);
+      const following = state.input.charCodeAt(state.position + 1)
 
-      if (is_WS_OR_EOL(following)) {
-        isPair = isExplicitPair = true;
-        state.position++;
-        skipSeparationSpace(state, true, nodeIndent);
+      if (isWsOrEol(following)) {
+        isPair = isExplicitPair = true
+        state.position++
+        skipSeparationSpace(state, true, nodeIndent)
       }
     }
 
-    _line = state.line; // Save the current line.
-    _lineStart = state.lineStart;
-    _pos = state.position;
-    composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
-    keyTag = state.tag;
-    keyNode = state.result;
-    skipSeparationSpace(state, true, nodeIndent);
+    _line = state.line // Save the current line.
+    _lineStart = state.lineStart
+    _pos = state.position
+    composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)
+    keyTag = state.tag
+    keyNode = state.result
+    skipSeparationSpace(state, true, nodeIndent)
 
-    ch = state.input.charCodeAt(state.position);
+    ch = state.input.charCodeAt(state.position)
 
     if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) {
-      isPair = true;
-      ch = state.input.charCodeAt(++state.position);
-      skipSeparationSpace(state, true, nodeIndent);
-      composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true);
-      valueNode = state.result;
+      isPair = true
+      ch = state.input.charCodeAt(++state.position)
+      skipSeparationSpace(state, true, nodeIndent)
+      composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)
+      valueNode = state.result
     }
 
     if (isMapping) {
-      storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos);
+      storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos)
     } else if (isPair) {
-      _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos));
+      _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode, _line, _lineStart, _pos))
     } else {
-      _result.push(keyNode);
+      _result.push(keyNode)
     }
 
-    skipSeparationSpace(state, true, nodeIndent);
+    skipSeparationSpace(state, true, nodeIndent)
 
-    ch = state.input.charCodeAt(state.position);
+    ch = state.input.charCodeAt(state.position)
 
     if (ch === 0x2C/* , */) {
-      readNext = true;
-      ch = state.input.charCodeAt(++state.position);
+      readNext = true
+      ch = state.input.charCodeAt(++state.position)
     } else {
-      readNext = false;
+      readNext = false
     }
   }
 
-  throwError(state, 'unexpected end of the stream within a flow collection');
+  throwError(state, 'unexpected end of the stream within a flow collection')
 }
 
-function readBlockScalar(state, nodeIndent) {
-  var captureStart,
-      folding,
-      chomping       = CHOMPING_CLIP,
-      didReadContent = false,
-      detectedIndent = false,
-      textIndent     = nodeIndent,
-      emptyLines     = 0,
-      atMoreIndented = false,
-      tmp,
-      ch;
+function readBlockScalar (state, nodeIndent) {
+  let folding
+  let chomping = CHOMPING_CLIP
+  let didReadContent = false
+  let detectedIndent = false
+  let textIndent = nodeIndent
+  let emptyLines = 0
+  let atMoreIndented = false
+  let tmp
 
-  ch = state.input.charCodeAt(state.position);
+  let ch = state.input.charCodeAt(state.position)
 
   if (ch === 0x7C/* | */) {
-    folding = false;
+    folding = false
   } else if (ch === 0x3E/* > */) {
-    folding = true;
+    folding = true
   } else {
-    return false;
+    return false
   }
 
-  state.kind = 'scalar';
-  state.result = '';
+  state.kind = 'scalar'
+  state.result = ''
 
   while (ch !== 0) {
-    ch = state.input.charCodeAt(++state.position);
+    ch = state.input.charCodeAt(++state.position)
 
     if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
       if (CHOMPING_CLIP === chomping) {
-        chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP;
+        chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP
       } else {
-        throwError(state, 'repeat of a chomping mode identifier');
+        throwError(state, 'repeat of a chomping mode identifier')
       }
-
     } else if ((tmp = fromDecimalCode(ch)) >= 0) {
       if (tmp === 0) {
-        throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one');
+        throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one')
       } else if (!detectedIndent) {
-        textIndent = nodeIndent + tmp - 1;
-        detectedIndent = true;
+        textIndent = nodeIndent + tmp - 1
+        detectedIndent = true
       } else {
-        throwError(state, 'repeat of an indentation width identifier');
+        throwError(state, 'repeat of an indentation width identifier')
       }
-
     } else {
-      break;
+      break
     }
   }
 
-  if (is_WHITE_SPACE(ch)) {
-    do { ch = state.input.charCodeAt(++state.position); }
-    while (is_WHITE_SPACE(ch));
+  if (isWhiteSpace(ch)) {
+    do { ch = state.input.charCodeAt(++state.position) }
+    while (isWhiteSpace(ch))
 
     if (ch === 0x23/* # */) {
-      do { ch = state.input.charCodeAt(++state.position); }
-      while (!is_EOL(ch) && (ch !== 0));
+      do { ch = state.input.charCodeAt(++state.position) }
+      while (!isEol(ch) && (ch !== 0))
     }
   }
 
   while (ch !== 0) {
-    readLineBreak(state);
-    state.lineIndent = 0;
+    readLineBreak(state)
+    state.lineIndent = 0
 
-    ch = state.input.charCodeAt(state.position);
+    ch = state.input.charCodeAt(state.position)
 
+    // eslint-disable-next-line no-unmodified-loop-condition
     while ((!detectedIndent || state.lineIndent < textIndent) &&
            (ch === 0x20/* Space */)) {
-      state.lineIndent++;
-      ch = state.input.charCodeAt(++state.position);
+      state.lineIndent++
+      ch = state.input.charCodeAt(++state.position)
     }
 
     if (!detectedIndent && state.lineIndent > textIndent) {
-      textIndent = state.lineIndent;
+      textIndent = state.lineIndent
+    }
+
+    if (isEol(ch)) {
+      emptyLines++
+      continue
     }
 
-    if (is_EOL(ch)) {
-      emptyLines++;
-      continue;
+    if (!detectedIndent && textIndent === 0) {
+      throwError(state, 'missing indentation for block scalar')
     }
 
     // End of the scalar.
     if (state.lineIndent < textIndent) {
-
       // Perform the chomping.
       if (chomping === CHOMPING_KEEP) {
-        state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
+        state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines)
       } else if (chomping === CHOMPING_CLIP) {
         if (didReadContent) { // i.e. only if the scalar is not empty.
-          state.result += '\n';
+          state.result += '\n'
         }
       }
 
       // Break this `while` cycle and go to the funciton's epilogue.
-      break;
+      break
     }
 
     // Folded style: use fancy rules to handle line breaks.
     if (folding) {
-
       // Lines starting with white space characters (more-indented lines) are not folded.
-      if (is_WHITE_SPACE(ch)) {
-        atMoreIndented = true;
+      if (isWhiteSpace(ch)) {
+        atMoreIndented = true
         // except for the first content line (cf. Example 8.1)
-        state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
+        state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines)
 
       // End of more-indented block.
       } else if (atMoreIndented) {
-        atMoreIndented = false;
-        state.result += common.repeat('\n', emptyLines + 1);
+        atMoreIndented = false
+        state.result += common.repeat('\n', emptyLines + 1)
 
       // Just one line break - perceive as the same line.
       } else if (emptyLines === 0) {
         if (didReadContent) { // i.e. only if we have already read some scalar content.
-          state.result += ' ';
+          state.result += ' '
         }
 
       // Several line breaks - perceive as different lines.
       } else {
-        state.result += common.repeat('\n', emptyLines);
+        state.result += common.repeat('\n', emptyLines)
       }
 
     // Literal style: just add exact number of line breaks between content lines.
     } else {
       // Keep all line breaks except the header line break.
-      state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines);
+      state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines)
     }
 
-    didReadContent = true;
-    detectedIndent = true;
-    emptyLines = 0;
-    captureStart = state.position;
+    didReadContent = true
+    detectedIndent = true
+    emptyLines = 0
+    const captureStart = state.position
 
-    while (!is_EOL(ch) && (ch !== 0)) {
-      ch = state.input.charCodeAt(++state.position);
+    while (!isEol(ch) && (ch !== 0)) {
+      ch = state.input.charCodeAt(++state.position)
     }
 
-    captureSegment(state, captureStart, state.position, false);
+    captureSegment(state, captureStart, state.position, false)
   }
 
-  return true;
+  return true
 }
 
-function readBlockSequence(state, nodeIndent) {
-  var _line,
-      _tag      = state.tag,
-      _anchor   = state.anchor,
-      _result   = [],
-      following,
-      detected  = false,
-      ch;
+function readBlockSequence (state, nodeIndent) {
+  const _tag = state.tag
+  const _anchor = state.anchor
+  const _result = []
+  let detected = false
 
   // there is a leading tab before this token, so it can't be a block sequence/mapping;
   // it can still be flow sequence/mapping or a scalar
-  if (state.firstTabInLine !== -1) return false;
+  if (state.firstTabInLine !== -1) return false
 
   if (state.anchor !== null) {
-    state.anchorMap[state.anchor] = _result;
+    storeAnchor(state, state.anchor, _result)
   }
 
-  ch = state.input.charCodeAt(state.position);
+  let ch = state.input.charCodeAt(state.position)
 
   while (ch !== 0) {
     if (state.firstTabInLine !== -1) {
-      state.position = state.firstTabInLine;
-      throwError(state, 'tab characters must not be used in indentation');
+      state.position = state.firstTabInLine
+      throwError(state, 'tab characters must not be used in indentation')
     }
 
     if (ch !== 0x2D/* - */) {
-      break;
+      break
     }
 
-    following = state.input.charCodeAt(state.position + 1);
+    const following = state.input.charCodeAt(state.position + 1)
 
-    if (!is_WS_OR_EOL(following)) {
-      break;
+    if (!isWsOrEol(following)) {
+      break
     }
 
-    detected = true;
-    state.position++;
+    detected = true
+    state.position++
 
     if (skipSeparationSpace(state, true, -1)) {
       if (state.lineIndent <= nodeIndent) {
-        _result.push(null);
-        ch = state.input.charCodeAt(state.position);
-        continue;
+        _result.push(null)
+        ch = state.input.charCodeAt(state.position)
+        continue
       }
     }
 
-    _line = state.line;
-    composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true);
-    _result.push(state.result);
-    skipSeparationSpace(state, true, -1);
+    const _line = state.line
+    composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true)
+    _result.push(state.result)
+    skipSeparationSpace(state, true, -1)
 
-    ch = state.input.charCodeAt(state.position);
+    ch = state.input.charCodeAt(state.position)
 
     if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {
-      throwError(state, 'bad indentation of a sequence entry');
+      throwError(state, 'bad indentation of a sequence entry')
     } else if (state.lineIndent < nodeIndent) {
-      break;
+      break
     }
   }
 
   if (detected) {
-    state.tag = _tag;
-    state.anchor = _anchor;
-    state.kind = 'sequence';
-    state.result = _result;
-    return true;
+    state.tag = _tag
+    state.anchor = _anchor
+    state.kind = 'sequence'
+    state.result = _result
+    return true
   }
-  return false;
-}
-
-function readBlockMapping(state, nodeIndent, flowIndent) {
-  var following,
-      allowCompact,
-      _line,
-      _keyLine,
-      _keyLineStart,
-      _keyPos,
-      _tag          = state.tag,
-      _anchor       = state.anchor,
-      _result       = {},
-      overridableKeys = Object.create(null),
-      keyTag        = null,
-      keyNode       = null,
-      valueNode     = null,
-      atExplicitKey = false,
-      detected      = false,
-      ch;
+  return false
+}
+
+function readBlockMapping (state, nodeIndent, flowIndent) {
+  let allowCompact
+  let _keyLine
+  let _keyLineStart
+  let _keyPos
+  const _tag = state.tag
+  const _anchor = state.anchor
+  const _result = {}
+  const overridableKeys = Object.create(null)
+  let keyTag = null
+  let keyNode = null
+  let valueNode = null
+  let atExplicitKey = false
+  let detected = false
 
   // there is a leading tab before this token, so it can't be a block sequence/mapping;
   // it can still be flow sequence/mapping or a scalar
-  if (state.firstTabInLine !== -1) return false;
+  if (state.firstTabInLine !== -1) return false
 
   if (state.anchor !== null) {
-    state.anchorMap[state.anchor] = _result;
+    storeAnchor(state, state.anchor, _result)
   }
 
-  ch = state.input.charCodeAt(state.position);
+  let ch = state.input.charCodeAt(state.position)
 
   while (ch !== 0) {
     if (!atExplicitKey && state.firstTabInLine !== -1) {
-      state.position = state.firstTabInLine;
-      throwError(state, 'tab characters must not be used in indentation');
+      state.position = state.firstTabInLine
+      throwError(state, 'tab characters must not be used in indentation')
     }
 
-    following = state.input.charCodeAt(state.position + 1);
-    _line = state.line; // Save the current line.
+    const following = state.input.charCodeAt(state.position + 1)
+    const _line = state.line // Save the current line.
 
     //
     // Explicit notation case. There are two separate blocks:
     // first for the key (denoted by "?") and second for the value (denoted by ":")
     //
-    if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) {
-
+    if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && isWsOrEol(following)) {
       if (ch === 0x3F/* ? */) {
         if (atExplicitKey) {
-          storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);
-          keyTag = keyNode = valueNode = null;
+          storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos)
+          keyTag = keyNode = valueNode = null
         }
 
-        detected = true;
-        atExplicitKey = true;
-        allowCompact = true;
-
+        detected = true
+        atExplicitKey = true
+        allowCompact = true
       } else if (atExplicitKey) {
         // i.e. 0x3A/* : */ === character after the explicit key.
-        atExplicitKey = false;
-        allowCompact = true;
-
+        atExplicitKey = false
+        allowCompact = true
       } else {
-        throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line');
+        throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line')
       }
 
-      state.position += 1;
-      ch = following;
+      state.position += 1
+      ch = following
 
     //
     // Implicit notation case. Flow-style node as the key first, then ":", and the value.
     //
     } else {
-      _keyLine = state.line;
-      _keyLineStart = state.lineStart;
-      _keyPos = state.position;
+      _keyLine = state.line
+      _keyLineStart = state.lineStart
+      _keyPos = state.position
 
       if (!composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {
         // Neither implicit nor explicit notation.
         // Reading is done. Go to the epilogue.
-        break;
+        break
       }
 
       if (state.line === _line) {
-        ch = state.input.charCodeAt(state.position);
+        ch = state.input.charCodeAt(state.position)
 
-        while (is_WHITE_SPACE(ch)) {
-          ch = state.input.charCodeAt(++state.position);
+        while (isWhiteSpace(ch)) {
+          ch = state.input.charCodeAt(++state.position)
         }
 
         if (ch === 0x3A/* : */) {
-          ch = state.input.charCodeAt(++state.position);
+          ch = state.input.charCodeAt(++state.position)
 
-          if (!is_WS_OR_EOL(ch)) {
-            throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping');
+          if (!isWsOrEol(ch)) {
+            throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping')
           }
 
           if (atExplicitKey) {
-            storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);
-            keyTag = keyNode = valueNode = null;
+            storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos)
+            keyTag = keyNode = valueNode = null
           }
 
-          detected = true;
-          atExplicitKey = false;
-          allowCompact = false;
-          keyTag = state.tag;
-          keyNode = state.result;
-
+          detected = true
+          atExplicitKey = false
+          allowCompact = false
+          keyTag = state.tag
+          keyNode = state.result
         } else if (detected) {
-          throwError(state, 'can not read an implicit mapping pair; a colon is missed');
-
+          throwError(state, 'can not read an implicit mapping pair; a colon is missed')
         } else {
-          state.tag = _tag;
-          state.anchor = _anchor;
-          return true; // Keep the result of `composeNode`.
+          state.tag = _tag
+          state.anchor = _anchor
+          return true // Keep the result of `composeNode`.
         }
-
       } else if (detected) {
-        throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key');
-
+        throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key')
       } else {
-        state.tag = _tag;
-        state.anchor = _anchor;
-        return true; // Keep the result of `composeNode`.
+        state.tag = _tag
+        state.anchor = _anchor
+        return true // Keep the result of `composeNode`.
       }
     }
 
@@ -43296,32 +42986,32 @@ function readBlockMapping(state, nodeIndent, flowIndent) {
     //
     if (state.line === _line || state.lineIndent > nodeIndent) {
       if (atExplicitKey) {
-        _keyLine = state.line;
-        _keyLineStart = state.lineStart;
-        _keyPos = state.position;
+        _keyLine = state.line
+        _keyLineStart = state.lineStart
+        _keyPos = state.position
       }
 
       if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {
         if (atExplicitKey) {
-          keyNode = state.result;
+          keyNode = state.result
         } else {
-          valueNode = state.result;
+          valueNode = state.result
         }
       }
 
       if (!atExplicitKey) {
-        storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _keyLine, _keyLineStart, _keyPos);
-        keyTag = keyNode = valueNode = null;
+        storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _keyLine, _keyLineStart, _keyPos)
+        keyTag = keyNode = valueNode = null
       }
 
-      skipSeparationSpace(state, true, -1);
-      ch = state.input.charCodeAt(state.position);
+      skipSeparationSpace(state, true, -1)
+      ch = state.input.charCodeAt(state.position)
     }
 
     if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) {
-      throwError(state, 'bad indentation of a mapping entry');
+      throwError(state, 'bad indentation of a mapping entry')
     } else if (state.lineIndent < nodeIndent) {
-      break;
+      break
     }
   }
 
@@ -43331,293 +43021,330 @@ function readBlockMapping(state, nodeIndent, flowIndent) {
 
   // Special case: last mapping's node contains only the key in explicit notation.
   if (atExplicitKey) {
-    storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos);
+    storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null, _keyLine, _keyLineStart, _keyPos)
   }
 
   // Expose the resulting mapping.
   if (detected) {
-    state.tag = _tag;
-    state.anchor = _anchor;
-    state.kind = 'mapping';
-    state.result = _result;
+    state.tag = _tag
+    state.anchor = _anchor
+    state.kind = 'mapping'
+    state.result = _result
   }
 
-  return detected;
+  return detected
 }
 
-function readTagProperty(state) {
-  var _position,
-      isVerbatim = false,
-      isNamed    = false,
-      tagHandle,
-      tagName,
-      ch;
+function readTagProperty (state) {
+  let isVerbatim = false
+  let isNamed = false
+  let tagHandle
+  let tagName
 
-  ch = state.input.charCodeAt(state.position);
+  let ch = state.input.charCodeAt(state.position)
 
-  if (ch !== 0x21/* ! */) return false;
+  if (ch !== 0x21/* ! */) return false
 
   if (state.tag !== null) {
-    throwError(state, 'duplication of a tag property');
+    throwError(state, 'duplication of a tag property')
   }
 
-  ch = state.input.charCodeAt(++state.position);
+  ch = state.input.charCodeAt(++state.position)
 
   if (ch === 0x3C/* < */) {
-    isVerbatim = true;
-    ch = state.input.charCodeAt(++state.position);
-
+    isVerbatim = true
+    ch = state.input.charCodeAt(++state.position)
   } else if (ch === 0x21/* ! */) {
-    isNamed = true;
-    tagHandle = '!!';
-    ch = state.input.charCodeAt(++state.position);
-
+    isNamed = true
+    tagHandle = '!!'
+    ch = state.input.charCodeAt(++state.position)
   } else {
-    tagHandle = '!';
+    tagHandle = '!'
   }
 
-  _position = state.position;
+  let _position = state.position
 
   if (isVerbatim) {
-    do { ch = state.input.charCodeAt(++state.position); }
-    while (ch !== 0 && ch !== 0x3E/* > */);
+    do { ch = state.input.charCodeAt(++state.position) }
+    while (ch !== 0 && ch !== 0x3E/* > */)
 
     if (state.position < state.length) {
-      tagName = state.input.slice(_position, state.position);
-      ch = state.input.charCodeAt(++state.position);
+      tagName = state.input.slice(_position, state.position)
+      ch = state.input.charCodeAt(++state.position)
     } else {
-      throwError(state, 'unexpected end of the stream within a verbatim tag');
+      throwError(state, 'unexpected end of the stream within a verbatim tag')
     }
   } else {
-    while (ch !== 0 && !is_WS_OR_EOL(ch)) {
-
+    while (ch !== 0 && !isWsOrEol(ch)) {
       if (ch === 0x21/* ! */) {
         if (!isNamed) {
-          tagHandle = state.input.slice(_position - 1, state.position + 1);
+          tagHandle = state.input.slice(_position - 1, state.position + 1)
 
           if (!PATTERN_TAG_HANDLE.test(tagHandle)) {
-            throwError(state, 'named tag handle cannot contain such characters');
+            throwError(state, 'named tag handle cannot contain such characters')
           }
 
-          isNamed = true;
-          _position = state.position + 1;
+          isNamed = true
+          _position = state.position + 1
         } else {
-          throwError(state, 'tag suffix cannot contain exclamation marks');
+          throwError(state, 'tag suffix cannot contain exclamation marks')
         }
       }
 
-      ch = state.input.charCodeAt(++state.position);
+      ch = state.input.charCodeAt(++state.position)
     }
 
-    tagName = state.input.slice(_position, state.position);
+    tagName = state.input.slice(_position, state.position)
 
     if (PATTERN_FLOW_INDICATORS.test(tagName)) {
-      throwError(state, 'tag suffix cannot contain flow indicator characters');
+      throwError(state, 'tag suffix cannot contain flow indicator characters')
     }
   }
 
   if (tagName && !PATTERN_TAG_URI.test(tagName)) {
-    throwError(state, 'tag name cannot contain such characters: ' + tagName);
+    throwError(state, 'tag name cannot contain such characters: ' + tagName)
   }
 
   try {
-    tagName = decodeURIComponent(tagName);
+    tagName = decodeURIComponent(tagName)
   } catch (err) {
-    throwError(state, 'tag name is malformed: ' + tagName);
+    throwError(state, 'tag name is malformed: ' + tagName)
   }
 
   if (isVerbatim) {
-    state.tag = tagName;
-
+    state.tag = tagName
   } else if (_hasOwnProperty.call(state.tagMap, tagHandle)) {
-    state.tag = state.tagMap[tagHandle] + tagName;
-
+    state.tag = state.tagMap[tagHandle] + tagName
   } else if (tagHandle === '!') {
-    state.tag = '!' + tagName;
-
+    state.tag = '!' + tagName
   } else if (tagHandle === '!!') {
-    state.tag = 'tag:yaml.org,2002:' + tagName;
-
+    state.tag = 'tag:yaml.org,2002:' + tagName
   } else {
-    throwError(state, 'undeclared tag handle "' + tagHandle + '"');
+    throwError(state, 'undeclared tag handle "' + tagHandle + '"')
   }
 
-  return true;
+  return true
 }
 
-function readAnchorProperty(state) {
-  var _position,
-      ch;
+function readAnchorProperty (state) {
+  let ch = state.input.charCodeAt(state.position)
 
-  ch = state.input.charCodeAt(state.position);
-
-  if (ch !== 0x26/* & */) return false;
+  if (ch !== 0x26/* & */) return false
 
   if (state.anchor !== null) {
-    throwError(state, 'duplication of an anchor property');
+    throwError(state, 'duplication of an anchor property')
   }
 
-  ch = state.input.charCodeAt(++state.position);
-  _position = state.position;
+  ch = state.input.charCodeAt(++state.position)
+  const _position = state.position
 
-  while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
-    ch = state.input.charCodeAt(++state.position);
+  while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) {
+    ch = state.input.charCodeAt(++state.position)
   }
 
   if (state.position === _position) {
-    throwError(state, 'name of an anchor node must contain at least one character');
+    throwError(state, 'name of an anchor node must contain at least one character')
   }
 
-  state.anchor = state.input.slice(_position, state.position);
-  return true;
+  state.anchor = state.input.slice(_position, state.position)
+  return true
 }
 
-function readAlias(state) {
-  var _position, alias,
-      ch;
+function readAlias (state) {
+  let ch = state.input.charCodeAt(state.position)
 
-  ch = state.input.charCodeAt(state.position);
+  if (ch !== 0x2A/* * */) return false
 
-  if (ch !== 0x2A/* * */) return false;
+  ch = state.input.charCodeAt(++state.position)
+  const _position = state.position
 
-  ch = state.input.charCodeAt(++state.position);
-  _position = state.position;
-
-  while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) {
-    ch = state.input.charCodeAt(++state.position);
+  while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) {
+    ch = state.input.charCodeAt(++state.position)
   }
 
   if (state.position === _position) {
-    throwError(state, 'name of an alias node must contain at least one character');
+    throwError(state, 'name of an alias node must contain at least one character')
   }
 
-  alias = state.input.slice(_position, state.position);
+  const alias = state.input.slice(_position, state.position)
 
   if (!_hasOwnProperty.call(state.anchorMap, alias)) {
-    throwError(state, 'unidentified alias "' + alias + '"');
+    throwError(state, 'unidentified alias "' + alias + '"')
+  }
+
+  state.result = state.anchorMap[alias]
+  skipSeparationSpace(state, true, -1)
+  return true
+}
+
+function tryReadBlockMappingFromProperty (state, propertyStart, nodeIndent, flowIndent) {
+  const fallbackState = snapshotState(state)
+
+  beginAnchorTransaction(state)
+  restoreState(state, propertyStart)
+
+  // Re-read the leading properties as part of the first implicit key, not as
+  // properties of the current node.
+  state.tag = null
+  state.anchor = null
+  state.kind = null
+  state.result = null
+
+  if (readBlockMapping(state, nodeIndent, flowIndent) && state.kind === 'mapping') {
+    commitAnchorTransaction(state)
+    return true
   }
 
-  state.result = state.anchorMap[alias];
-  skipSeparationSpace(state, true, -1);
-  return true;
+  rollbackAnchorTransaction(state)
+  restoreState(state, fallbackState)
+  return false
 }
 
-function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) {
-  var allowBlockStyles,
-      allowBlockScalars,
-      allowBlockCollections,
-      indentStatus = 1, // 1: this>parent, 0: this=parent, -1: thisparent, 0: this=parent, -1: this= state.maxDepth) {
+    throwError(state, 'nesting exceeded maxDepth (' + state.maxDepth + ')')
+  }
+
+  state.depth += 1
 
   if (state.listener !== null) {
-    state.listener('open', state);
+    state.listener('open', state)
   }
 
-  state.tag    = null;
-  state.anchor = null;
-  state.kind   = null;
-  state.result = null;
+  state.tag = null
+  state.anchor = null
+  state.kind = null
+  state.result = null
 
-  allowBlockStyles = allowBlockScalars = allowBlockCollections =
+  const allowBlockStyles = allowBlockScalars = allowBlockCollections =
     CONTEXT_BLOCK_OUT === nodeContext ||
-    CONTEXT_BLOCK_IN  === nodeContext;
+    CONTEXT_BLOCK_IN === nodeContext
 
   if (allowToSeek) {
     if (skipSeparationSpace(state, true, -1)) {
-      atNewLine = true;
+      atNewLine = true
 
       if (state.lineIndent > parentIndent) {
-        indentStatus = 1;
+        indentStatus = 1
       } else if (state.lineIndent === parentIndent) {
-        indentStatus = 0;
+        indentStatus = 0
       } else if (state.lineIndent < parentIndent) {
-        indentStatus = -1;
+        indentStatus = -1
       }
     }
   }
 
   if (indentStatus === 1) {
-    while (readTagProperty(state) || readAnchorProperty(state)) {
+    while (true) {
+      const ch = state.input.charCodeAt(state.position)
+      const propertyState = snapshotState(state)
+
+      // A duplicate property token after a line break can be the first key of
+      // a nested block mapping, e.g. `!!map\n  !!str key: value`.
+      if (atNewLine &&
+          ((ch === 0x21/* ! */ && state.tag !== null) ||
+           (ch === 0x26/* & */ && state.anchor !== null))) {
+        break
+      }
+
+      if (!readTagProperty(state) && !readAnchorProperty(state)) {
+        break
+      }
+
+      if (propertyStart === null) {
+        propertyStart = propertyState
+      }
+
       if (skipSeparationSpace(state, true, -1)) {
-        atNewLine = true;
-        allowBlockCollections = allowBlockStyles;
+        atNewLine = true
+        allowBlockCollections = allowBlockStyles
 
         if (state.lineIndent > parentIndent) {
-          indentStatus = 1;
+          indentStatus = 1
         } else if (state.lineIndent === parentIndent) {
-          indentStatus = 0;
+          indentStatus = 0
         } else if (state.lineIndent < parentIndent) {
-          indentStatus = -1;
+          indentStatus = -1
         }
       } else {
-        allowBlockCollections = false;
+        allowBlockCollections = false
       }
     }
   }
 
   if (allowBlockCollections) {
-    allowBlockCollections = atNewLine || allowCompact;
+    allowBlockCollections = atNewLine || allowCompact
   }
 
   if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) {
     if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) {
-      flowIndent = parentIndent;
+      flowIndent = parentIndent
     } else {
-      flowIndent = parentIndent + 1;
+      flowIndent = parentIndent + 1
     }
 
-    blockIndent = state.position - state.lineStart;
+    blockIndent = state.position - state.lineStart
 
     if (indentStatus === 1) {
-      if (allowBlockCollections &&
-          (readBlockSequence(state, blockIndent) ||
-           readBlockMapping(state, blockIndent, flowIndent)) ||
+      if ((allowBlockCollections &&
+          (readBlockSequence(state, blockIndent) || readBlockMapping(state, blockIndent, flowIndent))) ||
           readFlowCollection(state, flowIndent)) {
-        hasContent = true;
+        hasContent = true
       } else {
-        if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||
+        const ch = state.input.charCodeAt(state.position)
+
+        if (propertyStart !== null && allowBlockStyles && !allowBlockCollections &&
+            ch !== 0x7C/* | */ && ch !== 0x3E/* > */ &&
+            tryReadBlockMappingFromProperty(
+              state,
+              propertyStart,
+              propertyStart.position - propertyStart.lineStart,
+              flowIndent
+            )) {
+          hasContent = true
+        } else if ((allowBlockScalars && readBlockScalar(state, flowIndent)) ||
             readSingleQuotedScalar(state, flowIndent) ||
             readDoubleQuotedScalar(state, flowIndent)) {
-          hasContent = true;
-
+          hasContent = true
         } else if (readAlias(state)) {
-          hasContent = true;
+          hasContent = true
 
           if (state.tag !== null || state.anchor !== null) {
-            throwError(state, 'alias node should not have any properties');
+            throwError(state, 'alias node should not have any properties')
           }
-
         } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) {
-          hasContent = true;
+          hasContent = true
 
           if (state.tag === null) {
-            state.tag = '?';
+            state.tag = '?'
           }
         }
 
         if (state.anchor !== null) {
-          state.anchorMap[state.anchor] = state.result;
+          storeAnchor(state, state.anchor, state.result)
         }
       }
     } else if (indentStatus === 0) {
       // Special case: block sequences are allowed to have same indentation level as the parent.
       // http://www.yaml.org/spec/1.2/spec.html#id2799784
-      hasContent = allowBlockCollections && readBlockSequence(state, blockIndent);
+      hasContent = allowBlockCollections && readBlockSequence(state, blockIndent)
     }
   }
 
   if (state.tag === null) {
     if (state.anchor !== null) {
-      state.anchorMap[state.anchor] = state.result;
+      storeAnchor(state, state.anchor, state.result)
     }
-
   } else if (state.tag === '?') {
     // Implicit resolving is not allowed for non-scalar types, and '?'
     // non-specific tag is only automatically assigned to plain scalars.
@@ -43626,245 +43353,234 @@ function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact
     // tag, for example like this: "! [0]"
     //
     if (state.result !== null && state.kind !== 'scalar') {
-      throwError(state, 'unacceptable node kind for ! tag; it should be "scalar", not "' + state.kind + '"');
+      throwError(state, 'unacceptable node kind for ! tag; it should be "scalar", not "' + state.kind + '"')
     }
 
-    for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {
-      type = state.implicitTypes[typeIndex];
+    for (let typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {
+      type = state.implicitTypes[typeIndex]
 
       if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
-        state.result = type.construct(state.result);
-        state.tag = type.tag;
+        state.result = type.construct(state.result)
+        state.tag = type.tag
         if (state.anchor !== null) {
-          state.anchorMap[state.anchor] = state.result;
+          storeAnchor(state, state.anchor, state.result)
         }
-        break;
+        break
       }
     }
   } else if (state.tag !== '!') {
     if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
-      type = state.typeMap[state.kind || 'fallback'][state.tag];
+      type = state.typeMap[state.kind || 'fallback'][state.tag]
     } else {
       // looking for multi type
-      type = null;
-      typeList = state.typeMap.multi[state.kind || 'fallback'];
+      type = null
+      const typeList = state.typeMap.multi[state.kind || 'fallback']
 
-      for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {
+      for (let typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {
         if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {
-          type = typeList[typeIndex];
-          break;
+          type = typeList[typeIndex]
+          break
         }
       }
     }
 
     if (!type) {
-      throwError(state, 'unknown tag !<' + state.tag + '>');
+      throwError(state, 'unknown tag !<' + state.tag + '>')
     }
 
     if (state.result !== null && type.kind !== state.kind) {
-      throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
+      throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"')
     }
 
     if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched
-      throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
+      throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag')
     } else {
-      state.result = type.construct(state.result, state.tag);
+      state.result = type.construct(state.result, state.tag)
       if (state.anchor !== null) {
-        state.anchorMap[state.anchor] = state.result;
+        storeAnchor(state, state.anchor, state.result)
       }
     }
   }
 
   if (state.listener !== null) {
-    state.listener('close', state);
+    state.listener('close', state)
   }
-  return state.tag !== null ||  state.anchor !== null || hasContent;
+
+  state.depth -= 1
+  return state.tag !== null || state.anchor !== null || hasContent
 }
 
-function readDocument(state) {
-  var documentStart = state.position,
-      _position,
-      directiveName,
-      directiveArgs,
-      hasDirectives = false,
-      ch;
+function readDocument (state) {
+  const documentStart = state.position
+  let hasDirectives = false
+  let ch
 
-  state.version = null;
-  state.checkLineBreaks = state.legacy;
-  state.tagMap = Object.create(null);
-  state.anchorMap = Object.create(null);
+  state.version = null
+  state.checkLineBreaks = state.legacy
+  state.tagMap = Object.create(null)
+  state.anchorMap = Object.create(null)
 
   while ((ch = state.input.charCodeAt(state.position)) !== 0) {
-    skipSeparationSpace(state, true, -1);
+    skipSeparationSpace(state, true, -1)
 
-    ch = state.input.charCodeAt(state.position);
+    ch = state.input.charCodeAt(state.position)
 
     if (state.lineIndent > 0 || ch !== 0x25/* % */) {
-      break;
+      break
     }
 
-    hasDirectives = true;
-    ch = state.input.charCodeAt(++state.position);
-    _position = state.position;
+    hasDirectives = true
+    ch = state.input.charCodeAt(++state.position)
+    let _position = state.position
 
-    while (ch !== 0 && !is_WS_OR_EOL(ch)) {
-      ch = state.input.charCodeAt(++state.position);
+    while (ch !== 0 && !isWsOrEol(ch)) {
+      ch = state.input.charCodeAt(++state.position)
     }
 
-    directiveName = state.input.slice(_position, state.position);
-    directiveArgs = [];
+    const directiveName = state.input.slice(_position, state.position)
+    const directiveArgs = []
 
     if (directiveName.length < 1) {
-      throwError(state, 'directive name must not be less than one character in length');
+      throwError(state, 'directive name must not be less than one character in length')
     }
 
     while (ch !== 0) {
-      while (is_WHITE_SPACE(ch)) {
-        ch = state.input.charCodeAt(++state.position);
+      while (isWhiteSpace(ch)) {
+        ch = state.input.charCodeAt(++state.position)
       }
 
       if (ch === 0x23/* # */) {
-        do { ch = state.input.charCodeAt(++state.position); }
-        while (ch !== 0 && !is_EOL(ch));
-        break;
+        do { ch = state.input.charCodeAt(++state.position) }
+        while (ch !== 0 && !isEol(ch))
+        break
       }
 
-      if (is_EOL(ch)) break;
+      if (isEol(ch)) break
 
-      _position = state.position;
+      _position = state.position
 
-      while (ch !== 0 && !is_WS_OR_EOL(ch)) {
-        ch = state.input.charCodeAt(++state.position);
+      while (ch !== 0 && !isWsOrEol(ch)) {
+        ch = state.input.charCodeAt(++state.position)
       }
 
-      directiveArgs.push(state.input.slice(_position, state.position));
+      directiveArgs.push(state.input.slice(_position, state.position))
     }
 
-    if (ch !== 0) readLineBreak(state);
+    if (ch !== 0) readLineBreak(state)
 
     if (_hasOwnProperty.call(directiveHandlers, directiveName)) {
-      directiveHandlers[directiveName](state, directiveName, directiveArgs);
+      directiveHandlers[directiveName](state, directiveName, directiveArgs)
     } else {
-      throwWarning(state, 'unknown document directive "' + directiveName + '"');
+      throwWarning(state, 'unknown document directive "' + directiveName + '"')
     }
   }
 
-  skipSeparationSpace(state, true, -1);
+  skipSeparationSpace(state, true, -1)
 
   if (state.lineIndent === 0 &&
-      state.input.charCodeAt(state.position)     === 0x2D/* - */ &&
+      state.input.charCodeAt(state.position) === 0x2D/* - */ &&
       state.input.charCodeAt(state.position + 1) === 0x2D/* - */ &&
       state.input.charCodeAt(state.position + 2) === 0x2D/* - */) {
-    state.position += 3;
-    skipSeparationSpace(state, true, -1);
-
+    state.position += 3
+    skipSeparationSpace(state, true, -1)
   } else if (hasDirectives) {
-    throwError(state, 'directives end mark is expected');
+    throwError(state, 'directives end mark is expected')
   }
 
-  composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true);
-  skipSeparationSpace(state, true, -1);
+  composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true)
+  skipSeparationSpace(state, true, -1)
 
   if (state.checkLineBreaks &&
       PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {
-    throwWarning(state, 'non-ASCII line breaks are interpreted as content');
+    throwWarning(state, 'non-ASCII line breaks are interpreted as content')
   }
 
-  state.documents.push(state.result);
+  state.documents.push(state.result)
 
   if (state.position === state.lineStart && testDocumentSeparator(state)) {
-
     if (state.input.charCodeAt(state.position) === 0x2E/* . */) {
-      state.position += 3;
-      skipSeparationSpace(state, true, -1);
+      state.position += 3
+      skipSeparationSpace(state, true, -1)
     }
-    return;
+    return
   }
 
   if (state.position < (state.length - 1)) {
-    throwError(state, 'end of the stream or a document separator is expected');
-  } else {
-    return;
+    throwError(state, 'end of the stream or a document separator is expected')
   }
 }
 
-
-function loadDocuments(input, options) {
-  input = String(input);
-  options = options || {};
+function loadDocuments (input, options) {
+  input = String(input)
+  options = options || {}
 
   if (input.length !== 0) {
-
     // Add tailing `\n` if not exists
     if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ &&
         input.charCodeAt(input.length - 1) !== 0x0D/* CR */) {
-      input += '\n';
+      input += '\n'
     }
 
     // Strip BOM
     if (input.charCodeAt(0) === 0xFEFF) {
-      input = input.slice(1);
+      input = input.slice(1)
     }
   }
 
-  var state = new State(input, options);
+  const state = new State(input, options)
 
-  var nullpos = input.indexOf('\0');
+  const nullpos = input.indexOf('\0')
 
   if (nullpos !== -1) {
-    state.position = nullpos;
-    throwError(state, 'null byte is not allowed in input');
+    state.position = nullpos
+    throwError(state, 'null byte is not allowed in input')
   }
 
   // Use 0 as string terminator. That significantly simplifies bounds check.
-  state.input += '\0';
+  state.input += '\0'
 
   while (state.input.charCodeAt(state.position) === 0x20/* Space */) {
-    state.lineIndent += 1;
-    state.position += 1;
+    state.lineIndent += 1
+    state.position += 1
   }
 
   while (state.position < (state.length - 1)) {
-    readDocument(state);
+    readDocument(state)
   }
 
-  return state.documents;
+  return state.documents
 }
 
-
-function loadAll(input, iterator, options) {
+function loadAll (input, iterator, options) {
   if (iterator !== null && typeof iterator === 'object' && typeof options === 'undefined') {
-    options = iterator;
-    iterator = null;
+    options = iterator
+    iterator = null
   }
 
-  var documents = loadDocuments(input, options);
+  const documents = loadDocuments(input, options)
 
   if (typeof iterator !== 'function') {
-    return documents;
+    return documents
   }
 
-  for (var index = 0, length = documents.length; index < length; index += 1) {
-    iterator(documents[index]);
+  for (let index = 0, length = documents.length; index < length; index += 1) {
+    iterator(documents[index])
   }
 }
 
-
-function load(input, options) {
-  var documents = loadDocuments(input, options);
+function load (input, options) {
+  const documents = loadDocuments(input, options)
 
   if (documents.length === 0) {
-    /*eslint-disable no-undefined*/
-    return undefined;
+    return undefined
   } else if (documents.length === 1) {
-    return documents[0];
+    return documents[0]
   }
-  throw new YAMLException('expected a single document in the stream, but found more');
+  throw new YAMLException('expected a single document in the stream, but found more')
 }
 
-
-module.exports.loadAll = loadAll;
-module.exports.load    = load;
+module.exports.loadAll = loadAll
+module.exports.load = load
 
 
 /***/ }),
@@ -43875,125 +43591,113 @@ module.exports.load    = load;
 "use strict";
 
 
-/*eslint-disable max-len*/
-
-var YAMLException = __nccwpck_require__(41248);
-var Type          = __nccwpck_require__(9557);
-
+const YAMLException = __nccwpck_require__(41248)
+const Type = __nccwpck_require__(9557)
 
-function compileList(schema, name) {
-  var result = [];
+function compileList (schema, name) {
+  const result = []
 
   schema[name].forEach(function (currentType) {
-    var newIndex = result.length;
+    let newIndex = result.length
 
     result.forEach(function (previousType, previousIndex) {
       if (previousType.tag === currentType.tag &&
           previousType.kind === currentType.kind &&
           previousType.multi === currentType.multi) {
-
-        newIndex = previousIndex;
+        newIndex = previousIndex
       }
-    });
+    })
 
-    result[newIndex] = currentType;
-  });
+    result[newIndex] = currentType
+  })
 
-  return result;
+  return result
 }
 
-
-function compileMap(/* lists... */) {
-  var result = {
-        scalar: {},
-        sequence: {},
-        mapping: {},
-        fallback: {},
-        multi: {
-          scalar: [],
-          sequence: [],
-          mapping: [],
-          fallback: []
-        }
-      }, index, length;
-
-  function collectType(type) {
+function compileMap (/* lists... */) {
+  const result = {
+    scalar: {},
+    sequence: {},
+    mapping: {},
+    fallback: {},
+    multi: {
+      scalar: [],
+      sequence: [],
+      mapping: [],
+      fallback: []
+    }
+  }
+  function collectType (type) {
     if (type.multi) {
-      result.multi[type.kind].push(type);
-      result.multi['fallback'].push(type);
+      result.multi[type.kind].push(type)
+      result.multi['fallback'].push(type)
     } else {
-      result[type.kind][type.tag] = result['fallback'][type.tag] = type;
+      result[type.kind][type.tag] = result['fallback'][type.tag] = type
     }
   }
 
-  for (index = 0, length = arguments.length; index < length; index += 1) {
-    arguments[index].forEach(collectType);
+  for (let index = 0, length = arguments.length; index < length; index += 1) {
+    arguments[index].forEach(collectType)
   }
-  return result;
+  return result
 }
 
-
-function Schema(definition) {
-  return this.extend(definition);
+function Schema (definition) {
+  return this.extend(definition)
 }
 
-
-Schema.prototype.extend = function extend(definition) {
-  var implicit = [];
-  var explicit = [];
+Schema.prototype.extend = function extend (definition) {
+  let implicit = []
+  let explicit = []
 
   if (definition instanceof Type) {
     // Schema.extend(type)
-    explicit.push(definition);
-
+    explicit.push(definition)
   } else if (Array.isArray(definition)) {
     // Schema.extend([ type1, type2, ... ])
-    explicit = explicit.concat(definition);
-
+    explicit = explicit.concat(definition)
   } else if (definition && (Array.isArray(definition.implicit) || Array.isArray(definition.explicit))) {
     // Schema.extend({ explicit: [ type1, type2, ... ], implicit: [ type1, type2, ... ] })
-    if (definition.implicit) implicit = implicit.concat(definition.implicit);
-    if (definition.explicit) explicit = explicit.concat(definition.explicit);
-
+    if (definition.implicit) implicit = implicit.concat(definition.implicit)
+    if (definition.explicit) explicit = explicit.concat(definition.explicit)
   } else {
     throw new YAMLException('Schema.extend argument should be a Type, [ Type ], ' +
-      'or a schema definition ({ implicit: [...], explicit: [...] })');
+      'or a schema definition ({ implicit: [...], explicit: [...] })')
   }
 
   implicit.forEach(function (type) {
     if (!(type instanceof Type)) {
-      throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.');
+      throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.')
     }
 
     if (type.loadKind && type.loadKind !== 'scalar') {
-      throw new YAMLException('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.');
+      throw new YAMLException('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.')
     }
 
     if (type.multi) {
-      throw new YAMLException('There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.');
+      throw new YAMLException('There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.')
     }
-  });
+  })
 
   explicit.forEach(function (type) {
     if (!(type instanceof Type)) {
-      throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.');
+      throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.')
     }
-  });
-
-  var result = Object.create(Schema.prototype);
+  })
 
-  result.implicit = (this.implicit || []).concat(implicit);
-  result.explicit = (this.explicit || []).concat(explicit);
+  const result = Object.create(Schema.prototype)
 
-  result.compiledImplicit = compileList(result, 'implicit');
-  result.compiledExplicit = compileList(result, 'explicit');
-  result.compiledTypeMap  = compileMap(result.compiledImplicit, result.compiledExplicit);
+  result.implicit = (this.implicit || []).concat(implicit)
+  result.explicit = (this.explicit || []).concat(explicit)
 
-  return result;
-};
+  result.compiledImplicit = compileList(result, 'implicit')
+  result.compiledExplicit = compileList(result, 'explicit')
+  result.compiledTypeMap = compileMap(result.compiledImplicit, result.compiledExplicit)
 
+  return result
+}
 
-module.exports = Schema;
+module.exports = Schema
 
 
 /***/ }),
@@ -44010,9 +43714,7 @@ module.exports = Schema;
 
 
 
-
-
-module.exports = __nccwpck_require__(58927);
+module.exports = __nccwpck_require__(58927)
 
 
 /***/ }),
@@ -44029,8 +43731,6 @@ module.exports = __nccwpck_require__(58927);
 
 
 
-
-
 module.exports = (__nccwpck_require__(55746).extend)({
   implicit: [
     __nccwpck_require__(28966),
@@ -44042,7 +43742,7 @@ module.exports = (__nccwpck_require__(55746).extend)({
     __nccwpck_require__(16267),
     __nccwpck_require__(78758)
   ]
-});
+})
 
 
 /***/ }),
@@ -44056,10 +43756,7 @@ module.exports = (__nccwpck_require__(55746).extend)({
 
 
 
-
-
-var Schema = __nccwpck_require__(62046);
-
+const Schema = __nccwpck_require__(62046)
 
 module.exports = new Schema({
   explicit: [
@@ -44067,7 +43764,7 @@ module.exports = new Schema({
     __nccwpck_require__(77161),
     __nccwpck_require__(47316)
   ]
-});
+})
 
 
 /***/ }),
@@ -44085,8 +43782,6 @@ module.exports = new Schema({
 
 
 
-
-
 module.exports = (__nccwpck_require__(69832).extend)({
   implicit: [
     __nccwpck_require__(4333),
@@ -44094,7 +43789,7 @@ module.exports = (__nccwpck_require__(69832).extend)({
     __nccwpck_require__(62271),
     __nccwpck_require__(57584)
   ]
-});
+})
 
 
 /***/ }),
@@ -44105,105 +43800,100 @@ module.exports = (__nccwpck_require__(69832).extend)({
 "use strict";
 
 
-
-var common = __nccwpck_require__(19816);
-
+const common = __nccwpck_require__(19816)
 
 // get snippet for a single line, respecting maxLength
-function getLine(buffer, lineStart, lineEnd, position, maxLineLength) {
-  var head = '';
-  var tail = '';
-  var maxHalfLength = Math.floor(maxLineLength / 2) - 1;
+function getLine (buffer, lineStart, lineEnd, position, maxLineLength) {
+  let head = ''
+  let tail = ''
+  const maxHalfLength = Math.floor(maxLineLength / 2) - 1
 
   if (position - lineStart > maxHalfLength) {
-    head = ' ... ';
-    lineStart = position - maxHalfLength + head.length;
+    head = ' ... '
+    lineStart = position - maxHalfLength + head.length
   }
 
   if (lineEnd - position > maxHalfLength) {
-    tail = ' ...';
-    lineEnd = position + maxHalfLength - tail.length;
+    tail = ' ...'
+    lineEnd = position + maxHalfLength - tail.length
   }
 
   return {
     str: head + buffer.slice(lineStart, lineEnd).replace(/\t/g, '→') + tail,
     pos: position - lineStart + head.length // relative position
-  };
+  }
 }
 
-
-function padStart(string, max) {
-  return common.repeat(' ', max - string.length) + string;
+function padStart (string, max) {
+  return common.repeat(' ', max - string.length) + string
 }
 
+function makeSnippet (mark, options) {
+  options = Object.create(options || null)
 
-function makeSnippet(mark, options) {
-  options = Object.create(options || null);
-
-  if (!mark.buffer) return null;
+  if (!mark.buffer) return null
 
-  if (!options.maxLength) options.maxLength = 79;
-  if (typeof options.indent      !== 'number') options.indent      = 1;
-  if (typeof options.linesBefore !== 'number') options.linesBefore = 3;
-  if (typeof options.linesAfter  !== 'number') options.linesAfter  = 2;
+  if (!options.maxLength) options.maxLength = 79
+  if (typeof options.indent !== 'number') options.indent = 1
+  if (typeof options.linesBefore !== 'number') options.linesBefore = 3
+  if (typeof options.linesAfter !== 'number') options.linesAfter = 2
 
-  var re = /\r?\n|\r|\0/g;
-  var lineStarts = [ 0 ];
-  var lineEnds = [];
-  var match;
-  var foundLineNo = -1;
+  const re = /\r?\n|\r|\0/g
+  const lineStarts = [0]
+  const lineEnds = []
+  let match
+  let foundLineNo = -1
 
   while ((match = re.exec(mark.buffer))) {
-    lineEnds.push(match.index);
-    lineStarts.push(match.index + match[0].length);
+    lineEnds.push(match.index)
+    lineStarts.push(match.index + match[0].length)
 
     if (mark.position <= match.index && foundLineNo < 0) {
-      foundLineNo = lineStarts.length - 2;
+      foundLineNo = lineStarts.length - 2
     }
   }
 
-  if (foundLineNo < 0) foundLineNo = lineStarts.length - 1;
+  if (foundLineNo < 0) foundLineNo = lineStarts.length - 1
 
-  var result = '', i, line;
-  var lineNoLength = Math.min(mark.line + options.linesAfter, lineEnds.length).toString().length;
-  var maxLineLength = options.maxLength - (options.indent + lineNoLength + 3);
+  let result = ''
+  const lineNoLength = Math.min(mark.line + options.linesAfter, lineEnds.length).toString().length
+  const maxLineLength = options.maxLength - (options.indent + lineNoLength + 3)
 
-  for (i = 1; i <= options.linesBefore; i++) {
-    if (foundLineNo - i < 0) break;
-    line = getLine(
+  for (let i = 1; i <= options.linesBefore; i++) {
+    if (foundLineNo - i < 0) break
+    const line = getLine(
       mark.buffer,
       lineStarts[foundLineNo - i],
       lineEnds[foundLineNo - i],
       mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]),
       maxLineLength
-    );
+    )
     result = common.repeat(' ', options.indent) + padStart((mark.line - i + 1).toString(), lineNoLength) +
-      ' | ' + line.str + '\n' + result;
+      ' | ' + line.str + '\n' + result
   }
 
-  line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength);
+  const line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength)
   result += common.repeat(' ', options.indent) + padStart((mark.line + 1).toString(), lineNoLength) +
-    ' | ' + line.str + '\n';
-  result += common.repeat('-', options.indent + lineNoLength + 3 + line.pos) + '^' + '\n';
+    ' | ' + line.str + '\n'
+  result += common.repeat('-', options.indent + lineNoLength + 3 + line.pos) + '^' + '\n'
 
-  for (i = 1; i <= options.linesAfter; i++) {
-    if (foundLineNo + i >= lineEnds.length) break;
-    line = getLine(
+  for (let i = 1; i <= options.linesAfter; i++) {
+    if (foundLineNo + i >= lineEnds.length) break
+    const line = getLine(
       mark.buffer,
       lineStarts[foundLineNo + i],
       lineEnds[foundLineNo + i],
       mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]),
       maxLineLength
-    );
+    )
     result += common.repeat(' ', options.indent) + padStart((mark.line + i + 1).toString(), lineNoLength) +
-      ' | ' + line.str + '\n';
+      ' | ' + line.str + '\n'
   }
 
-  return result.replace(/\n$/, '');
+  return result.replace(/\n$/, '')
 }
 
-
-module.exports = makeSnippet;
+module.exports = makeSnippet
 
 
 /***/ }),
@@ -44214,9 +43904,9 @@ module.exports = makeSnippet;
 "use strict";
 
 
-var YAMLException = __nccwpck_require__(41248);
+const YAMLException = __nccwpck_require__(41248)
 
-var TYPE_CONSTRUCTOR_OPTIONS = [
+const TYPE_CONSTRUCTOR_OPTIONS = [
   'kind',
   'multi',
   'resolve',
@@ -44227,57 +43917,57 @@ var TYPE_CONSTRUCTOR_OPTIONS = [
   'representName',
   'defaultStyle',
   'styleAliases'
-];
+]
 
-var YAML_NODE_KINDS = [
+const YAML_NODE_KINDS = [
   'scalar',
   'sequence',
   'mapping'
-];
+]
 
-function compileStyleAliases(map) {
-  var result = {};
+function compileStyleAliases (map) {
+  const result = {}
 
   if (map !== null) {
     Object.keys(map).forEach(function (style) {
       map[style].forEach(function (alias) {
-        result[String(alias)] = style;
-      });
-    });
+        result[String(alias)] = style
+      })
+    })
   }
 
-  return result;
+  return result
 }
 
-function Type(tag, options) {
-  options = options || {};
+function Type (tag, options) {
+  options = options || {}
 
   Object.keys(options).forEach(function (name) {
     if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) {
-      throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.');
+      throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.')
     }
-  });
+  })
 
   // TODO: Add tag format check.
-  this.options       = options; // keep original options in case user wants to extend this type later
-  this.tag           = tag;
-  this.kind          = options['kind']          || null;
-  this.resolve       = options['resolve']       || function () { return true; };
-  this.construct     = options['construct']     || function (data) { return data; };
-  this.instanceOf    = options['instanceOf']    || null;
-  this.predicate     = options['predicate']     || null;
-  this.represent     = options['represent']     || null;
-  this.representName = options['representName'] || null;
-  this.defaultStyle  = options['defaultStyle']  || null;
-  this.multi         = options['multi']         || false;
-  this.styleAliases  = compileStyleAliases(options['styleAliases'] || null);
+  this.options = options // keep original options in case user wants to extend this type later
+  this.tag = tag
+  this.kind = options['kind'] || null
+  this.resolve = options['resolve'] || function () { return true }
+  this.construct = options['construct'] || function (data) { return data }
+  this.instanceOf = options['instanceOf'] || null
+  this.predicate = options['predicate'] || null
+  this.represent = options['represent'] || null
+  this.representName = options['representName'] || null
+  this.defaultStyle = options['defaultStyle'] || null
+  this.multi = options['multi'] || false
+  this.styleAliases = compileStyleAliases(options['styleAliases'] || null)
 
   if (YAML_NODE_KINDS.indexOf(this.kind) === -1) {
-    throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.');
+    throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.')
   }
 }
 
-module.exports = Type;
+module.exports = Type
 
 
 /***/ }),
@@ -44288,120 +43978,117 @@ module.exports = Type;
 "use strict";
 
 
-/*eslint-disable no-bitwise*/
-
-
-var Type = __nccwpck_require__(9557);
-
+const Type = __nccwpck_require__(9557)
 
 // [ 64, 65, 66 ] -> [ padding, CR, LF ]
-var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r';
-
+const BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r'
 
-function resolveYamlBinary(data) {
-  if (data === null) return false;
+function resolveYamlBinary (data) {
+  if (data === null) return false
 
-  var code, idx, bitlen = 0, max = data.length, map = BASE64_MAP;
+  let bitlen = 0
+  const max = data.length
+  const map = BASE64_MAP
 
   // Convert one by one.
-  for (idx = 0; idx < max; idx++) {
-    code = map.indexOf(data.charAt(idx));
+  for (let idx = 0; idx < max; idx++) {
+    const code = map.indexOf(data.charAt(idx))
 
     // Skip CR/LF
-    if (code > 64) continue;
+    if (code > 64) continue
 
     // Fail on illegal characters
-    if (code < 0) return false;
+    if (code < 0) return false
 
-    bitlen += 6;
+    bitlen += 6
   }
 
   // If there are any bits left, source was corrupted
-  return (bitlen % 8) === 0;
+  return (bitlen % 8) === 0
 }
 
-function constructYamlBinary(data) {
-  var idx, tailbits,
-      input = data.replace(/[\r\n=]/g, ''), // remove CR/LF & padding to simplify scan
-      max = input.length,
-      map = BASE64_MAP,
-      bits = 0,
-      result = [];
+function constructYamlBinary (data) {
+  const input = data.replace(/[\r\n=]/g, '') // remove CR/LF & padding to simplify scan
+  const max = input.length
+  const map = BASE64_MAP
+  let bits = 0
+  const result = []
 
   // Collect by 6*4 bits (3 bytes)
 
-  for (idx = 0; idx < max; idx++) {
+  for (let idx = 0; idx < max; idx++) {
     if ((idx % 4 === 0) && idx) {
-      result.push((bits >> 16) & 0xFF);
-      result.push((bits >> 8) & 0xFF);
-      result.push(bits & 0xFF);
+      result.push((bits >> 16) & 0xFF)
+      result.push((bits >> 8) & 0xFF)
+      result.push(bits & 0xFF)
     }
 
-    bits = (bits << 6) | map.indexOf(input.charAt(idx));
+    bits = (bits << 6) | map.indexOf(input.charAt(idx))
   }
 
   // Dump tail
 
-  tailbits = (max % 4) * 6;
+  const tailbits = (max % 4) * 6
 
   if (tailbits === 0) {
-    result.push((bits >> 16) & 0xFF);
-    result.push((bits >> 8) & 0xFF);
-    result.push(bits & 0xFF);
+    result.push((bits >> 16) & 0xFF)
+    result.push((bits >> 8) & 0xFF)
+    result.push(bits & 0xFF)
   } else if (tailbits === 18) {
-    result.push((bits >> 10) & 0xFF);
-    result.push((bits >> 2) & 0xFF);
+    result.push((bits >> 10) & 0xFF)
+    result.push((bits >> 2) & 0xFF)
   } else if (tailbits === 12) {
-    result.push((bits >> 4) & 0xFF);
+    result.push((bits >> 4) & 0xFF)
   }
 
-  return new Uint8Array(result);
+  return new Uint8Array(result)
 }
 
-function representYamlBinary(object /*, style*/) {
-  var result = '', bits = 0, idx, tail,
-      max = object.length,
-      map = BASE64_MAP;
+function representYamlBinary (object /*, style */) {
+  let result = ''
+  let bits = 0
+  const max = object.length
+  const map = BASE64_MAP
 
   // Convert every three bytes to 4 ASCII characters.
 
-  for (idx = 0; idx < max; idx++) {
+  for (let idx = 0; idx < max; idx++) {
     if ((idx % 3 === 0) && idx) {
-      result += map[(bits >> 18) & 0x3F];
-      result += map[(bits >> 12) & 0x3F];
-      result += map[(bits >> 6) & 0x3F];
-      result += map[bits & 0x3F];
+      result += map[(bits >> 18) & 0x3F]
+      result += map[(bits >> 12) & 0x3F]
+      result += map[(bits >> 6) & 0x3F]
+      result += map[bits & 0x3F]
     }
 
-    bits = (bits << 8) + object[idx];
+    bits = (bits << 8) + object[idx]
   }
 
   // Dump tail
 
-  tail = max % 3;
+  const tail = max % 3
 
   if (tail === 0) {
-    result += map[(bits >> 18) & 0x3F];
-    result += map[(bits >> 12) & 0x3F];
-    result += map[(bits >> 6) & 0x3F];
-    result += map[bits & 0x3F];
+    result += map[(bits >> 18) & 0x3F]
+    result += map[(bits >> 12) & 0x3F]
+    result += map[(bits >> 6) & 0x3F]
+    result += map[bits & 0x3F]
   } else if (tail === 2) {
-    result += map[(bits >> 10) & 0x3F];
-    result += map[(bits >> 4) & 0x3F];
-    result += map[(bits << 2) & 0x3F];
-    result += map[64];
+    result += map[(bits >> 10) & 0x3F]
+    result += map[(bits >> 4) & 0x3F]
+    result += map[(bits << 2) & 0x3F]
+    result += map[64]
   } else if (tail === 1) {
-    result += map[(bits >> 2) & 0x3F];
-    result += map[(bits << 4) & 0x3F];
-    result += map[64];
-    result += map[64];
+    result += map[(bits >> 2) & 0x3F]
+    result += map[(bits << 4) & 0x3F]
+    result += map[64]
+    result += map[64]
   }
 
-  return result;
+  return result
 }
 
-function isBinary(obj) {
-  return Object.prototype.toString.call(obj) ===  '[object Uint8Array]';
+function isBinary (obj) {
+  return Object.prototype.toString.call(obj) === '[object Uint8Array]'
 }
 
 module.exports = new Type('tag:yaml.org,2002:binary', {
@@ -44410,7 +44097,7 @@ module.exports = new Type('tag:yaml.org,2002:binary', {
   construct: constructYamlBinary,
   predicate: isBinary,
   represent: representYamlBinary
-});
+})
 
 
 /***/ }),
@@ -44421,25 +44108,25 @@ module.exports = new Type('tag:yaml.org,2002:binary', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
-function resolveYamlBoolean(data) {
-  if (data === null) return false;
+function resolveYamlBoolean (data) {
+  if (data === null) return false
 
-  var max = data.length;
+  const max = data.length
 
   return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) ||
-         (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE'));
+         (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE'))
 }
 
-function constructYamlBoolean(data) {
+function constructYamlBoolean (data) {
   return data === 'true' ||
          data === 'True' ||
-         data === 'TRUE';
+         data === 'TRUE'
 }
 
-function isBoolean(object) {
-  return Object.prototype.toString.call(object) === '[object Boolean]';
+function isBoolean (object) {
+  return Object.prototype.toString.call(object) === '[object Boolean]'
 }
 
 module.exports = new Type('tag:yaml.org,2002:bool', {
@@ -44448,12 +44135,12 @@ module.exports = new Type('tag:yaml.org,2002:bool', {
   construct: constructYamlBoolean,
   predicate: isBoolean,
   represent: {
-    lowercase: function (object) { return object ? 'true' : 'false'; },
-    uppercase: function (object) { return object ? 'TRUE' : 'FALSE'; },
-    camelcase: function (object) { return object ? 'True' : 'False'; }
+    lowercase: function (object) { return object ? 'true' : 'false' },
+    uppercase: function (object) { return object ? 'TRUE' : 'FALSE' },
+    camelcase: function (object) { return object ? 'True' : 'False' }
   },
   defaultStyle: 'lowercase'
-});
+})
 
 
 /***/ }),
@@ -44464,91 +44151,93 @@ module.exports = new Type('tag:yaml.org,2002:bool', {
 "use strict";
 
 
-var common = __nccwpck_require__(19816);
-var Type   = __nccwpck_require__(9557);
+const common = __nccwpck_require__(19816)
+const Type = __nccwpck_require__(9557)
 
-var YAML_FLOAT_PATTERN = new RegExp(
+const YAML_FLOAT_PATTERN = new RegExp(
   // 2.5e4, 2.5 and integers
-  '^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?' +
+  '^(?:[-+]?(?:[0-9]+)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?' +
   // .2e4, .2
   // special case, seems not from spec
-  '|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?' +
+  '|\\.[0-9]+(?:[eE][-+]?[0-9]+)?' +
   // .inf
   '|[-+]?\\.(?:inf|Inf|INF)' +
   // .nan
-  '|\\.(?:nan|NaN|NAN))$');
+  '|\\.(?:nan|NaN|NAN))$')
 
-function resolveYamlFloat(data) {
-  if (data === null) return false;
+const YAML_FLOAT_SPECIAL_PATTERN = new RegExp(
+  '^(?:' +
+  // .inf
+  '[-+]?\\.(?:inf|Inf|INF)' +
+  // .nan
+  '|\\.(?:nan|NaN|NAN))$')
 
-  if (!YAML_FLOAT_PATTERN.test(data) ||
-      // Quick hack to not allow integers end with `_`
-      // Probably should update regexp & check speed
-      data[data.length - 1] === '_') {
-    return false;
+function resolveYamlFloat (data) {
+  if (data === null) return false
+
+  if (!YAML_FLOAT_PATTERN.test(data)) {
+    return false
   }
 
-  return true;
-}
+  if (isFinite(parseFloat(data, 10))) {
+    return true
+  }
 
-function constructYamlFloat(data) {
-  var value, sign;
+  return YAML_FLOAT_SPECIAL_PATTERN.test(data)
+}
 
-  value  = data.replace(/_/g, '').toLowerCase();
-  sign   = value[0] === '-' ? -1 : 1;
+function constructYamlFloat (data) {
+  let value = data.toLowerCase()
+  const sign = value[0] === '-' ? -1 : 1
 
   if ('+-'.indexOf(value[0]) >= 0) {
-    value = value.slice(1);
+    value = value.slice(1)
   }
 
   if (value === '.inf') {
-    return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
-
+    return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY
   } else if (value === '.nan') {
-    return NaN;
+    return NaN
   }
-  return sign * parseFloat(value, 10);
+  return sign * parseFloat(value, 10)
 }
 
+const SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/
 
-var SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/;
-
-function representYamlFloat(object, style) {
-  var res;
-
+function representYamlFloat (object, style) {
   if (isNaN(object)) {
     switch (style) {
-      case 'lowercase': return '.nan';
-      case 'uppercase': return '.NAN';
-      case 'camelcase': return '.NaN';
+      case 'lowercase': return '.nan'
+      case 'uppercase': return '.NAN'
+      case 'camelcase': return '.NaN'
     }
   } else if (Number.POSITIVE_INFINITY === object) {
     switch (style) {
-      case 'lowercase': return '.inf';
-      case 'uppercase': return '.INF';
-      case 'camelcase': return '.Inf';
+      case 'lowercase': return '.inf'
+      case 'uppercase': return '.INF'
+      case 'camelcase': return '.Inf'
     }
   } else if (Number.NEGATIVE_INFINITY === object) {
     switch (style) {
-      case 'lowercase': return '-.inf';
-      case 'uppercase': return '-.INF';
-      case 'camelcase': return '-.Inf';
+      case 'lowercase': return '-.inf'
+      case 'uppercase': return '-.INF'
+      case 'camelcase': return '-.Inf'
     }
   } else if (common.isNegativeZero(object)) {
-    return '-0.0';
+    return '-0.0'
   }
 
-  res = object.toString(10);
+  const res = object.toString(10)
 
   // JS stringifier can build scientific format without dots: 5e-100,
   // while YAML requres dot: 5.e-100. Fix it with simple hack
 
-  return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res;
+  return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res
 }
 
-function isFloat(object) {
+function isFloat (object) {
   return (Object.prototype.toString.call(object) === '[object Number]') &&
-         (object % 1 !== 0 || common.isNegativeZero(object));
+         (object % 1 !== 0 || common.isNegativeZero(object))
 }
 
 module.exports = new Type('tag:yaml.org,2002:float', {
@@ -44558,7 +44247,7 @@ module.exports = new Type('tag:yaml.org,2002:float', {
   predicate: isFloat,
   represent: representYamlFloat,
   defaultStyle: 'lowercase'
-});
+})
 
 
 /***/ }),
@@ -44569,138 +44258,125 @@ module.exports = new Type('tag:yaml.org,2002:float', {
 "use strict";
 
 
-var common = __nccwpck_require__(19816);
-var Type   = __nccwpck_require__(9557);
+const common = __nccwpck_require__(19816)
+const Type = __nccwpck_require__(9557)
 
-function isHexCode(c) {
-  return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) ||
-         ((0x41/* A */ <= c) && (c <= 0x46/* F */)) ||
-         ((0x61/* a */ <= c) && (c <= 0x66/* f */));
+function isHexCode (c) {
+  return ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */)) ||
+         ((c >= 0x41/* A */) && (c <= 0x46/* F */)) ||
+         ((c >= 0x61/* a */) && (c <= 0x66/* f */))
 }
 
-function isOctCode(c) {
-  return ((0x30/* 0 */ <= c) && (c <= 0x37/* 7 */));
+function isOctCode (c) {
+  return ((c >= 0x30/* 0 */) && (c <= 0x37/* 7 */))
 }
 
-function isDecCode(c) {
-  return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */));
+function isDecCode (c) {
+  return ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */))
 }
 
-function resolveYamlInteger(data) {
-  if (data === null) return false;
+function resolveYamlInteger (data) {
+  if (data === null) return false
 
-  var max = data.length,
-      index = 0,
-      hasDigits = false,
-      ch;
+  const max = data.length
+  let index = 0
+  let hasDigits = false
 
-  if (!max) return false;
+  if (!max) return false
 
-  ch = data[index];
+  let ch = data[index]
 
   // sign
   if (ch === '-' || ch === '+') {
-    ch = data[++index];
+    ch = data[++index]
   }
 
   if (ch === '0') {
     // 0
-    if (index + 1 === max) return true;
-    ch = data[++index];
+    if (index + 1 === max) return true
+    ch = data[++index]
 
     // base 2, base 8, base 16
 
     if (ch === 'b') {
       // base 2
-      index++;
+      index++
 
       for (; index < max; index++) {
-        ch = data[index];
-        if (ch === '_') continue;
-        if (ch !== '0' && ch !== '1') return false;
-        hasDigits = true;
+        ch = data[index]
+        if (ch !== '0' && ch !== '1') return false
+        hasDigits = true
       }
-      return hasDigits && ch !== '_';
+      return hasDigits && isFinite(parseYamlInteger(data))
     }
 
-
     if (ch === 'x') {
       // base 16
-      index++;
+      index++
 
       for (; index < max; index++) {
-        ch = data[index];
-        if (ch === '_') continue;
-        if (!isHexCode(data.charCodeAt(index))) return false;
-        hasDigits = true;
+        if (!isHexCode(data.charCodeAt(index))) return false
+        hasDigits = true
       }
-      return hasDigits && ch !== '_';
+      return hasDigits && isFinite(parseYamlInteger(data))
     }
 
-
     if (ch === 'o') {
       // base 8
-      index++;
+      index++
 
       for (; index < max; index++) {
-        ch = data[index];
-        if (ch === '_') continue;
-        if (!isOctCode(data.charCodeAt(index))) return false;
-        hasDigits = true;
+        if (!isOctCode(data.charCodeAt(index))) return false
+        hasDigits = true
       }
-      return hasDigits && ch !== '_';
+      return hasDigits && isFinite(parseYamlInteger(data))
     }
   }
 
   // base 10 (except 0)
 
-  // value should not start with `_`;
-  if (ch === '_') return false;
-
   for (; index < max; index++) {
-    ch = data[index];
-    if (ch === '_') continue;
     if (!isDecCode(data.charCodeAt(index))) {
-      return false;
+      return false
     }
-    hasDigits = true;
+    hasDigits = true
   }
 
-  // Should have digits and should not end with `_`
-  if (!hasDigits || ch === '_') return false;
+  if (!hasDigits) return false
 
-  return true;
+  return isFinite(parseYamlInteger(data))
 }
 
-function constructYamlInteger(data) {
-  var value = data, sign = 1, ch;
-
-  if (value.indexOf('_') !== -1) {
-    value = value.replace(/_/g, '');
-  }
+function parseYamlInteger (data) {
+  let value = data
+  let sign = 1
 
-  ch = value[0];
+  let ch = value[0]
 
   if (ch === '-' || ch === '+') {
-    if (ch === '-') sign = -1;
-    value = value.slice(1);
-    ch = value[0];
+    if (ch === '-') sign = -1
+    value = value.slice(1)
+    ch = value[0]
   }
 
-  if (value === '0') return 0;
+  if (value === '0') return 0
 
   if (ch === '0') {
-    if (value[1] === 'b') return sign * parseInt(value.slice(2), 2);
-    if (value[1] === 'x') return sign * parseInt(value.slice(2), 16);
-    if (value[1] === 'o') return sign * parseInt(value.slice(2), 8);
+    if (value[1] === 'b') return sign * parseInt(value.slice(2), 2)
+    if (value[1] === 'x') return sign * parseInt(value.slice(2), 16)
+    if (value[1] === 'o') return sign * parseInt(value.slice(2), 8)
   }
 
-  return sign * parseInt(value, 10);
+  return sign * parseInt(value, 10)
+}
+
+function constructYamlInteger (data) {
+  return parseYamlInteger(data)
 }
 
-function isInteger(object) {
+function isInteger (object) {
   return (Object.prototype.toString.call(object)) === '[object Number]' &&
-         (object % 1 === 0 && !common.isNegativeZero(object));
+         (object % 1 === 0 && !common.isNegativeZero(object))
 }
 
 module.exports = new Type('tag:yaml.org,2002:int', {
@@ -44709,20 +44385,19 @@ module.exports = new Type('tag:yaml.org,2002:int', {
   construct: constructYamlInteger,
   predicate: isInteger,
   represent: {
-    binary:      function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); },
-    octal:       function (obj) { return obj >= 0 ? '0o'  + obj.toString(8) : '-0o'  + obj.toString(8).slice(1); },
-    decimal:     function (obj) { return obj.toString(10); },
-    /* eslint-disable max-len */
-    hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() :  '-0x' + obj.toString(16).toUpperCase().slice(1); }
+    binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1) },
+    octal: function (obj) { return obj >= 0 ? '0o' + obj.toString(8) : '-0o' + obj.toString(8).slice(1) },
+    decimal: function (obj) { return obj.toString(10) },
+    hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1) }
   },
   defaultStyle: 'decimal',
   styleAliases: {
-    binary:      [ 2,  'bin' ],
-    octal:       [ 8,  'oct' ],
-    decimal:     [ 10, 'dec' ],
-    hexadecimal: [ 16, 'hex' ]
+    binary: [2, 'bin'],
+    octal: [8, 'oct'],
+    decimal: [10, 'dec'],
+    hexadecimal: [16, 'hex']
   }
-});
+})
 
 
 /***/ }),
@@ -44733,12 +44408,12 @@ module.exports = new Type('tag:yaml.org,2002:int', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
 module.exports = new Type('tag:yaml.org,2002:map', {
   kind: 'mapping',
-  construct: function (data) { return data !== null ? data : {}; }
-});
+  construct: function (data) { return data !== null ? data : {} }
+})
 
 
 /***/ }),
@@ -44749,16 +44424,16 @@ module.exports = new Type('tag:yaml.org,2002:map', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
-function resolveYamlMerge(data) {
-  return data === '<<' || data === null;
+function resolveYamlMerge (data) {
+  return data === '<<' || data === null
 }
 
 module.exports = new Type('tag:yaml.org,2002:merge', {
   kind: 'scalar',
   resolve: resolveYamlMerge
-});
+})
 
 
 /***/ }),
@@ -44769,23 +44444,23 @@ module.exports = new Type('tag:yaml.org,2002:merge', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
-function resolveYamlNull(data) {
-  if (data === null) return true;
+function resolveYamlNull (data) {
+  if (data === null) return true
 
-  var max = data.length;
+  const max = data.length
 
   return (max === 1 && data === '~') ||
-         (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'));
+         (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'))
 }
 
-function constructYamlNull() {
-  return null;
+function constructYamlNull () {
+  return null
 }
 
-function isNull(object) {
-  return object === null;
+function isNull (object) {
+  return object === null
 }
 
 module.exports = new Type('tag:yaml.org,2002:null', {
@@ -44794,14 +44469,14 @@ module.exports = new Type('tag:yaml.org,2002:null', {
   construct: constructYamlNull,
   predicate: isNull,
   represent: {
-    canonical: function () { return '~';    },
-    lowercase: function () { return 'null'; },
-    uppercase: function () { return 'NULL'; },
-    camelcase: function () { return 'Null'; },
-    empty:     function () { return '';     }
+    canonical: function () { return '~' },
+    lowercase: function () { return 'null' },
+    uppercase: function () { return 'NULL' },
+    camelcase: function () { return 'Null' },
+    empty: function () { return '' }
   },
   defaultStyle: 'lowercase'
-});
+})
 
 
 /***/ }),
@@ -44812,48 +44487,49 @@ module.exports = new Type('tag:yaml.org,2002:null', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
-var _hasOwnProperty = Object.prototype.hasOwnProperty;
-var _toString       = Object.prototype.toString;
+const _hasOwnProperty = Object.prototype.hasOwnProperty
+const _toString = Object.prototype.toString
 
-function resolveYamlOmap(data) {
-  if (data === null) return true;
+function resolveYamlOmap (data) {
+  if (data === null) return true
 
-  var objectKeys = [], index, length, pair, pairKey, pairHasKey,
-      object = data;
+  const objectKeys = []
+  const object = data
 
-  for (index = 0, length = object.length; index < length; index += 1) {
-    pair = object[index];
-    pairHasKey = false;
+  for (let index = 0, length = object.length; index < length; index += 1) {
+    const pair = object[index]
+    let pairHasKey = false
 
-    if (_toString.call(pair) !== '[object Object]') return false;
+    if (_toString.call(pair) !== '[object Object]') return false
 
+    let pairKey
     for (pairKey in pair) {
       if (_hasOwnProperty.call(pair, pairKey)) {
-        if (!pairHasKey) pairHasKey = true;
-        else return false;
+        if (!pairHasKey) pairHasKey = true
+        else return false
       }
     }
 
-    if (!pairHasKey) return false;
+    if (!pairHasKey) return false
 
-    if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey);
-    else return false;
+    if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey)
+    else return false
   }
 
-  return true;
+  return true
 }
 
-function constructYamlOmap(data) {
-  return data !== null ? data : [];
+function constructYamlOmap (data) {
+  return data !== null ? data : []
 }
 
 module.exports = new Type('tag:yaml.org,2002:omap', {
   kind: 'sequence',
   resolve: resolveYamlOmap,
   construct: constructYamlOmap
-});
+})
 
 
 /***/ }),
@@ -44864,57 +44540,54 @@ module.exports = new Type('tag:yaml.org,2002:omap', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
-var _toString = Object.prototype.toString;
+const _toString = Object.prototype.toString
 
-function resolveYamlPairs(data) {
-  if (data === null) return true;
+function resolveYamlPairs (data) {
+  if (data === null) return true
 
-  var index, length, pair, keys, result,
-      object = data;
+  const object = data
 
-  result = new Array(object.length);
+  const result = new Array(object.length)
 
-  for (index = 0, length = object.length; index < length; index += 1) {
-    pair = object[index];
+  for (let index = 0, length = object.length; index < length; index += 1) {
+    const pair = object[index]
 
-    if (_toString.call(pair) !== '[object Object]') return false;
+    if (_toString.call(pair) !== '[object Object]') return false
 
-    keys = Object.keys(pair);
+    const keys = Object.keys(pair)
 
-    if (keys.length !== 1) return false;
+    if (keys.length !== 1) return false
 
-    result[index] = [ keys[0], pair[keys[0]] ];
+    result[index] = [keys[0], pair[keys[0]]]
   }
 
-  return true;
+  return true
 }
 
-function constructYamlPairs(data) {
-  if (data === null) return [];
+function constructYamlPairs (data) {
+  if (data === null) return []
 
-  var index, length, pair, keys, result,
-      object = data;
+  const object = data
+  const result = new Array(object.length)
 
-  result = new Array(object.length);
+  for (let index = 0, length = object.length; index < length; index += 1) {
+    const pair = object[index]
 
-  for (index = 0, length = object.length; index < length; index += 1) {
-    pair = object[index];
+    const keys = Object.keys(pair)
 
-    keys = Object.keys(pair);
-
-    result[index] = [ keys[0], pair[keys[0]] ];
+    result[index] = [keys[0], pair[keys[0]]]
   }
 
-  return result;
+  return result
 }
 
 module.exports = new Type('tag:yaml.org,2002:pairs', {
   kind: 'sequence',
   resolve: resolveYamlPairs,
   construct: constructYamlPairs
-});
+})
 
 
 /***/ }),
@@ -44925,12 +44598,12 @@ module.exports = new Type('tag:yaml.org,2002:pairs', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
 module.exports = new Type('tag:yaml.org,2002:seq', {
   kind: 'sequence',
-  construct: function (data) { return data !== null ? data : []; }
-});
+  construct: function (data) { return data !== null ? data : [] }
+})
 
 
 /***/ }),
@@ -44941,33 +44614,33 @@ module.exports = new Type('tag:yaml.org,2002:seq', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
-var _hasOwnProperty = Object.prototype.hasOwnProperty;
+const _hasOwnProperty = Object.prototype.hasOwnProperty
 
-function resolveYamlSet(data) {
-  if (data === null) return true;
+function resolveYamlSet (data) {
+  if (data === null) return true
 
-  var key, object = data;
+  const object = data
 
-  for (key in object) {
+  for (const key in object) {
     if (_hasOwnProperty.call(object, key)) {
-      if (object[key] !== null) return false;
+      if (object[key] !== null) return false
     }
   }
 
-  return true;
+  return true
 }
 
-function constructYamlSet(data) {
-  return data !== null ? data : {};
+function constructYamlSet (data) {
+  return data !== null ? data : {}
 }
 
 module.exports = new Type('tag:yaml.org,2002:set', {
   kind: 'mapping',
   resolve: resolveYamlSet,
   construct: constructYamlSet
-});
+})
 
 
 /***/ }),
@@ -44978,12 +44651,12 @@ module.exports = new Type('tag:yaml.org,2002:set', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
 module.exports = new Type('tag:yaml.org,2002:str', {
   kind: 'scalar',
-  construct: function (data) { return data !== null ? data : ''; }
-});
+  construct: function (data) { return data !== null ? data : '' }
+})
 
 
 /***/ }),
@@ -44994,83 +44667,83 @@ module.exports = new Type('tag:yaml.org,2002:str', {
 "use strict";
 
 
-var Type = __nccwpck_require__(9557);
+const Type = __nccwpck_require__(9557)
 
-var YAML_DATE_REGEXP = new RegExp(
-  '^([0-9][0-9][0-9][0-9])'          + // [1] year
-  '-([0-9][0-9])'                    + // [2] month
-  '-([0-9][0-9])$');                   // [3] day
+const YAML_DATE_REGEXP = new RegExp(
+  '^([0-9][0-9][0-9][0-9])' + // [1] year
+  '-([0-9][0-9])' + // [2] month
+  '-([0-9][0-9])$')                   // [3] day
 
-var YAML_TIMESTAMP_REGEXP = new RegExp(
-  '^([0-9][0-9][0-9][0-9])'          + // [1] year
-  '-([0-9][0-9]?)'                   + // [2] month
-  '-([0-9][0-9]?)'                   + // [3] day
-  '(?:[Tt]|[ \\t]+)'                 + // ...
-  '([0-9][0-9]?)'                    + // [4] hour
-  ':([0-9][0-9])'                    + // [5] minute
-  ':([0-9][0-9])'                    + // [6] second
-  '(?:\\.([0-9]*))?'                 + // [7] fraction
-  '(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour
-  '(?::([0-9][0-9]))?))?$');           // [11] tz_minute
+const YAML_TIMESTAMP_REGEXP = new RegExp(
+  '^([0-9][0-9][0-9][0-9])' + // [1] year
+  '-([0-9][0-9]?)' + // [2] month
+  '-([0-9][0-9]?)' + // [3] day
+  '(?:[Tt]|[ \\t]+)' + // ...
+  '([0-9][0-9]?)' + // [4] hour
+  ':([0-9][0-9])' + // [5] minute
+  ':([0-9][0-9])' + // [6] second
+  '(?:\\.([0-9]*))?' + // [7] fraction
+  '(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tzHour
+  '(?::([0-9][0-9]))?))?$')           // [11] tzMinute
 
-function resolveYamlTimestamp(data) {
-  if (data === null) return false;
-  if (YAML_DATE_REGEXP.exec(data) !== null) return true;
-  if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true;
-  return false;
+function resolveYamlTimestamp (data) {
+  if (data === null) return false
+  if (YAML_DATE_REGEXP.exec(data) !== null) return true
+  if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true
+  return false
 }
 
-function constructYamlTimestamp(data) {
-  var match, year, month, day, hour, minute, second, fraction = 0,
-      delta = null, tz_hour, tz_minute, date;
+function constructYamlTimestamp (data) {
+  let fraction = 0
+  let delta = null
 
-  match = YAML_DATE_REGEXP.exec(data);
-  if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data);
+  let match = YAML_DATE_REGEXP.exec(data)
+  if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data)
 
-  if (match === null) throw new Error('Date resolve error');
+  if (match === null) throw new Error('Date resolve error')
 
   // match: [1] year [2] month [3] day
 
-  year = +(match[1]);
-  month = +(match[2]) - 1; // JS month starts with 0
-  day = +(match[3]);
+  const year = +(match[1])
+  const month = +(match[2]) - 1 // JS month starts with 0
+  const day = +(match[3])
 
   if (!match[4]) { // no hour
-    return new Date(Date.UTC(year, month, day));
+    return new Date(Date.UTC(year, month, day))
   }
 
   // match: [4] hour [5] minute [6] second [7] fraction
 
-  hour = +(match[4]);
-  minute = +(match[5]);
-  second = +(match[6]);
+  const hour = +(match[4])
+  const minute = +(match[5])
+  const second = +(match[6])
 
   if (match[7]) {
-    fraction = match[7].slice(0, 3);
+    fraction = match[7].slice(0, 3)
     while (fraction.length < 3) { // milli-seconds
-      fraction += '0';
+      fraction += '0'
     }
-    fraction = +fraction;
+    fraction = +fraction
   }
 
-  // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute
+  // match: [8] tz [9] tz_sign [10] tzHour [11] tzMinute
 
   if (match[9]) {
-    tz_hour = +(match[10]);
-    tz_minute = +(match[11] || 0);
-    delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds
-    if (match[9] === '-') delta = -delta;
+    const tzHour = +(match[10])
+    const tzMinute = +(match[11] || 0)
+    delta = (tzHour * 60 + tzMinute) * 60000 // delta in mili-seconds
+    if (match[9] === '-') delta = -delta
   }
 
-  date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction));
+  const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction))
 
-  if (delta) date.setTime(date.getTime() - delta);
+  if (delta) date.setTime(date.getTime() - delta)
 
-  return date;
+  return date
 }
 
-function representYamlTimestamp(object /*, style*/) {
-  return object.toISOString();
+function representYamlTimestamp (object /*, style */) {
+  return object.toISOString()
 }
 
 module.exports = new Type('tag:yaml.org,2002:timestamp', {
@@ -45079,1282 +44752,270 @@ module.exports = new Type('tag:yaml.org,2002:timestamp', {
   construct: constructYamlTimestamp,
   instanceOf: Date,
   represent: representYamlTimestamp
-});
+})
 
 
 /***/ }),
 
-/***/ 43772:
-/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
-
-module.exports = minimatch
-minimatch.Minimatch = Minimatch
-
-var path = (function () { try { return __nccwpck_require__(16928) } catch (e) {}}()) || {
-  sep: '/'
-}
-minimatch.sep = path.sep
-
-var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
-var expand = __nccwpck_require__(94691)
-
-var plTypes = {
-  '!': { open: '(?:(?!(?:', close: '))[^/]*?)'},
-  '?': { open: '(?:', close: ')?' },
-  '+': { open: '(?:', close: ')+' },
-  '*': { open: '(?:', close: ')*' },
-  '@': { open: '(?:', close: ')' }
-}
-
-// any single thing other than /
-// don't need to escape / when using new RegExp()
-var qmark = '[^/]'
-
-// * => any number of characters
-var star = qmark + '*?'
-
-// ** when dots are allowed.  Anything goes, except .. and .
-// not (^ or / followed by one or two dots followed by $ or /),
-// followed by anything, any number of times.
-var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
-
-// not a ^ or / followed by a dot,
-// followed by anything, any number of times.
-var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
-
-// characters that need to be escaped in RegExp.
-var reSpecials = charSet('().*{}+?[]^$\\!')
-
-// "abc" -> { a:true, b:true, c:true }
-function charSet (s) {
-  return s.split('').reduce(function (set, c) {
-    set[c] = true
-    return set
-  }, {})
-}
+/***/ 70744:
+/***/ ((module) => {
 
-// normalizes slashes.
-var slashSplit = /\/+/
+/**
+ * Helpers.
+ */
 
-minimatch.filter = filter
-function filter (pattern, options) {
-  options = options || {}
-  return function (p, i, list) {
-    return minimatch(p, pattern, options)
-  }
-}
+var s = 1000;
+var m = s * 60;
+var h = m * 60;
+var d = h * 24;
+var w = d * 7;
+var y = d * 365.25;
 
-function ext (a, b) {
-  b = b || {}
-  var t = {}
-  Object.keys(a).forEach(function (k) {
-    t[k] = a[k]
-  })
-  Object.keys(b).forEach(function (k) {
-    t[k] = b[k]
-  })
-  return t
-}
+/**
+ * Parse or format the given `val`.
+ *
+ * Options:
+ *
+ *  - `long` verbose formatting [false]
+ *
+ * @param {String|Number} val
+ * @param {Object} [options]
+ * @throws {Error} throw an error if val is not a non-empty string or a number
+ * @return {String|Number}
+ * @api public
+ */
 
-minimatch.defaults = function (def) {
-  if (!def || typeof def !== 'object' || !Object.keys(def).length) {
-    return minimatch
+module.exports = function (val, options) {
+  options = options || {};
+  var type = typeof val;
+  if (type === 'string' && val.length > 0) {
+    return parse(val);
+  } else if (type === 'number' && isFinite(val)) {
+    return options.long ? fmtLong(val) : fmtShort(val);
   }
+  throw new Error(
+    'val is not a non-empty string or a valid number. val=' +
+      JSON.stringify(val)
+  );
+};
 
-  var orig = minimatch
+/**
+ * Parse the given `str` and return milliseconds.
+ *
+ * @param {String} str
+ * @return {Number}
+ * @api private
+ */
 
-  var m = function minimatch (p, pattern, options) {
-    return orig(p, pattern, ext(def, options))
+function parse(str) {
+  str = String(str);
+  if (str.length > 100) {
+    return;
   }
-
-  m.Minimatch = function Minimatch (pattern, options) {
-    return new orig.Minimatch(pattern, ext(def, options))
+  var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
+    str
+  );
+  if (!match) {
+    return;
   }
-  m.Minimatch.defaults = function defaults (options) {
-    return orig.defaults(ext(def, options)).Minimatch
+  var n = parseFloat(match[1]);
+  var type = (match[2] || 'ms').toLowerCase();
+  switch (type) {
+    case 'years':
+    case 'year':
+    case 'yrs':
+    case 'yr':
+    case 'y':
+      return n * y;
+    case 'weeks':
+    case 'week':
+    case 'w':
+      return n * w;
+    case 'days':
+    case 'day':
+    case 'd':
+      return n * d;
+    case 'hours':
+    case 'hour':
+    case 'hrs':
+    case 'hr':
+    case 'h':
+      return n * h;
+    case 'minutes':
+    case 'minute':
+    case 'mins':
+    case 'min':
+    case 'm':
+      return n * m;
+    case 'seconds':
+    case 'second':
+    case 'secs':
+    case 'sec':
+    case 's':
+      return n * s;
+    case 'milliseconds':
+    case 'millisecond':
+    case 'msecs':
+    case 'msec':
+    case 'ms':
+      return n;
+    default:
+      return undefined;
   }
+}
 
-  m.filter = function filter (pattern, options) {
-    return orig.filter(pattern, ext(def, options))
-  }
+/**
+ * Short format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
 
-  m.defaults = function defaults (options) {
-    return orig.defaults(ext(def, options))
+function fmtShort(ms) {
+  var msAbs = Math.abs(ms);
+  if (msAbs >= d) {
+    return Math.round(ms / d) + 'd';
   }
-
-  m.makeRe = function makeRe (pattern, options) {
-    return orig.makeRe(pattern, ext(def, options))
+  if (msAbs >= h) {
+    return Math.round(ms / h) + 'h';
   }
-
-  m.braceExpand = function braceExpand (pattern, options) {
-    return orig.braceExpand(pattern, ext(def, options))
+  if (msAbs >= m) {
+    return Math.round(ms / m) + 'm';
   }
-
-  m.match = function (list, pattern, options) {
-    return orig.match(list, pattern, ext(def, options))
+  if (msAbs >= s) {
+    return Math.round(ms / s) + 's';
   }
-
-  return m
-}
-
-Minimatch.defaults = function (def) {
-  return minimatch.defaults(def).Minimatch
+  return ms + 'ms';
 }
 
-function minimatch (p, pattern, options) {
-  assertValidPattern(pattern)
-
-  if (!options) options = {}
-
-  // shortcut: comments match nothing.
-  if (!options.nocomment && pattern.charAt(0) === '#') {
-    return false
-  }
-
-  return new Minimatch(pattern, options).match(p)
-}
+/**
+ * Long format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
 
-function Minimatch (pattern, options) {
-  if (!(this instanceof Minimatch)) {
-    return new Minimatch(pattern, options)
+function fmtLong(ms) {
+  var msAbs = Math.abs(ms);
+  if (msAbs >= d) {
+    return plural(ms, msAbs, d, 'day');
   }
-
-  assertValidPattern(pattern)
-
-  if (!options) options = {}
-
-  pattern = pattern.trim()
-
-  // windows support: need to use /, not \
-  if (!options.allowWindowsEscape && path.sep !== '/') {
-    pattern = pattern.split(path.sep).join('/')
+  if (msAbs >= h) {
+    return plural(ms, msAbs, h, 'hour');
   }
-
-  this.options = options
-  this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined
-    ? options.maxGlobstarRecursion : 200
-  this.set = []
-  this.pattern = pattern
-  this.regexp = null
-  this.negate = false
-  this.comment = false
-  this.empty = false
-  this.partial = !!options.partial
-
-  // make the set of regexps etc.
-  this.make()
-}
-
-Minimatch.prototype.debug = function () {}
-
-Minimatch.prototype.make = make
-function make () {
-  var pattern = this.pattern
-  var options = this.options
-
-  // empty patterns and comments match nothing.
-  if (!options.nocomment && pattern.charAt(0) === '#') {
-    this.comment = true
-    return
+  if (msAbs >= m) {
+    return plural(ms, msAbs, m, 'minute');
   }
-  if (!pattern) {
-    this.empty = true
-    return
+  if (msAbs >= s) {
+    return plural(ms, msAbs, s, 'second');
   }
+  return ms + ' ms';
+}
 
-  // step 1: figure out negation, etc.
-  this.parseNegate()
-
-  // step 2: expand braces
-  var set = this.globSet = this.braceExpand()
-
-  if (options.debug) this.debug = function debug() { console.error.apply(console, arguments) }
-
-  this.debug(this.pattern, set)
-
-  // step 3: now we have a set, so turn each one into a series of path-portion
-  // matching patterns.
-  // These will be regexps, except in the case of "**", which is
-  // set to the GLOBSTAR object for globstar behavior,
-  // and will not contain any / characters
-  set = this.globParts = set.map(function (s) {
-    return s.split(slashSplit)
-  })
-
-  this.debug(this.pattern, set)
-
-  // glob --> regexps
-  set = set.map(function (s, si, set) {
-    return s.map(this.parse, this)
-  }, this)
+/**
+ * Pluralization helper.
+ */
 
-  this.debug(this.pattern, set)
+function plural(ms, msAbs, n, name) {
+  var isPlural = msAbs >= n * 1.5;
+  return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
+}
 
-  // filter out everything that didn't compile properly.
-  set = set.filter(function (s) {
-    return s.indexOf(false) === -1
-  })
 
-  this.debug(this.pattern, set)
+/***/ }),
 
-  this.set = set
-}
+/***/ 89379:
+/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
-Minimatch.prototype.parseNegate = parseNegate
-function parseNegate () {
-  var pattern = this.pattern
-  var negate = false
-  var options = this.options
-  var negateOffset = 0
+"use strict";
 
-  if (options.nonegate) return
 
-  for (var i = 0, l = pattern.length
-    ; i < l && pattern.charAt(i) === '!'
-    ; i++) {
-    negate = !negate
-    negateOffset++
+const ANY = Symbol('SemVer ANY')
+// hoisted class for cyclic dependency
+class Comparator {
+  static get ANY () {
+    return ANY
   }
 
-  if (negateOffset) this.pattern = pattern.substr(negateOffset)
-  this.negate = negate
-}
+  constructor (comp, options) {
+    options = parseOptions(options)
 
-// Brace expansion:
-// a{b,c}d -> abd acd
-// a{b,}c -> abc ac
-// a{0..3}d -> a0d a1d a2d a3d
-// a{b,c{d,e}f}g -> abg acdfg acefg
-// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
-//
-// Invalid sets are not expanded.
-// a{2..}b -> a{2..}b
-// a{b}c -> a{b}c
-minimatch.braceExpand = function (pattern, options) {
-  return braceExpand(pattern, options)
-}
+    if (comp instanceof Comparator) {
+      if (comp.loose === !!options.loose) {
+        return comp
+      } else {
+        comp = comp.value
+      }
+    }
 
-Minimatch.prototype.braceExpand = braceExpand
+    comp = comp.trim().split(/\s+/).join(' ')
+    debug('comparator', comp, options)
+    this.options = options
+    this.loose = !!options.loose
+    this.parse(comp)
 
-function braceExpand (pattern, options) {
-  if (!options) {
-    if (this instanceof Minimatch) {
-      options = this.options
+    if (this.semver === ANY) {
+      this.value = ''
     } else {
-      options = {}
+      this.value = this.operator + this.semver.version
     }
-  }
 
-  pattern = typeof pattern === 'undefined'
-    ? this.pattern : pattern
+    debug('comp', this)
+  }
 
-  assertValidPattern(pattern)
+  parse (comp) {
+    const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
+    const m = comp.match(r)
 
-  // Thanks to Yeting Li  for
-  // improving this regexp to avoid a ReDOS vulnerability.
-  if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
-    // shortcut. no need to expand.
-    return [pattern]
-  }
+    if (!m) {
+      throw new TypeError(`Invalid comparator: ${comp}`)
+    }
 
-  return expand(pattern)
-}
+    this.operator = m[1] !== undefined ? m[1] : ''
+    if (this.operator === '=') {
+      this.operator = ''
+    }
 
-var MAX_PATTERN_LENGTH = 1024 * 64
-var assertValidPattern = function (pattern) {
-  if (typeof pattern !== 'string') {
-    throw new TypeError('invalid pattern')
+    // if it literally is just '>' or '' then allow anything.
+    if (!m[2]) {
+      this.semver = ANY
+    } else {
+      this.semver = new SemVer(m[2], this.options.loose)
+    }
   }
 
-  if (pattern.length > MAX_PATTERN_LENGTH) {
-    throw new TypeError('pattern is too long')
+  toString () {
+    return this.value
   }
-}
 
-// parse a component of the expanded set.
-// At this point, no pattern may contain "/" in it
-// so we're going to return a 2d array, where each entry is the full
-// pattern, split on '/', and then turned into a regular expression.
-// A regexp is made at the end which joins each array with an
-// escaped /, and another full one which joins each regexp with |.
-//
-// Following the lead of Bash 4.1, note that "**" only has special meaning
-// when it is the *only* thing in a path portion.  Otherwise, any series
-// of * is equivalent to a single *.  Globstar behavior is enabled by
-// default, and can be disabled by setting options.noglobstar.
-Minimatch.prototype.parse = parse
-var SUBPARSE = {}
-function parse (pattern, isSub) {
-  assertValidPattern(pattern)
+  test (version) {
+    debug('Comparator.test', version, this.options.loose)
 
-  var options = this.options
+    if (this.semver === ANY || version === ANY) {
+      return true
+    }
 
-  // shortcuts
-  if (pattern === '**') {
-    if (!options.noglobstar)
-      return GLOBSTAR
-    else
-      pattern = '*'
-  }
-  if (pattern === '') return ''
-
-  var re = ''
-  var hasMagic = !!options.nocase
-  var escaping = false
-  // ? => one single character
-  var patternListStack = []
-  var negativeLists = []
-  var stateChar
-  var inClass = false
-  var reClassStart = -1
-  var classStart = -1
-  // . and .. never match anything that doesn't start with .,
-  // even when options.dot is set.
-  var patternStart = pattern.charAt(0) === '.' ? '' // anything
-  // not (start or / followed by . or .. followed by / or end)
-  : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
-  : '(?!\\.)'
-  var self = this
-
-  function clearStateChar () {
-    if (stateChar) {
-      // we had some state-tracking character
-      // that wasn't consumed by this pass.
-      switch (stateChar) {
-        case '*':
-          re += star
-          hasMagic = true
-        break
-        case '?':
-          re += qmark
-          hasMagic = true
-        break
-        default:
-          re += '\\' + stateChar
-        break
+    if (typeof version === 'string') {
+      try {
+        version = new SemVer(version, this.options)
+      } catch (er) {
+        return false
       }
-      self.debug('clearStateChar %j %j', stateChar, re)
-      stateChar = false
     }
-  }
 
-  for (var i = 0, len = pattern.length, c
-    ; (i < len) && (c = pattern.charAt(i))
-    ; i++) {
-    this.debug('%s\t%s %s %j', pattern, i, re, c)
+    return cmp(version, this.operator, this.semver, this.options)
+  }
 
-    // skip over any that are escaped.
-    if (escaping && reSpecials[c]) {
-      re += '\\' + c
-      escaping = false
-      continue
+  intersects (comp, options) {
+    if (!(comp instanceof Comparator)) {
+      throw new TypeError('a Comparator is required')
     }
 
-    switch (c) {
-      /* istanbul ignore next */
-      case '/': {
-        // completely not allowed, even escaped.
-        // Should already be path-split by now.
-        return false
-      }
-
-      case '\\':
-        clearStateChar()
-        escaping = true
-      continue
-
-      // the various stateChar values
-      // for the "extglob" stuff.
-      case '?':
-      case '*':
-      case '+':
-      case '@':
-      case '!':
-        this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
-
-        // all of those are literals inside a class, except that
-        // the glob [!a] means [^a] in regexp
-        if (inClass) {
-          this.debug('  in class')
-          if (c === '!' && i === classStart + 1) c = '^'
-          re += c
-          continue
-        }
-
-        // coalesce consecutive non-globstar * characters
-        if (c === '*' && stateChar === '*') continue
-
-        // if we already have a stateChar, then it means
-        // that there was something like ** or +? in there.
-        // Handle the stateChar, then proceed with this one.
-        self.debug('call clearStateChar %j', stateChar)
-        clearStateChar()
-        stateChar = c
-        // if extglob is disabled, then +(asdf|foo) isn't a thing.
-        // just clear the statechar *now*, rather than even diving into
-        // the patternList stuff.
-        if (options.noext) clearStateChar()
-      continue
-
-      case '(':
-        if (inClass) {
-          re += '('
-          continue
-        }
-
-        if (!stateChar) {
-          re += '\\('
-          continue
-        }
-
-        patternListStack.push({
-          type: stateChar,
-          start: i - 1,
-          reStart: re.length,
-          open: plTypes[stateChar].open,
-          close: plTypes[stateChar].close
-        })
-        // negation is (?:(?!js)[^/]*)
-        re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
-        this.debug('plType %j %j', stateChar, re)
-        stateChar = false
-      continue
-
-      case ')':
-        if (inClass || !patternListStack.length) {
-          re += '\\)'
-          continue
-        }
-
-        clearStateChar()
-        hasMagic = true
-        var pl = patternListStack.pop()
-        // negation is (?:(?!js)[^/]*)
-        // The others are (?:)
-        re += pl.close
-        if (pl.type === '!') {
-          negativeLists.push(pl)
-        }
-        pl.reEnd = re.length
-      continue
-
-      case '|':
-        if (inClass || !patternListStack.length || escaping) {
-          re += '\\|'
-          escaping = false
-          continue
-        }
-
-        clearStateChar()
-        re += '|'
-      continue
-
-      // these are mostly the same in regexp and glob
-      case '[':
-        // swallow any state-tracking char before the [
-        clearStateChar()
-
-        if (inClass) {
-          re += '\\' + c
-          continue
-        }
-
-        inClass = true
-        classStart = i
-        reClassStart = re.length
-        re += c
-      continue
-
-      case ']':
-        //  a right bracket shall lose its special
-        //  meaning and represent itself in
-        //  a bracket expression if it occurs
-        //  first in the list.  -- POSIX.2 2.8.3.2
-        if (i === classStart + 1 || !inClass) {
-          re += '\\' + c
-          escaping = false
-          continue
-        }
-
-        // handle the case where we left a class open.
-        // "[z-a]" is valid, equivalent to "\[z-a\]"
-        // split where the last [ was, make sure we don't have
-        // an invalid re. if so, re-walk the contents of the
-        // would-be class to re-translate any characters that
-        // were passed through as-is
-        // TODO: It would probably be faster to determine this
-        // without a try/catch and a new RegExp, but it's tricky
-        // to do safely.  For now, this is safe and works.
-        var cs = pattern.substring(classStart + 1, i)
-        try {
-          RegExp('[' + cs + ']')
-        } catch (er) {
-          // not a valid class!
-          var sp = this.parse(cs, SUBPARSE)
-          re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
-          hasMagic = hasMagic || sp[1]
-          inClass = false
-          continue
-        }
-
-        // finish up the class.
-        hasMagic = true
-        inClass = false
-        re += c
-      continue
-
-      default:
-        // swallow any state char that wasn't consumed
-        clearStateChar()
-
-        if (escaping) {
-          // no need
-          escaping = false
-        } else if (reSpecials[c]
-          && !(c === '^' && inClass)) {
-          re += '\\'
-        }
-
-        re += c
-
-    } // switch
-  } // for
-
-  // handle the case where we left a class open.
-  // "[abc" is valid, equivalent to "\[abc"
-  if (inClass) {
-    // split where the last [ was, and escape it
-    // this is a huge pita.  We now have to re-walk
-    // the contents of the would-be class to re-translate
-    // any characters that were passed through as-is
-    cs = pattern.substr(classStart + 1)
-    sp = this.parse(cs, SUBPARSE)
-    re = re.substr(0, reClassStart) + '\\[' + sp[0]
-    hasMagic = hasMagic || sp[1]
-  }
-
-  // handle the case where we had a +( thing at the *end*
-  // of the pattern.
-  // each pattern list stack adds 3 chars, and we need to go through
-  // and escape any | chars that were passed through as-is for the regexp.
-  // Go through and escape them, taking care not to double-escape any
-  // | chars that were already escaped.
-  for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
-    var tail = re.slice(pl.reStart + pl.open.length)
-    this.debug('setting tail', re, pl)
-    // maybe some even number of \, then maybe 1 \, followed by a |
-    tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) {
-      if (!$2) {
-        // the | isn't already escaped, so escape it.
-        $2 = '\\'
-      }
-
-      // need to escape all those slashes *again*, without escaping the
-      // one that we need for escaping the | character.  As it works out,
-      // escaping an even number of slashes can be done by simply repeating
-      // it exactly after itself.  That's why this trick works.
-      //
-      // I am sorry that you have to see this.
-      return $1 + $1 + $2 + '|'
-    })
-
-    this.debug('tail=%j\n   %s', tail, tail, pl, re)
-    var t = pl.type === '*' ? star
-      : pl.type === '?' ? qmark
-      : '\\' + pl.type
-
-    hasMagic = true
-    re = re.slice(0, pl.reStart) + t + '\\(' + tail
-  }
-
-  // handle trailing things that only matter at the very end.
-  clearStateChar()
-  if (escaping) {
-    // trailing \\
-    re += '\\\\'
-  }
-
-  // only need to apply the nodot start if the re starts with
-  // something that could conceivably capture a dot
-  var addPatternStart = false
-  switch (re.charAt(0)) {
-    case '[': case '.': case '(': addPatternStart = true
-  }
-
-  // Hack to work around lack of negative lookbehind in JS
-  // A pattern like: *.!(x).!(y|z) needs to ensure that a name
-  // like 'a.xyz.yz' doesn't match.  So, the first negative
-  // lookahead, has to look ALL the way ahead, to the end of
-  // the pattern.
-  for (var n = negativeLists.length - 1; n > -1; n--) {
-    var nl = negativeLists[n]
-
-    var nlBefore = re.slice(0, nl.reStart)
-    var nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
-    var nlLast = re.slice(nl.reEnd - 8, nl.reEnd)
-    var nlAfter = re.slice(nl.reEnd)
-
-    nlLast += nlAfter
-
-    // Handle nested stuff like *(*.js|!(*.json)), where open parens
-    // mean that we should *not* include the ) in the bit that is considered
-    // "after" the negated section.
-    var openParensBefore = nlBefore.split('(').length - 1
-    var cleanAfter = nlAfter
-    for (i = 0; i < openParensBefore; i++) {
-      cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
-    }
-    nlAfter = cleanAfter
-
-    var dollar = ''
-    if (nlAfter === '' && isSub !== SUBPARSE) {
-      dollar = '$'
-    }
-    var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast
-    re = newRe
-  }
-
-  // if the re is not "" at this point, then we need to make sure
-  // it doesn't match against an empty path part.
-  // Otherwise a/* will match a/, which it should not.
-  if (re !== '' && hasMagic) {
-    re = '(?=.)' + re
-  }
-
-  if (addPatternStart) {
-    re = patternStart + re
-  }
-
-  // parsing just a piece of a larger pattern.
-  if (isSub === SUBPARSE) {
-    return [re, hasMagic]
-  }
-
-  // skip the regexp for non-magical patterns
-  // unescape anything in it, though, so that it'll be
-  // an exact match against a file etc.
-  if (!hasMagic) {
-    return globUnescape(pattern)
-  }
-
-  var flags = options.nocase ? 'i' : ''
-  try {
-    var regExp = new RegExp('^' + re + '$', flags)
-  } catch (er) /* istanbul ignore next - should be impossible */ {
-    // If it was an invalid regular expression, then it can't match
-    // anything.  This trick looks for a character after the end of
-    // the string, which is of course impossible, except in multi-line
-    // mode, but it's not a /m regex.
-    return new RegExp('$.')
-  }
-
-  regExp._glob = pattern
-  regExp._src = re
-
-  return regExp
-}
-
-minimatch.makeRe = function (pattern, options) {
-  return new Minimatch(pattern, options || {}).makeRe()
-}
-
-Minimatch.prototype.makeRe = makeRe
-function makeRe () {
-  if (this.regexp || this.regexp === false) return this.regexp
-
-  // at this point, this.set is a 2d array of partial
-  // pattern strings, or "**".
-  //
-  // It's better to use .match().  This function shouldn't
-  // be used, really, but it's pretty convenient sometimes,
-  // when you just want to work with a regex.
-  var set = this.set
-
-  if (!set.length) {
-    this.regexp = false
-    return this.regexp
-  }
-  var options = this.options
-
-  var twoStar = options.noglobstar ? star
-    : options.dot ? twoStarDot
-    : twoStarNoDot
-  var flags = options.nocase ? 'i' : ''
-
-  var re = set.map(function (pattern) {
-    return pattern.map(function (p) {
-      return (p === GLOBSTAR) ? twoStar
-      : (typeof p === 'string') ? regExpEscape(p)
-      : p._src
-    }).join('\\\/')
-  }).join('|')
-
-  // must match entire pattern
-  // ending in a * or ** will make it less strict.
-  re = '^(?:' + re + ')$'
-
-  // can match anything, as long as it's not this.
-  if (this.negate) re = '^(?!' + re + ').*$'
-
-  try {
-    this.regexp = new RegExp(re, flags)
-  } catch (ex) /* istanbul ignore next - should be impossible */ {
-    this.regexp = false
-  }
-  return this.regexp
-}
-
-minimatch.match = function (list, pattern, options) {
-  options = options || {}
-  var mm = new Minimatch(pattern, options)
-  list = list.filter(function (f) {
-    return mm.match(f)
-  })
-  if (mm.options.nonull && !list.length) {
-    list.push(pattern)
-  }
-  return list
-}
-
-Minimatch.prototype.match = function match (f, partial) {
-  if (typeof partial === 'undefined') partial = this.partial
-  this.debug('match', f, this.pattern)
-  // short-circuit in the case of busted things.
-  // comments, etc.
-  if (this.comment) return false
-  if (this.empty) return f === ''
-
-  if (f === '/' && partial) return true
-
-  var options = this.options
-
-  // windows: need to use /, not \
-  if (path.sep !== '/') {
-    f = f.split(path.sep).join('/')
-  }
-
-  // treat the test path as a set of pathparts.
-  f = f.split(slashSplit)
-  this.debug(this.pattern, 'split', f)
-
-  // just ONE of the pattern sets in this.set needs to match
-  // in order for it to be valid.  If negating, then just one
-  // match means that we have failed.
-  // Either way, return on the first hit.
-
-  var set = this.set
-  this.debug(this.pattern, 'set', set)
-
-  // Find the basename of the path by looking for the last non-empty segment
-  var filename
-  var i
-  for (i = f.length - 1; i >= 0; i--) {
-    filename = f[i]
-    if (filename) break
-  }
-
-  for (i = 0; i < set.length; i++) {
-    var pattern = set[i]
-    var file = f
-    if (options.matchBase && pattern.length === 1) {
-      file = [filename]
-    }
-    var hit = this.matchOne(file, pattern, partial)
-    if (hit) {
-      if (options.flipNegate) return true
-      return !this.negate
-    }
-  }
-
-  // didn't get any hits.  this is success if it's a negative
-  // pattern, failure otherwise.
-  if (options.flipNegate) return false
-  return this.negate
-}
-
-// set partial to true to test if, for example,
-// "/a/b" matches the start of "/*/b/*/d"
-// Partial means, if you run out of file before you run
-// out of pattern, then that's fine, as long as all
-// the parts match.
-Minimatch.prototype.matchOne = function (file, pattern, partial) {
-  if (pattern.indexOf(GLOBSTAR) !== -1) {
-    return this._matchGlobstar(file, pattern, partial, 0, 0)
-  }
-  return this._matchOne(file, pattern, partial, 0, 0)
-}
-
-Minimatch.prototype._matchGlobstar = function (file, pattern, partial, fileIndex, patternIndex) {
-  var i
-
-  // find first globstar from patternIndex
-  var firstgs = -1
-  for (i = patternIndex; i < pattern.length; i++) {
-    if (pattern[i] === GLOBSTAR) { firstgs = i; break }
-  }
-
-  // find last globstar
-  var lastgs = -1
-  for (i = pattern.length - 1; i >= 0; i--) {
-    if (pattern[i] === GLOBSTAR) { lastgs = i; break }
-  }
-
-  var head = pattern.slice(patternIndex, firstgs)
-  var body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs)
-  var tail = partial ? [] : pattern.slice(lastgs + 1)
-
-  // check the head
-  if (head.length) {
-    var fileHead = file.slice(fileIndex, fileIndex + head.length)
-    if (!this._matchOne(fileHead, head, partial, 0, 0)) {
-      return false
-    }
-    fileIndex += head.length
-  }
-
-  // check the tail
-  var fileTailMatch = 0
-  if (tail.length) {
-    if (tail.length + fileIndex > file.length) return false
-
-    var tailStart = file.length - tail.length
-    if (this._matchOne(file, tail, partial, tailStart, 0)) {
-      fileTailMatch = tail.length
-    } else {
-      // affordance for stuff like a/**/* matching a/b/
-      if (file[file.length - 1] !== '' ||
-          fileIndex + tail.length === file.length) {
-        return false
-      }
-      tailStart--
-      if (!this._matchOne(file, tail, partial, tailStart, 0)) {
-        return false
-      }
-      fileTailMatch = tail.length + 1
-    }
-  }
-
-  // if body is empty (single ** between head and tail)
-  if (!body.length) {
-    var sawSome = !!fileTailMatch
-    for (i = fileIndex; i < file.length - fileTailMatch; i++) {
-      var f = String(file[i])
-      sawSome = true
-      if (f === '.' || f === '..' ||
-          (!this.options.dot && f.charAt(0) === '.')) {
-        return false
-      }
-    }
-    return partial || sawSome
-  }
-
-  // split body into segments at each GLOBSTAR
-  var bodySegments = [[[], 0]]
-  var currentBody = bodySegments[0]
-  var nonGsParts = 0
-  var nonGsPartsSums = [0]
-  for (var bi = 0; bi < body.length; bi++) {
-    var b = body[bi]
-    if (b === GLOBSTAR) {
-      nonGsPartsSums.push(nonGsParts)
-      currentBody = [[], 0]
-      bodySegments.push(currentBody)
-    } else {
-      currentBody[0].push(b)
-      nonGsParts++
-    }
-  }
-
-  var idx = bodySegments.length - 1
-  var fileLength = file.length - fileTailMatch
-  for (var si = 0; si < bodySegments.length; si++) {
-    bodySegments[si][1] = fileLength -
-      (nonGsPartsSums[idx--] + bodySegments[si][0].length)
-  }
-
-  return !!this._matchGlobStarBodySections(
-    file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch
-  )
-}
-
-// return false for "nope, not matching"
-// return null for "not matching, cannot keep trying"
-Minimatch.prototype._matchGlobStarBodySections = function (
-  file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail
-) {
-  var bs = bodySegments[bodyIndex]
-  if (!bs) {
-    // just make sure there are no bad dots
-    for (var i = fileIndex; i < file.length; i++) {
-      sawTail = true
-      var f = file[i]
-      if (f === '.' || f === '..' ||
-          (!this.options.dot && f.charAt(0) === '.')) {
-        return false
-      }
-    }
-    return sawTail
-  }
-
-  var body = bs[0]
-  var after = bs[1]
-  while (fileIndex <= after) {
-    var m = this._matchOne(
-      file.slice(0, fileIndex + body.length),
-      body,
-      partial,
-      fileIndex,
-      0
-    )
-    // if limit exceeded, no match. intentional false negative,
-    // acceptable break in correctness for security.
-    if (m && globStarDepth < this.maxGlobstarRecursion) {
-      var sub = this._matchGlobStarBodySections(
-        file, bodySegments,
-        fileIndex + body.length, bodyIndex + 1,
-        partial, globStarDepth + 1, sawTail
-      )
-      if (sub !== false) {
-        return sub
-      }
-    }
-    var f = file[fileIndex]
-    if (f === '.' || f === '..' ||
-        (!this.options.dot && f.charAt(0) === '.')) {
-      return false
-    }
-    fileIndex++
-  }
-  return partial || null
-}
-
-Minimatch.prototype._matchOne = function (file, pattern, partial, fileIndex, patternIndex) {
-  var fi, pi, fl, pl
-  for (
-    fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length
-    ; (fi < fl) && (pi < pl)
-    ; fi++, pi++
-  ) {
-    this.debug('matchOne loop')
-    var p = pattern[pi]
-    var f = file[fi]
-
-    this.debug(pattern, p, f)
-
-    // should be impossible.
-    // some invalid regexp stuff in the set.
-    /* istanbul ignore if */
-    if (p === false || p === GLOBSTAR) return false
-
-    // something other than **
-    // non-magic patterns just have to match exactly
-    // patterns with magic have been turned into regexps.
-    var hit
-    if (typeof p === 'string') {
-      hit = f === p
-      this.debug('string match', p, f, hit)
-    } else {
-      hit = f.match(p)
-      this.debug('pattern match', p, f, hit)
-    }
-
-    if (!hit) return false
-  }
-
-  // now either we fell off the end of the pattern, or we're done.
-  if (fi === fl && pi === pl) {
-    // ran out of pattern and filename at the same time.
-    // an exact hit!
-    return true
-  } else if (fi === fl) {
-    // ran out of file, but still had pattern left.
-    // this is ok if we're doing the match as part of
-    // a glob fs traversal.
-    return partial
-  } else /* istanbul ignore else */ if (pi === pl) {
-    // ran out of pattern, still have file left.
-    // this is only acceptable if we're on the very last
-    // empty segment of a file with a trailing slash.
-    // a/* should match a/b/
-    return (fi === fl - 1) && (file[fi] === '')
-  }
-
-  // should be unreachable.
-  /* istanbul ignore next */
-  throw new Error('wtf?')
-}
-
-// replace stuff like \* with *
-function globUnescape (s) {
-  return s.replace(/\\(.)/g, '$1')
-}
-
-function regExpEscape (s) {
-  return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
-}
-
-
-/***/ }),
-
-/***/ 70744:
-/***/ ((module) => {
-
-/**
- * Helpers.
- */
-
-var s = 1000;
-var m = s * 60;
-var h = m * 60;
-var d = h * 24;
-var w = d * 7;
-var y = d * 365.25;
-
-/**
- * Parse or format the given `val`.
- *
- * Options:
- *
- *  - `long` verbose formatting [false]
- *
- * @param {String|Number} val
- * @param {Object} [options]
- * @throws {Error} throw an error if val is not a non-empty string or a number
- * @return {String|Number}
- * @api public
- */
-
-module.exports = function (val, options) {
-  options = options || {};
-  var type = typeof val;
-  if (type === 'string' && val.length > 0) {
-    return parse(val);
-  } else if (type === 'number' && isFinite(val)) {
-    return options.long ? fmtLong(val) : fmtShort(val);
-  }
-  throw new Error(
-    'val is not a non-empty string or a valid number. val=' +
-      JSON.stringify(val)
-  );
-};
-
-/**
- * Parse the given `str` and return milliseconds.
- *
- * @param {String} str
- * @return {Number}
- * @api private
- */
-
-function parse(str) {
-  str = String(str);
-  if (str.length > 100) {
-    return;
-  }
-  var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
-    str
-  );
-  if (!match) {
-    return;
-  }
-  var n = parseFloat(match[1]);
-  var type = (match[2] || 'ms').toLowerCase();
-  switch (type) {
-    case 'years':
-    case 'year':
-    case 'yrs':
-    case 'yr':
-    case 'y':
-      return n * y;
-    case 'weeks':
-    case 'week':
-    case 'w':
-      return n * w;
-    case 'days':
-    case 'day':
-    case 'd':
-      return n * d;
-    case 'hours':
-    case 'hour':
-    case 'hrs':
-    case 'hr':
-    case 'h':
-      return n * h;
-    case 'minutes':
-    case 'minute':
-    case 'mins':
-    case 'min':
-    case 'm':
-      return n * m;
-    case 'seconds':
-    case 'second':
-    case 'secs':
-    case 'sec':
-    case 's':
-      return n * s;
-    case 'milliseconds':
-    case 'millisecond':
-    case 'msecs':
-    case 'msec':
-    case 'ms':
-      return n;
-    default:
-      return undefined;
-  }
-}
-
-/**
- * Short format for `ms`.
- *
- * @param {Number} ms
- * @return {String}
- * @api private
- */
-
-function fmtShort(ms) {
-  var msAbs = Math.abs(ms);
-  if (msAbs >= d) {
-    return Math.round(ms / d) + 'd';
-  }
-  if (msAbs >= h) {
-    return Math.round(ms / h) + 'h';
-  }
-  if (msAbs >= m) {
-    return Math.round(ms / m) + 'm';
-  }
-  if (msAbs >= s) {
-    return Math.round(ms / s) + 's';
-  }
-  return ms + 'ms';
-}
-
-/**
- * Long format for `ms`.
- *
- * @param {Number} ms
- * @return {String}
- * @api private
- */
-
-function fmtLong(ms) {
-  var msAbs = Math.abs(ms);
-  if (msAbs >= d) {
-    return plural(ms, msAbs, d, 'day');
-  }
-  if (msAbs >= h) {
-    return plural(ms, msAbs, h, 'hour');
-  }
-  if (msAbs >= m) {
-    return plural(ms, msAbs, m, 'minute');
-  }
-  if (msAbs >= s) {
-    return plural(ms, msAbs, s, 'second');
-  }
-  return ms + ' ms';
-}
-
-/**
- * Pluralization helper.
- */
-
-function plural(ms, msAbs, n, name) {
-  var isPlural = msAbs >= n * 1.5;
-  return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
-}
-
-
-/***/ }),
-
-/***/ 89379:
-/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
-
-"use strict";
-
-
-const ANY = Symbol('SemVer ANY')
-// hoisted class for cyclic dependency
-class Comparator {
-  static get ANY () {
-    return ANY
-  }
-
-  constructor (comp, options) {
-    options = parseOptions(options)
-
-    if (comp instanceof Comparator) {
-      if (comp.loose === !!options.loose) {
-        return comp
-      } else {
-        comp = comp.value
-      }
-    }
-
-    comp = comp.trim().split(/\s+/).join(' ')
-    debug('comparator', comp, options)
-    this.options = options
-    this.loose = !!options.loose
-    this.parse(comp)
-
-    if (this.semver === ANY) {
-      this.value = ''
-    } else {
-      this.value = this.operator + this.semver.version
-    }
-
-    debug('comp', this)
-  }
-
-  parse (comp) {
-    const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
-    const m = comp.match(r)
-
-    if (!m) {
-      throw new TypeError(`Invalid comparator: ${comp}`)
-    }
-
-    this.operator = m[1] !== undefined ? m[1] : ''
-    if (this.operator === '=') {
-      this.operator = ''
-    }
-
-    // if it literally is just '>' or '' then allow anything.
-    if (!m[2]) {
-      this.semver = ANY
-    } else {
-      this.semver = new SemVer(m[2], this.options.loose)
-    }
-  }
-
-  toString () {
-    return this.value
-  }
-
-  test (version) {
-    debug('Comparator.test', version, this.options.loose)
-
-    if (this.semver === ANY || version === ANY) {
-      return true
-    }
-
-    if (typeof version === 'string') {
-      try {
-        version = new SemVer(version, this.options)
-      } catch (er) {
-        return false
-      }
-    }
-
-    return cmp(version, this.operator, this.semver, this.options)
-  }
-
-  intersects (comp, options) {
-    if (!(comp instanceof Comparator)) {
-      throw new TypeError('a Comparator is required')
-    }
-
-    if (this.operator === '') {
-      if (this.value === '') {
-        return true
+    if (this.operator === '') {
+      if (this.value === '') {
+        return true
       }
       return new Range(comp.value, options).test(this.value)
     } else if (comp.operator === '') {
@@ -123778,6 +122439,2588 @@ function randomUUID() {
 
 /***/ }),
 
+/***/ 62649:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.range = exports.balanced = void 0;
+const balanced = (a, b, str) => {
+    const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
+    const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
+    const r = ma !== null && mb != null && (0, exports.range)(ma, mb, str);
+    return (r && {
+        start: r[0],
+        end: r[1],
+        pre: str.slice(0, r[0]),
+        body: str.slice(r[0] + ma.length, r[1]),
+        post: str.slice(r[1] + mb.length),
+    });
+};
+exports.balanced = balanced;
+const maybeMatch = (reg, str) => {
+    const m = str.match(reg);
+    return m ? m[0] : null;
+};
+const range = (a, b, str) => {
+    let begs, beg, left, right = undefined, result;
+    let ai = str.indexOf(a);
+    let bi = str.indexOf(b, ai + 1);
+    let i = ai;
+    if (ai >= 0 && bi > 0) {
+        if (a === b) {
+            return [ai, bi];
+        }
+        begs = [];
+        left = str.length;
+        while (i >= 0 && !result) {
+            if (i === ai) {
+                begs.push(i);
+                ai = str.indexOf(a, i + 1);
+            }
+            else if (begs.length === 1) {
+                const r = begs.pop();
+                if (r !== undefined)
+                    result = [r, bi];
+            }
+            else {
+                beg = begs.pop();
+                if (beg !== undefined && beg < left) {
+                    left = beg;
+                    right = bi;
+                }
+                bi = str.indexOf(b, i + 1);
+            }
+            i = ai < bi && ai >= 0 ? ai : bi;
+        }
+        if (begs.length && right !== undefined) {
+            result = [left, right];
+        }
+    }
+    return result;
+};
+exports.range = range;
+//# sourceMappingURL=index.js.map
+
+/***/ }),
+
+/***/ 38968:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.EXPANSION_MAX_LENGTH = exports.EXPANSION_MAX = void 0;
+exports.expand = expand;
+const balanced_match_1 = __nccwpck_require__(62649);
+const escSlash = '\0SLASH' + Math.random() + '\0';
+const escOpen = '\0OPEN' + Math.random() + '\0';
+const escClose = '\0CLOSE' + Math.random() + '\0';
+const escComma = '\0COMMA' + Math.random() + '\0';
+const escPeriod = '\0PERIOD' + Math.random() + '\0';
+const escSlashPattern = new RegExp(escSlash, 'g');
+const escOpenPattern = new RegExp(escOpen, 'g');
+const escClosePattern = new RegExp(escClose, 'g');
+const escCommaPattern = new RegExp(escComma, 'g');
+const escPeriodPattern = new RegExp(escPeriod, 'g');
+const slashPattern = /\\\\/g;
+const openPattern = /\\{/g;
+const closePattern = /\\}/g;
+const commaPattern = /\\,/g;
+const periodPattern = /\\\./g;
+exports.EXPANSION_MAX = 100_000;
+// `EXPANSION_MAX` caps the *number* of expansions, but not their length. An
+// input like `'{a,b}'.repeat(1500)` stays under that count - its output is
+// truncated to 100k results - while making every result ~1500 characters
+// long. The result set, and the intermediate arrays built while combining
+// brace sets, then grow large enough to exhaust memory and crash the process
+// (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of
+// characters the accumulator may hold at any point, so memory stays flat no
+// matter how many brace groups are chained. The limit sits well above any
+// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
+// characters) so legitimate input is unaffected.
+exports.EXPANSION_MAX_LENGTH = 4_000_000;
+function numeric(str) {
+    return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
+}
+function escapeBraces(str) {
+    return str
+        .replace(slashPattern, escSlash)
+        .replace(openPattern, escOpen)
+        .replace(closePattern, escClose)
+        .replace(commaPattern, escComma)
+        .replace(periodPattern, escPeriod);
+}
+function unescapeBraces(str) {
+    return str
+        .replace(escSlashPattern, '\\')
+        .replace(escOpenPattern, '{')
+        .replace(escClosePattern, '}')
+        .replace(escCommaPattern, ',')
+        .replace(escPeriodPattern, '.');
+}
+/**
+ * Basically just str.split(","), but handling cases
+ * where we have nested braced sections, which should be
+ * treated as individual members, like {a,{b,c},d}
+ */
+function parseCommaParts(str) {
+    if (!str) {
+        return [''];
+    }
+    const parts = [];
+    const m = (0, balanced_match_1.balanced)('{', '}', str);
+    if (!m) {
+        return str.split(',');
+    }
+    const { pre, body, post } = m;
+    const p = pre.split(',');
+    p[p.length - 1] += '{' + body + '}';
+    const postParts = parseCommaParts(post);
+    if (post.length) {
+        ;
+        p[p.length - 1] += postParts.shift();
+        p.push.apply(p, postParts);
+    }
+    parts.push.apply(parts, p);
+    return parts;
+}
+function expand(str, options = {}) {
+    if (!str) {
+        return [];
+    }
+    const { max = exports.EXPANSION_MAX, maxLength = exports.EXPANSION_MAX_LENGTH } = options;
+    // I don't know why Bash 4.3 does this, but it does.
+    // Anything starting with {} will have the first two bytes preserved
+    // but *only* at the top level, so {},a}b will not expand to anything,
+    // but a{},b}c will be expanded to [a}c,abc].
+    // One could argue that this is a bug in Bash, but since the goal of
+    // this module is to match Bash's rules, we escape a leading {}
+    if (str.slice(0, 2) === '{}') {
+        str = '\\{\\}' + str.slice(2);
+    }
+    return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
+}
+function embrace(str) {
+    return '{' + str + '}';
+}
+function isPadded(el) {
+    return /^-?0\d/.test(el);
+}
+function lte(i, y) {
+    return i <= y;
+}
+function gte(i, y) {
+    return i >= y;
+}
+// Build `{ acc[a] + pre + values[v] }` for every combination, capping the
+// number of results at `max` and the total number of characters at `maxLength`.
+// This is the one place output grows, so bounding it here keeps the single
+// accumulator - and therefore memory - flat regardless of how many brace groups
+// are combined (CVE-2026-14257).
+function combine(acc, pre, values, max, maxLength, dropEmpties) {
+    const out = [];
+    let length = 0;
+    for (let a = 0; a < acc.length; a++) {
+        for (let v = 0; v < values.length; v++) {
+            if (out.length >= max)
+                return out;
+            const expansion = acc[a] + pre + values[v];
+            // Bash drops empty results at the top level. Skip them before they count
+            // against `max`, so `max` bounds the number of *kept* results.
+            if (dropEmpties && !expansion)
+                continue;
+            if (length + expansion.length > maxLength)
+                return out;
+            out.push(expansion);
+            length += expansion.length;
+        }
+    }
+    return out;
+}
+// The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
+// sequence body.
+function expandSequence(body, isAlphaSequence, max) {
+    const n = body.split(/\.\./);
+    const N = [];
+    // A sequence body always splits into two or three parts, but the compiler
+    // can't know that.
+    /* c8 ignore start */
+    if (n[0] === undefined || n[1] === undefined) {
+        return N;
+    }
+    /* c8 ignore stop */
+    const x = numeric(n[0]);
+    const y = numeric(n[1]);
+    const width = Math.max(n[0].length, n[1].length);
+    let incr = n.length === 3 && n[2] !== undefined ?
+        Math.max(Math.abs(numeric(n[2])), 1)
+        : 1;
+    let test = lte;
+    const reverse = y < x;
+    if (reverse) {
+        incr *= -1;
+        test = gte;
+    }
+    const pad = n.some(isPadded);
+    for (let i = x; test(i, y) && N.length < max; i += incr) {
+        let c;
+        if (isAlphaSequence) {
+            c = String.fromCharCode(i);
+            if (c === '\\') {
+                c = '';
+            }
+        }
+        else {
+            c = String(i);
+            if (pad) {
+                const need = width - c.length;
+                if (need > 0) {
+                    const z = new Array(need + 1).join('0');
+                    if (i < 0) {
+                        c = '-' + z + c.slice(1);
+                    }
+                    else {
+                        c = z + c;
+                    }
+                }
+            }
+        }
+        N.push(c);
+    }
+    return N;
+}
+function expand_(str, max, maxLength, isTop) {
+    // Consume the string's top-level brace groups left to right, threading a
+    // running set of combined prefixes (`acc`). Expanding the tail iteratively -
+    // rather than recursing on `m.post` once per group - keeps the native stack
+    // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
+    // longer overflow the stack, and leaves a single accumulator whose size
+    // `maxLength` bounds directly (CVE-2026-14257).
+    let acc = [''];
+    // Bash drops empty results, but only when the *first* top-level group is a
+    // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
+    // is on the final strings, so it is applied to whichever `combine` produces
+    // them (the one with no brace set left in the tail).
+    let dropEmpties = false;
+    let firstGroup = true;
+    for (;;) {
+        const m = (0, balanced_match_1.balanced)('{', '}', str);
+        // No brace set left: the rest of the string is literal.
+        if (!m) {
+            return combine(acc, str, [''], max, maxLength, dropEmpties);
+        }
+        // no need to expand pre, since it is guaranteed to be free of brace-sets
+        const pre = m.pre;
+        if (/\$$/.test(pre)) {
+            acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
+            firstGroup = false;
+            if (!m.post.length)
+                break;
+            str = m.post;
+            continue;
+        }
+        const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
+        const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
+        const isSequence = isNumericSequence || isAlphaSequence;
+        const isOptions = m.body.indexOf(',') >= 0;
+        if (!isSequence && !isOptions) {
+            // {a},b}
+            if (m.post.match(/,(?!,).*\}/)) {
+                str = m.pre + '{' + m.body + escClose + m.post;
+                isTop = true;
+                continue;
+            }
+            // Nothing here expands, so the whole remaining string is literal.
+            return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
+        }
+        if (firstGroup) {
+            dropEmpties = isTop && !isSequence;
+            firstGroup = false;
+        }
+        let values;
+        if (isSequence) {
+            values = expandSequence(m.body, isAlphaSequence, max);
+        }
+        else {
+            let n = parseCommaParts(m.body);
+            if (n.length === 1 && n[0] !== undefined) {
+                // x{{a,b}}y ==> x{a}y x{b}y
+                n = expand_(n[0], max, maxLength, false).map(embrace);
+                //XXX is this necessary? Can't seem to hit it in tests.
+                /* c8 ignore start */
+                if (n.length === 1) {
+                    acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
+                    if (!m.post.length)
+                        break;
+                    str = m.post;
+                    continue;
+                }
+                /* c8 ignore stop */
+            }
+            values = [];
+            for (let j = 0; j < n.length; j++) {
+                values.push.apply(values, expand_(n[j], max, maxLength, false));
+            }
+        }
+        acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
+        if (!m.post.length)
+            break;
+        str = m.post;
+    }
+    return acc;
+}
+//# sourceMappingURL=index.js.map
+
+/***/ }),
+
+/***/ 57305:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.assertValidPattern = void 0;
+const MAX_PATTERN_LENGTH = 1024 * 64;
+const assertValidPattern = (pattern) => {
+    if (typeof pattern !== 'string') {
+        throw new TypeError('invalid pattern');
+    }
+    if (pattern.length > MAX_PATTERN_LENGTH) {
+        throw new TypeError('pattern is too long');
+    }
+};
+exports.assertValidPattern = assertValidPattern;
+//# sourceMappingURL=assert-valid-pattern.js.map
+
+/***/ }),
+
+/***/ 31803:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+// parse a single path portion
+var _a;
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.AST = void 0;
+const brace_expressions_js_1 = __nccwpck_require__(11090);
+const unescape_js_1 = __nccwpck_require__(70851);
+const types = new Set(['!', '?', '+', '*', '@']);
+const isExtglobType = (c) => types.has(c);
+const isExtglobAST = (c) => isExtglobType(c.type);
+// Map of which extglob types can adopt the children of a nested extglob
+//
+// anything but ! can adopt a matching type:
+// +(a|+(b|c)|d) => +(a|b|c|d)
+// *(a|*(b|c)|d) => *(a|b|c|d)
+// @(a|@(b|c)|d) => @(a|b|c|d)
+// ?(a|?(b|c)|d) => ?(a|b|c|d)
+//
+// * can adopt anything, because 0 or repetition is allowed
+// *(a|?(b|c)|d) => *(a|b|c|d)
+// *(a|+(b|c)|d) => *(a|b|c|d)
+// *(a|@(b|c)|d) => *(a|b|c|d)
+//
+// + can adopt @, because 1 or repetition is allowed
+// +(a|@(b|c)|d) => +(a|b|c|d)
+//
+// + and @ CANNOT adopt *, because 0 would be allowed
+// +(a|*(b|c)|d) => would match "", on *(b|c)
+// @(a|*(b|c)|d) => would match "", on *(b|c)
+//
+// + and @ CANNOT adopt ?, because 0 would be allowed
+// +(a|?(b|c)|d) => would match "", on ?(b|c)
+// @(a|?(b|c)|d) => would match "", on ?(b|c)
+//
+// ? can adopt @, because 0 or 1 is allowed
+// ?(a|@(b|c)|d) => ?(a|b|c|d)
+//
+// ? and @ CANNOT adopt * or +, because >1 would be allowed
+// ?(a|*(b|c)|d) => would match bbb on *(b|c)
+// @(a|*(b|c)|d) => would match bbb on *(b|c)
+// ?(a|+(b|c)|d) => would match bbb on +(b|c)
+// @(a|+(b|c)|d) => would match bbb on +(b|c)
+//
+// ! CANNOT adopt ! (nothing else can either)
+// !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c)
+//
+// ! can adopt @
+// !(a|@(b|c)|d) => !(a|b|c|d)
+//
+// ! CANNOT adopt *
+// !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
+//
+// ! CANNOT adopt +
+// !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
+//
+// ! CANNOT adopt ?
+// x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x"
+const adoptionMap = new Map([
+    ['!', ['@']],
+    ['?', ['?', '@']],
+    ['@', ['@']],
+    ['*', ['*', '+', '?', '@']],
+    ['+', ['+', '@']],
+]);
+// nested extglobs that can be adopted in, but with the addition of
+// a blank '' element.
+const adoptionWithSpaceMap = new Map([
+    ['!', ['?']],
+    ['@', ['?']],
+    ['+', ['?', '*']],
+]);
+// union of the previous two maps
+const adoptionAnyMap = new Map([
+    ['!', ['?', '@']],
+    ['?', ['?', '@']],
+    ['@', ['?', '@']],
+    ['*', ['*', '+', '?', '@']],
+    ['+', ['+', '@', '?', '*']],
+]);
+// Extglobs that can take over their parent if they are the only child
+// the key is parent, value maps child to resulting extglob parent type
+// '@' is omitted because it's a special case. An `@` extglob with a single
+// member can always be usurped by that subpattern.
+const usurpMap = new Map([
+    ['!', new Map([['!', '@']])],
+    [
+        '?',
+        new Map([
+            ['*', '*'],
+            ['+', '*'],
+        ]),
+    ],
+    [
+        '@',
+        new Map([
+            ['!', '!'],
+            ['?', '?'],
+            ['@', '@'],
+            ['*', '*'],
+            ['+', '+'],
+        ]),
+    ],
+    [
+        '+',
+        new Map([
+            ['?', '*'],
+            ['*', '*'],
+        ]),
+    ],
+]);
+// Patterns that get prepended to bind to the start of either the
+// entire string, or just a single path portion, to prevent dots
+// and/or traversal patterns, when needed.
+// Exts don't need the ^ or / bit, because the root binds that already.
+const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';
+const startNoDot = '(?!\\.)';
+// characters that indicate a start of pattern needs the "no dots" bit,
+// because a dot *might* be matched. ( is not in the list, because in
+// the case of a child extglob, it will handle the prevention itself.
+const addPatternStart = new Set(['[', '.']);
+// cases where traversal is A-OK, no dot prevention needed
+const justDots = new Set(['..', '.']);
+const reSpecials = new Set('().*{}+?[]^$\\!');
+const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
+// any single thing other than /
+const qmark = '[^/]';
+// * => any number of characters
+const star = qmark + '*?';
+// use + when we need to ensure that *something* matches, because the * is
+// the only thing in the path portion.
+const starNoEmpty = qmark + '+?';
+// remove the \ chars that we added if we end up doing a nonmagic compare
+// const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
+let ID = 0;
+class AST {
+    type;
+    #root;
+    #hasMagic;
+    #uflag = false;
+    #parts = [];
+    #parent;
+    #parentIndex;
+    #negs;
+    #filledNegs = false;
+    #options;
+    #toString;
+    // set to true if it's an extglob with no children
+    // (which really means one child of '')
+    #emptyExt = false;
+    id = ++ID;
+    get depth() {
+        return (this.#parent?.depth ?? -1) + 1;
+    }
+    [Symbol.for('nodejs.util.inspect.custom')]() {
+        return {
+            '@@type': 'AST',
+            id: this.id,
+            type: this.type,
+            root: this.#root.id,
+            parent: this.#parent?.id,
+            depth: this.depth,
+            partsLength: this.#parts.length,
+            parts: this.#parts,
+        };
+    }
+    constructor(type, parent, options = {}) {
+        this.type = type;
+        // extglobs are inherently magical
+        if (type)
+            this.#hasMagic = true;
+        this.#parent = parent;
+        this.#root = this.#parent ? this.#parent.#root : this;
+        this.#options = this.#root === this ? options : this.#root.#options;
+        this.#negs = this.#root === this ? [] : this.#root.#negs;
+        if (type === '!' && !this.#root.#filledNegs)
+            this.#negs.push(this);
+        this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
+    }
+    get hasMagic() {
+        /* c8 ignore start */
+        if (this.#hasMagic !== undefined)
+            return this.#hasMagic;
+        /* c8 ignore stop */
+        for (const p of this.#parts) {
+            if (typeof p === 'string')
+                continue;
+            if (p.type || p.hasMagic)
+                return (this.#hasMagic = true);
+        }
+        // note: will be undefined until we generate the regexp src and find out
+        return this.#hasMagic;
+    }
+    // reconstructs the pattern
+    toString() {
+        return (this.#toString !== undefined ? this.#toString
+            : !this.type ?
+                (this.#toString = this.#parts.map(p => String(p)).join(''))
+                : (this.#toString =
+                    this.type +
+                        '(' +
+                        this.#parts.map(p => String(p)).join('|') +
+                        ')'));
+    }
+    #fillNegs() {
+        /* c8 ignore start */
+        if (this !== this.#root)
+            throw new Error('should only call on root');
+        if (this.#filledNegs)
+            return this;
+        /* c8 ignore stop */
+        // call toString() once to fill this out
+        this.toString();
+        this.#filledNegs = true;
+        let n;
+        while ((n = this.#negs.pop())) {
+            if (n.type !== '!')
+                continue;
+            // walk up the tree, appending everthing that comes AFTER parentIndex
+            let p = n;
+            let pp = p.#parent;
+            while (pp) {
+                for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
+                    for (const part of n.#parts) {
+                        /* c8 ignore start */
+                        if (typeof part === 'string') {
+                            throw new Error('string part in extglob AST??');
+                        }
+                        /* c8 ignore stop */
+                        part.copyIn(pp.#parts[i]);
+                    }
+                }
+                p = pp;
+                pp = p.#parent;
+            }
+        }
+        return this;
+    }
+    push(...parts) {
+        for (const p of parts) {
+            if (p === '')
+                continue;
+            /* c8 ignore start */
+            if (typeof p !== 'string' &&
+                !(p instanceof _a && p.#parent === this)) {
+                throw new Error('invalid part: ' + p);
+            }
+            /* c8 ignore stop */
+            this.#parts.push(p);
+        }
+    }
+    toJSON() {
+        const ret = this.type === null ?
+            this.#parts
+                .slice()
+                .map(p => (typeof p === 'string' ? p : p.toJSON()))
+            : [this.type, ...this.#parts.map(p => p.toJSON())];
+        if (this.isStart() && !this.type)
+            ret.unshift([]);
+        if (this.isEnd() &&
+            (this === this.#root ||
+                (this.#root.#filledNegs && this.#parent?.type === '!'))) {
+            ret.push({});
+        }
+        return ret;
+    }
+    isStart() {
+        if (this.#root === this)
+            return true;
+        // if (this.type) return !!this.#parent?.isStart()
+        if (!this.#parent?.isStart())
+            return false;
+        if (this.#parentIndex === 0)
+            return true;
+        // if everything AHEAD of this is a negation, then it's still the "start"
+        const p = this.#parent;
+        for (let i = 0; i < this.#parentIndex; i++) {
+            const pp = p.#parts[i];
+            if (!(pp instanceof _a && pp.type === '!')) {
+                return false;
+            }
+        }
+        return true;
+    }
+    isEnd() {
+        if (this.#root === this)
+            return true;
+        if (this.#parent?.type === '!')
+            return true;
+        if (!this.#parent?.isEnd())
+            return false;
+        if (!this.type)
+            return this.#parent?.isEnd();
+        // if not root, it'll always have a parent
+        /* c8 ignore start */
+        const pl = this.#parent ? this.#parent.#parts.length : 0;
+        /* c8 ignore stop */
+        return this.#parentIndex === pl - 1;
+    }
+    copyIn(part) {
+        if (typeof part === 'string')
+            this.push(part);
+        else
+            this.push(part.clone(this));
+    }
+    clone(parent) {
+        const c = new _a(this.type, parent);
+        for (const p of this.#parts) {
+            c.copyIn(p);
+        }
+        return c;
+    }
+    static #parseAST(str, ast, pos, opt, extDepth) {
+        const maxDepth = opt.maxExtglobRecursion ?? 2;
+        let escaping = false;
+        let inBrace = false;
+        let braceStart = -1;
+        let braceNeg = false;
+        if (ast.type === null) {
+            // outside of a extglob, append until we find a start
+            let i = pos;
+            let acc = '';
+            while (i < str.length) {
+                const c = str.charAt(i++);
+                // still accumulate escapes at this point, but we do ignore
+                // starts that are escaped
+                if (escaping || c === '\\') {
+                    escaping = !escaping;
+                    acc += c;
+                    continue;
+                }
+                if (inBrace) {
+                    if (i === braceStart + 1) {
+                        if (c === '^' || c === '!') {
+                            braceNeg = true;
+                        }
+                    }
+                    else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
+                        inBrace = false;
+                    }
+                    acc += c;
+                    continue;
+                }
+                else if (c === '[') {
+                    inBrace = true;
+                    braceStart = i;
+                    braceNeg = false;
+                    acc += c;
+                    continue;
+                }
+                // we don't have to check for adoption here, because that's
+                // done at the other recursion point.
+                const doRecurse = !opt.noext &&
+                    isExtglobType(c) &&
+                    str.charAt(i) === '(' &&
+                    extDepth <= maxDepth;
+                if (doRecurse) {
+                    ast.push(acc);
+                    acc = '';
+                    const ext = new _a(c, ast);
+                    i = _a.#parseAST(str, ext, i, opt, extDepth + 1);
+                    ast.push(ext);
+                    continue;
+                }
+                acc += c;
+            }
+            ast.push(acc);
+            return i;
+        }
+        // some kind of extglob, pos is at the (
+        // find the next | or )
+        let i = pos + 1;
+        let part = new _a(null, ast);
+        const parts = [];
+        let acc = '';
+        while (i < str.length) {
+            const c = str.charAt(i++);
+            // still accumulate escapes at this point, but we do ignore
+            // starts that are escaped
+            if (escaping || c === '\\') {
+                escaping = !escaping;
+                acc += c;
+                continue;
+            }
+            if (inBrace) {
+                if (i === braceStart + 1) {
+                    if (c === '^' || c === '!') {
+                        braceNeg = true;
+                    }
+                }
+                else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
+                    inBrace = false;
+                }
+                acc += c;
+                continue;
+            }
+            else if (c === '[') {
+                inBrace = true;
+                braceStart = i;
+                braceNeg = false;
+                acc += c;
+                continue;
+            }
+            const doRecurse = !opt.noext &&
+                isExtglobType(c) &&
+                str.charAt(i) === '(' &&
+                /* c8 ignore start - the maxDepth is sufficient here */
+                (extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));
+            /* c8 ignore stop */
+            if (doRecurse) {
+                const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
+                part.push(acc);
+                acc = '';
+                const ext = new _a(c, part);
+                part.push(ext);
+                i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);
+                continue;
+            }
+            if (c === '|') {
+                part.push(acc);
+                acc = '';
+                parts.push(part);
+                part = new _a(null, ast);
+                continue;
+            }
+            if (c === ')') {
+                if (acc === '' && ast.#parts.length === 0) {
+                    ast.#emptyExt = true;
+                }
+                part.push(acc);
+                acc = '';
+                ast.push(...parts, part);
+                return i;
+            }
+            acc += c;
+        }
+        // unfinished extglob
+        // if we got here, it was a malformed extglob! not an extglob, but
+        // maybe something else in there.
+        ast.type = null;
+        ast.#hasMagic = undefined;
+        ast.#parts = [str.substring(pos - 1)];
+        return i;
+    }
+    #canAdoptWithSpace(child) {
+        return this.#canAdopt(child, adoptionWithSpaceMap);
+    }
+    #canAdopt(child, map = adoptionMap) {
+        if (!child ||
+            typeof child !== 'object' ||
+            child.type !== null ||
+            child.#parts.length !== 1 ||
+            this.type === null) {
+            return false;
+        }
+        const gc = child.#parts[0];
+        if (!gc || typeof gc !== 'object' || gc.type === null) {
+            return false;
+        }
+        return this.#canAdoptType(gc.type, map);
+    }
+    #canAdoptType(c, map = adoptionAnyMap) {
+        return !!map.get(this.type)?.includes(c);
+    }
+    #adoptWithSpace(child, index) {
+        const gc = child.#parts[0];
+        const blank = new _a(null, gc, this.options);
+        blank.#parts.push('');
+        gc.push(blank);
+        this.#adopt(child, index);
+    }
+    #adopt(child, index) {
+        const gc = child.#parts[0];
+        this.#parts.splice(index, 1, ...gc.#parts);
+        for (const p of gc.#parts) {
+            if (typeof p === 'object')
+                p.#parent = this;
+        }
+        this.#toString = undefined;
+    }
+    #canUsurpType(c) {
+        const m = usurpMap.get(this.type);
+        return !!m?.has(c);
+    }
+    #canUsurp(child) {
+        if (!child ||
+            typeof child !== 'object' ||
+            child.type !== null ||
+            child.#parts.length !== 1 ||
+            this.type === null ||
+            this.#parts.length !== 1) {
+            return false;
+        }
+        const gc = child.#parts[0];
+        if (!gc || typeof gc !== 'object' || gc.type === null) {
+            return false;
+        }
+        return this.#canUsurpType(gc.type);
+    }
+    #usurp(child) {
+        const m = usurpMap.get(this.type);
+        const gc = child.#parts[0];
+        const nt = m?.get(gc.type);
+        /* c8 ignore start - impossible */
+        if (!nt)
+            return false;
+        /* c8 ignore stop */
+        this.#parts = gc.#parts;
+        for (const p of this.#parts) {
+            if (typeof p === 'object') {
+                p.#parent = this;
+            }
+        }
+        this.type = nt;
+        this.#toString = undefined;
+        this.#emptyExt = false;
+    }
+    static fromGlob(pattern, options = {}) {
+        const ast = new _a(null, undefined, options);
+        _a.#parseAST(pattern, ast, 0, options, 0);
+        return ast;
+    }
+    // returns the regular expression if there's magic, or the unescaped
+    // string if not.
+    toMMPattern() {
+        // should only be called on root
+        /* c8 ignore start */
+        if (this !== this.#root)
+            return this.#root.toMMPattern();
+        /* c8 ignore stop */
+        const glob = this.toString();
+        const [re, body, hasMagic, uflag] = this.toRegExpSource();
+        // if we're in nocase mode, and not nocaseMagicOnly, then we do
+        // still need a regular expression if we have to case-insensitively
+        // match capital/lowercase characters.
+        const anyMagic = hasMagic ||
+            this.#hasMagic ||
+            (this.#options.nocase &&
+                !this.#options.nocaseMagicOnly &&
+                glob.toUpperCase() !== glob.toLowerCase());
+        if (!anyMagic) {
+            return body;
+        }
+        const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');
+        return Object.assign(new RegExp(`^${re}$`, flags), {
+            _src: re,
+            _glob: glob,
+        });
+    }
+    get options() {
+        return this.#options;
+    }
+    // returns the string match, the regexp source, whether there's magic
+    // in the regexp (so a regular expression is required) and whether or
+    // not the uflag is needed for the regular expression (for posix classes)
+    // TODO: instead of injecting the start/end at this point, just return
+    // the BODY of the regexp, along with the start/end portions suitable
+    // for binding the start/end in either a joined full-path makeRe context
+    // (where we bind to (^|/), or a standalone matchPart context (where
+    // we bind to ^, and not /).  Otherwise slashes get duped!
+    //
+    // In part-matching mode, the start is:
+    // - if not isStart: nothing
+    // - if traversal possible, but not allowed: ^(?!\.\.?$)
+    // - if dots allowed or not possible: ^
+    // - if dots possible and not allowed: ^(?!\.)
+    // end is:
+    // - if not isEnd(): nothing
+    // - else: $
+    //
+    // In full-path matching mode, we put the slash at the START of the
+    // pattern, so start is:
+    // - if first pattern: same as part-matching mode
+    // - if not isStart(): nothing
+    // - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
+    // - if dots allowed or not possible: /
+    // - if dots possible and not allowed: /(?!\.)
+    // end is:
+    // - if last pattern, same as part-matching mode
+    // - else nothing
+    //
+    // Always put the (?:$|/) on negated tails, though, because that has to be
+    // there to bind the end of the negated pattern portion, and it's easier to
+    // just stick it in now rather than try to inject it later in the middle of
+    // the pattern.
+    //
+    // We can just always return the same end, and leave it up to the caller
+    // to know whether it's going to be used joined or in parts.
+    // And, if the start is adjusted slightly, can do the same there:
+    // - if not isStart: nothing
+    // - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
+    // - if dots allowed or not possible: (?:/|^)
+    // - if dots possible and not allowed: (?:/|^)(?!\.)
+    //
+    // But it's better to have a simpler binding without a conditional, for
+    // performance, so probably better to return both start options.
+    //
+    // Then the caller just ignores the end if it's not the first pattern,
+    // and the start always gets applied.
+    //
+    // But that's always going to be $ if it's the ending pattern, or nothing,
+    // so the caller can just attach $ at the end of the pattern when building.
+    //
+    // So the todo is:
+    // - better detect what kind of start is needed
+    // - return both flavors of starting pattern
+    // - attach $ at the end of the pattern when creating the actual RegExp
+    //
+    // Ah, but wait, no, that all only applies to the root when the first pattern
+    // is not an extglob. If the first pattern IS an extglob, then we need all
+    // that dot prevention biz to live in the extglob portions, because eg
+    // +(*|.x*) can match .xy but not .yx.
+    //
+    // So, return the two flavors if it's #root and the first child is not an
+    // AST, otherwise leave it to the child AST to handle it, and there,
+    // use the (?:^|/) style of start binding.
+    //
+    // Even simplified further:
+    // - Since the start for a join is eg /(?!\.) and the start for a part
+    // is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
+    // or start or whatever) and prepend ^ or / at the Regexp construction.
+    toRegExpSource(allowDot) {
+        const dot = allowDot ?? !!this.#options.dot;
+        if (this.#root === this) {
+            this.#flatten();
+            this.#fillNegs();
+        }
+        if (!isExtglobAST(this)) {
+            const noEmpty = this.isStart() &&
+                this.isEnd() &&
+                !this.#parts.some(s => typeof s !== 'string');
+            const src = this.#parts
+                .map(p => {
+                const [re, _, hasMagic, uflag] = typeof p === 'string' ?
+                    _a.#parseGlob(p, this.#hasMagic, noEmpty)
+                    : p.toRegExpSource(allowDot);
+                this.#hasMagic = this.#hasMagic || hasMagic;
+                this.#uflag = this.#uflag || uflag;
+                return re;
+            })
+                .join('');
+            let start = '';
+            if (this.isStart()) {
+                if (typeof this.#parts[0] === 'string') {
+                    // this is the string that will match the start of the pattern,
+                    // so we need to protect against dots and such.
+                    // '.' and '..' cannot match unless the pattern is that exactly,
+                    // even if it starts with . or dot:true is set.
+                    const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
+                    if (!dotTravAllowed) {
+                        const aps = addPatternStart;
+                        // check if we have a possibility of matching . or ..,
+                        // and prevent that.
+                        const needNoTrav = 
+                        // dots are allowed, and the pattern starts with [ or .
+                        (dot && aps.has(src.charAt(0))) ||
+                            // the pattern starts with \., and then [ or .
+                            (src.startsWith('\\.') && aps.has(src.charAt(2))) ||
+                            // the pattern starts with \.\., and then [ or .
+                            (src.startsWith('\\.\\.') && aps.has(src.charAt(4)));
+                        // no need to prevent dots if it can't match a dot, or if a
+                        // sub-pattern will be preventing it anyway.
+                        const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
+                        start =
+                            needNoTrav ? startNoTraversal
+                                : needNoDot ? startNoDot
+                                    : '';
+                    }
+                }
+            }
+            // append the "end of path portion" pattern to negation tails
+            let end = '';
+            if (this.isEnd() &&
+                this.#root.#filledNegs &&
+                this.#parent?.type === '!') {
+                end = '(?:$|\\/)';
+            }
+            const final = start + src + end;
+            return [
+                final,
+                (0, unescape_js_1.unescape)(src),
+                (this.#hasMagic = !!this.#hasMagic),
+                this.#uflag,
+            ];
+        }
+        // We need to calculate the body *twice* if it's a repeat pattern
+        // at the start, once in nodot mode, then again in dot mode, so a
+        // pattern like *(?) can match 'x.y'
+        const repeated = this.type === '*' || this.type === '+';
+        // some kind of extglob
+        const start = this.type === '!' ? '(?:(?!(?:' : '(?:';
+        let body = this.#partsToRegExp(dot);
+        if (this.isStart() && this.isEnd() && !body && this.type !== '!') {
+            // invalid extglob, has to at least be *something* present, if it's
+            // the entire path portion.
+            const s = this.toString();
+            const me = this;
+            me.#parts = [s];
+            me.type = null;
+            me.#hasMagic = undefined;
+            return [s, (0, unescape_js_1.unescape)(this.toString()), false, false];
+        }
+        let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ?
+            ''
+            : this.#partsToRegExp(true);
+        if (bodyDotAllowed === body) {
+            bodyDotAllowed = '';
+        }
+        if (bodyDotAllowed) {
+            body = `(?:${body})(?:${bodyDotAllowed})*?`;
+        }
+        // an empty !() is exactly equivalent to a starNoEmpty
+        let final = '';
+        if (this.type === '!' && this.#emptyExt) {
+            final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
+        }
+        else {
+            const close = this.type === '!' ?
+                // !() must match something,but !(x) can match ''
+                '))' +
+                    (this.isStart() && !dot && !allowDot ? startNoDot : '') +
+                    star +
+                    ')'
+                : this.type === '@' ? ')'
+                    : this.type === '?' ? ')?'
+                        : this.type === '+' && bodyDotAllowed ? ')'
+                            : this.type === '*' && bodyDotAllowed ? `)?`
+                                : `)${this.type}`;
+            final = start + body + close;
+        }
+        return [
+            final,
+            (0, unescape_js_1.unescape)(body),
+            (this.#hasMagic = !!this.#hasMagic),
+            this.#uflag,
+        ];
+    }
+    #flatten() {
+        if (!isExtglobAST(this)) {
+            for (const p of this.#parts) {
+                if (typeof p === 'object') {
+                    p.#flatten();
+                }
+            }
+        }
+        else {
+            // do up to 10 passes to flatten as much as possible
+            let iterations = 0;
+            let done = false;
+            do {
+                done = true;
+                for (let i = 0; i < this.#parts.length; i++) {
+                    const c = this.#parts[i];
+                    if (typeof c === 'object') {
+                        c.#flatten();
+                        if (this.#canAdopt(c)) {
+                            done = false;
+                            this.#adopt(c, i);
+                        }
+                        else if (this.#canAdoptWithSpace(c)) {
+                            done = false;
+                            this.#adoptWithSpace(c, i);
+                        }
+                        else if (this.#canUsurp(c)) {
+                            done = false;
+                            this.#usurp(c);
+                        }
+                    }
+                }
+            } while (!done && ++iterations < 10);
+        }
+        this.#toString = undefined;
+    }
+    #partsToRegExp(dot) {
+        return this.#parts
+            .map(p => {
+            // extglob ASTs should only contain parent ASTs
+            /* c8 ignore start */
+            if (typeof p === 'string') {
+                throw new Error('string type in extglob ast??');
+            }
+            /* c8 ignore stop */
+            // can ignore hasMagic, because extglobs are already always magic
+            const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
+            this.#uflag = this.#uflag || uflag;
+            return re;
+        })
+            .filter(p => !(this.isStart() && this.isEnd()) || !!p)
+            .join('|');
+    }
+    static #parseGlob(glob, hasMagic, noEmpty = false) {
+        let escaping = false;
+        let re = '';
+        let uflag = false;
+        // multiple stars that aren't globstars coalesce into one *
+        let inStar = false;
+        for (let i = 0; i < glob.length; i++) {
+            const c = glob.charAt(i);
+            if (escaping) {
+                escaping = false;
+                re += (reSpecials.has(c) ? '\\' : '') + c;
+                continue;
+            }
+            if (c === '*') {
+                if (inStar)
+                    continue;
+                inStar = true;
+                re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star;
+                hasMagic = true;
+                continue;
+            }
+            else {
+                inStar = false;
+            }
+            if (c === '\\') {
+                if (i === glob.length - 1) {
+                    re += '\\\\';
+                }
+                else {
+                    escaping = true;
+                }
+                continue;
+            }
+            if (c === '[') {
+                const [src, needUflag, consumed, magic] = (0, brace_expressions_js_1.parseClass)(glob, i);
+                if (consumed) {
+                    re += src;
+                    uflag = uflag || needUflag;
+                    i += consumed - 1;
+                    hasMagic = hasMagic || magic;
+                    continue;
+                }
+            }
+            if (c === '?') {
+                re += qmark;
+                hasMagic = true;
+                continue;
+            }
+            re += regExpEscape(c);
+        }
+        return [re, (0, unescape_js_1.unescape)(glob), !!hasMagic, uflag];
+    }
+}
+exports.AST = AST;
+_a = AST;
+//# sourceMappingURL=ast.js.map
+
+/***/ }),
+
+/***/ 11090:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+// translate the various posix character classes into unicode properties
+// this works across all unicode locales
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.parseClass = void 0;
+// { : [, /u flag required, negated]
+const posixClasses = {
+    '[:alnum:]': ['\\p{L}\\p{Nl}\\p{Nd}', true],
+    '[:alpha:]': ['\\p{L}\\p{Nl}', true],
+    '[:ascii:]': ['\\x' + '00-\\x' + '7f', false],
+    '[:blank:]': ['\\p{Zs}\\t', true],
+    '[:cntrl:]': ['\\p{Cc}', true],
+    '[:digit:]': ['\\p{Nd}', true],
+    '[:graph:]': ['\\p{Z}\\p{C}', true, true],
+    '[:lower:]': ['\\p{Ll}', true],
+    '[:print:]': ['\\p{C}', true],
+    '[:punct:]': ['\\p{P}', true],
+    '[:space:]': ['\\p{Z}\\t\\r\\n\\v\\f', true],
+    '[:upper:]': ['\\p{Lu}', true],
+    '[:word:]': ['\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}', true],
+    '[:xdigit:]': ['A-Fa-f0-9', false],
+};
+// only need to escape a few things inside of brace expressions
+// escapes: [ \ ] -
+const braceEscape = (s) => s.replace(/[[\]\\-]/g, '\\$&');
+// escape all regexp magic characters
+const regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
+// everything has already been escaped, we just have to join
+const rangesToString = (ranges) => ranges.join('');
+// takes a glob string at a posix brace expression, and returns
+// an equivalent regular expression source, and boolean indicating
+// whether the /u flag needs to be applied, and the number of chars
+// consumed to parse the character class.
+// This also removes out of order ranges, and returns ($.) if the
+// entire class just no good.
+const parseClass = (glob, position) => {
+    const pos = position;
+    /* c8 ignore start */
+    if (glob.charAt(pos) !== '[') {
+        throw new Error('not in a brace expression');
+    }
+    /* c8 ignore stop */
+    const ranges = [];
+    const negs = [];
+    let i = pos + 1;
+    let sawStart = false;
+    let uflag = false;
+    let escaping = false;
+    let negate = false;
+    let endPos = pos;
+    let rangeStart = '';
+    WHILE: while (i < glob.length) {
+        const c = glob.charAt(i);
+        if ((c === '!' || c === '^') && i === pos + 1) {
+            negate = true;
+            i++;
+            continue;
+        }
+        if (c === ']' && sawStart && !escaping) {
+            endPos = i + 1;
+            break;
+        }
+        sawStart = true;
+        if (c === '\\') {
+            if (!escaping) {
+                escaping = true;
+                i++;
+                continue;
+            }
+            // escaped \ char, fall through and treat like normal char
+        }
+        if (c === '[' && !escaping) {
+            // either a posix class, a collation equivalent, or just a [
+            for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) {
+                if (glob.startsWith(cls, i)) {
+                    // invalid, [a-[] is fine, but not [a-[:alpha]]
+                    if (rangeStart) {
+                        return ['$.', false, glob.length - pos, true];
+                    }
+                    i += cls.length;
+                    if (neg)
+                        negs.push(unip);
+                    else
+                        ranges.push(unip);
+                    uflag = uflag || u;
+                    continue WHILE;
+                }
+            }
+        }
+        // now it's just a normal character, effectively
+        escaping = false;
+        if (rangeStart) {
+            // throw this range away if it's not valid, but others
+            // can still match.
+            if (c > rangeStart) {
+                ranges.push(braceEscape(rangeStart) + '-' + braceEscape(c));
+            }
+            else if (c === rangeStart) {
+                ranges.push(braceEscape(c));
+            }
+            rangeStart = '';
+            i++;
+            continue;
+        }
+        // now might be the start of a range.
+        // can be either c-d or c-] or c] or c] at this point
+        if (glob.startsWith('-]', i + 1)) {
+            ranges.push(braceEscape(c + '-'));
+            i += 2;
+            continue;
+        }
+        if (glob.startsWith('-', i + 1)) {
+            rangeStart = c;
+            i += 2;
+            continue;
+        }
+        // not the start of a range, just a single character
+        ranges.push(braceEscape(c));
+        i++;
+    }
+    if (endPos < i) {
+        // didn't see the end of the class, not a valid class,
+        // but might still be valid as a literal match.
+        return ['', false, 0, false];
+    }
+    // if we got no ranges and no negates, then we have a range that
+    // cannot possibly match anything, and that poisons the whole glob
+    if (!ranges.length && !negs.length) {
+        return ['$.', false, glob.length - pos, true];
+    }
+    // if we got one positive range, and it's a single character, then that's
+    // not actually a magic pattern, it's just that one literal character.
+    // we should not treat that as "magic", we should just return the literal
+    // character. [_] is a perfectly valid way to escape glob magic chars.
+    if (negs.length === 0 &&
+        ranges.length === 1 &&
+        /^\\?.$/.test(ranges[0]) &&
+        !negate) {
+        const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0];
+        return [regexpEscape(r), false, endPos - pos, false];
+    }
+    const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']';
+    const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']';
+    const comb = ranges.length && negs.length ? '(' + sranges + '|' + snegs + ')'
+        : ranges.length ? sranges
+            : snegs;
+    return [comb, uflag, endPos - pos, true];
+};
+exports.parseClass = parseClass;
+//# sourceMappingURL=brace-expressions.js.map
+
+/***/ }),
+
+/***/ 10800:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.escape = void 0;
+/**
+ * Escape all magic characters in a glob pattern.
+ *
+ * If the {@link MinimatchOptions.windowsPathsNoEscape}
+ * option is used, then characters are escaped by wrapping in `[]`, because
+ * a magic character wrapped in a character class can only be satisfied by
+ * that exact character.  In this mode, `\` is _not_ escaped, because it is
+ * not interpreted as a magic character, but instead as a path separator.
+ *
+ * If the {@link MinimatchOptions.magicalBraces} option is used,
+ * then braces (`{` and `}`) will be escaped.
+ */
+const escape = (s, { windowsPathsNoEscape = false, magicalBraces = false, } = {}) => {
+    // don't need to escape +@! because we escape the parens
+    // that make those magic, and escaping ! as [!] isn't valid,
+    // because [!]] is a valid glob class meaning not ']'.
+    if (magicalBraces) {
+        return windowsPathsNoEscape ?
+            s.replace(/[?*()[\]{}]/g, '[$&]')
+            : s.replace(/[?*()[\]\\{}]/g, '\\$&');
+    }
+    return windowsPathsNoEscape ?
+        s.replace(/[?*()[\]]/g, '[$&]')
+        : s.replace(/[?*()[\]\\]/g, '\\$&');
+};
+exports.escape = escape;
+//# sourceMappingURL=escape.js.map
+
+/***/ }),
+
+/***/ 46507:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.unescape = exports.escape = exports.AST = exports.Minimatch = exports.match = exports.makeRe = exports.braceExpand = exports.defaults = exports.filter = exports.GLOBSTAR = exports.sep = exports.minimatch = void 0;
+const brace_expansion_1 = __nccwpck_require__(38968);
+const assert_valid_pattern_js_1 = __nccwpck_require__(57305);
+const ast_js_1 = __nccwpck_require__(31803);
+const escape_js_1 = __nccwpck_require__(10800);
+const unescape_js_1 = __nccwpck_require__(70851);
+const minimatch = (p, pattern, options = {}) => {
+    (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
+    // shortcut: comments match nothing.
+    if (!options.nocomment && pattern.charAt(0) === '#') {
+        return false;
+    }
+    return new Minimatch(pattern, options).match(p);
+};
+exports.minimatch = minimatch;
+// Optimized checking for the most common glob patterns.
+const starDotExtRE = /^\*+([^+@!?*[(]*)$/;
+const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext);
+const starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
+const starDotExtTestNocase = (ext) => {
+    ext = ext.toLowerCase();
+    return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext);
+};
+const starDotExtTestNocaseDot = (ext) => {
+    ext = ext.toLowerCase();
+    return (f) => f.toLowerCase().endsWith(ext);
+};
+const starDotStarRE = /^\*+\.\*+$/;
+const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.');
+const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.');
+const dotStarRE = /^\.\*+$/;
+const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.');
+const starRE = /^\*+$/;
+const starTest = (f) => f.length !== 0 && !f.startsWith('.');
+const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..';
+const qmarksRE = /^\?+([^+@!?*[(]*)?$/;
+const qmarksTestNocase = ([$0, ext = '']) => {
+    const noext = qmarksTestNoExt([$0]);
+    if (!ext)
+        return noext;
+    ext = ext.toLowerCase();
+    return (f) => noext(f) && f.toLowerCase().endsWith(ext);
+};
+const qmarksTestNocaseDot = ([$0, ext = '']) => {
+    const noext = qmarksTestNoExtDot([$0]);
+    if (!ext)
+        return noext;
+    ext = ext.toLowerCase();
+    return (f) => noext(f) && f.toLowerCase().endsWith(ext);
+};
+const qmarksTestDot = ([$0, ext = '']) => {
+    const noext = qmarksTestNoExtDot([$0]);
+    return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
+};
+const qmarksTest = ([$0, ext = '']) => {
+    const noext = qmarksTestNoExt([$0]);
+    return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
+};
+const qmarksTestNoExt = ([$0]) => {
+    const len = $0.length;
+    return (f) => f.length === len && !f.startsWith('.');
+};
+const qmarksTestNoExtDot = ([$0]) => {
+    const len = $0.length;
+    return (f) => f.length === len && f !== '.' && f !== '..';
+};
+/* c8 ignore start */
+const defaultPlatform = (typeof process === 'object' && process ?
+    (typeof process.env === 'object' &&
+        process.env &&
+        process.env.__MINIMATCH_TESTING_PLATFORM__) ||
+        process.platform
+    : 'posix');
+const path = {
+    win32: { sep: '\\' },
+    posix: { sep: '/' },
+};
+/* c8 ignore stop */
+exports.sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep;
+exports.minimatch.sep = exports.sep;
+exports.GLOBSTAR = Symbol('globstar **');
+exports.minimatch.GLOBSTAR = exports.GLOBSTAR;
+// any single thing other than /
+// don't need to escape / when using new RegExp()
+const qmark = '[^/]';
+// * => any number of characters
+const star = qmark + '*?';
+// ** when dots are allowed.  Anything goes, except .. and .
+// not (^ or / followed by one or two dots followed by $ or /),
+// followed by anything, any number of times.
+const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?';
+// not a ^ or / followed by a dot,
+// followed by anything, any number of times.
+const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?';
+const filter = (pattern, options = {}) => (p) => (0, exports.minimatch)(p, pattern, options);
+exports.filter = filter;
+exports.minimatch.filter = exports.filter;
+const ext = (a, b = {}) => Object.assign({}, a, b);
+const defaults = (def) => {
+    if (!def || typeof def !== 'object' || !Object.keys(def).length) {
+        return exports.minimatch;
+    }
+    const orig = exports.minimatch;
+    const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options));
+    return Object.assign(m, {
+        Minimatch: class Minimatch extends orig.Minimatch {
+            constructor(pattern, options = {}) {
+                super(pattern, ext(def, options));
+            }
+            static defaults(options) {
+                return orig.defaults(ext(def, options)).Minimatch;
+            }
+        },
+        AST: class AST extends orig.AST {
+            /* c8 ignore start */
+            constructor(type, parent, options = {}) {
+                super(type, parent, ext(def, options));
+            }
+            /* c8 ignore stop */
+            static fromGlob(pattern, options = {}) {
+                return orig.AST.fromGlob(pattern, ext(def, options));
+            }
+        },
+        unescape: (s, options = {}) => orig.unescape(s, ext(def, options)),
+        escape: (s, options = {}) => orig.escape(s, ext(def, options)),
+        filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)),
+        defaults: (options) => orig.defaults(ext(def, options)),
+        makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)),
+        braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)),
+        match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)),
+        sep: orig.sep,
+        GLOBSTAR: exports.GLOBSTAR,
+    });
+};
+exports.defaults = defaults;
+exports.minimatch.defaults = exports.defaults;
+// Brace expansion:
+// a{b,c}d -> abd acd
+// a{b,}c -> abc ac
+// a{0..3}d -> a0d a1d a2d a3d
+// a{b,c{d,e}f}g -> abg acdfg acefg
+// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
+//
+// Invalid sets are not expanded.
+// a{2..}b -> a{2..}b
+// a{b}c -> a{b}c
+const braceExpand = (pattern, options = {}) => {
+    (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
+    // Thanks to Yeting Li  for
+    // improving this regexp to avoid a ReDOS vulnerability.
+    if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
+        // shortcut. no need to expand.
+        return [pattern];
+    }
+    return (0, brace_expansion_1.expand)(pattern, { max: options.braceExpandMax });
+};
+exports.braceExpand = braceExpand;
+exports.minimatch.braceExpand = exports.braceExpand;
+// parse a component of the expanded set.
+// At this point, no pattern may contain "/" in it
+// so we're going to return a 2d array, where each entry is the full
+// pattern, split on '/', and then turned into a regular expression.
+// A regexp is made at the end which joins each array with an
+// escaped /, and another full one which joins each regexp with |.
+//
+// Following the lead of Bash 4.1, note that "**" only has special meaning
+// when it is the *only* thing in a path portion.  Otherwise, any series
+// of * is equivalent to a single *.  Globstar behavior is enabled by
+// default, and can be disabled by setting options.noglobstar.
+const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
+exports.makeRe = makeRe;
+exports.minimatch.makeRe = exports.makeRe;
+const match = (list, pattern, options = {}) => {
+    const mm = new Minimatch(pattern, options);
+    list = list.filter(f => mm.match(f));
+    if (mm.options.nonull && !list.length) {
+        list.push(pattern);
+    }
+    return list;
+};
+exports.match = match;
+exports.minimatch.match = exports.match;
+// replace stuff like \* with *
+const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
+const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
+class Minimatch {
+    options;
+    set;
+    pattern;
+    windowsPathsNoEscape;
+    nonegate;
+    negate;
+    comment;
+    empty;
+    preserveMultipleSlashes;
+    partial;
+    globSet;
+    globParts;
+    nocase;
+    isWindows;
+    platform;
+    windowsNoMagicRoot;
+    maxGlobstarRecursion;
+    regexp;
+    constructor(pattern, options = {}) {
+        (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
+        options = options || {};
+        this.options = options;
+        this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200;
+        this.pattern = pattern;
+        this.platform = options.platform || defaultPlatform;
+        this.isWindows = this.platform === 'win32';
+        // avoid the annoying deprecation flag lol
+        const awe = ('allowWindow' + 'sEscape');
+        this.windowsPathsNoEscape =
+            !!options.windowsPathsNoEscape || options[awe] === false;
+        if (this.windowsPathsNoEscape) {
+            this.pattern = this.pattern.replace(/\\/g, '/');
+        }
+        this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
+        this.regexp = null;
+        this.negate = false;
+        this.nonegate = !!options.nonegate;
+        this.comment = false;
+        this.empty = false;
+        this.partial = !!options.partial;
+        this.nocase = !!this.options.nocase;
+        this.windowsNoMagicRoot =
+            options.windowsNoMagicRoot !== undefined ?
+                options.windowsNoMagicRoot
+                : !!(this.isWindows && this.nocase);
+        this.globSet = [];
+        this.globParts = [];
+        this.set = [];
+        // make the set of regexps etc.
+        this.make();
+    }
+    hasMagic() {
+        if (this.options.magicalBraces && this.set.length > 1) {
+            return true;
+        }
+        for (const pattern of this.set) {
+            for (const part of pattern) {
+                if (typeof part !== 'string')
+                    return true;
+            }
+        }
+        return false;
+    }
+    debug(..._) { }
+    make() {
+        const pattern = this.pattern;
+        const options = this.options;
+        // empty patterns and comments match nothing.
+        if (!options.nocomment && pattern.charAt(0) === '#') {
+            this.comment = true;
+            return;
+        }
+        if (!pattern) {
+            this.empty = true;
+            return;
+        }
+        // step 1: figure out negation, etc.
+        this.parseNegate();
+        // step 2: expand braces
+        this.globSet = [...new Set(this.braceExpand())];
+        if (options.debug) {
+            //oxlint-disable-next-line no-console
+            this.debug = (...args) => console.error(...args);
+        }
+        this.debug(this.pattern, this.globSet);
+        // step 3: now we have a set, so turn each one into a series of
+        // path-portion matching patterns.
+        // These will be regexps, except in the case of "**", which is
+        // set to the GLOBSTAR object for globstar behavior,
+        // and will not contain any / characters
+        //
+        // First, we preprocess to make the glob pattern sets a bit simpler
+        // and deduped.  There are some perf-killing patterns that can cause
+        // problems with a glob walk, but we can simplify them down a bit.
+        const rawGlobParts = this.globSet.map(s => this.slashSplit(s));
+        this.globParts = this.preprocess(rawGlobParts);
+        this.debug(this.pattern, this.globParts);
+        // glob --> regexps
+        let set = this.globParts.map((s, _, __) => {
+            if (this.isWindows && this.windowsNoMagicRoot) {
+                // check if it's a drive or unc path.
+                const isUNC = s[0] === '' &&
+                    s[1] === '' &&
+                    (s[2] === '?' || !globMagic.test(s[2])) &&
+                    !globMagic.test(s[3]);
+                const isDrive = /^[a-z]:/i.test(s[0]);
+                if (isUNC) {
+                    return [
+                        ...s.slice(0, 4),
+                        ...s.slice(4).map(ss => this.parse(ss)),
+                    ];
+                }
+                else if (isDrive) {
+                    return [s[0], ...s.slice(1).map(ss => this.parse(ss))];
+                }
+            }
+            return s.map(ss => this.parse(ss));
+        });
+        this.debug(this.pattern, set);
+        // filter out everything that didn't compile properly.
+        this.set = set.filter(s => s.indexOf(false) === -1);
+        // do not treat the ? in UNC paths as magic
+        if (this.isWindows) {
+            for (let i = 0; i < this.set.length; i++) {
+                const p = this.set[i];
+                if (p[0] === '' &&
+                    p[1] === '' &&
+                    this.globParts[i][2] === '?' &&
+                    typeof p[3] === 'string' &&
+                    /^[a-z]:$/i.test(p[3])) {
+                    p[2] = '?';
+                }
+            }
+        }
+        this.debug(this.pattern, this.set);
+    }
+    // various transforms to equivalent pattern sets that are
+    // faster to process in a filesystem walk.  The goal is to
+    // eliminate what we can, and push all ** patterns as far
+    // to the right as possible, even if it increases the number
+    // of patterns that we have to process.
+    preprocess(globParts) {
+        // if we're not in globstar mode, then turn ** into *
+        if (this.options.noglobstar) {
+            for (const partset of globParts) {
+                for (let j = 0; j < partset.length; j++) {
+                    if (partset[j] === '**') {
+                        partset[j] = '*';
+                    }
+                }
+            }
+        }
+        const { optimizationLevel = 1 } = this.options;
+        if (optimizationLevel >= 2) {
+            // aggressive optimization for the purpose of fs walking
+            globParts = this.firstPhasePreProcess(globParts);
+            globParts = this.secondPhasePreProcess(globParts);
+        }
+        else if (optimizationLevel >= 1) {
+            // just basic optimizations to remove some .. parts
+            globParts = this.levelOneOptimize(globParts);
+        }
+        else {
+            // just collapse multiple ** portions into one
+            globParts = this.adjascentGlobstarOptimize(globParts);
+        }
+        return globParts;
+    }
+    // just get rid of adjascent ** portions
+    adjascentGlobstarOptimize(globParts) {
+        return globParts.map(parts => {
+            let gs = -1;
+            while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
+                let i = gs;
+                while (parts[i + 1] === '**') {
+                    i++;
+                }
+                if (i !== gs) {
+                    parts.splice(gs, i - gs);
+                }
+            }
+            return parts;
+        });
+    }
+    // get rid of adjascent ** and resolve .. portions
+    levelOneOptimize(globParts) {
+        return globParts.map(parts => {
+            parts = parts.reduce((set, part) => {
+                const prev = set[set.length - 1];
+                if (part === '**' && prev === '**') {
+                    return set;
+                }
+                if (part === '..') {
+                    if (prev && prev !== '..' && prev !== '.' && prev !== '**') {
+                        set.pop();
+                        return set;
+                    }
+                }
+                set.push(part);
+                return set;
+            }, []);
+            return parts.length === 0 ? [''] : parts;
+        });
+    }
+    levelTwoFileOptimize(parts) {
+        if (!Array.isArray(parts)) {
+            parts = this.slashSplit(parts);
+        }
+        let didSomething = false;
+        do {
+            didSomething = false;
+            // 
// -> 
/
+            if (!this.preserveMultipleSlashes) {
+                for (let i = 1; i < parts.length - 1; i++) {
+                    const p = parts[i];
+                    // don't squeeze out UNC patterns
+                    if (i === 1 && p === '' && parts[0] === '')
+                        continue;
+                    if (p === '.' || p === '') {
+                        didSomething = true;
+                        parts.splice(i, 1);
+                        i--;
+                    }
+                }
+                if (parts[0] === '.' &&
+                    parts.length === 2 &&
+                    (parts[1] === '.' || parts[1] === '')) {
+                    didSomething = true;
+                    parts.pop();
+                }
+            }
+            // 
/

/../ ->

/
+            let dd = 0;
+            while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
+                const p = parts[dd - 1];
+                if (p &&
+                    p !== '.' &&
+                    p !== '..' &&
+                    p !== '**' &&
+                    !(this.isWindows && /^[a-z]:$/i.test(p))) {
+                    didSomething = true;
+                    parts.splice(dd - 1, 2);
+                    dd -= 2;
+                }
+            }
+        } while (didSomething);
+        return parts.length === 0 ? [''] : parts;
+    }
+    // First phase: single-pattern processing
+    // 
 is 1 or more portions
+    //  is 1 or more portions
+    // 

is any portion other than ., .., '', or ** + // is . or '' + // + // **/.. is *brutal* for filesystem walking performance, because + // it effectively resets the recursive walk each time it occurs, + // and ** cannot be reduced out by a .. pattern part like a regexp + // or most strings (other than .., ., and '') can be. + // + //

/**/../

/

/ -> {

/../

/

/,

/**/

/

/} + //

// -> 
/
+    // 
/

/../ ->

/
+    // **/**/ -> **/
+    //
+    // **/*/ -> */**/ <== not valid because ** doesn't follow
+    // this WOULD be allowed if ** did follow symlinks, or * didn't
+    firstPhasePreProcess(globParts) {
+        let didSomething = false;
+        do {
+            didSomething = false;
+            // 
/**/../

/

/ -> {

/../

/

/,

/**/

/

/} + for (let parts of globParts) { + let gs = -1; + while (-1 !== (gs = parts.indexOf('**', gs + 1))) { + let gss = gs; + while (parts[gss + 1] === '**') { + //

/**/**/ -> 
/**/
+                        gss++;
+                    }
+                    // eg, if gs is 2 and gss is 4, that means we have 3 **
+                    // parts, and can remove 2 of them.
+                    if (gss > gs) {
+                        parts.splice(gs + 1, gss - gs);
+                    }
+                    let next = parts[gs + 1];
+                    const p = parts[gs + 2];
+                    const p2 = parts[gs + 3];
+                    if (next !== '..')
+                        continue;
+                    if (!p ||
+                        p === '.' ||
+                        p === '..' ||
+                        !p2 ||
+                        p2 === '.' ||
+                        p2 === '..') {
+                        continue;
+                    }
+                    didSomething = true;
+                    // edit parts in place, and push the new one
+                    parts.splice(gs, 1);
+                    const other = parts.slice(0);
+                    other[gs] = '**';
+                    globParts.push(other);
+                    gs--;
+                }
+                // 
// -> 
/
+                if (!this.preserveMultipleSlashes) {
+                    for (let i = 1; i < parts.length - 1; i++) {
+                        const p = parts[i];
+                        // don't squeeze out UNC patterns
+                        if (i === 1 && p === '' && parts[0] === '')
+                            continue;
+                        if (p === '.' || p === '') {
+                            didSomething = true;
+                            parts.splice(i, 1);
+                            i--;
+                        }
+                    }
+                    if (parts[0] === '.' &&
+                        parts.length === 2 &&
+                        (parts[1] === '.' || parts[1] === '')) {
+                        didSomething = true;
+                        parts.pop();
+                    }
+                }
+                // 
/

/../ ->

/
+                let dd = 0;
+                while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
+                    const p = parts[dd - 1];
+                    if (p && p !== '.' && p !== '..' && p !== '**') {
+                        didSomething = true;
+                        const needDot = dd === 1 && parts[dd + 1] === '**';
+                        const splin = needDot ? ['.'] : [];
+                        parts.splice(dd - 1, 2, ...splin);
+                        if (parts.length === 0)
+                            parts.push('');
+                        dd -= 2;
+                    }
+                }
+            }
+        } while (didSomething);
+        return globParts;
+    }
+    // second phase: multi-pattern dedupes
+    // {
/*/,
/

/} ->

/*/
+    // {
/,
/} -> 
/
+    // {
/**/,
/} -> 
/**/
+    //
+    // {
/**/,
/**/

/} ->

/**/
+    // ^-- not valid because ** doens't follow symlinks
+    secondPhasePreProcess(globParts) {
+        for (let i = 0; i < globParts.length - 1; i++) {
+            for (let j = i + 1; j < globParts.length; j++) {
+                const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
+                if (matched) {
+                    globParts[i] = [];
+                    globParts[j] = matched;
+                    break;
+                }
+            }
+        }
+        return globParts.filter(gs => gs.length);
+    }
+    partsMatch(a, b, emptyGSMatch = false) {
+        let ai = 0;
+        let bi = 0;
+        let result = [];
+        let which = '';
+        while (ai < a.length && bi < b.length) {
+            if (a[ai] === b[bi]) {
+                result.push(which === 'b' ? b[bi] : a[ai]);
+                ai++;
+                bi++;
+            }
+            else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {
+                result.push(a[ai]);
+                ai++;
+            }
+            else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {
+                result.push(b[bi]);
+                bi++;
+            }
+            else if (a[ai] === '*' &&
+                b[bi] &&
+                (this.options.dot || !b[bi].startsWith('.')) &&
+                b[bi] !== '**') {
+                if (which === 'b')
+                    return false;
+                which = 'a';
+                result.push(a[ai]);
+                ai++;
+                bi++;
+            }
+            else if (b[bi] === '*' &&
+                a[ai] &&
+                (this.options.dot || !a[ai].startsWith('.')) &&
+                a[ai] !== '**') {
+                if (which === 'a')
+                    return false;
+                which = 'b';
+                result.push(b[bi]);
+                ai++;
+                bi++;
+            }
+            else {
+                return false;
+            }
+        }
+        // if we fall out of the loop, it means they two are identical
+        // as long as their lengths match
+        return a.length === b.length && result;
+    }
+    parseNegate() {
+        if (this.nonegate)
+            return;
+        const pattern = this.pattern;
+        let negate = false;
+        let negateOffset = 0;
+        for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
+            negate = !negate;
+            negateOffset++;
+        }
+        if (negateOffset)
+            this.pattern = pattern.slice(negateOffset);
+        this.negate = negate;
+    }
+    // set partial to true to test if, for example,
+    // "/a/b" matches the start of "/*/b/*/d"
+    // Partial means, if you run out of file before you run
+    // out of pattern, then that's fine, as long as all
+    // the parts match.
+    matchOne(file, pattern, partial = false) {
+        let fileStartIndex = 0;
+        let patternStartIndex = 0;
+        // UNC paths like //?/X:/... can match X:/... and vice versa
+        // Drive letters in absolute drive or unc paths are always compared
+        // case-insensitively.
+        if (this.isWindows) {
+            const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
+            const fileUNC = !fileDrive &&
+                file[0] === '' &&
+                file[1] === '' &&
+                file[2] === '?' &&
+                /^[a-z]:$/i.test(file[3]);
+            const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]);
+            const patternUNC = !patternDrive &&
+                pattern[0] === '' &&
+                pattern[1] === '' &&
+                pattern[2] === '?' &&
+                typeof pattern[3] === 'string' &&
+                /^[a-z]:$/i.test(pattern[3]);
+            const fdi = fileUNC ? 3
+                : fileDrive ? 0
+                    : undefined;
+            const pdi = patternUNC ? 3
+                : patternDrive ? 0
+                    : undefined;
+            if (typeof fdi === 'number' && typeof pdi === 'number') {
+                const [fd, pd] = [
+                    file[fdi],
+                    pattern[pdi],
+                ];
+                // start matching at the drive letter index of each
+                if (fd.toLowerCase() === pd.toLowerCase()) {
+                    pattern[pdi] = fd;
+                    patternStartIndex = pdi;
+                    fileStartIndex = fdi;
+                }
+            }
+        }
+        // resolve and reduce . and .. portions in the file as well.
+        // don't need to do the second phase, because it's only one string[]
+        const { optimizationLevel = 1 } = this.options;
+        if (optimizationLevel >= 2) {
+            file = this.levelTwoFileOptimize(file);
+        }
+        if (pattern.includes(exports.GLOBSTAR)) {
+            return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
+        }
+        return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
+    }
+    #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
+        // split the pattern into head, tail, and middle of ** delimited parts
+        const firstgs = pattern.indexOf(exports.GLOBSTAR, patternIndex);
+        const lastgs = pattern.lastIndexOf(exports.GLOBSTAR);
+        // split the pattern up into globstar-delimited sections
+        // the tail has to be at the end, and the others just have
+        // to be found in order from the head.
+        const [head, body, tail] = partial ?
+            [
+                pattern.slice(patternIndex, firstgs),
+                pattern.slice(firstgs + 1),
+                [],
+            ]
+            : [
+                pattern.slice(patternIndex, firstgs),
+                pattern.slice(firstgs + 1, lastgs),
+                pattern.slice(lastgs + 1),
+            ];
+        // check the head, from the current file/pattern index.
+        if (head.length) {
+            const fileHead = file.slice(fileIndex, fileIndex + head.length);
+            if (!this.#matchOne(fileHead, head, partial, 0, 0)) {
+                return false;
+            }
+            fileIndex += head.length;
+            patternIndex += head.length;
+        }
+        // now we know the head matches!
+        // if the last portion is not empty, it MUST match the end
+        // check the tail
+        let fileTailMatch = 0;
+        if (tail.length) {
+            // if head + tail > file, then we cannot possibly match
+            if (tail.length + fileIndex > file.length)
+                return false;
+            // try to match the tail
+            let tailStart = file.length - tail.length;
+            if (this.#matchOne(file, tail, partial, tailStart, 0)) {
+                fileTailMatch = tail.length;
+            }
+            else {
+                // affordance for stuff like a/**/* matching a/b/
+                // if the last file portion is '', and there's more to the pattern
+                // then try without the '' bit.
+                if (file[file.length - 1] !== '' ||
+                    fileIndex + tail.length === file.length) {
+                    return false;
+                }
+                tailStart--;
+                if (!this.#matchOne(file, tail, partial, tailStart, 0)) {
+                    return false;
+                }
+                fileTailMatch = tail.length + 1;
+            }
+        }
+        // now we know the tail matches!
+        // the middle is zero or more portions wrapped in **, possibly
+        // containing more ** sections.
+        // so a/**/b/**/c/**/d has become **/b/**/c/**
+        // if it's empty, it means a/**/b, just verify we have no bad dots
+        // if there's no tail, so it ends on /**, then we must have *something*
+        // after the head, or it's not a matc
+        if (!body.length) {
+            let sawSome = !!fileTailMatch;
+            for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
+                const f = String(file[i]);
+                sawSome = true;
+                if (f === '.' ||
+                    f === '..' ||
+                    (!this.options.dot && f.startsWith('.'))) {
+                    return false;
+                }
+            }
+            // in partial mode, we just need to get past all file parts
+            return partial || sawSome;
+        }
+        // now we know that there's one or more body sections, which can
+        // be matched anywhere from the 0 index (because the head was pruned)
+        // through to the length-fileTailMatch index.
+        // split the body up into sections, and note the minimum index it can
+        // be found at (start with the length of all previous segments)
+        // [section, before, after]
+        const bodySegments = [[[], 0]];
+        let currentBody = bodySegments[0];
+        let nonGsParts = 0;
+        const nonGsPartsSums = [0];
+        for (const b of body) {
+            if (b === exports.GLOBSTAR) {
+                nonGsPartsSums.push(nonGsParts);
+                currentBody = [[], 0];
+                bodySegments.push(currentBody);
+            }
+            else {
+                currentBody[0].push(b);
+                nonGsParts++;
+            }
+        }
+        let i = bodySegments.length - 1;
+        const fileLength = file.length - fileTailMatch;
+        for (const b of bodySegments) {
+            b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
+        }
+        return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
+    }
+    // return false for "nope, not matching"
+    // return null for "not matching, cannot keep trying"
+    #matchGlobStarBodySections(file, 
+    // pattern section, last possible position for it
+    bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
+        // take the first body segment, and walk from fileIndex to its "after"
+        // value at the end
+        // If it doesn't match at that position, we increment, until we hit
+        // that final possible position, and give up.
+        // If it does match, then advance and try to rest.
+        // If any of them fail we keep walking forward.
+        // this is still a bit recursively painful, but it's more constrained
+        // than previous implementations, because we never test something that
+        // can't possibly be a valid matching condition.
+        const bs = bodySegments[bodyIndex];
+        if (!bs) {
+            // just make sure that there's no bad dots
+            for (let i = fileIndex; i < file.length; i++) {
+                sawTail = true;
+                const f = file[i];
+                if (f === '.' ||
+                    f === '..' ||
+                    (!this.options.dot && f.startsWith('.'))) {
+                    return false;
+                }
+            }
+            return sawTail;
+        }
+        // have a non-globstar body section to test
+        const [body, after] = bs;
+        while (fileIndex <= after) {
+            const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
+            // if limit exceeded, no match. intentional false negative,
+            // acceptable break in correctness for security.
+            if (m && globStarDepth < this.maxGlobstarRecursion) {
+                // match! see if the rest match. if so, we're done!
+                const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
+                if (sub !== false) {
+                    return sub;
+                }
+            }
+            const f = file[fileIndex];
+            if (f === '.' ||
+                f === '..' ||
+                (!this.options.dot && f.startsWith('.'))) {
+                return false;
+            }
+            fileIndex++;
+        }
+        // walked off. no point continuing
+        return partial || null;
+    }
+    #matchOne(file, pattern, partial, fileIndex, patternIndex) {
+        let fi;
+        let pi;
+        let pl;
+        let fl;
+        for (fi = fileIndex,
+            pi = patternIndex,
+            fl = file.length,
+            pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
+            this.debug('matchOne loop');
+            let p = pattern[pi];
+            let f = file[fi];
+            this.debug(pattern, p, f);
+            // should be impossible.
+            // some invalid regexp stuff in the set.
+            /* c8 ignore start */
+            if (p === false || p === exports.GLOBSTAR) {
+                return false;
+            }
+            /* c8 ignore stop */
+            // something other than **
+            // non-magic patterns just have to match exactly
+            // patterns with magic have been turned into regexps.
+            let hit;
+            if (typeof p === 'string') {
+                hit = f === p;
+                this.debug('string match', p, f, hit);
+            }
+            else {
+                hit = p.test(f);
+                this.debug('pattern match', p, f, hit);
+            }
+            if (!hit)
+                return false;
+        }
+        // Note: ending in / means that we'll get a final ""
+        // at the end of the pattern.  This can only match a
+        // corresponding "" at the end of the file.
+        // If the file ends in /, then it can only match a
+        // a pattern that ends in /, unless the pattern just
+        // doesn't have any more for it. But, a/b/ should *not*
+        // match "a/b/*", even though "" matches against the
+        // [^/]*? pattern, except in partial mode, where it might
+        // simply not be reached yet.
+        // However, a/b/ should still satisfy a/*
+        // now either we fell off the end of the pattern, or we're done.
+        if (fi === fl && pi === pl) {
+            // ran out of pattern and filename at the same time.
+            // an exact hit!
+            return true;
+        }
+        else if (fi === fl) {
+            // ran out of file, but still had pattern left.
+            // this is ok if we're doing the match as part of
+            // a glob fs traversal.
+            return partial;
+        }
+        else if (pi === pl) {
+            // ran out of pattern, still have file left.
+            // this is only acceptable if we're on the very last
+            // empty segment of a file with a trailing slash.
+            // a/* should match a/b/
+            return fi === fl - 1 && file[fi] === '';
+            /* c8 ignore start */
+        }
+        else {
+            // should be unreachable.
+            throw new Error('wtf?');
+        }
+        /* c8 ignore stop */
+    }
+    braceExpand() {
+        return (0, exports.braceExpand)(this.pattern, this.options);
+    }
+    parse(pattern) {
+        (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
+        const options = this.options;
+        // shortcuts
+        if (pattern === '**')
+            return exports.GLOBSTAR;
+        if (pattern === '')
+            return '';
+        // far and away, the most common glob pattern parts are
+        // *, *.*, and *.  Add a fast check method for those.
+        let m;
+        let fastTest = null;
+        if ((m = pattern.match(starRE))) {
+            fastTest = options.dot ? starTestDot : starTest;
+        }
+        else if ((m = pattern.match(starDotExtRE))) {
+            fastTest = (options.nocase ?
+                options.dot ?
+                    starDotExtTestNocaseDot
+                    : starDotExtTestNocase
+                : options.dot ? starDotExtTestDot
+                    : starDotExtTest)(m[1]);
+        }
+        else if ((m = pattern.match(qmarksRE))) {
+            fastTest = (options.nocase ?
+                options.dot ?
+                    qmarksTestNocaseDot
+                    : qmarksTestNocase
+                : options.dot ? qmarksTestDot
+                    : qmarksTest)(m);
+        }
+        else if ((m = pattern.match(starDotStarRE))) {
+            fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
+        }
+        else if ((m = pattern.match(dotStarRE))) {
+            fastTest = dotStarTest;
+        }
+        const re = ast_js_1.AST.fromGlob(pattern, this.options).toMMPattern();
+        if (fastTest && typeof re === 'object') {
+            // Avoids overriding in frozen environments
+            Reflect.defineProperty(re, 'test', { value: fastTest });
+        }
+        return re;
+    }
+    makeRe() {
+        if (this.regexp || this.regexp === false)
+            return this.regexp;
+        // at this point, this.set is a 2d array of partial
+        // pattern strings, or "**".
+        //
+        // It's better to use .match().  This function shouldn't
+        // be used, really, but it's pretty convenient sometimes,
+        // when you just want to work with a regex.
+        const set = this.set;
+        if (!set.length) {
+            this.regexp = false;
+            return this.regexp;
+        }
+        const options = this.options;
+        const twoStar = options.noglobstar ? star
+            : options.dot ? twoStarDot
+                : twoStarNoDot;
+        const flags = new Set(options.nocase ? ['i'] : []);
+        // regexpify non-globstar patterns
+        // if ** is only item, then we just do one twoStar
+        // if ** is first, and there are more, prepend (\/|twoStar\/)? to next
+        // if ** is last, append (\/twoStar|) to previous
+        // if ** is in the middle, append (\/|\/twoStar\/) to previous
+        // then filter out GLOBSTAR symbols
+        let re = set
+            .map(pattern => {
+            const pp = pattern.map(p => {
+                if (p instanceof RegExp) {
+                    for (const f of p.flags.split(''))
+                        flags.add(f);
+                }
+                return (typeof p === 'string' ? regExpEscape(p)
+                    : p === exports.GLOBSTAR ? exports.GLOBSTAR
+                        : p._src);
+            });
+            pp.forEach((p, i) => {
+                const next = pp[i + 1];
+                const prev = pp[i - 1];
+                if (p !== exports.GLOBSTAR || prev === exports.GLOBSTAR) {
+                    return;
+                }
+                if (prev === undefined) {
+                    if (next !== undefined && next !== exports.GLOBSTAR) {
+                        pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
+                    }
+                    else {
+                        pp[i] = twoStar;
+                    }
+                }
+                else if (next === undefined) {
+                    pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + ')?';
+                }
+                else if (next !== exports.GLOBSTAR) {
+                    pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
+                    pp[i + 1] = exports.GLOBSTAR;
+                }
+            });
+            const filtered = pp.filter(p => p !== exports.GLOBSTAR);
+            // For partial matches, we need to make the pattern match
+            // any prefix of the full path. We do this by generating
+            // alternative patterns that match progressively longer prefixes.
+            if (this.partial && filtered.length >= 1) {
+                const prefixes = [];
+                for (let i = 1; i <= filtered.length; i++) {
+                    prefixes.push(filtered.slice(0, i).join('/'));
+                }
+                return '(?:' + prefixes.join('|') + ')';
+            }
+            return filtered.join('/');
+        })
+            .join('|');
+        // need to wrap in parens if we had more than one thing with |,
+        // otherwise only the first will be anchored to ^ and the last to $
+        const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
+        // must match entire pattern
+        // ending in a * or ** will make it less strict.
+        re = '^' + open + re + close + '$';
+        // In partial mode, '/' should always match as it's a valid prefix for any pattern
+        if (this.partial) {
+            re = '^(?:\\/|' + open + re.slice(1, -1) + close + ')$';
+        }
+        // can match anything, as long as it's not this.
+        if (this.negate)
+            re = '^(?!' + re + ').+$';
+        try {
+            this.regexp = new RegExp(re, [...flags].join(''));
+            /* c8 ignore start */
+        }
+        catch {
+            // should be impossible
+            this.regexp = false;
+        }
+        /* c8 ignore stop */
+        return this.regexp;
+    }
+    slashSplit(p) {
+        // if p starts with // on windows, we preserve that
+        // so that UNC paths aren't broken.  Otherwise, any number of
+        // / characters are coalesced into one, unless
+        // preserveMultipleSlashes is set to true.
+        if (this.preserveMultipleSlashes) {
+            return p.split('/');
+        }
+        else if (this.isWindows && /^\/\/[^/]+/.test(p)) {
+            // add an extra '' for the one we lose
+            return ['', ...p.split(/\/+/)];
+        }
+        else {
+            return p.split(/\/+/);
+        }
+    }
+    match(f, partial = this.partial) {
+        this.debug('match', f, this.pattern);
+        // short-circuit in the case of busted things.
+        // comments, etc.
+        if (this.comment) {
+            return false;
+        }
+        if (this.empty) {
+            return f === '';
+        }
+        if (f === '/' && partial) {
+            return true;
+        }
+        const options = this.options;
+        // windows: need to use /, not \
+        if (this.isWindows) {
+            f = f.split('\\').join('/');
+        }
+        // treat the test path as a set of pathparts.
+        const ff = this.slashSplit(f);
+        this.debug(this.pattern, 'split', ff);
+        // just ONE of the pattern sets in this.set needs to match
+        // in order for it to be valid.  If negating, then just one
+        // match means that we have failed.
+        // Either way, return on the first hit.
+        const set = this.set;
+        this.debug(this.pattern, 'set', set);
+        // Find the basename of the path by looking for the last non-empty segment
+        let filename = ff[ff.length - 1];
+        if (!filename) {
+            for (let i = ff.length - 2; !filename && i >= 0; i--) {
+                filename = ff[i];
+            }
+        }
+        for (const pattern of set) {
+            let file = ff;
+            if (options.matchBase && pattern.length === 1) {
+                file = [filename];
+            }
+            const hit = this.matchOne(file, pattern, partial);
+            if (hit) {
+                if (options.flipNegate) {
+                    return true;
+                }
+                return !this.negate;
+            }
+        }
+        // didn't get any hits.  this is success if it's a negative
+        // pattern, failure otherwise.
+        if (options.flipNegate) {
+            return false;
+        }
+        return this.negate;
+    }
+    static defaults(def) {
+        return exports.minimatch.defaults(def).Minimatch;
+    }
+}
+exports.Minimatch = Minimatch;
+/* c8 ignore start */
+var ast_js_2 = __nccwpck_require__(31803);
+Object.defineProperty(exports, "AST", ({ enumerable: true, get: function () { return ast_js_2.AST; } }));
+var escape_js_2 = __nccwpck_require__(10800);
+Object.defineProperty(exports, "escape", ({ enumerable: true, get: function () { return escape_js_2.escape; } }));
+var unescape_js_2 = __nccwpck_require__(70851);
+Object.defineProperty(exports, "unescape", ({ enumerable: true, get: function () { return unescape_js_2.unescape; } }));
+/* c8 ignore stop */
+exports.minimatch.AST = ast_js_1.AST;
+exports.minimatch.Minimatch = Minimatch;
+exports.minimatch.escape = escape_js_1.escape;
+exports.minimatch.unescape = unescape_js_1.unescape;
+//# sourceMappingURL=index.js.map
+
+/***/ }),
+
+/***/ 70851:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.unescape = void 0;
+/**
+ * Un-escape a string that has been escaped with {@link escape}.
+ *
+ * If the {@link MinimatchOptions.windowsPathsNoEscape} option is used, then
+ * square-bracket escapes are removed, but not backslash escapes.
+ *
+ * For example, it will turn the string `'[*]'` into `*`, but it will not
+ * turn `'\\*'` into `'*'`, because `\` is a path separator in
+ * `windowsPathsNoEscape` mode.
+ *
+ * When `windowsPathsNoEscape` is not set, then both square-bracket escapes and
+ * backslash escapes are removed.
+ *
+ * Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
+ * or unescaped.
+ *
+ * When `magicalBraces` is not set, escapes of braces (`{` and `}`) will not be
+ * unescaped.
+ */
+const unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true, } = {}) => {
+    if (magicalBraces) {
+        return windowsPathsNoEscape ?
+            s.replace(/\[([^/\\])\]/g, '$1')
+            : s
+                .replace(/((?!\\).|^)\[([^/\\])\]/g, '$1$2')
+                .replace(/\\([^/])/g, '$1');
+    }
+    return windowsPathsNoEscape ?
+        s.replace(/\[([^/\\{}])\]/g, '$1')
+        : s
+            .replace(/((?!\\).|^)\[([^/\\{}])\]/g, '$1$2')
+            .replace(/\\([^/{}])/g, '$1');
+};
+exports.unescape = unescape;
+//# sourceMappingURL=unescape.js.map
+
+/***/ }),
+
 /***/ 69373:
 /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
 
diff --git a/package-lock.json b/package-lock.json
index c5938e234..6aa068b64 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1211,9 +1211,9 @@
       }
     },
     "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
-      "version": "3.14.2",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
-      "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+      "version": "3.15.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz",
+      "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -1505,16 +1505,6 @@
         }
       }
     },
-    "node_modules/@jest/reporters/node_modules/brace-expansion": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
-      "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
     "node_modules/@jest/reporters/node_modules/glob": {
       "version": "10.5.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
@@ -1537,22 +1527,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/@jest/reporters/node_modules/minimatch": {
-      "version": "9.0.9",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
-      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@jest/schemas": {
       "version": "30.4.1",
       "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
@@ -2215,29 +2189,6 @@
         "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",
@@ -2251,22 +2202,6 @@
         "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": {
       "version": "8.48.0",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.0.tgz",
@@ -2391,32 +2326,6 @@
         "typescript": ">=4.8.4 <6.0.0"
       }
     },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
-      "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "9.0.9",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
-      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/@typescript-eslint/utils": {
       "version": "8.48.0",
       "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.0.tgz",
@@ -3046,9 +2955,13 @@
       }
     },
     "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+      "license": "MIT",
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
     },
     "node_modules/baseline-browser-mapping": {
       "version": "2.10.36",
@@ -3064,13 +2977,15 @@
       }
     },
     "node_modules/brace-expansion": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
-      "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
+      "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
       "license": "MIT",
       "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "20 || >=22"
       }
     },
     "node_modules/browserslist": {
@@ -3354,11 +3269,6 @@
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
       "dev": true
     },
-    "node_modules/concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
-    },
     "node_modules/convert-source-map": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -4688,16 +4598,6 @@
         }
       }
     },
-    "node_modules/jest-config/node_modules/brace-expansion": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
-      "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
     "node_modules/jest-config/node_modules/glob": {
       "version": "10.5.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
@@ -4720,22 +4620,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/jest-config/node_modules/minimatch": {
-      "version": "9.0.9",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
-      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/jest-diff": {
       "version": "30.4.1",
       "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz",
@@ -5049,16 +4933,6 @@
         "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
       }
     },
-    "node_modules/jest-runtime/node_modules/brace-expansion": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
-      "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0"
-      }
-    },
     "node_modules/jest-runtime/node_modules/glob": {
       "version": "10.5.0",
       "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
@@ -5081,22 +4955,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/jest-runtime/node_modules/minimatch": {
-      "version": "9.0.9",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
-      "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=16 || 14 >=14.17"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
     "node_modules/jest-snapshot": {
       "version": "30.4.1",
       "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz",
@@ -5253,9 +5111,19 @@
       "license": "MIT"
     },
     "node_modules/js-yaml": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
-      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+      "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
       "license": "MIT",
       "dependencies": {
         "argparse": "^2.0.1"
@@ -5737,15 +5605,18 @@
       }
     },
     "node_modules/minimatch": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
-      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
-      "license": "ISC",
+      "version": "10.2.6",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
+      "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
+      "license": "BlueOak-1.0.0",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^5.0.8"
       },
       "engines": {
-        "node": "*"
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/minimist": {
diff --git a/package.json b/package.json
index 6f5d50247..3f9acc849 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,8 @@
     "build": "ncc build -o dist/setup src/setup-java.ts && ncc build -o dist/cleanup src/cleanup-java.ts",
     "format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"",
     "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",
+    "lint": "eslint --config ./.eslintrc.js . --ext .ts",
+    "lint:fix": "eslint --config ./.eslintrc.js . --ext .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",
@@ -73,5 +73,8 @@
   "bugs": {
     "url": "https://github.com/actions/setup-java/issues"
   },
-  "homepage": "https://github.com/actions/setup-java#readme"
+  "homepage": "https://github.com/actions/setup-java#readme",
+  "overrides": {
+    "minimatch": "10.2.6"
+  }
 }

From e498d2a66a953492f322542257b22125c989b422 Mon Sep 17 00:00:00 2001
From: Bruno Borges 
Date: Tue, 28 Jul 2026 21:46:39 -0400
Subject: [PATCH 59/60] Backport #1151: Fix missing wrapper cache distributions
 (#1153)

Skip optional Maven and Gradle wrapper cache saves when their distribution paths do not exist, while allowing the main dependency cache to save.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c0b19ff2-ef52-4ac1-a832-0f8f72c43219
---
 __tests__/cache.test.ts | 63 +++++++++++++++++++++++++++++++++++------
 dist/cleanup/index.js   |  8 ++++++
 dist/setup/index.js     |  8 ++++++
 src/cache.ts            | 12 ++++++++
 4 files changed, 82 insertions(+), 9 deletions(-)

diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts
index 88660ae65..08ded85be 100644
--- a/__tests__/cache.test.ts
+++ b/__tests__/cache.test.ts
@@ -377,6 +377,10 @@ describe('dependency cache', () => {
       ReturnType,
       Parameters
     >;
+    let spyGlobCreate: jest.SpyInstance<
+      ReturnType,
+      Parameters
+    >;
 
     beforeEach(() => {
       spyCacheSave = jest
@@ -384,6 +388,9 @@ describe('dependency cache', () => {
         .mockImplementation((paths: string[], key: string) =>
           Promise.resolve(0)
         );
+      spyGlobCreate = jest
+        .spyOn(glob, 'create')
+        .mockResolvedValue(createGlobber(['wrapper-path']));
       spyWarning.mockImplementation(() => null);
     });
 
@@ -469,17 +476,24 @@ describe('dependency cache', () => {
       });
       it('does not fail the post step when the wrapper distribution path is missing', async () => {
         createFile(join(workspace, 'pom.xml'));
+        createDirectory(join(workspace, '.mvn'));
+        createDirectory(join(workspace, '.mvn', 'wrapper'));
+        createFile(
+          join(workspace, '.mvn', 'wrapper', 'maven-wrapper.properties')
+        );
         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);
-        });
+        spyGlobCreate.mockResolvedValue(createGlobber([]));
 
-        await expect(save('maven')).resolves.not.toThrow();
+        await expect(save('maven')).resolves.toBeUndefined();
+        expect(spyCacheSave).not.toHaveBeenCalledWith(
+          [join(os.homedir(), '.m2', 'wrapper', 'dists')],
+          expect.any(String)
+        );
+        expect(spyCacheSave).toHaveBeenCalledWith(
+          [join(os.homedir(), '.m2', 'repository')],
+          'setup-java-cache-primary-key'
+        );
+        expect(spyWarning).not.toHaveBeenCalled();
       });
     });
     describe('for gradle', () => {
@@ -553,6 +567,23 @@ describe('dependency cache', () => {
           expect.any(String)
         );
       });
+      it('does not fail the post step when the wrapper distribution path is missing', async () => {
+        createFile(join(workspace, 'build.gradle'));
+        createFile(join(workspace, 'gradle-wrapper.properties'));
+        createStateForWrapperRestore('gradle-wrapper', false);
+        spyGlobCreate.mockResolvedValue(createGlobber([]));
+
+        await expect(save('gradle')).resolves.toBeUndefined();
+        expect(spyCacheSave).not.toHaveBeenCalledWith(
+          [join(os.homedir(), '.gradle', 'wrapper')],
+          expect.any(String)
+        );
+        expect(spyCacheSave).toHaveBeenCalledWith(
+          [join(os.homedir(), '.gradle', 'caches')],
+          'setup-java-cache-primary-key'
+        );
+        expect(spyWarning).not.toHaveBeenCalled();
+      });
     });
     describe('for sbt', () => {
       it('uploads cache even if no build.sbt found', async () => {
@@ -663,6 +694,20 @@ function createDirectory(path: string) {
   fs.mkdirSync(path);
 }
 
+function createGlobber(
+  paths: string[]
+): Awaited> {
+  return {
+    getSearchPaths: () => [],
+    glob: () => Promise.resolve(paths),
+    globGenerator: async function* () {
+      for (const path of paths) {
+        yield path;
+      }
+    }
+  };
+}
+
 function projectRoot(workspace: string): string {
   if (os.platform() === 'darwin') {
     return `/private${workspace}`;
diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js
index 7ff509c06..d0fef1efe 100644
--- a/dist/cleanup/index.js
+++ b/dist/cleanup/index.js
@@ -50898,6 +50898,14 @@ function saveAdditionalCache(packageManager, additionalCache) {
             core.info(`Cache hit occurred on the ${additionalCache.name} primary key ${primaryKey}, not saving cache.`);
             return;
         }
+        const globber = yield glob.create(additionalCache.path.join('\n'), {
+            implicitDescendants: false
+        });
+        const cachePaths = yield globber.glob();
+        if (cachePaths.length === 0) {
+            core.debug(`${additionalCache.name} cache paths do not exist, not saving cache.`);
+            return;
+        }
         try {
             const cacheId = yield cache.saveCache(additionalCache.path, primaryKey);
             if (cacheId === -1) {
diff --git a/dist/setup/index.js b/dist/setup/index.js
index f99f5cb71..72eec4855 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -76733,6 +76733,14 @@ function saveAdditionalCache(packageManager, additionalCache) {
             core.info(`Cache hit occurred on the ${additionalCache.name} primary key ${primaryKey}, not saving cache.`);
             return;
         }
+        const globber = yield glob.create(additionalCache.path.join('\n'), {
+            implicitDescendants: false
+        });
+        const cachePaths = yield globber.glob();
+        if (cachePaths.length === 0) {
+            core.debug(`${additionalCache.name} cache paths do not exist, not saving cache.`);
+            return;
+        }
         try {
             const cacheId = yield cache.saveCache(additionalCache.path, primaryKey);
             if (cacheId === -1) {
diff --git a/src/cache.ts b/src/cache.ts
index 1ca7e7628..9f9c079a7 100644
--- a/src/cache.ts
+++ b/src/cache.ts
@@ -319,6 +319,18 @@ async function saveAdditionalCache(
     );
     return;
   }
+
+  const globber = await glob.create(additionalCache.path.join('\n'), {
+    implicitDescendants: false
+  });
+  const cachePaths = await globber.glob();
+  if (cachePaths.length === 0) {
+    core.debug(
+      `${additionalCache.name} cache paths do not exist, not saving cache.`
+    );
+    return;
+  }
+
   try {
     const cacheId = await cache.saveCache(additionalCache.path, primaryKey);
     if (cacheId === -1) {

From b6effb05e454b25005698d916606bdc6ffcbf961 Mon Sep 17 00:00:00 2001
From: Bruno Borges 
Date: Fri, 31 Jul 2026 15:34:51 -0400
Subject: [PATCH 60/60] Deprecate legacy Adopt distributions in v5 (#1186)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0cd196f2-1044-4d33-96bb-8e794a00b03c
---
 README.md                                     |  6 +--
 .../distributors/distribution-factory.test.ts | 51 +++++++++++++++++++
 action.yml                                    |  2 +-
 dist/setup/index.js                           | 34 +++++++++++--
 docs/advanced-usage.md                        |  3 +-
 src/distributions/adopt/installer.ts          |  6 ---
 src/distributions/distribution-factory.ts     | 12 +++++
 7 files changed, 98 insertions(+), 16 deletions(-)
 create mode 100644 __tests__/distributors/distribution-factory.test.ts

diff --git a/README.md b/README.md
index 78c595801..8514c7b7a 100644
--- a/README.md
+++ b/README.md
@@ -108,8 +108,8 @@ Currently, the following distributions are supported:
 |-|-|-|
 | `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) |
+| `adopt` or `adopt-hotspot` (deprecated; use `temurin`) | [AdoptOpenJDK Hotspot](https://adoptopenjdk.net/) | [`adopt-hotspot` license](https://adoptopenjdk.net/about.html) |
+| `adopt-openj9` (deprecated; use `semeru`) | [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/)
@@ -125,7 +125,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/).
+> - The legacy AdoptOpenJDK distributions are deprecated in setup-java v5 and will be removed in setup-java v6. Migrate `adopt` and `adopt-hotspot` to `temurin`, and `adopt-openj9` to `semeru`, 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, 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__/distributors/distribution-factory.test.ts b/__tests__/distributors/distribution-factory.test.ts
new file mode 100644
index 000000000..39322f130
--- /dev/null
+++ b/__tests__/distributors/distribution-factory.test.ts
@@ -0,0 +1,51 @@
+import * as core from '@actions/core';
+import {
+  AdoptDistribution,
+  AdoptImplementation
+} from '../../src/distributions/adopt/installer';
+import {JavaInstallerOptions} from '../../src/distributions/base-models';
+import {getJavaDistribution} from '../../src/distributions/distribution-factory';
+
+describe('getJavaDistribution', () => {
+  const installerOptions: JavaInstallerOptions = {
+    version: '11',
+    architecture: 'x64',
+    packageType: 'jdk',
+    checkLatest: false
+  };
+
+  afterEach(() => {
+    jest.restoreAllMocks();
+  });
+
+  it.each([
+    ['adopt', AdoptImplementation.Hotspot, 'temurin'],
+    ['adopt-hotspot', AdoptImplementation.Hotspot, 'temurin'],
+    ['adopt-openj9', AdoptImplementation.OpenJ9, 'semeru']
+  ])(
+    'warns that %s is deprecated while preserving its installer',
+    (
+      distributionName: string,
+      implementation: AdoptImplementation,
+      replacement: string
+    ) => {
+      const warningSpy = jest
+        .spyOn(core, 'warning')
+        .mockImplementation(() => {});
+
+      const distribution = getJavaDistribution(
+        distributionName,
+        installerOptions
+      );
+
+      expect(distribution).toBeInstanceOf(AdoptDistribution);
+      if (!(distribution instanceof AdoptDistribution)) {
+        throw new Error(`Expected an Adopt installer for ${distributionName}`);
+      }
+      expect(distribution['jvmImpl']).toBe(implementation);
+      expect(warningSpy).toHaveBeenCalledWith(
+        `The '${distributionName}' legacy AdoptOpenJDK distribution is deprecated and will be removed in setup-java v6. Please migrate to the '${replacement}' distribution.`
+      );
+    }
+  );
+});
diff --git a/action.yml b/action.yml
index 5237f1a8f..396e53c70 100644
--- a/action.yml
+++ b/action.yml
@@ -10,7 +10,7 @@ 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. This input is required except when java-version-file points to .sdkmanrc with a recognized distribution suffix (e.g., java=21.0.5-tem).'
+    description: 'Java distribution. See the list of supported distributions in README file. Legacy AdoptOpenJDK values (adopt, adopt-hotspot, adopt-openj9) are deprecated and will be removed in setup-java v6. 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, jdk+crac, jre+crac)'
diff --git a/dist/setup/index.js b/dist/setup/index.js
index 72eec4855..cca156f80 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -76905,9 +76905,6 @@ class AdoptDistribution extends base_installer_1.JavaBase {
     }
     findPackageForDownload(version) {
         return __awaiter(this, void 0, void 0, function* () {
-            if (this.jvmImpl === AdoptImplementation.Hotspot) {
-                core.notice("AdoptOpenJDK has moved to Eclipse Temurin https://github.com/actions/setup-java#supported-distributions please consider changing to the 'temurin' distribution type in your setup-java configuration.");
-            }
             if (this.jvmImpl === AdoptImplementation.Hotspot &&
                 this.temurinDistribution !== null) {
                 try {
@@ -77560,12 +77557,36 @@ exports.CorrettoDistribution = CorrettoDistribution;
 /***/ }),
 
 /***/ 2970:
-/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+/***/ (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.getJavaDistribution = void 0;
+const core = __importStar(__nccwpck_require__(37484));
 const installer_1 = __nccwpck_require__(21019);
 const installer_2 = __nccwpck_require__(41978);
 const installer_3 = __nccwpck_require__(37874);
@@ -77606,8 +77627,10 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) {
             return new installer_1.LocalDistribution(installerOptions, jdkFile);
         case JavaDistribution.Adopt:
         case JavaDistribution.AdoptHotspot:
+            warnIfAdoptDistributionIsDeprecated(distributionName, 'temurin');
             return new installer_3.AdoptDistribution(installerOptions, installer_3.AdoptImplementation.Hotspot);
         case JavaDistribution.AdoptOpenJ9:
+            warnIfAdoptDistributionIsDeprecated(distributionName, 'semeru');
             return new installer_3.AdoptDistribution(installerOptions, installer_3.AdoptImplementation.OpenJ9);
         case JavaDistribution.Temurin:
             return new installer_4.TemurinDistribution(installerOptions, installer_4.TemurinImplementation.Hotspot);
@@ -77640,6 +77663,9 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) {
     }
 }
 exports.getJavaDistribution = getJavaDistribution;
+function warnIfAdoptDistributionIsDeprecated(distributionName, replacement) {
+    core.warning(`The '${distributionName}' legacy AdoptOpenJDK distribution is deprecated and will be removed in setup-java v6. Please migrate to the '${replacement}' distribution.`);
+}
 
 
 /***/ }),
diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md
index bbd6a8903..b914a24e7 100644
--- a/docs/advanced-usage.md
+++ b/docs/advanced-usage.md
@@ -46,7 +46,7 @@ steps:
 ```
 
 ### Adopt
-**NOTE:** Adopt OpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from `adopt` to `temurin` 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:** The legacy `adopt`, `adopt-hotspot`, and `adopt-openj9` distributions are deprecated in setup-java v5 and will be removed in setup-java v6. Migrate `adopt` and `adopt-hotspot` to `temurin`, and `adopt-openj9` to `semeru`, 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/).
 
 ```yaml
 steps:
@@ -953,4 +953,3 @@ Notes and caveats:
 - 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.
-
diff --git a/src/distributions/adopt/installer.ts b/src/distributions/adopt/installer.ts
index 78798bfc1..cb41137b6 100644
--- a/src/distributions/adopt/installer.ts
+++ b/src/distributions/adopt/installer.ts
@@ -59,12 +59,6 @@ export class AdoptDistribution extends JavaBase {
   protected async findPackageForDownload(
     version: string
   ): Promise {
-    if (this.jvmImpl === AdoptImplementation.Hotspot) {
-      core.notice(
-        "AdoptOpenJDK has moved to Eclipse Temurin https://github.com/actions/setup-java#supported-distributions please consider changing to the 'temurin' distribution type in your setup-java configuration."
-      );
-    }
-
     if (
       this.jvmImpl === AdoptImplementation.Hotspot &&
       this.temurinDistribution !== null
diff --git a/src/distributions/distribution-factory.ts b/src/distributions/distribution-factory.ts
index 22b48aee2..4cb83df82 100644
--- a/src/distributions/distribution-factory.ts
+++ b/src/distributions/distribution-factory.ts
@@ -1,3 +1,4 @@
+import * as core from '@actions/core';
 import {JavaBase} from './base-installer';
 import {JavaInstallerOptions} from './base-models';
 import {LocalDistribution} from './local/installer';
@@ -48,11 +49,13 @@ export function getJavaDistribution(
       return new LocalDistribution(installerOptions, jdkFile);
     case JavaDistribution.Adopt:
     case JavaDistribution.AdoptHotspot:
+      warnIfAdoptDistributionIsDeprecated(distributionName, 'temurin');
       return new AdoptDistribution(
         installerOptions,
         AdoptImplementation.Hotspot
       );
     case JavaDistribution.AdoptOpenJ9:
+      warnIfAdoptDistributionIsDeprecated(distributionName, 'semeru');
       return new AdoptDistribution(
         installerOptions,
         AdoptImplementation.OpenJ9
@@ -90,3 +93,12 @@ export function getJavaDistribution(
       return null;
   }
 }
+
+function warnIfAdoptDistributionIsDeprecated(
+  distributionName: string,
+  replacement: string
+): void {
+  core.warning(
+    `The '${distributionName}' legacy AdoptOpenJDK distribution is deprecated and will be removed in setup-java v6. Please migrate to the '${replacement}' distribution.`
+  );
+}