5
0
mirror of https://gitea.com/actions/setup-java.git synced 2026-08-07 02:31:20 +00:00

Cache resolved JDK releases to remove the vendor API from warm jobs (#1208)

* Cache resolved JDK releases to remove the vendor API from warm jobs

Only Temurin is preinstalled in the runner tool cache, so for every other
distribution `findInToolcache()` misses on essentially every job. That
forces a call to the distribution's metadata API before the JDK cache key
can even be computed, which makes the vendor a hard per-job dependency
even when the JDK bytes are already cached, and turns a vendor 403, 429,
or outage into a job failure.

Store the resolved release in a small companion cache entry keyed only on
inputs known before any network call: runner OS, architecture,
distribution, package type, requested version, and stability. A job that
finds a current entry installs the JDK without contacting the metadata API
at all.

`@actions/cache` derives a cache version by hashing the requested paths, so
save and restore paths must match. The entry therefore uses a path that
excludes the date bucket while the key includes it, which lets restore keys
fall back to an older bucket. An entry older than the current day is not
used directly: the metadata API is still queried so floating requests such
as `java-version: 21` keep picking up new releases, and the older entry is
used only when that query fails. Because the entry also carries the
download URL and checksum, that fallback works even when the JDK itself is
not cached.

Releases whose URL is not content-addressed are never stored. Oracle JDK
and Oracle GraalVM build a `/latest/` URL for a major-only version, and its
bytes change when a new build is published, so the URL and checksum are
only consistent at the moment they are resolved. Mark those releases
floating and skip recording them.

Restored payloads are validated as untrusted input, and the post-job save
rewrites the payload the key was computed for rather than uploading
whatever is on disk, since a restore in a later step targets the same path.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b76d8cb0-f629-46e1-bf9a-ffde06948644

* Widen the resolution freshness window from a day to a week

A daily window gives no benefit to the repositories that need it most.
A repository whose workflows run once a day would re-resolve on every job,
and one running weekly would never see a current entry at all, yet those
are exactly the repositories with nothing warm in the tool cache.

Seven days is also the ceiling. GitHub removes cache entries that have not
been accessed for seven days, so a longer window would leave the previous
entry evicted by the time the window rolls over, removing the stale
fallback at the moment it is most likely to be needed. It comfortably
covers JDK release cadence, which is monthly at its fastest and usually
quarterly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b76d8cb0-f629-46e1-bf9a-ffde06948644

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Rebuild dist to match the linted source

The pre-commit hook runs `eslint --fix` after `npm run check` has already
built `dist/`, so the fix it applied to the resolution fallback warning in
`base-installer.ts` never reached the bundle.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b76d8cb0-f629-46e1-bf9a-ffde06948644

* Rebuild dist to match the linted source

The autofix accepted on the pull request edited the resolution fallback
warning in `base-installer.ts` through the GitHub UI, which does not run
`npm run build`, so `dist/` still carried the pre-fix bundle.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b76d8cb0-f629-46e1-bf9a-ffde06948644

---------

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>
Copilot-Session: b76d8cb0-f629-46e1-bf9a-ffde06948644
This commit is contained in:
Bruno Borges
2026-08-04 23:58:03 -04:00
committed by GitHub
parent ef9440a2b8
commit ab597f914a
17 changed files with 1624 additions and 9 deletions
+50 -1
View File
@@ -322,7 +322,7 @@ class JavaBase {
else {
core/* info */.pq('Trying to resolve the latest version from remote');
try {
const javaRelease = await this.findPackageForDownload(this.version);
const javaRelease = await this.resolveJavaRelease();
core/* info */.pq(`Resolved latest version as ${javaRelease.version}`);
if (!this.forceDownload && foundJava?.version === javaRelease.version) {
core/* info */.pq(`Resolved Java ${foundJava.version} from tool-cache`);
@@ -391,6 +391,55 @@ class JavaBase {
}
return foundJava;
}
/**
* Resolves the release to install, preferring a cached resolution over the
* distribution's metadata API.
*
* Only Temurin is preinstalled on hosted runners, so for every other
* distribution the tool-cache lookup misses and the vendor API becomes a
* per-job dependency even when the JDK itself is already in the GitHub
* Actions cache. A cached resolution removes that dependency, and because it
* carries the download URL and checksum it also keeps a job working when the
* vendor API is unavailable but the JDK still has to be downloaded.
*/
async resolveJavaRelease() {
if (!this.cacheJdk ||
this.checkLatest ||
this.latest ||
this.forceDownload) {
return this.findPackageForDownload(this.version);
}
const { restoreJdkResolution, registerJdkResolution } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(348)]).then(__webpack_require__.bind(__webpack_require__, 967));
const request = {
distribution: this.distribution,
packageType: this.packageType,
architecture: this.architecture,
versionSpec: this.version,
stable: this.stable
};
const restored = await restoreJdkResolution(request);
if (restored?.fresh) {
core/* info */.pq(`Resolved ${this.distribution} ${restored.release.version} from the resolution cache`);
return restored.release;
}
try {
const javaRelease = await this.findPackageForDownload(this.version);
if (!javaRelease.floating) {
registerJdkResolution(request, javaRelease);
}
return javaRelease;
}
catch (error) {
if (!restored) {
throw error;
}
// The cached resolution is older than the current bucket, but falling
// back to it is strictly better than failing the job because the vendor
// metadata API is down.
core/* warning */.$e(`Failed to resolve ${this.distribution} ${this.version} from remote (${error instanceof Error ? error.message : String(error)}); falling back to the cached resolution for ${restored.release.version}.`);
return restored.release;
}
}
logSetupError(error) {
const httpStatusCode = error instanceof tool_cache/* HTTPError */.Hl
? error.httpStatusCode