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 
			
		
		
		
	Update gradleExecuteBuild.go (#4079)
* Update gradleExecuteBuild to apply publish and cyclonedx for all projects Co-authored-by: “Raman <“raman_susla@epam.com”>
This commit is contained in:
		| @@ -4,6 +4,9 @@ import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/fs" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/command" | ||||
| @@ -21,51 +24,55 @@ const ( | ||||
| var ( | ||||
| 	bomGradleTaskName = "cyclonedxBom" | ||||
| 	publishTaskName   = "publish" | ||||
| 	pathToModuleFile  = "./build/publications/maven/module.json" | ||||
| 	pathToModuleFile  = filepath.Join("build", "publications", "maven", "module.json") | ||||
| 	rootPath          = "." | ||||
| ) | ||||
|  | ||||
| const publishInitScriptContentTemplate = ` | ||||
| rootProject { | ||||
|     apply plugin: 'maven-publish' | ||||
|     apply plugin: 'java' | ||||
|  | ||||
|     publishing { | ||||
|         publications { | ||||
|             maven(MavenPublication) { | ||||
|                 versionMapping { | ||||
|                     usage('java-api') { | ||||
|                         fromResolutionOf('runtimeClasspath') | ||||
|                     } | ||||
|                     usage('java-runtime') { | ||||
|                         fromResolutionResult() | ||||
| {{ if .ApplyPublishingForAllProjects}}allprojects{{else}}rootProject{{ end }} { | ||||
|     def gradleExecuteBuild_skipPublishingProjects = [{{ if .ApplyPublishingForAllProjects}}{{range .ExcludePublishingForProjects}} "{{.}}",{{end}}{{end}} ]; | ||||
|     if (!gradleExecuteBuild_skipPublishingProjects.contains(project.name)) { | ||||
|         apply plugin: 'maven-publish' | ||||
|         apply plugin: 'java' | ||||
|         publishing { | ||||
|             publications { | ||||
|                 maven(MavenPublication) { | ||||
|                     versionMapping { | ||||
|                         usage('java-api') { | ||||
|                             fromResolutionOf('runtimeClasspath') | ||||
|                         } | ||||
|                         usage('java-runtime') { | ||||
|                             fromResolutionResult() | ||||
|                         } | ||||
|                     } | ||||
|                     {{- if .ArtifactGroupID}} | ||||
|                     groupId = '{{.ArtifactGroupID}}' | ||||
|                     {{- end }} | ||||
|                     {{- if .ApplyPublishingForAllProjects }} | ||||
|                     {{else if .ArtifactID}} | ||||
|                     artifactId = '{{.ArtifactID}}' | ||||
|                     {{- end }} | ||||
|                     {{- if .ArtifactVersion}} | ||||
|                     version = '{{.ArtifactVersion}}' | ||||
|                     {{- end }} | ||||
|                     from components.java | ||||
|                 } | ||||
| 				{{- if .ArtifactGroupID}} | ||||
| 				groupId = '{{.ArtifactGroupID}}' | ||||
| 				{{- end }} | ||||
| 				{{- if .ArtifactID}} | ||||
| 				artifactId = '{{.ArtifactID}}' | ||||
| 				{{- end }} | ||||
| 				{{- if .ArtifactVersion}} | ||||
| 				version = '{{.ArtifactVersion}}' | ||||
| 				{{- end }} | ||||
|                 from components.java | ||||
|             } | ||||
|         } | ||||
|         repositories { | ||||
|             maven { | ||||
|                 credentials { | ||||
|                     username = "{{.RepositoryUsername}}" | ||||
|                     password = "{{.RepositoryPassword}}" | ||||
|             repositories { | ||||
|                 maven { | ||||
|                     credentials { | ||||
|                         username = "{{.RepositoryUsername}}" | ||||
|                         password = "{{.RepositoryPassword}}" | ||||
|                     } | ||||
|                     url = "{{.RepositoryURL}}" | ||||
|                 } | ||||
|                 url = "{{.RepositoryURL}}" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ` | ||||
|  | ||||
| const bomInitScriptContent = ` | ||||
| const bomInitScriptContentTemplate = ` | ||||
| initscript { | ||||
|   repositories { | ||||
|     mavenCentral() | ||||
| @@ -78,24 +85,33 @@ initscript { | ||||
|   } | ||||
| } | ||||
|  | ||||
| rootProject { | ||||
|     apply plugin: 'java' | ||||
|     apply plugin: 'maven' | ||||
|     apply plugin: org.cyclonedx.gradle.CycloneDxPlugin | ||||
| allprojects { | ||||
|     def gradleExecuteBuild_skipBOMProjects = [{{range .ExcludeCreateBOMForProjects}} "{{.}}",{{end}} ]; | ||||
|     if (!gradleExecuteBuild_skipBOMProjects.contains(project.name)) { | ||||
|         apply plugin: 'java' | ||||
|         apply plugin: org.cyclonedx.gradle.CycloneDxPlugin | ||||
|  | ||||
|     cyclonedxBom { | ||||
| 	outputName = "` + gradleBomFilename + `" | ||||
| 	outputFormat = "xml" | ||||
| 	schemaVersion = "1.2" | ||||
|         cyclonedxBom { | ||||
|             outputName = "` + gradleBomFilename + `" | ||||
|             outputFormat = "xml" | ||||
|             schemaVersion = "1.2" | ||||
|             includeConfigs = ["runtimeClasspath"] | ||||
|             skipConfigs = ["compileClasspath", "testCompileClasspath"] | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ` | ||||
|  | ||||
| // PublishedArtifacts contains information about published artifacts | ||||
| type PublishedArtifacts struct { | ||||
| 	Info     Component `json:"component,omitempty"` | ||||
| 	Elements []Element `json:"variants,omitempty"` | ||||
| } | ||||
|  | ||||
| type Component struct { | ||||
| 	Module string `json:"module,omitempty"` | ||||
| } | ||||
|  | ||||
| type Element struct { | ||||
| 	Name      string     `json:"name,omitempty"` | ||||
| 	Artifacts []Artifact `json:"files,omitempty"` | ||||
| @@ -105,22 +121,38 @@ type Artifact struct { | ||||
| 	Name string `json:"name,omitempty"` | ||||
| } | ||||
|  | ||||
| type WalkDir func(root string, fn fs.WalkDirFunc) error | ||||
|  | ||||
| type Filepath interface { | ||||
| 	WalkDir(root string, fn fs.WalkDirFunc) error | ||||
| } | ||||
|  | ||||
| type WalkDirFunc func(root string, fn fs.WalkDirFunc) error | ||||
|  | ||||
| func (f WalkDirFunc) WalkDir(root string, fn fs.WalkDirFunc) error { | ||||
| 	return f(root, fn) | ||||
| } | ||||
|  | ||||
| type gradleExecuteBuildUtils interface { | ||||
| 	command.ExecRunner | ||||
| 	piperutils.FileUtils | ||||
| 	Filepath | ||||
| } | ||||
|  | ||||
| type gradleExecuteBuildUtilsBundle struct { | ||||
| 	*command.Command | ||||
| 	*piperutils.Files | ||||
| 	Filepath | ||||
| } | ||||
|  | ||||
| func newGradleExecuteBuildUtils() gradleExecuteBuildUtils { | ||||
| 	var walkDirFunc WalkDirFunc = filepath.WalkDir | ||||
| 	utils := gradleExecuteBuildUtilsBundle{ | ||||
| 		Command: &command.Command{ | ||||
| 			StepName: "gradleExecuteBuild", | ||||
| 		}, | ||||
| 		Files: &piperutils.Files{}, | ||||
| 		Files:    &piperutils.Files{}, | ||||
| 		Filepath: walkDirFunc, | ||||
| 	} | ||||
| 	utils.Stdout(log.Writer()) | ||||
| 	utils.Stderr(log.Writer()) | ||||
| @@ -137,6 +169,7 @@ func gradleExecuteBuild(config gradleExecuteBuildOptions, telemetryData *telemet | ||||
|  | ||||
| func runGradleExecuteBuild(config *gradleExecuteBuildOptions, telemetryData *telemetry.CustomData, utils gradleExecuteBuildUtils, pipelineEnv *gradleExecuteBuildCommonPipelineEnvironment) error { | ||||
| 	log.Entry().Info("BOM file creation...") | ||||
|  | ||||
| 	if config.CreateBOM { | ||||
| 		if err := createBOM(config, utils); err != nil { | ||||
| 			return err | ||||
| @@ -165,11 +198,15 @@ func runGradleExecuteBuild(config *gradleExecuteBuildOptions, telemetryData *tel | ||||
| } | ||||
|  | ||||
| func createBOM(config *gradleExecuteBuildOptions, utils gradleExecuteBuildUtils) error { | ||||
| 	createBOMInitScriptContent, err := getInitScriptContent(config, bomInitScriptContentTemplate) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to get BOM init script content: %v", err) | ||||
| 	} | ||||
| 	gradleOptions := &gradle.ExecuteOptions{ | ||||
| 		BuildGradlePath:   config.Path, | ||||
| 		Task:              bomGradleTaskName, | ||||
| 		UseWrapper:        config.UseWrapper, | ||||
| 		InitScriptContent: bomInitScriptContent, | ||||
| 		InitScriptContent: createBOMInitScriptContent, | ||||
| 	} | ||||
| 	if _, err := gradle.Execute(gradleOptions, utils); err != nil { | ||||
| 		log.Entry().WithError(err).Errorf("failed to create BOM: %v", err) | ||||
| @@ -179,7 +216,7 @@ func createBOM(config *gradleExecuteBuildOptions, utils gradleExecuteBuildUtils) | ||||
| } | ||||
|  | ||||
| func publishArtifacts(config *gradleExecuteBuildOptions, utils gradleExecuteBuildUtils, pipelineEnv *gradleExecuteBuildCommonPipelineEnvironment) error { | ||||
| 	publishInitScriptContent, err := getPublishInitScriptContent(config) | ||||
| 	publishInitScriptContent, err := getInitScriptContent(config, publishInitScriptContentTemplate) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to get publish init script content: %v", err) | ||||
| 	} | ||||
| @@ -193,16 +230,29 @@ func publishArtifacts(config *gradleExecuteBuildOptions, utils gradleExecuteBuil | ||||
| 		log.Entry().WithError(err).Errorf("failed to publish artifacts: %v", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	artifacts, err := getPublishedArtifactsNames(pathToModuleFile, utils) | ||||
| 	var artifacts piperenv.Artifacts | ||||
| 	err = utils.WalkDir(rootPath, func(path string, d fs.DirEntry, err error) error { | ||||
| 		if d.IsDir() { | ||||
| 			return nil | ||||
| 		} | ||||
| 		if strings.HasSuffix(path, pathToModuleFile) { | ||||
| 			pathArtifacts, artifactsErr := getPublishedArtifactsNames(path, utils) | ||||
| 			if artifactsErr != nil { | ||||
| 				return fmt.Errorf("failed to get published artifacts in path %s: %v", path, artifactsErr) | ||||
| 			} | ||||
| 			artifacts = append(artifacts, pathArtifacts...) | ||||
| 		} | ||||
| 		return nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to get published artifacts: %v", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	pipelineEnv.custom.artifacts = artifacts | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getPublishInitScriptContent(options *gradleExecuteBuildOptions) (string, error) { | ||||
| 	tmpl, err := template.New("resources").Parse(publishInitScriptContentTemplate) | ||||
| func getInitScriptContent(options *gradleExecuteBuildOptions, templateContent string) (string, error) { | ||||
| 	tmpl, err := template.New("resources").Parse(templateContent) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| @@ -239,7 +289,7 @@ func getPublishedArtifactsNames(file string, utils gradleExecuteBuildUtils) (pip | ||||
| 			continue | ||||
| 		} | ||||
| 		for _, artifact := range element.Artifacts { | ||||
| 			artifacts = append(artifacts, piperenv.Artifact{Name: artifact.Name}) | ||||
| 			artifacts = append(artifacts, piperenv.Artifact{Id: publishedArtifacts.Info.Module, Name: artifact.Name}) | ||||
| 		} | ||||
| 	} | ||||
| 	return artifacts, nil | ||||
|   | ||||
| @@ -22,17 +22,20 @@ import ( | ||||
| ) | ||||
|  | ||||
| type gradleExecuteBuildOptions struct { | ||||
| 	Path               string `json:"path,omitempty"` | ||||
| 	Task               string `json:"task,omitempty"` | ||||
| 	Publish            bool   `json:"publish,omitempty"` | ||||
| 	RepositoryURL      string `json:"repositoryUrl,omitempty"` | ||||
| 	RepositoryPassword string `json:"repositoryPassword,omitempty"` | ||||
| 	RepositoryUsername string `json:"repositoryUsername,omitempty"` | ||||
| 	CreateBOM          bool   `json:"createBOM,omitempty"` | ||||
| 	ArtifactVersion    string `json:"artifactVersion,omitempty"` | ||||
| 	ArtifactGroupID    string `json:"artifactGroupId,omitempty"` | ||||
| 	ArtifactID         string `json:"artifactId,omitempty"` | ||||
| 	UseWrapper         bool   `json:"useWrapper,omitempty"` | ||||
| 	Path                          string   `json:"path,omitempty"` | ||||
| 	Task                          string   `json:"task,omitempty"` | ||||
| 	Publish                       bool     `json:"publish,omitempty"` | ||||
| 	RepositoryURL                 string   `json:"repositoryUrl,omitempty"` | ||||
| 	RepositoryPassword            string   `json:"repositoryPassword,omitempty"` | ||||
| 	RepositoryUsername            string   `json:"repositoryUsername,omitempty"` | ||||
| 	CreateBOM                     bool     `json:"createBOM,omitempty"` | ||||
| 	ArtifactVersion               string   `json:"artifactVersion,omitempty"` | ||||
| 	ArtifactGroupID               string   `json:"artifactGroupId,omitempty"` | ||||
| 	ArtifactID                    string   `json:"artifactId,omitempty"` | ||||
| 	UseWrapper                    bool     `json:"useWrapper,omitempty"` | ||||
| 	ApplyPublishingForAllProjects bool     `json:"applyPublishingForAllProjects,omitempty"` | ||||
| 	ExcludeCreateBOMForProjects   []string `json:"excludeCreateBOMForProjects,omitempty"` | ||||
| 	ExcludePublishingForProjects  []string `json:"excludePublishingForProjects,omitempty"` | ||||
| } | ||||
|  | ||||
| type gradleExecuteBuildReports struct { | ||||
| @@ -209,6 +212,9 @@ func addGradleExecuteBuildFlags(cmd *cobra.Command, stepConfig *gradleExecuteBui | ||||
| 	cmd.Flags().StringVar(&stepConfig.ArtifactGroupID, "artifactGroupId", os.Getenv("PIPER_artifactGroupId"), "The group of the artifact.") | ||||
| 	cmd.Flags().StringVar(&stepConfig.ArtifactID, "artifactId", os.Getenv("PIPER_artifactId"), "The name of the artifact.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.UseWrapper, "useWrapper", false, "If set to false all commands are executed using 'gradle', otherwise 'gradlew' is executed.") | ||||
| 	cmd.Flags().BoolVar(&stepConfig.ApplyPublishingForAllProjects, "applyPublishingForAllProjects", false, "If set to false publishing logic will be applied in 'rootProject' directive, otherwise 'allprojects' will be directive used") | ||||
| 	cmd.Flags().StringSliceVar(&stepConfig.ExcludeCreateBOMForProjects, "excludeCreateBOMForProjects", []string{}, "Defines which projects/subprojects will be ignored during bom creation. Only if applyCreateBOMForAllProjects is set to true") | ||||
| 	cmd.Flags().StringSliceVar(&stepConfig.ExcludePublishingForProjects, "excludePublishingForProjects", []string{}, "Defines which projects/subprojects will be ignored during publishing. Only if applyCreateBOMForAllProjects is set to true") | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -352,6 +358,33 @@ func gradleExecuteBuildMetadata() config.StepData { | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     false, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "applyPublishingForAllProjects", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"STEPS", "STAGES", "PARAMETERS"}, | ||||
| 						Type:        "bool", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     false, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "excludeCreateBOMForProjects", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "[]string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     []string{}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:        "excludePublishingForProjects", | ||||
| 						ResourceRef: []config.ResourceReference{}, | ||||
| 						Scope:       []string{"PARAMETERS", "STAGES", "STEPS"}, | ||||
| 						Type:        "[]string", | ||||
| 						Mandatory:   false, | ||||
| 						Aliases:     []config.Alias{}, | ||||
| 						Default:     []string{}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			Containers: []config.Container{ | ||||
|   | ||||
| @@ -2,6 +2,8 @@ package cmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/fs" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/pkg/errors" | ||||
| @@ -16,6 +18,24 @@ const moduleFileContent = `{"variants": [{"name": "apiElements","files": [{"name | ||||
| type gradleExecuteBuildMockUtils struct { | ||||
| 	*mock.ExecMockRunner | ||||
| 	*mock.FilesMock | ||||
| 	Filepath | ||||
| } | ||||
|  | ||||
| type isDirEntryMock func() bool | ||||
|  | ||||
| func (d isDirEntryMock) Name() string { | ||||
| 	panic("not implemented") | ||||
| } | ||||
|  | ||||
| func (d isDirEntryMock) IsDir() bool { | ||||
| 	return d() | ||||
| } | ||||
|  | ||||
| func (d isDirEntryMock) Type() fs.FileMode { | ||||
| 	panic("not implemented") | ||||
| } | ||||
| func (d isDirEntryMock) Info() (fs.FileInfo, error) { | ||||
| 	panic("not implemented") | ||||
| } | ||||
|  | ||||
| func TestRunGradleExecuteBuild(t *testing.T) { | ||||
| @@ -79,12 +99,19 @@ func TestRunGradleExecuteBuild(t *testing.T) { | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("success case - publishing of artifacts", func(t *testing.T) { | ||||
| 		var walkDir WalkDirFunc = func(root string, fn fs.WalkDirFunc) error { | ||||
| 			var dirMock isDirEntryMock = func() bool { | ||||
| 				return false | ||||
| 			} | ||||
| 			return fn(filepath.Join("test_subproject_path", "build", "publications", "maven", "module.json"), dirMock, nil) | ||||
| 		} | ||||
| 		utils := gradleExecuteBuildMockUtils{ | ||||
| 			ExecMockRunner: &mock.ExecMockRunner{}, | ||||
| 			FilesMock:      &mock.FilesMock{}, | ||||
| 			Filepath:       walkDir, | ||||
| 		} | ||||
| 		utils.FilesMock.AddFile("path/to/build.gradle", []byte{}) | ||||
| 		utils.FilesMock.AddFile("/build/publications/maven/module.json", []byte(moduleFileContent)) | ||||
| 		utils.FilesMock.AddFile(filepath.Join("test_subproject_path", "build", "publications", "maven", "module.json"), []byte(moduleFileContent)) | ||||
| 		options := &gradleExecuteBuildOptions{ | ||||
| 			Path:       "path/to", | ||||
| 			Task:       "build", | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package piperenv | ||||
|  | ||||
| type Artifact struct { | ||||
| 	Id   string `json:"id,omitempty"` | ||||
| 	Name string `json:"name,omitempty"` | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -115,6 +115,28 @@ spec: | ||||
|           - STAGES | ||||
|           - PARAMETERS | ||||
|         default: false | ||||
|       - name: applyPublishingForAllProjects | ||||
|         type: bool | ||||
|         description: If set to false publishing logic will be applied in 'rootProject' directive, otherwise 'allprojects' will be directive used | ||||
|         scope: | ||||
|           - STEPS | ||||
|           - STAGES | ||||
|           - PARAMETERS | ||||
|         default: false | ||||
|       - name: excludeCreateBOMForProjects | ||||
|         description: Defines which projects/subprojects will be ignored during bom creation. Only if applyCreateBOMForAllProjects is set to true | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         type: "[]string" | ||||
|       - name: excludePublishingForProjects | ||||
|         description: Defines which projects/subprojects will be ignored during publishing. Only if applyCreateBOMForAllProjects is set to true | ||||
|         scope: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|         type: "[]string" | ||||
|   outputs: | ||||
|     resources: | ||||
|       - name: reports | ||||
|   | ||||
		Reference in New Issue
	
	Block a user