diff --git a/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl b/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl index 7fa07c00..b9951be7 100644 --- a/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl +++ b/internal/embed/infrastructure/values/obol-frontend.yaml.gotmpl @@ -23,6 +23,12 @@ image: - name: OBOL_AUTH_DB_PATH value: "/data/auth.sqlite" + # Running Obol CLI version — shown in the local frontend footer. + # Injected by `obol stack up` via OBOL_STACK_VERSION; the frontend also + # reads obolVersion from the obol-stack-config ConfigMap. + - name: OBOL_STACK_VERSION + value: {{ env "OBOL_STACK_VERSION" | default "" | quote }} + # Obol Agent (ADK) in-cluster URL for CopilotKit runtime - name: ADK_AGENT_URL value: "http://obol-agent.agent.svc.cluster.local:8000/" diff --git a/internal/stack/stack.go b/internal/stack/stack.go index 6c1b5464..eb070dd1 100644 --- a/internal/stack/stack.go +++ b/internal/stack/stack.go @@ -32,6 +32,7 @@ import ( "github.com/ObolNetwork/obol-stack/internal/tunnel" "github.com/ObolNetwork/obol-stack/internal/ui" "github.com/ObolNetwork/obol-stack/internal/update" + "github.com/ObolNetwork/obol-stack/internal/version" x402verifier "github.com/ObolNetwork/obol-stack/internal/x402" petname "github.com/dustinkirkland/golang-petname" "gopkg.in/yaml.v3" @@ -488,6 +489,7 @@ func syncDefaults(cfg *config.Config, u *ui.UI, kubeconfigPath string, dataDir s helmfileCmd.Env = append(os.Environ(), "KUBECONFIG="+kubeconfigPath, "STACK_DATA_DIR="+dataDir, + "OBOL_STACK_VERSION="+version.Version, ) // In development mode, build and import local repo images that aren't on a @@ -522,6 +524,11 @@ func syncDefaults(cfg *config.Config, u *ui.UI, kubeconfigPath string, dataDir s u.Success("Default infrastructure deployed") + // Publish the running CLI version for the local frontend footer. + if err := tunnel.SyncStackConfigVersion(cfg); err != nil { + u.Warnf("Could not publish Obol version to frontend config: %v", err) + } + restoredLiteLLMConfig := false if previousLiteLLMConfig != "" { var err error diff --git a/internal/tunnel/tunnel.go b/internal/tunnel/tunnel.go index d20f6553..aa21eddc 100644 --- a/internal/tunnel/tunnel.go +++ b/internal/tunnel/tunnel.go @@ -20,6 +20,7 @@ import ( "github.com/ObolNetwork/obol-stack/internal/config" "github.com/ObolNetwork/obol-stack/internal/images" "github.com/ObolNetwork/obol-stack/internal/ui" + "github.com/ObolNetwork/obol-stack/internal/version" ) const ( @@ -987,27 +988,23 @@ func freeLocalPort() (int, error) { } // SyncTunnelConfigMap creates or patches the obol-stack-config ConfigMap in the -// obol-frontend namespace with the current tunnel URL. The frontend reads this -// ConfigMap to construct the correct dashboard URL. +// obol-frontend namespace with the current tunnel URL and the running Obol CLI +// version. The frontend reads this ConfigMap for the public tunnel URL and the +// footer version display. Server-side apply merges fields; call after +// obol-frontend exists (helmfile). func SyncTunnelConfigMap(cfg *config.Config, tunnelURL string) error { kubectlPath := filepath.Join(cfg.BinDir, "kubectl") kubeconfigPath := filepath.Join(cfg.ConfigDir, "kubeconfig.yaml") - // Ensure the namespace exists (non-fatal if it doesn't). - _ = exec.Command(kubectlPath, - "--kubeconfig", kubeconfigPath, - "create", "namespace", "obol-frontend", - "--dry-run=client", "-o", "yaml", - ).Run() - manifest := fmt.Sprintf(`apiVersion: v1 kind: ConfigMap metadata: name: obol-stack-config namespace: obol-frontend data: - tunnelURL: %s -`, strings.TrimRight(tunnelURL, "/")) + tunnelURL: %q + obolVersion: %q +`, strings.TrimRight(tunnelURL, "/"), version.Version) // Server-side apply avoids the flaky client-side /openapi/v2 download on k3d. cmd := exec.Command(kubectlPath, @@ -1023,6 +1020,33 @@ data: return nil } +// SyncStackConfigVersion SSA-merges only obolVersion into +// obol-frontend/obol-stack-config, leaving tunnelURL untouched. Used on stack +// up when no tunnel sync runs yet (after infra deploy creates the namespace). +func SyncStackConfigVersion(cfg *config.Config) error { + kubectlPath := filepath.Join(cfg.BinDir, "kubectl") + kubeconfigPath := filepath.Join(cfg.ConfigDir, "kubeconfig.yaml") + + manifest := fmt.Sprintf(`apiVersion: v1 +kind: ConfigMap +metadata: + name: obol-stack-config + namespace: obol-frontend +data: + obolVersion: %q +`, version.Version) + + cmd := exec.Command(kubectlPath, + "--kubeconfig", kubeconfigPath, + "apply", "--server-side", "--force-conflicts", "-f", "-", + ) + cmd.Stdin = strings.NewReader(manifest) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("kubectl apply obol-stack-config failed: %w: %s", err, strings.TrimSpace(string(out))) + } + return nil +} + // EnsureTunnelForSell ensures the tunnel is running and propagates the URL to // the public service discovery surfaces needed by seller flows. It updates the // frontend ConfigMap and storefront, but deliberately avoids syncing the