Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to libherokubuildpack inventory #297

Merged
merged 2 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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