Skip to content

Commit

Permalink
Configurable Environment Variables
Browse files Browse the repository at this point in the history
Added a FromStr implementation to debian::MultiarchName
Implment functionality to load the environment variables associate with packages from the project.toml file
use PACKAGE_ENV_VARS constand to map default env vars to be defined if the package is in the project.toml file - these can be overwritten by the values in the project.toml file
define the package_env_vars before the env vars in the project.toml file
update README to reflect the PACKAGE_ENV_VARS

testing the buildpack, added a variable for skipped_packages.  Will incorporate that into install_packages

move the env logic into configure_layer_environment.  Iterate through the skipped_packages to make sure all the env vars are set and the post install command are run

flushed out a test in order to accomindate the new parameters passed to configure_layer_environment

finished the test for skipped packages

update the unit test to use a fixture to create the env correctly

use  a fixture for the test project.toml file; update the README
  • Loading branch information
tlhmerry0098 committed Jan 10, 2025
1 parent 6f8a70e commit 933ea9d
Show file tree
Hide file tree
Showing 17 changed files with 990 additions and 152 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ reqwest-retry = "0.7"
serde = { version = "1", features = ["derive"] }
sequoia-openpgp = { version = "1", default-features = false, features = ["crypto-rust", "allow-experimental-crypto", "allow-variable-time-crypto"] }
sha2 = "0.10"
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "time"] }
tempfile = "3"
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "time", "macros"] }
tokio-tar = "0.3"
tokio-util = { version = "0.7", default-features = false, features = ["compat", "io"] }
toml_edit = "0.22"
walkdir = "2"
libcnb-package = "0.26.0"

[dev-dependencies]
libcnb-package = "0.26.0"
libcnb-test = "=0.26.0"
regex = "1"
strip-ansi-escapes = "0.2"
tempfile = "3"
buildpacks-deb-packages = { path = "." }

