refactor: filecollector into new package (#2174)

* refactor: filecollector into new package

* Add test for symlinks

* add test fix bug of GetContainerArchive

* add test data
This commit is contained in:
ChristopherHX 2024-01-30 01:46:45 +01:00 committed by GitHub
parent 4ca35d2192
commit 5a80a044f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 160 additions and 35 deletions

View file

@ -35,6 +35,7 @@ import (
"golang.org/x/term" "golang.org/x/term"
"github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/filecollector"
) )
// NewContainer creates a reference to a container // NewContainer creates a reference to a container
@ -735,12 +736,12 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
ignorer = gitignore.NewMatcher(ps) ignorer = gitignore.NewMatcher(ps)
} }
fc := &fileCollector{ fc := &filecollector.FileCollector{
Fs: &defaultFs{}, Fs: &filecollector.DefaultFs{},
Ignorer: ignorer, Ignorer: ignorer,
SrcPath: srcPath, SrcPath: srcPath,
SrcPrefix: srcPrefix, SrcPrefix: srcPrefix,
Handler: &tarCollector{ Handler: &filecollector.TarCollector{
TarWriter: tw, TarWriter: tw,
UID: cr.UID, UID: cr.UID,
GID: cr.GID, GID: cr.GID,
@ -748,7 +749,7 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
}, },
} }
err = filepath.Walk(srcPath, fc.collectFiles(ctx, []string{})) err = filepath.Walk(srcPath, fc.CollectFiles(ctx, []string{}))
if err != nil { if err != nil {
return err return err
} }

View file

