From 097a25d2c28652d6f5822249bc348a2bde0a7d86 Mon Sep 17 00:00:00 2001 From: Manuel Fuchs Date: Thu, 23 May 2024 15:22:12 +0200 Subject: [PATCH] Add arm64 architecture support (#668) --- .github/workflows/ci.yml | 48 +++--- buildpacks/gradle/CHANGELOG.md | 4 + buildpacks/gradle/buildpack.toml | 4 + .../test-apps/heroku-gradle-getting-started | 2 +- buildpacks/gradle/tests/integration/main.rs | 32 +++- buildpacks/gradle/tests/integration/smoke.rs | 8 +- buildpacks/gradle/tests/integration/ux.rs | 31 ---- .../tests/integration/main.rs | 31 ++++ .../tests/integration/smoke.rs | 12 +- buildpacks/jvm/CHANGELOG.md | 6 +- buildpacks/jvm/buildpack.toml | 4 + buildpacks/jvm/openjdk_inventory.toml | 43 ++++- .../test-apps/java-11-app/system.properties | 1 + .../test-apps/java-17-app/system.properties | 1 + .../test-apps/java-21-app/system.properties | 1 + .../test-apps/java-22-app/system.properties | 1 + buildpacks/jvm/tests/integration/main.rs | 25 +++ buildpacks/jvm/tests/integration/versions.rs | 70 +++++--- buildpacks/maven/CHANGELOG.md | 4 + buildpacks/maven/buildpack.toml | 4 + .../test-apps/heroku-java-getting-started | 2 +- .../integration/automatic_process_type.rs | 14 +- buildpacks/maven/tests/integration/caching.rs | 20 ++- .../maven/tests/integration/customization.rs | 31 ++-- buildpacks/maven/tests/integration/main.rs | 37 +++-- buildpacks/maven/tests/integration/misc.rs | 33 ++-- .../maven/tests/integration/polyglot.rs | 11 +- .../maven/tests/integration/settings_xml.rs | 17 +- buildpacks/maven/tests/integration/smoke.rs | 8 +- .../maven/tests/integration/versions.rs | 14 +- buildpacks/sbt/CHANGELOG.md | 4 + buildpacks/sbt/buildpack.toml | 4 + buildpacks/sbt/sbt-extras | 2 +- buildpacks/sbt/tests/integration/caching.rs | 155 +++++++++--------- buildpacks/sbt/tests/integration/main.rs | 31 +++- .../sbt/tests/integration/sbt_at_launch.rs | 18 +- buildpacks/sbt/tests/integration/smoke.rs | 20 +-- buildpacks/sbt/tests/integration/ux.rs | 37 ++--- meta-buildpacks/java/buildpack.toml | 10 ++ meta-buildpacks/scala/buildpack.toml | 10 ++ shared-test/src/lib.rs | 27 +-- 41 files changed, 503 insertions(+), 334 deletions(-) delete mode 100644 buildpacks/gradle/tests/integration/ux.rs create mode 100644 buildpacks/jvm/test-apps/java-11-app/system.properties create mode 100644 buildpacks/jvm/test-apps/java-17-app/system.properties create mode 100644 buildpacks/jvm/test-apps/java-21-app/system.properties create mode 100644 buildpacks/jvm/test-apps/java-22-app/system.properties diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7228c17..c8a7f2de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,24 +46,46 @@ jobs: run: cargo test --locked integration-test: - name: Integration Tests (${{ matrix.buildpack-directory }}) - runs-on: pub-hk-ubuntu-22.04-large - needs: gather-repo-metadata + name: Integration Tests (${{ matrix.buildpack-directory }}, ${{matrix.builder}}, ${{matrix.arch}}) + runs-on: ${{ matrix.arch == 'arm64' && 'pub-hk-ubuntu-22.04-arm-large' || 'pub-hk-ubuntu-22.04-large' }} strategy: fail-fast: false matrix: - buildpack-directory: ${{ fromJson(needs.gather-repo-metadata.outputs.buildpack_dirs) }} + builder: [ "builder:24", "builder:22", "builder:20" ] + arch: [ "amd64", "arm64" ] + buildpack-directory: [ "buildpacks/gradle", "buildpacks/jvm", "buildpacks/jvm-function-invoker", "buildpacks/maven", "buildpacks/sbt" ] + exclude: + - builder: "builder:22" + arch: "arm64" + - builder: "builder:20" + arch: "arm64" + - buildpack-directory: "buildpacks/jvm-function-invoker" + builder: "builder:20" + - buildpack-directory: "buildpacks/jvm-function-invoker" + builder: "builder:24" + env: + INTEGRATION_TEST_BUILDER: heroku/${{ matrix.builder }} steps: - name: Checkout uses: actions/checkout@v4 with: submodules: true + # The beta ARM64 runners don't yet ship with the normal installed tools. + - name: Install Docker, Rust and missing development libs (ARM64 only) + if: matrix.arch == 'arm64' + run: | + sudo apt-get update --error-on=any + sudo apt-get install -y --no-install-recommends acl docker.io docker-buildx libc6-dev + sudo usermod -aG docker $USER + sudo setfacl --modify user:$USER:rw /var/run/docker.sock + curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal + echo "${HOME}/.cargo/bin" >> "${GITHUB_PATH}" - name: Install musl-tools - run: sudo apt-get install musl-tools --no-install-recommends + run: sudo apt-get install -y --no-install-recommends musl-tools - name: Update Rust toolchain run: rustup update - name: Install Rust linux-musl target - run: rustup target add x86_64-unknown-linux-musl + run: rustup target add ${{ matrix.arch == 'arm64' && 'aarch64-unknown-linux-musl' || 'x86_64-unknown-linux-musl' }} - name: Rust Cache uses: Swatinem/rust-cache@v2 - name: Install Pack CLI @@ -72,17 +94,3 @@ jobs: working-directory: ${{ matrix.buildpack-directory }} # Runs only tests annotated with the `ignore` attribute (which in this repo, are the integration tests). run: cargo test --locked -- --ignored --test-threads 16 - - gather-repo-metadata: - name: "Gather Repository Metadata" - runs-on: ubuntu-22.04 - outputs: - buildpack_dirs: ${{ steps.find-buildpack-dirs.outputs.buildpack_dirs }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: true - - id: find-buildpack-dirs - name: Find buildpack directories - run: echo "buildpack_dirs=$(find . -type f -name 'buildpack.toml' -exec dirname {} \; | grep -v './meta-buildpacks' | sort | uniq | jq -nRc '[inputs]')" >> $GITHUB_OUTPUT diff --git a/buildpacks/gradle/CHANGELOG.md b/buildpacks/gradle/CHANGELOG.md index c53b8d14..58b1f3b4 100644 --- a/buildpacks/gradle/CHANGELOG.md +++ b/buildpacks/gradle/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Support for the `arm64` architecture. ([#668](https://github.com/heroku/buildpacks-jvm/pull/668)) + ### Changed - Buildpack API version changed from `0.9` to `0.10`, and so requires `lifecycle` `0.17.x` or newer. ([#662](https://github.com/heroku/buildpacks-jvm/pull/662)) diff --git a/buildpacks/gradle/buildpack.toml b/buildpacks/gradle/buildpack.toml index 740aad21..4c436f54 100644 --- a/buildpacks/gradle/buildpack.toml +++ b/buildpacks/gradle/buildpack.toml @@ -21,5 +21,9 @@ id = "*" os = "linux" arch = "amd64" +[[targets]] +os = "linux" +arch = "arm64" + [metadata.release] image = { repository = "docker.io/heroku/buildpack-gradle" } diff --git a/buildpacks/gradle/test-apps/heroku-gradle-getting-started b/buildpacks/gradle/test-apps/heroku-gradle-getting-started index d4184a48..fdb64cf4 160000 --- a/buildpacks/gradle/test-apps/heroku-gradle-getting-started +++ b/buildpacks/gradle/test-apps/heroku-gradle-getting-started @@ -1 +1 @@ -Subproject commit d4184a48e32efeb2b2bc6923b2b487ab7ea61c9e +Subproject commit fdb64cf4c1d2e88e9444526a3d326fcae0950e71 diff --git a/buildpacks/gradle/tests/integration/main.rs b/buildpacks/gradle/tests/integration/main.rs index ecc9afc3..58a28ea8 100644 --- a/buildpacks/gradle/tests/integration/main.rs +++ b/buildpacks/gradle/tests/integration/main.rs @@ -8,15 +8,35 @@ // Required due to: https://github.com/rust-lang/rust/issues/95513 #![allow(unused_crate_dependencies)] -use libcnb_test::BuildpackReference; +use libcnb::data::buildpack_id; +use libcnb_test::{BuildConfig, BuildpackReference}; +use std::path::Path; mod smoke; -mod ux; -fn default_buildpacks() -> Vec { - vec![ - BuildpackReference::Other(String::from("heroku/jvm")), +fn default_build_config(fixture_path: impl AsRef) -> BuildConfig { + let builder = builder(); + + // 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). + let target_triple = match builder.as_str() { + // Compile the buildpack for ARM64 iff the builder supports multi-arch and the host is ARM64. + "heroku/builder:24" if cfg!(target_arch = "aarch64") => "aarch64-unknown-linux-musl", + _ => "x86_64-unknown-linux-musl", + }; + + let mut config = BuildConfig::new(&builder, fixture_path); + config.target_triple(target_triple); + config.buildpacks(vec![ + BuildpackReference::WorkspaceBuildpack(buildpack_id!("heroku/jvm")), BuildpackReference::CurrentCrate, BuildpackReference::Other(String::from("heroku/procfile")), - ] + ]); + config } + +fn builder() -> String { + std::env::var("INTEGRATION_TEST_BUILDER").unwrap_or(String::from(DEFAULT_BUILDER)) +} + +const DEFAULT_BUILDER: &str = "heroku/builder:24"; diff --git a/buildpacks/gradle/tests/integration/smoke.rs b/buildpacks/gradle/tests/integration/smoke.rs index ea7b155a..84df71a4 100644 --- a/buildpacks/gradle/tests/integration/smoke.rs +++ b/buildpacks/gradle/tests/integration/smoke.rs @@ -5,16 +5,14 @@ //! //! These tests are strictly happy-path tests and do not assert any output of the buildpack. -use crate::default_buildpacks; -use buildpacks_jvm_shared_test::{smoke_test, DEFAULT_INTEGRATION_TEST_BUILDER}; +use crate::default_build_config; +use buildpacks_jvm_shared_test::smoke_test; #[test] #[ignore = "integration test"] fn smoke_test_getting_started_guide() { smoke_test( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/heroku-gradle-getting-started", - default_buildpacks(), + &default_build_config("test-apps/heroku-gradle-getting-started"), "Getting Started with Gradle on Heroku", ); } diff --git a/buildpacks/gradle/tests/integration/ux.rs b/buildpacks/gradle/tests/integration/ux.rs deleted file mode 100644 index ac668986..00000000 --- a/buildpacks/gradle/tests/integration/ux.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::default_buildpacks; -use buildpacks_jvm_shared::system_properties::write_system_properties; -use buildpacks_jvm_shared_test::DEFAULT_INTEGRATION_TEST_BUILDER; -use libcnb_test::{assert_contains, BuildConfig, PackResult, TestRunner}; -use std::collections::HashMap; - -#[test] -#[ignore = "integration test"] -fn test_unsupported_java_version() { - let build_config = BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/heroku-gradle-getting-started", - ) - .buildpacks(default_buildpacks()) - .expected_pack_result(PackResult::Failure) - .app_dir_preprocessor(|dir| { - write_system_properties( - &dir, - &HashMap::from([(String::from("java.runtime.version"), String::from("7"))]), - ) - .unwrap(); - }) - .to_owned(); - - TestRunner::default().build(&build_config, |context| { - assert_contains!( - context.pack_stderr, - "Gradle 7.6.1 requires Java 1.8 or later to run. You are currently using Java 1.7." - ); - }); -} diff --git a/buildpacks/jvm-function-invoker/tests/integration/main.rs b/buildpacks/jvm-function-invoker/tests/integration/main.rs index 46b0391f..983e4b62 100644 --- a/buildpacks/jvm-function-invoker/tests/integration/main.rs +++ b/buildpacks/jvm-function-invoker/tests/integration/main.rs @@ -8,4 +8,35 @@ // Required due to: https://github.com/rust-lang/rust/issues/95513 #![allow(unused_crate_dependencies)] +use libcnb::data::buildpack_id; +use libcnb_test::{BuildConfig, BuildpackReference}; +use std::path::Path; + mod smoke; + +fn default_build_config(fixture_path: impl AsRef) -> BuildConfig { + let builder = builder(); + + // 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). + let target_triple = match builder.as_str() { + // Compile the buildpack for ARM64 iff the builder supports multi-arch and the host is ARM64. + "heroku/builder:24" if cfg!(target_arch = "aarch64") => "aarch64-unknown-linux-musl", + _ => "x86_64-unknown-linux-musl", + }; + + let mut config = BuildConfig::new(&builder, fixture_path); + config.target_triple(target_triple); + config.buildpacks(vec![ + BuildpackReference::WorkspaceBuildpack(buildpack_id!("heroku/jvm")), + BuildpackReference::WorkspaceBuildpack(buildpack_id!("heroku/maven")), + BuildpackReference::CurrentCrate, + ]); + config +} + +fn builder() -> String { + std::env::var("INTEGRATION_TEST_BUILDER").unwrap_or(String::from(DEFAULT_BUILDER)) +} + +const DEFAULT_BUILDER: &str = "heroku/builder:24"; diff --git a/buildpacks/jvm-function-invoker/tests/integration/smoke.rs b/buildpacks/jvm-function-invoker/tests/integration/smoke.rs index 6a0148ad..9e0c2036 100644 --- a/buildpacks/jvm-function-invoker/tests/integration/smoke.rs +++ b/buildpacks/jvm-function-invoker/tests/integration/smoke.rs @@ -1,20 +1,16 @@ +use crate::default_build_config; use base64::Engine; use buildpacks_jvm_shared_test::{ - DEFAULT_INTEGRATION_TEST_BUILDER, UREQ_RESPONSE_AS_STRING_EXPECT_MESSAGE, - UREQ_RESPONSE_RESULT_EXPECT_MESSAGE, + UREQ_RESPONSE_AS_STRING_EXPECT_MESSAGE, UREQ_RESPONSE_RESULT_EXPECT_MESSAGE, }; -use libcnb_test::{BuildConfig, BuildpackReference, ContainerConfig, TestRunner}; +use libcnb_test::{ContainerConfig, TestRunner}; use std::time::Duration; #[test] #[ignore = "integration test"] fn smoke_test_simple_function() { TestRunner::default().build( - BuildConfig::new(DEFAULT_INTEGRATION_TEST_BUILDER, "test-apps/simple-function").buildpacks([ - BuildpackReference::Other(String::from("heroku/jvm")), - BuildpackReference::Other(String::from("heroku/maven")), - BuildpackReference::CurrentCrate, - ]), + default_build_config("test-apps/simple-function"), |context| { context.start_container( ContainerConfig::new() diff --git a/buildpacks/jvm/CHANGELOG.md b/buildpacks/jvm/CHANGELOG.md index 28c74dc5..253fbd13 100644 --- a/buildpacks/jvm/CHANGELOG.md +++ b/buildpacks/jvm/CHANGELOG.md @@ -7,10 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Support for the `arm64` architecture. ([#668](https://github.com/heroku/buildpacks-jvm/pull/668)) + ### Changed - Buildpack API version changed from `0.9` to `0.10`, and so requires `lifecycle` `0.17.x` or newer. ([#662](https://github.com/heroku/buildpacks-jvm/pull/662)) -- This buildpack is no longer Heroku stack specific and can be used with most x86 Linux based CNB build and run images. ([#662](https://github.com/heroku/buildpacks-jvm/pull/662)) +- This buildpack is no longer Heroku stack specific and can be used with most `amd64` or `arm64` Linux based CNB build and run images. ([#662](https://github.com/heroku/buildpacks-jvm/pull/662)) - Default OpenJDK distribution is now always AzulĀ® ZuluĀ®. ([#662](https://github.com/heroku/buildpacks-jvm/pull/662)) - Some error messages relating to OpenJDK installation changed. ([#665](https://github.com/heroku/buildpacks-jvm/pull/665)) - OpenJDK is now downloaded from `heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com`. Users that use allow-listing for internet access during builds might need to add this new URL to their allow-list. ([#665](https://github.com/heroku/buildpacks-jvm/pull/665)) diff --git a/buildpacks/jvm/buildpack.toml b/buildpacks/jvm/buildpack.toml index 0ea42a25..10158ae2 100644 --- a/buildpacks/jvm/buildpack.toml +++ b/buildpacks/jvm/buildpack.toml @@ -20,6 +20,10 @@ id = "*" os = "linux" arch = "amd64" +[[targets]] +os = "linux" +arch = "arm64" + [metadata.heroku-metrics-agent] url = "https://repo1.maven.org/maven2/com/heroku/agent/heroku-java-metrics-agent/4.0.1/heroku-java-metrics-agent-4.0.1.jar" sha256 = "9a718680e4a93f93a8755b20fb21cc541e5c2692acc9da27c667530f48a716db" diff --git a/buildpacks/jvm/openjdk_inventory.toml b/buildpacks/jvm/openjdk_inventory.toml index cd2692aa..05d2323d 100644 --- a/buildpacks/jvm/openjdk_inventory.toml +++ b/buildpacks/jvm/openjdk_inventory.toml @@ -142,6 +142,14 @@ url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/amd checksum = "sha256:739a1707b8fe24798ae8793667263b2d283d61b7958a576808b38a34fbd79ef1" metadata.distribution = "zulu" +[[artifacts]] +version = "1.8.0_412" +os = "linux" +arch = "arm64" +url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/arm64/1.8.0_412.tar.gz" +checksum = "sha256:9189e22cdef4d80f74e4415c4ad82ac0669b8ca1a4afad1ba132e2035f29f53c" +metadata.distribution = "zulu" + [[artifacts]] version = "11.0.10" os = "linux" @@ -278,12 +286,20 @@ url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/amd checksum = "sha256:b8278982c689cab951ef4daf60a6690f0aef671bbd13ead4f467e2d27e8af034" metadata.distribution = "zulu" +[[artifacts]] +version = "11.0.23" +os = "linux" +arch = "arm64" +url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/arm64/11.0.23.tar.gz" +checksum = "sha256:7ab8f4ff3f1c675d8ca9a904f5d3657afcb59b6d852c4c1b2c5988a2854896cd" +metadata.distribution = "zulu" + [[artifacts]] version = "11.0.23" os = "linux" arch = "amd64" url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/amd64/11.0.23.tar.gz" -checksum = "sha256:7ab8f4ff3f1c675d8ca9a904f5d3657afcb59b6d852c4c1b2c5988a2854896cd" +checksum = "sha256:5b8b551785c4f23417c10feba5e0342af300f491cb7f4c62c7ed84fd37857960" metadata.distribution = "zulu" [[artifacts]] @@ -342,6 +358,14 @@ url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/amd checksum = "sha256:474a59b57d194cf1e1189f51f14325b3be46001cf41b4246cee68a7cef29ddeb" metadata.distribution = "zulu" +[[artifacts]] +version = "17.0.11" +os = "linux" +arch = "arm64" +url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/arm64/17.0.11.tar.gz" +checksum = "sha256:e406854386e38f35a9529a14d1262f9db082afa3bef331f755943922c2f0f091" +metadata.distribution = "zulu" + [[artifacts]] version = "17.0.2" os = "linux" @@ -454,6 +478,15 @@ url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/amd checksum = "sha256:057674a3aff2110a5e11187ecb57ed1342efa89fab63587b20cd3785e374b9b8" metadata.distribution = "zulu" +[[artifacts]] +version = "21.0.3" +os = "linux" +arch = "arm64" +url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/arm64/21.0.3.tar.gz" +checksum = "sha256:02968e30fb3037a89e89a9d4e56e39918db8e5118893e42bf4c880e9779027dc" +metadata.distribution = "zulu" + + [[artifacts]] version = "22.0.0" os = "linux" @@ -469,3 +502,11 @@ arch = "amd64" url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/amd64/22.0.1.tar.gz" checksum = "sha256:44a1a5883f1c67a59e5dafcef0538c09e09ac849dfb24e1088f9529080c1fd43" metadata.distribution = "zulu" + +[[artifacts]] +version = "22.0.1" +os = "linux" +arch = "arm64" +url = "https://heroku-buildpacks-jvm.s3.us-east-1.amazonaws.com/openjdk/zulu/arm64/22.0.1.tar.gz" +checksum = "sha256:263f1773515a42d1ae85a4c911530f4af6e4dabe466a386a9698e922c94e950f" +metadata.distribution = "zulu" diff --git a/buildpacks/jvm/test-apps/java-11-app/system.properties b/buildpacks/jvm/test-apps/java-11-app/system.properties new file mode 100644 index 00000000..6bea4c3c --- /dev/null +++ b/buildpacks/jvm/test-apps/java-11-app/system.properties @@ -0,0 +1 @@ +java.runtime.version = 11 diff --git a/buildpacks/jvm/test-apps/java-17-app/system.properties b/buildpacks/jvm/test-apps/java-17-app/system.properties new file mode 100644 index 00000000..88e47604 --- /dev/null +++ b/buildpacks/jvm/test-apps/java-17-app/system.properties @@ -0,0 +1 @@ +java.runtime.version = 17 diff --git a/buildpacks/jvm/test-apps/java-21-app/system.properties b/buildpacks/jvm/test-apps/java-21-app/system.properties new file mode 100644 index 00000000..4edf1771 --- /dev/null +++ b/buildpacks/jvm/test-apps/java-21-app/system.properties @@ -0,0 +1 @@ +java.runtime.version = 21 diff --git a/buildpacks/jvm/test-apps/java-22-app/system.properties b/buildpacks/jvm/test-apps/java-22-app/system.properties new file mode 100644 index 00000000..44adfe9c --- /dev/null +++ b/buildpacks/jvm/test-apps/java-22-app/system.properties @@ -0,0 +1 @@ +java.runtime.version = 22 diff --git a/buildpacks/jvm/tests/integration/main.rs b/buildpacks/jvm/tests/integration/main.rs index 6ddb1f69..16d472a2 100644 --- a/buildpacks/jvm/tests/integration/main.rs +++ b/buildpacks/jvm/tests/integration/main.rs @@ -8,4 +8,29 @@ // Required due to: https://github.com/rust-lang/rust/issues/95513 #![allow(unused_crate_dependencies)] +use libcnb_test::BuildConfig; +use std::path::Path; + mod versions; + +fn default_build_config(fixture_path: impl AsRef) -> BuildConfig { + let builder = builder(); + + // 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). + let target_triple = match builder.as_str() { + // Compile the buildpack for ARM64 iff the builder supports multi-arch and the host is ARM64. + "heroku/builder:24" if cfg!(target_arch = "aarch64") => "aarch64-unknown-linux-musl", + _ => "x86_64-unknown-linux-musl", + }; + + let mut config = BuildConfig::new(&builder, fixture_path); + config.target_triple(target_triple); + config +} + +fn builder() -> String { + std::env::var("INTEGRATION_TEST_BUILDER").unwrap_or(String::from(DEFAULT_BUILDER)) +} + +const DEFAULT_BUILDER: &str = "heroku/builder:24"; diff --git a/buildpacks/jvm/tests/integration/versions.rs b/buildpacks/jvm/tests/integration/versions.rs index eeb52466..e9f27faf 100644 --- a/buildpacks/jvm/tests/integration/versions.rs +++ b/buildpacks/jvm/tests/integration/versions.rs @@ -1,29 +1,57 @@ -use libcnb_test::{assert_contains, BuildConfig, TestRunner}; +use crate::default_build_config; +use libcnb_test::{assert_contains, TestRunner}; #[test] #[ignore = "integration test"] -fn test_openjdk_8_distribution_heroku_20() { - TestRunner::default().build( - BuildConfig::new("heroku/builder:20", "test-apps/java-8-app"), - |context| { - assert_contains!( - context.run_shell_command("java -version").stderr, - "openjdk version \"1.8.0_412\"" - ); - }, - ); +fn openjdk_8() { + TestRunner::default().build(default_build_config("test-apps/java-8-app"), |context| { + assert_contains!( + context.run_shell_command("java -version").stderr, + "openjdk version \"1.8.0_412\"" + ); + }); } #[test] #[ignore = "integration test"] -fn test_openjdk_8_distribution_heroku_22() { - TestRunner::default().build( - BuildConfig::new("heroku/builder:22", "test-apps/java-8-app"), - |context| { - assert_contains!( - context.run_shell_command("java -version").stderr, - "openjdk version \"1.8.0_412\"" - ); - }, - ); +fn openjdk_11() { + TestRunner::default().build(default_build_config("test-apps/java-11-app"), |context| { + assert_contains!( + context.run_shell_command("java -version").stderr, + "openjdk version \"11.0.23\"" + ); + }); +} + +#[test] +#[ignore = "integration test"] +fn openjdk_17() { + TestRunner::default().build(default_build_config("test-apps/java-17-app"), |context| { + assert_contains!( + context.run_shell_command("java -version").stderr, + "openjdk version \"17.0.11\"" + ); + }); +} + +#[test] +#[ignore = "integration test"] +fn openjdk_21() { + TestRunner::default().build(default_build_config("test-apps/java-21-app"), |context| { + assert_contains!( + context.run_shell_command("java -version").stderr, + "openjdk version \"21.0.3\"" + ); + }); +} + +#[test] +#[ignore = "integration test"] +fn openjdk_22() { + TestRunner::default().build(default_build_config("test-apps/java-22-app"), |context| { + assert_contains!( + context.run_shell_command("java -version").stderr, + "openjdk version \"22.0.1\"" + ); + }); } diff --git a/buildpacks/maven/CHANGELOG.md b/buildpacks/maven/CHANGELOG.md index cb6567f3..0a3754de 100644 --- a/buildpacks/maven/CHANGELOG.md +++ b/buildpacks/maven/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Support for the `arm64` architecture. ([#668](https://github.com/heroku/buildpacks-jvm/pull/668)) + ### Changed - Buildpack API version changed from `0.9` to `0.10`, and so requires `lifecycle` `0.17.x` or newer. ([#662](https://github.com/heroku/buildpacks-jvm/pull/662)) diff --git a/buildpacks/maven/buildpack.toml b/buildpacks/maven/buildpack.toml index 862bda77..677631d8 100644 --- a/buildpacks/maven/buildpack.toml +++ b/buildpacks/maven/buildpack.toml @@ -21,6 +21,10 @@ id = "*" os = "linux" arch = "amd64" +[[targets]] +os = "linux" +arch = "arm64" + [metadata] default-version = "3.9.4" diff --git a/buildpacks/maven/test-apps/heroku-java-getting-started b/buildpacks/maven/test-apps/heroku-java-getting-started index 089d653e..25fc3716 160000 --- a/buildpacks/maven/test-apps/heroku-java-getting-started +++ b/buildpacks/maven/test-apps/heroku-java-getting-started @@ -1 +1 @@ -Subproject commit 089d653e9b461afc5a7420fe396ac69f10bada61 +Subproject commit 25fc3716eed64e6eb58b304f672b530f0c9a4645 diff --git a/buildpacks/maven/tests/integration/automatic_process_type.rs b/buildpacks/maven/tests/integration/automatic_process_type.rs index 87b824b3..ab119402 100644 --- a/buildpacks/maven/tests/integration/automatic_process_type.rs +++ b/buildpacks/maven/tests/integration/automatic_process_type.rs @@ -1,18 +1,12 @@ -use crate::default_buildpacks; -use buildpacks_jvm_shared_test::{ - start_container_assert_basic_http_response, DEFAULT_INTEGRATION_TEST_BUILDER, -}; -use libcnb_test::{BuildConfig, TestRunner}; +use crate::default_build_config; +use buildpacks_jvm_shared_test::start_container_assert_basic_http_response; +use libcnb_test::TestRunner; #[test] #[ignore = "integration test"] fn spring_boot_process_type() { TestRunner::default().build( - BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/buildpack-java-spring-boot-test", - ) - .buildpacks(default_buildpacks()), + default_build_config("test-apps/buildpack-java-spring-boot-test"), |context| { start_container_assert_basic_http_response(&context, "Hello from Spring Boot!"); }, diff --git a/buildpacks/maven/tests/integration/caching.rs b/buildpacks/maven/tests/integration/caching.rs index 5ec89285..0b753fe6 100644 --- a/buildpacks/maven/tests/integration/caching.rs +++ b/buildpacks/maven/tests/integration/caching.rs @@ -1,14 +1,20 @@ -use crate::default_config; +use crate::default_build_config; use libcnb_test::{assert_contains, assert_not_contains, TestRunner}; #[test] #[ignore = "integration test"] fn cache_dependencies_between_builds() { - TestRunner::default().build(default_config(), |context| { - assert_contains!(context.pack_stdout, "Downloading from central"); + TestRunner::default().build( + default_build_config("test-apps/simple-http-service"), + |context| { + assert_contains!(context.pack_stdout, "Downloading from central"); - context.rebuild(default_config(), |context| { - assert_not_contains!(context.pack_stdout, "Downloading from central"); - }); - }); + context.rebuild( + default_build_config("test-apps/simple-http-service"), + |context| { + assert_not_contains!(context.pack_stdout, "Downloading from central"); + }, + ); + }, + ); } diff --git a/buildpacks/maven/tests/integration/customization.rs b/buildpacks/maven/tests/integration/customization.rs index a01eaab9..a76366ba 100644 --- a/buildpacks/maven/tests/integration/customization.rs +++ b/buildpacks/maven/tests/integration/customization.rs @@ -1,11 +1,11 @@ -use crate::default_config; +use crate::default_build_config; use indoc::indoc; use libcnb_test::{assert_contains, TestRunner}; #[test] #[ignore = "integration test"] fn maven_custom_goals() { - TestRunner::default().build(default_config().env("MAVEN_CUSTOM_GOALS", "site"), |context| { + TestRunner::default().build(default_build_config("test-apps/simple-http-service").env("MAVEN_CUSTOM_GOALS", "site"), |context| { // Assert only the goals in MAVEN_CUSTOM_GOALS are executed assert_contains!(context.pack_stdout, "./mvnw -DskipTests site"); assert_contains!(context.pack_stdout,"[INFO] --- maven-site-plugin:3.7.1:site (default-site) @ simple-http-service ---"); @@ -13,7 +13,7 @@ fn maven_custom_goals() { // The dependency list is implemented by using the dependency:list goal. We need to // assert it won't be overwritten by the user's choice of goals. assert_eq!( - context.run_shell_command("cat /app/target/mvn-dependency-list.log").stdout, + context.run_shell_command("cat /workspace/target/mvn-dependency-list.log").stdout, indoc! {" The following files have been resolved: @@ -42,16 +42,19 @@ fn maven_custom_goals() { #[test] #[ignore = "integration test"] fn maven_custom_opts() { - TestRunner::default().build(default_config().env("MAVEN_CUSTOM_OPTS", "-X"), |context| { - // Assert only the options in MAVEN_CUSTOM_GOALS are used - assert_contains!(context.pack_stdout, "./mvnw -X clean install"); - assert_contains!(context.pack_stdout, "[DEBUG] -- end configuration --"); + TestRunner::default().build( + default_build_config("test-apps/simple-http-service").env("MAVEN_CUSTOM_OPTS", "-X"), + |context| { + // Assert only the options in MAVEN_CUSTOM_GOALS are used + assert_contains!(context.pack_stdout, "./mvnw -X clean install"); + assert_contains!(context.pack_stdout, "[DEBUG] -- end configuration --"); - // -DskipTests is part of the default Maven options. We expect it to be overridden by MAVEN_CUSTOM_OPTS and - // therefore expect Maven to run tests. - assert_contains!( - context.pack_stdout, - "[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0" - ); - }); + // -DskipTests is part of the default Maven options. We expect it to be overridden by MAVEN_CUSTOM_OPTS and + // therefore expect Maven to run tests. + assert_contains!( + context.pack_stdout, + "[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0" + ); + }, + ); } diff --git a/buildpacks/maven/tests/integration/main.rs b/buildpacks/maven/tests/integration/main.rs index 4644282a..db72a6a8 100644 --- a/buildpacks/maven/tests/integration/main.rs +++ b/buildpacks/maven/tests/integration/main.rs @@ -10,8 +10,9 @@ // Required due to: https://github.com/rust-lang/rust-clippy/issues/11119 #![allow(clippy::unwrap_used)] -use buildpacks_jvm_shared_test::DEFAULT_INTEGRATION_TEST_BUILDER; +use libcnb::data::buildpack_id; use libcnb_test::{BuildConfig, BuildpackReference}; +use std::path::Path; mod automatic_process_type; mod caching; @@ -22,18 +23,28 @@ mod settings_xml; mod smoke; mod versions; -fn default_config() -> BuildConfig { - BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/simple-http-service", - ) - .buildpacks(default_buildpacks()) - .clone() -} +fn default_build_config(fixture_path: impl AsRef) -> BuildConfig { + let builder = builder(); + + // 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). + let target_triple = match builder.as_str() { + // Compile the buildpack for ARM64 iff the builder supports multi-arch and the host is ARM64. + "heroku/builder:24" if cfg!(target_arch = "aarch64") => "aarch64-unknown-linux-musl", + _ => "x86_64-unknown-linux-musl", + }; -fn default_buildpacks() -> Vec { - vec![ - BuildpackReference::Other(String::from("heroku/jvm")), + let mut config = BuildConfig::new(&builder, fixture_path); + config.target_triple(target_triple); + config.buildpacks(vec![ + BuildpackReference::WorkspaceBuildpack(buildpack_id!("heroku/jvm")), BuildpackReference::CurrentCrate, - ] + ]); + config } + +fn builder() -> String { + std::env::var("INTEGRATION_TEST_BUILDER").unwrap_or(String::from(DEFAULT_BUILDER)) +} + +const DEFAULT_BUILDER: &str = "heroku/builder:24"; diff --git a/buildpacks/maven/tests/integration/misc.rs b/buildpacks/maven/tests/integration/misc.rs index 8899ad15..79dbe643 100644 --- a/buildpacks/maven/tests/integration/misc.rs +++ b/buildpacks/maven/tests/integration/misc.rs @@ -1,4 +1,4 @@ -use crate::default_config; +use crate::default_build_config; use indoc::indoc; use libcnb_test::{assert_contains, assert_not_contains, PackResult, TestRunner}; use std::fs; @@ -8,7 +8,7 @@ use std::os::unix::fs::PermissionsExt; #[ignore = "integration test"] fn mvnw_executable_bit() { TestRunner::default().build( - default_config().app_dir_preprocessor(|dir| { + default_build_config("test-apps/simple-http-service").app_dir_preprocessor(|dir| { fs::set_permissions(dir.join("mvnw"), fs::Permissions::from_mode(0o444)).unwrap(); }), |context| { @@ -22,9 +22,9 @@ fn mvnw_executable_bit() { #[test] #[ignore = "integration test"] fn mvn_dependency_list() { - TestRunner::default().build(default_config(), |context| { + TestRunner::default().build(default_build_config("test-apps/simple-http-service"), |context| { assert_eq!( - context.run_shell_command("cat /app/target/mvn-dependency-list.log").stdout, + context.run_shell_command("cat /workspace/target/mvn-dependency-list.log").stdout, indoc! {" The following files have been resolved: @@ -53,7 +53,7 @@ fn mvn_dependency_list() { #[test] #[ignore = "integration test"] fn no_unexpected_files_in_app_dir() { - TestRunner::default().build(default_config(), |context| { + TestRunner::default().build(default_build_config("test-apps/simple-http-service"), |context| { assert_eq!( context.run_shell_command("find /workspace -type f | sort -s").stdout, indoc! {" @@ -101,21 +101,24 @@ fn no_unexpected_files_in_app_dir() { #[test] #[ignore = "integration test"] fn no_internal_maven_options_logging() { - TestRunner::default().build(default_config(), |context| { - assert_not_contains!(context.pack_stdout, "-Dmaven.repo.local="); - assert_not_contains!(context.pack_stdout, "-Duser.home="); - assert_not_contains!(context.pack_stdout, "dependency:list"); - assert_not_contains!( - context.pack_stdout, - "-DoutputFile=target/mvn-dependency-list.log" - ); - }); + TestRunner::default().build( + default_build_config("test-apps/simple-http-service"), + |context| { + assert_not_contains!(context.pack_stdout, "-Dmaven.repo.local="); + assert_not_contains!(context.pack_stdout, "-Duser.home="); + assert_not_contains!(context.pack_stdout, "dependency:list"); + assert_not_contains!( + context.pack_stdout, + "-DoutputFile=target/mvn-dependency-list.log" + ); + }, + ); } #[test] #[ignore = "integration test"] fn descriptive_error_message_on_failed_build() { - TestRunner::default().build(default_config().app_dir("test-apps/app-with-compile-error").expected_pack_result(PackResult::Failure), |context| { + TestRunner::default().build(default_build_config("test-apps/app-with-compile-error").expected_pack_result(PackResult::Failure), |context| { assert_contains!(context.pack_stdout, "[INFO] BUILD FAILURE"); assert_contains!( diff --git a/buildpacks/maven/tests/integration/polyglot.rs b/buildpacks/maven/tests/integration/polyglot.rs index d0269112..36a9ba7b 100644 --- a/buildpacks/maven/tests/integration/polyglot.rs +++ b/buildpacks/maven/tests/integration/polyglot.rs @@ -1,16 +1,11 @@ -use crate::default_buildpacks; -use buildpacks_jvm_shared_test::DEFAULT_INTEGRATION_TEST_BUILDER; -use libcnb_test::{assert_contains, BuildConfig, TestRunner}; +use crate::default_build_config; +use libcnb_test::{assert_contains, TestRunner}; #[test] #[ignore = "integration test"] fn polyglot_maven_app() { TestRunner::default().build( - BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/simple-http-service-groovy-polyglot", - ) - .buildpacks(default_buildpacks()), + default_build_config("test-apps/simple-http-service-groovy-polyglot"), |context| { assert_contains!(context.pack_stdout, "[INFO] BUILD SUCCESS"); }, diff --git a/buildpacks/maven/tests/integration/settings_xml.rs b/buildpacks/maven/tests/integration/settings_xml.rs index 1c06038c..c1cc866f 100644 --- a/buildpacks/maven/tests/integration/settings_xml.rs +++ b/buildpacks/maven/tests/integration/settings_xml.rs @@ -1,4 +1,4 @@ -use crate::default_config; +use crate::default_build_config; use indoc::formatdoc; use libcnb_test::{assert_contains, assert_not_contains, PackResult, TestRunner}; use std::fs; @@ -8,7 +8,8 @@ use std::path::PathBuf; #[ignore = "integration test"] fn maven_settings_url_success() { TestRunner::default().build( - default_config().env("MAVEN_SETTINGS_URL", SETTINGS_XML_URL), + default_build_config("test-apps/simple-http-service") + .env("MAVEN_SETTINGS_URL", SETTINGS_XML_URL), |context| { assert_contains!( context.pack_stdout, @@ -24,7 +25,7 @@ fn maven_settings_url_success() { #[ignore = "integration test"] fn maven_settings_url_failure() { TestRunner::default().build( - default_config() + default_build_config("test-apps/simple-http-service") .env("MAVEN_SETTINGS_URL", SETTINGS_XML_URL_404) .expected_pack_result(PackResult::Failure), |context| { @@ -46,7 +47,7 @@ fn maven_settings_path() { let settings_xml_test_value = "Take off every 'ZIG'!!"; TestRunner::default().build( - default_config() + default_build_config("test-apps/simple-http-service") .app_dir_preprocessor(move |dir| { write_settings_xml(dir.join(settings_xml_filename), settings_xml_test_value); }) @@ -69,7 +70,7 @@ fn maven_settings_path_and_settings_url() { let settings_xml_test_value = "We get signal."; TestRunner::default().build( - default_config() + default_build_config("test-apps/simple-http-service") .app_dir_preprocessor(move |dir| { write_settings_xml(dir.join(settings_xml_filename), settings_xml_test_value); }) @@ -95,7 +96,7 @@ fn maven_settings_xml_in_app_root() { TestRunner::default().build( // Note that there is no MAVEN_SETTINGS_PATH here - default_config().app_dir_preprocessor(move |dir| { + default_build_config("test-apps/simple-http-service").app_dir_preprocessor(move |dir| { write_settings_xml(dir.join(settings_xml_filename), settings_xml_test_value); }), |context| { @@ -119,7 +120,7 @@ fn maven_settings_xml_in_app_root_and_explicit_settings_path() { TestRunner::default().build( // Note that there is no MAVEN_SETTINGS_PATH here - default_config() + default_build_config("test-apps/simple-http-service") .app_dir_preprocessor(move |dir| { write_settings_xml(dir.join(settings_xml_filename), settings_xml_test_value); write_settings_xml(dir.join(zero_wing_filename), zero_wing_test_value); @@ -144,7 +145,7 @@ fn maven_settings_xml_in_app_root_and_explicit_settings_url() { TestRunner::default().build( // Note that there is no MAVEN_SETTINGS_PATH here - default_config() + default_build_config("test-apps/simple-http-service") .app_dir_preprocessor(move |dir| { write_settings_xml(dir.join(settings_xml_filename), settings_xml_test_value); }) diff --git a/buildpacks/maven/tests/integration/smoke.rs b/buildpacks/maven/tests/integration/smoke.rs index 41ce7b79..8d6719c2 100644 --- a/buildpacks/maven/tests/integration/smoke.rs +++ b/buildpacks/maven/tests/integration/smoke.rs @@ -5,16 +5,14 @@ //! //! These tests are strictly happy-path tests and do not assert any output of the buildpack. -use crate::default_buildpacks; -use buildpacks_jvm_shared_test::{smoke_test, DEFAULT_INTEGRATION_TEST_BUILDER}; +use crate::default_build_config; +use buildpacks_jvm_shared_test::smoke_test; #[test] #[ignore = "integration test"] fn smoke_test_getting_started_guide() { smoke_test( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/heroku-java-getting-started", - default_buildpacks(), + &default_build_config("test-apps/heroku-java-getting-started"), "Getting Started with Java on Heroku", ); } diff --git a/buildpacks/maven/tests/integration/versions.rs b/buildpacks/maven/tests/integration/versions.rs index 5f79557b..6e9c140b 100644 --- a/buildpacks/maven/tests/integration/versions.rs +++ b/buildpacks/maven/tests/integration/versions.rs @@ -1,4 +1,4 @@ -use crate::default_config; +use crate::default_build_config; use libcnb_test::{assert_contains, assert_not_contains, PackResult, TestRunner}; use std::fs::File; use std::path::Path; @@ -6,7 +6,7 @@ use std::path::Path; #[test] #[ignore = "integration test"] fn with_wrapper() { - TestRunner::default().build(default_config(), |context| { + TestRunner::default().build( default_build_config("test-apps/simple-http-service"), |context| { assert_not_contains!(context.pack_stdout, "Selected Maven version:"); assert_contains!(context.pack_stdout, "Maven wrapper detected, skipping installation."); assert_contains!(context.pack_stdout, "$ ./mvnw"); @@ -18,7 +18,7 @@ fn with_wrapper() { #[ignore = "integration test"] fn with_wrapper_and_system_properties() { TestRunner::default().build( - default_config().app_dir_preprocessor(|path| { + default_build_config("test-apps/simple-http-service").app_dir_preprocessor(|path| { remove_maven_wrapper(&path); set_maven_version_app_dir_preprocessor(DEFAULT_MAVEN_VERSION, &path); }), @@ -40,7 +40,7 @@ fn with_wrapper_and_system_properties() { #[ignore = "integration test"] fn with_wrapper_and_unknown_system_properties() { TestRunner::default().build( - default_config().app_dir_preprocessor(|path| set_maven_version_app_dir_preprocessor( + default_build_config("test-apps/simple-http-service").app_dir_preprocessor(|path| set_maven_version_app_dir_preprocessor( UNKNOWN_MAVEN_VERSION, &path )).expected_pack_result(PackResult::Failure), |context| { @@ -54,7 +54,7 @@ fn with_wrapper_and_unknown_system_properties() { #[ignore = "integration test"] fn without_wrapper_and_without_system_properties() { TestRunner::default().build( - default_config().app_dir_preprocessor(|path| { + default_build_config("test-apps/simple-http-service").app_dir_preprocessor(|path| { remove_maven_wrapper(&path); }), |context| { @@ -75,7 +75,7 @@ fn without_wrapper_and_without_system_properties() { #[ignore = "integration test"] fn without_wrapper_and_unknown_system_properties() { TestRunner::default().build( - default_config().app_dir_preprocessor(|path| { + default_build_config("test-apps/simple-http-service").app_dir_preprocessor(|path| { remove_maven_wrapper(&path); set_maven_version_app_dir_preprocessor(UNKNOWN_MAVEN_VERSION, &path); }).expected_pack_result(PackResult::Failure), @@ -90,7 +90,7 @@ fn without_wrapper_and_unknown_system_properties() { #[ignore = "integration test"] fn without_wrapper_and_maven_3_9_4_system_properties() { TestRunner::default().build( - default_config().app_dir_preprocessor(|path| { + default_build_config("test-apps/simple-http-service").app_dir_preprocessor(|path| { remove_maven_wrapper(&path); set_maven_version_app_dir_preprocessor("3.9.4", &path); }), diff --git a/buildpacks/sbt/CHANGELOG.md b/buildpacks/sbt/CHANGELOG.md index a9168b00..1015ea77 100644 --- a/buildpacks/sbt/CHANGELOG.md +++ b/buildpacks/sbt/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for sbt `<1.0`. This buildpack supported old and deprecated sbt versions on a best-effort basis before. Artifacts required by those older versions recently started to be unavailable upstream which caused us to drop support for those versions. If you're affected, please migrate your project to the latest stable sbt version `1.10.0`. ([#669](https://github.com/heroku/buildpacks-jvm/pull/669)) +### Added + +- Support for the `arm64` architecture. ([#668](https://github.com/heroku/buildpacks-jvm/pull/668)) + ### Changed - Buildpack API version changed from `0.9` to `0.10`, and so requires `lifecycle` `0.17.x` or newer. ([#662](https://github.com/heroku/buildpacks-jvm/pull/662)) diff --git a/buildpacks/sbt/buildpack.toml b/buildpacks/sbt/buildpack.toml index 55934d80..9ddaca1f 100644 --- a/buildpacks/sbt/buildpack.toml +++ b/buildpacks/sbt/buildpack.toml @@ -21,5 +21,9 @@ id = "*" os = "linux" arch = "amd64" +[[targets]] +os = "linux" +arch = "arm64" + [metadata.release] image = { repository = "docker.io/heroku/buildpack-sbt" } diff --git a/buildpacks/sbt/sbt-extras b/buildpacks/sbt/sbt-extras index 32c96866..72081700 160000 --- a/buildpacks/sbt/sbt-extras +++ b/buildpacks/sbt/sbt-extras @@ -1 +1 @@ -Subproject commit 32c96866364964b3e2f7272e0f9ef3e1a76ea7d7 +Subproject commit 7208170046457c70a67e4b86c833a074be9de958 diff --git a/buildpacks/sbt/tests/integration/caching.rs b/buildpacks/sbt/tests/integration/caching.rs index 96a5b24f..be292128 100644 --- a/buildpacks/sbt/tests/integration/caching.rs +++ b/buildpacks/sbt/tests/integration/caching.rs @@ -1,71 +1,65 @@ //! Tests that ensure subsequent builds use cached dependencies of various kinds, speeding up //! builds. -use crate::default_buildpacks; -use buildpacks_jvm_shared_test::DEFAULT_INTEGRATION_TEST_BUILDER; -use libcnb_test::{assert_contains, assert_not_contains, BuildConfig, TestRunner}; +use crate::default_build_config; +use libcnb_test::{assert_contains, assert_not_contains, TestRunner}; #[test] #[ignore = "integration test"] fn test_caching_sbt_1_8_2_coursier() { - let build_config = BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/sbt-1.8.2-coursier-scala-2.13.10", - ) - .buildpacks(default_buildpacks()) - .to_owned(); - - TestRunner::default().build(&build_config, |context| { - assert_contains!(&context.pack_stderr, "Downloading sbt launcher for 1.8.2:"); - assert_contains!( - &context.pack_stderr, - "[info] [launcher] getting org.scala-sbt sbt 1.8.2 (this may take some time)..." - ); - assert_contains!( - &context.pack_stderr, - "[info] [launcher] getting Scala 2.12.17 (for sbt)..." - ); - assert_contains!( - &context.pack_stdout, - "[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.17. Compiling..." - ); - assert_contains!( - &context.pack_stdout, - "[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.10. Compiling..." - ); - - context.rebuild(&build_config, |context| { - assert_not_contains!(&context.pack_stderr, "Downloading sbt launcher for 1.8.2:"); - assert_not_contains!( + TestRunner::default().build( + default_build_config("test-apps/sbt-1.8.2-coursier-scala-2.13.10"), + |context| { + assert_contains!(&context.pack_stderr, "Downloading sbt launcher for 1.8.2:"); + assert_contains!( &context.pack_stderr, "[info] [launcher] getting org.scala-sbt sbt 1.8.2 (this may take some time)..." ); - assert_not_contains!( + assert_contains!( &context.pack_stderr, "[info] [launcher] getting Scala 2.12.17 (for sbt)..." ); - assert_not_contains!( + assert_contains!( + &context.pack_stdout, + "[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.17. Compiling..." + ); + assert_contains!( + &context.pack_stdout, + "[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.10. Compiling..." + ); + + context.rebuild( + default_build_config("test-apps/sbt-1.8.2-coursier-scala-2.13.10"), + |context| { + assert_not_contains!( + &context.pack_stderr, + "Downloading sbt launcher for 1.8.2:" + ); + assert_not_contains!( + &context.pack_stderr, + "[info] [launcher] getting org.scala-sbt sbt 1.8.2 (this may take some time)..." + ); + assert_not_contains!( + &context.pack_stderr, + "[info] [launcher] getting Scala 2.12.17 (for sbt)..." + ); + assert_not_contains!( &context.pack_stdout, "[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.17. Compiling..." ); - assert_not_contains!( + assert_not_contains!( &context.pack_stdout, "[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.10. Compiling..." ); - }); - }); + }, + ); + }, + ); } #[test] #[ignore = "integration test"] fn test_caching_sbt_1_8_2_ivy() { - let build_config = BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/sbt-1.8.2-ivy-scala-2.13.10", - ) - .buildpacks(default_buildpacks()) - .to_owned(); - let dependency_download_lines = [ "[info] downloading https://repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar ...", "[info] downloading https://repo1.maven.org/maven2/com/twitter/finagle-toggle_2.13/22.12.0/finagle-toggle_2.13-22.12.0.jar ...", @@ -75,51 +69,60 @@ fn test_caching_sbt_1_8_2_ivy() { "[info] downloading https://repo1.maven.org/maven2/com/twitter/finagle-http2_2.13/22.12.0/finagle-http2_2.13-22.12.0.jar ..." ]; - TestRunner::default().build(&build_config, |context| { - assert_contains!(&context.pack_stderr, "Downloading sbt launcher for 1.8.2:"); - assert_contains!( - &context.pack_stderr, - "[info] [launcher] getting org.scala-sbt sbt 1.8.2 (this may take some time)..." - ); - assert_contains!( - &context.pack_stderr, - "[info] [launcher] getting Scala 2.12.17 (for sbt)..." - ); - assert_contains!( - &context.pack_stdout, - "[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.17. Compiling..." - ); - assert_contains!( - &context.pack_stdout, - "[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.10. Compiling..." - ); - - for dependency_download_line in dependency_download_lines { - assert_contains!(&context.pack_stdout, dependency_download_line); - } - - context.rebuild(&build_config, |context| { - assert_not_contains!(&context.pack_stderr, "Downloading sbt launcher for 1.8.2:"); - assert_not_contains!( + TestRunner::default().build( + default_build_config("test-apps/sbt-1.8.2-ivy-scala-2.13.10"), + |context| { + assert_contains!(&context.pack_stderr, "Downloading sbt launcher for 1.8.2:"); + assert_contains!( &context.pack_stderr, "[info] [launcher] getting org.scala-sbt sbt 1.8.2 (this may take some time)..." ); - assert_not_contains!( + assert_contains!( &context.pack_stderr, "[info] [launcher] getting Scala 2.12.17 (for sbt)..." ); - assert_not_contains!( + assert_contains!( &context.pack_stdout, "[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.17. Compiling..." ); - assert_not_contains!( + assert_contains!( &context.pack_stdout, "[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.10. Compiling..." ); for dependency_download_line in dependency_download_lines { - assert_not_contains!(&context.pack_stdout, dependency_download_line); + assert_contains!(&context.pack_stdout, dependency_download_line); } - }); - }); + + context.rebuild( + default_build_config("test-apps/sbt-1.8.2-ivy-scala-2.13.10"), + |context| { + assert_not_contains!( + &context.pack_stderr, + "Downloading sbt launcher for 1.8.2:" + ); + assert_not_contains!( + &context.pack_stderr, + "[info] [launcher] getting org.scala-sbt sbt 1.8.2 (this may take some time)..." + ); + assert_not_contains!( + &context.pack_stderr, + "[info] [launcher] getting Scala 2.12.17 (for sbt)..." + ); + assert_not_contains!( + &context.pack_stdout, + "[info] Non-compiled module 'compiler-bridge_2.12' for Scala 2.12.17. Compiling..." + ); + assert_not_contains!( + &context.pack_stdout, + "[info] Non-compiled module 'compiler-bridge_2.13' for Scala 2.13.10. Compiling..." + ); + + for dependency_download_line in dependency_download_lines { + assert_not_contains!(&context.pack_stdout, dependency_download_line); + } + }, + ); + }, + ); } diff --git a/buildpacks/sbt/tests/integration/main.rs b/buildpacks/sbt/tests/integration/main.rs index 5a7de0c4..4d590b71 100644 --- a/buildpacks/sbt/tests/integration/main.rs +++ b/buildpacks/sbt/tests/integration/main.rs @@ -8,17 +8,38 @@ // Required due to: https://github.com/rust-lang/rust/issues/95513 #![allow(unused_crate_dependencies)] -use libcnb_test::BuildpackReference; +use libcnb::data::buildpack_id; +use libcnb_test::{BuildConfig, BuildpackReference}; +use std::path::Path; mod caching; mod sbt_at_launch; mod smoke; mod ux; -fn default_buildpacks() -> Vec { - vec![ - BuildpackReference::Other(String::from("heroku/jvm")), +fn default_build_config(fixture_path: impl AsRef) -> BuildConfig { + let builder = builder(); + + // 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). + let target_triple = match builder.as_str() { + // Compile the buildpack for ARM64 iff the builder supports multi-arch and the host is ARM64. + "heroku/builder:24" if cfg!(target_arch = "aarch64") => "aarch64-unknown-linux-musl", + _ => "x86_64-unknown-linux-musl", + }; + + let mut config = BuildConfig::new(&builder, fixture_path); + config.target_triple(target_triple); + config.buildpacks(vec![ + BuildpackReference::WorkspaceBuildpack(buildpack_id!("heroku/jvm")), BuildpackReference::CurrentCrate, BuildpackReference::Other(String::from("heroku/procfile")), - ] + ]); + config } + +fn builder() -> String { + std::env::var("INTEGRATION_TEST_BUILDER").unwrap_or(String::from(DEFAULT_BUILDER)) +} + +const DEFAULT_BUILDER: &str = "heroku/builder:24"; diff --git a/buildpacks/sbt/tests/integration/sbt_at_launch.rs b/buildpacks/sbt/tests/integration/sbt_at_launch.rs index 0658c508..2f11a73e 100644 --- a/buildpacks/sbt/tests/integration/sbt_at_launch.rs +++ b/buildpacks/sbt/tests/integration/sbt_at_launch.rs @@ -1,9 +1,9 @@ -use crate::default_buildpacks; +use crate::default_build_config; use buildpacks_jvm_shared_test::{ - http_request_backoff, DEFAULT_INTEGRATION_TEST_BUILDER, UREQ_RESPONSE_AS_STRING_EXPECT_MESSAGE, + http_request_backoff, UREQ_RESPONSE_AS_STRING_EXPECT_MESSAGE, UREQ_RESPONSE_RESULT_EXPECT_MESSAGE, }; -use libcnb_test::{assert_contains, assert_not_contains, BuildConfig, ContainerConfig, TestRunner}; +use libcnb_test::{assert_contains, assert_not_contains, ContainerConfig, TestRunner}; /// Users can request to have sbt and all caches to be available at launch. One use-case for this /// is not using native-packager and wanting to rely on `sbt run` to run the application in prod. @@ -12,14 +12,10 @@ use libcnb_test::{assert_contains, assert_not_contains, BuildConfig, ContainerCo #[test] #[ignore = "integration test"] fn test_the_thing() { - let build_config = BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/sbt-1.8.2-scala-2.13.10-no-native-packager", - ) - .buildpacks(default_buildpacks()) - .env("SBT_TASKS", "compile") - .env("SBT_AVAILABLE_AT_LAUNCH", "true") - .to_owned(); + let build_config = default_build_config("test-apps/sbt-1.8.2-scala-2.13.10-no-native-packager") + .env("SBT_TASKS", "compile") + .env("SBT_AVAILABLE_AT_LAUNCH", "true") + .to_owned(); TestRunner::default().build(&build_config, |context| { context.start_container( diff --git a/buildpacks/sbt/tests/integration/smoke.rs b/buildpacks/sbt/tests/integration/smoke.rs index 379390f1..95d6c7fc 100644 --- a/buildpacks/sbt/tests/integration/smoke.rs +++ b/buildpacks/sbt/tests/integration/smoke.rs @@ -5,16 +5,14 @@ //! //! These tests are strictly happy-path tests and do not assert any output of the buildpack. -use crate::default_buildpacks; -use buildpacks_jvm_shared_test::{smoke_test, DEFAULT_INTEGRATION_TEST_BUILDER}; +use crate::default_build_config; +use buildpacks_jvm_shared_test::smoke_test; #[test] #[ignore = "integration test"] fn smoke_test_play_framework_3_0_3() { smoke_test( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/play-framework-3.0.3", - default_buildpacks(), + &default_build_config("test-apps/play-framework-3.0.3"), "Welcome to Play!", ); } @@ -23,9 +21,7 @@ fn smoke_test_play_framework_3_0_3() { #[ignore = "integration test"] fn smoke_test_sbt_1_8_2_coursier_scala_2_13_10() { smoke_test( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/sbt-1.8.2-coursier-scala-2.13.10", - default_buildpacks(), + &default_build_config("test-apps/sbt-1.8.2-coursier-scala-2.13.10"), "Hello from Scala!", ); } @@ -34,9 +30,7 @@ fn smoke_test_sbt_1_8_2_coursier_scala_2_13_10() { #[ignore = "integration test"] fn smoke_test_sbt_1_8_2_ivy_scala_2_13_10() { smoke_test( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/sbt-1.8.2-ivy-scala-2.13.10", - default_buildpacks(), + &default_build_config("test-apps/sbt-1.8.2-ivy-scala-2.13.10"), "Hello from Scala!", ); } @@ -45,9 +39,7 @@ fn smoke_test_sbt_1_8_2_ivy_scala_2_13_10() { #[ignore = "integration test"] fn smoke_test_getting_started_guide() { smoke_test( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/heroku-scala-getting-started", - default_buildpacks(), + &default_build_config("test-apps/heroku-scala-getting-started"), "Getting Started with Scala on Heroku", ); } diff --git a/buildpacks/sbt/tests/integration/ux.rs b/buildpacks/sbt/tests/integration/ux.rs index 87668054..ac530273 100644 --- a/buildpacks/sbt/tests/integration/ux.rs +++ b/buildpacks/sbt/tests/integration/ux.rs @@ -1,25 +1,20 @@ -use crate::default_buildpacks; -use buildpacks_jvm_shared_test::DEFAULT_INTEGRATION_TEST_BUILDER; -use libcnb_test::{assert_contains, assert_not_contains, BuildConfig, PackResult, TestRunner}; +use crate::default_build_config; +use libcnb_test::{assert_contains, assert_not_contains, PackResult, TestRunner}; /// Tests that no confusing or non-actionable warnings caused by the buildpack are shown in the /// sbt 1.x log during build. #[test] #[ignore = "integration test"] fn test_sbt_1_x_logging() { - let build_config = BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/sbt-1.8.2-coursier-scala-2.13.10", - ) - .buildpacks(default_buildpacks()) - .to_owned(); - - TestRunner::default().build(&build_config, |context| { - assert_not_contains!( - &context.pack_stdout, - "Executing in batch mode. For better performance use sbt's shell" - ); - }); + TestRunner::default().build( + default_build_config("test-apps/sbt-1.8.2-coursier-scala-2.13.10"), + |context| { + assert_not_contains!( + &context.pack_stdout, + "Executing in batch mode. For better performance use sbt's shell" + ); + }, + ); } /// The buildpack requires (unless otherwise configured) that the application build defines a @@ -31,13 +26,9 @@ fn test_sbt_1_x_logging() { #[test] #[ignore = "integration test"] fn test_missing_stage_task_logging() { - let build_config = BuildConfig::new( - DEFAULT_INTEGRATION_TEST_BUILDER, - "test-apps/sbt-1.8.2-scala-2.13.10-no-native-packager", - ) - .buildpacks(default_buildpacks()) - .expected_pack_result(PackResult::Failure) - .to_owned(); + let build_config = default_build_config("test-apps/sbt-1.8.2-scala-2.13.10-no-native-packager") + .expected_pack_result(PackResult::Failure) + .to_owned(); TestRunner::default().build(&build_config, |context| { assert_contains!(&context.pack_stdout, "[error] Not a valid key: stage"); diff --git a/meta-buildpacks/java/buildpack.toml b/meta-buildpacks/java/buildpack.toml index ac13da9f..af7641d1 100644 --- a/meta-buildpacks/java/buildpack.toml +++ b/meta-buildpacks/java/buildpack.toml @@ -33,3 +33,13 @@ version = "4.1.1" [metadata.release] image = { repository = "docker.io/heroku/buildpack-java" } + +# Targets for this buildpack, used by languages-github-actions when releasing. +# Can be removed as soon as pack supports packaging composite multi-arch buildpacks. +[[metadata.targets]] +os = "linux" +arch = "amd64" + +[[metadata.targets]] +os = "linux" +arch = "arm64" diff --git a/meta-buildpacks/scala/buildpack.toml b/meta-buildpacks/scala/buildpack.toml index 7c1a8748..6e2d5354 100644 --- a/meta-buildpacks/scala/buildpack.toml +++ b/meta-buildpacks/scala/buildpack.toml @@ -23,3 +23,13 @@ version = "4.1.1" [metadata.release] image = { repository = "docker.io/heroku/buildpack-scala" } + +# Targets for this buildpack, used by languages-github-actions when releasing. +# Can be removed as soon as pack supports packaging composite multi-arch buildpacks. +[[metadata.targets]] +os = "linux" +arch = "amd64" + +[[metadata.targets]] +os = "linux" +arch = "arm64" diff --git a/shared-test/src/lib.rs b/shared-test/src/lib.rs index 4890dd94..445cc3d3 100644 --- a/shared-test/src/lib.rs +++ b/shared-test/src/lib.rs @@ -1,7 +1,4 @@ -use libcnb_test::{ - assert_contains, BuildConfig, BuildpackReference, ContainerConfig, TestContext, TestRunner, -}; -use std::path::Path; +use libcnb_test::{assert_contains, BuildConfig, ContainerConfig, TestContext, TestRunner}; use std::time::Duration; /// Extremely opinionated helper for testing containers that expose a HTTP interface. @@ -61,27 +58,15 @@ where /// Opinionated helper for smoke-testing JVM buildpacks. /// -/// Builds the app with the given buildpacks, asserts that the build finished successfully and +/// Builds the app with the given build config, asserts that the build finished successfully and /// builds the app again to ensure that any caching logic does not break subsequent builds. After /// each build, an HTTP request is made to the resulting container, asserting that the given string /// is present in the response. -pub fn smoke_test( - builder_name: &str, - app_dir: P, - buildpacks: B, - expected_http_response_body_contains: &str, -) where - P: AsRef, - B: Into>, -{ - let build_config = BuildConfig::new(builder_name, app_dir) - .buildpacks(buildpacks.into()) - .clone(); - - TestRunner::default().build(&build_config, |context| { +pub fn smoke_test(build_config: &BuildConfig, expected_http_response_body_contains: &str) { + TestRunner::default().build(build_config, |context| { start_container_assert_basic_http_response(&context, expected_http_response_body_contains); - context.rebuild(&build_config, |context| { + context.rebuild(build_config, |context| { start_container_assert_basic_http_response( &context, expected_http_response_body_contains, @@ -90,8 +75,6 @@ pub fn smoke_test( }); } -pub const DEFAULT_INTEGRATION_TEST_BUILDER: &str = "heroku/builder:22"; - pub const UREQ_RESPONSE_RESULT_EXPECT_MESSAGE: &str = "http request should be successful"; pub const UREQ_RESPONSE_AS_STRING_EXPECT_MESSAGE: &str =