3 minutes
Go Modules: Working outside GOPATH
When I started learning Golang (a couple of months ago) one of the things that I concerned most with was the project structure.
It may sound irrelevant to bother with this when you are learning the language; but, every time I am browsing .net core applications or libraries on GitHub, I find it extremely unappealing when certain projects are not structured in a conventional way.
My starting point was the following talk:
Being that I did not know how to manage dependencies, I started investigating and saw that lots of people were using dep
, and how bad could it be?
I started building a sample app that that could be containerised (again, just to see how the Dockerfile would look like), and the following happened:
FROM golang:1.12-alpine AS builder
COPY . /go/src/github.com/pmorelli92/go-ddd-cqrs/
WORKDIR /go/src/github.com/pmorelli92/go-ddd-cqrs/
RUN set -x && go get github.com/golang/dep/cmd/dep && dep ensure -v
RUN CGO_ENABLED=0 go build -a -o goapp ./cmd/server/main.go
FROM scratch
WORKDIR /root/
COPY --from=builder /go/src/github.com/pmorelli92/go-ddd-cqrs/goapp .
EXPOSE 8080
ENTRYPOINT ["./goapp"]
That Dockerfile, as ugly as it looks, was the result of some iterations were I was getting path errors, dependency errors, and others. The constraint of only working on GOPATH
gave me headaches and I had to use a really bad WORKDIR
as you can see above.
But then after some searching, I came across that GO Modules was supported from version 1.11, so I decided to give it a try.
And this is how the Dockerfile looks like now:
FROM golang:1.12-alpine AS builder
WORKDIR /app
ADD go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -a -o goapp ./cmd/main.go
FROM scratch
COPY --from=builder /app/goapp .
EXPOSE 8080
ENTRYPOINT ["./goapp"]
We can achieve the same using golang:1.11
but we wil have to execute an additional step ENV GO111MODULE=on
before RUN go mod download
.
So how do we do this? Easily!
mkdir my-demo // Create a project folder, or clone a github repository
cd my-demo
touch main.go // Create a file if we are starting the project
go mod init my-demo // This will initiate the module
After doing this, we can proceed installing dependencies by doing go get -u <path>
go get -u github.com/labstack/echo/v4
This will be tracked on the go.mod
where it will state all the dependencies required for the project:
my-demo > cat go.mod
module my-demo
go 1.12
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/labstack/echo/v4 v4.0.0 // indirect
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/stretchr/objx v0.1.1 // indirect
github.com/valyala/fasttemplate v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
golang.org/x/sys v0.0.0-20190312061237-fead79001313 // indirect
)
And we are done! Now we can edit the main.go
to this:
package main
import "net/http"
import "github.com/labstack/echo/v4"
func main() {
e := echo.New()
e.GET("/hello", func(c echo.Context) error {
return c.JSON(http.StatusOK, "hey there")
})
_ = e.Start(":8080")
}
If now we look again at go.mod
we should see that the module github.com/labstack/echo/v4 v4.0.0
is no longer indirect, that means that it is being actively used on the code.
Things to remember
- Both go.mod and
go.sum
have to be committed. - When building a docker file, in order to take advantage of the layered cache steps, we should first copy
go.mod
andgo.sum
and then executingRUN go mod download
; after that, we can safely copy our code.