|
| 1 | +# Package configuration |
| 2 | +PROJECT ?= |
| 3 | +COMMANDS ?= |
| 4 | +DEPENDENCIES ?= |
| 5 | + |
| 6 | +# Default shell |
| 7 | +SHELL := /bin/bash |
| 8 | + |
| 9 | +# Dockerfiles to be built, list of file:name pairs, example `Dockerfile:my-image` |
| 10 | +DOCKERFILES ?= Dockerfile:$(PROJECT) |
| 11 | +# Docker registry where the docker image should be pushed to. |
| 12 | +DOCKER_REGISTRY ?= docker.io |
| 13 | +# Docker organization to be used at the docker image name. |
| 14 | +DOCKER_ORG ?= srcd |
| 15 | +# Username used to login on the docker registry. |
| 16 | +DOCKER_USERNAME ?= |
| 17 | +# Password used to login on the docker registry. |
| 18 | +DOCKER_PASSWORD ?= |
| 19 | +# When `make docker-push`, setting DOCKER_PUSH_LATEST to any non-empty value |
| 20 | +# will cause make docker-push to also push the latest tag. |
| 21 | +DOCKER_PUSH_LATEST ?= |
| 22 | +# When `make docker-push`, setting DOCKER_PUSH_MASTER to any non-empty value |
| 23 | +# will cause make docker-push to also push when on the master branch. |
| 24 | +DOCKER_PUSH_MASTER ?= |
| 25 | +# Docker OS/Arch used to match the right binaries. |
| 26 | +# If docker is not installed, fallback to GOOS/GOARCH. |
| 27 | +ifneq ($(shell which docker),) |
| 28 | +DOCKER_OS ?= $(shell docker version -f "{{.Server.Os}}") |
| 29 | +DOCKER_ARCH ?= $(shell docker version -f "{{.Server.Arch}}") |
| 30 | +else |
| 31 | +DOCKER_OS ?= $(shell go env GOOS) |
| 32 | +DOCKER_ARCH ?= $(shell go env GOARCH) |
| 33 | +endif |
| 34 | + |
| 35 | +# Backend services |
| 36 | +POSTGRESQL_VERSION ?= |
| 37 | +RABBITMQ_VERSION ?= |
| 38 | + |
| 39 | +# Checking mandatory variables |
| 40 | +ifndef PROJECT |
| 41 | +$(error ERROR! The PROJECT variable cannot be empty) |
| 42 | +endif |
| 43 | + |
| 44 | +# Environment |
| 45 | +BUILD_PATH := build |
| 46 | +BIN_PATH := $(BUILD_PATH)/bin |
| 47 | +BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) |
| 48 | +CI_PATH ?= .ci |
| 49 | + |
| 50 | +# Build information |
| 51 | +BUILD ?= $(shell date +"%m-%d-%Y_%H_%M_%S") |
| 52 | +COMMIT ?= $(shell git rev-parse --short HEAD) |
| 53 | +GIT_DIRTY = $(shell test -n "`git status --porcelain`" && echo "-dirty" || true) |
| 54 | +DEV_PREFIX := dev |
| 55 | +VERSION ?= $(DEV_PREFIX)-$(COMMIT)$(GIT_DIRTY) |
| 56 | + |
| 57 | +# Travis CI |
| 58 | +ifneq ($(TRAVIS_TAG), ) |
| 59 | + VERSION := $(TRAVIS_TAG) |
| 60 | +endif |
| 61 | + |
| 62 | +# Drone CI |
| 63 | +ifeq ($(DRONE_BUILD_EVENT), tag) |
| 64 | + VERSION := $(DRONE_TAG) |
| 65 | +endif |
| 66 | + |
| 67 | +# IS_RELEASE is "true" if tag is semantic version and not a pre-release |
| 68 | +IS_RELEASE := $(shell echo $(VERSION) | grep -q -E '^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$$' && echo "true" || true) |
| 69 | + |
| 70 | +# Packages content |
| 71 | +PKG_OS ?= darwin linux |
| 72 | +PKG_ARCH = amd64 |
| 73 | + |
| 74 | +# LD_FLAGS to be use at `go build` calls. |
| 75 | +LD_FLAGS ?= -X main.version=$(VERSION) -X main.build=$(BUILD) -X main.commit=$(COMMIT) |
| 76 | +# Tags to be used as `-tags` argument at `go build` and `go install` |
| 77 | +GO_TAGS ?= |
| 78 | +# Arguments to be used in `go` commands. |
| 79 | +GO_GET_ARGS ?= -v -t |
| 80 | +GO_TEST_ARGS ?= -v |
| 81 | +GO_BUILD_ARGS ?= -ldflags "$(LD_FLAGS)" |
| 82 | +# Environment variable to use at `go build` calls. |
| 83 | +GO_BUILD_ENV ?= |
| 84 | + |
| 85 | +# Go parameters |
| 86 | +ifneq ($(GO_TAGS), ) |
| 87 | + GO_GET_ARGS += -tags "$(GO_TAGS)" |
| 88 | + GO_TEST_ARGS += -tags "$(GO_TAGS)" |
| 89 | + GO_BUILD_ARGS += -tags "$(GO_TAGS)" |
| 90 | +endif |
| 91 | + |
| 92 | +GOCMD = go |
| 93 | +GOGET = $(GOCMD) get $(GO_GET_ARGS) |
| 94 | +GOBUILD = $(GOCMD) build $(GO_BUILD_ARGS) |
| 95 | +GOTEST = $(GOCMD) test $(GO_TEST_ARGS) |
| 96 | +GOTEST_RACE = $(GOTEST) -race |
| 97 | +GOCLEAN = $(GOCMD) clean |
| 98 | + |
| 99 | +ifdef APPVEYOR |
| 100 | + GODEP := $(CI_PATH)/dep.exe |
| 101 | +endif |
| 102 | +GODEP ?= $(CI_PATH)/dep |
| 103 | + |
| 104 | +# Coverage |
| 105 | +COVERAGE_REPORT = coverage.txt |
| 106 | +COVERAGE_PROFILE = profile.out |
| 107 | +COVERAGE_MODE = atomic |
| 108 | + |
| 109 | +PACKAGES = $(shell go list ./... | grep -v '/vendor/') |
| 110 | + |
| 111 | +# Helm deployment information |
| 112 | +HELM_VERSION ?= v2.8.2 |
| 113 | +HELM_DEPLOY_SCRIPT ?= https://raw.githubusercontent.com/src-d/ci/v1/scripts/helm-deploy.sh |
| 114 | +HELM_RELEASE ?= |
| 115 | +HELM_CHART ?= |
| 116 | +K8S_NAMESPACE ?= default |
| 117 | +K8S_SERVICE_ACCOUNT ?= default |
| 118 | +HELM_ARGS ?= |
| 119 | + |
| 120 | +# Rules |
| 121 | + |
| 122 | +.SUFFIXES: |
| 123 | + |
| 124 | +dependencies: $(DEPENDENCIES) |
| 125 | + $(GOGET) -t ./... |
| 126 | + |
| 127 | +$(DEPENDENCIES): |
| 128 | + $(GOGET) $@/... |
| 129 | + |
| 130 | +test: |
| 131 | + $(GOTEST) $(PACKAGES) |
| 132 | + |
| 133 | +test-race: |
| 134 | + $(GOTEST_RACE) $(PACKAGES) |
| 135 | + |
| 136 | +test-coverage: |
| 137 | + echo "" > $(COVERAGE_REPORT); \ |
| 138 | + for dir in $(PACKAGES); do \ |
| 139 | + $(GOTEST) $$dir -coverprofile=$(COVERAGE_PROFILE) -covermode=$(COVERAGE_MODE); \ |
| 140 | + if [ $$? != 0 ]; then \ |
| 141 | + exit 2; \ |
| 142 | + fi; \ |
| 143 | + if [ -f $(COVERAGE_PROFILE) ]; then \ |
| 144 | + cat $(COVERAGE_PROFILE) >> $(COVERAGE_REPORT); \ |
| 145 | + rm $(COVERAGE_PROFILE); \ |
| 146 | + fi; \ |
| 147 | + done || exit 1; \ |
| 148 | + |
| 149 | +codecov: |
| 150 | + @if [ ! -f $(COVERAGE_REPORT) ]; then \ |
| 151 | + echo "Unable to find '$(COVERAGE_REPORT)', execute 'make test-coverage' first."; \ |
| 152 | + exit 1; \ |
| 153 | + fi; \ |
| 154 | + wget -q -O - https://codecov.io/bash | bash |
| 155 | + |
| 156 | +build: $(COMMANDS) |
| 157 | +$(COMMANDS): |
| 158 | + @if [ "$@" == "." ]; then \ |
| 159 | + BIN=`basename $(CURDIR)` ; \ |
| 160 | + else \ |
| 161 | + BIN=`basename $@` ; \ |
| 162 | + fi && \ |
| 163 | + for os in $(PKG_OS); do \ |
| 164 | + NBIN="$${BIN}" ; \ |
| 165 | + if [ "$${os}" == windows ]; then \ |
| 166 | + NBIN="$${NBIN}.exe"; \ |
| 167 | + fi && \ |
| 168 | + for arch in $(PKG_ARCH); do \ |
| 169 | + mkdir -p $(BUILD_PATH)/$(PROJECT)_$${os}_$${arch} && \ |
| 170 | + $(GO_BUILD_ENV) GOOS=$${os} GOARCH=$${arch} \ |
| 171 | + $(GOBUILD) -o "$(BUILD_PATH)/$(PROJECT)_$${os}_$${arch}/$${NBIN}" ./$@ && \ |
| 172 | + if [ "$(DOCKER_OS)" == "$${os}" ] && [ "$(DOCKER_ARCH)" == "$${arch}" ]; then \ |
| 173 | + echo "Linking matching OS/Arch binaries to "build/bin" folder" && \ |
| 174 | + mkdir -p $(BIN_PATH) && \ |
| 175 | + cp -rf $(BUILD_PATH)/$(PROJECT)_$${os}_$${arch}/$${NBIN} $(BIN_PATH); \ |
| 176 | + fi; \ |
| 177 | + done; \ |
| 178 | + done |
| 179 | + |
| 180 | +docker-login: docker-validate |
| 181 | + @docker login -u "$(DOCKER_USERNAME)" -p "$(DOCKER_PASSWORD)" $(DOCKER_REGISTRY); \ |
| 182 | + |
| 183 | +docker-validate: |
| 184 | + @if [ -z "$(DOCKER_USERNAME)" ]; then \ |
| 185 | + echo "DOCKER_USERNAME variable cannot be empty."; \ |
| 186 | + exit 1; \ |
| 187 | + fi; \ |
| 188 | + if [ -z "$(DOCKER_PASSWORD)" ]; then \ |
| 189 | + echo "DOCKER_PASSWORD variable cannot be empty."; \ |
| 190 | + exit 1; \ |
| 191 | + fi |
| 192 | + |
| 193 | +docker-build: $(COMMANDS) |
| 194 | + @if [ -z "$(DOCKER_ORG)" ]; then \ |
| 195 | + echo "DOCKER_ORG variable cannot be empty."; \ |
| 196 | + exit 1; \ |
| 197 | + fi; \ |
| 198 | + for d in $(DOCKERFILES); do \ |
| 199 | + dockerfile=`echo $${d} | cut -d":" -f 1`; \ |
| 200 | + repository=`echo $${d} | cut -d":" -f 2`; \ |
| 201 | + docker build -t $(DOCKER_REGISTRY)/$(DOCKER_ORG)/$${repository}:$(VERSION) -f $$dockerfile .; \ |
| 202 | + done; |
| 203 | + |
| 204 | +docker-push: docker-login docker-build |
| 205 | + @if [ "$(BRANCH)" == "master" && "$(DOCKER_PUSH_MASTER)" == "" ]; then \ |
| 206 | + echo "docker-push is disabled on master branch" \ |
| 207 | + exit 1; \ |
| 208 | + fi; \ |
| 209 | + for d in $(DOCKERFILES); do \ |
| 210 | + dockerfile=`echo $${d} | cut -d":" -f 1`; \ |
| 211 | + repository=`echo $${d} | cut -d":" -f 2`; \ |
| 212 | + docker push $(DOCKER_REGISTRY)/$(DOCKER_ORG)/$${repository}:$(VERSION); \ |
| 213 | + if [ -n "$(DOCKER_PUSH_LATEST)" ]; then \ |
| 214 | + docker tag $(DOCKER_REGISTRY)/$(DOCKER_ORG)/$${repository}:$(VERSION) \ |
| 215 | + $(DOCKER_REGISTRY)/$(DOCKER_ORG)/$${repository}:latest; \ |
| 216 | + docker push $(DOCKER_REGISTRY)/$(DOCKER_ORG)/$${repository}:latest; \ |
| 217 | + fi; \ |
| 218 | + done; |
| 219 | + |
| 220 | +docker-push-latest-release: |
| 221 | + @DOCKER_PUSH_LATEST=$(IS_RELEASE) make docker-push |
| 222 | + |
| 223 | +packages: build |
| 224 | + @cd $(BUILD_PATH); \ |
| 225 | + for os in $(PKG_OS); do \ |
| 226 | + for arch in $(PKG_ARCH); do \ |
| 227 | + TAR_VERSION=`echo $(VERSION) | tr "/" "-"`; \ |
| 228 | + tar -cvzf $(PROJECT)_$${TAR_VERSION}_$${os}_$${arch}.tar.gz $(PROJECT)_$${os}_$${arch}/; \ |
| 229 | + done; \ |
| 230 | + done |
| 231 | + |
| 232 | +clean: |
| 233 | + rm -rf $(BUILD_PATH) $(BIN_PATH) $(VENDOR_PATH) |
| 234 | + $(GOCLEAN) . |
| 235 | + |
| 236 | +no-changes-in-commit: |
| 237 | + @git status --untracked-files=normal --porcelain | grep -qe '..*'; \ |
| 238 | + if [ $$? -eq 0 ] ; then \ |
| 239 | + git diff|cat; \ |
| 240 | + git status --untracked-files=normal --porcelain; \ |
| 241 | + echo >&2 "generated assets are out of sync"; \ |
| 242 | + exit 2; \ |
| 243 | + fi |
| 244 | + |
| 245 | +godep: |
| 246 | + export INSTALL_DIRECTORY=$(CI_PATH) ; \ |
| 247 | + test -f $(GODEP) || \ |
| 248 | + curl https://raw.githubusercontent.com/golang/dep/master/install.sh | bash ; \ |
| 249 | + $(GODEP) ensure -v |
| 250 | + |
| 251 | +export POSTGRESQL_VERSION RABBITMQ_VERSION |
| 252 | +ifdef APPVEYOR |
| 253 | +prepare-services: |
| 254 | + cd $(CI_PATH) && \ |
| 255 | + pwsh -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/smola/ci-tricks/master/get.ps1'))" |
| 256 | +else |
| 257 | +prepare-services: |
| 258 | + cd $(CI_PATH) && \ |
| 259 | + wget -qO - https://raw.githubusercontent.com/smola/ci-tricks/master/get.sh | bash |
| 260 | +endif |
| 261 | + |
| 262 | +ci-install: | prepare-services dependencies |
| 263 | + @echo |
| 264 | + |
| 265 | +ifeq ($($strip $(COMMANDS)),) |
| 266 | +ci-script: | test-coverage codecov |
| 267 | + @echo |
| 268 | +else |
| 269 | +ci-script: | test-coverage codecov packages |
| 270 | + @echo |
| 271 | +endif |
| 272 | + |
| 273 | +install-helm: |
| 274 | + GET_HELM=`mktemp` ; \ |
| 275 | + trap "rm -f $${GET_HELM}" EXIT; \ |
| 276 | + curl -L https://raw.githubusercontent.com/helm/helm/master/scripts/get > $${GET_HELM}; \ |
| 277 | + chmod +x $${GET_HELM} ; \ |
| 278 | + DESIRED_VERSION=$(HELM_VERSION) $${GET_HELM} |
| 279 | + |
| 280 | +deploy: install-helm |
| 281 | + HELM_DEPLOY=`mktemp` ; \ |
| 282 | + trap "rm -f $${HELM_DEPLOY}" EXIT; \ |
| 283 | + curl -L $(HELM_DEPLOY_SCRIPT) > $${HELM_DEPLOY}; \ |
| 284 | + chmod +x $${HELM_DEPLOY}; \ |
| 285 | + $${HELM_DEPLOY} $(HELM_RELEASE) $(HELM_CHART) $(K8S_NAMESPACE) $(K8S_SERVICE_ACCOUNT) $(HELM_ARGS) |
| 286 | + |
| 287 | +.PHONY: dependencies $(DEPENDENCIES) \ |
| 288 | + build $(COMMANDS) \ |
| 289 | + test test-race test-coverage \ |
| 290 | + docker-login docker-validate docker-build docker-push \ |
| 291 | + packages \ |
| 292 | + clean \ |
| 293 | + no-changes-in-commit \ |
| 294 | + prepare-services \ |
| 295 | + ci-script ci-install \ |
| 296 | + install-helm deploy |
0 commit comments