mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
chore(multiarch): helper to parse targetArchitectures (#3525)
* chore(docker): helper to parse targetArchitectures * missing files
This commit is contained in:
parent
93e3801945
commit
c888e21e6c
@ -20,6 +20,7 @@ import (
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/multiarch"
|
||||
"github.com/SAP/jenkins-library/pkg/versioning"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
@ -177,8 +178,14 @@ func runGolangBuild(config *golangBuildOptions, telemetryData *telemetry.CustomD
|
||||
|
||||
binaries := []string{}
|
||||
|
||||
for _, architecture := range config.TargetArchitectures {
|
||||
binary, err := runGolangBuildPerArchitecture(config, utils, ldflags, architecture)
|
||||
platforms, err := multiarch.ParsePlatformStrings(config.TargetArchitectures)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, platform := range platforms {
|
||||
binary, err := runGolangBuildPerArchitecture(config, utils, ldflags, platform)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@ -393,12 +400,11 @@ func prepareLdflags(config *golangBuildOptions, utils golangBuildUtils, envRootP
|
||||
return generatedLdflags.String(), nil
|
||||
}
|
||||
|
||||
func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags, architecture string) (string, error) {
|
||||
func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags string, architecture multiarch.Platform) (string, error) {
|
||||
var binaryName string
|
||||
|
||||
envVars := os.Environ()
|
||||
goos, goarch := splitTargetArchitecture(architecture)
|
||||
envVars = append(envVars, fmt.Sprintf("GOOS=%v", goos), fmt.Sprintf("GOARCH=%v", goarch))
|
||||
envVars = append(envVars, fmt.Sprintf("GOOS=%v", architecture.OS), fmt.Sprintf("GOARCH=%v", architecture.Arch))
|
||||
|
||||
if !config.CgoEnabled {
|
||||
envVars = append(envVars, "CGO_ENABLED=0")
|
||||
@ -408,10 +414,10 @@ func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuild
|
||||
buildOptions := []string{"build"}
|
||||
if len(config.Output) > 0 {
|
||||
fileExtension := ""
|
||||
if goos == "windows" {
|
||||
if architecture.OS == "windows" {
|
||||
fileExtension = ".exe"
|
||||
}
|
||||
binaryName = fmt.Sprintf("%v-%v.%v%v", config.Output, goos, goarch, fileExtension)
|
||||
binaryName = fmt.Sprintf("%v-%v.%v%v", config.Output, architecture.OS, architecture.Arch, fileExtension)
|
||||
buildOptions = append(buildOptions, "-o", binaryName)
|
||||
}
|
||||
buildOptions = append(buildOptions, config.BuildFlags...)
|
||||
@ -423,18 +429,11 @@ func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuild
|
||||
if err := utils.RunExecutable("go", buildOptions...); err != nil {
|
||||
log.Entry().Debugf("buildOptions: %v", buildOptions)
|
||||
log.SetErrorCategory(log.ErrorBuild)
|
||||
return "", fmt.Errorf("failed to run build for %v.%v: %w", goos, goarch, err)
|
||||
return "", fmt.Errorf("failed to run build for %v.%v: %w", architecture.OS, architecture.Arch, err)
|
||||
}
|
||||
return binaryName, nil
|
||||
}
|
||||
|
||||
func splitTargetArchitecture(architecture string) (string, string) {
|
||||
// architecture expected to be in format os,arch due to possibleValues check of step
|
||||
|
||||
architectureParts := strings.Split(architecture, ",")
|
||||
return architectureParts[0], architectureParts[1]
|
||||
}
|
||||
|
||||
// lookupPrivateModulesRepositories returns a slice of all modules that match the given glob pattern
|
||||
func lookupGolangPrivateModulesRepositories(goModFile *modfile.File, globPattern string, utils golangBuildUtils) ([]string, error) {
|
||||
if globPattern == "" {
|
||||
|
@ -11,7 +11,9 @@ import (
|
||||
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
"github.com/SAP/jenkins-library/pkg/mock"
|
||||
"github.com/SAP/jenkins-library/pkg/multiarch"
|
||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
@ -603,7 +605,7 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
|
||||
config := golangBuildOptions{}
|
||||
utils := newGolangBuildTestsUtils()
|
||||
ldflags := ""
|
||||
architecture := "linux,amd64"
|
||||
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
|
||||
|
||||
binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
||||
assert.NoError(t, err)
|
||||
@ -621,7 +623,7 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
|
||||
config := golangBuildOptions{BuildFlags: []string{"--flag1", "val1", "--flag2", "val2"}, Output: "testBin", Packages: []string{"./test/.."}}
|
||||
utils := newGolangBuildTestsUtils()
|
||||
ldflags := "-X test=test"
|
||||
architecture := "linux,amd64"
|
||||
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
|
||||
|
||||
binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
||||
assert.NoError(t, err)
|
||||
@ -638,7 +640,7 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
|
||||
config := golangBuildOptions{Output: "testBin"}
|
||||
utils := newGolangBuildTestsUtils()
|
||||
ldflags := ""
|
||||
architecture := "windows,amd64"
|
||||
architecture, _ := multiarch.ParsePlatformString("windows,amd64")
|
||||
|
||||
binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
||||
assert.NoError(t, err)
|
||||
@ -653,7 +655,7 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) {
|
||||
utils := newGolangBuildTestsUtils()
|
||||
utils.ShouldFailOnCommand = map[string]error{"go build": fmt.Errorf("execution error")}
|
||||
ldflags := ""
|
||||
architecture := "linux,amd64"
|
||||
architecture, _ := multiarch.ParsePlatformString("linux,amd64")
|
||||
|
||||
_, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture)
|
||||
assert.EqualError(t, err, "failed to run build for linux.amd64: execution error")
|
||||
|
73
pkg/multiarch/multiarch.go
Normal file
73
pkg/multiarch/multiarch.go
Normal file
@ -0,0 +1,73 @@
|
||||
package multiarch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||
)
|
||||
|
||||
var knownGoos = []string{"aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "ios", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos"}
|
||||
var knownGoarch = []string{"386", "amd64", "amd64p32", "arm", "arm64", "arm64be", "armbe", "loong64", "mips", "mips64", "mips64le", "mips64p32", "mips64p32le", "mipsle", "ppc", "ppc64", "ppc64le", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm"}
|
||||
|
||||
// Platform .
|
||||
type Platform struct {
|
||||
OS string
|
||||
Arch string
|
||||
Variant string
|
||||
}
|
||||
|
||||
// ToString returns a string representation of the platform
|
||||
func (p Platform) ToString() string {
|
||||
if len(p.Variant) > 0 {
|
||||
return fmt.Sprintf("%s/%s/%s", p.OS, p.Arch, p.Variant)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%s", p.OS, p.Arch)
|
||||
}
|
||||
|
||||
// ParsePlatformString parses the given string and returns a platform obj
|
||||
func ParsePlatformString(s string) (Platform, error) {
|
||||
r := regexp.MustCompile(`(?P<os>[^,/]+)[,/](?P<arch>[^,/]+)(?:[,/](?P<variant>[^,/]+))?`)
|
||||
|
||||
matches := r.FindStringSubmatch(strings.ToLower(s))
|
||||
|
||||
if len(matches) < 2 {
|
||||
return Platform{}, fmt.Errorf("unable to parse platform '%s'", s)
|
||||
}
|
||||
|
||||
p := Platform{}
|
||||
|
||||
p.OS = strings.Trim(matches[1], " ")
|
||||
|
||||
if !piperutils.ContainsString(knownGoos, p.OS) {
|
||||
log.Entry().Warningf("OS '%s' is unknown to us", p.OS)
|
||||
}
|
||||
|
||||
p.Arch = strings.Trim(matches[2], " ")
|
||||
|
||||
if !piperutils.ContainsString(knownGoarch, p.Arch) {
|
||||
log.Entry().Warningf("Architecture '%s' is unknown to us", p.Arch)
|
||||
}
|
||||
|
||||
p.Variant = strings.Trim(matches[3], " ")
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// ParsePlatformStrings parses the given slice of strings and returns a slice with platform objects
|
||||
func ParsePlatformStrings(ss []string) ([]Platform, error) {
|
||||
pp := []Platform{}
|
||||
|
||||
for _, s := range ss {
|
||||
if p, err := ParsePlatformString(s); err == nil {
|
||||
pp = append(pp, p)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return pp, nil
|
||||
}
|
155
pkg/multiarch/multiarch_test.go
Normal file
155
pkg/multiarch/multiarch_test.go
Normal file
@ -0,0 +1,155 @@
|
||||
package multiarch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPlatformToString(t *testing.T) {
|
||||
tt := []struct {
|
||||
uut Platform
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
uut: Platform{
|
||||
OS: "linux",
|
||||
Arch: "arm64",
|
||||
},
|
||||
expect: "linux/arm64",
|
||||
},
|
||||
{
|
||||
uut: Platform{
|
||||
OS: "linux",
|
||||
Arch: "arm64",
|
||||
Variant: "v8",
|
||||
},
|
||||
expect: "linux/arm64/v8",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tt {
|
||||
t.Run(test.expect, func(t *testing.T) {
|
||||
assert.Equal(t, test.expect, test.uut.ToString())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePlatformString(t *testing.T) {
|
||||
tt := []struct {
|
||||
description string
|
||||
input string
|
||||
expect Platform
|
||||
expectError string
|
||||
}{
|
||||
{
|
||||
description: "format used by golangBuild - only os + arch",
|
||||
input: "linux,amd64",
|
||||
expect: Platform{
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "format used by kanikoExecute - os/arch/variant",
|
||||
input: "linux/amd64/v8",
|
||||
expect: Platform{
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
Variant: "v8",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "sth in between - os,arch,variant",
|
||||
input: "linux,amd64,v8",
|
||||
expect: Platform{
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
Variant: "v8",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "should be case insensitive",
|
||||
input: "LINUX/AMD64/V8",
|
||||
expect: Platform{
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
Variant: "v8",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "whitespaces shall be trimmed",
|
||||
input: " linux/ amd64 / v8",
|
||||
expect: Platform{
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
Variant: "v8",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "reads unsupported values",
|
||||
input: "myfancyos/runningonafancyarchitecture",
|
||||
expect: Platform{
|
||||
OS: "myfancyos",
|
||||
Arch: "runningonafancyarchitecture",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "os + arch are required, variant is optional",
|
||||
input: "linux",
|
||||
expectError: "unable to parse platform 'linux'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tt {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
p, err := ParsePlatformString(test.input)
|
||||
|
||||
if len(test.expectError) > 0 {
|
||||
assert.EqualError(t, err, test.expectError)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expect, p)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePlatformStringStrings(t *testing.T) {
|
||||
tt := []struct {
|
||||
description string
|
||||
inputs []string
|
||||
expect []Platform
|
||||
expectError string
|
||||
}{
|
||||
{
|
||||
description: "format used by golangBuild - only os + arch",
|
||||
inputs: []string{"linux,amd64", "windows,amd64"},
|
||||
expect: []Platform{
|
||||
Platform{
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
},
|
||||
Platform{
|
||||
OS: "windows",
|
||||
Arch: "amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tt {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
p, err := ParsePlatformStrings(test.inputs)
|
||||
|
||||
if len(test.expectError) > 0 {
|
||||
assert.EqualError(t, err, test.expectError)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expect, p)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user