From 6f7aaf2dbf7ddd2075d5235c004fa258968c452a Mon Sep 17 00:00:00 2001 From: Markus Wiegand Date: Sat, 9 Nov 2024 18:27:09 +0100 Subject: [PATCH] add `fail-on-any` input --- README.md | 6 ++++ action.yaml | 3 ++ input.go | 75 ++++++++++++++++++++++++++++++++++++------- input_test.go | 88 +++++++++++++++++++++++++++++++++++++-------------- main.go | 11 ++++++- 5 files changed, 147 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a5c16b5..56f5af0 100644 --- a/README.md +++ b/README.md @@ -40,19 +40,25 @@ This Action runs [Revive](https://github.com/mgechev/revive) on your [Go](https: with: # Path to your Revive config within the repo (optional) config: revive/config.toml + # Exclude patterns (optional) exclude: | file.go foo/bar.go ./foo/bar/... + # Path pattern (default: ./...) path: "./foo/..." + + # Fail on any issue. Overrides the error and warning code in config (default: false) + fail-on-any: true ``` ### Workflow example ```YAML name: Lint + on: pull_request: push: diff --git a/action.yaml b/action.yaml index 4c7742f..00c9e30 100644 --- a/action.yaml +++ b/action.yaml @@ -14,6 +14,9 @@ inputs: path: description: "Revive path pattern" required: false + fail-on-any: + description: "Fail on any issue. Overrides the error and warning code in config" + required: false runs: using: "docker" image: "Dockerfile" diff --git a/input.go b/input.go index a4a1543..c4d3741 100644 --- a/input.go +++ b/input.go @@ -1,42 +1,93 @@ package main import ( + "fmt" "os" + "strconv" "strings" ) const ( + // Environmental variable constants + envExclude = "INPUT_EXCLUDE" + envConfig = "INPUT_CONFIG" + envPath = "INPUT_PATH" + envFailOnAny = "INPUT_FAIL_ON_ANY" + + // Default path defaultPath = "./..." ) type input struct { - exclude []string - config string - path string + exclude []string + config string + path string + failOnAny bool } -func parseInput() *input { +func parseInput() (*input, error) { input := &input{ exclude: make([]string, 0), path: defaultPath, + config: "", + } + + if err := input.parseExclude(); err != nil { + return nil, fmt.Errorf("parsing exclude: %w", err) + } + + if err := input.parseConfig(); err != nil { + return nil, fmt.Errorf("parsing config: %w", err) } - if v, ok := os.LookupEnv("INPUT_EXCLUDE"); ok { + if err := input.parsePath(); err != nil { + return nil, fmt.Errorf("parsing path: %w", err) + } + + if err := input.parseFailOnAny(); err != nil { + return nil, fmt.Errorf("parsing fail-on-any: %w", err) + } + + return input, nil +} + +func (i *input) parseExclude() error { + if v, ok := os.LookupEnv(envExclude); ok && v != "" { switch { case strings.Contains(v, ";"): - input.exclude = strings.Split(v, ";") + i.exclude = strings.Split(v, ";") default: - input.exclude = strings.Split(v, "\n") + i.exclude = strings.Split(v, "\n") } } - if v, ok := os.LookupEnv("INPUT_CONFIG"); ok { - input.config = v + return nil +} + +func (i *input) parseConfig() error { + if v, ok := os.LookupEnv(envConfig); ok { + i.config = v + } + + return nil +} + +func (i *input) parsePath() error { + if v, ok := os.LookupEnv(envPath); ok { + i.path = v } - if v, ok := os.LookupEnv("INPUT_PATH"); ok { - input.path = v + return nil +} + +func (i *input) parseFailOnAny() error { + if v, ok := os.LookupEnv(envFailOnAny); ok { + b, err := strconv.ParseBool(v) + if err != nil { + return fmt.Errorf("invalid value: %w", err) + } + i.failOnAny = b } - return input + return nil } diff --git a/input_test.go b/input_test.go index e730872..fe3f289 100644 --- a/input_test.go +++ b/input_test.go @@ -12,73 +12,115 @@ func TestParseInput(t *testing.T) { name string env map[string]string expected *input + wantErr bool }{ { name: "default values", env: map[string]string{}, expected: &input{ - exclude: make([]string, 0), - path: defaultPath, - config: "", + exclude: make([]string, 0), + path: defaultPath, + config: "", + failOnAny: false, }, }, { name: "with semicolon separated excludes", env: map[string]string{ - "INPUT_EXCLUDE": "dir1;./dir2/...;dir3/dir4/...", + envExclude: "dir1;./dir2/...;dir3/dir4/...", }, expected: &input{ - exclude: []string{"dir1", "./dir2/...", "dir3/dir4/..."}, - path: defaultPath, - config: "", + exclude: []string{"dir1", "./dir2/...", "dir3/dir4/..."}, + path: defaultPath, + config: "", + failOnAny: false, }, }, { name: "with newline separated excludes", env: map[string]string{ - "INPUT_EXCLUDE": "dir1\n./dir2/...\ndir3/dir4/...", + envExclude: "dir1\n./dir2/...\ndir3/dir4/...", }, expected: &input{ - exclude: []string{"dir1", "./dir2/...", "dir3/dir4/..."}, - path: defaultPath, - config: "", + exclude: []string{"dir1", "./dir2/...", "dir3/dir4/..."}, + path: defaultPath, + config: "", + failOnAny: false, }, }, { name: "with custom config", env: map[string]string{ - "INPUT_CONFIG": "custom.toml", + envConfig: "custom.toml", }, expected: &input{ - exclude: make([]string, 0), - path: defaultPath, - config: "custom.toml", + exclude: make([]string, 0), + path: defaultPath, + config: "custom.toml", + failOnAny: false, }, }, { name: "with custom path", env: map[string]string{ - "INPUT_PATH": "./src/...", + envPath: "./src/...", }, expected: &input{ - exclude: make([]string, 0), - path: "./src/...", - config: "", + exclude: make([]string, 0), + path: "./src/...", + config: "", + failOnAny: false, + }, + }, + { + name: "with fail on any true", + env: map[string]string{ + envFailOnAny: "true", + }, + expected: &input{ + exclude: make([]string, 0), + path: defaultPath, + config: "", + failOnAny: true, + }, + }, + { + name: "with invalid fail on any", + env: map[string]string{ + envFailOnAny: "invalid", + }, + wantErr: true, + }, + { + name: "with all options", + env: map[string]string{ + envExclude: "dir1;dir2", + envConfig: "custom.toml", + envPath: "./src/...", + envFailOnAny: "true", + }, + expected: &input{ + exclude: []string{"dir1", "dir2"}, + path: "./src/...", + config: "custom.toml", + failOnAny: true, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Clear environment before each test os.Clearenv() - - // Set up test environment for k, v := range tt.env { os.Setenv(k, v) } - got := parseInput() + got, err := parseInput() + if tt.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) assert.Equal(t, tt.expected, got) }) } diff --git a/main.go b/main.go index f4bd8b0..e2108cd 100644 --- a/main.go +++ b/main.go @@ -95,6 +95,10 @@ func buildArgs(input *input) []string { args = append(args, "-exclude", path) } + if input.failOnAny { + args = append(args, "-set_exit_status") + } + args = append(args, input.path) return args @@ -109,7 +113,12 @@ func main() { os.Exit(0) } - input := parseInput() + input, err := parseInput() + if err != nil { + fmt.Fprintf(os.Stderr, "::error %s", err) + os.Exit(1) + } + args := buildArgs(input) reviveVersion, err := getReviveVersion()