Repository: buildpacks/lifecycle (go)

Issue:
`[[targets.distros]]` in `buildpack.toml` is ignored if the run image distro name/version isn't available
### Summary

The Buildpack API 0.10 spec says:

> Metadata specified in `[[targets]]` is validated against the runtime and build-time base images.
> * A buildpack target satisfies a base image target when `os`, `arch`, and `variant` match and at least one distribution in `distros` (if provided) matches

(https://github.com/buildpacks/spec/blob/buildpack/v0.10/buildpack.md#targets)

As such, if a buildpack's `buildpack.toml` contains:

```toml
[[targets]]
os = "linux"
arch = "amd64"

[[targets.distros]]
name = "ubuntu"
version = "123"
```

...then I would expect the buildpack to only pass detection if the run image's distro name was "ubuntu" and version was "123".

However, it seems that if the distro name/version could not be determined by `lifecycle` (for example, the run image doesn't have the `io.buildpacks.base.distro.*` Docker labels set and the `/etc/os-release` fallback isn't available or [is broken](https://github.com/buildpacks/lifecycle/issues/1336)), then `lifecycle` ignores the `[[targets.distros]]` requirements and instead matches against any distro.

The cause of this appears to be the `base.Distro == nil` part of this conditional:
https://github.com/buildpacks/lifecycle/blob/435d226f1ed54b0bec806716ba79e14a2a093736/platform/target_data.go#L94-L96

---

### Reproduction

##### Steps

1. `pack buildpack new testcase-buildpack-targets --api 0.10 --targets "linux/amd64"`
1. `echo -e '\n[[targets.distros]]\nname = "ubuntu"\nversion = "123"' >> testcase-buildpack-targets/buildpack.toml`
1. `pack build --builder heroku/builder:22 --run-image heroku/heroku:22-cnb.v122 --buildpack testcase-buildpack-targets/ --path testcase-buildpack-targets/ testapp --verbose`

##### Current behavior

Buildpack detection unexpectedly passes:

```
$ pack build --builder heroku/builder:22 --run-image heroku/heroku:22-cnb.v122 --buildpack testcase-buildpack-targets/ --path testcase-buildpack-targets/ testapp --verbose
...
===> ANALYZING
...
Run image info in analyzed metadata is: 
{"Reference":"6d5f868e91eba6a6ec22a9a9b9b31a2fd486a0f0f57082fd0864e0bf0b3e4395","Image":"heroku/heroku:22-cnb.v122","Extend":false,"target":{"os":"linux","arch":"amd64"}}
===> DETECTING
...
Checking for match against descriptor: {linux amd64  [{ubuntu 123}]}
======== Results ========
pass: testcase-buildpack-targets@1.0.0
```

...even though the buildpack declares that it only supports:

```
[[targets.distros]]
name = "ubuntu"
version = "123"
```

...and the run image's Ubuntu version is not "123".

##### Expected behavior

I would expect buildpack detection to fail, like how it fails if I use a newer version of our run image (by omitting the `--run-image heroku/heroku:22-cnb.v122` argument) that has the relevant `io.buildpacks.base.distro.*` Docker labels set:

```
$ pack build --builder heroku/builder:22 --buildpack testcase-buildpack-targets/ --path testcase-b
uildpack-targets/ testapp --verbose
...
===> ANALYZING
...
Run image info in analyzed metadata is: 
{"Reference":"9e85c0884ff5a53485a1f53e4c3f958811ea755ddb24885e55b2bcdec2ddf7f1","Image":"heroku/heroku:22-cnb","Extend":false,"target":{"os":"linux","arch":"amd64","distro":{"name":"ubuntu","version":"22.04"}}}
===> DETECTING
...
Checking for match against descriptor: {linux amd64  [{ubuntu 123}]}
======== Error: testcase-buildpack-targets@1.0.0 ========
unable to satisfy target os/arch constraints; run image: {"os":"linux","arch":"amd64","distro":{"name":"ubuntu","version":"22.04"}}, buildpack: [{"os":"linux","arch":"amd64","distros":[{"name":"ubuntu","version":"123"}]}]
======== Results ========
err:  testcase-buildpack-targets@1.0.0
```

---

### Context

##### lifecycle version
`0.19.3`

##### platform version(s)

```
$ pack report
Pack:
  Version:  0.33.2+git-f2cffc4.build-5562
  OS/Arch:  darwin/arm64
```

The build is using Platform API 0.12 and Buildpack API 0.10.


Pull request:
When a buildpack declares distro information, but a base image does not, consider it not a match
<!-- 🎉🎉🎉 Thank you for the PR!!! 🎉🎉🎉 -->

### Summary
<!-- Please describe your changes at a high level. -->

Fixes https://github.com/buildpacks/lifecycle/issues/1337

#### 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 -->

The lifecycle, when determining if a base image satisfies target constraints declared by a buildpack, fails if the buildpack declares distro information but the base image does not (fixes spec compliance)

---

### Related
<!-- If this PR addresses an issue, please provide the issue number below. -->

Resolves #1337

---

### Context
<!-- Add any other context that may help reviewers (e.g., code that requires special attention, etc.). -->



Patch:
diff --git a/platform/target_data.go b/platform/target_data.go
index 26977d67..e88954db 100644
--- a/platform/target_data.go
+++ b/platform/target_data.go
@@ -91,9 +91,12 @@ func TargetSatisfiedForBuild(base files.TargetMetadata, module buildpack.TargetM
 	if !matches(base.ArchVariant, module.ArchVariant) {
 		return false
 	}
-	if base.Distro == nil || len(module.Distros) == 0 {
+	if len(module.Distros) == 0 {
 		return true
 	}
+	if base.Distro == nil {
+		return false
+	}
 	foundMatchingDist := false
 	for _, modDist := range module.Distros {
 		if matches(base.Distro.Name, modDist.Name) && matches(base.Distro.Version, modDist.Version) {


Test patch:
diff --git a/.github/workflows/test-s390x.yml b/.github/workflows/test-s390x.yml
index 546d9395..2340a9cc 100644
--- a/.github/workflows/test-s390x.yml
+++ b/.github/workflows/test-s390x.yml
@@ -32,7 +32,7 @@ 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 }}
+          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
           #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
diff --git a/platform/target_data_test.go b/platform/target_data_test.go
index cc9bd5dc..2742f8f4 100644
--- a/platform/target_data_test.go
+++ b/platform/target_data_test.go
@@ -41,14 +41,14 @@ func testTargetData(t *testing.T, when spec.G, it spec.S) {
 						})
 					})
 
-					when("has extra information", func() {
-						it("matches", func() {
+					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{
 								OS:      baseTarget.OS,
 								Arch:    baseTarget.Arch,
 								Distros: []buildpack.OSDistro{{Name: "a", Version: "2"}},
-							}), true)
+							}), false)
 						})
 					})
 				})