forked from kubeflow/kale
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
261 lines (212 loc) · 11.1 KB
/
Makefile
File metadata and controls
261 lines (212 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
.PHONY: help dev install \
test test-backend test-backend-unit test-labextension test-e2e test-e2e-install \
lint lint-backend lint-labextension format-labextension format-backend \
build \
kfp-build kfp-serve kfp-compile kfp-run \
clean clean-venv lock lock-upgrade check-uv \
jupyter jupyter-kfp watch-labextension \
docker-build docker-run \
release verify check-versions
UV := uv
# jlpm is a yarn wrapper provided by JupyterLab - use it for extension development
JLPM := $(UV) run jlpm
# Colors for output
BLUE := \033[0;34m
GREEN := \033[0;32m
YELLOW := \033[0;33m
NC := \033[0m
# Default target
.DEFAULT_GOAL := help
##@ General
help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\n$(BLUE)Kale Development Commands$(NC)\n\nUsage:\n make $(GREEN)<target>$(NC)\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " $(GREEN)%-20s$(NC) %s\n", $$1, $$2 } /^##@/ { printf "\n$(YELLOW)%s$(NC)\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
check-uv: ## Verify uv is installed
@command -v $(UV) >/dev/null 2>&1 || { \
printf "$(YELLOW)uv not found. Installing...\n$(NC)"; \
curl -LsSf https://astral.sh/uv/install.sh | sh; \
}
##@ Installation
dev: check-uv ## Set up development environment
@printf "$(BLUE)Setting up Kale development environment...\n$(NC)"
@# Step 1: Install package to get jlpm available
@# Skip the hatch jupyter-builder hook — we build the labextension explicitly below
SKIP_JUPYTER_BUILDER=1 $(UV) sync --all-extras
@# Step 2: Build labextension (requires jlpm from step 1)
cd labextension && $(JLPM) install
@# Clean tsbuildinfo cache before build (fixes incremental build issues after make clean)
cd labextension && $(JLPM) clean:lib
cd labextension && $(JLPM) build
@# Step 3: Link extension for development
$(UV) run jupyter labextension develop --overwrite .
@# Step 4: Set up pre-commit hooks (Python + TypeScript/JavaScript linting)
@$(UV) run pre-commit install 2>/dev/null || { \
printf "$(YELLOW)Note: pre-commit hooks not installed (core.hooksPath is set globally).\n$(NC)"; \
printf "$(YELLOW)To enable pre-commit hooks, run: git config --unset core.hooksPath\n$(NC)"; \
}
@printf "$(GREEN)Setup complete! Run 'make jupyter' to start JupyterLab\n$(NC)"
install: dev ## Alias for dev
##@ Testing
test: test-backend test-labextension ## Run all tests
test-backend: ## Run backend tests
@printf "$(BLUE)Running backend tests...\n$(NC)"
$(UV) run pytest kale/tests -vv
test-backend-unit: ## Run backend unit tests only
@printf "$(BLUE)Running backend unit tests...\n$(NC)"
$(UV) run pytest kale/tests/unit_tests -vv
test-labextension: ## Run labextension tests
@printf "$(BLUE)Running labextension tests...\n$(NC)"
cd labextension && $(JLPM) test
test-e2e-install: ## Install Playwright browsers (run once)
@printf "$(BLUE)Installing Playwright dependencies...\n$(NC)"
cd labextension/ui-tests && $(JLPM) install
cd labextension/ui-tests && $(JLPM) playwright install
@printf "$(GREEN)Playwright installed!\n$(NC)"
test-e2e: ## Run Playwright e2e tests (experimental)
@printf "$(BLUE)Running Playwright e2e tests...\n$(NC)"
@printf "$(YELLOW)Note: e2e tests may timeout due to extension kernel startup\n$(NC)"
cd labextension/ui-tests && $(JLPM) playwright test
##@ Linting
lint: lint-backend lint-labextension ## Run all linters
lint-backend: ## Lint backend code (ruff)
@printf "$(BLUE)Linting backend...\n$(NC)"
$(UV) run ruff check kale
$(UV) run ruff format --check kale
lint-labextension: ## Lint labextension code (eslint + prettier)
@printf "$(BLUE)Linting labextension...\n$(NC)"
cd labextension && $(JLPM) lint:check
format-labextension: ## Format labextension code
cd labextension && $(JLPM) prettier && $(JLPM) eslint
format-backend: ## Format backend code (ruff)
$(UV) run ruff check --fix kale
$(UV) run ruff format kale
##@ Building
build: ## Build wheel
@printf "$(BLUE)Building wheel...\n$(NC)"
$(UV) build
@printf "$(GREEN)Wheel built: dist/\n$(NC)"
##@ KFP Development (replaces devpi workflow)
KFP_WHEEL_DIR ?= $(CURDIR)/.kfp-wheels
KFP_PORT ?= 8765
# Host address for KFP to reach the wheel server (Linux users: override with your host IP)
KFP_HOST_ADDR ?= host.docker.internal
kfp-build: ## Build wheel for KFP cluster testing (fixed version for reproducibility)
@printf "$(BLUE)Building wheel...\n$(NC)"
rm -f dist/kubeflow_kale-*.whl
$(UV) build
@# Create PEP 503 compliant simple index structure
rm -rf $(KFP_WHEEL_DIR)
mkdir -p $(KFP_WHEEL_DIR)/kubeflow-kale
cp dist/kubeflow_kale-*.whl $(KFP_WHEEL_DIR)/kubeflow-kale/
@# Generate index files for pip simple API
@echo '<!DOCTYPE html><html><body><a href="kubeflow-kale/">kubeflow-kale</a></body></html>' > $(KFP_WHEEL_DIR)/index.html
@cd $(KFP_WHEEL_DIR)/kubeflow-kale && for f in *.whl; do echo "<a href=\"$$f\">$$f</a><br>"; done > index.html
@printf "$(GREEN)Wheel ready at $(KFP_WHEEL_DIR)\n$(NC)"
kfp-serve: kfp-build ## Serve wheel via HTTP for Kind/Docker clusters
@printf "$(BLUE)Starting wheel server on port $(KFP_PORT)...\n$(NC)"
cd $(KFP_WHEEL_DIR) && python3 -m http.server $(KFP_PORT)
kfp-compile: ## Compile notebook with local wheel (usage: make kfp-compile NB=path/to/notebook.ipynb)
@test -n "$(NB)" || { printf "$(YELLOW)Usage: make kfp-compile NB=path/to/notebook.ipynb\n$(NC)"; exit 1; }
@printf "$(YELLOW)Make sure 'make kfp-serve' is running in another terminal\n$(NC)"
KALE_PIP_INDEX_URLS="http://$(KFP_HOST_ADDR):$(KFP_PORT)" \
KALE_PIP_TRUSTED_HOSTS="$(KFP_HOST_ADDR)" \
$(UV) run kale --nb $(NB)
kfp-run: ## Compile and run on KFP with local wheel (usage: make kfp-run NB=... KFP_HOST=...)
@test -n "$(NB)" || { printf "$(YELLOW)Usage: make kfp-run NB=path/to/notebook.ipynb KFP_HOST=http://localhost:8080\n$(NC)"; exit 1; }
@test -n "$(KFP_HOST)" || { printf "$(YELLOW)Error: KFP_HOST not set\n$(NC)"; exit 1; }
@printf "$(YELLOW)Make sure 'make kfp-serve' is running in another terminal\n$(NC)"
KALE_PIP_INDEX_URLS="http://$(KFP_HOST_ADDR):$(KFP_PORT)" \
KALE_PIP_TRUSTED_HOSTS="$(KFP_HOST_ADDR)" \
$(UV) run kale --nb $(NB) --kfp_host $(KFP_HOST) --run_pipeline
##@ Cleanup
clean: ## Clean all build artifacts
@printf "$(BLUE)Cleaning...\n$(NC)"
rm -rf .venv
rm -rf dist *.egg-info
rm -rf labextension/lib labextension/node_modules
rm -rf jupyterlab_kubeflow_kale/labextension
rm -rf .kfp-wheels .kale
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true
@printf "$(GREEN)Clean complete\n$(NC)"
clean-venv: ## Remove virtual environment only
rm -rf .venv
##@ Lockfile Management
lock: check-uv ## Update uv.lock
$(UV) lock
lock-upgrade: check-uv ## Upgrade all dependencies and update lock
$(UV) lock --upgrade
##@ Development Helpers
jupyter: ## Start JupyterLab
$(UV) run jupyter lab
jupyter-kfp: ## Start JupyterLab with KFP dev environment (run kfp-serve first!)
@printf "$(YELLOW)Make sure 'make kfp-serve' is running in another terminal\n$(NC)"
KALE_PIP_INDEX_URLS="http://$(KFP_HOST_ADDR):$(KFP_PORT)" \
KALE_PIP_TRUSTED_HOSTS="$(KFP_HOST_ADDR)" \
$(UV) run jupyter lab
watch-labextension: ## Watch labextension for changes (run in separate terminal)
cd labextension && $(JLPM) watch
##@ Release
check-versions: ## Verify backend and labextension versions match
@PY_VERSION=$$($(UV) run python -c "import re; m = re.search(r'__version__\s*=\s*\"([^\"]+)\"', open('kale/__init__.py').read()); print(m.group(1))"); \
NPM_VERSION=$$(node -p "require('./labextension/package.json').version"); \
PY_AS_SEMVER=$$(echo "$$PY_VERSION" | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+)(a)([0-9]+)/\1-alpha.\3/; s/([0-9]+\.[0-9]+\.[0-9]+)(b)([0-9]+)/\1-beta.\3/; s/([0-9]+\.[0-9]+\.[0-9]+)(rc)([0-9]+)/\1-rc.\3/'); \
if [ "$$PY_AS_SEMVER" != "$$NPM_VERSION" ]; then \
printf "$(YELLOW)Version mismatch!\n$(NC)"; \
printf " Python (PEP 440): $$PY_VERSION\n"; \
printf " Python (as semver): $$PY_AS_SEMVER\n"; \
printf " npm (semver): $$NPM_VERSION\n"; \
exit 1; \
fi; \
printf "$(GREEN)Versions match: $$PY_VERSION (Python) == $$NPM_VERSION (npm)\n$(NC)"
verify: lint test check-versions ## Run all checks (lint + test + version check)
release: ## Set release version (usage: make release VERSION=X.Y.Z)
@test -n "$(VERSION)" || { printf "$(YELLOW)Usage: make release VERSION=X.Y.Za1\n$(NC)"; exit 1; }
@# Validate PEP 440 version format
@echo "$(VERSION)" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(a[0-9]+|b[0-9]+|rc[0-9]+)?$$' || { \
printf "$(YELLOW)Invalid version format: $(VERSION)\n$(NC)"; \
printf "$(YELLOW)Expected: X.Y.Z, X.Y.Za1, X.Y.Zb1, or X.Y.Zrc1\n$(NC)"; \
exit 1; \
}
@# Update version
python3 -c "import re; p='kale/__init__.py'; t=open(p).read(); open(p,'w').write(re.sub(r'__version__ = \".*\"', '__version__ = \"$(VERSION)\"', t))"
@# Convert PEP 440 to semver for package.json
@NPM_VERSION=$$(echo "$(VERSION)" | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+)(a)([0-9]+)/\1-alpha.\3/; s/([0-9]+\.[0-9]+\.[0-9]+)(b)([0-9]+)/\1-beta.\3/; s/([0-9]+\.[0-9]+\.[0-9]+)(rc)([0-9]+)/\1-rc.\3/'); \
cd labextension && npm version "$$NPM_VERSION" --no-git-tag-version --allow-same-version; \
printf "$(GREEN)Version set to $(VERSION) (npm: $$NPM_VERSION)\n$(NC)"
@# Generate changelog if git-cliff is available
@command -v git-cliff >/dev/null 2>&1 && { \
MAJOR=$$(echo "$(VERSION)" | cut -d. -f1); \
MINOR=$$(echo "$(VERSION)" | cut -d. -f2); \
git-cliff --output CHANGELOG/CHANGELOG-$$MAJOR.$$MINOR.md; \
printf "$(GREEN)Changelog generated: CHANGELOG/CHANGELOG-$$MAJOR.$$MINOR.md\n$(NC)"; \
} || printf "$(YELLOW)git-cliff not found, skipping changelog generation\n$(NC)"
##@ Docker
DOCKER_IMAGE ?= kubeflow-kale
DOCKER_TAG ?= dev
KFP_HOST ?= http://host.docker.internal:8080
docker-build: build ## Build Docker image with Kale pre-installed
@printf "$(BLUE)Building Docker image $(DOCKER_IMAGE):$(DOCKER_TAG)...\n$(NC)"
docker build -f docker/Dockerfile -t $(DOCKER_IMAGE):$(DOCKER_TAG) .
@printf "$(GREEN)Image built: $(DOCKER_IMAGE):$(DOCKER_TAG)\n$(NC)"
docker-run: ## Run Kale in Docker (JupyterLab on http://localhost:8888)
@printf "$(YELLOW)Requires: kubectl port-forward -n kubeflow svc/ml-pipeline-ui 8080:8080\n$(NC)"
@printf "$(YELLOW)Requires: make kfp-serve (in another terminal)\n$(NC)"
@printf "$(BLUE)Starting $(DOCKER_IMAGE):$(DOCKER_TAG) on http://localhost:8888\n$(NC)"
docker run --rm -p 8888:8888 \
--add-host=host.docker.internal:host-gateway \
-e KF_PIPELINES_ENDPOINT=$(KFP_HOST) \
-e KF_PIPELINES_UI_ENDPOINT=http://localhost:8080 \
-e KALE_PIP_INDEX_URLS=http://$(KFP_HOST_ADDR):$(KFP_PORT) \
-e KALE_PIP_TRUSTED_HOSTS=$(KFP_HOST_ADDR) \
$(DOCKER_IMAGE):$(DOCKER_TAG)
podman-run: ## Run Kale with Podman (JupyterLab on http://localhost:8888)
@printf "$(YELLOW)Requires: kubectl port-forward -n kubeflow svc/ml-pipeline-ui 8080:8080\n$(NC)"
@printf "$(YELLOW)Requires: make kfp-serve (in another terminal)\n$(NC)"
@printf "$(BLUE)Starting $(DOCKER_IMAGE):$(DOCKER_TAG) on http://localhost:8888\n$(NC)"
podman run --rm \
--network=host \
-e KF_PIPELINES_ENDPOINT=$(KFP_HOST) \
-e KF_PIPELINES_UI_ENDPOINT=http://localhost:8080 \
-e KALE_PIP_INDEX_URLS=http://$(KFP_HOST_ADDR):$(KFP_PORT) \
-e KALE_PIP_TRUSTED_HOSTS=$(KFP_HOST_ADDR) \
$(DOCKER_IMAGE):$(DOCKER_TAG)