mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-28 13:25:47 +00:00
fix(actions): keep step-level continue-on-error expressions unevaluated (#39141)
Signed-off-by: silverwind <me@silverwind.io> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/actionslib/pkg/expreval"
|
||||
"gitea.dev/actionslib/pkg/exprparser"
|
||||
@@ -175,17 +176,29 @@ func (j *Job) RunsOn() []string {
|
||||
}
|
||||
|
||||
type Step struct {
|
||||
ID string `yaml:"id,omitempty"`
|
||||
If yaml.Node `yaml:"if,omitempty"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Uses string `yaml:"uses,omitempty"`
|
||||
Run string `yaml:"run,omitempty"`
|
||||
WorkingDirectory string `yaml:"working-directory,omitempty"`
|
||||
Shell string `yaml:"shell,omitempty"`
|
||||
Env yaml.Node `yaml:"env,omitempty"`
|
||||
With map[string]string `yaml:"with,omitempty"`
|
||||
ContinueOnError bool `yaml:"continue-on-error,omitempty"`
|
||||
TimeoutMinutes string `yaml:"timeout-minutes,omitempty"`
|
||||
ID string `yaml:"id,omitempty"`
|
||||
If yaml.Node `yaml:"if,omitempty"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Uses string `yaml:"uses,omitempty"`
|
||||
Run string `yaml:"run,omitempty"`
|
||||
WorkingDirectory string `yaml:"working-directory,omitempty"`
|
||||
Shell string `yaml:"shell,omitempty"`
|
||||
Env yaml.Node `yaml:"env,omitempty"`
|
||||
With map[string]string `yaml:"with,omitempty"`
|
||||
RawContinueOnError yaml.Node `yaml:"continue-on-error,omitempty"` // raw: the runner evaluates it with the steps context
|
||||
TimeoutMinutes string `yaml:"timeout-minutes,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML canonicalizes booleans like continue-on-error
|
||||
func (s *Step) UnmarshalYAML(node *yaml.Node) error {
|
||||
type rawStep Step
|
||||
if err := node.Decode((*rawStep)(s)); err != nil {
|
||||
return err
|
||||
}
|
||||
if raw := &s.RawContinueOnError; raw.Tag == "!!bool" {
|
||||
raw.Value = strings.ToLower(raw.Value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String gets the name of step
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
package jobparser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/actionslib/pkg/model"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -62,3 +65,42 @@ jobs:
|
||||
require.Len(t, gotJob.Steps, 1)
|
||||
require.Equal(t, wantRun, gotJob.Steps[0].Run, "round-trip must preserve run content; got payload:\n%s", payload)
|
||||
}
|
||||
|
||||
// Typing a step's continue-on-error as a bool used to reject the whole `jobs:` node.
|
||||
func TestSingleWorkflowRoundTripStepContinueOnError(t *testing.T) {
|
||||
const wf = `name: demo
|
||||
on: push
|
||||
jobs:
|
||||
job1:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: quarantine
|
||||
run: echo "q=true" >> "$GITHUB_OUTPUT"
|
||||
- run: exit 1
|
||||
continue-on-error: ${{ steps.quarantine.outputs.q == 'true' }}
|
||||
- run: exit 1
|
||||
continue-on-error: true
|
||||
- run: exit 1
|
||||
continue-on-error: TRUE
|
||||
- run: exit 1
|
||||
continue-on-error: false
|
||||
- run: exit 1
|
||||
continue-on-error: yes
|
||||
`
|
||||
want := []string{"", "${{ steps.quarantine.outputs.q == 'true' }}", "true", "true", "false", "yes"}
|
||||
|
||||
sws, err := Parse([]byte(wf))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sws, 1)
|
||||
|
||||
payload, err := sws[0].Marshal()
|
||||
require.NoError(t, err)
|
||||
|
||||
rw, err := model.ReadWorkflow(bytes.NewReader(payload))
|
||||
require.NoError(t, err, "payload:\n%s", payload)
|
||||
steps := rw.Jobs["job1"].Steps
|
||||
require.Len(t, steps, len(want))
|
||||
for i, w := range want {
|
||||
require.Equal(t, w, steps[i].RawContinueOnError, "step %d, payload:\n%s", i, payload)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user