diff --git a/pkg/runner/action.go b/pkg/runner/action.go
index dedb497eb6f..355668069c0 100644
--- a/pkg/runner/action.go
+++ b/pkg/runner/action.go
@@ -100,6 +100,27 @@ func readActionImpl(step *model.Step, actionDir string, actionPath string, readF
 	return action, err
 }
 
+func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir string, actionPath string, containerActionDir string) error {
+	rc := step.getRunContext()
+	stepModel := step.getStepModel()
+
+	if stepModel.Type() != model.StepTypeUsesActionRemote {
+		return nil
+	}
+	if err := removeGitIgnore(actionDir); err != nil {
+		return err
+	}
+
+	var containerActionDirCopy string
+	containerActionDirCopy = strings.TrimSuffix(containerActionDir, actionPath)
+	log.Debug(containerActionDirCopy)
+
+	if !strings.HasSuffix(containerActionDirCopy, `/`) {
+		containerActionDirCopy += `/`
+	}
+	return rc.JobContainer.CopyDir(containerActionDirCopy, actionDir+"/", rc.Config.UseGitIgnore)(ctx)
+}
+
 func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction) common.Executor {
 	rc := step.getRunContext()
 	stepModel := step.getStepModel()
@@ -139,27 +160,9 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
 
 		log.Debugf("type=%v actionDir=%s actionPath=%s workdir=%s actionCacheDir=%s actionName=%s containerActionDir=%s", stepModel.Type(), actionDir, actionPath, rc.Config.Workdir, rc.ActionCacheDir(), actionName, containerActionDir)
 
-		maybeCopyToActionDir := func() error {
-			if stepModel.Type() != model.StepTypeUsesActionRemote {
-				return nil
-			}
-			if err := removeGitIgnore(actionDir); err != nil {
-				return err
-			}
-
-			var containerActionDirCopy string
-			containerActionDirCopy = strings.TrimSuffix(containerActionDir, actionPath)
-			log.Debug(containerActionDirCopy)
-
-			if !strings.HasSuffix(containerActionDirCopy, `/`) {
-				containerActionDirCopy += `/`
-			}
-			return rc.JobContainer.CopyDir(containerActionDirCopy, actionDir+"/", rc.Config.UseGitIgnore)(ctx)
-		}
-
 		switch action.Runs.Using {
 		case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16:
-			if err := maybeCopyToActionDir(); err != nil {
+			if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
 				return err
 			}
 			containerArgs := []string{"node", path.Join(containerActionDir, action.Runs.Main)}
@@ -172,7 +175,7 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
 			}
 			return execAsDocker(ctx, step, actionName, location, remoteAction == nil)
 		case model.ActionRunsUsingComposite:
-			if err := maybeCopyToActionDir(); err != nil {
+			if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
 				return err
 			}
 
@@ -372,6 +375,29 @@ func newStepContainer(ctx context.Context, step step, image string, cmd []string
 	return stepContainer
 }
 
+func (rc *RunContext) setupActionInputs(step actionStep) {
+	if step.getActionModel() == nil {
+		// e.g. local checkout skip has no action model
+		return
+	}
+
+	stepModel := step.getStepModel()
+	action := step.getActionModel()
+
+	eval := rc.NewExpressionEvaluator()
+	inputs := make(map[string]interface{})
+	for k, input := range action.Inputs {
+		inputs[k] = eval.Interpolate(input.Default)
+	}
+	if stepModel.With != nil {
+		for k, v := range stepModel.With {
+			inputs[k] = eval.Interpolate(v)
+		}
+	}
+
+	rc.Inputs = inputs
+}
+
 func populateEnvsFromSavedState(env *map[string]string, step actionStep, rc *RunContext) {
 	stepResult := rc.StepResults[step.getStepModel().ID]
 	if stepResult != nil {
@@ -424,6 +450,78 @@ func getOsSafeRelativePath(s, prefix string) string {
 	return actionName
 }
 
+func shouldRunPreStep(step actionStep) common.Conditional {
+	return func(ctx context.Context) bool {
+		log := common.Logger(ctx)
+
+		if step.getActionModel() == nil {
+			log.Debugf("skip pre step for '%s': no action model available", step.getStepModel())
+			return false
+		}
+
+		return true
+	}
+}
+
+func hasPreStep(step actionStep) common.Conditional {
+	return func(ctx context.Context) bool {
+		action := step.getActionModel()
+		return action.Runs.Using == model.ActionRunsUsingComposite ||
+			((action.Runs.Using == model.ActionRunsUsingNode12 ||
+				action.Runs.Using == model.ActionRunsUsingNode16) &&
+				action.Runs.Pre != "")
+	}
+}
+
+func runPreStep(step actionStep) common.Executor {
+	return func(ctx context.Context) error {
+		common.Logger(ctx).Debugf("run pre step for '%s'", step.getStepModel())
+
+		rc := step.getRunContext()
+		stepModel := step.getStepModel()
+		action := step.getActionModel()
+
+		switch action.Runs.Using {
+		case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16:
+			// todo: refactor into step
+			var actionDir string
+			var actionPath string
+			if _, ok := step.(*stepActionRemote); ok {
+				actionPath = newRemoteAction(stepModel.Uses).Path
+				actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), strings.ReplaceAll(stepModel.Uses, "/", "-"))
+			} else {
+				actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses)
+				actionPath = ""
+			}
+
+			actionLocation := ""
+			if actionPath != "" {
+				actionLocation = path.Join(actionDir, actionPath)
+			} else {
+				actionLocation = actionDir
+			}
+
+			_, containerActionDir := getContainerActionPaths(stepModel, actionLocation, rc)
+
+			if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
+				return err
+			}
+
+			containerArgs := []string{"node", path.Join(containerActionDir, action.Runs.Pre)}
+			log.Debugf("executing remote job container: %s", containerArgs)
+
+			return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx)
+
+		case model.ActionRunsUsingComposite:
+			step.getCompositeRunContext().updateCompositeRunContext(step.getRunContext(), step)
+			return step.getCompositeSteps().pre(ctx)
+
+		default:
+			return nil
+		}
+	}
+}
+
 func shouldRunPostStep(step actionStep) common.Conditional {
 	return func(ctx context.Context) bool {
 		log := common.Logger(ctx)
@@ -467,30 +565,30 @@ func runPostStep(step actionStep) common.Executor {
 		stepModel := step.getStepModel()
 		action := step.getActionModel()
 
-		switch action.Runs.Using {
-		case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16:
+		// todo: refactor into step
+		var actionDir string
+		var actionPath string
+		if _, ok := step.(*stepActionRemote); ok {
+			actionPath = newRemoteAction(stepModel.Uses).Path
+			actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), strings.ReplaceAll(stepModel.Uses, "/", "-"))
+		} else {
+			actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses)
+			actionPath = ""
+		}
 
-			populateEnvsFromSavedState(step.getEnv(), step, rc)
+		actionLocation := ""
+		if actionPath != "" {
+			actionLocation = path.Join(actionDir, actionPath)
+		} else {
+			actionLocation = actionDir
+		}
 
-			// todo: refactor into step
-			var actionDir string
-			var actionPath string
-			if _, ok := step.(*stepActionRemote); ok {
-				actionPath = newRemoteAction(stepModel.Uses).Path
-				actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), strings.ReplaceAll(stepModel.Uses, "/", "-"))
-			} else {
-				actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses)
-				actionPath = ""
-			}
+		_, containerActionDir := getContainerActionPaths(stepModel, actionLocation, rc)
 
