diff --git a/cmd/mavenBuild.go b/cmd/mavenBuild.go index c1e61c0a5..af6461a8a 100644 --- a/cmd/mavenBuild.go +++ b/cmd/mavenBuild.go @@ -1,18 +1,21 @@ package cmd import ( + "encoding/json" "os" "path" "path/filepath" "reflect" "strings" + "github.com/SAP/jenkins-library/pkg/build" "github.com/SAP/jenkins-library/pkg/buildsettings" "github.com/SAP/jenkins-library/pkg/command" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/maven" "github.com/SAP/jenkins-library/pkg/piperutils" "github.com/SAP/jenkins-library/pkg/telemetry" + "github.com/SAP/jenkins-library/pkg/versioning" "github.com/pkg/errors" piperhttp "github.com/SAP/jenkins-library/pkg/http" @@ -159,7 +162,45 @@ func runMavenBuild(config *mavenBuildOptions, telemetryData *telemetry.CustomDat mavenOptions.Goals = []string{"deploy"} mavenOptions.Defines = []string{} _, err := maven.Execute(&mavenOptions, utils) - return err + if err != nil { + return err + } + if config.CreateBuildArtifactsMetadata { + buildCoordinates := []versioning.Coordinates{} + options := versioning.Options{} + var utils versioning.Utils + + matches, _ := fileUtils.Glob("**/pom.xml") + for _, match := range matches { + + artifact, err := versioning.GetArtifact("maven", match, &options, utils) + if err != nil { + log.Entry().Warnf("unable to get artifact metdata : %v", err) + } else { + coordinate, err := artifact.GetCoordinates() + if err != nil { + log.Entry().Warnf("unable to get artifact coordinates : %v", err) + } else { + coordinate.BuildPath = filepath.Dir(match) + coordinate.URL = config.AltDeploymentRepositoryURL + buildCoordinates = append(buildCoordinates, coordinate) + } + } + } + + if len(buildCoordinates) == 0 { + log.Entry().Warnf("unable to identify artifact coordinates for the maven packages published") + return nil + } + + var buildArtifacts build.BuildArtifacts + + buildArtifacts.Coordinates = buildCoordinates + jsonResult, _ := json.Marshal(buildArtifacts) + commonPipelineEnvironment.custom.mavenBuildArtifacts = string(jsonResult) + } + + return nil } else { log.Entry().Infof("publish not detected, ignoring maven deploy") } diff --git a/cmd/mavenBuild_generated.go b/cmd/mavenBuild_generated.go index 278af421b..57af28804 100644 --- a/cmd/mavenBuild_generated.go +++ b/cmd/mavenBuild_generated.go @@ -40,11 +40,13 @@ type mavenBuildOptions struct { JavaCaCertFilePath string `json:"javaCaCertFilePath,omitempty"` BuildSettingsInfo string `json:"buildSettingsInfo,omitempty"` DeployFlags []string `json:"deployFlags,omitempty"` + CreateBuildArtifactsMetadata bool `json:"createBuildArtifactsMetadata,omitempty"` } type mavenBuildCommonPipelineEnvironment struct { custom struct { - buildSettingsInfo string + buildSettingsInfo string + mavenBuildArtifacts string } } @@ -55,6 +57,7 @@ func (p *mavenBuildCommonPipelineEnvironment) persist(path, resourceName string) value interface{} }{ {category: "custom", name: "buildSettingsInfo", value: p.custom.buildSettingsInfo}, + {category: "custom", name: "mavenBuildArtifacts", value: p.custom.mavenBuildArtifacts}, } errCount := 0 @@ -269,6 +272,7 @@ func addMavenBuildFlags(cmd *cobra.Command, stepConfig *mavenBuildOptions) { cmd.Flags().StringVar(&stepConfig.JavaCaCertFilePath, "javaCaCertFilePath", os.Getenv("PIPER_javaCaCertFilePath"), "path to the cacerts file used by Java. When maven publish is set to True and customTlsCertificateLinks (to deploy the artifact to a repository with a self signed cert) are provided to trust the self signed certs, Piper will extend the existing Java cacerts to include the new self signed certs. if not provided Piper will search for the cacerts in $JAVA_HOME/jre/lib/security/cacerts") cmd.Flags().StringVar(&stepConfig.BuildSettingsInfo, "buildSettingsInfo", os.Getenv("PIPER_buildSettingsInfo"), "build settings info is typically filled by the step automatically to create information about the build settings that were used during the maven build . This information is typically used for compliance related processes.") cmd.Flags().StringSliceVar(&stepConfig.DeployFlags, "deployFlags", []string{`-Dmaven.main.skip=true`, `-Dmaven.test.skip=true`, `-Dmaven.install.skip=true`}, "maven deploy flags that will be used when publish is detected.") + cmd.Flags().BoolVar(&stepConfig.CreateBuildArtifactsMetadata, "createBuildArtifactsMetadata", false, "metadata about the artifacts that are build and published , this metadata is generally used by steps downstream in the pipeline") } @@ -507,6 +511,15 @@ func mavenBuildMetadata() config.StepData { Aliases: []config.Alias{}, Default: []string{`-Dmaven.main.skip=true`, `-Dmaven.test.skip=true`, `-Dmaven.install.skip=true`}, }, + { + Name: "createBuildArtifactsMetadata", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"STEPS", "STAGES", "PARAMETERS"}, + Type: "bool", + Mandatory: false, + Aliases: []config.Alias{}, + Default: false, + }, }, }, Containers: []config.Container{ @@ -519,6 +532,7 @@ func mavenBuildMetadata() config.StepData { Type: "piperEnvironment", Parameters: []map[string]interface{}{ {"name": "custom/buildSettingsInfo"}, + {"name": "custom/mavenBuildArtifacts"}, }, }, { diff --git a/cmd/mavenBuild_test.go b/cmd/mavenBuild_test.go index bb0cd1167..c7f6bb3d5 100644 --- a/cmd/mavenBuild_test.go +++ b/cmd/mavenBuild_test.go @@ -135,4 +135,25 @@ func TestMavenBuild(t *testing.T) { assert.Contains(t, mockedUtils.Calls[0].Params, "profile1,profile2") }) + t.Run("mavenBuild should not create build artifacts metadata when CreateBuildArtifactsMetadata is false and Publish is true", func(t *testing.T) { + mockedUtils := newMavenMockUtils() + mockedUtils.AddFile("pom.xml", []byte{}) + config := mavenBuildOptions{CreateBuildArtifactsMetadata: false, Publish: true} + err := runMavenBuild(&config, nil, &mockedUtils, &cpe) + assert.Nil(t, err) + assert.Equal(t, mockedUtils.Calls[0].Exec, "mvn") + assert.Contains(t, mockedUtils.Calls[0].Params, "install") + assert.Empty(t, cpe.custom.mavenBuildArtifacts) + }) + + t.Run("mavenBuild should not create build artifacts metadata when CreateBuildArtifactsMetadata is true and Publish is false", func(t *testing.T) { + mockedUtils := newMavenMockUtils() + mockedUtils.AddFile("pom.xml", []byte{}) + config := mavenBuildOptions{CreateBuildArtifactsMetadata: true, Publish: false} + err := runMavenBuild(&config, nil, &mockedUtils, &cpe) + assert.Nil(t, err) + assert.Equal(t, mockedUtils.Calls[0].Exec, "mvn") + assert.Empty(t, cpe.custom.mavenBuildArtifacts) + }) + } diff --git a/cmd/npmExecuteScripts.go b/cmd/npmExecuteScripts.go index b3de55b92..0b6cd4b18 100644 --- a/cmd/npmExecuteScripts.go +++ b/cmd/npmExecuteScripts.go @@ -1,12 +1,15 @@ package cmd import ( + "encoding/json" "os" + "github.com/SAP/jenkins-library/pkg/build" "github.com/SAP/jenkins-library/pkg/buildsettings" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/npm" "github.com/SAP/jenkins-library/pkg/telemetry" + "github.com/SAP/jenkins-library/pkg/versioning" ) func npmExecuteScripts(config npmExecuteScriptsOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *npmExecuteScriptsCommonPipelineEnvironment) { @@ -85,9 +88,11 @@ func runNpmExecuteScripts(npmExecutor npm.Executor, config *npmExecuteScriptsOpt } commonPipelineEnvironment.custom.buildSettingsInfo = buildSettingsInfo + buildCoordinates := []versioning.Coordinates{} + if config.Publish { if len(config.BuildDescriptorList) > 0 { - err = npmExecutor.PublishAllPackages(config.BuildDescriptorList, config.RepositoryURL, config.RepositoryUsername, config.RepositoryPassword, config.PackBeforePublish) + err = npmExecutor.PublishAllPackages(config.BuildDescriptorList, config.RepositoryURL, config.RepositoryUsername, config.RepositoryPassword, config.PackBeforePublish, &buildCoordinates) if err != nil { return err } @@ -97,12 +102,25 @@ func runNpmExecuteScripts(npmExecutor npm.Executor, config *npmExecuteScriptsOpt return err } - err = npmExecutor.PublishAllPackages(packageJSONFiles, config.RepositoryURL, config.RepositoryUsername, config.RepositoryPassword, config.PackBeforePublish) + err = npmExecutor.PublishAllPackages(packageJSONFiles, config.RepositoryURL, config.RepositoryUsername, config.RepositoryPassword, config.PackBeforePublish, &buildCoordinates) if err != nil { return err } } } + if config.CreateBuildArtifactsMetadata { + if len(buildCoordinates) == 0 { + log.Entry().Warnf("unable to identify artifact coordinates for the npm packages published") + return nil + } + + var buildArtifacts build.BuildArtifacts + + buildArtifacts.Coordinates = buildCoordinates + jsonResult, _ := json.Marshal(buildArtifacts) + commonPipelineEnvironment.custom.npmBuildArtifacts = string(jsonResult) + } + return nil } diff --git a/cmd/npmExecuteScripts_generated.go b/cmd/npmExecuteScripts_generated.go index 47d2cb1ef..7359c8827 100644 --- a/cmd/npmExecuteScripts_generated.go +++ b/cmd/npmExecuteScripts_generated.go @@ -22,26 +22,28 @@ import ( ) type npmExecuteScriptsOptions struct { - Install bool `json:"install,omitempty"` - RunScripts []string `json:"runScripts,omitempty"` - DefaultNpmRegistry string `json:"defaultNpmRegistry,omitempty"` - VirtualFrameBuffer bool `json:"virtualFrameBuffer,omitempty"` - ScriptOptions []string `json:"scriptOptions,omitempty"` - BuildDescriptorExcludeList []string `json:"buildDescriptorExcludeList,omitempty"` - BuildDescriptorList []string `json:"buildDescriptorList,omitempty"` - CreateBOM bool `json:"createBOM,omitempty"` - Publish bool `json:"publish,omitempty"` - RepositoryURL string `json:"repositoryUrl,omitempty"` - RepositoryPassword string `json:"repositoryPassword,omitempty"` - RepositoryUsername string `json:"repositoryUsername,omitempty"` - BuildSettingsInfo string `json:"buildSettingsInfo,omitempty"` - PackBeforePublish bool `json:"packBeforePublish,omitempty"` - Production bool `json:"production,omitempty"` + Install bool `json:"install,omitempty"` + RunScripts []string `json:"runScripts,omitempty"` + DefaultNpmRegistry string `json:"defaultNpmRegistry,omitempty"` + VirtualFrameBuffer bool `json:"virtualFrameBuffer,omitempty"` + ScriptOptions []string `json:"scriptOptions,omitempty"` + BuildDescriptorExcludeList []string `json:"buildDescriptorExcludeList,omitempty"` + BuildDescriptorList []string `json:"buildDescriptorList,omitempty"` + CreateBOM bool `json:"createBOM,omitempty"` + Publish bool `json:"publish,omitempty"` + RepositoryURL string `json:"repositoryUrl,omitempty"` + RepositoryPassword string `json:"repositoryPassword,omitempty"` + RepositoryUsername string `json:"repositoryUsername,omitempty"` + BuildSettingsInfo string `json:"buildSettingsInfo,omitempty"` + PackBeforePublish bool `json:"packBeforePublish,omitempty"` + Production bool `json:"production,omitempty"` + CreateBuildArtifactsMetadata bool `json:"createBuildArtifactsMetadata,omitempty"` } type npmExecuteScriptsCommonPipelineEnvironment struct { custom struct { buildSettingsInfo string + npmBuildArtifacts string } } @@ -52,6 +54,7 @@ func (p *npmExecuteScriptsCommonPipelineEnvironment) persist(path, resourceName value interface{} }{ {category: "custom", name: "buildSettingsInfo", value: p.custom.buildSettingsInfo}, + {category: "custom", name: "npmBuildArtifacts", value: p.custom.npmBuildArtifacts}, } errCount := 0 @@ -241,6 +244,7 @@ func addNpmExecuteScriptsFlags(cmd *cobra.Command, stepConfig *npmExecuteScripts cmd.Flags().StringVar(&stepConfig.BuildSettingsInfo, "buildSettingsInfo", os.Getenv("PIPER_buildSettingsInfo"), "build settings info is typically filled by the step automatically to create information about the build settings that were used during the npm build . This information is typically used for compliance related processes.") cmd.Flags().BoolVar(&stepConfig.PackBeforePublish, "packBeforePublish", false, "used for executing npm pack first, followed by npm publish. This two step maybe required in two cases. case 1) When building multiple npm packages (multiple package.json) please keep this parameter true and also see `buildDescriptorList` or `buildDescriptorExcludeList` to choose which package(s) to publish. case 2)when you are building a single npm (single `package.json` in your repo) / multiple npm (multiple package.json) scoped package(s) and have npm dependencies from the same scope.") cmd.Flags().BoolVar(&stepConfig.Production, "production", false, "used for omitting installation of dev. dependencies if true") + cmd.Flags().BoolVar(&stepConfig.CreateBuildArtifactsMetadata, "createBuildArtifactsMetadata", false, "metadata about the artifacts that are build and published , this metadata is generally used by steps downstream in the pipeline") } @@ -428,6 +432,15 @@ func npmExecuteScriptsMetadata() config.StepData { Aliases: []config.Alias{}, Default: false, }, + { + Name: "createBuildArtifactsMetadata", + ResourceRef: []config.ResourceReference{}, + Scope: []string{"STEPS", "STAGES", "PARAMETERS"}, + Type: "bool", + Mandatory: false, + Aliases: []config.Alias{}, + Default: false, + }, }, }, Containers: []config.Container{ @@ -440,6 +453,7 @@ func npmExecuteScriptsMetadata() config.StepData { Type: "piperEnvironment", Parameters: []map[string]interface{}{ {"name": "custom/buildSettingsInfo"}, + {"name": "custom/npmBuildArtifacts"}, }, }, { diff --git a/cmd/npmExecuteScripts_test.go b/cmd/npmExecuteScripts_test.go index c98bf7096..878dc8f24 100644 --- a/cmd/npmExecuteScripts_test.go +++ b/cmd/npmExecuteScripts_test.go @@ -194,4 +194,5 @@ func TestNpmExecuteScripts(t *testing.T) { v := os.Getenv("NODE_ENV") assert.Equal(t, "production", v) }) + } diff --git a/pkg/build/artifact.go b/pkg/build/artifact.go new file mode 100644 index 000000000..392402ff4 --- /dev/null +++ b/pkg/build/artifact.go @@ -0,0 +1,7 @@ +package build + +import "github.com/SAP/jenkins-library/pkg/versioning" + +type BuildArtifacts struct { + Coordinates []versioning.Coordinates +} diff --git a/pkg/npm/mock.go b/pkg/npm/mock.go index 036d3f2e6..d16218133 100644 --- a/pkg/npm/mock.go +++ b/pkg/npm/mock.go @@ -5,6 +5,7 @@ package npm import ( "fmt" + "github.com/SAP/jenkins-library/pkg/versioning" "github.com/SAP/jenkins-library/pkg/mock" ) @@ -123,6 +124,6 @@ func (n *NpmExecutorMock) CreateBOM(packageJSONFiles []string) error { } // CreateBOM mock implementation -func (n *NpmExecutorMock) PublishAllPackages(packageJSONFiles []string, registry, username, password string, packBeforePublish bool) error { +func (n *NpmExecutorMock) PublishAllPackages(packageJSONFiles []string, registry, username, password string, packBeforePublish bool, buildCoordinates *[]versioning.Coordinates) error { return nil } diff --git a/pkg/npm/npm.go b/pkg/npm/npm.go index 4768a0247..45433e589 100644 --- a/pkg/npm/npm.go +++ b/pkg/npm/npm.go @@ -11,6 +11,7 @@ import ( "github.com/SAP/jenkins-library/pkg/command" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/piperutils" + "github.com/SAP/jenkins-library/pkg/versioning" ) const ( @@ -34,7 +35,7 @@ type Executor interface { FindPackageJSONFilesWithScript(packageJSONFiles []string, script string) ([]string, error) RunScriptsInAllPackages(runScripts []string, runOptions []string, scriptOptions []string, virtualFrameBuffer bool, excludeList []string, packagesList []string) error InstallAllDependencies(packageJSONFiles []string) error - PublishAllPackages(packageJSONFiles []string, registry, username, password string, packBeforePublish bool) error + PublishAllPackages(packageJSONFiles []string, registry, username, password string, packBeforePublish bool, buildCoordinates *[]versioning.Coordinates) error SetNpmRegistries() error CreateBOM(packageJSONFiles []string) error } diff --git a/pkg/npm/publish.go b/pkg/npm/publish.go index 628729894..660e1ae11 100644 --- a/pkg/npm/publish.go +++ b/pkg/npm/publish.go @@ -11,6 +11,7 @@ import ( "github.com/SAP/jenkins-library/pkg/log" CredentialUtils "github.com/SAP/jenkins-library/pkg/piperutils" + "github.com/SAP/jenkins-library/pkg/versioning" ) type npmMinimalPackageDescriptor struct { @@ -31,7 +32,7 @@ func (pd *npmMinimalPackageDescriptor) Scope() string { } // PublishAllPackages executes npm publish for all package.json files defined in packageJSONFiles list -func (exec *Execute) PublishAllPackages(packageJSONFiles []string, registry, username, password string, packBeforePublish bool) error { +func (exec *Execute) PublishAllPackages(packageJSONFiles []string, registry, username, password string, packBeforePublish bool, buildCoordinates *[]versioning.Coordinates) error { for _, packageJSON := range packageJSONFiles { log.Entry().Infof("triggering publish for %s", packageJSON) @@ -43,7 +44,7 @@ func (exec *Execute) PublishAllPackages(packageJSONFiles []string, registry, use return fmt.Errorf("package.json file '%s' not found: %w", packageJSON, err) } - err = exec.publish(packageJSON, registry, username, password, packBeforePublish) + err = exec.publish(packageJSON, registry, username, password, packBeforePublish, buildCoordinates) if err != nil { return err } @@ -52,7 +53,7 @@ func (exec *Execute) PublishAllPackages(packageJSONFiles []string, registry, use } // publish executes npm publish for package.json -func (exec *Execute) publish(packageJSON, registry, username, password string, packBeforePublish bool) error { +func (exec *Execute) publish(packageJSON, registry, username, password string, packBeforePublish bool, buildCoordinates *[]versioning.Coordinates) error { execRunner := exec.Utils.GetExecRunner() oldWorkingDirectory, err := exec.Utils.Getwd() @@ -202,6 +203,25 @@ func (exec *Execute) publish(packageJSON, registry, username, password string, p } } + options := versioning.Options{} + var utils versioning.Utils + + artifact, err := versioning.GetArtifact("npm", packageJSON, &options, utils) + if err != nil { + log.Entry().Warnf("unable to get artifact metdata : %v", err) + } else { + coordinate, err := artifact.GetCoordinates() + if err != nil { + log.Entry().Warnf("unable to get artifact coordinates : %v", err) + } else { + coordinate.BuildPath = filepath.Dir(packageJSON) + coordinate.URL = registry + coordinate.Packaging = "tgz" + + *buildCoordinates = append(*buildCoordinates, coordinate) + } + } + return nil } diff --git a/pkg/npm/publish_test.go b/pkg/npm/publish_test.go index 6ca7fdaaa..782e1b7c5 100644 --- a/pkg/npm/publish_test.go +++ b/pkg/npm/publish_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/SAP/jenkins-library/pkg/piperutils" + "github.com/SAP/jenkins-library/pkg/versioning" "github.com/stretchr/testify/assert" ) @@ -534,7 +535,8 @@ func TestNpmPublish(t *testing.T) { return nil } - err := exec.PublishAllPackages(test.packageDescriptors, test.registryURL, test.registryUser, test.registryPassword, test.packBeforePublish) + coordinates := []versioning.Coordinates{} + err := exec.PublishAllPackages(test.packageDescriptors, test.registryURL, test.registryUser, test.registryPassword, test.packBeforePublish, &coordinates) if len(test.wants.err) == 0 && assert.NoError(t, err) { if assert.NotEmpty(t, utils.execRunner.Calls) { diff --git a/pkg/versioning/versioning.go b/pkg/versioning/versioning.go index 471bd5584..1ea42b48f 100644 --- a/pkg/versioning/versioning.go +++ b/pkg/versioning/versioning.go @@ -17,6 +17,8 @@ type Coordinates struct { ArtifactID string Version string Packaging string + BuildPath string + URL string } // Artifact defines the versioning operations for various build tools diff --git a/resources/metadata/mavenBuild.yaml b/resources/metadata/mavenBuild.yaml index e2ccfd7c7..dc56fb323 100644 --- a/resources/metadata/mavenBuild.yaml +++ b/resources/metadata/mavenBuild.yaml @@ -242,6 +242,14 @@ spec: - -Dmaven.main.skip=true - -Dmaven.test.skip=true - -Dmaven.install.skip=true + - name: createBuildArtifactsMetadata + type: bool + default: false + description: metadata about the artifacts that are build and published , this metadata is generally used by steps downstream in the pipeline + scope: + - STEPS + - STAGES + - PARAMETERS resources: - type: stash outputs: @@ -250,6 +258,7 @@ spec: type: piperEnvironment params: - name: custom/buildSettingsInfo + - name: custom/mavenBuildArtifacts - name: reports type: reports params: diff --git a/resources/metadata/npmExecuteScripts.yaml b/resources/metadata/npmExecuteScripts.yaml index 2dd39b941..529c6d32c 100644 --- a/resources/metadata/npmExecuteScripts.yaml +++ b/resources/metadata/npmExecuteScripts.yaml @@ -163,12 +163,21 @@ spec: - STEPS - STAGES - PARAMETERS + - name: createBuildArtifactsMetadata + type: bool + default: false + description: metadata about the artifacts that are build and published , this metadata is generally used by steps downstream in the pipeline + scope: + - STEPS + - STAGES + - PARAMETERS outputs: resources: - name: commonPipelineEnvironment type: piperEnvironment params: - name: custom/buildSettingsInfo + - name: custom/npmBuildArtifacts - name: reports type: reports params: diff --git a/vars/commonPipelineEnvironment.groovy b/vars/commonPipelineEnvironment.groovy index b0997292b..b44842d9c 100644 --- a/vars/commonPipelineEnvironment.groovy +++ b/vars/commonPipelineEnvironment.groovy @@ -60,6 +60,7 @@ class commonPipelineEnvironment implements Serializable { String abapAddonDescriptor + private Map valueMap = [:] void setValue(String property, value) {