diff --git a/Cargo.lock b/Cargo.lock index 23c51f5e..01af3817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1036,9 +1036,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.61" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", diff --git a/buildpacks/ruby/CHANGELOG.md b/buildpacks/ruby/CHANGELOG.md index f3fc2868..b26e80f3 100644 --- a/buildpacks/ruby/CHANGELOG.md +++ b/buildpacks/ruby/CHANGELOG.md @@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- The buildpack now implements Buildpack API 0.10 instead of 0.9, and so requires `lifecycle` 0.17.x or newer. ([#283](https://github.com/heroku/buildpacks-ruby/pull/283/files#commit-suggestions)) +### Changed + +- The buildpack now implements Buildpack API 0.10 instead of 0.9, and so requires `lifecycle` 0.17.x or newer. ([#283](https://github.com/heroku/buildpacks-ruby/pull/283)) + +### Added + +- Added support for Ubuntu 24.04 (and thus Heroku-24 / `heroku/builder:24`). ([#284](https://github.com/heroku/buildpacks-ruby/pull/284)) ## [2.1.3] - 2024-03-18 diff --git a/buildpacks/ruby/buildpack.toml b/buildpacks/ruby/buildpack.toml index 502b2fc3..254da129 100644 --- a/buildpacks/ruby/buildpack.toml +++ b/buildpacks/ruby/buildpack.toml @@ -28,5 +28,17 @@ version = "20.04" name = "ubuntu" version = "22.04" +[[targets.distros]] +name = "ubuntu" +version = "24.04" + +[[targets]] +os = "linux" +arch = "arm64" + +[[targets.distros]] +name = "ubuntu" +version = "24.04" + [metadata.release] image = { repository = "docker.io/heroku/buildpack-ruby" } diff --git a/buildpacks/ruby/src/layers/ruby_install_layer.rs b/buildpacks/ruby/src/layers/ruby_install_layer.rs index cd6f90cd..614f9a86 100644 --- a/buildpacks/ruby/src/layers/ruby_install_layer.rs +++ b/buildpacks/ruby/src/layers/ruby_install_layer.rs @@ -236,11 +236,18 @@ fn download_url( let filename = format!("ruby-{version}.tgz"); let base = "https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com"; let mut url = Url::parse(base).map_err(RubyInstallError::UrlParseError)?; + { + let mut segments = url + .path_segments_mut() + .map_err(|()| RubyInstallError::InvalidBaseUrl(String::from(base)))?; + + segments.push(&target.stack_name().map_err(RubyInstallError::TargetError)?); + if target.is_arch_aware() { + segments.push(&target.cpu_architecture); + } + segments.push(&filename); + } - url.path_segments_mut() - .map_err(|()| RubyInstallError::InvalidBaseUrl(String::from(base)))? - .push(&target.stack_name().map_err(RubyInstallError::TargetError)?) - .push(&filename); Ok(url) } diff --git a/buildpacks/ruby/src/target_id.rs b/buildpacks/ruby/src/target_id.rs index cad39764..42c07387 100644 --- a/buildpacks/ruby/src/target_id.rs +++ b/buildpacks/ruby/src/target_id.rs @@ -1,28 +1,30 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] -#[serde(deny_unknown_fields)] +#[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct TargetId { pub(crate) distro_name: String, pub(crate) distro_version: String, pub(crate) cpu_architecture: String, } - +const ARCH_AWARE_VERSIONS: &[&str] = &["24.04"]; const DISTRO_VERSION_STACK: &[(&str, &str, &str)] = &[ ("ubuntu", "20.04", "heroku-20"), ("ubuntu", "22.04", "heroku-22"), + ("ubuntu", "24.04", "heroku-24"), ]; #[derive(Debug, thiserror::Error)] pub(crate) enum TargetIdError { - #[error("Distro name and version {0}-{1} is not supported. Must be one of: {}", DISTRO_VERSION_STACK.iter().map(|&(name, version, _)| format!("{name}-{version}")).collect::>().join(", "))] + #[error("Distro name and version '{0}-{1}' is not supported. Must be one of: {}", DISTRO_VERSION_STACK.iter().map(|&(name, version, _)| format!("'{name}-{version}'")).collect::>().join(", "))] UnknownDistroNameVersionCombo(String, String), - #[error("Cannot convert stack name {0} into a target OS. Must be one of: {}", DISTRO_VERSION_STACK.iter().map(|&(_, _, stack)| String::from(stack)).collect::>().join(", "))] + #[error("Cannot convert stack name '{0}' into a target OS. Must be one of: {}", DISTRO_VERSION_STACK.iter().map(|&(_, _, stack)| format!("'{stack}'")).collect::>().join(", "))] UnknownStack(String), } impl TargetId { + pub(crate) fn is_arch_aware(&self) -> bool { + ARCH_AWARE_VERSIONS.contains(&self.distro_version.as_str()) + } + pub(crate) fn stack_name(&self) -> Result { DISTRO_VERSION_STACK .iter() @@ -53,6 +55,13 @@ impl TargetId { mod test { use super::*; + #[test] + fn test_arch_aware_versions_are_also_known_as_a_stack() { + for version in ARCH_AWARE_VERSIONS { + assert!(DISTRO_VERSION_STACK.iter().any(|&(_, v, _)| &v == version)); + } + } + #[test] fn test_stack_name() { assert_eq!( diff --git a/buildpacks/ruby/tests/integration_test.rs b/buildpacks/ruby/tests/integration_test.rs index b6e0c4f5..ff17b57f 100644 --- a/buildpacks/ruby/tests/integration_test.rs +++ b/buildpacks/ruby/tests/integration_test.rs @@ -16,6 +16,11 @@ use ureq::Response; #[test] #[ignore = "integration test"] fn test_migrating_metadata() { + // This test is a placeholder for when a change modifies metadata structures. + // Remove the return and update the `buildpack-ruby` reference to the latest version. + #![allow(unreachable_code)] + return; + let builder = "heroku/builder:22"; let app_dir = "tests/fixtures/default_ruby"; @@ -57,9 +62,28 @@ fn test_default_app_ubuntu20() { #[test] #[ignore = "integration test"] -fn test_default_app_latest_distro() { +fn test_default_app_ubuntu22() { TestRunner::default().build( BuildConfig::new("heroku/builder:22", "tests/fixtures/default_ruby"), + |context| { + println!("{}", context.pack_stdout); + assert_contains!(context.pack_stdout, "# Heroku Ruby Buildpack"); + assert_contains!( + context.pack_stdout, + r#"`BUNDLE_BIN="/layers/heroku_ruby/gems/bin" BUNDLE_CLEAN="1" BUNDLE_DEPLOYMENT="1" BUNDLE_GEMFILE="/workspace/Gemfile" BUNDLE_PATH="/layers/heroku_ruby/gems" BUNDLE_WITHOUT="development:test" bundle install`"#); + + assert_contains!(context.pack_stdout, "Installing webrick"); + }, + ); +} + +#[test] +#[ignore = "integration test"] +fn test_default_app_latest_distro() { + let config = amd_arm_builder_config("heroku/builder:24", "tests/fixtures/default_ruby"); + + TestRunner::default().build( + config, |context| { println!("{}", context.pack_stdout); assert_contains!(context.pack_stdout, "# Heroku Ruby Buildpack"); @@ -257,3 +281,17 @@ fn frac_seconds(seconds: f64) -> Duration { } const TEST_PORT: u16 = 1234; + +// TODO: Once Pack build supports `--platform` and libcnb-test adjusted accordingly, change this +// to allow configuring the target arch independently of the builder name (eg via env var). +fn amd_arm_builder_config(builder_name: &str, app_dir: &str) -> BuildConfig { + let mut config = BuildConfig::new(builder_name, app_dir); + + match builder_name { + "heroku/builder:24" if cfg!(target_arch = "aarch64") => { + config.target_triple("aarch64-unknown-linux-musl") + } + _ => config.target_triple("x86_64-unknown-linux-musl"), + }; + config +}