setup-rust/src/cargo.ts
2023-04-17 16:23:45 -07:00

192 lines
4.7 KiB
TypeScript

import crypto from 'crypto';
import fs from 'fs';
import os from 'os';
import path from '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 io from '@actions/io';
import * as tc from '@actions/tool-cache';
import { RUST_HASH, RUST_VERSION } from './rust';
export const CARGO_HOME = process.env.CARGO_HOME ?? path.join(os.homedir(), '.cargo');
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 'arm64':
arch = 'aarch64';
break;
default:
throw new Error(`Unsupported architecture: ${process.arch}`);
}
switch (process.platform) {
case 'linux':
file = `${arch}-unknown-linux-gnu.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)
.map((bin) => (bin.startsWith('cargo-') ? bin : `cargo-${bin}`));
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 getCachePaths(): string[] {
return [
// ~/.cargo/registry
path.join(CARGO_HOME, 'registry'),
// /workspace/target/debug
path.join(process.env.GITHUB_WORKSPACE ?? process.cwd(), 'target/debug'),
];
}
export function getCachePrefixes(): string[] {
return [`setup-rustcargo-${process.platform}`, 'setup-rustcargo'];
}
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 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 cache 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');
for (const index of fs.readdirSync(indexDir)) {
if (fs.existsSync(path.join(indexDir, index, '.git'))) {
const dir = path.join(indexDir, index, '.cache');
core.debug(`Deleting ${dir}`);
try {
// eslint-disable-next-line no-await-in-loop
await io.rmRF(dir);
} catch (error: unknown) {
core.warning(`Failed to delete ${dir}: ${error}`);
}
}
}
// .cargo/registry/cache - Do nothing?
}
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();
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.warning(`Cache does not exist using key ${primaryKey}`);
}
core.setOutput('cache-key', cacheKey ?? primaryKey);
core.setOutput('cache-hit', !!cacheKey);
}