From bf7efae20549c4038b9d9b42a5f95087610f0acb Mon Sep 17 00:00:00 2001 From: Nicolas Simonds Date: Tue, 20 Feb 2024 17:25:04 -0800 Subject: [PATCH] refactor: replace afero with avfs [avfs](https://github.com/avfs/avfs) does the same thing as afero, but provides a more complete abstraction layer, and a more fully-functional MemFS for testing, i.e., it supports symlinks. Move all file operations over to avfs, including `exec.RunInTempDir`, and add unit tests for symlink handling. --- examples/go-client/go.mod | 2 +- examples/go-client/go.sum | 4 +- go.mod | 3 +- go.sum | 2 + internal/exec/exec.go | 9 +- internal/exec/exec_public_test.go | 2 + internal/exec/types.go | 3 + internal/git/git.go | 10 +- internal/git/git_public_test.go | 4 +- internal/git/types.go | 4 +- internal/repositories/repositories.go | 18 ++- .../repositories/repositories_public_test.go | 19 ++-- internal/repositories/repositories_test.go | 12 +- internal/repositories/types.go | 4 +- internal/repository/copy.go | 30 +++-- internal/repository/copy_public_test.go | 88 +++++++++------ internal/repository/helper_test.go | 4 +- internal/repository/repository.go | 20 ++-- internal/repository/repository_public_test.go | 103 +++++++++--------- internal/repository/types.go | 6 +- pkg/repositories/repositories.go | 8 +- pkg/repositories/types.go | 4 +- 22 files changed, 196 insertions(+), 163 deletions(-) diff --git a/examples/go-client/go.mod b/examples/go-client/go.mod index 41e2658..2619166 100644 --- a/examples/go-client/go.mod +++ b/examples/go-client/go.mod @@ -10,13 +10,13 @@ require ( ) require ( + github.com/avfs/avfs v0.33.0 // indirect github.com/danjacques/gofslock v0.0.0-20230728142113-ae8f59f9e88b // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.18.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/spf13/afero v1.11.0 // indirect golang.org/x/crypto v0.19.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.17.0 // indirect diff --git a/examples/go-client/go.sum b/examples/go-client/go.sum index 1b974ec..11925eb 100644 --- a/examples/go-client/go.sum +++ b/examples/go-client/go.sum @@ -1,3 +1,5 @@ +github.com/avfs/avfs v0.33.0 h1:5WQXbUbr6VS7aani39ZN2Vrd/s3wLnyih1Sc4ExWTxs= +github.com/avfs/avfs v0.33.0/go.mod h1:Q59flcFRYe9KYkNMfrLUJney3yeKGQpcWRyxsDBW7vI= github.com/danjacques/gofslock v0.0.0-20230728142113-ae8f59f9e88b h1:BBkZ6LZYtzMQ2Oo5LkovMmUp0gxAD+AnXzfknZlFTBo= github.com/danjacques/gofslock v0.0.0-20230728142113-ae8f59f9e88b/go.mod h1:9LABMmUSkKzt6FVQNEWdUTM0bz8Bt8MPyEcuZe0Sr8c= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -20,8 +22,6 @@ github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc= github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= diff --git a/go.mod b/go.mod index 68307e7..6a5f10a 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/retr0h/gilt/v2 go 1.21.0 require ( + github.com/avfs/avfs v0.33.0 github.com/danjacques/gofslock v0.0.0-20230728142113-ae8f59f9e88b github.com/go-playground/validator/v10 v10.18.0 github.com/golang/mock v1.6.0 github.com/lmittmann/tint v1.0.4 - github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 @@ -32,6 +32,7 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect diff --git a/go.sum b/go.sum index f0e8519..6cd8f15 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/avfs/avfs v0.33.0 h1:5WQXbUbr6VS7aani39ZN2Vrd/s3wLnyih1Sc4ExWTxs= +github.com/avfs/avfs v0.33.0/go.mod h1:Q59flcFRYe9KYkNMfrLUJney3yeKGQpcWRyxsDBW7vI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= diff --git a/internal/exec/exec.go b/internal/exec/exec.go index 2ef44c8..133f410 100644 --- a/internal/exec/exec.go +++ b/internal/exec/exec.go @@ -22,16 +22,19 @@ package exec import ( "log/slog" - "os" "os/exec" "strings" + + "github.com/avfs/avfs" ) // New factory to create a new Exec instance. func New( + appFs avfs.VFS, logger *slog.Logger, ) *Exec { return &Exec{ + appFs: appFs, logger: logger, } } @@ -79,7 +82,7 @@ func (e *Exec) RunCmdInDir( // with the name of the directory as input. Then it cleans up the temporary // directory. func (e *Exec) RunInTempDir(dir, pattern string, fn func(string) error) error { - tmpDir, err := os.MkdirTemp(dir, pattern) + tmpDir, err := e.appFs.MkdirTemp(dir, pattern) if err != nil { return err } @@ -88,7 +91,7 @@ func (e *Exec) RunInTempDir(dir, pattern string, fn func(string) error) error { // Ignoring errors as there's not much we can do and this is a cleanup function. defer func() { e.logger.Debug("removing tempdir", slog.String("dir", tmpDir)) - _ = os.RemoveAll(tmpDir) + _ = e.appFs.RemoveAll(tmpDir) }() // Run the provided function. diff --git a/internal/exec/exec_public_test.go b/internal/exec/exec_public_test.go index 7c77760..47f54c7 100644 --- a/internal/exec/exec_public_test.go +++ b/internal/exec/exec_public_test.go @@ -25,6 +25,7 @@ import ( "os" "testing" + "github.com/avfs/avfs/vfs/memfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -38,6 +39,7 @@ type ExecManagerPublicTestSuite struct { func (suite *ExecManagerPublicTestSuite) NewTestExecManager() internal.ExecManager { return exec.New( + memfs.New(), slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug, })), diff --git a/internal/exec/types.go b/internal/exec/types.go index e44300f..1673724 100644 --- a/internal/exec/types.go +++ b/internal/exec/types.go @@ -22,9 +22,12 @@ package exec import ( "log/slog" + + "github.com/avfs/avfs" ) // Exec disk implementation. type Exec struct { + appFs avfs.VFS logger *slog.Logger } diff --git a/internal/git/git.go b/internal/git/git.go index 37ca6c9..1bdb062 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -25,17 +25,15 @@ package git import ( "log/slog" - "os" - "path/filepath" - "github.com/spf13/afero" + "github.com/avfs/avfs" "github.com/retr0h/gilt/v2/internal" ) // New factory to create a new Git instance. func New( - appFs afero.Fs, + appFs avfs.VFS, execManager internal.ExecManager, logger *slog.Logger, ) *Git { @@ -71,7 +69,7 @@ func (g *Git) Worktree( version string, dstDir string, ) error { - dst, err := filepath.Abs(dstDir) + dst, err := g.appFs.Abs(dstDir) if err != nil { return err } @@ -91,7 +89,7 @@ func (g *Git) Worktree( // `git worktree add` creates a breadcrumb file back to the original repo; // this is just junk data in our use case, so get rid of it if err == nil { - _ = os.Remove(filepath.Join(dst, ".git")) + _ = g.appFs.Remove(g.appFs.Join(dst, ".git")) _ = g.execManager.RunCmdInDir("git", []string{"worktree", "prune", "--verbose"}, cloneDir) } return err diff --git a/internal/git/git_public_test.go b/internal/git/git_public_test.go index 3ba617a..c56a64b 100644 --- a/internal/git/git_public_test.go +++ b/internal/git/git_public_test.go @@ -26,8 +26,8 @@ import ( "os" "testing" + "github.com/avfs/avfs/vfs/memfs" "github.com/golang/mock/gomock" - "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -52,7 +52,7 @@ type GitManagerPublicTestSuite struct { func (suite *GitManagerPublicTestSuite) NewTestGitManager() internal.GitManager { return git.New( - afero.NewMemMapFs(), + memfs.New(), suite.mockExec, slog.New(slog.NewTextHandler(os.Stdout, nil)), ) diff --git a/internal/git/types.go b/internal/git/types.go index b755ec8..500fa9a 100644 --- a/internal/git/types.go +++ b/internal/git/types.go @@ -23,14 +23,14 @@ package git import ( "log/slog" - "github.com/spf13/afero" + "github.com/avfs/avfs" "github.com/retr0h/gilt/v2/internal" ) // Git implementation responsible for Git operations. type Git struct { - appFs afero.Fs + appFs avfs.VFS execManager internal.ExecManager logger *slog.Logger } diff --git a/internal/repositories/repositories.go b/internal/repositories/repositories.go index 9c8c8bb..797fa23 100644 --- a/internal/repositories/repositories.go +++ b/internal/repositories/repositories.go @@ -22,11 +22,9 @@ package repositories import ( "log/slog" - "os" - "path/filepath" "strings" - "github.com/spf13/afero" + "github.com/avfs/avfs" "github.com/retr0h/gilt/v2/internal" intPath "github.com/retr0h/gilt/v2/internal/path" @@ -35,7 +33,7 @@ import ( // New factory to create a new Repository instance. func New( - appFs afero.Fs, + appFs avfs.VFS, c config.Repositories, repoManager internal.RepositoryManager, execManager internal.ExecManager, @@ -62,13 +60,13 @@ func (r *Repositories) getCacheDir() (string, error) { return "", err } - cacheDir := filepath.Join(giltDir, "cache") - if _, err := r.appFs.Stat(cacheDir); os.IsNotExist(err) { - if err := r.appFs.Mkdir(cacheDir, 0o700); err != nil { - return "", err + cacheDir := r.appFs.Join(giltDir, "cache") + if err := r.appFs.MkdirAll(cacheDir, 0o700); err != nil { + if i, e := r.appFs.Stat(cacheDir); e == nil && i.IsDir() { + return cacheDir, nil } + return "", err } - return cacheDir, nil } @@ -112,7 +110,7 @@ func (r *Repositories) Overlay() error { return err } err = r.execManager.RunInTempDir(giltDir, "tmp", func(tmpDir string) error { - tmpClone := filepath.Join(tmpDir, filepath.Base(targetDir)) + tmpClone := r.appFs.Join(tmpDir, r.appFs.Base(targetDir)) if err := r.repoManager.Worktree(c, targetDir, tmpClone); err != nil { return err } diff --git a/internal/repositories/repositories_public_test.go b/internal/repositories/repositories_public_test.go index 5efc57d..aca816c 100644 --- a/internal/repositories/repositories_public_test.go +++ b/internal/repositories/repositories_public_test.go @@ -24,11 +24,12 @@ import ( "errors" "log/slog" "os" - "path/filepath" "testing" + "github.com/avfs/avfs" + "github.com/avfs/avfs/vfs/memfs" + "github.com/avfs/avfs/vfs/rofs" "github.com/golang/mock/gomock" - "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -46,7 +47,7 @@ type RepositoriesPublicTestSuite struct { mockRepo *repository.MockRepositoryManager mockExec *exec.MockExecManager - appFs afero.Fs + appFs avfs.VFS dstDir string giltDir string gitURL string @@ -80,7 +81,7 @@ func (suite *RepositoriesPublicTestSuite) SetupTest() { suite.mockExec = exec.NewMockExecManager(suite.ctrl) defer suite.ctrl.Finish() - suite.appFs = afero.NewMemMapFs() + suite.appFs = memfs.New() suite.dstDir = "/dstDir" suite.giltDir = "/giltDir" suite.gitURL = "https://example.com/user/repo.git" @@ -98,7 +99,7 @@ func (suite *RepositoriesPublicTestSuite) SetupTest() { func (suite *RepositoriesPublicTestSuite) TestOverlayOkWhenDstDir() { repos := suite.NewTestRepositoriesManager(suite.repoConfigDstDir) - expected := filepath.Join(suite.giltDir, "cache") + expected := suite.appFs.Join(suite.giltDir, "cache") suite.mockRepo.EXPECT(). Clone(suite.repoConfigDstDir[0], expected). @@ -113,7 +114,7 @@ func (suite *RepositoriesPublicTestSuite) TestOverlayOkWhenDstDir() { func (suite *RepositoriesPublicTestSuite) TestOverlayErrorWhenDstDir() { repos := suite.NewTestRepositoriesManager(suite.repoConfigDstDir) - expected := filepath.Join(suite.giltDir, "cache") + expected := suite.appFs.Join(suite.giltDir, "cache") errors := errors.New("tests error") suite.mockRepo.EXPECT(). @@ -129,7 +130,7 @@ func (suite *RepositoriesPublicTestSuite) TestOverlayErrorWhenDstDir() { func (suite *RepositoriesPublicTestSuite) TestOverlayCacheDirCreateError() { // Replace the test FS with a read-only copy - suite.appFs = afero.NewReadOnlyFs(suite.appFs) + suite.appFs = rofs.New(suite.appFs) repos := suite.NewTestRepositoriesManager(suite.repoConfigDstDir) err := repos.Overlay() assert.Error(suite.T(), err) @@ -148,10 +149,10 @@ func (suite *RepositoriesPublicTestSuite) TestOverlayDstDirExists() { func (suite *RepositoriesPublicTestSuite) TestOverlayErrorRemovingDstDir() { suite.mockRepo.EXPECT().Clone(gomock.Any(), gomock.Any()).Return("", nil) - _ = suite.appFs.MkdirAll(filepath.Join(suite.giltDir, "cache"), 0o700) + _ = suite.appFs.MkdirAll(suite.appFs.Join(suite.giltDir, "cache"), 0o700) _ = suite.appFs.MkdirAll(suite.dstDir, 0o755) // Replace the test FS with a read-only copy - suite.appFs = afero.NewReadOnlyFs(suite.appFs) + suite.appFs = rofs.New(suite.appFs) repos := suite.NewTestRepositoriesManager(suite.repoConfigDstDir) err := repos.Overlay() assert.Error(suite.T(), err) diff --git a/internal/repositories/repositories_test.go b/internal/repositories/repositories_test.go index acead66..7202852 100644 --- a/internal/repositories/repositories_test.go +++ b/internal/repositories/repositories_test.go @@ -25,8 +25,10 @@ import ( "os" "testing" + "github.com/avfs/avfs" + "github.com/avfs/avfs/vfs/memfs" + "github.com/avfs/avfs/vfs/rofs" "github.com/golang/mock/gomock" - "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -42,7 +44,7 @@ type RepositoriesTestSuite struct { mockRepo *repository.MockRepositoryManager mockExec *exec.MockExecManager - appFs afero.Fs + appFs avfs.VFS giltDir string logger *slog.Logger } @@ -72,7 +74,7 @@ func (suite *RepositoriesTestSuite) SetupTest() { suite.mockExec = exec.NewMockExecManager(suite.ctrl) defer suite.ctrl.Finish() - suite.appFs = afero.NewMemMapFs() + suite.appFs = memfs.New() suite.giltDir = "/giltDir" suite.logger = slog.New(slog.NewTextHandler(os.Stdout, nil)) @@ -86,14 +88,14 @@ func (suite *RepositoriesTestSuite) TestgetCacheDir() { assert.NoError(suite.T(), err) assert.Equal(suite.T(), expectedDir, got) - exists, err := afero.Exists(suite.appFs, expectedDir) + exists, err := avfs.Exists(suite.appFs, expectedDir) assert.NoError(suite.T(), err) assert.True(suite.T(), exists) } func (suite *RepositoriesTestSuite) TestgetCacheDirCannotCreateError() { // Replace the test FS with a read-only copy - suite.appFs = afero.NewReadOnlyFs(suite.appFs) + suite.appFs = rofs.New(suite.appFs) repos := suite.NewTestRepositories(suite.giltDir) got, err := repos.getCacheDir() diff --git a/internal/repositories/types.go b/internal/repositories/types.go index 27ddf5e..7bf8bb9 100644 --- a/internal/repositories/types.go +++ b/internal/repositories/types.go @@ -23,7 +23,7 @@ package repositories import ( "log/slog" - "github.com/spf13/afero" + "github.com/avfs/avfs" "github.com/retr0h/gilt/v2/internal" "github.com/retr0h/gilt/v2/pkg/config" @@ -31,7 +31,7 @@ import ( // Repositories perform repository operations. type Repositories struct { - appFs afero.Fs + appFs avfs.VFS config config.Repositories logger *slog.Logger diff --git a/internal/repository/copy.go b/internal/repository/copy.go index dd3ed41..b4dc03b 100644 --- a/internal/repository/copy.go +++ b/internal/repository/copy.go @@ -24,15 +24,13 @@ import ( "fmt" "io" "log/slog" - "os" - "path/filepath" - "github.com/spf13/afero" + "github.com/avfs/avfs" ) // NewCopy factory to create a new copy instance. func NewCopy( - appFs afero.Fs, + appFs avfs.VFS, logger *slog.Logger, ) *Copy { return &Copy{ @@ -50,7 +48,7 @@ func (r *Copy) CopyFile( src string, dst string, ) (err error) { - baseSrc := filepath.Base(src) + baseSrc := r.appFs.Base(src) r.logger.Info( "copying file", @@ -109,9 +107,9 @@ func (r *Copy) CopyDir( src string, dst string, ) (err error) { - src = filepath.Clean(src) - dst = filepath.Clean(dst) - baseSrc := filepath.Base(src) + src = r.appFs.Clean(src) + dst = r.appFs.Clean(dst) + baseSrc := r.appFs.Base(src) r.logger.Info( "copying dir", @@ -127,10 +125,8 @@ func (r *Copy) CopyDir( return fmt.Errorf("source is not a directory") } - _, err = r.appFs.Stat(dst) - if err != nil && !os.IsNotExist(err) { - return err - } + d, err := r.appFs.Open(dst) + _ = d.Close() if err == nil { return fmt.Errorf("destination already exists") } @@ -140,22 +136,22 @@ func (r *Copy) CopyDir( return err } - entries, err := afero.ReadDir(r.appFs, src) + entries, err := r.appFs.ReadDir(src) if err != nil { return err } for _, entry := range entries { - srcPath := filepath.Join(src, entry.Name()) - dstPath := filepath.Join(dst, entry.Name()) + srcPath := r.appFs.Join(src, entry.Name()) + dstPath := r.appFs.Join(dst, entry.Name()) // Dereference any symlinks and copy their contents instead - entry, err = r.appFs.Stat(srcPath) + target, err := r.appFs.Stat(srcPath) if err != nil { return err } - if entry.IsDir() { + if target.IsDir() { err = r.CopyDir(srcPath, dstPath) if err != nil { return err diff --git a/internal/repository/copy_public_test.go b/internal/repository/copy_public_test.go index dd46b5a..ef9ac19 100644 --- a/internal/repository/copy_public_test.go +++ b/internal/repository/copy_public_test.go @@ -23,10 +23,11 @@ package repository_test import ( "log/slog" "os" - "path/filepath" "testing" - "github.com/spf13/afero" + "github.com/avfs/avfs" + "github.com/avfs/avfs/vfs/memfs" + "github.com/avfs/avfs/vfs/rofs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -36,7 +37,7 @@ import ( type CopyPublicTestSuite struct { suite.Suite - appFs afero.Fs + appFs avfs.VFS cloneDir string dstDir string } @@ -49,7 +50,7 @@ func (suite *CopyPublicTestSuite) NewTestCopyManager() repository.CopyManager { } func (suite *CopyPublicTestSuite) SetupTest() { - suite.appFs = afero.NewMemMapFs() + suite.appFs = memfs.New() suite.cloneDir = "/cloneDir" suite.dstDir = "/dstDir" } @@ -60,17 +61,21 @@ func (suite *CopyPublicTestSuite) TestCopyFileOk() { specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "srcDir"), - srcFile: filepath.Join(suite.cloneDir, "srcDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "srcDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "srcDir", "1.txt"), + }, + { + appFs: suite.appFs, + srcDir: suite.dstDir, }, } createFileSpecs(specs) - assertFile := filepath.Join(suite.dstDir, "1.txt") + assertFile := suite.appFs.Join(suite.dstDir, "1.txt") err := cm.CopyFile(specs[0].srcFile, assertFile) assert.NoError(suite.T(), err) - got, _ := afero.Exists(suite.appFs, assertFile) + got, _ := avfs.Exists(suite.appFs, assertFile) assert.True(suite.T(), got) } @@ -81,7 +86,7 @@ func (suite *CopyPublicTestSuite) TestCopyFileReturnsError() { err := cm.CopyFile("/invalidSrc", assertFile) assert.Error(suite.T(), err) - got, _ := afero.Exists(suite.appFs, assertFile) + got, _ := avfs.Exists(suite.appFs, assertFile) assert.False(suite.T(), got) } @@ -89,15 +94,15 @@ func (suite *CopyPublicTestSuite) TestCopyFileReturnsErrorEPERM() { specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "srcDir"), - srcFile: filepath.Join(suite.cloneDir, "srcDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "srcDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "srcDir", "1.txt"), }, } createFileSpecs(specs) - assertFile := filepath.Join(suite.dstDir, "1.txt") + assertFile := suite.appFs.Join(suite.dstDir, "1.txt") // Replace the test FS with a read-only copy - suite.appFs = afero.NewReadOnlyFs(suite.appFs) + suite.appFs = rofs.New(suite.appFs) cm := suite.NewTestCopyManager() err := cm.CopyFile(specs[0].srcFile, assertFile) assert.Error(suite.T(), err) @@ -109,17 +114,17 @@ func (suite *CopyPublicTestSuite) TestCopyDirOk() { specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "srcDir"), - srcFile: filepath.Join(suite.cloneDir, "srcDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "srcDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "srcDir", "1.txt"), }, } createFileSpecs(specs) - assertFile := filepath.Join(suite.dstDir, "1.txt") + assertFile := suite.appFs.Join(suite.dstDir, "1.txt") err := cm.CopyDir(specs[0].srcDir, suite.dstDir) assert.NoError(suite.T(), err) - got, _ := afero.Exists(suite.appFs, assertFile) + got, _ := avfs.Exists(suite.appFs, assertFile) assert.True(suite.T(), got) } @@ -129,22 +134,42 @@ func (suite *CopyPublicTestSuite) TestCopyDirNestedOk() { specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "srcDir", "subDir"), - srcFile: filepath.Join(suite.cloneDir, "srcDir", "subDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "srcDir", "subDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "srcDir", "subDir", "1.txt"), }, } createFileSpecs(specs) - assertFile := filepath.Join(suite.dstDir, "subDir", "1.txt") - err := cm.CopyDir(filepath.Join(suite.cloneDir, "srcDir"), suite.dstDir) + assertFile := suite.appFs.Join(suite.dstDir, "subDir", "1.txt") + err := cm.CopyDir(suite.appFs.Join(suite.cloneDir, "srcDir"), suite.dstDir) assert.NoError(suite.T(), err) - got, _ := afero.Exists(suite.appFs, assertFile) + got, _ := avfs.Exists(suite.appFs, assertFile) assert.True(suite.T(), got) } func (suite *CopyPublicTestSuite) TestCopyDirSymlinksOk() { - suite.T().Skip("afero.MemMapFS does not support symlinks") + cm := suite.NewTestCopyManager() + + specs := []FileSpec{ + { + appFs: suite.appFs, + srcDir: suite.appFs.Join(suite.cloneDir, "srcDir", "subDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "srcDir", "subDir", "1.txt"), + }, + } + createFileSpecs(specs) + _ = suite.appFs.Symlink( + suite.appFs.Join(suite.cloneDir, "srcDir"), + suite.appFs.Join(suite.cloneDir, "srcLink"), + ) + + assertFile := suite.appFs.Join(suite.dstDir, "subDir", "1.txt") + err := cm.CopyDir(suite.appFs.Join(suite.cloneDir, "srcLink"), suite.dstDir) + assert.NoError(suite.T(), err) + + got, _ := avfs.Exists(suite.appFs, assertFile) + assert.True(suite.T(), got) } func (suite *CopyPublicTestSuite) TestCopyDirReturnsError() { @@ -154,7 +179,7 @@ func (suite *CopyPublicTestSuite) TestCopyDirReturnsError() { err := cm.CopyDir("/invalidSrc", assertDir) assert.Error(suite.T(), err) - got, _ := afero.Exists(suite.appFs, assertDir) + got, _ := avfs.Exists(suite.appFs, assertDir) assert.False(suite.T(), got) } @@ -164,22 +189,23 @@ func (suite *CopyPublicTestSuite) TestCopyDirReturnsErrorEEXIST() { specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "srcDir"), - srcFile: filepath.Join(suite.cloneDir, "srcDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "srcDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "srcDir", "1.txt"), }, { appFs: suite.appFs, - srcFile: filepath.Join(suite.dstDir, "1.txt"), + srcDir: suite.dstDir, + srcFile: suite.appFs.Join(suite.dstDir, "1.txt"), }, } createFileSpecs(specs) - assertFile := filepath.Join(suite.dstDir, "1.txt") + assertFile := suite.appFs.Join(suite.dstDir, "1.txt") err := cm.CopyDir(specs[0].srcDir, suite.dstDir) assert.Error(suite.T(), err) assert.Contains(suite.T(), err.Error(), "destination already exists") - got, _ := afero.Exists(suite.appFs, assertFile) + got, _ := avfs.Exists(suite.appFs, assertFile) assert.True(suite.T(), got) } @@ -189,8 +215,8 @@ func (suite *CopyPublicTestSuite) TestCopyDirReturnsErrorWhenSrcIsNotDir() { specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "srcDir"), - srcFile: filepath.Join(suite.cloneDir, "srcDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "srcDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "srcDir", "1.txt"), }, } createFileSpecs(specs) diff --git a/internal/repository/helper_test.go b/internal/repository/helper_test.go index f62b53a..9cd1c60 100644 --- a/internal/repository/helper_test.go +++ b/internal/repository/helper_test.go @@ -21,11 +21,11 @@ package repository_test import ( - "github.com/spf13/afero" + "github.com/avfs/avfs" ) type FileSpec struct { - appFs afero.Fs + appFs avfs.VFS srcDir string srcFile string srcFiles []string diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 529c76b..3115bd0 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -23,11 +23,9 @@ package repository import ( "fmt" "log/slog" - "os" - "path/filepath" "strings" - "github.com/spf13/afero" + "github.com/avfs/avfs" "github.com/retr0h/gilt/v2/internal" "github.com/retr0h/gilt/v2/pkg/config" @@ -38,7 +36,7 @@ var replacer = strings.NewReplacer("/", "-", ":", "-") // New factory to create a new Repository instance. func New( - appFs afero.Fs, + appFs avfs.VFS, copyManager CopyManager, gitManager internal.GitManager, logger *slog.Logger, @@ -56,18 +54,20 @@ func (r *Repository) Clone( c config.Repository, cloneDir string, ) (string, error) { - targetDir := filepath.Join(cloneDir, replacer.Replace(c.Git)) + targetDir := r.appFs.Join(cloneDir, replacer.Replace(c.Git)) r.logger.Info( "cloning", slog.String("repository", c.Git), slog.String("dstDir", targetDir), ) - if _, err := r.appFs.Stat(targetDir); os.IsNotExist(err) { + if d, err := r.appFs.Open(targetDir); err != nil { + _ = d.Close() if err := r.gitManager.Clone(c.Git, targetDir); err != nil { return targetDir, err } } else { + _ = d.Close() r.logger.Info("clone already exists", slog.String("dstDir", targetDir)) if err := r.gitManager.Update(targetDir); err != nil { return targetDir, err @@ -93,8 +93,8 @@ func (r *Repository) CopySources( ) error { r.logger.Debug("copy", slog.String("origin", cloneDir)) for _, source := range c.Sources { - cloneDirWithSrcPath := filepath.Join(cloneDir, source.Src) // join clone dir with head - globbedSrc, err := afero.Glob(r.appFs, cloneDirWithSrcPath) + cloneDirWithSrcPath := r.appFs.Join(cloneDir, source.Src) // join clone dir with head + globbedSrc, err := r.appFs.Glob(cloneDirWithSrcPath) if err != nil { return err } @@ -112,8 +112,8 @@ func (r *Repository) CopySources( if err := r.appFs.MkdirAll(source.DstDir, 0o755); err != nil { return fmt.Errorf("unable to create dest dir: %s", err) } - srcBaseFile := filepath.Base(src) - newDst := filepath.Join(source.DstDir, srcBaseFile) + srcBaseFile := r.appFs.Base(src) + newDst := r.appFs.Join(source.DstDir, srcBaseFile) if err := r.copyManager.CopyFile(src, newDst); err != nil { return err } diff --git a/internal/repository/repository_public_test.go b/internal/repository/repository_public_test.go index 11ff25b..9fb9b41 100644 --- a/internal/repository/repository_public_test.go +++ b/internal/repository/repository_public_test.go @@ -24,11 +24,12 @@ import ( "errors" "log/slog" "os" - "path/filepath" "testing" + "github.com/avfs/avfs" + "github.com/avfs/avfs/vfs/memfs" + "github.com/avfs/avfs/vfs/rofs" "github.com/golang/mock/gomock" - "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -46,7 +47,7 @@ type RepositoryPublicTestSuite struct { mockGit *git.MockGitManager mockCopyManager *mock_repo.MockCopyManager - appFs afero.Fs + appFs avfs.VFS cloneDir string dstDir string gitURL string @@ -71,7 +72,7 @@ func (suite *RepositoryPublicTestSuite) SetupTest() { suite.mockCopyManager = mock_repo.NewMockCopyManager(suite.ctrl) defer suite.ctrl.Finish() - suite.appFs = afero.NewMemMapFs() + suite.appFs = memfs.New() suite.cloneDir = "/cloneDir" suite.dstDir = "/dstDir" suite.gitURL = "https://example.com/user/repo.git" @@ -88,7 +89,7 @@ func (suite *RepositoryPublicTestSuite) TestCloneOk() { Git: suite.gitURL, Version: suite.gitSHA, } - targetDir := filepath.Join(suite.cloneDir, suite.cacheDir) + targetDir := suite.appFs.Join(suite.cloneDir, suite.cacheDir) gomock.InOrder( suite.mockGit.EXPECT().Clone(suite.gitURL, targetDir).Return(nil), @@ -122,7 +123,7 @@ func (suite *RepositoryPublicTestSuite) TestCloneDoesNotCloneWhenCloneDirExists( Git: suite.gitURL, Version: suite.gitSHA, } - targetDir := filepath.Join(suite.cloneDir, suite.cacheDir) + targetDir := suite.appFs.Join(suite.cloneDir, suite.cacheDir) _ = suite.appFs.MkdirAll(targetDir, 0o755) suite.mockGit.EXPECT().Update(targetDir).Return(nil) @@ -138,7 +139,7 @@ func (suite *RepositoryPublicTestSuite) TestCloneUpdateCloneDirThrowsError() { Git: suite.gitURL, Version: suite.gitSHA, } - targetDir := filepath.Join(suite.cloneDir, suite.cacheDir) + targetDir := suite.appFs.Join(suite.cloneDir, suite.cacheDir) _ = suite.appFs.MkdirAll(targetDir, 0o755) errors := errors.New("tests error") @@ -153,8 +154,8 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsDirAndDstDi specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), - srcFile: filepath.Join(suite.cloneDir, "subDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), }, } createFileSpecs(specs) @@ -164,14 +165,14 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsDirAndDstDi Version: suite.gitSHA, Sources: []config.Source{ { - Src: filepath.Base(specs[0].srcDir), + Src: suite.appFs.Base(specs[0].srcDir), DstDir: suite.dstDir, }, }, } suite.mockCopyManager.EXPECT(). - CopyDir(filepath.Join(suite.cloneDir, c.Sources[0].Src), c.Sources[0].DstDir). + CopyDir(suite.appFs.Join(suite.cloneDir, c.Sources[0].Src), c.Sources[0].DstDir). Return(nil) err := repo.CopySources(c, suite.cloneDir) @@ -183,8 +184,8 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesReturnsErrorWhenSourceIsD specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), - srcFile: filepath.Join(suite.cloneDir, "subDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), }, } createFileSpecs(specs) @@ -194,7 +195,7 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesReturnsErrorWhenSourceIsD Version: suite.gitSHA, Sources: []config.Source{ { - Src: filepath.Base(specs[0].srcDir), + Src: suite.appFs.Base(specs[0].srcDir), DstDir: suite.dstDir, }, }, @@ -212,8 +213,8 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsDirAndDstDi specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), - srcFile: filepath.Join(suite.cloneDir, "subDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), }, } createFileSpecs(specs) @@ -223,14 +224,14 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsDirAndDstDi Version: suite.gitSHA, Sources: []config.Source{ { - Src: filepath.Base(specs[0].srcDir), + Src: suite.appFs.Base(specs[0].srcDir), DstDir: suite.dstDir, }, }, } suite.mockCopyManager.EXPECT(). - CopyDir(filepath.Join(suite.cloneDir, c.Sources[0].Src), c.Sources[0].DstDir). + CopyDir(suite.appFs.Join(suite.cloneDir, c.Sources[0].Src), c.Sources[0].DstDir). Return(nil) // create dstDir @@ -243,15 +244,15 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesErrorWhenSourceIsDirAndDs specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), - srcFile: filepath.Join(suite.cloneDir, "subDir", "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), + srcFile: suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), }, } createFileSpecs(specs) // create dstDir _ = suite.appFs.MkdirAll(suite.dstDir, 0o755) // Replace the test FS with a read-only copy - suite.appFs = afero.NewReadOnlyFs(suite.appFs) + suite.appFs = rofs.New(suite.appFs) repo := suite.NewRepositoryManager() c := config.Repository{ @@ -259,7 +260,7 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesErrorWhenSourceIsDirAndDs Version: suite.gitSHA, Sources: []config.Source{ { - Src: filepath.Base(specs[0].srcDir), + Src: suite.appFs.Base(specs[0].srcDir), DstDir: suite.dstDir, }, }, @@ -279,22 +280,22 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsFilesAndDst specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), srcFiles: []string{ - filepath.Join(suite.cloneDir, "subDir", "1.txt"), - filepath.Join(suite.cloneDir, "subDir", "cinder_manage"), - filepath.Join(suite.cloneDir, "subDir", "nova_manage"), - filepath.Join(suite.cloneDir, "subDir", "glance_manage"), + suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), + suite.appFs.Join(suite.cloneDir, "subDir", "cinder_manage"), + suite.appFs.Join(suite.cloneDir, "subDir", "nova_manage"), + suite.appFs.Join(suite.cloneDir, "subDir", "glance_manage"), }, }, { appFs: suite.appFs, srcDir: suite.cloneDir, srcFiles: []string{ - filepath.Join(suite.cloneDir, "1.txt"), - filepath.Join(suite.cloneDir, "cinder_manage"), - filepath.Join(suite.cloneDir, "nova_manage"), - filepath.Join(suite.cloneDir, "glance_manage"), + suite.appFs.Join(suite.cloneDir, "1.txt"), + suite.appFs.Join(suite.cloneDir, "cinder_manage"), + suite.appFs.Join(suite.cloneDir, "nova_manage"), + suite.appFs.Join(suite.cloneDir, "glance_manage"), }, }, } @@ -316,23 +317,23 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsFilesAndDst } suite.mockCopyManager.EXPECT(). - CopyFile(filepath.Join(suite.cloneDir, "subDir", "cinder_manage"), filepath.Join(suite.dstDir, "cinder_manage")). + CopyFile(suite.appFs.Join(suite.cloneDir, "subDir", "cinder_manage"), suite.appFs.Join(suite.dstDir, "cinder_manage")). Return(nil) suite.mockCopyManager.EXPECT(). - CopyFile(filepath.Join(suite.cloneDir, "subDir", "glance_manage"), filepath.Join(suite.dstDir, "glance_manage")). + CopyFile(suite.appFs.Join(suite.cloneDir, "subDir", "glance_manage"), suite.appFs.Join(suite.dstDir, "glance_manage")). Return(nil) suite.mockCopyManager.EXPECT(). - CopyFile(filepath.Join(suite.cloneDir, "subDir", "nova_manage"), filepath.Join(suite.dstDir, "nova_manage")). + CopyFile(suite.appFs.Join(suite.cloneDir, "subDir", "nova_manage"), suite.appFs.Join(suite.dstDir, "nova_manage")). Return(nil) suite.mockCopyManager.EXPECT(). - CopyFile(filepath.Join(suite.cloneDir, "cinder_manage"), filepath.Join(suite.dstDir, "cinder_manage")). + CopyFile(suite.appFs.Join(suite.cloneDir, "cinder_manage"), suite.appFs.Join(suite.dstDir, "cinder_manage")). Return(nil) suite.mockCopyManager.EXPECT(). - CopyFile(filepath.Join(suite.cloneDir, "glance_manage"), filepath.Join(suite.dstDir, "glance_manage")). + CopyFile(suite.appFs.Join(suite.cloneDir, "glance_manage"), suite.appFs.Join(suite.dstDir, "glance_manage")). Return(nil) suite.mockCopyManager.EXPECT(). - CopyFile(filepath.Join(suite.cloneDir, "nova_manage"), filepath.Join(suite.dstDir, "nova_manage")). + CopyFile(suite.appFs.Join(suite.cloneDir, "nova_manage"), suite.appFs.Join(suite.dstDir, "nova_manage")). Return(nil) err := repo.CopySources(c, suite.cloneDir) @@ -343,15 +344,15 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesReturnsErrorWhenSourceIsF specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), srcFiles: []string{ - filepath.Join(suite.cloneDir, "subDir", "1.txt"), + suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), }, }, } createFileSpecs(specs) // Replace the test FS with a read-only copy - suite.appFs = afero.NewReadOnlyFs(suite.appFs) + suite.appFs = rofs.New(suite.appFs) repo := suite.NewRepositoryManager() c := config.Repository{ @@ -379,9 +380,9 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesReturnsErrorWhenSourceIsF specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), srcFiles: []string{ - filepath.Join(suite.cloneDir, "subDir", "1.txt"), + suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), }, }, } @@ -410,8 +411,8 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsFileAndDstF specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir), - srcFile: filepath.Join(suite.cloneDir, "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir), + srcFile: suite.appFs.Join(suite.cloneDir, "1.txt"), }, } createFileSpecs(specs) @@ -422,13 +423,13 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesOkWhenSourceIsFileAndDstF Sources: []config.Source{ { Src: "1.txt", - DstFile: filepath.Join(suite.dstDir, "1.txt"), + DstFile: suite.appFs.Join(suite.dstDir, "1.txt"), }, }, } suite.mockCopyManager.EXPECT(). - CopyFile(filepath.Join(suite.cloneDir, "1.txt"), filepath.Join(suite.dstDir, "1.txt")). + CopyFile(suite.appFs.Join(suite.cloneDir, "1.txt"), suite.appFs.Join(suite.dstDir, "1.txt")). Return(nil) err := repo.CopySources(c, suite.cloneDir) @@ -440,8 +441,8 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesReturnsErrorWhenSourceIsF specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir), - srcFile: filepath.Join(suite.cloneDir, "1.txt"), + srcDir: suite.appFs.Join(suite.cloneDir), + srcFile: suite.appFs.Join(suite.cloneDir, "1.txt"), }, } createFileSpecs(specs) @@ -452,7 +453,7 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesReturnsErrorWhenSourceIsF Sources: []config.Source{ { Src: "1.txt", - DstFile: filepath.Join(suite.dstDir, "1.txt"), + DstFile: suite.appFs.Join(suite.dstDir, "1.txt"), }, }, } @@ -469,9 +470,9 @@ func (suite *RepositoryPublicTestSuite) TestCopySourcesReturnsErrorOnGarbagePatt specs := []FileSpec{ { appFs: suite.appFs, - srcDir: filepath.Join(suite.cloneDir, "subDir"), + srcDir: suite.appFs.Join(suite.cloneDir, "subDir"), srcFiles: []string{ - filepath.Join(suite.cloneDir, "subDir", "1.txt"), + suite.appFs.Join(suite.cloneDir, "subDir", "1.txt"), }, }, } diff --git a/internal/repository/types.go b/internal/repository/types.go index b660767..fa82131 100644 --- a/internal/repository/types.go +++ b/internal/repository/types.go @@ -23,14 +23,14 @@ package repository import ( "log/slog" - "github.com/spf13/afero" + "github.com/avfs/avfs" "github.com/retr0h/gilt/v2/internal" ) // Repository contains the repository's details for cloning. type Repository struct { - appFs afero.Fs + appFs avfs.VFS copyManager CopyManager gitManager internal.GitManager logger *slog.Logger @@ -44,6 +44,6 @@ type CopyManager interface { // Copy copy implementation. type Copy struct { - appFs afero.Fs + appFs avfs.VFS logger *slog.Logger } diff --git a/pkg/repositories/repositories.go b/pkg/repositories/repositories.go index 63aa9bf..3030371 100644 --- a/pkg/repositories/repositories.go +++ b/pkg/repositories/repositories.go @@ -24,11 +24,10 @@ import ( "errors" "fmt" "log/slog" - "path/filepath" "strconv" + "github.com/avfs/avfs/vfs/osfs" "github.com/danjacques/gofslock/fslock" - "github.com/spf13/afero" "github.com/retr0h/gilt/v2/internal/exec" "github.com/retr0h/gilt/v2/internal/git" @@ -43,7 +42,7 @@ func New( c config.Repositories, logger *slog.Logger, ) *Repositories { - appFs := afero.NewOsFs() + appFs := osfs.NewWithNoIdm() copyManager := repository.NewCopy( appFs, @@ -51,6 +50,7 @@ func New( ) execManager := exec.New( + appFs, logger, ) @@ -105,7 +105,7 @@ func (r *Repositories) withLock(fn func() error) error { return err } - lockFile := filepath.Join(lockDir, "gilt.lock") + lockFile := r.appFs.Join(lockDir, "gilt.lock") r.logger.Info( "acquiring lock", slog.String("lockfile", lockFile), diff --git a/pkg/repositories/types.go b/pkg/repositories/types.go index abfa9d4..9eb119d 100644 --- a/pkg/repositories/types.go +++ b/pkg/repositories/types.go @@ -23,7 +23,7 @@ package repositories import ( "log/slog" - "github.com/spf13/afero" + "github.com/avfs/avfs" "github.com/retr0h/gilt/v2/internal" "github.com/retr0h/gilt/v2/pkg/config" @@ -31,7 +31,7 @@ import ( // Repositories perform repository operations. type Repositories struct { - appFs afero.Fs + appFs avfs.VFS c config.Repositories reposManager internal.RepositoriesManager logger *slog.Logger