Skip to content

Commit

Permalink
feat: Add new Action Cache
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherHX committed Jul 16, 2023
1 parent e343ea9 commit ac7392b
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 0 deletions.
163 changes: 163 additions & 0 deletions pkg/runner/action_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package runner

import (
"archive/tar"
"context"
"encoding/hex"
"errors"
"io"
"io/fs"
"math/rand"
"path"
"strings"

git "github.com/go-git/go-git/v5"
config "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
)

type ActionCache interface {
Fetch(ctx context.Context, cacheDir, url, ref, token string) (string, error)
GetTarArchive(ctx context.Context, cacheDir, sha, fpath string) (io.ReadCloser, error)
}

type GoGitActionCache struct {
Path string
}

func (c GoGitActionCache) Fetch(ctx context.Context, cacheDir, url, ref, token string) (string, error) {
gitPath := path.Join(c.Path, safeFilename(cacheDir)+".git")
gogitrepo, err := git.PlainInit(gitPath, true)
if errors.Is(err, git.ErrRepositoryAlreadyExists) {
gogitrepo, err = git.PlainOpen(gitPath)
}
if err != nil {
return "", err
}
tmpBranch := make([]byte, 12)
rand.Read(tmpBranch)

Check failure on line 41 in pkg/runner/action_cache.go

View workflow job for this annotation

GitHub Actions / lint

G404: Use of weak random number generator (math/rand instead of crypto/rand) (gosec)
branchName := hex.EncodeToString(tmpBranch)
var refSpec config.RefSpec
spec := config.RefSpec(ref + ":" + branchName)
tagOrSha := false
if spec.IsExactSHA1() {
refSpec = spec
} else if strings.HasPrefix(ref, "refs/") {
refSpec = config.RefSpec(ref + ":refs/heads/" + branchName)
} else {
tagOrSha = true
refSpec = config.RefSpec("refs/*/" + ref + ":refs/heads/*/" + branchName)
}
var auth transport.AuthMethod
if token != "" {
auth = &http.BasicAuth{
Username: "token",
Password: token,
}
}
remote, err := gogitrepo.CreateRemoteAnonymous(&config.RemoteConfig{
Name: "anonymous",
URLs: []string{
url,
},
})
if err != nil {
return "", err
}
defer func() {
if refs, err := gogitrepo.References(); err == nil {
refs.ForEach(func(r *plumbing.Reference) error {

Check failure on line 72 in pkg/runner/action_cache.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `refs.ForEach` is not checked (errcheck)
if strings.Contains(r.Name().String(), branchName) {
return gogitrepo.DeleteBranch(r.Name().String())
}
return nil
})
}
}()
if err := remote.FetchContext(ctx, &git.FetchOptions{
RefSpecs: []config.RefSpec{
refSpec,
},
Auth: auth,
Force: true,
}); err != nil {
return "", err
}
if tagOrSha {
for _, prefix := range []string{"refs/heads/tags/", "refs/heads/heads/"} {
hash, err := gogitrepo.ResolveRevision(plumbing.Revision(prefix + branchName))
if err == nil {
return hash.String(), nil
}
}
}
hash, err := gogitrepo.ResolveRevision(plumbing.Revision(branchName))
if err != nil {
return "", err
}
return hash.String(), nil
}

func (c GoGitActionCache) GetTarArchive(ctx context.Context, cacheDir, sha, fpath string) (io.ReadCloser, error) {

Check warning on line 104 in pkg/runner/action_cache.go

View workflow job for this annotation

GitHub Actions / lint

unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
gitPath := path.Join(c.Path, safeFilename(cacheDir)+".git")
gogitrepo, err := git.PlainOpen(gitPath)
if err != nil {
return nil, err
}
commit, err := gogitrepo.CommitObject(plumbing.NewHash(sha))
if err != nil {
return nil, err
}
files, err := commit.Files()
if err != nil {
return nil, err
}
rpipe, wpipe := io.Pipe()
go func() {
defer wpipe.Close()
tw := tar.NewWriter(wpipe)
fcpath := path.Clean(fpath)
files.ForEach(func(f *object.File) error {

Check failure on line 123 in pkg/runner/action_cache.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `files.ForEach` is not checked (errcheck)
name := f.Name
if strings.HasPrefix(name, fcpath+"/") {
name = name[len(fcpath)+1:]
} else if fcpath != "" && name != fcpath {
return nil
}
fmode, err := f.Mode.ToOSFileMode()
if err != nil {
return err
}
if fmode&fs.ModeSymlink == fs.ModeSymlink {
content, err := f.Contents()
if err != nil {
return err
}
return tw.WriteHeader(&tar.Header{
Name: name,
Mode: int64(fmode),
Linkname: content,
})
} else {

Check warning on line 144 in pkg/runner/action_cache.go

View workflow job for this annotation

GitHub Actions / lint

indent-error-flow: if block ends with a return statement, so drop this else and outdent its block (revive)
err = tw.WriteHeader(&tar.Header{
Name: name,
Mode: int64(fmode),
Size: f.Size,
})
if err != nil {
return err
}
reader, err := f.Reader()
if err != nil {
return err
}
_, err = io.Copy(tw, reader)
return err
}
})
}()
return rpipe, err
}
34 changes: 34 additions & 0 deletions pkg/runner/action_cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package runner

import (
"archive/tar"
"bytes"
"context"
"io"
"testing"

"github.com/stretchr/testify/assert"
)

func TestActionCache(t *testing.T) {
a := assert.New(t)
cache := &GoGitActionCache{
Path: "C:\\Users\\Christopher\\Downloads\\act-windows-amd64 (1)\\github-act-runner\\act\\pkg\\runner\\testdata",
}
ctx := context.Background()
sha, err := cache.Fetch(ctx, "christopherhx/script", "https://github.com/christopherhx/script", "main", "")
a.NoError(err)
a.NotEmpty(sha)
atar, err := cache.GetTarArchive(ctx, "christopherhx/script", sha, "node_modules")
a.NoError(err)
a.NotEmpty(atar)
mytar := tar.NewReader(atar)
th, err := mytar.Next()
a.NoError(err)
a.NotEqual(0, th.Size)
buf := &bytes.Buffer{}
_, err = io.Copy(buf, mytar)

Check failure on line 30 in pkg/runner/action_cache_test.go

View workflow job for this annotation

GitHub Actions / lint

G110: Potential DoS vulnerability via decompression bomb (gosec)
a.NoError(err)
str := buf.String()
a.NotEmpty(str)
}

0 comments on commit ac7392b

Please sign in to comment.