SHELL = /usr/bin/env bash -eo pipefail

# NOTE: see ./build/print_workspace_status.sh for details about SSoT of these values
WORKSPACE_STATUS := $(shell ./build/print_workspace_status.sh)
VERSION := $(shell echo '$(WORKSPACE_STATUS)' | awk '{ print $$2 }' )
BUILD_REF := $(shell echo '$(WORKSPACE_STATUS)' | awk '{ print $$4 }' )
BUILD_TIME := $(shell echo '$(WORKSPACE_STATUS)' | awk '{ print $$6 }' )

AGENTK_IMAGE_TAG_VERSION = $(VERSION)
AGENTK_IMAGE_TAG_DEBUG_VERSION = $(VERSION)-debug
AGENTK_IMAGE_TAG_LATEST_VERSION := latest
AGENTK_IMAGE_TAG_LATEST_DEBUG_VERSION := latest-debug
AGENTK_IMAGE_TAG_STABLE_VERSION := stable
AGENTK_IMAGE_TAG_STABLE_DEBUG_VERSION := stable-debug
AGENTK_IMAGE_TAG_COMMIT_VERSION = $(VERSION)
AGENTK_IMAGE_TAG_COMMIT_DEBUG_VERSION = $(VERSION)-debug

AGENTK_FIPS_IMAGE_TAG = $(CI_REGISTRY_IMAGE)/agentk-fips:$(AGENTK_IMAGE_TAG_VERSION)
AGENTK_FIPS_IMAGE_TAG_WITH_ARCH = $(AGENTK_FIPS_IMAGE_TAG)-$(ARCH)
AGENTK_FIPS_STABLE_IMAGE_TAG = $(CI_REGISTRY_IMAGE)/agentk-fips:stable
AGENTK_FIPS_STABLE_IMAGE_TAG_WITH_ARCH = $(AGENTK_FIPS_STABLE_IMAGE_TAG)-$(ARCH)

LDFLAGS := -X "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/cmd.Version=$(VERSION)"
LDFLAGS += -X "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/cmd.Commit=$(BUILD_REF)"
LDFLAGS += -X "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/cmd.BuildTime=$(BUILD_TIME)"

GO_BUILD_FLAGS = -ldflags '$(LDFLAGS)'

ifeq ($(FIPS_MODE), 1)
	GO_BUILD_FLAGS += -tags fips
endif

CI_REGISTRY ?= registry.gitlab.com
CI_PROJECT_PATH ?= gitlab-org/cluster-integration/gitlab-agent
OCI_REPO = $(CI_REGISTRY)/$(CI_PROJECT_PATH)/agentk

# Convenience target to print versions
.PHONY: version
version:
	@echo "VERSION: '$(VERSION)'"
	@echo "BUILD_REF: '$(BUILD_REF)'"
	@echo "BUILD_TIME: '$(BUILD_TIME)'"

# Install using your package manager, as recommended by
# https://golangci-lint.run/usage/install/#local-installation
.PHONY: lint
lint:
	golangci-lint run

.PHONY: buildozer
buildozer:
	bazel run //:buildozer

.PHONY: buildifier
buildifier:
	bazel run //:buildifier

.PHONY: fmt-bazel
fmt-bazel: gazelle buildozer buildifier

.PHONY: gazelle
gazelle:
	bazel run //:gazelle

.PHONY: internal-regenerate-proto
internal-regenerate-proto:
	bazel run //build:extract_generated_proto

.PHONY: regenerate-proto
regenerate-proto: internal-regenerate-proto fmt update-bazel

.PHONY: internal-regenerate-mocks
internal-regenerate-mocks:
	PATH="${PATH}:$(shell pwd)/build" go generate -x -v \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/cmd/agentk/agentkapp" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/cmd/kas/kasapp" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/flux/agent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/flux/rpc" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/gitops/agent/manifestops" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/google_profiler/agent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/starboard_vulnerability/agent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/redistool" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/syncz" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_agent_registrar" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_agent_tracker" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_agentk2kas_router" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_cache" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_event_tracker" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_gitaly" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_gitlab_access" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_internalgitaly" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_k8s" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_kubernetes_api" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_modagent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_modserver" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_modshared" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_otel" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_redis" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_reverse_tunnel_rpc" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_rpc" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_stdlib" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_tool" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_tunnel_tunserver" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/testing/mock_usage_metrics" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tunnel/tunserver"

