mirror of
https://github.com/moonrepo/setup-rust.git
synced 2025-04-19 09:08:43 +00:00
243 lines
5.9 KiB
TypeScript
243 lines
5.9 KiB
TypeScript
import crypto from 'node:crypto';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import * as cache from '@actions/cache';
|
|
import * as core from '@actions/core';
|
|
import * as exec from '@actions/exec';
|
|
import * as glob from '@actions/glob';
|
|
import * as tc from '@actions/tool-cache';
|
|
import { rmrf } from './fs';
|
|
import { RUST_HASH, RUST_VERSION } from './rust';
|
|
|
|
export const CARGO_HOME = process.env.CARGO_HOME ?? path.join(os.homedir(), '.cargo');
|
|
|
|
export const WORKSPACE_ROOT = process.env.GITHUB_WORKSPACE ?? process.cwd();
|
|
|
|
export const CACHE_ENABLED = core.getBooleanInput('cache') && cache.isFeatureAvailable();
|
|
|
|
export async function downloadAndInstallBinstall(binDir: string) {
|
|
core.info('cargo-binstall does not exist, attempting to install');
|
|
|
|
let arch;
|
|
let file;
|
|
|
|
switch (process.arch) {
|
|
case 'x64':
|
|
arch = 'x86_64';
|
|
break;
|
|
case 'arm':
|
|
arch = 'armv7';
|
|
break;
|
|
case 'arm64':
|
|
arch = 'aarch64';
|
|
break;
|
|
default:
|
|
throw new Error(`Unsupported architecture: ${process.arch}`);
|
|
}
|
|
|
|
switch (process.platform) {
|
|
case 'linux': {
|
|
const { family } = await import('detect-libc');
|
|
let lib = 'gnu';
|
|
|
|
if ((await family()) === 'musl') {
|
|
lib = 'musl';
|
|
}
|
|
|
|
if (process.arch === 'arm') {
|
|
lib += 'eabihf';
|
|
}
|
|
|
|
file = `${arch}-unknown-linux-${lib}.tgz`;
|
|
break;
|
|
}
|
|
case 'darwin':
|
|
file = `${arch}-apple-darwin.zip`;
|
|
break;
|
|
case 'win32':
|
|
file = `${arch}-pc-windows-msvc.zip`;
|
|
break;
|
|
default:
|
|
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
}
|
|
|
|
const url = `https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-${file}`;
|
|
const dlPath = await tc.downloadTool(url);
|
|
|
|
if (url.endsWith('.zip')) {
|
|
await tc.extractZip(dlPath, binDir);
|
|
} else if (url.endsWith('.tgz')) {
|
|
await tc.extractTar(dlPath, binDir);
|
|
}
|
|
}
|
|
|
|
export async function installBins() {
|
|
const bins = core
|
|
.getInput('bins')
|
|
.split(',')
|
|
.map((bin) => bin.trim())
|
|
.filter(Boolean);
|
|
|
|
if (CACHE_ENABLED) {
|
|
bins.push('cargo-cache');
|
|
}
|
|
|
|
if (bins.length === 0) {
|
|
return;
|
|
}
|
|
|
|
core.info('Installing additional binaries');
|
|
|
|
const binDir = path.join(CARGO_HOME, 'bin');
|
|
|
|
if (!fs.existsSync(path.join(binDir, 'cargo-binstall'))) {
|
|
await downloadAndInstallBinstall(binDir);
|
|
}
|
|
|
|
await exec.exec('cargo', ['binstall', '--no-confirm', '--log-level', 'info', ...bins]);
|
|
}
|
|
|
|
export function getCacheTarget(): string {
|
|
return core.getInput('cache-target') || 'debug';
|
|
}
|
|
|
|
export function getCachePaths(): string[] {
|
|
return [
|
|
// ~/.cargo/registry
|
|
path.join(CARGO_HOME, 'registry'),
|
|
// /workspace/target/debug
|
|
path.join(WORKSPACE_ROOT, 'target', getCacheTarget()),
|
|
];
|
|
}
|
|
|
|
export function getCachePrefixes(): string[] {
|
|
return [`setup-rustcargo-v1-${process.platform}`, 'setup-rustcargo-v1'];
|
|
}
|
|
|
|
export async function getPrimaryCacheKey() {
|
|
const hasher = crypto.createHash('sha1');
|
|
|
|
core.info('Generating cache key');
|
|
|
|
core.debug(`Hashing Rust version = ${RUST_VERSION}`);
|
|
hasher.update(RUST_VERSION);
|
|
|
|
core.debug(`Hashing Rust commit hash = ${RUST_HASH}`);
|
|
hasher.update(RUST_HASH);
|
|
|
|
const lockHash = await glob.hashFiles('Cargo.lock');
|
|
|
|
core.debug(`Hashing Cargo.lock = ${lockHash}`);
|
|
hasher.update(lockHash);
|
|
|
|
const cacheTarget = getCacheTarget();
|
|
|
|
core.debug(`Hashing target profile = ${cacheTarget}`);
|
|
hasher.update(cacheTarget);
|
|
|
|
const job = process.env.GITHUB_JOB;
|
|
|
|
if (job) {
|
|
core.debug(`Hashing GITHUB_JOB = ${job}`);
|
|
hasher.update(job);
|
|
}
|
|
|
|
return `${getCachePrefixes()[0]}-${hasher.digest('hex')}`;
|
|
}
|
|
|
|
export async function cleanCargoRegistry() {
|
|
core.info('Cleaning ~/.cargo before saving');
|
|
|
|
const registryDir = path.join(CARGO_HOME, 'registry');
|
|
|
|
// .cargo/registry/src - Delete entirely
|
|
await exec.exec('cargo', ['cache', '--autoclean']);
|
|
|
|
// .cargo/registry/index - Delete .cache directories
|
|
const indexDir = path.join(registryDir, 'index');
|
|
|
|
if (fs.existsSync(indexDir)) {
|
|
await Promise.all(
|
|
fs.readdirSync(indexDir).map(async (index) => {
|
|
if (fs.existsSync(path.join(indexDir, index, '.git'))) {
|
|
await rmrf(path.join(indexDir, index, '.cache'));
|
|
}
|
|
}),
|
|
);
|
|
}
|
|
|
|
// .cargo/registry/cache - Do nothing?
|
|
}
|
|
|
|
// https://doc.rust-lang.org/cargo/guide/build-cache.html
|
|
export async function cleanTargetProfile() {
|
|
const targetProfile = getCacheTarget();
|
|
|
|
core.info(`Cleaning target/${targetProfile} before saving`);
|
|
|
|
const targetDir = path.join(WORKSPACE_ROOT, 'target', targetProfile);
|
|
|
|
// target/*/{examples,incremental} - Not required in CI
|
|
core.info('Removing examples and incremental directories');
|
|
|
|
await Promise.all(
|
|
['examples', 'incremental'].map(async (dirName) => {
|
|
const dir = path.join(targetDir, dirName);
|
|
|
|
if (fs.existsSync(dir)) {
|
|
await rmrf(dir);
|
|
}
|
|
}),
|
|
);
|
|
|
|
// target/**/*.d - Not required in CI?
|
|
core.info('Removing dep-info files (*.d)');
|
|
|
|
const globber = await glob.create(path.join(targetDir, '**/*.d'));
|
|
const files = await globber.glob();
|
|
|
|
await Promise.all(files.map(rmrf));
|
|
}
|
|
|
|
export async function saveCache() {
|
|
if (!CACHE_ENABLED) {
|
|
return;
|
|
}
|
|
|
|
const primaryKey = await getPrimaryCacheKey();
|
|
const cacheHitKey = core.getState('cache-hit-key');
|
|
|
|
if (cacheHitKey === primaryKey) {
|
|
core.info(`Cache hit occured on the key ${cacheHitKey}, not saving cache`);
|
|
return;
|
|
}
|
|
|
|
await cleanCargoRegistry();
|
|
await cleanTargetProfile();
|
|
|
|
core.info(`Saving cache with key ${primaryKey}`);
|
|
|
|
await cache.saveCache(getCachePaths(), primaryKey);
|
|
}
|
|
|
|
export async function restoreCache() {
|
|
if (!CACHE_ENABLED) {
|
|
return;
|
|
}
|
|
|
|
core.info('Attempting to restore cache');
|
|
|
|
const primaryKey = await getPrimaryCacheKey();
|
|
const cacheKey = await cache.restoreCache(getCachePaths(), primaryKey, getCachePrefixes());
|
|
|
|
if (cacheKey) {
|
|
core.saveState('cache-hit-key', cacheKey);
|
|
core.info(`Cache restored using key ${primaryKey}`);
|
|
} else {
|
|
core.info(`Cache does not exist using key ${primaryKey}`);
|
|
}
|
|
|
|
core.setOutput('cache-key', cacheKey ?? primaryKey);
|
|
core.setOutput('cache-hit', !!cacheKey);
|
|
}
|