Feat: $( ) conditions in a step's if, and a step's status under steps
Merged
BR
7d ago+1200+1200+0123456789012345678901234567890123456789-59-59-01234567890123456789
Description of your changes
Merge after #271, which this branch sits on. Only the last three commits are this PR's.
A step's if is CUE today. With this PR, an if that is exactly one $( ) expression is CEL, evaluated with the same roots as the step's properties (#271), in a run that resolves expressions.
steps:
- name: notify
type: notification
if: '$(steps.build.failed && context.name != "dev")'
- name: cleanup
type: cleanup
if: status.build.failed # CUE, as before
- Nothing that works changes. Only a value that is entirely one
$( )is CEL.always, an emptyif, any CUE expression, and a$(inside a CUE string are unchanged. A whole$(...)never worked as CUE ($is an identifier, so$(x)is a call to something undefined), and in a run without expressions enabled a$( )ifstill fails as it does today. - Roots:
context,steps,eachand the host's roots, as in properties. A condition must be a bool. steps.<name>now offers a finished step's status:phase,message,reason,succeeded,failed,skipped,timeout,failedAfterRetries,terminate, asstatus.<name>does in a CUEif. A read waits while the step has still to finish; it works in properties too.- Ordering: a condition's reads of
stepsare implieddependsOn, as a property's are (#271), soif: $(steps.build.failed)waits forbuildwithout a writtendependsOn. - Loops: a loop step's own
ifapplies to the loop as a whole, decided once, so it cannot readeach; a looping group's sub-steps'ifis decided each pass, with that pass'seach. - Waiting: a read still to be answered holds the step back, running, rather than skipping it.
- Admission: the webhook checks a
$( )ifas it does properties: roots, the steps and outputs it reads, the order it can wait in, cycles,eachplacement. - The executor's pre-check hook cannot reach the evaluator, so the runners (a step, a step group, a loop) pass the condition in
PreCheckOptions.Condition;PreCheckResult.Waitcarries the wait.PropertyEvaluatorgainsCondition.
I have:
- Read and followed KubeVela's pull request process.
- All commits are signed off (DCO).
- Related Docs updated properly: to follow.
- Run
make reviewableto ensure this PR is ready for review: golangci-lint and staticcheck with Go 1.27, 0 issues.
How has this code been tested
- Unit: condition detection;
Condition(true, false, every root, a host root given the run, not a bool, waiting); each status field in each finished phase, waiting while running or retrying, a loop's status from outside and inside, a step reading its own status; the executor's decision (true, false, wait, error, CUE left to CUE, no condition offered); a group's and a loop's condition (skip, wait, CUE, no expressions; a loop's decided once); validation of conditions and their implied dependencies. - e2e (
e2e/expressions_test.go) on k3d: a WorkflowRun where a true condition runs its step, a false one skips it, a looping group's sub-step is decided each pass, a CUEifbeside them is unchanged, and the reads are recorded as dependencies. With the condition switched off in the executor, the spec fails.
Summary by cubic
Step properties, forEach.items, and a step's if can now be $( ) CEL expressions, resolved when the step runs with the same roots as its properties (context, steps, the host's roots, and each inside a loop pass).
steps.<name>.outputs.<output>reads an output the step declares;steps.<name>exposes a finished step's status (phase,succeeded,failed,skipped,timeout, and so on). A read waits while the step has still to finish; a failed step fails the read, and a skipped step is read as skipped — its status never changes, and its outputs read as absent.- Reads of
stepsare implieddependsOn, which—unlike a written one—does not skip the reader when the step read is skipped. The run status records what each step waits on and whetherdependsOnor an expression implied it; restarting a step also restarts every step that reads it. - A whole
$( )ifis CEL only when the run opts in via theEnableCelExpressionsfeature gate and theworkflowrun.oam.dev/cel-expressionsannotation; any otherif, including$( )inside CUE text, stays CUE. An escaped$(still counts as an expression. - A loop step's own
ifis decided once for the loop and cannot readeach; a looping group's sub-stepifis decided each pass, and a group whose condition errors runs none of its sub-steps. - Admission checks every expression without evaluating it and refuses a read that does not compile, names a step or output that does not exist, uses
eachoutside a forEach body, or could never be answered (the reader itself, a later step in order, a cycle), plus reads below a status field.forEach.itemsmust be one sole expression; text around it is refused.