Lint - Run linters from Go

Lint is a library that automatically downloads and runs linters - tools that analyze source code for errors. It integrates easily into tests and makes it trivial to run a variety of linters as part of your test workflow without any extra scripts.

Why?

While building our mobile calendar app TimeFerret in Go, we started using linters and found that setting up build scripts per repository and using grep to ignore false positives grew tiresome quickly. Lint was built to address these issues.

How?

Download


    go get -t github.com/surullabs/lint
        

and create a test


    import (
        "testing"
        "github.com/surullabs/lint"
    )

    func TestLint(t *testing.T) {
        if err := lint.Default.Check("./..."); err != nil {
            t.Fatal("lint failures: %v", err)
        }
    }
        

Eliminating false positives

Even the best linters can have false positives, making it difficult to run them in an automated manner. The usual solution to this is to either turn off entire classes of checks or use error prone mechanisms like lint thresholds. Since Lint tests are part of your regular source code, any false positives can be ignored in your tests and reviewed as part of a normal code review.


    func TestLint(t *testing.T) {
        err := lint.Default.Check("./...")

        // Ignore lint errors from auto-generated files
        ignored := lint.RegexpMatch(`_string\.go`, `\.pb\.go`)
        err = lint.Skip(err, ignored)

        if err != nil {
            t.Fatal("lint failures: %v", err)
        }
     }
        

Enforce linter usage with no operational overhead

Since Lint automatically downloads linters, a go get followed by go test will run the required linters without any extra setup needed. This makes it extremely easy for both public open source projects and internal company projects to enforce linter usage without custom Makefiles or detailed instructions.


    func TestLint(t *testing.T) {
        // Create a custom lint policy
        custom := lint.Group{
            gofmt.Check{},             // Enforce gofmt usage
            govet.Check{},             // Use govet without -shadow
            golint.Check{},            // Enforce Google Go style guide
            dupl.Check{Threshold: 25}, // Identify duplicates
            gosimple.Check{},          // Simplification suggestions
            gostaticcheck.Check{},      // Verify function parameters
            aligncheck.Check{},        // Struct alignment issues
            structcheck.Check{},       // Unused struct fields
            varcheck.Check{},          // Unused global variables
        }

        err := custom.Check("./...")

        err = lint.Skip(
            err,

            // Ignore lint errors from auto-generated files
            lint.RegexpMatch(`_string\.go`, `\.pb\.go`),

            // Ignore all duplicates with just two instances
            dupl.SkipTwo)

        if err != nil {
            t.Fatal("lint failures: %v", err)
        }
     }
        

Easy CI integration

A combination of the above means all commits must pass lint checks. Since Lint integrates linters into tests any lint failure will now also show up as a broken test, without the overhead of setting up a special CI plugin.

Fork me on GitHub