Repository: MartinHeinz/go-project-blueprint Branch: master Commit: 9b0b8cad667d Files: 18 Total size: 21.6 KB Directory structure: gitextract_w1egxyca/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── build/ │ ├── build.sh │ ├── test.sh │ └── test_ci.sh ├── cmd/ │ └── blueprint/ │ ├── config/ │ │ └── config.go │ ├── main.go │ └── main_test.go ├── config/ │ └── example.yaml ├── go.mod ├── in.Dockerfile ├── pkg/ │ └── version.go ├── reports.sh ├── sonar-project.properties └── test.Dockerfile ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test bin .go .push-* .container-* .dockerfile-* go.sum ================================================ FILE: .travis.yml ================================================ # Matrix build runs 4 parallel builds matrix: include: - language: go # Build and Test sudo: required services: - docker script: - export GO111MODULE=on - go mod vendor # Download dependencies - make build # Build application - test -f bin/linux_amd64/blueprint # Test for presence of binary built in previous step - make all-container # Make all Docker containers - docker images | grep "^docker.pkg.github.com/martinheinz/go-project-blueprint/blueprint.*__linux_amd64" # Check presence of created images - make test # Runs tests inside test image - language: go # SonarCloud addons: sonarcloud: organization: martinheinz-github token: secure: "tYsUxue9kLZWb+Y8kwU28j2sa0pq20z2ZvZrbKCN7Sw0WGtODQLaK9tZ94u1Sy02qL5QcabukbENmbvfouzXf4EfaKjDmYH9+Ja22X26MfTLVpaCDTQEGmNyREOFCHpjNXPgDMv1C70By5U+aPWSYF/lehB5rFijwCf7rmTFRNUDeotCTCuWb2dIkrX2i6raVu34SvqqGxKQmmH+NPLe7uKO/wXqH+cWQH1P9oJYeVksNGruw4M0MznUeQHeJQYpTLooxhEEzYiBbkerWGDMwBdZdPQwVrO2b8FEDRw/GWTFoL+FkdVMl4n4lrbO/cQLbPMTGcfupNCuVHh1n8cGp8spMkrfQGtKqvDRuz2tBs0n1PWXCRS6pgZQw/ClLPgi/vVryVRwOabIHSQQLRVhcdp8pkYdyX3aH1EdlIHiJLT6sacS0vJPqZMF/HNsPEoHe4YdiYvx/tcYMU63KQVZzgF4HfQMWy69s1d0RZUqd+wrtHU1DHwnkq1TSe+8nMlbvbmMsm6FVqGistrnVjx4C9TjDWQcjprYU40zCvc1uvoSPimVcaD8ITalCDHlEfoV7wZuisV8+gJzOh9pDZ/joohW7/P3zklGgI2sH7qt62GE4o5UyRArzJC7eIj7Oxx6GdbeEqw09M4rCfR1g5tHWIqVHz5CajvkXkPqrRGu2oI=" before_script: - ./reports.sh # Creates directories and files for reports - export GO111MODULE=on - go mod vendor # Download dependencies - make ci # Run tests and generate reports (See `ci` step in Makefile) script: - sonar-scanner # Run analysis using SonarCloud scanner plugin - language: go # CodeClimate before_script: - ./reports.sh # Create directories and files for reports - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter # Download CodeClimate test reporter - chmod +x ./cc-test-reporter # Make it executable - ./cc-test-reporter before-build # Notify CodeClimate of pending report script: - export GO111MODULE=on - go mod vendor # Download dependencies - make ci # Run tests and generate reports (See `ci` step in Makefile) after_script: - ./cc-test-reporter after-build -t gocov --exit-code $TRAVIS_TEST_RESULT # Send report to CodeClimate or notify it of failing build based on exit code - language: go # Push if on master services: - docker if: branch = master script: - export GO111MODULE=on - go mod vendor # Download dependencies - echo "$DOCKER_PASSWORD" | docker login docker.pkg.github.com -u "$DOCKER_USERNAME" --password-stdin # Login to GitHub Registry using Travis environment variables - make container # Create dirty and latest images - make push # Push image to registry notifications: email: false ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 MartinHeinz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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 OR COPYRIGHT HOLDERS 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. ================================================ FILE: Makefile ================================================ # This is adapted from https://github.com/thockin/go-build-template # The binary to build (just the basename). BIN := blueprint # Where to push the docker image. REGISTRY ?= docker.pkg.github.com/martinheinz/go-project-blueprint # This version-strategy uses git tags to set the version string VERSION := $(shell git describe --tags --always --dirty) # This version-strategy uses a manual value to set the version string #VERSION := 1.2.3 ### ### These variables should not need tweaking. ### SRC_DIRS := cmd pkg # directories which hold app source (not vendored) ALL_PLATFORMS := linux/amd64 # Used internally. Users should pass GOOS and/or GOARCH. OS := $(if $(GOOS),$(GOOS),$(shell go env GOOS)) ARCH := $(if $(GOARCH),$(GOARCH),$(shell go env GOARCH)) BASEIMAGE ?= gcr.io/distroless/static IMAGE := $(REGISTRY)/$(BIN) TAG := $(VERSION)__$(OS)_$(ARCH) BUILD_IMAGE ?= golang:1.12-alpine # Tweaked image used for test runs (see `test.Dockerfile`) TEST_IMAGE ?= martinheinz/golang:1.12-alpine-test # If you want to build all binaries, see the 'all-build' rule. # If you want to build all containers, see the 'all-container' rule. all: build # For the following OS/ARCH expansions, we transform OS/ARCH into OS_ARCH # because make pattern rules don't match with embedded '/' characters. build-%: @$(MAKE) build \ --no-print-directory \ GOOS=$(firstword $(subst _, ,$*)) \ GOARCH=$(lastword $(subst _, ,$*)) container-%: @$(MAKE) container \ --no-print-directory \ GOOS=$(firstword $(subst _, ,$*)) \ GOARCH=$(lastword $(subst _, ,$*)) push-%: @$(MAKE) push \ --no-print-directory \ GOOS=$(firstword $(subst _, ,$*)) \ GOARCH=$(lastword $(subst _, ,$*)) all-build: $(addprefix build-, $(subst /,_, $(ALL_PLATFORMS))) all-container: $(addprefix container-, $(subst /,_, $(ALL_PLATFORMS))) build: bin/$(OS)_$(ARCH)/$(BIN) # Directories that we need created to build/test. BUILD_DIRS := bin/$(OS)_$(ARCH) \ .go/bin/$(OS)_$(ARCH) \ .go/cache # The following structure defeats Go's (intentional) behavior to always touch # result files, even if they have not changed. This will still run `go` but # will not trigger further work if nothing has actually changed. OUTBIN = bin/$(OS)_$(ARCH)/$(BIN) $(OUTBIN): .go/$(OUTBIN).stamp @true # This will build the binary under ./.go and update the real binary if needed. .PHONY: .go/$(OUTBIN).stamp .go/$(OUTBIN).stamp: $(BUILD_DIRS) @echo "making $(OUTBIN)" @docker run \ -i \ --rm \ -u $$(id -u):$$(id -g) \ -v $$(pwd):/src \ -w /src \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin/$(OS)_$(ARCH) \ -v $$(pwd)/.go/cache:/.cache \ --env HTTP_PROXY=$(HTTP_PROXY) \ --env HTTPS_PROXY=$(HTTPS_PROXY) \ $(BUILD_IMAGE) \ /bin/sh -c " \ ARCH=$(ARCH) \ OS=$(OS) \ VERSION=$(VERSION) \ ./build/build.sh \ " @if ! cmp -s .go/$(OUTBIN) $(OUTBIN); then \ mv .go/$(OUTBIN) $(OUTBIN); \ date >$@; \ fi # Example: make shell CMD="-c 'date > datefile'" shell: $(BUILD_DIRS) @echo "launching a shell in the containerized build environment" @docker run \ -ti \ --rm \ -u $$(id -u):$$(id -g) \ -v $$(pwd):/src \ -w /src \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin/$(OS)_$(ARCH) \ -v $$(pwd)/.go/cache:/.cache \ --env HTTP_PROXY=$(HTTP_PROXY) \ --env HTTPS_PROXY=$(HTTPS_PROXY) \ $(BUILD_IMAGE) \ /bin/sh $(CMD) # Used to track state in hidden files. DOTFILE_IMAGE = $(subst /,_,$(IMAGE))-$(TAG) container: .container-$(DOTFILE_IMAGE) say_container_name .container-$(DOTFILE_IMAGE): bin/$(OS)_$(ARCH)/$(BIN) in.Dockerfile @sed \ -e 's|{ARG_BIN}|$(BIN)|g' \ -e 's|{ARG_ARCH}|$(ARCH)|g' \ -e 's|{ARG_OS}|$(OS)|g' \ -e 's|{ARG_FROM}|$(BASEIMAGE)|g' \ in.Dockerfile > .dockerfile-$(OS)_$(ARCH) @docker build -t $(IMAGE):$(TAG) -t $(IMAGE):latest -f .dockerfile-$(OS)_$(ARCH) . @docker images -q $(IMAGE):$(TAG) > $@ say_container_name: @echo "container: $(IMAGE):$(TAG)" push: .push-$(DOTFILE_IMAGE) say_push_name .push-$(DOTFILE_IMAGE): .container-$(DOTFILE_IMAGE) @docker push $(IMAGE):$(TAG) push-latest: .push-$(DOTFILE_IMAGE) say_push_name_latest @docker push $(IMAGE):latest say_push_name: @echo "pushed: $(IMAGE):$(TAG)" say_push_name_latest: @echo "pushed: $(IMAGE):$(TAG)" version: @echo $(VERSION) test: $(BUILD_DIRS) @docker run \ -i \ --rm \ -u $$(id -u):$$(id -g) \ -v $$(pwd):/src \ -w /src \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin/$(OS)_$(ARCH) \ -v $$(pwd)/.go/cache:/.cache \ -v $$(pwd)/config:/config \ --env HTTP_PROXY=$(HTTP_PROXY) \ --env HTTPS_PROXY=$(HTTPS_PROXY) \ $(TEST_IMAGE) \ /bin/sh -c " \ ARCH=$(ARCH) \ OS=$(OS) \ VERSION=$(VERSION) \ ./build/test.sh $(SRC_DIRS) \ " ci: $(BUILD_DIRS) @docker run \ -i \ --rm \ -u $$(id -u):$$(id -g) \ -v $$(pwd):/src \ -w /src \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin \ -v $$(pwd)/.go/bin/$(OS)_$(ARCH):/go/bin/$(OS)_$(ARCH) \ -v $$(pwd)/.go/cache:/.cache \ -v $$(pwd)/reports:/reports \ -v $$(pwd)/config:/config \ -v $$(pwd)/:/coverage \ --env HTTP_PROXY=$(HTTP_PROXY) \ --env HTTPS_PROXY=$(HTTPS_PROXY) \ $(TEST_IMAGE) \ /bin/sh -c " \ ARCH=$(ARCH) \ OS=$(OS) \ VERSION=$(VERSION) \ ./build/test_ci.sh $(SRC_DIRS) \ " $(BUILD_DIRS): @mkdir -p $@ clean: container-clean bin-clean container-clean: rm -rf .container-* .dockerfile-* .push-* bin-clean: rm -rf .go bin ================================================ FILE: README.md ================================================ # Blueprint/Boilerplate For Golang Projects [![Build Status](https://travis-ci.com/MartinHeinz/go-project-blueprint.svg?branch=master)](https://travis-ci.com/MartinHeinz/go-project-blueprint) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=MartinHeinz_go-project-blueprint&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=MartinHeinz_go-project-blueprint) [![Test Coverage](https://api.codeclimate.com/v1/badges/ec7ebefe63609984cb5c/test_coverage)](https://codeclimate.com/github/MartinHeinz/go-project-blueprint/test_coverage) [![Go Report Card](https://goreportcard.com/badge/github.com/MartinHeinz/go-project-blueprint)](https://goreportcard.com/report/github.com/MartinHeinz/go-project-blueprint) ----- If you find this useful, you can support me on Ko-Fi (Donations are always appreciated, but never required): [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/K3K6F4XN6) ## Blog Posts - More Information About This Repo You can find more information about this project/repository and how to use it in following blog posts: - [Ultimate Setup for Your Next Golang Project](https://towardsdatascience.com/ultimate-setup-for-your-next-golang-project-1cc989ad2a96) - [Setting up GitHub Package Registry with Docker and Golang](https://towardsdatascience.com/setting-up-github-package-registry-with-docker-and-golang-7a75a2533139) - [Building RESTful APIs in Golang](https://towardsdatascience.com/building-restful-apis-in-golang-e3fe6e3f8f95) - [Setting Up Swagger Docs for Golang API](https://towardsdatascience.com/setting-up-swagger-docs-for-golang-api-8d0442263641) ### Setting Up - Replace All Occurrences of `martinheinz/go-project-blueprint` with your username repository name - Replace All Occurrences of `blueprint` with your desired image name ### Adding New Libraries/Dependencies ```bash go mod vendor ``` ### Using GitHub Registry Create and Push: ```bash docker login docker.pkg.github.com -u -p docker build -t docker.pkg.github.com/martinheinz/go-project-blueprint/blueprint:latest . # make container docker push docker.pkg.github.com/martinheinz/go-project-blueprint/blueprint:latest # make push ``` Pull and Run: ```bash docker pull docker.pkg.github.com/martinheinz/go-project-blueprint/blueprint:latest docker run docker.pkg.github.com/martinheinz/go-project-blueprint/blueprint:latest ``` ### Setup new SonarCloud Project - On _SonarCloud_: - Click _Plus_ Sign in Upper Right Corner - _Analyze New Project_ - Click _GitHub app configuration_ link - Configure SonarCloud - Select Repository and Save - Go Back to Analyze Project - Tick Newly Added Repository - Click Set Up - Click Configure with Travis - Copy the Command to Encrypt the Travis Token - Run `travis encrypt --com ` - Populate the `secure` Field in `.travis.yml` with outputted string - Follow steps to populate your `sonar-project.properties` - Push - On Travis CI: - Set `DOCKER_USERNAME` - Set `DOCKER_PASSWORD` to Your GitHub Registry Token ### Setup CodeClimate - Go to - Add Repository - Go to Test Coverage Tab - Copy Test Reporter ID - Go to Travis and Open Settings for Your Repository - Add Environment Variable: name: `CC_TEST_REPORTER_ID`, value: _Copied from CodeClimate_ ================================================ FILE: build/build.sh ================================================ #!/bin/sh set -o errexit set -o nounset set -o pipefail # Check if OS, architecture and application version variables are set in Makefile if [ -z "${OS:-}" ]; then echo "OS must be set" exit 1 fi if [ -z "${ARCH:-}" ]; then echo "ARCH must be set" exit 1 fi if [ -z "${VERSION:-}" ]; then echo "VERSION must be set" exit 1 fi # Disable C code, enable Go modules export CGO_ENABLED=0 export GOARCH="${ARCH}" export GOOS="${OS}" export GO111MODULE=on export GOFLAGS="-mod=vendor" # Build the application. `-ldflags -X` sets version variable in importpath for each `go tool link` invocation go install \ -installsuffix "static" \ -ldflags "-X $(go list -m)/pkg/version.VERSION=${VERSION}" \ ./... ================================================ FILE: build/test.sh ================================================ #!/bin/sh set -o errexit set -o nounset set -o pipefail # Enable C code, as it is needed for SQLite3 database binary # Enable go modules export CGO_ENABLED=1 export GO111MODULE=on export GOFLAGS="-mod=vendor" # Collect test targets TARGETS=$(for d in "$@"; do echo ./$d/...; done) # Run tests echo "Running tests:" go test -installsuffix "static" ${TARGETS} 2>&1 echo # Collect all `.go` files and `gofmt` against them. If some need formatting - print them. echo -n "Checking gofmt: " ERRS=$(find "$@" -type f -name \*.go | xargs gofmt -l 2>&1 || true) if [ -n "${ERRS}" ]; then echo "FAIL - the following files need to be gofmt'ed:" for e in ${ERRS}; do echo " $e" done echo exit 1 fi echo "PASS" echo # Run `go vet` against all targets. If problems are found - print them. echo -n "Checking go vet: " ERRS=$(go vet ${TARGETS} 2>&1 || true) if [ -n "${ERRS}" ]; then echo "FAIL" echo "${ERRS}" echo exit 1 fi echo "PASS" echo ================================================ FILE: build/test_ci.sh ================================================ #!/bin/sh set -o errexit set -o nounset set -o pipefail # Enable C code, as it is needed for SQLite3 database binary # Enable go modules export CGO_ENABLED=1 export GO111MODULE=on export GOFLAGS="-mod=vendor" # Collect test targets TARGETS=$(for d in "$@"; do echo ./$d/...; done) # Run tests and generate reports in JSON format, then copy them to path/file expected CodeClimate and SonarCloud echo "Running tests and Generating reports..." go test -coverprofile=/reports/coverage.out -installsuffix "static" ${TARGETS} -json > /reports/test-report.out cp /reports/coverage.out /coverage/c.out echo # Check coverage using test report from previous step echo "Coverage:" go tool cover -func=/coverage/c.out echo # Collect all `.go` files and `gofmt` against them. If some need formatting - print them. echo -n "Checking gofmt: " ERRS=$(find "$@" -type f -name \*.go | xargs gofmt -l 2>&1 || true) if [ -n "${ERRS}" ]; then echo "FAIL - the following files need to be gofmt'ed:" for e in ${ERRS}; do echo " $e" done echo exit 1 fi echo "PASS" echo # Run `go vet` against all targets. If problems are found - print them. echo -n "Checking go vet: " ERRS=$(go vet ${TARGETS} 2>&1 | tee "/reports/vet.out" || true) if [ -n "${ERRS}" ]; then echo "FAIL" echo "${ERRS}" echo exit 1 fi echo "PASS" echo ================================================ FILE: cmd/blueprint/config/config.go ================================================ package config import ( "fmt" "github.com/spf13/viper" ) // Config is global object that holds all application level variables. var Config appConfig type appConfig struct { // Example Variable ConfigVar string } // LoadConfig loads config from files func LoadConfig(configPaths ...string) error { v := viper.New() v.SetConfigName("example") v.SetConfigType("yaml") v.SetEnvPrefix("blueprint") v.AutomaticEnv() for _, path := range configPaths { v.AddConfigPath(path) } if err := v.ReadInConfig(); err != nil { return fmt.Errorf("failed to read the configuration file: %s", err) } return v.Unmarshal(&Config) } ================================================ FILE: cmd/blueprint/main.go ================================================ package main import ( "fmt" "github.com/MartinHeinz/go-project-blueprint/cmd/blueprint/config" ) func main() { // load application configurations if err := config.LoadConfig("./config"); err != nil { panic(fmt.Errorf("invalid application configuration: %s", err)) } fmt.Println(config.Config.ConfigVar) } ================================================ FILE: cmd/blueprint/main_test.go ================================================ package main import ( "github.com/MartinHeinz/go-project-blueprint/cmd/blueprint/config" "github.com/stretchr/testify/assert" "testing" ) // Example test to show usage of `make test` func TestDummy(t *testing.T) { assert.Equal(t, 1, 1) } func TestLoadConfig(t *testing.T) { if err := config.LoadConfig("/config"); err != nil { assert.Error(t, err) } assert.Equal(t, "Dummy Value", config.Config.ConfigVar) } ================================================ FILE: config/example.yaml ================================================ ConfigVar: "Dummy Value" ================================================ FILE: go.mod ================================================ module github.com/MartinHeinz/go-project-blueprint go 1.12 require ( github.com/spf13/viper v1.4.0 github.com/stretchr/testify v1.4.0 ) ================================================ FILE: in.Dockerfile ================================================ FROM {ARG_FROM} ADD bin/{ARG_OS}_{ARG_ARCH}/{ARG_BIN} /{ARG_BIN} COPY ./config /config EXPOSE 1234 ENTRYPOINT ["/{ARG_BIN}"] # Used in Makefile - `make container` step ================================================ FILE: pkg/version.go ================================================ package pkg // VERSION is the app-global version string, which should be substituted with a // real value during build. var VERSION = "UNKNOWN" ================================================ FILE: reports.sh ================================================ #!/usr/bin/env bash # Prepares files and directories needed when generating reports (mainly used in CI/CD build) mkdir -p reports touch reports/coverage.out reports/test-report.out reports/vet.out touch c.out ================================================ FILE: sonar-project.properties ================================================ sonar.projectKey=MartinHeinz_go-project-blueprint sonar.organization=martinheinz-github sonar.projectName=go-project-blueprint sonar.projectVersion=0.0.1 sonar.login=b806fa3a541f3d3cf15922c6cb13d67429423a00 sonar.password= sonar.links.homepage= sonar.links.ci= sonar.links.scm= sonar.sources=cmd sonar.tests=cmd sonar.binaries=cmd/bin/linux_amd64 sonar.exclusions=**/vendor/** sonar.test.exclusions=**/*.go sonar.test.inclusions=**/*_test.go sonar.go.coverage.reportPath=reports/coverage.out sonar.go.tests.reportPaths=reports/test-report.out sonar.go.govet.reportPaths=reports/vet.out ================================================ FILE: test.Dockerfile ================================================ FROM golang:1.12-alpine RUN apk add gcc g++ # Docker image for running tests. This image is needed because tests use SQLite3 as in-memory database # and that requires CGO to be enabled, which in turn requires GCC and G++ to be installed.