-1

I am aware of Golang requirements.txt equivalent , but the context of the question is rather different.

I am trying to optimize builds of golang programs inside a docker container. My dockerfile looks something like this:

FROM golang:1.12.5 as builder

WORKDIR $GOPATH/src/test-ldap/

COPY main.go .

RUN go get -d -v ./...
...

while my main.go looks like

package main

import (
  "log"
  "fmt"
  "gopkg.in/ldap.v3"
)

func main() {
...

Of course, every time I make a change in the source code, the docker layer
COPY main.go . is changed, hence the go get command needs to be re-run and cannot be reused from the docker build cache even though the import block is unchanged.

Now of course I could type something like

RUN go get -d -v log fmt gopkg.in/ldap.v3

and place this before the COPY statement, but this violates the so-called single source of truth principle. I would then have to change the same thing in two different places in my codebase if I will ever wish to add extra imports.

How can I store my import requirements in a separate file? What is an idiomatic way to do this in go development?

LLlAMnYP
  • 223
  • 3
  • 11
  • 3
    Why don't you just vendor your deps? – Jonathan Hall Jul 18 '19 at 14:44
  • An alternative strategy is to maintain the go build dependencies across docker builds. This should be [achievable as highlighted here](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md#example-cache-go-packages). However, I tried it quickly myself and couldn't immediately get the expected benefit, so YMMV~ – JamesJJ Jul 18 '19 at 14:46
  • @Flimzy I think I understand from glancing at google "golang vendor dependencies" (I am too new to go to know the right search queries), but if you can care to elaborate, that would be nice. – LLlAMnYP Jul 18 '19 at 14:51
  • https://stackoverflow.com/q/35109393/13860 – Jonathan Hall Jul 18 '19 at 14:52
  • @Flimzy but won't I still have to keep `import ( "this" "that" )` in the source, even if the package is vendored? As I asked, that means I have to both, keep track of the packages in my codebase, plus update the `import` statement. – LLlAMnYP Jul 18 '19 at 15:02
  • Of course you still need to import the packages you need. I don't understand what you think the problem is. – Jonathan Hall Jul 18 '19 at 15:03
  • @Flimzy when I build a docker container I want to separately add info about the packages to be imported, then run `go get` then add the rest of my source code. If the list of packages to import and the source code is stored in one file, the docker daemon cannot use its cache to skip the `go get` step if I change the source code but not the `import` part. I additionally do not want to store the same information in two different places. – LLlAMnYP Jul 18 '19 at 15:17
  • 1
    If you vendor your packages, you don't need to do `go get` at all--they're already cached. – Jonathan Hall Jul 18 '19 at 15:19
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/196646/discussion-between-lllamnyp-and-flimzy). – LLlAMnYP Jul 18 '19 at 15:19

1 Answers1

2

Use Go modules. Then, treat the resulting go.mod file just as you would a requirements.txt:

FROM golang:1.12.5 as builder

# NOT in $GOPATH (or explicitly set GO111MODULES=on)
WORKDIR /usr/src/test-ldap/

COPY go.mod .
RUN go mod download  # alternatively: "go mod vendor" to build a vendor/ dir instead

COPY main.go .
# ...
helmbert
  • 35,797
  • 13
  • 82
  • 95
  • I have come to the realization that the python-like requirements.txt also doesn't solve the second part of my question, since now the list of packages to import must be contained **both** in go.mod and in main.go. If only I could put something like `import ( #include "go.mod" )` inside my main.go, like it can be done in C. As such, +1 and I'll accept if nothing else comes up. – LLlAMnYP Jul 19 '19 at 10:46