Structuring my Go projects
Recently I've been maintaining a Github repository to serve as a generic template for my Golang projects, and its been working rather well for me.
The repository is here: Self-contained Go Project
The basic idea is that using this template, you can setup a Go project with
vendored dependencies not just for the main project but also for every tool
used in building and linting it (with the exception of make
, git
and a
working Golang install).
go get <my project>
cd $GOPATH/src/<my project>
make
does a production build.
How to Use It
Out of the box (i.e. git clone https://github.com/wrouesnel/self-contained-go-project.git
)
on a Linux machine it should be all setup to go. I've made some effort to try
and remove Linux specific things from it, but since I don't run Mac OS or
Windows for Go development it's probably not working too well there.
Essentially, it'll build multi-platform, CGO-less binaries for any main
package you place in a folder underneath the cmd
directory. Running make binary
will build all current commands for your current platform and symlink them into
the root folder, while running make release
will build all binaries and then
create tarballs with the name and version in the release
directory.
It also includes bevy of other CI-friendly commands - namely make style
which
checks for gofmt
and goimports
formatting and make lint
which runs
gometalinter against the entire
project.
Philosophy
Just looking at the commands, the main thing accomplished is a lot of use of
make
. It's practically used for ergonomics more then utility to some level
since make
is a familiar "build whatever this is" command in the Unix world.
But, importantly, make
is used correctly - build dependencies are expressed
and managed in a form it understands so it only rebuilds as necessary.
But there is more important element, and that is not just that there is a
Makefile but that the repository for the project, through govendor
ing includes
not just the code but also the linting and checking tools needed to build it,
and a mechanism to update them all.
Under the tools
directory we have a secondary Makefile
which is called from
the top-level and is reposible for managing the tools. By running make update
here we can go get
a new version of gometalinter
, extract the list of tools
it runs, then automatically have them updated and installed inside the source
directory and made available to the top level Makefile
to use to run CI tasks.
This combines to make project management extremely ergonomic in my opinion,
and avoids dragging a heavier tool like Docker
into the mix (which often means
some uncontrolled external dependencies).
Basically: you check in everything your project needs to be built and run and tested into the one Git repository, because storage is cheap but your time is not and external dependencies can't be trusted to always exist.
Conclusion
It's not the be all and end all - in build tooling there never is one, but I'm thusfar really happy with how this basic structure has turned out as I've evolved it and it's proven relatively easy to extend when I need to (i.e. adding more testing levels, building web assets as well with npm and including them in the go-binary etc.)