fix: update output handling for reusable workflows (#1521)
* fix: map job output for reusable workflows This fixes the job outputs for reusable workflows. There is a required indirection. Before this we took the outputs from all jobs which is not what users express with the workflow outputs. * fix: remove double evaluation --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
53095d76f4
commit
89cb558558
5 changed files with 35 additions and 4 deletions
|
@ -15,6 +15,7 @@ type EvaluationEnvironment struct {
|
||||||
Github *model.GithubContext
|
Github *model.GithubContext
|
||||||
Env map[string]string
|
Env map[string]string
|
||||||
Job *model.JobContext
|
Job *model.JobContext
|
||||||
|
Jobs *map[string]*model.WorkflowCallResult
|
||||||
Steps map[string]*model.StepResult
|
Steps map[string]*model.StepResult
|
||||||
Runner map[string]interface{}
|
Runner map[string]interface{}
|
||||||
Secrets map[string]string
|
Secrets map[string]string
|
||||||
|
@ -155,6 +156,11 @@ func (impl *interperterImpl) evaluateVariable(variableNode *actionlint.VariableN
|
||||||
return impl.env.Env, nil
|
return impl.env.Env, nil
|
||||||
case "job":
|
case "job":
|
||||||
return impl.env.Job, nil
|
return impl.env.Job, nil
|
||||||
|
case "jobs":
|
||||||
|
if impl.env.Jobs == nil {
|
||||||
|
return nil, fmt.Errorf("Unavailable context: jobs")
|
||||||
|
}
|
||||||
|
return impl.env.Jobs, nil
|
||||||
case "steps":
|
case "steps":
|
||||||
return impl.env.Steps, nil
|
return impl.env.Steps, nil
|
||||||
case "runner":
|
case "runner":
|
||||||
|
|
|
@ -117,6 +117,10 @@ type WorkflowCall struct {
|
||||||
Outputs map[string]WorkflowCallOutput `yaml:"outputs"`
|
Outputs map[string]WorkflowCallOutput `yaml:"outputs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WorkflowCallResult struct {
|
||||||
|
Outputs map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Workflow) WorkflowCallConfig() *WorkflowCall {
|
func (w *Workflow) WorkflowCallConfig() *WorkflowCall {
|
||||||
if w.RawOn.Kind != yaml.MappingNode {
|
if w.RawOn.Kind != yaml.MappingNode {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -25,6 +25,8 @@ func (rc *RunContext) NewExpressionEvaluator(ctx context.Context) ExpressionEval
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map[string]string) ExpressionEvaluator {
|
func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map[string]string) ExpressionEvaluator {
|
||||||
|
var workflowCallResult map[string]*model.WorkflowCallResult
|
||||||
|
|
||||||
// todo: cleanup EvaluationEnvironment creation
|
// todo: cleanup EvaluationEnvironment creation
|
||||||
using := make(map[string]exprparser.Needs)
|
using := make(map[string]exprparser.Needs)
|
||||||
strategy := make(map[string]interface{})
|
strategy := make(map[string]interface{})
|
||||||
|
@ -44,6 +46,23 @@ func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map
|
||||||
Result: jobs[needs].Result,
|
Result: jobs[needs].Result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only setup jobs context in case of workflow_call
|
||||||
|
// and existing expression evaluator (this means, jobs are at
|
||||||
|
// least ready to run)
|
||||||
|
if rc.caller != nil && rc.ExprEval != nil {
|
||||||
|
workflowCallResult = map[string]*model.WorkflowCallResult{}
|
||||||
|
|
||||||
|
for jobName, job := range jobs {
|
||||||
|
result := model.WorkflowCallResult{
|
||||||
|
Outputs: map[string]string{},
|
||||||
|
}
|
||||||
|
for k, v := range job.Outputs {
|
||||||
|
result.Outputs[k] = v
|
||||||
|
}
|
||||||
|
workflowCallResult[jobName] = &result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ghc := rc.getGithubContext(ctx)
|
ghc := rc.getGithubContext(ctx)
|
||||||
|
@ -53,6 +72,7 @@ func (rc *RunContext) NewExpressionEvaluatorWithEnv(ctx context.Context, env map
|
||||||
Github: ghc,
|
Github: ghc,
|
||||||
Env: env,
|
Env: env,
|
||||||
Job: rc.getJobContext(),
|
Job: rc.getJobContext(),
|
||||||
|
Jobs: &workflowCallResult,
|
||||||
// todo: should be unavailable
|
// todo: should be unavailable
|
||||||
// but required to interpolate/evaluate the step outputs on the job
|
// but required to interpolate/evaluate the step outputs on the job
|
||||||
Steps: rc.getStepsContext(),
|
Steps: rc.getStepsContext(),
|
||||||
|
|
|
@ -162,8 +162,9 @@ func setJobOutputs(ctx context.Context, rc *RunContext) {
|
||||||
callerOutputs := make(map[string]string)
|
callerOutputs := make(map[string]string)
|
||||||
|
|
||||||
ee := rc.NewExpressionEvaluator(ctx)
|
ee := rc.NewExpressionEvaluator(ctx)
|
||||||
for k, v := range rc.Run.Job().Outputs {
|
|
||||||
callerOutputs[k] = ee.Interpolate(ctx, v)
|
for k, v := range rc.Run.Workflow.WorkflowCallConfig().Outputs {
|
||||||
|
callerOutputs[k] = ee.Interpolate(ctx, ee.Interpolate(ctx, v.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.caller.runContext.Run.Job().Outputs = callerOutputs
|
rc.caller.runContext.Run.Job().Outputs = callerOutputs
|
||||||
|
|
|
@ -27,7 +27,7 @@ on:
|
||||||
outputs:
|
outputs:
|
||||||
output:
|
output:
|
||||||
description: "A workflow output"
|
description: "A workflow output"
|
||||||
value: ${{ jobs.reusable_workflow_job.outputs.output }}
|
value: ${{ jobs.reusable_workflow_job.outputs.job-output }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
reusable_workflow_job:
|
reusable_workflow_job:
|
||||||
|
@ -79,4 +79,4 @@ jobs:
|
||||||
echo "value=${{ inputs.string_required }}" >> $GITHUB_OUTPUT
|
echo "value=${{ inputs.string_required }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
output: ${{ steps.output_test.outputs.value }}
|
job-output: ${{ steps.output_test.outputs.value }}
|
||||||
|
|
Loading…
Reference in a new issue