diff --git a/buildpacks/go/CHANGELOG.md b/buildpacks/go/CHANGELOG.md index a884082..8c02d45 100644 --- a/buildpacks/go/CHANGELOG.md +++ b/buildpacks/go/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- The build cache is now invalidated when the target distribution changes. +- The build cache is no longer invalidated on minor go version changes. + ## [0.3.1] - 2024-05-07 - Added go1.21.10 (linux-amd64), go1.21.10 (linux-arm64), go1.22.3 (linux-amd64), go1.22.3 (linux-arm64). diff --git a/buildpacks/go/src/layers/build.rs b/buildpacks/go/src/layers/build.rs index 3551e7f..326341c 100644 --- a/buildpacks/go/src/layers/build.rs +++ b/buildpacks/go/src/layers/build.rs @@ -1,26 +1,27 @@ use crate::{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, Scope}; -use libcnb::Buildpack; +use libcnb::{Buildpack, Target}; use libherokubuildpack::log::log_info; use serde::{Deserialize, Serialize}; -use sha2::Sha256; use std::fs; use std::path::Path; /// A layer for go incremental build cache artifacts pub(crate) struct BuildLayer { - pub(crate) artifact: Artifact, + pub(crate) go_version: GoVersion, } #[derive(Deserialize, Serialize, Clone, PartialEq)] pub(crate) struct BuildLayerMetadata { layer_version: String, - artifact: Artifact, + go_major_version: GoVersion, + target_arch: String, + target_distro_name: String, + target_distro_version: String, cache_usage_count: f32, } @@ -47,36 +48,31 @@ impl Layer for BuildLayer { fn create( &mut self, - _ctx: &BuildContext, + ctx: &BuildContext, layer_path: &Path, ) -> Result, GoBuildpackError> { log_info("Creating Go build cache"); let cache_dir = layer_path.join(CACHE_DIR); fs::create_dir(&cache_dir).map_err(BuildLayerError)?; - LayerResultBuilder::new(BuildLayerMetadata { - artifact: self.artifact.clone(), - layer_version: LAYER_VERSION.to_string(), - cache_usage_count: 1.0, - }) - .env(LayerEnv::new().chainable_insert( - Scope::Build, - libcnb::layer_env::ModificationBehavior::Override, - CACHE_ENV, - cache_dir, - )) - .build() + LayerResultBuilder::new(self.generate_layer_metadata(&ctx.target, 1.0)) + .env(LayerEnv::new().chainable_insert( + Scope::Build, + libcnb::layer_env::ModificationBehavior::Override, + CACHE_ENV, + cache_dir, + )) + .build() } fn update( &mut self, - _ctx: &BuildContext, + ctx: &BuildContext, layer: &LayerData, ) -> Result, GoBuildpackError> { - LayerResultBuilder::new(BuildLayerMetadata { - artifact: self.artifact.clone(), - layer_version: LAYER_VERSION.to_string(), - cache_usage_count: layer.content_metadata.metadata.cache_usage_count + 1.0, - }) + LayerResultBuilder::new(self.generate_layer_metadata( + &ctx.target, + layer.content_metadata.metadata.cache_usage_count + 1.0, + )) .env(LayerEnv::new().chainable_insert( Scope::Build, libcnb::layer_env::ModificationBehavior::Override, @@ -88,18 +84,39 @@ impl Layer for BuildLayer { fn existing_layer_strategy( &mut self, - _ctx: &BuildContext, + ctx: &BuildContext, layer: &LayerData, ) -> Result::Error> { - let mdata = &layer.content_metadata.metadata; - if mdata.cache_usage_count >= MAX_CACHE_USAGE_COUNT - || mdata.layer_version != LAYER_VERSION - || mdata.artifact != self.artifact - { - log_info("Expired Go build cache"); + let cached_metadata = &layer.content_metadata.metadata; + if cached_metadata.cache_usage_count >= MAX_CACHE_USAGE_COUNT { + log_info("Discarding expired Go build cache"); return Ok(ExistingLayerStrategy::Recreate); } - log_info("Reusing Go build cache"); + let new_metadata = + &self.generate_layer_metadata(&ctx.target, cached_metadata.cache_usage_count); + + if cached_metadata != new_metadata { + log_info("Discarding invalid Go build cache"); + return Ok(ExistingLayerStrategy::Recreate); + } + log_info("Reusing existing Go build cache"); Ok(ExistingLayerStrategy::Update) } } + +impl BuildLayer { + fn generate_layer_metadata( + &self, + target: &Target, + cache_usage_count: f32, + ) -> BuildLayerMetadata { + BuildLayerMetadata { + layer_version: LAYER_VERSION.to_string(), + go_major_version: self.go_version.major_release_version(), + target_arch: target.arch.to_string(), + target_distro_name: target.distro_name.to_string(), + target_distro_version: target.distro_version.to_string(), + cache_usage_count, + } + } +} diff --git a/buildpacks/go/src/main.rs b/buildpacks/go/src/main.rs index 61f44bf..1f67dd6 100644 --- a/buildpacks/go/src/main.rs +++ b/buildpacks/go/src/main.rs @@ -115,7 +115,7 @@ impl Buildpack for GoBuildpack { .handle_layer( layer_name!("go_build"), BuildLayer { - artifact: artifact.clone(), + go_version: artifact.version.clone(), }, )? .env diff --git a/common/go-utils/src/vrs.rs b/common/go-utils/src/vrs.rs index 65cada7..2193ab9 100644 --- a/common/go-utils/src/vrs.rs +++ b/common/go-utils/src/vrs.rs @@ -40,6 +40,21 @@ pub struct GoVersion { semantic_version: semver::Version, } +impl GoVersion { + /// Get the corresponding Go major release. Go identifies major releases + /// as increments to the second identifier (e.g.: 1.16.0, 1.22.0). In + /// semver this corresponds to a change to major and/or minor identifiers. + pub fn major_release_version(&self) -> GoVersion { + let go_major_release = + semver::Version::new(self.semantic_version.major, self.semantic_version.minor, 0); + + GoVersion { + value: go_major_release.to_string(), + semantic_version: go_major_release, + } + } +} + impl Display for GoVersion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.value)