Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
Expand Down
7 changes: 7 additions & 0 deletions internal/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
46 changes: 35 additions & 11 deletions internal/tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down
Loading