Initial commit
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
bin/*
|
||||||
21
.github/workflows/ci.yaml
vendored
Normal file
21
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Continuous Integration
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: CI
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKER_BUILDKIT: "1"
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Run linter
|
||||||
|
run: make lint
|
||||||
|
- name: Run unit tests
|
||||||
|
run: make unit-test
|
||||||
|
- name: Build Linux binary
|
||||||
|
run: make PLATFORM=linux/amd64
|
||||||
|
- name: Build Windows binary
|
||||||
|
run: make PLATFORM=windows/amd64
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
bin/
|
||||||
40
Dockerfile
Normal file
40
Dockerfile
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# syntax = docker/dockerfile:1-experimental
|
||||||
|
|
||||||
|
FROM --platform=${BUILDPLATFORM} golang:1.14.3-alpine AS base
|
||||||
|
WORKDIR /src
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
COPY go.* .
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
FROM base AS build
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
RUN --mount=target=. \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
|
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /out/example .
|
||||||
|
|
||||||
|
FROM base AS unit-test
|
||||||
|
RUN --mount=target=. \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
|
go test -v .
|
||||||
|
|
||||||
|
FROM golangci/golangci-lint:v1.27-alpine AS lint-base
|
||||||
|
|
||||||
|
FROM base AS lint
|
||||||
|
RUN --mount=target=. \
|
||||||
|
--mount=from=lint-base,src=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \
|
||||||
|
--mount=type=cache,target=/root/.cache/go-build \
|
||||||
|
--mount=type=cache,target=/root/.cache/golangci-lint \
|
||||||
|
golangci-lint run --timeout 10m0s ./...
|
||||||
|
|
||||||
|
FROM scratch AS bin-unix
|
||||||
|
COPY --from=build /out/example /
|
||||||
|
|
||||||
|
FROM bin-unix AS bin-linux
|
||||||
|
FROM bin-unix AS bin-darwin
|
||||||
|
|
||||||
|
FROM scratch AS bin-windows
|
||||||
|
COPY --from=build /out/example /example.exe
|
||||||
|
|
||||||
|
FROM bin-${TARGETOS} as bin
|
||||||
|
|
||||||
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <https://unlicense.org>
|
||||||
18
Makefile
Normal file
18
Makefile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
all: bin/example
|
||||||
|
test: lint unit-test
|
||||||
|
|
||||||
|
PLATFORM=local
|
||||||
|
|
||||||
|
.PHONY: bin/example
|
||||||
|
bin/example:
|
||||||
|
@docker build . --target bin \
|
||||||
|
--output bin/ \
|
||||||
|
--platform ${PLATFORM}
|
||||||
|
|
||||||
|
.PHONY: unit-test
|
||||||
|
unit-test:
|
||||||
|
@docker build . --target unit-test
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
@docker build . --target lint
|
||||||
106
README.md
Normal file
106
README.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|

|
||||||
|
|
||||||
|
Example Containerized Go Development Environment
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
This repository contains an example Go project with a containerized development
|
||||||
|
environment. The example project is a simple CLI tool that echos back its
|
||||||
|
inputs.
|
||||||
|
|
||||||
|
## Why should I containerize my development environment?
|
||||||
|
|
||||||
|
There are several advantages to containerizing your development environment:
|
||||||
|
* You make explicit the tools and versions of tools required to develop your
|
||||||
|
project
|
||||||
|
* Your builds will be more deterministic and reproducible
|
||||||
|
|
||||||
|
These will both make it easier for people to collaborate on your project, as
|
||||||
|
everyone will have the same environment, and make it easier to debug things like
|
||||||
|
CI failures.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
The only requirements to build and use this project are Docker and `make`. The
|
||||||
|
latter can easily be substituted with your scripting tool of choice.
|
||||||
|
|
||||||
|
You will also need to enable the BuildKit builder in the Docker CLI. This can be
|
||||||
|
done by setting `DOCKER_BUILDKIT=1` in your environment.
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
* Install [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||||
|
* Ensure that you have `make` (included with Xcode)
|
||||||
|
* Run `export DOCKER_BUILDKIT=1` in your terminal or add to your shell
|
||||||
|
initialization scripts
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
* Install [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||||
|
* Ensure that you have `make`
|
||||||
|
* If using PowerShell, run `$env:DOCKER_BUILDKIT=1`
|
||||||
|
* If using command prompt, run `set DOCKER_BUILDKIT=1`
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
* Install [Docker](https://docs.docker.com/engine/install/)
|
||||||
|
* Ensure that you have `make`
|
||||||
|
* Run `export DOCKER_BUILDKIT=1` in your terminal or add to your shell
|
||||||
|
initialization scripts
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
Building the project will output a static binary in the bin/ folder. The
|
||||||
|
default platform is for macOS but this can be changed using the `PLATFORM` variable:
|
||||||
|
```console
|
||||||
|
$ make # build for your host OS
|
||||||
|
$ make PLATFORM=darwin/amd64 # build for macOS
|
||||||
|
$ make PLATFORM=windows/amd64 # build for Windows x86_64
|
||||||
|
$ make PLATFORM=linux/amd64 # build for Linux x86_64
|
||||||
|
$ make PLATFORM=linux/arm # build for Linux ARM
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then run the binary, which is a simple echo binary, as follows:
|
||||||
|
```console
|
||||||
|
$ ./bin/example hello world!
|
||||||
|
hello world!
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the unit tests run:
|
||||||
|
```console
|
||||||
|
$ make unit-test
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the linter:
|
||||||
|
```console
|
||||||
|
$ make lint
|
||||||
|
```
|
||||||
|
|
||||||
|
There's then a helpful `test` alias for running both the linter and the unit
|
||||||
|
tests:
|
||||||
|
```console
|
||||||
|
$ make test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Structure of project
|
||||||
|
|
||||||
|
### Dockerfile
|
||||||
|
|
||||||
|
The [Dockerfile](./Dockerfile) codifies all the tools needed for the project
|
||||||
|
and the commands that need to be run for building and testing it.
|
||||||
|
|
||||||
|
### Makefile
|
||||||
|
|
||||||
|
The [Makefile](./Makefile) is purely used to script the required `docker build`
|
||||||
|
commands as these can get quite long. You can replace this file with a scripting
|
||||||
|
language of your choice.
|
||||||
|
|
||||||
|
### CI
|
||||||
|
|
||||||
|
The CI is configured in the [ci.yaml file](./github/workflows/ci.yaml). By
|
||||||
|
containerizing the toolchain, the CI relies on the toolchain we defined in the
|
||||||
|
Dockerfile and doesn't require any custom setup.
|
||||||
|
|
||||||
|
## Read more
|
||||||
|
|
||||||
|
* [Docker build reference documentation](https://docs.docker.com/engine/reference/commandline/build/)
|
||||||
|
* [Experimental Dockerfile syntax](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md)
|
||||||
0
bin/.placeholder
Normal file
0
bin/.placeholder
Normal file
8
go.mod
Normal file
8
go.mod
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module github.com/chris-crone/containerized-go
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/stretchr/testify v1.5.1
|
||||||
|
)
|
||||||
13
go.sum
Normal file
13
go.sum
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
24
main.go
Normal file
24
main.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func echo(args []string) error {
|
||||||
|
if len(args) < 2 {
|
||||||
|
return errors.New("no message to echo")
|
||||||
|
}
|
||||||
|
_, err := fmt.Println(strings.Join(args[1:], " "))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := echo(os.Args); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%+v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
19
main_test.go
Normal file
19
main_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEcho(t *testing.T) {
|
||||||
|
// Test happy path
|
||||||
|
err := echo([]string{"bin-name", "hello", "world!"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEchoErrorNoArgs(t *testing.T) {
|
||||||
|
// Test empty arguments
|
||||||
|
err := echo([]string{})
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user