mirror of
https://gitea.com/actions/setup-python.git
synced 2026-06-20 02:31:17 +00:00
Add RHEL support and include Linux distro in cache keys (#1323)
* Add RHEL support for manifest matching and OS detection * update dist * make cache keys distro-aware and key RHEL by major version * Normalize RHEL OS detection and improve cache key consistency * Refactor OS info retrieval to use getOSInfo and handle null cases for improved reliability
This commit is contained in:
@@ -190,19 +190,12 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
|
|||||||
|
|
||||||
restoredKeys.forEach(restoredKey => {
|
restoredKeys.forEach(restoredKey => {
|
||||||
if (restoredKey) {
|
if (restoredKey) {
|
||||||
if (process.platform === 'linux' && packageManager === 'pip') {
|
const osSegment =
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
process.platform === 'linux' ? '-20.04-Ubuntu' : '';
|
||||||
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-${process.arch}-20.04-Ubuntu-python-${pythonVersion}-${packageManager}-${fileHash}`
|
const versionSuffix = packageManager === 'poetry' ? '-v2' : '';
|
||||||
);
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
} else if (packageManager === 'poetry') {
|
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${pythonVersion}-${packageManager}${versionSuffix}-${fileHash}`
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
);
|
||||||
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-${process.arch}-python-${pythonVersion}-${packageManager}-v2-${fileHash}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
|
||||||
`Cache restored from key: setup-python-${process.env['RUNNER_OS']}-${process.arch}-python-${pythonVersion}-${packageManager}-${fileHash}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`${packageManager} cache is not found`
|
`${packageManager} cache is not found`
|
||||||
|
|||||||
Vendored
+5342
File diff suppressed because it is too large
Load Diff
Vendored
+114
-19
@@ -53813,6 +53813,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|||||||
exports.State = void 0;
|
exports.State = void 0;
|
||||||
const cache = __importStar(__nccwpck_require__(5116));
|
const cache = __importStar(__nccwpck_require__(5116));
|
||||||
const core = __importStar(__nccwpck_require__(37484));
|
const core = __importStar(__nccwpck_require__(37484));
|
||||||
|
const utils_1 = __nccwpck_require__(71798);
|
||||||
const constants_1 = __nccwpck_require__(10565);
|
const constants_1 = __nccwpck_require__(10565);
|
||||||
var State;
|
var State;
|
||||||
(function (State) {
|
(function (State) {
|
||||||
@@ -53829,6 +53830,28 @@ class CacheDistributor {
|
|||||||
this.cacheDependencyPath = cacheDependencyPath;
|
this.cacheDependencyPath = cacheDependencyPath;
|
||||||
}
|
}
|
||||||
async handleLoadedCache() { }
|
async handleLoadedCache() { }
|
||||||
|
/**
|
||||||
|
* Builds the Linux distro portion of a cache key (e.g. `-26.04-Ubuntu`, `-9-rhel`).
|
||||||
|
* RHEL is keyed by major version since it ships one ABI-stable artifact per major.
|
||||||
|
*/
|
||||||
|
async getLinuxInfoKeySegment() {
|
||||||
|
if (!utils_1.IS_LINUX) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const osInfo = await (0, utils_1.getOSInfo)();
|
||||||
|
if (!osInfo) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
// lsb_release reports RHEL as "RedHatEnterpriseLinux" while /etc/os-release
|
||||||
|
// reports it as "rhel"; normalize both to "rhel" so the key is consistent.
|
||||||
|
const normalizedName = osInfo.osName.toLowerCase();
|
||||||
|
const isRhel = normalizedName === 'rhel' || normalizedName.includes('redhat');
|
||||||
|
const osName = isRhel ? 'rhel' : osInfo.osName;
|
||||||
|
const osVersion = isRhel
|
||||||
|
? osInfo.osVersion.split('.')[0]
|
||||||
|
: osInfo.osVersion;
|
||||||
|
return `-${osVersion}-${osName}`;
|
||||||
|
}
|
||||||
async restoreCache() {
|
async restoreCache() {
|
||||||
const { primaryKey, restoreKey } = await this.computeKeys();
|
const { primaryKey, restoreKey } = await this.computeKeys();
|
||||||
if (primaryKey.endsWith('-')) {
|
if (primaryKey.endsWith('-')) {
|
||||||
@@ -54011,17 +54034,9 @@ class PipCache extends cache_distributor_1.default {
|
|||||||
async computeKeys() {
|
async computeKeys() {
|
||||||
const hash = (await glob.hashFiles(this.cacheDependencyPath)) ||
|
const hash = (await glob.hashFiles(this.cacheDependencyPath)) ||
|
||||||
(await glob.hashFiles(this.cacheDependencyBackupPath));
|
(await glob.hashFiles(this.cacheDependencyBackupPath));
|
||||||
let primaryKey = '';
|
const osSegment = await this.getLinuxInfoKeySegment();
|
||||||
let restoreKey = '';
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
||||||
if (utils_1.IS_LINUX) {
|
const restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}`;
|
||||||
const osInfo = await (0, utils_1.getLinuxInfo)();
|
|
||||||
primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${osInfo.osVersion}-${osInfo.osName}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
|
||||||
restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${osInfo.osVersion}-${osInfo.osName}-python-${this.pythonVersion}-${this.packageManager}`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
|
||||||
restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}`;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
restoreKey: [restoreKey]
|
restoreKey: [restoreKey]
|
||||||
@@ -54105,7 +54120,8 @@ class PipenvCache extends cache_distributor_1.default {
|
|||||||
}
|
}
|
||||||
async computeKeys() {
|
async computeKeys() {
|
||||||
const hash = await glob.hashFiles(this.patterns);
|
const hash = await glob.hashFiles(this.patterns);
|
||||||
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
const osSegment = await this.getLinuxInfoKeySegment();
|
||||||
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
||||||
const restoreKey = undefined;
|
const restoreKey = undefined;
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
@@ -54197,8 +54213,9 @@ class PoetryCache extends cache_distributor_1.default {
|
|||||||
}
|
}
|
||||||
async computeKeys() {
|
async computeKeys() {
|
||||||
const hash = await glob.hashFiles(this.patterns);
|
const hash = await glob.hashFiles(this.patterns);
|
||||||
|
const osSegment = await this.getLinuxInfoKeySegment();
|
||||||
// "v2" is here to invalidate old caches of this cache distributor, which were created broken:
|
// "v2" is here to invalidate old caches of this cache distributor, which were created broken:
|
||||||
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}-v2-${hash}`;
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}-v2-${hash}`;
|
||||||
const restoreKey = undefined;
|
const restoreKey = undefined;
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
@@ -55275,6 +55292,8 @@ const core = __importStar(__nccwpck_require__(37484));
|
|||||||
const tc = __importStar(__nccwpck_require__(33472));
|
const tc = __importStar(__nccwpck_require__(33472));
|
||||||
const exec = __importStar(__nccwpck_require__(95236));
|
const exec = __importStar(__nccwpck_require__(95236));
|
||||||
const httpm = __importStar(__nccwpck_require__(54844));
|
const httpm = __importStar(__nccwpck_require__(54844));
|
||||||
|
const fs = __importStar(__nccwpck_require__(79896));
|
||||||
|
const semver = __importStar(__nccwpck_require__(62088));
|
||||||
const utils_1 = __nccwpck_require__(71798);
|
const utils_1 = __nccwpck_require__(71798);
|
||||||
const TOKEN = core.getInput('token');
|
const TOKEN = core.getInput('token');
|
||||||
const AUTH = !TOKEN ? undefined : `token ${TOKEN}`;
|
const AUTH = !TOKEN ? undefined : `token ${TOKEN}`;
|
||||||
@@ -55282,10 +55301,69 @@ const MANIFEST_REPO_OWNER = 'actions';
|
|||||||
const MANIFEST_REPO_NAME = 'python-versions';
|
const MANIFEST_REPO_NAME = 'python-versions';
|
||||||
const MANIFEST_REPO_BRANCH = 'main';
|
const MANIFEST_REPO_BRANCH = 'main';
|
||||||
exports.MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
|
exports.MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
|
||||||
|
function getLinuxOsRelease() {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync('/etc/os-release', 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
let id = '';
|
||||||
|
let versionId = '';
|
||||||
|
for (const line of lines) {
|
||||||
|
const parts = line.split('=');
|
||||||
|
if (parts.length === 2) {
|
||||||
|
const key = parts[0].trim();
|
||||||
|
const value = parts[1].trim().replace(/^"/, '').replace(/"$/, '');
|
||||||
|
if (key === 'ID')
|
||||||
|
id = value;
|
||||||
|
if (key === 'VERSION_ID')
|
||||||
|
versionId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (id && versionId) {
|
||||||
|
return { id, versionId };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function findRhelRelease(semanticVersionSpec, architecture, manifest, osVersion) {
|
||||||
|
for (const candidate of manifest) {
|
||||||
|
const version = candidate.version;
|
||||||
|
core.debug(`check ${version} satisfies ${semanticVersionSpec}`);
|
||||||
|
if (!semver.satisfies(version, semanticVersionSpec))
|
||||||
|
continue;
|
||||||
|
const file = candidate.files.find(item => {
|
||||||
|
core.debug(`${item.arch}===${architecture} && ${item.platform}===rhel && ${item.platform_version}===${osVersion}`);
|
||||||
|
const archMatch = item.arch === architecture;
|
||||||
|
const platformMatch = item.platform === 'rhel';
|
||||||
|
const versionMatch = !item.platform_version ||
|
||||||
|
item.platform_version === osVersion ||
|
||||||
|
osVersion.startsWith(item.platform_version);
|
||||||
|
return archMatch && platformMatch && versionMatch;
|
||||||
|
});
|
||||||
|
if (file) {
|
||||||
|
core.debug(`matched ${candidate.version}`);
|
||||||
|
const result = Object.assign({}, candidate);
|
||||||
|
result.files = [file];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
async function findReleaseFromManifest(semanticVersionSpec, architecture, manifest) {
|
async function findReleaseFromManifest(semanticVersionSpec, architecture, manifest) {
|
||||||
if (!manifest) {
|
if (!manifest) {
|
||||||
manifest = await getManifest();
|
manifest = await getManifest();
|
||||||
}
|
}
|
||||||
|
// On RHEL, tc.findFromManifest() won't match because os.platform() returns 'linux'
|
||||||
|
// but manifest entries use platform 'rhel'. Use custom filtering for RHEL.
|
||||||
|
if (utils_1.IS_LINUX) {
|
||||||
|
const osRelease = getLinuxOsRelease();
|
||||||
|
if (osRelease && osRelease.id === 'rhel') {
|
||||||
|
core.debug(`Detected RHEL ${osRelease.versionId}, using custom manifest filtering`);
|
||||||
|
return findRhelRelease(semanticVersionSpec, architecture, manifest, osRelease.versionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
const foundRelease = await tc.findFromManifest(semanticVersionSpec, false, manifest, architecture);
|
const foundRelease = await tc.findFromManifest(semanticVersionSpec, false, manifest, architecture);
|
||||||
return foundRelease;
|
return foundRelease;
|
||||||
}
|
}
|
||||||
@@ -55743,12 +55821,29 @@ async function getMacOSInfo() {
|
|||||||
return { osName: 'macOS', osVersion: macOSVersion };
|
return { osName: 'macOS', osVersion: macOSVersion };
|
||||||
}
|
}
|
||||||
async function getLinuxInfo() {
|
async function getLinuxInfo() {
|
||||||
const { stdout } = await exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], {
|
try {
|
||||||
silent: true
|
const { stdout } = await exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], {
|
||||||
});
|
silent: true
|
||||||
const [osName, osVersion] = stdout.trim().split('\n');
|
});
|
||||||
core.debug(`OS Name: ${osName}, Version: ${osVersion}`);
|
const [osName, osVersion] = stdout.trim().split('\n');
|
||||||
return { osName: osName, osVersion: osVersion };
|
core.debug(`OS Name: ${osName}, Version: ${osVersion}`);
|
||||||
|
return { osName, osVersion };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
core.debug(`lsb_release failed (${err.message}). Falling back to /etc/os-release.`);
|
||||||
|
const osReleaseContent = fs_1.default.readFileSync('/etc/os-release', 'utf8');
|
||||||
|
const osInfo = {};
|
||||||
|
osReleaseContent.split('\n').forEach(line => {
|
||||||
|
const [key, value] = line.split('=');
|
||||||
|
if (key && value) {
|
||||||
|
osInfo[key.trim()] = value.trim().replace(/"/g, '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const osName = osInfo['ID'] || 'Linux';
|
||||||
|
const osVersion = osInfo['VERSION_ID'] || '';
|
||||||
|
core.debug(`OS Name: ${osName}, Version: ${osVersion}`);
|
||||||
|
return { osName, osVersion };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async function getOSInfo() {
|
async function getOSInfo() {
|
||||||
let osInfo;
|
let osInfo;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as cache from '@actions/cache';
|
import * as cache from '@actions/cache';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
|
import {getOSInfo, IS_LINUX} from '../utils';
|
||||||
import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants';
|
import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants';
|
||||||
|
|
||||||
export enum State {
|
export enum State {
|
||||||
@@ -22,6 +23,33 @@ abstract class CacheDistributor {
|
|||||||
}>;
|
}>;
|
||||||
protected async handleLoadedCache() {}
|
protected async handleLoadedCache() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the Linux distro portion of a cache key (e.g. `-26.04-Ubuntu`, `-9-rhel`).
|
||||||
|
* RHEL is keyed by major version since it ships one ABI-stable artifact per major.
|
||||||
|
*/
|
||||||
|
protected async getLinuxInfoKeySegment(): Promise<string> {
|
||||||
|
if (!IS_LINUX) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const osInfo = await getOSInfo();
|
||||||
|
if (!osInfo) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// lsb_release reports RHEL as "RedHatEnterpriseLinux" while /etc/os-release
|
||||||
|
// reports it as "rhel"; normalize both to "rhel" so the key is consistent.
|
||||||
|
const normalizedName = osInfo.osName.toLowerCase();
|
||||||
|
const isRhel =
|
||||||
|
normalizedName === 'rhel' || normalizedName.includes('redhat');
|
||||||
|
const osName = isRhel ? 'rhel' : osInfo.osName;
|
||||||
|
const osVersion = isRhel
|
||||||
|
? osInfo.osVersion.split('.')[0]
|
||||||
|
: osInfo.osVersion;
|
||||||
|
|
||||||
|
return `-${osVersion}-${osName}`;
|
||||||
|
}
|
||||||
|
|
||||||
public async restoreCache() {
|
public async restoreCache() {
|
||||||
const {primaryKey, restoreKey} = await this.computeKeys();
|
const {primaryKey, restoreKey} = await this.computeKeys();
|
||||||
if (primaryKey.endsWith('-')) {
|
if (primaryKey.endsWith('-')) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import * as path from 'path';
|
|||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
|
||||||
import CacheDistributor from './cache-distributor';
|
import CacheDistributor from './cache-distributor';
|
||||||
import {getLinuxInfo, IS_LINUX, IS_WINDOWS} from '../utils';
|
import {IS_WINDOWS} from '../utils';
|
||||||
import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants';
|
import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants';
|
||||||
|
|
||||||
class PipCache extends CacheDistributor {
|
class PipCache extends CacheDistributor {
|
||||||
@@ -62,17 +62,9 @@ class PipCache extends CacheDistributor {
|
|||||||
const hash =
|
const hash =
|
||||||
(await glob.hashFiles(this.cacheDependencyPath)) ||
|
(await glob.hashFiles(this.cacheDependencyPath)) ||
|
||||||
(await glob.hashFiles(this.cacheDependencyBackupPath));
|
(await glob.hashFiles(this.cacheDependencyBackupPath));
|
||||||
let primaryKey = '';
|
const osSegment = await this.getLinuxInfoKeySegment();
|
||||||
let restoreKey = '';
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
||||||
|
const restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}`;
|
||||||
if (IS_LINUX) {
|
|
||||||
const osInfo = await getLinuxInfo();
|
|
||||||
primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${osInfo.osVersion}-${osInfo.osName}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
|
||||||
restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-${osInfo.osVersion}-${osInfo.osName}-python-${this.pythonVersion}-${this.packageManager}`;
|
|
||||||
} else {
|
|
||||||
primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
|
||||||
restoreKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ class PipenvCache extends CacheDistributor {
|
|||||||
|
|
||||||
protected async computeKeys() {
|
protected async computeKeys() {
|
||||||
const hash = await glob.hashFiles(this.patterns);
|
const hash = await glob.hashFiles(this.patterns);
|
||||||
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
const osSegment = await this.getLinuxInfoKeySegment();
|
||||||
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
|
||||||
const restoreKey = undefined;
|
const restoreKey = undefined;
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
|
|||||||
@@ -46,8 +46,9 @@ class PoetryCache extends CacheDistributor {
|
|||||||
|
|
||||||
protected async computeKeys() {
|
protected async computeKeys() {
|
||||||
const hash = await glob.hashFiles(this.patterns);
|
const hash = await glob.hashFiles(this.patterns);
|
||||||
|
const osSegment = await this.getLinuxInfoKeySegment();
|
||||||
// "v2" is here to invalidate old caches of this cache distributor, which were created broken:
|
// "v2" is here to invalidate old caches of this cache distributor, which were created broken:
|
||||||
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-python-${this.pythonVersion}-${this.packageManager}-v2-${hash}`;
|
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}${osSegment}-python-${this.pythonVersion}-${this.packageManager}-v2-${hash}`;
|
||||||
const restoreKey = undefined;
|
const restoreKey = undefined;
|
||||||
return {
|
return {
|
||||||
primaryKey,
|
primaryKey,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import * as core from '@actions/core';
|
|||||||
import * as tc from '@actions/tool-cache';
|
import * as tc from '@actions/tool-cache';
|
||||||
import * as exec from '@actions/exec';
|
import * as exec from '@actions/exec';
|
||||||
import * as httpm from '@actions/http-client';
|
import * as httpm from '@actions/http-client';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as semver from 'semver';
|
||||||
import {ExecOptions} from '@actions/exec/lib/interfaces';
|
import {ExecOptions} from '@actions/exec/lib/interfaces';
|
||||||
import {IS_WINDOWS, IS_LINUX, getDownloadFileName} from './utils';
|
import {IS_WINDOWS, IS_LINUX, getDownloadFileName} from './utils';
|
||||||
import {IToolRelease} from '@actions/tool-cache';
|
import {IToolRelease} from '@actions/tool-cache';
|
||||||
@@ -14,6 +16,70 @@ const MANIFEST_REPO_NAME = 'python-versions';
|
|||||||
const MANIFEST_REPO_BRANCH = 'main';
|
const MANIFEST_REPO_BRANCH = 'main';
|
||||||
export const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
|
export const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`;
|
||||||
|
|
||||||
|
interface LinuxOsRelease {
|
||||||
|
id: string;
|
||||||
|
versionId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLinuxOsRelease(): LinuxOsRelease | null {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync('/etc/os-release', 'utf8');
|
||||||
|
const lines = content.split('\n');
|
||||||
|
let id = '';
|
||||||
|
let versionId = '';
|
||||||
|
for (const line of lines) {
|
||||||
|
const parts = line.split('=');
|
||||||
|
if (parts.length === 2) {
|
||||||
|
const key = parts[0].trim();
|
||||||
|
const value = parts[1].trim().replace(/^"/, '').replace(/"$/, '');
|
||||||
|
if (key === 'ID') id = value;
|
||||||
|
if (key === 'VERSION_ID') versionId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (id && versionId) {
|
||||||
|
return {id, versionId};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findRhelRelease(
|
||||||
|
semanticVersionSpec: string,
|
||||||
|
architecture: string,
|
||||||
|
manifest: tc.IToolRelease[],
|
||||||
|
osVersion: string
|
||||||
|
): tc.IToolRelease | undefined {
|
||||||
|
for (const candidate of manifest) {
|
||||||
|
const version = candidate.version;
|
||||||
|
core.debug(`check ${version} satisfies ${semanticVersionSpec}`);
|
||||||
|
|
||||||
|
if (!semver.satisfies(version, semanticVersionSpec)) continue;
|
||||||
|
|
||||||
|
const file = candidate.files.find(item => {
|
||||||
|
core.debug(
|
||||||
|
`${item.arch}===${architecture} && ${item.platform}===rhel && ${item.platform_version}===${osVersion}`
|
||||||
|
);
|
||||||
|
const archMatch = item.arch === architecture;
|
||||||
|
const platformMatch = item.platform === 'rhel';
|
||||||
|
const versionMatch =
|
||||||
|
!item.platform_version ||
|
||||||
|
item.platform_version === osVersion ||
|
||||||
|
osVersion.startsWith(item.platform_version);
|
||||||
|
return archMatch && platformMatch && versionMatch;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
core.debug(`matched ${candidate.version}`);
|
||||||
|
const result = Object.assign({}, candidate);
|
||||||
|
result.files = [file];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export async function findReleaseFromManifest(
|
export async function findReleaseFromManifest(
|
||||||
semanticVersionSpec: string,
|
semanticVersionSpec: string,
|
||||||
architecture: string,
|
architecture: string,
|
||||||
@@ -23,6 +89,23 @@ export async function findReleaseFromManifest(
|
|||||||
manifest = await getManifest();
|
manifest = await getManifest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On RHEL, tc.findFromManifest() won't match because os.platform() returns 'linux'
|
||||||
|
// but manifest entries use platform 'rhel'. Use custom filtering for RHEL.
|
||||||
|
if (IS_LINUX) {
|
||||||
|
const osRelease = getLinuxOsRelease();
|
||||||
|
if (osRelease && osRelease.id === 'rhel') {
|
||||||
|
core.debug(
|
||||||
|
`Detected RHEL ${osRelease.versionId}, using custom manifest filtering`
|
||||||
|
);
|
||||||
|
return findRhelRelease(
|
||||||
|
semanticVersionSpec,
|
||||||
|
architecture,
|
||||||
|
manifest,
|
||||||
|
osRelease.versionId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const foundRelease = await tc.findFromManifest(
|
const foundRelease = await tc.findFromManifest(
|
||||||
semanticVersionSpec,
|
semanticVersionSpec,
|
||||||
false,
|
false,
|
||||||
@@ -32,6 +115,7 @@ export async function findReleaseFromManifest(
|
|||||||
|
|
||||||
return foundRelease;
|
return foundRelease;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIToolRelease(obj: any): obj is IToolRelease {
|
function isIToolRelease(obj: any): obj is IToolRelease {
|
||||||
return (
|
return (
|
||||||
typeof obj === 'object' &&
|
typeof obj === 'object' &&
|
||||||
@@ -48,6 +132,7 @@ function isIToolRelease(obj: any): obj is IToolRelease {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getManifest(): Promise<tc.IToolRelease[]> {
|
export async function getManifest(): Promise<tc.IToolRelease[]> {
|
||||||
try {
|
try {
|
||||||
const repoManifest = await getManifestFromRepo();
|
const repoManifest = await getManifestFromRepo();
|
||||||
|
|||||||
+29
-6
@@ -173,15 +173,38 @@ async function getMacOSInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getLinuxInfo() {
|
export async function getLinuxInfo() {
|
||||||
const {stdout} = await exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], {
|
try {
|
||||||
silent: true
|
const {stdout} = await exec.getExecOutput(
|
||||||
});
|
'lsb_release',
|
||||||
|
['-i', '-r', '-s'],
|
||||||
|
{
|
||||||
|
silent: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const [osName, osVersion] = stdout.trim().split('\n');
|
const [osName, osVersion] = stdout.trim().split('\n');
|
||||||
|
core.debug(`OS Name: ${osName}, Version: ${osVersion}`);
|
||||||
|
return {osName, osVersion};
|
||||||
|
} catch (err) {
|
||||||
|
core.debug(
|
||||||
|
`lsb_release failed (${(err as Error).message}). Falling back to /etc/os-release.`
|
||||||
|
);
|
||||||
|
|
||||||
core.debug(`OS Name: ${osName}, Version: ${osVersion}`);
|
const osReleaseContent = fs.readFileSync('/etc/os-release', 'utf8');
|
||||||
|
const osInfo: {[key: string]: string} = {};
|
||||||
|
|
||||||
return {osName: osName, osVersion: osVersion};
|
osReleaseContent.split('\n').forEach(line => {
|
||||||
|
const [key, value] = line.split('=');
|
||||||
|
if (key && value) {
|
||||||
|
osInfo[key.trim()] = value.trim().replace(/"/g, '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const osName = osInfo['ID'] || 'Linux';
|
||||||
|
const osVersion = osInfo['VERSION_ID'] || '';
|
||||||
|
core.debug(`OS Name: ${osName}, Version: ${osVersion}`);
|
||||||
|
return {osName, osVersion};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOSInfo() {
|
export async function getOSInfo() {
|
||||||
|
|||||||
Reference in New Issue
Block a user