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)
ifeq ($(strip $(WORKSPACE_STATUS)),)
$(error Error: WORKSPACE_STATUS is empty.)
endif
BUILD_VERSION := $(shell echo '$(WORKSPACE_STATUS)' | awk '{ print $$2 }' )
BUILD_GIT_REF := $(shell echo '$(WORKSPACE_STATUS)' | awk '{ print $$4 }' )

# OCI Registry Information
OCI_REGISTRY ?= registry.gitlab.com/gitlab-org/cluster-integration/gitlab-agent
AGENTK_OCI_REPOSITORY_NAME ?= agentk
AGENTK_OCI_REPOSITORY_NAME_FIPS ?= agentk-fips
AGENTK_OCI_REPOSITORY = $(OCI_REGISTRY)/$(AGENTK_OCI_REPOSITORY_NAME)
AGENTK_OCI_REPOSITORY_FIPS = $(OCI_REGISTRY)/$(AGENTK_OCI_REPOSITORY_NAME_FIPS)
# Configuration for regular image tags
AGENTK_IMAGE_TAG_FOR_RELEASE = $(BUILD_VERSION)
AGENTK_DEBUG_IMAGE_TAG_FOR_RELEASE = $(BUILD_VERSION)-debug
# Configuration for FIPS image tags
AGENTK_FIPS_IMAGE_REF_FOR_RELEASE = $(AGENTK_OCI_REPOSITORY_FIPS):$(AGENTK_IMAGE_TAG_FOR_RELEASE)
AGENTK_FIPS_IMAGE_REF_WITH_ARCH_FOR_RELEASE = $(AGENTK_FIPS_IMAGE_REF_FOR_RELEASE)-$(ARCH)

# Deprecated: will be removed in 18.0
# see https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/issues/655
AGENTK_IMAGE_TAG_STABLE_VERSION := stable
AGENTK_IMAGE_TAG_STABLE_DEBUG_VERSION := stable-debug

# Ruby Gem convenience variables
GEM_DIR = ./pkg/ruby
GEM_NAME = gitlab-kas-grpc

# Dependency Versioning
GCI_VERSION := v0.13.5

# Go compiler configuration
LDFLAGS := -X "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/cmd.Version=$(BUILD_VERSION)"
LDFLAGS += -X "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/cmd.GitRef=$(BUILD_GIT_REF)"
GO_BUILD_FLAGS = -ldflags '$(LDFLAGS)'
ifeq ($(FIPS_MODE), 1)
	GO_BUILD_FLAGS += -tags fips
endif
ifeq ($(RACE_MODE), 1)
	GO_BUILD_FLAGS += -race
endif

# Convenience target to print versions
.PHONY: version
version: ## Show version information that would be used in the build artifacts
	@echo "BUILD_VERSION: '$(BUILD_VERSION)'"
	@echo "BUILD_GIT_REF: '$(BUILD_GIT_REF)'"

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

# exit 3 means no changes have been made
.PHONY: buildozer
buildozer: ## Run buildozer
	go run github.com/bazelbuild/buildtools/buildozer@latest -f ./build/buildozer_commands.txt || true

.PHONY: buildifier
buildifier: ## Run buildifer
	go run github.com/bazelbuild/buildtools/buildifier@latest -r .

.PHONY: fmt-bazel
fmt-bazel: gazelle buildozer buildifier  ## Format bazel code

.PHONY: gazelle
gazelle: ## Run gazelle
	bazel run //:gazelle

.PHONY: internal-regenerate-proto
internal-regenerate-proto: ## (internal, use regenerate-proto) Runs bazel directly to regenerate proto
	bazel run //build:extract_generated_proto

.PHONY: regenerate-proto ## Regenerate protobuf files
regenerate-proto: internal-regenerate-proto fmt gazelle

