From ceb85f25b55c9a71bfe9160f5f967eb002bb519f Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Fri, 4 Sep 2026 10:03:11 +0100 Subject: [PATCH] Distinguish GitHub-hosted runners from ones that look hosted `isHostedRunner` infers hostedness from the runner name and the toolcache path, so it also matches self-hosted runners that are configured to resemble hosted ones. Rename it to `looksLikeHostedRunner` so callers can see they are getting a heuristic, and add `isGitHubHostedRunner`, which reads the `RUNNER_ENVIRONMENT` value the Actions service reports. The existing callers keep the heuristic, so there is no behaviour change. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- lib/entry-points.js | 6 +++--- src/actions-util.ts | 13 +++++++++++++ src/caching-utils.ts | 4 ++-- src/config-utils.ts | 5 ++--- src/util.ts | 8 ++++++-- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index afaa9ba10..b0d3b63da 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -145993,7 +145993,7 @@ async function checkForTimeout() { process.exit(); } } -function isHostedRunner() { +function looksLikeHostedRunner() { return ( // Name of the runner on hosted Windows runners process.env["RUNNER_NAME"]?.includes("Hosted Agent") || // Name of the runner on hosted POSIX runners @@ -148924,7 +148924,7 @@ function createCacheKeyHash(components) { function getDependencyCachingEnabled() { const dependencyCaching = getOptionalInput("dependency-caching") || process.env["CODEQL_ACTION_DEPENDENCY_CACHING" /* DEPENDENCY_CACHING */]; if (dependencyCaching !== void 0) return getCachingKind(dependencyCaching); - if (!isHostedRunner()) return "none" /* None */; + if (!looksLikeHostedRunner()) return "none" /* None */; if (!isDefaultSetup()) return "none" /* None */; return "none" /* None */; } @@ -150771,7 +150771,7 @@ async function validateOverlayDatabaseMode(overlayDatabaseMode, useOverlayDataba async function isTrapCachingEnabled(features, overlayDatabaseMode) { const trapCaching = getOptionalInput("trap-caching"); if (trapCaching !== void 0) return trapCaching === "true"; - if (!isHostedRunner()) return false; + if (!looksLikeHostedRunner()) return false; if (overlayDatabaseMode !== "none" /* None */ && await features.getValue("overlay_analysis_disable_trap_caching" /* OverlayAnalysisDisableTrapCaching */)) { return false; } diff --git a/src/actions-util.ts b/src/actions-util.ts index dd5124620..eb7d92b51 100644 --- a/src/actions-util.ts +++ b/src/actions-util.ts @@ -283,6 +283,19 @@ export function isSelfHostedRunner(env: Env = getEnv()) { return env.getOptional(ActionsEnvVars.RUNNER_ENVIRONMENT) === "self-hosted"; } +/** + * Whether the job is running on a runner that GitHub hosts, and whose toolcache is therefore thrown + * away once the job has finished. + * + * Unlike `looksLikeHostedRunner`, this is based on what the service reports for the job rather than + * on how the runner's filesystem happens to be laid out, so it does not match self-hosted runners + * that are configured to resemble hosted ones, such as those that mount a persistent volume at + * `/opt/hostedtoolcache`. + */ +export function isGitHubHostedRunner(env: Env = getEnv()) { + return env.getOptional(ActionsEnvVars.RUNNER_ENVIRONMENT) === "github-hosted"; +} + /** Determines whether the workflow trigger is `dynamic`. */ export function isDynamicWorkflow(env: Env = getEnv()): boolean { return getWorkflowEventName(env) === "dynamic"; diff --git a/src/caching-utils.ts b/src/caching-utils.ts index 33dac7cfb..d5cdb8445 100644 --- a/src/caching-utils.ts +++ b/src/caching-utils.ts @@ -5,7 +5,7 @@ import * as core from "@actions/core"; import { getOptionalInput, isDefaultSetup } from "./actions-util"; import { EnvVar } from "./environment"; import { Logger } from "./logging"; -import { isHostedRunner, tryGetFolderBytes } from "./util"; +import { looksLikeHostedRunner, tryGetFolderBytes } from "./util"; /** * Returns the total size of all the specified paths. @@ -109,7 +109,7 @@ export function getDependencyCachingEnabled(): CachingKind { if (dependencyCaching !== undefined) return getCachingKind(dependencyCaching); // On self-hosted runners which may have dependencies installed centrally, disable caching by default - if (!isHostedRunner()) return CachingKind.None; + if (!looksLikeHostedRunner()) return CachingKind.None; // Disable in advanced workflows by default. if (!isDefaultSetup()) return CachingKind.None; diff --git a/src/config-utils.ts b/src/config-utils.ts index 0a6ced00a..288b4f02f 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -90,9 +90,8 @@ import { Result, Success, Failure, - isHostedRunner, + looksLikeHostedRunner, } from "./util"; - export { type Config } from "./config/action-config"; /** @@ -938,7 +937,7 @@ export async function isTrapCachingEnabled( if (trapCaching !== undefined) return trapCaching === "true"; // On self-hosted runners which may have slow network access, disable TRAP caching by default. - if (!isHostedRunner()) return false; + if (!looksLikeHostedRunner()) return false; // If overlay analysis is enabled, then disable TRAP caching since overlay analysis supersedes it. // This change is gated behind a feature flag. diff --git a/src/util.ts b/src/util.ts index 2d910dec3..f6258b285 100644 --- a/src/util.ts +++ b/src/util.ts @@ -842,9 +842,13 @@ export async function checkForTimeout() { * directory with the name hostedtoolcache which is present on * GitHub-hosted runners. * - * @returns true iff the runner is hosted by GitHub + * Since this is a heuristic over how the runner happens to be named and laid out, it also matches + * self-hosted runners that are configured to resemble hosted ones. Prefer + * `isGitHubHostedRunner` when you need the answer the Actions service reports. + * + * @returns true iff the runner looks like it is hosted by GitHub */ -export function isHostedRunner() { +export function looksLikeHostedRunner() { return ( // Name of the runner on hosted Windows runners process.env["RUNNER_NAME"]?.includes("Hosted Agent") ||