Skip to content

Commit 0c0a521

Browse files
committed
Merge branch 'paths-config' (fix #342, fix #217)
2 parents d66fe00 + c7a09e0 commit 0c0a521

File tree

18 files changed

+474
-99
lines changed

18 files changed

+474
-99
lines changed

README.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
actionlint
22
==========
33
[![CI Badge][]][CI]
4-
[![API Document][api-badge]][apidoc]
4+
[![API Document][apidoc-badge]][apidoc]
55

66
[actionlint][repo] is a static checker for GitHub Actions workflow files. [Try it online!][playground]
77

@@ -97,7 +97,7 @@ test.yaml:22:17: receiver of object dereference "permissions" must be type of ob
9797
## Quick start
9898

9999
Install `actionlint` command by downloading [the released binary][releases] or by Homebrew or by `go install`. See
100-
[the installation document](docs/install.md) for more details like how to manage the command with several package managers
100+
[the installation document][install] for more details like how to manage the command with several package managers
101101
or run via Docker container.
102102

103103
```sh
@@ -114,19 +114,19 @@ actionlint
114114

115115
Another option to try actionlint is [the online playground][playground]. Your browser can run actionlint through WebAssembly.
116116

117-
See [the usage document](docs/usage.md) for more details.
117+
See [the usage document][usage] for more details.
118118

119119
## Documents
120120

121121
- [Checks][checks]: Full list of all checks done by actionlint with example inputs, outputs, and playground links.
122-
- [Installation](docs/install.md): Installation instructions. Prebuilt binaries, a Docker image, building from
123-
source, a download script (for CI), supports by several package managers are available.
124-
- [Usage](docs/usage.md): How to use `actionlint` command locally or on GitHub Actions, the online playground, an official Docker
125-
image, and integrations with reviewdog, Problem Matchers, super-linter, pre-commit, VS Code.
126-
- [Configuration](docs/config.md): How to configure actionlint behavior. Currently, the labels of self-hosted runners and the
127-
configuration variables can be set.
128-
- [Go API](docs/api.md): How to use actionlint as Go library.
129-
- [References](docs/reference.md): Links to resources.
122+
- [Installation][install]: Installation instructions. Prebuilt binaries, a Docker image, building from source, a download script
123+
(for CI), supports by several package managers are available.
124+
- [Usage][usage]: How to use `actionlint` command locally or on GitHub Actions, the online playground, an official Docker image,
125+
and integrations with reviewdog, Problem Matchers, super-linter, pre-commit, VS Code.
126+
- [Configuration][config]: How to configure actionlint behavior. Currently, the labels of self-hosted runners, the configuration
127+
variables, and ignore patterns of errors for each file paths can be set.
128+
- [Go API][api]: How to use actionlint as Go library.
129+
- [References][refs]: Links to resources.
130130

131131
## Bug reporting
132132

@@ -139,7 +139,7 @@ actionlint is distributed under [the MIT license](./LICENSE.txt).
139139

140140
[CI Badge]: https://github.com/rhysd/actionlint/workflows/CI/badge.svg?branch=main&event=push
141141
[CI]: https://github.com/rhysd/actionlint/actions?query=workflow%3ACI+branch%3Amain
142-
[api-badge]: https://pkg.go.dev/badge/github.com/rhysd/actionlint.svg
142+
[apidoc-badge]: https://pkg.go.dev/badge/github.com/rhysd/actionlint.svg
143143
[apidoc]: https://pkg.go.dev/github.com/rhysd/actionlint
144144
[repo]: https://github.com/rhysd/actionlint
145145
[playground]: https://rhysd.github.io/actionlint/
@@ -149,6 +149,11 @@ actionlint is distributed under [the MIT license](./LICENSE.txt).
149149
[syntax-doc]: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
150150
[filter-pattern-doc]: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet
151151
[script-injection-doc]: https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#understanding-the-risk-of-script-injections
152-
[issue-form]: https://github.com/rhysd/actionlint/issues/new
153152
[releases]: https://github.com/rhysd/actionlint/releases
154153
[checks]: https://github.com/rhysd/actionlint/blob/v1.7.3/docs/checks.md
154+
[install]: https://github.com/rhysd/actionlint/blob/v1.7.3/docs/install.md
155+
[usage]: https://github.com/rhysd/actionlint/blob/v1.7.3/docs/usage.md
156+
[config]: https://github.com/rhysd/actionlint/blob/v1.7.3/docs/config.md
157+
[api]: https://github.com/rhysd/actionlint/blob/v1.7.3/docs/api.md
158+
[refs]: https://github.com/rhysd/actionlint/blob/v1.7.3/docs/reference.md
159+
[issue-form]: https://github.com/rhysd/actionlint/issues/new

command.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ func (cmd *Command) runLinter(args []string, opts *LinterOptions, initConfig boo
9494
}
9595

9696
if initConfig {
97-
return nil, l.GenerateDefaultConfig(".")
97+
return nil, l.GenerateDefaultConfig("")
9898
}
9999

100100
if len(args) == 0 {
101-
return l.LintRepository(".")
101+
return l.LintRepository("")
102102
}
103103

104104
if len(args) == 1 && args[0] == "-" {

config.go

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,56 @@
11
package actionlint
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67
"path/filepath"
8+
"regexp"
79
"strings"
810

11+
"github.com/bmatcuk/doublestar/v4"
912
"gopkg.in/yaml.v3"
1013
)
1114

15+
// IgnorePatterns is a list of regular expressions. They are used for ignoring errors by matching
16+
// to the error messages.
17+
type IgnorePatterns []*regexp.Regexp
18+
19+
// Match returns whether the given error should be ignored due to the "ignore" configuration.
20+
func (pats IgnorePatterns) Match(err *Error) bool {
21+
for _, r := range pats {
22+
if r.MatchString(err.Message) {
23+
return true
24+
}
25+
}
26+
return false
27+
}
28+
29+
// UnmarshalYAML implements yaml.Unmarshaler.
30+
func (pats *IgnorePatterns) UnmarshalYAML(n *yaml.Node) error {
31+
if n.Kind != yaml.SequenceNode {
32+
return fmt.Errorf("yaml: \"ignore\" must be a sequence node at line:%d,col:%d", n.Line, n.Column)
33+
}
34+
rs := make([]*regexp.Regexp, 0, len(n.Content))
35+
for _, p := range n.Content {
36+
r, err := regexp.Compile(p.Value)
37+
if err != nil {
38+
return fmt.Errorf("invalid regular expression %q in \"ignore\" at line%d,col:%d: %w", p.Value, n.Line, n.Column, err)
39+
}
40+
rs = append(rs, r)
41+
}
42+
*pats = rs
43+
return nil
44+
}
45+
46+
// PathConfig is a configuration for specific file path pattern. This is for values of the "paths" mapping
47+
// in the configuration file.
48+
type PathConfig struct {
49+
// Ignore is a list of patterns. They are used for ignoring errors by matching to the error messages.
50+
// It is similar to the "-ignore" command line option.
51+
Ignore IgnorePatterns `yaml:"ignore"`
52+
}
53+
1254
// Config is configuration of actionlint. This struct instance is parsed from "actionlint.yaml"
1355
// file usually put in ".github" directory.
1456
type Config struct {
@@ -22,13 +64,40 @@ type Config struct {
2264
// listed here as undefined config variables.
2365
// https://docs.github.com/en/actions/learn-github-actions/variables
2466
ConfigVariables []string `yaml:"config-variables"`
67+
// Paths is a "paths" mapping in the configuration file. The keys are glob patterns to match file paths.
68+
// And the values are corresponding configurations applied to the file paths.
69+
Paths map[string]PathConfig `yaml:"paths"`
2570
}
2671

27-
func parseConfig(b []byte, path string) (*Config, error) {
72+
// PathConfigsFor returns a list of all PathConfig values matching to the given file path. The path must
73+
// be relative to the root of the project.
74+
func (cfg *Config) PathConfigsFor(path string) []PathConfig {
75+
path = filepath.ToSlash(path)
76+
77+
var ret []PathConfig
78+
if cfg != nil {
79+
for p, c := range cfg.Paths {
80+
// Glob patterns were validated in `ParseConfig()`
81+
if doublestar.MatchUnvalidated(p, path) {
82+
ret = append(ret, c)
83+
}
84+
}
85+
}
86+
return ret
87+
}
88+
89+
// ParseConfig parses the given bytes as an actionlint config file. When deserializing the YAML file
90+
// or the config validation fails, this function returns an error.
91+
func ParseConfig(b []byte) (*Config, error) {
2892
var c Config
2993
if err := yaml.Unmarshal(b, &c); err != nil {
3094
msg := strings.ReplaceAll(err.Error(), "\n", " ")
31-
return nil, fmt.Errorf("could not parse config file %q: %s", path, msg)
95+
return nil, errors.New(msg)
96+
}
97+
for pat := range c.Paths {
98+
if !doublestar.ValidatePattern(pat) {
99+
return nil, fmt.Errorf("invalid glob pattern %q in \"paths\"", pat)
100+
}
32101
}
33102
return &c, nil
34103
}
@@ -39,23 +108,27 @@ func ReadConfigFile(path string) (*Config, error) {
39108
if err != nil {
40109
return nil, fmt.Errorf("could not read config file %q: %w", path, err)
41110
}
42-
return parseConfig(b, path)
111+
c, err := ParseConfig(b)
112+
if err != nil {
113+
return nil, fmt.Errorf("could not parse config file %q: %w", path, err)
114+
}
115+
return c, nil
43116
}
44117

45118
// loadRepoConfig reads config file from the repository's .github/actionlint.yml or
46119
// .github/actionlint.yaml.
47120
func loadRepoConfig(root string) (*Config, error) {
48121
for _, f := range []string{"actionlint.yaml", "actionlint.yml"} {
49-
path := filepath.Join(root, ".github", f)
50-
b, err := os.ReadFile(path)
51-
if err != nil {
52-
continue // file does not exist
122+
p := filepath.Join(root, ".github", f)
123+
c, err := ReadConfigFile(p)
124+
switch {
125+
case errors.Is(err, os.ErrNotExist):
126+
continue
127+
case err != nil:
128+
return nil, fmt.Errorf("could not parse config file %q: %w", p, err)
129+
default:
130+
return c, nil
53131
}
54-
cfg, err := parseConfig(b, path)
55-
if err != nil {
56-
return nil, err
57-
}
58-
return cfg, nil
59132
}
60133
return nil, nil
61134
}
@@ -64,10 +137,22 @@ func writeDefaultConfigFile(path string) error {
64137
b := []byte(`self-hosted-runner:
65138
# Labels of self-hosted runner in array of strings.
66139
labels: []
140+
67141
# Configuration variables in array of strings defined in your repository or
68142
# organization. ` + "`null`" + ` means disabling configuration variables check.
69143
# Empty array means no configuration variable is allowed.
70144
config-variables: null
145+
146+
# Configuration for file paths. The keys are glob patterns to match to file
147+
# paths relative to the repository root. The values are the configurations for
148+
# the file paths. Note that the path separator is always '/'.
149+
# The following configurations are available.
150+
#
151+
# "ignore" is an array of regular expression patterns. Matched error messages
152+
# are ignored. This is similar to the "-ignore" command line option.
153+
paths:
154+
# .github/workflows/**/*.yml:
155+
# ignore: []
71156
`)
72157
if err := os.WriteFile(path, b, 0644); err != nil {
73158
return fmt.Errorf("could not write default configuration file at %q: %w", path, err)

0 commit comments

Comments
 (0)