Repository: buildpacks/lifecycle
Pull request #1346 (2024-04-23 12:29:29)

Restore cached launch layers not found in `appLayers`
<!-- 🎉🎉🎉 Thank you for the PR!!! 🎉🎉🎉 -->

### Summary
<!-- Please describe your changes at a high level. -->

In case if a layer is found in cache and marked as `launch = true` and `cache = true`, it will be restored if not found in the `appLayers`.

This case is possible if `-previous-image` is not set/found, however a `-cache-dir` is provided (backed by persistent volumes etc.)

#### Release notes
<!-- Please provide 1-2 sentences for release notes. -->
<!-- Example: When using platform API `0.7` or greater, the `creator` logs the expected phase header for the analyze phase -->



---

### Related
<!-- If this PR addresses an issue, please provide the issue number below. -->

Resolves #___

---

### Context
<!-- Add any other context that may help reviewers (e.g., code that requires special attention, etc.). -->



loewenstein: @natalieparellano @jabrown85 Did you have a chance to take a look?
natalieparellano: @loewenstein taking a look...

I hate to be that person, but I think this may require a spec change, as the spec is actually quite specific about where the layer metadata comes from: https://github.com/buildpacks/spec/blob/main/buildpack.md#layer-types

That said, having been around when this table was added, I believe its value is simply to clarify how the lifecycle works, as even CNB contributors could not keep track of this behavior. I don't think buildpack authors (or platform authors for that matter) care or need to care where this metadata comes from.
natalieparellano: @pbusko I think if you rebase onto the latest main that will fix the Codecov issue
pbusko: @natalieparellano unfortunately after rebase the unit tests started to fail on windows platform (is the issue is more general, since #1347 experiences it as well?)

The PR to the spec: https://github.com/buildpacks/spec/pull/406
natalieparellano: @pbusko that looks like a flake. We can re-kick it

> I hate to be that person, but I think this may require a spec change, as the spec is actually quite specific about where the layer metadata comes from: https://github.com/buildpacks/spec/blob/main/buildpack.md#layer-types

> That said, having been around when this table was added, I believe its value is simply to clarify how the lifecycle works, as even CNB contributors could not keep track of this behavior. I don't think buildpack authors (or platform authors for that matter) care or need to care where this metadata comes from (maybe we could just update the table to remove that additional detail).

We talked about this in Working Group today. We agreed that (1) the current level of detail can be removed from the spec, and (2) it's still a behavior change for our platform users and would be better if gated behind a Platform API version bump.

Would you be amenable to this? ^^

Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6f54ffb2..06763f68 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -200,7 +200,7 @@ jobs:
         with:
           name: lifecycle-bom-cdx-sha256
           path: lifecycle-v*-bom.cdx.json.sha256
-      - uses: azure/docker-login@v1
+      - uses: azure/docker-login@v2
         if: github.event_name == 'push'
         with:
           username: ${{ secrets.DOCKER_USERNAME }}
diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml
index 797b0b2f..5112a21e 100644
--- a/.github/workflows/post-release.yml
+++ b/.github/workflows/post-release.yml
@@ -22,7 +22,7 @@ jobs:
           go install github.com/google/go-containerregistry/cmd/crane@latest
       - name: Install cosign
         uses: sigstore/cosign-installer@v3
-      - uses: azure/docker-login@v1
+      - uses: azure/docker-login@v2
         with:
           username: ${{ secrets.DOCKER_USERNAME }}
           password: ${{ secrets.DOCKER_PASSWORD }}
diff --git a/api/apis.go b/api/apis.go
index e46a0470..821df457 100644
--- a/api/apis.go
+++ b/api/apis.go
@@ -11,7 +11,7 @@ var (
 	// Platform is a pair of lists of Platform API versions:
 	// 1. All supported versions (including deprecated versions)
 	// 2. The versions that are deprecated
-	Platform = newApisMustParse([]string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}, []string{})
+	Platform = newApisMustParse([]string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13", "0.14"}, []string{})
 	// Buildpack is a pair of lists of Buildpack API versions:
 	// 1. All supported versions (including deprecated versions)
 	// 2. The versions that are deprecated
diff --git a/buildpack/bp_descriptor.go b/buildpack/bp_descriptor.go
index 18fa6c60..57c58258 100644
--- a/buildpack/bp_descriptor.go
+++ b/buildpack/bp_descriptor.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/BurntSushi/toml"
 
+	"github.com/buildpacks/lifecycle/api"
 	"github.com/buildpacks/lifecycle/internal/encoding"
 )
 
@@ -17,7 +18,7 @@ type BpDescriptor struct {
 	Order       Order            `toml:"order"`
 	WithRootDir string           `toml:"-"`
 	Targets     []TargetMetadata `toml:"targets"`
-	Stacks      []StackMetadata  `tome:"stacks"` // just for backwards compat so we can check if it's the bionic stack, which we translate to a target
+	Stacks      []StackMetadata  `toml:"stacks"` // just for backwards compat so we can check if it's the bionic stack, which we translate to a target
 
 }
 
@@ -69,7 +70,9 @@ func ReadBpDescriptor(path string) (*BpDescriptor, error) {
 	if len(descriptor.Targets) == 0 {
 		for _, stack := range descriptor.Stacks {
 			if stack.ID == "io.buildpacks.stacks.bionic" {
-				descriptor.Targets = append(descriptor.Targets, TargetMetadata{OS: "linux", Arch: "amd64", Distros: []OSDistro{{Name: "ubuntu", Version: "18.04"}}})
+				if api.MustParse(descriptor.API()).AtLeast("0.10") || len(descriptor.Stacks) == 1 {
+					descriptor.Targets = append(descriptor.Targets, TargetMetadata{OS: "linux", Arch: "amd64", Distros: []OSDistro{{Name: "ubuntu", Version: "18.04"}}})
+				}
 			} else if stack.ID == "*" {
 				descriptor.Targets = append(descriptor.Targets, TargetMetadata{}) // matches any
 			}
diff --git a/cmd/lifecycle/restorer.go b/cmd/lifecycle/restorer.go
index dc2bfc65..e0798dbf 100644
--- a/cmd/lifecycle/restorer.go
+++ b/cmd/lifecycle/restorer.go
@@ -50,6 +50,9 @@ func (r *restoreCmd) DefineFlags() {
 		cli.FlagBuildImage(&r.BuildImageRef)
 	}
 	cli.FlagAnalyzedPath(&r.AnalyzedPath)
+	if r.PlatformAPI.AtLeast("0.14") {
+		cli.FlagRunPath(&r.RunPath)
+	}
 	cli.FlagCacheDir(&r.CacheDir)
 	cli.FlagCacheImage(&r.CacheImageRef)
 	cli.FlagGID(&r.GID)
@@ -123,6 +126,15 @@ func (r *restoreCmd) Exec() error {
 			runImage imgutil.Image
 		)
 		runImageName := analyzedMD.RunImageImage() // FIXME: if we have a digest reference available in `Reference` (e.g., in the non-daemon case) we should use it
+		accessibleRunImage, err := r.runImageAccessCheck(runImageName)
+		if err != nil {
+			return err
+		}
+		if runImageName != accessibleRunImage {
+			analyzedMD.RunImage.Image = accessibleRunImage
+			analyzedMD.RunImage.Reference = accessibleRunImage
+		}
+
 		if r.supportsRunImageExtension() && needsPulling(analyzedMD.RunImage) {
 			cmd.DefaultLogger.Debugf("Pulling run image metadata for %s...", runImageName)
 			runImage, err = r.pullSparse(runImageName)
@@ -192,6 +204,23 @@ func needsPulling(runImage *files.RunImage) bool {
 	return runImage.Extend
 }
 
+func (r *restoreCmd) runImageAccessCheck(runImageName string) (string, error) {
+	if r.PlatformAPI.LessThan("0.14") {
+		return runImageName, nil
+	}
+
+	runToml, err := files.Handler.ReadRun(r.RunPath, cmd.DefaultLogger)
+	if err != nil {
+		return "", err
+	}
+
+	if !runToml.Contains(runImageName) {
+		return runImageName, nil
+	}
+
+	return platform.BestRunImageMirrorFor("", runToml.FindByRef(runImageName), r.AccessChecker())
+}
+
 func (r *restoreCmd) needsUpdating(runImage *files.RunImage, group buildpack.Group) bool {
 	if r.PlatformAPI.LessThan("0.10") {
 		return false
@@ -276,7 +305,7 @@ func (r *restoreCmd) restore(layerMetadata files.LayersMetadata, group buildpack
 		Buildpacks:            group.Group,
 		Logger:                cmd.DefaultLogger,
 		PlatformAPI:           r.PlatformAPI,
-		LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(r.LayersDir, r.SkipLayers, cmd.DefaultLogger),
+		LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(r.LayersDir, r.SkipLayers, cmd.DefaultLogger, r.PlatformAPI),
 		LayersMetadata:        layerMetadata,
 		SBOMRestorer: layer.NewSBOMRestorer(layer.SBOMRestorerOpts{
 			LayersDir: r.LayersDir,
diff --git a/go.mod b/go.mod
index c93889fd..ee5ac968 100644
--- a/go.mod
+++ b/go.mod
@@ -1,29 +1,28 @@
 module github.com/buildpacks/lifecycle
 
 require (
-	github.com/BurntSushi/toml v1.3.2
-	github.com/GoogleContainerTools/kaniko v1.21.1
+	github.com/BurntSushi/toml v1.4.0
+	github.com/GoogleContainerTools/kaniko v1.23.0
 	github.com/apex/log v1.9.0
-	github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6
-	github.com/buildpacks/imgutil v0.0.0-20240422175901-30b002586ecc
+	github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240419161514-af205d85bb44
+	github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168
 	github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589
-	github.com/containerd/containerd v1.7.14
-	github.com/docker/docker v26.0.1+incompatible
+	github.com/containerd/containerd v1.7.17
+	github.com/docker/docker v26.1.2+incompatible
 	github.com/golang/mock v1.6.0
 	github.com/google/go-cmp v0.6.0
 	github.com/google/go-containerregistry v0.19.1
 	github.com/google/uuid v1.6.0
 	github.com/heroku/color v0.0.6
-	github.com/moby/buildkit v0.13.1
+	github.com/moby/buildkit v0.13.2
 	github.com/pkg/errors v0.9.1
 	github.com/sclevine/spec v1.4.0
 	golang.org/x/sync v0.7.0
-	golang.org/x/sys v0.18.0
+	golang.org/x/sys v0.20.0
 )
 
 require (
-	cloud.google.com/go/compute v1.24.0 // indirect
-	cloud.google.com/go/compute/metadata v0.2.3 // indirect
+	cloud.google.com/go/compute/metadata v0.3.0 // indirect
 	github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
 	github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
 	github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
@@ -36,24 +35,24 @@ require (
 	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
 	github.com/Azure/go-autorest/logger v0.2.1 // indirect
 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
-	github.com/Microsoft/go-winio v0.6.1 // indirect
-	github.com/Microsoft/hcsshim v0.11.4 // indirect
+	github.com/Microsoft/go-winio v0.6.2 // indirect
+	github.com/Microsoft/hcsshim v0.11.5 // indirect
 	github.com/agext/levenshtein v1.2.3 // indirect
-	github.com/aws/aws-sdk-go-v2 v1.25.2 // indirect
-	github.com/aws/aws-sdk-go-v2/config v1.27.4 // indirect
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.4 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 // indirect
+	github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
+	github.com/aws/aws-sdk-go-v2/config v1.27.13 // indirect
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.13 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.28.1 // indirect
-	github.com/aws/smithy-go v1.20.1 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ecr v1.27.4 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.4 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 // indirect
+	github.com/aws/smithy-go v1.20.2 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/cilium/ebpf v0.12.3 // indirect
@@ -63,14 +62,14 @@ require (
 	github.com/containerd/fifo v1.1.0 // indirect
 	github.com/containerd/log v0.1.0 // indirect
 	github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
-	github.com/containerd/ttrpc v1.2.3 // indirect
+	github.com/containerd/ttrpc v1.2.4 // indirect
 	github.com/containerd/typeurl/v2 v2.1.1 // indirect
 	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
 	github.com/dimchansky/utfbom v1.1.1 // indirect
 	github.com/distribution/reference v0.6.0 // indirect
-	github.com/docker/cli v25.0.3+incompatible // indirect
+	github.com/docker/cli v26.1.0+incompatible // indirect
 	github.com/docker/distribution v2.8.3+incompatible // indirect
-	github.com/docker/docker-credential-helpers v0.8.0 // indirect
+	github.com/docker/docker-credential-helpers v0.8.1 // indirect
 	github.com/docker/go-connections v0.5.0 // indirect
 	github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
 	github.com/docker/go-metrics v0.0.1 // indirect
@@ -84,7 +83,7 @@ require (
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
-	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/gorilla/mux v1.8.0 // indirect
 	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
 	github.com/hashicorp/go-memdb v1.3.4 // indirect
@@ -109,7 +108,7 @@ require (
 	github.com/moby/term v0.5.0 // indirect
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
-	github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
+	github.com/opencontainers/image-spec v1.1.0 // indirect
 	github.com/opencontainers/runtime-spec v1.1.0 // indirect
 	github.com/opencontainers/selinux v1.11.0 // indirect
 	github.com/otiai10/copy v1.14.0 // indirect
@@ -126,19 +125,16 @@ require (
 	go.opentelemetry.io/otel v1.25.0 // indirect
 	go.opentelemetry.io/otel/metric v1.25.0 // indirect
 	go.opentelemetry.io/otel/trace v1.25.0 // indirect
-	golang.org/x/crypto v0.21.0 // indirect
-	golang.org/x/exp v0.0.0-20231219160207-73b9e39aefca // indirect
-	golang.org/x/mod v0.14.0 // indirect
-	golang.org/x/net v0.23.0 // indirect
-	golang.org/x/oauth2 v0.17.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/crypto v0.23.0 // indirect
+	golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
+	golang.org/x/net v0.25.0 // indirect
+	golang.org/x/oauth2 v0.20.0 // indirect
+	golang.org/x/text v0.15.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	golang.org/x/tools v0.17.0 // indirect
-	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
-	google.golang.org/grpc v1.61.1 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
+	google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 // indirect
+	google.golang.org/grpc v1.63.2 // indirect
+	google.golang.org/protobuf v1.34.1 // indirect
 )
 
 go 1.22
diff --git a/go.sum b/go.sum
index fe974793..71d48c6c 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,6 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
-cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3hFDlscY40=
-cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
-cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
+cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
+cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
 github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA=
@@ -35,14 +33,14 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
 github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
-github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/GoogleContainerTools/kaniko v1.21.1 h1:Q77TGiuSRopS1FvZY9Bzu9Wp9VYlpP6zU+/mu08/COs=
-github.com/GoogleContainerTools/kaniko v1.21.1/go.mod h1:5kbaXGmhHLNc2Zzi/P1Se0qhFYDvK8R62QJh/O0n8rk=
-github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
-github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
-github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
-github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/GoogleContainerTools/kaniko v1.23.0 h1:/YTWOF3uL40kNyK2821Wc+12Czlon1U+gmISWw9CLTM=
+github.com/GoogleContainerTools/kaniko v1.23.0/go.mod h1:lTA7EDyPzSGYxcXxaxWCztINdHMb1Qxj6+eAn1vo2EI=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38=
+github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
 github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
 github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -53,45 +51,45 @@ github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDw
 github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
 github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
 github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go-v2 v1.25.2 h1:/uiG1avJRgLGiQM9X3qJM8+Qa6KRGK5rRPuXE0HUM+w=
-github.com/aws/aws-sdk-go-v2 v1.25.2/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo=
-github.com/aws/aws-sdk-go-v2/config v1.27.4 h1:AhfWb5ZwimdsYTgP7Od8E9L1u4sKmDW2ZVeLcf2O42M=
-github.com/aws/aws-sdk-go-v2/config v1.27.4/go.mod h1:zq2FFXK3A416kiukwpsd+rD4ny6JC7QSkp4QdN1Mp2g=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.4 h1:h5Vztbd8qLppiPwX+y0Q6WiwMZgpd9keKe2EAENgAuI=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.4/go.mod h1:+30tpwrkOgvkJL1rUZuRLoxcJwtI/OkeBLYnHxJtVe0=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 h1:AK0J8iYBFeUk2Ax7O8YpLtFsfhdOByh2QIkHmigpRYk=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2/go.mod h1:iRlGzMix0SExQEviAyptRWRGdYNo3+ufW/lCzvKVTUc=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 h1:bNo4LagzUKbjdxE0tIcR9pMzLR2U/Tgie1Hq1HQ3iH8=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2/go.mod h1:wRQv0nN6v9wDXuWThpovGQjqF1HFdcgWjporw14lS8k=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 h1:EtOU5jsPdIQNP+6Q2C5e3d65NKT1PeCiQk+9OdzO12Q=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2/go.mod h1:tyF5sKccmDz0Bv4NrstEr+/9YkSPJHrcO7UsUKf7pWM=
+github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
+github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
+github.com/aws/aws-sdk-go-v2/config v1.27.13 h1:WbKW8hOzrWoOA/+35S5okqO/2Ap8hkkFUzoW8Hzq24A=
+github.com/aws/aws-sdk-go-v2/config v1.27.13/go.mod h1:XLiyiTMnguytjRER7u5RIkhIqS8Nyz41SwAWb4xEjxs=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.13 h1:XDCJDzk/u5cN7Aple7D/MiAhx1Rjo/0nueJ0La8mRuE=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.13/go.mod h1:FMNcjQrmuBYvOTZDtOLCIu0esmxjF7RuA/89iSXWzQI=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
-github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5 h1:wLPDAUFT50NEXGXpywRU3AA74pg35RJjWol/68ruvQQ=
-github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5/go.mod h1:AOHmGMoPtSY9Zm2zBuwUJQBisIvYAZeA1n7b6f4e880=
-github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 h1:PQp21GBlGNaQ+AVJAB8w2KTmLx0DkFS2fDET2Iy3+f0=
-github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5/go.mod h1:WMntdAol8KgeYsa5sDZPsRTXs4jVZIMYu0eQVVIQxnc=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2 h1:5ffmXjPtwRExp1zc7gENLgCPyHFbhEPwVTkTiH9niSk=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2/go.mod h1:Ru7vg1iQ7cR4i7SZ/JTLYN9kaXtbL69UdgG0OQWQxW0=
-github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 h1:utEGkfdQ4L6YW/ietH7111ZYglLJvS+sLriHJ1NBJEQ=
-github.com/aws/aws-sdk-go-v2/service/sso v1.20.1/go.mod h1:RsYqzYr2F2oPDdpy+PdhephuZxTfjHQe7SOBcZGoAU8=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 h1:9/GylMS45hGGFCcMrUZDVayQE1jYSIN6da9jo7RAYIw=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1/go.mod h1:YjAPFn4kGFqKC54VsHs5fn5B6d+PCY2tziEa3U/GB5Y=
-github.com/aws/aws-sdk-go-v2/service/sts v1.28.1 h1:3I2cBEYgKhrWlwyZgfpSO2BpaMY1LHPqXYk/QGlu2ew=
-github.com/aws/aws-sdk-go-v2/service/sts v1.28.1/go.mod h1:uQ7YYKZt3adCRrdCBREm1CD3efFLOUNH77MrUCvx5oA=
-github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
-github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
-github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6 h1:PlJRmqKlSlEUlwem1c3zdPaEMtJc/ktnV7naD5Qvsx4=
-github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6/go.mod h1:08sPJIlDHu4HwQ1xScPgsBWezvM6U10ghGKBJu0mowA=
+github.com/aws/aws-sdk-go-v2/service/ecr v1.27.4 h1:Qr9W21mzWT3RhfYn9iAux7CeRIdbnTAqmiOlASqQgZI=
+github.com/aws/aws-sdk-go-v2/service/ecr v1.27.4/go.mod h1:if7ybzzjOmDB8pat9FE35AHTY6ZxlYSy3YviSmFZv8c=
+github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.4 h1:aNuiieMaS2IHxqAsTdM/pjHyY1aoaDLBGLqpNnFMMqk=
+github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.4/go.mod h1:8pvvNAklmq+hKmqyvFoMRg0bwg9sdGOvdwximmKiKP0=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 h1:o5cTaeunSpfXiLTIBx5xo2enQmiChtu1IBbzXnfU9Hs=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.6/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 h1:Qe0r0lVURDDeBQJ4yP+BOrJkvkiCo/3FH/t+wY11dmw=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1qYpP5uU7cvNql8ns=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
+github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
+github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
+github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240419161514-af205d85bb44 h1:oNDkocd5/+6jUuxyz07jQWnKhgpNtKQoZSXKMb7emqQ=
+github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240419161514-af205d85bb44/go.mod h1:2nlYPkG0rFrODp6R875pk/kOnB8Ivj3+onhzk2mO57g=
 github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/buildpacks/imgutil v0.0.0-20240422175901-30b002586ecc h1:Pxgpm4bIjgWY9a8r8xHlMYp6wzm5hIZHtw4Iux3XA0c=
-github.com/buildpacks/imgutil v0.0.0-20240422175901-30b002586ecc/go.mod h1:n2R6VRuWsAX3cyHCp/u0Z4WJcixny0gYg075J39owrk=
+github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168 h1:yVYVi1V7x1bXklOx9lpbTfteyzQKGZC/wkl+IlaVRlU=
+github.com/buildpacks/imgutil v0.0.0-20240605145725-186f89b2d168/go.mod h1:n2R6VRuWsAX3cyHCp/u0Z4WJcixny0gYg075J39owrk=
 github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
 github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -107,8 +105,8 @@ github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaD
 github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
 github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
 github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
-github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA=
-github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg=
+github.com/containerd/containerd v1.7.17 h1:KjNnn0+tAVQHAoaWRjmdak9WlvnFR/8rU1CHHy8Rm2A=
+github.com/containerd/containerd v1.7.17/go.mod h1:vK+hhT4TIv2uejlcDlbVIc8+h/BqtKLIyNrtCZol8lI=
 github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
 github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
 github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
@@ -117,8 +115,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
 github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
 github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
 github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
-github.com/containerd/ttrpc v1.2.3 h1:4jlhbXIGvijRtNC8F/5CpuJZ7yKOBFGFOOXg1bkISz0=
-github.com/containerd/ttrpc v1.2.3/go.mod h1:ieWsXucbb8Mj9PH0rXCw1i8IunRbbAiDkpXkbfflWBM=
+github.com/containerd/ttrpc v1.2.4 h1:eQCQK4h9dxDmpOb9QOOMh2NHTfzroH1IkmHiKZi05Oo=
+github.com/containerd/ttrpc v1.2.4/go.mod h1:ojvb8SJBSch0XkqNO0L0YX/5NxR3UnVk2LzFKBK0upc=
 github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
 github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
 github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
@@ -132,14 +130,14 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/docker/cli v25.0.3+incompatible h1:KLeNs7zws74oFuVhgZQ5ONGZiXUUdgsdy6/EsX/6284=
-github.com/docker/cli v25.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v26.1.0+incompatible h1:+nwRy8Ocd8cYNQ60mozDDICICD8aoFGtlPXifX/UQ3Y=
+github.com/docker/cli v26.1.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v26.0.1+incompatible h1:t39Hm6lpXuXtgkF0dm1t9a5HkbUfdGy6XbWexmGr+hA=
-github.com/docker/docker v26.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
-github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
+github.com/docker/docker v26.1.2+incompatible h1:UVX5ZOrrfTGZZYEP+ZDq3Xn9PdHNXaSYMFPDumMqG2k=
+github.com/docker/docker v26.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
+github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
 github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
@@ -199,9 +197,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -275,8 +272,8 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA
 github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/moby/buildkit v0.13.1 h1:L8afOFhPq2RPJJSr/VyzbufwID7jquZVB7oFHbPRcPE=
-github.com/moby/buildkit v0.13.1/go.mod h1:aNmNQKLBFYAOFuzQjR3VA27/FijlvtBD1pjNwTSN37k=
+github.com/moby/buildkit v0.13.2 h1:nXNszM4qD9E7QtG7bFWPnDI1teUQFQglBzon/IU3SzI=
+github.com/moby/buildkit v0.13.2/go.mod h1:2cyVOv9NoHM7arphK9ZfHIWKn9YVZRFd1wXB8kKmEzY=
 github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
 github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
 github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
@@ -311,8 +308,8 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
-github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
+github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
+github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
 github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
 github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
@@ -418,11 +415,11 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20231219160207-73b9e39aefca h1:+xQfFu/HO/82Wwg4zuJ5xiLp0yaOLJjBGnuafXp85YQ=
-golang.org/x/exp v0.0.0-20231219160207-73b9e39aefca/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
+golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
+golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -430,8 +427,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
-golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -449,11 +446,11 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
-golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
+golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
+golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -487,8 +484,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -497,10 +494,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
 golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -513,32 +509,30 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
-golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
+golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
+golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
-google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
-google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
-google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c h1:9g7erC9qu44ks7UK4gDNlnk4kOxZG707xKm4jVniy6o=
-google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
+google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
+google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
+google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be h1:Zz7rLWqp0ApfsR/l7+zSHhY3PMiH2xqgxlfYfAfNpoU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be/go.mod h1:dvdCTIoAGbkWbcIKBniID56/7XHTt6WfxXNMxuziJ+w=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
-google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
+google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
+google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -549,10 +543,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/layer/metadata_restorer.go b/internal/layer/metadata_restorer.go
index f33e0e17..6660e7a0 100644
--- a/internal/layer/metadata_restorer.go
+++ b/internal/layer/metadata_restorer.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/pkg/errors"
 
+	"github.com/buildpacks/lifecycle/api"
 	"github.com/buildpacks/lifecycle/buildpack"
 	"github.com/buildpacks/lifecycle/internal/encoding"
 	"github.com/buildpacks/lifecycle/launch"
@@ -24,18 +25,21 @@ type MetadataRestorer interface {
 	Restore(buildpacks []buildpack.GroupElement, appMeta files.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error
 }
 
-func NewDefaultMetadataRestorer(layersDir string, skipLayers bool, logger log.Logger) *DefaultMetadataRestorer {
+// NewDefaultMetadataRestorer returns an instance of the DefaultMetadataRestorer struct
+func NewDefaultMetadataRestorer(layersDir string, skipLayers bool, logger log.Logger, platformAPI *api.Version) *DefaultMetadataRestorer {
 	return &DefaultMetadataRestorer{
-		Logger:     logger,
-		LayersDir:  layersDir,
-		SkipLayers: skipLayers,
+		Logger:      logger,
+		LayersDir:   layersDir,
+		SkipLayers:  skipLayers,
+		PlatformAPI: platformAPI,
 	}
 }
 
 type DefaultMetadataRestorer struct {
-	LayersDir  string
-	SkipLayers bool
-	Logger     log.Logger
+	LayersDir   string
+	SkipLayers  bool
+	Logger      log.Logger
+	PlatformAPI *api.Version
 }
 
 func (r *DefaultMetadataRestorer) Restore(buildpacks []buildpack.GroupElement, appMeta files.LayersMetadata, cacheMeta platform.CacheMetadata, layerSHAStore SHAStore) error {
@@ -113,10 +117,12 @@ func (r *DefaultMetadataRestorer) restoreLayerMetadata(layerSHAStore SHAStore, a
 				r.Logger.Debugf("Not restoring %q from cache, marked as cache=false", identifier)
 				continue
 			}
-			// If launch=true, the metadata was restored from the app image or the layer is stale.
+			// If launch=true, the metadata was restored from the appLayers if present.
 			if layer.Launch {
-				r.Logger.Debugf("Not restoring %q from cache, marked as launch=true", identifier)
-				continue
+				if _, ok := appLayers[layerName]; ok || r.PlatformAPI.LessThan("0.14") {
+					r.Logger.Debugf("Not restoring %q from cache, marked as launch=true", identifier)
+					continue
+				}
 			}
 			r.Logger.Infof("Restoring metadata for %q from cache", identifier)
 			if err := r.writeLayerMetadata(layerSHAStore, buildpackDir, layerName, layer, bp.ID); err != nil {
diff --git a/phase/analyzer.go b/phase/analyzer.go
index 0217411b..946d5ba4 100644
--- a/phase/analyzer.go
+++ b/phase/analyzer.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/buildpacks/lifecycle/api"
 	"github.com/buildpacks/lifecycle/image"
-	"github.com/buildpacks/lifecycle/internal/fsutil"
 	"github.com/buildpacks/lifecycle/internal/layer"
 	"github.com/buildpacks/lifecycle/log"
 	"github.com/buildpacks/lifecycle/platform"
@@ -88,7 +87,7 @@ func (a *Analyzer) Analyze() (files.Analyzed, error) {
 				return files.Analyzed{}, errors.Wrap(err, "unpacking metadata from image")
 			}
 			if atm.OS == "" {
-				platform.GetTargetOSFromFileSystem(&fsutil.Detect{}, atm, a.Logger)
+				return files.Analyzed{}, errors.New("failed to find OS in run image config")
 			}
 		}
 	}
diff --git a/phase/builder.go b/phase/builder.go
index eae12dec..397b0378 100644
--- a/phase/builder.go
+++ b/phase/builder.go
@@ -149,7 +149,7 @@ func (b *Builder) getBuildInputs() buildpack.BuildInputs {
 		LayersDir:      b.LayersDir,
 		PlatformDir:    b.PlatformDir,
 		Env:            env.NewBuildEnv(os.Environ()),
-		TargetEnv:      platform.EnvVarsFor(b.AnalyzeMD.RunImageTarget(), b.Logger),
+		TargetEnv:      platform.EnvVarsFor(&fsutil.Detect{}, b.AnalyzeMD.RunImageTarget(), b.Logger),
 		Out:            b.Out,
 		Err:            b.Err,
 	}
diff --git a/phase/detector.go b/phase/detector.go
index 4324fdc6..a4d3522a 100644
--- a/phase/detector.go
+++ b/phase/detector.go
@@ -13,6 +13,7 @@ import (
 	"github.com/buildpacks/lifecycle/buildpack"
 	"github.com/buildpacks/lifecycle/env"
 	"github.com/buildpacks/lifecycle/internal/encoding"
+	"github.com/buildpacks/lifecycle/internal/fsutil"
 	"github.com/buildpacks/lifecycle/log"
 	"github.com/buildpacks/lifecycle/platform"
 	"github.com/buildpacks/lifecycle/platform/files"
@@ -143,26 +144,14 @@ func (d *Detector) detectOrder(order buildpack.Order, done, next []buildpack.Gro
 	return nil, nil, ErrFailedDetection
 }
 
-// isWildcard returns true IFF the Arch and OS are unspecified, meaning that the target arch/os are "any"
-func isWildcard(t files.TargetMetadata) bool {
-	return t.Arch == "" && t.OS == ""
-}
-
-func hasWildcard(ts []buildpack.TargetMetadata) bool {
-	for _, tm := range ts {
-		if tm.OS == "" && tm.Arch == "" {
-			return true
-		}
-	}
-	return false
-}
-
 func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElement, wg *sync.WaitGroup) ([]buildpack.GroupElement, []files.BuildPlanEntry, error) {
 	// used below to mark each item as "done" by appending it to the done list
 	markDone := func(groupEl buildpack.GroupElement, descriptor buildpack.Descriptor) {
 		done = append(done, groupEl.WithAPI(descriptor.API()).WithHomepage(descriptor.Homepage()))
 	}
 
+	runImageTargetInfo := d.AnalyzeMD.RunImageTarget()
+
 	for i, groupEl := range group.Group {
 		// Continue if element has already been processed.
 		if hasIDForKind(done, groupEl.Kind(), groupEl.ID) {
@@ -174,7 +163,7 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem
 			return d.detectOrder(groupEl.OrderExtensions, done, group.Group[i+1:], true, wg)
 		}
 
-		// Lookup element in store.  <-- "the store" is the directory where all the buildpacks are.
+		// Lookup element in store (the "store" is the directory where all the buildpacks are).
 		var (
 			descriptor buildpack.Descriptor
 			err        error
@@ -191,26 +180,31 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem
 				// FIXME: cyclical references lead to infinite recursion
 				return d.detectOrder(order, done, group.Group[i+1:], groupEl.Optional, wg)
 			}
-			descriptor = bpDescriptor // standardize the type so below we don't have to care whether it was an extension
+			descriptor = bpDescriptor // Standardize the type so below we don't have to care whether it is an extension.
 		} else {
 			descriptor, err = d.DirStore.LookupExt(groupEl.ID, groupEl.Version)
 			if err != nil {
 				return nil, nil, err
 			}
 		}
+
+		// Check target compatibility.
 		if d.PlatformAPI.AtLeast("0.12") {
-			targetMatch := false
-			if isWildcard(d.AnalyzeMD.RunImageTarget()) || hasWildcard(descriptor.TargetsList()) {
+			var targetMatch bool
+			if len(descriptor.TargetsList()) == 0 {
+				// This is actually just for tests. In practice, the lifecycle will infer a target with at least an OS
+				// when targets are missing from buildpack.toml.
 				targetMatch = true
 			} else {
-				for i := range descriptor.TargetsList() {
-					d.Logger.Debugf("Checking for match against descriptor: %s", descriptor.TargetsList()[i])
-					if platform.TargetSatisfiedForBuild(*d.AnalyzeMD.RunImage.TargetMetadata, descriptor.TargetsList()[i]) {
+				for _, target := range descriptor.TargetsList() {
+					d.Logger.Debugf("Checking for match against descriptor: %s", target)
+					if platform.TargetSatisfiedForBuild(&fsutil.Detect{}, &runImageTargetInfo, target, d.Logger) {
 						targetMatch = true
 						break
 					}
 				}
 			}
+
 			if !targetMatch && !groupEl.Optional {
 				markDone(groupEl, descriptor)
 				d.Runs.Store(
@@ -219,7 +213,7 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem
 						Code: -1,
 						Err: fmt.Errorf(
 							"unable to satisfy target os/arch constraints; run image: %s, buildpack: %s",
-							encoding.ToJSONMaybe(d.AnalyzeMD.RunImage.TargetMetadata),
+							encoding.ToJSONMaybe(runImageTargetInfo),
 							encoding.ToJSONMaybe(descriptor.TargetsList()),
 						),
 					})
@@ -239,7 +233,7 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem
 					BuildConfigDir: d.BuildConfigDir,
 					PlatformDir:    d.PlatformDir,
 					Env:            env.NewBuildEnv(os.Environ()),
-					TargetEnv:      platform.EnvVarsFor(d.AnalyzeMD.RunImageTarget(), d.Logger),
+					TargetEnv:      platform.EnvVarsFor(&fsutil.Detect{}, runImageTargetInfo, d.Logger),
 				}
 				d.Runs.Store(key, d.Executor.Detect(descriptor, inputs, d.Logger)) // this is where we finally invoke bin/detect
 			}
diff --git a/phase/generator.go b/phase/generator.go
index 7f6e5410..8a2106e8 100644
--- a/phase/generator.go
+++ b/phase/generator.go
@@ -151,7 +151,7 @@ func (g *Generator) getGenerateInputs() buildpack.GenerateInputs {
 		BuildConfigDir: g.BuildConfigDir,
 		PlatformDir:    g.PlatformDir,
 		Env:            env.NewBuildEnv(os.Environ()),
-		TargetEnv:      platform.EnvVarsFor(g.AnalyzedMD.RunImageTarget(), g.Logger),
+		TargetEnv:      platform.EnvVarsFor(&fsutil.Detect{}, g.AnalyzedMD.RunImageTarget(), g.Logger),
 		Out:            g.Out,
 		Err:            g.Err,
 	}
diff --git a/phase/restorer.go b/phase/restorer.go
index 48b6da59..e6e6ee6f 100644
--- a/phase/restorer.go
+++ b/phase/restorer.go
@@ -36,7 +36,7 @@ func (r *Restorer) Restore(cache Cache) error {
 	}
 
 	if r.LayerMetadataRestorer == nil {
-		r.LayerMetadataRestorer = layer.NewDefaultMetadataRestorer(r.LayersDir, false, r.Logger)
+		r.LayerMetadataRestorer = layer.NewDefaultMetadataRestorer(r.LayersDir, false, r.Logger, r.PlatformAPI)
 	}
 
 	if r.SBOMRestorer == nil {
diff --git a/platform/files/run.go b/platform/files/run.go
index 00fbe038..310448b8 100644
--- a/platform/files/run.go
+++ b/platform/files/run.go
@@ -19,3 +19,14 @@ func (r *Run) Contains(providedImage string) bool {
 	}
 	return false
 }
+
+// FindByRef return the RunImageForExport struct which contains the imageRef.
+func (r *Run) FindByRef(imageRef string) RunImageForExport {
+	for _, i := range r.Images {
+		if i.Contains(imageRef) {
+			return i
+		}
+	}
+
+	return RunImageForExport{}
+}
diff --git a/platform/target_data.go b/platform/target_data.go
index e88954db..b77ce477 100644
--- a/platform/target_data.go
+++ b/platform/target_data.go
@@ -9,29 +9,6 @@ import (
 	"github.com/buildpacks/lifecycle/platform/files"
 )
 
-// EnvVarsFor fulfills the prophecy set forth in https://github.com/buildpacks/rfcs/blob/b8abe33f2bdc58792acf0bd094dc4ce3c8a54dbb/text/0096-remove-stacks-mixins.md?plain=1#L97
-// by returning an array of "VARIABLE=value" strings suitable for inclusion in your environment or complete breakfast.
-func EnvVarsFor(tm files.TargetMetadata, logger log.Logger) []string {
-	// we should always have os & arch,
-	// if they are not populated try to get target information from the build-time base image
-	if tm.OS == "" {
-		GetTargetOSFromFileSystem(&fsutil.Detect{}, &tm, logger)
-	}
-	ret := []string{
-		"CNB_TARGET_OS=" + tm.OS,
-		"CNB_TARGET_ARCH=" + tm.Arch,
-		"CNB_TARGET_ARCH_VARIANT=" + tm.ArchVariant,
-	}
-	var distName, distVersion string
-	if tm.Distro != nil {
-		distName = tm.Distro.Name
-		distVersion = tm.Distro.Version
-	}
-	ret = append(ret, "CNB_TARGET_DISTRO_NAME="+distName)
-	ret = append(ret, "CNB_TARGET_DISTRO_VERSION="+distVersion)
-	return ret
-}
-
 func GetTargetMetadata(fromImage imgutil.Image) (*files.TargetMetadata, error) {
 	tm := files.TargetMetadata{}
 	var err error
@@ -63,25 +40,20 @@ func GetTargetMetadata(fromImage imgutil.Image) (*files.TargetMetadata, error) {
 	return &tm, nil
 }
 
-// GetTargetOSFromFileSystem populates the target metadata you pass in if the information is available
-// returns a boolean indicating whether it populated any data.
-func GetTargetOSFromFileSystem(d fsutil.Detector, tm *files.TargetMetadata, logger log.Logger) {
-	if d.HasSystemdFile() {
-		contents, err := d.ReadSystemdFile()
-		if err != nil {
-			logger.Warnf("Encountered error trying to read /etc/os-release file: %s", err.Error())
-			return
-		}
-		info := d.GetInfo(contents)
-		if info.Version != "" || info.Name != "" {
-			tm.OS = "linux"
-			tm.Distro = &files.OSDistro{Name: info.Name, Version: info.Version}
-		}
+// TargetSatisfiedForBuild modifies the provided target information for the base (run) image if distribution information is missing,
+// by reading distribution information from /etc/os-release.
+// OS, arch, and arch variant if not specified by at least one entity (image or module) will be treated as matches.
+// If a module specifies distribution information, the image must also specify matching information.
+func TargetSatisfiedForBuild(d fsutil.Detector, base *files.TargetMetadata, module buildpack.TargetMetadata, logger log.Logger) bool {
+	if base == nil {
+		base = &files.TargetMetadata{}
+	}
+	// ensure we have all available data
+	if base.Distro == nil {
+		logger.Info("target distro name/version labels not found, reading /etc/os-release file")
+		GetTargetOSFromFileSystem(d, base, logger)
 	}
-}
-
-// TargetSatisfiedForBuild treats empty fields as wildcards and returns true if all populated fields match.
-func TargetSatisfiedForBuild(base files.TargetMetadata, module buildpack.TargetMetadata) bool {
+	// check matches
 	if !matches(base.OS, module.OS) {
 		return false
 	}
@@ -91,6 +63,7 @@ func TargetSatisfiedForBuild(base files.TargetMetadata, module buildpack.TargetM
 	if !matches(base.ArchVariant, module.ArchVariant) {
 		return false
 	}
+	// check distro
 	if len(module.Distros) == 0 {
 		return true
 	}
@@ -114,6 +87,46 @@ func matches(target1, target2 string) bool {
 	return target1 == target2
 }
 
+// GetTargetOSFromFileSystem populates the target metadata you pass in if the information is available
+// returns a boolean indicating whether it populated any data.
+func GetTargetOSFromFileSystem(d fsutil.Detector, tm *files.TargetMetadata, logger log.Logger) {
+	if d.HasSystemdFile() {
+		contents, err := d.ReadSystemdFile()
+		if err != nil {
+			logger.Warnf("Encountered error trying to read /etc/os-release file: %s", err.Error())
+			return
+		}
+		info := d.GetInfo(contents)
+		if info.Version != "" || info.Name != "" {
+			tm.Distro = &files.OSDistro{Name: info.Name, Version: info.Version}
+		}
+	}
+}
+
+// EnvVarsFor fulfills the prophecy set forth in https://github.com/buildpacks/rfcs/blob/b8abe33f2bdc58792acf0bd094dc4ce3c8a54dbb/text/0096-remove-stacks-mixins.md?plain=1#L97
+// by returning an array of "VARIABLE=value" strings suitable for inclusion in your environment or complete breakfast.
+func EnvVarsFor(d fsutil.Detector, tm files.TargetMetadata, logger log.Logger) []string {
+	// we should always have os & arch,
+	// if they are not populated try to get target information from the build-time base image
+	if tm.Distro == nil {
+		logger.Info("target distro name/version labels not found, reading /etc/os-release file")
+		GetTargetOSFromFileSystem(d, &tm, logger)
+	}
+	ret := []string{
+		"CNB_TARGET_OS=" + tm.OS,
+		"CNB_TARGET_ARCH=" + tm.Arch,
+		"CNB_TARGET_ARCH_VARIANT=" + tm.ArchVariant,
+	}
+	var distName, distVersion string
+	if tm.Distro != nil {
+		distName = tm.Distro.Name
+		distVersion = tm.Distro.Version
+	}
+	ret = append(ret, "CNB_TARGET_DISTRO_NAME="+distName)
+	ret = append(ret, "CNB_TARGET_DISTRO_VERSION="+distVersion)
+	return ret
+}
+
 // TargetSatisfiedForRebase treats optional fields (ArchVariant and Distribution fields) as wildcards if empty, returns true if all populated fields match
 func TargetSatisfiedForRebase(t files.TargetMetadata, appTargetMetadata files.TargetMetadata) bool {
 	if t.OS != appTargetMetadata.OS || t.Arch != appTargetMetadata.Arch {


Test patch:
diff --git a/.github/workflows/test-s390x.yml b/.github/workflows/test-s390x.yml
index 2340a9cc..4c45b1f2 100644
--- a/.github/workflows/test-s390x.yml
+++ b/.github/workflows/test-s390x.yml
@@ -32,11 +32,11 @@ jobs:
         id: ZVSI
         run: |
           #creation of zvsi
-          ibmcloud is instance-create $ZVSI_INSTANCE_NAME ${{ secrets.ZVSI_VPC }} $ZVSI_ZONE_NAME $ZVSI_PROFILE_NAME ${{ secrets.ZVSI_SUBNET }} --image ${{ secrets.ZVSI_IMAGE }} --keys ${{ secrets.ZVSI_KEY }} --resource-group-id ${{ secrets.ZVSI_RG_ID }} --sgs ${{ secrets.ZVSI_SG }} --allow-ip-spoofing false
+          ibmcloud is instance-create $ZVSI_INSTANCE_NAME ${{ secrets.ZVSI_VPC }} $ZVSI_ZONE_NAME $ZVSI_PROFILE_NAME ${{ secrets.ZVSI_SUBNET }} --image ${{ secrets.ZVSI_IMAGE }} --keys ${{ secrets.ZVSI_KEY }} --resource-group-id ${{ secrets.ZVSI_RG_ID }}  --primary-network-interface "{\"name\":\"eth0\",\"allow_ip_spoofing\":false,\"subnet\": {\"name\":\"${{ secrets.ZVSI_SUBNET }}\"},\"security_groups\":[{\"id\":\"${{ secrets.ZVSI_SG }}\"}]}"
           #Reserving a floating ip to the ZVSI
           ibmcloud is floating-ip-reserve $ZVSI_FP_NAME --zone $ZVSI_ZONE_NAME --resource-group-id ${{ secrets.ZVSI_RG_ID }} --in $ZVSI_INSTANCE_NAME
           #Bouding the Floating ip to the ZVSI
-          ibmcloud is floating-ip-update $ZVSI_FP_NAME --nic primary --in $ZVSI_INSTANCE_NAME
+          ibmcloud is floating-ip-update $ZVSI_FP_NAME --nic eth0 --in $ZVSI_INSTANCE_NAME
           sleep 60
           #Saving the Floating IP to login ZVSI
           ZVSI_HOST=$(ibmcloud is floating-ip $ZVSI_FP_NAME | awk '/Address/{print $2}')
diff --git a/acceptance/testdata/detector/Dockerfile b/acceptance/testdata/detector/Dockerfile
index 07364b9a..695e030d 100644
--- a/acceptance/testdata/detector/Dockerfile
+++ b/acceptance/testdata/detector/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:bionic
+FROM ubuntu:jammy
 
 ARG cnb_uid=1234
 ARG cnb_gid=1000
diff --git a/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml b/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml
index b70a985e..6cb40946 100644
--- a/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml
+++ b/acceptance/testdata/detector/container/cnb/buildpacks/simple_buildpack/simple_buildpack_version/buildpack.toml
@@ -1,6 +1,12 @@
-api = "0.10"
+api = "0.9"
 
 [buildpack]
-    id = "simple_buildpack"
-    version = "simple_buildpack_version"
-    name = "Simple Buildpack"
+id = "simple_buildpack"
+version = "simple_buildpack_version"
+name = "Simple Buildpack"
+
+[[stacks]]
+id = "io.buildpacks.stacks.bionic"
+
+[[stacks]]
+id = "io.buildpacks.stacks.jammy"
diff --git a/buildpack/bp_descriptor_test.go b/buildpack/bp_descriptor_test.go
index 2fff71ac..cd438709 100644
--- a/buildpack/bp_descriptor_test.go
+++ b/buildpack/bp_descriptor_test.go
@@ -81,44 +81,140 @@ func testBpDescriptor(t *testing.T, when spec.G, it spec.S) {
 			h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "V8.4-2L3")
 		})
 
-		it("does translate one special stack value into target values for older apis", func() {
-			path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1", "buildpack.toml")
-			descriptor, err := buildpack.ReadBpDescriptor(path)
-			h.AssertNil(t, err)
-			// common sanity checks
-			h.AssertEq(t, descriptor.WithAPI, "0.7")
-			h.AssertEq(t, descriptor.Buildpack.ID, "B")
-			h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
-			h.AssertEq(t, descriptor.Buildpack.Version, "v1")
-			h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
-			h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
-			// specific behaviors for this test
-			h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic")
-			h.AssertEq(t, len(descriptor.Targets), 1)
-			h.AssertEq(t, descriptor.Targets[0].Arch, "amd64")
-			h.AssertEq(t, descriptor.Targets[0].OS, "linux")
-			h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu")
-			h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04")
-		})
+		when("translating stacks to targets", func() {
+			when("older buildpacks", func() {
+				when("there is only bionic", func() {
+					it("creates a target", func() {
+						path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1", "buildpack.toml")
+						descriptor, err := buildpack.ReadBpDescriptor(path)
+						h.AssertNil(t, err)
+						// common sanity checks
+						h.AssertEq(t, descriptor.WithAPI, "0.7")
+						h.AssertEq(t, descriptor.Buildpack.ID, "B")
+						h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
+						h.AssertEq(t, descriptor.Buildpack.Version, "v1")
+						h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
+						h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
+						// specific behaviors for this test
+						h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic")
+						h.AssertEq(t, len(descriptor.Targets), 1)
+						h.AssertEq(t, descriptor.Targets[0].Arch, "amd64")
+						h.AssertEq(t, descriptor.Targets[0].OS, "linux")
+						h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu")
+						h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04")
+					})
+				})
 
-		it("translates one special stack value into target values", func() {
-			path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2", "buildpack.toml")
-			descriptor, err := buildpack.ReadBpDescriptor(path)
-			h.AssertNil(t, err)
-			// common sanity checks
-			h.AssertEq(t, descriptor.WithAPI, "0.12")
-			h.AssertEq(t, descriptor.Buildpack.ID, "B")
-			h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
-			h.AssertEq(t, descriptor.Buildpack.Version, "v1")
-			h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
-			h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
-			// specific behaviors for this test
-			h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic")
-			h.AssertEq(t, len(descriptor.Targets), 1)
-			h.AssertEq(t, descriptor.Targets[0].Arch, "amd64")
-			h.AssertEq(t, descriptor.Targets[0].OS, "linux")
-			h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu")
-			h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04")
+				when("there are multiple stacks", func() {
+					it("does NOT create a target", func() {
+						path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1.2", "buildpack.toml")
+						descriptor, err := buildpack.ReadBpDescriptor(path)
+						h.AssertNil(t, err)
+						// common sanity checks
+						h.AssertEq(t, descriptor.WithAPI, "0.7")
+						h.AssertEq(t, descriptor.Buildpack.ID, "B")
+						h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
+						h.AssertEq(t, descriptor.Buildpack.Version, "v1.2")
+						h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
+						h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
+						// specific behaviors for this test
+						h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic")
+						h.AssertEq(t, len(descriptor.Targets), 0)
+					})
+				})
+
+				when("there is a wildcard stack", func() {
+					it("creates a wildcard target", func() {
+						path := filepath.Join("testdata", "buildpack", "by-id", "B", "v1.star", "buildpack.toml")
+						descriptor, err := buildpack.ReadBpDescriptor(path)
+						h.AssertNil(t, err)
+						// common sanity checks
+						h.AssertEq(t, descriptor.WithAPI, "0.7")
+						h.AssertEq(t, descriptor.Buildpack.ID, "B")
+						h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
+						h.AssertEq(t, descriptor.Buildpack.Version, "v1.star")
+						h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
+						h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
+						// specific behaviors for this test
+						h.AssertEq(t, descriptor.Stacks[0].ID, "*")
+						h.AssertEq(t, len(descriptor.Targets), 1)
+						// a target that is completely empty will always match whatever is the base image target
+						h.AssertEq(t, descriptor.Targets[0].Arch, "")
+						h.AssertEq(t, descriptor.Targets[0].OS, "")
+						h.AssertEq(t, descriptor.Targets[0].ArchVariant, "")
+						h.AssertEq(t, len(descriptor.Targets[0].Distros), 0)
+					})
+				})
+			})
+
+			when("newer buildpacks", func() {
+				when("there is only bionic", func() {
+					it("creates a target", func() {
+						path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2", "buildpack.toml")
+						descriptor, err := buildpack.ReadBpDescriptor(path)
+						h.AssertNil(t, err)
+						// common sanity checks
+						h.AssertEq(t, descriptor.WithAPI, "0.12")
+						h.AssertEq(t, descriptor.Buildpack.ID, "B")
+						h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
+						h.AssertEq(t, descriptor.Buildpack.Version, "v2")
+						h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
+						h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
+						// specific behaviors for this test
+						h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic")
+						h.AssertEq(t, len(descriptor.Targets), 1)
+						h.AssertEq(t, descriptor.Targets[0].Arch, "amd64")
+						h.AssertEq(t, descriptor.Targets[0].OS, "linux")
+						h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu")
+						h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04")
+					})
+				})
+
+				when("there are multiple stacks", func() {
+					it("creates a target", func() {
+						path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2.2", "buildpack.toml")
+						descriptor, err := buildpack.ReadBpDescriptor(path)
+						h.AssertNil(t, err)
+						// common sanity checks
+						h.AssertEq(t, descriptor.WithAPI, "0.12")
+						h.AssertEq(t, descriptor.Buildpack.ID, "B")
+						h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
+						h.AssertEq(t, descriptor.Buildpack.Version, "v2.2")
+						h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
+						h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
+						// specific behaviors for this test
+						h.AssertEq(t, descriptor.Stacks[0].ID, "io.buildpacks.stacks.bionic")
+						h.AssertEq(t, len(descriptor.Targets), 1)
+						h.AssertEq(t, descriptor.Targets[0].Arch, "amd64")
+						h.AssertEq(t, descriptor.Targets[0].OS, "linux")
+						h.AssertEq(t, descriptor.Targets[0].Distros[0].Name, "ubuntu")
+						h.AssertEq(t, descriptor.Targets[0].Distros[0].Version, "18.04")
+					})
+				})
+
+				when("there is a wildcard stack", func() {
+					it("creates a wildcard target", func() {
+						path := filepath.Join("testdata", "buildpack", "by-id", "B", "v2.star", "buildpack.toml")
+						descriptor, err := buildpack.ReadBpDescriptor(path)
+						h.AssertNil(t, err)
+						// common sanity checks
+						h.AssertEq(t, descriptor.WithAPI, "0.12")
+						h.AssertEq(t, descriptor.Buildpack.ID, "B")
+						h.AssertEq(t, descriptor.Buildpack.Name, "Buildpack B")
+						h.AssertEq(t, descriptor.Buildpack.Version, "v2.star")
+						h.AssertEq(t, descriptor.Buildpack.Homepage, "Buildpack B Homepage")
+						h.AssertEq(t, descriptor.Buildpack.SBOM, []string{"application/vnd.cyclonedx+json"})
+						// specific behaviors for this test
+						h.AssertEq(t, descriptor.Stacks[0].ID, "*")
+						h.AssertEq(t, len(descriptor.Targets), 1)
+						// a target that is completely empty will always match whatever is the base image target
+						h.AssertEq(t, descriptor.Targets[0].Arch, "")
+						h.AssertEq(t, descriptor.Targets[0].OS, "")
+						h.AssertEq(t, descriptor.Targets[0].ArchVariant, "")
+						h.AssertEq(t, len(descriptor.Targets[0].Distros), 0)
+					})
+				})
+			})
 		})
 
 		it("does not translate non-special stack values", func() {
diff --git a/buildpack/build_test.go b/buildpack/build_test.go
index a28b3101..f336c913 100644
--- a/buildpack/build_test.go
+++ b/buildpack/build_test.go
@@ -941,7 +941,7 @@ func testBuild(t *testing.T, when spec.G, it spec.S) {
 							filepath.Join(appDir, "launch-A-v1.toml"),
 						)
 						_, err := executor.Build(descriptor, inputs, logger)
-						h.AssertError(t, err, "toml: line 2 (last key \"processes.command\"): incompatible types: TOML value has type []interface {}; destination has type string")
+						h.AssertError(t, err, "toml: line 2 (last key \"processes.command\"): incompatible types: TOML value has type []any; destination has type string")
 					})
 				})
 			})
diff --git a/buildpack/testdata/buildpack/by-id/B/v1.2/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v1.2/buildpack.toml
new file mode 100644
index 00000000..7b2df167
--- /dev/null
+++ b/buildpack/testdata/buildpack/by-id/B/v1.2/buildpack.toml
@@ -0,0 +1,14 @@
+api = "0.7"
+
+[buildpack]
+id = "B"
+name = "Buildpack B"
+version = "v1.2"
+homepage = "Buildpack B Homepage"
+sbom-formats = ["application/vnd.cyclonedx+json"]
+
+[[stacks]]
+id = "io.buildpacks.stacks.bionic"
+
+[[stacks]]
+id = "io.buildpacks.stacks.jammy"
diff --git a/buildpack/testdata/buildpack/by-id/B/v1.star/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v1.star/buildpack.toml
new file mode 100644
index 00000000..9267b69f
--- /dev/null
+++ b/buildpack/testdata/buildpack/by-id/B/v1.star/buildpack.toml
@@ -0,0 +1,11 @@
+api = "0.7"
+
+[buildpack]
+id = "B"
+name = "Buildpack B"
+version = "v1.star"
+homepage = "Buildpack B Homepage"
+sbom-formats = ["application/vnd.cyclonedx+json"]
+
+[[stacks]]
+id = "*"
diff --git a/buildpack/testdata/buildpack/by-id/B/v2.2/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v2.2/buildpack.toml
new file mode 100644
index 00000000..87cc955e
--- /dev/null
+++ b/buildpack/testdata/buildpack/by-id/B/v2.2/buildpack.toml
@@ -0,0 +1,14 @@
+api = "0.12"
+
+[buildpack]
+id = "B"
+name = "Buildpack B"
+version = "v2.2"
+homepage = "Buildpack B Homepage"
+sbom-formats = ["application/vnd.cyclonedx+json"]
+
+[[stacks]]
+id = "io.buildpacks.stacks.bionic"
+
+[[stacks]]
+id = "io.buildpacks.stacks.jammy"
diff --git a/buildpack/testdata/buildpack/by-id/B/v2.star/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v2.star/buildpack.toml
new file mode 100644
index 00000000..018823b0
--- /dev/null
+++ b/buildpack/testdata/buildpack/by-id/B/v2.star/buildpack.toml
@@ -0,0 +1,11 @@
+api = "0.12"
+
+[buildpack]
+id = "B"
+name = "Buildpack B"
+version = "v2.star"
+homepage = "Buildpack B Homepage"
+sbom-formats = ["application/vnd.cyclonedx+json"]
+
+[[stacks]]
+id = "*"
diff --git a/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml b/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml
index ad55d56b..7780b29f 100644
--- a/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml
+++ b/buildpack/testdata/buildpack/by-id/B/v2/buildpack.toml
@@ -3,7 +3,7 @@ api = "0.12"
 [buildpack]
 id = "B"
 name = "Buildpack B"
-version = "v1"
+version = "v2"
 homepage = "Buildpack B Homepage"
 sbom-formats = ["application/vnd.cyclonedx+json"]
 
diff --git a/internal/layer/metadata_restorer_test.go b/internal/layer/metadata_restorer_test.go
index aec91d37..d1aec921 100644
--- a/internal/layer/metadata_restorer_test.go
+++ b/internal/layer/metadata_restorer_test.go
@@ -42,7 +42,7 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) {
 		layerDir, err = os.MkdirTemp("", "lifecycle-layer-dir")
 		h.AssertNil(t, err)
 		logger = log.Logger{Handler: &discard.Handler{}}
-		layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger)
+		layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.Platform.Latest())
 		layerSHAStore = layer.NewSHAStore()
 	})
 
@@ -136,6 +136,8 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) {
 					{"no.cache.buildpack/some-layer.toml", "[metadata]\n  some-layer-key = \"some-layer-value\""},
 					// Cache-image-only layers.
 					{"metadata.buildpack/cache.toml", "[metadata]\n  cache-key = \"cache-value\""},
+					// Cached launch layers not in app
+					{"metadata.buildpack/launch-cache-not-in-app.toml", "[metadata]\n  cache-only-key = \"cache-only-value\"\n  launch-cache-key = \"cache-specific-value\""},
 				} {
 					got := h.MustReadFile(t, filepath.Join(layerDir, data.name))
 					h.AssertStringContains(t, string(got), data.want)
@@ -143,6 +145,33 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) {
 				}
 			})
 
+			when("platformAPI is less than 0.14", func() {
+				it.Before(func() {
+					layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.MustParse("0.13"))
+				})
+
+				it("ignores launch-cache-not-in-app", func() {
+					err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore)
+					h.AssertNil(t, err)
+
+					h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack/launch-cache-not-in-app.toml"))
+					unsetFlags := "[types]"
+					for _, data := range []struct{ name, want string }{
+						// App layers.
+						{"metadata.buildpack/launch.toml", "[metadata]\n  launch-key = \"launch-value\""},
+						{"metadata.buildpack/launch-build-cache.toml", "[metadata]\n  launch-build-cache-key = \"launch-build-cache-value\""},
+						{"metadata.buildpack/launch-cache.toml", "[metadata]\n  launch-cache-key = \"launch-cache-value\""},
+						{"no.cache.buildpack/some-layer.toml", "[metadata]\n  some-layer-key = \"some-layer-value\""},
+						// Cache-image-only layers.
+						{"metadata.buildpack/cache.toml", "[metadata]\n  cache-key = \"cache-value\""},
+					} {
+						got := h.MustReadFile(t, filepath.Join(layerDir, data.name))
+						h.AssertStringContains(t, string(got), data.want)
+						h.AssertStringDoesNotContain(t, string(got), unsetFlags) // The [types] table shouldn't exist. The build, cache and launch flags are set to false.
+					}
+				})
+			})
+
 			it("restores layer metadata without the launch, build and cache flags", func() {
 				buildpacks = []buildpack.GroupElement{
 					{ID: "metadata.buildpack", API: api.Buildpack.Latest().String()},
@@ -186,14 +215,6 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) {
 				h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "no.group.buildpack"))
 			})
 
-			it("does not restore launch=true layer metadata", func() {
-				err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore)
-				h.AssertNil(t, err)
-
-				h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack", "launch-cache-not-in-app.toml"))
-				h.AssertPathDoesNotExist(t, filepath.Join(layerDir, "metadata.buildpack", "launch-cache-not-in-app.sha"))
-			})
-
 			it("does not restore cache=false layer metadata", func() {
 				err := layerMetadataRestorer.Restore(buildpacks, layersMetadata, cacheMetadata, layerSHAStore)
 				h.AssertNil(t, err)
@@ -299,7 +320,7 @@ func testLayerMetadataRestorer(t *testing.T, when spec.G, it spec.S) {
 			when("skip layers is true", func() {
 				it.Before(func() {
 					skipLayers = true
-					layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger)
+					layerMetadataRestorer = layer.NewDefaultMetadataRestorer(layerDir, skipLayers, &logger, api.Platform.Latest())
 				})
 
 				it("does not write buildpack layer metadata", func() {
diff --git a/internal/layer/testdata/cache_metadata.json b/internal/layer/testdata/cache_metadata.json
index cc982aa9..a60ee35a 100644
--- a/internal/layer/testdata/cache_metadata.json
+++ b/internal/layer/testdata/cache_metadata.json
@@ -36,7 +36,7 @@
                     "launch": true,
                     "sha": "launch-cache-old-sha"
                 },
-		"launch-cache-not-in-app": {
+		        "launch-cache-not-in-app": {
                     "cache": true,
                     "data": {
                         "launch-cache-key": "cache-specific-value",
diff --git a/phase/analyzer_test.go b/phase/analyzer_test.go
index 71f965f0..0424f3f6 100644
--- a/phase/analyzer_test.go
+++ b/phase/analyzer_test.go
@@ -484,6 +484,7 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S)
 
 					h.AssertEq(t, md.RunImage.Reference, "s0m3D1g3sT")
 				})
+
 				it("populates target metadata from the run image", func() {
 					h.AssertNil(t, previousImage.SetLabel("io.buildpacks.base.id", "id software"))
 					h.AssertNil(t, previousImage.SetOS("windows"))
@@ -508,6 +509,18 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S)
 						h.AssertEq(t, md.RunImage.TargetMetadata.Distro.Version, "Helpful Holstein")
 					}
 				})
+
+				when("run image is missing OS", func() {
+					it("errors", func() {
+						h.AssertNil(t, previousImage.SetOS(""))
+						_, err := analyzer.Analyze()
+						if api.MustParse(platformAPI).LessThan("0.12") {
+							h.AssertNil(t, err)
+						} else {
+							h.AssertError(t, err, "failed to find OS")
+						}
+					})
+				})
 			})
 		})
 	}
diff --git a/phase/builder_test.go b/phase/builder_test.go
index 04507307..e4f4fa1e 100644
--- a/phase/builder_test.go
+++ b/phase/builder_test.go
@@ -191,8 +191,6 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64")
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH_VARIANT=")
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_OS=linux")
-					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_NAME=")
-					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_VERSION=")
 					return buildpack.BuildOutputs{}, nil
 				},
 			)
@@ -205,8 +203,6 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64")
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH_VARIANT=")
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_OS=linux")
-					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_NAME=")
-					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_VERSION=")
 					return buildpack.BuildOutputs{}, nil
 				})
 
diff --git a/phase/detector_test.go b/phase/detector_test.go
index 6820ef37..1c0e3be0 100644
--- a/phase/detector_test.go
+++ b/phase/detector_test.go
@@ -223,8 +223,6 @@ func testDetector(t *testing.T, when spec.G, it spec.S) {
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH=amd64")
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_ARCH_VARIANT=")
 					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_OS=linux")
-					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_NAME=")
-					h.AssertContains(t, inputs.TargetEnv, "CNB_TARGET_DISTRO_VERSION=")
 					return buildpack.DetectOutputs{}
 				})
 
diff --git a/phase/generator_test.go b/phase/generator_test.go
index cd6895bf..aefc13e1 100644
--- a/phase/generator_test.go
+++ b/phase/generator_test.go
@@ -284,8 +284,6 @@ func testGenerator(t *testing.T, when spec.G, it spec.S) {
 							h.AssertContains(t, inputs.TargetEnv,
 								"CNB_TARGET_ARCH=amd64",
 								"CNB_TARGET_ARCH_VARIANT=",
-								"CNB_TARGET_DISTRO_NAME=",
-								"CNB_TARGET_DISTRO_VERSION=",
 								"CNB_TARGET_OS=linux",
 							)
 							return buildpack.GenerateOutputs{Dockerfiles: []buildpack.DockerfileInfo{{ExtensionID: d.Extension.ID,
@@ -296,8 +294,6 @@ func testGenerator(t *testing.T, when spec.G, it spec.S) {
 							h.AssertContains(t, inputs.TargetEnv,
 								"CNB_TARGET_ARCH=amd64",
 								"CNB_TARGET_ARCH_VARIANT=",
-								"CNB_TARGET_DISTRO_NAME=",
-								"CNB_TARGET_DISTRO_VERSION=",
 								"CNB_TARGET_OS=linux",
 							)
 							return buildpack.GenerateOutputs{Dockerfiles: []buildpack.DockerfileInfo{{ExtensionID: d.Extension.ID,
diff --git a/phase/restorer_test.go b/phase/restorer_test.go
index 61066c88..94b3e165 100644
--- a/phase/restorer_test.go
+++ b/phase/restorer_test.go
@@ -81,7 +81,7 @@ func testRestorer(buildpackAPI, platformAPI string) func(t *testing.T, when spec
 						{ID: "buildpack.id", API: buildpackAPI},
 						{ID: "escaped/buildpack/id", API: buildpackAPI},
 					},
-					LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(layersDir, skipLayers, &logger),
+					LayerMetadataRestorer: layer.NewDefaultMetadataRestorer(layersDir, skipLayers, &logger, api.Platform.Latest()),
 					SBOMRestorer:          sbomRestorer,
 					PlatformAPI:           api.MustParse(platformAPI),
 				}
diff --git a/platform/run_image_test.go b/platform/run_image_test.go
index 7a14e684..566b00cc 100644
--- a/platform/run_image_test.go
+++ b/platform/run_image_test.go
@@ -201,32 +201,6 @@ func testRunImage(t *testing.T, when spec.G, it spec.S) {
 		})
 	})
 
-	when(".EnvVarsFor", func() {
-		it("returns the right thing", func() {
-			tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: &files.OSDistro{Name: "nix", Version: "22.11"}}
-			observed := platform.EnvVarsFor(tm, nil)
-			h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
-			h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant)
-			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME="+tm.Distro.Name)
-			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION="+tm.Distro.Version)
-			h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
-			h.AssertEq(t, len(observed), 5)
-		})
-
-		it("does not return the wrong thing", func() {
-			tm := files.TargetMetadata{Arch: "pentium", OS: "linux"}
-			observed := platform.EnvVarsFor(tm, nil)
-			h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
-			h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
-			// note: per the spec only the ID field is optional, so I guess the others should always be set: https://github.com/buildpacks/rfcs/blob/main/text/0096-remove-stacks-mixins.md#runtime-metadata
-			// the empty ones:
-			h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT=")
-			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME=")
-			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION=")
-			h.AssertEq(t, len(observed), 5)
-		})
-	})
-
 	when(".BestRunImageMirrorFor", func() {
 		var (
 			stackMD            *files.Stack
diff --git a/platform/target_data_test.go b/platform/target_data_test.go
index 2742f8f4..cb78ade9 100644
--- a/platform/target_data_test.go
+++ b/platform/target_data_test.go
@@ -21,34 +21,61 @@ func TestTargetData(t *testing.T) {
 
 func testTargetData(t *testing.T, when spec.G, it spec.S) {
 	when(".TargetSatisfiedForBuild", func() {
-		var baseTarget files.TargetMetadata
+		baseTarget := &files.TargetMetadata{OS: "Win95", Arch: "Pentium"}
+		d := mockDetector{
+			contents: "this is just test contents really",
+			t:        t,
+			HasFile:  false, // by default, don't use info from /etc/os-release for these tests
+		}
+
 		when("base image data", func() {
 			when("has os and arch", func() {
-				baseTarget = files.TargetMetadata{OS: "Win95", Arch: "Pentium"}
-
 				when("buildpack data", func() {
 					when("has os and arch", func() {
 						it("must match", func() {
-							h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}), true)
-							h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: "Win98", Arch: baseTarget.Arch}), false)
-							h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: "Pentium MMX"}), false)
+							h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), true)
+							h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: "Win98", Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), false)
+							h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: "Pentium MMX"}, &log.Logger{Handler: memory.New()}), false)
 						})
 					})
 
 					when("missing os and arch", func() {
 						it("matches", func() {
-							h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: "", Arch: ""}), true)
+							h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: "", Arch: ""}, &log.Logger{Handler: memory.New()}), true)
 						})
 					})
 
 					when("has distro information", func() {
 						it("does not match", func() {
-							h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "MMX"}), true)
-							h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{
+							h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{
 								OS:      baseTarget.OS,
 								Arch:    baseTarget.Arch,
 								Distros: []buildpack.OSDistro{{Name: "a", Version: "2"}},
-							}), false)
+							}, &log.Logger{Handler: memory.New()}), false)
+						})
+
+						when("/etc/os-release has information", func() {
+							it("must match", func() {
+								d := mockDetector{
+									contents: "this is just test contents really",
+									t:        t,
+									HasFile:  true,
+								}
+								h.AssertEq(
+									t,
+									platform.TargetSatisfiedForBuild(
+										&d,
+										baseTarget,
+										buildpack.TargetMetadata{
+											OS:   baseTarget.OS,
+											Arch: baseTarget.Arch,
+											Distros: []buildpack.OSDistro{
+												{Name: "opensesame", Version: "3.14"},
+											},
+										},
+										&log.Logger{Handler: memory.New()},
+									), true)
+							})
 						})
 					})
 				})
