diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bf29ec6..bcb0f09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Now targets [Buildpack API 0.10](https://github.com/buildpacks/spec/releases/tag/buildpack%2Fv0.10). Buildpacks need to upgrade the `api` key to `0.10` in their `buildpack.toml`. ([#773](https://github.com/heroku/libcnb.rs/pull/773)) - Improved the consistency of cross-compilation assistance provided across all supported `target_triple` and host OS/architecture combinations. [#769](https://github.com/heroku/libcnb.rs/pull/769) - Added cross-compilation assistance for `aarch64-unknown-linux-musl` (on macOS and ARM64 Linux) and `x86_64-unknown-linux-musl` (on ARM64 Linux). [#769](https://github.com/heroku/libcnb.rs/pull/769) - Raised Minimum Supported Rust Version (MSRV) to `1.76`. ([#774](https://github.com/heroku/libcnb.rs/pull/774)) @@ -22,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `libherokubuildpack`: - `MappedWrite::unwrap` for getting the wrapped `Write` back out. ([#765](https://github.com/heroku/libcnb.rs/pull/765)) +### Removed + +- Types, errors, macros and functions related to stacks. The concept of stacks has been removed from the CNB spec. Use `Target` instead. ([#773](https://github.com/heroku/libcnb.rs/pull/773)) + ## [0.17.0] - 2023-12-06 ### Added diff --git a/README.md b/README.md index 1e228a98..849e590b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ It is an opinionated implementation adding language constructs and convenience methods for working with the spec. It values strong adherence to the spec and data formats. -It currently targets version `0.9` of the CNB [Buildpack API specification](https://github.com/buildpacks/spec/blob/buildpack/0.9/buildpack.md). +It currently targets version `0.10` of the CNB [Buildpack API specification](https://github.com/buildpacks/spec/blob/buildpack/0.10/buildpack.md). ## Quick Start Guide @@ -78,15 +78,12 @@ Since we're writing a Cloud Native Buildpack, we also need a `buildpack.toml`. U file named `buildpack.toml` in the root of your project, right next to `Cargo.toml`. ```toml -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-examples/my-buildpack" version = "0.1.0" name = "My Buildpack" - -[[stacks]] -id = "*" ``` That's all we need! We can now move on to finally write some buildpack code! @@ -132,7 +129,7 @@ impl Buildpack for HelloWorldBuildpack { type Error = GenericError; // This method will be called when the CNB lifecycle executes the detect phase (`bin/detect`). - // Use the `DetectContext` to access CNB data such as the stack this buildpack is currently + // Use the `DetectContext` to access CNB data such as the operating system this buildpack is currently // executed on, the app directory and similar things. When using libcnb.rs, you never have // to read environment variables or read/write files to disk to interact with the CNB lifecycle. // @@ -148,7 +145,7 @@ impl Buildpack for HelloWorldBuildpack { // build phase (`bin/build`). fn build(&self, context: BuildContext) -> libcnb::Result { println!("Hello World!"); - println!("Build runs on stack {}!", context.stack_id); + println!("The build is running on: {} ({})!", context.target.os, context.target.arch); BuildResultBuilder::new() .launch( @@ -218,7 +215,7 @@ libcnb-examples/my-buildpack 0.1.0 ===> RESTORING ===> BUILDING Hello World! -Build runs on stack heroku-22! +The build is running on: linux (amd64)! ===> EXPORTING Adding layer 'buildpacksio/lifecycle:launch.sbom' Adding 1/1 app layer(s) @@ -230,7 +227,7 @@ Adding label 'io.buildpacks.build.metadata' Adding label 'io.buildpacks.project.metadata' Setting default process type 'web' Saving my-image... -*** Images (aa4695184718): +*** Images (85b067fc926a): my-image Successfully built image my-image ``` diff --git a/examples/basics/buildpack.toml b/examples/basics/buildpack.toml index 542775d9..88ab3b18 100644 --- a/examples/basics/buildpack.toml +++ b/examples/basics/buildpack.toml @@ -1,9 +1,6 @@ -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-examples/basics" version = "0.1.0" name = "Example libcnb buildpack: basics" - -[[stacks]] -id = "*" diff --git a/examples/basics/src/main.rs b/examples/basics/src/main.rs index 973d96a8..530bd27b 100644 --- a/examples/basics/src/main.rs +++ b/examples/basics/src/main.rs @@ -15,7 +15,11 @@ impl Buildpack for BasicBuildpack { } fn build(&self, context: BuildContext) -> libcnb::Result { - println!("Build runs on stack {}!", context.stack_id); + println!( + "The build is running on {} ({})!", + context.target.os, context.target.arch + ); + BuildResultBuilder::new().build() } } diff --git a/examples/execd/buildpack.toml b/examples/execd/buildpack.toml index 8300fc51..7e041601 100644 --- a/examples/execd/buildpack.toml +++ b/examples/execd/buildpack.toml @@ -1,9 +1,6 @@ -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-examples/execd" version = "0.1.0" name = "Example libcnb buildpack: execd" - -[[stacks]] -id = "*" diff --git a/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/not_libcnb/buildpack.toml b/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/not_libcnb/buildpack.toml index 2e50669f..b46e7d98 100644 --- a/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/not_libcnb/buildpack.toml +++ b/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/not_libcnb/buildpack.toml @@ -3,6 +3,3 @@ api = "0.8" [buildpack] id = "multiple-buildpacks/not-libcnb" version = "0.0.0" - -[[stacks]] -id = "some-stack" diff --git a/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/one/buildpack.toml b/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/one/buildpack.toml index ede40ad4..d5333dd8 100644 --- a/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/one/buildpack.toml +++ b/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/one/buildpack.toml @@ -3,6 +3,3 @@ api = "0.8" [buildpack] id = "multiple-buildpacks/one" version = "0.0.0" - -[[stacks]] -id = "some-stack" diff --git a/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/two/buildpack.toml b/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/two/buildpack.toml index e9c05496..3bdd2a87 100644 --- a/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/two/buildpack.toml +++ b/libcnb-cargo/tests/fixtures/multiple_buildpacks/buildpacks/two/buildpack.toml @@ -3,6 +3,3 @@ api = "0.8" [buildpack] id = "multiple-buildpacks/two" version = "0.0.0" - -[[stacks]] -id = "some-stack" diff --git a/libcnb-cargo/tests/fixtures/single_buildpack/buildpack.toml b/libcnb-cargo/tests/fixtures/single_buildpack/buildpack.toml index 5a46317c..46e199dc 100644 --- a/libcnb-cargo/tests/fixtures/single_buildpack/buildpack.toml +++ b/libcnb-cargo/tests/fixtures/single_buildpack/buildpack.toml @@ -1,8 +1,5 @@ -api = "0.8" +api = "0.10" [buildpack] id = "single-buildpack" version = "0.0.0" - -[[stacks]] -id = "some-stack" diff --git a/libcnb-data/src/buildpack/mod.rs b/libcnb-data/src/buildpack/mod.rs index 950e17c9..9801c548 100644 --- a/libcnb-data/src/buildpack/mod.rs +++ b/libcnb-data/src/buildpack/mod.rs @@ -1,7 +1,6 @@ mod api; mod id; -mod stack; -mod stack_id; +mod target; mod version; use crate::generic::GenericMetadata; @@ -9,9 +8,8 @@ use crate::sbom::SbomFormat; pub use api::*; pub use id::*; use serde::Deserialize; -pub use stack::*; -pub use stack_id::*; use std::collections::HashSet; +pub use target::*; pub use version::*; /// Data structures for the Buildpack descriptor (buildpack.toml). @@ -28,7 +26,7 @@ pub use version::*; /// use libcnb_data::buildpack::BuildpackDescriptor; /// /// let toml_str = r#" -/// api = "0.9" +/// api = "0.10" /// /// [buildpack] /// id = "foo/bar" @@ -41,9 +39,6 @@ pub use version::*; /// /// [[buildpack.licenses]] /// type = "BSD-3-Clause" -/// -/// [[stacks]] -/// id = "*" /// "#; /// /// let buildpack_descriptor = @@ -84,11 +79,11 @@ impl BuildpackDescriptor { /// /// # Example: /// ``` -/// use libcnb_data::buildpack::{ComponentBuildpackDescriptor, Stack}; +/// use libcnb_data::buildpack::{ComponentBuildpackDescriptor, Target}; /// use libcnb_data::buildpack_id; /// /// let toml_str = r#" -/// api = "0.9" +/// api = "0.10" /// /// [buildpack] /// id = "foo/bar" @@ -102,22 +97,35 @@ impl BuildpackDescriptor { /// [[buildpack.licenses]] /// type = "BSD-3-Clause" /// -/// [[stacks]] -/// id = "*" +/// [[targets]] +/// os = "linux" /// "#; /// /// let buildpack_descriptor = /// toml::from_str::(toml_str).unwrap(); /// assert_eq!(buildpack_descriptor.buildpack.id, buildpack_id!("foo/bar")); -/// assert_eq!(buildpack_descriptor.stacks, [Stack::Any]); +/// assert_eq!( +/// buildpack_descriptor.targets, +/// [Target { +/// os: Some(String::from("linux")), +/// arch: None, +/// variant: None, +/// distros: vec![] +/// }] +/// ); /// ``` #[derive(Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct ComponentBuildpackDescriptor { pub api: BuildpackApi, pub buildpack: Buildpack, - pub stacks: Vec, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub targets: Vec, pub metadata: BM, + // As of 2024-02-09, the CNB spec does not forbid component buildpacks + // to contain `order`. This is a change from buildpack API 0.9 where `order` + // was disallowed in component buildpacks. However, `pack` does not allow this. + // We believe this to be a spec error and libcnb.rs does intentionally not support this. } /// Data structure for the Buildpack descriptor (buildpack.toml) of a composite buildpack. @@ -135,7 +143,7 @@ pub struct ComponentBuildpackDescriptor { /// use libcnb_data::buildpack_id; /// /// let toml_str = r#" -/// api = "0.9" +/// api = "0.10" /// /// [buildpack] /// id = "foo/bar" @@ -167,6 +175,10 @@ pub struct CompositeBuildpackDescriptor { pub buildpack: Buildpack, pub order: Vec, pub metadata: BM, + // As of 2024-02-09, the CNB spec does not forbid composite buildpacks + // to contain `targets`. This is a change from buildpack API 0.9 where `stack` + // was disallowed in composite buildpacks. However, `pack` does not allow this. + // We believe this to be a spec error and libcnb.rs does intentionally not support this. } #[derive(Deserialize, Debug)] @@ -222,7 +234,7 @@ mod tests { #[allow(clippy::too_many_lines)] fn deserialize_component_buildpack() { let toml_str = r#" -api = "0.9" +api = "0.10" [buildpack] id = "foo/bar" @@ -245,21 +257,23 @@ uri = "https://example.tld/my-license" [[buildpack.licenses]] uri = "https://example.tld/my-license" -[[stacks]] -id = "heroku-20" +[[targets]] +os = "linux" +arch = "amd64" +[[targets.distros]] +name = "ubuntu" +version = "18.04" -[[stacks]] -id = "io.buildpacks.stacks.bionic" -mixins = [] +[[targets]] +os = "linux" +arch = "arm" +variant = "v8" -[[stacks]] -id = "io.buildpacks.stacks.focal" -mixins = ["build:jq", "wget"] +[[targets]] +os = "windows" +arch = "amd64" -# As counter-intuitive as it may seem, the CNB spec permits specifying -# the "any" stack at the same time as stacks with specific IDs. -[[stacks]] -id = "*" +[[targets]] [metadata] checksum = "abc123" @@ -270,7 +284,10 @@ checksum = "abc123" assert_eq!( buildpack_descriptor.api, - BuildpackApi { major: 0, minor: 9 } + BuildpackApi { + major: 0, + minor: 10 + } ); assert_eq!( buildpack_descriptor.buildpack.id, @@ -323,22 +340,35 @@ checksum = "abc123" ]) ); assert_eq!( - buildpack_descriptor.stacks, + buildpack_descriptor.targets, [ - Stack::Specific { - // Cannot use the `stack_id!` macro due to: https://github.com/heroku/libcnb.rs/issues/179 - id: "heroku-20".parse().unwrap(), - mixins: Vec::new() + Target { + os: Some(String::from("linux")), + arch: Some(String::from("amd64")), + variant: None, + distros: vec![Distro { + name: String::from("ubuntu"), + version: String::from("18.04"), + }], }, - Stack::Specific { - id: "io.buildpacks.stacks.bionic".parse().unwrap(), - mixins: Vec::new() + Target { + os: Some(String::from("linux")), + arch: Some(String::from("arm")), + variant: Some(String::from("v8")), + distros: Vec::new(), }, - Stack::Specific { - id: "io.buildpacks.stacks.focal".parse().unwrap(), - mixins: vec![String::from("build:jq"), String::from("wget")] + Target { + os: Some(String::from("windows")), + arch: Some(String::from("amd64")), + variant: None, + distros: Vec::new(), }, - Stack::Any + Target { + os: None, + arch: None, + variant: None, + distros: Vec::new() + } ] ); assert_eq!( @@ -350,7 +380,7 @@ checksum = "abc123" #[test] fn deserialize_composite_buildpack() { let toml_str = r#" -api = "0.9" +api = "0.10" [buildpack] id = "foo/bar" @@ -391,7 +421,10 @@ checksum = "abc123" assert_eq!( buildpack_descriptor.api, - BuildpackApi { major: 0, minor: 9 } + BuildpackApi { + major: 0, + minor: 10 + } ); assert_eq!( buildpack_descriptor.buildpack.id, @@ -461,14 +494,11 @@ checksum = "abc123" #[test] fn deserialize_minimal_component_buildpack() { let toml_str = r#" -api = "0.9" +api = "0.10" [buildpack] id = "foo/bar" version = "0.0.1" - -[[stacks]] -id = "*" "#; let buildpack_descriptor = @@ -476,7 +506,10 @@ id = "*" assert_eq!( buildpack_descriptor.api, - BuildpackApi { major: 0, minor: 9 } + BuildpackApi { + major: 0, + minor: 10 + } ); assert_eq!( buildpack_descriptor.buildpack.id, @@ -496,14 +529,14 @@ id = "*" ); assert_eq!(buildpack_descriptor.buildpack.licenses, Vec::new()); assert_eq!(buildpack_descriptor.buildpack.sbom_formats, HashSet::new()); - assert_eq!(buildpack_descriptor.stacks, [Stack::Any]); + assert_eq!(buildpack_descriptor.targets, []); assert_eq!(buildpack_descriptor.metadata, None); } #[test] fn deserialize_minimal_composite_buildpack() { let toml_str = r#" -api = "0.9" +api = "0.10" [buildpack] id = "foo/bar" @@ -521,7 +554,10 @@ version = "0.0.1" assert_eq!( buildpack_descriptor.api, - BuildpackApi { major: 0, minor: 9 } + BuildpackApi { + major: 0, + minor: 10 + } ); assert_eq!( buildpack_descriptor.buildpack.id, @@ -556,14 +592,11 @@ version = "0.0.1" #[test] fn deserialize_buildpackdescriptor_component() { let toml_str = r#" -api = "0.9" +api = "0.10" [buildpack] id = "foo/bar" version = "0.0.1" - -[[stacks]] -id = "*" "#; let buildpack_descriptor = toml::from_str::(toml_str).unwrap(); @@ -576,7 +609,7 @@ id = "*" #[test] fn deserialize_buildpackdescriptor_composite() { let toml_str = r#" -api = "0.9" +api = "0.10" [buildpack] id = "foo/bar" @@ -597,16 +630,16 @@ version = "0.0.1" } #[test] - fn reject_buildpack_with_both_stacks_and_order() { + fn reject_buildpack_with_both_targets_and_order() { let toml_str = r#" -api = "0.9" +api = "0.10" [buildpack] id = "foo/bar" version = "0.0.1" -[[stacks]] -id = "*" +[[targets]] +os = "linux" [[order]] @@ -625,6 +658,6 @@ version = "0.0.1" assert!(err.to_string().contains("unknown field `order`")); let err = toml::from_str::(toml_str).unwrap_err(); - assert!(err.to_string().contains("unknown field `stacks`")); + assert!(err.to_string().contains("unknown field `targets`")); } } diff --git a/libcnb-data/src/buildpack/stack.rs b/libcnb-data/src/buildpack/stack.rs deleted file mode 100644 index a8a308fc..00000000 --- a/libcnb-data/src/buildpack/stack.rs +++ /dev/null @@ -1,116 +0,0 @@ -use super::{StackId, StackIdError}; -use serde::Deserialize; - -// Used as a "shadow" struct to store -// potentially invalid `Stack` data when deserializing -// https://dev.to/equalma/validate-fields-and-types-in-serde-with-tryfrom-c2n -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -struct StackUnchecked { - id: String, - #[serde(default)] - mixins: Vec, -} - -#[derive(Deserialize, Debug, Eq, PartialEq)] -#[serde(try_from = "StackUnchecked")] -pub enum Stack { - Any, - Specific { id: StackId, mixins: Vec }, -} - -impl TryFrom for Stack { - type Error = StackError; - - fn try_from(value: StackUnchecked) -> Result { - let StackUnchecked { id, mixins } = value; - - if id.as_str() == "*" { - if mixins.is_empty() { - Ok(Self::Any) - } else { - Err(Self::Error::InvalidAnyStack(mixins)) - } - } else { - Ok(Self::Specific { - id: id.parse()?, - mixins, - }) - } - } -} - -#[derive(thiserror::Error, Debug)] -pub enum StackError { - #[error("Stack with id `*` MUST NOT contain mixins, however the following mixins were specified: `{}`", .0.join("`, `"))] - InvalidAnyStack(Vec), - - #[error("Invalid Stack ID: {0}")] - InvalidStackId(#[from] StackIdError), -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn deserialize_specific_stack_without_mixins() { - let toml_str = r#" -id = "heroku-20" -"#; - assert_eq!( - toml::from_str::(toml_str), - Ok(Stack::Specific { - // Cannot use the `stack_id!` macro due to: https://github.com/heroku/libcnb.rs/issues/179 - id: "heroku-20".parse().unwrap(), - mixins: Vec::new() - }), - ); - } - - #[test] - fn deserialize_specific_stack_with_mixins() { - let toml_str = r#" -id = "io.buildpacks.stacks.focal" -mixins = ["build:jq", "wget"] -"#; - assert_eq!( - toml::from_str::(toml_str), - Ok(Stack::Specific { - id: "io.buildpacks.stacks.focal".parse().unwrap(), - mixins: vec![String::from("build:jq"), String::from("wget")] - }), - ); - } - - #[test] - fn deserialize_any_stack() { - let toml_str = r#" -id = "*" -"#; - assert_eq!(toml::from_str::(toml_str), Ok(Stack::Any)); - } - - #[test] - fn reject_specific_stack_with_invalid_name() { - let toml_str = r#" -id = "io.buildpacks.stacks.*" -"#; - let err = toml::from_str::(toml_str).unwrap_err(); - assert!(err - .to_string() - .contains("Invalid Stack ID: Invalid Value: io.buildpacks.stacks.*")); - } - - #[test] - fn reject_any_stack_with_mixins() { - let toml_str = r#" -id = "*" -mixins = ["build:jq", "wget"] -"#; - let err = toml::from_str::(toml_str).unwrap_err(); - assert!(err - .to_string() - .contains("Stack with id `*` MUST NOT contain mixins, however the following mixins were specified: `build:jq`, `wget`")); - } -} diff --git a/libcnb-data/src/buildpack/stack_id.rs b/libcnb-data/src/buildpack/stack_id.rs deleted file mode 100644 index e558934f..00000000 --- a/libcnb-data/src/buildpack/stack_id.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::newtypes::libcnb_newtype; - -libcnb_newtype!( - buildpack, - /// Construct a [`StackId`] value at compile time. - /// - /// Passing a string that is not a valid `StackId` value will yield a compilation error. - /// - /// # Examples: - /// ``` - /// use libcnb_data::buildpack::StackId; - /// use libcnb_data::stack_id; - /// - /// let stack_id: StackId = stack_id!("heroku-20"); - /// ``` - stack_id, - /// The ID of a stack. - /// - /// It MUST only contain numbers, letters, and the characters `.`, `/`, and `-`. - /// - /// Use the [`stack_id`](crate::stack_id) macro to construct a `StackId` from a - /// literal string. To parse a dynamic string into a `StackId`, use - /// [`str::parse`](str::parse). - /// - /// # Examples - /// ``` - /// use libcnb_data::buildpack::StackId; - /// use libcnb_data::stack_id; - /// - /// let from_literal = stack_id!("heroku-20"); - /// - /// let input = "heroku-20"; - /// let from_dynamic: StackId = input.parse().unwrap(); - /// assert_eq!(from_dynamic, from_literal); - /// - /// let input = "_stack_"; - /// let invalid: Result = input.parse(); - /// assert!(invalid.is_err()); - /// ``` - StackId, - StackIdError, - r"^[[:alnum:]./-]+$" -); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn stack_id_validation_valid() { - assert!("heroku-20".parse::().is_ok()); - assert!("Abc123./-".parse::().is_ok()); - } - - #[test] - fn stack_id_validation_invalid() { - assert_eq!( - "heroku_20".parse::(), - Err(StackIdError::InvalidValue(String::from("heroku_20"))) - ); - assert_eq!( - "heroku:20".parse::(), - Err(StackIdError::InvalidValue(String::from("heroku:20"))) - ); - assert_eq!( - "heroku 20".parse::(), - Err(StackIdError::InvalidValue(String::from("heroku 20"))) - ); - assert_eq!( - "".parse::(), - Err(StackIdError::InvalidValue(String::new())) - ); - } -} diff --git a/libcnb-data/src/buildpack/target.rs b/libcnb-data/src/buildpack/target.rs new file mode 100644 index 00000000..5b7251cf --- /dev/null +++ b/libcnb-data/src/buildpack/target.rs @@ -0,0 +1,18 @@ +use serde::Deserialize; + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Target { + pub os: Option, + pub arch: Option, + pub variant: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub distros: Vec, +} + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Distro { + pub name: String, + pub version: String, +} diff --git a/libcnb-test/tests/fixtures/buildpacks/compile-error/buildpack.toml b/libcnb-test/tests/fixtures/buildpacks/compile-error/buildpack.toml index ec475691..71603a19 100644 --- a/libcnb-test/tests/fixtures/buildpacks/compile-error/buildpack.toml +++ b/libcnb-test/tests/fixtures/buildpacks/compile-error/buildpack.toml @@ -3,6 +3,3 @@ api = "0.8" [buildpack] id = "libcnb-test/compile-error" version = "0.0.0" - -[[stacks]] -id = "*" diff --git a/libcnb-test/tests/fixtures/buildpacks/component-a/buildpack.toml b/libcnb-test/tests/fixtures/buildpacks/component-a/buildpack.toml index f2e10539..969e1e36 100644 --- a/libcnb-test/tests/fixtures/buildpacks/component-a/buildpack.toml +++ b/libcnb-test/tests/fixtures/buildpacks/component-a/buildpack.toml @@ -3,6 +3,3 @@ api = "0.8" [buildpack] id = "libcnb-test/a" version = "0.0.0" - -[[stacks]] -id = "*" diff --git a/libcnb-test/tests/fixtures/buildpacks/component-b/buildpack.toml b/libcnb-test/tests/fixtures/buildpacks/component-b/buildpack.toml index fd1940bf..1e16ebb4 100644 --- a/libcnb-test/tests/fixtures/buildpacks/component-b/buildpack.toml +++ b/libcnb-test/tests/fixtures/buildpacks/component-b/buildpack.toml @@ -3,6 +3,3 @@ api = "0.8" [buildpack] id = "libcnb-test/b" version = "0.0.0" - -[[stacks]] -id = "*" diff --git a/libcnb-test/tests/fixtures/buildpacks/invalid-cargo-toml/buildpack.toml b/libcnb-test/tests/fixtures/buildpacks/invalid-cargo-toml/buildpack.toml index 3fec6b1e..0e78fa15 100644 --- a/libcnb-test/tests/fixtures/buildpacks/invalid-cargo-toml/buildpack.toml +++ b/libcnb-test/tests/fixtures/buildpacks/invalid-cargo-toml/buildpack.toml @@ -3,6 +3,3 @@ api = "0.8" [buildpack] id = "libcnb-test/invalid-cargo-toml" version = "0.0.0" - -[[stacks]] -id = "*" diff --git a/libcnb/src/build.rs b/libcnb/src/build.rs index 13d17fae..0c14a128 100644 --- a/libcnb/src/build.rs +++ b/libcnb/src/build.rs @@ -1,7 +1,6 @@ //! Provides build phase specific types and helpers. use crate::buildpack::Buildpack; -use crate::data::buildpack::StackId; use crate::data::layer::LayerName; use crate::data::store::Store; use crate::data::{ @@ -9,6 +8,7 @@ use crate::data::{ }; use crate::layer::{HandleLayerErrorOrBuildpackError, Layer, LayerData}; use crate::sbom::Sbom; +use crate::target::ContextTarget; use std::path::PathBuf; /// Context for the build phase execution. @@ -16,7 +16,7 @@ pub struct BuildContext { pub layers_dir: PathBuf, pub app_dir: PathBuf, pub buildpack_dir: PathBuf, - pub stack_id: StackId, + pub target: ContextTarget, pub platform: B::Platform, pub buildpack_plan: BuildpackPlan, pub buildpack_descriptor: ComponentBuildpackDescriptor, diff --git a/libcnb/src/buildpack.rs b/libcnb/src/buildpack.rs index 30e52188..8aecd633 100644 --- a/libcnb/src/buildpack.rs +++ b/libcnb/src/buildpack.rs @@ -27,11 +27,11 @@ pub trait Buildpack { type Error: Debug; /// Detect logic for this buildpack. Directly corresponds to - /// [detect in the CNB buildpack interface](https://github.com/buildpacks/spec/blob/platform/v0.9/buildpack.md#detection). + /// [detect in the CNB buildpack interface](https://github.com/buildpacks/spec/blob/platform/v0.10/buildpack.md#detection). fn detect(&self, context: DetectContext) -> crate::Result; /// Build logic for this buildpack. Directly corresponds to - /// [build in the CNB buildpack interface](https://github.com/buildpacks/spec/blob/platform/v0.9/buildpack.md#build). + /// [build in the CNB buildpack interface](https://github.com/buildpacks/spec/blob/platform/v0.10/buildpack.md#build). fn build(&self, context: BuildContext) -> crate::Result; /// If an unhandled error occurred within the framework or the buildpack, this method will be diff --git a/libcnb/src/detect.rs b/libcnb/src/detect.rs index dd347b9e..6fa8a452 100644 --- a/libcnb/src/detect.rs +++ b/libcnb/src/detect.rs @@ -1,7 +1,7 @@ //! Provides detect phase specific types and helpers. use crate::buildpack::Buildpack; -use crate::data::buildpack::StackId; +use crate::target::ContextTarget; use crate::{data::build_plan::BuildPlan, data::buildpack::ComponentBuildpackDescriptor}; use std::fmt::Debug; use std::path::PathBuf; @@ -10,7 +10,7 @@ use std::path::PathBuf; pub struct DetectContext { pub app_dir: PathBuf, pub buildpack_dir: PathBuf, - pub stack_id: StackId, + pub target: ContextTarget, pub platform: B::Platform, pub buildpack_descriptor: ComponentBuildpackDescriptor, } diff --git a/libcnb/src/error.rs b/libcnb/src/error.rs index f0a26ef7..7b75c414 100644 --- a/libcnb/src/error.rs +++ b/libcnb/src/error.rs @@ -1,4 +1,3 @@ -use crate::data::buildpack::StackIdError; use crate::data::launch::ProcessTypeError; use crate::layer::HandleLayerError; use libcnb_common::toml_file::TomlFileError; @@ -18,17 +17,17 @@ pub enum Error { #[error("Process type error: {0}")] ProcessTypeError(#[from] ProcessTypeError), - #[error("Stack ID error: {0}")] - StackIdError(#[from] StackIdError), - #[error("Couldn't determine app directory: {0}")] CannotDetermineAppDirectory(std::io::Error), #[error("Couldn't determine buildpack directory: {0}")] CannotDetermineBuildpackDirectory(std::env::VarError), - #[error("Couldn't determine stack id: {0}")] - CannotDetermineStackId(std::env::VarError), + #[error("Couldn't determine target os: {0}")] + CannotDetermineTargetOs(std::env::VarError), + + #[error("Couldn't determine target arch: {0}")] + CannotDetermineTargetArch(std::env::VarError), #[error("Couldn't create platform from platform path: {0}")] CannotCreatePlatformFromPath(std::io::Error), diff --git a/libcnb/src/layer/tests.rs b/libcnb/src/layer/tests.rs index 40c6b2d9..a22da020 100644 --- a/libcnb/src/layer/tests.rs +++ b/libcnb/src/layer/tests.rs @@ -12,7 +12,6 @@ use crate::build::{BuildContext, BuildResult, BuildResultBuilder}; use crate::data::buildpack_id; use crate::data::layer_content_metadata::LayerTypes; -use crate::data::stack_id; use crate::detect::{DetectContext, DetectResult, DetectResultBuilder}; use crate::generic::{GenericMetadata, GenericPlatform}; use crate::layer::{ @@ -20,8 +19,9 @@ use crate::layer::{ MetadataMigration, }; use crate::layer_env::{LayerEnv, ModificationBehavior, Scope}; +use crate::target::ContextTarget; use crate::{read_toml_file, Buildpack, Env, LIBCNB_SUPPORTED_BUILDPACK_API}; -use libcnb_data::buildpack::{BuildpackVersion, ComponentBuildpackDescriptor, Stack}; +use libcnb_data::buildpack::{BuildpackVersion, ComponentBuildpackDescriptor, Target}; use libcnb_data::buildpack_plan::BuildpackPlan; use libcnb_data::layer::LayerName; use libcnb_data::layer_content_metadata::LayerContentMetadata; @@ -902,7 +902,13 @@ fn build_context(temp_dir: &TempDir) -> BuildContext { layers_dir, app_dir, buildpack_dir, - stack_id: stack_id!("heroku-20"), + target: ContextTarget { + os: String::from("linux"), + arch: String::from("amd64"), + arch_variant: None, + distro_name: Some(String::from("ubuntu")), + distro_version: Some(String::from("22.04")), + }, platform: GenericPlatform::new(Env::new()), buildpack_plan: BuildpackPlan { entries: Vec::new(), @@ -920,7 +926,12 @@ fn build_context(temp_dir: &TempDir) -> BuildContext { licenses: Vec::new(), sbom_formats: HashSet::new(), }, - stacks: vec![Stack::Any], + targets: vec![Target { + os: Some(String::from("linux")), + arch: Some(String::from("amd64")), + variant: None, + distros: vec![], + }], metadata: GenericMetadata::default(), }, store: None, diff --git a/libcnb/src/lib.rs b/libcnb/src/lib.rs index 1c3fa7ac..f0287f54 100644 --- a/libcnb/src/lib.rs +++ b/libcnb/src/lib.rs @@ -18,6 +18,7 @@ mod error; mod exit_code; mod platform; mod runtime; +mod target; #[cfg(feature = "trace")] mod tracing; mod util; @@ -37,7 +38,10 @@ use serde_json as _; pub use libcnb_data as data; const LIBCNB_SUPPORTED_BUILDPACK_API: data::buildpack::BuildpackApi = - data::buildpack::BuildpackApi { major: 0, minor: 9 }; + data::buildpack::BuildpackApi { + major: 0, + minor: 10, + }; /// Generates a main function for the given buildpack. /// diff --git a/libcnb/src/runtime.rs b/libcnb/src/runtime.rs index 3025b544..133fc9bf 100644 --- a/libcnb/src/runtime.rs +++ b/libcnb/src/runtime.rs @@ -1,10 +1,11 @@ use crate::build::{BuildContext, InnerBuildResult}; use crate::buildpack::Buildpack; -use crate::data::buildpack::{BuildpackApi, StackId}; +use crate::data::buildpack::BuildpackApi; use crate::detect::{DetectContext, InnerDetectResult}; use crate::error::Error; use crate::platform::Platform; use crate::sbom::cnb_sbom_path; +use crate::target::ContextTarget; #[cfg(feature = "trace")] use crate::tracing::start_trace; use crate::util::is_not_found_error_kind; @@ -138,12 +139,7 @@ pub fn libcnb_runtime_detect( }; #[cfg(not(feature = "trace"))] - let trace_error = |_: &dyn std::error::Error| {}; - - let stack_id: StackId = env::var("CNB_STACK_ID") - .map_err(Error::CannotDetermineStackId) - .and_then(|stack_id_string| stack_id_string.parse().map_err(Error::StackIdError)) - .inspect_err(|err| trace_error(err))?; + let mut trace_error = |_: &dyn std::error::Error| {}; let platform = B::Platform::from_path(&args.platform_dir_path) .map_err(Error::CannotCreatePlatformFromPath) @@ -151,10 +147,12 @@ pub fn libcnb_runtime_detect( let build_plan_path = args.build_plan_path; + let target = context_target().inspect_err(|err| trace_error(err))?; + let detect_context = DetectContext { app_dir, buildpack_dir, - stack_id, + target, platform, buildpack_descriptor, }; @@ -209,12 +207,7 @@ pub fn libcnb_runtime_build( }; #[cfg(not(feature = "trace"))] - let trace_error = |_: &dyn std::error::Error| {}; - - let stack_id: StackId = env::var("CNB_STACK_ID") - .map_err(Error::CannotDetermineStackId) - .and_then(|stack_id_string| stack_id_string.parse().map_err(Error::StackIdError)) - .inspect_err(|err| trace_error(err))?; + let mut trace_error = |_: &dyn std::error::Error| {}; let platform = Platform::from_path(&args.platform_dir_path) .map_err(Error::CannotCreatePlatformFromPath) @@ -231,11 +224,13 @@ pub fn libcnb_runtime_build( .map_err(Error::CannotReadStore) .inspect_err(|err| trace_error(err))?; + let target = context_target().inspect_err(|err| trace_error(err))?; + let build_context = BuildContext { layers_dir: layers_dir.clone(), app_dir, - stack_id, platform, + target, buildpack_plan, buildpack_dir, buildpack_descriptor, @@ -362,3 +357,22 @@ fn read_buildpack_descriptor() -> crate::Result< .map_err(Error::CannotReadBuildpackDescriptor) }) } + +fn context_target() -> crate::Result +where + E: Debug, +{ + let os = env::var("CNB_TARGET_OS").map_err(Error::CannotDetermineTargetOs)?; + let arch = env::var("CNB_TARGET_ARCH").map_err(Error::CannotDetermineTargetArch)?; + let arch_variant = env::var("CNB_TARGET_ARCH_VARIANT").ok(); + let distro_name = env::var("CNB_TARGET_DISTRO_NAME").ok(); + let distro_version = env::var("CNB_TARGET_DISTRO_VERSION").ok(); + + Ok(ContextTarget { + os, + arch, + arch_variant, + distro_name, + distro_version, + }) +} diff --git a/libcnb/src/target.rs b/libcnb/src/target.rs new file mode 100644 index 00000000..c667a174 --- /dev/null +++ b/libcnb/src/target.rs @@ -0,0 +1,36 @@ +pub struct ContextTarget { + /// The name of the target operating system. + /// + /// The value should conform to [Go's `$GOOS`](https://golang.org/doc/install/source#environment), for example + /// `linux` or `windows`. + /// + /// CNB `lifecycle` sources this value from the build OCI image's [`os` property](https://github.com/opencontainers/image-spec/blob/main/config.md#properties). + pub os: String, + /// The name of the target CPU architecture. + /// + /// The value should conform to [Go's $GOARCH](https://golang.org/doc/install/source#environment), for example + /// `amd64` or `arm64`. + /// + /// CNB `lifecycle` sources this value from the build OCI image's [`architecture` property](https://github.com/opencontainers/image-spec/blob/main/config.md#properties). + /// `` + pub arch: String, + /// The variant of the specified CPU architecture. + /// + /// The value should conform to [OCI image spec platform variants](https://github.com/opencontainers/image-spec/blob/main/image-index.md#platform-variants), for example + /// `v7` or `v8`. + /// + /// CNB `lifecycle` sources this value from the build OCI image's [`variant` property](https://github.com/opencontainers/image-spec/blob/main/config.md#properties). + pub arch_variant: Option, + /// The name of the operating system distribution. Should be empty for Windows. + /// + /// For example: `ubuntu` or `alpine`. + /// + /// CNB `lifecycle` sources this value from the build OCI image's `io.buildpacks.distro.name` label. + pub distro_name: Option, + /// The version of the operating system distribution. + /// + /// For example: `18.02` or `3.19`. + /// + /// CNB `lifecycle` sources this value from the build OCI image's `io.buildpacks.distro.version` label. + pub distro_version: Option, +} diff --git a/test-buildpacks/readonly-layer-files/buildpack.toml b/test-buildpacks/readonly-layer-files/buildpack.toml index ae677edc..19af6a2c 100644 --- a/test-buildpacks/readonly-layer-files/buildpack.toml +++ b/test-buildpacks/readonly-layer-files/buildpack.toml @@ -1,9 +1,6 @@ -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-test-buildpacks/readonly-layer-files" version = "0.1.0" name = "libcnb test buildpack: readonly-layer-files" - -[[stacks]] -id = "*" diff --git a/test-buildpacks/sbom/buildpack.toml b/test-buildpacks/sbom/buildpack.toml index 11098c5e..0906bf46 100644 --- a/test-buildpacks/sbom/buildpack.toml +++ b/test-buildpacks/sbom/buildpack.toml @@ -1,10 +1,7 @@ -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-test-buildpacks/sbom" version = "0.1.0" name = "libcnb test buildpack: SBOM" sbom-formats = ["application/vnd.cyclonedx+json", "application/spdx+json", "application/vnd.syft+json"] - -[[stacks]] -id = "*" diff --git a/test-buildpacks/store/buildpack.toml b/test-buildpacks/store/buildpack.toml index cd4d7d34..0625d567 100644 --- a/test-buildpacks/store/buildpack.toml +++ b/test-buildpacks/store/buildpack.toml @@ -1,9 +1,6 @@ -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-test-buildpacks/store" version = "0.1.0" name = "libcnb test buildpack: store" - -[[stacks]] -id = "*" diff --git a/test-buildpacks/tracing/buildpack.toml b/test-buildpacks/tracing/buildpack.toml index 3820284a..544691d4 100644 --- a/test-buildpacks/tracing/buildpack.toml +++ b/test-buildpacks/tracing/buildpack.toml @@ -1,9 +1,6 @@ -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-test-buildpacks/tracing" version = "0.1.0" name = "libcnb test buildpack: tracing" - -[[stacks]] -id = "*" diff --git a/test-buildpacks/tracing/tests/fixtures/buildpacks/tracing-reader/buildpack.toml b/test-buildpacks/tracing/tests/fixtures/buildpacks/tracing-reader/buildpack.toml index 3b35a264..e2a8eafa 100644 --- a/test-buildpacks/tracing/tests/fixtures/buildpacks/tracing-reader/buildpack.toml +++ b/test-buildpacks/tracing/tests/fixtures/buildpacks/tracing-reader/buildpack.toml @@ -1,9 +1,6 @@ -api = "0.9" +api = "0.10" [buildpack] id = "libcnb-test-buildpacks/tracing-reader" version = "0.1.0" name = "libcnb test buildpack fixture: tracing-reader" - -[[stacks]] -id = "*"