From f13d0adbcdefb3934667d840d7599b798736757f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 20 Feb 2024 15:37:05 -0500 Subject: [PATCH] Ensure that builds within the cache aren't considered Git repositories (#1782) ## Summary Some packages encode logic to embed the current commit SHA in the version tag, when built within a Git repo. This typically results in an invalid (non-compliant) version. Here's an example from `pylzma`: https://github.com/fancycode/pylzma/blob/ccb0e7cff3f6ecd5d38e73e9ca35502d7d670176/version.py#L45. This PR adds a phony, empty `.git` to the cache root, to ensure that any `git` commands fail. Closes https://github.com/astral-sh/uv/issues/1768. ## Test Plan - Create a tag on the current commit, like `v0.5.0`. - Build `pylzma`, using a cache _within_ the repo: ``` rm -rf foo cargo run venv cargo run pip install "pylzma @ https://files.pythonhosted.org/packages/03/d8/10ef072c3cd4301a65a1b762b09eefa02baf8da23b9ea77ebe9546499975/pylzma-0.5.0.tar.gz" --verbose --cache-dir bar ``` --- crates/uv-cache/src/lib.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/uv-cache/src/lib.rs b/crates/uv-cache/src/lib.rs index da3ffecbc..5c8a187ea 100644 --- a/crates/uv-cache/src/lib.rs +++ b/crates/uv-cache/src/lib.rs @@ -235,12 +235,25 @@ impl Cache { cachedir::ensure_tag(&root)?; // Add the .gitignore. - let gitignore_path = root.join(".gitignore"); - if !gitignore_path.exists() { - let mut file = fs::File::create(gitignore_path)?; - file.write_all(b"*")?; + match fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(root.join(".gitignore")) + { + Ok(mut file) => file.write_all(b"*")?, + Err(err) if err.kind() == io::ErrorKind::AlreadyExists => (), + Err(err) => return Err(err), } + // Add a phony .git, if it doesn't exist, to ensure that the cache isn't considered to be + // part of a Git repository. (Some packages will include Git metadata (like a hash) in the + // built version if they're in a Git repository, but the cache should be viewed as an + // isolated store.) + fs::OpenOptions::new() + .create(true) + .write(true) + .open(root.join(".git"))?; + fs::canonicalize(root) }