Skip to content
Merged
22 changes: 22 additions & 0 deletions internal/commands/buildpack_new.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/spf13/cobra"


"github.com/buildpacks/pack/internal/style"
"github.com/buildpacks/pack/pkg/client"
"github.com/buildpacks/pack/pkg/dist"
Expand All @@ -19,7 +20,9 @@ import (
type BuildpackNewFlags struct {
API string
Path string
// Deprecated: use `targets` instead
Stacks []string
Targets dist.Targets
Version string
}

Expand Down Expand Up @@ -66,11 +69,22 @@ func BuildpackNew(logger logging.Logger, creator BuildpackCreator) *cobra.Comman
})
}

var targets dist.Targets
for _, t := range flags.Targets {
targets = append(targets, dist.Target{
OS: t.OS,
Arch: t.Arch,
ArchVariant: t.ArchVariant,
Distributions: t.Distributions,
})
}
Comment thread
ep0ll marked this conversation as resolved.

if err := creator.NewBuildpack(cmd.Context(), client.NewBuildpackOptions{
API: flags.API,
ID: id,
Path: path,
Stacks: stacks,
Targets: targets,
Version: flags.Version,
}); err != nil {
return err
Expand All @@ -85,6 +99,14 @@ func BuildpackNew(logger logging.Logger, creator BuildpackCreator) *cobra.Comman
cmd.Flags().StringVarP(&flags.Path, "path", "p", "", "Path to generate the buildpack")
cmd.Flags().StringVarP(&flags.Version, "version", "V", "1.0.0", "Version of the generated buildpack")
cmd.Flags().StringSliceVarP(&flags.Stacks, "stacks", "s", []string{"io.buildpacks.stacks.jammy"}, "Stack(s) this buildpack will be compatible with"+stringSliceHelp("stack"))
cmd.Flags().MarkDeprecated("stacks", "stacks is deprecated in the favor of `targets`")
cmd.Flags().Var(&flags.Targets, "targets",
Comment thread
ep0ll marked this conversation as resolved.
Outdated
`Targets is a list of platforms that you wish to support. one can provide target platforms in format [os][/arch][/variant]:[name@osversion]
- Base case for two different architectures : '--targets "linux/amd64" --targets "linux/arm64"'
- case for distribution versions: '--targets "windows/amd64:windows-nano@10.0.19041.1415"'
- case for different architecture with distrubuted versions : '--targets "linux/arm/v6:ubuntu@14.04" --targets "linux/arm/v6:ubuntu@16.04"'
- If no name is provided, a random name will be generated.
`)

AddHelpFlag(cmd, "new")
return cmd
Expand Down
2 changes: 1 addition & 1 deletion pkg/buildpack/buildpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Descriptor interface {
Kind() string
Order() dist.Order
Stacks() []dist.Stack
Targets() []dist.Target
Targets() dist.Targets
}

type Blob interface {
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1307,7 +1307,7 @@ func createInlineBuildpack(bp projectTypes.Buildpack, stackID string) (string, e
bp.Version = "0.0.0"
}

if err = createBuildpackTOML(pathToInlineBuilpack, bp.ID, bp.Version, bp.Script.API, []dist.Stack{{ID: stackID}}, nil); err != nil {
if err = createBuildpackTOML(pathToInlineBuilpack, bp.ID, bp.Version, bp.Script.API, []dist.Stack{{ID: stackID}}, dist.Targets{}, nil); err != nil {
return pathToInlineBuilpack, err
}

Expand Down
10 changes: 7 additions & 3 deletions pkg/client/new_buildpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ type NewBuildpackOptions struct {
// version of the output buildpack artifact.
Version string

// The stacks this buildpack will work with
// Deprecated: The stacks this buildpack will work with
Stacks []dist.Stack

// the targets this buildpack will work with
Targets dist.Targets
}

func (c *Client) NewBuildpack(ctx context.Context, opts NewBuildpackOptions) error {
err := createBuildpackTOML(opts.Path, opts.ID, opts.Version, opts.API, opts.Stacks, c)
err := createBuildpackTOML(opts.Path, opts.ID, opts.Version, opts.API, opts.Stacks, opts.Targets, c)
if err != nil {
return err
}
Expand Down Expand Up @@ -94,7 +97,7 @@ func createBinScript(path, name, contents string, c *Client) error {
return nil
}

func createBuildpackTOML(path, id, version, apiStr string, stacks []dist.Stack, c *Client) error {
func createBuildpackTOML(path, id, version, apiStr string, stacks []dist.Stack, targets dist.Targets, c *Client) error {
api, err := api.NewVersion(apiStr)
if err != nil {
return err
Expand All @@ -103,6 +106,7 @@ func createBuildpackTOML(path, id, version, apiStr string, stacks []dist.Stack,
buildpackTOML := dist.BuildpackDescriptor{
WithAPI: api,
WithStacks: stacks,
WithTargets: targets,
WithInfo: dist.ModuleInfo{
ID: id,
Version: version,
Expand Down
190 changes: 189 additions & 1 deletion pkg/dist/buildmodule.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dist

import (
"strings"

"github.com/pkg/errors"

"github.com/buildpacks/pack/internal/style"
Expand Down Expand Up @@ -55,10 +57,196 @@ type Stack struct {
type Target struct {
OS string `json:"os" toml:"os"`
Arch string `json:"arch" toml:"arch"`
Distributions []Distribution `json:"distributions,omitempty" toml:"distributions,omitempty"`
ArchVariant string `json:"variant,omitempty" toml:"variant"`
Distributions Distributions `json:"distributions,omitempty" toml:"distributions,omitempty"`
}

type Targets []Target

func(*Targets) Type() string {
return "Targets"
}

func(targets *Targets) String() string {
var sb strings.Builder
// each target is converted to string & is separated by `,`
for _,t := range *targets {
sb.WriteString(t.String() + ",")
}
return sb.String()
}

// set the value of multiple targets in a single command where each target is separated by a `,` or pass multiple `--targets` to specify each target individually.
func(targets *Targets) Set(value string) error {
// spliting multiple targets into a slice of targets by `,` separater
s := strings.Split(value, ",")
for i,v := range *targets {
// for each individual target set it's value from s[i] & getting an error
err := v.Set(s[i])
// if error is not nill return error
if err != nil {
return err
}
}
return nil
}

func(target *Target) Type() string {
return "Target"
}

// `--target` is passed in the format of `[os][/arch][/variant]:[name@osversion]`
// one can specify multiple distro versions in the format `name@osversion1@osversion2@...`
// one can specify multiple distros seperated by `;`
// example:
// `name1@osverion1;name2@osversion2;...`
func(target *Target) String() string {
var s string
// if target's OS is not nill append it to string
if target.OS != "" {
s += target.OS
}
// if target's Arch is not nill append it to string
if target.Arch != "" {
// if os is not defined append target's arch without '/'
if s == "" {
s += target.Arch
}
// if os is defined append target's arch separated by '/'
s += "/" + target.Arch
}
// if target's ArchVariant is not nill append it to string
if target.ArchVariant != "" {
// if os and/or arch is/are not defined append target's archVariant without '/'
if s == "" {
s += target.ArchVariant
}
// if os and/or arch is/are not defined append target's archVariant separated by '/'
s += "/" + target.ArchVariant
}

// if distros are nill return the string `s`
if target.Distributions != nil {
// a map of distributions with distro's name as key and versions(slice of string) as value
var v map[string][]string
for _,d := range target.Distributions {
// for each distro add version to map with key as distro's name
v[d.Name] = append(v[d.Name], d.Versions...)
}
// after adding all distributions to map
// if [os] or [arch] or [archVariant] is defined append distro to string in format `:[name]@[version1]@[version2];[name1]@[version1]@[version2];`
if s == "" {
for k,d := range v {
if len(d) > 0 {
s += k + "@" + strings.Join(v[k], "@") + ";"
}
// if no version is specified append distro to string in format `[name];[name1];[name2];` without any version
s += k + ";"
}
}
// after adding all distributions to map
// if [os] or [arch] or [archVariant] is defined append distro to string in format `[name]@[version1]@[version2];[name1]@[version1]@[version2];`
s += ":"
for k,d := range v {
if len(d) > 0 {
s += k + "@" + strings.Join(v[k], "@") + ";"
}
s += k + ";"
}
}
// return the final stringified version of target
return s
}

func(target *Target) Set(value string) error {
// each target can only have one `:` to separate [os] [arch] [archVariant] with distro
osDistro := strings.Split(value, ":")
if len(osDistro) > 2 {
return errors.Errorf("invalid format, target should not have more than one `:` separator. got %s number of separators", len(osDistro) - 1)
}
var os, distro string

if len(osDistro) == 2 {
// get the [os][/arch][/archVariant] at index 0 if osDistro is of length 2
os = osDistro[0]
// get [name@osversion] from index 1 if osDistro has length 2
distro = osDistro[1]
} else {
// if osDistro is nil or with length 1 add value of osDistro[0] to
// assign to `os` variable if string has separator '/'
// else assign it to distro
if v := osDistro[0]; strings.ContainsRune(v, '/') {
os = v
} else {
distro = v
}
}
// separate [os] [arch] [archVariant] with separator '/' and assign values to their respective variable
osArch := strings.SplitN(os, "/", 3)
if os := osArch[0]; os != "" {
target.OS = os
}
if arch := osArch[1]; arch != "" {
target.Arch = arch
}
if archVariant := osArch[2]; archVariant != "" {
target.ArchVariant = archVariant
}

// split the distros with the separator ';'
err := target.Distributions.Set(distro)
if err != nil {
return err
}

return nil
}

func(distros *Distributions) String() string {
var s strings.Builder
for _,d := range *distros {
s.WriteString(d.String() + ";")
}
return s.String()
}

func(distros *Distributions) Set(value string) error {
v := strings.Split(value, ";")
for i,d := range *distros {
err := d.Set(v[i])
if err != nil {
return err
}
}
return nil
}

func(distros *Distributions) Type() string {
return "Distributions"
}

func(distro *Distribution) String() string {
return distro.Name + strings.Join(distro.Versions, "@")
}

func(distro *Distribution) Set(value string) error {
v := strings.Split(value, "@")
for i,d := range v {
if i == 0 {
distro.Name = d
}
distro.Versions = append(distro.Versions, d)
}
return nil
}

func(distro *Distribution) Type() string {
return "Distribution"
}

type Distribution struct {
Name string `json:"name,omitempty" toml:"name,omitempty"`
Versions []string `json:"versions,omitempty" toml:"versions,omitempty"`
}

type Distributions []Distribution
4 changes: 2 additions & 2 deletions pkg/dist/buildpack_descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type BuildpackDescriptor struct {
WithAPI *api.Version `toml:"api"`
WithInfo ModuleInfo `toml:"buildpack"`
WithStacks []Stack `toml:"stacks"`
WithTargets []Target `toml:"targets,omitempty"`
WithTargets Targets `toml:"targets,omitempty"`
WithOrder Order `toml:"order"`
WithWindowsBuild bool
WithLinuxBuild bool
Expand Down Expand Up @@ -134,7 +134,7 @@ func (b *BuildpackDescriptor) Stacks() []Stack {
return b.WithStacks
}

func (b *BuildpackDescriptor) Targets() []Target {
func (b *BuildpackDescriptor) Targets() Targets {
return b.WithTargets
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/dist/extension_descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ func (e *ExtensionDescriptor) Stacks() []Stack {
return nil
}

func (e *ExtensionDescriptor) Targets() []Target {
func (e *ExtensionDescriptor) Targets() Targets {
return nil
}
2 changes: 1 addition & 1 deletion pkg/dist/layers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type Descriptor interface {
Info() ModuleInfo
Order() Order
Stacks() []Stack
Targets() []Target
Targets() Targets
}

func LayerDiffID(layerTarPath string) (v1.Hash, error) {
Expand Down