Skip to content

Commit

Permalink
Add helper functions & rename env to env_layer
Browse files Browse the repository at this point in the history
Added functions so people don't have to bypass the `handle_layer` dance alltogether:

- set_default
- set_envs

After writing that I realized that the `env` namespace could cause confusion since there's commonly an `env` variable:

```rust
// This is confusing
let env = ...
env::set_default(...
```

So I changed the module name to `env_layer` so it avoids confusion.

There's some redundancy in `env_layer::DefaultEnvLayer` but I thought it would be more confusing making it `env_layer::Default` (collision with `std::default::Default`).
  • Loading branch information
schneems committed Jul 24, 2023
1 parent 3d15e59 commit fced2f0
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 65 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ separate changelogs for each crate were used. If you need to refer to these old

### Added

- `libherokubuildpack`: Add `env::DefaultEnvLayer` and `env::ConfigureEnvLayer` structs for setting environment variables ([#598](https://github.com/heroku/libcnb.rs/pull/598))
- `libherokubuildpack`: Add `env_layer::set_default` and `env_layer::set_envs` helper functions for setting environment variables ([#598](https://github.com/heroku/libcnb.rs/pull/598))
- `libcnb-package`: Add cross-compilation assistance for Linux `aarch64-unknown-linux-musl`. ([#577](https://github.com/heroku/libcnb.rs/pull/577))

### Changed
Expand Down
4 changes: 2 additions & 2 deletions libherokubuildpack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ include = ["src/**/*", "LICENSE", "README.md"]
all-features = true

[features]
default = ["command", "download", "digest", "error", "env", "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 = ["dep:libcnb"]
env_layer = ["dep:libcnb"]
error = ["log", "dep:libcnb"]
log = ["dep:termcolor"]
tar = ["dep:tar", "dep:flate2"]
Expand Down
147 changes: 87 additions & 60 deletions libherokubuildpack/src/env.rs → libherokubuildpack/src/env_layer.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use libcnb::build::BuildContext;
use libcnb::data::layer::LayerName;
use libcnb::data::layer_content_metadata::LayerTypes;
use libcnb::generic::GenericMetadata;
use libcnb::layer::{Layer, LayerResult, LayerResultBuilder};
use libcnb::layer::{Layer, LayerData, LayerResult, LayerResultBuilder};
use libcnb::layer_env::{LayerEnv, ModificationBehavior, Scope};
use libcnb::Buildpack;
use std::ffi::OsString;
use std::marker::PhantomData;
use std::path::Path;

/// Set default environment variables
///
/// If all you need to do is set default environment values, you can use
/// the `DefaultEnvLayer::new` function to set those values without having
/// to create a struct from scratch.
/// the `env_layer::set_default` function to set those values without having
/// to create a struct from scratch. Example:
///
/// ```no_run
///# use libcnb::build::{BuildContext, BuildResult, BuildResultBuilder};
Expand All @@ -27,7 +29,7 @@ use std::path::Path;
/// use libcnb::Env;
/// use libcnb::data::layer_name;
/// use libcnb::layer_env::Scope;
/// use libherokubuildpack::env::DefaultEnvLayer;
/// use libherokubuildpack::env_layer;
///
///# impl Buildpack for HelloWorldBuildpack {
///# type Platform = GenericPlatform;
Expand All @@ -39,32 +41,45 @@ use std::path::Path;
///# }
///
///# fn build(&self, context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
/// // Don't forget to apply context.platform.env() in addition to current envs;
/// let env = Env::from_current();
/// // Don't forget to apply context.platform.env() too;
///
/// let layer = context //
/// .handle_layer(
/// layer_name!("default_env"),
/// DefaultEnvLayer::new(
/// [
/// ("JRUBY_OPTS", "-Xcompile.invokedynamic=false"),
/// ("RACK_ENV", "production"),
/// ("RAILS_ENV", "production"),
/// ("RAILS_SERVE_STATIC_FILES", "enabled"),
/// ("RAILS_LOG_TO_STDOUT", "enabled"),
/// ("MALLOC_ARENA_MAX", "2"),
/// ("DISABLE_SPRING", "1"),
/// ]
/// .into_iter(),
/// ),
/// )?;
/// let layer = env_layer::set_default(&context, layer_name!("default_env"),
/// [
/// ("JRUBY_OPTS", "-Xcompile.invokedynamic=false"),
/// ("RACK_ENV", "production"),
/// ("RAILS_ENV", "production"),
/// ("RAILS_SERVE_STATIC_FILES", "enabled"),
/// ("RAILS_LOG_TO_STDOUT", "enabled"),
/// ("MALLOC_ARENA_MAX", "2"),
/// ("DISABLE_SPRING", "1"),
/// ]
/// .into_iter(),
/// )?;
/// let env = layer.env.apply(Scope::Build, &env);
///
///# todo!()
///# }
///# }
///
/// ```
pub fn set_default<B, E, K, V>(
context: &BuildContext<B>,
layer_name: LayerName,
envs: E,
) -> libcnb::Result<LayerData<GenericMetadata>, <B as Buildpack>::Error>
where
B: Buildpack,
E: IntoIterator<Item = (K, V)> + Clone,
K: Into<OsString>,
V: Into<OsString>,
{
context.handle_layer(layer_name, DefaultEnvLayer::new(envs))
}

/// Set default environment variables in a layer
///
/// This struct is used by the helper function `set_default`. You can also use it directly with
/// with [`BuildContext::handle_layer`] to set default environment variables.
pub struct DefaultEnvLayer;

impl DefaultEnvLayer {
Expand All @@ -91,11 +106,9 @@ impl DefaultEnvLayer {

/// Set environment variables
///
/// If you want to set many default environment variables you can use
/// `DefaultEnvLayer`. If you need to set different types of environment
/// variables you can use this struct `ConfigureEnvLayer`
///
/// Example:
/// If you want to set many default environment variables you can use the
/// `env_layer::set_default` function. If you need to set different types of environment
/// variables you can use the `env_layer::set_envs` function. Example:
///
/// ```no_run
///# use libcnb::build::{BuildContext, BuildResult, BuildResultBuilder};
Expand All @@ -111,7 +124,7 @@ impl DefaultEnvLayer {
/// use libcnb::Env;
/// use libcnb::data::layer_name;
/// use libcnb::layer_env::{LayerEnv, ModificationBehavior, Scope};
/// use libherokubuildpack::env::ConfigureEnvLayer;
/// use libherokubuildpack::env_layer;
///
///# impl Buildpack for HelloWorldBuildpack {
///# type Platform = GenericPlatform;
Expand All @@ -123,47 +136,61 @@ impl DefaultEnvLayer {
///# }
///
///# fn build(&self, context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
/// let env = Env::from_current();
/// // Don't forget to apply context.platform.env() too;
/// let env = Env::from_current();
///
/// let layer = context //
/// .handle_layer(
/// let env = {
/// let layer = env_layer::set_envs(
/// &context,
/// layer_name!("configure_env"),
/// 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"
/// )
/// ),
/// 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"
/// )
/// )?;
/// let env = layer.env.apply(Scope::Build, &env);
/// layer.env.apply(Scope::Build, &env)
/// };
///
///# todo!()
///# }
///# }
///
/// ```
pub fn set_envs<B>(
context: &BuildContext<B>,
layer_name: LayerName,
envs: LayerEnv,
) -> libcnb::Result<LayerData<GenericMetadata>, <B as Buildpack>::Error>
where
B: Buildpack,
{
context.handle_layer(layer_name, ConfigureEnvLayer::new(envs))
}

/// Set custom environment variables in a layer
///
/// This struct is used by the helper function `set_envs`. You can also use it directly with
/// use directly with [`BuildContext::handle_layer`] to set specific environment variables.
pub struct ConfigureEnvLayer<B: libcnb::Buildpack> {
pub(crate) data: LayerEnv,
pub(crate) _buildpack: std::marker::PhantomData<B>,
Expand Down
4 changes: 2 additions & 2 deletions libherokubuildpack/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ pub mod command;
pub mod digest;
#[cfg(feature = "download")]
pub mod download;
#[cfg(feature = "env")]
pub mod env;
#[cfg(feature = "env_layer")]
pub mod env_layer;
#[cfg(feature = "error")]
pub mod error;
#[cfg(feature = "fs")]
Expand Down

0 comments on commit fced2f0

Please sign in to comment.