Compare commits

...

16 commits

Author SHA1 Message Date
Miles Johnson
52e0bc3392 deps: Update to latest. 2025-02-20 10:02:54 -08:00
Adi Yakovian
2d9112aa0c
deps: Update cache version (#26) 2025-02-20 09:59:51 -08:00
Miles Johnson
1670d14080
fix: Fix binstall 404. (#21) 2024-08-03 22:14:37 -07:00
Miles Johnson
dcab3dcf9f
new: Support custom target dirs. (#19) 2024-05-03 12:01:30 -07:00
Miles Johnson
c91b4202a2
new: Add cache base warmup strategy. (#16) 2023-11-17 10:27:56 -08:00
Miles Johnson
c15c42022b deps: Update to latest. 2023-11-07 14:03:02 -08:00
mlga
9c00f9cafd
Use GITHUB_WORKFLOW to generate cache key (#15) 2023-11-07 13:59:29 -08:00
Miles Johnson
393c8c7871 build: Bump version. 2023-10-27 10:28:30 -07:00
Konstantin Azizov
7a64551755
build: bump Node from v16 to v20 (#14) 2023-10-27 10:07:58 -07:00
Miles Johnson
deec13b3d0 fix: Remove dynamic import. 2023-08-02 09:39:59 -07:00
Miles Johnson
889bf1db60
new: Prepare v1 release. (#9) 2023-08-01 15:29:15 -07:00
Miles Johnson
711a320c49 fix: Fix cache toggle not working. 2023-05-09 11:18:04 -07:00
Miles Johnson
7229efd154 docs: Add inputs to readme. 2023-05-08 10:21:58 -07:00
Miles Johnson
b07b2e2e9e deps: Update to latest. 2023-05-08 10:14:18 -07:00
Miles Johnson
28b46eda6c new: Add cache-target input. 2023-05-08 10:13:11 -07:00
Miles Johnson
cef1bc3f53 breaking: Rework bins setting. 2023-05-08 10:00:11 -07:00
14 changed files with 3096 additions and 2118 deletions

View file

@ -7,4 +7,7 @@ module.exports = {
project: 'tsconfig.json', project: 'tsconfig.json',
tsconfigRootDir: __dirname, tsconfigRootDir: __dirname,
}, },
rules: {
'unicorn/prefer-top-level-await': 'off',
},
}; };

View file

@ -9,8 +9,42 @@ jobs:
name: 'CI' name: 'CI'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
- run: npm install -g pnpm - run: npm install -g pnpm
- run: pnpm install - run: pnpm install
- run: pnpm run check - run: pnpm run check
action-default:
name: 'Action - Default'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm run build
- uses: ./ # self
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
action-warmup:
name: 'Action - Cache warmup'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm run build
- uses: ./ # self
with:
cache-base: master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -6,10 +6,10 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
ref: ${{ github.event.release.tag_name }} ref: ${{ github.event.release.tag_name }}
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
- run: npm install -g pnpm - run: npm install -g pnpm
- run: pnpm install - run: pnpm install
- run: pnpm run build - run: pnpm run build

View file

@ -1,3 +1,49 @@
# 1.2.2
- Updated dependencies.
# 1.2.1
- Pinned the `cargo-binstall` version to v1.8 to work around the 404 errors in CI.
- Added support for the `CARGO_BINSTALL_VERSION` environment variable.
# 1.2.0
- Added a `target-dirs` input, allowing the target folders to be specified. Can now cache multiple
target folders.
- Updated to skip caching a directory if it does not exist, instead of failing.
- Updated dependencies.
# 1.1.0
- Added a `cache-base` input. When provided, will only save cache on this branch/ref, but will
restore cache on all branches/refs.
- Updated dependencies.
# 1.0.3
- Include `GITHUB_WORKFLOW` in cache key.
- Updated dependencies.
# 1.0.2
- Switch to Node.js v20.
# 1.0.1
- Fixed an issue where a module was missing from the build.
# 1.0.0
- Will now install `rustup` if it does not exist in the environment.
- Added musl support to `cargo-binstall`.
# 0.6.0
- Breaking: Cargo `bins` must provide the `cargo-` crate prefix manually. This change allows
non-crate globals to be installed.
- Added a `cache-target` input, to customize which target profile is cached. Defaults to `debug`.
# 0.5.0 # 0.5.0
- Added `inherit-toolchain` input to inherit all settings from `rust-toolchain.toml`, and not just - Added `inherit-toolchain` input to inherit all settings from `rust-toolchain.toml`, and not just

View file

@ -11,10 +11,29 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# ... # ...
- uses: moonrepo/setup-rust@v0 - uses: moonrepo/setup-rust@v1
- run: cargo test - run: cargo test
``` ```
## Inputs
The following inputs are available for the action, and can be passed via `with`. All inputs are
optional.
- `bins` - Comma-separated list of global binaries to install into Cargo.
- `cache` - Toggle caching of directories. Defaults to `true`.
- `cache-base` - Base branch/ref to save a warmup cache on. Other branches/refs will restore from
this base.
- `cache-target` - Name of the target profile to cache. Defaults to `debug`.
- `channel` - Toolchain specification/channel to explicitly install.
- `components` - Comma-separated list of additional components to install.
- `inherit-toolchain` - Inherit all toolchain settings from the `rust-toolchain.toml` file. Defaults
to `false`.
- `targets` - Comma-separated list of additional targets to install.
- `target-dirs` - Comma-separated list of target folder paths, relative from the repository root.
Defaults to `target`.
- `profile` - Profile to install. Defaults to `minimal`.
## Configuring the Rust toolchain ## Configuring the Rust toolchain
This action will automatically install the appropriate toolchain with `rustup` by inspecting the This action will automatically install the appropriate toolchain with `rustup` by inspecting the
@ -36,18 +55,19 @@ The toolchain/channel can also be explicitly configured with the `channel` input
highest precedence. highest precedence.
```yaml ```yaml
- uses: moonrepo/setup-rust@v0 - uses: moonrepo/setup-rust@v1
with: with:
channel: '1.65.0' channel: '1.65.0'
``` ```
### Profile and components ### Profile, components, and targets
Furthermore, this action supports rustup profiles and components, which can be configured with the Furthermore, this action supports rustup profiles, components, and targets, which can be configured
`profile` and `components` inputs respectively. When not defined, profile defaults to `minimal`. with the `profile`, `components`, and `targets` inputs respectively. When not defined, profile
defaults to `minimal`.
```yaml ```yaml
- uses: moonrepo/setup-rust@v0 - uses: moonrepo/setup-rust@v1
with: with:
profile: complete profile: complete
``` ```
@ -55,36 +75,48 @@ Furthermore, this action supports rustup profiles and components, which can be c
When using components, the input requires a comma separated list of component names. When using components, the input requires a comma separated list of component names.
```yaml ```yaml
- uses: moonrepo/setup-rust@v0 - uses: moonrepo/setup-rust@v1
with: with:
components: clippy components: clippy
- run: cargo clippy --workspace - run: cargo clippy --workspace
``` ```
When using targets, the input requires a comma separated list of target triples.
```yaml
- uses: moonrepo/setup-rust@v1
with:
targets: 'x86_64-pc-windows-msvc,x86_64-pc-windows-gnu'
```
## Installing Cargo binaries ## Installing Cargo binaries
If you require `cargo-make`, `cargo-nextest`, or other global binaries, this action supports If you require `cargo-make`, `cargo-nextest`, or other global binaries, this action supports
installing Cargo binaries through the `bins` input, which requires a comma-separated list of crate installing Cargo binaries through the `bins` input, which requires a comma-separated list of crate
names (`cargo-` prefix optional). names.
```yaml ```yaml
- uses: moonrepo/setup-rust@v0 - uses: moonrepo/setup-rust@v1
with: with:
bins: nextest, cargo-insta@1.28.0 bins: cargo-nextest, cargo-insta@1.28.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
``` ```
> Binaries are installed with [`cargo-binstall`](https://crates.io/crates/cargo-binstall) under the > Binaries are installed with [`cargo-binstall`](https://crates.io/crates/cargo-binstall) under the
> hood. > hood. We suggest setting `GITHUB_TOKEN` to avoid rate limiting.
## Caching in CI ## Caching in CI
By default this action will cache the `~/.cargo/registry` and `/target/debug` directories to improve By default this action will cache the `~/.cargo/registry` and `/target/debug` directories to improve
CI times. To disable caching, set the `cache` input to `false`. CI times. To disable caching, set the `cache` input to `false`. Furthermore, the target profile can
be changed with the `cache-target` input, which defaults to `debug`.
```yaml ```yaml
- uses: moonrepo/setup-rust@v0 - uses: moonrepo/setup-rust@v1
with: with:
cache: false cache: false
cache-target: release
``` ```
The following optimizations and considerations are taken into account when caching: The following optimizations and considerations are taken into account when caching:
@ -98,7 +130,7 @@ The following optimizations and considerations are taken into account when cachi
`.cache`, and any other unnecessary files. `.cache`, and any other unnecessary files.
- `/target` - `/target`
- Only the `/debug` profile is cached, as this profile is typically used for formatting, linting, - Only the `/debug` profile is cached, as this profile is typically used for formatting, linting,
and testing. and testing. This can be changed with the `cache-target` input.
- The `/examples` and `/incremental` sub-directories are not cached as they are not necessary for - The `/examples` and `/incremental` sub-directories are not cached as they are not necessary for
CI. CI.
- All dep-info (`*.d`) files are removed, as they're meant for build systems and signaling - All dep-info (`*.d`) files are removed, as they're meant for build systems and signaling
@ -107,6 +139,23 @@ The following optimizations and considerations are taken into account when cachi
> The following sources are hashed for the generated cache key: `$GITHUB_JOB`, `Cargo.lock`, Rust > The following sources are hashed for the generated cache key: `$GITHUB_JOB`, `Cargo.lock`, Rust
> version, Rust commit hash, and OS. > version, Rust commit hash, and OS.
### Warmup strategy
Another strategy that we support is called a warmup cache, where a base branch/ref is used to
generate and save the cache (like master), and all other branches/refs will _only_ restore this
cache (and not save).
This can be enabled with the `cache-base` input, which requires a branch/ref name. This input also
supports regex.
```yaml
- uses: moonrepo/setup-rust@v1
with:
cache-base: master
# With regex
cache-base: (master|main|develop)
```
## Compared to ## Compared to
### `actions-rs/*` ### `actions-rs/*`
@ -121,11 +170,6 @@ Outside of being evergreen, this action also supports the following features:
- Assumes `rustup`, `cargo`, and other commands are available globally. This allows you to use them - Assumes `rustup`, `cargo`, and other commands are available globally. This allows you to use them
directly in a `run` command, without having to use `actions-rs/cargo`. directly in a `run` command, without having to use `actions-rs/cargo`.
However, this action _does not_:
- Install `rustup` if it does not exist, while `actions-rs` will. This is typically fine if using
GitHub provided runners as all Rust tooling comes pre-installed.
### `dtolnay/rust-toolchain` ### `dtolnay/rust-toolchain`
Our action is very similar to theirs, which was a great implementation reference, but our action Our action is very similar to theirs, which was a great implementation reference, but our action

View file

@ -7,19 +7,28 @@ inputs:
bins: bins:
description: 'Comma-separated list of global binaries to install into Cargo.' description: 'Comma-separated list of global binaries to install into Cargo.'
cache: cache:
description: 'Toggle caching of ~/.cargo/registry and /target/debug directories.' description: 'Toggle caching of ~/.cargo/registry and /target/<cache-target> directories.'
default: true default: 'true'
cache-base:
description:
'Base branch/ref to save a warmup cache on. Other branches/refs will restore from this base.'
cache-target:
description: 'Name of the target profile to cache.'
default: 'debug'
channel: channel:
description: 'Toolchain specification/channel to install.' description: 'Toolchain specification/channel to explicitly install.'
components: components:
description: 'Comma-separated list of additional components to install.' description: 'Comma-separated list of additional components to install.'
targets:
description: 'Comma-separated list of additional targets to install.'
profile:
description: 'Profile to install. Defaults to "minimal".'
inherit-toolchain: inherit-toolchain:
description: 'Inherit all toolchain settings from the rust-toolchain.toml file.' description: 'Inherit all toolchain settings from the rust-toolchain.toml file.'
default: false default: 'false'
targets:
description: 'Comma-separated list of additional targets to install.'
target-dirs:
description: 'Comma-separated list of target folder paths, relative from the repository root.'
default: 'target'
profile:
description: 'Profile to install. Defaults to "minimal".'
outputs: outputs:
cache-key: cache-key:
description: 'The generated cache key used.' description: 'The generated cache key used.'
@ -30,7 +39,7 @@ outputs:
rust-hash: rust-hash:
description: 'Commit hash of the installed rustc.' description: 'Commit hash of the installed rustc.'
runs: runs:
using: 'node16' using: 'node20'
main: 'dist/index.js' main: 'dist/index.js'
post: 'dist/post/index.js' post: 'dist/post/index.js'
branding: branding:

View file

@ -1,8 +1,39 @@
import path from 'path'; import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import * as core from '@actions/core'; import * as core from '@actions/core';
import { CARGO_HOME, installBins, restoreCache } from './src/cargo'; import * as exec from '@actions/exec';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import { CARGO_HOME } from './src/cache';
import { installBins, restoreCache } from './src/cargo';
import { installToolchain } from './src/rust'; import { installToolchain } from './src/rust';
export async function installRustup() {
try {
await io.which('rustup', true);
return;
} catch {
// Doesn't exist
}
core.info('rustup does not exist, attempting to install');
const script = await tc.downloadTool(
process.platform === 'win32' ? 'https://win.rustup.rs' : 'https://sh.rustup.rs',
path.join(os.tmpdir(), 'rustup-init'),
);
core.info(`Downloaded installation script to ${script}`);
// eslint-disable-next-line no-magic-numbers
await fs.promises.chmod(script, 0o755);
await exec.exec(script, ['--default-toolchain', 'none', '-y']);
core.info('Installed rustup');
}
async function run() { async function run() {
core.info('Setting cargo environment variables'); core.info('Setting cargo environment variables');
@ -14,6 +45,7 @@ async function run() {
core.addPath(path.join(CARGO_HOME, 'bin')); core.addPath(path.join(CARGO_HOME, 'bin'));
try { try {
await installRustup();
await installToolchain(); await installToolchain();
await installBins(); await installBins();

View file

@ -1,6 +1,6 @@
{ {
"name": "@moonrepo/setup-rust", "name": "@moonrepo/setup-rust",
"version": "0.5.0", "version": "1.2.2",
"description": "A GitHub action for setting up Rust and Cargo.", "description": "A GitHub action for setting up Rust and Cargo.",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
@ -16,22 +16,26 @@
"author": "Miles Johnson", "author": "Miles Johnson",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^3.2.1", "@actions/cache": "^4.0.0",
"@actions/core": "^1.10.0", "@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/glob": "^0.4.0", "@actions/glob": "^0.5.0",
"@actions/io": "^1.1.3", "@actions/io": "^1.1.3",
"@actions/tool-cache": "^2.0.1", "@actions/tool-cache": "^2.0.1",
"@ltd/j-toml": "^1.38.0" "@ltd/j-toml": "^1.38.0",
"detect-libc": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.15.11", "@types/node": "^20.13.4",
"@vercel/ncc": "^0.36.1", "@vercel/ncc": "^0.38.1",
"eslint": "^8.38.0", "eslint": "^8.53.0",
"eslint-config-moon": "^2.0.2", "eslint-config-moon": "^2.0.11",
"prettier": "^2.8.7", "prettier": "^3.2.5",
"prettier-config-moon": "^1.1.2", "prettier-config-moon": "^1.1.2",
"tsconfig-moon": "^1.3.0", "tsconfig-moon": "^1.3.0",
"typescript": "^5.0.4" "typescript": "^5.4.5"
},
"engines": {
"node": ">=20.0.0"
} }
} }

4695
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,14 @@ import { saveCache } from './src/cargo';
async function run() { async function run() {
try { try {
await saveCache(); const base = core.getInput('cache-base');
// Only save the cache for the following 2 scenarios:
// - If not using the base warmup strategy.
// - If using the base warmup strategy, and the current ref matches.
if (!base || (base && !!(process.env.GITHUB_REF_NAME ?? '').match(base))) {
await saveCache();
}
} catch (error: unknown) { } catch (error: unknown) {
core.setFailed((error as Error).message); core.setFailed((error as Error).message);
} }

102
src/cache.ts Normal file
View file

@ -0,0 +1,102 @@
import crypto from 'node:crypto';
import os from 'node:os';
import path from 'node:path';
import * as cache from '@actions/cache';
import * as core from '@actions/core';
import * as glob from '@actions/glob';
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 function isCacheEnabled(): boolean {
return core.getBooleanInput('cache') && cache.isFeatureAvailable();
}
export function getCacheTarget(): string {
return core.getInput('cache-target') || 'debug';
}
export function getTargetPaths(): string[] {
const profile = getCacheTarget();
const dirs = core.getInput('target-dirs', { required: true }).split(',');
return dirs
.map((dir) => dir.trim())
.filter(Boolean)
.map((dir) => path.join(WORKSPACE_ROOT, dir, profile));
}
export function getCachePaths(): string[] {
return [
// ~/.cargo/registry
path.join(CARGO_HOME, 'registry'),
// /workspace/target/debug
...getTargetPaths(),
];
}
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 cacheTarget = getCacheTarget();
core.debug(`Hashing target profile = ${cacheTarget}`);
hasher.update(cacheTarget);
// When warming up, loosen the cache key to allow for more cache hits
if (core.getInput('cache-base')) {
core.debug('Using warmup strategy, not hashing Cargo.lock, GITHUB_WORKFLOW, or GITHUB_JOB');
hasher.update('warmup');
const baseRef = process.env.GITHUB_BASE_REF ?? '';
if (
baseRef === 'master' ||
baseRef === 'main' ||
baseRef === 'trunk' ||
baseRef.startsWith('develop') ||
baseRef.startsWith('release')
) {
core.debug(`Hashing GITHUB_BASE_REF = ${baseRef}`);
hasher.update(baseRef);
}
}
// Otherwise, these add far too much granularity to the cache key
else {
const lockHash = await glob.hashFiles('Cargo.lock');
core.debug(`Hashing Cargo.lock = ${lockHash}`);
hasher.update(lockHash);
const workflow = process.env.GITHUB_WORKFLOW;
if (workflow) {
core.debug(`Hashing GITHUB_WORKFLOW = ${workflow}`);
hasher.update(workflow);
}
const job = process.env.GITHUB_JOB;
if (job) {
core.debug(`Hashing GITHUB_JOB = ${job}`);
hasher.update(job);
}
}
return `${getCachePrefixes()[0]}-${hasher.digest('hex')}`;
}

View file

@ -1,21 +1,23 @@
import crypto from 'crypto'; import fs from 'node:fs';
import fs from 'fs'; import path from 'node:path';
import os from 'os'; import { family } from 'detect-libc';
import path from 'path';
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 * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import * as glob from '@actions/glob'; import * as glob from '@actions/glob';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
import {
CARGO_HOME,
getCachePaths,
getCachePrefixes,
getCacheTarget,
getPrimaryCacheKey,
isCacheEnabled,
WORKSPACE_ROOT,
} from './cache';
import { rmrf } from './fs'; 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();
// eslint-disable-next-line complexity
export async function downloadAndInstallBinstall(binDir: string) { export async function downloadAndInstallBinstall(binDir: string) {
core.info('cargo-binstall does not exist, attempting to install'); core.info('cargo-binstall does not exist, attempting to install');
@ -26,6 +28,9 @@ export async function downloadAndInstallBinstall(binDir: string) {
case 'x64': case 'x64':
arch = 'x86_64'; arch = 'x86_64';
break; break;
case 'arm':
arch = 'armv7';
break;
case 'arm64': case 'arm64':
arch = 'aarch64'; arch = 'aarch64';
break; break;
@ -34,9 +39,20 @@ export async function downloadAndInstallBinstall(binDir: string) {
} }
switch (process.platform) { switch (process.platform) {
case 'linux': case 'linux': {
file = `${arch}-unknown-linux-gnu.tgz`; let lib = 'gnu';
if ((await family()) === 'musl') {
lib = 'musl';
}
if (process.arch === 'arm') {
lib += 'eabihf';
}
file = `${arch}-unknown-linux-${lib}.tgz`;
break; break;
}
case 'darwin': case 'darwin':
file = `${arch}-apple-darwin.zip`; file = `${arch}-apple-darwin.zip`;
break; break;
@ -47,7 +63,11 @@ export async function downloadAndInstallBinstall(binDir: string) {
throw new Error(`Unsupported platform: ${process.platform}`); throw new Error(`Unsupported platform: ${process.platform}`);
} }
const url = `https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-${file}`; // 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); const dlPath = await tc.downloadTool(url);
if (url.endsWith('.zip')) { if (url.endsWith('.zip')) {
@ -62,10 +82,9 @@ export async function installBins() {
.getInput('bins') .getInput('bins')
.split(',') .split(',')
.map((bin) => bin.trim()) .map((bin) => bin.trim())
.filter(Boolean) .filter(Boolean);
.map((bin) => (bin.startsWith('cargo-') ? bin : `cargo-${bin}`));
if (CACHE_ENABLED) { if (isCacheEnabled()) {
bins.push('cargo-cache'); bins.push('cargo-cache');
} }
@ -84,45 +103,6 @@ export async function installBins() {
await exec.exec('cargo', ['binstall', '--no-confirm', '--log-level', 'info', ...bins]); 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(WORKSPACE_ROOT, 'target/debug'),
];
}
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 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() { export async function cleanCargoRegistry() {
core.info('Cleaning ~/.cargo before saving'); core.info('Cleaning ~/.cargo before saving');
@ -149,11 +129,13 @@ export async function cleanCargoRegistry() {
// https://doc.rust-lang.org/cargo/guide/build-cache.html // https://doc.rust-lang.org/cargo/guide/build-cache.html
export async function cleanTargetProfile() { export async function cleanTargetProfile() {
core.info('Cleaning target/debug before saving'); const targetProfile = getCacheTarget();
const targetDir = path.join(WORKSPACE_ROOT, 'target/debug'); core.info(`Cleaning target/${targetProfile} before saving`);
// target/debug/{examples,incremental} - Not required in CI const targetDir = path.join(WORKSPACE_ROOT, 'target', targetProfile);
// target/*/{examples,incremental} - Not required in CI
core.info('Removing examples and incremental directories'); core.info('Removing examples and incremental directories');
await Promise.all( await Promise.all(
@ -166,7 +148,7 @@ export async function cleanTargetProfile() {
}), }),
); );
// target/debug/**/*.d - Not required in CI? // target/**/*.d - Not required in CI?
core.info('Removing dep-info files (*.d)'); core.info('Removing dep-info files (*.d)');
const globber = await glob.create(path.join(targetDir, '**/*.d')); const globber = await glob.create(path.join(targetDir, '**/*.d'));
@ -176,7 +158,7 @@ export async function cleanTargetProfile() {
} }
export async function saveCache() { export async function saveCache() {
if (!CACHE_ENABLED) { if (!isCacheEnabled()) {
return; return;
} }
@ -188,29 +170,59 @@ export async function saveCache() {
return; 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 cleanCargoRegistry();
await cleanTargetProfile(); await cleanTargetProfile();
core.info(`Saving cache with key ${primaryKey}`); core.info(`Saving cache with key ${primaryKey}`);
await cache.saveCache(getCachePaths(), 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() { export async function restoreCache() {
if (!CACHE_ENABLED) { if (!isCacheEnabled()) {
return; return;
} }
core.info('Attempting to restore cache'); core.info('Attempting to restore cache');
const primaryKey = await getPrimaryCacheKey(); 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()); const cacheKey = await cache.restoreCache(getCachePaths(), primaryKey, getCachePrefixes());
if (cacheKey) { if (cacheKey) {
core.saveState('cache-hit-key', cacheKey); core.saveState('cache-hit-key', cacheKey);
core.info(`Cache restored using key ${primaryKey}`); core.info(`Cache restored using key ${primaryKey}`);
} else { } else {
core.warning(`Cache does not exist using key ${primaryKey}`); core.info(`Cache does not exist using key ${primaryKey}`);
} }
core.setOutput('cache-key', cacheKey ?? primaryKey); core.setOutput('cache-key', cacheKey ?? primaryKey);

View file

@ -1,7 +1,7 @@
/* eslint-disable import/no-mutable-exports */ /* eslint-disable import/no-mutable-exports */
import fs from 'fs'; import fs from 'node:fs';
import path from 'path'; import path from 'node:path';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import TOML from '@ltd/j-toml'; import TOML from '@ltd/j-toml';

View file

@ -2,7 +2,7 @@
"extends": "tsconfig-moon/tsconfig.json", "extends": "tsconfig-moon/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"allowJs": true, "allowJs": true,
"module": "Node16", "module": "NodeNext",
"moduleResolution": "NodeNext", "moduleResolution": "NodeNext",
"noEmit": true, "noEmit": true,
"verbatimModuleSyntax": false "verbatimModuleSyntax": false