.PHONY: internal-regenerate-mocks
internal-regenerate-mocks: ## (internal, use regenerate-mocks) Runs Go generate directly to regenerate mocks
	PATH="${PATH}:$(shell pwd)/build" go generate -x -v \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/cmd/agentk/agentkapp" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/cmd/kas/kasapp" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/agentk2kas_tunnel/router" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/autoflow/engine" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/autoflow/flow" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/autoflow_codec_server/server" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/flux/agent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/google_profiler/agent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/kubernetes_api/server/watch_aggregator" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/starboard_vulnerability/agent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/redistool" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/syncz" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_agent_registrar" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_agent_tracker" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_agentk2kas_router" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_cache" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_event_tracker" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_gitaly" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_gitlab_access" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_internalgitaly" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_k8s" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_kubernetes_api" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_modagent" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_modserver" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_modshared" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_otel" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_redis" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_rpc" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_stdlib" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_tool" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_tunnel_rpc" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_tunnel_tunserver" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tool/testing/mock_usage_metrics" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/tunnel/tunserver" \
		"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/it"

.PHONY: regenerate-mocks ## Regenerate Go mocks
regenerate-mocks: internal-regenerate-mocks fmt gazelle

.PHONY: update-repos
update-repos: ## Update Go and bazel dependencies
	go mod tidy
	bazel mod tidy

