Skip to content

Commit 9868e13

Browse files
ChristopherHXKnisterPeterZauberNerdRyanmergify[bot]
authored
Feature: uses in composite (#793)
* Feature: uses in composite * Negate logic * Reduce complexity * Update step_context.go * Update step_context.go * Update step_context.go * Fix syntax error in test * Bump * Disable usage of actions/setup-node@v2 * Bump * Fix step id collision * Fix output command workaround * Make secrets context inaccessible in composite * Fix order after adding a workaround (needs tests) Fixes #793 (comment) * Evaluate env before passing one step deeper If env would contain any inputs, steps ctx or secrets there was undefined behaviour * [no ci] prepare secret test * Initial test pass inputs as env * Fix syntax error * extend test also for direct invoke * Fix passing provided env as composite output * Fix syntax error * toUpper 'no such secret', act has a bug * fix indent * Fix env outputs in composite * Test env outputs of composite * Fix inputs not defined in docker actions * Fix interpolate args input of docker actions * Fix lint * AllowCompositeIf now defaults to true see https://github.com/actions/runner/releases/tag/v2.284.0 * Fix lint * Fix env of docker action.yml * Test calling a local docker action from composite With input context hirachy * local-action-dockerfile Test pass on action/runner It seems action/runner ignores overrides of args, if the target docker action has the args property set. * Fix exec permissions of docker-local-noargs * Revert getStepsContext change * fix: handle composite action on error and continue This change is a follow up of #840 and integrates with #793 There are two things included here: - The default value for a step.if in an action need to be 'success()' - We need to hand the error from a composite action back to the calling executor Co-authored-by: Björn Brauer <[email protected]> * Patch inputs can be bool, float64 and string for workflow_call Also inputs is now always defined, but may be null * Simplify cherry-picked commit * Minor style adjustments * Remove chmod +x from tests now fails on windows like before * Fix GITHUB_ACTION_PATH some action env vars Fixes GITHUB_ACTION_REPOSITORY, GITHUB_ACTION_REF. * Add comment to CompositeRestrictions Co-authored-by: Markus Wolf <[email protected]> Co-authored-by: Björn Brauer <[email protected]> Co-authored-by: Ryan <[email protected]> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 2ef30c3 commit 9868e13

File tree

19 files changed

+463
-157
lines changed

19 files changed

+463
-157
lines changed

pkg/model/action.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,16 @@ type Output struct {
8383
func ReadAction(in io.Reader) (*Action, error) {
8484
a := new(Action)
8585
err := yaml.NewDecoder(in).Decode(a)
86-
return a, err
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
for i := range a.Runs.Steps {
91+
step := &a.Runs.Steps[i]
92+
if step.If.Value == "" {
93+
step.If.Value = "success()"
94+
}
95+
}
96+
97+
return a, nil
8798
}

pkg/model/workflow.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@ type Workflow struct {
2323
Defaults Defaults `yaml:"defaults"`
2424
}
2525

26+
// CompositeRestrictions is the structure to control what is allowed in composite actions
27+
type CompositeRestrictions struct {
28+
AllowCompositeUses bool
29+
AllowCompositeIf bool
30+
AllowCompositeContinueOnError bool
31+
}
32+
33+
func defaultCompositeRestrictions() *CompositeRestrictions {
34+
return &CompositeRestrictions{
35+
AllowCompositeUses: true,
36+
AllowCompositeIf: true,
37+
AllowCompositeContinueOnError: false,
38+
}
39+
}
40+
2641
// On events for the workflow
2742
func (w *Workflow) On() []string {
2843
switch w.RawOn.Kind {
@@ -411,11 +426,18 @@ func (s *Step) Type() StepType {
411426
return StepTypeUsesActionRemote
412427
}
413428

414-
func (s *Step) Validate() error {
415-
if s.Type() != StepTypeRun {
429+
func (s *Step) Validate(config *CompositeRestrictions) error {
430+
if config == nil {
431+
config = defaultCompositeRestrictions()
432+
}
433+
if s.Type() != StepTypeRun && !config.AllowCompositeUses {
416434
return fmt.Errorf("(StepID: %s): Unexpected value 'uses'", s.String())
417-
} else if s.Shell == "" {
435+
} else if s.Type() == StepTypeRun && s.Shell == "" {
418436
return fmt.Errorf("(StepID: %s): Required property is missing: 'shell'", s.String())
437+
} else if !s.If.IsZero() && !config.AllowCompositeIf {
438+
return fmt.Errorf("(StepID: %s): Property is not available: 'if'", s.String())
439+
} else if s.ContinueOnError && !config.AllowCompositeContinueOnError {
440+
return fmt.Errorf("(StepID: %s): Property is not available: 'continue-on-error'", s.String())
419441
}
420442
return nil
421443
}

pkg/runner/expression.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ func (sc *StepContext) NewExpressionEvaluator() ExpressionEvaluator {
3838
vm := sc.RunContext.newVM()
3939
configers := []func(*otto.Otto){
4040
sc.vmEnv(),
41-
sc.vmInputs(),
42-
4341
sc.vmNeeds(),
4442
sc.vmSuccess(),
4543
sc.vmFailure(),
@@ -237,6 +235,7 @@ func (rc *RunContext) newVM() *otto.Otto {
237235
rc.vmMatrix(),
238236
rc.vmEnv(),
239237
rc.vmNeeds(),
238+
rc.vmInputs(),
240239
}
241240
vm := otto.New()
242241
for _, configer := range configers {
@@ -447,22 +446,9 @@ func (sc *StepContext) vmEnv() func(*otto.Otto) {
447446
}
448447
}
449448

450-
func (sc *StepContext) vmInputs() func(*otto.Otto) {
451-
inputs := make(map[string]string)
452-
453-
// Set Defaults
454-
if sc.Action != nil {
455-
for k, input := range sc.Action.Inputs {
456-
inputs[k] = sc.RunContext.NewExpressionEvaluator().Interpolate(input.Default)
457-
}
458-
}
459-
460-
for k, v := range sc.Step.With {
461-
inputs[k] = sc.RunContext.NewExpressionEvaluator().Interpolate(v)
462-
}
463-
449+
func (rc *RunContext) vmInputs() func(*otto.Otto) {
464450
return func(vm *otto.Otto) {
465-
_ = vm.Set("inputs", inputs)
451+
_ = vm.Set("inputs", rc.Inputs)
466452
}
467453
}
468454

@@ -587,7 +573,10 @@ func (rc *RunContext) vmRunner() func(*otto.Otto) {
587573

588574
func (rc *RunContext) vmSecrets() func(*otto.Otto) {
589575
return func(vm *otto.Otto) {
590-
_ = vm.Set("secrets", rc.Config.Secrets)
576+
// Hide secrets from composite actions
577+
if rc.Composite == nil {
578+
_ = vm.Set("secrets", rc.Config.Secrets)
579+
}
591580
}
592581
}
593582

pkg/runner/run_context.go

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,35 @@ const ActPath string = "/var/run/act"
2727

2828
// RunContext contains info about current job
2929
type RunContext struct {
30-
Name string
31-
Config *Config
32-
Matrix map[string]interface{}
33-
Run *model.Run
34-
EventJSON string
35-
Env map[string]string
36-
ExtraPath []string
37-
CurrentStep string
38-
StepResults map[string]*stepResult
39-
ExprEval ExpressionEvaluator
40-
JobContainer container.Container
41-
OutputMappings map[MappableOutput]MappableOutput
42-
JobName string
30+
Name string
31+
Config *Config
32+
Matrix map[string]interface{}
33+
Run *model.Run
34+
EventJSON string
35+
Env map[string]string
36+
ExtraPath []string
37+
CurrentStep string
38+
StepResults map[string]*stepResult
39+
ExprEval ExpressionEvaluator
40+
JobContainer container.Container
41+
OutputMappings map[MappableOutput]MappableOutput
42+
JobName string
43+
ActionPath string
44+
ActionRef string
45+
ActionRepository string
46+
Composite *model.Action
47+
Inputs map[string]interface{}
48+
Parent *RunContext
49+
}
50+
51+
func (rc *RunContext) Clone() *RunContext {
52+
clone := *rc
53+
clone.CurrentStep = ""
54+
clone.Composite = nil
55+
clone.Inputs = nil
56+
clone.StepResults = make(map[string]*stepResult)
57+
clone.Parent = rc
58+
return &clone
4359
}
4460

4561
type MappableOutput struct {
@@ -310,6 +326,22 @@ func (rc *RunContext) Executor() common.Executor {
310326
}).If(rc.isEnabled)
311327
}
312328

329+
// Executor returns a pipeline executor for all the steps in the job
330+
func (rc *RunContext) CompositeExecutor() common.Executor {
331+
steps := make([]common.Executor, 0)
332+
333+
for i, step := range rc.Composite.Runs.Steps {
334+
if step.ID == "" {
335+
step.ID = fmt.Sprintf("%d", i)
336+
}
337+
stepcopy := step
338+
steps = append(steps, rc.newStepExecutor(&stepcopy))
339+
}
340+
341+
steps = append(steps, common.JobError)
342+
return common.NewPipelineExecutor(steps...)
343+
}
344+
313345
func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
314346
sc := &StepContext{
315347
RunContext: rc,
@@ -568,9 +600,9 @@ func (rc *RunContext) getGithubContext() *githubContext {
568600
Workspace: rc.Config.ContainerWorkdir(),
569601
Action: rc.CurrentStep,
570602
Token: rc.Config.Secrets["GITHUB_TOKEN"],
571-
ActionPath: rc.Config.Env["GITHUB_ACTION_PATH"],
572-
ActionRef: rc.Config.Env["RUNNER_ACTION_REF"],
573-
ActionRepository: rc.Config.Env["RUNNER_ACTION_REPOSITORY"],
603+
ActionPath: rc.ActionPath,
604+
ActionRef: rc.ActionRef,
605+
ActionRepository: rc.ActionRepository,
574606
RepositoryOwner: rc.Config.Env["GITHUB_REPOSITORY_OWNER"],
575607
RetentionDays: rc.Config.Env["GITHUB_RETENTION_DAYS"],
576608
RunnerPerflog: rc.Config.Env["RUNNER_PERFLOG"],
@@ -737,9 +769,9 @@ func (rc *RunContext) withGithubEnv(env map[string]string) map[string]string {
737769
env["GITHUB_RUN_ID"] = github.RunID
738770
env["GITHUB_RUN_NUMBER"] = github.RunNumber
739771
env["GITHUB_ACTION"] = github.Action
740-
if github.ActionPath != "" {
741-
env["GITHUB_ACTION_PATH"] = github.ActionPath
742-
}
772+
env["GITHUB_ACTION_PATH"] = github.ActionPath
773+
env["GITHUB_ACTION_REPOSITORY"] = github.ActionRepository
774+
env["GITHUB_ACTION_REF"] = github.ActionRef
743775
env["GITHUB_ACTIONS"] = "true"
744776
env["GITHUB_ACTOR"] = github.Actor
745777
env["GITHUB_REPOSITORY"] = github.Repository

pkg/runner/runner.go

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,32 @@ type Runner interface {
2121

2222
// Config contains the config for a new runner
2323
type Config struct {
24-
Actor string // the user that triggered the event
25-
Workdir string // path to working directory
26-
BindWorkdir bool // bind the workdir to the job container
27-
EventName string // name of event to run
28-
EventPath string // path to JSON file to use for event.json in containers
29-
DefaultBranch string // name of the main branch for this repository
30-
ReuseContainers bool // reuse containers to maintain state
31-
ForcePull bool // force pulling of the image, even if already present
32-
ForceRebuild bool // force rebuilding local docker image action
33-
LogOutput bool // log the output from docker run
34-
Env map[string]string // env for containers
35-
Secrets map[string]string // list of secrets
36-
InsecureSecrets bool // switch hiding output when printing to terminal
37-
Platforms map[string]string // list of platforms
38-
Privileged bool // use privileged mode
39-
UsernsMode string // user namespace to use
40-
ContainerArchitecture string // Desired OS/architecture platform for running containers
41-
ContainerDaemonSocket string // Path to Docker daemon socket
42-
UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true
43-
GitHubInstance string // GitHub instance to use, default "github.com"
44-
ContainerCapAdd []string // list of kernel capabilities to add to the containers
45-
ContainerCapDrop []string // list of kernel capabilities to remove from the containers
46-
AutoRemove bool // controls if the container is automatically removed upon workflow completion
47-
ArtifactServerPath string // the path where the artifact server stores uploads
48-
ArtifactServerPort string // the port the artifact server binds to
24+
Actor string // the user that triggered the event
25+
Workdir string // path to working directory
26+
BindWorkdir bool // bind the workdir to the job container
27+
EventName string // name of event to run
28+
EventPath string // path to JSON file to use for event.json in containers
29+
DefaultBranch string // name of the main branch for this repository
30+
ReuseContainers bool // reuse containers to maintain state
31+
ForcePull bool // force pulling of the image, even if already present
32+
ForceRebuild bool // force rebuilding local docker image action
33+
LogOutput bool // log the output from docker run
34+
Env map[string]string // env for containers
35+
Secrets map[string]string // list of secrets
36+
InsecureSecrets bool // switch hiding output when printing to terminal
37+
Platforms map[string]string // list of platforms
38+
Privileged bool // use privileged mode
39+
UsernsMode string // user namespace to use
40+
ContainerArchitecture string // Desired OS/architecture platform for running containers
41+
ContainerDaemonSocket string // Path to Docker daemon socket
42+
UseGitIgnore bool // controls if paths in .gitignore should not be copied into container, default true
43+
GitHubInstance string // GitHub instance to use, default "github.com"
44+
ContainerCapAdd []string // list of kernel capabilities to add to the containers
45+
ContainerCapDrop []string // list of kernel capabilities to remove from the containers
46+
AutoRemove bool // controls if the container is automatically removed upon workflow completion
47+
ArtifactServerPath string // the path where the artifact server stores uploads
48+
ArtifactServerPort string // the port the artifact server binds to
49+
CompositeRestrictions *model.CompositeRestrictions // describes which features are available in composite actions
4950
}
5051

5152
// Resolves the equivalent host path inside the container

pkg/runner/runner_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,16 @@ func TestRunEvent(t *testing.T) {
112112
{"testdata", "remote-action-js", "push", "", platforms, ""},
113113
{"testdata", "local-action-docker-url", "push", "", platforms, ""},
114114
{"testdata", "local-action-dockerfile", "push", "", platforms, ""},
115+
{"testdata", "local-action-via-composite-dockerfile", "push", "", platforms, ""},
115116
{"testdata", "local-action-js", "push", "", platforms, ""},
116117
{"testdata", "matrix", "push", "", platforms, ""},
117118
{"testdata", "matrix-include-exclude", "push", "", platforms, ""},
118119
{"testdata", "commands", "push", "", platforms, ""},
119120
{"testdata", "workdir", "push", "", platforms, ""},
120121
{"testdata", "defaults-run", "push", "", platforms, ""},
121122
{"testdata", "uses-composite", "push", "", platforms, ""},
123+
{"testdata", "uses-composite-with-error", "push", "Job 'failing-composite-action' failed", platforms, ""},
124+
{"testdata", "uses-nested-composite", "push", "", platforms, ""},
122125
{"testdata", "issue-597", "push", "", platforms, ""},
123126
{"testdata", "issue-598", "push", "", platforms, ""},
124127
{"testdata", "env-and-path", "push", "", platforms, ""},

0 commit comments

Comments
 (0)