diff --git a/Dockerfile b/Dockerfile index f7ce572..8a1d823 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,16 +25,14 @@ RUN pip install --upgrade pip build \ # ── Stage 3: final runtime ──────────────────────────────────────────────────── -FROM python:3.12-slim +# Use Microsoft's official .NET runtime image — avoids the SHA1-signed APT key +# issue on newer Debian hosts (trixie+ rejects packages.microsoft.com GPG since 2026-02-01). +FROM mcr.microsoft.com/dotnet/runtime:8.0 -# Install .NET runtime (needed by Bicep.LangServer.dll) +# Install Python 3 + pip (the dotnet base image is Debian bookworm) RUN apt-get update \ && apt-get install -y --no-install-recommends \ - wget apt-transport-https ca-certificates \ - && wget -q https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb \ - && dpkg -i packages-microsoft-prod.deb && rm packages-microsoft-prod.deb \ - && apt-get update \ - && apt-get install -y --no-install-recommends dotnet-runtime-8.0 \ + python3 python3-pip wget \ && rm -rf /var/lib/apt/lists/* # Copy Bicep Language Server (baked in at build time — no volume needed) @@ -42,7 +40,7 @@ COPY --from=bicep-downloader /opt/bicep-langserver /opt/bicep-langserver # Install Python package and dependencies COPY --from=builder /dist/*.whl /tmp/ -RUN pip install --no-cache-dir /tmp/*.whl && rm /tmp/*.whl +RUN pip3 install --no-cache-dir --break-system-packages /tmp/*.whl && rm /tmp/*.whl # Configuration defaults (override via Nomad env) ENV PYTHON_LSP_PORT=2087 \ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9015a3d --- /dev/null +++ b/Makefile @@ -0,0 +1,109 @@ +IMAGE := ilsp-local +TAG := dev +CONTAINER := ilsp-dev + +PYTHON_PORT := 2087 +BICEP_PORT := 2088 +HEALTH_PORT := 2089 + +# External services (used at container startup for catalog fetching) +DEVOPS_MCP_URL ?= https://devops-mcp.i80.dk + +.PHONY: build run stop restart logs shell test smoke clean help + +## ── Build ──────────────────────────────────────────────────────────────────── + +build: ## Build Docker image locally (no push) + docker build -t $(IMAGE):$(TAG) . + +rebuild: ## Force rebuild (no cache) + docker build --no-cache -t $(IMAGE):$(TAG) . + +## ── Run ────────────────────────────────────────────────────────────────────── + +run: stop ## Build and start container (rebuilds image) + $(MAKE) build + docker run -d \ + --name $(CONTAINER) \ + -p $(PYTHON_PORT):$(PYTHON_PORT) \ + -p $(BICEP_PORT):$(BICEP_PORT) \ + -p $(HEALTH_PORT):$(HEALTH_PORT) \ + -e DEVOPS_MCP_URL=$(DEVOPS_MCP_URL) \ + -e PYTHONUNBUFFERED=1 \ + $(IMAGE):$(TAG) + @echo "" + @echo " Container started — waiting for health check..." + @sleep 5 + @$(MAKE) health + +run-quick: ## Start container without rebuilding image + $(MAKE) stop + docker run -d \ + --name $(CONTAINER) \ + -p $(PYTHON_PORT):$(PYTHON_PORT) \ + -p $(BICEP_PORT):$(BICEP_PORT) \ + -p $(HEALTH_PORT):$(HEALTH_PORT) \ + -e DEVOPS_MCP_URL=$(DEVOPS_MCP_URL) \ + -e PYTHONUNBUFFERED=1 \ + $(IMAGE):$(TAG) + @sleep 5 + @$(MAKE) health + +stop: ## Stop and remove container (if running) + -docker stop $(CONTAINER) 2>/dev/null + -docker rm $(CONTAINER) 2>/dev/null + +restart: stop run-quick ## Restart container without rebuilding + +logs: ## Follow container logs + docker logs -f $(CONTAINER) + +logs-tail: ## Show last 50 lines of logs + docker logs --tail 50 $(CONTAINER) + +shell: ## Open bash inside running container + docker exec -it $(CONTAINER) bash + +## ── Health & Testing ───────────────────────────────────────────────────────── + +health: ## Check health endpoint + @curl -sf http://localhost:$(HEALTH_PORT)/health | python3 -m json.tool \ + || echo " ✗ Health endpoint not reachable — is the container running? (make run)" + +smoke: ## Run end-to-end smoke test against local container + bash scripts/smoke_test.sh localhost + +test: ## Run unit tests (local Python, no Docker needed) + python3 -m pytest tests/ -v + +## ── Status ─────────────────────────────────────────────────────────────────── + +ps: ## Show running container status + @docker ps --filter name=$(CONTAINER) --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" + +ports: ## Show port mappings + @echo " Python LSP : localhost:$(PYTHON_PORT) (TCP)" + @echo " Bicep LSP : localhost:$(BICEP_PORT) (TCP)" + @echo " Health : http://localhost:$(HEALTH_PORT)/health" + +## ── Cleanup ────────────────────────────────────────────────────────────────── + +clean: stop ## Stop container and remove local image + -docker rmi $(IMAGE):$(TAG) 2>/dev/null + +## ── Help ───────────────────────────────────────────────────────────────────── + +help: ## Show this help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile \ + | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-14s\033[0m %s\n", $$1, $$2}' + @echo "" + @echo " Env vars:" + @echo " DEVOPS_MCP_URL (default: $(DEVOPS_MCP_URL))" + @echo "" + @echo " Examples:" + @echo " make run # build + start" + @echo " make logs # follow logs" + @echo " make smoke # end-to-end test" + @echo " DEVOPS_MCP_URL=http://localhost:8000 make run # point at local MCP" + +.DEFAULT_GOAL := help