github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/dist/buildpack_descriptor.go (about)

     1  package dist
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/buildpacks/lifecycle/api"
    10  
    11  	"github.com/buildpacks/pack/internal/stringset"
    12  	"github.com/buildpacks/pack/internal/style"
    13  )
    14  
    15  type BuildpackDescriptor struct {
    16  	WithAPI          *api.Version `toml:"api"`
    17  	WithInfo         ModuleInfo   `toml:"buildpack"`
    18  	WithStacks       []Stack      `toml:"stacks,omitempty"`
    19  	WithTargets      []Target     `toml:"targets,omitempty"`
    20  	WithOrder        Order        `toml:"order"`
    21  	WithWindowsBuild bool
    22  	WithLinuxBuild   bool
    23  }
    24  
    25  func (b *BuildpackDescriptor) EscapedID() string {
    26  	return strings.ReplaceAll(b.Info().ID, "/", "_")
    27  }
    28  
    29  func (b *BuildpackDescriptor) EnsureStackSupport(stackID string, providedMixins []string, validateRunStageMixins bool) error {
    30  	if len(b.Stacks()) == 0 {
    31  		return nil // Order buildpack or a buildpack using Targets, no validation required
    32  	}
    33  
    34  	bpMixins, err := b.findMixinsForStack(stackID)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	if !validateRunStageMixins {
    40  		var filtered []string
    41  		for _, m := range bpMixins {
    42  			if !strings.HasPrefix(m, "run:") {
    43  				filtered = append(filtered, m)
    44  			}
    45  		}
    46  		bpMixins = filtered
    47  	}
    48  
    49  	_, missing, _ := stringset.Compare(providedMixins, bpMixins)
    50  	if len(missing) > 0 {
    51  		sort.Strings(missing)
    52  		return fmt.Errorf("buildpack %s requires missing mixin(s): %s", style.Symbol(b.Info().FullName()), strings.Join(missing, ", "))
    53  	}
    54  	return nil
    55  }
    56  
    57  func (b *BuildpackDescriptor) EnsureTargetSupport(givenOS, givenArch, givenDistroName, givenDistroVersion string) error {
    58  	if len(b.Targets()) == 0 {
    59  		if (!b.WithLinuxBuild && !b.WithWindowsBuild) || len(b.Stacks()) > 0 { // nolint
    60  			return nil // Order buildpack or stack buildpack, no validation required
    61  		} else if b.WithLinuxBuild && givenOS == DefaultTargetOSLinux && givenArch == DefaultTargetArch {
    62  			return nil
    63  		} else if b.WithWindowsBuild && givenOS == DefaultTargetOSWindows && givenArch == DefaultTargetArch {
    64  			return nil
    65  		}
    66  	}
    67  	for _, bpTarget := range b.Targets() {
    68  		if bpTarget.OS == givenOS {
    69  			if bpTarget.Arch == "" || givenArch == "" || bpTarget.Arch == givenArch {
    70  				if len(bpTarget.Distributions) == 0 || givenDistroName == "" || givenDistroVersion == "" {
    71  					return nil
    72  				}
    73  				for _, bpDistro := range bpTarget.Distributions {
    74  					if bpDistro.Name == givenDistroName && bpDistro.Version == givenDistroVersion {
    75  						return nil
    76  					}
    77  				}
    78  			}
    79  		}
    80  	}
    81  	type osDistribution struct {
    82  		Name    string `json:"name,omitempty"`
    83  		Version string `json:"version,omitempty"`
    84  	}
    85  	type target struct {
    86  		OS           string         `json:"os"`
    87  		Arch         string         `json:"arch"`
    88  		Distribution osDistribution `json:"distribution"`
    89  	}
    90  	return fmt.Errorf(
    91  		"unable to satisfy target os/arch constraints; build image: %s, buildpack %s: %s",
    92  		toJSONMaybe(target{
    93  			OS:           givenOS,
    94  			Arch:         givenArch,
    95  			Distribution: osDistribution{Name: givenDistroName, Version: givenDistroVersion},
    96  		}),
    97  		style.Symbol(b.Info().FullName()),
    98  		toJSONMaybe(b.Targets()),
    99  	)
   100  }
   101  
   102  func toJSONMaybe(v interface{}) string {
   103  	b, err := json.Marshal(v)
   104  	if err != nil {
   105  		return fmt.Sprintf("%s", v) // hopefully v is a Stringer
   106  	}
   107  	return string(b)
   108  }
   109  
   110  func (b *BuildpackDescriptor) Kind() string {
   111  	return "buildpack"
   112  }
   113  
   114  func (b *BuildpackDescriptor) API() *api.Version {
   115  	return b.WithAPI
   116  }
   117  
   118  func (b *BuildpackDescriptor) Info() ModuleInfo {
   119  	return b.WithInfo
   120  }
   121  
   122  func (b *BuildpackDescriptor) Order() Order {
   123  	return b.WithOrder
   124  }
   125  
   126  func (b *BuildpackDescriptor) Stacks() []Stack {
   127  	return b.WithStacks
   128  }
   129  
   130  func (b *BuildpackDescriptor) Targets() []Target {
   131  	return b.WithTargets
   132  }
   133  
   134  func (b *BuildpackDescriptor) findMixinsForStack(stackID string) ([]string, error) {
   135  	for _, s := range b.Stacks() {
   136  		if s.ID == stackID || s.ID == "*" {
   137  			return s.Mixins, nil
   138  		}
   139  	}
   140  	return nil, fmt.Errorf("buildpack %s does not support stack %s", style.Symbol(b.Info().FullName()), style.Symbol(stackID))
   141  }