From e77cad4af8beca2fbb3b2b79eeb87a733b9c4af1 Mon Sep 17 00:00:00 2001 From: Val Redchenko Date: Thu, 28 May 2026 17:17:39 +0100 Subject: [PATCH 1/3] fix(dev-k8s): resolve Dockerfile.dev from smartem-decisions, refresh devops skill dev-k8s.sh `ensure_local_image` invoked `docker build -f Dockerfile.dev` from $PROJECT_ROOT (smartem-devtools), but Dockerfile.dev lives in smartem-decisions. The build failed with "no such file or directory" on every fresh `up`, leaving operators to discover the workaround themselves. Introduce SMARTEM_DECISIONS_PATH, defaulting to a sibling checkout next to smartem-devtools. Fail fast with a clear message when the file is missing so non-standard layouts can be configured rather than silently broken. Refresh the devops skill in the same change: the script path moved to smartem-devtools, the dev stack now ships an in-cluster Keycloak (with the docker-compose mock kept as the FE-only fallback), and the build command in the skill's container section matches what dev-k8s.sh expects. --- claude-code/shared/skills/devops/SKILL.md | 19 ++++++++++++++++--- scripts/k8s/dev-k8s.sh | 14 ++++++++++++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/claude-code/shared/skills/devops/SKILL.md b/claude-code/shared/skills/devops/SKILL.md index fbf7842..0670945 100644 --- a/claude-code/shared/skills/devops/SKILL.md +++ b/claude-code/shared/skills/devops/SKILL.md @@ -39,10 +39,12 @@ If you encounter this error, advise the user to run the above command. Other per **Cluster management:** +The `dev-k8s.sh` orchestration script lives in **smartem-devtools** (it used to live in smartem-decisions). It needs a sibling checkout of `smartem-decisions` to build the backend image from `Dockerfile.dev`; override `SMARTEM_DECISIONS_PATH` if your layout differs. + ```bash -cd repos/DiamondLightSource/smartem-decisions +cd repos/DiamondLightSource/smartem-devtools -# Start local k3s cluster with all services +# Start local k3s cluster with all services (backend, postgres, rabbitmq, mongo, es, keycloak mock, FE) ./scripts/k8s/dev-k8s.sh up # Stop and cleanup @@ -53,6 +55,12 @@ kubectl get pods -n smartem-decisions kubectl get services -n smartem-decisions ``` +**Keycloak in the dev stack:** + +The in-cluster Keycloak mock (`keycloak-mock/keycloak.yaml`, pulled in via the development kustomization) is the primary local auth provider. It is brought up automatically by `dev-k8s.sh up` and reachable at `http://localhost:30090`. + +The `keycloak-mock/docker-compose.yml` in the same directory is a **fallback for FE-only development** — use it when working inside `smartem-frontend` in isolation without the rest of the k3s stack. Do not run both at once or the NodePort/host-port mapping will collide. + ### Services (smartem-decisions namespace) | Service | Port | NodePort | Purpose | @@ -77,7 +85,10 @@ k8s/ ```bash cd repos/DiamondLightSource/smartem-decisions -# Build backend image +# Build local dev backend image (matches what dev-k8s.sh expects) +docker build -f Dockerfile.dev -t smartem-decisions:latest . + +# Build production image docker build -t smartem-backend:dev . # Build with specific Python version (from Dockerfile) @@ -87,6 +98,8 @@ docker build --build-arg PYTHON_VERSION=3.12 -t smartem-backend:dev . docker build --target production -t smartem-backend:prod . ``` +`dev-k8s.sh up` will build `smartem-decisions:latest` automatically if it is missing, loading it into k3s containerd. Pre-build it manually only if you want to iterate without re-running `up`. + ### Push to Registry ```bash diff --git a/scripts/k8s/dev-k8s.sh b/scripts/k8s/dev-k8s.sh index 63e1a9b..d62afd4 100755 --- a/scripts/k8s/dev-k8s.sh +++ b/scripts/k8s/dev-k8s.sh @@ -6,6 +6,9 @@ NAMESPACE="smartem-decisions" K8S_ENV_PATH="k8s/environments/development" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +# smartem-decisions repo holds Dockerfile.dev for the local backend image build. +# Override SMARTEM_DECISIONS_PATH if your checkout layout is not the conventional sibling. +SMARTEM_DECISIONS_PATH="${SMARTEM_DECISIONS_PATH:-$PROJECT_ROOT/../smartem-decisions}" DEPLOY_ENV="${DEPLOY_ENV:-development}" # Colors for output @@ -380,9 +383,16 @@ ensure_local_image() { # Check if image exists in Docker if ! docker image inspect "$image_name" &> /dev/null; then - log_info "Building SmartEM image..." - cd "$PROJECT_ROOT" + if [[ ! -f "$SMARTEM_DECISIONS_PATH/Dockerfile.dev" ]]; then + log_error "Dockerfile.dev not found at: $SMARTEM_DECISIONS_PATH/Dockerfile.dev" + log_error "Set SMARTEM_DECISIONS_PATH to the smartem-decisions repo checkout, or" + log_error "clone it as a sibling of smartem-devtools." + exit 1 + fi + log_info "Building SmartEM image from $SMARTEM_DECISIONS_PATH..." + cd "$SMARTEM_DECISIONS_PATH" docker build -f Dockerfile.dev -t "$image_name" . + cd "$PROJECT_ROOT" else log_info "SmartEM image already exists in Docker" fi From bba516cb6c4f042a8d00b7b68aea40dfdb12ebab Mon Sep 17 00:00:00 2001 From: Val Redchenko Date: Thu, 28 May 2026 17:44:18 +0100 Subject: [PATCH 2/3] fix(webui): default fenceless code blocks to plain text rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language-less ASCII-art fences (diagrams, terminal output, plain text) were being heuristically tokenised by shiki/rehype-pretty-code, producing stripey per-line backgrounds and broken alignment because the highlighter guessed at keywords that were not there. Set rehype-pretty-code's `defaultLang: 'text'` so any fenced block without an explicit language renders as a uniform monospace block. Source markdown stays unchanged — no doc-side annotations needed. Verified locally against docs/agent/authentication.md: the client_credentials diagram now renders as a single contiguous block. See also #219 (view-source link on docs pages) and #220 (rewrite relative .md links in webui rendering). --- webui/vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/webui/vite.config.ts b/webui/vite.config.ts index 3744951..09e006f 100644 --- a/webui/vite.config.ts +++ b/webui/vite.config.ts @@ -81,6 +81,7 @@ export default defineConfig({ { theme: 'github-dark', keepBackground: true, + defaultLang: 'text', }, ], ], From ffa5811542ef5b31cca2b828a9e20ea19ac9879f Mon Sep 17 00:00:00 2001 From: Val Redchenko Date: Fri, 29 May 2026 17:54:21 +0100 Subject: [PATCH 3/3] docs(k8s): explain how to point FE dev server at local k3s API The Vite proxy defaults to localhost:8000, but the dev k3s stack exposes the API on NodePort 30080. Spell out both ways to bridge that - the new VITE_API_PROXY_TARGET env var (no port-forward needed) and the equivalent kubectl port-forward command - so devs don't have to rediscover the gap each time. --- docs/operations/kubernetes.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/operations/kubernetes.md b/docs/operations/kubernetes.md index 42830bd..1659cf4 100644 --- a/docs/operations/kubernetes.md +++ b/docs/operations/kubernetes.md @@ -35,6 +35,15 @@ Once the environment is running, you can access: > **Note**: The script automatically handles GitHub Container Registry authentication and waits for all pods to be ready. +### Pointing the SmartEM frontend dev server at this stack + +The Vite dev server in `smartem-frontend` proxies `/api` to `http://localhost:8000` by default — the conventional port for a standalone backend (`uvicorn` directly, or a `kubectl port-forward`). When the backend is in the dev k3s stack, two options: + +- **No port-forward** (simplest) — set `VITE_API_PROXY_TARGET=http://localhost:30080` in `apps/smartem/.env.local`. Vite proxies straight to the NodePort. +- **Port-forward to 8000** — `kubectl port-forward -n smartem-decisions svc/smartem-http-api-service 8000:80` keeps the default proxy target working. Useful if you also want CLI tools that hit `http://localhost:8000` to keep working unchanged. + +Keycloak is reachable on `http://localhost:30090` regardless; see [Local Keycloak](../development/local-keycloak.md) for the SPA's runtime auth config. + ## Kubernetes Structure ```