Skip to content

Commit be72b84

Browse files
committed
go toolbox
Signed-off-by: Yves Brissaud <[email protected]>
1 parent d990ee2 commit be72b84

8 files changed

+307
-0
lines changed

Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM golang:1.23.2-alpine3.20
2+
3+
RUN apk add --no-cache curl git ca-certificates openssh-client zip make build-base
4+
RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
5+
6+
RUN go install mvdan.cc/gofumpt@latest && \
7+
go install golang.org/x/tools/cmd/goimports@latest
8+
9+
COPY tools /tools

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# `docker runx` Go Toolbox
2+
3+
This is an opinionated set of tools to work with Go codebases.
4+
5+
On the most opinionated side, it requires a specific layout for you Go codebase, where the entrypoint is
6+
defined in `cmd/<NAME>/main.go` and will produce a `NAME` binary that will be put in a `dist/` folder.
7+
8+
This is designed to be used with [docker runx](https://github.com/eunomie/runx).
9+
10+
Image is available on [Docker Hub](https://hub.docker.com/r/eunomie/runx-go).
11+
12+
```
13+
$ docker runx eunomie/runx-go --help
14+
```
15+
16+
If you want to make it easier to use, create a `.docker/runx.yaml` file in your project folder with the following content:
17+
18+
```
19+
ref: eunomie/runx-go
20+
images:
21+
eunomie/runx-go:
22+
all-actions:
23+
opts:
24+
bin_name: <NAME>
25+
actions:
26+
go:build:all:
27+
opts:
28+
platforms: linux/amd64,linux/arm64
29+
```
30+
31+
That way, inside the project folder, you can simply run:
32+
33+
```
34+
$ docker runx go:build
35+
```

Taskfile.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
version: '3'
2+
3+
vars:
4+
IMAGE: eunomie/runx-go:latest
5+
PLATFORMS: linux/amd64,linux/arm64
6+
7+
tasks:
8+
build:
9+
- |
10+
docker buildx build \
11+
--platform {{.PLATFORMS}} \
12+
--attest type=sbom \
13+
--attest type=provenance,mode=max \
14+
--tag {{.IMAGE}} \
15+
--push \
16+
.
17+
- |
18+
(cd runx; docker runx decorate {{.IMAGE}} -t {{.IMAGE}})

runx/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# eunomie/go-toolbox
2+
3+
This is an opinionated set of tools to work with Go codebases.

runx/gobuild.Dockerfile

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# syntax=docker/dockerfile:1.4
2+
3+
ARG XX_VERSION=1.2.1
4+
ARG ALPINE_VERSION=3.20
5+
ARG GO_VERSION=1.23.1
6+
7+
ARG BIN_NAME
8+
9+
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
10+
11+
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS build-base
12+
COPY --from=xx / /
13+
RUN apk add --no-cache curl
14+
RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
15+
RUN apk add --no-cache git ca-certificates openssh-client zip
16+
17+
FROM build-base AS build
18+
ARG TARGETPLATFORM
19+
RUN xx-go --wrap
20+
WORKDIR /go/src/
21+
COPY go.mod ./
22+
COPY go.sum ./
23+
RUN --mount=type=ssh \
24+
--mount=type=cache,target=/root/.cache \
25+
--mount=type=cache,target=/go/pkg/mod \
26+
go mod download
27+
COPY . ./
28+
29+
FROM build AS binary
30+
ARG TARGETOS
31+
ARG TARGETARCH
32+
ARG BIN_NAME
33+
ARG NO_ARCHIVE
34+
ENV CGO_ENABLED=0
35+
RUN --mount=type=cache,target=/root/.cache \
36+
--mount=type=cache,target=/go/pkg/mod \
37+
GIT_VERSION=$(git describe --tags | cut -c 2-) && \
38+
PKG_NAME=$(go mod graph | head -n 1 | cut -d ' ' -f 1) && \
39+
xx-go build \
40+
-o dist/${BIN_NAME} \
41+
-ldflags="-w -s \
42+
-X $PKG_NAME/internal/constants.Version=$GIT_VERSION" \
43+
./cmd/${BIN_NAME} && \
44+
xx-verify dist/${BIN_NAME} && \
45+
if [ -z "${NO_ARCHIVE}" ]; then \
46+
# on windows add the .exe extension and zip the binary \
47+
if [ "${TARGETOS}" = "windows" ]; then \
48+
mv dist/${BIN_NAME} dist/${BIN_NAME}.exe && \
49+
(cd dist && zip ${BIN_NAME}-${TARGETOS}-${TARGETARCH}.zip ${BIN_NAME}.exe && rm -f ${BIN_NAME}.exe); \
50+
fi && \
51+
# if target os is not windows, tar and gzip the binary \
52+
if [ "${TARGETOS}" != "windows" ]; then \
53+
tar -C dist -czf dist/${BIN_NAME}-${TARGETOS}-${TARGETARCH}.tar.gz ${BIN_NAME} && rm -f dist/${BIN_NAME}; \
54+
fi \
55+
fi
56+
57+
FROM scratch AS export-bin
58+
ARG BIN_NAME
59+
ARG TARGETOS
60+
ARG TARGETARCH
61+
COPY --from=binary /go/src/dist/* /

runx/runx.yaml

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
actions:
2+
- id: go:build
3+
desc: Build the binary for the local platform
4+
type: build
5+
dockerfile: gobuild.Dockerfile
6+
opts:
7+
- name: bin_name
8+
desc: The name of the binary that also defines the build target as cmd/<bin_name>
9+
prompt: Please enter then name of the binary
10+
type: input
11+
required: true
12+
cmd: |
13+
-f {{.Dockerfile}} \
14+
--build-arg BIN_NAME={{opt "bin_name"}} \
15+
--build-arg NO_ARCHIVE=true \
16+
--platform local \
17+
--target export-bin \
18+
--output type=local,dest=dist/ \
19+
.
20+
21+
- id: go:build:all
22+
desc: Build for multiple platforms
23+
type: build
24+
dockerfile: gobuild.Dockerfile
25+
opts:
26+
- name: bin_name
27+
desc: The name of the binary that also defines the build target as cmd/<bin_name>
28+
prompt: Please enter then name of the binary
29+
type: input
30+
required: true
31+
- name: platforms
32+
desc: "Comma separated list of platforms to build for (ex: linux/amd64,linux/arm64)"
33+
prompt: "Please enter the comma separated list of platforms (ex: linux/amd64,linux/arm64)"
34+
type: input
35+
cmd: |
36+
-f {{.Dockerfile}} \
37+
--build-arg BIN_NAME={{opt "bin_name"}} \
38+
{{if opt "platforms"}}--platform {{opt "platforms"}}{{end}} \
39+
--target export-bin \
40+
--output type=local,dest=dist/,platform-split=false \
41+
.
42+
43+
- id: go:lint
44+
desc: Run golangci-lint
45+
type: run
46+
shell:
47+
pwd: pwd
48+
gopath: go env GOPATH
49+
gocache: go env GOCACHE
50+
cmd: |
51+
--rm \
52+
{{if .IsTTY}}-t{{end}} \
53+
-v {{sh "pwd"}}:/app \
54+
-v {{sh "gopath"}}/pkg:/go/pkg \
55+
-v {{sh "gocache"}}:/cache/go \
56+
-e GOFLAGS=-buildvcs=false \
57+
-e GOCACHE=/cache/go \
58+
-e GOLANGCI_LINT_CACHE=/cache/go \
59+
-w /app \
60+
golangci/golangci-lint:v1.61-alpine \
61+
golangci-lint run --timeout 5m
62+
63+
- id: go:mocks
64+
desc: Generate mocks using vektra/mockery
65+
type: run
66+
shell:
67+
pwd: pwd
68+
gopath: go env GOPATH
69+
gocache: go env GOCACHE
70+
cmd: |
71+
--rm \
72+
-v {{sh "pwd"}}:/app \
73+
-v {{sh "gopath"}}/pkg:/go/pkg \
74+
-v {{sh "gocache"}}:/cache/go \
75+
-e GOCACHE=/cache/go \
76+
-v {{sh "pwd"}}:/src \
77+
-w /src \
78+
vektra/mockery:v2.46 \
79+
--keeptree -r --all
80+
81+
- id: go:fmt
82+
desc: Organize imports and format code using gofumpt
83+
type: run
84+
shell:
85+
pwd: pwd
86+
gopath: go env GOPATH
87+
gocache: go env GOCACHE
88+
cmd: |
89+
--rm \
90+
-v {{sh "pwd"}}:/app \
91+
-v {{sh "gopath"}}/pkg:/go/pkg \
92+
-v {{sh "gocache"}}:/cache/go \
93+
-e GOCACHE=/cache/go \
94+
-v {{sh "pwd"}}:/src \
95+
-w /src \
96+
{{.Ref}} sh /tools/fmt.sh
97+
98+
- id: go:test
99+
desc: Run tests
100+
type: run
101+
shell:
102+
pwd: pwd
103+
gopath: go env GOPATH
104+
gocache: go env GOCACHE
105+
opts:
106+
- name: race
107+
desc: Check for race conditions
108+
no-prompt: true
109+
cmd: |
110+
{{$race := opt "race"}} \
111+
--rm \
112+
-v {{sh "pwd"}}:/app \
113+
-v {{sh "gopath"}}/pkg:/go/pkg \
114+
-v {{sh "gocache"}}:/cache/go \
115+
-e GOCACHE=/cache/go \
116+
{{if eq $race "true"}}-e CGO_ENABLED=1{{end}} \
117+
-v {{sh "pwd"}}:/src \
118+
-w /src \
119+
{{.Ref}} \
120+
go test {{if eq $race "true"}}-race{{end}} -shuffle=on ./...
121+
122+
- id: docker:build
123+
desc: Build a Docker image
124+
type: build
125+
opts:
126+
- name: platform
127+
desc: The platform to build for
128+
no-prompt: true
129+
- name: push
130+
type: confirm
131+
desc: Push the image after building
132+
no-prompt: true
133+
- name: target
134+
desc: The target to build
135+
no-prompt: true
136+
- name: image
137+
desc: The name of the image
138+
prompt: Please enter the reference of the image to build
139+
type: input
140+
required: true
141+
cmd: |
142+
{{$platform := opt "platform"}} \
143+
{{$push := optBool "push"}} \
144+
--attest type=sbom \
145+
--attest type=provenance,mode=max \
146+
--ssh default \
147+
-f Dockerfile \
148+
-t {{opt "image"}} \
149+
{{if opt "target"}}--target {{opt "target"}}{{end}} \
150+
{{if $platform}}--platform {{$platform}}{{end}} \
151+
{{if $push}}--push{{end}} \
152+
{{if not $push}}--output type=local,dest=dist/{{end}} \
153+
.

tools/fmt.sh

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env sh
2+
3+
find . -type f -name "*.go" -exec /tools/remove_empty_imports.sh "{}" \;
4+
goimports -w .
5+
gofumpt -w .

tools/remove_empty_imports.sh

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env sh
2+
3+
# remove all blank lines in go 'imports' statements,
4+
5+
if [ $# != 1 ] ; then
6+
echo "usage: $0 <filename>"
7+
exit 1
8+
fi
9+
10+
if [[ "$(uname -o)" == "Darwin" ]]; then
11+
sed -i '' '
12+
/^import/,/)/ {
13+
/^$/ d
14+
}
15+
' $1
16+
else
17+
sed -i'' '
18+
/^import/,/)/ {
19+
/^$/ d
20+
}
21+
' $1
22+
fi
23+

0 commit comments

Comments
 (0)