Clear cache in GitHub Actions

Viewed 38635

I am working on an R package and using GitHub Action (GHA) as a Continuous Integration (CI) provider. I cache R packages (dependencies) by using actions/cache. And now I want to clear all cache. How can I do that?


A part of GHA Workflow I use:
on: push

name: R-CMD-check

jobs:
  R-CMD-check:
    runs-on: ${{ matrix.config.os }}

    name: ${{ matrix.config.os }} (${{ matrix.config.r }})

    strategy:
      fail-fast: false
      matrix:
        config:
          # - {os: windows-latest, r: 'devel'}
          - {os: macOS-latest,   r: 'release'}

    env:
      R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
      RSPM: ${{ matrix.config.rspm }}
      GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - uses: actions/checkout@v2

      - uses: r-lib/actions/setup-r@master

      - name: Query dependencies
        run: |
          repos <- c("https://r-hyperspec.github.io/hySpc.pkgs/", getOption("repos"))
          saveRDS("remotes::dev_package_deps(dependencies = TRUE)", ".github/depends.Rds", version = 2)
          writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
        shell: Rscript {0}

      - name: Cache R packages
        if: runner.os != 'Windows'
        uses: actions/cache@v1
        with:
          path: ${{ env.R_LIBS_USER }}
          key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
          restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-

      - name: Install dependencies
        run:   remotes::install_deps(dependencies = TRUE)
        shell: Rscript {0}

      - name: Session info
        run: |
          options(width = 100)
          pkgs <- installed.packages()[, "Package"]
          sessioninfo::session_info(pkgs, include_base = TRUE)
        shell: Rscript {0}
6 Answers

As pointed out in the corresponding issue, you can now (since June 27, 2022) query and manage caches via the GitHub Actions Cache API. For example:

  • GET list of caches for a repository:

    $ curl \
        -H "Accept: application/vnd.github.v3+json" \
        -H "Authorization: token <TOKEN>" \
        https://api.github.com/repos/OWNER/REPO/actions/caches
    
  • DELETE cache for a repository using a cache ID:

    $ curl \
        -X DELETE \
        -H "Accept: application/vnd.github.v3+json" \
        -H "Authorization: token <TOKEN>" \
        https://api.github.com/repos/OWNER/REPO/actions/caches/CACHE_ID
    

You can also use the GitHub CLI to interact with the API, but it is unclear when you can use the UI.


Alternatively, two practical workarounds can be used to force the use of a new cache. This is not exactly the same as clearing the current cache, but it does the job.

In order to do so, you have to change the cache key (and any restore-keys). Because if the key(s) is/are different, this is considered a cache miss and you start with a new one.

You can change the cache key either by modifying the workflow file directly, e.g., by adding a version number:

key: ${{ runner.os }}-mycache-v1-${{ hashFiles(...) }}

If you now want to use a new cache, all you have to do is to commit a different version number:

key: ${{ runner.os }}-mycache-v2-${{ hashFiles(...) }}

If you don't want to modify the workflow file and prefer using the UI, you can abuse secrets:

key: ${{ runner.os }}-mycache-${{ secrets.CACHE_VERSION }}-${{ hashFiles(...) }}

Whenever the secret changes, a new cache will be used.

You cannot force a clear cache currently and it seems there is an open feature request for it at the moment https://github.com/actions/cache/issues/2. If I were you, I would post the request there as well so that they know that more people want the feature implemented.

A few things to note about the action:

There are not parameters in the action and not even in the toolkit package that this action is build on top of.

Getting deep into the toolkit code they use a cache api url to do all the good stuff. This means we don't even know if that api supports it with the assumption that we try to test it out and see what else it provides by hitting it directly. Here is the line for the api call for which the base url is taken from env ACTIONS_CACHE_URL

https://github.com/actions/toolkit/blob/c2bc747506bf562195a02bd4fdb1ff2a95d8b7ed/packages/cache/src/internal/cacheHttpClient.ts#L44

npm package as reference https://www.npmjs.com/package/@actions/cache

