I recently read Dave Cheney’s post on converting httpstat to use Go 1.11’s module support in a Travis CI workflow. Go 1.11 is nearing official release so it’s a good time to start using Go modules in CircleCI, a continuous integration and delivery platform similar to Travis CI.

Go Modules

Go modules are collections of related Go packages. Go 1.11 provides experimental support for working with modules using the go command.

Module behavior is controlled by an environment variable, GO111MODULE , which can be set to on , off or auto (default). When GO111MODULE is set to off , the go command maintains the same functionality as in previous versions and uses packages in the GOPATH during the build. If GO111MODULE is set to on , the go command disregards the GOPATH and requires the use of modules instead.

Setting GO111MODULE to auto (or unset) allows both GOPATH and module support with some caveats. In this mode, the behavior of the go command is dictated by the location of the current directory. If the current directory is outside the GOPATH and contains a go.mod file, then module support is enabled. Otherwise, the GOPATH and vendor directories are used as in previous versions of Go.

There are several good articles about working with Go modules and the go.mod file. Please take a look at the Resources section for more information.

CircleCI

Let’s create a Go module and use CircleCI to illustrate the auto module behavior in Go 1.11. We’ll start with a very simple module that includes a unit test. The module has two external dependencies so we’ll get to see dependency management in action. Source for the example is available on Github.

First, create a file called helloworld.go that contains the following:

package gomodules

import (

log "github.com/sirupsen/logrus"

) func sayHello() string {

log.Info("saying hello...")

return "Hello, world"

}

Let’s also create the test for helloworld.go called helloworld_test.go :

package gomodules import (

"testing"

"github.com/stretchr/testify/assert"

) func Test_HelloWorld(t *testing.T) {

tests := map[string]struct {

expectedResponse string

}{

"success": {

expectedResponse: "Hello, world",

},

}



for name, test := range tests {

t.Logf("Running test case: %s", name)

response := sayHello()

assert.Equal(t, test.expectedResponse, response)

}

}

We can execute the tests for Go 1.10 in CircleCI by adding the following config.yml file to the project and configuring the Github repository in CircleCI (see the Resources section for more information on configuring a Go project in CircleCI).

# Golang CircleCI 2.0 configuration file

version: 2

jobs:

build-go1.10:

docker:

- image: circleci/golang:1.10

working_directory: /go/src/github.com/tkeech1/gomodules

steps:

- checkout

- run: go get -v -t -d ./...

- run: go test -race workflows:

version: 2

build_and_test:

jobs:

- build-go1.10

This example uses CircleCI Workflows. Workflows can execute multiple jobs in parallel during each build. The steps section defines the specific commands to run for each job. In this example, the steps are:

Checkout the Github repository to the working_directory . In this case, the working_directory is located on the GOPATH .

. In this case, the is located on the . go get the package and test dependencies.

the package and test dependencies. Run the tests.

CircleCI build results for Go 1.10

The CircleCI build shows that the go get and go test job steps executed successfully. We have a working build for Go 1.10.

Go 1.11 Modules and CircleCI

Now we’ll add a CircleCI job for Go 1.11 without using modules. We can use the same configuration as the Go 1.10 build job, substituting the Go 1.11 image for the Go 1.10 image.

# Golang CircleCI 2.0 configuration file

version: 2

jobs:

build-go1.10:

docker:

- image: circleci/golang:1.10

working_directory: /go/src/github.com/tkeech1/gomodules

steps:

- checkout

- run: go get -v -t -d ./...

- run: go test -race

build-go1.11:

docker:

- image: circleci/golang:1.11-rc

working_directory: /go/src/github.com/tkeech1/gomodules

steps:

- checkout

- run: go get -v -t -d ./...

- run: go test -race workflows:

version: 2

build_and_test:

jobs:

- build-go1.10

- build-go1.11

To add a new CircleCI build that uses Go modules, we must first initialize the module using the Go 1.11 go mod command. One way to do this without installing Go 1.11 is to use Docker. The command below starts a Go 1.11 Docker container, mounts the local source into the container, and runs the module initialization command.

docker run -it --rm -v $PWD:/opt golang:1.11-rc go mod -init -module github.com/tkeech1/gomodules

Update (8/7/2018) — The beta3 version of Go uses the following command to initialize a module:

docker run -it --rm -v $PWD:/opt golang:1.11-rc go mod init github.com/tkeech1/gomodules

Module initialization produces a go.mod file containing the following:

module github.com/tkeech1/gomodules

Let’s add dependencies. Many of the go tool commands such as build , run , and test are module-aware and will add missing dependencies to the go.mod file. In addition to adding missing dependencies, the go mod -sync command will also remove unnecessary dependencies. You can run it in Docker with the following command:

docker run -it --rm -v $PWD:/opt --workdir /opt golang:1.11-rc go mod -sync

Update (8/7/2018) — The beta3 version of Go uses the following command to synchronize dependencies:

docker run -it --rm -v $PWD:/opt --workdir /opt golang:1.11-rc go mod tidy

The dependencies are now listed in the go.mod file.

module github.com/tkeech1/gomodules require (

github.com/davecgh/go-spew v1.1.0 // indirect

github.com/fsnotify/fsnotify v1.4.7 // indirect

github.com/golang/protobuf v1.1.0 // indirect

github.com/hpcloud/tail v1.0.0 // indirect

github.com/onsi/ginkgo v1.6.0 // indirect

github.com/onsi/gomega v1.4.1 // indirect

github.com/pmezard/go-difflib v1.0.0 // indirect

github.com/sirupsen/logrus v1.0.6

github.com/stretchr/testify v1.2.2

golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb // indirect

golang.org/x/net v0.0.0-20180724234803-3673e40ba225 // indirect

golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect

golang.org/x/sys v0.0.0-20180727230415-bd9dbc187b6e // indirect

golang.org/x/text v0.3.0 // indirect

gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect

gopkg.in/fsnotify.v1 v1.4.7 // indirect

gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect

gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect

gopkg.in/yaml.v2 v2.2.1 // indirect

)

Finally, create the Go 1.11 module build configuration in CircleCI by adding a new job to the config.yml file.

# Golang CircleCI 2.0 configuration file

version: 2

jobs:

build-go1.10:

docker:

- image: circleci/golang:1.10

working_directory: /go/src/github.com/tkeech1/gomodules

steps:

- checkout

- run: go get -v -t -d ./...

- run: go test -race

build-go1.11:

docker:

- image: circleci/golang:1.11-rc

working_directory: /go/src/github.com/tkeech1/gomodules

steps:

- checkout

- run: go get -v -t -d ./...

- run: go test -race

build-go1.11-gomodules:

docker:

- image: circleci/golang:1.11-rc

working_directory: ~/github.com/tkeech1/gomodules

steps:

- checkout

- run: go test -race workflows:

version: 2

build_and_test:

jobs:

- build-go1.10

- build-go1.11

- build-go1.11-gomodules

There are a few minor changes to the new job, build-go1.11-gomodules :

There’s no need to run go get . go test will handle downloading the necessary dependencies listed in the go.mod file.

. will handle downloading the necessary dependencies listed in the file. The working_directory is located outside the GOPATH

The GO111MODULE environment variable is unset (defaulting to auto ) in the Docker container and there’s an existing go.mod file so the build will use Go modules.

CircleCI build results for Go 1.11 with modules

In the CircleCI output, we see that running go test caused the build to download the dependencies listed in the go.mod file. Now we have a CircleCI workflow that builds and tests a small project on Go 1.10, Go 1.11 and Go 1.11 with modules.

Summary

Modules are an experimental feature in Go 1.11

The GO111MODULE environment variable determines the behavior of Go 1.11’s module support

environment variable determines the behavior of Go 1.11’s module support CircleCI Workflows can be configured to build a single project using both the traditional GOPATH approach and modules

I hope this was helpful. Thanks for reading.

Resources

Using Go Modules with Travis CI

Go Modules Have Landed

Versioned Go Modules Proposal

CircleCI Workflows

Configuring a Go project in CircleCI