github.com/SAP/jenkins-library@v1.362.0/cmd/gradleExecuteBuild.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/fs" 8 "path/filepath" 9 "strings" 10 "text/template" 11 12 "github.com/SAP/jenkins-library/pkg/command" 13 "github.com/SAP/jenkins-library/pkg/gradle" 14 "github.com/SAP/jenkins-library/pkg/log" 15 "github.com/SAP/jenkins-library/pkg/piperenv" 16 "github.com/SAP/jenkins-library/pkg/piperutils" 17 "github.com/SAP/jenkins-library/pkg/telemetry" 18 ) 19 20 const ( 21 gradleBomFilename = "bom-gradle" 22 ) 23 24 var ( 25 bomGradleTaskName = "cyclonedxBom" 26 publishTaskName = "publish" 27 pathToModuleFile = filepath.Join("build", "publications", "maven", "module.json") 28 rootPath = "." 29 ) 30 31 const publishInitScriptContentTemplate = ` 32 {{ if .ApplyPublishingForAllProjects}}allprojects{{else}}rootProject{{ end }} { 33 def gradleExecuteBuild_skipPublishingProjects = [{{ if .ApplyPublishingForAllProjects}}{{range .ExcludePublishingForProjects}} "{{.}}",{{end}}{{end}} ]; 34 if (!gradleExecuteBuild_skipPublishingProjects.contains(project.name)) { 35 apply plugin: 'maven-publish' 36 apply plugin: 'java' 37 publishing { 38 publications { 39 maven(MavenPublication) { 40 versionMapping { 41 usage('java-api') { 42 fromResolutionOf('runtimeClasspath') 43 } 44 usage('java-runtime') { 45 fromResolutionResult() 46 } 47 } 48 {{- if .ArtifactGroupID}} 49 groupId = '{{.ArtifactGroupID}}' 50 {{- end }} 51 {{- if .ApplyPublishingForAllProjects }} 52 {{else if .ArtifactID}} 53 artifactId = '{{.ArtifactID}}' 54 {{- end }} 55 {{- if .ArtifactVersion}} 56 version = '{{.ArtifactVersion}}' 57 {{- end }} 58 from components.java 59 } 60 } 61 repositories { 62 maven { 63 credentials { 64 username = "{{.RepositoryUsername}}" 65 password = "{{.RepositoryPassword}}" 66 } 67 url = "{{.RepositoryURL}}" 68 } 69 } 70 } 71 } 72 } 73 ` 74 75 const bomInitScriptContentTemplate = ` 76 initscript { 77 repositories { 78 mavenCentral() 79 maven { 80 url "https://plugins.gradle.org/m2/" 81 } 82 } 83 dependencies { 84 classpath "org.cyclonedx:cyclonedx-gradle-plugin:1.7.0" 85 } 86 } 87 88 allprojects { 89 def gradleExecuteBuild_skipBOMProjects = [{{range .ExcludeCreateBOMForProjects}} "{{.}}",{{end}} ]; 90 if (!gradleExecuteBuild_skipBOMProjects.contains(project.name)) { 91 apply plugin: 'java' 92 apply plugin: org.cyclonedx.gradle.CycloneDxPlugin 93 94 cyclonedxBom { 95 outputName = "` + gradleBomFilename + `" 96 outputFormat = "xml" 97 schemaVersion = "1.4" 98 includeConfigs = ["runtimeClasspath"] 99 skipConfigs = ["compileClasspath", "testCompileClasspath"] 100 } 101 } 102 } 103 ` 104 105 // PublishedArtifacts contains information about published artifacts 106 type PublishedArtifacts struct { 107 Info Component `json:"component,omitempty"` 108 Elements []Element `json:"variants,omitempty"` 109 } 110 111 type Component struct { 112 Module string `json:"module,omitempty"` 113 } 114 115 type Element struct { 116 Name string `json:"name,omitempty"` 117 Artifacts []Artifact `json:"files,omitempty"` 118 } 119 120 type Artifact struct { 121 Name string `json:"name,omitempty"` 122 } 123 124 type WalkDir func(root string, fn fs.WalkDirFunc) error 125 126 type Filepath interface { 127 WalkDir(root string, fn fs.WalkDirFunc) error 128 } 129 130 type WalkDirFunc func(root string, fn fs.WalkDirFunc) error 131 132 func (f WalkDirFunc) WalkDir(root string, fn fs.WalkDirFunc) error { 133 return f(root, fn) 134 } 135 136 type gradleExecuteBuildUtils interface { 137 command.ExecRunner 138 piperutils.FileUtils 139 Filepath 140 } 141 142 type gradleExecuteBuildUtilsBundle struct { 143 *command.Command 144 *piperutils.Files 145 Filepath 146 } 147 148 func newGradleExecuteBuildUtils() gradleExecuteBuildUtils { 149 var walkDirFunc WalkDirFunc = filepath.WalkDir 150 utils := gradleExecuteBuildUtilsBundle{ 151 Command: &command.Command{ 152 StepName: "gradleExecuteBuild", 153 }, 154 Files: &piperutils.Files{}, 155 Filepath: walkDirFunc, 156 } 157 utils.Stdout(log.Writer()) 158 utils.Stderr(log.Writer()) 159 return &utils 160 } 161 162 func gradleExecuteBuild(config gradleExecuteBuildOptions, telemetryData *telemetry.CustomData, pipelineEnv *gradleExecuteBuildCommonPipelineEnvironment) { 163 utils := newGradleExecuteBuildUtils() 164 err := runGradleExecuteBuild(&config, telemetryData, utils, pipelineEnv) 165 if err != nil { 166 log.Entry().WithError(err).Fatalf("step execution failed: %v", err) 167 } 168 } 169 170 func runGradleExecuteBuild(config *gradleExecuteBuildOptions, telemetryData *telemetry.CustomData, utils gradleExecuteBuildUtils, pipelineEnv *gradleExecuteBuildCommonPipelineEnvironment) error { 171 log.Entry().Info("BOM file creation...") 172 173 if config.CreateBOM { 174 if err := createBOM(config, utils); err != nil { 175 return err 176 } 177 } 178 179 // gradle build 180 // if user provides BuildFlags, it is respected over a single Task 181 gradleOptions := &gradle.ExecuteOptions{ 182 BuildGradlePath: config.Path, 183 Task: config.Task, 184 BuildFlags: config.BuildFlags, 185 UseWrapper: config.UseWrapper, 186 } 187 if _, err := gradle.Execute(gradleOptions, utils); err != nil { 188 log.Entry().WithError(err).Errorf("gradle build execution was failed: %v", err) 189 return err 190 } 191 192 log.Entry().Info("Publishing of artifacts to staging repository...") 193 if config.Publish { 194 if err := publishArtifacts(config, utils, pipelineEnv); err != nil { 195 return err 196 } 197 } 198 199 return nil 200 } 201 202 func createBOM(config *gradleExecuteBuildOptions, utils gradleExecuteBuildUtils) error { 203 createBOMInitScriptContent, err := getInitScriptContent(config, bomInitScriptContentTemplate) 204 if err != nil { 205 return fmt.Errorf("failed to get BOM init script content: %v", err) 206 } 207 gradleOptions := &gradle.ExecuteOptions{ 208 BuildGradlePath: config.Path, 209 Task: bomGradleTaskName, 210 UseWrapper: config.UseWrapper, 211 InitScriptContent: createBOMInitScriptContent, 212 } 213 if _, err := gradle.Execute(gradleOptions, utils); err != nil { 214 log.Entry().WithError(err).Errorf("failed to create BOM: %v", err) 215 return err 216 } 217 return nil 218 } 219 220 func publishArtifacts(config *gradleExecuteBuildOptions, utils gradleExecuteBuildUtils, pipelineEnv *gradleExecuteBuildCommonPipelineEnvironment) error { 221 publishInitScriptContent, err := getInitScriptContent(config, publishInitScriptContentTemplate) 222 if err != nil { 223 return fmt.Errorf("failed to get publish init script content: %v", err) 224 } 225 gradleOptions := &gradle.ExecuteOptions{ 226 BuildGradlePath: config.Path, 227 Task: publishTaskName, 228 UseWrapper: config.UseWrapper, 229 InitScriptContent: publishInitScriptContent, 230 } 231 if _, err := gradle.Execute(gradleOptions, utils); err != nil { 232 log.Entry().WithError(err).Errorf("failed to publish artifacts: %v", err) 233 return err 234 } 235 var artifacts piperenv.Artifacts 236 err = utils.WalkDir(rootPath, func(path string, d fs.DirEntry, err error) error { 237 if d.IsDir() { 238 return nil 239 } 240 if strings.HasSuffix(path, pathToModuleFile) { 241 pathArtifacts, artifactsErr := getPublishedArtifactsNames(path, utils) 242 if artifactsErr != nil { 243 return fmt.Errorf("failed to get published artifacts in path %s: %v", path, artifactsErr) 244 } 245 artifacts = append(artifacts, pathArtifacts...) 246 } 247 return nil 248 }) 249 if err != nil { 250 return err 251 } 252 pipelineEnv.custom.artifacts = artifacts 253 return nil 254 } 255 256 func getInitScriptContent(options *gradleExecuteBuildOptions, templateContent string) (string, error) { 257 tmpl, err := template.New("resources").Parse(templateContent) 258 if err != nil { 259 return "", err 260 } 261 262 var generatedCode bytes.Buffer 263 err = tmpl.Execute(&generatedCode, options) 264 if err != nil { 265 return "", err 266 } 267 268 return generatedCode.String(), nil 269 } 270 271 func getPublishedArtifactsNames(file string, utils gradleExecuteBuildUtils) (piperenv.Artifacts, error) { 272 artifacts := piperenv.Artifacts{} 273 publishedArtifacts := PublishedArtifacts{} 274 exists, err := utils.FileExists(file) 275 if err != nil { 276 return nil, fmt.Errorf("failed to check existence of the file '%s': %v", file, err) 277 } 278 if !exists { 279 return nil, fmt.Errorf("failed to get '%s': file does not exist", file) 280 } 281 content, err := utils.ReadFile(file) 282 if err != nil { 283 return nil, fmt.Errorf("failed to read '%s': %v", file, err) 284 } 285 err = json.Unmarshal(content, &publishedArtifacts) 286 if err != nil { 287 return nil, fmt.Errorf("failed to unmarshal '%s': %v", file, err) 288 } 289 for _, element := range publishedArtifacts.Elements { 290 if element.Name != "apiElements" { 291 continue 292 } 293 for _, artifact := range element.Artifacts { 294 artifacts = append(artifacts, piperenv.Artifact{Id: publishedArtifacts.Info.Module, Name: artifact.Name}) 295 } 296 } 297 return artifacts, nil 298 }