forked from IceRhymers/databricks-code-search
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
146 lines (119 loc) · 9.38 KB
/
Copy pathMakefile
File metadata and controls
146 lines (119 loc) · 9.38 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
.DEFAULT_GOAL := help
.PHONY: install run test test-integration lint fmt fmt-check requirements clean help migrate migration set-secrets deploy deploy-prod smoke index destroy diagrams webui-wheel webui-build webui-test webui-verify-dist
# Secret scope/key for `set-secrets`. These MUST match the bundle variables
# `github_token_secret_scope` / `github_token_secret_key` in databricks.yml
# (override both here and via --var together if you change them).
SECRET_SCOPE ?= code-search
SECRET_KEY ?= github_token
# Bundle target that Lakebase-facing commands (migrate) resolve their connection
# from. Override: `make migrate TARGET=prod`.
TARGET ?= dev
# Databricks CLI/SDK auth profile (~/.databrickscfg). Defaults to DEFAULT, same as an
# unset DATABRICKS_CONFIG_PROFILE resolves today. Override: `make deploy PROFILE=myprofile`.
PROFILE ?= DEFAULT
export DATABRICKS_CONFIG_PROFILE := $(PROFILE)
install: ## Install all dependencies (incl. dev group and optional extras, e.g. webui's fastapi)
uv sync --all-groups --all-extras
run: ## Run the MCP server locally (dual-mode: set PGHOST + PG* for local Postgres; binds DATABRICKS_APP_PORT or 8000)
uv run sh -c 'uvicorn app.main:app --host 0.0.0.0 --port $${DATABRICKS_APP_PORT:-8000}'
test: ## Run unit tests (no external deps: unit + pure observability/logging tests)
uv run pytest -m "unit or observability"
test-integration: ## Run integration tests (need Postgres: integration + streamable-HTTP e2e)
uv run pytest -m "integration or e2e"
lint: ## Run ruff check + format check + mypy
uv run ruff check . && uv run ruff format --check . && uv run mypy app indexer webui
fmt: ## Auto-format code with ruff
uv run ruff format .
fmt-check: ## Check formatting without modifying files
uv run ruff format --check .
migrate: ## Apply migrations to the TARGET's Lakebase (TARGET=dev|prod, default dev); grants via ARGS=--apply-grants
@JSON="$$(databricks bundle validate -t $(TARGET) -o json 2>/dev/null)" || true; \
test -n "$$JSON" || { echo "could not read bundle target '$(TARGET)' (try: databricks bundle validate -t $(TARGET))"; exit 1; }; \
EP="$$(printf '%s' "$$JSON" | python3 -c 'import json,sys;v=json.load(sys.stdin)["variables"];print("projects/%s/branches/production/endpoints/%s"%(v["lakebase_project_name"]["value"],v["lakebase_endpoint_name"]["value"]))')"; \
DB="$$(printf '%s' "$$JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["variables"]["database_name"]["value"])')"; \
echo "-> migrating target '$(TARGET)' against $$EP (db=$$DB)"; \
LAKEBASE_ENDPOINT="$$EP" LAKEBASE_DATABASE="$$DB" uv run python scripts/migrate.py $(ARGS)
migration: ## Autogenerate a revision against a disposable Lakebase branch: make migration MSG="message" (bring the branch up via scripts/ci_branch.py up, export LAKEBASE_ENDPOINT/LAKEBASE_DATABASE, then run; never autogenerate against the production branch)
@test -n "$(MSG)" || (echo "migration requires MSG=\"...\"" && exit 1)
@test -n "$$LAKEBASE_ENDPOINT" || (echo "migration requires LAKEBASE_ENDPOINT pointing at a disposable Lakebase branch (scripts/ci_branch.py up); never autogenerate against the production branch" && exit 1)
uv run alembic revision --autogenerate -m "$(MSG)"
deploy: ## Deploy + activate the app on TARGET via the full ordered pipeline (scripts/deploy.sh)
bash scripts/deploy.sh full $(TARGET)
deploy-prod: ## Deploy + activate on prod (requires JOB_RUN_AS_SP=<job run-as SP client id>)
@test -n "$$JOB_RUN_AS_SP" || (echo "deploy-prod requires JOB_RUN_AS_SP=<client-id> (the job run-as SP); an empty value creates a broken NO-LOGIN role" && exit 1)
TARGET=prod bash scripts/deploy.sh full prod
smoke: ## Smoke-test the deployed app (TARGET=dev|prod; ARGS=--expect-indexed/--enable-mcp)
@test -z "$$PGHOST" || (echo "PGHOST is set -> smoke would hit local Postgres. Unset PGHOST to target the bundle's Lakebase." && exit 1)
@JSON="$$(databricks bundle validate -t $(TARGET) -o json 2>/dev/null)" || true; \
test -n "$$JSON" || { echo "could not read bundle target '$(TARGET)' (try: databricks bundle validate -t $(TARGET))"; exit 1; }; \
APP="$$(printf '%s' "$$JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["variables"]["app_name"]["value"])')"; \
EP="$$(printf '%s' "$$JSON" | python3 -c 'import json,sys;v=json.load(sys.stdin)["variables"];print("projects/%s/branches/production/endpoints/%s"%(v["lakebase_project_name"]["value"],v["lakebase_endpoint_name"]["value"]))')"; \
DB="$$(printf '%s' "$$JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["variables"]["database_name"]["value"])')"; \
URL="$$(databricks apps get "$$APP" -o json | jq -er '.url')" || { echo "could not resolve app URL for '$$APP' (is it deployed?)"; exit 1; }; \
echo "-> smoke '$(TARGET)' against $$URL (endpoint=$$EP db=$$DB)"; \
LAKEBASE_ENDPOINT="$$EP" LAKEBASE_DATABASE="$$DB" uv run python scripts/smoke.py --app-url "$$URL" $(ARGS)
index: ## Run the indexing job on TARGET (populates repos/files/symbols from configured repos)
databricks bundle run code_search_index -t $(TARGET)
webui-wheel: ## Build the app wheel + refresh webui/uv.lock and stage under webui/wheels/ (run before bundle deploy; the webui App installs on the uv path / Python 3.12)
rm -f dist/*.whl
uv build --wheel
mkdir -p webui/wheels
# Preserve the real {name}-{version}-...-.whl filename: webui/pyproject.toml's path source
# points at it by name, and a renamed `app.whl` is not a valid wheel filename. Clear stale
# wheels first so exactly one version is present.
rm -f webui/wheels/*.whl
cp dist/*.whl webui/wheels/
# Regenerate uv.lock against the freshly-staged wheel. Databricks Apps installs the webui App on
# the uv path (pyproject.toml + uv.lock, no requirements.txt) and runs `uv sync --locked`, which
# hash-verifies the wheel -- so the lock's pinned hash MUST match what ships. `uv build` is NOT
# byte-reproducible (new hash every build) AND a plain `uv lock` is a no-op when the version is
# unchanged, so it keeps a STALE hash and `--locked` fails ("Hash mismatch"). --refresh-package
# forces uv to re-read and re-hash the local wheel every time. The lock is a deploy-time
# artifact (gitignored, like the wheel). It also fails loudly if a version bump left
# pyproject.toml's path source pointing at a now-missing wheel filename.
cd webui && uv lock --refresh-package databricks-code-search
# De-databricks the lock: a workstation behind the corp proxy resolves through the internal
# PyPI mirror (~/.config/uv/uv.toml pins pypi-proxy.dev.databricks.com as default index), so
# the lock's registry + artifact URLs point there -- and the Apps build environment cannot
# reach that host (observed: pgvector wheel download "operation timed out" at deploy). The
# mirror preserves PyPI's URL layout 1:1 (/simple/ index, /packages/<hash-path>/<file>), so a
# host rewrite to the public equivalents is exact; the lock pins per-artifact hashes and the
# Apps build's `uv sync --locked` verifies them, so any divergence fails loudly at install
# rather than shipping a wrong artifact. No-op on workstations already resolving public PyPI.
sed -i.bak \
-e 's#https://pypi-proxy\.dev\.databricks\.com/simple/#https://pypi.org/simple/#g' \
-e 's#https://pypi-proxy\.dev\.databricks\.com/packages/#https://files.pythonhosted.org/packages/#g' \
webui/uv.lock && rm -f webui/uv.lock.bak
webui-build: ## Build the webui frontend (npm ci + vite build) into webui/frontend/dist/, which is committed
cd webui/frontend && npm ci && npm run build
webui-test: ## Run the webui frontend test suite (vitest; gated in CI's webui job, needs npm ci first)
cd webui/frontend && npm test
webui-verify-dist: ## Rebuild the frontend and fail if the committed dist/ is stale (CI freshness gate; issue #80)
cd webui/frontend && npm ci && npm run build
# Must run from the repo root: the git pathspec below is repo-root-relative. The `cd` on the
# build line above does not leak here — each recipe line runs in its own shell.
@status=$$(git status --porcelain -- webui/frontend/dist); \
if [ -n "$$status" ]; then \
echo "ERROR: webui/frontend/dist/ is stale relative to src/ — run 'make webui-build' and commit the result."; \
echo "$$status"; \
git --no-pager diff --stat -- webui/frontend/dist; \
exit 1; \
fi
destroy: ## Tear down the whole bundle for TARGET (typed-confirm; irreversible Lakebase data loss)
bash scripts/deploy.sh destroy $(TARGET)
set-secrets: ## Write the GitHub token into the bundle's secret scope (run after deploy). Requires GITHUB_TOKEN; scope/key via SECRET_SCOPE/SECRET_KEY.
@test -n "$$GITHUB_TOKEN" || (echo "set-secrets requires GITHUB_TOKEN in env" && exit 1)
databricks secrets put-secret "$(SECRET_SCOPE)" "$(SECRET_KEY)" --string-value "$$GITHUB_TOKEN"
requirements: ## Export production requirements.txt for the app (no `-e .`: the app runs from shipped source, not an installed package)
uv export --no-dev --no-hashes --no-emit-project -o app/requirements.txt
diagrams: ## Re-render docs/diagrams/*.dot to PNG (needs graphviz; PNGs are committed)
@command -v dot >/dev/null || (echo "graphviz not installed: 'dot' not on PATH" && exit 1)
@for f in docs/diagrams/*.dot; do \
echo "-> $${f%.dot}.png"; \
dot -Tpng -Gdpi=144 "$$f" -o "$${f%.dot}.png"; \
done
clean: ## Remove caches and build artifacts
rm -rf .pytest_cache .ruff_cache .mypy_cache dist build *.egg-info
help: ## Show this help message
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'