-			actionLocation := ""
-			if actionPath != "" {
-				actionLocation = path.Join(actionDir, actionPath)
-			} else {
-				actionLocation = actionDir
-			}
+		switch action.Runs.Using {
+		case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16:
 
-			_, containerActionDir := getContainerActionPaths(stepModel, actionLocation, rc)
+			populateEnvsFromSavedState(step.getEnv(), step, rc)
 
 			containerArgs := []string{"node", path.Join(containerActionDir, action.Runs.Post)}
 			log.Debugf("executing remote job container: %s", containerArgs)
@@ -498,6 +596,10 @@ func runPostStep(step actionStep) common.Executor {
 			return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx)
 
 		case model.ActionRunsUsingComposite:
+			if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
+				return err
+			}
+
 			step.getCompositeRunContext().updateCompositeRunContext(step.getRunContext(), step)
 			return step.getCompositeSteps().post(ctx)
 
diff --git a/pkg/runner/action_composite.go b/pkg/runner/action_composite.go
index bb6c5dc6d1b..924bc7b5bbc 100644
--- a/pkg/runner/action_composite.go
+++ b/pkg/runner/action_composite.go
@@ -85,18 +85,12 @@ func execAsComposite(step actionStep) common.Executor {
 	return func(ctx context.Context) error {
 		compositerc := step.getCompositeRunContext()
 
-		var err error
 		steps := step.getCompositeSteps()
-		compositerc.updateCompositeRunContext(rc, step)
 
 		ctx = WithCompositeLogger(ctx, &compositerc.Masks)
 
-		// todo: pre should be run in the pre step
-		err = steps.pre(ctx)
-		if err == nil {
-			compositerc.updateCompositeRunContext(rc, step)
-			err = steps.main(ctx)
-		}
+		compositerc.updateCompositeRunContext(rc, step)
+		err := steps.main(ctx)
 
 		// Map outputs from composite RunContext to job RunContext
 		eval := compositerc.NewExpressionEvaluator()
diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go
index fa27995ee5f..224d1012b59 100644
--- a/pkg/runner/runner_test.go
+++ b/pkg/runner/runner_test.go
@@ -173,6 +173,7 @@ func TestRunEvent(t *testing.T) {
 		{workdir, "job-status-check", "push", "job 'fail' failed", platforms},
 		{workdir, "if-expressions", "push", "Job 'mytest' failed", platforms},
 		{workdir, "actions-environment-and-context-tests", "push", "", platforms},
+		{workdir, "uses-action-with-pre-and-post-step", "push", "", platforms},
 		{"../model/testdata", "strategy", "push", "", platforms}, // TODO: move all testdata into pkg so we can validate it with planner and runner
 		// {"testdata", "issue-228", "push", "", platforms, }, // TODO [igni]: Remove this once everything passes
 		{"../model/testdata", "container-volumes", "push", "", platforms},
diff --git a/pkg/runner/step.go b/pkg/runner/step.go
index c57ad64e957..60a37542022 100644
--- a/pkg/runner/step.go
+++ b/pkg/runner/step.go
@@ -169,7 +169,7 @@ func isStepEnabled(ctx context.Context, expr string, step step) (bool, error) {
 
 	runStep, err := EvalBool(rc.NewStepExpressionEvaluator(step), expr)
 	if err != nil {
-		return false, fmt.Errorf("  \u274C  Error in if-expression: \"if: %s\" (%s)", step.getStepModel().If.Value, err)
+		return false, fmt.Errorf("  \u274C  Error in if-expression: \"if: %s\" (%s)", expr, err)
 	}
 
 	return runStep, nil
diff --git a/pkg/runner/step_action_local.go b/pkg/runner/step_action_local.go
index 5697c3ed791..be4303aac75 100644
--- a/pkg/runner/step_action_local.go
+++ b/pkg/runner/step_action_local.go
@@ -25,15 +25,9 @@ type stepActionLocal struct {
 }
 
 func (sal *stepActionLocal) pre() common.Executor {
-	return func(ctx context.Context) error {
-		return nil
-	}
-}
-
-func (sal *stepActionLocal) main() common.Executor {
 	sal.env = map[string]string{}
 
-	return runStepExecutor(sal, stepStageMain, func(ctx context.Context) error {
+	return func(ctx context.Context) error {
 		actionDir := filepath.Join(sal.getRunContext().Config.Workdir, sal.Step.Uses)
 
 		localReader := func(ctx context.Context) actionYamlReader {
@@ -58,6 +52,20 @@ func (sal *stepActionLocal) main() common.Executor {
 
 		sal.action = actionModel
 
+		// run local pre step only for composite actions, to allow to run
+		// inside pre steps
+		if sal.action.Runs.Using == model.ActionRunsUsingComposite {
+			sal.RunContext.setupActionInputs(sal)
+			return runStepExecutor(sal, stepStagePre, runPreStep(sal)).If(hasPreStep(sal)).If(shouldRunPreStep(sal))(ctx)
+		}
+
+		return nil
+	}
+}
+
+func (sal *stepActionLocal) main() common.Executor {
+	return runStepExecutor(sal, stepStageMain, func(ctx context.Context) error {
+		actionDir := filepath.Join(sal.getRunContext().Config.Workdir, sal.Step.Uses)
 		return sal.runAction(sal, actionDir, nil)(ctx)
 	})
 }
diff --git a/pkg/runner/step_action_local_test.go b/pkg/runner/step_action_local_test.go
index 055169b8608..d0b0dfb0ee0 100644
--- a/pkg/runner/step_action_local_test.go
+++ b/pkg/runner/step_action_local_test.go
@@ -82,8 +82,10 @@ func TestStepActionLocalTest(t *testing.T) {
 		return nil
 	})
 
-	err := sal.main()(ctx)
+	err := sal.pre()(ctx)
+	assert.Nil(t, err)
 
+	err = sal.main()(ctx)
 	assert.Nil(t, err)
 
 	cm.AssertExpectations(t)
@@ -91,12 +93,49 @@ func TestStepActionLocalTest(t *testing.T) {
 }
 
 func TestStepActionLocalPre(t *testing.T) {
+	cm := &containerMock{}
+	salm := &stepActionLocalMocks{}
+
 	ctx := context.Background()
 
-	sal := &stepActionLocal{}
+	sal := &stepActionLocal{
+		readAction: salm.readAction,
+		RunContext: &RunContext{
+			StepResults: map[string]*model.StepResult{},
+			ExprEval:    &expressionEvaluator{},
+			Config: &Config{
+				Workdir: "/tmp",
+			},
+			Run: &model.Run{
+				JobID: "1",
+				Workflow: &model.Workflow{
+					Jobs: map[string]*model.Job{
+						"1": {
+							Defaults: model.Defaults{
+								Run: model.RunDefaults{
+									Shell: "bash",
+								},
+							},
+						},
+					},
+				},
+			},
+			JobContainer: cm,
+		},
+		Step: &model.Step{
+			ID:   "1",
+			Uses: "./path/to/action",
+		},
+	}
+
+	salm.On("readAction", sal.Step, "/tmp/path/to/action", "", mock.Anything, mock.Anything).
+		Return(&model.Action{}, nil)
 
 	err := sal.pre()(ctx)
 	assert.Nil(t, err)
+
+	cm.AssertExpectations(t)
+	salm.AssertExpectations(t)
 }
 
 func TestStepActionLocalPost(t *testing.T) {
diff --git a/pkg/runner/step_action_remote.go b/pkg/runner/step_action_remote.go
index 784ba2b14c9..edf4218bd3c 100644
--- a/pkg/runner/step_action_remote.go
+++ b/pkg/runner/step_action_remote.go
@@ -25,69 +25,85 @@ type stepActionRemote struct {
 	runAction           runAction
 	action              *model.Action
 	env                 map[string]string
-}
-
-func (sar *stepActionRemote) pre() common.Executor {
-	return func(ctx context.Context) error {
-		return nil
-	}
+	remoteAction        *remoteAction
 }
 
 var (
 	stepActionRemoteNewCloneExecutor = common.NewGitCloneExecutor
 )
 
-func (sar *stepActionRemote) main() common.Executor {
+func (sar *stepActionRemote) pre() common.Executor {
 	sar.env = map[string]string{}
 
-	return runStepExecutor(sar, stepStageMain, func(ctx context.Context) error {
-		remoteAction := newRemoteAction(sar.Step.Uses)
-		if remoteAction == nil {
-			return fmt.Errorf("Expected format {org}/{repo}[/path]@ref. Actual '%s' Input string was not in a correct format", sar.Step.Uses)
-		}
+	return common.NewPipelineExecutor(
+		func(ctx context.Context) error {
+			sar.remoteAction = newRemoteAction(sar.Step.Uses)
+			if sar.remoteAction == nil {
+				return fmt.Errorf("Expected format {org}/{repo}[/path]@ref. Actual '%s' Input string was not in a correct format", sar.Step.Uses)
+			}
+
+			sar.remoteAction.URL = sar.RunContext.Config.GitHubInstance
+
+			github := sar.RunContext.getGithubContext()
+			if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
+				common.Logger(ctx).Debugf("Skipping local actions/checkout because workdir was already copied")
+				return nil
+			}
+
+			actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), strings.ReplaceAll(sar.Step.Uses, "/", "-"))
+			gitClone := stepActionRemoteNewCloneExecutor(common.NewGitCloneExecutorInput{
+				URL:   sar.remoteAction.CloneURL(),
+				Ref:   sar.remoteAction.Ref,
+				Dir:   actionDir,
+				Token: github.Token,
+			})
+			var ntErr common.Executor
+			if err := gitClone(ctx); err != nil {
+				if err.Error() == "short SHA references are not supported" {
+					err = errors.Cause(err)
+					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.Error())
+				} else if err.Error() != "some refs were not updated" {
+					return err
+				} else {
+					ntErr = common.NewInfoExecutor("Non-terminating error while running 'git clone': %v", err)
+				}
+			}
 
-		remoteAction.URL = sar.RunContext.Config.GitHubInstance
+			remoteReader := func(ctx 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
+				}
+			}
 
+			return common.NewPipelineExecutor(
+				ntErr,
+				func(ctx context.Context) error {
+					actionModel, err := sar.readAction(sar.Step, actionDir, sar.remoteAction.Path, remoteReader(ctx), ioutil.WriteFile)
+					sar.action = actionModel
+					return err
+				},
+			)(ctx)
+		},
+		func(ctx context.Context) error {
+			sar.RunContext.setupActionInputs(sar)
+			return nil
+		},
+		runStepExecutor(sar, stepStagePre, runPreStep(sar)).If(hasPreStep(sar)).If(shouldRunPreStep(sar)))
+}
+
+func (sar *stepActionRemote) main() common.Executor {
+	return runStepExecutor(sar, stepStageMain, func(ctx context.Context) error {
 		github := sar.RunContext.getGithubContext()
-		if remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
+		if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
 			common.Logger(ctx).Debugf("Skipping local actions/checkout because workdir was already copied")
 			return nil
 		}
 
 		actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), strings.ReplaceAll(sar.Step.Uses, "/", "-"))
-		gitClone := stepActionRemoteNewCloneExecutor(common.NewGitCloneExecutorInput{
-			URL:   remoteAction.CloneURL(),
-			Ref:   remoteAction.Ref,
-			Dir:   actionDir,
-			Token: github.Token,
-		})
-		var ntErr common.Executor
-		if err := gitClone(ctx); err != nil {
-			if err.Error() == "short SHA references are not supported" {
-				err = errors.Cause(err)
-				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, remoteAction.Ref, err.Error())
-			} else if err.Error() != "some refs were not updated" {
-				return err
-			} else {
-				ntErr = common.NewInfoExecutor("Non-terminating error while running 'git clone': %v", err)
-			}
-		}
-
-		remoteReader := func(ctx context.Context) actionYamlReader {
-			return func(filename string) (io.Reader, io.Closer, error) {
-				f, err := os.Open(filepath.Join(actionDir, remoteAction.Path, filename))
-				return f, f, err
-			}
-		}
 
 		return common.NewPipelineExecutor(
-			ntErr,
-			func(ctx context.Context) error {
-				actionModel, err := sar.readAction(sar.Step, actionDir, remoteAction.Path, remoteReader(ctx), ioutil.WriteFile)
-				sar.action = actionModel
-				return err
-			},
-			sar.runAction(sar, actionDir, remoteAction),
+			sar.runAction(sar, actionDir, sar.remoteAction),
 		)(ctx)
 	})
 }
@@ -111,6 +127,11 @@ func (sar *stepActionRemote) getEnv() *map[string]string {
 func (sar *stepActionRemote) getIfExpression(stage stepStage) string {
 	switch stage {
 	case stepStagePre:
+		github := sar.RunContext.getGithubContext()
+		if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
+			// skip local checkout pre step
+			return "false"
+		}
 		return sar.action.Runs.PreIf
 	case stepStageMain:
 		return sar.Step.If.Value
@@ -127,7 +148,7 @@ func (sar *stepActionRemote) getActionModel() *model.Action {
 func (sar *stepActionRemote) getCompositeRunContext() *RunContext {
 	if sar.compositeRunContext == nil {
 		actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), strings.ReplaceAll(sar.Step.Uses, "/", "-"))
-		actionLocation := path.Join(actionDir, newRemoteAction(sar.Step.Uses).Path)
+		actionLocation := path.Join(actionDir, sar.remoteAction.Path)
 		_, containerActionDir := getContainerActionPaths(sar.getStepModel(), actionLocation, sar.RunContext)
 
 		sar.compositeRunContext = newCompositeRunContext(sar.RunContext, sar, containerActionDir)
diff --git a/pkg/runner/step_action_remote_test.go b/pkg/runner/step_action_remote_test.go
index 4425268069e..209bee5c38a 100644
--- a/pkg/runner/step_action_remote_test.go
+++ b/pkg/runner/step_action_remote_test.go
@@ -82,8 +82,8 @@ func TestStepActionRemote(t *testing.T) {
 				run    bool
 			}{
 				env:    true,
-				cloned: false,
-				read:   false,
+				cloned: true,
+				read:   true,
 				run:    false,
 			},
 		},
@@ -172,7 +172,10 @@ func TestStepActionRemote(t *testing.T) {
 				sarm.On("runAction", sar, suffixMatcher("act/remote-action@v1"), newRemoteAction(sar.Step.Uses)).Return(func(ctx context.Context) error { return tt.runError })
 			}
 
-			err := sar.main()(ctx)
+			err := sar.pre()(ctx)
+			if err == nil {
+				err = sar.main()(ctx)
+			}
 
 			assert.Equal(t, tt.runError, err)
 			assert.Equal(t, tt.mocks.cloned, clonedAction)
@@ -185,12 +188,70 @@ func TestStepActionRemote(t *testing.T) {
 }
 
 func TestStepActionRemotePre(t *testing.T) {
-	ctx := context.Background()
+	table := []struct {
+		name      string
+		stepModel *model.Step
+	}{
+		{
+			name: "run-pre",
+			stepModel: &model.Step{
+				Uses: "org/repo/path@ref",
+			},
+		},
+	}
+
+	for _, tt := range table {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx := context.Background()
+
+			clonedAction := false
+			sarm := &stepActionRemoteMocks{}
+
+			origStepAtionRemoteNewCloneExecutor := stepActionRemoteNewCloneExecutor
+			stepActionRemoteNewCloneExecutor = func(input common.NewGitCloneExecutorInput) common.Executor {
+				return func(ctx context.Context) error {
+					clonedAction = true
+					return nil
+				}
+			}
+			defer (func() {
+				stepActionRemoteNewCloneExecutor = origStepAtionRemoteNewCloneExecutor
+			})()
 
-	sar := &stepActionRemote{}
+			sar := &stepActionRemote{
+				Step: tt.stepModel,
+				RunContext: &RunContext{
+					Config: &Config{
+						GitHubInstance: "https://github.com",
+					},
+					Run: &model.Run{
+						JobID: "1",
+						Workflow: &model.Workflow{
+							Jobs: map[string]*model.Job{
+								"1": {},
+							},
+						},
+					},
+				},
+				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)
+
+			err := sar.pre()(ctx)
+
+			assert.Nil(t, err)
+			assert.Equal(t, true, clonedAction)
 
-	err := sar.pre()(ctx)
-	assert.Nil(t, err)
+			sarm.AssertExpectations(t)
+		})
+	}
 }
 
 func TestStepActionRemotePost(t *testing.T) {
diff --git a/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/action.yml b/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/action.yml
new file mode 100644
index 00000000000..1ba0fc61eb7
--- /dev/null
+++ b/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/action.yml
@@ -0,0 +1,7 @@
+name: "last action check"
+description: "last action check"
+
+runs:
+  using: "node16"
+  main: main.js
+  post: post.js
diff --git a/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/main.js b/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/main.js
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/post.js b/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/post.js
new file mode 100644
index 00000000000..e147e37d297
--- /dev/null
+++ b/pkg/runner/testdata/uses-action-with-pre-and-post-step/last-action/post.js
@@ -0,0 +1,17 @@
+const pre = process.env['ACTION_OUTPUT_PRE'];
+const main = process.env['ACTION_OUTPUT_MAIN'];
+const post = process.env['ACTION_OUTPUT_POST'];
+
+console.log({pre, main, post});
+
+if (pre !== 'pre') {
+  throw new Error(`Expected 'pre' but got '${pre}'`);
+}
+
+if (main !== 'main') {
+  throw new Error(`Expected 'main' but got '${main}'`);
+}
+
+if (post !== 'post') {
+  throw new Error(`Expected 'post' but got '${post}'`);
+}
diff --git a/pkg/runner/testdata/uses-action-with-pre-and-post-step/push.yml b/pkg/runner/testdata/uses-action-with-pre-and-post-step/push.yml
new file mode 100644
index 00000000000..0c3b793340e
--- /dev/null
+++ b/pkg/runner/testdata/uses-action-with-pre-and-post-step/push.yml
@@ -0,0 +1,15 @@
+name: uses-action-with-pre-and-post-step
+on: push
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./uses-action-with-pre-and-post-step/last-action
+      - uses: nektos/act-test-actions/js-with-pre-and-post-step@main
+        with:
+          pre: true
+          post: true
+      - run: |
+          cat $GITHUB_ENV