Skip to content

Commit

Permalink
prepare v2
Browse files Browse the repository at this point in the history
  • Loading branch information
Swatinem committed Jul 16, 2022
1 parent 0497f93 commit 6226160
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
CARGO_TERM_COLOR: always

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- run: rustup toolchain install stable --profile minimal --component llvm-tools-preview

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
CARGO_TERM_COLOR: always

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- run: rustup toolchain install stable --profile minimal

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/simple.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
CARGO_TERM_COLOR: always

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- run: rustup toolchain install stable --profile minimal

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sparse-registry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
CARGO_UNSTABLE_SPARSE_REGISTRY: true

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- run: |
rustup toolchain install nightly --profile minimal
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/target-dir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
CARGO_TERM_COLOR: always

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- run: rustup toolchain install stable --profile minimal

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/workspaces.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
CARGO_TERM_COLOR: always

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- run: rustup toolchain install stable --profile minimal --target wasm32-unknown-unknown

Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 2.0.0

- The action code was refactored to allow for caching multiple workspaces and
different `target` directory layouts.
- The `working-directory` and `target-dir` input options were replaced by a
single `workspaces` option that has the form of `$workspace -> $target`.
- Support for considering `env-vars` as part of the cache key.
- The `sharedKey` input option was renamed to `shared-key` for consistency.

## 1.4.0

- Clean both `debug` and `release` target directories.
Expand Down
108 changes: 68 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,48 @@ sensible defaults.
## Example usage

```yaml
- uses: actions/checkout@v2
- uses: actions/checkout@v3

# selecting a toolchain either by action or manual `rustup` calls should happen
# before the plugin, as it uses the current rustc version as its cache key
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
# before the plugin, as the cache uses the current rustc version as its cache key
- run: rustup toolchain install stable --profile minimal

- uses: Swatinem/rust-cache@v1
- uses: Swatinem/rust-cache@v2
with:
# An explicit cache key that is used instead of the automatic `job`-based
# cache key and is thus stable across jobs.
# Default: empty
shared-key: ""

# An additional cache key that is added alongside the automatic `job`-based
# cache key and can be used to further differentiate jobs.
# Default: empty
key: ""

# A whitespace separated list of env-var *prefixes* who's value contributes
# to the environment cache key.
# The env-vars are matched by *prefix*, so the default `RUST` var will
# match all of `RUSTC`, `RUSTUP_*`, `RUSTFLAGS`, `RUSTDOC_*`, etc.
# Default: "CARGO CC CFLAGS CXX CMAKE RUST"
env-vars: ""

# The cargo workspaces and target directory configuration.
# These entries are separated by newlines and have the form
# `$workspace -> $target`. The `$target` part is treated as a directory
# relative to the `$workspace` and defaults to "target" if not explicitly given.
# Default: ". -> target"
workspaces: ""

# Determines if the cache should be saved even when the workflow has failed.
# Default: "false"
cache-on-failure: ""
```

## Inputs

: `key`
An optional key that is added to the automatic cache key.

: `sharedKey`
An additional key that is stable over multiple jobs.

: `envVars`
A space-separated list of regular expressions that define additional environment variable filters.
These are added to an additional cache key that's generated from environment variable contents.

: `working-directory`
The working directory the action operates in, is case the cargo project is not
located in the repo root.

: `target-dir`
The target directory that should be cleaned and persisted, defaults to `./target`.

: `cache-on-failure`
Cache even if the build fails, defaults to false
Further examples are available in the [.github/workflows][] directory.

## Outputs

: `cache-hit`
**`cache-hit`**

This is a boolean flag that will be set to `true` when there was an exact cache hit.

Expand All @@ -67,24 +72,21 @@ a more stable experience, please use a fixed revision or tag.

This action currently caches the following files/directories:

