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
@ -55,6 +55,51 @@ func (w *Workflow) On() []string {
|
|||||||
return nil
|
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
|
// Job is the structure of one job in a workflow
|
||||||
type Job struct {
|
type Job struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/nektos/act/pkg/common"
|
"github.com/nektos/act/pkg/common"
|
||||||
"github.com/nektos/act/pkg/container"
|
"github.com/nektos/act/pkg/container"
|
||||||
"github.com/nektos/act/pkg/exprparser"
|
"github.com/nektos/act/pkg/exprparser"
|
||||||
|
"github.com/nektos/act/pkg/model"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,15 +40,11 @@ func (rc *RunContext) NewExpressionEvaluator(ctx context.Context) ExpressionEval
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs := make(map[string]interface{})
|
ghc := rc.getGithubContext(ctx)
|
||||||
for k, v := range rc.GetEnv() {
|
inputs := getEvaluatorInputs(ctx, rc, nil, ghc)
|
||||||
if strings.HasPrefix(k, "INPUT_") {
|
|
||||||
inputs[strings.ToLower(strings.TrimPrefix(k, "INPUT_"))] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ee := &exprparser.EvaluationEnvironment{
|
ee := &exprparser.EvaluationEnvironment{
|
||||||
Github: rc.getGithubContext(ctx),
|
Github: ghc,
|
||||||
Env: rc.GetEnv(),
|
Env: rc.GetEnv(),
|
||||||
Job: rc.getJobContext(),
|
Job: rc.getJobContext(),
|
||||||
// todo: should be unavailable
|
// todo: should be unavailable
|
||||||
@ -94,12 +91,8 @@ func (rc *RunContext) NewStepExpressionEvaluator(ctx context.Context, step step)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs := make(map[string]interface{})
|
ghc := rc.getGithubContext(ctx)
|
||||||
for k, v := range *step.getEnv() {
|
inputs := getEvaluatorInputs(ctx, rc, step, ghc)
|
||||||
if strings.HasPrefix(k, "INPUT_") {
|
|
||||||
inputs[strings.ToLower(strings.TrimPrefix(k, "INPUT_"))] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ee := &exprparser.EvaluationEnvironment{
|
ee := &exprparser.EvaluationEnvironment{
|
||||||
Github: step.getGithubContext(ctx),
|
Github: step.getGithubContext(ctx),
|
||||||
@ -319,3 +312,37 @@ func rewriteSubExpression(ctx context.Context, in string, forceFormat bool) (str
|
|||||||
}
|
}
|
||||||
return out, nil
|
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, "uses-action-with-pre-and-post-step", "push", "", platforms},
|
||||||
{workdir, "evalenv", "push", "", platforms},
|
{workdir, "evalenv", "push", "", platforms},
|
||||||
{workdir, "ensure-post-steps", "push", "Job 'second-post-step-should-fail' failed", 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
|
{"../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
|
// {"testdata", "issue-228", "push", "", platforms, }, // TODO [igni]: Remove this once everything passes
|
||||||
{"../model/testdata", "container-volumes", "push", "", platforms},
|
{"../model/testdata", "container-volumes", "push", "", platforms},
|
||||||
@ -189,7 +190,14 @@ func TestRunEvent(t *testing.T) {
|
|||||||
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
t.Run(table.workflowPath, func(t *testing.T) {
|
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 New Issue
Block a user