feat: Test writing remote Actions locally (#2155)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
9e6ce121bc
commit
ac6a465e27
|
@ -59,6 +59,7 @@ type Input struct {
|
||||||
logPrefixJobID bool
|
logPrefixJobID bool
|
||||||
networkName string
|
networkName string
|
||||||
useNewActionCache bool
|
useNewActionCache bool
|
||||||
|
localRepository []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) resolve(path string) string {
|
func (i *Input) resolve(path string) string {
|
||||||
|
|
15
cmd/root.go
15
cmd/root.go
|
@ -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().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().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().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())
|
rootCmd.SetArgs(args())
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
@ -561,7 +562,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||||
Matrix: matrixes,
|
Matrix: matrixes,
|
||||||
ContainerNetworkMode: docker_container.NetworkMode(input.networkName),
|
ContainerNetworkMode: docker_container.NetworkMode(input.networkName),
|
||||||
}
|
}
|
||||||
if input.useNewActionCache {
|
if input.useNewActionCache || len(input.localRepository) > 0 {
|
||||||
if input.actionOfflineMode {
|
if input.actionOfflineMode {
|
||||||
config.ActionCache = &runner.GoGitActionCacheOfflineMode{
|
config.ActionCache = &runner.GoGitActionCacheOfflineMode{
|
||||||
Parent: runner.GoGitActionCache{
|
Parent: runner.GoGitActionCache{
|
||||||
|
@ -573,6 +574,18 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
|
||||||
Path: config.ActionCacheDir,
|
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)
|
r, err := runner.New(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
} 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
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f, err = os.Open(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
err := tc.WriteFile(fi.Name(), fi, linkname, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return io.NopCloser(buf), nil
|
||||||
|
}
|
||||||
|
return l.Parent.GetTarArchive(ctx, cacheDir, sha, includePrefix)
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
assert "github.com/stretchr/testify/assert"
|
assert "github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/common"
|
"github.com/nektos/act/pkg/common"
|
||||||
"github.com/nektos/act/pkg/model"
|
"github.com/nektos/act/pkg/model"
|
||||||
|
@ -187,6 +189,7 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
|
||||||
GitHubInstance: "github.com",
|
GitHubInstance: "github.com",
|
||||||
ContainerArchitecture: cfg.ContainerArchitecture,
|
ContainerArchitecture: cfg.ContainerArchitecture,
|
||||||
Matrix: cfg.Matrix,
|
Matrix: cfg.Matrix,
|
||||||
|
ActionCache: cfg.ActionCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
runner, err := New(runnerConfig)
|
runner, err := New(runnerConfig)
|
||||||
|
@ -209,6 +212,10 @@ func (j *TestJobFileInfo) runTest(ctx context.Context, t *testing.T, cfg *Config
|
||||||
fmt.Println("::endgroup::")
|
fmt.Println("::endgroup::")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TestConfig struct {
|
||||||
|
LocalRepositories map[string]string `yaml:"local-repositories"`
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunEvent(t *testing.T) {
|
func TestRunEvent(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
|
@ -307,6 +314,9 @@ func TestRunEvent(t *testing.T) {
|
||||||
{workdir, "services", "push", "", platforms, secrets},
|
{workdir, "services", "push", "", platforms, secrets},
|
||||||
{workdir, "services-host-network", "push", "", platforms, secrets},
|
{workdir, "services-host-network", "push", "", platforms, secrets},
|
||||||
{workdir, "services-with-container", "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 {
|
for _, table := range tables {
|
||||||
|
@ -320,6 +330,22 @@ func TestRunEvent(t *testing.T) {
|
||||||
config.EventPath = eventFile
|
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)
|
table.runTest(ctx, t, config)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
local-repositories:
|
||||||
|
https://github.com/nektos/test-override@a: testdata/actions/node20
|
||||||
|
nektos/test-override@b: testdata/actions/node16
|
|
@ -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
|
Loading…
Reference in New Issue