Compare commits

...

26 Commits

Author SHA1 Message Date
Rik Huijzer
cbfbe4981e Add cache-name to main example
This patch suggests to add `cache-name` to the main example since most Julia projects test multiple versions in a matrix.
2024-05-21 15:23:32 +02:00
Dilum Aluthge
2185d31b12 Update README to recommend julia-actions/cache@v2 (instead of @v1) (#129) 2024-05-16 11:30:59 -04:00
dependabot[bot]
d48542bb7b Bump actions/cache from 3.3.3 to 4.0.2 (#123)
Bumps [actions/cache](https://github.com/actions/cache) from 3.3.3 to 4.0.2.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](e12d46a63a...0c45773b62)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
2024-05-01 12:53:50 -04:00
dependabot[bot]
caa655642c Bump pyTooling/Actions from 1.0.1 to 1.0.5 (#127)
Bumps [pyTooling/Actions](https://github.com/pytooling/actions) from 1.0.1 to 1.0.5.
- [Release notes](https://github.com/pytooling/actions/releases)
- [Changelog](https://github.com/pyTooling/Actions/blob/main/doc/Releases.rst)
- [Commits](60281e01e2...e9d0dc3dba)

---
updated-dependencies:
- dependency-name: pyTooling/Actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
2024-05-01 12:38:23 -04:00
dependabot[bot]
1c20782198 Bump actions/checkout from 4.1.2 to 4.1.4 (#126)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](9bb56186c3...0ad4b8fada)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 10:26:19 -04:00
Ian Butterworth
4b23ab708f update setup-julia to v2 in readme (#124) 2024-04-04 21:03:48 -04:00
dependabot[bot]
cc87ef305e Bump pyTooling/Actions from 0.4.6 to 1.0.1 (#117)
* Bump pyTooling/Actions from 0.4.6 to 1.0.1

Bumps [pyTooling/Actions](https://github.com/pytooling/actions) from 0.4.6 to 1.0.1.
- [Release notes](https://github.com/pytooling/actions/releases)
- [Changelog](https://github.com/pyTooling/Actions/blob/main/doc/Releases.rst)
- [Commits](adef08d3bd...60281e01e2)

---
updated-dependencies:
- dependency-name: pyTooling/Actions
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Apply suggestions from code review

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ian Butterworth <i.r.butterworth@gmail.com>
2024-04-04 20:56:26 -04:00
dependabot[bot]
c36389738b Bump actions/checkout from 4.1.1 to 4.1.2 (#118)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](b4ffde65f4...9bb56186c3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 20:04:17 -04:00
dependabot[bot]
1b0706c0c7 Bump julia-actions/setup-julia from 1 to 2 (#116)
Bumps [julia-actions/setup-julia](https://github.com/julia-actions/setup-julia) from 1 to 2.
- [Release notes](https://github.com/julia-actions/setup-julia/releases)
- [Commits](https://github.com/julia-actions/setup-julia/compare/v1...v2)

---
updated-dependencies:
- dependency-name: julia-actions/setup-julia
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 19:47:11 -04:00
Ian Butterworth
8b63b4a15d Update actions/cache to v3.3.3 (#121) 2024-04-04 19:46:22 -04:00
Dilum Aluthge
0413f3b1ae Only run Dependabot once per month, but increase the PR limit (#115) 2024-04-03 11:00:28 -04:00
Mosè Giordano
e8521d4bd2 More accurate info about GitHub cache retion policy (#111) 2024-02-01 07:30:48 -05:00
Rik Huijzer
b1b004ffca Mention why the extra permissions are required (#108) 2024-01-20 11:35:32 -05:00
Sascha Mann
216aaef29a Pin third party action to hash (#106)
Third party actions should never not be pinned to a hash. Otherwise, in case the action repo is taken over by a malicious actor, they can change what runs in all of the workflows that julia-actions/cache is used in as well. Pinning to a hash prevents that.
2024-01-18 10:21:26 -05:00
nathan musoke
3e0649aaee fix: Attempt to install jq on all runners (#105) 2024-01-17 17:38:32 -05:00
Ian Butterworth
dc1a3cdeac make depot if not restored (#91) 2024-01-16 10:48:25 -05:00
Curtis Vogt
4491ed7a86 Pass cache-name between save/restore jobs (#103)
* Pass cache-name between save/restore

* Use save job name in cache-name

* Re-order test jobs

* Fix typo in registry warning
2024-01-16 09:40:31 -06:00
Curtis Vogt
b84ca24db8 Avoid corrupting existing cloned Julia registries (#102)
* Reproduce add-julia-registry issue

* Skip registries restore when already present

* Expand ~

* Refactor paths step to use bash array
2024-01-15 19:28:33 -06:00
Curtis Vogt
b3b34e3264 Test cache action against Julia 1.0 and nightly (#101)
* CI test action on Julia 1.0

* Avoid quoting Julia shell string

* Test against Julia nightly
2024-01-15 15:20:46 -06:00
Curtis Vogt
0c5d92d73a Delete cache entries only on the workflow branch (#97)
* Delete cache entries on the workflow branch

* Grant permissions for cache cleanup

* Add delete-old-caches required for testing purposes

* Revise help message

* Faster generate-key

* Use distinct cache-names for matrix/no-matrix jobs

* Remove redundant permissions

* Better fork detection logic
2024-01-15 10:15:36 -06:00
Curtis Vogt
fca1a91340 URL encode any invalid key characters (#93)
* URL encode any invalid key characters

* Test we handle invalid chars

* Job matrices must match

* Empty commit

* Empty commit
2024-01-09 16:50:08 -05:00
Curtis Vogt
207a5a0786 Unique cache-key for job matrix objects (#88)
* Unique cache-key for job matrix objects

* Update workflow to use object in job matrix

* Restore key should always match startswith

* fixup! Unique cache-key for job matrix objects

* Debug no-matrix

* Tests require overriding workflow/job

* Skip generating matrix_key when no matrix is used

* Install jq for self-hosted runners

* fixup! Install jq for self-hosted runners

* Skip install when not needed

* fixup! Skip install when not needed

* fixup! Skip install when not needed

* Improve `cache-name` description

Co-authored-by: Ian Butterworth <i.r.butterworth@gmail.com>

* Update  description in README

* Use actions/checkout@v4 in example

* add missing period

---------

Co-authored-by: Ian Butterworth <i.r.butterworth@gmail.com>
2024-01-04 20:29:52 -05:00
Ian Butterworth
fab7d6ae0a enable registries by default (#62) 2024-01-04 16:39:29 -05:00
Curtis Vogt
b430ec2989 Add option for user specified Julia depot (#83)
* Add input user-depot

* Only list restored depot directory size on cache-hit

* Remove unnecessary quoting on inputs/outputs

* Rename input to just depot

* Include the depot path in the key

* Documentation update

* Allow JULIA_DEPOT_PATH to specify default depot

* fixup! Allow JULIA_DEPOT_PATH to specify default depot
2024-01-03 19:19:34 -05:00
Curtis Vogt
930a18227b Documentation consistency fixes (#84)
* Consistent use of periods

* Consistent casing of GitHub

* Use 2-space indentation for YAML example

* Add links to for further details
2024-01-02 21:59:12 -05:00
Christian Proud
ff08d7d467 Fix incorrect GitHub actions path on containers (#78)
Co-authored-by: Ian Butterworth <i.r.butterworth@gmail.com>
2023-11-28 11:06:28 -05:00
5 changed files with 381 additions and 176 deletions

View File

@@ -1,9 +1,10 @@
version: 2
updates:
# Keep dependencies for GitHub Actions up-to-date
- package-ecosystem: 'github-actions'
directory: '/'
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: 'daily'
interval: "monthly"
open-pull-requests-limit: 99
labels:
- "dependencies"
- "github-actions"

View File

@@ -20,120 +20,204 @@ permissions:
contents: read
jobs:
generate-key:
generate-prefix:
runs-on: ubuntu-latest
outputs:
cache-name: ${{ steps.name.outputs.cache-name }}
cache-prefix: ${{ steps.name.outputs.cache-prefix }}
steps:
- name: Generate random file
shell: 'julia --color=yes {0}'
run: 'write("random.txt", string(rand(10)))'
- name: Set cache-name as output
- name: Generate random cache-prefix
id: name
run: echo "cache-name=${{ hashFiles('random.txt') }}" >> $GITHUB_OUTPUT
run: |
cache_prefix=$(head -n 100 </dev/urandom | shasum -a 256 | cut -d ' ' -f 1)
echo "cache-prefix=$cache_prefix" >>"$GITHUB_OUTPUT"
test-save:
needs: generate-key
needs: generate-prefix
runs-on: ${{ matrix.os }}
outputs:
cache-name: ${{ steps.cache-name.outputs.cache-name }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
nested:
- name: matrix
invalid-chars: "," # Use invalid characters in job matrix to ensure we escape them
version:
- "1.0"
- "1"
- "nightly"
os:
- ubuntu-latest
- windows-latest
- macOS-latest
exclude:
# Test Julia "1.0" on Linux only
- version: "1.0"
os: windows-latest
- version: "1.0"
os: macOS-latest
fail-fast: false
runs-on: ${{ matrix.os }}
env:
JULIA_DEPOT_PATH: /tmp/julia-depot
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b
- name: Set cache-name
id: cache-name
shell: bash
run: |
echo "cache-name=${{ needs.generate-prefix.outputs.cache-prefix }}-${{ github.job }}" >>"$GITHUB_OUTPUT"
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
- name: Save cache
id: cache
uses: ./
with:
cache-name: ${{ needs.generate-key.outputs.cache-name }}
cache-name: ${{ steps.cache-name.outputs.cache-name }}
delete-old-caches: required
- name: Check no artifacts dir
shell: 'julia --color=yes {0}'
shell: julia --color=yes {0}
run: |
dir = joinpath(first(DEPOT_PATH), "artifacts")
@assert !isdir(dir)
- name: Install a small binary
shell: 'julia --color=yes {0}'
run: 'using Pkg; Pkg.add("pandoc_jll")'
shell: julia --color=yes {0}
run: |
using Pkg
if VERSION >= v"1.3"
Pkg.add(PackageSpec(name="pandoc_jll", version="3"))
else
Pkg.add(PackageSpec(name="Scratch", version="1"))
using Scratch
get_scratch!("test")
end
test-restore:
needs: test-save
runs-on: ${{ matrix.os }}
strategy:
matrix:
nested:
- name: matrix
invalid-chars: "," # Use invalid characters in job matrix to ensure we escape them
version:
- "1.0"
- "1"
- "nightly"
os:
- ubuntu-latest
- windows-latest
- macOS-latest
exclude:
# Test Julia "1.0" on Linux only
- version: "1.0"
os: windows-latest
- version: "1.0"
os: macOS-latest
fail-fast: false
env:
JULIA_DEPOT_PATH: /tmp/julia-depot
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
- name: Restore cache
id: cache
uses: ./
with:
cache-name: ${{ needs.test-save.outputs.cache-name }}
# Cannot require a successful cache delete on forked PRs as the permissions for actions is limited to read
delete-old-caches: ${{ github.event.pull_request.head.repo.fork && 'false' || 'required' }}
- name: Test cache-hit output
shell: julia --color=yes {0}
run: |
@show ENV["cache-hit"]
@assert ENV["cache-hit"] == "true"
env:
cache-hit: ${{ steps.cache.outputs.cache-hit }}
- name: Check existance or emptiness of affected dirs
shell: julia --color=yes {0}
run: |
# These dirs should exist as they've been cached
artifacts_dir = joinpath(first(DEPOT_PATH), "artifacts")
if VERSION >= v"1.3"
@assert !isempty(readdir(artifacts_dir))
else
@assert !isdir(artifacts_dir)
end
packages_dir = joinpath(first(DEPOT_PATH), "packages")
@assert !isempty(readdir(packages_dir))
compiled_dir = joinpath(first(DEPOT_PATH), "compiled")
@assert !isempty(readdir(compiled_dir))
scratchspaces_dir = joinpath(first(DEPOT_PATH), "scratchspaces")
@assert !isempty(readdir(scratchspaces_dir))
logs_dir = joinpath(first(DEPOT_PATH), "logs")
@assert !isempty(readdir(logs_dir))
# Do tests with no matrix also given the matrix is auto-included in cache key
test-save-nomatrix:
needs: generate-key
needs: generate-prefix
runs-on: ubuntu-latest
outputs:
cache-name: ${{ steps.cache-name.outputs.cache-name }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b
- name: Set cache-name
id: cache-name
run: |
echo "cache-name=${{ needs.generate-prefix.outputs.cache-prefix }}-${{ github.job }}" >>"$GITHUB_OUTPUT"
- name: Save cache
id: cache
uses: ./
with:
cache-name: ${{ needs.generate-key.outputs.cache-name }}
cache-name: ${{ steps.cache-name.outputs.cache-name }}
delete-old-caches: required
- name: Check no artifacts dir
shell: 'julia --color=yes {0}'
shell: julia --color=yes {0}
run: |
dir = joinpath(first(DEPOT_PATH), "artifacts")
@assert !isdir(dir)
- name: Install a small binary
shell: 'julia --color=yes {0}'
run: 'using Pkg; Pkg.add("pandoc_jll")'
test-restore:
needs: [generate-key, test-save]
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Restore cache
id: cache
uses: ./
with:
cache-name: ${{ needs.generate-key.outputs.cache-name }}
- name: Test cache-hit output
shell: 'julia --color=yes {0}'
shell: julia --color=yes {0}
run: |
@show ENV["cache-hit"]
@assert ENV["cache-hit"] == "true"
env:
cache-hit: ${{ steps.cache.outputs.cache-hit }}
- name: Check existance or emptiness of affected dirs
shell: 'julia --color=yes {0}'
run: |
# These dirs should exist as they've been cached
artifacts_dir = joinpath(first(DEPOT_PATH), "artifacts")
@assert !isempty(readdir(artifacts_dir))
packages_dir = joinpath(first(DEPOT_PATH), "packages")
@assert !isempty(readdir(packages_dir))
compiled_dir = joinpath(first(DEPOT_PATH), "compiled")
@assert !isempty(readdir(compiled_dir))
scratchspaces_dir = joinpath(first(DEPOT_PATH), "scratchspaces")
@assert !isempty(readdir(scratchspaces_dir))
logs_dir = joinpath(first(DEPOT_PATH), "logs")
@assert !isempty(readdir(logs_dir))
using Pkg
if VERSION >= v"1.3"
Pkg.add(PackageSpec(name="pandoc_jll", version="3"))
else
Pkg.add(PackageSpec(name="Scratch", version="1"))
using Scratch
get_scratch!("test")
end
test-restore-nomatrix:
needs: [generate-key, test-save-nomatrix]
needs: test-save-nomatrix
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b
- name: Restore cache
id: cache
uses: ./
with:
cache-name: ${{ needs.generate-key.outputs.cache-name }}
cache-name: ${{ needs.test-save-nomatrix.outputs.cache-name }}
# Cannot require a successful cache delete on forked PRs as the permissions for actions is limited to read
delete-old-caches: ${{ github.event.pull_request.head.repo.fork && 'false' || 'required' }}
- name: Test cache-hit output
shell: 'julia --color=yes {0}'
shell: julia --color=yes {0}
run: |
@show ENV["cache-hit"]
@assert ENV["cache-hit"] == "true"
env:
cache-hit: ${{ steps.cache.outputs.cache-hit }}
- name: Check existance or emptiness of affected dirs
shell: 'julia --color=yes {0}'
shell: julia --color=yes {0}
run: |
# These dirs should exist as they've been cached
artifacts_dir = joinpath(first(DEPOT_PATH), "artifacts")
@assert !isempty(readdir(artifacts_dir))
if VERSION >= v"1.3"
@assert !isempty(readdir(artifacts_dir))
else
@assert !isdir(artifacts_dir)
end
packages_dir = joinpath(first(DEPOT_PATH), "packages")
@assert !isempty(readdir(packages_dir))
compiled_dir = joinpath(first(DEPOT_PATH), "compiled")
@@ -143,3 +227,54 @@ jobs:
logs_dir = joinpath(first(DEPOT_PATH), "logs")
@assert !isempty(readdir(logs_dir))
test-save-cloned-registry:
needs: generate-prefix
runs-on: ubuntu-latest
outputs:
cache-name: ${{ steps.cache-name.outputs.cache-name }}
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b
- name: Set cache-name
id: cache-name
run: |
echo "cache-name=${{ needs.generate-prefix.outputs.cache-prefix }}-${{ github.job }}" >>"$GITHUB_OUTPUT"
- name: Save cache
uses: ./
with:
cache-name: ${{ steps.cache-name.outputs.cache-name }}
# Cannot require a successful cache delete on forked PRs as the permissions for actions is limited to read
delete-old-caches: ${{ github.event.pull_request.head.repo.fork && 'false' || 'required' }}
- name: Add General registry clone
shell: julia --color=yes {0}
run: |
using Pkg
Pkg.Registry.add("General")
env:
JULIA_PKG_SERVER: ""
# Set the registry worktree to an older state to simulate the cache storing an old version of the registry.
- name: Use outdated General worktree
run: git -C ~/.julia/registries/General reset --hard HEAD~20
test-restore-cloned-registry:
needs: test-save-cloned-registry
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b
- name: Add General registry clone
shell: julia --color=yes {0}
run: |
using Pkg
Pkg.Registry.add("General")
env:
JULIA_PKG_SERVER: ""
- name: Restore cache
uses: ./
with:
cache-name: ${{ needs.test-save-cloned-registry.outputs.cache-name }}
# Cannot require a successful cache delete on forked PRs as the permissions for actions is limited to read
delete-old-caches: ${{ github.event.pull_request.head.repo.fork && 'false' || 'required' }}
- name: Test registry is not corrupt
shell: julia --color=yes {0}
run: |
using Pkg
Pkg.Registry.update()

View File

@@ -19,36 +19,43 @@ permissions:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
version:
- '1.6'
- '1'
steps:
- uses: actions/checkout@v3
- uses: julia-actions/setup-julia@v1
- uses: julia-actions/cache@v1
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
- uses: julia-actions/cache@v2
with:
cache-name: 'test-${{ matrix.os }}'
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
```
By default the majority of the depot is cached. To also cache `~/.julia/registries/`, use
By default all depot directories called out below are cached.
```yaml
- uses: julia-actions/cache@v1
with:
cache-registries: "true"
```
### Requirements
However note that caching the registries may mean that the registry will not be updated each run.
This action uses [`jq`](https://github.com/jqlang/jq) to parse JSON.
`jq` is installed by default in GitHub-hosted runners.
[`dcarbone/install-jq-action`](https://github.com/dcarbone/install-jq-action) is used to check that `jq` is available and install it if not.
**Note:** installing `jq` with `dcarbone/install-jq-action` requires that curl is available; this may not be the case in custom containers.
### Optional Inputs
- `cache-name` - The cache key prefix. Defaults to `julia-cache-${{ github.workflow }}-${{ github.job }}`. The key body automatically includes matrix vars and the OS. Include any other parameters/details in this prefix to ensure one unique cache key per concurrent job type.
- `cache-name` - The cache key prefix. Defaults to `julia-cache;workflow=${{ github.workflow }};job=${{ github.job }}`. The key body automatically includes the OS and, unless disabled with `include-matrix`, the matrix vars. Include any other parameters/details in this prefix to ensure one unique cache key per concurrent job type.
- `include-matrix` - Whether to include the matrix values when constructing the cache key. Defaults to `true`.
- `cache-artifacts` - Whether to cache `~/.julia/artifacts/`. Defaults to `true`.
- `cache-packages` - Whether to cache `~/.julia/packages/`. Defaults to `true`.
- `cache-registries` - Whether to cache `~/.julia/registries/`. Defaults to `false`. Disabled to ensure CI gets latest versions.
- `cache-compiled` - Whether to cache `~/.julia/compiled/`. Defaults to `true`.
- `cache-scratchspaces` - Whether to cache `~/.julia/scratchspaces/`. Defaults to `true`.
- `cache-logs` - Whether to cache `~/.julia/logs/`. Defaults to `true`. Helps auto-`Pkg.gc()` keep the cache small.
- `delete-old-caches` - Whether to delete old caches for the given key. Defaults to `true`
- `token` - A github PAT. Defaults to `github.token`. Requires `repo` scope to enable the deletion of old caches.
- `depot` - Path to a Julia [depot](https://pkgdocs.julialang.org/v1/glossary/) directory where cached data will be saved to and restored from. Defaults to the first depot in [`JULIA_DEPOT_PATH`](https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_DEPOT_PATH) if specified. Otherwise, defaults to `~/.julia`.
- `cache-artifacts` - Whether to cache the depot's `artifacts` directory. Defaults to `true`.
- `cache-packages` - Whether to cache the depot's `packages` directory. Defaults to `true`.
- `cache-registries` - Whether to cache the depot's `registries` directory. Defaults to `true`.
- `cache-compiled` - Whether to cache the depot's `compiled` directory. Defaults to `true`.
- `cache-scratchspaces` - Whether to cache the depot's `scratchspaces` directory. Defaults to `true`.
- `cache-logs` - Whether to cache the depot's `logs` directory. Defaults to `true`. Helps auto-`Pkg.gc()` keep the cache small.
- `delete-old-caches` - Whether to delete old caches for the given key. Defaults to `true`.
- `token` - A [GitHub PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). Defaults to `github.token`. Requires `repo` scope to enable the deletion of old caches.
### Outputs
@@ -62,6 +69,10 @@ This cached file is then restored upon the second run, and afterwards resaved un
The benefit of caching is that downloading one big file is quicker than downloading many different files from many different locations
and precompiling them.
By default, this action removes caches that were previously made by jobs on the same branch with the same restore key.
GitHub automatically removes old caches after a certain period or when the repository cache allocation is full.
It is, however, more efficient to explicitly remove old caches to improve caching for less frequently run jobs.
### Cache keys
The cache key that the cache will be saved as is based on:
@@ -82,16 +93,16 @@ This action automatically deletes old caches that match the first 4 fields of th
- All variables in the `matrix` (unless disabled via `include-matrix: 'false'`)
- The `runner.os` (may be in the matrix too, but included for safety)
Which means your caches files will not grow needlessly. Github also deletes cache files after
[90 days which can be increased in private repos to up to 400 days](https://docs.github.com/en/organizations/managing-organization-settings/configuring-the-retention-period-for-github-actions-artifacts-and-logs-in-your-organization)
Which means your caches files will not grow needlessly. GitHub also deletes cache files after
[7 days of not being accessed](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy), and there is a limit of 10 GB for the total size of cache files associated to each repository.
> [!NOTE]
> To allow deletion of caches you will likely need to grant the following to the default
> `GITHUB_TOKEN` by adding this to your yml:
> ```
> To allow deletion of caches you will likely need to [grant the following permissions](https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs)
> to the `GITHUB_TOKEN` by adding this to your GitHub actions workflow:
> ```yaml
> permissions:
> actions: write
> contents: read
> actions: write
> contents: read
> ```
> (Note this won't work for fork PRs but should once merged)
> Or provide a token with `repo` scope via the `token` input option.

View File

@@ -8,101 +8,150 @@ branding:
inputs:
cache-name:
description: 'The cache key prefix. Unless disabled the key body automatically includes matrix vars, and the OS. Include any other parameters/details in this prefix to ensure one unique cache key per concurrent job type.'
default: 'julia-cache-${{ github.workflow }}-${{ github.job }}'
description: >-
The cache key prefix. The key body automatically includes the OS and, unless disabled, the matrix vars.
Include any other parameters/details in this prefix to ensure one unique cache key per concurrent job type.
default: julia-cache;workflow=${{ github.workflow }};job=${{ github.job }}
include-matrix:
description: 'Whether to include the matrix values when constructing the cache key'
description: Whether to include the matrix values when constructing the cache key.
default: 'true'
depot:
description: Path to a Julia depot directory where cached data will be saved to and restored from.
default: ''
cache-artifacts:
description: 'Whether to cache ~/.julia/artifacts/'
description: Whether to cache the depot's `artifacts` directory.
default: 'true'
cache-packages:
description: 'Whether to cache ~/.julia/packages/'
description: Whether to cache the depot's `packages` directory.
default: 'true'
cache-registries:
description: 'Whether to cache ~/.julia/registries/. This is off by default to ensure CI gets latest versions'
default: 'false'
description: Whether to cache the depot's `registries` directory.
default: 'true'
cache-compiled:
description: 'Whether to cache ~/.julia/compiled/'
description: Whether to cache the depot's `compiled` directory.
default: 'true'
cache-scratchspaces:
description: 'Whether to cache ~/.julia/scratchspaces/'
description: Whether to cache the depot's `scratchspaces` directory.
default: 'true'
cache-logs:
description: 'Whether to cache ~/.julia/logs/. This helps automatic Pkg.gc() keep the cache size down'
description: Whether to cache the depot's `logs` directory. This helps automatic `Pkg.gc()` keep the cache size down.
default: 'true'
delete-old-caches:
description: 'Whether to delete old caches for the given key'
description: Whether to delete old caches for the given key.
default: 'true'
token:
description: 'A github PAT. Requires `repo` scope to enable the deletion of old caches'
default: '${{ github.token }}'
description: A GitHub PAT. Requires `repo` scope to enable the deletion of old caches.
default: ${{ github.token }}
outputs:
cache-hit:
description: 'A boolean value to indicate an exact match was found for the primary key. Returns \"\" when the key is new. Forwarded from actions/cache'
description: A boolean value to indicate an exact match was found for the primary key. Returns "" when the key is new. Forwarded from actions/cache.
value: ${{ steps.hit.outputs.cache-hit }}
runs:
using: 'composite'
steps:
- name: Install jq
uses: dcarbone/install-jq-action@8867ddb4788346d7c22b72ea2e2ffe4d514c7bcb
with:
force: false # Skip install when an existing `jq` is present
- id: paths
run: |
[ "${{ inputs.cache-artifacts }}" = "true" ] && A_PATH="~/.julia/artifacts"
echo "artifacts-path=$A_PATH" >> $GITHUB_OUTPUT
[ "${{ inputs.cache-packages }}" = "true" ] && P_PATH="~/.julia/packages"
echo "packages-path=$P_PATH" >> $GITHUB_OUTPUT
[ "${{ inputs.cache-registries }}" = "true" ] && R_PATH="~/.julia/registries"
echo "registries-path=$R_PATH" >> $GITHUB_OUTPUT
[ "${{ inputs.cache-compiled }}" = "true" ] && PCC_PATH="~/.julia/compiled"
echo "compiled-path=$PCC_PATH" >> $GITHUB_OUTPUT
[ "${{ inputs.cache-scratchspaces }}" = "true" ] && S_PATH="~/.julia/scratchspaces"
echo "scratchspaces-path=$S_PATH" >> $GITHUB_OUTPUT
[ "${{ inputs.cache-logs }}" = "true" ] && L_PATH="~/.julia/logs"
echo "logs-path=$L_PATH" >> $GITHUB_OUTPUT
shell: bash
if [ -n "${{ inputs.depot }}" ]; then
depot="${{ inputs.depot }}"
elif [ -n "$JULIA_DEPOT_PATH" ]; then
# Use the first depot path
depot=$(echo $JULIA_DEPOT_PATH | cut -d$PATH_DELIMITER -f1)
else
depot="~/.julia"
fi
echo "depot=$depot" | tee -a "$GITHUB_OUTPUT"
# MATRIX_STRING is a join of all matrix variables that helps concurrent runs have a unique cache key.
# The underscore at the end of the restore key demarks the end of the restore section. Without this
# a runner without a matrix has a restore key that will cause impropper clearing of caches from those
# with a matrix.
- id: keys
cache_paths=()
artifacts_path="${depot}/artifacts"
[ "${{ inputs.cache-artifacts }}" = "true" ] && cache_paths+=("$artifacts_path")
packages_path="${depot}/packages"
[ "${{ inputs.cache-packages }}" = "true" ] && cache_paths+=("$packages_path")
registries_path="${depot}/registries"
if [ "${{ inputs.cache-registries }}" = "true" ]; then
if [ ! -d "${registries_path/#\~/$HOME}" ]; then
cache_paths+=("$registries_path")
else
echo "::warning::Julia depot registries already exist. Skipping restoring of cached registries to avoid potential merge conflicts when updating. Please ensure that \`julia-actions/cache\` precedes any workflow steps which add registries."
fi
fi
compiled_path="${depot}/compiled"
[ "${{ inputs.cache-compiled }}" = "true" ] && cache_paths+=("$compiled_path")
scratchspaces_path="${depot}/scratchspaces"
[ "${{ inputs.cache-scratchspaces }}" = "true" ] && cache_paths+=("$scratchspaces_path")
logs_path="${depot}/logs"
[ "${{ inputs.cache-logs }}" = "true" ] && cache_paths+=("$logs_path")
{
echo "cache-paths<<EOF"
printf "%s\n" "${cache_paths[@]}"
echo "EOF"
} | tee -a "$GITHUB_OUTPUT"
shell: bash
env:
PATH_DELIMITER: ${{ runner.OS == 'Windows' && ';' || ':' }}
- name: Generate Keys
id: keys
run: |
[ "${{ inputs.include-matrix }}" == "true" ] && MATRIX_STRING="${{ join(matrix.*, '-') }}"
[ -n "$MATRIX_STRING" ] && MATRIX_STRING="-${MATRIX_STRING}"
RESTORE_KEY="${{ inputs.cache-name }}-${{ runner.os }}${MATRIX_STRING}_"
echo "restore-key=${RESTORE_KEY}" >> $GITHUB_OUTPUT
echo "key=${RESTORE_KEY}${{ github.run_id }}-${{ github.run_attempt }}" >> $GITHUB_OUTPUT
# `matrix_key` joins all of matrix keys/values (including nested objects) to ensure that concurrent runs each use a unique cache key.
# When `matrix` isn't set for the job then `MATRIX_JSON=null`.
if [ "${{ inputs.include-matrix }}" == "true" ] && [ "$MATRIX_JSON" != "null" ]; then
matrix_key=$(echo "$MATRIX_JSON" | jq 'paths(type != "object") as $p | ($p | join("-")) + "=" + (getpath($p) | tostring)' | jq -rs 'join(";") | . + ";"')
fi
restore_key="${{ inputs.cache-name }};os=${{ runner.os }};${matrix_key}"
# URL encode any restricted characters:
# https://github.com/actions/toolkit/blob/5430c5d84832076372990c7c27f900878ff66dc9/packages/cache/src/cache.ts#L38-L43
restore_key=$(sed 's/,/%2C/g' <<<"${restore_key}")
key="${restore_key}run_id=${{ github.run_id }};run_attempt=${{ github.run_attempt }}"
echo "restore-key=${restore_key}" >> $GITHUB_OUTPUT
echo "key=${key}" >> $GITHUB_OUTPUT
shell: bash
env:
MATRIX_JSON: ${{ toJSON(matrix) }}
- uses: actions/cache@4d4ae6ae148a43d0fd1eda1800170683e9882738
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
id: cache
with:
path: |
${{ steps.paths.outputs.artifacts-path }}
${{ steps.paths.outputs.packages-path }}
${{ steps.paths.outputs.registries-path }}
${{ steps.paths.outputs.scratchspaces-path }}
${{ steps.paths.outputs.compiled-path }}
${{ steps.paths.outputs.logs-path }}
${{ steps.paths.outputs.cache-paths }}
key: ${{ steps.keys.outputs.key }}
restore-keys: ${{ steps.keys.outputs.restore-key }}
enableCrossOsArchive: false
- name: list restored depot directory sizes
run: du -shc ~/.julia/* || true
# if it wasn't restored make the depot anyway as a signal that this action ran
# for other julia actions to check, like https://github.com/julia-actions/julia-buildpkg/pull/41
- name: make depot if not restored, then list depot directory sizes
run: |
mkdir -p ${{ steps.paths.outputs.depot }}
du -shc ${{ steps.paths.outputs.depot }}/* || true
shell: bash
# github and actions/cache doesn't provide a way to update a cache at a given key, so we delete any
# that match the restore key just before saving the new cache
- uses: pyTooling/Actions/with-post-step@adef08d3bdef092282614f3b683897cefae82ee3
if: ${{ inputs.delete-old-caches == 'true' }}
# Not windows
- uses: pyTooling/Actions/with-post-step@e9d0dc3dba9fda45f195946858708f60c0240caf # v1.0.5
if: ${{ inputs.delete-old-caches != 'false' && runner.OS != 'Windows' }}
with:
# seems like there has to be a `main` step in this action. Could list caches for info if we wanted
# main: julia ${{ github.action_path }}/handle_caches.jl "${{ github.repository }}" "list"
main: echo ""
post: julia ${{ github.action_path }}/handle_caches.jl "${{ github.repository }}" "rm" "${{ steps.keys.outputs.restore-key }}"
post: julia $GITHUB_ACTION_PATH/handle_caches.jl rm "${{ github.repository }}" "${{ steps.keys.outputs.restore-key }}" "${{ github.ref }}" "${{ inputs.delete-old-caches != 'required' }}"
env:
GH_TOKEN: ${{ inputs.token }}
# Windows (because this action uses command prompt on windows)
- uses: pyTooling/Actions/with-post-step@e9d0dc3dba9fda45f195946858708f60c0240caf # v1.0.5
if: ${{ inputs.delete-old-caches != 'false' && runner.OS == 'Windows' }}
with:
main: echo ""
post: cd %GITHUB_ACTION_PATH% && julia handle_caches.jl rm "${{ github.repository }}" "${{ steps.keys.outputs.restore-key }}" "${{ github.ref }}" "${{ inputs.delete-old-caches != 'required' }}"
env:
GH_TOKEN: ${{ inputs.token }}

View File

@@ -1,58 +1,67 @@
using Pkg, Dates
function handle_caches()
repo = ARGS[1]
func = ARGS[2]
restore_key = get(ARGS, 3, "")
subcommand = ARGS[1]
if func == "list"
if subcommand == "list"
repo = ARGS[2]
println("Listing existing caches")
run(`gh cache list --limit 100 --repo $repo`)
elseif func == "rm"
caches = String[]
failed = String[]
for _ in 1:5 # limit to avoid accidental rate limiting
hits = split(strip(read(`gh cache list --limit 100 --repo $repo`, String)), keepempty=false)
search_again = length(hits) == 100
filter!(contains(restore_key), hits)
isempty(hits) && break
# We can delete everything that matches the restore key because the new cache is saved later.
for c in hits
elseif subcommand == "rm"
repo, restore_key, ref = ARGS[2:4]
allow_failure = ARGS[5] == "true"
endpoint = "/repos/$repo/actions/caches"
page = 1
per_page = 100
escaped_restore_key = replace(restore_key, "\"" => "\\\"")
query = ".actions_caches[] | select(.key | startswith(\"$escaped_restore_key\")) | .id"
deletions = String[]
failures = String[]
while 1 <= page <= 5 # limit to avoid accidental rate limiting
cmd = `gh api -X GET $endpoint -F ref=$ref -F per_page=$per_page -F page=$page --jq $query`
ids = split(read(cmd, String); keepempty=false)
page = length(ids) == per_page ? page + 1 : -1
# We can delete all cache entries on this branch that matches the restore key
# because the new cache is saved later.
for id in ids
try
run(`gh cache delete $(split(c)[1]) --repo $repo`)
push!(caches, c)
run(`gh cache delete $id --repo $repo`)
push!(deletions, id)
catch e
@error e
push!(failed, c)
push!(failures, id)
end
end
search_again || break
end
if isempty(failed) && isempty(caches)
println("No existing caches found for restore key `$restore_key`")
if isempty(failures) && isempty(deletions)
println("No existing caches found on ref `$ref` matching restore key `$restore_key`")
else
if !isempty(failed)
println("Failed to delete $(length(failed)) existing caches for restore key `$restore_key`")
println.(failed)
if !isempty(failures)
println("Failed to delete $(length(failures)) existing caches on ref `$ref` matching restore key `$restore_key`")
println.(failures)
@info """
To delete caches you need to grant the following to the default `GITHUB_TOKEN` by adding
this to your yml:
this to your workflow:
```
permissions:
actions: write
contents: read
actions: write
contents: read
```
(Note this won't work for fork PRs but should once merged)
Or provide a token with `repo` scope via the `token` input option.
See https://cli.github.com/manual/gh_cache_delete
"""
allow_failure || exit(1)
end
if !isempty(caches)
println("$(length(caches)) existing caches deleted that match restore key `$restore_key`:")
println.(caches)
if !isempty(deletions)
println("Deleted $(length(deletions)) caches on ref `$ref` matching restore key `$restore_key`")
println.(deletions)
end
end
else
throw(ArgumentError("Unexpected second argument: $func"))
throw(ArgumentError("Unexpected subcommand: $subcommand"))
end
end