# Load local overrides (gitignored) — e.g. KAGENT_HELM_EXTRA_ARGS=-f helm/kagent/values.local.yaml
-include .env

# Image configuration
DOCKER_REGISTRY ?= localhost:5001
BASE_IMAGE_REGISTRY ?= cgr.dev
DOCKER_REPO ?= kagent-dev/kagent
HELM_REPO ?= oci://ghcr.io/kagent-dev
HELM_DIST_FOLDER ?= dist

BUILD_DATE := $(shell date -u '+%Y-%m-%d')
GIT_COMMIT := $(shell git rev-parse --short HEAD || echo "unknown")
VERSION ?= $(shell git describe --tags --always 2>/dev/null | grep v || echo "v0.0.0-$(GIT_COMMIT)")

# Local architecture detection to build for the current platform
LOCALARCH ?= $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')

KUBECONFIG_PERM ?= $(shell \
  if [ "$$(uname -s | tr '[:upper:]' '[:lower:]')" = "darwin" ]; then \
    stat -f "%Lp" ~/.kube/config; \
  else \
    stat -c "%a" ~/.kube/config; \
  fi)


# Container runtime: "docker" (default) or "podman".
# Set CONTAINER_RUNTIME=podman to use Podman for all container operations.
CONTAINER_RUNTIME ?= docker

# Buildx configuration
BUILDKIT_VERSION = v0.23.0
BUILDX_NO_DEFAULT_ATTESTATIONS=1
BUILDX_BUILDER_NAME ?= kagent-builder-$(BUILDKIT_VERSION)

ifeq ($(CONTAINER_RUNTIME),podman)
  DOCKER_BUILDER ?= $(CONTAINER_RUNTIME) build
  DOCKER_BUILD_ARGS ?= --platform linux/$(LOCALARCH)
  # Podman needs a separate push step (no --push on build).
  # --tls-verify=false is needed for local insecure registries (e.g. kind-registry).
  # Override PODMAN_TLS_VERIFY=true when pushing to a remote TLS registry.
  PODMAN_TLS_VERIFY ?= false
  DOCKER_PUSH = $(CONTAINER_RUNTIME) push --tls-verify=$(PODMAN_TLS_VERIFY)
else
  DOCKER_BUILDER ?= $(CONTAINER_RUNTIME) buildx build
  DOCKER_BUILD_ARGS ?= --push --platform linux/$(LOCALARCH)
  # Docker buildx --push handles push inline; no separate push step needed.
  DOCKER_PUSH = @true
endif

KIND_CLUSTER_NAME ?= kagent
KIND_IMAGE_VERSION ?= 1.35.0

CONTROLLER_IMAGE_NAME ?= controller
UI_IMAGE_NAME ?= ui
APP_IMAGE_NAME ?= app
KAGENT_ADK_IMAGE_NAME ?= kagent-adk
GOLANG_ADK_IMAGE_NAME ?= golang-adk
SKILLS_INIT_IMAGE_NAME ?= skills-init

CONTROLLER_IMAGE_TAG ?= $(VERSION)
UI_IMAGE_TAG ?= $(VERSION)
APP_IMAGE_TAG ?= $(VERSION)
KAGENT_ADK_IMAGE_TAG ?= $(VERSION)
GOLANG_ADK_IMAGE_TAG ?= $(VERSION)
GOLANG_ADK_FULL_IMAGE_TAG ?= $(VERSION)-full
SKILLS_INIT_IMAGE_TAG ?= $(VERSION)
CONTROLLER_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(CONTROLLER_IMAGE_NAME):$(CONTROLLER_IMAGE_TAG)
UI_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(UI_IMAGE_NAME):$(UI_IMAGE_TAG)
APP_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(APP_IMAGE_NAME):$(APP_IMAGE_TAG)
KAGENT_ADK_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(KAGENT_ADK_IMAGE_NAME):$(KAGENT_ADK_IMAGE_TAG)
GOLANG_ADK_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(GOLANG_ADK_IMAGE_NAME):$(GOLANG_ADK_IMAGE_TAG)
GOLANG_ADK_FULL_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(GOLANG_ADK_IMAGE_NAME):$(GOLANG_ADK_FULL_IMAGE_TAG)
SKILLS_INIT_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(SKILLS_INIT_IMAGE_NAME):$(SKILLS_INIT_IMAGE_TAG)

