From 8dddcc9b69523d961132ca166b979d9edcc8515c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Suszy=C5=84ski?= Date: Wed, 13 Oct 2021 20:50:06 +0200 Subject: [PATCH] LDflags for images works --- config/types.go | 11 ++++- pkg/artifact/binary.go | 6 +-- pkg/artifact/image.go | 1 + pkg/artifact/ko_build.go | 58 ++++++++++++++++++++++++--- pkg/artifact/ko_publish.go | 6 +-- pkg/ldflags/builder.go | 20 +++++---- testing.go | 2 +- tests/example/.ko.yaml | 1 + tests/example/Magefile.go | 12 ++---- tests/example/cmd/dummy/Containerfile | 7 ---- tests/project_build_test.go | 18 +++++++++ 11 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 tests/example/.ko.yaml delete mode 100644 tests/example/cmd/dummy/Containerfile diff --git a/config/types.go b/config/types.go index 9706dc0..947aa10 100644 --- a/config/types.go +++ b/config/types.go @@ -68,6 +68,13 @@ type Publisher interface { // Resolver is a func that resolves to a string. type Resolver func() string +// StaticResolver can be used to create a Resolver from static data. +func StaticResolver(value string) Resolver { + return func() string { + return value + } +} + // MageTag holds a mage tag. type MageTag struct { Color color.Attribute @@ -103,8 +110,8 @@ type Version struct { // Metadata holds additional contextual information. type Metadata struct { - Name string - Args map[string]Resolver + Name string + BuildVariables map[string]Resolver } func (m Metadata) GetName() string { diff --git a/pkg/artifact/binary.go b/pkg/artifact/binary.go index cbe0127..1e0ea11 100644 --- a/pkg/artifact/binary.go +++ b/pkg/artifact/binary.go @@ -82,15 +82,15 @@ func (b Binary) buildForPlatform( "build", } version := config.Actual().Version - if version != nil || len(b.Args) > 0 { + if version != nil || len(b.BuildVariables) > 0 { builder := ldflags.NewBuilder() if version != nil { builder.Add(version.Path, version.Resolver) } - for key, resolver := range b.Args { + for key, resolver := range b.BuildVariables { builder.Add(key, resolver) } - args = builder.Build(args) + args = builder.BuildOnto(args) } binary := fullBinaryName(platform, name) args = append(args, "-o", binary, fullBinaryDirectory(name)) diff --git a/pkg/artifact/image.go b/pkg/artifact/image.go index ed43551..1aebd1e 100644 --- a/pkg/artifact/image.go +++ b/pkg/artifact/image.go @@ -12,6 +12,7 @@ const imageReferenceKey = "oci.image.reference" // Image is an OCI image that will be built from a binary. type Image struct { config.Metadata + Labels map[string]config.Resolver Architectures []platform.Architecture } diff --git a/pkg/artifact/ko_build.go b/pkg/artifact/ko_build.go index 1ff3f50..b36576f 100644 --- a/pkg/artifact/ko_build.go +++ b/pkg/artifact/ko_build.go @@ -13,6 +13,7 @@ import ( "github.com/google/ko/pkg/commands/options" "github.com/wavesoftware/go-magetasks/config" "github.com/wavesoftware/go-magetasks/pkg/files" + "github.com/wavesoftware/go-magetasks/pkg/ldflags" "github.com/wavesoftware/go-magetasks/pkg/output/color" "golang.org/x/mod/modfile" ) @@ -38,13 +39,17 @@ func (kb KoBuilder) Build(artifact config.Artifact, notifier config.Notifier) co if !ok { return config.Result{Error: ErrInvalidArtifact} } - bo := &options.BuildOptions{} - ctx := config.Actual().Context - builder, err := commands.NewBuilder(ctx, bo) + importPath, err := imageImportPath(image) if err != nil { return resultErrKoFailed(err) } - importPath, err := imageImportPath(image) + bo := &options.BuildOptions{ + Platform: buildPlatformString(image), + Labels: buildLabels(image, importPath), + } + fillInLdflags(bo, importPath, image) + ctx := config.Actual().Context + builder, err := commands.NewBuilder(ctx, bo) if err != nil { return resultErrKoFailed(err) } @@ -63,6 +68,49 @@ func (kb KoBuilder) Build(artifact config.Artifact, notifier config.Notifier) co }} } +func fillInLdflags(bo *options.BuildOptions, importPath string, image Image) { + version := config.Actual().Version + args := make([]string, 0) + if version != nil || len(image.BuildVariables) > 0 { + builder := ldflags.NewBuilder() + if version != nil { + builder.Add(version.Path, version.Resolver) + } + for key, resolver := range image.BuildVariables { + builder.Add(key, resolver) + } + args = builder.Build() + } + if len(args) > 0 { + bo.BuildConfigs = map[string]build.Config{ + importPath: { + ID: "ldflags-config", + Ldflags: args, + }, + } + } +} + +func buildLabels(image Image, importPath string) []string { + labels := make([]string, 0, len(image.Labels)) + if version := config.Actual().Version; version != nil { + labels = append(labels, fmt.Sprintf("version=%s", version.Resolver())) + } + labels = append(labels, fmt.Sprintf("%s=%s", koImportPath, importPath)) + for key, resolver := range image.Labels { + labels = append(labels, fmt.Sprintf("%s=%s", key, resolver())) + } + return labels +} + +func buildPlatformString(im Image) string { + platforms := make([]string, len(im.Architectures)) + for i, architecture := range im.Architectures { + platforms[i] = fmt.Sprintf("linux/%s", architecture) + } + return strings.Join(platforms, ",") +} + func calculateImageReference(result build.Result, artifact config.Artifact) (name.Reference, error) { kp := KoPublisher{} po, err := kp.publishOptions() @@ -99,7 +147,7 @@ func imageImportPath(image Image) (string, error) { return "", err } importPath := rs.resolve(binDir) - if resolver, ok := image.Args[koImportPath]; ok { + if resolver, ok := image.Labels[koImportPath]; ok { importPath = resolver() } return importPath, nil diff --git a/pkg/artifact/ko_publish.go b/pkg/artifact/ko_publish.go index b6d88a5..92056c0 100644 --- a/pkg/artifact/ko_publish.go +++ b/pkg/artifact/ko_publish.go @@ -76,9 +76,9 @@ func (kp KoPublisher) publishOptions() (*options.PublishOptions, error) { opts := &options.PublishOptions{ BaseImportPaths: true, Push: true, - Tags: []string{ - config.Actual().Version.Resolver(), - }, + } + if version := config.Actual().Version; version != nil { + opts.Tags = []string{version.Resolver()} } if v, ok := os.LookupEnv(magetasksImageBasenameSeparator); ok { opts.ImageNameSeparator = v diff --git a/pkg/ldflags/builder.go b/pkg/ldflags/builder.go index 2d8202e..23aa127 100644 --- a/pkg/ldflags/builder.go +++ b/pkg/ldflags/builder.go @@ -11,8 +11,10 @@ import ( type Builder interface { // Add a name and a resolver to the builder. Add(name string, resolver config.Resolver) Builder - // Build onto the args. - Build(args []string) []string + // Build into slice args for ldflags. + Build() []string + // BuildOnto provided args. + BuildOnto(args []string) []string } // NewBuilder creates a new builder. @@ -31,13 +33,17 @@ func (d *defaultBuilder) Add(name string, resolver config.Resolver) Builder { return d } -func (d *defaultBuilder) Build(args []string) []string { - if len(args) == 0 { - return args - } +func (d *defaultBuilder) Build() []string { collected := make([]string, 0, len(d.resolvers)) + if len(d.resolvers) == 0 { + return collected + } for name, resolver := range d.resolvers { collected = append(collected, fmt.Sprintf("-X %s=%s", name, resolver())) } - return append(args, "-ldflags", strings.Join(collected, " ")) + return collected +} + +func (d *defaultBuilder) BuildOnto(args []string) []string { + return append(args, "-ldflags", strings.Join(d.Build(), " ")) } diff --git a/testing.go b/testing.go index 2de4ee1..8dbe2da 100644 --- a/testing.go +++ b/testing.go @@ -34,7 +34,7 @@ func appendVersion(args []string) []string { if version != nil { args = ldflags.NewBuilder(). Add(version.Path, version.Resolver). - Build(args) + BuildOnto(args) } return args } diff --git a/tests/example/.ko.yaml b/tests/example/.ko.yaml new file mode 100644 index 0000000..eaa5b97 --- /dev/null +++ b/tests/example/.ko.yaml @@ -0,0 +1 @@ +defaultBaseImage: registry.access.redhat.com/ubi8/ubi-minimal diff --git a/tests/example/Magefile.go b/tests/example/Magefile.go index 608337c..89d2c97 100644 --- a/tests/example/Magefile.go +++ b/tests/example/Magefile.go @@ -22,13 +22,9 @@ var Default = magetasks.Build func init() { //nolint:gochecknoinits dummy := artifact.Image{ - Metadata: config.Metadata{ - Name: "dummy", - Args: map[string]config.Resolver{ - "DESC": func() string { - return "A dummy image" - }, - }, + Metadata: config.Metadata{Name: "dummy"}, + Labels: map[string]config.Resolver{ + "description": config.StaticResolver("A dummy image description"), }, Architectures: []platform.Architecture{ platform.AMD64, platform.ARM64, platform.S390X, platform.PPC64LE, @@ -37,7 +33,7 @@ func init() { //nolint:gochecknoinits other := artifact.Binary{ Metadata: config.Metadata{ Name: "other", - Args: map[string]config.Resolver{ + BuildVariables: map[string]config.Resolver{ metadata.ImagePath(): artifact.ImageReferenceOf(dummy), }, }, diff --git a/tests/example/cmd/dummy/Containerfile b/tests/example/cmd/dummy/Containerfile deleted file mode 100644 index 1e6d86e..0000000 --- a/tests/example/cmd/dummy/Containerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM registry.access.redhat.com/ubi8/ubi-minimal -ARG DESC -LABEL description=$DESC \ - io.k8s.description=$DESC - -COPY ./build/_output/bin/dummy /dummy -CMD ["/dummy"] diff --git a/tests/project_build_test.go b/tests/project_build_test.go index 48cff1a..192062a 100644 --- a/tests/project_build_test.go +++ b/tests/project_build_test.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "runtime" + "strings" "testing" "gotest.tools/v3/assert" @@ -19,9 +20,26 @@ func TestProjectBuild(t *testing.T) { func execCmd(tb testing.TB, dir, name string, args ...string) { tb.Helper() c := exec.Command(name, args...) + c.Env = env(func(e string) bool { + return e == "GOARCH" || e == "GOOS" || e == "GOARM" + }) c.Dir = dir c.Stdout = os.Stdout c.Stderr = os.Stderr err := c.Run() assert.NilError(tb, err) } + +func env(filter func(string) bool) []string { + ret := make([]string, 0, len(os.Environ())) + for _, e := range os.Environ() { + envPair := strings.SplitN(e, "=", 2) + key := envPair[0] + if filter(key) { + continue + } + + ret = append(ret, e) + } + return ret +}