mirror of
https://github.com/astral-sh/setup-uv.git
synced 2025-12-29 11:01:17 +00:00
fix: add OS version to cache key to prevent binary incompatibility (#716)
## Summary - Adds OS name and version (e.g., `ubuntu-22.04`, `macos-14`, `windows-2022`) to cache keys to prevent binary incompatibility when GitHub updates runner images - Fixes issue where cached uv binaries compiled against older glibc/library versions fail on newer runner OS versions ## Changes - Added `getOSNameVersion()` function to `src/utils/platforms.ts` with OS-specific detection for Linux (via `/etc/os-release`), macOS (Darwin kernel version mapping), and Windows - Updated cache key format to include OS version, bumped `CACHE_VERSION` to `"2"` - Added `cache-key` output to expose the generated cache key for debugging - Added `test-cache-key-os-version` job testing across multiple OS versions - Updated `docs/caching.md` with cache key documentation Closes #703
This commit is contained in:
committed by
GitHub
parent
e8b52af86e
commit
58b6d7b303
8
src/cache/restore-cache.ts
vendored
8
src/cache/restore-cache.ts
vendored
@@ -13,15 +13,16 @@ import {
|
||||
restoreCache as shouldRestoreCache,
|
||||
workingDirectory,
|
||||
} from "../utils/inputs";
|
||||
import { getArch, getPlatform } from "../utils/platforms";
|
||||
import { getArch, getOSNameVersion, getPlatform } from "../utils/platforms";
|
||||
|
||||
export const STATE_CACHE_KEY = "cache-key";
|
||||
export const STATE_CACHE_MATCHED_KEY = "cache-matched-key";
|
||||
const CACHE_VERSION = "1";
|
||||
const CACHE_VERSION = "2";
|
||||
|
||||
export async function restoreCache(): Promise<void> {
|
||||
const cacheKey = await computeKeys();
|
||||
core.saveState(STATE_CACHE_KEY, cacheKey);
|
||||
core.setOutput("cache-key", cacheKey);
|
||||
|
||||
if (!shouldRestoreCache) {
|
||||
core.info("restore-cache is false. Skipping restore cache step.");
|
||||
@@ -72,9 +73,10 @@ async function computeKeys(): Promise<string> {
|
||||
const suffix = cacheSuffix ? `-${cacheSuffix}` : "";
|
||||
const pythonVersion = await getPythonVersion();
|
||||
const platform = await getPlatform();
|
||||
const osNameVersion = getOSNameVersion();
|
||||
const pruned = pruneCache ? "-pruned" : "";
|
||||
const python = cachePython ? "-py" : "";
|
||||
return `setup-uv-${CACHE_VERSION}-${getArch()}-${platform}-${pythonVersion}${pruned}${python}${cacheDependencyPathHash}${suffix}`;
|
||||
return `setup-uv-${CACHE_VERSION}-${getArch()}-${platform}-${osNameVersion}-${pythonVersion}${pruned}${python}${cacheDependencyPathHash}${suffix}`;
|
||||
}
|
||||
|
||||
async function getPythonVersion(): Promise<string> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import * as core from "@actions/core";
|
||||
import * as exec from "@actions/exec";
|
||||
export type Platform =
|
||||
@@ -74,3 +76,71 @@ async function isMuslOs(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns OS name and version for cache key differentiation.
|
||||
* Examples: "ubuntu-22.04", "macos-14", "windows-2022"
|
||||
* Throws if OS detection fails.
|
||||
*/
|
||||
export function getOSNameVersion(): string {
|
||||
const platform = process.platform;
|
||||
|
||||
if (platform === "linux") {
|
||||
return getLinuxOSNameVersion();
|
||||
}
|
||||
if (platform === "darwin") {
|
||||
return getMacOSNameVersion();
|
||||
}
|
||||
if (platform === "win32") {
|
||||
return getWindowsNameVersion();
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
|
||||
function getLinuxOSNameVersion(): string {
|
||||
const files = ["/etc/os-release", "/usr/lib/os-release"];
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const content = fs.readFileSync(file, "utf8");
|
||||
const id = parseOsReleaseValue(content, "ID");
|
||||
const versionId = parseOsReleaseValue(content, "VERSION_ID");
|
||||
|
||||
if (id && versionId) {
|
||||
return `${id}-${versionId}`;
|
||||
}
|
||||
} catch {
|
||||
// Try next file
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"Failed to determine Linux distribution. " +
|
||||
"Could not read /etc/os-release or /usr/lib/os-release",
|
||||
);
|
||||
}
|
||||
|
||||
function parseOsReleaseValue(content: string, key: string): string | undefined {
|
||||
const regex = new RegExp(`^${key}=["']?([^"'\\n]*)["']?$`, "m");
|
||||
const match = content.match(regex);
|
||||
return match?.[1];
|
||||
}
|
||||
|
||||
function getMacOSNameVersion(): string {
|
||||
const darwinVersion = Number.parseInt(os.release().split(".")[0], 10);
|
||||
if (Number.isNaN(darwinVersion)) {
|
||||
throw new Error(`Failed to parse macOS version from: ${os.release()}`);
|
||||
}
|
||||
const macosVersion = darwinVersion - 9;
|
||||
return `macos-${macosVersion}`;
|
||||
}
|
||||
|
||||
function getWindowsNameVersion(): string {
|
||||
const version = os.version();
|
||||
const match = version.match(/Windows(?: Server)? (\d+)/);
|
||||
if (!match) {
|
||||
throw new Error(`Failed to parse Windows version from: ${version}`);
|
||||
}
|
||||
return `windows-${match[1]}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user