Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions init-pants/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,19 @@ If you need to ignore old caches, please change `gha-cache-key`.

## Output

This action has no output.
The following outputs are available for passing to the
[`save-pants-cache`](../save-pants-cache) companion action:

| Output | Description |
|--------|-------------|
| `setup-cache-path` | Path to the Pants setup cache directory |
| `setup-cache-key` | GHA cache key for the Pants setup cache |
| `named-caches-path` | Path to the Pants named caches directory |
| `named-caches-key` | GHA cache key for the Pants named caches |
| `lmdb-store-path` | Path to the Pants LMDB store directory |
| `lmdb-store-key` | GHA cache key for the Pants LMDB store |
| `cache-lmdb-store` | Whether the LMDB store cache is enabled |
| `named-caches-hash` | The named caches hash value |

## Input

Expand Down Expand Up @@ -94,13 +106,41 @@ The environment variable should be available for the remainder of the workflow t

## Usage Example

Here is an example of how to use this action in a workflow. Please note that you should check whether the `v10` tag is in fact the latest tagged release of these actions, and update accordingly if it is not to either a more recent tag or a known-good commit on `main` (but not `main` directly!).
This action only restores caches. You must add `save-pants-cache` with
`if: always()` at the end of your job to persist them. This ensures cache
saves survive workflow cancellation (e.g., `cancel-in-progress: true`).

```yaml
- name: Initialize Pants
uses: pantsbuild/actions/init-pants@v10
id: pants-init
uses: pantsbuild/actions/init-pants@v11
with:
# cache0 makes it easy to bust the cache if needed
gha-cache-key: cache0-py${{ matrix.python_version }}
named-caches-hash: ${{ hashFiles('lockfiles/*.json', '**/something-else.lock') }}

# ... your build/test steps ...

- name: Save Pants caches
if: always()
uses: pantsbuild/actions/save-pants-cache@v11
with:
setup-cache-path: ${{ steps.pants-init.outputs.setup-cache-path }}
setup-cache-key: ${{ steps.pants-init.outputs.setup-cache-key }}
named-caches-path: ${{ steps.pants-init.outputs.named-caches-path }}
named-caches-key: ${{ steps.pants-init.outputs.named-caches-key }}
named-caches-hash: ${{ steps.pants-init.outputs.named-caches-hash }}
lmdb-store-path: ${{ steps.pants-init.outputs.lmdb-store-path }}
lmdb-store-key: ${{ steps.pants-init.outputs.lmdb-store-key }}
cache-lmdb-store: ${{ steps.pants-init.outputs.cache-lmdb-store }}
```

### Migration from previous versions

Previously, `init-pants` used `actions/cache@v5` which saves caches via a
post-step hook. Post-step hooks do not run on workflow cancellation, so
workflows using `cancel-in-progress: true` would lose cache saves on every
cancelled run — creating a cold-cache cycle.

To migrate: add an `id` to your `init-pants` step and add the `save-pants-cache`
step shown above at the end of your job. No other changes are needed.
64 changes: 52 additions & 12 deletions init-pants/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ description: |
launcher binary to ~/bin, which will be placed on the $PATH. Otherwise, the `get-pants.sh`
script will be downloaded first and then invoked as above.

BREAKING: This action now restores caches only. You must add
pantsbuild/actions/save-pants-cache with `if: always()` at the end of
your job to persist caches. This ensures cache saves survive workflow
cancellation (e.g., cancel-in-progress: true).

