Mapping workflow_dispatch
inputs into the Expression inputs
context (#1363)
* test: check workflow_dispatch inputs This implements a test to check for `workflow_dispatch` inputs. This will be a prerequisite for implementing the inputs. * feat: map workflow_dispatch input to expression evaluator This changes adds the workflow_dispatch event inputs to the `inputs` context and maintaining the boolean type * fix: coerce boolean input types * fix: use step env if available, rc env otherwise
This commit is contained in:
parent
5d2eb1f238
commit
1e0ef8ce69
5 changed files with 136 additions and 14 deletions
|
@ -55,6 +55,51 @@ func (w *Workflow) On() []string {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *Workflow) OnEvent(event string) interface{} {
|
||||
if w.RawOn.Kind == yaml.MappingNode {
|
||||
var val map[string]interface{}
|
||||
err := w.RawOn.Decode(&val)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return val[event]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type WorkflowDispatchInput struct {
|
||||
Description string `yaml:"description"`
|
||||
Required bool `yaml:"required"`
|
||||
Default string `yaml:"default"`
|
||||
Type string `yaml:"type"`
|
||||
Options []string `yaml:"options"`
|
||||
}
|
||||
|
||||
type WorkflowDispatch struct {
|
||||
Inputs map[string]WorkflowDispatchInput `yaml:"inputs"`
|
||||
}
|
||||
|
||||
func (w *Workflow) WorkflowDispatchConfig() *WorkflowDispatch {
|
||||
if w.RawOn.Kind != yaml.MappingNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
var val map[string]yaml.Node
|
||||
err := w.RawOn.Decode(&val)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var config WorkflowDispatch
|
||||
node := val["workflow_dispatch"]
|
||||
err = node.Decode(&config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &config
|
||||
}
|
||||
|
||||
// Job is the structure of one job in a workflow
|
||||
type Job struct {
|
||||
Name string `yaml:"name"`
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/exprparser"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
@ -39,15 +40,11 @@ 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
|
||||
}
|
||||
}
|
||||
ghc := rc.getGithubContext(ctx)
|
||||
inputs := getEvaluatorInputs(ctx, rc, nil, ghc)
|
||||
|
||||
ee := &exprparser.EvaluationEnvironment{
|
||||
Github: rc.getGithubContext(ctx),
|
||||
Github: ghc,
|
||||
Env: rc.GetEnv(),
|
||||
Job: rc.getJobContext(),
|
||||
// todo: should be unavailable
|
||||
|
@ -94,12 +91,8 @@ 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
|
||||
}
|
||||
}
|
||||
ghc := rc.getGithubContext(ctx)
|
||||
inputs := getEvaluatorInputs(ctx, rc, step, ghc)
|
||||
|
||||
ee := &exprparser.EvaluationEnvironment{
|
||||
Github: step.getGithubContext(ctx),
|
||||
|
@ -319,3 +312,37 @@ func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (str
|
|||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func getEvaluatorInputs(ctx context.Context, rc *RunContext, step step, ghc *model.GithubContext) map[string]interface{} {
|
||||
inputs := map[string]interface{}{}
|
||||
|
||||
var env map[string]string
|
||||
if step != nil {
|
||||
env = *step.getEnv()
|
||||
} else {
|
||||
env = rc.GetEnv()
|
||||
}
|
||||
|
||||
for k, v := range env {
|
||||
if strings.HasPrefix(k, "INPUT_") {
|
||||
inputs[strings.ToLower(strings.TrimPrefix(k, "INPUT_"))] = v
|
||||
}
|
||||
}
|
||||
|
||||
if ghc.EventName == "workflow_dispatch" {
|
||||
config := rc.Run.Workflow.WorkflowDispatchConfig()
|
||||
for k, v := range config.Inputs {
|
||||
value := nestedMapLookup(ghc.Event, "inputs", k)
|
||||
if value == nil {
|
||||
value = v.Default
|
||||
}
|
||||
if v.Type == "boolean" {
|
||||
inputs[k] = value == "true"
|
||||
} else {
|
||||
inputs[k] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputs
|
||||
}
|
||||
|
|
|
@ -182,6 +182,7 @@ func TestRunEvent(t *testing.T) {
|
|||
{workdir, "uses-action-with-pre-and-post-step", "push", "", platforms},
|
||||
{workdir, "evalenv", "push", "", platforms},
|
||||
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", platforms},
|
||||
{workdir, "workflow_dispatch", "workflow_dispatch", "", 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},
|
||||
|
@ -189,7 +190,14 @@ func TestRunEvent(t *testing.T) {
|
|||
|
||||
for _, table := range tables {
|
||||
t.Run(table.workflowPath, func(t *testing.T) {
|
||||
table.runTest(ctx, t, &Config{})
|
||||
config := &Config{}
|
||||
|
||||
eventFile := filepath.Join(workdir, table.workflowPath, "event.json")
|
||||
if _, err := os.Stat(eventFile); err == nil {
|
||||
config.EventPath = eventFile
|
||||
}
|
||||
|
||||
table.runTest(ctx, t, config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
6
pkg/runner/testdata/workflow_dispatch/event.json
vendored
Normal file
6
pkg/runner/testdata/workflow_dispatch/event.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"inputs": {
|
||||
"required": "required input",
|
||||
"boolean": "true"
|
||||
}
|
||||
}
|
36
pkg/runner/testdata/workflow_dispatch/workflow_dispatch.yml
vendored
Normal file
36
pkg/runner/testdata/workflow_dispatch/workflow_dispatch.yml
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
name: workflow_dispatch
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
required:
|
||||
description: a required input
|
||||
required: true
|
||||
with_default:
|
||||
description: an input with default
|
||||
required: false
|
||||
default: default
|
||||
boolean:
|
||||
description: an input of type boolean
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: test required input
|
||||
run: |
|
||||
echo input.required=${{ inputs.required }}
|
||||
[[ "${{ inputs.required }}" = "required input" ]] || exit 1
|
||||
- name: test input with default
|
||||
run: |
|
||||
echo input.with_default=${{ inputs.with_default }}
|
||||
[[ "${{ inputs.with_default }}" = "default" ]] || exit 1
|
||||
- id: boolean-test
|
||||
name: run on boolean input
|
||||
if: ${{ inputs.boolean == true }}
|
||||
run: echo "::set-output name=value::executed"
|
||||
- name: has boolean test?
|
||||
run: |
|
||||
[[ "${{ steps.boolean-test.outputs.value }}" = "executed" ]] || exit 1
|
Loading…
Reference in a new issue