[lints.rust]
Expand Down
64 changes: 59 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@ The configuration for this buildpack must be added to the project descriptor fil
project using the `com.heroku.buildpacks.deb-packages` table. The list of packages to install must be
specified there. See below for the [configuration schema](#schema) and an [example](#example).

### Configuring Environment Variables

You can configure environment variables for the packages installed by this buildpack by defining them in the `project.toml` file. The environment variables are specified under the `env` key for each package. There can be more than one environment variable defined for each package.

During the build process, the buildpack will read the `project.toml` file and apply the specified environment variables. The `{install_dir}` placeholder will be replaced with the actual paths so the variables are available at both `build` and `launch` phases using [layer environment variables][cnb-environment].

### Default Package Environment Variables
The buildpack includes a set of default environment variables for each package, known as `PACKAGE_ENV_VARS`. These default environment variables are applied during the build process. However, you can override these default values by specifying environment variables in the project.toml file.

If a package is listed in the project.toml file with environment variables under the env key, those variables will take precedence over the default `PACKAGE_ENV_VARS`.

### Executing Commands After Package Installation

You can specify commands to be executed after the installation of each package by defining them in the project.toml file under the commands key for each package. These commands will be executed in the order they are listed.

## Environment Variables and Post-Install Commands for Skipped Packages

Even if a package is skipped, the environment variables and post-install commands defined for that package will still be applied and executed. This ensures that any necessary configuration or setup steps are performed, even if the package itself is not installed.

#### Example

```toml
Expand All @@ -61,10 +80,20 @@ schema-version = "0.2"
# buildpack configuration goes here
[com.heroku.buildpacks.deb-packages]
install = [
# string version of a dependency to install
"package-name",
# inline-table version of a dependency to install
{ name = "package-name", skip_dependencies = true, force = true }
# basic package with some dependencies
"libgwenhywfar79",
# child package of "libgwenhywfar79" so we should get a warning that it was already installed by the previous entry
"libgwenhywfar-data",
# package with child dependencies skipped so no "libxmlsec1" or "libxmlsec1-openssl" will be installed
{ name = "xmlsec1", skip_dependencies = true },
# a package already installed on the system
"wget",
# libvips is a virtual package which is only provided by libvips42 so no need to halt and ask the user which implementing package to install
"libvips",
# curl is already on the system so we're going to force it to be installed
{ name = "curl", force = true },
# git needs to have environment variables set and post installation commands run
{ name = "git", env = {"GIT_EXEC_PATH" = "{install_dir}/usr/lib/git-core", "GIT_TEMPLATE_DIR" = "{install_dir}/usr/share/git-core/templates"}, commands = ["echo 'Git installed successfully'", "git --version"]},
]
```

Expand Down Expand Up @@ -97,6 +126,14 @@ install = [

If set to `true`, the package will be installed even if it's already installed on the system.

- `env` *__([inline-table][toml-inline-table], optional, default={})__*

A table of environment variables to set for the package. The keys are the variable names and the values are the variable values. The `{build_dir}` placeholder can be used in the values and will be replaced with the actual build directory path.

- `commands` *__([array][toml-array], optional, default=[])__*

A list of commands to execute after the package is installed. The commands will be executed in the order they are listed.

> [!TIP]
> Users of the [heroku-community/apt][classic-apt-buildpack] can migrate their Aptfile to the above configuration by
> adding a `project.toml` file with:
Expand Down Expand Up @@ -199,9 +236,9 @@ For each package added after [determining the packages to install](#step-2-deter
| `INCLUDE_PATH` | `/<layer_dir>/usr/include/<arch>` <br> `/<layer_dir>/usr/include` | header files |
| `CPATH` | Same as `INCLUDE_PATH` | header files |
| `CPPPATH` | Same as `INCLUDE_PATH` | header files |
| `PKG_CONFIG_PATH` | `/<layer_dir>/usr/lib/<arch>/pkgconfig` <br> `/<layer_dir>/usr/lib/pkgconfig` | pc files |
| `GIT_EXEC_PATH` | `/<layer_dir>/app/.apt/usr/lib/git-core` | git files |
## Contributing
Issues and pull requests are welcome. See our [contributing guidelines](./CONTRIBUTING.md) if you would like to help.
Expand Down Expand Up @@ -260,3 +297,20 @@ Issues and pull requests are welcome. See our [contributing guidelines](./CONTRI
[toml-table]: https://toml.io/en/v1.0.0#table
## Testing Locally
To test the project locally, follow these steps:
### Prerequisites
Ensure you have the following installed:
- Rust and Cargo: [Installation Guide](https://www.rust-lang.org/tools/install)
- Docker (if applicable for your tests)
- `cargo install libcnb-cargo`
### Building the Project
`cargo build` to build the project
`cargo test` to run the automated tests
`cargo test --test integration_test` to run integration tests
`cargo libcnb package` builds an image of the buildpack that can be used with an application. The output of this command shows usage of the generated image.
5 changes: 3 additions & 2 deletions project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ schema-version = "0.2"

[com.heroku.buildpacks.deb-packages]
install = [
"git",
"systemd-cron"
{ name = "git", env = {"GIT_EXEC_PATH" = "{install_dir}/usr/lib/git-core", "GIT_TEMPLATE_DIR" = "{install_dir}/usr/share/git-core/templates"}, commands = ["echo 'Git installed successfully'", "git --version"]},
{ name = "babeld" },
{ name = "ghostscript", skip_dependencies = true, force = true, env = {"GS_LIB" = "{install_dir}/var/lib/ghostscript"}, commands = ["echo 'Ghostscript installed successfully'", "gs --version"]},
]
142 changes: 117 additions & 25 deletions src/config/buildpack_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,46 +108,138 @@ impl From<ConfigError> for libcnb::Error<DebianPackagesBuildpackError> {
#[cfg(test)]
mod test {
use crate::debian::PackageName;
use std::collections::HashMap;
use indexmap::IndexSet;
use std::str::FromStr;

use super::*;

#[test]
fn test_deserialize() {
fn test_deserialize_basic() {
let toml = r#"
[_]
schema-version = "0.2"
[com.heroku.buildpacks.deb-packages]
install = [
"package1",
{ name = "package2" },
{ name = "package3", skip_dependencies = true, force = true },
{ name = "package2", env = {"ENV_VAR_1" = "VALUE_1"}, commands = ["command1", "command2"] },
{ name = "package3", skip_dependencies = true, force = true, env = {"ENV_VAR_2" = "VALUE_2", "ENV_VAR_3" = "VALUE_3"}, commands = ["command3"] },
]
"#
.trim();

let config = BuildpackConfig::from_str(toml).unwrap();
assert_eq!(
config,
BuildpackConfig {
install: IndexSet::from([
RequestedPackage {
name: PackageName::from_str("package1").unwrap(),
skip_dependencies: false,
force: false,
},
RequestedPackage {
name: PackageName::from_str("package2").unwrap(),
skip_dependencies: false,
force: false,
},
RequestedPackage {
name: PackageName::from_str("package3").unwrap(),
skip_dependencies: true,
force: true,
}
])
}
);

let expected_config = BuildpackConfig {
install: IndexSet::from([
RequestedPackage {
name: PackageName::from_str("package1").unwrap(),
skip_dependencies: false,
force: false,
env: None,
commands: vec![],
},
RequestedPackage {
name: PackageName::from_str("package2").unwrap(),
skip_dependencies: false,
force: false,
env: Some(HashMap::from([
("ENV_VAR_1".to_string(), "VALUE_1".to_string()),
])),
commands: vec!["command1".to_string(), "command2".to_string()],
},
RequestedPackage {
name: PackageName::from_str("package3").unwrap(),
skip_dependencies: true,
force: true,
env: Some(HashMap::from([
("ENV_VAR_2".to_string(), "VALUE_2".to_string()),
("ENV_VAR_3".to_string(), "VALUE_3".to_string()),
])),
commands: vec!["command3".to_string()],
},
]),
};

assert_eq!(config, expected_config);
}

#[test]
fn test_deserialize_with_env() {
let toml = r#"
[_]
schema-version = "0.2"
[com.heroku.buildpacks.deb-packages]
install = [
{ name = "package1", env = {"ENV_VAR_1" = "VALUE_1"} },
]
"#
.trim();

let config = BuildpackConfig::from_str(toml).unwrap();

let expected_config = BuildpackConfig {
install: IndexSet::from([
RequestedPackage {
name: PackageName::from_str("package1").unwrap(),
skip_dependencies: false,
force: false,
env: Some(HashMap::from([("ENV_VAR_1".to_string(), "VALUE_1".to_string())])),
commands: vec![],
},
]),
};

assert_eq!(config, expected_config);
}

#[test]
fn test_deserialize_with_commands() {
let toml = r#"
[_]
schema-version = "0.2"
[com.heroku.buildpacks.deb-packages]
install = [
{ name = "package1", commands = ["echo 'Hello, world!'"] },
]
"#
.trim();

let config = BuildpackConfig::from_str(toml).unwrap();

let expected_config = BuildpackConfig {
install: IndexSet::from([
RequestedPackage {
name: PackageName::from_str("package1").unwrap(),
skip_dependencies: false,
force: false,
env: None,
commands: vec!["echo 'Hello, world!'".to_string()],
},
]),
};

assert_eq!(config, expected_config);
}

#[test]
fn test_deserialize_invalid_config() {
let toml = r#"
[_]
schema-version = "0.2"
[com.heroku.buildpacks.deb-packages]
install = [
{ name = 123 },
]
"#
.trim();

let result = BuildpackConfig::from_str(toml);
assert!(result.is_err());
}

#[test]
Expand Down
Loading

0 comments on commit 933ea9d

Please sign in to comment.