1
0
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:
raman-susla-epam
2022-11-15 16:17:31 +03:00
committed by GitHub
parent 3715b34631
commit fe4af3af7b
5 changed files with 193 additions and 60 deletions

View File

@@ -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

View File

@@ -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{

View File

@@ -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",

View File

@@ -1,6 +1,7 @@
package piperenv
type Artifact struct {
Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
}

View File

@@ -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