You've already forked sap-jenkins-library
							
							
				mirror of
				https://github.com/SAP/jenkins-library.git
				synced 2025-10-30 23:57:50 +02:00 
			
		
		
		
	fix(golangBuild) properly handle multi main package builds
Co-authored-by: Pavel Busko <pavel.busko@sap.com>
This commit is contained in:
		
				
					committed by
					
						 Pavel Busko
						Pavel Busko
					
				
			
			
				
	
			
			
			
						parent
						
							2ed1ed76fc
						
					
				
				
					commit
					74b6b09609
				
			| @@ -6,6 +6,7 @@ import ( | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
| @@ -18,7 +19,6 @@ import ( | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperenv" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperutils" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/multiarch" | ||||
| @@ -182,8 +182,7 @@ func runGolangBuild(config *golangBuildOptions, telemetryData *telemetry.CustomD | ||||
| 		log.Entry().Infof("ldflags from template: '%v'", ldflags) | ||||
| 	} | ||||
|  | ||||
| 	binaries := []string{} | ||||
|  | ||||
| 	var binaries []string | ||||
| 	platforms, err := multiarch.ParsePlatformStrings(config.TargetArchitectures) | ||||
|  | ||||
| 	if err != nil { | ||||
| @@ -191,14 +190,14 @@ func runGolangBuild(config *golangBuildOptions, telemetryData *telemetry.CustomD | ||||
| 	} | ||||
|  | ||||
| 	for _, platform := range platforms { | ||||
| 		binary, err := runGolangBuildPerArchitecture(config, utils, ldflags, platform) | ||||
| 		binaryNames, err := runGolangBuildPerArchitecture(config, utils, ldflags, platform) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if len(binary) > 0 { | ||||
| 			binaries = append(binaries, binary) | ||||
| 		if len(binaryNames) > 0 { | ||||
| 			binaries = append(binaries, binaryNames...) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -425,8 +424,8 @@ func prepareLdflags(config *golangBuildOptions, utils golangBuildUtils, envRootP | ||||
| 	return generatedLdflags.String(), nil | ||||
| } | ||||
|  | ||||
| func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags string, architecture multiarch.Platform) (string, error) { | ||||
| 	var binaryName string | ||||
| func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuildUtils, ldflags string, architecture multiarch.Platform) ([]string, error) { | ||||
| 	var binaryNames []string | ||||
|  | ||||
| 	envVars := os.Environ() | ||||
| 	envVars = append(envVars, fmt.Sprintf("GOOS=%v", architecture.OS), fmt.Sprintf("GOARCH=%v", architecture.Arch)) | ||||
| @@ -437,13 +436,25 @@ func runGolangBuildPerArchitecture(config *golangBuildOptions, utils golangBuild | ||||
| 	utils.SetEnv(envVars) | ||||
|  | ||||
| 	buildOptions := []string{"build", "-trimpath"} | ||||
|  | ||||
| 	if len(config.Output) > 0 { | ||||
| 		fileExtension := "" | ||||
| 		if architecture.OS == "windows" { | ||||
| 			fileExtension = ".exe" | ||||
| 		if len(config.Packages) > 1 { | ||||
| 			binaries, outputDir, err := getOutputBinaries(config.Output, config.Packages, utils, architecture) | ||||
| 			if err != nil { | ||||
| 				log.SetErrorCategory(log.ErrorBuild) | ||||
| 				return nil, fmt.Errorf("failed to calculate output binaries or directory, error: %s", err.Error()) | ||||
| 			} | ||||
| 			buildOptions = append(buildOptions, "-o", outputDir) | ||||
| 			binaryNames = append(binaryNames, binaries...) | ||||
| 		} else { | ||||
| 			fileExtension := "" | ||||
| 			if architecture.OS == "windows" { | ||||
| 				fileExtension = ".exe" | ||||
| 			} | ||||
| 			binaryName := fmt.Sprintf("%s-%s.%s%s", strings.TrimRight(config.Output, string(os.PathSeparator)), architecture.OS, architecture.Arch, fileExtension) | ||||
| 			buildOptions = append(buildOptions, "-o", binaryName) | ||||
| 			binaryNames = append(binaryNames, binaryName) | ||||
| 		} | ||||
| 		binaryName = fmt.Sprintf("%v-%v.%v%v", config.Output, architecture.OS, architecture.Arch, fileExtension) | ||||
| 		buildOptions = append(buildOptions, "-o", binaryName) | ||||
| 	} | ||||
| 	buildOptions = append(buildOptions, config.BuildFlags...) | ||||
| 	if len(ldflags) > 0 { | ||||
| @@ -454,9 +465,10 @@ 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", architecture.OS, architecture.Arch, err) | ||||
| 		return nil, fmt.Errorf("failed to run build for %v.%v: %w", architecture.OS, architecture.Arch, err) | ||||
| 	} | ||||
| 	return binaryName, nil | ||||
|  | ||||
| 	return binaryNames, nil | ||||
| } | ||||
|  | ||||
| // lookupPrivateModulesRepositories returns a slice of all modules that match the given glob pattern | ||||
| @@ -512,3 +524,41 @@ func readGoModFile(utils golangBuildUtils) (*modfile.File, error) { | ||||
|  | ||||
| 	return modfile.Parse(modFilePath, modFileContent, nil) | ||||
| } | ||||
|  | ||||
| func getOutputBinaries(out string, packages []string, utils golangBuildUtils, architecture multiarch.Platform) ([]string, string, error) { | ||||
| 	var binaries []string | ||||
| 	outDir := fmt.Sprintf("%s-%s-%s%c", strings.TrimRight(out, string(os.PathSeparator)), architecture.OS, architecture.Arch, os.PathSeparator) | ||||
|  | ||||
| 	for _, pkg := range packages { | ||||
| 		ok, err := isMainPackage(utils, pkg) | ||||
| 		if err != nil { | ||||
| 			return nil, "", err | ||||
| 		} | ||||
|  | ||||
| 		if ok { | ||||
| 			fileExt := "" | ||||
| 			if architecture.OS == "windows" { | ||||
| 				fileExt = ".exe" | ||||
| 			} | ||||
| 			binaries = append(binaries, filepath.Join(outDir, filepath.Base(pkg)+fileExt)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return binaries, outDir, nil | ||||
| } | ||||
|  | ||||
| func isMainPackage(utils golangBuildUtils, pkg string) (bool, error) { | ||||
| 	outBuffer := bytes.NewBufferString("") | ||||
| 	utils.Stdout(outBuffer) | ||||
| 	utils.Stderr(outBuffer) | ||||
| 	err := utils.RunExecutable("go", "list", "-f", "{{ .Name }}", pkg) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	if outBuffer.String() != "main" { | ||||
| 		return false, nil | ||||
| 	} | ||||
|  | ||||
| 	return true, nil | ||||
| } | ||||
|   | ||||
| @@ -72,7 +72,7 @@ func newGolangBuildTestsUtils() *golangBuildMockUtils { | ||||
| 	utils := golangBuildMockUtils{ | ||||
| 		ExecMockRunner: &mock.ExecMockRunner{}, | ||||
| 		FilesMock:      &mock.FilesMock{}, | ||||
| 		//clientOptions:  []piperhttp.ClientOptions{}, | ||||
| 		// clientOptions:  []piperhttp.ClientOptions{}, | ||||
| 		fileUploads: map[string]string{}, | ||||
| 	} | ||||
| 	return &utils | ||||
| @@ -632,14 +632,15 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) { | ||||
| 		ldflags := "-X test=test" | ||||
| 		architecture, _ := multiarch.ParsePlatformString("linux,amd64") | ||||
|  | ||||
| 		binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) | ||||
| 		binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "-o") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "testBin-linux.amd64") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "./test/..") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "-ldflags") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "-X test=test") | ||||
| 		assert.Equal(t, "testBin-linux.amd64", binaryName) | ||||
| 		assert.Len(t, binaryNames, 1) | ||||
| 		assert.Contains(t, binaryNames, "testBin-linux.amd64") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("success - windows", func(t *testing.T) { | ||||
| @@ -649,11 +650,80 @@ func TestRunGolangBuildPerArchitecture(t *testing.T) { | ||||
| 		ldflags := "" | ||||
| 		architecture, _ := multiarch.ParsePlatformString("windows,amd64") | ||||
|  | ||||
| 		binaryName, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) | ||||
| 		binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "-o") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "testBin-windows.amd64.exe") | ||||
| 		assert.Equal(t, "testBin-windows.amd64.exe", binaryName) | ||||
| 		assert.Len(t, binaryNames, 1) | ||||
| 		assert.Contains(t, binaryNames, "testBin-windows.amd64.exe") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("success - multiple main packages (linux)", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}} | ||||
| 		utils := newGolangBuildTestsUtils() | ||||
| 		utils.StdoutReturn = map[string]string{ | ||||
| 			"go list -f {{ .Name }} package/foo": "main", | ||||
| 			"go list -f {{ .Name }} package/bar": "main", | ||||
| 		} | ||||
| 		ldflags := "" | ||||
| 		architecture, _ := multiarch.ParsePlatformString("linux,amd64") | ||||
|  | ||||
| 		binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "list") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "package/foo") | ||||
| 		assert.Contains(t, utils.Calls[1].Params, "list") | ||||
| 		assert.Contains(t, utils.Calls[1].Params, "package/bar") | ||||
|  | ||||
| 		assert.Len(t, binaryNames, 2) | ||||
| 		assert.Contains(t, binaryNames, "test-linux-amd64/foo") | ||||
| 		assert.Contains(t, binaryNames, "test-linux-amd64/bar") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("success - multiple main packages (windows)", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}} | ||||
| 		utils := newGolangBuildTestsUtils() | ||||
| 		utils.StdoutReturn = map[string]string{ | ||||
| 			"go list -f {{ .Name }} package/foo": "main", | ||||
| 			"go list -f {{ .Name }} package/bar": "main", | ||||
| 		} | ||||
| 		ldflags := "" | ||||
| 		architecture, _ := multiarch.ParsePlatformString("windows,amd64") | ||||
|  | ||||
| 		binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "list") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "package/foo") | ||||
| 		assert.Contains(t, utils.Calls[1].Params, "list") | ||||
| 		assert.Contains(t, utils.Calls[1].Params, "package/bar") | ||||
|  | ||||
| 		assert.Len(t, binaryNames, 2) | ||||
| 		assert.Contains(t, binaryNames, "test-windows-amd64/foo.exe") | ||||
| 		assert.Contains(t, binaryNames, "test-windows-amd64/bar.exe") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("success - multiple mixed packages", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		config := golangBuildOptions{Output: "test/", Packages: []string{"package/foo", "package/bar"}} | ||||
| 		utils := newGolangBuildTestsUtils() | ||||
| 		utils.StdoutReturn = map[string]string{ | ||||
| 			"go list -f {{ .Name }} package/foo": "main", | ||||
| 			"go list -f {{ .Name }} package/bar": "bar", | ||||
| 		} | ||||
| 		ldflags := "" | ||||
| 		architecture, _ := multiarch.ParsePlatformString("linux,amd64") | ||||
|  | ||||
| 		binaryNames, err := runGolangBuildPerArchitecture(&config, utils, ldflags, architecture) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "list") | ||||
| 		assert.Contains(t, utils.Calls[0].Params, "package/foo") | ||||
| 		assert.Contains(t, utils.Calls[1].Params, "list") | ||||
| 		assert.Contains(t, utils.Calls[1].Params, "package/bar") | ||||
|  | ||||
| 		assert.Len(t, binaryNames, 1) | ||||
| 		assert.Contains(t, binaryNames, "test-linux-amd64/foo") | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("execution error", func(t *testing.T) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user