$ cat Cargo.toml
[package]
name = "first-path-buildpack-rust"
version = "0.1.0"
edition = "2021"
[dependencies]
libcnb = "0.26.1"
$ cat buildpack.toml
api = "0.10"
[buildpack]
id = "libcnb/first"
version = "0.1.0"
name = "My Buildpack"
use libcnb::build::{BuildContext, BuildResult, BuildResultBuilder};
use libcnb::data::launch::LaunchBuilder;
use libcnb::data::layer_name;
use libcnb::detect::{DetectContext, DetectResult, DetectResultBuilder};
use libcnb::generic::{GenericError, GenericMetadata, GenericPlatform};
use libcnb::layer::UncachedLayerDefinition;
use libcnb::layer_env::{LayerEnv, ModificationBehavior, Scope};
use libcnb::{buildpack_main, Buildpack, Platform};
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
pub(crate) struct HelloWorldBuildpack;
impl Buildpack for HelloWorldBuildpack {
// The CNB platform this buildpack targets, usually `GenericPlatform`. See the CNB spec for
// more information about platforms:
// https://github.com/buildpacks/spec/blob/main/buildpack.md
type Platform = GenericPlatform;
// The type for the metadata of the buildpack itself. This is the data found in the
// `[metadata]` section of your buildpack's `buildpack.toml`. The framework will automatically
// try to parse it into the specified type. This example buildpack uses GenericMetadata which
// provides low-level access to the TOML table.
type Metadata = GenericMetadata;
// The error type for this buildpack. Buildpack authors usually implement an enum with
// specific errors that can happen during buildpack execution. This error type should
// only contain error specific to this buildpack, such as `CouldNotExecuteMaven` or
// `InvalidGemfileLock`. This example buildpack uses `GenericError` which means this buildpack
// does not specify any errors.
//
// Common errors that happen during buildpack execution such as I/O errors while
// writing CNB TOML files are handled by libcnb.rs itself.
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 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.
//
// One example of this is the return type of this method. `DetectResult` encapsulates the
// required exit code as well as the data written to the build plan. libcnb.rs will,
// according to the returned value, handle both writing the build plan and exiting with
// the correct status code for you.
fn detect(&self, _context: DetectContext<Self>) -> libcnb::Result<DetectResult, Self::Error> {
DetectResultBuilder::pass().build()
}
// Similar to detect, this method will be called when the CNB lifecycle executes the
// build phase (`bin/build`).
fn build(&self, context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
println!("Hello World!");
println!(
"The build is running on: {} ({})!",
context.target.os, context.target.arch
);
let layer_ref = context.uncached_layer(
layer_name!("0001_first"),
UncachedLayerDefinition {
build: true,
launch: true,
},
)?;
std::fs::create_dir_all(layer_ref.path().join("bin")).unwrap();
std::fs::write(layer_ref.path().join("bin").join("lol"), "").unwrap();
chmod_plus_x(&layer_ref.path().join("bin").join("lol")).unwrap();
layer_ref.write_env(
LayerEnv::new()
.chainable_insert(Scope::All, ModificationBehavior::Delimiter, "PATH", ":")
.chainable_insert(
Scope::All,
ModificationBehavior::Prepend,
"PATH",
"/manually_set_path_prepend/bin",
),
)?;
let env = layer_ref
.read_env()?
.apply(Scope::Build, context.platform.env());
println!("PATH={:?}", env.get("PATH").unwrap());
BuildResultBuilder::new()
.launch(LaunchBuilder::new().build())
.build()
}
}
fn chmod_plus_x(path: &Path) -> Result<(), std::io::Error> {
let mut perms = std::fs::metadata(path)?.permissions();
let mut mode = perms.mode();
mode |= 0o700;
perms.set_mode(mode);
std::fs::set_permissions(path, perms)
}
// Implements the main function and wires up the framework for the given buildpack.
buildpack_main!(HelloWorldBuildpack);
$ cargo libcnb package
🚚 Preparing package directory...
🖥️ Gathering Cargo configuration (for x86_64-unknown-linux-musl)
🏗️ Building buildpack dependency graph...
🔀 Determining build order...
🚚 Building 1 buildpacks...
📦 [1/1] Building libcnb/first (./)
Finished `dev` profile [unoptimized] target(s) in 0.02s
Successfully wrote buildpack directory: packaged/x86_64-unknown-linux-musl/debug/libcnb_first (4.89 MiB)
✨ Packaging successfully finished!
💡 To test your buildpack locally with pack, run:
pack build my-image-name \
--buildpack packaged/x86_64-unknown-linux-musl/debug/libcnb_first \
--trust-extra-buildpacks \
--path /path/to/application
/private/tmp/ba6c8b19618d86f52e85c8c8896d5a9e/first-path-buildpack-rust/packaged/x86_64-unknown-linux-musl/debug/libcnb_first
$ pack build debugging-app --path . --buildpack /private/tmp/ba6c8b19618d86f52e85c8c8896d5a9e/first-path-buildpack-rust/packaged/x86_64-unknown-linux-musl/debug/libcnb_first --buildpack ./echo-path-buildpack --platform linux/amd64
24: Pulling from heroku/builder
Digest: sha256:770cbacea87ae3e06c3773723015b03706f98f78b31be4fd1b963c9ba861eb04
Status: Image is up to date for heroku/builder:24
24: Pulling from heroku/heroku
Digest: sha256:9483dc324ebd0615f936808da8da14c401c790f96b13a331208c4513a57c2051
Status: Image is up to date for heroku/heroku:24
Warning: Builder is trusted but additional modules were added; using the untrusted (5 phases) build flow
0.20.5: Pulling from buildpacksio/lifecycle
Digest: sha256:3274fe28594f484240fc3f17e46b7b45e304830e544dcdd82ce19472d159ef71
Status: Image is up to date for buildpacksio/lifecycle:0.20.5
===> ANALYZING
[analyzer] Restoring data for SBOM from previous image
===> DETECTING
[detector] libcnb/first 0.1.0
[detector] examples/echo-path 0.0.1
===> RESTORING
===> BUILDING
[builder] Hello World!
[builder] The build is running on: linux (amd64)!
[builder] read_env: PATH="/layers/libcnb_first/0001_first/bin:/manually_set_path_prepend/bin"
[builder] PATH=/manually_set_path_prepend/bin:/layers/libcnb_first/0001_first/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
===> EXPORTING
[exporter] Reusing layer 'libcnb/first:0001_first'
[exporter] Reusing layer 'buildpacksio/lifecycle:launch.sbom'
[exporter] Reused 1/1 app layer(s)
[exporter] Reusing layer 'buildpacksio/lifecycle:launcher'
[exporter] Reusing layer 'buildpacksio/lifecycle:config'
[exporter] Adding label 'io.buildpacks.lifecycle.metadata'
[exporter] Adding label 'io.buildpacks.build.metadata'
[exporter] Adding label 'io.buildpacks.project.metadata'
[exporter] no default process type
[exporter] Saving debugging-app...
[exporter] *** Images (9270e5dbb797):
[exporter] debugging-app
Successfully built image debugging-app