@@ -59,14 +86,14 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) {
 					when("buildpack data", func() {
 						when("has arch variant", func() {
 							it("must match", func() {
-								h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-arch-variant"}), true)
-								h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-other-arch-variant"}), false)
+								h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-arch-variant"}, &log.Logger{Handler: memory.New()}), true)
+								h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, ArchVariant: "some-other-arch-variant"}, &log.Logger{Handler: memory.New()}), false)
 							})
 						})
 
 						when("missing arch variant", func() {
 							it("matches", func() {
-								h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}), true)
+								h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), true)
 							})
 						})
 					})
@@ -78,14 +105,14 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) {
 					when("buildpack data", func() {
 						when("has distro information", func() {
 							it("must match", func() {
-								h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "B", Version: "2"}, {Name: "A", Version: "1"}}}), true)
-								h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "g", Version: "2"}, {Name: "B", Version: "2"}}}), false)
+								h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "B", Version: "2"}, {Name: "A", Version: "1"}}}, &log.Logger{Handler: memory.New()}), true)
+								h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch, Distros: []buildpack.OSDistro{{Name: "g", Version: "2"}, {Name: "B", Version: "2"}}}, &log.Logger{Handler: memory.New()}), false)
 							})
 						})
 
 						when("missing distro information", func() {
 							it("matches", func() {
-								h.AssertEq(t, platform.TargetSatisfiedForBuild(baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}), true)
+								h.AssertEq(t, platform.TargetSatisfiedForBuild(&d, baseTarget, buildpack.TargetMetadata{OS: baseTarget.OS, Arch: baseTarget.Arch}, &log.Logger{Handler: memory.New()}), true)
 							})
 						})
 					})
