Files
zeekayandGitHub 2d67d98b01 env: drop LUX_ prefix from env vars (noise) (#38)
Renames LUX_-prefixed env vars to canonical, non-noisy forms:

- LUX_MNEMONIC      -> MNEMONIC      (deploy script + LLM doc)
- LUX_PRIVATE_KEY   -> PRIVATE_KEY   (deploy script + LLM doc)
- LUX_INSECURE_TLS  -> INSECURE_TLS
- LUX_NODE_ENDPOINT -> NODE_ENDPOINT
- LUX_NETWORK_TYPE  -> NETWORK_TYPE  (backend gRPC server selector)
- LUX_RPC_URL       -> RPC_URL
- LUX_NODE_ROOT     -> NODE_ROOT
- LUX_CHART_PATH    -> CHART_PATH
- LUX_CHAINS_FILE   -> CHAINS_FILE
- LUX_KMS_ROOT_KEY  -> KMS_ROOT_KEY
- LUX_WC_PROJECT_ID -> WC_PROJECT_ID (WalletConnect)
- LUX_BLS_KEY       -> BLS_KEY       (was already supported via getEnv())
- LUX_KEY_INDEX     -> KEY_INDEX     (was already supported via getEnv())

pkg/key/backend_env.go: removed the LUX_-prefix-stripping getEnv()
helper (now reads env vars by exact name) and dropped the broken
"MNEMONIC / MNEMONIC" / "PRIVATE_KEY / PRIVATE_KEY" doc comments
which were a previous incomplete rename.

NOT renamed (defined in upstream luxfi/constants — separate concern):
- LUX_NODE_PATH      (constants.EnvNodePath)
- LUX_NETRUNNER_PATH (constants.EnvNetrunnerPath)
- LUX_EVM_PATH       (constants.EnvEVMPath)
- LUX_PLUGINS_DIR    (constants.EnvPluginsDir)

Doc comments referencing those env vars (cmd/root.go,
cmd/networkcmd/start.go, cmd/nodecmd/link.go) describe the
constants-defined names accurately and are left alone.

NOT renamed (script-local variables, not env vars):
- LUX_ZONE in scripts/update-cloudflare-dns.sh (Cloudflare DNS zone ID)
- LUX_BIN_DIR in scripts/install.sh (~/.lux/bin path constant)

BREAKING: external callers/operators must update env var names. Per
CLAUDE.md no-backwards-compatibility rule, the old forms are removed.
2026-05-15 16:35:26 -07:00

162 lines
4.3 KiB
Go

// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package prompts
import (
"errors"
"fmt"
"strings"
)
// MissingOpt describes a required option that was not provided.
type MissingOpt struct {
Flag string // e.g., "--chain-id"
Env string // e.g., "CHAIN_ID" (optional)
Prompt string // e.g., "EVM chain ID" - used for interactive prompts
Note string // optional additional context
Default string // optional default value hint
}
// MissingError creates a clear, actionable error listing all missing options.
// The error message follows UNIX conventions and guides users to the right flags.
func MissingError(cmd string, missing []MissingOpt) error {
if len(missing) == 0 {
return nil
}
var b strings.Builder
b.WriteString("missing required options:\n")
for _, m := range missing {
if m.Env != "" {
fmt.Fprintf(&b, " %s (or %s)", m.Flag, m.Env)
} else {
fmt.Fprintf(&b, " %s", m.Flag)
}
if m.Note != "" {
fmt.Fprintf(&b, " - %s", m.Note)
}
b.WriteString("\n")
}
fmt.Fprintf(&b, "\nrun '%s --help' to see all options", cmd)
if IsInteractive() {
b.WriteString("\nor run on a TTY to be prompted interactively")
}
return errors.New(b.String())
}
// PromptOrFail handles the common pattern of prompting for missing values.
// In interactive mode, prompts for each missing option.
// In non-interactive mode, returns an error listing all missing options.
//
// Usage:
//
// missing := []prompts.MissingOpt{}
// if chainID == "" {
// missing = append(missing, prompts.MissingOpt{Flag: "--chain-id", Env: "CHAIN_ID", Prompt: "EVM chain ID"})
// }
// if err := prompts.PromptOrFail("lux chain create", missing, func(m MissingOpt) (string, error) {
// return app.Prompt.CaptureString(m.Prompt)
// }, &chainID); err != nil {
// return err
// }
func PromptOrFail(cmd string, missing []MissingOpt, promptFn func(MissingOpt) (string, error), targets ...*string) error {
if len(missing) == 0 {
return nil
}
if len(missing) != len(targets) {
return fmt.Errorf("internal error: %d missing options but %d targets", len(missing), len(targets))
}
// Non-interactive: fail with complete error message
if !IsInteractive() {
return MissingError(cmd, missing)
}
// Interactive: prompt for each missing value
for i, m := range missing {
val, err := promptFn(m)
if err != nil {
return fmt.Errorf("failed to get %s: %w", m.Flag, err)
}
*targets[i] = val
}
return nil
}
// Validator holds options being collected and tracks missing ones.
// Use this for clean, declarative option handling.
type Validator struct {
cmd string
missing []MissingOpt
values []*string
}
// NewValidator creates a validator for a command.
func NewValidator(cmd string) *Validator {
return &Validator{cmd: cmd}
}
// Require marks a value as required. If empty, adds to missing list.
func (v *Validator) Require(target *string, opt MissingOpt) *Validator {
if *target == "" {
v.missing = append(v.missing, opt)
v.values = append(v.values, target)
}
return v
}
// RequireWithDefault marks a value as required with a default.
// Uses the default if empty and non-interactive, otherwise prompts.
func (v *Validator) RequireWithDefault(target *string, opt MissingOpt, defaultVal string) *Validator {
if *target == "" {
if !IsInteractive() {
*target = defaultVal
} else {
opt.Default = defaultVal
v.missing = append(v.missing, opt)
v.values = append(v.values, target)
}
}
return v
}
// Optional sets a default if the value is empty (no prompting).
func (v *Validator) Optional(target *string, defaultVal string) *Validator {
if *target == "" {
*target = defaultVal
}
return v
}
// Missing returns the list of missing options.
func (v *Validator) Missing() []MissingOpt {
return v.missing
}
// HasMissing returns true if any required options are missing.
func (v *Validator) HasMissing() bool {
return len(v.missing) > 0
}
// Resolve prompts for missing values (interactive) or returns error (non-interactive).
func (v *Validator) Resolve(promptFn func(MissingOpt) (string, error)) error {
if !v.HasMissing() {
return nil
}
if !IsInteractive() {
return MissingError(v.cmd, v.missing)
}
for i, m := range v.missing {
val, err := promptFn(m)
if err != nil {
return fmt.Errorf("failed to get %s: %w", m.Flag, err)
}
*v.values[i] = val
}
return nil
}