Skip to content

Commit

Permalink
Migrate to libherokubuildpack inventory (#297)
Browse files Browse the repository at this point in the history
* Migrate to `libherokubuildpack` inventory

Bump `libherokubuildpack` to version 0.24.0

* Delete inventory-utils
  • Loading branch information
runesoerensen authored Nov 1, 2024
1 parent 864029b commit 621fc27
Show file tree
Hide file tree
Showing 15 changed files with 79 additions and 596 deletions.
34 changes: 8 additions & 26 deletions Cargo.lock

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

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ resolver = "2"
members = [
"buildpacks/go",
"common/go-utils",
"common/inventory-utils",
]

[workspace.package]
Expand Down
3 changes: 1 addition & 2 deletions buildpacks/go/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ workspace = true

[dependencies]
heroku-go-utils = { path = "../../common/go-utils" }
heroku-inventory-utils = { path = "../../common/inventory-utils" }
hex = "0.4.3"
flate2 = { version = "1", default-features = false, features = ["zlib"] }
# libcnb has a much bigger impact on buildpack behaviour than any other dependencies,
# so it's pinned to an exact version to isolate it from lockfile refreshes.
libcnb = { version = "=0.21.0", features = ["trace"] }
libherokubuildpack = { version = "=0.21.0", default-features = false, features = ["log"] }
libherokubuildpack = { version = "=0.24.0", default-features = false, features = ["inventory", "log"] }
semver = "1"
serde = "1"
sha2 = "0.10"
Expand Down
15 changes: 9 additions & 6 deletions buildpacks/go/src/layers/dist.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
use crate::{tgz, GoBuildpack, GoBuildpackError};
use heroku_go_utils::vrs::GoVersion;
use heroku_inventory_utils::inv::Artifact;
use libcnb::build::BuildContext;
use libcnb::data::layer_content_metadata::LayerTypes;
use libcnb::layer::{ExistingLayerStrategy, Layer, LayerData, LayerResult, LayerResultBuilder};
use libcnb::layer_env::{LayerEnv, ModificationBehavior, Scope};
use libcnb::Buildpack;
use libherokubuildpack::inventory::artifact::Artifact;
use libherokubuildpack::log::log_info;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use std::path::Path;

/// A layer that downloads and installs the Go distribution artifacts
pub(crate) struct DistLayer {
pub(crate) artifact: Artifact<GoVersion, Sha256>,
pub(crate) artifact: Artifact<GoVersion, Sha256, Option<()>>,
}

#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
pub(crate) struct DistLayerMetadata {
layer_version: String,
artifact: Artifact<GoVersion, Sha256>,
artifact: Artifact<GoVersion, Sha256, Option<()>>,
}

#[derive(thiserror::Error, Debug)]
Expand Down Expand Up @@ -48,8 +48,8 @@ impl Layer for DistLayer {
layer_path: &Path,
) -> Result<LayerResult<Self::Metadata>, GoBuildpackError> {
log_info(format!(
"Installing {} from {}",
self.artifact, self.artifact.url
"Installing {} ({}-{}) from {}",
self.artifact.version, self.artifact.os, self.artifact.arch, self.artifact.url
));
tgz::fetch_strip_filter_extract_verify(
&self.artifact,
Expand Down Expand Up @@ -84,7 +84,10 @@ impl Layer for DistLayer {
layer_data: &LayerData<Self::Metadata>,
) -> Result<ExistingLayerStrategy, <Self::Buildpack as Buildpack>::Error> {
if layer_data.content_metadata.metadata == DistLayerMetadata::current(self) {
log_info(format!("Reusing {}", self.artifact));
log_info(format!(
"Reusing {} ({}-{})",
self.artifact.version, self.artifact.os, self.artifact.arch
));
Ok(ExistingLayerStrategy::Keep)
} else {
Ok(ExistingLayerStrategy::Recreate)
Expand Down
12 changes: 8 additions & 4 deletions buildpacks/go/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ mod proc;
mod tgz;

use heroku_go_utils::vrs::GoVersion;
use heroku_inventory_utils::inv::{resolve, Arch, Inventory, Os};
use layers::build::{BuildLayer, BuildLayerError};
use layers::deps::{DepsLayer, DepsLayerError};
use layers::dist::{DistLayer, DistLayerError};
Expand All @@ -19,6 +18,8 @@ use libcnb::generic::GenericMetadata;
use libcnb::generic::GenericPlatform;
use libcnb::layer_env::Scope;
use libcnb::{buildpack_main, Buildpack, Env};
use libherokubuildpack::inventory::artifact::{Arch, Os};
use libherokubuildpack::inventory::Inventory;
use libherokubuildpack::log::{log_error, log_header, log_info};
use sha2::Sha256;
use std::env::{self, consts};
Expand Down Expand Up @@ -63,7 +64,7 @@ impl Buildpack for GoBuildpack {
go_env.insert(k, v);
});

let inv: Inventory<GoVersion, Sha256> =
let inv: Inventory<GoVersion, Sha256, Option<()>> =
toml::from_str(INVENTORY).map_err(GoBuildpackError::InventoryParse)?;

let config = cfg::read_gomod_config(context.app_dir.join("go.mod"))
Expand All @@ -72,12 +73,15 @@ impl Buildpack for GoBuildpack {
log_info(format!("Detected Go version requirement: {requirement}"));

let artifact = match (consts::OS.parse::<Os>(), consts::ARCH.parse::<Arch>()) {
(Ok(os), Ok(arch)) => resolve(inv.artifacts.as_slice(), os, arch, &requirement),
(Ok(os), Ok(arch)) => inv.resolve(os, arch, &requirement),
(_, _) => None,
}
.ok_or(GoBuildpackError::VersionResolution(requirement.clone()))?;

log_info(format!("Resolved Go version: {artifact}"));
log_info(format!(
"Resolved Go version: {} ({}-{})",
artifact.version, artifact.os, artifact.arch
));

log_header("Installing Go distribution");
go_env = context
Expand Down
20 changes: 10 additions & 10 deletions buildpacks/go/src/tgz.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use flate2::read::GzDecoder;
use libherokubuildpack::inventory::artifact::Artifact;
use sha2::{
digest::{generic_array::GenericArray, OutputSizeUser},
Digest,
};
use std::{fs, io::Read, path::StripPrefixError};
use tar::Archive;

use heroku_inventory_utils::inv::Artifact;

#[derive(thiserror::Error, Debug)]
pub(crate) enum Error {
#[error("HTTP error while fetching archive: {0}")]
Expand Down Expand Up @@ -44,7 +43,7 @@ pub(crate) enum Error {
///
/// See `Error` for an enumeration of error scenarios.
pub(crate) fn fetch_strip_filter_extract_verify<'a, D: Digest, V>(
artifact: &Artifact<V, D>,
artifact: &Artifact<V, D, Option<()>>,
strip_prefix: impl AsRef<str>,
filter_prefixes: impl Iterator<Item = &'a str>,
dest_dir: impl AsRef<std::path::Path>,
Expand Down Expand Up @@ -113,25 +112,26 @@ impl<R: Read, H: sha2::Digest> Read for DigestingReader<R, H> {

#[cfg(test)]
mod tests {
use heroku_inventory_utils::{
use libherokubuildpack::inventory::{
artifact::{Arch, Os},
checksum::Checksum,
inv::{Arch, Os},
};
use sha2::Sha256;

use super::*;

#[test]
fn test_fetch_strip_filter_extract_verify() {
let artifact = Artifact::<String, Sha256> {
let artifact = Artifact::<String, Sha256, Option<()>> {
version: "0.0.1".to_string(),
os: Os::Linux,
arch: Arch::Amd64,
url: "https://mirrors.edge.kernel.org/pub/software/scm/git/git-0.01.tar.gz".to_string(),
checksum: Checksum::try_from(
"9bdf8a4198b269c5cbe4263b1f581aae885170a6cb93339a2033cb468e57dcd3".to_string(),
)
.unwrap(),
checksum: "sha256:9bdf8a4198b269c5cbe4263b1f581aae885170a6cb93339a2033cb468e57dcd3"
.to_string()
.parse::<Checksum<Sha256>>()
.unwrap(),
metadata: None,
};
let dest = tempfile::tempdir().expect("Couldn't create test tmpdir");

Expand Down
2 changes: 1 addition & 1 deletion common/go-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ serde = { version = "1", features = ["derive"] }
thiserror = "1"
toml = "0.8"
ureq = { version = "2", features = ["json"] }
heroku-inventory-utils = { path = "../../common/inventory-utils" }
libherokubuildpack = { version = "=0.24.0", default-features = false, features = ["inventory", "log", "inventory-sha2"] }
42 changes: 26 additions & 16 deletions common/go-utils/src/bin/update_inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
#![allow(unused_crate_dependencies)]

use heroku_go_utils::{inv::list_upstream_artifacts, vrs::GoVersion};
use heroku_inventory_utils::inv::{read_inventory_file, Artifact, Inventory};
use libherokubuildpack::inventory::{artifact::Artifact, Inventory};
use sha2::Sha256;
use std::{collections::HashSet, env, fs, process};
use std::{env, fs, process};

/// Updates the local go inventory.toml with versions published on go.dev.
fn main() {
Expand All @@ -13,15 +13,17 @@ fn main() {
process::exit(2);
});

let inventory_artifacts: HashSet<Artifact<GoVersion, Sha256>> =
read_inventory_file(&inventory_path)
.unwrap_or_else(|e| {
eprintln!("Error reading inventory at '{inventory_path}': {e}");
std::process::exit(1);
})
.artifacts
.into_iter()
.collect();
let inventory_artifacts = fs::read_to_string(&inventory_path)
.unwrap_or_else(|e| {
eprintln!("Error reading inventory at '{inventory_path}': {e}");
std::process::exit(1);
})
.parse::<Inventory<GoVersion, Sha256, Option<()>>>()
.unwrap_or_else(|e| {
eprintln!("Error parsing inventory file at '{inventory_path}': {e}");
process::exit(1);
})
.artifacts;

// List available upstream release versions.
let remote_artifacts = list_upstream_artifacts().unwrap_or_else(|e| {
Expand All @@ -43,25 +45,33 @@ fn main() {
process::exit(7);
});

let remote_artifacts: HashSet<Artifact<GoVersion, Sha256>> =
let remote_artifacts: Vec<Artifact<GoVersion, Sha256, Option<()>>> =
inventory.artifacts.into_iter().collect();

[
("Added", &remote_artifacts - &inventory_artifacts),
("Removed", &inventory_artifacts - &remote_artifacts),
("Added", difference(&remote_artifacts, &inventory_artifacts)),
(
"Removed",
difference(&inventory_artifacts, &remote_artifacts),
),
]
.iter()
.filter(|(_, artifact_diff)| !artifact_diff.is_empty())
.for_each(|(action, artifacts)| {
let mut list: Vec<&Artifact<GoVersion, Sha256>> = artifacts.iter().collect();
let mut list: Vec<_> = artifacts.iter().collect();
list.sort_by_key(|a| &a.version);
println!(
"{} {}.",
action,
list.iter()
.map(ToString::to_string)
.map(|artifact| format!("{} ({}-{})", artifact.version, artifact.os, artifact.arch))
.collect::<Vec<_>>()
.join(", ")
);
});
}

/// Finds the difference between two slices.
fn difference<'a, T: Eq>(a: &'a [T], b: &'a [T]) -> Vec<&'a T> {
a.iter().filter(|&artifact| !b.contains(artifact)).collect()
}
Loading

0 comments on commit 621fc27

Please sign in to comment.