Fix composite input handling (#1345)

* test: add test case for #1319

* fix: setup of composite inputs

This change fixes the composite action setup handling of inputs.

All inputs are taken from the env now. The env is composed of
the 'level above'.
For example:
- step env -> taken from run context
- action env -> taken from step env
- composite env -> taken from action env

Before this change the env setup for steps, actions and composite
run contexts was harder to understand as all parts looked into
one of these: parent run context, step, action, composite run context.

Now the 'data flow' is from higher levels to lower levels which should
make it more clean.

Fixes #1319

* test: add simple remote composite action test

Since we don't have a remote composite test at all
before this, we need at least the simplest case.
This does not check every feature, but ensures basic
availability of remote composite actions.

* refactor: move ActionRef and ActionRepository

Moving ActionRef and ActionRepository from RunContext into the
step, allows us to remove the - more or less - ugly copy operations
from the RunContext.

This is more clean, as each step does hold the data required anyway
and the RunContext shouldn't know about the action details.

* refactor: remove unused properties
This commit is contained in:
Markus Wolf 2022-10-06 23:58:16 +02:00 committed by GitHub
parent 1bade27534
commit 679cac1677
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 246 additions and 149 deletions

View file

@ -215,7 +215,8 @@ func (j *Job) Matrix() map[string][]interface{} {
// GetMatrixes returns the matrix cross product
// It skips includes and hard fails excludes for non-existing keys
// nolint:gocyclo
//
//nolint:gocyclo
func (j *Job) GetMatrixes() []map[string]interface{} {
matrixes := make([]map[string]interface{}, 0)
if j.Strategy != nil {
@ -376,8 +377,16 @@ func (s *Step) String() string {
}
// Environments returns string-based key=value map for a step
// Note: all keys are uppercase
func (s *Step) Environment() map[string]string {
return environment(s.Env)
env := environment(s.Env)
for k, v := range env {
delete(env, k)
env[strings.ToUpper(k)] = v
}
return env
}
// GetEnv gets the env for a step
@ -436,6 +445,22 @@ const (
StepTypeInvalid
)
func (s StepType) String() string {
switch s {
case StepTypeInvalid:
return "invalid"
case StepTypeRun:
return "run"
case StepTypeUsesActionLocal:
return "local-action"
case StepTypeUsesActionRemote:
return "remote-action"
case StepTypeUsesDockerURL:
return "docker"
}
return "unknown"
}
// Type returns the type of the step
func (s *Step) Type() StepType {
if s.Run == "" && s.Uses == "" {

View file

@ -136,26 +136,10 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
action := step.getActionModel()
logger.Debugf("About to run action %v", action)
if remoteAction != nil {
rc.ActionRepository = fmt.Sprintf("%s/%s", remoteAction.Org, remoteAction.Repo)
rc.ActionRef = remoteAction.Ref
} else {
rc.ActionRepository = ""
rc.ActionRef = ""
err := setupActionEnv(ctx, step, remoteAction)
if err != nil {
return err
}
defer (func() {
// cleanup after the action is done, to avoid side-effects in
// the next step/action
rc.ActionRepository = ""
rc.ActionRef = ""
})()
// we need to merge with github-env again, since at the step setup
// time, we don't have all environment prepared
mergeIntoMap(step.getEnv(), rc.withGithubEnv(ctx, map[string]string{}))
populateEnvsFromSavedState(step.getEnv(), step, rc)
populateEnvsFromInput(ctx, step.getEnv(), action, rc)
actionLocation := path.Join(actionDir, actionPath)
actionName, containerActionDir := getContainerActionPaths(stepModel, actionLocation, rc)
@ -169,6 +153,7 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
}
containerArgs := []string{"node", path.Join(containerActionDir, action.Runs.Main)}
logger.Debugf("executing remote job container: %s", containerArgs)
return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx)
case model.ActionRunsUsingDocker:
location := actionLocation
@ -193,6 +178,20 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
}
}
func setupActionEnv(ctx context.Context, step actionStep, remoteAction *remoteAction) error {
rc := step.getRunContext()
// A few fields in the environment (e.g. GITHUB_ACTION_REPOSITORY)
// are dependent on the action. That means we can complete the
// setup only after resolving the whole action model and cloning
// the action
rc.withGithubEnv(ctx, step.getGithubContext(ctx), *step.getEnv())
populateEnvsFromSavedState(step.getEnv(), step, rc)
populateEnvsFromInput(ctx, step.getEnv(), step.getActionModel(), rc)
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
@ -211,7 +210,8 @@ func removeGitIgnore(ctx context.Context, directory string) error {
}
// TODO: break out parts of function to reduce complexicity
// nolint:gocyclo
//
//nolint:gocyclo
func execAsDocker(ctx context.Context, step actionStep, actionName string, basedir string, localAction bool) error {
logger := common.Logger(ctx)
rc := step.getRunContext()
@ -299,11 +299,8 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based
func evalDockerArgs(ctx context.Context, step step, action *model.Action, cmd *[]string) {
rc := step.getRunContext()
stepModel := step.getStepModel()
oldInputs := rc.Inputs
defer func() {
rc.Inputs = oldInputs
}()
inputs := make(map[string]interface{})
inputs := make(map[string]string)
eval := rc.NewExpressionEvaluator(ctx)
// Set Defaults
for k, input := range action.Inputs {
@ -314,7 +311,8 @@ func evalDockerArgs(ctx context.Context, step step, action *model.Action, cmd *[
inputs[k] = eval.Interpolate(ctx, v)
}
}
rc.Inputs = inputs
mergeIntoMap(step.getEnv(), inputs)
stepEE := rc.NewStepExpressionEvaluator(ctx, step)
for i, v := range *cmd {
(*cmd)[i] = stepEE.Interpolate(ctx, v)
@ -372,29 +370,6 @@ func newStepContainer(ctx context.Context, step step, image string, cmd []string
return stepContainer
}
func (rc *RunContext) setupActionInputs(ctx context.Context, 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(ctx)
inputs := make(map[string]interface{})
for k, input := range action.Inputs {
inputs[k] = eval.Interpolate(ctx, input.Default)
}
if stepModel.With != nil {
for k, v := range stepModel.With {
inputs[k] = eval.Interpolate(ctx, v)
}
}
rc.Inputs = inputs
}
func populateEnvsFromSavedState(env *map[string]string, step actionStep, rc *RunContext) {
stepResult := rc.StepResults[step.getStepModel().ID]
if stepResult != nil {
@ -513,7 +488,10 @@ func runPreStep(step actionStep) common.Executor {
return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx)
case model.ActionRunsUsingComposite:
step.getCompositeRunContext(ctx).updateCompositeRunContext(ctx, step.getRunContext(), step)
if step.getCompositeSteps() == nil {
step.getCompositeRunContext(ctx)
}
return step.getCompositeSteps().pre(ctx)
default:
@ -601,7 +579,6 @@ func runPostStep(step actionStep) common.Executor {
return err
}
step.getCompositeRunContext(ctx).updateCompositeRunContext(ctx, step.getRunContext(), step)
return step.getCompositeSteps().post(ctx)
default:

View file

@ -3,37 +3,43 @@ package runner
import (
"context"
"fmt"
"regexp"
"strings"
"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/model"
)
func evaluteCompositeInputAndEnv(ctx context.Context, parent *RunContext, step actionStep) (inputs map[string]interface{}, env map[string]string) {
eval := parent.NewExpressionEvaluator(ctx)
inputs = make(map[string]interface{})
for k, input := range step.getActionModel().Inputs {
inputs[k] = eval.Interpolate(ctx, input.Default)
}
if step.getStepModel().With != nil {
for k, v := range step.getStepModel().With {
inputs[k] = eval.Interpolate(ctx, v)
func evaluateCompositeInputAndEnv(ctx context.Context, parent *RunContext, step actionStep) map[string]string {
env := make(map[string]string)
stepEnv := *step.getEnv()
for k, v := range stepEnv {
// do not set current inputs into composite action
// the required inputs are added in the second loop
if !strings.HasPrefix(k, "INPUT_") {
env[k] = v
}
}
env = make(map[string]string)
for k, v := range parent.Env {
env[k] = eval.Interpolate(ctx, v)
}
for k, v := range step.getStepModel().Environment() {
env[k] = eval.Interpolate(ctx, v)
for inputID, input := range step.getActionModel().Inputs {
envKey := regexp.MustCompile("[^A-Z0-9-]").ReplaceAllString(strings.ToUpper(inputID), "_")
envKey = fmt.Sprintf("INPUT_%s", strings.ToUpper(envKey))
// lookup if key is defined in the step but the the already
// evaluated value from the environment
_, defined := step.getStepModel().With[inputID]
if value, ok := stepEnv[envKey]; defined && ok {
env[envKey] = value
} else {
env[envKey] = input.Default
}
}
return inputs, env
return env
}
func newCompositeRunContext(ctx context.Context, parent *RunContext, step actionStep, actionPath string) *RunContext {
inputs, env := evaluteCompositeInputAndEnv(ctx, parent, step)
env := evaluateCompositeInputAndEnv(ctx, parent, step)
// run with the global config but without secrets
configCopy := *(parent.Config)
@ -52,57 +58,42 @@ func newCompositeRunContext(ctx context.Context, parent *RunContext, step action
},
},
},
Config: &configCopy,
StepResults: map[string]*model.StepResult{},
JobContainer: parent.JobContainer,
Inputs: inputs,
ActionPath: actionPath,
ActionRepository: parent.ActionRepository,
ActionRef: parent.ActionRef,
Env: env,
Masks: parent.Masks,
ExtraPath: parent.ExtraPath,
Parent: parent,
Config: &configCopy,
StepResults: map[string]*model.StepResult{},
JobContainer: parent.JobContainer,
ActionPath: actionPath,
Env: env,
Masks: parent.Masks,
ExtraPath: parent.ExtraPath,
Parent: parent,
}
return compositerc
}
// This updates a composite context inputs, env and masks.
// This is needed to re-evalute/update that context between pre/main/post steps.
// Some of the inputs/env may requires the results of in-between steps.
func (rc *RunContext) updateCompositeRunContext(ctx context.Context, parent *RunContext, step actionStep) {
inputs, env := evaluteCompositeInputAndEnv(ctx, parent, step)
rc.Inputs = inputs
rc.Env = env
rc.Masks = append(rc.Masks, parent.Masks...)
}
func execAsComposite(step actionStep) common.Executor {
rc := step.getRunContext()
action := step.getActionModel()
return func(ctx context.Context) error {
compositerc := step.getCompositeRunContext(ctx)
compositeRC := step.getCompositeRunContext(ctx)
steps := step.getCompositeSteps()
ctx = WithCompositeLogger(ctx, &compositerc.Masks)
ctx = WithCompositeLogger(ctx, &compositeRC.Masks)
compositerc.updateCompositeRunContext(ctx, rc, step)
err := steps.main(ctx)
// Map outputs from composite RunContext to job RunContext
eval := compositerc.NewExpressionEvaluator(ctx)
eval := compositeRC.NewExpressionEvaluator(ctx)
for outputName, output := range action.Outputs {
rc.setOutput(ctx, map[string]string{
"name": outputName,
}, eval.Interpolate(ctx, output.Value))
}
rc.Masks = append(rc.Masks, compositerc.Masks...)
rc.ExtraPath = compositerc.ExtraPath
rc.Masks = append(rc.Masks, compositeRC.Masks...)
rc.ExtraPath = compositeRC.ExtraPath
return err
}

View file

@ -187,10 +187,8 @@ func TestActionRunner(t *testing.T) {
Uses: "org/repo/path@ref",
},
RunContext: &RunContext{
ActionRepository: "org/repo",
ActionPath: "path",
ActionRef: "ref",
Config: &Config{},
ActionPath: "path",
Config: &Config{},
Run: &model.Run{
JobID: "job",
Workflow: &model.Workflow{

View file

@ -39,6 +39,13 @@ func (rc *RunContext) NewExpressionEvaluator(ctx context.Context) ExpressionEval
}
}
inputs := make(map[string]interface{})
for k, v := range rc.GetEnv() {
if strings.HasPrefix(k, "INPUT_") {
inputs[strings.ToLower(strings.TrimPrefix(k, "INPUT_"))] = v
}
}
ee := &exprparser.EvaluationEnvironment{
Github: rc.getGithubContext(ctx),
Env: rc.GetEnv(),
@ -56,7 +63,7 @@ func (rc *RunContext) NewExpressionEvaluator(ctx context.Context) ExpressionEval
Strategy: strategy,
Matrix: rc.Matrix,
Needs: using,
Inputs: rc.Inputs,
Inputs: inputs,
}
return expressionEvaluator{
interpreter: exprparser.NewInterpeter(ee, exprparser.Config{
@ -87,8 +94,15 @@ func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step)
}
}
inputs := make(map[string]interface{})
for k, v := range *step.getEnv() {
if strings.HasPrefix(k, "INPUT_") {
inputs[strings.ToLower(strings.TrimPrefix(k, "INPUT_"))] = v
}
}
ee := &exprparser.EvaluationEnvironment{
Github: rc.getGithubContext(ctx),
Github: step.getGithubContext(ctx),
Env: *step.getEnv(),
Job: rc.getJobContext(),
Steps: rc.getStepsContext(),
@ -104,7 +118,7 @@ func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step)
Needs: using,
// todo: should be unavailable
// but required to interpolate/evaluate the inputs in actions/composite
Inputs: rc.Inputs,
Inputs: inputs,
}
return expressionEvaluator{
interpreter: exprparser.NewInterpeter(ee, exprparser.Config{

View file

@ -27,25 +27,22 @@ const ActPath string = "/var/run/act"
// RunContext contains info about current job
type RunContext struct {
Name string
Config *Config
Matrix map[string]interface{}
Run *model.Run
EventJSON string
Env map[string]string
ExtraPath []string
CurrentStep string
StepResults map[string]*model.StepResult
ExprEval ExpressionEvaluator
JobContainer container.Container
OutputMappings map[MappableOutput]MappableOutput
JobName string
ActionPath string
ActionRef string
ActionRepository string
Inputs map[string]interface{}
Parent *RunContext
Masks []string
Name string
Config *Config
Matrix map[string]interface{}
Run *model.Run
EventJSON string
Env map[string]string
ExtraPath []string
CurrentStep string
StepResults map[string]*model.StepResult
ExprEval ExpressionEvaluator
JobContainer container.Container
OutputMappings map[MappableOutput]MappableOutput
JobName string
ActionPath string
Parent *RunContext
Masks []string
}
func (rc *RunContext) AddMask(mask string) {
@ -438,8 +435,6 @@ func (rc *RunContext) getGithubContext(ctx context.Context) *model.GithubContext
Action: rc.CurrentStep,
Token: rc.Config.Token,
ActionPath: rc.ActionPath,
ActionRef: rc.ActionRef,
ActionRepository: rc.ActionRepository,
RepositoryOwner: rc.Config.Env["GITHUB_REPOSITORY_OWNER"],
RetentionDays: rc.Config.Env["GITHUB_RETENTION_DAYS"],
RunnerPerflog: rc.Config.Env["RUNNER_PERFLOG"],
@ -557,8 +552,7 @@ func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{})
}
}
func (rc *RunContext) withGithubEnv(ctx context.Context, env map[string]string) map[string]string {
github := rc.getGithubContext(ctx)
func (rc *RunContext) withGithubEnv(ctx context.Context, github *model.GithubContext, env map[string]string) map[string]string {
env["CI"] = "true"
env["GITHUB_ENV"] = ActPath + "/workflow/envs.txt"
env["GITHUB_PATH"] = ActPath + "/workflow/paths.txt"

View file

@ -16,6 +16,7 @@ type step interface {
post() common.Executor
getRunContext() *RunContext
getGithubContext(ctx context.Context) *model.GithubContext
getStepModel() *model.Step
getEnv() *map[string]string
getIfExpression(context context.Context, stage stepStage) string
@ -136,9 +137,10 @@ func setupEnv(ctx context.Context, step step) error {
if err != nil {
return err
}
mergeIntoMap(step.getEnv(), step.getStepModel().GetEnv()) // step env should not be overwritten
// merge step env last, since it should not be overwritten
mergeIntoMap(step.getEnv(), step.getStepModel().GetEnv())
exprEval := rc.NewStepExpressionEvaluator(ctx, step)
exprEval := rc.NewExpressionEvaluator(ctx)
for k, v := range *step.getEnv() {
(*step.getEnv())[k] = exprEval.Interpolate(ctx, v)
}
@ -169,7 +171,7 @@ func mergeEnv(ctx context.Context, step step) {
(*env)["PATH"] += `:` + p
}
mergeIntoMap(env, rc.withGithubEnv(ctx, *env))
rc.withGithubEnv(ctx, step.getGithubContext(ctx), *env)
}
func isStepEnabled(ctx context.Context, expr string, step step, stage stepStage) (bool, error) {

View file

@ -4,7 +4,6 @@ import (
"archive/tar"
"context"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
@ -55,7 +54,7 @@ func (sal *stepActionLocal) main() common.Executor {
}
}
actionModel, err := sal.readAction(ctx, sal.Step, actionDir, "", localReader(ctx), ioutil.WriteFile)
actionModel, err := sal.readAction(ctx, sal.Step, actionDir, "", localReader(ctx), os.WriteFile)
if err != nil {
return err
}
@ -74,6 +73,10 @@ func (sal *stepActionLocal) getRunContext() *RunContext {
return sal.RunContext
}
func (sal *stepActionLocal) getGithubContext(ctx context.Context) *model.GithubContext {
return sal.getRunContext().getGithubContext(ctx)
}
func (sal *stepActionLocal) getStepModel() *model.Step {
return sal.Step
}

View file

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
@ -49,7 +48,7 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
sar.remoteAction.URL = sar.RunContext.Config.GitHubInstance
github := sar.RunContext.getGithubContext(ctx)
github := sar.getGithubContext(ctx)
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
@ -92,14 +91,10 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
return common.NewPipelineExecutor(
ntErr,
func(ctx context.Context) error {
actionModel, err := sar.readAction(ctx, sar.Step, actionDir, sar.remoteAction.Path, remoteReader(ctx), ioutil.WriteFile)
actionModel, err := sar.readAction(ctx, sar.Step, actionDir, sar.remoteAction.Path, remoteReader(ctx), os.WriteFile)
sar.action = actionModel
return err
},
func(ctx context.Context) error {
sar.RunContext.setupActionInputs(ctx, sar)
return nil
},
)(ctx)
}
}
@ -116,7 +111,7 @@ func (sar *stepActionRemote) main() common.Executor {
return common.NewPipelineExecutor(
sar.prepareActionExecutor(),
runStepExecutor(sar, stepStageMain, func(ctx context.Context) error {
github := sar.RunContext.getGithubContext(ctx)
github := sar.getGithubContext(ctx)
if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
if sar.RunContext.Config.BindWorkdir {
common.Logger(ctx).Debugf("Skipping local actions/checkout because you bound your workspace")
@ -129,9 +124,7 @@ func (sar *stepActionRemote) main() common.Executor {
actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), strings.ReplaceAll(sar.Step.Uses, "/", "-"))
return common.NewPipelineExecutor(
sar.runAction(sar, actionDir, sar.remoteAction),
)(ctx)
return sar.runAction(sar, actionDir, sar.remoteAction)(ctx)
}),
)
}
@ -144,6 +137,19 @@ func (sar *stepActionRemote) getRunContext() *RunContext {
return sar.RunContext
}
func (sar *stepActionRemote) getGithubContext(ctx context.Context) *model.GithubContext {
ghc := sar.getRunContext().getGithubContext(ctx)
// extend github context if we already have an initialized remoteAction
remoteAction := sar.remoteAction
if remoteAction != nil {
ghc.ActionRepository = fmt.Sprintf("%s/%s", remoteAction.Org, remoteAction.Repo)
ghc.ActionRef = remoteAction.Ref
}
return ghc
}
func (sar *stepActionRemote) getStepModel() *model.Step {
return sar.Step
}
@ -155,7 +161,7 @@ func (sar *stepActionRemote) getEnv() *map[string]string {
func (sar *stepActionRemote) getIfExpression(ctx context.Context, stage stepStage) string {
switch stage {
case stepStagePre:
github := sar.RunContext.getGithubContext(ctx)
github := sar.getGithubContext(ctx)
if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout {
// skip local checkout pre step
return "false"

View file

@ -39,6 +39,10 @@ func (sd *stepDocker) getRunContext() *RunContext {
return sd.RunContext
}
func (sd *stepDocker) getGithubContext(ctx context.Context) *model.GithubContext {
return sd.getRunContext().getGithubContext(ctx)
}
func (sd *stepDocker) getStepModel() *model.Step {
return sd.Step
}

View file

@ -45,6 +45,10 @@ func (sr *stepRun) getRunContext() *RunContext {
return sr.RunContext
}
func (sr *stepRun) getGithubContext(ctx context.Context) *model.GithubContext {
return sr.getRunContext().getGithubContext(ctx)
}
func (sr *stepRun) getStepModel() *model.Step {
return sr.Step
}

View file

@ -94,6 +94,11 @@ func (sm *stepMock) getRunContext() *RunContext {
return args.Get(0).(*RunContext)
}
func (sm *stepMock) getGithubContext(ctx context.Context) *model.GithubContext {
args := sm.Called()
return args.Get(0).(*RunContext).getGithubContext(ctx)
}
func (sm *stepMock) getStepModel() *model.Step {
args := sm.Called()
return args.Get(0).(*model.Step)
@ -142,6 +147,7 @@ func TestSetupEnv(t *testing.T) {
}
sm.On("getRunContext").Return(rc)
sm.On("getGithubContext").Return(rc)
sm.On("getStepModel").Return(step)
sm.On("getEnv").Return(&env)

View file

@ -0,0 +1,19 @@
name: "action"
description: "action"
inputs:
some:
description: "some input"
required: true
outputs:
out:
description: "some output"
value: "output value"
runs:
using: "composite"
steps:
- run: |
echo "action input=${{ inputs.some }}"
[[ "${{ inputs.some == 'value' }}" = "true" ]] || exit 1
shell: bash

View file

@ -0,0 +1,22 @@
name: "composite"
description: "composite"
inputs:
composite-input:
description: "value"
required: true
runs:
using: "composite"
steps:
- name: test input value
run: |
echo "input value 1=${{ inputs.composite-input }}"
[[ "${{ inputs.composite-input == 'value' }}" = "true" ]] || exit 1
shell: bash
- uses: nektos/act-test-actions/js@main
- name: test input value again
run: |
echo "input value 2=${{ inputs.composite-input }}"
[[ "${{ inputs.composite-input == 'value' }}" = "true" ]] || exit 1
shell: bash

View file

@ -0,0 +1,30 @@
name: push
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: use simple composite action
uses: ./uses-composite-with-inputs/action
with:
some: value
- name: use nested composite action
uses: ./uses-composite-with-inputs/composite
with:
composite-input: value
###
#
# Remote composite test
#
- name: use remote composite action
id: remote-composite
uses: nektos/act-test-actions/composite@main
with:
input: value
- name: test remote composite output
run: |
echo "steps.remote-composite.outputs.output=${{ steps.remote-composite.outputs.output }}"
[[ "${{ steps.remote-composite.outputs.output == 'value' }}" = "true" ]] || exit 1
#
###

View file

@ -35,7 +35,8 @@ outputs:
runs:
using: "composite"
steps:
- run: |
- name: echo inputs
run: |
echo "#####################################"
echo "Inputs:"
echo "---"

View file

@ -46,7 +46,8 @@ runs:
[[ "${{steps.composite.outputs.secret_output == format('{0}/{0}', inputs.test_input_optional)}}" = "true" ]] || exit 1
shell: bash
# Now test again with default values
- uses: ./uses-composite/composite_action
- name: ./uses-composite/composite_action with defaults
uses: ./uses-composite/composite_action
id: composite2
with:
test_input_required: 'test_input_required_value'