Skip to content

Commit

Permalink
feat: Test writing remote Actions locally
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherHX committed Jan 30, 2024
1 parent 5a80a04 commit ba69f85
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 1 deletion.
1 change: 1 addition & 0 deletions cmd/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type Input struct {
logPrefixJobID bool
networkName string
useNewActionCache bool
localRepository []string
}

func (i *Input) resolve(path string) string {
Expand Down
15 changes: 14 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func Execute(ctx context.Context, version string) {
rootCmd.PersistentFlags().BoolVarP(&input.actionOfflineMode, "action-offline-mode", "", false, "If action contents exists, it will not be fetch and pull again. If turn on this,will turn off force pull")
rootCmd.PersistentFlags().StringVarP(&input.networkName, "network", "", "host", "Sets a docker network name. Defaults to host.")
rootCmd.PersistentFlags().BoolVarP(&input.useNewActionCache, "use-new-action-cache", "", false, "Enable using the new Action Cache for storing Actions locally")
rootCmd.PersistentFlags().StringArrayVarP(&input.localRepository, "local-repository", "", []string{}, "Replaces the specified repository and ref with a local folder (e.g. https://github.com/test/test@v0=/home/act/test or test/test@v0=/home/act/test, the latter matches any hosts or protocols)")
rootCmd.SetArgs(args())

if err := rootCmd.Execute(); err != nil {
Expand Down Expand Up @@ -618,10 +619,22 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
Matrix: matrixes,
ContainerNetworkMode: docker_container.NetworkMode(input.networkName),
}
if input.useNewActionCache {
if input.useNewActionCache || len(input.localRepository) > 0 {
config.ActionCache = &runner.GoGitActionCache{
Path: config.ActionCacheDir,
}
if len(input.localRepository) > 0 {
localRepositories := map[string]string{}
for _, l := range input.localRepository {
k, v, _ := strings.Cut(l, "=")
localRepositories[k] = v
}
config.ActionCache = &runner.LocalRepositoryCache{
Parent: config.ActionCache,
LocalRepositories: localRepositories,
CacheDirCache: map[string]string{},
}
}
}
r, err := runner.New(config)
if err != nil {
Expand Down
91 changes: 91 additions & 0 deletions pkg/runner/local_repository_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package runner

import (
"archive/tar"
"bytes"
"context"
"fmt"
"io"
"io/fs"
goURL "net/url"
"os"
"path/filepath"
"strings"

"github.com/nektos/act/pkg/filecollector"
)

type LocalRepositoryCache struct {
Parent ActionCache
LocalRepositories map[string]string
CacheDirCache map[string]string
}

func (l *LocalRepositoryCache) Fetch(ctx context.Context, cacheDir, url, ref, token string) (string, error) {
if dest, ok := l.LocalRepositories[fmt.Sprintf("%s@%s", url, ref)]; ok {
l.CacheDirCache[fmt.Sprintf("%s@%s", cacheDir, ref)] = dest
return ref, nil
}
if purl, err := goURL.Parse(url); err == nil {
if dest, ok := l.LocalRepositories[fmt.Sprintf("%s@%s", strings.TrimPrefix(purl.Path, "/"), ref)]; ok {
l.CacheDirCache[fmt.Sprintf("%s@%s", cacheDir, ref)] = dest
return ref, nil
}
}
return l.Parent.Fetch(ctx, cacheDir, url, ref, token)

Check warning on line 35 in pkg/runner/local_repository_cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/local_repository_cache.go#L35

Added line #L35 was not covered by tests
}

func (l *LocalRepositoryCache) GetTarArchive(ctx context.Context, cacheDir, sha, includePrefix string) (io.ReadCloser, error) {
// sha is mapped to ref in fetch if there is a local override
if dest, ok := l.CacheDirCache[fmt.Sprintf("%s@%s", cacheDir, sha)]; ok {
srcPath := filepath.Join(dest, includePrefix)
buf := &bytes.Buffer{}
tw := tar.NewWriter(buf)
defer tw.Close()
srcPath = filepath.Clean(srcPath)
fi, err := os.Lstat(srcPath)
if err != nil {
return nil, err
}

Check warning on line 49 in pkg/runner/local_repository_cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/local_repository_cache.go#L48-L49

Added lines #L48 - L49 were not covered by tests
tc := &filecollector.TarCollector{
TarWriter: tw,
}
if fi.IsDir() {
srcPrefix := srcPath
if !strings.HasSuffix(srcPrefix, string(filepath.Separator)) {
srcPrefix += string(filepath.Separator)
}
fc := &filecollector.FileCollector{
Fs: &filecollector.DefaultFs{},
SrcPath: srcPath,
SrcPrefix: srcPrefix,
Handler: tc,
}
err = filepath.Walk(srcPath, fc.CollectFiles(ctx, []string{}))
if err != nil {
return nil, err
}

Check warning on line 67 in pkg/runner/local_repository_cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/local_repository_cache.go#L66-L67

Added lines #L66 - L67 were not covered by tests
} else {
var f io.ReadCloser
var linkname string
if fi.Mode()&fs.ModeSymlink != 0 {
linkname, err = os.Readlink(srcPath)
if err != nil {
return nil, err
}

Check warning on line 75 in pkg/runner/local_repository_cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/local_repository_cache.go#L72-L75

Added lines #L72 - L75 were not covered by tests
} else {
f, err = os.Open(srcPath)
if err != nil {
return nil, err
}

Check warning on line 80 in pkg/runner/local_repository_cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/local_repository_cache.go#L79-L80

Added lines #L79 - L80 were not covered by tests
defer f.Close()
}
err := tc.WriteFile(fi.Name(), fi, linkname, f)
if err != nil {
return nil, err
}

Check warning on line 86 in pkg/runner/local_repository_cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/local_repository_cache.go#L85-L86

Added lines #L85 - L86 were not covered by tests
}
return io.NopCloser(buf), nil
}
return l.Parent.GetTarArchive(ctx, cacheDir, sha, includePrefix)

Check warning on line 90 in pkg/runner/local_repository_cache.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/local_repository_cache.go#L90

Added line #L90 was not covered by tests
}
26 changes: 26 additions & 0 deletions pkg/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"runtime"
"strings"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/joho/godotenv"
log "github.com/sirupsen/logrus"
assert "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"

"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/model"
Expand Down Expand Up @@ -187,6 +189,7 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
GitHubInstance: "github.com",
ContainerArchitecture: cfg.ContainerArchitecture,
Matrix: cfg.Matrix,
ActionCache: cfg.ActionCache,
}

runner, err := New(runnerConfig)
Expand All @@ -209,6 +212,10 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
fmt.Println("::endgroup::")
}

type TestConfig struct {
LocalRepositories map[string]string `yaml:"local-repositories"`
}

func TestRunEvent(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
Expand Down Expand Up @@ -307,6 +314,9 @@ func TestRunEvent(t *testing.T) {
{workdir, "services", "push", "", platforms, secrets},
{workdir, "services-host-network", "push", "", platforms, secrets},
{workdir, "services-with-container", "push", "", platforms, secrets},

// local remote action overrides
{workdir, "local-remote-action-overrides", "push", "", platforms, secrets},
}

for _, table := range tables {
Expand All @@ -320,6 +330,22 @@ func TestRunEvent(t *testing.T) {
config.EventPath = eventFile
}

testConfigFile := filepath.Join(workdir, table.workflowPath, "config.yml")
if file, err := os.ReadFile(testConfigFile); err == nil {
testConfig := &TestConfig{}
if yaml.Unmarshal(file, testConfig) == nil {
if testConfig.LocalRepositories != nil {
config.ActionCache = &LocalRepositoryCache{
Parent: GoGitActionCache{
path.Clean(path.Join(workdir, "cache")),
},
LocalRepositories: testConfig.LocalRepositories,
CacheDirCache: map[string]string{},
}
}
}
}

table.runTest(ctx, t, config)
})
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/runner/testdata/local-remote-action-overrides/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
local-repositories:
https://github.com/nektos/test-override@a: testdata/actions/node20
nektos/test-override@b: testdata/actions/node16
9 changes: 9 additions & 0 deletions pkg/runner/testdata/local-remote-action-overrides/push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: basic
on: push

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: nektos/test-override@a
- uses: nektos/test-override@b

0 comments on commit ba69f85

Please sign in to comment.