
# Image URL to use all building/pushing image targets
CM_IMG ?= config-manager:latest
DOCKER_PLATFORMS ?= linux/amd64,linux/arm64
UNAME_S := $(shell uname -s)

ifeq ($(UNAME_S),Linux)
  SED = sed
endif
ifeq ($(UNAME_S),Darwin)
  ifeq ($(shell which gsed),)
    $(error "Please install gnu-sed: brew install gnu-sed")
  endif
  SED = gsed
endif

GITSHA ?= "no-git-module"
ifneq ("$(wildcard .git)", "")
	GITSHA = $(shell git rev-parse --short=7 HEAD)
endif
GITSHASYM="github.com/tidbcloud/infra-provider/pkg/version.Version"
GO_LDFLAGS ?= "-X $(GITSHASYM)=$(GITSHA)"

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif

# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

# E2E related environment variables.
E2E_NODES ?= 1

all: build

##@ General

# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php

help: ## Display this help.
	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

##@ Development

.PHONY: manifests
manifests: controller-gen ## Generate webhook configs for cluster's revision.
	# config-manager depends on the api defined in infra-api
	make -C ../infra-api/api manifests

	$(CONTROLLER_GEN) webhook paths="./..." output:webhook:artifacts:config=./tmp
	$(SED) -i 's/mutating-webhook-configuration/revision-mutating-webhook-configuration/g' ./tmp/manifests.yaml
	# place all webhook config files into the unify directory, ${ROOT}/infra-api/config/webhook/
	cp ./tmp/manifests.yaml ../infra-api/config/webhook/revision-webhook.yaml

.PHONY: generate
generate: ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
	make -C ../infra-api/api generate

gen-swagger:
	swag init --outputTypes yaml --generalInfo  ./api.go --dir ./pkg/gitops-v2/secondaryconfirm/,../infra-api/api/v1/

# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
CRD_OPTIONS ?= "crd:deprecatedV1beta1CompatibilityPreserveUnknownFields=false"

crd: manifests kustomize ## Generate final CRD
	$(KUSTOMIZE) build config/crd > config/crd.yaml

webhook: manifests kustomize ## General final webhook deployment
	$(KUSTOMIZE) build config/webhook > config/webhooks.yaml

lint: golangci-lint ## Run golangci-lint linter
	$(GOLANGCI_LINT) run ./...
	GOLANGCI_LINT=$(GOLANGCI_LINT) make -C ../pkg/gitops-sdk lint
	GOLANGCI_LINT=$(GOLANGCI_LINT) make -C ../util lint

lint-fix: golangci-lint ## ## Run golangci-lint linter and perform fixes
	$(GOLANGCI_LINT) run --fix ./...
	GOLANGCI_LINT=$(GOLANGCI_LINT) make -C ../pkg/gitops-sdk lint-fix
	GOLANGCI_LINT=$(GOLANGCI_LINT) make -C ../util lint-fix

doc:
	cd api && make doc

unit: generate
	mkdir -p coverage
	go test ./... -coverprofile coverage/cover.out -coverpkg ./...
	make -C ../pkg/gitops-sdk unit
	make -C ../util unit

ENVTEST_ASSETS_DIR=$(shell pwd)/test/testbin
export PATH := $(PATH):$(PWD)/test/testbin/bin
integration: manifests generate ginkgo ## Run tests.
	mkdir -p ./test/coverage
	mkdir -p ${ENVTEST_ASSETS_DIR}
	mkdir -p ${ENVTEST_ASSETS_DIR}/manifests/
	#cp -r ./manifests/* ${ENVTEST_ASSETS_DIR}/manifests/
	#./hack/grab-manifest.sh ./testbin
	test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl --show-error --fail -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.2/hack/setup-envtest.sh
	source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh && \
	fetch_envtest_tools $(ENVTEST_ASSETS_DIR) && \
	setup_envtest_env $(ENVTEST_ASSETS_DIR) && \
	$(GINKGO) -output-dir=./test/coverage/ --focus="$(or $(E2E_FOCUS), $(focus))" --timeout=10m --poll-progress-after=120s --poll-progress-interval=30s ./test/integration/...

test: unit integration

