Skip to content

Commit e484837

Browse files
Add pixi-lock/create-and-cache and pixi-lock/restore actions. (#2)
1 parent 9593bde commit e484837

8 files changed

Lines changed: 314 additions & 0 deletions

File tree

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SCM syntax highlighting & preventing 3-way merges
2+
pixi.lock merge=binary linguist-language=YAML linguist-generated=true -diff

.github/workflows/test.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Test pixi-lock actions
2+
3+
on:
4+
push:
5+
branches: [main, test-me/*]
6+
pull_request:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
env:
14+
PIXI_VERSION: "v0.63.0"
15+
16+
jobs:
17+
cache-pixi-lock:
18+
runs-on: ubuntu-latest
19+
outputs:
20+
cache-key: ${{ steps.create-and-cache.outputs.cache-key }}
21+
pixi-version: ${{ steps.create-and-cache.outputs.pixi-version }}
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v6
25+
with:
26+
path: pixi-lock
27+
28+
- name: Copy test files to working directory
29+
shell: bash
30+
run: cp -r pixi-lock/ci/test/* .
31+
32+
- name: Run create-and-cache action
33+
id: create-and-cache
34+
uses: ./pixi-lock/create-and-cache
35+
with:
36+
pixi-version: ${{ env.PIXI_VERSION }}
37+
38+
- name: Verify pixi.lock exists
39+
shell: bash
40+
run: |
41+
if [ -f "pixi.lock" ]; then
42+
echo "pixi.lock exists"
43+
else
44+
echo "pixi.lock does not exist"
45+
exit 1
46+
fi
47+
48+
restore-and-install:
49+
needs: cache-pixi-lock
50+
runs-on: ${{ matrix.os }}
51+
strategy:
52+
fail-fast: false
53+
matrix:
54+
os: [ubuntu-latest, macos-latest, windows-latest]
55+
steps:
56+
- name: Checkout repository
57+
uses: actions/checkout@v6
58+
with:
59+
path: pixi-lock
60+
61+
- name: Copy test files to working directory
62+
shell: bash
63+
run: cp -r pixi-lock/ci/test/* .
64+
65+
- name: Restore pixi.lock from cache
66+
uses: ./pixi-lock/restore
67+
with:
68+
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
69+
70+
- name: Verify pixi.lock exists
71+
shell: bash
72+
run: |
73+
if [ -f "pixi.lock" ]; then
74+
echo "pixi.lock exists"
75+
else
76+
echo "pixi.lock does not exist"
77+
exit 1
78+
fi
79+
80+
- name: Setup pixi and install environment
81+
uses: prefix-dev/setup-pixi@v0.9.3
82+
with:
83+
pixi-version: ${{ needs.cache-pixi-lock.outputs.pixi-version }}
84+
85+
- name: Verify environment installed
86+
shell: bash
87+
run: |
88+
if [ -d ".pixi/envs/default" ]; then
89+
echo "Environment installed successfully"
90+
else
91+
echo "Environment not installed"
92+
exit 1
93+
fi

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# pixi environments
2+
.pixi/*
3+
!.pixi/config.toml
4+
5+
pixi.lock

.pre-commit-config.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v6.0.0
4+
hooks:
5+
- id: trailing-whitespace
6+
- id: end-of-file-fixer
7+
- id: check-yaml
8+
- id: check-ast
9+
- id: check-json
10+
types: [text]
11+
files: \.(json|ipynb)$
12+
- repo: https://github.com/rbubley/mirrors-prettier # Update mirror as official mirror is deprecated
13+
rev: v3.6.2
14+
hooks:
15+
- id: prettier
16+
- repo: https://github.com/ComPWA/taplo-pre-commit
17+
rev: v0.9.3
18+
hooks:
19+
- id: taplo-format
20+
args:
21+
[
22+
"--option",
23+
"array_auto_collapse=false",
24+
"--option",
25+
"align_comments=false",
26+
]

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# pixi-lock
2+
3+
> [!NOTE]
4+
> This repo is likely to be moved to https://github.com/xarray-contrib
5+
6+
This repo provides two GitHub Actions for managing `pixi.lock` files with caching:
7+
8+
- **`create-and-cache`**: Generates a `pixi.lock` file and caches it
9+
- **`restore`**: Restores the cached `pixi.lock` file in downstream jobs
10+
11+
This two-action pattern is so that the lockfile can be omitted from the git
12+
history, but still be generated in a performant manner (i.e., regenerated
13+
and cached with a key that depends on `pixi.toml` and the date -
14+
then shared across jobs).
15+
16+
17+
## Usage
18+
19+
```yaml
20+
jobs:
21+
cache-pixi-lock:
22+
runs-on: ubuntu-latest
23+
outputs:
24+
cache-key: ${{ steps.pixi-lock.outputs.cache-key }}
25+
pixi-version: ${{ steps.pixi-lock.outputs.pixi-version }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
- uses: Parcels-code/pixi-lock/create-and-cache@v1
29+
id: pixi-lock
30+
with:
31+
pixi-version: ... # TODO: update with your selected pixi version
32+
33+
ci:
34+
needs: cache-pixi-lock
35+
runs-on: ${{ matrix.os }}
36+
strategy:
37+
matrix:
38+
os: [ubuntu-latest, macos-latest, windows-latest]
39+
steps:
40+
- uses: actions/checkout@v4
41+
- uses: Parcels-code/pixi-lock/restore@v1
42+
with:
43+
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
44+
- uses: prefix-dev/setup-pixi@v... # TODO: update with your selected setup-pixi version
45+
with:
46+
pixi-version: ${{ needs.cache-pixi-lock.outputs.pixi-version }}
47+
# ... your CI steps
48+
```
49+
50+
### Inputs & Outputs
51+
52+
#### `create-and-cache`
53+
54+
| Input | Description | Required | Default |
55+
|-------|-------------|----------|---------|
56+
| `pixi-version` | Version of pixi to use for generating the lock file | No | `latest` |
57+
58+
| Output | Description |
59+
|--------|-------------|
60+
| `pixi-version` | The pixi version used |
61+
| `cache-key` | The cache key (includes today's date) |
62+
63+
#### `restore`
64+
65+
| Input | Description | Required |
66+
|-------|-------------|----------|
67+
| `cache-key` | The cache key from `create-and-cache` | Yes |
68+
69+
> [!NOTE]
70+
> The cache key includes the current date, so the lock file is regenerated daily.
71+
> The fallback key (yesterday's date) is calculated automatically to handle edge cases where the restore job runs just after midnight.
72+
73+
## Why not commit the lock file?
74+
75+
Committing your lock file is considered good practice when working on application code. Providing fixed package versions results in perfect reproducibility of environments between machines, and hence also reproducibility of results.
76+
77+
When developing and testing _library_ code, we don't want our environments to stay completely fixed - we want to test against environments covering a wide range of package versions to ensure compatability, including an environment includ the latest available versions of packages.
78+
79+
The easiest way to test against the latest versions of packages - and avoid the noisy commit history (and additional overhead) of regularly updating a lock file in git - is instead to ignore the lock file and rely on developers and CI to generate their own lock files. This forgoes perfect reprodubility between developer machines, and with CI machines.
80+
81+
82+
## Dev notes
83+
84+
### Release checklist
85+
86+
- Update the `README.md` bumping the version of action
87+
- Cut release

ci/test/pixi.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[workspace]
2+
authors = ["Vecko <36369090+VeckoTheGecko@users.noreply.github.com>"]
3+
channels = ["conda-forge"]
4+
name = "test"
5+
platforms = ["win-64", "linux-64", "osx-64", "osx-arm64", "linux-aarch64"]
6+
version = "0.1.0"
7+
8+
[tasks]
9+
10+
[dependencies]
11+
python = "*"
12+
numpy = "*"
13+
pandas = "*"
14+
xarray = "*"

create-and-cache/action.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Create and Cache Pixi Lock
2+
description: Generate a pixi.lock file and cache it for use by other jobs.
3+
4+
inputs:
5+
pixi-version:
6+
description: "Version of pixi to use for generating the lock file"
7+
required: false
8+
default: "latest"
9+
10+
outputs:
11+
pixi-version:
12+
description: "The pixi version used (for passing to downstream jobs)"
13+
value: ${{ inputs.pixi-version }}
14+
cache-key:
15+
description: "The cache key used"
16+
value: ${{ steps.cache-key.outputs.key }}
17+
18+
runs:
19+
using: "composite"
20+
steps:
21+
- name: Get cache key
22+
id: cache-key
23+
shell: bash
24+
run: |
25+
today=$(date +'%Y-%m-%d')
26+
key="pixi-lock_${{ inputs.pixi-version }}_${{ hashFiles('pixi.toml') }}_${today}"
27+
echo "key=${key}" >> "$GITHUB_OUTPUT"
28+
29+
- name: Restore pixi.lock from cache
30+
uses: actions/cache/restore@v5
31+
id: restore
32+
with:
33+
path: pixi.lock
34+
key: ${{ steps.cache-key.outputs.key }}
35+
36+
- name: Setup pixi
37+
uses: prefix-dev/setup-pixi@v0.9.3
38+
if: ${{ !steps.restore.outputs.cache-hit }}
39+
with:
40+
pixi-version: ${{ inputs.pixi-version }}
41+
run-install: false
42+
43+
- name: Run pixi lock
44+
if: ${{ !steps.restore.outputs.cache-hit }}
45+
shell: bash
46+
run: pixi lock
47+
48+
- name: Save pixi.lock to cache
49+
uses: actions/cache/save@v5
50+
if: ${{ !steps.restore.outputs.cache-hit }}
51+
with:
52+
path: pixi.lock
53+
key: ${{ steps.cache-key.outputs.key }}
54+
enableCrossOsArchive: true

restore/action.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Restore Pixi Lock
2+
description: Restore a previously cached pixi.lock file.
3+
4+
inputs:
5+
cache-key:
6+
description: "The cache key from create-and-cache"
7+
required: true
8+
9+
runs:
10+
using: "composite"
11+
steps:
12+
- name: Calculate fallback key
13+
id: fallback
14+
shell: bash
15+
run: |
16+
cache_key="${{ inputs.cache-key }}"
17+
# Extract base (everything before the last _) and date (after the last _)
18+
base="${cache_key%_*}"
19+
current_date="${cache_key##*_}"
20+
# Calculate yesterday's date
21+
yesterday=$(date -d "${current_date} - 1 day" +'%Y-%m-%d' 2>/dev/null || date -j -f '%Y-%m-%d' -v-1d "${current_date}" +'%Y-%m-%d')
22+
echo "key=${base}_${yesterday}" >> "$GITHUB_OUTPUT"
23+
24+
- name: Restore pixi.lock from cache
25+
uses: actions/cache/restore@v5
26+
id: restore
27+
with:
28+
path: pixi.lock
29+
key: ${{ inputs.cache-key }}
30+
enableCrossOsArchive: true
31+
restore-keys: |
32+
${{ steps.fallback.outputs.key }}
33+
fail-on-cache-miss: true

0 commit comments

Comments
 (0)