e360811570
* refactor: remove composite action runcontext workaround The RunContext is cloned to execute a composite action with all its steps in a similar context. This required some workaround, since the command handler has kept a reference to the original RunContext. This is solved now, by replacing the docker LogWriter with a proper scoped LogWriter. This prepares for a simpler setup of composite actions to be able to create and re-create the composite RunContext for pre/main/post action steps. * test: check env-vars for local js and docker actions * test: test remote docker and js actions * fix: merge github context into env when read and setup * refacotr: simplify composite context setup * test: use a map matcher to test input setup * fix: restore composite log output Since we create a new line writer, we need to log the raw_output as well. Otherwise no output will be available from the log-writer * fix: add RunContext JobName to fill GITHUB_JOBNAME * test: use nektos/act-test-actions * fix: allow masking values in composite actions To allow masking of values from composite actions, we need to use a custom job logger with a reference to the masked values for the composite run context. * refactor: keep existing logger for composite actions To not introduce another new logger while still be able to use the masking from the composite action, we add the masks to the go context. To leverage that context, we also add the context to the log entries where the valueMasker then could get the actual mask values. With this way to 'inject' the masked values into the logger, we do - keep the logger - keep the coloring - stay away from inconsistencies due to parallel jobs * fix: re-add removed color increase This one should have never removed :-) * fix: add missing ExtraPath attribute * fix: merge run context env into composite run context env This adds a test and fix for the parent environment. It should be inherited by the composite environment. * test: add missing test case * fix: store github token next to secrets We must not expose the secrets to composite actions, but the `github.token` is available inside composite actions. To provide this we store the token in the config and create it in the GithubContext from there. The token can be used with `github.token` but is not available as `secrets.GITHUB_TOKEN`. This implements the same behavior as on GitHub. Co-authored-by: Björn Brauer <bjoern.brauer@new-work.se> Co-authored-by: Marcus Noll <markus.noll@new-work.se> * fixup! fix: allow masking values in composite actions * style: use tabs instead of spaces to fix linter errors Co-authored-by: Björn Brauer <bjoern.brauer@new-work.se> Co-authored-by: Marcus Noll <markus.noll@new-work.se> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
169 lines
4.8 KiB
Go
169 lines
4.8 KiB
Go
package runner
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/kballard/go-shellquote"
|
|
"github.com/nektos/act/pkg/common"
|
|
"github.com/nektos/act/pkg/container"
|
|
"github.com/nektos/act/pkg/model"
|
|
)
|
|
|
|
type stepRun struct {
|
|
Step *model.Step
|
|
RunContext *RunContext
|
|
cmd []string
|
|
env map[string]string
|
|
}
|
|
|
|
func (sr *stepRun) pre() common.Executor {
|
|
return func(ctx context.Context) error {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (sr *stepRun) main() common.Executor {
|
|
sr.env = map[string]string{}
|
|
|
|
return runStepExecutor(sr, common.NewPipelineExecutor(
|
|
sr.setupShellCommandExecutor(),
|
|
func(ctx context.Context) error {
|
|
return sr.getRunContext().JobContainer.Exec(sr.cmd, sr.env, "", sr.Step.WorkingDirectory)(ctx)
|
|
},
|
|
))
|
|
}
|
|
|
|
func (sr *stepRun) post() common.Executor {
|
|
return func(ctx context.Context) error {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (sr *stepRun) getRunContext() *RunContext {
|
|
return sr.RunContext
|
|
}
|
|
|
|
func (sr *stepRun) getStepModel() *model.Step {
|
|
return sr.Step
|
|
}
|
|
|
|
func (sr *stepRun) getEnv() *map[string]string {
|
|
return &sr.env
|
|
}
|
|
|
|
func (sr *stepRun) setupShellCommandExecutor() common.Executor {
|
|
return func(ctx context.Context) error {
|
|
scriptName, script, err := sr.setupShellCommand(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return sr.RunContext.JobContainer.Copy(ActPath, &container.FileEntry{
|
|
Name: scriptName,
|
|
Mode: 0755,
|
|
Body: script,
|
|
})(ctx)
|
|
}
|
|
}
|
|
|
|
func getScriptName(rc *RunContext, step *model.Step) string {
|
|
scriptName := step.ID
|
|
for rcs := rc; rcs.Parent != nil; rcs = rcs.Parent {
|
|
scriptName = fmt.Sprintf("%s-composite-%s", rcs.Parent.CurrentStep, scriptName)
|
|
}
|
|
return fmt.Sprintf("workflow/%s", scriptName)
|
|
}
|
|
|
|
// TODO: Currently we just ignore top level keys, BUT we should return proper error on them
|
|
// BUTx2 I leave this for when we rewrite act to use actionlint for workflow validation
|
|
// so we return proper errors before any execution or spawning containers
|
|
// it will error anyway with:
|
|
// OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "${{": executable file not found in $PATH: unknown
|
|
func (sr *stepRun) setupShellCommand(ctx context.Context) (name, script string, err error) {
|
|
sr.setupShell()
|
|
sr.setupWorkingDirectory()
|
|
|
|
step := sr.Step
|
|
|
|
script = sr.RunContext.NewStepExpressionEvaluator(sr).Interpolate(step.Run)
|
|
|
|
scCmd := step.ShellCommand()
|
|
|
|
name = getScriptName(sr.RunContext, step)
|
|
|
|
// Reference: https://github.com/actions/runner/blob/8109c962f09d9acc473d92c595ff43afceddb347/src/Runner.Worker/Handlers/ScriptHandlerHelpers.cs#L47-L64
|
|
// Reference: https://github.com/actions/runner/blob/8109c962f09d9acc473d92c595ff43afceddb347/src/Runner.Worker/Handlers/ScriptHandlerHelpers.cs#L19-L27
|
|
runPrepend := ""
|
|
runAppend := ""
|
|
switch step.Shell {
|
|
case "bash", "sh":
|
|
name += ".sh"
|
|
case "pwsh", "powershell":
|
|
name += ".ps1"
|
|
runPrepend = "$ErrorActionPreference = 'stop'"
|
|
runAppend = "if ((Test-Path -LiteralPath variable:/LASTEXITCODE)) { exit $LASTEXITCODE }"
|
|
case "cmd":
|
|
name += ".cmd"
|
|
runPrepend = "@echo off"
|
|
case "python":
|
|
name += ".py"
|
|
}
|
|
|
|
script = fmt.Sprintf("%s\n%s\n%s", runPrepend, script, runAppend)
|
|
|
|
if !strings.Contains(script, "::add-mask::") && !sr.RunContext.Config.InsecureSecrets {
|
|
common.Logger(ctx).Debugf("Wrote command \n%s\n to '%s'", script, name)
|
|
} else {
|
|
common.Logger(ctx).Debugf("Wrote add-mask command to '%s'", name)
|
|
}
|
|
|
|
scriptPath := fmt.Sprintf("%s/%s", ActPath, name)
|
|
sr.cmd, err = shellquote.Split(strings.Replace(scCmd, `{0}`, scriptPath, 1))
|
|
|
|
return name, script, err
|
|
}
|
|
|
|
func (sr *stepRun) setupShell() {
|
|
rc := sr.RunContext
|
|
step := sr.Step
|
|
|
|
if step.Shell == "" {
|
|
step.Shell = rc.Run.Job().Defaults.Run.Shell
|
|
}
|
|
|
|
step.Shell = rc.NewExpressionEvaluator().Interpolate(step.Shell)
|
|
|
|
if step.Shell == "" {
|
|
step.Shell = rc.Run.Workflow.Defaults.Run.Shell
|
|
}
|
|
|
|
// current GitHub Runner behaviour is that default is `sh`,
|
|
// but if it's not container it validates with `which` command
|
|
// if `bash` is available, and provides `bash` if it is
|
|
// for now I'm going to leave below logic, will address it in different PR
|
|
// https://github.com/actions/runner/blob/9a829995e02d2db64efb939dc2f283002595d4d9/src/Runner.Worker/Handlers/ScriptHandler.cs#L87-L91
|
|
if rc.Run.Job().Container() != nil {
|
|
if rc.Run.Job().Container().Image != "" && step.Shell == "" {
|
|
step.Shell = "sh"
|
|
}
|
|
}
|
|
}
|
|
|
|
func (sr *stepRun) setupWorkingDirectory() {
|
|
rc := sr.RunContext
|
|
step := sr.Step
|
|
|
|
if step.WorkingDirectory == "" {
|
|
step.WorkingDirectory = rc.Run.Job().Defaults.Run.WorkingDirectory
|
|
}
|
|
|
|
// jobs can receive context values, so we interpolate
|
|
step.WorkingDirectory = rc.NewExpressionEvaluator().Interpolate(step.WorkingDirectory)
|
|
|
|
// but top level keys in workflow file like `defaults` or `env` can't
|
|
if step.WorkingDirectory == "" {
|
|
step.WorkingDirectory = rc.Run.Workflow.Defaults.Run.WorkingDirectory
|
|
}
|
|
}
|