You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-11-06 09:09:19 +02:00
feat(cnbBuild): preserve maven test results in the workspace (#3429)
Co-authored-by: Pavel Busko <pavel.busko@sap.com> Co-authored-by: Ralf Pannemans <ralf.pannemans@sap.com>
This commit is contained in:
@@ -98,14 +98,6 @@ func isIgnored(find string, include, exclude *ignore.GitIgnore) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDir(path string) (bool, error) {
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return info.IsDir(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBuilder(utils cnbutils.BuildUtils) error {
|
func isBuilder(utils cnbutils.BuildUtils) error {
|
||||||
exists, err := utils.FileExists(creatorPath)
|
exists, err := utils.FileExists(creatorPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -163,7 +155,7 @@ func copyProject(source, target string, include, exclude *ignore.GitIgnore, util
|
|||||||
}
|
}
|
||||||
if !isIgnored(relPath, include, exclude) {
|
if !isIgnored(relPath, include, exclude) {
|
||||||
target := path.Join(target, strings.ReplaceAll(sourceFile, source, ""))
|
target := path.Join(target, strings.ReplaceAll(sourceFile, source, ""))
|
||||||
dir, err := isDir(sourceFile)
|
dir, err := utils.DirExists(sourceFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.SetErrorCategory(log.ErrorBuild)
|
log.SetErrorCategory(log.ErrorBuild)
|
||||||
return errors.Wrapf(err, "Checking file info '%s' failed", target)
|
return errors.Wrapf(err, "Checking file info '%s' failed", target)
|
||||||
@@ -221,6 +213,27 @@ func prepareDockerConfig(source string, utils cnbutils.BuildUtils) (string, erro
|
|||||||
return source, nil
|
return source, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func linkTargetFolder(utils cnbutils.BuildUtils, source, target string) error {
|
||||||
|
var err error
|
||||||
|
linkPath := filepath.Join(target, "target")
|
||||||
|
targetPath := filepath.Join(source, "target")
|
||||||
|
if ok, _ := utils.DirExists(targetPath); !ok {
|
||||||
|
err = utils.MkdirAll(targetPath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, _ := utils.DirExists(linkPath); ok {
|
||||||
|
err = utils.RemoveAll(linkPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.Symlink(targetPath, linkPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *cnbBuildOptions) mergeEnvVars(vars map[string]interface{}) {
|
func (c *cnbBuildOptions) mergeEnvVars(vars map[string]interface{}) {
|
||||||
if c.BuildEnvVars == nil {
|
if c.BuildEnvVars == nil {
|
||||||
c.BuildEnvVars = vars
|
c.BuildEnvVars = vars
|
||||||
@@ -302,20 +315,23 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
target := "/workspace"
|
target := "/workspace"
|
||||||
source, err := utils.Getwd()
|
pwd, err := utils.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.SetErrorCategory(log.ErrorBuild)
|
log.SetErrorCategory(log.ErrorBuild)
|
||||||
return errors.Wrap(err, "failed to get current working directory")
|
return errors.Wrap(err, "failed to get current working directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var source string
|
||||||
if len(config.Path) > 0 {
|
if len(config.Path) > 0 {
|
||||||
source = config.Path
|
source = config.Path
|
||||||
|
} else {
|
||||||
|
source = pwd
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, err := isDir(source)
|
dir, err := utils.DirExists(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.SetErrorCategory(log.ErrorBuild)
|
log.SetErrorCategory(log.ErrorBuild)
|
||||||
return errors.Wrapf(err, "Checking file info '%s' failed", target)
|
return errors.Wrapf(err, "Checking file info '%s' failed", source)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dir {
|
if dir {
|
||||||
@@ -332,6 +348,14 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ok, _ := utils.FileExists(filepath.Join(target, "pom.xml")); ok {
|
||||||
|
err = linkTargetFolder(utils, pwd, target)
|
||||||
|
if err != nil {
|
||||||
|
log.SetErrorCategory(log.ErrorBuild)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
metadata.WriteProjectMetadata(GeneralConfig.EnvRootPath, utils)
|
metadata.WriteProjectMetadata(GeneralConfig.EnvRootPath, utils)
|
||||||
|
|
||||||
var buildpacksPath = "/cnb/buildpacks"
|
var buildpacksPath = "/cnb/buildpacks"
|
||||||
|
|||||||
@@ -192,6 +192,55 @@ func TestRunCnbBuild(t *testing.T) {
|
|||||||
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3.1.5", registry, config.ContainerImageName))
|
assert.Contains(t, runner.Calls[0].Params, fmt.Sprintf("%s/%s:3.1.5", registry, config.ContainerImageName))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("pom.xml exists (symlink for the target folder)", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
config := cnbBuildOptions{
|
||||||
|
ContainerImageName: "my-image",
|
||||||
|
ContainerImageTag: "3.1.5",
|
||||||
|
ContainerRegistryURL: "some-registry",
|
||||||
|
DockerConfigJSON: "/path/to/config.json",
|
||||||
|
}
|
||||||
|
|
||||||
|
utils := newCnbBuildTestsUtils()
|
||||||
|
utils.FilesMock.CurrentDir = "/jenkins"
|
||||||
|
utils.FilesMock.AddDir("/jenkins")
|
||||||
|
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||||
|
utils.FilesMock.AddFile("/workspace/pom.xml", []byte("test"))
|
||||||
|
addBuilderFiles(&utils)
|
||||||
|
|
||||||
|
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
runner := utils.ExecMockRunner
|
||||||
|
assertLifecycleCalls(t, runner)
|
||||||
|
|
||||||
|
assert.True(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no pom.xml exists (no symlink for the target folder)", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
config := cnbBuildOptions{
|
||||||
|
ContainerImageName: "my-image",
|
||||||
|
ContainerImageTag: "3.1.5",
|
||||||
|
ContainerRegistryURL: "some-registry",
|
||||||
|
DockerConfigJSON: "/path/to/config.json",
|
||||||
|
}
|
||||||
|
|
||||||
|
utils := newCnbBuildTestsUtils()
|
||||||
|
utils.FilesMock.CurrentDir = "/jenkins"
|
||||||
|
utils.FilesMock.AddDir("/jenkins")
|
||||||
|
utils.FilesMock.AddFile(config.DockerConfigJSON, []byte(`{"auths":{"my-registry":{"auth":"dXNlcjpwYXNz"}}}`))
|
||||||
|
addBuilderFiles(&utils)
|
||||||
|
|
||||||
|
err := runCnbBuild(&config, &telemetry.CustomData{}, &utils, &cnbBuildCommonPipelineEnvironment{}, &piperhttp.Client{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
runner := utils.ExecMockRunner
|
||||||
|
assertLifecycleCalls(t, runner)
|
||||||
|
|
||||||
|
assert.False(t, utils.FilesMock.HasCreatedSymlink("/jenkins/target", "/workspace/target"))
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("error case: Invalid DockerConfigJSON file", func(t *testing.T) {
|
t.Run("error case: Invalid DockerConfigJSON file", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
|
commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build !release
|
||||||
// +build !release
|
// +build !release
|
||||||
|
|
||||||
package mock
|
package mock
|
||||||
@@ -38,6 +39,8 @@ func (fInfo fileInfoMock) Sys() interface{} { return nil }
|
|||||||
type fileProperties struct {
|
type fileProperties struct {
|
||||||
content *[]byte
|
content *[]byte
|
||||||
mode os.FileMode
|
mode os.FileMode
|
||||||
|
isLink bool
|
||||||
|
target string
|
||||||
}
|
}
|
||||||
|
|
||||||
// isDir returns true when the properties describe a directory entry.
|
// isDir returns true when the properties describe a directory entry.
|
||||||
@@ -153,6 +156,18 @@ func (f *FilesMock) HasCopiedFile(src string, dest string) bool {
|
|||||||
return f.copiedFiles[f.toAbsPath(src)] == f.toAbsPath(dest)
|
return f.copiedFiles[f.toAbsPath(src)] == f.toAbsPath(dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasCreatedSymlink returns true if the virtual file system has a symlink with a specific target.
|
||||||
|
func (f *FilesMock) HasCreatedSymlink(oldname, newname string) bool {
|
||||||
|
if f.files == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
props, exists := f.files[f.toAbsPath(newname)]
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return props.isLink && props.target == oldname
|
||||||
|
}
|
||||||
|
|
||||||
// FileExists returns true if file content has been associated with the given path, false otherwise.
|
// FileExists returns true if file content has been associated with the given path, false otherwise.
|
||||||
// Only relative paths are supported.
|
// Only relative paths are supported.
|
||||||
func (f *FilesMock) FileExists(path string) (bool, error) {
|
func (f *FilesMock) FileExists(path string) (bool, error) {
|
||||||
@@ -455,6 +470,33 @@ func (f *FilesMock) Abs(path string) (string, error) {
|
|||||||
return f.toAbsPath(path), nil
|
return f.toAbsPath(path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FilesMock) Symlink(oldname, newname string) error {
|
||||||
|
if f.FileWriteError != nil {
|
||||||
|
return f.FileWriteError
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.FileWriteErrors[newname] != nil {
|
||||||
|
return f.FileWriteErrors[newname]
|
||||||
|
}
|
||||||
|
|
||||||
|
parentExists, err := f.DirExists(filepath.Dir(newname))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !parentExists {
|
||||||
|
return fmt.Errorf("failed to create symlink: parent directory %s doesn't exist", filepath.Dir(newname))
|
||||||
|
}
|
||||||
|
|
||||||
|
f.init()
|
||||||
|
|
||||||
|
f.files[newname] = &fileProperties{
|
||||||
|
isLink: true,
|
||||||
|
target: oldname,
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// FileMock can be used in places where a io.Closer, io.StringWriter or io.Writer is expected.
|
// FileMock can be used in places where a io.Closer, io.StringWriter or io.Writer is expected.
|
||||||
// It is the concrete type returned from FilesMock.Open()
|
// It is the concrete type returned from FilesMock.Open()
|
||||||
type FileMock struct {
|
type FileMock struct {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mock
|
package mock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -642,3 +643,33 @@ func TestFilesMockTempDir(t *testing.T) {
|
|||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFilesMockSymlink(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("creates a symlink", func(t *testing.T) {
|
||||||
|
files := FilesMock{}
|
||||||
|
files.AddDir("/backup")
|
||||||
|
assert.NoError(t, files.Symlink("/folder", "/backup/folder"))
|
||||||
|
|
||||||
|
assert.True(t, files.HasCreatedSymlink("/folder", "/backup/folder"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails if parent directory doesn't exist", func(t *testing.T) {
|
||||||
|
files := FilesMock{}
|
||||||
|
err := files.Symlink("/non/existent/folder", "/symbolic/link")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "failed to create symlink: parent directory /symbolic doesn't exist", err.Error())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails if FileWriteError is specified", func(t *testing.T) {
|
||||||
|
expectedErr := errors.New("test")
|
||||||
|
files := FilesMock{
|
||||||
|
FileWriteErrors: map[string]error{
|
||||||
|
"/symbolic/link": expectedErr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := files.Symlink("/non/existent/folder", "/symbolic/link")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, expectedErr, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ type FileUtils interface {
|
|||||||
RemoveAll(string) error
|
RemoveAll(string) error
|
||||||
FileRename(string, string) error
|
FileRename(string, string) error
|
||||||
Getwd() (string, error)
|
Getwd() (string, error)
|
||||||
|
Symlink(oldname string, newname string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Files ...
|
// Files ...
|
||||||
@@ -384,3 +385,8 @@ func (f Files) Stat(path string) (os.FileInfo, error) {
|
|||||||
func (f Files) Abs(path string) (string, error) {
|
func (f Files) Abs(path string) (string, error) {
|
||||||
return filepath.Abs(path)
|
return filepath.Abs(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Symlink is a wrapper for os.Symlink
|
||||||
|
func (f Files) Symlink(oldname, newname string) error {
|
||||||
|
return os.Symlink(oldname, newname)
|
||||||
|
}
|
||||||
|
|||||||
@@ -548,5 +548,5 @@ steps:
|
|||||||
whitesource: 'whitesourceExecuteScan'
|
whitesource: 'whitesourceExecuteScan'
|
||||||
labelPrefix: pr_
|
labelPrefix: pr_
|
||||||
cnbBuild:
|
cnbBuild:
|
||||||
stashExcludes:
|
stashIncludes:
|
||||||
stashBack: '**/*'
|
stashBack: '**/target/*.exec, **/*.jtl, **/target/**/*.xml'
|
||||||
|
|||||||
Reference in New Issue
Block a user