From fdf31bdeb8b74c7402edb573e2f68177d2ed72b6 Mon Sep 17 00:00:00 2001 From: HananINouman Date: Fri, 26 Jun 2026 17:28:17 +0300 Subject: [PATCH] chore(dev): document local frontend workflow and k3d image import Clarify single-cluster dev via .envrc.local, in-cluster vs pnpm dev URLs, and auto k3d image import after dev-frontend push so :dev tag updates stick. Co-authored-by: Cursor --- .../skills/obol-stack-dev/references/dev.md | 17 +++++++++++- .envrc.local.example | 27 ++++++++----------- CLAUDE.md | 23 +++++++++++++++- README.md | 2 ++ justfile | 17 +++++++++++- 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/.agents/skills/obol-stack-dev/references/dev.md b/.agents/skills/obol-stack-dev/references/dev.md index 82bac1f6..69beea3d 100644 --- a/.agents/skills/obol-stack-dev/references/dev.md +++ b/.agents/skills/obol-stack-dev/references/dev.md @@ -115,5 +115,20 @@ When `OBOL_DEVELOPMENT=true`, `obol stack up` creates k3d pull-through caches an Caveats: -- Pull-through caches do **not** speed up local-build flows (`docker build` runs on the host daemon, `k3d image import` bypasses registries). Use the local push target (`just dev-frontend` does this). +- Pull-through caches do **not** speed up local-build flows (`docker build` runs on the host daemon). `just dev-frontend` pushes to `localhost:54103` **and** runs `k3d image import` so k3s picks up the new `:dev` digest (`imagePullPolicy: IfNotPresent` otherwise serves a stale image). - Registry config is only set up at cluster create. If `obol stack up` is starting an existing cluster, registry setup is skipped — recreate (`obol stack down && obol stack up`) once to pick up new entries. + +### Local frontend (`obol-stack-front-end`) + +```bash +# From obol-stack — use .envrc.local so OBOL_CONFIG_DIR points at ~/.config/obol (one cluster) +source .envrc.local +FRONTEND_DIR=../obol-stack-front-end just dev-frontend-rebuild +open http://obol.stack:8080 +``` + +- `dev-frontend` — build (may use Docker cache) + push + import + rollout +- `dev-frontend-rebuild` — same with `--no-cache` (use after editing frontend source) +- `dev-frontend-reset` — restore released chart image + +Host `pnpm run dev` on `:3000` is optional for HMR; see frontend repo `.env.example` for kubeconfig/Prometheus port-forwards. diff --git a/.envrc.local.example b/.envrc.local.example index 2cbf9221..38a3084f 100644 --- a/.envrc.local.example +++ b/.envrc.local.example @@ -1,22 +1,17 @@ -# Local environment overrides (copy to .envrc.local) -# This file is for local development customizations and is gitignored +# Local environment overrides (copy to .envrc.local, then: source .envrc.local) +# This file is gitignored. Use direnv (`direnv allow`) for auto-load in this repo. -# Recommended when your stack lives in ~/.config/obol but you build obol from this repo: +# --- Recommended: dev-build obol CLI against your real stack (single cluster) --- +# Without OBOL_CONFIG_DIR, OBOL_DEVELOPMENT=true alone uses .workspace/config and +# creates a SECOND k3d cluster (new petname stack ID). Point at ~/.config/obol instead. +# +# export OBOL_DEVELOPMENT=true # export OBOL_CONFIG_DIR="${HOME}/.config/obol" +# export OBOL_DATA_DIR="${HOME}/.local/share/obol" +# export OBOL_STATE_DIR="${HOME}/.local/state/obol" # export OBOL_BIN_DIR="${PWD}/.workspace/bin" -# PATH_add .workspace/bin -# -# Without OBOL_CONFIG_DIR, OBOL_DEVELOPMENT=true uses .workspace/config and may -# create a second k3d cluster (separate stack ID from your real install). +# if type PATH_add >/dev/null 2>&1; then PATH_add .workspace/bin; else export PATH="${PWD}/.workspace/bin:${PATH}"; fi -# Example: Override development mode -# export OBOL_DEVELOPMENT=false - -# Example: Override config directories +# Example: custom paths # export OBOL_CONFIG_DIR=/custom/config/path # export OBOL_BIN_DIR=/custom/bin/path -# export OBOL_STATE_DIR=/custom/state/path - -# Example: Set custom XDG directories -# export XDG_CONFIG_HOME=/custom/xdg/config -# export XDG_DATA_HOME=/custom/xdg/data diff --git a/CLAUDE.md b/CLAUDE.md index 612f6ec3..773d31c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -238,7 +238,28 @@ k3d: 1 server, ports `80:80` + `8080:80` + `443:443` + `8443:443`, image `ranche Generated k3d registry config written to `$OBOL_CONFIG_DIR/registries.yaml`. Cache data under `~/.local/state/obol/registry-cache/` by default, or under `OBOL_REGISTRY_CACHE_DIR` when set. -Local push target: `just dev-frontend` swaps layered diffs into cluster via `docker push localhost:54103/...` (deployment image `localhost:54103/...:dev`) — only changed layers transfer, vs. `k3d image import`'s full-tarball round-trip. +Local push target: `just dev-frontend` builds `obol-stack-front-end`, pushes `localhost:54103/obol-stack-front-end:dev`, **imports into the active k3d cluster** (`k3d image import` — required because `imagePullPolicy: IfNotPresent` caches the `:dev` tag), and restarts the frontend pod. Use `just dev-frontend-rebuild` after code changes (forces `docker build --no-cache`). Reset: `just dev-frontend-reset`. + +### Local frontend development + +**Two ways to run the UI:** + +| Mode | URL | When | +|------|-----|------| +| In-cluster (recommended) | `http://obol.stack:8080` | Stable Prometheus/disk metrics; no port-forwards | +| Host `pnpm run dev` | `http://obol.stack:3000` | Fast React HMR; needs kubeconfig + optional Prometheus/eRPC port-forwards (see frontend `.env.example`) | + +**Single cluster when dev-building from this repo** — copy `.envrc.local.example` → `.envrc.local` and `source` it (or `direnv allow`). This sets `OBOL_CONFIG_DIR=$HOME/.config/obol` so `obol kubectl` / `just dev-frontend` hit your real stack instead of spawning a second `.workspace` cluster. + +```bash +cd obol-stack && source .envrc.local +FRONTEND_DIR=../obol-stack-front-end just dev-frontend-rebuild # after UI code changes +open http://obol.stack:8080 +``` + +**k3d vs `obol stack up`:** `k3d cluster start` only powers Docker containers + API. `obol stack up` deploys Helm infra, agents, tunnel replay, etc. Use k3d directly only for loadbalancer/port emergencies; otherwise `obol stack up`. + +**Stale frontend image symptoms:** build log shows all `CACHED` layers → use `dev-frontend-rebuild`; rollout succeeds but UI unchanged → `k3d image import` (now automatic in `just dev-frontend`). Caveats: - Pull-through caches don't help host `docker build` flows — `k3d image import` bypasses registries entirely. The local push target is what speeds up local-build redeploys. diff --git a/README.md b/README.md index 97a09d7f..ebc4ec5d 100644 --- a/README.md +++ b/README.md @@ -357,6 +357,8 @@ OBOL_DEVELOPMENT=true ./obolup.sh Development mode uses `.workspace/` instead of XDG directories and runs `go run` on every `obol` invocation — no build step needed. +**Already have a stack in `~/.config/obol`?** Copy `.envrc.local.example` → `.envrc.local` and `source` it so dev commands use that cluster (avoids a second k3d stack in `.workspace/config`). **Frontend in-cluster:** `FRONTEND_DIR=../obol-stack-front-end just dev-frontend-rebuild` → `http://obol.stack:8080` (see `CLAUDE.md` → Local frontend development). + Networks are embedded at `internal/embed/networks/`. Each uses annotated Go templates that auto-generate CLI flags: ```yaml diff --git a/justfile b/justfile index 802f66b4..5324ecc0 100644 --- a/justfile +++ b/justfile @@ -45,7 +45,7 @@ dev_image := "localhost:54103/obol-stack-front-end:dev" # Build frontend from local source, push to local registry, and restart the pod dev-frontend: (_dev-frontend-build "false" "true") -# Rebuild and hot-swap frontend (skip docker cache for faster iteration) +# Rebuild and hot-swap frontend (forces docker --no-cache; use after code changes) dev-frontend-rebuild: (_dev-frontend-build "true" "false") # Internal: build the frontend dev image, push it, and roll out the deployment. @@ -103,6 +103,21 @@ _dev-frontend-build no_cache set_image: docker build "${build_args[@]}" -t {{ dev_image }} {{ frontend_dir }} echo "→ Pushing {{ dev_image }} to local registry" docker push {{ dev_image }} + + # k3s caches images by tag (imagePullPolicy: IfNotPresent). Pushing a new + # digest to :dev does not replace the node's copy — import forces the update. + cfg_dir="${OBOL_CONFIG_DIR:-}" + if [ -z "$cfg_dir" ] && [ "${OBOL_DEVELOPMENT:-}" = "true" ]; then + cfg_dir="{{ justfile_directory() }}/.workspace/config" + fi + if [ -z "$cfg_dir" ]; then + cfg_dir="${XDG_CONFIG_HOME:-$HOME/.config}/obol" + fi + stack_id="$(cat "$cfg_dir/.stack-id")" + cluster="obol-stack-${stack_id}" + echo "→ Importing {{ dev_image }} into ${cluster}" + k3d image import {{ dev_image }} -c "${cluster}" + echo "→ Restarting frontend deployment" if [ "{{ set_image }}" = "true" ]; then obol kubectl set image deployment/obol-frontend-obol-app \