diff --git a/rule_pyflakes.go b/rule_pyflakes.go index 2f6e665a7..5e4b62719 100644 --- a/rule_pyflakes.go +++ b/rule_pyflakes.go @@ -35,15 +35,8 @@ type RulePyflakes struct { mu sync.Mutex } -// NewRulePyflakes creates new RulePyflakes instance. Parameter executable can be command name -// or relative/absolute file path. When the given executable is not found in system, it returns -// an error. -func NewRulePyflakes(executable string, proc *concurrentProcess) (*RulePyflakes, error) { - cmd, err := proc.newCommandRunner(executable) - if err != nil { - return nil, err - } - r := &RulePyflakes{ +func newRulePyflakes(cmd *externalCommand) *RulePyflakes { + return &RulePyflakes{ RuleBase: RuleBase{ name: "pyflakes", desc: "Checks for Python script when \"shell: python\" is configured using Pyflakes", @@ -52,7 +45,17 @@ func NewRulePyflakes(executable string, proc *concurrentProcess) (*RulePyflakes, workflowShellIsPython: shellIsPythonKindUnspecified, jobShellIsPython: shellIsPythonKindUnspecified, } - return r, nil +} + +// NewRulePyflakes creates new RulePyflakes instance. Parameter executable can be command name +// or relative/absolute file path. When the given executable is not found in system, it returns +// an error. +func NewRulePyflakes(executable string, proc *concurrentProcess) (*RulePyflakes, error) { + cmd, err := proc.newCommandRunner(executable) + if err != nil { + return nil, err + } + return newRulePyflakes(cmd), nil } // VisitJobPre is callback when visiting Job node before visiting its children. diff --git a/rule_pyflakes_test.go b/rule_pyflakes_test.go new file mode 100644 index 000000000..e2555d446 --- /dev/null +++ b/rule_pyflakes_test.go @@ -0,0 +1,84 @@ +package actionlint + +import ( + "testing" +) + +func TestRulePyflakesDetectPythonShell(t *testing.T) { + tests := []struct { + what string + isPython bool + workflow string // Shell name set at 'defaults' in Workflow node + job string // Shell name set at 'defaults' in Job node + step string // Shell name set at 'shell' in Step node + }{ + { + what: "no default shell", + isPython: false, + }, + { + what: "workflow default", + isPython: true, + workflow: "python", + }, + { + what: "job default", + isPython: true, + job: "python", + }, + { + what: "step shell", + isPython: true, + step: "python", + }, + { + what: "custom shell", + isPython: true, + workflow: "python {0}", + }, + { + what: "other shell", + isPython: false, + workflow: "pwsh", + }, + { + what: "other custom shell", + isPython: false, + workflow: "bash -e {0}", + }, + } + + for _, tc := range tests { + t.Run(tc.what, func(t *testing.T) { + r := newRulePyflakes(&externalCommand{}) + + w := &Workflow{} + if tc.workflow != "" { + w.Defaults = &Defaults{ + Run: &DefaultsRun{ + Shell: &String{Value: tc.workflow}, + }, + } + } + r.VisitWorkflowPre(w) + + j := &Job{} + if tc.job != "" { + j.Defaults = &Defaults{ + Run: &DefaultsRun{ + Shell: &String{Value: tc.job}, + }, + } + } + r.VisitJobPre(j) + + e := &ExecRun{} + if tc.step != "" { + e.Shell = &String{Value: tc.step} + } + if have := r.isPythonShell(e); have != tc.isPython { + t.Fatalf("Actual isPython=%v but wanted isPython=%v", have, tc.isPython) + } + }) + } +}