Skip to content

cache-homebrew-prefix: restoring cache overwrites Homebrew source code on brew PRs #796

@costajohnt

Description

@costajohnt

Summary

The cache-homebrew-prefix action caches the entire $(brew --prefix) directory, which on Linux includes the Homebrew/ subdirectory containing the Homebrew source code (git repository). When the cache is restored during a Homebrew/brew PR CI run, it overwrites the PR's code changes that were previously checked out by setup-homebrew.

Steps to reproduce

  1. Open a PR on Homebrew/brew that modifies existing Ruby source files (e.g., Library/Homebrew/keg.rb)
  2. CI runs the syntax job, which uses cache-homebrew-prefix to install shellcheck and shfmt
  3. The syntax job fails because brew typecheck runs against stale (cached) source code instead of the PR's code

Root cause

In the syntax job of .github/workflows/tests.yml, the step order is:

  1. setup-homebrew: Fetches the PR's SHA and does git checkout --force -B main FETCH_HEAD in $HOMEBREW_REPOSITORY (/home/linuxbrew/.linuxbrew/Homebrew)
  2. cache-homebrew-prefix: Restores a cached tarball of the entire /home/linuxbrew/.linuxbrew directory, overwriting the files checked out in step 1
  3. brew typecheck: Runs Sorbet against the now-stale source code

The cache key is based on installed formula versions (shellcheck/shfmt), not on source code, so the same stale cache is restored on every run.

Example

PR Homebrew/brew#21594 has the correct code in its merge ref (refs/pull/21594/merge), verified via:

git fetch upstream refs/pull/21594/merge
git show FETCH_HEAD:Library/Homebrew/keg.rb | sed -n '318p'
# Output: LinkageCacheStore.new(path.to_s, db).delete!

But CI reports:

keg.rb:318: Expected `String` but found `Pathname` for argument `keg_path`
     318 |      LinkageCacheStore.new(path, db).delete!

The old code (path instead of path.to_s) comes from the cached prefix.

Possible fixes

  1. Exclude Homebrew/ from the cache: Change the cached path to exclude the source code directory, or cache individual subdirectories (Cellar/, bin/, opt/, lib/) instead of the entire prefix
  2. Restore git state after cache: Add a git checkout -f step in $HOMEBREW_REPOSITORY after cache restoration
  3. Scope cache to non-source paths: Use multiple path entries that avoid the git repository

Environment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions