Victor is a full stack software engineer who loves travelling and building things. Most recently created Ewolo, a cross-platform workout logger.
A quick introduction to Go modules

This is a great time to jump into Go programming since starting with Go 1.12, we have module support and we don't have to have muck about with the GOPATH environment variable. This article is a quick introduction to Go modules and while the examples are provided for Ubuntu 18.04, the concepts are operating system agnostic.

A Go module is a collection of related go packages. More specifically, a Go module is a tree of Go source files which have a go.mod file. This file contains the module import name, dependency requirements, exclusions and replacements.

Assuming that go is already installed and available, let's see go modules in action. Before we begin however, note that the project directories below need to be outside your $GOPATH because Go module support is disabled inside it for backwards compatibility. Let's start by creating a module:


$ mkdir hello-world-mod
$ cd hello-world-mod
$ vim hello-world-mod.go

package helloworld

import "fmt" 

// Hello returns a friendly greeting
func Hello(name string) string {
    return fmt.Sprintf("Hello, %s", name)
}

Now convert this package into a module via: go mod init github.com/smalldatatech/hello-world-mod. This creates a relevant go.mod file. We will now push and tag this module:


$ git init 
$ git add * 
$ git commit -am "First commit" 
$ git push -u origin master
$ git tag v1.0.0
$ git push --tags

We can now use our freshly minted module in a project:


$ mkdir ~/hello-world-go
$ cd ~/hello-world-go
$ go mod init github.com/smalldatatech/hello-world-go
$ go get github.com/smalldatatech/hello-world-mod
go: finding github.com/smalldatatech/hello-world-mod v1.0.0
go: downloading github.com/smalldatatech/hello-world-mod v1.0.0
go: extracting github.com/smalldatatech/hello-world-mod v1.0.0
$ vim hello.go

package main

import (
	"fmt"

	helloworld "github.com/smalldatatech/hello-world-mod"
)

func main() {
	fmt.Println(helloworld.Hello("Snoop"))
}

Test that this works via go run hello.go. Note that apart from go.mod, there is a go.sum file also created which contains cryptographic checksums of module dependencies. It is used to verify that cached dependencies meet module requirements.

Go modules are integrated with Go tools and on invocation of commands (go build, go get, etc), actions such as populating the cache, creating or updating go.mod and go.sum will automagically happen.

The following is an overview of module management commands:

Initialize module
go mod init <project-root-name>
Vendor dependencies
go mod vendor, this will create a vendor directory under the root of your project containing the source code for all dependencies. However, go build will ignore the contents of this directory by default. To build dependencies from the vendor directory run, go build -mod vendor
Install/clean-up dependencies
go mod tidy
Pin a dependency
go mod edit -require some/module@version or simply go get package@version
Print dependency graph
go mod graph
Get help
go help mod

Note that the actual dependencies are stored under $GOPATH/pkg/mod which when using gvm translates to: ~/.gvm/pkgsets/go1.12.1/global/pkg/mod.

Well that's about it, Go modules are really cool and they remind me a lot about Node.js package management, maybe even better since the dependencies are cached globally. Happy coding!

References