.PHONY: regenerate-mocks
regenerate-mocks: internal-regenerate-mocks fmt update-bazel

.PHONY: update-repos
update-repos:
	go mod tidy
	bazel run \
		//:gazelle -- \
		update-repos \
		-from_file=go.mod \
		-prune=true \
		-build_file_proto_mode=disable_global \
		-to_macro=build/repositories.bzl%go_repositories
	go mod tidy

.PHONY: update-bazel
update-bazel: gazelle

.PHONY: fmt
fmt:
	go run github.com/daixiang0/gci@v0.11.2 write cmd internal pkg -s standard -s default

.PHONY: test
test: fmt update-bazel
	bazel test -- //...

.PHONY: test-ci
test-ci:
	bazel test -- //... //cmd/agentk:push //cmd/agentk:push_debug //cmd/kas:push //cmd/kas:push_debug

.PHONY: test-ci-fips
test-ci-fips:
	# No -race because it doesn't work on arm64: https://github.com/golang/go/issues/29948
	# FATAL: ThreadSanitizer: unsupported VMA range
	# FATAL: Found 39 - Supported 48
	go test $(GO_BUILD_FLAGS) -v ./...

.PHONY: verify-ci
verify-ci: delete-generated-files internal-regenerate-proto internal-regenerate-mocks fmt update-bazel update-repos
	git add .
	git diff --cached --quiet ':(exclude).bazelrc' || (echo "Error: uncommitted changes detected:" && git --no-pager diff --cached && exit 1)

.PHONY: quick-test
quick-test:
	bazel test \
		--build_tests_only \
		-- //...

# Build and push all docker images tagged as "latest".
# This only works on a linux machine
# Flags are for oci_push rule. Docs https://docs.aspect.build/rules/rules_oci/docs/push.
.PHONY: release-latest
release-latest:
	bazel run //cmd/agentk:push -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_LATEST_VERSION)'
	bazel run //cmd/agentk:push_debug -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_LATEST_DEBUG_VERSION)'

# Build and push all docker images tagged as "stable".
# This only works on a linux machine
# Flags are for oci_push rule. Docs https://docs.aspect.build/rules/rules_oci/docs/push.
.PHONY: release-stable
release-stable:
	bazel run //cmd/agentk:push -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_STABLE_VERSION)'
	bazel run //cmd/agentk:push_debug -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_STABLE_DEBUG_VERSION)'

# Build and push all FIPS docker images tagged with the tag on the current commit and as "stable".
# This only works on a linux machine
# Set ARCH to the desired CPU architecture.
# Set CI_REGISTRY_IMAGE to the desired container image name.
# Set BUILDER_IMAGE to the image to be used for building the agentk binary.
.PHONY: release-tag-and-stable-fips
release-tag-and-stable-fips:
	docker buildx build --build-arg 'BUILDER_IMAGE=$(BUILDER_IMAGE)' --platform 'linux/$(ARCH)' --file build/agentk.ubi8-fips.Dockerfile --tag '$(AGENTK_FIPS_IMAGE_TAG_WITH_ARCH)' --tag '$(AGENTK_FIPS_STABLE_IMAGE_TAG_WITH_ARCH)' .
	docker push '$(AGENTK_FIPS_IMAGE_TAG_WITH_ARCH)'
	docker push '$(AGENTK_FIPS_STABLE_IMAGE_TAG_WITH_ARCH)'

