setup-rust/src/cargo.ts
2024-08-03 22:09:36 -07:00

230 lines
5.4 KiB
TypeScript

import fs from 'node:fs';
import path from 'node:path';
import { family } from 'detect-libc';
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 {
CARGO_HOME,
getCachePaths,
getCachePrefixes,
getCacheTarget,
getPrimaryCacheKey,
isCacheEnabled,
WORKSPACE_ROOT,
} from './cache';
import { rmrf } from './fs';
// eslint-disable-next-line complexity
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': {
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}`);
}
// https://github.com/cargo-bins/cargo-binstall/issues/1864
const version = process.env.CARGO_BINSTALL_VERSION ?? 'v1.8.0';
const url = version === 'latest'
? `https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-${file}`
: `https://github.com/cargo-bins/cargo-binstall/releases/download/${version}/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 (isCacheEnabled()) {
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 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 (!isCacheEnabled()) {
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;
}
const cachePaths = getCachePaths().filter((cachePath) => {
if (!fs.existsSync(cachePath)) {
core.info(`Cache path ${cachePath} does not exist, skipping`);
return false;
}
return true;
});
if (cachePaths.length === 0) {
core.info('No paths to cache, skipping save entirely');
return;
}
await cleanCargoRegistry();
await cleanTargetProfile();
core.info(`Saving cache with key ${primaryKey}`);
core.debug(`Cache key: ${primaryKey}`);
core.debug('Cache paths:');
for (const cachePath of cachePaths) {
core.debug(`- ${cachePath}`);
}
await cache.saveCache(cachePaths, primaryKey);
}
export async function restoreCache() {
if (!isCacheEnabled()) {
return;
}
core.info('Attempting to restore cache');
const primaryKey = await getPrimaryCacheKey();
const cachePaths = getCachePaths();
core.debug(`Cache key: ${primaryKey}`);
core.debug('Cache paths:');
for (const cachePath of cachePaths) {
core.debug(`- ${cachePath}`);
}
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);
}