@@ -22,27 +22,32 @@ type RuleShellcheck struct {
22
22
cmd * externalCommand
23
23
workflowShell string
24
24
jobShell string
25
+ runnerShell string
25
26
mu sync.Mutex
26
27
}
27
28
28
- // NewRuleShellcheck creates new RuleShellcheck instance. Parameter executable can be command name
29
- // or relative/absolute file path. When the given executable is not found in system, it returns an
30
- // error as 2nd return value.
31
- func NewRuleShellcheck (executable string , proc * concurrentProcess ) (* RuleShellcheck , error ) {
32
- cmd , err := proc .newCommandRunner (executable )
33
- if err != nil {
34
- return nil , err
35
- }
36
- r := & RuleShellcheck {
29
+ func newRuleShellcheck (cmd * externalCommand ) * RuleShellcheck {
30
+ return & RuleShellcheck {
37
31
RuleBase : RuleBase {
38
32
name : "shellcheck" ,
39
33
desc : "Checks for shell script sources in \" run:\" using shellcheck" ,
40
34
},
41
35
cmd : cmd ,
42
36
workflowShell : "" ,
43
37
jobShell : "" ,
38
+ runnerShell : "" ,
44
39
}
45
- return r , nil
40
+ }
41
+
42
+ // NewRuleShellcheck creates new RuleShellcheck instance. The executable argument can be command
43
+ // name or relative/absolute file path. When the given executable is not found in system, it returns
44
+ // an error as 2nd return value.
45
+ func NewRuleShellcheck (executable string , proc * concurrentProcess ) (* RuleShellcheck , error ) {
46
+ cmd , err := proc .newCommandRunner (executable )
47
+ if err != nil {
48
+ return nil , err
49
+ }
50
+ return newRuleShellcheck (cmd ), nil
46
51
}
47
52
48
53
// VisitStep is callback when visiting Step node.
@@ -52,44 +57,35 @@ func (rule *RuleShellcheck) VisitStep(n *Step) error {
52
57
return nil
53
58
}
54
59
55
- name := rule .getShellName (run )
56
- if name != "bash" && name != "sh" {
57
- return nil
58
- }
59
-
60
- rule .runShellcheck (run .Run .Value , name , run .RunPos )
60
+ rule .runShellcheck (run .Run .Value , rule .getShellName (run ), run .RunPos )
61
61
return nil
62
62
}
63
63
64
64
// VisitJobPre is callback when visiting Job node before visiting its children.
65
65
func (rule * RuleShellcheck ) VisitJobPre (n * Job ) error {
66
66
if n .Defaults != nil && n .Defaults .Run != nil && n .Defaults .Run .Shell != nil {
67
67
rule .jobShell = n .Defaults .Run .Shell .Value
68
- return nil
69
68
}
70
69
71
- if n .RunsOn == nil {
72
- return nil
73
- }
74
-
75
- for _ , label := range n .RunsOn .Labels {
76
- l := strings .ToLower (label .Value )
77
- // Default shell on Windows is PowerShell.
78
- // https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#using-a-specific-shell
79
- if l == "windows" || strings .HasPrefix (l , "windows-" ) {
80
- return nil
70
+ if n .RunsOn != nil {
71
+ for _ , label := range n .RunsOn .Labels {
72
+ l := strings .ToLower (label .Value )
73
+ // Default shell on Windows is PowerShell.
74
+ // https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#using-a-specific-shell
75
+ if l == "windows" || strings .HasPrefix (l , "windows-" ) {
76
+ rule .runnerShell = "pwsh"
77
+ break
78
+ }
81
79
}
82
80
}
83
81
84
- // TODO: When bash is not found, GitHub-hosted runner fallbacks to sh. What OSes require this behavior?
85
- rule .jobShell = "bash"
86
-
87
82
return nil
88
83
}
89
84
90
85
// VisitJobPost is callback when visiting Job node after visiting its children.
91
86
func (rule * RuleShellcheck ) VisitJobPost (n * Job ) error {
92
87
rule .jobShell = ""
88
+ rule .runnerShell = ""
93
89
return nil
94
90
}
95
91
@@ -114,7 +110,15 @@ func (rule *RuleShellcheck) getShellName(exec *ExecRun) string {
114
110
if rule .jobShell != "" {
115
111
return rule .jobShell
116
112
}
117
- return rule .workflowShell
113
+ if rule .workflowShell != "" {
114
+ return rule .workflowShell
115
+ }
116
+ if rule .runnerShell != "" {
117
+ return rule .runnerShell
118
+ }
119
+ // Note: Default shell on Windows is pwsh so this value is not always correct.
120
+ // Note: When bash is not found, GitHub-hosted runner fallbacks to sh.
121
+ return "bash"
118
122
}
119
123
120
124
// Replace ${{ ... }} with underscores like __________
@@ -156,7 +160,18 @@ func sanitizeExpressionsInScript(src string) string {
156
160
}
157
161
}
158
162
159
- func (rule * RuleShellcheck ) runShellcheck (src , sh string , pos * Pos ) {
163
+ func (rule * RuleShellcheck ) runShellcheck (src , shell string , pos * Pos ) {
164
+ var sh string
165
+ if shell == "bash" || shell == "sh" {
166
+ sh = shell
167
+ } else if strings .HasPrefix (shell , "bash " ) {
168
+ sh = "bash"
169
+ } else if strings .HasPrefix (shell , "sh " ) {
170
+ sh = "sh"
171
+ } else {
172
+ return // Skip checking this shell script since shellcheck doesn't support it
173
+ }
174
+
160
175
src = sanitizeExpressionsInScript (src )
161
176
rule .Debug ("%s: Run shellcheck for %s script:\n %s" , pos , sh , src )
162
177
0 commit comments