-
Notifications
You must be signed in to change notification settings - Fork 293
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1921 from WYGIN/buildpack-new-targets-flag
added targets flag for buildpack new cli
- Loading branch information
Showing
10 changed files
with
526 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,21 +5,25 @@ import ( | |
"fmt" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/buildpacks/pack/internal/style" | ||
"github.com/buildpacks/pack/internal/target" | ||
"github.com/buildpacks/pack/pkg/client" | ||
"github.com/buildpacks/pack/pkg/dist" | ||
"github.com/buildpacks/pack/pkg/logging" | ||
) | ||
|
||
// BuildpackNewFlags define flags provided to the BuildpackNew command | ||
type BuildpackNewFlags struct { | ||
API string | ||
Path string | ||
API string | ||
Path string | ||
// Deprecated: Stacks are deprecated | ||
Stacks []string | ||
Targets []string | ||
Version string | ||
} | ||
|
||
|
@@ -66,11 +70,24 @@ func BuildpackNew(logger logging.Logger, creator BuildpackCreator) *cobra.Comman | |
}) | ||
} | ||
|
||
var targets []dist.Target | ||
if len(flags.Targets) == 0 && len(flags.Stacks) == 0 { | ||
targets = []dist.Target{{ | ||
OS: runtime.GOOS, | ||
Arch: runtime.GOARCH, | ||
}} | ||
} else { | ||
if targets, err = target.ParseTargets(flags.Targets, logger); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
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 | ||
|
@@ -84,7 +101,14 @@ func BuildpackNew(logger logging.Logger, creator BuildpackCreator) *cobra.Comman | |
cmd.Flags().StringVarP(&flags.API, "api", "a", "0.8", "Buildpack API compatibility of the generated buildpack") | ||
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().StringSliceVarP(&flags.Stacks, "stacks", "s", nil, "Stack(s) this buildpack will be compatible with"+stringSliceHelp("stack")) | ||
cmd.Flags().MarkDeprecated("stacks", "prefer `--targets` instead: https://github.com/buildpacks/rfcs/blob/main/text/0096-remove-stacks-mixins.md") | ||
cmd.Flags().StringSliceVarP(&flags.Targets, "targets", "t", nil, | ||
`Targets are the list platforms that one targeting, these are generated as part of scaffolding inside buildpack.toml file. one can provide target platforms in format [os][/arch][/variant]:[distroname@osversion@anotherversion];[distroname@osversion] | ||
- Base case for two different architectures : '--targets "linux/amd64" --targets "linux/arm64"' | ||
- case for distribution version: '--targets "windows/amd64:[email protected]"' | ||
- case for different architecture with distributed versions : '--targets "linux/arm/v6:[email protected]" --targets "linux/arm/v6:[email protected]"' | ||
`) | ||
|
||
AddHelpFlag(cmd, "new") | ||
return cmd | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import ( | |
"bytes" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"testing" | ||
|
||
"github.com/buildpacks/pack/pkg/client" | ||
|
@@ -36,6 +37,10 @@ func testBuildpackNewCommand(t *testing.T, when spec.G, it spec.S) { | |
mockClient *testmocks.MockPackClient | ||
tmpDir string | ||
) | ||
targets := []dist.Target{{ | ||
OS: runtime.GOOS, | ||
Arch: runtime.GOARCH, | ||
}} | ||
|
||
it.Before(func() { | ||
var err error | ||
|
@@ -60,10 +65,7 @@ func testBuildpackNewCommand(t *testing.T, when spec.G, it spec.S) { | |
ID: "example/some-cnb", | ||
Path: filepath.Join(tmpDir, "some-cnb"), | ||
Version: "1.0.0", | ||
Stacks: []dist.Stack{{ | ||
ID: "io.buildpacks.stacks.jammy", | ||
Mixins: []string{}, | ||
}}, | ||
Targets: targets, | ||
}).Return(nil).MaxTimes(1) | ||
|
||
path := filepath.Join(tmpDir, "some-cnb") | ||
|
@@ -82,5 +84,121 @@ func testBuildpackNewCommand(t *testing.T, when spec.G, it spec.S) { | |
h.AssertNotNil(t, err) | ||
h.AssertContains(t, outBuf.String(), "ERROR: directory") | ||
}) | ||
|
||
when("target flag is specified, ", func() { | ||
it("it uses target to generate artifacts", func() { | ||
mockClient.EXPECT().NewBuildpack(gomock.Any(), client.NewBuildpackOptions{ | ||
API: "0.8", | ||
ID: "example/targets", | ||
Path: filepath.Join(tmpDir, "targets"), | ||
Version: "1.0.0", | ||
Targets: []dist.Target{{ | ||
OS: "linux", | ||
Arch: "arm", | ||
ArchVariant: "v6", | ||
Distributions: []dist.Distribution{{ | ||
Name: "ubuntu", | ||
Versions: []string{"14.04", "16.04"}, | ||
}}, | ||
}}, | ||
}).Return(nil).MaxTimes(1) | ||
|
||
path := filepath.Join(tmpDir, "targets") | ||
command.SetArgs([]string{"--path", path, "example/targets", "--targets", "linux/arm/v6:[email protected]@16.04"}) | ||
|
||
err := command.Execute() | ||
h.AssertNil(t, err) | ||
}) | ||
it("it should show error when invalid [os]/[arch] passed", func() { | ||
mockClient.EXPECT().NewBuildpack(gomock.Any(), client.NewBuildpackOptions{ | ||
API: "0.8", | ||
ID: "example/targets", | ||
Path: filepath.Join(tmpDir, "targets"), | ||
Version: "1.0.0", | ||
Targets: []dist.Target{{ | ||
OS: "os", | ||
Arch: "arm", | ||
ArchVariant: "v6", | ||
Distributions: []dist.Distribution{{ | ||
Name: "ubuntu", | ||
Versions: []string{"14.04", "16.04"}, | ||
}}, | ||
}}, | ||
}).Return(nil).MaxTimes(1) | ||
|
||
path := filepath.Join(tmpDir, "targets") | ||
command.SetArgs([]string{"--path", path, "example/targets", "--targets", "os/arm/v6:[email protected]@16.04"}) | ||
|
||
err := command.Execute() | ||
h.AssertNotNil(t, err) | ||
}) | ||
when("it should", func() { | ||
it("support format [os][/arch][/variant]:[name@version@version2];[some-name@version@version2]", func() { | ||
mockClient.EXPECT().NewBuildpack(gomock.Any(), client.NewBuildpackOptions{ | ||
API: "0.8", | ||
ID: "example/targets", | ||
Path: filepath.Join(tmpDir, "targets"), | ||
Version: "1.0.0", | ||
Targets: []dist.Target{ | ||
{ | ||
OS: "linux", | ||
Arch: "arm", | ||
ArchVariant: "v6", | ||
Distributions: []dist.Distribution{ | ||
{ | ||
Name: "ubuntu", | ||
Versions: []string{"14.04", "16.04"}, | ||
}, | ||
{ | ||
Name: "debian", | ||
Versions: []string{"8.10", "10.9"}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
OS: "windows", | ||
Arch: "amd64", | ||
Distributions: []dist.Distribution{ | ||
{ | ||
Name: "windows-nano", | ||
Versions: []string{"10.0.19041.1415"}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}).Return(nil).MaxTimes(1) | ||
|
||
path := filepath.Join(tmpDir, "targets") | ||
command.SetArgs([]string{"--path", path, "example/targets", "--targets", "linux/arm/v6:[email protected]@16.04;[email protected]@10.9", "-t", "windows/amd64:[email protected]"}) | ||
|
||
err := command.Execute() | ||
h.AssertNil(t, err) | ||
}) | ||
}) | ||
when("stacks ", func() { | ||
it("flag should show deprecated message when used", func() { | ||
mockClient.EXPECT().NewBuildpack(gomock.Any(), client.NewBuildpackOptions{ | ||
API: "0.8", | ||
ID: "example/stacks", | ||
Path: filepath.Join(tmpDir, "stacks"), | ||
Version: "1.0.0", | ||
Stacks: []dist.Stack{{ | ||
ID: "io.buildpacks.stacks.jammy", | ||
Mixins: []string{}, | ||
}}, | ||
}).Return(nil).MaxTimes(1) | ||
|
||
path := filepath.Join(tmpDir, "stacks") | ||
output := new(bytes.Buffer) | ||
command.SetOut(output) | ||
command.SetErr(output) | ||
command.SetArgs([]string{"--path", path, "example/stacks", "--stacks", "io.buildpacks.stacks.jammy"}) | ||
|
||
err := command.Execute() | ||
h.AssertNil(t, err) | ||
h.AssertContains(t, output.String(), "Flag --stacks has been deprecated,") | ||
}) | ||
}) | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package target | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/buildpacks/pack/internal/style" | ||
"github.com/buildpacks/pack/pkg/dist" | ||
"github.com/buildpacks/pack/pkg/logging" | ||
) | ||
|
||
func ParseTargets(t []string, logger logging.Logger) (targets []dist.Target, err error) { | ||
for _, v := range t { | ||
target, err := ParseTarget(v, logger) | ||
if err != nil { | ||
return nil, err | ||
} | ||
targets = append(targets, target) | ||
} | ||
return targets, nil | ||
} | ||
|
||
func ParseTarget(t string, logger logging.Logger) (output dist.Target, err error) { | ||
nonDistro, distros, err := getTarget(t, logger) | ||
if v, _ := getSliceAt[string](nonDistro, 0); len(nonDistro) <= 1 && v == "" { | ||
logger.Warn("os/arch must be defined") | ||
} | ||
if err != nil { | ||
return output, err | ||
} | ||
os, arch, variant, err := getPlatform(nonDistro, logger) | ||
if err != nil { | ||
return output, err | ||
} | ||
v, err := ParseDistros(distros, logger) | ||
if err != nil { | ||
return output, err | ||
} | ||
output = dist.Target{ | ||
OS: os, | ||
Arch: arch, | ||
ArchVariant: variant, | ||
Distributions: v, | ||
} | ||
return output, err | ||
} | ||
|
||
func ParseDistros(distroSlice string, logger logging.Logger) (distros []dist.Distribution, err error) { | ||
distro := strings.Split(distroSlice, ";") | ||
if l := len(distro); l == 1 && distro[0] == "" { | ||
return nil, err | ||
} | ||
for _, d := range distro { | ||
v, err := ParseDistro(d, logger) | ||
if err != nil { | ||
return nil, err | ||
} | ||
distros = append(distros, v) | ||
} | ||
return distros, nil | ||
} | ||
|
||
func ParseDistro(distroString string, logger logging.Logger) (distro dist.Distribution, err error) { | ||
d := strings.Split(distroString, "@") | ||
if d[0] == "" || len(d) == 0 { | ||
return distro, errors.Errorf("distro's versions %s cannot be specified without distro's name", style.Symbol("@"+strings.Join(d[1:], "@"))) | ||
} | ||
if len(d) <= 2 && (strings.Contains(strings.Join(d[1:], ""), "") || d[1] == "") { | ||
logger.Warnf("distro with name %s has no specific version!", style.Symbol(d[0])) | ||
} | ||
distro.Name = d[0] | ||
distro.Versions = d[1:] | ||
return distro, err | ||
} | ||
|
||
func getTarget(t string, logger logging.Logger) (nonDistro []string, distros string, err error) { | ||
target := strings.Split(t, ":") | ||
if (len(target) == 1 && target[0] == "") || len(target) == 0 { | ||
return nonDistro, distros, errors.Errorf("invalid target %s, atleast one of [os][/arch][/archVariant] must be specified", t) | ||
} | ||
if len(target) == 2 && target[0] == "" { | ||
v, _ := getSliceAt[string](target, 1) | ||
logger.Warn(style.Warn("adding distros %s without [os][/arch][/variant]", v)) | ||
} else { | ||
i, _ := getSliceAt[string](target, 0) | ||
nonDistro = strings.Split(i, "/") | ||
} | ||
if i, err := getSliceAt[string](target, 1); err == nil { | ||
distros = i | ||
} | ||
return nonDistro, distros, err | ||
} | ||
|
||
func getSliceAt[T interface{}](slice []T, index int) (value T, err error) { | ||
if index < 0 || index >= len(slice) { | ||
return value, errors.Errorf("index out of bound, cannot access item at index %d of slice with length %d", index, len(slice)) | ||
} | ||
|
||
return slice[index], err | ||
} |
Oops, something went wrong.