DEV Community

Enda
Enda

Posted on

Committing Better Go Code with Static Analysis and Git Hooks

I've been using Go for a while now and there are three static analysis tools (linters) that I use daily. There are dozens more that I could be using, but I don't think it's wise to become too dependent on these tools (most of which are unofficial) to do your job for you. Eventually they will become a nightmare to track, maintain and they may eventually become unmaintained or unsupported.

Gofmt

Gofmt is an official tool for formatting Go code that has come as part of the language since 2013.

It's as simple as this to format a file:

$ go fmt main.go
Enter fullscreen mode Exit fullscreen mode

Or you can target an entire package:

$ go fmt ./pkg/models
Enter fullscreen mode Exit fullscreen mode

To format all packages (excluding vendor) beneath the current directory:

$ go fmt $(go list ./... | grep -v /vendor/)
Enter fullscreen mode Exit fullscreen mode

errcheck

In the maker's own words, errcheck "checks that you checked errors". In other words, it will scan your code and tell you if there are any errors. This can be useful for those times you sillily forget to run your application after making a small change.

To check errors in a package:

$ errcheck ./pkg/models
Enter fullscreen mode Exit fullscreen mode

Or to scan all packages (excluding vendor) beneath the current directory:

errcheck $(go list ./... | grep -v /vendor/)
Enter fullscreen mode Exit fullscreen mode

Vet

Vet is another official static analysis tool in Go. Vet inspects the source code for suspicious constructs, such as Printf calls whose arguments do not align with the format string.

It's execution is very similar to that of Gofmt.

Vetting a single file:

$ go vet main.go
Enter fullscreen mode Exit fullscreen mode

Vetting an entire package:

$ go vet ./pkg/models
Enter fullscreen mode Exit fullscreen mode

Vetting all packages (excluding vendor) beneath the current directory:

$ go fmt $(go list ./... | grep -v /vendor/)
Enter fullscreen mode Exit fullscreen mode

Automating These Tools With Git Hooks

Git hooks are a great way to automate these repetitive tasks and ensure you don't commit or push any incomplete code. The hooks are all stored in the .git/hooks subdirectory of your Git project and will automatically execute on or after a pre-defined list of Git events.

For this, we are going to bind to the pre-commit hook as we only want our script to check the files before committing them to Git.

Create a new file in .git/hooks called pre-commit and open it in your favourite editor. Make it executable by running chmod +x .git/hooks/pre-commit from the terminal.

First, let's store the list of files in our project into a variable called FILES:

FILES=$(go list ./...  | grep -v /vendor/)
Enter fullscreen mode Exit fullscreen mode

Add Gofmt next to format your code:

go fmt ${FILES}
Enter fullscreen mode Exit fullscreen mode

Once formatted, the most important thing we need to check for is if there are any errors. If there are we can exit the script and prevent the commit.

{
    errcheck -ignoretests ${FILES}
} || {
    exitStatus=$?

    if [ $exitStatus ]; then
        printf "\nErrors found in your code, please fix them and try again."
        exit 1
    fi
}
Enter fullscreen mode Exit fullscreen mode

If the code is error-free, the next thing we should check is if there are any suspicious constructs.

{
    go vet ${FILES}
} || {
    exitStatus=$?

    if [ $exitStatus ]; then
        printf "\nIssues found in your code, please fix them and try again."
        exit 1
    fi
}
Enter fullscreen mode Exit fullscreen mode

Here's the complete pre-commit hook. Add this to your project today and start committing better Go code.

Top comments (2)

Collapse
 
jonasbn profile image
Jonas Brømsø

Thanks for the list of awesome tools.

As for pre-commit hooks, you should have a look at the pre-commit project.

The tools you mention already have hook implementations, which make it very easy to get going, search for "golang" on the page referenced.

Collapse
 
jonasbn profile image
Jonas Brømsø

I have been playing with the Go hooks listed in the resource I mentioned this afternoon. After observing different issues, I came across a recommendation for this collection, which until now has proven to be the best implementation.