Skip to content

Commit

Permalink
Copy projects with symlinks
Browse files Browse the repository at this point in the history
  • Loading branch information
modulo11 committed Aug 30, 2024
1 parent cb45ad1 commit 44e6920
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 91 deletions.
18 changes: 1 addition & 17 deletions cmd/gradleExecuteBuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,38 +121,22 @@ type Artifact struct {
Name string `json:"name,omitempty"`
}

type WalkDir func(root string, fn fs.WalkDirFunc) error

type Filepath interface {
WalkDir(root string, fn fs.WalkDirFunc) error
}

type WalkDirFunc func(root string, fn fs.WalkDirFunc) error

func (f WalkDirFunc) WalkDir(root string, fn fs.WalkDirFunc) error {
return f(root, fn)
}

type gradleExecuteBuildUtils interface {
command.ExecRunner
piperutils.FileUtils
Filepath
}

type gradleExecuteBuildUtilsBundle struct {
*command.Command
*piperutils.Files
Filepath
}

func newGradleExecuteBuildUtils() gradleExecuteBuildUtils {
var walkDirFunc WalkDirFunc = filepath.WalkDir
utils := gradleExecuteBuildUtilsBundle{
Command: &command.Command{
StepName: "gradleExecuteBuild",
},
Files: &piperutils.Files{},
Filepath: walkDirFunc,
Files: &piperutils.Files{},
}
utils.Stdout(log.Writer())
utils.Stderr(log.Writer())
Expand Down
8 changes: 0 additions & 8 deletions cmd/gradleExecuteBuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const moduleFileContent = `{"variants": [{"name": "apiElements","files": [{"name
type gradleExecuteBuildMockUtils struct {
*mock.ExecMockRunner
*mock.FilesMock
Filepath
}

type isDirEntryMock func() bool
Expand Down Expand Up @@ -121,16 +120,9 @@ func TestRunGradleExecuteBuild(t *testing.T) {
})

t.Run("success case - publishing of artifacts", func(t *testing.T) {
var walkDir WalkDirFunc = func(root string, fn fs.WalkDirFunc) error {
var dirMock isDirEntryMock = func() bool {
return false
}
return fn(filepath.Join("test_subproject_path", "build", "publications", "maven", "module.json"), dirMock, nil)
}
utils := gradleExecuteBuildMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
Filepath: walkDir,
}
utils.FilesMock.AddFile("path/to/build.gradle", []byte{})
utils.FilesMock.AddFile(filepath.Join("test_subproject_path", "build", "publications", "maven", "module.json"), []byte(moduleFileContent))
Expand Down
81 changes: 30 additions & 51 deletions pkg/cnbutils/copy_project.go
Original file line number Diff line number Diff line change
@@ -1,78 +1,59 @@
package cnbutils

import (
"io/fs"
"os"
"path"
"path/filepath"
"strings"

"github.com/SAP/jenkins-library/pkg/log"
"github.com/pkg/errors"
ignore "github.com/sabhiram/go-gitignore"
)

func CopyProject(source, target string, include, exclude *ignore.GitIgnore, utils BuildUtils) error {
sourceFiles, _ := utils.Glob(path.Join(source, "**"))
for _, sourceFile := range sourceFiles {
relPath, err := filepath.Rel(source, sourceFile)
utils.WalkDir(source, func(path string, d fs.DirEntry, err error) error {
target := filepath.Join(target, strings.ReplaceAll(path, source, ""))

if err != nil {
log.SetErrorCategory(log.ErrorBuild)
return errors.Wrapf(err, "Calculating relative path for '%s' failed", sourceFile)
return err
}

relPath, err := filepath.Rel(source, path)
if isIgnored(relPath, include, exclude) {
return filepath.SkipDir
}
if !isIgnored(relPath, include, exclude) {
target := path.Join(target, strings.ReplaceAll(sourceFile, source, ""))
dir, err := utils.DirExists(sourceFile)

switch d.Type() {
case fs.ModeDir:
log.Entry().Debugf("Creating directpry '%s'", path)
err := utils.MkdirAll(target, os.ModePerm)
if err != nil {
log.SetErrorCategory(log.ErrorBuild)
return errors.Wrapf(err, "Checking file info '%s' failed", target)
panic(err)
}

if dir {
err = utils.MkdirAll(target, os.ModePerm)
if err != nil {
log.SetErrorCategory(log.ErrorBuild)
return errors.Wrapf(err, "Creating directory '%s' failed", target)
}
} else {
log.Entry().Debugf("Copying '%s' to '%s'", sourceFile, target)
err = copyFile(sourceFile, target, utils)
if err != nil {
log.SetErrorCategory(log.ErrorBuild)
return errors.Wrapf(err, "Copying '%s' to '%s' failed", sourceFile, target)
}
case fs.ModeSymlink:
linkTarget, err := utils.Readlink(path)
log.Entry().Debugf("Creating symlink from '%s' to '%s'", target, linkTarget)
if err != nil {
panic(err)
}
os.Symlink(linkTarget, target)
default:
log.Entry().Debugf("Copying file from '%s' to '%s'", path, target)
_, err := utils.Copy(path, target)
if err != nil {
panic(err)
}

}
}
return nil
})
return nil
}

func copyFile(source, target string, utils BuildUtils) error {
targetDir := filepath.Dir(target)

exists, err := utils.DirExists(targetDir)
if err != nil {
return err
}

if !exists {
log.Entry().Debugf("Creating directory %s", targetDir)
err = utils.MkdirAll(targetDir, os.ModePerm)
if err != nil {
return err
}
}

_, err = utils.Copy(source, target)
return err
}

func isIgnored(find string, include, exclude *ignore.GitIgnore) bool {
if exclude != nil {
filtered := exclude.MatchesPath(find)

if filtered {
log.Entry().Debugf("%s matches exclude pattern, ignoring", find)
return true
}
}
Expand All @@ -81,10 +62,8 @@ func isIgnored(find string, include, exclude *ignore.GitIgnore) bool {
filtered := !include.MatchesPath(find)

if filtered {
log.Entry().Debugf("%s doesn't match include pattern, ignoring", find)
return true
} else {
log.Entry().Debugf("%s matches include pattern", find)
return false
}
}
Expand Down
21 changes: 6 additions & 15 deletions pkg/cnbutils/copy_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,18 @@ import (
)

func TestCopyProject(t *testing.T) {
t.Run("copies file according to doublestart globs", func(t *testing.T) {
t.Run("project is copied successfully", func(t *testing.T) {
mockUtils := &cnbutils.MockUtils{
FilesMock: &mock.FilesMock{},
}
mockUtils.AddFile("workdir/src/test.yaml", []byte(""))
mockUtils.AddFile("workdir/src/subdir1/test2.yaml", []byte(""))
mockUtils.AddFile("workdir/src/subdir1/subdir2/test3.yaml", []byte(""))
err := cnbutils.CopyProject("workdir/src", "/dest", ignore.CompileIgnoreLines([]string{"**/*.yaml"}...), nil, mockUtils)
mockUtils.AddFile("/workdir/src/test.yaml", []byte(""))
mockUtils.Symlink("/workdir/src/test.yaml", "/workdir/src/test-symlink.yaml")
mockUtils.AddFile("/workdir/src/subdir1/test2.yaml", []byte(""))
mockUtils.AddFile("/workdir/src/subdir1/subdir2/test3.yaml", []byte(""))
err := cnbutils.CopyProject("/workdir/src", "/dest", ignore.CompileIgnoreLines([]string{"**/*.yaml"}...), nil, mockUtils)
assert.NoError(t, err)
assert.True(t, mockUtils.HasCopiedFile("workdir/src/test.yaml", "/dest/test.yaml"))
assert.True(t, mockUtils.HasCopiedFile("workdir/src/subdir1/test2.yaml", "/dest/subdir1/test2.yaml"))
assert.True(t, mockUtils.HasCopiedFile("workdir/src/subdir1/subdir2/test3.yaml", "/dest/subdir1/subdir2/test3.yaml"))
})

t.Run("copies file according to simple globs", func(t *testing.T) {
mockUtils := &cnbutils.MockUtils{
FilesMock: &mock.FilesMock{},
}
mockUtils.AddFile("src/test.yaml", []byte(""))
err := cnbutils.CopyProject("src", "/dest", ignore.CompileIgnoreLines([]string{"*.yaml"}...), nil, mockUtils)
assert.NoError(t, err)
assert.True(t, mockUtils.HasCopiedFile("src/test.yaml", "/dest/test.yaml"))
})
}
51 changes: 51 additions & 0 deletions pkg/mock/fileUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
Expand Down Expand Up @@ -700,3 +701,53 @@ func (f *FilesMockRelativeGlob) Glob(pattern string) ([]string, error) {
sort.Strings(matches)
return matches, nil
}

type dirEntry struct {
name string
isDir bool
mode fs.FileMode
}

func (d dirEntry) Name() string {
return "foo"
}

func (d dirEntry) IsDir() bool {
return false
}

func (d dirEntry) Type() fs.FileMode {
return d.mode
}

func (d dirEntry) Info() (fs.FileInfo, error) {
return nil, nil
}

func (f *FilesMock) Readlink(name string) (string, error) {
properties, ok := f.files[name]
if ok && properties.isLink {
return properties.target, nil
}
return "", fmt.Errorf("could not retrieve target for %s", name)
}

func (f *FilesMock) WalkDir(root string, fn fs.WalkDirFunc) error {
for name, properties := range f.files {
dirEntry := dirEntry{
name: filepath.Base(name),
}
dirEntry.mode = fs.ModePerm

if properties.isLink {
dirEntry.mode = fs.ModeSymlink
}
if properties.isDir() {
dirEntry.mode = fs.ModeDir
dirEntry.isDir = true
}

fn(name, dirEntry, nil)
}
return nil
}
12 changes: 12 additions & 0 deletions pkg/piperutils/fileUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type FileUtils interface {
CurrentTime(format string) string
Open(name string) (io.ReadWriteCloser, error)
Create(name string) (io.ReadWriteCloser, error)
WalkDir(root string, fn fs.WalkDirFunc) error
Readlink(name string) (string, error)
}

// Files ...
Expand Down Expand Up @@ -513,3 +515,13 @@ func (f Files) Open(name string) (io.ReadWriteCloser, error) {
func (f Files) Create(name string) (io.ReadWriteCloser, error) {
return os.Create(name)
}

// WalkDir wraps filepath.WalkDir
func (f Files) WalkDir(root string, fn fs.WalkDirFunc) error {
return filepath.WalkDir(root, fn)
}

// Readlink wraps os.Readlink
func (f Files) Readlink(name string) (string, error) {
return os.Readlink(name)
}

0 comments on commit 44e6920

Please sign in to comment.