diff --git a/cmd/input.go b/cmd/input.go index 59c14002..0c80ec64 100644 --- a/cmd/input.go +++ b/cmd/input.go @@ -58,7 +58,6 @@ type Input struct { actionOfflineMode bool logPrefixJobID bool networkName string - useNewActionCache bool localRepository []string } diff --git a/cmd/root.go b/cmd/root.go index 848a91f2..9b74b081 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -102,7 +102,6 @@ func Execute(ctx context.Context, version string) { rootCmd.PersistentFlags().StringVarP(&input.actionCachePath, "action-cache-path", "", filepath.Join(CacheHomeDir, "act"), "Defines the path where the actions get cached and host workspaces created.") 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()) @@ -393,6 +392,16 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str vars := newSecrets(input.vars) _ = readEnvs(input.Varfile(), vars) + log.Debugf("Cleaning up %s old action cache format", input.actionCachePath) + entries, _ := os.ReadDir(input.actionCachePath) + for _, entry := range entries { + if strings.Contains(entry.Name(), "@") { + fullPath := filepath.Join(input.actionCachePath, entry.Name()) + log.Debugf("Removing %s", fullPath) + _ = os.RemoveAll(fullPath) + } + } + matrixes := parseMatrix(input.matrix) log.Debugf("Evaluated matrix inclusions: %v", matrixes) @@ -588,31 +597,26 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str Matrix: matrixes, ContainerNetworkMode: docker_container.NetworkMode(input.networkName), } - if input.useNewActionCache || len(input.localRepository) > 0 { - if input.actionOfflineMode { - config.ActionCache = &runner.GoGitActionCacheOfflineMode{ - Parent: runner.GoGitActionCache{ - Path: config.ActionCacheDir, - }, - } - } else { - config.ActionCache = &runner.GoGitActionCache{ + if input.actionOfflineMode { + config.ActionCache = &runner.GoGitActionCacheOfflineMode{ + Parent: 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{}, - } + }, } } + 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 { return err diff --git a/pkg/container/docker_run.go b/pkg/container/docker_run.go index d2dc2fde..0ce1cb9b 100644 --- a/pkg/container/docker_run.go +++ b/pkg/container/docker_run.go @@ -699,6 +699,9 @@ func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal boo } func (cr *containerReference) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error { + if common.Dryrun(ctx) { + return nil + } // Mkdir buf := &bytes.Buffer{} tw := tar.NewWriter(buf) diff --git a/pkg/container/host_environment.go b/pkg/container/host_environment.go index b2dc67c8..cccb1cc1 100644 --- a/pkg/container/host_environment.go +++ b/pkg/container/host_environment.go @@ -62,6 +62,9 @@ func (e *HostEnvironment) Copy(destPath string, files ...*FileEntry) common.Exec } func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error { + if common.Dryrun(ctx) { + return nil + } if err := os.RemoveAll(destPath); err != nil { return err } diff --git a/pkg/runner/action.go b/pkg/runner/action.go index 1b916afc..9a97519f 100644 --- a/pkg/runner/action.go +++ b/pkg/runner/action.go @@ -116,7 +116,7 @@ func readActionImpl(ctx context.Context, step *model.Step, actionDir string, act return action, err } -func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir string, actionPath string, containerActionDir string) error { +func maybeCopyToActionDir(ctx context.Context, step actionStep, actionPath string, containerActionDir string) error { logger := common.Logger(ctx) rc := step.getRunContext() stepModel := step.getStepModel() @@ -133,21 +133,13 @@ func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir string containerActionDirCopy += `/` } - if rc.Config != nil && rc.Config.ActionCache != nil { - raction := step.(*stepActionRemote) - ta, err := rc.Config.ActionCache.GetTarArchive(ctx, raction.cacheDir, raction.resolvedSha, "") - if err != nil { - return err - } - defer ta.Close() - return rc.JobContainer.CopyTarStream(ctx, containerActionDirCopy, ta) - } - - if err := removeGitIgnore(ctx, actionDir); err != nil { + raction := step.(*stepActionRemote) + ta, err := rc.getActionCache().GetTarArchive(ctx, raction.cacheDir, raction.resolvedSha, "") + if err != nil { return err } - - return rc.JobContainer.CopyDir(containerActionDirCopy, actionDir+"/", rc.Config.UseGitIgnore)(ctx) + defer ta.Close() + return rc.JobContainer.CopyTarStream(ctx, containerActionDirCopy, ta) } func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction) common.Executor { @@ -176,7 +168,7 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction switch action.Runs.Using { case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16, model.ActionRunsUsingNode20: - if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { + if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil { return err } containerArgs := []string{rc.GetNodeToolFullPath(ctx), path.Join(containerActionDir, action.Runs.Main)} @@ -186,13 +178,13 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) case model.ActionRunsUsingDocker: - location := actionLocation if remoteAction == nil { - location = containerActionDir + actionDir = "" + actionPath = containerActionDir } - return execAsDocker(ctx, step, actionName, location, remoteAction == nil, "entrypoint") + return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "entrypoint") case model.ActionRunsUsingComposite: - if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { + if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil { return err } @@ -223,27 +215,10 @@ func setupActionEnv(ctx context.Context, step actionStep, _ *remoteAction) error return nil } -// https://github.com/nektos/act/issues/228#issuecomment-629709055 -// files in .gitignore are not copied in a Docker container -// this causes issues with actions that ignore other important resources -// such as `node_modules` for example -func removeGitIgnore(ctx context.Context, directory string) error { - gitIgnorePath := path.Join(directory, ".gitignore") - if _, err := os.Stat(gitIgnorePath); err == nil { - // .gitignore exists - common.Logger(ctx).Debugf("Removing %s before docker cp", gitIgnorePath) - err := os.Remove(gitIgnorePath) - if err != nil { - return err - } - } - return nil -} - // TODO: break out parts of function to reduce complexicity // //nolint:gocyclo -func execAsDocker(ctx context.Context, step actionStep, actionName string, basedir string, localAction bool, entrypointType string) error { +func execAsDocker(ctx context.Context, step actionStep, actionName, basedir, subpath string, localAction bool, entrypointType string) error { logger := common.Logger(ctx) rc := step.getRunContext() action := step.getActionModel() @@ -260,7 +235,7 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based image = fmt.Sprintf("%s-dockeraction:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-"), "latest") image = fmt.Sprintf("act-%s", strings.TrimLeft(image, "-")) image = strings.ToLower(image) - contextDir, fileName := filepath.Split(filepath.Join(basedir, action.Runs.Image)) + contextDir, fileName := path.Split(path.Join(subpath, action.Runs.Image)) anyArchExists, err := container.ImageExistsLocally(ctx, image, "any") if err != nil { @@ -291,16 +266,16 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based return err } defer buildContext.Close() - } else if rc.Config.ActionCache != nil { + } else { rstep := step.(*stepActionRemote) - buildContext, err = rc.Config.ActionCache.GetTarArchive(ctx, rstep.cacheDir, rstep.resolvedSha, contextDir) + buildContext, err = rc.getActionCache().GetTarArchive(ctx, rstep.cacheDir, rstep.resolvedSha, contextDir) if err != nil { return err } defer buildContext.Close() } prepImage = container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{ - ContextDir: contextDir, + ContextDir: filepath.Join(basedir, contextDir), Dockerfile: fileName, ImageTag: image, BuildContext: buildContext, @@ -324,6 +299,7 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based if len(entrypoint) == 0 { if entrypointType == "pre-entrypoint" && action.Runs.PreEntrypoint != "" { entrypoint, err = shellquote.Split(action.Runs.PreEntrypoint) + if err != nil { return err } @@ -545,7 +521,7 @@ func runPreStep(step actionStep) common.Executor { switch action.Runs.Using { case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16, model.ActionRunsUsingNode20: - if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { + if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil { return err } @@ -557,11 +533,11 @@ func runPreStep(step actionStep) common.Executor { return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) case model.ActionRunsUsingDocker: - location := actionLocation if remoteAction == nil { - location = containerActionDir + actionDir = "" + actionPath = containerActionDir } - return execAsDocker(ctx, step, actionName, location, remoteAction == nil, "pre-entrypoint") + return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "pre-entrypoint") case model.ActionRunsUsingComposite: if step.getCompositeSteps() == nil { @@ -662,14 +638,14 @@ func runPostStep(step actionStep) common.Executor { return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx) case model.ActionRunsUsingDocker: - location := actionLocation if remoteAction == nil { - location = containerActionDir + actionDir = "" + actionPath = containerActionDir } - return execAsDocker(ctx, step, actionName, location, remoteAction == nil, "post-entrypoint") + return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "post-entrypoint") case model.ActionRunsUsingComposite: - if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil { + if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil { return err } diff --git a/pkg/runner/action_test.go b/pkg/runner/action_test.go index bc55bd77..3286facf 100644 --- a/pkg/runner/action_test.go +++ b/pkg/runner/action_test.go @@ -227,7 +227,10 @@ func TestActionRunner(t *testing.T) { ctx := context.Background() cm := &containerMock{} - cm.On("CopyDir", "/var/run/act/actions/dir/", "dir/", false).Return(func(_ context.Context) error { return nil }) + cm.Mock.On("CopyTarStream", ctx, "/var/run/act/actions/dir/", mock.Anything).Return(nil) + + cacheMock := &TestRepositoryCache{} + cacheMock.Mock.On("GetTarArchive", ctx, "", "", "").Return(io.NopCloser(io.MultiReader())) envMatcher := mock.MatchedBy(func(env map[string]string) bool { for k, v := range tt.expectedEnv { @@ -241,6 +244,7 @@ func TestActionRunner(t *testing.T) { cm.On("Exec", []string{"node", "/var/run/act/actions/dir/path"}, envMatcher, "", "").Return(func(_ context.Context) error { return nil }) tt.step.getRunContext().JobContainer = cm + tt.step.getRunContext().Config.ActionCache = cacheMock err := runActionImpl(tt.step, "dir", newRemoteAction("org/repo/path@ref"))(ctx) diff --git a/pkg/runner/container_mock_test.go b/pkg/runner/container_mock_test.go index 04d6261b..86343d5a 100644 --- a/pkg/runner/container_mock_test.go +++ b/pkg/runner/container_mock_test.go @@ -73,3 +73,12 @@ func (cm *containerMock) GetContainerArchive(ctx context.Context, srcPath string } return args.Get(0).(io.ReadCloser), err } + +func (cm *containerMock) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error { + args := cm.Mock.Called(ctx, destPath, tarStream) + err, hasErr := args.Get(0).(error) + if !hasErr { + err = nil + } + return err +} diff --git a/pkg/runner/reusable_workflow.go b/pkg/runner/reusable_workflow.go index 09dfe5a3..d8b183c4 100644 --- a/pkg/runner/reusable_workflow.go +++ b/pkg/runner/reusable_workflow.go @@ -3,16 +3,11 @@ package runner import ( "archive/tar" "context" - "errors" "fmt" - "io/fs" - "os" "path" "regexp" - "sync" "github.com/nektos/act/pkg/common" - "github.com/nektos/act/pkg/common/git" "github.com/nektos/act/pkg/model" ) @@ -32,27 +27,20 @@ func newRemoteReusableWorkflowExecutor(rc *RunContext) common.Executor { // instead we will just use {owner}-{repo}@{ref} as our target directory. This should also improve performance when we are using // multiple reusable workflows from the same repository and ref since for each workflow we won't have to clone it again filename := fmt.Sprintf("%s/%s@%s", remoteReusableWorkflow.Org, remoteReusableWorkflow.Repo, remoteReusableWorkflow.Ref) - workflowDir := fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(filename)) - if rc.Config.ActionCache != nil { - return newActionCacheReusableWorkflowExecutor(rc, filename, remoteReusableWorkflow) - } - - return common.NewPipelineExecutor( - newMutexExecutor(cloneIfRequired(rc, *remoteReusableWorkflow, workflowDir)), - newReusableWorkflowExecutor(rc, workflowDir, fmt.Sprintf("./.github/workflows/%s", remoteReusableWorkflow.Filename)), - ) + return newActionCacheReusableWorkflowExecutor(rc, filename, remoteReusableWorkflow) } func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, remoteReusableWorkflow *remoteReusableWorkflow) common.Executor { return func(ctx context.Context) error { ghctx := rc.getGithubContext(ctx) remoteReusableWorkflow.URL = ghctx.ServerURL - sha, err := rc.Config.ActionCache.Fetch(ctx, filename, remoteReusableWorkflow.CloneURL(), remoteReusableWorkflow.Ref, ghctx.Token) + cache := rc.getActionCache() + sha, err := cache.Fetch(ctx, filename, remoteReusableWorkflow.CloneURL(), remoteReusableWorkflow.Ref, ghctx.Token) if err != nil { return err } - archive, err := rc.Config.ActionCache.GetTarArchive(ctx, filename, sha, fmt.Sprintf(".github/workflows/%s", remoteReusableWorkflow.Filename)) + archive, err := cache.GetTarArchive(ctx, filename, sha, fmt.Sprintf(".github/workflows/%s", remoteReusableWorkflow.Filename)) if err != nil { return err } @@ -79,40 +67,6 @@ func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, rem } } -var ( - executorLock sync.Mutex -) - -func newMutexExecutor(executor common.Executor) common.Executor { - return func(ctx context.Context) error { - executorLock.Lock() - defer executorLock.Unlock() - - return executor(ctx) - } -} - -func cloneIfRequired(rc *RunContext, remoteReusableWorkflow remoteReusableWorkflow, targetDirectory string) common.Executor { - return common.NewConditionalExecutor( - func(_ context.Context) bool { - _, err := os.Stat(targetDirectory) - notExists := errors.Is(err, fs.ErrNotExist) - return notExists - }, - func(ctx context.Context) error { - remoteReusableWorkflow.URL = rc.getGithubContext(ctx).ServerURL - return git.NewGitCloneExecutor(git.NewGitCloneExecutorInput{ - URL: remoteReusableWorkflow.CloneURL(), - Ref: remoteReusableWorkflow.Ref, - Dir: targetDirectory, - Token: rc.Config.Token, - OfflineMode: rc.Config.ActionOfflineMode, - })(ctx) - }, - nil, - ) -} - func newReusableWorkflowExecutor(rc *RunContext, directory string, workflow string) common.Executor { return func(ctx context.Context) error { planner, err := model.NewWorkflowPlanner(path.Join(directory, workflow), true) diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 2efad2c5..a124da0d 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -627,6 +627,15 @@ func (rc *RunContext) ActionCacheDir() string { return filepath.Join(xdgCache, "act") } +func (rc *RunContext) getActionCache() ActionCache { + if rc.Config.ActionCache == nil { + rc.Config.ActionCache = &GoGitActionCache{ + Path: rc.ActionCacheDir(), + } + } + return rc.Config.ActionCache +} + // Interpolate outputs after a job is done func (rc *RunContext) interpolateOutputs() common.Executor { return func(ctx context.Context) error { diff --git a/pkg/runner/step_action_remote.go b/pkg/runner/step_action_remote.go index 4c1ef353..8b0ed0ac 100644 --- a/pkg/runner/step_action_remote.go +++ b/pkg/runner/step_action_remote.go @@ -3,7 +3,6 @@ package runner import ( "archive/tar" "context" - "errors" "fmt" "io" "os" @@ -12,10 +11,7 @@ import ( "regexp" "strings" - gogit "github.com/go-git/go-git/v5" - "github.com/nektos/act/pkg/common" - "github.com/nektos/act/pkg/common/git" "github.com/nektos/act/pkg/model" ) @@ -33,10 +29,6 @@ type stepActionRemote struct { resolvedSha string } -var ( - stepActionRemoteNewCloneExecutor = git.NewGitCloneExecutor -) - func (sar *stepActionRemote) prepareActionExecutor() common.Executor { return func(ctx context.Context) error { if sar.remoteAction != nil && sar.action != nil { @@ -63,84 +55,46 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor { github.Token = sar.RunContext.Config.ReplaceGheActionTokenWithGithubCom } } - if sar.RunContext.Config.ActionCache != nil { - cache := sar.RunContext.Config.ActionCache + cache := sar.RunContext.getActionCache() - var err error - sar.cacheDir = fmt.Sprintf("%s/%s", sar.remoteAction.Org, sar.remoteAction.Repo) - repoURL := sar.remoteAction.URL + "/" + sar.cacheDir - repoRef := sar.remoteAction.Ref - sar.resolvedSha, err = cache.Fetch(ctx, sar.cacheDir, repoURL, repoRef, github.Token) - if err != nil { - return fmt.Errorf("failed to fetch \"%s\" version \"%s\": %w", repoURL, repoRef, err) - } - - remoteReader := func(ctx context.Context) actionYamlReader { - return func(filename string) (io.Reader, io.Closer, error) { - spath := path.Join(sar.remoteAction.Path, filename) - for i := 0; i < maxSymlinkDepth; i++ { - tars, err := cache.GetTarArchive(ctx, sar.cacheDir, sar.resolvedSha, spath) - if err != nil { - return nil, nil, os.ErrNotExist - } - treader := tar.NewReader(tars) - header, err := treader.Next() - if err != nil { - return nil, nil, os.ErrNotExist - } - if header.FileInfo().Mode()&os.ModeSymlink == os.ModeSymlink { - spath, err = symlinkJoin(spath, header.Linkname, ".") - if err != nil { - return nil, nil, err - } - } else { - return treader, tars, nil - } - } - return nil, nil, fmt.Errorf("max depth %d of symlinks exceeded while reading %s", maxSymlinkDepth, spath) - } - } - - actionModel, err := sar.readAction(ctx, sar.Step, sar.resolvedSha, sar.remoteAction.Path, remoteReader(ctx), os.WriteFile) - sar.action = actionModel - return err - } - - actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), safeFilename(sar.Step.Uses)) - gitClone := stepActionRemoteNewCloneExecutor(git.NewGitCloneExecutorInput{ - URL: sar.remoteAction.CloneURL(), - Ref: sar.remoteAction.Ref, - Dir: actionDir, - Token: github.Token, - OfflineMode: sar.RunContext.Config.ActionOfflineMode, - }) - var ntErr common.Executor - if err := gitClone(ctx); err != nil { - if errors.Is(err, git.ErrShortRef) { - return fmt.Errorf("Unable to resolve action `%s`, the provided ref `%s` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `%s` instead", - sar.Step.Uses, sar.remoteAction.Ref, err.(*git.Error).Commit()) - } else if errors.Is(err, gogit.ErrForceNeeded) { // TODO: figure out if it will be easy to shadow/alias go-git err's - ntErr = common.NewInfoExecutor("Non-terminating error while running 'git clone': %v", err) - } else { - return err - } + var err error + sar.cacheDir = fmt.Sprintf("%s/%s", sar.remoteAction.Org, sar.remoteAction.Repo) + repoURL := sar.remoteAction.URL + "/" + sar.cacheDir + repoRef := sar.remoteAction.Ref + sar.resolvedSha, err = cache.Fetch(ctx, sar.cacheDir, repoURL, repoRef, github.Token) + if err != nil { + return fmt.Errorf("failed to fetch \"%s\" version \"%s\": %w", repoURL, repoRef, err) } remoteReader := func(_ context.Context) actionYamlReader { return func(filename string) (io.Reader, io.Closer, error) { - f, err := os.Open(filepath.Join(actionDir, sar.remoteAction.Path, filename)) - return f, f, err + spath := path.Join(sar.remoteAction.Path, filename) + for i := 0; i < maxSymlinkDepth; i++ { + tars, err := cache.GetTarArchive(ctx, sar.cacheDir, sar.resolvedSha, spath) + if err != nil { + return nil, nil, os.ErrNotExist + } + treader := tar.NewReader(tars) + header, err := treader.Next() + if err != nil { + return nil, nil, os.ErrNotExist + } + if header.FileInfo().Mode()&os.ModeSymlink == os.ModeSymlink { + spath, err = symlinkJoin(spath, header.Linkname, ".") + if err != nil { + return nil, nil, err + } + } else { + return treader, tars, nil + } + } + return nil, nil, fmt.Errorf("max depth %d of symlinks exceeded while reading %s", maxSymlinkDepth, spath) } } - return common.NewPipelineExecutor( - ntErr, - func(ctx context.Context) error { - actionModel, err := sar.readAction(ctx, sar.Step, actionDir, sar.remoteAction.Path, remoteReader(ctx), os.WriteFile) - sar.action = actionModel - return err - }, - )(ctx) + actionModel, err := sar.readAction(ctx, sar.Step, sar.resolvedSha, sar.remoteAction.Path, remoteReader(ctx), os.WriteFile) + sar.action = actionModel + return err } } diff --git a/pkg/runner/step_action_remote_test.go b/pkg/runner/step_action_remote_test.go index a21d244b..848f254b 100644 --- a/pkg/runner/step_action_remote_test.go +++ b/pkg/runner/step_action_remote_test.go @@ -13,7 +13,6 @@ import ( "gopkg.in/yaml.v3" "github.com/nektos/act/pkg/common" - "github.com/nektos/act/pkg/common/git" "github.com/nektos/act/pkg/model" ) @@ -31,6 +30,20 @@ func (sarm *stepActionRemoteMocks) runAction(step actionStep, actionDir string, return args.Get(0).(func(context.Context) error) } +type TestRepositoryCache struct { + mock.Mock +} + +func (l *TestRepositoryCache) Fetch(ctx context.Context, cacheDir, url, ref, token string) (string, error) { + args := l.Mock.Called(ctx, cacheDir, url, ref, token) + return args.Get(0).(string), nil +} + +func (l *TestRepositoryCache) GetTarArchive(ctx context.Context, cacheDir, sha, includePrefix string) (io.ReadCloser, error) { + args := l.Mock.Called(ctx, cacheDir, sha, includePrefix) + return args.Get(0).(io.ReadCloser), nil +} + func TestStepActionRemote(t *testing.T) { table := []struct { name string @@ -124,23 +137,13 @@ func TestStepActionRemote(t *testing.T) { cm := &containerMock{} sarm := &stepActionRemoteMocks{} - clonedAction := false - - origStepAtionRemoteNewCloneExecutor := stepActionRemoteNewCloneExecutor - stepActionRemoteNewCloneExecutor = func(_ git.NewGitCloneExecutorInput) common.Executor { - return func(_ context.Context) error { - clonedAction = true - return nil - } - } - defer (func() { - stepActionRemoteNewCloneExecutor = origStepAtionRemoteNewCloneExecutor - })() + cacheMock := &TestRepositoryCache{} sar := &stepActionRemote{ RunContext: &RunContext{ Config: &Config{ GitHubInstance: "github.com", + ActionCache: cacheMock, }, Run: &model.Run{ JobID: "1", @@ -159,6 +162,7 @@ func TestStepActionRemote(t *testing.T) { } sar.RunContext.ExprEval = sar.RunContext.NewExpressionEvaluator(ctx) + cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/remote/action", "v1", "").Return("someval") suffixMatcher := func(suffix string) interface{} { return mock.MatchedBy(func(actionDir string) bool { return strings.HasSuffix(actionDir, suffix) @@ -166,7 +170,7 @@ func TestStepActionRemote(t *testing.T) { } if tt.mocks.read { - sarm.On("readAction", sar.Step, suffixMatcher("act/remote-action@v1"), "", mock.Anything, mock.Anything).Return(&model.Action{}, nil) + sarm.Mock.On("readAction", sar.Step, "someval", "", mock.Anything, mock.Anything).Return(&model.Action{}, nil) } if tt.mocks.run { sarm.On("runAction", sar, suffixMatcher("act/remote-action@v1"), newRemoteAction(sar.Step.Uses)).Return(func(_ context.Context) error { return tt.runError }) @@ -196,11 +200,11 @@ func TestStepActionRemote(t *testing.T) { } assert.Equal(t, tt.runError, err) - assert.Equal(t, tt.mocks.cloned, clonedAction) assert.Equal(t, tt.result, sar.RunContext.StepResults["step"]) sarm.AssertExpectations(t) cm.AssertExpectations(t) + cacheMock.Mock.AssertExpectations(t) }) } } @@ -222,25 +226,15 @@ func TestStepActionRemotePre(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - clonedAction := false + cacheMock := &TestRepositoryCache{} sarm := &stepActionRemoteMocks{} - origStepAtionRemoteNewCloneExecutor := stepActionRemoteNewCloneExecutor - stepActionRemoteNewCloneExecutor = func(_ git.NewGitCloneExecutorInput) common.Executor { - return func(_ context.Context) error { - clonedAction = true - return nil - } - } - defer (func() { - stepActionRemoteNewCloneExecutor = origStepAtionRemoteNewCloneExecutor - })() - sar := &stepActionRemote{ Step: tt.stepModel, RunContext: &RunContext{ Config: &Config{ - GitHubInstance: "https://github.com", + GitHubInstance: "github.com", + ActionCache: cacheMock, }, Run: &model.Run{ JobID: "1", @@ -254,20 +248,15 @@ func TestStepActionRemotePre(t *testing.T) { readAction: sarm.readAction, } - suffixMatcher := func(suffix string) interface{} { - return mock.MatchedBy(func(actionDir string) bool { - return strings.HasSuffix(actionDir, suffix) - }) - } - - sarm.On("readAction", sar.Step, suffixMatcher("org-repo-path@ref"), "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil) + sarm.Mock.On("readAction", sar.Step, "someval", "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil) + cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/org/repo", "ref", "").Return("someval") err := sar.pre()(ctx) assert.Nil(t, err) - assert.Equal(t, true, clonedAction) sarm.AssertExpectations(t) + cacheMock.Mock.AssertExpectations(t) }) } } @@ -289,28 +278,16 @@ func TestStepActionRemotePreThroughAction(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - clonedAction := false + cacheMock := &TestRepositoryCache{} sarm := &stepActionRemoteMocks{} - origStepAtionRemoteNewCloneExecutor := stepActionRemoteNewCloneExecutor - stepActionRemoteNewCloneExecutor = func(input git.NewGitCloneExecutorInput) common.Executor { - return func(_ context.Context) error { - if input.URL == "https://github.com/org/repo" { - clonedAction = true - } - return nil - } - } - defer (func() { - stepActionRemoteNewCloneExecutor = origStepAtionRemoteNewCloneExecutor - })() - sar := &stepActionRemote{ Step: tt.stepModel, RunContext: &RunContext{ Config: &Config{ GitHubInstance: "https://enterprise.github.com", ReplaceGheActionWithGithubCom: []string{"org/repo"}, + ActionCache: cacheMock, }, Run: &model.Run{ JobID: "1", @@ -324,20 +301,15 @@ func TestStepActionRemotePreThroughAction(t *testing.T) { readAction: sarm.readAction, } - suffixMatcher := func(suffix string) interface{} { - return mock.MatchedBy(func(actionDir string) bool { - return strings.HasSuffix(actionDir, suffix) - }) - } - - sarm.On("readAction", sar.Step, suffixMatcher("org-repo-path@ref"), "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil) + sarm.Mock.On("readAction", sar.Step, mock.AnythingOfType("string"), "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil) + cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/org/repo", "ref", "").Return("someval") err := sar.pre()(ctx) assert.Nil(t, err) - assert.Equal(t, true, clonedAction) sarm.AssertExpectations(t) + cacheMock.Mock.AssertExpectations(t) }) } } @@ -359,22 +331,9 @@ func TestStepActionRemotePreThroughActionToken(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() - clonedAction := false sarm := &stepActionRemoteMocks{} - origStepAtionRemoteNewCloneExecutor := stepActionRemoteNewCloneExecutor - stepActionRemoteNewCloneExecutor = func(input git.NewGitCloneExecutorInput) common.Executor { - return func(_ context.Context) error { - if input.URL == "https://github.com/org/repo" && input.Token == "PRIVATE_ACTIONS_TOKEN_ON_GITHUB" { - clonedAction = true - } - return nil - } - } - defer (func() { - stepActionRemoteNewCloneExecutor = origStepAtionRemoteNewCloneExecutor - })() - + cacheMock := &TestRepositoryCache{} sar := &stepActionRemote{ Step: tt.stepModel, RunContext: &RunContext{ @@ -382,6 +341,7 @@ func TestStepActionRemotePreThroughActionToken(t *testing.T) { GitHubInstance: "https://enterprise.github.com", ReplaceGheActionWithGithubCom: []string{"org/repo"}, ReplaceGheActionTokenWithGithubCom: "PRIVATE_ACTIONS_TOKEN_ON_GITHUB", + ActionCache: cacheMock, }, Run: &model.Run{ JobID: "1", @@ -395,20 +355,15 @@ func TestStepActionRemotePreThroughActionToken(t *testing.T) { readAction: sarm.readAction, } - suffixMatcher := func(suffix string) interface{} { - return mock.MatchedBy(func(actionDir string) bool { - return strings.HasSuffix(actionDir, suffix) - }) - } - - sarm.On("readAction", sar.Step, suffixMatcher("org-repo-path@ref"), "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil) + sarm.Mock.On("readAction", sar.Step, mock.AnythingOfType("string"), "path", mock.Anything, mock.Anything).Return(&model.Action{}, nil) + cacheMock.Mock.On("Fetch", ctx, mock.AnythingOfType("string"), "https://github.com/org/repo", "ref", "PRIVATE_ACTIONS_TOKEN_ON_GITHUB").Return("someval") err := sar.pre()(ctx) assert.Nil(t, err) - assert.Equal(t, true, clonedAction) sarm.AssertExpectations(t) + cacheMock.Mock.AssertExpectations(t) }) } }