Files

Locked CI toolchains

Check out this repository, then use ./.github/actions/setup-pm. The runner's preinstalled Python bootstraps PM with the standard library only. PM downloads and verifies the exact native artifacts from pm/lock.json. The action does not resolve a version range, install another setup action, or modify the lock.

- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup-pm
  with:
    toolchain: all
    extras: '[]'
    test-environment: 'true'
- run: python --version && node --version && npm --version

toolchain defaults to python (Python and PM's private installer). node installs Node and npm; all installs both pairs. There are no version overrides. extras is a JSON array because GitHub action inputs are strings. Omit it for tools only; [] installs the core Python dependencies. Set test-environment: 'true' to build an independent interpreter with the dev and test dependency groups; any extras then select runtime features in that interpreter. PM checks uv.lock, installs the requested dependencies, and validates the environment. Only non-test installs publish an application selection. It does not enable plugins.

Subsequent steps get python, python3, node, npm and npx for the selected toolchain on PATH. The pinned npm precedes Node's bundled npm. On Windows, PM selects the host architecture even if the bootstrap interpreter runs under x64 emulation. A disposable command environment supplies the missing python3.exe alias without changing the verified interpreter store.

For Python dependencies, the action exports HERMES_PYTHON and VIRTUAL_ENV. Use scripts/run_tests.sh; do not activate .venv. The environment belongs to PM under the runner's temporary home, not the checkout. No installer executable or UV_* policy variables are exposed. Tool-only jobs can prepare small isolated CI environments with python -m scripts.ci.python_packages package==version. This exports the selected Python and tool entrypoints for subsequent steps; append -- -m module ... to run a Python command directly and preserve its exit status. Neither route modifies the cached tool store. Native builds still need their system libraries and compiler, such as OpenSSL for Windows ARM64 cryptography.

Caches

The official, SHA-pinned actions/cache transports three independent caches:

Cache Contents Identity
Tools PM store and installed facts Native target, toolchain and PM lock hash
Python PM's actual uv download/build cache Native target, OS version, Python version, prune policy and dependency-file hash
Node npm config get cache Runner OS, native architecture and npm dependency-lock hash

All restores use the exact primary key, without fallback prefixes, matching setup-uv and setup-node's npm behavior. Successful jobs save at teardown; exact hits are not saved again. Only dependency-carrying callers (extras set or test-environment: 'true') auto-save the uv cache: a tool-only job never runs a dependency operation, so letting it save would freeze an empty cache under the production key, where an immutable exact hit blocks real saves forever. PM re-verifies restored tools before use. Dependency caches never contain node_modules or virtual environments. Keep an installed-tree cache in the caller if that job needs one.

cache, cache-python and cache-node independently disable the store, uv, and npm caches. Each defaults to true; language-specific caches run only for that toolchain. python-cache-dependency-glob defaults to pyproject.toml and uv.lock. node-cache-dependency-path defaults to package-lock.json; use website/package-lock.json for site jobs. Both accept multiline glob strings. An npm cache without a matching lockfile fails, rather than caching an unversioned dependency set.

prune-python-cache: true registers PM's lock-exact cache-pruning operation at teardown, after the caller's installs and before the cache save. It is skipped on an exact hit. The default is false, as in setup-uv v9; migrated v8 callers opt in to retain their former policy. The small nested JavaScript action exists only because GitHub composite actions cannot declare their own post step. It uses Node's standard library and has no bundled dependencies. Pruning deletes cache entries the project's uv.lock cannot resolve (the same exactness contract the bundle ship gate enforces) and keeps downloaded wheels the lock still needs — uv cache prune --ci would discard them, leaving a snapshot that warms almost nothing.

Outputs include python-version, uv-version, node-version, npm-version, python-path, venv, target, and the three *-cache-hit flags. Use the version outputs in installed-tree cache keys instead of repeating pins.

The tools cache key hashes pm/**, this action, and scripts/ci/setup_toolchain.py, so a provisioning change re-runs the cold path on every lane that uses the action (tests-os.yml covers macOS and both Windows architectures). cache-suffix is an optional namespace for isolated cache experiments; it changes lookup, not storage.