If we take a step back for a moment and go back to the github docs now that we have looked deep into the action/cache code and how it works,

Per the github docs https://docs.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows

There are two things to note,

Once you create a cache, you cannot change the contents of an existing 
cache but you can create a new cache with a new key.
GitHub will remove any cache entries that have not been accessed in over 7 days.

Based @beatngu13 answer I've created Makefile target that remove all cache from Github Actions:

GH_REPO ?= ALLATRA-IT/ephyr  # replace with your `org/repo`

# Clear Github Acions usage cache.
#
# Need to install github cli first [https://cli.github.com/] and authorize
#
# Usage:
#   make gh.clear

gh.clear:
    gh api -H "Accept: application/vnd.github+json" \
        /repos/$(GH_REPO)/actions/caches \
        | for ID in `jq '.actions_caches[].id'`; \
          do echo "Deleting $$ID"; \
             gh api --method DELETE /repos/$(GH_REPO)/actions/caches/$$ID | echo; done

Need to install Github CLI and jq tool tools first. After install Github CLI you need to authorize with:

gh auth login

@GegznaV you can use something like tmate, and manually clear cache by sshing into a runner.

This can be done from GH Actions as well.

name: Clear cache

on:
  workflow_dispatch:

permissions:
  actions: write

jobs:
  clear-ccache:
    runs-on: ubuntu-latest
    steps:
      - name: Clear cache
        uses: actions/github-script@v6
        with:
          script: |
            console.log("About to clear")
            const caches = await github.rest.actions.getActionsCacheList({
              owner: context.repo.owner,
              repo: context.repo.repo,
            })
            for (const cache of caches.data.actions_caches) {
              console.log(cache)
              github.rest.actions.deleteActionsCacheById({
                owner: context.repo.owner,
                repo: context.repo.repo,
                cache_id: cache.id,
              })
            }
            console.log("Clear completed")

Make sure it's merged to HEAD branch and trigger it manually from Actions.

Here's another automated solution similar to @beatngu13's cache key invalidation method. This one uses a generated timestamp file that gets committed and then used in a hashFiles(...) prefix to the cache key.

Included is a set of GNU Makefile targets to make using this method very easy: make clear-github-cache

Note: Make sure each Makefile "recipe" section is indented with 1 tab character! StackOverflow converts tabs to spaces in the code block.

.github/clear_github_actions_cache:
    date  +%s > .github/clear_github_actions_cache

.PHONY: clean-github-cache-file clear-github-cache
clear-github-cache: clean-github-cache-file .github/clear_github_actions_cache ## Force GitHub Actions Cache key to change for fresh CI run
    git add .github/clear_github_actions_cache
    git commit -m "Clear GitHub Actions Cache @ $$(cat .github/clear_github_actions_cache)"

clean-github-cache-file: ## Remove GitHub Actions Cache timestamp invalidator file.
    [ -f '.github/clear_github_actions_cache' ] && rm -f '.github/clear_github_actions_cache' || true

Then, inside the GitHub Actions workflow YAML, add a cache key prefix that uses the hash of this file:

      - name: Cache R packages
        if: runner.os != 'Windows'
        uses: actions/cache@v2
        with:
          path: ${{ env.R_LIBS_USER }}
          key: ${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.github/clear_github_actions_cache') }}-${{ hashFiles('.github/R-version') }}-${{ hashFiles('.github/depends.Rds') }}
          restore-keys: |
            ${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.github/clear_github_actions_cache') }}-${{ hashFiles('.github/R-version') }}-${{ hashFiles('.github/depends.Rds') }}
            ${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.github/clear_github_actions_cache') }}-${{ hashFiles('.github/R-version') }}-
            ${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.github/clear_github_actions_cache') }}-

Now just run:

make clear-github-cache

This generates and commits a file, .github/clear_github_actions_cache, with Unix timestamp as contents. The hashFiles(...) cache key prefix uses this and will generate a unique hash of this file as a prefix to the rest of the cache / restore-key.

Next time you use git push, the cache key will be invalidated... effectively running GitHub Actions with an empty cache.

Related