@ -21,6 +21,7 @@ import (
"golang.org/x/term" "golang.org/x/term"
"github.com/nektos/act/pkg/common" "github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/filecollector"
"github.com/nektos/act/pkg/lookpath" "github.com/nektos/act/pkg/lookpath"
) )
@ -65,7 +66,7 @@ func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, ta
return err return err
} }
tr := tar.NewReader(tarStream) tr := tar.NewReader(tarStream)
cp := &copyCollector{ cp := &filecollector.CopyCollector{
DstDir: destPath, DstDir: destPath,
} }
for { for {
@ -104,16 +105,16 @@ func (e *HostEnvironment) CopyDir(destPath string, srcPath string, useGitIgnore
ignorer = gitignore.NewMatcher(ps) ignorer = gitignore.NewMatcher(ps)
} }
fc := &fileCollector{ fc := &filecollector.FileCollector{
Fs: &defaultFs{}, Fs: &filecollector.DefaultFs{},
Ignorer: ignorer, Ignorer: ignorer,
SrcPath: srcPath, SrcPath: srcPath,
SrcPrefix: srcPrefix, SrcPrefix: srcPrefix,
Handler: &copyCollector{ Handler: &filecollector.CopyCollector{
DstDir: destPath, DstDir: destPath,
}, },
} }
return filepath.Walk(srcPath, fc.collectFiles(ctx, []string{})) return filepath.Walk(srcPath, fc.CollectFiles(ctx, []string{}))
} }
} }
@ -126,21 +127,21 @@ func (e *HostEnvironment) GetContainerArchive(ctx context.Context, srcPath strin
if err != nil { if err != nil {
return nil, err return nil, err
} }
tc := &tarCollector{ tc := &filecollector.TarCollector{
TarWriter: tw, TarWriter: tw,
} }
if fi.IsDir() { if fi.IsDir() {
srcPrefix := filepath.Dir(srcPath) srcPrefix := srcPath
if !strings.HasSuffix(srcPrefix, string(filepath.Separator)) { if !strings.HasSuffix(srcPrefix, string(filepath.Separator)) {
srcPrefix += string(filepath.Separator) srcPrefix += string(filepath.Separator)
} }
fc := &fileCollector{ fc := &filecollector.FileCollector{
Fs: &defaultFs{}, Fs: &filecollector.DefaultFs{},
SrcPath: srcPath, SrcPath: srcPath,
SrcPrefix: srcPrefix, SrcPrefix: srcPrefix,
Handler: tc, Handler: tc,
} }
err = filepath.Walk(srcPath, fc.collectFiles(ctx, []string{})) err = filepath.Walk(srcPath, fc.CollectFiles(ctx, []string{}))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -1,4 +1,71 @@
package container package container
import (
"archive/tar"
"context"
"io"
"os"
"path"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
// Type assert HostEnvironment implements ExecutionsEnvironment // Type assert HostEnvironment implements ExecutionsEnvironment
var _ ExecutionsEnvironment = &HostEnvironment{} var _ ExecutionsEnvironment = &HostEnvironment{}
func TestCopyDir(t *testing.T) {
dir, err := os.MkdirTemp("", "test-host-env-*")
assert.NoError(t, err)
defer os.RemoveAll(dir)
ctx := context.Background()
e := &HostEnvironment{
Path: filepath.Join(dir, "path"),
TmpDir: filepath.Join(dir, "tmp"),
ToolCache: filepath.Join(dir, "tool_cache"),
ActPath: filepath.Join(dir, "act_path"),
StdOut: os.Stdout,
Workdir: path.Join("testdata", "scratch"),
}
_ = os.MkdirAll(e.Path, 0700)
_ = os.MkdirAll(e.TmpDir, 0700)
_ = os.MkdirAll(e.ToolCache, 0700)
_ = os.MkdirAll(e.ActPath, 0700)
err = e.CopyDir(e.Workdir, e.Path, true)(ctx)
assert.NoError(t, err)
}
func TestGetContainerArchive(t *testing.T) {
dir, err := os.MkdirTemp("", "test-host-env-*")
assert.NoError(t, err)
defer os.RemoveAll(dir)
ctx := context.Background()
e := &HostEnvironment{
Path: filepath.Join(dir, "path"),
TmpDir: filepath.Join(dir, "tmp"),
ToolCache: filepath.Join(dir, "tool_cache"),
ActPath: filepath.Join(dir, "act_path"),
StdOut: os.Stdout,
Workdir: path.Join("testdata", "scratch"),
}
_ = os.MkdirAll(e.Path, 0700)
_ = os.MkdirAll(e.TmpDir, 0700)
_ = os.MkdirAll(e.ToolCache, 0700)
_ = os.MkdirAll(e.ActPath, 0700)
expectedContent := []byte("sdde/7sh")
err = os.WriteFile(filepath.Join(e.Path, "action.yml"), expectedContent, 0600)
assert.NoError(t, err)
archive, err := e.GetContainerArchive(ctx, e.Path)
assert.NoError(t, err)
defer archive.Close()
reader := tar.NewReader(archive)
h, err := reader.Next()
assert.NoError(t, err)
assert.Equal(t, "action.yml", h.Name)
content, err := io.ReadAll(reader)
assert.NoError(t, err)
assert.Equal(t, expectedContent, content)
_, err = reader.Next()
assert.ErrorIs(t, err, io.EOF)
}

View file

@ -0,0 +1 @@
testfile

View file

@ -1,4 +1,4 @@
package container package filecollector
import ( import (
"archive/tar" "archive/tar"
@ -17,18 +17,18 @@ import (
"github.com/go-git/go-git/v5/plumbing/format/index" "github.com/go-git/go-git/v5/plumbing/format/index"
) )
type fileCollectorHandler interface { type Handler interface {
WriteFile(path string, fi fs.FileInfo, linkName string, f io.Reader) error WriteFile(path string, fi fs.FileInfo, linkName string, f io.Reader) error
} }
type tarCollector struct { type TarCollector struct {
TarWriter *tar.Writer TarWriter *tar.Writer
UID int UID int
GID int GID int
DstDir string DstDir string
} }
func (tc tarCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error { func (tc TarCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error {
// create a new dir/file header // create a new dir/file header
header, err := tar.FileInfoHeader(fi, linkName) header, err := tar.FileInfoHeader(fi, linkName)
if err != nil { if err != nil {
@ -59,11 +59,11 @@ func (tc tarCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string,
return nil return nil
} }
type copyCollector struct { type CopyCollector struct {
DstDir string DstDir string
} }
func (cc *copyCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error { func (cc *CopyCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error {
fdestpath := filepath.Join(cc.DstDir, fpath) fdestpath := filepath.Join(cc.DstDir, fpath)
if err := os.MkdirAll(filepath.Dir(fdestpath), 0o777); err != nil { if err := os.MkdirAll(filepath.Dir(fdestpath), 0o777); err != nil {
return err return err
@ -82,29 +82,29 @@ func (cc *copyCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string
return nil return nil
} }
type fileCollector struct { type FileCollector struct {
Ignorer gitignore.Matcher Ignorer gitignore.Matcher
SrcPath string SrcPath string
SrcPrefix string SrcPrefix string
Fs fileCollectorFs Fs Fs
Handler fileCollectorHandler Handler Handler
} }
type fileCollectorFs interface { type Fs interface {
Walk(root string, fn filepath.WalkFunc) error Walk(root string, fn filepath.WalkFunc) error
OpenGitIndex(path string) (*index.Index, error) OpenGitIndex(path string) (*index.Index, error)
Open(path string) (io.ReadCloser, error) Open(path string) (io.ReadCloser, error)
Readlink(path string) (string, error) Readlink(path string) (string, error)
} }
type defaultFs struct { type DefaultFs struct {
} }
func (*defaultFs) Walk(root string, fn filepath.WalkFunc) error { func (*DefaultFs) Walk(root string, fn filepath.WalkFunc) error {
return filepath.Walk(root, fn) return filepath.Walk(root, fn)
} }
func (*defaultFs) OpenGitIndex(path string) (*index.Index, error) { func (*DefaultFs) OpenGitIndex(path string) (*index.Index, error) {
r, err := git.PlainOpen(path) r, err := git.PlainOpen(path)
if err != nil { if err != nil {
return nil, err return nil, err
@ -116,16 +116,16 @@ func (*defaultFs) OpenGitIndex(path string) (*index.Index, error) {
return i, nil return i, nil
} }
func (*defaultFs) Open(path string) (io.ReadCloser, error) { func (*DefaultFs) Open(path string) (io.ReadCloser, error) {
return os.Open(path) return os.Open(path)
} }
func (*defaultFs) Readlink(path string) (string, error) { func (*DefaultFs) Readlink(path string) (string, error) {
return os.Readlink(path) return os.Readlink(path)
} }
//nolint:gocyclo //nolint:gocyclo
func (fc *fileCollector) collectFiles(ctx context.Context, submodulePath []string) filepath.WalkFunc { func (fc *FileCollector) CollectFiles(ctx context.Context, submodulePath []string) filepath.WalkFunc {
i, _ := fc.Fs.OpenGitIndex(path.Join(fc.SrcPath, path.Join(submodulePath...))) i, _ := fc.Fs.OpenGitIndex(path.Join(fc.SrcPath, path.Join(submodulePath...)))
return func(file string, fi os.FileInfo, err error) error { return func(file string, fi os.FileInfo, err error) error {
if err != nil { if err != nil {
@ -166,7 +166,7 @@ func (fc *fileCollector) collectFiles(ctx context.Context, submodulePath []strin
} }
} }
if err == nil && entry.Mode == filemode.Submodule { if err == nil && entry.Mode == filemode.Submodule {
err = fc.Fs.Walk(file, fc.collectFiles(ctx, split)) err = fc.Fs.Walk(file, fc.CollectFiles(ctx, split))
if err != nil { if err != nil {
return err return err
} }

View file

@ -1,4 +1,4 @@
package container package filecollector
import ( import (
"archive/tar" "archive/tar"
@ -95,16 +95,16 @@ func TestIgnoredTrackedfile(t *testing.T) {
tw := tar.NewWriter(tmpTar) tw := tar.NewWriter(tmpTar)
ps, _ := gitignore.ReadPatterns(worktree, []string{}) ps, _ := gitignore.ReadPatterns(worktree, []string{})
ignorer := gitignore.NewMatcher(ps) ignorer := gitignore.NewMatcher(ps)
fc := &fileCollector{ fc := &FileCollector{
Fs: &memoryFs{Filesystem: fs}, Fs: &memoryFs{Filesystem: fs},
Ignorer: ignorer, Ignorer: ignorer,
SrcPath: "mygitrepo", SrcPath: "mygitrepo",
SrcPrefix: "mygitrepo" + string(filepath.Separator), SrcPrefix: "mygitrepo" + string(filepath.Separator),
Handler: &tarCollector{ Handler: &TarCollector{
TarWriter: tw, TarWriter: tw,
}, },
} }
err := fc.Fs.Walk("mygitrepo", fc.collectFiles(context.Background(), []string{})) err := fc.Fs.Walk("mygitrepo", fc.CollectFiles(context.Background(), []string{}))
assert.NoError(t, err, "successfully collect files") assert.NoError(t, err, "successfully collect files")
tw.Close() tw.Close()
_, _ = tmpTar.Seek(0, io.SeekStart) _, _ = tmpTar.Seek(0, io.SeekStart)
@ -115,3 +115,58 @@ func TestIgnoredTrackedfile(t *testing.T) {
_, err = tr.Next() _, err = tr.Next()
assert.ErrorIs(t, err, io.EOF, "tar must only contain one element") assert.ErrorIs(t, err, io.EOF, "tar must only contain one element")
} }
func TestSymlinks(t *testing.T) {
fs := memfs.New()
_ = fs.MkdirAll("mygitrepo/.git", 0o777)
dotgit, _ := fs.Chroot("mygitrepo/.git")
worktree, _ := fs.Chroot("mygitrepo")
repo, _ := git.Init(filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault()), worktree)
// This file shouldn't be in the tar
f, err := worktree.Create(".env")
assert.NoError(t, err)
_, err = f.Write([]byte("test=val1\n"))
assert.NoError(t, err)
f.Close()
err = worktree.Symlink(".env", "test.env")
assert.NoError(t, err)
w, err := repo.Worktree()
assert.NoError(t, err)
// .gitignore is in the tar after adding it to the index
_, err = w.Add(".env")
assert.NoError(t, err)
_, err = w.Add("test.env")
assert.NoError(t, err)
tmpTar, _ := fs.Create("temp.tar")
tw := tar.NewWriter(tmpTar)
ps, _ := gitignore.ReadPatterns(worktree, []string{})
ignorer := gitignore.NewMatcher(ps)
fc := &FileCollector{
Fs: &memoryFs{Filesystem: fs},
Ignorer: ignorer,
SrcPath: "mygitrepo",
SrcPrefix: "mygitrepo" + string(filepath.Separator),
Handler: &TarCollector{
TarWriter: tw,
},
}
err = fc.Fs.Walk("mygitrepo", fc.CollectFiles(context.Background(), []string{}))
assert.NoError(t, err, "successfully collect files")
tw.Close()
_, _ = tmpTar.Seek(0, io.SeekStart)
tr := tar.NewReader(tmpTar)
h, err := tr.Next()
files := map[string]tar.Header{}
for err == nil {
files[h.Name] = *h
h, err = tr.Next()
}
assert.Equal(t, ".env", files[".env"].Name)
assert.Equal(t, "test.env", files["test.env"].Name)
assert.Equal(t, ".env", files["test.env"].Linkname)
assert.ErrorIs(t, err, io.EOF, "tar must be read cleanly to EOF")
}