1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-11-28 08:49:44 +02:00

Exposing build artifact metadata from maven and npm (#5008)

This commit is contained in:
Anil Keshav 2024-08-27 08:24:38 +02:00 committed by GitHub
parent 70d2abf40f
commit 238339c87e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 186 additions and 25 deletions

View File

@ -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")
}

View File

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

View File

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

View File

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

View File

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

View File

@ -194,4 +194,5 @@ func TestNpmExecuteScripts(t *testing.T) {
v := os.Getenv("NODE_ENV")
assert.Equal(t, "production", v)
})
}

7
pkg/build/artifact.go Normal file
View File

@ -0,0 +1,7 @@
package build
import "github.com/SAP/jenkins-library/pkg/versioning"
type BuildArtifacts struct {
Coordinates []versioning.Coordinates
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,6 +60,7 @@ class commonPipelineEnvironment implements Serializable {
String abapAddonDescriptor
private Map valueMap = [:]
void setValue(String property, value) {