- `~/.cargo/bin`
- `~/.cargo/registry/index`
- `~/.cargo/registry/cache`
- `~/.cargo/git`
- `~/.cargo/.crates.toml`
- `~/.cargo/.crates2.json`
- `./target`
- `~/.cargo` (installed binaries, the cargo registry, cache, and git dependencies)
- `./target` (build artifacts of dependencies)

This cache is automatically keyed by:

- the github [`job_id`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_id),
- the rustc release / host / hash, and
- the rustc release / host / hash,
- the value of some compiler-specific environment variables (eg. RUSTFLAGS, etc), and
- a hash of all `Cargo.lock` / `Cargo.toml` files found anywhere in the repository (if present).
- a hash of all `rust-toolchain` / `rust-toolchain.toml` files in the root of the repository (if present).

An additional input `key` can be provided if the builtin keys are not sufficient.

Before being persisted, the cache is cleaned of:

- Any files in `~/.cargo/bin` that were present before the action ran (for example `rustc`).
- Dependencies that are no longer used.
- Anything that is not a dependency.
Expand All @@ -103,13 +105,39 @@ to recreate it from the compressed crate archives in `~/.cargo/registry/cache`.
The action will try to restore from a previous `Cargo.lock` version as well, so
lockfile updates should only re-build changed dependencies.

The action invokes `cargo metadata` to determine the current set of dependencies.

