17

Using GitHub Actions, I am unable to see a significant improvement from using cached artifacts produced from previous builds, when building with Rust's cargo.

I suspect I forgot something in the following code, what could be the problem here?

EDIT: Here are the logs if you want to have a look at them!

name: Rust on: push: branches: [ main ] pull_request: branches: [ main ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - name: Cargo Cache uses: actions/cache@v1 with: path: ~/.cargo key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }} ${{ runner.os }}-cargo - name: Cargo Target Cache uses: actions/cache@v1 with: path: target key: ${{ runner.os }}-cargo-target-${{ hashFiles('**/Cargo.toml') }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-target-${{ hashFiles('**/Cargo.toml') }} ${{ runner.os }}-cargo-target - uses: actions/checkout@v2 - name: Build run: cargo build --verbose --all --features "strict" - name: Run tests run: cargo test --verbose --all --features "strict" 

4 Answers 4

23

You can cache the target and .cargo folders in one step. Here's an example of my setup for building and testing a back end web API.

name: Continuous Integration on: [push, pull_request] jobs: build_and_test: runs-on: ubuntu-latest services: postgres: image: postgres:10.12-alpine env: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: pongstars ports: - 5432:5432 # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v2 - name: ⚡ Cache uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - uses: actions-rs/toolchain@v1 with: toolchain: stable - name: 🔨 Build uses: actions-rs/cargo@v1 with: command: build - name: Create env file run: mv .env.example .env - name: 🔎 Test uses: actions-rs/cargo@v1 with: command: test - name: ⚙ Integration test uses: actions-rs/cargo@v1 with: command: test args: --features "integration_tests" 
Sign up to request clarification or add additional context in comments.

1 Comment

I don't understand how this single action both creates the cache and restores from the cache. If the action is creating a cache, and nothing has been installed yet in a prior step, how would anything ever be cached?
8

This is the official example provided by the authors of actions/cache:

- uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 

Comments

5

I think I got it working now: build time reduced from 4 minutes down to 1m15s.

The fixes I applied were:

  • I needed to first check out the repository to have the Cargo.toml file used in the hashing function for the cache (!).
  • **/Cargo.toml was refactored to Cargo.toml, so that the file is found for hashing.

Here's the final rust.yaml:

name: Rust on: push: branches: [ main ] pull_request: branches: [ main ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Cargo Cache uses: actions/cache@v1 with: path: ~/.cargo key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }} restore-keys: | ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }} ${{ runner.os }}-cargo - name: Cargo Target Cache uses: actions/cache@v1 with: path: target key: ${{ runner.os }}-cargo-target-${{ hashFiles('Cargo.toml') }} restore-keys: | ${{ runner.os }}-cargo-target-${{ hashFiles('Cargo.toml') }} ${{ runner.os }}-cargo-target - name: Build run: cargo build --verbose --all --features "strict" - name: Run tests run: cargo test --verbose --all --features "strict" 

4 Comments

build --all is now deprecated. build --all-targets might be a better choice.
Caching all of ~/.cargo/ seems to cause Cargo to fail on macOS. Caching just the git and registry subdirectories, as suggested in some other answers, works better.
This is the correct answer. In the original question checkout was done after the caching so there was no Cargo.toml / Cargo.lock file on which to create a cache key. In my experience caching on Cargo.lock is better that Cargo.toml since not all changes to the toml should trigger a cache invalidation (for example adding a comment). Cargo.lock would only change when there is a change in the dependency tree, but you're not always able to use it, for example when building a library.
The GitHub example template worked for me, but only after pointing to Cargo.lock in the top-level project directory, rather than **/Cargo.lock.
4

Looking at https://github.com/actions/cache/blob/main/examples.md#rust---cargo as an example, I think you could switch to something like:

name: Rust on: push: branches: [ main ] pull_request: branches: [ main ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/cache@v2 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Build run: cargo build --verbose --all --features "strict" - name: Run tests run: cargo test --verbose --all --features "strict" 

I tried it out on my own project because I was having trouble, but Mihai's suggestion of adding the checkout step first made a big difference.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.