diff --git a/Cargo.lock b/Cargo.lock index 4c6ff6b95..8d0405ba3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5167,6 +5167,7 @@ dependencies = [ name = "uv-build-backend" version = "0.1.0" dependencies = [ + "base64 0.22.1", "csv", "flate2", "fs-err", diff --git a/crates/uv-build-backend/Cargo.toml b/crates/uv-build-backend/Cargo.toml index 750778df8..269491582 100644 --- a/crates/uv-build-backend/Cargo.toml +++ b/crates/uv-build-backend/Cargo.toml @@ -26,6 +26,7 @@ uv-pypi-types = { workspace = true } uv-version = { workspace = true } uv-warnings = { workspace = true } +base64 = { workspace = true } csv = { workspace = true } flate2 = { workspace = true, default-features = false } fs-err = { workspace = true } diff --git a/crates/uv-build-backend/src/lib.rs b/crates/uv-build-backend/src/lib.rs index 319925b6a..aa90a88c5 100644 --- a/crates/uv-build-backend/src/lib.rs +++ b/crates/uv-build-backend/src/lib.rs @@ -622,7 +622,7 @@ mod tests { // Check that the wheel is reproducible across platforms. assert_snapshot!( format!("{:x}", sha2::Sha256::digest(fs_err::read(&wheel_path).unwrap())), - @"342bf60c8406144f459358cde92408686c1631fe22389d042ce80379e589d6ec" + @"319afb04e87caf894b1362b508ec745253c6d241423ea59021694d2015e821da" ); assert_snapshot!(build.wheel_contents.join("\n"), @r" built_by_uv-0.1.0.data/data/ @@ -665,6 +665,31 @@ mod tests { built_by_uv-0.1.0.dist-info/entry_points.txt (generated) built_by_uv-0.1.0.dist-info/METADATA (generated) "); + + let mut wheel = zip::ZipArchive::new(File::open(wheel_path).unwrap()).unwrap(); + let mut record = String::new(); + wheel + .by_name("built_by_uv-0.1.0.dist-info/RECORD") + .unwrap() + .read_to_string(&mut record) + .unwrap(); + assert_snapshot!(record, @r###" + built_by_uv/__init__.py,sha256=AJ7XpTNWxYktP97ydb81UpnNqoebH7K4sHRakAMQKG4,44 + built_by_uv/arithmetic/__init__.py,sha256=x2agwFbJAafc9Z6TdJ0K6b6bLMApQdvRSQjP4iy7IEI,67 + built_by_uv/arithmetic/circle.py,sha256=FYZkv6KwrF9nJcwGOKigjke1dm1Fkie7qW1lWJoh3AE,287 + built_by_uv/arithmetic/pi.txt,sha256=-4HqoLoIrSKGf0JdTrM8BTTiIz8rq-MSCDL6LeF0iuU,8 + built_by_uv/cli.py,sha256=Jcm3PxSb8wTAN3dGm5vKEDQwCgoUXkoeggZeF34QyKM,44 + built_by_uv-0.1.0.dist-info/licenses/LICENSE-APACHE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356 + built_by_uv-0.1.0.dist-info/licenses/LICENSE-MIT,sha256=F5Z0Cpu8QWyblXwXhrSo0b9WmYXQxd1LwLjVLJZwbiI,1077 + built_by_uv-0.1.0.dist-info/licenses/third-party-licenses/PEP-401.txt,sha256=KN-KAx829G2saLjVmByc08RFFtIDWvHulqPyD0qEBZI,270 + built_by_uv-0.1.0.data/headers/built_by_uv.h,sha256=p5-HBunJ1dY-xd4dMn03PnRClmGyRosScIp8rT46kg4,144 + built_by_uv-0.1.0.data/scripts/whoami.sh,sha256=T2cmhuDFuX-dTkiSkuAmNyIzvv8AKopjnuTCcr9o-eE,20 + built_by_uv-0.1.0.data/data/data.csv,sha256=7z7u-wXu7Qr2eBZFVpBILlNUiGSngv_1vYqZHVWOU94,265 + built_by_uv-0.1.0.dist-info/WHEEL,sha256=PaG_oOj9G2zCRqoLK0SjWBVZbGAMtIXDmm-MEGw9Wo0,83 + built_by_uv-0.1.0.dist-info/entry_points.txt,sha256=-IO6yaq6x6HSl-zWH96rZmgYvfyHlH00L5WQoCpz-YI,50 + built_by_uv-0.1.0.dist-info/METADATA,sha256=m6EkVvKrGmqx43b_VR45LHD37IZxPYC0NI6Qx9_UXLE,474 + built_by_uv-0.1.0.dist-info/RECORD,, + "###); } /// Test that `license = { file = "LICENSE" }` is supported. diff --git a/crates/uv-build-backend/src/wheel.rs b/crates/uv-build-backend/src/wheel.rs index 4800bb435..7009a6468 100644 --- a/crates/uv-build-backend/src/wheel.rs +++ b/crates/uv-build-backend/src/wheel.rs @@ -1,3 +1,4 @@ +use base64::{Engine, prelude::BASE64_URL_SAFE_NO_PAD as base64}; use fs_err::File; use globset::{GlobSet, GlobSetBuilder}; use itertools::Itertools; @@ -346,7 +347,7 @@ struct RecordEntry { /// /// While the spec would allow backslashes, we always use portable paths with forward slashes. path: String, - /// The SHA256 of the files. + /// The urlsafe-base64-nopad encoded SHA256 of the files. hash: String, /// The size of the file in bytes. size: usize, @@ -381,7 +382,7 @@ fn write_hashed( } Ok(RecordEntry { path: path.to_string(), - hash: format!("{:x}", hasher.finalize()), + hash: base64.encode(hasher.finalize()), size, }) } @@ -641,7 +642,7 @@ impl DirectoryWriter for ZipDirectoryWriter { self.writer.start_file(path, options)?; self.writer.write_all(bytes)?; - let hash = format!("{:x}", Sha256::new().chain_update(bytes).finalize()); + let hash = base64.encode(Sha256::new().chain_update(bytes).finalize()); self.record.push(RecordEntry { path: path.to_string(), hash, @@ -719,7 +720,7 @@ impl FilesystemWriter { impl DirectoryWriter for FilesystemWriter { fn write_bytes(&mut self, path: &str, bytes: &[u8]) -> Result<(), Error> { trace!("Adding {}", path); - let hash = format!("{:x}", Sha256::new().chain_update(bytes).finalize()); + let hash = base64.encode(Sha256::new().chain_update(bytes).finalize()); self.record.push(RecordEntry { path: path.to_string(), hash, @@ -795,14 +796,14 @@ mod test { fn test_record() { let record = vec![RecordEntry { path: "built_by_uv/__init__.py".to_string(), - hash: "89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865".to_string(), + hash: "ifhp5To6AGGlLAIz5kQtTXLegKii00BtnqC_05fteGU".to_string(), size: 37, }]; let mut writer = Vec::new(); write_record(&mut writer, "built_by_uv-0.1.0", record).unwrap(); assert_snapshot!(String::from_utf8(writer).unwrap(), @r" - built_by_uv/__init__.py,sha256=89f869e53a3a0061a52c0233e6442d4d72de80a8a2d3406d9ea0bfd397ed7865,37 + built_by_uv/__init__.py,sha256=ifhp5To6AGGlLAIz5kQtTXLegKii00BtnqC_05fteGU,37 built_by_uv-0.1.0/RECORD,, "); } @@ -861,9 +862,9 @@ mod test { .path() .join("built_by_uv-0.1.0.dist-info/RECORD"); assert_snapshot!(fs_err::read_to_string(record_file).unwrap(), @r###" - built_by_uv-0.1.0.dist-info/WHEEL,sha256=3da1bfa0e8fd1b6cc246aa0b2b44a35815596c600cb485c39a6f8c106c3d5a8d,83 - built_by_uv-0.1.0.dist-info/entry_points.txt,sha256=f883bac9aabac7a1d297ecd61fdeab666818bdfc87947d342f9590a02a73f982,50 - built_by_uv-0.1.0.dist-info/METADATA,sha256=9ba12456f2ab1a6ab1e376ff551e392c70f7ec86713d80b4348e90c7dfd45cb1,474 + built_by_uv-0.1.0.dist-info/WHEEL,sha256=PaG_oOj9G2zCRqoLK0SjWBVZbGAMtIXDmm-MEGw9Wo0,83 + built_by_uv-0.1.0.dist-info/entry_points.txt,sha256=-IO6yaq6x6HSl-zWH96rZmgYvfyHlH00L5WQoCpz-YI,50 + built_by_uv-0.1.0.dist-info/METADATA,sha256=m6EkVvKrGmqx43b_VR45LHD37IZxPYC0NI6Qx9_UXLE,474 built_by_uv-0.1.0.dist-info/RECORD,, "###);