diff --git a/modules/actions/jobparser/model.go b/modules/actions/jobparser/model.go index 7132c278e9..eb17a43b01 100644 --- a/modules/actions/jobparser/model.go +++ b/modules/actions/jobparser/model.go @@ -298,6 +298,9 @@ func toGitContext(input map[string]any) *model.GithubContext { return gitContext } +// workflowCallEvent is only fired by another workflow's `uses:`, so it is excluded from trigger detection. +const workflowCallEvent = "workflow_call" + func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { switch rawOn.Kind { case yaml.ScalarNode: @@ -306,6 +309,9 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { if err != nil { return nil, err } + if val == workflowCallEvent { + return []*Event{}, nil + } return []*Event{ {Name: val}, }, nil @@ -319,6 +325,9 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { for _, v := range val { switch t := v.(type) { case string: + if t == workflowCallEvent { + continue + } res = append(res, &Event{Name: t}) default: return nil, fmt.Errorf("invalid type %T", t) @@ -332,6 +341,9 @@ func ParseRawOn(rawOn *yaml.Node) ([]*Event, error) { } res := make([]*Event, 0, len(events)) for i, k := range events { + if k == workflowCallEvent { + continue + } v := triggers[i] switch v.Kind { case yaml.ScalarNode: diff --git a/modules/actions/jobparser/model_test.go b/modules/actions/jobparser/model_test.go index d0e8204161..7b0be62c26 100644 --- a/modules/actions/jobparser/model_test.go +++ b/modules/actions/jobparser/model_test.go @@ -254,6 +254,53 @@ func TestParseRawOn(t *testing.T) { }, }, }, + { + // `workflow_call` is only fired by another workflow's `uses:`, so ParseRawOn intentionally excludes it from trigger detection. + input: `on: + workflow_call: + inputs: + env: + type: string + required: true + outputs: + sha: + value: ${{ jobs.build.outputs.commit }} + secrets: + DEPLOY_KEY: + required: true +`, + result: []*Event{}, + }, + { + // Mixed: a workflow that is both callable AND triggered by push. Only the "push" event surfaces. + input: `on: + workflow_call: + inputs: + env: + type: string + push: + branches: [main] +`, + result: []*Event{ + { + Name: "push", + acts: map[string][]string{"branches": {"main"}}, + }, + }, + }, + { + // Scalar form: a purely reusable workflow has no event triggers. + input: "on: workflow_call", + result: []*Event{}, + }, + { + // Sequence form: `workflow_call` is excluded while sibling events are kept. + input: "on:\n - push\n - workflow_call\n - pull_request", + result: []*Event{ + {Name: "push"}, + {Name: "pull_request"}, + }, + }, } for _, kase := range kases { t.Run(kase.input, func(t *testing.T) {