diff --git a/src/hash.rs b/src/hash.rs index 304368d39..44f7cce14 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -84,16 +84,42 @@ pub struct HashInfo { /// The hash (first 7 letters of the sha1sum) pub hash: String, - /// The exact input that was used to compute the hash - pub hash_input: String, - /// The hash prefix (e.g. `py38` or `np111`) - pub hash_prefix: String, + #[serde(default, skip_serializing_if = "String::is_empty")] + pub prefix: String, +} + +/// Represents the input to compute the hash +pub struct HashInput(String); + +impl HashInput { + /// Create a new hash input from a variant + pub fn from_variant(variant: &BTreeMap) -> Self { + let mut buf = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter(&mut buf, PythonFormatter {}); + + // BTree has sorted keys, which is important for hashing + variant + .serialize(&mut ser) + .expect("Failed to serialize input"); + + Self(String::from_utf8(buf).expect("Failed to convert to string")) + } + + /// Get the hash input as a string + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Returns the hash input as bytes + pub fn as_bytes(&self) -> &[u8] { + self.0.as_bytes() + } } impl std::fmt::Display for HashInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}h{}", self.hash_prefix, self.hash) + write!(f, "{}h{}", self.prefix, self.hash) } } @@ -136,34 +162,22 @@ impl HashInfo { result } - fn hash_variant(variant: &BTreeMap) -> (String, String) { - let mut buf = Vec::new(); - let mut ser = serde_json::Serializer::with_formatter(&mut buf, PythonFormatter {}); - - // BTree has sorted keys, which is important for hashing - variant - .serialize(&mut ser) - .expect("Failed to serialize input"); - - let string = String::from_utf8(buf).expect("Failed to convert to string"); - + fn hash_from_input(hash_input: &HashInput) -> String { let mut hasher = Sha1::new(); - hasher.update(string.as_bytes()); + hasher.update(hash_input.as_bytes()); let result = hasher.finalize(); const HASH_LENGTH: usize = 7; let res = format!("{:x}", result); - (res[..HASH_LENGTH].to_string(), string) + res[..HASH_LENGTH].to_string() } /// Compute the build string for a given variant pub fn from_variant(variant: &BTreeMap, noarch: &NoArchType) -> Self { - let (hash, hash_input) = Self::hash_variant(variant); Self { - hash, - hash_input, - hash_prefix: Self::hash_prefix(variant, noarch), + hash: Self::hash_from_input(&HashInput::from_variant(variant)), + prefix: Self::hash_prefix(variant, noarch), } } } diff --git a/src/metadata.rs b/src/metadata.rs index a208978ec..63f6cd530 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -622,6 +622,7 @@ mod tests { #[cfg(test)] mod test { + use rstest::*; use std::str::FromStr; use chrono::TimeZone; @@ -716,21 +717,16 @@ mod test { assert_eq!("pip", parsed_yaml3.specs[0].render(false)); } - #[test] - fn read_full_recipe() { + #[rstest] + #[case::rich("rich_recipe.yaml")] + #[case::curl("curl_recipe.yaml")] + fn read_full_recipe(#[case] recipe_path: String) { let test_data_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("test-data/rendered_recipes"); - let recipe_1 = test_data_dir.join("rich_recipe.yaml"); - - let recipe_1 = std::fs::read_to_string(recipe_1).unwrap(); - - let output_rich: Output = serde_yaml::from_str(&recipe_1).unwrap(); - assert_yaml_snapshot!(output_rich); - let recipe_2 = test_data_dir.join("curl_recipe.yaml"); - let recipe_2 = std::fs::read_to_string(recipe_2).unwrap(); - let output_curl: Output = serde_yaml::from_str(&recipe_2).unwrap(); - assert_yaml_snapshot!(output_curl); + let recipe = std::fs::read_to_string(test_data_dir.join(&recipe_path)).unwrap(); + let output: Output = serde_yaml::from_str(&recipe).unwrap(); + assert_yaml_snapshot!(recipe_path, output); } #[test] diff --git a/src/packaging/metadata.rs b/src/packaging/metadata.rs index 6e6930327..fe9ffc89b 100644 --- a/src/packaging/metadata.rs +++ b/src/packaging/metadata.rs @@ -15,14 +15,15 @@ use rattler_digest::{compute_bytes_digest, compute_file_digest}; use std::{ borrow::Cow, collections::HashSet, - io::Write, ops::Deref, path::{Path, PathBuf}, }; +use rattler_conda_types::package::PackageFile; #[cfg(target_family = "unix")] use std::os::unix::prelude::OsStrExt; +use crate::hash::HashInput; use crate::{metadata::Output, recipe::parser::PrefixDetection}; use super::{PackagingError, TempFiles}; @@ -202,6 +203,11 @@ impl Output { } } + /// Returns the contents of the `hash_input.json` file. + pub fn hash_input(&self) -> HashInput { + HashInput::from_variant(&self.build_configuration.variant) + } + /// Create the about.json file for the given output. pub fn about_json(&self) -> AboutJson { let recipe = &self.recipe; @@ -416,36 +422,36 @@ impl Output { temp_files: &TempFiles, ) -> Result, PackagingError> { let mut new_files = HashSet::new(); + let root_dir = temp_files.temp_dir.path(); let info_folder = temp_files.temp_dir.path().join("info"); fs::create_dir_all(&info_folder)?; - let paths_json = File::create(info_folder.join("paths.json"))?; - let paths_json_struct = self.paths_json(temp_files)?; - serde_json::to_writer_pretty(paths_json, &paths_json_struct)?; - new_files.insert(info_folder.join("paths.json")); + let paths_json_path = root_dir.join(PathsJson::package_path()); + let paths_json = File::create(&paths_json_path)?; + serde_json::to_writer_pretty(paths_json, &self.paths_json(temp_files)?)?; + new_files.insert(paths_json_path); - let index_json = File::create(info_folder.join("index.json"))?; + let index_json_path = root_dir.join(IndexJson::package_path()); + let index_json = File::create(&index_json_path)?; serde_json::to_writer_pretty(index_json, &self.index_json()?)?; - new_files.insert(info_folder.join("index.json")); + new_files.insert(index_json_path); - let hash_input_json = File::create(info_folder.join("hash_input.json"))?; - serde_json::to_writer_pretty(hash_input_json, &self.build_configuration.hash.hash_input)?; - new_files.insert(info_folder.join("hash_input.json")); + let hash_input_path = info_folder.join("hash_input.json"); + std::fs::write(&hash_input_path, self.hash_input().as_bytes())?; + new_files.insert(hash_input_path); - let about_json = File::create(info_folder.join("about.json"))?; + let about_json_path = root_dir.join(AboutJson::package_path()); + let about_json = File::create(&about_json_path)?; serde_json::to_writer_pretty(about_json, &self.about_json())?; - new_files.insert(info_folder.join("about.json")); + new_files.insert(about_json_path); if let Some(run_exports) = self.run_exports_json()? { - let run_exports_json = File::create(info_folder.join("run_exports.json"))?; + let run_exports_path = root_dir.join(RunExportsJson::package_path()); + let run_exports_json = File::create(&run_exports_path)?; serde_json::to_writer_pretty(run_exports_json, &run_exports)?; - new_files.insert(info_folder.join("run_exports.json")); + new_files.insert(run_exports_path); } - let mut variant_config = File::create(info_folder.join("hash_input.json"))?; - variant_config.write_all( - serde_json::to_string_pretty(&self.build_configuration.variant)?.as_bytes(), - )?; Ok(new_files) } } diff --git a/src/snapshots/rattler_build__metadata__test__read_full_recipe-2.snap b/src/snapshots/rattler_build__metadata__test__curl_recipe.yaml.snap similarity index 99% rename from src/snapshots/rattler_build__metadata__test__read_full_recipe-2.snap rename to src/snapshots/rattler_build__metadata__test__curl_recipe.yaml.snap index 511a2891c..46529aa65 100644 --- a/src/snapshots/rattler_build__metadata__test__read_full_recipe-2.snap +++ b/src/snapshots/rattler_build__metadata__test__curl_recipe.yaml.snap @@ -1,6 +1,7 @@ --- source: src/metadata.rs -expression: output_curl +assertion_line: 729 +expression: output --- recipe: schema_version: 1 @@ -37,8 +38,6 @@ build_configuration: target_platform: osx-arm64 hash: hash: 60d57d3 - hash_input: "{\"target_platform\": \"osx-arm64\"}" - hash_prefix: "" directories: host_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_curl_1700573293/host_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold build_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_curl_1700573293/build_env diff --git a/src/snapshots/rattler_build__metadata__test__read_recipe_with_sources.snap b/src/snapshots/rattler_build__metadata__test__read_recipe_with_sources.snap index f243792dd..e78eca9b3 100644 --- a/src/snapshots/rattler_build__metadata__test__read_recipe_with_sources.snap +++ b/src/snapshots/rattler_build__metadata__test__read_recipe_with_sources.snap @@ -1,5 +1,6 @@ --- source: src/metadata.rs +assertion_line: 744 expression: git_source_output --- recipe: @@ -33,8 +34,6 @@ build_configuration: target_platform: osx-arm64 hash: hash: 60d57d3 - hash_input: "{\"target_platform\": \"osx-arm64\"}" - hash_prefix: "" directories: host_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_git_source_1704379826/host_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_pla build_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_git_source_1704379826/build_env diff --git a/src/snapshots/rattler_build__metadata__test__read_full_recipe.snap b/src/snapshots/rattler_build__metadata__test__rich_recipe.yaml.snap similarity index 99% rename from src/snapshots/rattler_build__metadata__test__read_full_recipe.snap rename to src/snapshots/rattler_build__metadata__test__rich_recipe.yaml.snap index 26f7e7ca2..ecaecdd79 100644 --- a/src/snapshots/rattler_build__metadata__test__read_full_recipe.snap +++ b/src/snapshots/rattler_build__metadata__test__rich_recipe.yaml.snap @@ -1,6 +1,7 @@ --- source: src/metadata.rs -expression: output_rich +assertion_line: 729 +expression: output --- recipe: schema_version: 1 @@ -53,8 +54,7 @@ build_configuration: target_platform: noarch hash: hash: 4616a5c - hash_input: "{\"target_platform\": \"noarch\"}" - hash_prefix: py + prefix: py directories: host_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_rich_1702492812/host_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold build_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_rich_1702492812/build_env diff --git a/test-data/rendered_recipes/curl_recipe.yaml b/test-data/rendered_recipes/curl_recipe.yaml index 021c83864..814e11b3d 100644 --- a/test-data/rendered_recipes/curl_recipe.yaml +++ b/test-data/rendered_recipes/curl_recipe.yaml @@ -35,8 +35,6 @@ build_configuration: target_platform: osx-arm64 hash: hash: 60d57d3 - hash_input: '{"target_platform": "osx-arm64"}' - hash_prefix: '' directories: host_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_curl_1700573293/host_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold build_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_curl_1700573293/build_env diff --git a/test-data/rendered_recipes/git_source.yaml b/test-data/rendered_recipes/git_source.yaml index 8f8b2a0f5..687d47d2b 100644 --- a/test-data/rendered_recipes/git_source.yaml +++ b/test-data/rendered_recipes/git_source.yaml @@ -29,8 +29,6 @@ build_configuration: target_platform: osx-arm64 hash: hash: 60d57d3 - hash_input: '{"target_platform": "osx-arm64"}' - hash_prefix: '' directories: host_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_git_source_1704379826/host_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_pla build_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_git_source_1704379826/build_env diff --git a/test-data/rendered_recipes/rich_recipe.yaml b/test-data/rendered_recipes/rich_recipe.yaml index 2bbcfd615..2653084a0 100644 --- a/test-data/rendered_recipes/rich_recipe.yaml +++ b/test-data/rendered_recipes/rich_recipe.yaml @@ -54,8 +54,7 @@ build_configuration: target_platform: noarch hash: hash: 4616a5c - hash_input: '{"target_platform": "noarch"}' - hash_prefix: py + prefix: py directories: host_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_rich_1702492812/host_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold build_prefix: /Users/wolfv/Programs/rattler-build/output/bld/rattler-build_rich_1702492812/build_env