.DEFAULT_TARGET: build
.PHONY: build test install_hooks install_protoc install_retool setup

# for retool
SHELL := /bin/bash -O extglob -c
RETOOL_PATH ?= $(CURDIR)/_tools
export PATH := $(RETOOL_PATH)/bin:$(PATH)

build: bin/upload-service

GO_SRC=$(shell find . -name '*.go' ! -path './__vendor/*' -o -name '*.proto' ! -path './__vendor/*' | grep -v '_test.go')

bin/upload-service: Dockerfile glide.lock $(GO_SRC) | setup
	mkdir -p bin deploy/bin
	docker build --rm --tag upload-service:build .
	docker run \
	  -u $(shell id -u) --group-add $(shell id -g) \
	  -v $(PWD):/go/src/code.justin.tv/web/upload-service \
	  --rm \
	  --env JENKINS_HOME=/ \
	  upload-service:build \
	  make build-in-container

build-in-container: rpc/uploader/service.pb.go rpc/uploader/service.twirp.go
	go build -o bin/upload-service cmd/upload-service/main.go
	go build -o deploy/bin/test cmd/e2e_test/end_to_end.go
	go build -o bin/barrel _tools/src/code.justin.tv/release/villagers/cmd/barrel/*

# inspired by https://git-aws.internal.justin.tv/edge/visage/blob/master/Makefile#L55
test-in-container: rpc/uploader/service.pb.go rpc/uploader/service.twirp.go
	moto_server &
	go install ./...
	go list ./... | awk '{printf("go test -coverprofile profile%d.out %s\n", NR, $$0)}' | parallel -a -
	cat profile*.out > coverage.txt
	rm profile*.out

test: build mocks setup
	docker run \
	  -v $(PWD):/go/src/code.justin.tv/web/upload-service \
	  --rm \
	  --env JENKINS_HOME=/ \
	  upload-service:build \
	  make test-in-container

setup: install_hooks install_protoc install_retool

GIT_SHA ?= $(shell git rev-parse HEAD)
DOCKER_TAG ?= "docker.internal.justin.tv/web-upload-service:$(GIT_SHA)"

.PHONY: is_git_clean push
is_git_clean:
	@[ -z "$(shell git status --porcelain)" ] || \
	  { echo >&2 "Can't push with pending changes in git" && \
		git status && \
		exit 1; }

#######################################
# TARGETS THAT ARE SKIPPED IN JENKINS #
#######################################
# We have to skip several targets in Jenkins because they are for local development but don't have any place in
# the CI/CD pipeline. Things like generating code that must be checked in and install pre-commit hooks.
.PHONY: mocks clean_mocks install_hooks install_protoc

############################
# If we are not in Jenkins #
############################
# These are the real implementations of the stubbed out targets above.
ifeq ($(strip $(JENKINS_HOME)),)

push:
	@echo "Only Jenkins is allowed to push Docker images to the registry"
	exit 1

# These mocks must be checked in.
# To add a new mock:
# 1. add the output filename to `MOCKS`
# 2. add a rule linking the output filename to the input filename
#   2a) the output filename must match the name of the interface (including case)
#   2b) the dirname of the first dependency is what goes into `mockery`, other dependencies can be used to determine
#       whether the mock needs to be regenerated
#   2c) the output package will match the name of the directory it is put in

# Step 1 here
MOCKS = awsmocks/DynamoDBAPI.go awsmocks/S3API.go awsmocks/SNSAPI.go backend/mocks/Backender.go pubclient/mocks/PubClient.go rpc/uploader/mocks/Uploader.go files/mocks/FileOperations.go transformations/mocks/ImageTransformer.go

# Step 2 here
awsmocks/DynamoDBAPI.go: __vendor/github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface/interface.go
awsmocks/S3API.go: __vendor/github.com/aws/aws-sdk-go/service/s3/s3iface/interface.go
awsmocks/SNSAPI.go: __vendor/github.com/aws/aws-sdk-go/service/sns/snsiface/interface.go
pubclient/mocks/PubClient.go: __vendor/code.justin.tv/chat/pubsub-go-pubclient/client/pub.go
backend/mocks/Backender.go: backend/backend.go
rpc/uploader/mocks/Uploader.go: rpc/uploader/service.twirp.go
files/mocks/FileOperations.go: files/files.go
transformations/mocks/ImageTransformer.go: transformations/transformer.go

# ~~~ sufficiently advanced technology ~~~
mocks: $(MOCKS)
clean_mocks:
	rm $(MOCKS)
$(MOCKS):
	mockery -dir $(shell dirname $<) -output $(shell dirname $@) -outpkg $(shell dirname $@ | xargs basename) -name $(shell basename $@ | sed 's/.go//')

# Pre-commit hooks are to ensure developers run `go imports` before committing. Jenkins should never commit.
install_hooks: .git/hooks/pre-commit _venv/bin/pre-commit

.git/hooks/pre-commit _venv/bin/pre-commit: .pre-commit-config.yaml | _venv/bin/pip
	_venv/bin/pip install "pre-commit==0.13.6"
	_venv/bin/pre-commit install --install-hooks
	touch -m $@

# Pip only needed for pre-commit hooks
_venv/bin/pip:
	mkdir -p _venv
	scripts/may_i_install.sh virtualenv sudo easy_install virtualenv
	virtualenv _venv

# Generated protobuf files must be committed.
install_protoc:
	scripts/may_i_install.sh protoc brew install protobuf

# Jenkins doesn't need to run any retool-ed tools
install_retool: $(RETOOL_PATH)/bin/.last_built

$(RETOOL_PATH)/bin/.last_built: tools.json
	GOPATH=$(RETOOL_PATH) go install all
	touch -m $@

# Vendored dependencies must be checked in
glide.lock: glide.yaml | install_retool
	glide update --strip-vendor
	glide-vc --only-code --no-tests --use-lock-file

# Protobuf generated files must be checked in
rpc/uploader/service.pb.go rpc/uploader/service.twirp.go: rpc/uploader/service.proto | setup
	protoc --proto_path=$(GOPATH)/src:. --twirp_out=. --go_out=. ./rpc/uploader/service.proto

.PHONY: proto
proto: rpc/uploader/service.pb.go rpc/uploader/service.twirp.go

else

############################
# If we are in Jenkins	 #
############################
push: test is_git_clean bin/Dockerfile
	docker build -t $(DOCKER_TAG) bin
	docker push $(DOCKER_TAG)

# We make empty targets as the default behavior that Jenkins will see.
mocks install_hooks install_protoc install_retool:

endif
