From a0ff67cf1a1cc8286a922f66ddb776785ea65b6a Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 12 Dec 2024 08:33:23 -0600 Subject: [PATCH 1/5] Upgrade Rust version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e787b059..b439a058 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = ["buildpacks/ruby", "commons"] [workspace.package] edition = "2021" -rust-version = "1.80" +rust-version = "1.82" [workspace.lints.rust] unreachable_pub = "warn" From 6a5b1d7318c49acf1d6a0f5aaad7a81b99e8cd37 Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 12 Dec 2024 08:33:50 -0600 Subject: [PATCH 2/5] Add CacheDiff to dependencies --- Cargo.lock | 51 ++++++++++++++++++++++++++++++++++++++ buildpacks/ruby/Cargo.toml | 1 + 2 files changed, 52 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index fe75f4b8..6f782069 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,28 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +[[package]] +name = "cache_diff" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2afc054cd5e2f8a0fb0122db671c3731849b1fcc86ec664e1031834a0828b84b" +dependencies = [ + "bullet_stream", + "cache_diff_derive", +] + +[[package]] +name = "cache_diff_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273bbd860603cb240372b460f8dc2440528ba95593f944b549194eb8600285a8" +dependencies = [ + "proc-macro2", + "quote", + "strum", + "syn 2.0.90", +] + [[package]] name = "camino" version = "1.1.6" @@ -571,6 +593,7 @@ name = "heroku-ruby-buildpack" version = "0.0.0" dependencies = [ "bullet_stream", + "cache_diff", "clap", "commons", "flate2", @@ -1307,6 +1330,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.17" @@ -1425,6 +1454,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.90", +] + [[package]] name = "subtle" version = "2.5.0" diff --git a/buildpacks/ruby/Cargo.toml b/buildpacks/ruby/Cargo.toml index 86953fa5..bb2739e5 100644 --- a/buildpacks/ruby/Cargo.toml +++ b/buildpacks/ruby/Cargo.toml @@ -30,6 +30,7 @@ ureq = { version = "2", default-features = false, features = ["tls"] } url = "2" magic_migrate = "0.2" toml = "0.8" +cache_diff = { version = "1.0.0", features = ["bullet_stream"] } [dev-dependencies] libcnb-test = "=0.26.1" From a0c1a67ffd91967c01a13fce2e0602e68889a06f Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 12 Dec 2024 08:37:10 -0600 Subject: [PATCH 3/5] Import and implement CacheDiff derive macro --- buildpacks/ruby/src/layers/bundle_download_layer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/buildpacks/ruby/src/layers/bundle_download_layer.rs b/buildpacks/ruby/src/layers/bundle_download_layer.rs index 59ad73db..9f9824cb 100644 --- a/buildpacks/ruby/src/layers/bundle_download_layer.rs +++ b/buildpacks/ruby/src/layers/bundle_download_layer.rs @@ -9,6 +9,7 @@ use crate::RubyBuildpack; use crate::RubyBuildpackError; use bullet_stream::state::SubBullet; use bullet_stream::{style, Print}; +use cache_diff::CacheDiff; use commons::gemfile_lock::ResolvedBundlerVersion; use fun_run::{self, CommandWithName}; use libcnb::data::layer_name; @@ -71,8 +72,9 @@ impl MetadataDiff for Metadata { } } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone, CacheDiff)] pub(crate) struct MetadataV1 { + #[cache_diff(rename = "Bundler version")] pub(crate) version: ResolvedBundlerVersion, } From b28174c17aaca3d403140421e3b78e9a2e513a83 Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 12 Dec 2024 08:39:13 -0600 Subject: [PATCH 4/5] Use CacheDiff implementation for MetadataDiff The CacheDiff is a public crate while MetadataDiff was a temporary experimentation platform internal to this buildpack --- .../ruby/src/layers/bundle_download_layer.rs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/buildpacks/ruby/src/layers/bundle_download_layer.rs b/buildpacks/ruby/src/layers/bundle_download_layer.rs index 9f9824cb..3574549a 100644 --- a/buildpacks/ruby/src/layers/bundle_download_layer.rs +++ b/buildpacks/ruby/src/layers/bundle_download_layer.rs @@ -60,15 +60,7 @@ try_migrate_deserializer_chain!( impl MetadataDiff for Metadata { fn diff(&self, other: &Self) -> Vec { - let mut differences = Vec::new(); - if self.version != other.version { - differences.push(format!( - "Bundler version ({old} to {now})", - old = style::value(other.version.to_string()), - now = style::value(self.version.to_string()) - )); - } - differences + ::diff(self, other) } } @@ -143,12 +135,14 @@ mod test { let old = Metadata { version: ResolvedBundlerVersion("2.3.5".to_string()), }; - assert!(old.diff(&old).is_empty()); + assert!(CacheDiff::diff(&old, &old).is_empty()); - let diff = Metadata { - version: ResolvedBundlerVersion("2.3.6".to_string()), - } - .diff(&old); + let diff = CacheDiff::diff( + &Metadata { + version: ResolvedBundlerVersion("2.3.6".to_string()), + }, + &old, + ); assert_eq!( diff.iter().map(strip_ansi).collect::>(), vec!["Bundler version (`2.3.5` to `2.3.6`)"] From 1f9119dcd85fe6281e6eefc5f71542e695fbec8c Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 12 Dec 2024 10:47:33 -0600 Subject: [PATCH 5/5] This code fails to compile with an error ``` error: macros that expand to items must be delimited with braces or followed by a semicolon --> buildpacks/ruby/src/layers/bundle_install_layer.rs:120:1 | 120 | / try_migrate_deserializer_chain!( 121 | | chain: [MetadataV1, MetadataV2, MetadataV3], 122 | | error: MetadataMigrateError, 123 | | deserializer: toml::Deserializer::new, 124 | | ); | |_^ | = note: this error originates in the macro `$crate::try_migrate_link` which comes from the expansion of the macro `try_migrate_deserializer_chain` (in Nightly builds, run with -Z macro-backtrace for more info) ``` Or with nightly: ``` $ RUSTFLAGS="-Zmacro-backtrace" cargo build error: macros that expand to items must be delimited with braces or followed by a semicolon --> /Users/rschneeman/.cargo/registry/src/index.crates.io-6f17d22bba15001f/magic_migrate-0.2.0/src/lib.rs:437:34 | 419 | macro_rules! try_migrate_link { | ----------------------------- in this expansion of `$crate::try_migrate_link!` (#3) ... 437 | $crate::try_migrate_link!($b, $($rest),*) | ^^^^^^^^^^^^^^^^ ... 714 | macro_rules! try_migrate_deserializer_chain { | ------------------------------------------- | | | in this expansion of `try_migrate_deserializer_chain!` (#1) | in this expansion of `$crate::try_migrate_deserializer_chain!` (#2) ... 737 | $crate::try_migrate_link!($a, $($rest),+); | ----------------------------------------- in this macro invocation (#3) ... 764 | $crate::try_migrate_deserializer_chain!(error: $err, deserializer: $deser, chain: [$a, $($rest),+]); | --------------------------------------------------------------------------------------------------- in this macro invocation (#2) | ::: buildpacks/ruby/src/layers/bundle_install_layer.rs:120:1 | 120 | / try_migrate_deserializer_chain!( 121 | | chain: [MetadataV1, MetadataV2, MetadataV3], 122 | | error: MetadataMigrateError, 123 | | deserializer: toml::Deserializer::new, 124 | | ); | |_- in this macro invocation (#1) error: could not compile `heroku-ruby-buildpack` (bin "heroku-ruby-buildpack") due to 1 previous error ``` I'm committing it because I want to figure out how to write a test case for this failure mode in the `magic_migrate` library. --- .../ruby/src/layers/bundle_install_layer.rs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/buildpacks/ruby/src/layers/bundle_install_layer.rs b/buildpacks/ruby/src/layers/bundle_install_layer.rs index 989482ba..ed90f7ec 100644 --- a/buildpacks/ruby/src/layers/bundle_install_layer.rs +++ b/buildpacks/ruby/src/layers/bundle_install_layer.rs @@ -118,7 +118,7 @@ pub(crate) fn handle( pub(crate) type Metadata = MetadataV2; try_migrate_deserializer_chain!( - chain: [MetadataV1, MetadataV2], + chain: [MetadataV1, MetadataV2, MetadataV3], error: MetadataMigrateError, deserializer: toml::Deserializer::new, ); @@ -176,6 +176,15 @@ pub(crate) struct MetadataV2 { pub(crate) cpu_architecture: String, pub(crate) ruby_version: ResolvedRubyVersion, pub(crate) force_bundle_install_key: String, + pub(crate) digest: MetadataDigest, // Must be last for serde to be happy https://github.com/toml-rs/toml-rs/issues/142 +} + +#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq)] +pub(crate) struct MetadataV3 { + pub(crate) os_distribution: String, + pub(crate) cpu_architecture: String, + pub(crate) ruby_version: ResolvedRubyVersion, + pub(crate) force_bundle_install_key: String, /// A struct that holds the cryptographic hash of components that can /// affect the result of `bundle install`. When these values do not @@ -217,6 +226,20 @@ impl TryFrom for MetadataV2 { } } +impl TryFrom for MetadataV3 { + type Error = Infallible; + + fn try_from(v2: MetadataV2) -> Result { + Ok(Self { + os_distribution: format!("{} {}", v2.distro_name, v2.distro_version), + cpu_architecture: v2.cpu_architecture, + ruby_version: v2.ruby_version, + force_bundle_install_key: v2.force_bundle_install_key, + digest: v2.digest, + }) + } +} + #[derive(Debug)] enum InstallState { /// Holds message indicating the reason why we want to run 'bundle install'