Skip to content

Commit 9a55e4f

Browse files
zdtswshmuelk
andauthored
refactor: Makefile, update docs (#463)
* refactor: Makefile, update docs - split Makefile 1. tools: include install tools, check tools, download dependency(gcc etc) and tokenizer. these will be download into "bin" folder than global path 2. cluster: include k8s and ocp 3. kind - rename "openshift-base" to "kubernetes-base" to be clear for purpose - uplift Go lint version to 2.1.6 to align with the same one set in Github Action - rename make targets for better visibility, deprcating old ones - add more print in "make env" Signed-off-by: Wen Zhou <wenzhou@redhat.com> * update: code review - move image tags from Makefile.tools.mk back to Makefile - update docuement to reflact how image and tag are created - do not export image tag env variables IMG_TAG - fix patch-deployments.yaml after EPP_TAG is not used but should only use EPP_IMAGE - fix kubernetes-dev-env.sh for EPP_IMAGE - remove flag on golangci_lint fmt Signed-off-by: Wen Zhou <wenzhou@redhat.com> * code review: - revert back to 1.3.0 - remove comments - set default as default namespace Signed-off-by: Wen Zhou <wenzhou@redhat.com> * Update Makefile Co-authored-by: Shmuel Kallner <kallner@il.ibm.com> Signed-off-by: Wen Zhou <wenzhou@redhat.com> * docs: fix broken link in the docs Signed-off-by: Wen Zhou <wenzhou@redhat.com> --------- Signed-off-by: Wen Zhou <wenzhou@redhat.com> Co-authored-by: Shmuel Kallner <kallner@il.ibm.com>
1 parent bbcf38b commit 9a55e4f

19 files changed

+466
-398
lines changed

DEVELOPMENT.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,18 @@ curl -s -w '\n' http://localhost:8080/v1/completions -H 'Content-Type: applicati
249249
250250
#### Environment Configurateion
251251

252-
**1. Setting the EPP image and tag:**
252+
**1. Setting the EPP image registry and tag:**
253253

254-
You can optionally set a custom EPP image (otherwise, the default will be used):
254+
You can optionally set a custom image registry and tag (otherwise, defaults will be used):
255255

256256
```bash
257+
export IMAGE_REGISTRY="<YOUR_REGISTRY>"
257258
export EPP_TAG="<YOUR_TAG>"
258-
export EPP_IMAGE="<YOUR_REGISTRY>/<YOUR_IMAGE>"
259259
```
260260

261+
> [!NOTE]
262+
> The full image reference will be constructed as `${EPP_IMAGE}`, where `EPP_IMAGE` defaults to `${IMAGE_REGISTRY}/llm-d-inference-scheduler:{EPP_TAG}`. For example, with `IMAGE_REGISTRY=quay.io/<my-id>` and `EPP_TAG=v1.0.0`, the final image will be `quay.io/<my-id>/llm-d-inference-scheduler:v1.0.0`.
263+
261264
**2. Setting the vLLM replicas:**
262265

263266
You can optionally set the vllm replicas:

Makefile

Lines changed: 100 additions & 386 deletions
Large diffs are not rendered by default.

Makefile.cluster.mk

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
##@ Cluster Development Environments
2+
3+
.PHONY: env-dev-kubernetes
4+
env-dev-kubernetes: check-kubectl check-kustomize check-envsubst ## Deploy full dev environment (vLLM + Gateway + EPP) to K8s/OpenShift cluster
5+
IMAGE_REGISTRY=$(IMAGE_REGISTRY) ./scripts/kubernetes-dev-env.sh 2>&1
6+
7+
# Kubernetes Development Environment - Teardown
8+
.PHONY: clean-env-dev-kubernetes
9+
clean-env-dev-kubernetes: check-kubectl check-kustomize check-envsubst ## Clean up full dev environment from K8s/OpenShift cluster
10+
@CLEAN=true ./scripts/kubernetes-dev-env.sh 2>&1
11+
@echo "INFO: Finished cleanup of development environment for namespace $(NAMESPACE)"
12+
13+
14+
##@ RBAC Targets
15+
16+
.PHONY: install-rbac
17+
install-rbac: check-kubectl check-kustomize check-envsubst ## Apply RBAC configuration to cluster
18+
@echo "Applying RBAC configuration from deploy/rbac..."
19+
$(KUSTOMIZE) build deploy/environments/kubernetes-base/rbac | envsubst '$$PROJECT_NAME $$NAMESPACE $$IMAGE_TAG_BASE $$VERSION' | kubectl apply -f -
20+
21+
.PHONY: uninstall-rbac
22+
uninstall-rbac: check-kubectl check-kustomize check-envsubst ## Remove RBAC configuration from cluster
23+
@echo "Removing RBAC configuration from deploy/rbac..."
24+
$(KUSTOMIZE) build deploy/environments/kubernetes-base/rbac | envsubst '$$PROJECT_NAME $$NAMESPACE $$IMAGE_TAG_BASE $$VERSION' | kubectl delete -f - || true
25+
26+
##@ Kubernetes Targets
27+
28+
.PHONY: install-k8s
29+
install-k8s: check-kubectl check-kustomize check-envsubst ## Deploy resources to Kubernetes
30+
@echo "Creating namespace (if needed) and setting context to $(NAMESPACE)..."
31+
kubectl create namespace $(NAMESPACE) 2>/dev/null || true
32+
kubectl config set-context --current --namespace=$(NAMESPACE)
33+
@echo "Deploying resources from deploy/ ..."
34+
# Build the kustomization from deploy, substitute variables, and apply the YAML
35+
$(KUSTOMIZE) build deploy/environments/kubernetes-base | envsubst | kubectl apply -f -
36+
@echo "Waiting for pod to become ready..."
37+
sleep 5
38+
@POD=$$(kubectl get pod -l app=$(PROJECT_NAME)-statefulset -o jsonpath='{.items[0].metadata.name}'); \
39+
echo "Kubernetes installation complete."; \
40+
echo "To use the app, run:"; \
41+
echo "alias $(PROJECT_NAME)='kubectl exec -n $(NAMESPACE) -it $$POD -- /app/$(PROJECT_NAME)'"
42+
43+
.PHONY: uninstall-k8s
44+
uninstall-k8s: check-kubectl check-kustomize check-envsubst ## Remove resources from Kubernetes
45+
@echo "Removing resources from Kubernetes..."
46+
$(KUSTOMIZE) build deploy/environments/kubernetes-base | envsubst | kubectl delete --force -f - || true
47+
POD=$$(kubectl get pod -l app=$(PROJECT_NAME)-statefulset -o jsonpath='{.items[0].metadata.name}'); \
48+
echo "Deleting pod: $$POD"; \
49+
kubectl delete pod "$$POD" --force --grace-period=0 || true; \
50+
echo "Kubernetes uninstallation complete. Remove alias if set: unalias $(PROJECT_NAME)"
51+
52+
##@ OpenShift Targets
53+
54+
.PHONY: install-openshift
55+
install-openshift: check-kubectl check-kustomize check-envsubst ## Deploy resources to OpenShift
56+
@echo $$PROJECT_NAME $$NAMESPACE $$IMAGE_TAG_BASE $$VERSION
57+
@echo "Creating namespace $(NAMESPACE)..."
58+
kubectl create namespace $(NAMESPACE) 2>/dev/null || true
59+
@echo "Deploying common resources from deploy/ ..."
60+
# Build and substitute the base manifests from deploy, then apply them
61+
$(KUSTOMIZE) build deploy/environments/kubernetes-base | envsubst '$$PROJECT_NAME $$NAMESPACE $$IMAGE_TAG_BASE $$VERSION' | kubectl apply -n $(NAMESPACE) -f -
62+
@echo "Waiting for pod to become ready..."
63+
sleep 5
64+
@POD=$$(kubectl get pod -l app=$(PROJECT_NAME)-statefulset -n $(NAMESPACE) -o jsonpath='{.items[0].metadata.name}'); \
65+
echo "OpenShift installation complete."; \
66+
echo "To use the app, run:"; \
67+
echo "alias $(PROJECT_NAME)='kubectl exec -n $(NAMESPACE) -it $$POD -- /app/$(PROJECT_NAME)'"
68+
69+
.PHONY: uninstall-openshift
70+
uninstall-openshift: check-kubectl check-kustomize check-envsubst ## Remove resources from OpenShift
71+
@echo "Removing resources from OpenShift..."
72+
$(KUSTOMIZE) build deploy/environments/kubernetes-base | envsubst '$$PROJECT_NAME $$NAMESPACE $$IMAGE_TAG_BASE $$VERSION' | kubectl delete --force -f - || true
73+
# @if kubectl api-resources --api-group=route.openshift.io | grep -q Route; then \
74+
# envsubst '$$PROJECT_NAME $$NAMESPACE $$IMAGE_TAG_BASE $$VERSION' < deploy/openshift/route.yaml | kubectl delete --force -f - || true; \
75+
# fi
76+
@POD=$$(kubectl get pod -l app=$(PROJECT_NAME)-statefulset -n $(NAMESPACE) -o jsonpath='{.items[0].metadata.name}'); \
77+
echo "Deleting pod: $$POD"; \
78+
kubectl delete pod "$$POD" --force --grace-period=0 || true; \
79+
echo "OpenShift uninstallation complete. Remove alias if set: unalias $(PROJECT_NAME)"

Makefile.kind.mk

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
##@ Kind Development Environments
2+
3+
KIND_CLUSTER_NAME ?= llm-d-inference-scheduler-dev
4+
KIND_GATEWAY_HOST_PORT ?= 30080
5+
6+
.PHONY: env-dev-kind
7+
env-dev-kind: image-build ## Run under kind ($(KIND_CLUSTER_NAME))
8+
@if [ "$$PD_ENABLED" = "true" ] && [ "$$KV_CACHE_ENABLED" = "true" ]; then \
9+
echo "Error: Both PD_ENABLED and KV_CACHE_ENABLED are true. Skipping env-dev-kind."; \
10+
exit 1; \
11+
else \
12+
CLUSTER_NAME=$(KIND_CLUSTER_NAME) \
13+
GATEWAY_HOST_PORT=$(KIND_GATEWAY_HOST_PORT) \
14+
IMAGE_REGISTRY=$(IMAGE_REGISTRY) \
15+
EPP_IMAGE=$(EPP_IMAGE) \
16+
VLLM_SIMULATOR_IMAGE=${VLLM_SIMULATOR_IMAGE} \
17+
SIDECAR_IMAGE=$(SIDECAR_IMAGE) \
18+
./scripts/kind-dev-env.sh; \
19+
fi
20+
21+
.PHONY: clean-env-dev-kind
22+
clean-env-dev-kind: ## Cleanup kind setup (delete cluster $(KIND_CLUSTER_NAME))
23+
@echo "INFO: cleaning up kind cluster $(KIND_CLUSTER_NAME)"
24+
kind delete cluster --name $(KIND_CLUSTER_NAME)
25+
26+
##@ Alias checking
27+
.PHONY: check-alias
28+
check-alias: check-container-tool ## Check if the container alias works correctly
29+
@echo "Checking alias functionality for container '$(PROJECT_NAME)-container'..."
30+
@if ! $(CONTAINER_RUNTIME) exec $(PROJECT_NAME)-container /app/$(PROJECT_NAME) --help >/dev/null 2>&1; then \
31+
echo "WARNING: The container '$(PROJECT_NAME)-container' is running, but the alias might not work."; \
32+
echo "Try: $(CONTAINER_RUNTIME) exec -it $(PROJECT_NAME)-container /app/$(PROJECT_NAME)"; \
33+
else \
34+
echo "Alias is likely to work: alias $(PROJECT_NAME)='$(CONTAINER_RUNTIME) exec -it $(PROJECT_NAME)-container /app/$(PROJECT_NAME)'"; \
35+
fi

Makefile.tools.mk

Lines changed: 241 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,253 @@
1-
LOCALBIN ?= $(shell pwd)/bin
1+
## Local directories are defined in main Makefile
22
$(LOCALBIN):
33
[ -d $@ ] || mkdir -p $@
44

5+
$(LOCALLIB):
6+
[ -d $@ ] || mkdir -p $@
7+
58
## Tool binary names.
9+
GINKGO = $(LOCALBIN)/ginkgo
10+
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
11+
KUSTOMIZE = $(LOCALBIN)/kustomize
612
TYPOS = $(LOCALBIN)/typos
13+
## Dependencies
14+
TOKENIZER_LIB = $(LOCALLIB)/libtokenizers.a
715

8-
## Tool versions.
16+
## Tool fixed versions.
17+
GINKGO_VERSION ?= v2.27.2
18+
GOLANGCI_LINT_VERSION ?= v2.1.6
19+
KUSTOMIZE_VERSION ?= v5.5.0
920
TYPOS_VERSION ?= v1.34.0
1021

11-
.PHONY: typos
12-
typos: $(TYPOS)
22+
## Python Configuration
23+
PYTHON_VERSION ?= 3.12
24+
# Extract RELEASE_VERSION from Dockerfile
25+
TOKENIZER_VERSION := $(shell grep '^ARG RELEASE_VERSION=' Dockerfile.epp | cut -d'=' -f2)
26+
27+
# Python executable for creating venv
28+
PYTHON_EXE := $(shell command -v python$(PYTHON_VERSION) || command -v python3)
29+
VENV_DIR := $(shell pwd)/build/venv
30+
VENV_BIN := $(VENV_DIR)/bin
31+
32+
## go-install-tool will 'go install' any package with custom target and version.
33+
define go-install-tool
34+
@[ -f "$(1)-$(3)" ] || { \
35+
set -e; \
36+
package=$(2)@$(3) ;\
37+
echo "Downloading $${package}" ;\
38+
rm -f $(1) || true ;\
39+
GOBIN=$(LOCALBIN) go install $${package} ;\
40+
mv $(1) $(1)-$(3) ;\
41+
} ;\
42+
ln -sf $(notdir $(1))-$(3) $(1)
43+
endef
44+
45+
46+
##@ Tools
47+
48+
.PHONY: install-tools
49+
install-tools: install-ginkgo install-golangci-lint install-kustomize install-typos install-dependencies download-tokenizer ## Install all development tools and dependencies
50+
@echo "All development tools and dependencies are installed."
51+
52+
.PHONY: install-ginkgo
53+
install-ginkgo: $(GINKGO)
54+
$(GINKGO): | $(LOCALBIN)
55+
$(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo,$(GINKGO_VERSION))
56+
57+
.PHONY: install-golangci-lint
58+
install-golangci-lint: $(GOLANGCI_LINT)
59+
$(GOLANGCI_LINT): | $(LOCALBIN)
60+
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
61+
62+
.PHONY: install-kustomize
63+
install-kustomize: $(KUSTOMIZE)
64+
$(KUSTOMIZE): | $(LOCALBIN)
65+
$(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
66+
67+
.PHONY: install-typos
68+
install-typos: $(TYPOS)
1369
$(TYPOS): | $(LOCALBIN)
1470
@echo "Downloading typos $(TYPOS_VERSION)..."
1571
curl -L https://github.com/crate-ci/typos/releases/download/$(TYPOS_VERSION)/typos-$(TYPOS_VERSION)-$(TYPOS_ARCH).tar.gz | tar -xz -C $(LOCALBIN) $(TAR_OPTS)
1672
chmod +x $(TYPOS)
73+
@echo "typos installed successfully."
74+
75+
##@ Dependencies
76+
77+
.PHONY: check-dependencies
78+
check-dependencies: ## Check if development dependencies are installed
79+
@if [ "$(TARGETOS)" = "linux" ]; then \
80+
if [ -x "$$(command -v apt)" ]; then \
81+
if ! dpkg -s libzmq3-dev >/dev/null 2>&1 || ! dpkg -s g++ >/dev/null 2>&1 || ! dpkg -s python$(PYTHON_VERSION)-dev >/dev/null 2>&1; then \
82+
echo "ERROR: Missing dependencies. Please run 'sudo make install-dependencies'"; \
83+
exit 1; \
84+
fi; \
85+
elif [ -x "$$(command -v dnf)" ]; then \
86+
if ! rpm -q zeromq-devel >/dev/null 2>&1 || ! rpm -q gcc-c++ >/dev/null 2>&1 || ! rpm -q python$(PYTHON_VERSION)-devel >/dev/null 2>&1; then \
87+
echo "ERROR: Missing dependencies. Please run 'sudo make install-dependencies'"; \
88+
exit 1; \
89+
fi; \
90+
else \
91+
echo "WARNING: Unsupported Linux package manager. Cannot verify dependencies."; \
92+
fi; \
93+
elif [ "$(TARGETOS)" = "darwin" ]; then \
94+
if [ -x "$$(command -v brew)" ]; then \
95+
if ! brew list zeromq pkg-config >/dev/null 2>&1; then \
96+
echo "ERROR: Missing dependencies. Please run 'make install-dependencies'"; \
97+
exit 1; \
98+
fi; \
99+
else \
100+
echo "ERROR: Homebrew is not installed and is required. Install it from https://brew.sh/"; \
101+
exit 1; \
102+
fi; \
103+
fi
104+
@echo "✅ All dependencies are installed."
105+
106+
.PHONY: install-dependencies
107+
install-dependencies: ## Install development dependencies based on OS/ARCH
108+
@echo "Checking and installing development dependencies..."
109+
@if [ "$(TARGETOS)" = "linux" ]; then \
110+
if [ -x "$$(command -v apt)" ]; then \
111+
if ! dpkg -s libzmq3-dev >/dev/null 2>&1 || ! dpkg -s g++ >/dev/null 2>&1 || ! dpkg -s python$(PYTHON_VERSION)-dev >/dev/null 2>&1; then \
112+
echo "Installing dependencies with apt..."; \
113+
apt-get update && apt-get install -y libzmq3-dev g++ python$(PYTHON_VERSION)-dev; \
114+
else \
115+
echo "✅ ZMQ, g++, and Python dev headers are already installed."; \
116+
fi; \
117+
elif [ -x "$$(command -v dnf)" ]; then \
118+
if ! rpm -q zeromq-devel >/dev/null 2>&1 || ! rpm -q gcc-c++ >/dev/null 2>&1 || ! rpm -q python$(PYTHON_VERSION)-devel >/dev/null 2>&1; then \
119+
echo "Installing dependencies with dnf..."; \
120+
dnf install -y zeromq-devel gcc-c++ python$(PYTHON_VERSION)-devel; \
121+
else \
122+
echo "✅ ZMQ, gcc-c++, and Python dev headers are already installed."; \
123+
fi; \
124+
else \
125+
echo "ERROR: Unsupported Linux package manager. Install libzmq, g++/gcc-c++, and python-devel manually."; \
126+
exit 1; \
127+
fi; \
128+
elif [ "$(TARGETOS)" = "darwin" ]; then \
129+
if [ -x "$$(command -v brew)" ]; then \
130+
if ! brew list zeromq pkg-config >/dev/null 2>&1; then \
131+
echo "Installing dependencies with brew..."; \
132+
brew install zeromq pkg-config; \
133+
else \
134+
echo "✅ ZeroMQ and pkgconf are already installed."; \
135+
fi; \
136+
else \
137+
echo "ERROR: Homebrew is not installed and is required to install zeromq. Install it from https://brew.sh/"; \
138+
exit 1; \
139+
fi; \
140+
else \
141+
echo "ERROR: Unsupported OS: $(TARGETOS). Install development dependencies manually."; \
142+
exit 1; \
143+
fi
144+
145+
.PHONY: download-tokenizer
146+
download-tokenizer: $(TOKENIZER_LIB)
147+
$(TOKENIZER_LIB): | $(LOCALLIB)
148+
## Download the HuggingFace tokenizer bindings.
149+
@echo "Downloading HuggingFace tokenizer bindings for version $(TOKENIZER_VERSION)..."
150+
@curl -L https://github.com/daulet/tokenizers/releases/download/$(TOKENIZER_VERSION)/libtokenizers.$(TARGETOS)-$(TOKENIZER_ARCH).tar.gz | tar -xz -C $(LOCALLIB)
151+
@ranlib $(LOCALLIB)/*.a
152+
@echo "Tokenizer bindings downloaded successfully."
153+
154+
155+
.PHONY: install-python-deps
156+
install-python-deps: ## Sets up Python virtual environment and installs dependencies
157+
@printf "\033[33;1m==== Setting up Python virtual environment in $(VENV_DIR) ====\033[0m\n"
158+
@if [ -z "$(PYTHON_EXE)" ]; then \
159+
echo "ERROR: Python 3 not found in PATH."; \
160+
exit 1; \
161+
fi
162+
@if [ ! -f "$(VENV_BIN)/pip" ]; then \
163+
echo "Creating virtual environment..."; \
164+
$(PYTHON_EXE) -m venv $(VENV_DIR) || { \
165+
echo "ERROR: Failed to create virtual environment."; \
166+
echo "Your Python installation may be missing the 'venv' module."; \
167+
exit 1; \
168+
}; \
169+
fi
170+
@echo "Upgrading pip and installing dependencies..."
171+
@$(VENV_BIN)/pip install --upgrade pip --quiet
172+
@KV_CACHE_PKG=$$(go list -m -f '{{.Dir}}' github.com/llm-d/llm-d-kv-cache-manager 2>/dev/null); \
173+
if [ -n "$$KV_CACHE_PKG" ] && [ -f "$$KV_CACHE_PKG/pkg/preprocessing/chat_completions/requirements.txt" ]; then \
174+
echo "Installing Python dependencies from kv-cache-manager..."; \
175+
$(VENV_BIN)/pip install --quiet -r "$$KV_CACHE_PKG/pkg/preprocessing/chat_completions/requirements.txt"; \
176+
else \
177+
echo "WARNING: Could not find kv-cache-manager requirements.txt, installing minimal deps..."; \
178+
$(VENV_BIN)/pip install --quiet 'transformers>=4.53.0' 'jinja2>=2.11'; \
179+
fi
180+
@echo "✅ Python dependencies installed in venv"
181+
182+
.PHONY: check-tools
183+
check-tools: check-go check-ginkgo check-golangci-lint check-kustomize check-envsubst check-container-tool check-kubectl check-buildah check-typos ## Check that all required tools are installed
184+
@echo "All required tools are available."
185+
186+
.PHONY: check-go
187+
check-go:
188+
@command -v go >/dev/null 2>&1 || { \
189+
echo "ERROR: Go is not installed. Install it from https://golang.org/dl/"; exit 1; }
190+
191+
.PHONY: check-ginkgo
192+
check-ginkgo:
193+
@command -v ginkgo >/dev/null 2>&1 || [ -f "$(GINKGO)" ] || { \
194+
echo "ERROR: ginkgo is not installed."; \
195+
echo "Run: make install-ginkgo (or install-tools)"; \
196+
exit 1; }
197+
198+
.PHONY: check-golangci-lint
199+
check-golangci-lint:
200+
@command -v golangci-lint >/dev/null 2>&1 || [ -f "$(GOLANGCI_LINT)" ] || { \
201+
echo "ERROR: golangci-lint is not installed."; \
202+
echo "Run: make install-golangci-lint (or install-tools)"; \
203+
exit 1; }
204+
205+
.PHONY: check-kustomize
206+
check-kustomize:
207+
@command -v kustomize >/dev/null 2>&1 || [ -f "$(KUSTOMIZE)" ] || { \
208+
echo "ERROR: kustomize is not installed."; \
209+
echo "Run: make install-kustomize (or install-tools)"; \
210+
exit 1; }
211+
212+
.PHONY: check-envsubst
213+
check-envsubst:
214+
@command -v envsubst >/dev/null 2>&1 || { \
215+
echo "ERROR: envsubst is not installed. It is part of gettext."; \
216+
echo "Try: sudo apt install gettext OR brew install gettext"; exit 1; }
217+
218+
.PHONY: check-container-tool
219+
check-container-tool:
220+
@if [ -z "$(CONTAINER_RUNTIME)" ]; then \
221+
echo "ERROR: Error: No container tool detected. Please install docker or podman."; \
222+
exit 1; \
223+
else \
224+
echo "Container tool '$(CONTAINER_RUNTIME)' found."; \
225+
fi
226+
227+
.PHONY: check-kubectl
228+
check-kubectl:
229+
@command -v kubectl >/dev/null 2>&1 || { \
230+
echo "ERROR: kubectl is not installed. Install it from https://kubernetes.io/docs/tasks/tools/"; exit 1; }
231+
232+
.PHONY: check-builder
233+
check-builder:
234+
@if [ -z "$(BUILDER)" ]; then \
235+
echo "ERROR: No container builder tool (buildah, docker, or podman) found."; \
236+
exit 1; \
237+
else \
238+
echo "Using builder: $(BUILDER)"; \
239+
fi
240+
241+
.PHONY: check-buildah
242+
check-buildah:
243+
@command -v buildah >/dev/null 2>&1 || { \
244+
echo "WARNING: buildah is not installed (optional - docker/podman can be used instead)."; }
245+
246+
.PHONY: check-typos
247+
check-typos:
248+
@command -v typos >/dev/null 2>&1 || [ -f "$(TYPOS)" ] || { \
249+
echo "ERROR: typos is not installed."; \
250+
echo "Run: make install-typos (or install-tools)"; \
251+
exit 1; }
252+
@echo "Checking for spelling errors with typos..."
253+
@$(TYPOS) --format brief

0 commit comments

Comments
 (0)