diff --git a/CHANGELOG.md b/CHANGELOG.md index db3a9478..4d32348e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `libherokubuildpack`: Add `env::DefaultEnvLayer` and `env::ConfigureEnvLayer` structs for setting environment variables ([#598](https://github.com/heroku/libcnb.rs/pull/598)) ## [0.17.0] - 2023-12-06 diff --git a/libherokubuildpack/Cargo.toml b/libherokubuildpack/Cargo.toml index 90e5a372..b58a460a 100644 --- a/libherokubuildpack/Cargo.toml +++ b/libherokubuildpack/Cargo.toml @@ -18,9 +18,10 @@ all-features = true workspace = true [features] -default = ["command", "download", "digest", "error", "log", "tar", "toml", "fs", "write"] +default = ["command", "download", "digest", "error", "env_layer", "log", "tar", "toml", "fs", "write"] download = ["dep:ureq", "dep:thiserror"] digest = ["dep:sha2"] +env_layer = ["dep:libcnb"] error = ["log", "dep:libcnb"] log = ["dep:termcolor"] tar = ["dep:tar", "dep:flate2"] diff --git a/libherokubuildpack/src/env_layer.rs b/libherokubuildpack/src/env_layer.rs new file mode 100644 index 00000000..9ce85f0a --- /dev/null +++ b/libherokubuildpack/src/env_layer.rs @@ -0,0 +1,125 @@ +use libcnb::build::BuildContext; +use libcnb::data::layer_content_metadata::LayerTypes; +use libcnb::generic::GenericMetadata; +use libcnb::layer::{Layer, LayerResult, LayerResultBuilder}; +use libcnb::layer_env::LayerEnv; +use std::marker::PhantomData; +use std::path::Path; + +/// Convenience layer for setting environment variables +/// +/// If you do not need to modify files on disk or cache metadata, you can use this layer along with +/// [`BuildContext::handle_layer`] to apply results of [`LayerEnv::chainable_insert`] to build and +/// launch (runtime) environments. +/// +/// Example: +/// +/// ```no_run +///# use libcnb::build::{BuildContext, BuildResult, BuildResultBuilder}; +///# use libcnb::data::launch::{LaunchBuilder, ProcessBuilder}; +///# use libcnb::data::process_type; +///# use libcnb::detect::{DetectContext, DetectResult, DetectResultBuilder}; +///# use libcnb::generic::{GenericError, GenericMetadata, GenericPlatform}; +///# use libcnb::{buildpack_main, Buildpack}; +///# use libcnb::data::layer::LayerName; +/// +///# pub(crate) struct HelloWorldBuildpack; +/// +/// use libcnb::Env; +/// use libcnb::data::layer_name; +/// use libcnb::layer_env::{LayerEnv, ModificationBehavior, Scope}; +/// use libherokubuildpack::env_layer; +/// +///# impl Buildpack for HelloWorldBuildpack { +///# type Platform = GenericPlatform; +///# type Metadata = GenericMetadata; +///# type Error = GenericError; +/// +///# fn detect(&self, _context: DetectContext) -> libcnb::Result { +///# todo!() +///# } +/// +///# fn build(&self, context: BuildContext) -> libcnb::Result { +/// let env = Env::from_current(); +/// +/// let env = { +/// let layer = context.handle_layer( +/// layer_name!("configure_env"), +/// env_layer::ConfigureEnvLayer::new( +/// LayerEnv::new() +/// .chainable_insert( +/// Scope::All, +/// ModificationBehavior::Override, +/// "BUNDLE_GEMFILE", // Tells bundler where to find the `Gemfile` +/// context.app_dir.join("Gemfile"), +/// ) +/// .chainable_insert( +/// Scope::All, +/// ModificationBehavior::Override, +/// "BUNDLE_CLEAN", // After successful `bundle install` bundler will automatically run `bundle clean` +/// "1", +/// ) +/// .chainable_insert( +/// Scope::All, +/// ModificationBehavior::Override, +/// "BUNDLE_DEPLOYMENT", // Requires the `Gemfile.lock` to be in sync with the current `Gemfile`. +/// "1", +/// ) +/// .chainable_insert( +/// Scope::All, +/// ModificationBehavior::Default, +/// "MY_ENV_VAR", +/// "Whatever I want", +/// ), +/// ), +/// )?; +/// layer.env.apply(Scope::Build, &env) +/// }; +/// +///# todo!() +///# } +///# } +/// ``` +pub struct ConfigureEnvLayer { + pub(crate) data: LayerEnv, + pub(crate) _buildpack: std::marker::PhantomData, +} + +impl ConfigureEnvLayer +where + B: libcnb::Buildpack, +{ + #[must_use] + pub fn new(env: LayerEnv) -> Self { + ConfigureEnvLayer { + data: env, + _buildpack: PhantomData, + } + } +} + +impl Layer for ConfigureEnvLayer +where + B: libcnb::Buildpack, +{ + type Buildpack = B; + type Metadata = GenericMetadata; + + fn types(&self) -> LayerTypes { + LayerTypes { + build: true, + launch: true, + cache: false, + } + } + + fn create( + &self, + _context: &BuildContext, + _layer_path: &Path, + ) -> Result, B::Error> { + LayerResultBuilder::new(GenericMetadata::default()) + .env(self.data.clone()) + .build() + } +} diff --git a/libherokubuildpack/src/lib.rs b/libherokubuildpack/src/lib.rs index 60a9c376..850b9afa 100644 --- a/libherokubuildpack/src/lib.rs +++ b/libherokubuildpack/src/lib.rs @@ -6,6 +6,8 @@ pub mod command; pub mod digest; #[cfg(feature = "download")] pub mod download; +#[cfg(feature = "env_layer")] +pub mod env_layer; #[cfg(feature = "error")] pub mod error; #[cfg(feature = "fs")]