BUILD-11964: Harden config-uv dependency cache keys#320
Conversation
config-uv's GitHub Actions cache key was shared across all workflows
in a repo (uv-${runner.os}-${hash}), unlike config-poetry which scopes
its key per workflow. Repos with multiple concurrent workflows thrash
a single shared uv cache bucket against GitHub's 10GB per-repo cache
limit, causing repeated cache misses and re-downloads from Artifactory.
Datadog logs show uv re-fetching identical content-hashed wheel URLs
up to 184 times over 14 days (avg 69/wheel) vs poetry's max of 4
(avg 2.2/wheel), and uv's Artifactory bandwidth is up 345.7% over 90
days -- the classic signature of a cache that isn't holding.
This mirrors config-poetry's existing sanitize-workflow-name + scoped
cache key pattern exactly.
|
Gitar fix CI |
On it — working on fixing the CI failure now. |
Co-authored-by: Brian Walsh <20115224+bwalsh434@users.noreply.github.com>
|
Will be telling Claude about the gh-action_cache, as I presume these cases are using that with S3 and not thrashing against the 10GB Limit of the GHA Cache. |
| dependency_hash="$DIRECTORY_DEPENDENCY_HASH" | ||
| dependency_source="working-directory '${WORKING_DIRECTORY}'" | ||
| if [[ -n "$CACHE_DEPENDENCY_GLOB" ]]; then | ||
| dependency_hash="$GLOB_DEPENDENCY_HASH" | ||
| dependency_source="cache-dependency-glob '${CACHE_DEPENDENCY_GLOB}'" | ||
| fi |
There was a problem hiding this comment.
💡 Edge Case: cache-dependency-glob silently overrides working-directory hash
When both working-directory and a non-empty cache-dependency-glob are provided, the glob hash unconditionally replaces the working-directory hash, so the working-directory's uv.lock/pyproject.toml are excluded from the cache key. This is fine for the documented multi-project use case (working-directory left at '.'), but a user who sets both intentionally would get a key that ignores their working-directory lockfiles without any warning. Consider documenting the precedence explicitly, or combining both file sets into the hash when both inputs are set.
Was this helpful? React with 👍 / 👎
| - name: Resolve uv cache key | ||
| id: uv-cache-key | ||
| if: steps.config-uv-completed.outputs.skip != 'true' && inputs.disable-caching != 'true' | ||
| shell: bash | ||
| env: | ||
| CACHE_DEPENDENCY_GLOB: ${{ inputs.cache-dependency-glob }} | ||
| DIRECTORY_DEPENDENCY_HASH: | ||
| ${{ hashFiles(format('{0}/uv.lock', inputs.working-directory), | ||
| format('{0}/pyproject.toml', inputs.working-directory)) }} | ||
| GLOB_DEPENDENCY_HASH: ${{ hashFiles(inputs.cache-dependency-glob) }} | ||
| WORKING_DIRECTORY: ${{ inputs.working-directory }} | ||
| run: | | ||
| dependency_hash="$DIRECTORY_DEPENDENCY_HASH" | ||
| dependency_source="working-directory '${WORKING_DIRECTORY}'" | ||
| if [[ -n "$CACHE_DEPENDENCY_GLOB" ]]; then |
There was a problem hiding this comment.
💡 Quality: New cache-key resolution bash logic has no test coverage
The 'Resolve uv cache key' step adds non-trivial branching (glob-vs-working-directory hash selection and the empty-hash 'disable caching + warn' path), but spec/config-uv_spec.sh only exercises uv_config.sh, not the composite action's embedded bash. The PR test plan explicitly notes the shell specs are unaffected, so these branches ship untested. Consider extracting the resolution logic into a sourced script that shellspec can cover, or adding a targeted test for the empty-hash and glob paths.
Was this helpful? React with 👍 / 👎
Code Review 👍 Approved with suggestions 0 resolved / 2 findingsPartitions 💡 Edge Case: cache-dependency-glob silently overrides working-directory hash📄 config-uv/action.yml:106-111 When both 💡 Quality: New cache-key resolution bash logic has no test coverageThe 'Resolve uv cache key' step adds non-trivial branching (glob-vs-working-directory hash selection and the empty-hash 'disable caching + warn' path), but spec/config-uv_spec.sh only exercises uv_config.sh, not the composite action's embedded bash. The PR test plan explicitly notes the shell specs are unaffected, so these branches ship untested. Consider extracting the resolution logic into a sourced script that shellspec can cover, or adding a targeted test for the empty-hash and glob paths. 🤖 Prompt for agentsOptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
|
| cache-dependency-glob: | ||
| description: Glob of dependency files for jobs that install more than one uv project | ||
| default: '' |
There was a problem hiding this comment.
Add to the README. Point in the PR description to the usages.
Isn't the default "inputs.working-directory/uv.lock"?
Why do we need this instead of using the working-directory?
| CACHE_DEPENDENCY_GLOB: ${{ inputs.cache-dependency-glob }} | ||
| DIRECTORY_DEPENDENCY_HASH: | ||
| ${{ hashFiles(format('{0}/uv.lock', inputs.working-directory), | ||
| format('{0}/pyproject.toml', inputs.working-directory)) }} |
There was a problem hiding this comment.
Why pyproject.toml? Maybe a question for @hedinasr since it was in "Cache uv dependencies".
| DIRECTORY_DEPENDENCY_HASH: | ||
| ${{ hashFiles(format('{0}/uv.lock', inputs.working-directory), | ||
| format('{0}/pyproject.toml', inputs.working-directory)) }} | ||
| GLOB_DEPENDENCY_HASH: ${{ hashFiles(inputs.cache-dependency-glob) }} |
There was a problem hiding this comment.
Rename GLOB_DEPENDENCY_HASH -> CACHE_DEPENDENCY_HASH



Summary
uv-Linux-Predicted savings
Approximately 0 TiB/month directly. This is the shared prerequisite and safety guard for BUILD-11965 through BUILD-11969. The measurable savings occur when those consumers supply correct project or lockfile scope.
Removing workflow-name partitioning avoids duplicate caches and also avoids the Unicode workflow-name failure independently confirmed in Semsitter.
Rollout and confirmation
Success for this PR is no new empty keys and correct key invalidation when scoped lockfiles change. Savings are attributed and verified in each consumer PR.
Test plan
config-uv/action.ymlgit diff --checkworking-directorycache-dependency-globThe full repository ShellSpec suite currently has two unrelated failures: one existing SCA mock expectation and one missing local
check-jsonschemaexecutable.