1
0
Fork 0
mirror of https://code.forgejo.org/forgejo/runner.git synced 2025-09-15 18:57:01 +00:00

implement pre and post steps (#1089)

* feat: add post step to actions and add state command

This commit includes requried changes for running post steps
for local and remote actions.
This allows general cleanup work to be done after executing
an action.

Communication is allowed between this steps, by using the
action state.

* feat: collect pre and post steps for composite actions

* refactor: move composite action logic into own file

* refactor: restructure composite handling

* feat: run composite post steps during post step lifecycle

* refactor: remove duplicate log output

* feat: run all composite post actions in a step

Since composite actions could have multiple pre/post steps inside,
we need to run all of them in a single top-level pre/post step.

This PR includes a test case for this and the correct order of steps
to be executed.

* refactor: remove unused lines of code

* refactor: simplify test expression

* fix: use composite job logger

* fix: make step output more readable

* fix: enforce running all post executor

To make sure every post executor/step is executed, it is chained
with it's own Finally executor.

* fix: do not run post step if no step result is available

Having no step result means we do not run any step (neither pre
nor main) and we do not need to run post.

* fix: setup defaults

If no pre-if or post-if is given, it should default to 'always()'.
This could be set even if there is no pre or post step.
In fact this is required for composite actions and included post
steps to run.

* fix: output step related if expression

* test: update expectation

* feat: run pre step from actions (#1110)

This PR implements running pre steps for remote actions.
This includes remote actions using inside local composite actions.

* fix: set correct expr default status checks

For post-if conditions the default status check should be
always(), while for all other if expression the default status
check is success()

References:
https://docs.github.com/en/actions/learn-github-actions/expressions#status-check-functions
https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runspost-if

* fix: remove code added during rebase
This commit is contained in:
Markus Wolf 2022-05-24 15:36:06 +02:00 committed by GitHub
parent 7aa863a21c
commit f84de6fb2b
37 changed files with 1560 additions and 350 deletions

View file

@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"
@ -13,88 +14,102 @@ import (
"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/model"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
type stepActionRemote struct {
Step *model.Step
RunContext *RunContext
readAction readAction
runAction runAction
action *model.Action
env map[string]string
}
func (sar *stepActionRemote) pre() common.Executor {
return func(ctx context.Context) error {
return nil
}
Step *model.Step
RunContext *RunContext
compositeRunContext *RunContext
compositeSteps *compositeSteps
readAction readAction
runAction runAction
action *model.Action
env map[string]string
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, 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)
}
remoteAction.URL = sar.RunContext.Config.GitHubInstance
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)
}
}
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
log.Debugf("Read action %v from '%s'", sar.action, "Unknown")
return err
},
sar.runAction(sar, actionDir, remoteAction),
sar.runAction(sar, actionDir, sar.remoteAction),
)(ctx)
})
}
func (sar *stepActionRemote) post() common.Executor {
return func(ctx context.Context) error {
return nil
}
return runStepExecutor(sar, stepStagePost, runPostStep(sar)).If(hasPostStep(sar)).If(shouldRunPostStep(sar))
}
func (sar *stepActionRemote) getRunContext() *RunContext {
@ -109,10 +124,43 @@ func (sar *stepActionRemote) getEnv() *map[string]string {
return &sar.env
}
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
case stepStagePost:
return sar.action.Runs.PostIf
}
return ""
}
func (sar *stepActionRemote) getActionModel() *model.Action {
return sar.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, sar.remoteAction.Path)
_, containerActionDir := getContainerActionPaths(sar.getStepModel(), actionLocation, sar.RunContext)
sar.compositeRunContext = newCompositeRunContext(sar.RunContext, sar, containerActionDir)
sar.compositeSteps = sar.compositeRunContext.compositeExecutor(sar.action)
}
return sar.compositeRunContext
}
func (sar *stepActionRemote) getCompositeSteps() *compositeSteps {
return sar.compositeSteps
}
type remoteAction struct {
URL string
Org string