Files
hermes-agent/.github/actions/save-pm-cache/README.md
ethernet 3a6e61b189 ci: drop the PM toolchain smoke matrix; key the tools cache on the PM code
The 6-target cold->warm smoke existed to prove setup-pm still cold-boots
when its code changes without a lock bump, and to reach linux-arm64,
darwin-x64 and win32-arm64. Both are now covered without a dedicated
workflow:

* The tools cache key hashes pm/**, the action itself and
  scripts/ci/setup_toolchain.py, not only pm/lock.json. A provisioning
  change misses the cache on every lane that uses the action, so the
  cold path runs where the tests already are.
* tests-os gains a windows-11-arm leg running the same windows-marked
  files (tests/pm carries ten of them). It needs the ARM64 build deps
  because several extras build from sdist, and fewer workers on the
  4-core runner.

The Windows SDK adapter test is the one thing left that no other lane
ran natively; it keeps its two Windows runners under
windows-bundle-sdk.yml, path-triggered on the signing scripts. The
run-scoped cache cleanup workflow and its script only served the smoke
and go with it.
2026-09-21 12:07:53 -04:00

2.2 KiB

Bundle dependency caches

setup-pm restores uv's real cache, not the installed virtual environment. Its fallback keys retain the native target, OS version, and Python version. A metadata or dependency change can reuse compatible wheels; uv still resolves and installs from the frozen project lock.

Bundle jobs set save-python-cache: false and call save-pm-cache after their build step with the python-path, uv-cache-path, and python-cache-key outputs. Both the caller and this composite use !cancelled() so a failed build does not suppress the save. Cancellation is excluded to avoid racing a dying uv process. A runner crash or job timeout can still prevent the save.

Snapshots use a run ID, attempt, and producer job suffix because Actions caches are immutable. Sibling bundle workflows in one caller run must not race to save different contents under the same key. Restore tries the current dependency set's rolling snapshots first, then the compatible v2 prefix. There is no fallback to older cache formats. This lets a retry add wheels to a snapshot saved by a partially failed build. The ordinary automatic cache path remains available.

Suffixed namespaces precede the native/dependency identity, outside production's restore prefix.

Before saving, python -m pm.build_env --exact-lock --cache PATH --lock-source REPO deletes every entry the project's uv.lock cannot resolve. It keeps downloaded wheels the lock resolves (bundles copy the full cache for offline dependency installation) — uv cache prune --ci would discard them. Pruning happens after payload staging and packaging, and it does not change the staged payload. The same lock-exactness contract governs the bundle ship gate (stage_uv_cache) and CI's rolling snapshots, so no snapshot accumulates sediment for superseded pins.

This is not a lockfile-aware or size-bounded cache. Old, still-referenced package versions can remain inside a snapshot and be copied forward. GitHub evicts whole cache snapshots under its retention and repository quota policies; that does not remove old versions inside the newest snapshot. A future size policy must account for the offline payload too, rather than deleting uv internals or promising that prune keeps only the current lock.