.PHONY: release-tag-and-stable-fips-manifest
release-tag-and-stable-fips-manifest:
	docker manifest create '$(AGENTK_FIPS_IMAGE_TAG)' \
		--amend '$(AGENTK_FIPS_IMAGE_TAG)-amd64' \
		--amend '$(AGENTK_FIPS_IMAGE_TAG)-arm64'
	docker manifest push '$(AGENTK_FIPS_IMAGE_TAG)'
	docker manifest create '$(AGENTK_FIPS_STABLE_IMAGE_TAG)' \
		--amend '$(AGENTK_FIPS_STABLE_IMAGE_TAG)-amd64' \
		--amend '$(AGENTK_FIPS_STABLE_IMAGE_TAG)-arm64'
	docker manifest push '$(AGENTK_FIPS_STABLE_IMAGE_TAG)'

# Build and push all docker images tagged with the tag on the current commit.
# This only works on a linux machine
# Flags are for oci_push rule. Docs https://docs.aspect.build/rules/rules_oci/docs/push.
.PHONY: release-tag
release-tag:
	bazel run //cmd/agentk:push -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_VERSION)'
	bazel run //cmd/agentk:push_debug -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_DEBUG_VERSION)'

# Build and push all FIPS docker images tagged with the tag on the current commit.
# This only works on a linux machine
# Set ARCH to the desired CPU architecture.
# Set CI_REGISTRY_IMAGE to the desired container image name.
# Set BUILDER_IMAGE to the image to be used for building the agentk binary.
.PHONY: release-tag-fips
release-tag-fips:
	docker buildx build --build-arg 'BUILDER_IMAGE=$(BUILDER_IMAGE)' --platform 'linux/$(ARCH)' --file build/agentk.ubi8-fips.Dockerfile --tag '$(AGENTK_FIPS_IMAGE_TAG_WITH_ARCH)' .
	docker push '$(AGENTK_FIPS_IMAGE_TAG_WITH_ARCH)'

.PHONY: release-tag-fips-manifest
release-tag-fips-manifest:
	docker manifest create '$(AGENTK_FIPS_IMAGE_TAG)' \
		--amend '$(AGENTK_FIPS_IMAGE_TAG)-amd64' \
		--amend '$(AGENTK_FIPS_IMAGE_TAG)-arm64'
	docker manifest push '$(AGENTK_FIPS_IMAGE_TAG)'

# Build and push all docker images tagged with as the current commit.
# This only works on a linux machine
.PHONY: release-commit
release-commit:
	bazel run //cmd/agentk:push -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_COMMIT_VERSION)'
	bazel run //cmd/agentk:push_debug -- --repository='$(OCI_REPO)' --tag='$(AGENTK_IMAGE_TAG_COMMIT_DEBUG_VERSION)'

# Set TARGET_DIRECTORY variable to the target directory before running this target
.PHONY: gdk-install
gdk-install:
	bazel run //cmd/kas:extract_kas_race
	mv 'cmd/kas/kas_race' '$(TARGET_DIRECTORY)'

# Set TARGET_DIRECTORY variable to the target directory before running this target
# This target is used by:
# - CNG: https://gitlab.com/gitlab-org/build/CNG/-/tree/master/gitlab-kas
# - Omnibus: https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/master/config/software/gitlab-kas.rb
.PHONY: kas
kas:
	go build \
		$(GO_BUILD_FLAGS) \
		-o '$(TARGET_DIRECTORY)' ./cmd/kas

# Set TARGET_DIRECTORY variable to the target directory before running this target
# This target is used by FIPS build in this repo.
.PHONY: agentk
agentk:
	go build \
		$(GO_BUILD_FLAGS) \
		-o '$(TARGET_DIRECTORY)' ./cmd/agentk

# https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies
.PHONY: show-go-dependency-updates
show-go-dependency-updates:
	go list \
		-u -f '{{if (and (not (or .Main .Indirect)) .Update)}}{{.Path}} {{.Version}} -> {{.Update.Version}}{{end}}' -m all

.PHONY: delete-generated-files
delete-generated-files:
	find . -name '*.pb.go' -type f -delete
	find . -name '*.pb.validate.go' -type f -delete
	find . \( -name '*_pb.rb' -and -not -name 'validate_pb.rb' \) -type f -delete
	find . -name '*_proto_docs.md' -type f -delete

# Build the KAS gRPC ruby gem
.PHONY: build-gem
build-gem:
	cd pkg/ruby && gem build kas-grpc.gemspec
