1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-16 05:16:08 +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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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
}
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 {
exists, err := utils.FileExists(creatorPath)
if err != nil {
@ -163,7 +155,7 @@ func copyProject(source, target string, include, exclude *ignore.GitIgnore, util
}
if !isIgnored(relPath, include, exclude) {
target := path.Join(target, strings.ReplaceAll(sourceFile, source, ""))
dir, err := isDir(sourceFile)
dir, err := utils.DirExists(sourceFile)
if err != nil {
log.SetErrorCategory(log.ErrorBuild)
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
}
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{}) {
if c.BuildEnvVars == nil {
c.BuildEnvVars = vars
@ -302,20 +315,23 @@ func runCnbBuild(config *cnbBuildOptions, telemetryData *telemetry.CustomData, u
}
target := "/workspace"
source, err := utils.Getwd()
pwd, err := utils.Getwd()
if err != nil {
log.SetErrorCategory(log.ErrorBuild)
return errors.Wrap(err, "failed to get current working directory")
}
var source string
if len(config.Path) > 0 {
source = config.Path
} else {
source = pwd
}
dir, err := isDir(source)
dir, err := utils.DirExists(source)
if err != nil {
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 {
@ -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)
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))
})
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.Parallel()
commonPipelineEnvironment := cnbBuildCommonPipelineEnvironment{}

View File

@ -1,3 +1,4 @@
//go:build !release
// +build !release
package mock
@ -38,6 +39,8 @@ func (fInfo fileInfoMock) Sys() interface{} { return nil }
type fileProperties struct {
content *[]byte
mode os.FileMode
isLink bool
target string
}
// 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)
}
// 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.
// Only relative paths are supported.
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
}
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.
// It is the concrete type returned from FilesMock.Open()
type FileMock struct {

View File

@ -1,6 +1,7 @@
package mock
import (
"errors"
"os"
"path/filepath"
"testing"
@ -642,3 +643,33 @@ func TestFilesMockTempDir(t *testing.T) {
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
FileRename(string, string) error
Getwd() (string, error)
Symlink(oldname string, newname string) error
}
// Files ...
@ -384,3 +385,8 @@ func (f Files) Stat(path string) (os.FileInfo, error) {
func (f Files) Abs(path string) (string, error) {
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'
labelPrefix: pr_
cnbBuild:
stashExcludes:
stashBack: '**/*'
stashIncludes:
stashBack: '**/target/*.exec, **/*.jtl, **/target/**/*.xml'