.PHONY: fmt
fmt: ## Format Go code
	go run github.com/daixiang0/gci@$(GCI_VERSION) write cmd $(shell ls -d internal/* | grep -v vendored) pkg -s standard -s default

.PHONY: test
test: fmt gazelle ## Run unit tests
	bazel test -- //...

.PHONY: test-ci
test-ci: ## Run unit tests in CI
	bazel test -- //...

.PHONY: test-it
test-it: ## Run integration tests
	RUN_IT=true go test $(GO_BUILD_FLAGS) -v ./internal/it/...

.PHONY: test-ci-fips
test-ci-fips: ## Run FIPS unit tests in CI
	# 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 ./cmd/... ./internal/... ./pkg/...

.PHONY: verify-ci
verify-ci: delete-generated-files internal-regenerate-proto internal-regenerate-mocks fmt gazelle update-repos ## Verify that all generated assets are up to date
	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: ## Run unit tests but quickly :)
	bazel test \
		--build_tests_only \
		-- //...

.PHONY: agentk-image-build-push-ci
agentk-image-build-push-ci: internal-ensure-ci-commit-sha-env-var ## Build and Push agentk image for use during CI (requires CI_COMMIT_SHA variable)
	bazel run //cmd/agentk:push -- --repository='$(AGENTK_OCI_REPOSITORY)' --tag='$(CI_COMMIT_SHA)'
	bazel run //cmd/agentk:push_debug -- --repository='$(AGENTK_OCI_REPOSITORY)' --tag='$(CI_COMMIT_SHA)'
	crane digest --full-ref '$(AGENTK_OCI_REPOSITORY):$(CI_COMMIT_SHA)' | xargs cosign sign
	crane digest --full-ref '$(AGENTK_OCI_REPOSITORY):$(CI_COMMIT_SHA)' | xargs cosign sign

.PHONY: agentk-fips-image-build-push-ci
agentk-fips-image-build-push-ci: ## Build and Push agentk FIPS image for use during CI (requires CI_COMMIT_SHA variable)
	docker buildx build --build-arg 'BUILDER_IMAGE=$(BUILDER_IMAGE)' --platform 'linux/$(ARCH)' --file build/agentk.fips.Dockerfile --tag '$(AGENTK_OCI_REPOSITORY_FIPS):$(CI_COMMIT_SHA)-$(ARCH)' .
	docker push '$(AGENTK_OCI_REPOSITORY_FIPS):$(CI_COMMIT_SHA)-$(ARCH)'
	crane digest --full-ref '$(AGENTK_OCI_REPOSITORY_FIPS):$(CI_COMMIT_SHA)-$(ARCH)' | xargs cosign sign

.PHONY: agentk-fips-image-manifest-build-push-ci
agentk-fips-image-manifest-build-push-ci: ## Build and Push agentk FIPS image for use during CI (requires CI_COMMIT_SHA variable)
	docker manifest create '$(AGENTK_OCI_REPOSITORY_FIPS):$(CI_COMMIT_SHA)' \
		--amend '$(AGENTK_OCI_REPOSITORY_FIPS):$(CI_COMMIT_SHA)-amd64' \
		--amend '$(AGENTK_OCI_REPOSITORY_FIPS):$(CI_COMMIT_SHA)-arm64'
	docker manifest push '$(AGENTK_OCI_REPOSITORY_FIPS):$(CI_COMMIT_SHA)'

.PHONY: internal-ensure-ci-commit-sha-env-var
internal-ensure-ci-commit-sha-env-var:
ifndef CI_COMMIT_SHA
	$(error CI_COMMIT_SHA is required, please set it)
endif

# 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: ## Release the agentk images (regular and debug) with the stable tag
	bazel run //cmd/agentk:push -- --repository='$(AGENTK_OCI_REPOSITORY)' --tag='$(AGENTK_IMAGE_TAG_STABLE_VERSION)'
	bazel run //cmd/agentk:push_debug -- --repository='$(AGENTK_OCI_REPOSITORY)' --tag='$(AGENTK_IMAGE_TAG_STABLE_DEBUG_VERSION)'
	crane digest --full-ref '$(AGENTK_OCI_REPOSITORY):$(AGENTK_IMAGE_TAG_STABLE_VERSION)' | xargs cosign sign
	crane digest --full-ref '$(AGENTK_OCI_REPOSITORY):$(AGENTK_IMAGE_TAG_STABLE_DEBUG_VERSION)' | xargs cosign sign

# 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: ## Release the agentk images (regular and debu) with the tag version
	bazel run //cmd/agentk:push -- --repository='$(AGENTK_OCI_REPOSITORY)' --tag='$(AGENTK_IMAGE_TAG_FOR_RELEASE)'
	bazel run //cmd/agentk:push_debug -- --repository='$(AGENTK_OCI_REPOSITORY)' --tag='$(AGENTK_DEBUG_IMAGE_TAG_FOR_RELEASE)'
	crane digest --full-ref '$(AGENTK_OCI_REPOSITORY):$(AGENTK_IMAGE_TAG_FOR_RELEASE)' | xargs cosign sign
	crane digest --full-ref '$(AGENTK_OCI_REPOSITORY):$(AGENTK_DEBUG_IMAGE_TAG_FOR_RELEASE)' | xargs cosign sign

# 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: ## Release the agentk FIPS image with the tag version
	docker buildx build --build-arg 'BUILDER_IMAGE=$(BUILDER_IMAGE)' --platform 'linux/$(ARCH)' --file build/agentk.fips.Dockerfile --tag '$(AGENTK_FIPS_IMAGE_REF_WITH_ARCH_FOR_RELEASE)' .
	docker push '$(AGENTK_FIPS_IMAGE_REF_WITH_ARCH_FOR_RELEASE)'
	crane digest --full-ref '$(AGENTK_FIPS_IMAGE_REF_WITH_ARCH_FOR_RELEASE)' | xargs cosign sign

.PHONY: release-tag-fips-manifest
release-tag-fips-manifest: ## Release the image manifest file for the agentk FIPS images
	docker manifest create '$(AGENTK_FIPS_IMAGE_REF_FOR_RELEASE)' \
		--amend '$(AGENTK_FIPS_IMAGE_REF_FOR_RELEASE)-amd64' \
		--amend '$(AGENTK_FIPS_IMAGE_REF_FOR_RELEASE)-arm64'
	docker manifest push '$(AGENTK_FIPS_IMAGE_REF_FOR_RELEASE)'

# Set TARGET_DIRECTORY variable to the target directory before running this target
.PHONY: gdk-install
gdk-install: ## Install KAS for GDK
	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: ## Build KAS binary
	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: ## Build agentk binary
	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: ## 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: ## Delete all generated files
	find . -name '*.pb.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: ## Build the gitlab-kas-grpc Ruby gem
	(cd $(GEM_DIR) && gem build $(GEM_NAME).gemspec)

# Publish the KAS gRPC ruby gem
.PHONY: publish-gem
publish-gem: build-gem ## Publish the gitlab-kas-grpc Ruby gem
	(cd $(GEM_DIR) && gem push $(GEM_NAME)-*.gem)

# Update releases info
.PHONY: vendor-releases-info
vendor-releases-info: ## Update the vendored GitLab release information assets
	./build/vendor_releases_info.sh

.PHONY: help
help: ## Show this help page
	@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