GENHTML=genhtml  # part of lcov
coverage: test gcov2lcov
	$(GCOV2LCOV) -infile=coverage/cover.out -outfile=coverage/cover.out.lcov
	$(GCOV2LCOV) -infile=coverage/cover.it.out -outfile=coverage/cover.it.out.lcov
	mkdir -p cover_html
	$(GENHTML) -o cover_html/all coverage/cover.out.lcov coverage/cover.it.out.lcov
	$(GENHTML) -o cover_html/ut_only coverage/cover.out.lcov

coverage-after-test: gcov2lcov
	$(GCOV2LCOV) -infile=coverage/cover.out -outfile=coverage/cover.out.lcov
	$(GCOV2LCOV) -infile=coverage/cover.it.out -outfile=coverage/cover.it.out.lcov
	mkdir -p cover_html
	$(GENHTML) -o coverage/cover_html/all coverage/cover.out.lcov coverage/cover.it.out.lcov
	$(GENHTML) -o coverage/cover_html/ut_only coverage/cover.out.lcov

# Make a PR ready for review.
reviewable: manifests generate crd webhook generate-mockgen lint test doc
	@go mod tidy

# Ensure a branch is reviewable and up to date
verify: reviewable
	@echo "checking that branch is clean"
	@test -z "$$(git status --porcelain)" || (echo -e "unclean working tree, did you forget to run 'make reviewable'?\ngit status:\n$$(git status --porcelain)" && exit 1)
	@echo "branch is clean"

check-generated-file-only: manifests generate crd webhook generate-mockgen doc
	@go mod tidy
	@echo "checking that branch is clean"
	@test -z "$$(git status --porcelain)" || (echo -e "unclean working tree, did you forget to run 'make reviewable'?\ngit status:\n$$(git status --porcelain)" && exit 1)
	@echo "branch is clean"

##@ Build
build: generate
	CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o bin/config-manager -ldflags $(GO_LDFLAGS) ./cmd/server/main.go

run: ## Run a controller from your host.
	./local/local-run.sh

docker-build: build ## Build the config-manager docker image.
	docker build --platform ${DOCKER_PLATFORMS} --file Dockerfile -t ${CM_IMG} .

docker-push: ## Push the config-manager docker image.
	docker push ${CM_IMG}

# We don't have multi-arch requirement for config-manager right now as it's deployed globally
# docker-buildx-config-manager: ## Build and push the config-manager docker image with buildx.
# 	go mod vendor
# 	docker buildx build --push --platform $(DOCKER_PLATFORMS) --file config-manager.Dockerfile --network host -t ${CM_IMG} .

# We don't have multi-arch requirement for config-manager right now as it's deployed globally
# docker-mutil-arch-config-manager: ## Build and push the multi-arch version config-manager docker image.
# 	go mod vendor
# 	docker buildx create --use --buildkitd-flags '--allow-insecure-entitlement network.host'
# 	docker buildx build --push --platform $(DOCKER_PLATFORMS) --file config-manager.Dockerfile --network host -t ${CM_IMG} .

##@ Deployment

KUSTOMIZE = $(shell pwd)/bin/kustomize
kustomize: ## Download kustomize locally if necessary.
	$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.7)

CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
controller-gen: ## Download controller-gen locally if necessary.
	$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.3)

GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint
golangci-lint: ## Download golangci-lint locally if necessary
	$(call go-get-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0)

MOCKGEN = $(shell pwd)/bin/mockgen
mockgen: ## Download mockgen locally if necessary
	$(call go-get-tool,$(MOCKGEN),github.com/golang/mock/mockgen@v1.6.0)

GINKGO = $(shell pwd)/bin/ginkgo
ginkgo:
	$(call go-get-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo@v2.13.2)

GCOV2LCOV = $(shell pwd)/bin/gcov2lcov
gcov2lcov:
	$(call go-get-tool, $(GCOV2LCOV),github.com/jandelgado/gcov2lcov@v1.0.5)

.PHONY: mockgen
generate-mockgen: mockgen ## General gomock(https://github.com/golang/mock) files
	$(MOCKGEN) -source=./pkg/controllers/configchange/applier_hot_reload.go -destination=./pkg/controllers/configchange/applier_hot_reload_mock.go -package=configchange

# go-get-tool will 'go get' any package $2 and install it to $1.
# go get is deprecated for installing executables since 1.17, https://go.dev/doc/go-get-install-deprecation
# (FIXME): try downgrade your go binary to install this.
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
define go-get-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
