Skip to content

Commit

Permalink
Merge branch 'rules-hook'
Browse files Browse the repository at this point in the history
  • Loading branch information
rhysd committed Aug 1, 2023
2 parents 0fa0c81 + 30497d9 commit 655d745
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 13 deletions.
65 changes: 65 additions & 0 deletions example_your_won_rule_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package actionlint_test

import (
"fmt"
"os"
"path/filepath"

"github.com/rhysd/actionlint"
)

// A rule type to check every steps have their names.
type RuleStepName struct {
// Embedding RuleBase struct implements the minimal Rule interface.
actionlint.RuleBase
}

// Reimplement methods in RuleBase. Visit* methods are called on checking workflows.
func (r *RuleStepName) VisitStep(n *actionlint.Step) error {
// Implement your own check
if n.Name == nil {
// RuleBase provides methods to report errors. See RuleBase.Error and RuleBase.Errorf.
r.Error(n.Pos, "every step must have its name")
}
return nil
}

func NewRuleStepName() *RuleStepName {
return &RuleStepName{
RuleBase: actionlint.NewRuleBase("step-name", "Checks every step has their own name"),
}
}

func ExampleLinter_yourOwnRule() {
// The function set at OnRulesCreated is called after rule instances are created. You can
// add/remove some rules and return the modified slice. This function is called on linting
// each workflow files.
o := &actionlint.LinterOptions{
OnRulesCreated: func(rules []actionlint.Rule) []actionlint.Rule {
rules = append(rules, NewRuleStepName())
return rules
},
}

l, err := actionlint.NewLinter(os.Stdout, o)
if err != nil {
panic(err)
}

f := filepath.Join("testdata", "ok", "minimal.yaml")

// First return value is an array of lint errors found in the workflow file.
errs, err := l.LintFile(f, nil)
if err != nil {
panic(err)
}

fmt.Println(len(errs), "lint errors found by actionlint")

// Output:
// testdata/ok/minimal.yaml:6:9: every step must have its name [step-name]
// |
// 6 | - run: echo
// | ^~~~
// 1 lint errors found by actionlint
}
31 changes: 20 additions & 11 deletions linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,27 @@ type LinterOptions struct {
// WorkingDir is a file path to the current working directory. When this value is empty, os.Getwd
// will be used to get a working directory.
WorkingDir string
// OnRulesCreated is a hook to add or remove the check rules. This function is called on checking
// every workflow files. Rules created by Linter instance is passed to the argument and the function
// should return the modified rules slice.
OnRulesCreated func([]Rule) []Rule
// More options will come here
}

// Linter is struct to lint workflow files.
type Linter struct {
projects *Projects
out io.Writer
logOut io.Writer
logLevel LogLevel
oneline bool
shellcheck string
pyflakes string
ignorePats []*regexp.Regexp
defaultConfig *Config
errFmt *ErrorFormatter
cwd string
projects *Projects
out io.Writer
logOut io.Writer
logLevel LogLevel
oneline bool
shellcheck string
pyflakes string
ignorePats []*regexp.Regexp
defaultConfig *Config
errFmt *ErrorFormatter
cwd string
onRulesCreated func([]Rule) []Rule
}

// NewLinter creates a new Linter instance.
Expand Down Expand Up @@ -178,6 +183,7 @@ func NewLinter(out io.Writer, opts *LinterOptions) (*Linter, error) {
cfg,
formatter,
cwd,
opts.OnRulesCreated,
}, nil
}

Expand Down Expand Up @@ -525,6 +531,9 @@ func (l *Linter) check(
} else {
l.log("Rule \"pyflakes\" was disabled since pyflakes command name was empty")
}
if l.onRulesCreated != nil {
rules = l.onRulesCreated(rules)
}

v := NewVisitor()
for _, rule := range rules {
Expand Down
27 changes: 27 additions & 0 deletions linter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,33 @@ func TestLinterPathsNotFound(t *testing.T) {
}
}

func TestLinterRemoveRuleOnRulesCreatedHook(t *testing.T) {
o := &LinterOptions{
OnRulesCreated: func(rules []Rule) []Rule {
for i, r := range rules {
if r.Name() == "runner-label" {
rules = append(rules[:i], rules[i+1:]...)
}
}
return rules
},
}

l, err := NewLinter(io.Discard, o)
if err != nil {
t.Fatal(err)
}

f := filepath.Join("testdata", "err", "invalid_runner_labels.yaml")
errs, err := l.LintFile(f, nil)
if err != nil {
t.Fatal(err)
}
if len(errs) != 0 {
t.Fatal("no error was expected because runner-label rule was removed but got:", errs)
}
}

func BenchmarkLintWorkflowFiles(b *testing.B) {
large := filepath.Join("testdata", "bench", "many_scripts.yaml")
small := filepath.Join("testdata", "bench", "small.yaml")
Expand Down
4 changes: 2 additions & 2 deletions rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ type RuleBase struct {

// NewRuleBase creates a new RuleBase instance. It should be embedded to your own
// rule instance.
func NewRuleBase(name string, desc string) *RuleBase {
return &RuleBase{
func NewRuleBase(name string, desc string) RuleBase {
return RuleBase{
name: name,
desc: desc,
}
Expand Down
3 changes: 3 additions & 0 deletions testdata/err/invalid_runner_labels.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/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\]/
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]
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 changes: 10 additions & 0 deletions testdata/err/invalid_runner_labels.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
on: push
jobs:
test1:
runs-on: ubuntu-oldest
steps:
- run: echo
test2:
runs-on: [ubuntu-latest, windows-latest, macos-latest]
steps:
- run: echo
6 changes: 6 additions & 0 deletions testdata/ok/minimal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo

0 comments on commit 655d745

Please sign in to comment.