Skip to content

Commit 655d745

Browse files
committedAug 1, 2023
Merge branch 'rules-hook'
2 parents 0fa0c81 + 30497d9 commit 655d745

7 files changed

+133
-13
lines changed
 

‎example_your_won_rule_test.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package actionlint_test
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/rhysd/actionlint"
9+
)
10+
11+
// A rule type to check every steps have their names.
12+
type RuleStepName struct {
13+
// Embedding RuleBase struct implements the minimal Rule interface.
14+
actionlint.RuleBase
15+
}
16+
17+
// Reimplement methods in RuleBase. Visit* methods are called on checking workflows.
18+
func (r *RuleStepName) VisitStep(n *actionlint.Step) error {
19+
// Implement your own check
20+
if n.Name == nil {
21+
// RuleBase provides methods to report errors. See RuleBase.Error and RuleBase.Errorf.
22+
r.Error(n.Pos, "every step must have its name")
23+
}
24+
return nil
25+
}
26+
27+
func NewRuleStepName() *RuleStepName {
28+
return &RuleStepName{
29+
RuleBase: actionlint.NewRuleBase("step-name", "Checks every step has their own name"),
30+
}
31+
}
32+
33+
func ExampleLinter_yourOwnRule() {
34+
// The function set at OnRulesCreated is called after rule instances are created. You can
35+
// add/remove some rules and return the modified slice. This function is called on linting
36+
// each workflow files.
37+
o := &actionlint.LinterOptions{
38+
OnRulesCreated: func(rules []actionlint.Rule) []actionlint.Rule {
39+
rules = append(rules, NewRuleStepName())
40+
return rules
41+
},
42+
}
43+
44+
l, err := actionlint.NewLinter(os.Stdout, o)
45+
if err != nil {
46+
panic(err)
47+
}
48+
49+
f := filepath.Join("testdata", "ok", "minimal.yaml")
50+
51+
// First return value is an array of lint errors found in the workflow file.
52+
errs, err := l.LintFile(f, nil)
53+
if err != nil {
54+
panic(err)
55+
}
56+
57+
fmt.Println(len(errs), "lint errors found by actionlint")
58+
59+
// Output:
60+
// testdata/ok/minimal.yaml:6:9: every step must have its name [step-name]
61+
// |
62+
// 6 | - run: echo
63+
// | ^~~~
64+
// 1 lint errors found by actionlint
65+
}

‎linter.go

+20-11
Original file line numberDiff line numberDiff line change
@@ -85,22 +85,27 @@ type LinterOptions struct {
8585
// WorkingDir is a file path to the current working directory. When this value is empty, os.Getwd
8686
// will be used to get a working directory.
8787
WorkingDir string
88+
// OnRulesCreated is a hook to add or remove the check rules. This function is called on checking
89+
// every workflow files. Rules created by Linter instance is passed to the argument and the function
90+
// should return the modified rules slice.
91+
OnRulesCreated func([]Rule) []Rule
8892
// More options will come here
8993
}
9094

9195
// Linter is struct to lint workflow files.
9296
type Linter struct {
93-
projects *Projects
94-
out io.Writer
95-
logOut io.Writer
96-
logLevel LogLevel
97-
oneline bool
98-
shellcheck string
99-
pyflakes string
100-
ignorePats []*regexp.Regexp
101-
defaultConfig *Config
102-
errFmt *ErrorFormatter
103-
cwd string
97+
projects *Projects
98+
out io.Writer
99+
logOut io.Writer
100+
logLevel LogLevel
101+
oneline bool
102+
shellcheck string
103+
pyflakes string
104+
ignorePats []*regexp.Regexp
105+
defaultConfig *Config
106+
errFmt *ErrorFormatter
107+
cwd string
108+
onRulesCreated func([]Rule) []Rule
104109
}
105110

106111
// NewLinter creates a new Linter instance.
@@ -178,6 +183,7 @@ func NewLinter(out io.Writer, opts *LinterOptions) (*Linter, error) {
178183
cfg,
179184
formatter,
180185
cwd,
186+
opts.OnRulesCreated,
181187
}, nil
182188
}
183189

@@ -525,6 +531,9 @@ func (l *Linter) check(
525531
} else {
526532
l.log("Rule \"pyflakes\" was disabled since pyflakes command name was empty")
527533
}
534+
if l.onRulesCreated != nil {
535+
rules = l.onRulesCreated(rules)
536+
}
528537

529538
v := NewVisitor()
530539
for _, rule := range rules {

‎linter_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,33 @@ func TestLinterPathsNotFound(t *testing.T) {
458458
}
459459
}
460460

461+
func TestLinterRemoveRuleOnRulesCreatedHook(t *testing.T) {
462+
o := &LinterOptions{
463+
OnRulesCreated: func(rules []Rule) []Rule {
464+
for i, r := range rules {
465+
if r.Name() == "runner-label" {
466+
rules = append(rules[:i], rules[i+1:]...)
467+
}
468+
}
469+
return rules
470+
},
471+
}
472+
473+
l, err := NewLinter(io.Discard, o)
474+
if err != nil {
475+
t.Fatal(err)
476+
}
477+
478+
f := filepath.Join("testdata", "err", "invalid_runner_labels.yaml")
479+
errs, err := l.LintFile(f, nil)
480+
if err != nil {
481+
t.Fatal(err)
482+
}
483+
if len(errs) != 0 {
484+
t.Fatal("no error was expected because runner-label rule was removed but got:", errs)
485+
}
486+
}
487+
461488
func BenchmarkLintWorkflowFiles(b *testing.B) {
462489
large := filepath.Join("testdata", "bench", "many_scripts.yaml")
463490
small := filepath.Join("testdata", "bench", "small.yaml")

‎rule.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ type RuleBase struct {
1717

1818
// NewRuleBase creates a new RuleBase instance. It should be embedded to your own
1919
// rule instance.
20-
func NewRuleBase(name string, desc string) *RuleBase {
21-
return &RuleBase{
20+
func NewRuleBase(name string, desc string) RuleBase {
21+
return RuleBase{
2222
name: name,
2323
desc: desc,
2424
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/test\.yaml:4:14: label "ubuntu-oldest" is unknown\. available labels are .+\. if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file \[runner-label\]/
2+
test.yaml:8:30: label "windows-latest" conflicts with label "ubuntu-latest" defined at line:8,col:15. note: to run your job on each workers, use matrix [runner-label]
3+
test.yaml:8:46: label "macos-latest" conflicts with label "ubuntu-latest" defined at line:8,col:15. note: to run your job on each workers, use matrix [runner-label]
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
on: push
2+
jobs:
3+
test1:
4+
runs-on: ubuntu-oldest
5+
steps:
6+
- run: echo
7+
test2:
8+
runs-on: [ubuntu-latest, windows-latest, macos-latest]
9+
steps:
10+
- run: echo

‎testdata/ok/minimal.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
on: push
2+
jobs:
3+
test:
4+
runs-on: ubuntu-latest
5+
steps:
6+
- run: echo

0 commit comments

Comments
 (0)
Please sign in to comment.