This is a collection of golang commands, improvements, information
and other stuff.
This page has all my knowledge that I wished I had in the beginning
already.
golangci-lint
is a very easy and simple tool for
checking your go code.
To install it run
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.2.2
To analyze your go project run
golangci-lint run
To see every recommendation you can enable all linters with:
golangci-lint run --default=all .
https://go.dev/doc/tutorial/govulncheck
To check for known vulnerabilities in your code or packages you can
use the govulncheck
application.
To install it run
go install golang.org/x/vuln/cmd/govulncheck@latest
To check your project run
govulncheck ./...
If it says “command not found” then verify that you have your home
folder’s go/bin folder in your path (/home/user/go/bin
), if
not you can use the following to add it:
export PATH=$PATH:/home/$(whoami)/go/bin
Go has, by default, many tools for testing and linting already inbuilt.
*_test.go
files use
go test .
go vet .
go run -race .
https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies
https://medium.com/@diogok/on-golang-static-binaries-cross-compiling-and-plugins-1aed33499671
https://stackoverflow.com/questions/61319677/flags-needed-to-create-static-binaries-in-golang
To build a static executable I personally use the following command:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-w -s' .
The “ldflags” are only for a smaller executable size, the main magic
happens with the “CGO_ENABLED=0” at the beginning.
To verify if an executable is static use the ldd
command.
To create an executable for another platform you can provide different GOOS/GOARCH variables like the following 32/64bit for win/linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-w -s' -o myapp-linux-amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags '-w -s' -o myapp-linux-386 .
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-w -s' -o myapp-windows-amd64 .
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-w -s' -o myapp-windows-386 .
https://github.com/upx/upx/releases
To make the executable smaller after building you can use
upx
.
To install it into your ~/.local/bin
folder you can use the
following commands:
wget -qO- https://github.com/upx/upx/releases/download/v4.2.4/upx-4.2.4-amd64_linux.tar.xz | tar xJvO upx-4.2.4-amd64_linux/upx > ~/.local/bin/upx
chmod +x ~/.local/bin/upx
To minify your executable use the following command (after building it with go build):
upx --best myapp
Documentation: https://pkg.go.dev/testing#hdr-Benchmarks
You can easily do benchmarks in go. This is done via _test.go
files.
Example: You have a file named main_test.go
and it contains
the following lines:
package main
import (
"testing"
"sync"
)
func BenchmarkMap(b *testing.B) {
for b.Loop() {
= map[string]string{}
_ }
}
func BenchmarkSyncMap(b *testing.B) {
for b.Loop() {
= sync.Map{}
_ }
}
The above main_test.go
file has 2 benchmarks that
compare the creation of a sync map and a default map.
To run this benchmark you can use the following command:
go test -benchmem -bench=.
The output is (on my machine):
goos: windows
goarch: amd64
pkg: a
cpu: 11th Gen Intel(R) Core(TM) i7-11700 @ 2.50GHz
BenchmarkMap-16 180920814 6.582 ns/op 0 B/op 0 allocs/op
BenchmarkSyncMap-16 1000000000 1.000 ns/op 0 B/op 0 allocs/op
PASS
ok a 5.862s
After programming with go for years I have come to like a few
specific packages or solutions to problems.
This is a short list of packages I can personally recommend using.
If you only have to write a small REST API or a single-page status
site then I recommend using the inbuilt net/http
package
with the inbuilt html/template
package if needed.
For bigger projects I can recommend gin: https://pkg.go.dev/github.com/gin-gonic/gin
It is very easy to use, fast and has a lot functions inbuilt that you
would need to code anyways (e.g. logging middleware).
My current database system is a mariadb
, which means I
can only use and recommend https://github.com/go-sql-driver/mysql.
It has never failed me and works perfectly, auto reconnecting and
pooling of connections has never created any problems for me.
For postgres I use github.com/lib/pq.
It uses the database/sql interface, which means everything is the same
as always and no code needs to be changed if you do a mysql->postgres
switch.
(I can not recommend “github.com/uptrace/bun/driver/pgdriver” anymore
because it had problems with parsing multiline sql from files.)
If you need more functions than the default database/sql
package can provide then I can recommend sqlx
: https://github.com/jmoiron/sqlx.
You can easily read sql into structs and write structs into sql and you
can use it with any database driver.
For configuring applications I use 3 different packages:
flag
package for commandline flags: https://pkg.go.dev/flagos
package for environment variables: https://pkg.go.dev/os#LookupEnvI personally enjoy yaml config files the most because they support comments (which json doesn’t) and are very easy to write and read (which xml doesn’t).
Passwords are provided by environment, the rest are either configured via flags or config file.
At the beginning I was using the inbuilt log
module,
then uber’s zap
module and I now ended up using the inbuilt
log/slog
package.
The slog
package supports structured logging with different
levels (info,error,etc.) and can log to different formats
(logfmt,json,plain) and locations (stdout,file).
If your application doesn’t log much then simple fmt
prints could be enough aswell, e.g. for commandline applications I
prefer to just print information in plaintext instead of using a
logger.
In a cloud environment you should always log to stdout. Containers do not keep data when being restarted, which means your logs would get lost afterwards. Docker and Kubernetes manage your stdout prints of your containers and log them to a more persistent place themselves, so the only thing you have to do is log to stdout and let them take care of the rest.
With the above tools combined, my current jenkins pipeline looks like this:
pipeline {
agent any
tools { go 'go1.22.2' }
stages {
stage('GitPull') {
steps {
git credentialsId: '11111111-1111-4111-1111-111111111111', url: 'http://10.0.0.2/git/me/testproject.git'
}
}
stage('Test') {
steps {
sh 'go version'
sh 'go test'
}
}
stage('Lint') {
steps {
sh 'curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b . v1.58.1'
sh './golangci-lint run'
sh 'go vet ./...'
}
}
stage('VulnCheck') {
steps {
sh 'GOBIN=$GOROOT/bin go install golang.org/x/vuln/cmd/govulncheck@latest'
sh 'govulncheck ./...'
}
}
stage('Build') {
steps {
sh 'go build .'
}
}
}
}