@@ -94,6 +121,93 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) {
 		})
 	})
 
+	when(".GetTargetOSFromFileSystem", func() {
+		it("populates appropriately", func() {
+			logr := &log.Logger{Handler: memory.New()}
+			tm := files.TargetMetadata{}
+			d := mockDetector{contents: "this is just test contents really",
+				t:       t,
+				HasFile: true}
+			platform.GetTargetOSFromFileSystem(&d, &tm, logr)
+			h.AssertEq(t, "opensesame", tm.Distro.Name)
+			h.AssertEq(t, "3.14", tm.Distro.Version)
+		})
+
+		it("doesn't populate if there's no file", func() {
+			logr := &log.Logger{Handler: memory.New()}
+			tm := files.TargetMetadata{}
+			d := mockDetector{contents: "in unit tests 2.0 the users will generate the content but we'll serve them ads",
+				t:       t,
+				HasFile: false}
+			platform.GetTargetOSFromFileSystem(&d, &tm, logr)
+			h.AssertNil(t, tm.Distro)
+		})
+
+		it("doesn't populate if there's an error reading the file", func() {
+			logr := &log.Logger{Handler: memory.New()}
+			tm := files.TargetMetadata{}
+			d := mockDetector{contents: "contentment is the greatest wealth",
+				t:           t,
+				HasFile:     true,
+				ReadFileErr: fmt.Errorf("I'm sorry Dave, I don't even remember exactly what HAL says"),
+			}
+			platform.GetTargetOSFromFileSystem(&d, &tm, logr)
+			h.AssertNil(t, tm.Distro)
+		})
+	})
+
+	when(".EnvVarsFor", func() {
+		it("returns the right thing", func() {
+			tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: &files.OSDistro{Name: "nix", Version: "22.11"}}
+			d := &mockDetector{
+				contents: "this is just test contents really",
+				t:        t,
+				HasFile:  false,
+			}
+			observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()})
+			h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
+			h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant)
+			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME="+tm.Distro.Name)
+			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION="+tm.Distro.Version)
+			h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
+			h.AssertEq(t, len(observed), 5)
+		})
+
+		it("returns the right thing from /etc/os-release", func() {
+			d := &mockDetector{
+				contents: "this is just test contents really",
+				t:        t,
+				HasFile:  true,
+			}
+			tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: nil}
+			observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()})
+			h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
+			h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant)
+			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME=opensesame")
+			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION=3.14")
+			h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
+			h.AssertEq(t, len(observed), 5)
+		})
+
+		it("does not return the wrong thing", func() {
+			tm := files.TargetMetadata{Arch: "pentium", OS: "linux"}
+			d := &mockDetector{
+				contents: "this is just test contents really",
+				t:        t,
+				HasFile:  false,
+			}
+			observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()})
+			h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
+			h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
+			// note: per the spec only the ID field is optional, so I guess the others should always be set: https://github.com/buildpacks/rfcs/blob/main/text/0096-remove-stacks-mixins.md#runtime-metadata
+			// the empty ones:
+			h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT=")
+			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME=")
+			h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION=")
+			h.AssertEq(t, len(observed), 5)
+		})
+	})
+
 	when(".TargetSatisfiedForRebase", func() {
 		var baseTarget files.TargetMetadata
 		when("orig image data", func() {
@@ -167,41 +281,6 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) {
 			})
 		})
 	})