inputs:
# Note: inputs are always string typed.
setup-commit:
Expand Down Expand Up @@ -81,7 +86,7 @@ inputs:
description: |
If set to 'true', this action will set up a Python interpreter suitable for testing/linting
custom Pants plugin code in your repo. Pants plugins will run on the interpreter embedded
in Pants, and so must be tested/linted on an interpreter of the same version (currently 3.14,
in Pants, and so must be tested/linted on an interpreter of the same version (currently 3.14,
3.11, or 3.9).
This may be different than the interpreter version(s) your other Python code requires.
So this convenience option streamlines installing an interpreter specifically for
Expand All @@ -103,6 +108,32 @@ inputs:
required: false
default: ${{ github.workspace }}

outputs:
setup-cache-path:
description: Path to the Pants setup cache directory
value: ${{ steps.cache_metadata.outputs.setup_cache_path }}
setup-cache-key:
description: GHA cache key for the Pants setup cache
value: ${{ steps.cache_metadata.outputs.setup_cache_key }}
named-caches-path:
description: Path to the Pants named caches directory
value: ${{ steps.cache_metadata.outputs.named_caches_path }}
named-caches-key:
description: GHA cache key for the Pants named caches
value: ${{ steps.cache_metadata.outputs.named_caches_key }}
lmdb-store-path:
description: Path to the Pants LMDB store directory
value: ${{ steps.cache_metadata.outputs.lmdb_store_path }}
lmdb-store-key:
description: GHA cache key for the Pants LMDB store
value: ${{ steps.cache_metadata.outputs.lmdb_store_key }}
cache-lmdb-store:
description: Whether the LMDB store cache is enabled (passthrough of the input)
value: ${{ inputs.cache-lmdb-store }}
named-caches-hash:
description: The named caches hash value (passthrough of the input)
value: ${{ inputs.named-caches-hash }}

runs:
using: "composite"
steps:
Expand Down Expand Up @@ -148,20 +179,30 @@ runs:
echo "pants_bootstrap_cache_key=$PANTS_BOOTSTRAP_CACHE_KEY" >> $GITHUB_OUTPUT
echo "pants_bootstrap_cache_dir=$PANTS_BOOTSTRAP_CACHE_DIR" >> $GITHUB_OUTPUT

- name: Cache Pants setup
id: cache-pants-setup
uses: actions/cache@v5
- name: Export cache metadata
id: cache_metadata
shell: bash
run: |
echo "setup_cache_path=${{ steps.pants_bootstrap_cache.outputs.pants_bootstrap_cache_dir }}" >> $GITHUB_OUTPUT
echo "setup_cache_key=pants-setup-${{ steps.pants_bootstrap_cache.outputs.pants_bootstrap_cache_key }}" >> $GITHUB_OUTPUT
echo "named_caches_path=${{ inputs.named-caches-location }}" >> $GITHUB_OUTPUT
echo "named_caches_key=pants-named-caches-${{ runner.os }}-${{ inputs.gha-cache-key }}-${{ hashFiles('pants.toml') }}-${{ inputs.named-caches-hash }}" >> $GITHUB_OUTPUT
echo "lmdb_store_path=${{ inputs.lmdb-store-location }}" >> $GITHUB_OUTPUT
echo "lmdb_store_key=pants-lmdb-store-${{ runner.os }}-${{ inputs.gha-cache-key }}-${{ github.sha }}" >> $GITHUB_OUTPUT

- name: Restore Pants setup cache
uses: actions/cache/restore@v5
with:
path: |
${{ steps.pants_bootstrap_cache.outputs.pants_bootstrap_cache_dir }}
key: pants-setup-${{ steps.pants_bootstrap_cache.outputs.pants_bootstrap_cache_key }}
key: ${{ steps.cache_metadata.outputs.setup_cache_key }}

- name: Cache Pants named caches
uses: actions/cache@v5
- name: Restore Pants named caches
if: inputs.named-caches-hash != 'disable'
uses: actions/cache/restore@v5
with:
path: ${{ inputs.named-caches-location }}
key: pants-named-caches-${{ runner.os }}-${{ inputs.gha-cache-key }}-${{ hashFiles('pants.toml') }}-${{ inputs.named-caches-hash }}
key: ${{ steps.cache_metadata.outputs.named_caches_key }}
restore-keys: |
pants-named-caches-${{ runner.os }}-${{ inputs.gha-cache-key }}-${{ hashFiles('pants.toml') }}-
pants-named-caches-${{ runner.os }}-${{ inputs.gha-cache-key }}-
Expand All @@ -188,15 +229,14 @@ runs:
GITHUB_ENTERPRISE_TOKEN: ${{ github.token }}
GH_HOST: ${{ inputs.gh-host }}

- name: Cache Pants LMDB store
- name: Restore Pants LMDB store
if: inputs.cache-lmdb-store == 'true'
uses: actions/cache@v5
id: cache-pants-lmdb-store
uses: actions/cache/restore@v5
with:
path: ${{ inputs.lmdb-store-location }}
# The commit SHA serves as a hash of all files in the repo.
# A remote cache service integrates with Pants's fine-grained invalidation and avoids these problems.
key: pants-lmdb-store-${{ runner.os }}-${{ inputs.gha-cache-key }}-${{ github.sha }}
key: ${{ steps.cache_metadata.outputs.lmdb_store_key }}
restore-keys: |
pants-lmdb-store-${{ runner.os }}-${{ inputs.gha-cache-key }}-${{ steps.pants_cache_commit.outputs.CACHECOMMIT }}
pants-lmdb-store-${{ runner.os }}-${{ inputs.gha-cache-key }}-
Expand Down
35 changes: 35 additions & 0 deletions save-pants-cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# save-pants-cache

Companion to [`init-pants`](../init-pants) that persists Pants caches.

`init-pants` restores caches at the start of a job. This action saves them at
the end. Using `if: always()` ensures saves survive workflow cancellation
(e.g., `cancel-in-progress: true`), which post-step hooks do not.

## Usage

```yaml
- name: Initialize Pants
id: pants-init
uses: pantsbuild/actions/init-pants@v11
with:
named-caches-hash: ${{ hashFiles('lockfiles/*.json') }}

# ... your build/test steps ...

- name: Save Pants caches
if: always()
uses: pantsbuild/actions/save-pants-cache@v11
with:
setup-cache-path: ${{ steps.pants-init.outputs.setup-cache-path }}
setup-cache-key: ${{ steps.pants-init.outputs.setup-cache-key }}
named-caches-path: ${{ steps.pants-init.outputs.named-caches-path }}
named-caches-key: ${{ steps.pants-init.outputs.named-caches-key }}
named-caches-hash: ${{ steps.pants-init.outputs.named-caches-hash }}
lmdb-store-path: ${{ steps.pants-init.outputs.lmdb-store-path }}
lmdb-store-key: ${{ steps.pants-init.outputs.lmdb-store-key }}
cache-lmdb-store: ${{ steps.pants-init.outputs.cache-lmdb-store }}
```

All inputs come from `init-pants` outputs. GHA cache keys are immutable, so
`actions/cache/save` is a safe no-op when the exact key already exists.
69 changes: 69 additions & 0 deletions save-pants-cache/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
name: Save Pants Caches
description: |
Companion action to init-pants when using `cache-save-mode: explicit`.

Saves Pants caches using actions/cache/save, which runs as a regular step
rather than a post-step hook. When used with `if: always()`, this ensures
caches are saved even when the workflow is cancelled (e.g., by
cancel-in-progress: true in a concurrency group).

This action should be placed at the end of the job, after all Pants commands
have completed.

inputs:
setup-cache-path:
description: Path to the Pants setup cache directory (from init-pants output)
required: true
setup-cache-key:
description: GHA cache key for the Pants setup cache (from init-pants output)
required: true
named-caches-path:
description: Path to the Pants named caches directory (from init-pants output)
required: true
named-caches-key:
description: GHA cache key for the Pants named caches (from init-pants output)
required: true
named-caches-hash:
description: |
The named caches hash value (from init-pants output). Pass this through
so the save can be skipped when set to "disable".
required: false
default: ''
lmdb-store-path:
description: Path to the Pants LMDB store directory (from init-pants output)
required: false
default: ''
lmdb-store-key:
description: GHA cache key for the Pants LMDB store (from init-pants output)
required: false
default: ''
cache-lmdb-store:
description: Whether the LMDB store cache is enabled (from init-pants output)
required: false
default: 'false'

runs:
using: "composite"
steps:
# GHA cache keys are immutable — save is a no-op when the exact key
# already exists, so it is always safe to attempt.
- name: Save Pants setup cache
uses: actions/cache/save@v5
with:
path: ${{ inputs.setup-cache-path }}
key: ${{ inputs.setup-cache-key }}

- name: Save Pants named caches
if: inputs.named-caches-hash != 'disable' && inputs.named-caches-hash != ''
uses: actions/cache/save@v5
with:
path: ${{ inputs.named-caches-path }}
key: ${{ inputs.named-caches-key }}

- name: Save Pants LMDB store
if: inputs.cache-lmdb-store == 'true'
uses: actions/cache/save@v5
with:
path: ${{ inputs.lmdb-store-path }}
key: ${{ inputs.lmdb-store-key }}