mirror of
https://github.com/astral-sh/setup-uv.git
synced 2025-12-27 11:04:43 +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
51
.github/workflows/test.yml
vendored
51
.github/workflows/test.yml
vendored
@@ -385,10 +385,60 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Install latest version
|
- name: Install latest version
|
||||||
|
id: setup-uv
|
||||||
uses: ./
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
- name: Verify cache key contains alpine
|
||||||
|
run: |
|
||||||
|
echo "Cache key: $CACHE_KEY"
|
||||||
|
if echo "$CACHE_KEY" | grep -qv "alpine"; then
|
||||||
|
echo "Cache key does not contain 'alpine'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: sh
|
||||||
|
env:
|
||||||
|
CACHE_KEY: ${{ steps.setup-uv.outputs.cache-key }}
|
||||||
- run: uv sync
|
- run: uv sync
|
||||||
working-directory: __tests__/fixtures/uv-project
|
working-directory: __tests__/fixtures/uv-project
|
||||||
|
|
||||||
|
test-cache-key-os-version:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
expected-os: "ubuntu-22.04"
|
||||||
|
- os: ubuntu-24.04
|
||||||
|
expected-os: "ubuntu-24.04"
|
||||||
|
- os: macos-14
|
||||||
|
expected-os: "macos-14"
|
||||||
|
- os: macos-15
|
||||||
|
expected-os: "macos-15"
|
||||||
|
- os: windows-2022
|
||||||
|
expected-os: "windows-2022"
|
||||||
|
- os: windows-2025
|
||||||
|
expected-os: "windows-2025"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Setup uv
|
||||||
|
id: setup-uv
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
- name: Verify cache key contains OS version
|
||||||
|
run: |
|
||||||
|
echo "Cache key: $CACHE_KEY"
|
||||||
|
if [[ "$CACHE_KEY" != *"${{ matrix.expected-os }}"* ]]; then
|
||||||
|
echo "Cache key does not contain expected OS version: ${{ matrix.expected-os }}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
CACHE_KEY: ${{ steps.setup-uv.outputs.cache-key }}
|
||||||
|
|
||||||
test-setup-cache:
|
test-setup-cache:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
@@ -1002,6 +1052,7 @@ jobs:
|
|||||||
- test-python-version
|
- test-python-version
|
||||||
- test-activate-environment
|
- test-activate-environment
|
||||||
- test-musl
|
- test-musl
|
||||||
|
- test-cache-key-os-version
|
||||||
- test-cache-local
|
- test-cache-local
|
||||||
- test-cache-local-cache-disabled
|
- test-cache-local-cache-disabled
|
||||||
- test-cache-local-cache-disabled-but-explicit-path
|
- test-cache-local-cache-disabled-but-explicit-path
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ outputs:
|
|||||||
description: "The path to the installed uvx binary."
|
description: "The path to the installed uvx binary."
|
||||||
cache-hit:
|
cache-hit:
|
||||||
description: "A boolean value to indicate a cache entry was found"
|
description: "A boolean value to indicate a cache entry was found"
|
||||||
|
cache-key:
|
||||||
|
description: "The cache key used for storing/restoring the cache"
|
||||||
venv:
|
venv:
|
||||||
description: "Path to the activated venv if activate-environment is true"
|
description: "Path to the activated venv if activate-environment is true"
|
||||||
runs:
|
runs:
|
||||||
|
|||||||
77
dist/save-cache/index.js
generated
vendored
77
dist/save-cache/index.js
generated
vendored
@@ -90609,10 +90609,11 @@ const inputs_1 = __nccwpck_require__(9612);
|
|||||||
const platforms_1 = __nccwpck_require__(8361);
|
const platforms_1 = __nccwpck_require__(8361);
|
||||||
exports.STATE_CACHE_KEY = "cache-key";
|
exports.STATE_CACHE_KEY = "cache-key";
|
||||||
exports.STATE_CACHE_MATCHED_KEY = "cache-matched-key";
|
exports.STATE_CACHE_MATCHED_KEY = "cache-matched-key";
|
||||||
const CACHE_VERSION = "1";
|
const CACHE_VERSION = "2";
|
||||||
async function restoreCache() {
|
async function restoreCache() {
|
||||||
const cacheKey = await computeKeys();
|
const cacheKey = await computeKeys();
|
||||||
core.saveState(exports.STATE_CACHE_KEY, cacheKey);
|
core.saveState(exports.STATE_CACHE_KEY, cacheKey);
|
||||||
|
core.setOutput("cache-key", cacheKey);
|
||||||
if (!inputs_1.restoreCache) {
|
if (!inputs_1.restoreCache) {
|
||||||
core.info("restore-cache is false. Skipping restore cache step.");
|
core.info("restore-cache is false. Skipping restore cache step.");
|
||||||
return;
|
return;
|
||||||
@@ -90652,9 +90653,10 @@ async function computeKeys() {
|
|||||||
const suffix = inputs_1.cacheSuffix ? `-${inputs_1.cacheSuffix}` : "";
|
const suffix = inputs_1.cacheSuffix ? `-${inputs_1.cacheSuffix}` : "";
|
||||||
const pythonVersion = await getPythonVersion();
|
const pythonVersion = await getPythonVersion();
|
||||||
const platform = await (0, platforms_1.getPlatform)();
|
const platform = await (0, platforms_1.getPlatform)();
|
||||||
|
const osNameVersion = (0, platforms_1.getOSNameVersion)();
|
||||||
const pruned = inputs_1.pruneCache ? "-pruned" : "";
|
const pruned = inputs_1.pruneCache ? "-pruned" : "";
|
||||||
const python = inputs_1.cachePython ? "-py" : "";
|
const python = inputs_1.cachePython ? "-py" : "";
|
||||||
return `setup-uv-${CACHE_VERSION}-${(0, platforms_1.getArch)()}-${platform}-${pythonVersion}${pruned}${python}${cacheDependencyPathHash}${suffix}`;
|
return `setup-uv-${CACHE_VERSION}-${(0, platforms_1.getArch)()}-${platform}-${osNameVersion}-${pythonVersion}${pruned}${python}${cacheDependencyPathHash}${suffix}`;
|
||||||
}
|
}
|
||||||
async function getPythonVersion() {
|
async function getPythonVersion() {
|
||||||
if (inputs_1.pythonVersion !== "") {
|
if (inputs_1.pythonVersion !== "") {
|
||||||
@@ -91282,9 +91284,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.getArch = getArch;
|
exports.getArch = getArch;
|
||||||
exports.getPlatform = getPlatform;
|
exports.getPlatform = getPlatform;
|
||||||
|
exports.getOSNameVersion = getOSNameVersion;
|
||||||
|
const node_fs_1 = __importDefault(__nccwpck_require__(3024));
|
||||||
|
const node_os_1 = __importDefault(__nccwpck_require__(8161));
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
const exec = __importStar(__nccwpck_require__(5236));
|
const exec = __importStar(__nccwpck_require__(5236));
|
||||||
function getArch() {
|
function getArch() {
|
||||||
@@ -91342,6 +91350,63 @@ async function isMuslOs() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns OS name and version for cache key differentiation.
|
||||||
|
* Examples: "ubuntu-22.04", "macos-14", "windows-2022"
|
||||||
|
* Throws if OS detection fails.
|
||||||
|
*/
|
||||||
|
function getOSNameVersion() {
|
||||||
|
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() {
|
||||||
|
const files = ["/etc/os-release", "/usr/lib/os-release"];
|
||||||
|
for (const file of files) {
|
||||||
|
try {
|
||||||
|
const content = node_fs_1.default.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, key) {
|
||||||
|
const regex = new RegExp(`^${key}=["']?([^"'\\n]*)["']?$`, "m");
|
||||||
|
const match = content.match(regex);
|
||||||
|
return match?.[1];
|
||||||
|
}
|
||||||
|
function getMacOSNameVersion() {
|
||||||
|
const darwinVersion = Number.parseInt(node_os_1.default.release().split(".")[0], 10);
|
||||||
|
if (Number.isNaN(darwinVersion)) {
|
||||||
|
throw new Error(`Failed to parse macOS version from: ${node_os_1.default.release()}`);
|
||||||
|
}
|
||||||
|
const macosVersion = darwinVersion - 9;
|
||||||
|
return `macos-${macosVersion}`;
|
||||||
|
}
|
||||||
|
function getWindowsNameVersion() {
|
||||||
|
const version = node_os_1.default.version();
|
||||||
|
const match = version.match(/Windows(?: Server)? (\d+)/);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(`Failed to parse Windows version from: ${version}`);
|
||||||
|
}
|
||||||
|
return `windows-${match[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@@ -91482,6 +91547,14 @@ module.exports = require("node:fs");
|
|||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 8161:
|
||||||
|
/***/ ((module) => {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
module.exports = require("node:os");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
/***/ 6760:
|
/***/ 6760:
|
||||||
/***/ ((module) => {
|
/***/ ((module) => {
|
||||||
|
|
||||||
|
|||||||
77
dist/setup/index.js
generated
vendored
77
dist/setup/index.js
generated
vendored
@@ -91512,10 +91512,11 @@ const inputs_1 = __nccwpck_require__(9612);
|
|||||||
const platforms_1 = __nccwpck_require__(8361);
|
const platforms_1 = __nccwpck_require__(8361);
|
||||||
exports.STATE_CACHE_KEY = "cache-key";
|
exports.STATE_CACHE_KEY = "cache-key";
|
||||||
exports.STATE_CACHE_MATCHED_KEY = "cache-matched-key";
|
exports.STATE_CACHE_MATCHED_KEY = "cache-matched-key";
|
||||||
const CACHE_VERSION = "1";
|
const CACHE_VERSION = "2";
|
||||||
async function restoreCache() {
|
async function restoreCache() {
|
||||||
const cacheKey = await computeKeys();
|
const cacheKey = await computeKeys();
|
||||||
core.saveState(exports.STATE_CACHE_KEY, cacheKey);
|
core.saveState(exports.STATE_CACHE_KEY, cacheKey);
|
||||||
|
core.setOutput("cache-key", cacheKey);
|
||||||
if (!inputs_1.restoreCache) {
|
if (!inputs_1.restoreCache) {
|
||||||
core.info("restore-cache is false. Skipping restore cache step.");
|
core.info("restore-cache is false. Skipping restore cache step.");
|
||||||
return;
|
return;
|
||||||
@@ -91555,9 +91556,10 @@ async function computeKeys() {
|
|||||||
const suffix = inputs_1.cacheSuffix ? `-${inputs_1.cacheSuffix}` : "";
|
const suffix = inputs_1.cacheSuffix ? `-${inputs_1.cacheSuffix}` : "";
|
||||||
const pythonVersion = await getPythonVersion();
|
const pythonVersion = await getPythonVersion();
|
||||||
const platform = await (0, platforms_1.getPlatform)();
|
const platform = await (0, platforms_1.getPlatform)();
|
||||||
|
const osNameVersion = (0, platforms_1.getOSNameVersion)();
|
||||||
const pruned = inputs_1.pruneCache ? "-pruned" : "";
|
const pruned = inputs_1.pruneCache ? "-pruned" : "";
|
||||||
const python = inputs_1.cachePython ? "-py" : "";
|
const python = inputs_1.cachePython ? "-py" : "";
|
||||||
return `setup-uv-${CACHE_VERSION}-${(0, platforms_1.getArch)()}-${platform}-${pythonVersion}${pruned}${python}${cacheDependencyPathHash}${suffix}`;
|
return `setup-uv-${CACHE_VERSION}-${(0, platforms_1.getArch)()}-${platform}-${osNameVersion}-${pythonVersion}${pruned}${python}${cacheDependencyPathHash}${suffix}`;
|
||||||
}
|
}
|
||||||
async function getPythonVersion() {
|
async function getPythonVersion() {
|
||||||
if (inputs_1.pythonVersion !== "") {
|
if (inputs_1.pythonVersion !== "") {
|
||||||
@@ -96791,9 +96793,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.getArch = getArch;
|
exports.getArch = getArch;
|
||||||
exports.getPlatform = getPlatform;
|
exports.getPlatform = getPlatform;
|
||||||
|
exports.getOSNameVersion = getOSNameVersion;
|
||||||
|
const node_fs_1 = __importDefault(__nccwpck_require__(3024));
|
||||||
|
const node_os_1 = __importDefault(__nccwpck_require__(8161));
|
||||||
const core = __importStar(__nccwpck_require__(7484));
|
const core = __importStar(__nccwpck_require__(7484));
|
||||||
const exec = __importStar(__nccwpck_require__(5236));
|
const exec = __importStar(__nccwpck_require__(5236));
|
||||||
function getArch() {
|
function getArch() {
|
||||||
@@ -96851,6 +96859,63 @@ async function isMuslOs() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns OS name and version for cache key differentiation.
|
||||||
|
* Examples: "ubuntu-22.04", "macos-14", "windows-2022"
|
||||||
|
* Throws if OS detection fails.
|
||||||
|
*/
|
||||||
|
function getOSNameVersion() {
|
||||||
|
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() {
|
||||||
|
const files = ["/etc/os-release", "/usr/lib/os-release"];
|
||||||
|
for (const file of files) {
|
||||||
|
try {
|
||||||
|
const content = node_fs_1.default.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, key) {
|
||||||
|
const regex = new RegExp(`^${key}=["']?([^"'\\n]*)["']?$`, "m");
|
||||||
|
const match = content.match(regex);
|
||||||
|
return match?.[1];
|
||||||
|
}
|
||||||
|
function getMacOSNameVersion() {
|
||||||
|
const darwinVersion = Number.parseInt(node_os_1.default.release().split(".")[0], 10);
|
||||||
|
if (Number.isNaN(darwinVersion)) {
|
||||||
|
throw new Error(`Failed to parse macOS version from: ${node_os_1.default.release()}`);
|
||||||
|
}
|
||||||
|
const macosVersion = darwinVersion - 9;
|
||||||
|
return `macos-${macosVersion}`;
|
||||||
|
}
|
||||||
|
function getWindowsNameVersion() {
|
||||||
|
const version = node_os_1.default.version();
|
||||||
|
const match = version.match(/Windows(?: Server)? (\d+)/);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(`Failed to parse Windows version from: ${version}`);
|
||||||
|
}
|
||||||
|
return `windows-${match[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@@ -97216,6 +97281,14 @@ module.exports = require("node:fs");
|
|||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 8161:
|
||||||
|
/***/ ((module) => {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
module.exports = require("node:os");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
/***/ 6760:
|
/***/ 6760:
|
||||||
/***/ ((module) => {
|
/***/ ((module) => {
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,34 @@
|
|||||||
|
|
||||||
This document covers all caching-related configuration options for setup-uv.
|
This document covers all caching-related configuration options for setup-uv.
|
||||||
|
|
||||||
|
## Cache key
|
||||||
|
|
||||||
|
The cache key is automatically generated based on:
|
||||||
|
|
||||||
|
- **Architecture**: CPU architecture (e.g., `x86_64`, `aarch64`)
|
||||||
|
- **Platform**: OS platform type (e.g., `unknown-linux-gnu`, `unknown-linux-musl`, `apple-darwin`,
|
||||||
|
`pc-windows-msvc`)
|
||||||
|
- **OS version**: OS name and version (e.g., `ubuntu-22.04`, `macos-14`, `windows-2022`)
|
||||||
|
- **Python version**: The Python version in use
|
||||||
|
- **Cache options**: Whether pruning and Python caching are enabled
|
||||||
|
- **Dependency hash**: Hash of files matching `cache-dependency-glob`
|
||||||
|
- **Suffix**: Optional `cache-suffix` if provided
|
||||||
|
|
||||||
|
Including the OS version ensures that caches are not shared between different OS versions,
|
||||||
|
preventing binary incompatibility issues when runner images change.
|
||||||
|
|
||||||
|
The computed cache key is available as the `cache-key` output:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Setup uv
|
||||||
|
id: setup-uv
|
||||||
|
uses: astral-sh/setup-uv@v7
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
- name: Print cache key
|
||||||
|
run: echo "Cache key: ${{ steps.setup-uv.outputs.cache-key }}"
|
||||||
|
```
|
||||||
|
|
||||||
## Enable caching
|
## Enable caching
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
|
|||||||
8
src/cache/restore-cache.ts
vendored
8
src/cache/restore-cache.ts
vendored
@@ -13,15 +13,16 @@ import {
|
|||||||
restoreCache as shouldRestoreCache,
|
restoreCache as shouldRestoreCache,
|
||||||
workingDirectory,
|
workingDirectory,
|
||||||
} from "../utils/inputs";
|
} 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_KEY = "cache-key";
|
||||||
export const STATE_CACHE_MATCHED_KEY = "cache-matched-key";
|
export const STATE_CACHE_MATCHED_KEY = "cache-matched-key";
|
||||||
const CACHE_VERSION = "1";
|
const CACHE_VERSION = "2";
|
||||||
|
|
||||||
export async function restoreCache(): Promise<void> {
|
export async function restoreCache(): Promise<void> {
|
||||||
const cacheKey = await computeKeys();
|
const cacheKey = await computeKeys();
|
||||||
core.saveState(STATE_CACHE_KEY, cacheKey);
|
core.saveState(STATE_CACHE_KEY, cacheKey);
|
||||||
|
core.setOutput("cache-key", cacheKey);
|
||||||
|
|
||||||
if (!shouldRestoreCache) {
|
if (!shouldRestoreCache) {
|
||||||
core.info("restore-cache is false. Skipping restore cache step.");
|
core.info("restore-cache is false. Skipping restore cache step.");
|
||||||
@@ -72,9 +73,10 @@ async function computeKeys(): Promise<string> {
|
|||||||
const suffix = cacheSuffix ? `-${cacheSuffix}` : "";
|
const suffix = cacheSuffix ? `-${cacheSuffix}` : "";
|
||||||
const pythonVersion = await getPythonVersion();
|
const pythonVersion = await getPythonVersion();
|
||||||
const platform = await getPlatform();
|
const platform = await getPlatform();
|
||||||
|
const osNameVersion = getOSNameVersion();
|
||||||
const pruned = pruneCache ? "-pruned" : "";
|
const pruned = pruneCache ? "-pruned" : "";
|
||||||
const python = cachePython ? "-py" : "";
|
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> {
|
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 core from "@actions/core";
|
||||||
import * as exec from "@actions/exec";
|
import * as exec from "@actions/exec";
|
||||||
export type Platform =
|
export type Platform =
|
||||||
@@ -74,3 +76,71 @@ async function isMuslOs(): Promise<boolean> {
|
|||||||
return false;
|
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