#take from go/go.mod
AWK ?= $(shell command -v gawk || command -v awk)
TOOLS_GO_VERSION ?= $(shell $(AWK) '/^go / { print $$2 }' go/go.mod)
export GOTOOLCHAIN=go$(TOOLS_GO_VERSION)

# Version information for the build
LDFLAGS := "-X github.com/$(DOCKER_REPO)/go/core/internal/version.Version=$(VERSION)      \
            -X github.com/$(DOCKER_REPO)/go/core/internal/version.GitCommit=$(GIT_COMMIT) \
            -X github.com/$(DOCKER_REPO)/go/core/internal/version.BuildDate=$(BUILD_DATE)"

#tools versions
TOOLS_UV_VERSION ?= 0.10.4
TOOLS_NODE_VERSION ?= 24.13.0
TOOLS_PYTHON_VERSION ?= 3.13

# build args
TOOLS_IMAGE_BUILD_ARGS =  --build-arg VERSION=$(VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg LDFLAGS=$(LDFLAGS)
TOOLS_IMAGE_BUILD_ARGS += --build-arg DOCKER_REPO=$(DOCKER_REPO)
TOOLS_IMAGE_BUILD_ARGS += --build-arg DOCKER_REGISTRY=$(DOCKER_REGISTRY)
TOOLS_IMAGE_BUILD_ARGS += --build-arg BASE_IMAGE_REGISTRY=$(BASE_IMAGE_REGISTRY)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_GO_VERSION=$(TOOLS_GO_VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_UV_VERSION=$(TOOLS_UV_VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_PYTHON_VERSION=$(TOOLS_PYTHON_VERSION)
TOOLS_IMAGE_BUILD_ARGS += --build-arg TOOLS_NODE_VERSION=$(TOOLS_NODE_VERSION)


##@ 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 command 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

.PHONY: help
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%-24s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

.PHONY: print-tools-versions
print-tools-versions: ## Print tools versions
	@echo "VERSION      : $(VERSION)"
	@echo "Tools Go     : $(TOOLS_GO_VERSION)"
	@echo "Tools UV     : $(TOOLS_UV_VERSION)"
	@echo "Tools Node   : $(TOOLS_NODE_VERSION)"
	@echo "Tools Istio  : $(TOOLS_ISTIO_VERSION)"
	@echo "Tools Argo CD: $(TOOLS_ARGO_CD_VERSION)"

##@ Git

.PHONY: init-git-hooks
init-git-hooks:  ## Use the tracked version of Git hooks from this repo
	git config core.hooksPath .githooks
	echo "Git hooks initialized"

# KMCP
KMCP_ENABLED ?= true
KMCP_VERSION ?= $(shell $(AWK) '/github\.com\/kagent-dev\/kmcp/ { print substr($$2, 2) }' go/go.mod) # KMCP version defaults to what's referenced in go.mod

HELM_ACTION=upgrade --install

# Helm chart variables
KAGENT_DEFAULT_MODEL_PROVIDER ?= openAI


##@ Build

.PHONY: check-api-key
check-api-key: ## Validate required API key for the configured model provider
	@if [ "$(KAGENT_DEFAULT_MODEL_PROVIDER)" = "openAI" ]; then \
		if [ -z "$(OPENAI_API_KEY)" ]; then \
			echo "Error: OPENAI_API_KEY environment variable is not set for OpenAI provider"; \
			echo "Please set it with: export OPENAI_API_KEY=your-api-key"; \
			exit 1; \
		fi; \
	elif [ "$(KAGENT_DEFAULT_MODEL_PROVIDER)" = "anthropic" ]; then \
		if [ -z "$(ANTHROPIC_API_KEY)" ]; then \
			echo "Error: ANTHROPIC_API_KEY environment variable is not set for Anthropic provider"; \
			echo "Please set it with: export ANTHROPIC_API_KEY=your-api-key"; \
			exit 1; \
		fi; \
	elif [ "$(KAGENT_DEFAULT_MODEL_PROVIDER)" = "azureOpenAI" ]; then \
		if [ -z "$(AZUREOPENAI_API_KEY)" ]; then \
			echo "Error: AZUREOPENAI_API_KEY environment variable is not set for Azure OpenAI provider"; \
			echo "Please set it with: export AZUREOPENAI_API_KEY=your-api-key"; \
			exit 1; \
		fi; \
	elif [ "$(KAGENT_DEFAULT_MODEL_PROVIDER)" = "gemini" ]; then \
		if [ -z "$(GOOGLE_API_KEY)" ]; then \
			echo "Error: GOOGLE_API_KEY environment variable is not set for Gemini provider"; \
			echo "Please set it with: export GOOGLE_API_KEY=your-api-key"; \
			exit 1; \
		fi; \
	elif [ "$(KAGENT_DEFAULT_MODEL_PROVIDER)" = "ollama" ]; then \
		echo "Note: Ollama provider does not require an API key"; \
	else \
		echo "Warning: Unknown model provider '$(KAGENT_DEFAULT_MODEL_PROVIDER)'. Skipping API key check."; \
	fi

.PHONY: buildx-create
buildx-create: ## Create or reuse the buildx builder instance
ifeq ($(CONTAINER_RUNTIME),podman)
	@echo "Podman detected; skipping buildx builder setup (using built-in buildx)."
else
	$(CONTAINER_RUNTIME) buildx inspect $(BUILDX_BUILDER_NAME) 2>&1 > /dev/null || \
	$(CONTAINER_RUNTIME) buildx create --name $(BUILDX_BUILDER_NAME) --platform linux/amd64,linux/arm64 --driver docker-container --use --driver-opt network=host || true
	$(CONTAINER_RUNTIME) buildx use $(BUILDX_BUILDER_NAME) || true
endif

.PHONY: build-all
build-all: ## Build all images for amd64+arm64 without pushing (outputs to /dev/null for CI validation)
build-all: BUILD_ARGS ?= --progress=plain --builder $(BUILDX_BUILDER_NAME) --platform linux/amd64,linux/arm64 --output type=tar,dest=/dev/null
build-all: buildx-create
	$(DOCKER_BUILDER) $(BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -f go/Dockerfile     ./go
	$(DOCKER_BUILDER) $(BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -f go/Dockerfile.full ./go
	$(DOCKER_BUILDER) $(BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -f ui/Dockerfile     ./ui
	$(DOCKER_BUILDER) $(BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -f python/Dockerfile ./python

.PHONY: build
build: ## Build and push all component images
build: buildx-create build-controller build-ui build-app build-golang-adk build-golang-adk-full build-skills-init
	@echo "Build completed successfully."
	@echo "Controller Image: $(CONTROLLER_IMG)"
	@echo "UI Image: $(UI_IMG)"
	@echo "App Image: $(APP_IMG)"
	@echo "Kagent ADK Image: $(KAGENT_ADK_IMG)"
	@echo "Golang ADK Image: $(GOLANG_ADK_IMG)"
	@echo "Golang ADK Full Image: $(GOLANG_ADK_FULL_IMG)"
	@echo "Skills Init Image: $(SKILLS_INIT_IMG)"

.PHONY: build-monitor
build-monitor: ## Watch BuildKit process list inside the buildx container
build-monitor: buildx-create
ifeq ($(CONTAINER_RUNTIME),podman)
	@echo "build-monitor is not supported with Podman (no external buildkit container)"
else
	watch $(CONTAINER_RUNTIME) exec -t  buildx_buildkit_$(BUILDX_BUILDER_NAME)0  ps
endif

.PHONY: build-cli
build-cli: ## Build the kagent CLI (cross-compiled via go sub-make)
	make -C go build

.PHONY: build-cli-local
build-cli-local: ## Build the kagent CLI binary for the local machine
	make -C go clean
	make -C go core/bin/kagent-local

.PHONY: build-img-versions
build-img-versions: ## Print the fully-qualified image tags for all components
	@echo controller=$(CONTROLLER_IMG)
	@echo ui=$(UI_IMG)
	@echo app=$(APP_IMG)
	@echo kagent-adk=$(KAGENT_ADK_IMG)
	@echo golang-adk=$(GOLANG_ADK_IMG)
	@echo golang-adk-full=$(GOLANG_ADK_FULL_IMG)
	@echo skills-init=$(SKILLS_INIT_IMG)

.PHONY: controller-manifests
controller-manifests: ## Regenerate CRD manifests and copy them into the Helm chart
	make -C go manifests
	cp go/api/config/crd/bases/* helm/kagent-crds/templates/

.PHONY: build-controller
build-controller: ## Build and push the controller image
build-controller: buildx-create controller-manifests
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg BUILD_PACKAGE=core/cmd/controller/main.go -t $(CONTROLLER_IMG) -f go/Dockerfile ./go
	$(DOCKER_PUSH) $(CONTROLLER_IMG)

.PHONY: build-ui
build-ui: ## Build and push the UI image
build-ui: buildx-create
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(UI_IMG) -f ui/Dockerfile ./ui
	$(DOCKER_PUSH) $(UI_IMG)

.PHONY: build-kagent-adk
build-kagent-adk: ## Build and push the Python kagent ADK image
build-kagent-adk: buildx-create
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(KAGENT_ADK_IMG) -f python/Dockerfile ./python
	$(DOCKER_PUSH) $(KAGENT_ADK_IMG)

.PHONY: build-app
build-app: ## Build and push the app image (depends on kagent-adk)
build-app: buildx-create build-kagent-adk
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg KAGENT_ADK_VERSION=$(KAGENT_ADK_IMAGE_TAG) --build-arg DOCKER_REGISTRY=$(DOCKER_REGISTRY) -t $(APP_IMG) -f python/Dockerfile.app ./python
	$(DOCKER_PUSH) $(APP_IMG)

.PHONY: build-golang-adk
build-golang-adk: ## Build and push the Go ADK image
build-golang-adk: buildx-create
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg BUILD_PACKAGE=adk/cmd/main.go -t $(GOLANG_ADK_IMG) -f go/Dockerfile ./go
	$(DOCKER_PUSH) $(GOLANG_ADK_IMG)

.PHONY: build-golang-adk-full
build-golang-adk-full: ## Build and push the Go ADK full image (with extra tooling)
build-golang-adk-full: buildx-create
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) --build-arg BUILD_PACKAGE=adk/cmd/main.go -t $(GOLANG_ADK_FULL_IMG) -f go/Dockerfile.full ./go
	$(DOCKER_PUSH) $(GOLANG_ADK_FULL_IMG)

.PHONY: build-skills-init
build-skills-init: ## Build and push the skills-init image
build-skills-init: buildx-create
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) -t $(SKILLS_INIT_IMG) -f docker/skills-init/Dockerfile docker/skills-init
	$(DOCKER_PUSH) $(SKILLS_INIT_IMG)

.PHONY: push
push: ## Push all component images (controller, ui, app, ADKs)
push: push-controller push-ui push-app push-kagent-adk push-golang-adk push-golang-adk-full


##@ Testing

.PHONY: lint
lint: ## Run linters for Go and Python
	make -C go lint
	make -C python lint

.PHONY: push-test-agent
push-test-agent: buildx-create build-kagent-adk ## Build and push E2E test agent images to the local registry
	echo "Building FROM DOCKER_REGISTRY=$(DOCKER_REGISTRY)/$(DOCKER_REPO)/kagent-adk:$(VERSION)"
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/kebab:latest -f go/core/test/e2e/agents/kebab/Dockerfile ./go/core/test/e2e/agents/kebab
	$(DOCKER_PUSH) $(DOCKER_REGISTRY)/kebab:latest
	kubectl apply --namespace kagent --context kind-$(KIND_CLUSTER_NAME) -f go/core/test/e2e/agents/kebab/agent.yaml
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/poem-flow:latest -f python/samples/crewai/poem_flow/Dockerfile ./python
	$(DOCKER_PUSH) $(DOCKER_REGISTRY)/poem-flow:latest
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/basic-openai:latest -f python/samples/openai/basic_agent/Dockerfile ./python
	$(DOCKER_PUSH) $(DOCKER_REGISTRY)/basic-openai:latest
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/langgraph-kebab:latest -f python/samples/langgraph/kebab/Dockerfile ./python
	$(DOCKER_PUSH) $(DOCKER_REGISTRY)/langgraph-kebab:latest

.PHONY: push-test-skill
push-test-skill: buildx-create ## Build and push E2E test skill images to the local registry
	echo "Building FROM DOCKER_REGISTRY=$(DOCKER_REGISTRY)/$(DOCKER_REPO)/kebab-maker:$(VERSION)"
	$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/kebab-maker:latest -f go/core/test/e2e/testdata/skills/kebab-maker/Dockerfile ./go/core/test/e2e/testdata/skills/kebab-maker
	$(DOCKER_PUSH) $(DOCKER_REGISTRY)/kebab-maker:latest


##@ Cluster

.PHONY: create-kind-cluster

create-kind-cluster: ## Create a local kind cluster with MetalLB
	CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) bash ./scripts/kind/setup-kind.sh
	CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) bash ./scripts/kind/setup-metallb.sh

.PHONY: use-kind-cluster
use-kind-cluster: ## Merge kind kubeconfig and set kagent as the default namespace
	kind get kubeconfig --name $(KIND_CLUSTER_NAME) > /tmp/kind-config
	KUBECONFIG=~/.kube/config:/tmp/kind-config kubectl config view --merge --flatten > ~/.kube/config.tmp && mv ~/.kube/config.tmp ~/.kube/config && chmod $(KUBECONFIG_PERM) ~/.kube/config
	kubectl --context kind-$(KIND_CLUSTER_NAME) create namespace kagent || true
	kubectl config set-context kind-$(KIND_CLUSTER_NAME) --namespace kagent || true

.PHONY: delete-kind-cluster
delete-kind-cluster: ## Delete the local kind cluster
	kind delete cluster --name $(KIND_CLUSTER_NAME)


##@ Helm

.PHONY: helm-cleanup
helm-cleanup: ## Remove packaged Helm charts from the dist folder
	rm -f ./$(HELM_DIST_FOLDER)/*.tgz

.PHONY: helm-test
helm-test: ## Render Helm templates for all providers and run helm unittest
helm-test: helm-version
	mkdir -p tmp
	echo $$(helm template kagent ./helm/kagent/ --namespace kagent --set providers.default=ollama																	| tee tmp/ollama.yaml 		| grep ^kind: | wc -l)
	echo $$(helm template kagent ./helm/kagent/ --namespace kagent --set providers.default=openAI       --set providers.openAI.apiKey=your-openai-api-key 			| tee tmp/openAI.yaml 		| grep ^kind: | wc -l)
	echo $$(helm template kagent ./helm/kagent/ --namespace kagent --set providers.default=anthropic    --set providers.anthropic.apiKey=your-anthropic-api-key 	| tee tmp/anthropic.yaml 	| grep ^kind: | wc -l)
	echo $$(helm template kagent ./helm/kagent/ --namespace kagent --set providers.default=azureOpenAI  --set providers.azureOpenAI.apiKey=your-openai-api-key		| tee tmp/azureOpenAI.yaml	| grep ^kind: | wc -l)
	echo $$(helm template kagent ./helm/kagent/ --namespace kagent --set providers.default=gemini       --set providers.gemini.apiKey=your-gemini-api-key 			| tee tmp/gemini.yaml 		| grep ^kind: | wc -l)
	helm plugin ls | grep unittest || helm plugin install https://github.com/helm-unittest/helm-unittest.git
	helm unittest helm/kagent

.PHONY: helm-agents
helm-agents: ## Package all agent Helm charts into the dist folder
	VERSION=$(VERSION) envsubst < helm/agents/k8s/Chart-template.yaml > helm/agents/k8s/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/k8s
	VERSION=$(VERSION) envsubst < helm/agents/kgateway/Chart-template.yaml > helm/agents/kgateway/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/kgateway
	VERSION=$(VERSION) envsubst < helm/agents/istio/Chart-template.yaml > helm/agents/istio/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/istio
	VERSION=$(VERSION) envsubst < helm/agents/promql/Chart-template.yaml > helm/agents/promql/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/promql
	VERSION=$(VERSION) envsubst < helm/agents/observability/Chart-template.yaml > helm/agents/observability/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/observability
	VERSION=$(VERSION) envsubst < helm/agents/helm/Chart-template.yaml > helm/agents/helm/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/helm
	VERSION=$(VERSION) envsubst < helm/agents/argo-rollouts/Chart-template.yaml > helm/agents/argo-rollouts/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/argo-rollouts
	VERSION=$(VERSION) envsubst < helm/agents/cilium-policy/Chart-template.yaml > helm/agents/cilium-policy/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/cilium-policy
	VERSION=$(VERSION) envsubst < helm/agents/cilium-debug/Chart-template.yaml > helm/agents/cilium-debug/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/cilium-debug
	VERSION=$(VERSION) envsubst < helm/agents/cilium-manager/Chart-template.yaml > helm/agents/cilium-manager/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/agents/cilium-manager

.PHONY: helm-tools
helm-tools: ## Package all tool Helm charts into the dist folder
	VERSION=$(VERSION) envsubst < helm/tools/grafana-mcp/Chart-template.yaml > helm/tools/grafana-mcp/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/tools/grafana-mcp
	VERSION=$(VERSION) envsubst < helm/tools/querydoc/Chart-template.yaml > helm/tools/querydoc/Chart.yaml
	helm package -d $(HELM_DIST_FOLDER) helm/tools/querydoc

.PHONY: helm-version
helm-version: ## Stamp chart versions, update dependencies, and package kagent + kagent-crds
helm-version: helm-cleanup helm-agents helm-tools
	VERSION=$(VERSION) KMCP_VERSION=$(KMCP_VERSION) envsubst < helm/kagent-crds/Chart-template.yaml > helm/kagent-crds/Chart.yaml
	VERSION=$(VERSION) KMCP_VERSION=$(KMCP_VERSION) envsubst < helm/kagent/Chart-template.yaml > helm/kagent/Chart.yaml
	helm dependency update helm/kagent
	helm dependency update helm/kagent-crds
	helm package -d $(HELM_DIST_FOLDER) helm/kagent-crds
	helm package -d $(HELM_DIST_FOLDER) helm/kagent

.PHONY: helm-install-provider
helm-install-provider: ## Install or upgrade kagent-crds and kagent Helm releases on the kind cluster
helm-install-provider: helm-version check-api-key
	helm $(HELM_ACTION) kagent-crds helm/kagent-crds \
		--namespace kagent \
		--create-namespace \
		--history-max 2    \
		--timeout 5m 			\
		--kube-context kind-$(KIND_CLUSTER_NAME) \
		--wait \
		--set kmcp.enabled=$(KMCP_ENABLED)
	helm $(HELM_ACTION) kagent helm/kagent \
		--namespace kagent \
		--create-namespace \
		--history-max 2    \
		--timeout 5m       \
		--kube-context kind-$(KIND_CLUSTER_NAME) \
		--wait \
		--set ui.service.type=LoadBalancer \
		--set registry=$(DOCKER_REGISTRY) \
		--set imagePullPolicy=Always \
		--set tag=$(VERSION) \
		--set controller.loglevel=debug \
		--set controller.image.pullPolicy=Always \
		--set ui.image.pullPolicy=Always \
		--set controller.service.type=LoadBalancer \
		--set providers.openAI.apiKey=$(OPENAI_API_KEY) \
		--set providers.azureOpenAI.apiKey=$(AZUREOPENAI_API_KEY) \
		--set providers.anthropic.apiKey=$(ANTHROPIC_API_KEY) \
		--set providers.gemini.apiKey=$(GOOGLE_API_KEY) \
		--set providers.default=$(KAGENT_DEFAULT_MODEL_PROVIDER) \
		--set kmcp.enabled=$(KMCP_ENABLED) \
		--set kmcp.image.tag=$(KMCP_VERSION) \
		--set querydoc.openai.apiKey=$(OPENAI_API_KEY) \
		--set database.postgres.bundled.image.repository=pgvector \
		--set database.postgres.bundled.image.name=pgvector \
		--set database.postgres.bundled.image.tag=pg18-trixie \
		--set database.postgres.vectorEnabled=true \
		$(KAGENT_HELM_EXTRA_ARGS)

.PHONY: helm-install
helm-install: ## Build all images then install kagent onto the kind cluster
helm-install: build
helm-install: helm-install-provider

.PHONY: helm-test-install
helm-test-install: ## Dry-run helm install to validate chart rendering (pipe to tee for inspection)
helm-test-install: HELM_ACTION+="--dry-run"
helm-test-install: helm-install-provider

.PHONY: helm-uninstall
helm-uninstall: ## Uninstall kagent and kagent-crds Helm releases from the kind cluster
	helm uninstall kagent --namespace kagent --kube-context kind-$(KIND_CLUSTER_NAME) --wait
	helm uninstall kagent-crds --namespace kagent --kube-context kind-$(KIND_CLUSTER_NAME) --wait

.PHONY: helm-publish
helm-publish: ## Package and push all Helm charts to the OCI registry
helm-publish: helm-version
	helm push ./$(HELM_DIST_FOLDER)/kagent-crds-$(VERSION).tgz $(HELM_REPO)/kagent/helm
	helm push ./$(HELM_DIST_FOLDER)/kagent-$(VERSION).tgz $(HELM_REPO)/kagent/helm
	helm push ./$(HELM_DIST_FOLDER)/helm-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/istio-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/promql-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/observability-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/argo-rollouts-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/cilium-policy-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/cilium-manager-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/cilium-debug-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents
	helm push ./$(HELM_DIST_FOLDER)/kgateway-agent-$(VERSION).tgz $(HELM_REPO)/kagent/agents

##@ Dev

.PHONY: kagent-cli-install
kagent-cli-install: ## Build CLI locally, install kagent, and open the dashboard
kagent-cli-install: use-kind-cluster build-cli-local helm-version helm-install-provider
	KAGENT_HELM_REPO=./helm/ ./go/core/bin/kagent-local dashboard

.PHONY: kagent-cli-port-forward
kagent-cli-port-forward: ## Port-forward the kagent controller API to localhost:8083
kagent-cli-port-forward: use-kind-cluster
	@echo "Port forwarding to kagent CLI..."
	kubectl --context kind-$(KIND_CLUSTER_NAME) port-forward -n kagent service/kagent-controller 8083:8083

.PHONY: kagent-ui-port-forward
kagent-ui-port-forward: ## Open the UI in a browser and port-forward to localhost:8082
kagent-ui-port-forward: use-kind-cluster
	open http://localhost:8082/
	kubectl --context kind-$(KIND_CLUSTER_NAME) port-forward -n kagent service/kagent-ui 8082:8080

.PHONY: kagent-addon-install
kagent-addon-install: ## Install Istio, Grafana, Prometheus, and metrics-server addons on the kind cluster
kagent-addon-install: use-kind-cluster
	istioctl install --context kind-$(KIND_CLUSTER_NAME) --set profile=demo -y
	kubectl apply --context kind-$(KIND_CLUSTER_NAME) -f contrib/addons/grafana.yaml
	kubectl apply --context kind-$(KIND_CLUSTER_NAME) -f contrib/addons/prometheus.yaml
	kubectl apply --context kind-$(KIND_CLUSTER_NAME) -f contrib/addons/metrics-server.yaml
	# wait for pods to be ready
	kubectl wait --context kind-$(KIND_CLUSTER_NAME) --for=condition=Ready pod -l app.kubernetes.io/name=grafana    -n kagent --timeout=60s
	kubectl wait --context kind-$(KIND_CLUSTER_NAME) --for=condition=Ready pod -l app.kubernetes.io/name=prometheus -n kagent --timeout=60s

.PHONY: open-dev-container
open-dev-container: ## Build and start the devcontainer
	@echo "Building and starting dev container..."
	devcontainer up --workspace-folder .

.PHONY: otel-local
otel-local: ## Start a local Jaeger container for OpenTelemetry tracing (UI at localhost:16686)
	$(CONTAINER_RUNTIME) rm -f jaeger-desktop || true
	$(CONTAINER_RUNTIME) run -d --name jaeger-desktop --restart=always -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/jaeger:2.7.0
	@echo "Jaeger UI available at http://localhost:16686/"

.PHONY: kind-debug
kind-debug: ## Install btop/htop inside the kind control-plane container and launch btop
	@echo "Debugging the kind cluster..."
	@echo "Enter the kind cluster control plane container..."
	$(CONTAINER_RUNTIME) exec -it $(KIND_CLUSTER_NAME)-control-plane bash -c 'apt-get update && apt-get install -y btop htop'
	$(CONTAINER_RUNTIME) exec -it $(KIND_CLUSTER_NAME)-control-plane bash -c 'btop --utf-force'


##@ Security

.PHONY: audit
audit: ## Run CVE audits for Go, UI, and Python dependencies
	echo "Running CVE audit GO"
	make -C go govulncheck
	echo "Running CVE audit UI"
	make -C ui audit
	echo "Running CVE audit PYTHON"
	make -C python audit

.PHONY: report/image-cve
report/image-cve: ## Scan built images with grype and write CVE CSV reports to reports/
report/image-cve: audit build
	echo "Running CVE scan :: CVE -> CSV ... reports/$(SEMVER)/"
	grype $(CONTAINER_RUNTIME):$(CONTROLLER_IMG) -o template -t reports/cve-report.tmpl --file reports/$(SEMVER)/controller-cve.csv
	grype $(CONTAINER_RUNTIME):$(APP_IMG)        -o template -t reports/cve-report.tmpl --file reports/$(SEMVER)/app-cve.csv
	grype $(CONTAINER_RUNTIME):$(UI_IMG)         -o template -t reports/cve-report.tmpl --file reports/$(SEMVER)/ui-cve.csv
	grype $(CONTAINER_RUNTIME):$(SKILLS_INIT_IMG) -o template -t reports/cve-report.tmpl --file reports/$(SEMVER)/skills-init-cve.csv


##@ Cleanup

.PHONY: clean
clean: ## Remove build artifacts, prune images, and delete the buildx builder
clean: prune-kind-cluster
clean: prune-images
ifneq ($(CONTAINER_RUNTIME),podman)
	$(CONTAINER_RUNTIME) buildx rm $(BUILDX_BUILDER_NAME)  -f || true
endif
	rm -rf ./go/core/bin

.PHONY: prune-kind-cluster
prune-kind-cluster: ## Remove dangling container images from the kind node
	echo "Pruning dangling container images from kind  ..."
	$(CONTAINER_RUNTIME) exec $(KIND_CLUSTER_NAME)-control-plane crictl images --no-trunc --quiet | \
	grep '<none>' | awk '{print $$3}' | xargs -r -n1 $(CONTAINER_RUNTIME) exec $(KIND_CLUSTER_NAME)-control-plane crictl rmi || :

.PHONY: prune-images
prune-images: ## Remove old kagent images and dangling images from the local daemon
	echo "Pruning dangling container images ..."
	$(CONTAINER_RUNTIME) images --format '{{.Repository}}:{{.Tag}} {{.ID}}' | \
	grep -v ":$(VERSION) " | grep kagent | grep -v '<none>' | awk '{print $$2}' | xargs -r $(CONTAINER_RUNTIME) rmi || :
	$(CONTAINER_RUNTIME) images --filter dangling=true -q | xargs -r $(CONTAINER_RUNTIME) rmi || :


