Avoid ignore crate finding cache gitignore (#2615)

We put a `.gitignore` with `*` at the top of our cache. When maturin was
building a source distribution inside the cache, it would walk up the
tree to find a gitignore, see that and ignore all python files. We now
add an (empty) `.git` directory one directory below, in the root of
built-wheels cache. This prevents ignore walking further up (it marks
the top level a git repository).

Deptry (from #2490) is a mid sized rust package with additional python
packages, so instead of using it in the test i've replaced it with a
small (44KB total) reproducer that uses cffi for faster building, the
entire test taking <2s on my machine.

Fixes #2490
This commit is contained in:
konsti 2024-03-22 20:36:07 +01:00 committed by GitHub
parent fc32a693c2
commit fca2883864
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 115 additions and 9 deletions

View file

@ -251,15 +251,6 @@ impl Cache {
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"))?;
// Add an empty .gitignore to the build bucket, to ensure that the cache's own .gitignore
// doesn't interfere with source distribution builds. Build backends (like hatchling) will
// traverse upwards to look for .gitignore files.
@ -273,6 +264,18 @@ impl Cache {
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.).
// We have to put this below the gitignore. Otherwise, if the build backend uses the rust
// ignore crate it will walk up to the top level .gitignore and ignore its python source
// files.
fs::OpenOptions::new()
.create(true)
.write(true)
.open(root.join(CacheBucket::BuiltWheels.to_str()).join(".git"))?;
fs::canonicalize(root)
}

View file

@ -6,10 +6,12 @@ use assert_fs::prelude::*;
use base64::{prelude::BASE64_STANDARD as base64, Engine};
use indoc::indoc;
use itertools::Itertools;
use std::env::current_dir;
use std::process::Command;
use url::Url;
use common::{uv_snapshot, TestContext, EXCLUDE_NEWER, INSTA_FILTERS};
use uv_fs::Simplified;
use crate::common::get_bin;
@ -2952,3 +2954,57 @@ fn install_site_packages_mtime_updated() -> Result<()> {
Ok(())
}
/// We had a bug where maturin would walk up to the top level gitignore of the cache with a `*`
/// entry (because we want to ignore the entire cache from outside), ignoring all python source
/// files.
#[test]
fn deptry_gitignore() -> Result<()> {
let context = TestContext::new("3.12");
let project_root = current_dir()?
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf();
let source_dist_dir = project_root
.join("scripts")
.join("packages")
.join("deptry_reproducer");
let filter_path = regex::escape(
Url::from_directory_path(source_dist_dir.simplified_display().to_string())
.unwrap()
.to_string()
.trim_end_matches('/'),
);
let filters: Vec<_> = [(filter_path.as_str(), "[SOURCE_DIST_DIR]")]
.into_iter()
.chain(INSTA_FILTERS.to_vec())
.collect();
uv_snapshot!(filters, command(&context)
.arg(format!("deptry_reproducer @ {}/deptry_reproducer-0.1.0.tar.gz", source_dist_dir.simplified_display()))
.arg("--strict")
.current_dir(source_dist_dir), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 3 packages in [TIME]
Downloaded 3 packages in [TIME]
Installed 3 packages in [TIME]
+ cffi==1.16.0
+ deptry-reproducer==0.1.0 (from [SOURCE_DIST_DIR]/deptry_reproducer-0.1.0.tar.gz)
+ pycparser==2.21
"###
);
// Check that we packed the python source files
context
.assert_command("import deptry_reproducer.foo")
.success();
Ok(())
}

View file

@ -0,0 +1 @@
/target

View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "deptry_reproducer"
version = "0.1.0"

View file

@ -0,0 +1,11 @@
[package]
name = "deptry_reproducer"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "deptry_reproducer"
crate-type = ["cdylib"]
[dependencies]

View file

@ -0,0 +1,21 @@
[build-system]
requires = ["maturin>=1,<2.0"]
build-backend = "maturin"
[project]
name = "deptry_reproducer"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = ["cffi"]
dynamic = ["version"]
[project.optional-dependencies]
tests = [
"pytest",
]
[tool.maturin]
bindings = "cffi"
python-source = "python"

View file

@ -0,0 +1,6 @@
from .deptry_reproducer import *
__doc__ = deptry_reproducer.__doc__
if hasattr(deptry_reproducer, "__all__"):
__all__ = deptry_reproducer.__all__

View file

@ -0,0 +1 @@