Getting Started
Bootstrapping a Go development environment can be quite repetitive as we need to put in quite a lot of tooling and coordination to get an easy to work with workspace like for example watch for file change and recompile.
In this article I will put the necessary tooling that need for Go development into one bundle by building a Docker base image that we can re-use for future development. What we need to include in the base image are following.
- File watcher entr
- Git configuration that used SSH over HTTPS to pull in private Go packages
- Custom script to recompile our project when file change
Adding File Watcher
To start of we will begin with compiling a file watcher binary. Our goal is to get a working sandbox environment while maintain as small image size as possible. We will do so by using docker multi-stage build with Alpine Linux as base image.
1 2 | <span class="token keyword">FROM</span> alpine<span class="token punctuation">:</span>3.10 AS builder |
Then we install neccessary dependencies to compile a binary, pull in the source code and do the compilation.
1 2 3 4 5 6 | <span class="token keyword">RUN</span> apk add <span class="token punctuation">-</span><span class="token punctuation">-</span>no<span class="token punctuation">-</span>cache curl g++ make <span class="token keyword">RUN</span> curl <span class="token punctuation">-</span>O http<span class="token punctuation">:</span>//eradman.com/entrproject/code/entr<span class="token punctuation">-</span>4.3.tar.gz <span class="token keyword">RUN</span> tar <span class="token punctuation">-</span>xvf entr<span class="token punctuation">-</span>4.3.tar.gz && cd entr<span class="token punctuation">-</span>4.3 && curl <span class="token punctuation">-</span>O http<span class="token punctuation">:</span>//entrproject.org/patches/entr<span class="token punctuation">-</span>3.9<span class="token punctuation">-</span>wsl && patch <span class="token punctuation">-</span>p1 < entr<span class="token punctuation">-</span>3.9<span class="token punctuation">-</span>wsl && ./configure && make |
After that we copy the final binary file from /usr/local/bin
into our final build stage, which based on official Go docker image
1 2 3 4 5 6 7 8 9 | <span class="token keyword">RUN</span> mv entr /usr/local/bin <span class="token keyword">FROM</span> golang<span class="token punctuation">:</span>1.13.5<span class="token punctuation">-</span>alpine3.10 <span class="token keyword">ENV</span> PACKAGE= <span class="token keyword">ENV</span> MAIN_APP= <span class="token keyword">COPY</span> <span class="token punctuation">-</span><span class="token punctuation">-</span>from=builder /usr/local/bin/entr /usr/local/bin/ |
After this step we are ready to go on to the next step
Play Nice with Private Go Package
The next step is to configure git to be able to pull in private package
1 2 3 4 5 | <span class="token keyword">RUN</span> apk add <span class="token punctuation">-</span><span class="token punctuation">-</span>no<span class="token punctuation">-</span>cache git openssh <span class="token keyword">RUN</span> echo $<span class="token string">'[url "<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>:"]n insteadOf = https://github.com/'</span> <span class="token punctuation">></span><span class="token punctuation">></span> /root/.gitconfig <span class="token keyword">RUN</span> mkdir /root/.ssh && echo <span class="token string">"StrictHostKeyChecking no "</span> <span class="token punctuation">></span> /root/.ssh/config |
Line 2 used to change go get from using HTTPS to SSH instead. Line 3 disable strict host checking to remove the need of user interaction when using ssh for the first time.
Adding Custom Re-Compile Script
And the final step is to write a custom script that make use of file watcher binary and re-compile our project then there is a code change. We do that by using shell script, make it executable and move it to local binary path named watch
.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">RUN</span> mkdir <span class="token punctuation">-</span>p /app/bin <span class="token keyword">RUN</span> echo $<span class="token string">'#!/bin/shn pkill $MAIN_APP && echo "Killing process..."n rm -r /app/bin/$MAIN_APP && echo "Removing old binary..."n echo "Restarting a new process"n cd /app && go build -o /app/bin/$MAIN_APP $PACKAGE/cmd/$MAIN_APPn /app/bin/$MAIN_APP'</span> <span class="token punctuation">></span> /usr/local/bin/compile <span class="token keyword">RUN</span> chmod +x /usr/local/bin/compile <span class="token keyword">RUN</span> echo $<span class="token string">'#!/bin/shn find . -path ./vendor -prune -o -type f ( -name "*.go" -o -name "*.yml" ) | entr -r compile'</span> <span class="token punctuation">></span> /usr/local/bin/watch <span class="token keyword">RUN</span> chmod +x /usr/local/bin/watch |
There it is a complete and final image specific for Go development. To use it for your project you just need to build it and publish it to your docker repository then write a Dockerfile and extend it as need in your project. For example
1 2 3 4 5 6 7 | <span class="token keyword">FROM</span> <span class="token punctuation">{</span>your<span class="token punctuation">-</span>docker<span class="token punctuation">-</span>image<span class="token punctuation">-</span>name<span class="token punctuation">}</span> <span class="token keyword">ENV</span> PACKAGE=<span class="token punctuation">{</span>pull/package/url<span class="token punctuation">}</span> <span class="token keyword">ENV</span> MAIN_APP=<span class="token punctuation">{</span>path/to/go/main/entrypoint<span class="token punctuation">}</span> <span class="token keyword">ENTRYPOINT</span> <span class="token punctuation">[</span><span class="token string">"watc"</span><span class="token punctuation">]</span> |