1
0
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:
Pavel Busko
2022-01-14 11:05:11 +01:00
committed by GitHub
parent 7007efc35f
commit 1750b75cb8
6 changed files with 166 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -548,5 +548,5 @@ steps:
whitesource: 'whitesourceExecuteScan' whitesource: 'whitesourceExecuteScan'
labelPrefix: pr_ labelPrefix: pr_
cnbBuild: cnbBuild:
stashExcludes: stashIncludes:
stashBack: '**/*' stashBack: '**/target/*.exec, **/*.jtl, **/target/**/*.xml'