From 0a5d2258fddef89e6f59458d260d8afa557f9cb4 Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 18:01:07 -0300 Subject: [PATCH 1/8] Update go.sum --- go.sum | 3 +++ linelint.go | 8 ++++++-- linter/config.go | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 772f011..d958512 100644 --- a/go.sum +++ b/go.sum @@ -221,6 +221,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kyoh86/exportloopref v0.1.7 h1:u+iHuTbkbTS2D/JP7fCuZDo/t3rBVGo3Hf58Rc+lQVY= github.com/kyoh86/exportloopref v0.1.7/go.mod h1:h1rDl2Kdj97+Kwh4gdz3ujE7XHmH51Q0lUiZ1z4NLj8= @@ -265,6 +266,7 @@ github.com/nakabonne/nestif v0.3.0 h1:+yOViDGhg8ygGrmII72nV9B/zGxY188TYpfolntsaP github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/exhaustive v0.0.0-20200708172631-8866003e3856 h1:W3KBC2LFyfgd+wNudlfgCCsTo4q97MeNWrfz8/wSdSc= github.com/nishanths/exhaustive v0.0.0-20200708172631-8866003e3856/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c= @@ -562,6 +564,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/linelint.go b/linelint.go index 3267ee8..5ebe07e 100644 --- a/linelint.go +++ b/linelint.go @@ -22,8 +22,9 @@ optional arguments: ` func main() { - var flagAutofix bool + var flagAutofix, flagVerbose bool flag.BoolVar(&flagAutofix, "a", false, "(autofix) will automatically fix files with errors in place") + flag.Usage = func() { fmt.Fprintf(os.Stderr, helpMsg, os.Args[0]) flag.PrintDefaults() @@ -39,7 +40,10 @@ func main() { } config := linter.NewConfig() - config.AutoFix = flagAutofix + + if flagAutofix { + config.AutoFix = true + } // get paths to ignore ignore := linter.MustCompileIgnoreLines(config.Ignore...) diff --git a/linter/config.go b/linter/config.go index 6c7745c..288ed87 100644 --- a/linter/config.go +++ b/linter/config.go @@ -12,6 +12,7 @@ import ( type Config struct { // AutoFix sets if the linter should try to fix the error AutoFix bool `yaml:"autofix"` + Verbose bool `yaml:"verbose"` // Ignore uses the gitignore syntax the select which files or folders to ignore Ignore []string `yaml:"ignore"` From 5f775f453e80e52d5170fead604f37d4df37107f Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 18:02:07 -0300 Subject: [PATCH 2/8] Fix #2 and add stdin * Fixes https://github.com/fernandrone/linelint/issues/2. The issue is that the "autofix" command line flag was overriding the Config object. So even if the config had autofix: true, later in the main function the flag value would override this configuration. It didn't help that the default bool value for the flag was 'false'. * Refactor the main code at linelint.go, allowing for input from STDIN. This took some inspiration from autopep8, in which to read from STDIN requires the "-" positional argument (any other positional argument is interpreted as a list of files/dirs to lint, and an "empty" list is interpreted as "lint the current directory tree"). This also makes it much easier to test the linting from the main entrypoint. * Add a couple tests as Example in linelint_test.go, using the new "read from stdin" functionality. --- README.md | 40 +++++++++--- linelint.go | 165 ++++++++++++++++++++++++++++++++++++----------- linelint_test.go | 66 +++++++++++++++++++ 3 files changed, 224 insertions(+), 47 deletions(-) create mode 100644 linelint_test.go diff --git a/README.md b/README.md index 1520e7e..0fc92d2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ See the **[#GitHub Actions](#GitHub-Actions)** and the **[#Docker](#Docker)** fo > This is a project in development. Use it at your own risk! -To run it locally, execute the binary and pass a list of file or directories as argument. +Executing the binary will automatically search the local directory tree for linting errors. ```console $ linelint . @@ -41,7 +41,7 @@ $ linelint . Total of 2 lint errors! ``` -Or: +Pass a list of files or directories to limit your search. ```console $ linelint README.md LICENSE linter/config.go @@ -52,7 +52,9 @@ Total of 1 lint errors! After checking all files, in case any rule has failed, Linelint will finish with an error (exit code 1). -If the `autofix` option is set to `true` (it is `false` by default, activate it with the `-a` flag), Linelint will attempt to fix any file with error by rewriting it. +### AutoFix + +If the `autofix` option is set to `true` (it is `false` by default, activate it with the `-a` flag or set it in the configuration file), Linelint will attempt to fix any linting error by rewriting the file. ```console $ linelint -a . @@ -62,21 +64,43 @@ $ linelint -a . [EOF Rule] File "linter/eof.go" lint errors fixed ``` -When all files are fixed successfully, Linelint terminates with with a success as well (exit code 0). +If all files are fixed successfully, Linelint terminates with exit code 0. + +### Stdin + +Pass "-" as an argument to read data from standard input instead of a list of files. + +```console +$ cat hello.txt +Hello World + + +``` + +```console +$ cat hello.txt | linelint - +Hello World +``` + +When reading from stdin, linelint behavior changes and it won't report lint errors. Instead when autofix is on, it will fix them and output the result to `/dev/stdout`. When autofix is off, it will terminate the program with an error code in case there are any linting violations, but won't output anything. + +### Help + +At any time run `linenlint --help` for a list of available command line arguments. ## Configuration -Create a `.linelint.yml` file in the same working directory you run `linelint` to adjust your settings. See [.linelint.yml](.linelint.yml) for an up-to-date example: +Create a `.linelint.yml` file in the same working directory you run `linelint` to adjust your settings. See [.linelint.yml](.linelint.yml) for an up-to-date example. ## Rules -Right now it only supports a single rule, "End of File", which is enabled by default. +Right now it supports only a single rule, "End of File", which is enabled by default. ### EndOfFile -The _End of File_ rule checks if the file ends in a newline character, or `\n`. You may find this rule useful if you dislike seeing these đŸš« symbols at the end of files on GitHub Pull Requests. +The _End of File_ rule checks if the file ends in a newline character, or `\n`. You may find it useful if you dislike seeing these đŸš« symbols at the end of files on GitHub Pull Requests. -By default it also checks if it ends strictly in a single newline character. This behavior can be disabled by setting the `single-new-line` parameter to `false`. +By default it also checks if it strictly ends in a single newline character. This behavior can be disabled by setting the `single-new-line` parameter to `false`. ```yaml rules: diff --git a/linelint.go b/linelint.go index 5ebe07e..5505dd8 100644 --- a/linelint.go +++ b/linelint.go @@ -2,8 +2,10 @@ package main import ( "bufio" + "errors" "flag" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -16,13 +18,20 @@ const helpMsg = `usage of %s [-a] [FILE_OR_DIR [FILE_OR_DIR ...]] Validates simple newline and whitespace rules in all sorts of files. positional arguments: - FILE_OR_DIR files to format + FILE_OR_DIR files to format or '-' for stdin optional arguments: ` +// Input is the main input structure to the program +type Input struct { + Paths []string + Stdin io.Reader + Config linter.Config +} + func main() { - var flagAutofix, flagVerbose bool + var flagAutofix bool flag.BoolVar(&flagAutofix, "a", false, "(autofix) will automatically fix files with errors in place") flag.Usage = func() { @@ -31,12 +40,12 @@ func main() { } flag.Parse() - var args, paths []string + var paths []string if flag.NArg() == 0 { - args = []string{"."} + paths = []string{"."} } else { - args = flag.Args() + paths = flag.Args() } config := linter.NewConfig() @@ -45,15 +54,102 @@ func main() { config.AutoFix = true } - // get paths to ignore - ignore := linter.MustCompileIgnoreLines(config.Ignore...) + input := Input{ + Paths: paths, + Stdin: os.Stdin, + Config: config, + } + + if err := run(input); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) + } +} + +func run(in Input) error { + var linters []linter.Linter + + if in.Config.Rules.EndOfFile.Enable { + linters = append(linters, linter.NewEndOfFileRule(in.Config)) + } + + if len(linters) == 0 { + return errors.New("No valid rule enabled") + } + + // read from stdin + if len(in.Paths) == 1 && in.Paths[0] == "-" { + return processSTDIN(in, linters) + } - for _, path := range args { + return processDirectoryTree(in, linters) +} + +func processSTDIN(in Input, linters []linter.Linter) error { + var lintErrors int + + b, err := ioutil.ReadAll(in.Stdin) + + if err != nil { + return fmt.Errorf("Error reading from Stdin: %v", err) + } + + if !linter.IsText(b) { + return errors.New("Stdin is not a valid UFT-8 input") + } + + for _, rule := range linters { + + valid, fix := rule.Lint(b) + + if !valid { + lintErrors++ + } + + if fix != nil { + + if err != nil { + return fmt.Errorf("[%s] Failed to fix Stdin: %v\n", rule.GetName(), err) + } + + w := bufio.NewWriter(os.Stdout) + defer w.Flush() + + _, err = w.Write(fix) + + if err != nil { + return fmt.Errorf("[%s] Failed to print fixed input to Stdout: %v\n", rule.GetName(), err) + } + + err = w.Flush() + + if err != nil { + return fmt.Errorf("[%s] Failed to flush fixed input to Stdout: %v\n", rule.GetName(), err) + } + + lintErrors-- + } + } + + if lintErrors != 0 { + // call exit directly to disable the error message + os.Exit(1) + } + + return nil +} + +func processDirectoryTree(in Input, linters []linter.Linter) error { + var files []string + + // get patterns to ignore + ignore := linter.MustCompileIgnoreLines(in.Config.Ignore...) + + for _, path := range in.Paths { f, err := os.Stat(path) if os.IsNotExist(err) { - fmt.Printf("File %q does not exist", path) - os.Exit(1) + return fmt.Errorf("File %q does not exist", path) } // if dir, walk and append only files @@ -73,38 +169,26 @@ func main() { return nil } - paths = append(paths, p) + files = append(files, p) return nil }) if err != nil { - fmt.Printf("Error walking the path %q: %v\n", path, err) - return + return fmt.Errorf("Error walking the path %q: %v\n", path, err) } } else { // if not dir, append - paths = append(paths, path) + files = append(files, path) } } var fileErrors, lintErrors int - var linters []linter.Linter - // TODO a better code for selecting rules - if config.Rules.EndOfFile.Enable { - linters = append(linters, linter.NewEndOfFileRule(config)) - } + for _, f := range files { - if len(linters) == 0 { - fmt.Printf("Fatal: no valid rule enabled\n") - os.Exit(1) - } - - for _, path := range paths { - - fr, err := os.Open(path) + fr, err := os.Open(f) if err != nil { - fmt.Printf("Error opening file %q: %v\n", path, err) + fmt.Printf("Error opening file %q: %v\n", f, err) fileErrors++ continue } @@ -112,34 +196,35 @@ func main() { defer fr.Close() if err != nil { - fmt.Printf("Skipping file %q: %v\n", path, err) + fmt.Printf("Skipping file %q: %v\n", f, err) continue } b, err := ioutil.ReadAll(fr) if err != nil { - fmt.Printf("Error reading file %q: %v\n", path, err) + fmt.Printf("Error reading file %q: %v\n", f, err) fileErrors++ continue } if !linter.IsText(b) { + // TODO add log levels // fmt.Printf("Ignoring file %q: not text file\n", path) continue } for _, rule := range linters { - if rule.ShouldIgnore(path) { - fmt.Printf("[%s] Ignoring file %q: in rule ignore path\n", rule.GetName(), path) + if rule.ShouldIgnore(f) { + fmt.Printf("[%s] Ignoring file %q: in rule ignore path\n", rule.GetName(), f) continue } valid, fix := rule.Lint(b) if !valid { - fmt.Printf("[%s] File %q has lint errors\n", rule.GetName(), path) + fmt.Printf("[%s] File %q has lint errors\n", rule.GetName(), f) lintErrors++ } @@ -147,12 +232,11 @@ func main() { fr.Close() if fix != nil { - // will erase the file - fw, err := os.Create(path) + fw, err := os.Create(f) if err != nil { - fmt.Printf("[%s] Failed to fix file %q: %v\n", rule.GetName(), path, err) + fmt.Printf("[%s] Failed to fix file %q: %v\n", rule.GetName(), f, err) break } @@ -164,18 +248,18 @@ func main() { _, err = w.Write(fix) if err != nil { - fmt.Printf("[%s] Failed to fix file %q: %v\n", rule.GetName(), path, err) + fmt.Printf("[%s] Failed to fix file %q: %v\n", rule.GetName(), f, err) break } err = w.Flush() if err != nil { - fmt.Printf("[%s] Failed to flush file %q: %v\n", rule.GetName(), path, err) + fmt.Printf("[%s] Failed to flush file %q: %v\n", rule.GetName(), f, err) break } - fmt.Printf("[%s] File %q lint errors fixed\n", rule.GetName(), path) + fmt.Printf("[%s] File %q lint errors fixed\n", rule.GetName(), f) lintErrors-- // ignore errors @@ -193,6 +277,9 @@ func main() { } if fileErrors != 0 || lintErrors != 0 { + // call exit directly to disable the error message os.Exit(1) } + + return nil } diff --git a/linelint_test.go b/linelint_test.go new file mode 100644 index 0000000..92b3e6c --- /dev/null +++ b/linelint_test.go @@ -0,0 +1,66 @@ +package main + +import ( + "strings" + + "github.com/fernandrone/linelint/linter" +) + +const textWithSingleNewLine = ` +As armas e os BarĂ”es assinalados +Que da Ocidental praia Lusitana, +Por mares nunca de antes navegados +Passaram ainda alĂ©m da Taprobana, +Em perigos e guerras esforçados, +Mais do que prometia a força humana, +E entre gente remota edificaram +Novo reino, que tanto sublimaram; +` + +const textWithTwoNewLines = ` +As armas e os BarĂ”es assinalados +Que da Ocidental praia Lusitana, +Por mares nunca de antes navegados +Passaram ainda alĂ©m da Taprobana, +Em perigos e guerras esforçados, +Mais do que prometia a força humana, +E entre gente remota edificaram +Novo reino, que tanto sublimaram; + +` + +func Example_NoFix() { + input := Input{ + Paths: []string{"-"}, + Stdin: strings.NewReader(textWithSingleNewLine), + Config: linter.NewConfig(), + } + + if err := run(input); err != nil { + panic(err) + } + + // Output: +} + +func Example_Fix() { + input := Input{ + Paths: []string{"-"}, + Stdin: strings.NewReader(textWithTwoNewLines), + Config: linter.NewConfig(), + } + + if err := run(input); err != nil { + panic(err) + } + + // Output: + // As armas e os BarĂ”es assinalados + // Que da Ocidental praia Lusitana, + // Por mares nunca de antes navegados + // Passaram ainda alĂ©m da Taprobana, + // Em perigos e guerras esforçados, + // Mais do que prometia a força humana, + // E entre gente remota edificaram + // Novo reino, que tanto sublimaram; +} From 6c9b77c1c8d763416ed743d1b141e38397de398b Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 18:19:10 -0300 Subject: [PATCH 3/8] Trim some lines --- linelint.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/linelint.go b/linelint.go index 5505dd8..de9be00 100644 --- a/linelint.go +++ b/linelint.go @@ -32,7 +32,10 @@ type Input struct { func main() { var flagAutofix bool - flag.BoolVar(&flagAutofix, "a", false, "(autofix) will automatically fix files with errors in place") + + flag.BoolVar( + &flagAutofix, "a", false, "(autofix) will automatically fix files with errors in place", + ) flag.Usage = func() { fmt.Fprintf(os.Stderr, helpMsg, os.Args[0]) @@ -118,13 +121,17 @@ func processSTDIN(in Input, linters []linter.Linter) error { _, err = w.Write(fix) if err != nil { - return fmt.Errorf("[%s] Failed to print fixed input to Stdout: %v\n", rule.GetName(), err) + return fmt.Errorf("[%s] Failed to print fixed input to Stdout: %v\n", + rule.GetName(), err, + ) } err = w.Flush() if err != nil { - return fmt.Errorf("[%s] Failed to flush fixed input to Stdout: %v\n", rule.GetName(), err) + return fmt.Errorf("[%s] Failed to flush fixed input to Stdout: %v\n", + rule.GetName(), err, + ) } lintErrors-- @@ -156,7 +163,7 @@ func processDirectoryTree(in Input, linters []linter.Linter) error { if f.IsDir() { err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error { if err != nil { - fmt.Printf("Prevent panic by handling failure accessing a path %q: %v\n", p, err) + fmt.Printf("Prevent panic by handling failure accessing %q: %v\n", p, err) return err } From e9137a9d3b0fb4ba1ff41db40d54ed402bc2910b Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 18:31:17 -0300 Subject: [PATCH 4/8] Add markdownlint step on drone --- .drone.yml | 8 ++++++++ .markdownlint.json | 4 ++++ README.md | 8 ++++---- 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 .markdownlint.json diff --git a/.drone.yml b/.drone.yml index 0b2321b..0705a77 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,6 +8,7 @@ platform: os: linux steps: + - name: get image: golang:1.14.0 commands: @@ -18,6 +19,13 @@ steps: image: fernandrone/linelint:latest pull: true + - name: markdown + image: node:1.14.3 + group: test + commands: + - npm install -g markdownlint-cli + - markdownlint . + - name: golangci-lint group: test image: golangci/golangci-lint:v1.26.0 diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..e120670 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,4 @@ +{ + "MD013": false, + "MD033": false +} diff --git a/README.md b/README.md index 0fc92d2..7377954 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Build Status](https://cloud.drone.io/api/badges/fernandrone/linelint/status.svg)](https://cloud.drone.io/fernandrone/linelint) [![Go Report Card](https://goreportcard.com/badge/github.com/fernandrone/linelint)](https://goreportcard.com/report/github.com/fernandrone/linelint) -A linter that validates simple _newline_ and _whitespace_ rules in all sorts of files. It can: +A linter that validates simple *newline* and *whitespace* rules in all sorts of files. It can: - Recursively check a directory tree for files that do not end in a newline - Automatically fix these files by adding a newline or trimming extra newlines @@ -71,7 +71,7 @@ If all files are fixed successfully, Linelint terminates with exit code 0. Pass "-" as an argument to read data from standard input instead of a list of files. ```console -$ cat hello.txt +$ cat hello.txt Hello World @@ -98,7 +98,7 @@ Right now it supports only a single rule, "End of File", which is enabled by def ### EndOfFile -The _End of File_ rule checks if the file ends in a newline character, or `\n`. You may find it useful if you dislike seeing these đŸš« symbols at the end of files on GitHub Pull Requests. +The *End of File* rule checks if the file ends in a newline character, or `\n`. You may find it useful if you dislike seeing these đŸš« symbols at the end of files on GitHub Pull Requests. By default it also checks if it strictly ends in a single newline character. This behavior can be disabled by setting the `single-new-line` parameter to `false`. @@ -126,7 +126,7 @@ This project is available at the [GitHub Actions Marketplace](https://github.com Create a workflow file at your repository's Workflow folder, like `.github/workflows/lint.yml` (see [lint.yml](.github/workflows/lint.yml) for an updated example): -``` +```yaml # .github/workflows/main.yml on: [push] name: lint From 7c760080ab8769605a6598cfa6db8f470313512b Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 18:39:40 -0300 Subject: [PATCH 5/8] Make defaultConf public and rm implict dependency on file --- linelint.go | 7 +++++-- linelint_test.go | 34 ++++++++++++++++++++-------------- linter/config.go | 14 ++++++-------- linter/config_test.go | 2 +- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/linelint.go b/linelint.go index de9be00..30ea59e 100644 --- a/linelint.go +++ b/linelint.go @@ -13,7 +13,9 @@ import ( "github.com/fernandrone/linelint/linter" ) -const helpMsg = `usage of %s [-a] [FILE_OR_DIR [FILE_OR_DIR ...]] +const ( + configFile = "./linelint.yml" + helpMsg = `usage of %s [-a] [FILE_OR_DIR [FILE_OR_DIR ...]] Validates simple newline and whitespace rules in all sorts of files. @@ -22,6 +24,7 @@ positional arguments: optional arguments: ` +) // Input is the main input structure to the program type Input struct { @@ -51,7 +54,7 @@ func main() { paths = flag.Args() } - config := linter.NewConfig() + config := linter.NewConfigFromFile(configFile) if flagAutofix { config.AutoFix = true diff --git a/linelint_test.go b/linelint_test.go index 92b3e6c..45024eb 100644 --- a/linelint_test.go +++ b/linelint_test.go @@ -29,11 +29,14 @@ Novo reino, que tanto sublimaram; ` -func Example_NoFix() { +func Example_two_new_lines() { + c := linter.NewDefaultConfig() + c.AutoFix = true + input := Input{ Paths: []string{"-"}, - Stdin: strings.NewReader(textWithSingleNewLine), - Config: linter.NewConfig(), + Stdin: strings.NewReader(textWithTwoNewLines), + Config: c, } if err := run(input); err != nil { @@ -41,13 +44,24 @@ func Example_NoFix() { } // Output: + // As armas e os BarĂ”es assinalados + // Que da Ocidental praia Lusitana, + // Por mares nunca de antes navegados + // Passaram ainda alĂ©m da Taprobana, + // Em perigos e guerras esforçados, + // Mais do que prometia a força humana, + // E entre gente remota edificaram + // Novo reino, que tanto sublimaram; } -func Example_Fix() { +func Example_single_new_line() { + c := linter.NewDefaultConfig() + c.AutoFix = true + input := Input{ Paths: []string{"-"}, - Stdin: strings.NewReader(textWithTwoNewLines), - Config: linter.NewConfig(), + Stdin: strings.NewReader(textWithSingleNewLine), + Config: c, } if err := run(input); err != nil { @@ -55,12 +69,4 @@ func Example_Fix() { } // Output: - // As armas e os BarĂ”es assinalados - // Que da Ocidental praia Lusitana, - // Por mares nunca de antes navegados - // Passaram ainda alĂ©m da Taprobana, - // Em perigos e guerras esforçados, - // Mais do que prometia a força humana, - // E entre gente remota edificaram - // Novo reino, que tanto sublimaram; } diff --git a/linter/config.go b/linter/config.go index 288ed87..fa09b22 100644 --- a/linter/config.go +++ b/linter/config.go @@ -38,34 +38,32 @@ type EndOfFileConfig struct { SingleNewLine bool `yaml:"single-new-line"` } -// NewConfig returns a new Config -func NewConfig() Config { - path := ".linelint.yml" - +// NewConfigFromFile returns a new Config +func NewConfigFromFile(path string) Config { var data []byte // check if config file exists if _, err := os.Stat(path); err != nil { - return newDefaultConfig() + return NewDefaultConfig() } // if config file does exist, read it data, err := ioutil.ReadFile(path) if err != nil { fmt.Printf("Error reading YAML file %s: %s (will use default configuration)\n", path, err) - return newDefaultConfig() + return NewDefaultConfig() } var config Config if err := yaml.Unmarshal(data, &config); err != nil { fmt.Printf("Error parsing YAML file: %s (will use default configuration)\n", err) - return newDefaultConfig() + return NewDefaultConfig() } return config } -func newDefaultConfig() Config { +func NewDefaultConfig() Config { return Config{ AutoFix: false, Ignore: []string{".git/"}, diff --git a/linter/config_test.go b/linter/config_test.go index 29288cb..3461fe2 100644 --- a/linter/config_test.go +++ b/linter/config_test.go @@ -39,7 +39,7 @@ func TestDefaultConfig(t *testing.T) { t.Fatalf("yaml.Unmarshal(Config): %v", err) } - if !reflect.DeepEqual(c, newDefaultConfig()) { + if !reflect.DeepEqual(c, NewDefaultConfig()) { t.Errorf("yaml.Unmarshal(Config):\n\tExpected %+v, got %+v", autofixTestConf, c) } } From 5680d8cfd26ca7b2e52994758145caf97ef3385a Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 18:43:07 -0300 Subject: [PATCH 6/8] Fix .drone.yml node img --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 0705a77..e3f8205 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,7 +20,7 @@ steps: pull: true - name: markdown - image: node:1.14.3 + image: node:14.1.3 group: test commands: - npm install -g markdownlint-cli From 4396f2bc72b4ce70f84710ebe4e7d4c1e783042f Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 20:19:27 -0300 Subject: [PATCH 7/8] Fix default linelint path and gitignore tests --- linelint.go | 2 +- linter/config.go | 5 ++-- linter/ignore_test.go | 64 +++++++++++++++++++++++++++---------------- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/linelint.go b/linelint.go index 30ea59e..5609810 100644 --- a/linelint.go +++ b/linelint.go @@ -14,7 +14,7 @@ import ( ) const ( - configFile = "./linelint.yml" + configFile = "./.linelint.yml" helpMsg = `usage of %s [-a] [FILE_OR_DIR [FILE_OR_DIR ...]] Validates simple newline and whitespace rules in all sorts of files. diff --git a/linter/config.go b/linter/config.go index fa09b22..9e04f95 100644 --- a/linter/config.go +++ b/linter/config.go @@ -44,19 +44,20 @@ func NewConfigFromFile(path string) Config { // check if config file exists if _, err := os.Stat(path); err != nil { + fmt.Printf("No configuration file found at %s (will use default configuration)\n", path) return NewDefaultConfig() } // if config file does exist, read it data, err := ioutil.ReadFile(path) if err != nil { - fmt.Printf("Error reading YAML file %s: %s (will use default configuration)\n", path, err) + fmt.Printf("Error reading configuration file %s: %s (will use default configuration)\n", path, err) return NewDefaultConfig() } var config Config if err := yaml.Unmarshal(data, &config); err != nil { - fmt.Printf("Error parsing YAML file: %s (will use default configuration)\n", err) + fmt.Printf("Error parsing configuration file: %s (will use default configuration)\n", err) return NewDefaultConfig() } diff --git a/linter/ignore_test.go b/linter/ignore_test.go index 1ed1de6..10710c6 100644 --- a/linter/ignore_test.go +++ b/linter/ignore_test.go @@ -6,55 +6,73 @@ import ( "gopkg.in/yaml.v2" ) -var ignoreTests = []struct { - file string - ignore bool -}{ - {"README", false}, - {".git/objects/04/9f2973ffc85f71da1fd5a", true}, -} +func TestShouldIgnore_DefaultConf(t *testing.T) { + c := Config{} -var yamlAutofixTestConfig = ` -autofix: true + err := yaml.Unmarshal([]byte(`ignore: [ ".git/" ]`), &c) -ignore: - - .git/ + if err != nil { + t.Fatalf("yaml.Unmarshal(Config): %v", err) + } -rules: - end-of-file: - enable: true - disable-autofix: false - single-new-line: true -` + ignoreTests := []struct { + file string + ignore bool + }{ + {".git/objects/04/9f2973ffc85f71da1fd5a", true}, + {"README", false}, + {"clusters/mycluster/applications/app.yml", false}, + {"java/bin/myclass.class", false}, + } -func TestShouldIgnore_DefaultConf(t *testing.T) { for _, tt := range ignoreTests { t.Run(tt.file, func(t *testing.T) { - got := NewEndOfFileRule(autofixTestConf).ShouldIgnore(tt.file) + got := NewEndOfFileRule(c).ShouldIgnore(tt.file) want := tt.ignore if got != want { - t.Errorf("NewEndOfFileRule(defaultTestConf).ShouldIgnore(%q):\n\tExpected %v, got %v", tt.file, want, got) + t.Errorf( + "NewEndOfFileRule(c).ShouldIgnore(%q):\n\tExpected %v, got %v", + tt.file, want, got, + ) } }) } } -func TestShouldIgnore_YAMLParsedConf(t *testing.T) { +func TestShouldIgnore_MoreComplexConf(t *testing.T) { c := Config{} - err := yaml.Unmarshal([]byte(yamlAutofixTestConfig), &c) + err := yaml.Unmarshal( + []byte(`ignore: [ ".git/", "**/bin/", "applications", "/projects/" ]`), &c, + ) + if err != nil { t.Fatalf("yaml.Unmarshal(Config): %v", err) } + ignoreTests := []struct { + file string + ignore bool + }{ + {".git/objects/04/9f2973ffc85f71da1fd5a", true}, + {"README", false}, + {"clusters/mycluster/applications/app.yml", true}, + {"home/projects/data.md", false}, + {"projects/data.md", true}, + {"java/bin/myclass.class", true}, + } + for _, tt := range ignoreTests { t.Run(tt.file, func(t *testing.T) { got := NewEndOfFileRule(c).ShouldIgnore(tt.file) want := tt.ignore if got != want { - t.Errorf("NewEndOfFileRule(defaultTestConf).ShouldIgnore(%q):\n\tExpected %v, got %v", tt.file, want, got) + t.Errorf( + "NewEndOfFileRule(c).ShouldIgnore(%q):\n\tExpected %v, got %v", + tt.file, want, got, + ) } }) } From af685f4ea2f4ea1aa070c2318d01efed11e38401 Mon Sep 17 00:00:00 2001 From: Fernando Barbosa Date: Fri, 9 Oct 2020 20:44:56 -0300 Subject: [PATCH 8/8] Fix (for real) drone.yml node img --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index e3f8205..7bf699b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,7 +20,7 @@ steps: pull: true - name: markdown - image: node:14.1.3 + image: node:14.13.1 group: test commands: - npm install -g markdownlint-cli