mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-08-07 09:24:22 +02:00
fix: evaluate each ${{ }} part on its own (#1146)
Every `${{ }}` part was spliced as raw text into a synthesized `format('...', <raw>)` call and re-parsed, so unbalanced parentheses restructured the whole expression:
```yaml
run: echo ${{ 1) && (2 }} # panicked with "did not evaluate to a string"
if: ${{ 1 }} ${{ 0) && (0 }} # silently skipped the step
```
One scanner shaped like GitHub's template reader now splits every value, and each part is evaluated on its own, so nothing builds an expression out of text.
An empty input to `Evaluate` asked `success()` whatever the caller requested, so a post step running under `always()` asked the wrong question.
Same fix as https://github.com/go-gitea/gitea/pull/38754 on the Gitea side.
Written by Claude Opus 5.
---------
Co-authored-by: bircni <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1146
Reviewed-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -85,37 +85,26 @@ func NewInterpeter(env *EvaluationEnvironment, config Config) Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate evaluates one expression. An empty input asks defaultStatusCheck on its own, which is
|
||||
// what a value that carries no expression of its own runs under.
|
||||
func (impl *interperterImpl) Evaluate(input string, defaultStatusCheck DefaultStatusCheck) (any, error) {
|
||||
input = strings.TrimPrefix(input, "${{")
|
||||
if defaultStatusCheck != DefaultStatusCheckNone && input == "" {
|
||||
input = "success()"
|
||||
|
||||
if input == "" && defaultStatusCheck != DefaultStatusCheckNone {
|
||||
return impl.evaluateNode(statusCheckNode(defaultStatusCheck))
|
||||
}
|
||||
|
||||
parser := actionlint.NewExprParser()
|
||||
exprNode, err := parser.Parse(actionlint.NewExprLexer(input + "}}"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse: %s", err.Message)
|
||||
}
|
||||
|
||||
if defaultStatusCheck != DefaultStatusCheckNone {
|
||||
hasStatusCheckFunction := false
|
||||
actionlint.VisitExprNode(exprNode, func(node, _ actionlint.ExprNode, entering bool) {
|
||||
if funcCallNode, ok := node.(*actionlint.FuncCallNode); entering && ok {
|
||||
switch strings.ToLower(funcCallNode.Callee) {
|
||||
case "success", "always", "cancelled", "failure":
|
||||
hasStatusCheckFunction = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if !hasStatusCheckFunction {
|
||||
exprNode = &actionlint.LogicalOpNode{
|
||||
Kind: actionlint.LogicalOpNodeKindAnd,
|
||||
Left: &actionlint.FuncCallNode{
|
||||
Callee: defaultStatusCheck.String(),
|
||||
Args: []actionlint.ExprNode{},
|
||||
},
|
||||
Right: exprNode,
|
||||
}
|
||||
if defaultStatusCheck != DefaultStatusCheckNone && !CallsStatusFunction(exprNode) {
|
||||
exprNode = &actionlint.LogicalOpNode{
|
||||
Kind: actionlint.LogicalOpNodeKindAnd,
|
||||
Left: statusCheckNode(defaultStatusCheck),
|
||||
Right: exprNode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +113,25 @@ func (impl *interperterImpl) Evaluate(input string, defaultStatusCheck DefaultSt
|
||||
return result, err2
|
||||
}
|
||||
|
||||
func statusCheckNode(defaultStatusCheck DefaultStatusCheck) *actionlint.FuncCallNode {
|
||||
return &actionlint.FuncCallNode{Callee: defaultStatusCheck.String(), Args: []actionlint.ExprNode{}}
|
||||
}
|
||||
|
||||
// CallsStatusFunction reports whether the expression calls a status function, which counts as the
|
||||
// expression asking its own status question instead of the default one.
|
||||
func CallsStatusFunction(exprNode actionlint.ExprNode) bool {
|
||||
found := false
|
||||
actionlint.VisitExprNode(exprNode, func(node, _ actionlint.ExprNode, entering bool) {
|
||||
if funcCallNode, ok := node.(*actionlint.FuncCallNode); entering && ok {
|
||||
switch strings.ToLower(funcCallNode.Callee) {
|
||||
case "success", "always", "cancelled", "failure":
|
||||
found = true
|
||||
}
|
||||
}
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
func (impl *interperterImpl) evaluateNode(exprNode actionlint.ExprNode) (any, error) {
|
||||
switch node := exprNode.(type) {
|
||||
case *actionlint.VariableNode:
|
||||
|
||||
Reference in New Issue
Block a user