Additionally, the action automatically works around
[cargo#8603](https://github.com/rust-lang/cargo/issues/8603) /
[actions/cache#403](https://github.com/actions/cache/issues/403) which would
otherwise corrupt the cache on macOS builds.

## Cache Limits and Control

This specialized cache action is built on top of the upstream cache action
maintained by GitHub. The same restrictions and limits apply, which are
documented here:
https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows

In particular, caches are currently limited to 10 GB in total and exceeding that
limit will cause eviction of older caches.

Caches from base branches are available to PRs, but not across unrelated
branches.

The caches can be controlled using the [Cache API](https://docs.github.com/en/rest/actions/cache)
which allows listing existing caches and manually removing entries.

## Debugging

The action prints detailed information about which information it considers
for its cache key, and it outputs more debug-only information about which
cleanup steps it performs before persisting the cache.

You can read up on how to [enable debug logging](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging)
to see those details as well as further details related to caching operations.

## Known issues

- The cache cleaning process currently only runs against the build artifacts under
`./target/debug/`, so projects using release or cross-compiled builds will experience
larger cache sizes.
- The cache cleaning process currently removes all the files from `~/.cargo/bin`
that were present before the action ran (for example `rustc`).
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- Update readme with better docs ;-)
- better .cargo/bin handling:
- get a list of all the files on "pre"/"restore"
- move the files out of the way on "post"/"save" and move them back afterwards
- properly clean sparse registry
10 changes: 5 additions & 5 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ name: "Rust Cache"
description: "A GitHub Action that implements smart caching for rust/cargo projects with sensible defaults."
author: "Arpad Borsos <swatinem@swatinem.de>"
inputs:
shared-key:
description: "An additional cache key that is stable over multiple jobs"
required: false
key:
description: "An additional key for the cache"
required: false
sharedKey:
description: "An additional cache key that is stable over multiple jobs"
required: false
envVars:
description: "Additional environment variables to include in the cache key, separeted by spaces"
env-vars:
description: "Additional environment variables to include in the cache key, separated by spaces"
required: false
workspaces:
description: "Paths to multiple Cargo workspaces and their target directories, separated by newlines"
Expand Down
18 changes: 11 additions & 7 deletions src/cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export async function cleanTargetDir(targetDir: string, packages: Packages) {
}

async function cleanProfileTarget(profileDir: string, packages: Packages) {
await io.rmRF(path.join(profileDir, "examples"));
await io.rmRF(path.join(profileDir, "incremental"));
await rmRF(path.join(profileDir, "examples"));
await rmRF(path.join(profileDir, "incremental"));

let dir: fs.Dir;
// remove all *files* from the profile directory
Expand Down Expand Up @@ -94,8 +94,7 @@ export async function cleanBin() {
export async function cleanRegistry(packages: Packages) {
// `.cargo/registry/src`
// we can remove this completely, as cargo will recreate this from `cache`
const srcDir = path.join(CARGO_HOME, "registry", "src");
await io.rmRF(srcDir);
await rmRF(path.join(CARGO_HOME, "registry", "src"));

// `.cargo/registry/index`
const indexDir = await fs.promises.opendir(path.join(CARGO_HOME, "registry", "index"));
Expand All @@ -107,7 +106,7 @@ export async function cleanRegistry(packages: Packages) {

// for a git registry, we can remove `.cache`, as cargo will recreate it from git
if (await exists(path.join(dir.path, ".git"))) {
await io.rmRF(path.join(dir.path, ".cache"));
await rmRF(path.join(dir.path, ".cache"));
}
// TODO: else, clean `.cache` based on the `packages`
}
Expand Down Expand Up @@ -183,7 +182,7 @@ export async function cleanGit(packages: Packages) {

const ONE_WEEK = 7 * 24 * 3600 * 1000;

export async function rmExcept(dirName: string, keepPrefix: Set<string>) {
async function rmExcept(dirName: string, keepPrefix: Set<string>) {
const dir = await fs.promises.opendir(dirName);
for await (const dirent of dir) {
let name = dirent.name;
Expand All @@ -200,7 +199,7 @@ export async function rmExcept(dirName: string, keepPrefix: Set<string>) {
}
}

export async function rm(parent: string, dirent: fs.Dirent) {
async function rm(parent: string, dirent: fs.Dirent) {
try {
const fileName = path.join(parent, dirent.name);
core.debug(`deleting "${fileName}"`);
Expand All @@ -212,6 +211,11 @@ export async function rm(parent: string, dirent: fs.Dirent) {
} catch {}
}

async function rmRF(dirName: string) {
core.debug(`deleting "${dirName}"`);
await io.rmRF(dirName);
}

async function exists(path: string) {
try {
await fs.promises.access(path);
Expand Down
12 changes: 6 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ export class CacheConfig {
const self = new CacheConfig();

// Construct key prefix:
// This uses either the `sharedKey` input,
// This uses either the `shared-key` input,
// or the `key` input combined with the `job` key.

let key = `v0-rust`;

const sharedKey = core.getInput("sharedKey");
const sharedKey = core.getInput("shared-key");
if (sharedKey) {
key += `-${sharedKey}`;
} else {
Expand All @@ -72,7 +72,7 @@ export class CacheConfig {
// Construct environment portion of the key:
// This consists of a hash that considers the rust version
// as well as all the environment variables as given by a default list
// and the `envVars` input.
// and the `env-vars` input.
// The env vars are sorted, matched by prefix and hashed into the
// resulting environment hash.

Expand All @@ -87,8 +87,8 @@ export class CacheConfig {
self.keyRust = keyRust;

// these prefixes should cover most of the compiler / rust / cargo keys
const envPrefixes = ["CARGO", "CC", "CXX", "CMAKE", "RUST"];
envPrefixes.push(...core.getInput("envVars").split(/\s+/).filter(Boolean));
const envPrefixes = ["CARGO", "CC", "CFLAGS", "CXX", "CMAKE", "RUST"];
envPrefixes.push(...core.getInput("env-vars").split(/\s+/).filter(Boolean));

// sort the available env vars so we have a more stable hash
const keyEnvs = [];
Expand Down Expand Up @@ -147,7 +147,7 @@ export class CacheConfig {
const workspaces: Array<Workspace> = [];
const workspacesInput = core.getInput("workspaces") || ".";
for (const workspace of workspacesInput.trim().split("\n")) {
let [root, target = "target"] = workspace.split(" -> ");
let [root, target = "target"] = workspace.split("->").map((s) => s.trim());
root = path.resolve(root);
target = path.join(root, target);
workspaces.push(new Workspace(root, target));
Expand Down

0 comments on commit 6226160

Please sign in to comment.