-
-	when(".GetTargetOSFromFileSystem", func() {
-		it("populates appropriately", func() {
-			logr := &log.Logger{Handler: memory.New()}
-			tm := files.TargetMetadata{}
-			d := mockDetector{contents: "this is just test contents really",
-				t:       t,
-				HasFile: true}
-			platform.GetTargetOSFromFileSystem(&d, &tm, logr)
-			h.AssertEq(t, "opensesame", tm.Distro.Name)
-			h.AssertEq(t, "3.14", tm.Distro.Version)
-		})
-
-		it("doesn't populate if there's no file", func() {
-			logr := &log.Logger{Handler: memory.New()}
-			tm := files.TargetMetadata{}
-			d := mockDetector{contents: "in unit tests 2.0 the users will generate the content but we'll serve them ads",
-				t:       t,
-				HasFile: false}
-			platform.GetTargetOSFromFileSystem(&d, &tm, logr)
-			h.AssertNil(t, tm.Distro)
-		})
-
-		it("doesn't populate if there's an error reading the file", func() {
-			logr := &log.Logger{Handler: memory.New()}
-			tm := files.TargetMetadata{}
-			d := mockDetector{contents: "contentment is the greatest wealth",
-				t:           t,
-				HasFile:     true,
-				ReadFileErr: fmt.Errorf("I'm sorry Dave, I don't even remember exactly what HAL says"),
-			}
-			platform.GetTargetOSFromFileSystem(&d, &tm, logr)
-			h.AssertNil(t, tm.Distro)
-		})
-	})
 }
 
 type mockDetector struct {