From 0772e5f38cf05dcb72f1f33944a44fdade83a88f Mon Sep 17 00:00:00 2001 From: gowridurgad Date: Fri, 17 Jul 2026 17:27:13 +0530 Subject: [PATCH 1/3] Scope the Tool-Cache Root --- src/setup-python.ts | 8 +++++- src/utils.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/setup-python.ts b/src/setup-python.ts index 2b062caac..354553b0d 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -12,7 +12,8 @@ import { logWarning, IS_MAC, getVersionInputFromFile, - getVersionsInputFromPlainFile + getVersionsInputFromPlainFile, + scopeToolCacheByOs } from './utils.js'; function isPyPyVersion(versionSpec: string) { @@ -86,6 +87,11 @@ async function run() { process.env['RUNNER_TOOL_CACHE'] = process.env['AGENT_TOOLSDIRECTORY']; } + // Issue #1087: on self-hosted Linux, scope the tool-cache root by OS id + + // version so runners that switch between distro versions do not reuse a + // Python built against a different glibc / OpenSSL. No-op on hosted runners. + scopeToolCacheByOs(); + core.debug( `Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}` ); diff --git a/src/utils.ts b/src/utils.ts index c75d17dcd..d719c8794 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -445,3 +445,62 @@ export function getDownloadFileName(downloadUrl: string): string | undefined { ? path.join(tempDir, path.basename(downloadUrl)) : undefined; } + +/** + * Issue #1087: on Linux, scope the tool-cache root by OS id + version so a + * self-hosted runner that switches between different distro versions + * (e.g. Ubuntu 20.04 / 24.04) does not reuse a Python built against a + * different glibc / OpenSSL. Returns e.g. "os-ubuntu-24.04", or null when + * not applicable (non-Linux, or /etc/os-release unavailable/incomplete). + */ +export function getOsScopedToolCacheSegment(): string | null { + if (!IS_LINUX) return null; + try { + const content = fs.readFileSync('/etc/os-release', 'utf8'); + const map: Record = {}; + for (const line of content.split('\n')) { + const eq = line.indexOf('='); + if (eq <= 0) continue; + const k = line.slice(0, eq).trim(); + const v = line + .slice(eq + 1) + .trim() + .replace(/^"|"$/g, ''); + if (k && v) map[k] = v; + } + const id = map['ID']; + const versionId = map['VERSION_ID']; + if (!id || !versionId) return null; + const safe = `${id}-${versionId}`.replace(/[^A-Za-z0-9._-]/g, '_'); + return `os-${safe}`; + } catch { + return null; + } +} + +/** + * Issue #1087: apply {@link getOsScopedToolCacheSegment} to the tool-cache + * root env vars so all downstream consumers (`@actions/tool-cache`, and the + * `setup.sh` inside `actions/python-versions` release tarballs) transparently + * install/read under an OS-scoped root. + * + * IMPORTANT: only applied on self-hosted runners. GitHub-hosted runners ship + * with a pre-installed, fully-configured Python under the un-scoped path + * (`/opt/hostedtoolcache/Python/…`); redirecting them would cause a needless + * re-install of a less-configured Python (e.g. breaks numpy source builds on + * freethreaded 3.13). Detected via the documented `RUNNER_ENVIRONMENT` env var + * (`github-hosted` on hosted, `self-hosted` on self-hosted runners). + * + * Idempotent and a no-op off Linux or on hosted runners. + */ +export function scopeToolCacheByOs(): void { + if (process.env['RUNNER_ENVIRONMENT'] === 'github-hosted') return; + const seg = getOsScopedToolCacheSegment(); + if (!seg) return; + for (const varName of ['RUNNER_TOOL_CACHE', 'AGENT_TOOLSDIRECTORY']) { + const cur = process.env[varName]; + if (!cur) continue; + if (path.basename(cur) === seg) continue; // already scoped + process.env[varName] = path.join(cur, seg); + } +} From 1744e1a82172f03a92c5faf4ef7da5792fd26b71 Mon Sep 17 00:00:00 2001 From: gowridurgad Date: Fri, 17 Jul 2026 17:28:14 +0530 Subject: [PATCH 2/3] dist --- dist/cache-save/index.js | 66 +++++++++++++++++++++++++++++++++++++ dist/setup/index.js | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 489447525..4bcf0bad7 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -97477,6 +97477,72 @@ function getDownloadFileName(downloadUrl) { ? path.join(tempDir, path.basename(downloadUrl)) : undefined; } +/** + * Issue #1087: on Linux, scope the tool-cache root by OS id + version so a + * self-hosted runner that switches between different distro versions + * (e.g. Ubuntu 20.04 / 24.04) does not reuse a Python built against a + * different glibc / OpenSSL. Returns e.g. "os-ubuntu-24.04", or null when + * not applicable (non-Linux, or /etc/os-release unavailable/incomplete). + */ +function getOsScopedToolCacheSegment() { + if (!utils_IS_LINUX) + return null; + try { + const content = fs.readFileSync('/etc/os-release', 'utf8'); + const map = {}; + for (const line of content.split('\n')) { + const eq = line.indexOf('='); + if (eq <= 0) + continue; + const k = line.slice(0, eq).trim(); + const v = line + .slice(eq + 1) + .trim() + .replace(/^"|"$/g, ''); + if (k && v) + map[k] = v; + } + const id = map['ID']; + const versionId = map['VERSION_ID']; + if (!id || !versionId) + return null; + const safe = `${id}-${versionId}`.replace(/[^A-Za-z0-9._-]/g, '_'); + return `os-${safe}`; + } + catch { + return null; + } +} +/** + * Issue #1087: apply {@link getOsScopedToolCacheSegment} to the tool-cache + * root env vars so all downstream consumers (`@actions/tool-cache`, and the + * `setup.sh` inside `actions/python-versions` release tarballs) transparently + * install/read under an OS-scoped root. + * + * IMPORTANT: only applied on self-hosted runners. GitHub-hosted runners ship + * with a pre-installed, fully-configured Python under the un-scoped path + * (`/opt/hostedtoolcache/Python/…`); redirecting them would cause a needless + * re-install of a less-configured Python (e.g. breaks numpy source builds on + * freethreaded 3.13). Detected via the documented `RUNNER_ENVIRONMENT` env var + * (`github-hosted` on hosted, `self-hosted` on self-hosted runners). + * + * Idempotent and a no-op off Linux or on hosted runners. + */ +function scopeToolCacheByOs() { + if (process.env['RUNNER_ENVIRONMENT'] === 'github-hosted') + return; + const seg = getOsScopedToolCacheSegment(); + if (!seg) + return; + for (const varName of ['RUNNER_TOOL_CACHE', 'AGENT_TOOLSDIRECTORY']) { + const cur = process.env[varName]; + if (!cur) + continue; + if (path.basename(cur) === seg) + continue; // already scoped + process.env[varName] = path.join(cur, seg); + } +} ;// CONCATENATED MODULE: ./src/cache-distributions/cache-distributor.ts diff --git a/dist/setup/index.js b/dist/setup/index.js index af2263ef0..ca756f8f0 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -97469,6 +97469,72 @@ function getDownloadFileName(downloadUrl) { ? external_path_.join(tempDir, external_path_.basename(downloadUrl)) : undefined; } +/** + * Issue #1087: on Linux, scope the tool-cache root by OS id + version so a + * self-hosted runner that switches between different distro versions + * (e.g. Ubuntu 20.04 / 24.04) does not reuse a Python built against a + * different glibc / OpenSSL. Returns e.g. "os-ubuntu-24.04", or null when + * not applicable (non-Linux, or /etc/os-release unavailable/incomplete). + */ +function getOsScopedToolCacheSegment() { + if (!IS_LINUX) + return null; + try { + const content = external_fs_default().readFileSync('/etc/os-release', 'utf8'); + const map = {}; + for (const line of content.split('\n')) { + const eq = line.indexOf('='); + if (eq <= 0) + continue; + const k = line.slice(0, eq).trim(); + const v = line + .slice(eq + 1) + .trim() + .replace(/^"|"$/g, ''); + if (k && v) + map[k] = v; + } + const id = map['ID']; + const versionId = map['VERSION_ID']; + if (!id || !versionId) + return null; + const safe = `${id}-${versionId}`.replace(/[^A-Za-z0-9._-]/g, '_'); + return `os-${safe}`; + } + catch { + return null; + } +} +/** + * Issue #1087: apply {@link getOsScopedToolCacheSegment} to the tool-cache + * root env vars so all downstream consumers (`@actions/tool-cache`, and the + * `setup.sh` inside `actions/python-versions` release tarballs) transparently + * install/read under an OS-scoped root. + * + * IMPORTANT: only applied on self-hosted runners. GitHub-hosted runners ship + * with a pre-installed, fully-configured Python under the un-scoped path + * (`/opt/hostedtoolcache/Python/…`); redirecting them would cause a needless + * re-install of a less-configured Python (e.g. breaks numpy source builds on + * freethreaded 3.13). Detected via the documented `RUNNER_ENVIRONMENT` env var + * (`github-hosted` on hosted, `self-hosted` on self-hosted runners). + * + * Idempotent and a no-op off Linux or on hosted runners. + */ +function scopeToolCacheByOs() { + if (process.env['RUNNER_ENVIRONMENT'] === 'github-hosted') + return; + const seg = getOsScopedToolCacheSegment(); + if (!seg) + return; + for (const varName of ['RUNNER_TOOL_CACHE', 'AGENT_TOOLSDIRECTORY']) { + const cur = process.env[varName]; + if (!cur) + continue; + if (external_path_.basename(cur) === seg) + continue; // already scoped + process.env[varName] = external_path_.join(cur, seg); + } +} ;// CONCATENATED MODULE: ./node_modules/@actions/tool-cache/lib/manifest.js var manifest_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { @@ -103004,6 +103070,10 @@ async function run() { if (process.env.AGENT_TOOLSDIRECTORY?.trim()) { process.env['RUNNER_TOOL_CACHE'] = process.env['AGENT_TOOLSDIRECTORY']; } + // Issue #1087: on self-hosted Linux, scope the tool-cache root by OS id + + // version so runners that switch between distro versions do not reuse a + // Python built against a different glibc / OpenSSL. No-op on hosted runners. + scopeToolCacheByOs(); core_debug(`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`); try { const versions = resolveVersionInput(); From cfc1d2a1c571c9c0abd308b227ea83e1393d1011 Mon Sep 17 00:00:00 2001 From: gowridurgad Date: Thu, 23 Jul 2026 11:25:16 +0530 Subject: [PATCH 3/3] updated --- dist/cache-save/index.js | 24 +----------------------- dist/setup/index.js | 27 +-------------------------- src/setup-python.ts | 3 --- src/utils.ts | 24 +----------------------- 4 files changed, 3 insertions(+), 75 deletions(-) diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 4bcf0bad7..7732d1c57 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -97477,13 +97477,6 @@ function getDownloadFileName(downloadUrl) { ? path.join(tempDir, path.basename(downloadUrl)) : undefined; } -/** - * Issue #1087: on Linux, scope the tool-cache root by OS id + version so a - * self-hosted runner that switches between different distro versions - * (e.g. Ubuntu 20.04 / 24.04) does not reuse a Python built against a - * different glibc / OpenSSL. Returns e.g. "os-ubuntu-24.04", or null when - * not applicable (non-Linux, or /etc/os-release unavailable/incomplete). - */ function getOsScopedToolCacheSegment() { if (!utils_IS_LINUX) return null; @@ -97513,21 +97506,6 @@ function getOsScopedToolCacheSegment() { return null; } } -/** - * Issue #1087: apply {@link getOsScopedToolCacheSegment} to the tool-cache - * root env vars so all downstream consumers (`@actions/tool-cache`, and the - * `setup.sh` inside `actions/python-versions` release tarballs) transparently - * install/read under an OS-scoped root. - * - * IMPORTANT: only applied on self-hosted runners. GitHub-hosted runners ship - * with a pre-installed, fully-configured Python under the un-scoped path - * (`/opt/hostedtoolcache/Python/…`); redirecting them would cause a needless - * re-install of a less-configured Python (e.g. breaks numpy source builds on - * freethreaded 3.13). Detected via the documented `RUNNER_ENVIRONMENT` env var - * (`github-hosted` on hosted, `self-hosted` on self-hosted runners). - * - * Idempotent and a no-op off Linux or on hosted runners. - */ function scopeToolCacheByOs() { if (process.env['RUNNER_ENVIRONMENT'] === 'github-hosted') return; @@ -97539,7 +97517,7 @@ function scopeToolCacheByOs() { if (!cur) continue; if (path.basename(cur) === seg) - continue; // already scoped + continue; process.env[varName] = path.join(cur, seg); } } diff --git a/dist/setup/index.js b/dist/setup/index.js index ca756f8f0..2c52df87d 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -97469,13 +97469,6 @@ function getDownloadFileName(downloadUrl) { ? external_path_.join(tempDir, external_path_.basename(downloadUrl)) : undefined; } -/** - * Issue #1087: on Linux, scope the tool-cache root by OS id + version so a - * self-hosted runner that switches between different distro versions - * (e.g. Ubuntu 20.04 / 24.04) does not reuse a Python built against a - * different glibc / OpenSSL. Returns e.g. "os-ubuntu-24.04", or null when - * not applicable (non-Linux, or /etc/os-release unavailable/incomplete). - */ function getOsScopedToolCacheSegment() { if (!IS_LINUX) return null; @@ -97505,21 +97498,6 @@ function getOsScopedToolCacheSegment() { return null; } } -/** - * Issue #1087: apply {@link getOsScopedToolCacheSegment} to the tool-cache - * root env vars so all downstream consumers (`@actions/tool-cache`, and the - * `setup.sh` inside `actions/python-versions` release tarballs) transparently - * install/read under an OS-scoped root. - * - * IMPORTANT: only applied on self-hosted runners. GitHub-hosted runners ship - * with a pre-installed, fully-configured Python under the un-scoped path - * (`/opt/hostedtoolcache/Python/…`); redirecting them would cause a needless - * re-install of a less-configured Python (e.g. breaks numpy source builds on - * freethreaded 3.13). Detected via the documented `RUNNER_ENVIRONMENT` env var - * (`github-hosted` on hosted, `self-hosted` on self-hosted runners). - * - * Idempotent and a no-op off Linux or on hosted runners. - */ function scopeToolCacheByOs() { if (process.env['RUNNER_ENVIRONMENT'] === 'github-hosted') return; @@ -97531,7 +97509,7 @@ function scopeToolCacheByOs() { if (!cur) continue; if (external_path_.basename(cur) === seg) - continue; // already scoped + continue; process.env[varName] = external_path_.join(cur, seg); } } @@ -103070,9 +103048,6 @@ async function run() { if (process.env.AGENT_TOOLSDIRECTORY?.trim()) { process.env['RUNNER_TOOL_CACHE'] = process.env['AGENT_TOOLSDIRECTORY']; } - // Issue #1087: on self-hosted Linux, scope the tool-cache root by OS id + - // version so runners that switch between distro versions do not reuse a - // Python built against a different glibc / OpenSSL. No-op on hosted runners. scopeToolCacheByOs(); core_debug(`Python is expected to be installed into ${process.env['RUNNER_TOOL_CACHE']}`); try { diff --git a/src/setup-python.ts b/src/setup-python.ts index 354553b0d..72c744fac 100644 --- a/src/setup-python.ts +++ b/src/setup-python.ts @@ -87,9 +87,6 @@ async function run() { process.env['RUNNER_TOOL_CACHE'] = process.env['AGENT_TOOLSDIRECTORY']; } - // Issue #1087: on self-hosted Linux, scope the tool-cache root by OS id + - // version so runners that switch between distro versions do not reuse a - // Python built against a different glibc / OpenSSL. No-op on hosted runners. scopeToolCacheByOs(); core.debug( diff --git a/src/utils.ts b/src/utils.ts index d719c8794..6916499c9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -446,13 +446,6 @@ export function getDownloadFileName(downloadUrl: string): string | undefined { : undefined; } -/** - * Issue #1087: on Linux, scope the tool-cache root by OS id + version so a - * self-hosted runner that switches between different distro versions - * (e.g. Ubuntu 20.04 / 24.04) does not reuse a Python built against a - * different glibc / OpenSSL. Returns e.g. "os-ubuntu-24.04", or null when - * not applicable (non-Linux, or /etc/os-release unavailable/incomplete). - */ export function getOsScopedToolCacheSegment(): string | null { if (!IS_LINUX) return null; try { @@ -478,21 +471,6 @@ export function getOsScopedToolCacheSegment(): string | null { } } -/** - * Issue #1087: apply {@link getOsScopedToolCacheSegment} to the tool-cache - * root env vars so all downstream consumers (`@actions/tool-cache`, and the - * `setup.sh` inside `actions/python-versions` release tarballs) transparently - * install/read under an OS-scoped root. - * - * IMPORTANT: only applied on self-hosted runners. GitHub-hosted runners ship - * with a pre-installed, fully-configured Python under the un-scoped path - * (`/opt/hostedtoolcache/Python/…`); redirecting them would cause a needless - * re-install of a less-configured Python (e.g. breaks numpy source builds on - * freethreaded 3.13). Detected via the documented `RUNNER_ENVIRONMENT` env var - * (`github-hosted` on hosted, `self-hosted` on self-hosted runners). - * - * Idempotent and a no-op off Linux or on hosted runners. - */ export function scopeToolCacheByOs(): void { if (process.env['RUNNER_ENVIRONMENT'] === 'github-hosted') return; const seg = getOsScopedToolCacheSegment(); @@ -500,7 +478,7 @@ export function scopeToolCacheByOs(): void { for (const varName of ['RUNNER_TOOL_CACHE', 'AGENT_TOOLSDIRECTORY']) { const cur = process.env[varName]; if (!cur) continue; - if (path.basename(cur) === seg) continue; // already scoped + if (path.basename(cur) === seg) continue; process.env[varName] = path.join(cur, seg); } }