Initial commit

This commit is contained in:
Christopher Crone
2020-05-28 13:29:02 +02:00
commit bff6023b1a
12 changed files with 275 additions and 0 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
bin/*

21
.github/workflows/ci.yaml vendored Normal file
View 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
View File

@@ -0,0 +1 @@
bin/

40
Dockerfile Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,106 @@
![Continuous Integration](https://github.com/chris-crone/containerized-go-dev/workflows/Continuous%20Integration/badge.svg)
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
View File

8
go.mod Normal file
View 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
View 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
View 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
View 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)
}