Skip to content
Merged
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
2 changes: 1 addition & 1 deletion internal/embed/k3d-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ports:
options:
k3d:
wait: true
timeout: "300s"
timeout: "600s"
disableLoadbalancer: false
k3s:
extraArgs:
Expand Down
50 changes: 50 additions & 0 deletions internal/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,32 @@ import (
"log/slog"
"os"
"os/exec"
"strings"
"sync"
)

// Executor wraps subprocess execution with automatic output logging via slog
type Executor struct {
logger *slog.Logger
binDir string // Directory containing obol binaries (added to PATH)
}

// New creates a new Executor that logs subprocess output via slog
// The logger should be configured to handle subprocess output appropriately
// binDir is prepended to PATH for all commands (so tools can find dependencies)
func New(logger *slog.Logger) *Executor {
return &Executor{
logger: logger,
binDir: "",
}
}

// NewWithBinDir creates an Executor with a custom bin directory
// The binDir will be prepended to PATH for all subprocess executions
func NewWithBinDir(logger *slog.Logger, binDir string) *Executor {
return &Executor{
logger: logger,
binDir: binDir,
}
}

Expand Down Expand Up @@ -74,11 +87,45 @@ func (c *cmdLogger) logComplete() {
}
}

// setupEnv configures the command environment with PATH including binDir
func (e *Executor) setupEnv(cmd *exec.Cmd) {
if e.binDir == "" {
return
}

// Get current environment or inherit from parent
env := os.Environ()

// Find and update PATH, or add it if not present
pathUpdated := false
for i, envVar := range env {
if strings.HasPrefix(envVar, "PATH=") {
currentPath := strings.TrimPrefix(envVar, "PATH=")
// Prepend binDir to PATH if not already present
if !strings.Contains(currentPath, e.binDir) {
env[i] = "PATH=" + e.binDir + string(os.PathListSeparator) + currentPath
}
pathUpdated = true
break
}
}

// If PATH wasn't found, add it
if !pathUpdated {
env = append(env, "PATH="+e.binDir)
}

cmd.Env = env
}

// Command creates a new command for use with Output()
// Only stderr is logged/displayed, stdout is captured by Output()
func (e *Executor) Command(name string, args ...string) *exec.Cmd {
cmd := exec.Command(name, args...)

// Configure PATH to include binDir
e.setupEnv(cmd)

if e.logger != nil {
// Capture stderr for display with indentation
cmd.Stderr = &outputAccumulator{
Expand All @@ -98,6 +145,9 @@ func (e *Executor) Command(name string, args ...string) *exec.Cmd {
func (e *Executor) CommandWithOutput(name string, args ...string) CmdRunner {
cmd := exec.Command(name, args...)

// Configure PATH to include binDir
e.setupEnv(cmd)

if e.logger != nil {
// Create command logger to accumulate output
cmdLog := newCmdLogger(e.logger, name, args)
Expand Down
6 changes: 3 additions & 3 deletions internal/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func Up(cfg *config.Config) error {
stackName := getStackName(cfg)

// Create executor for subprocess calls with the logger
exec := executor.New(l.Logger)
exec := executor.NewWithBinDir(l.Logger, cfg.BinDir)
defer exec.Close()

// Check if cluster already exists using cluster list
Expand Down Expand Up @@ -227,7 +227,7 @@ func Down(cfg *config.Config) error {
})
defer cleanup()

exec := executor.New(l.Logger)
exec := executor.NewWithBinDir(l.Logger, cfg.BinDir)
defer exec.Close()

l.Info("Stopping stack gracefully", "name", stackName, "id", stackID)
Expand Down Expand Up @@ -266,7 +266,7 @@ func Purge(cfg *config.Config, force bool) error {
defer cleanup()

// Create executor for subprocess calls
exec := executor.New(l.Logger)
exec := executor.NewWithBinDir(l.Logger, cfg.BinDir)
defer exec.Close()

// Delete cluster containers
Expand Down
16 changes: 8 additions & 8 deletions obolup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1178,8 +1178,8 @@ configure_path() {
return 0
fi

# Interactive terminal: ask for consent
if [[ -t 0 ]]; then
# Check if we can prompt the user via /dev/tty (works even with curl | bash)
if [[ -c /dev/tty ]]; then
echo ""
log_info "To use 'obol' command, $OBOL_BIN_DIR needs to be in your PATH"
echo ""
Expand All @@ -1191,7 +1191,7 @@ configure_path() {
echo ""

local choice
read -p "Choose [1/2]: " choice
read -p "Choose [1/2]: " choice </dev/tty

case "$choice" in
1)
Expand All @@ -1211,7 +1211,7 @@ configure_path() {
;;
esac
else
# Non-interactive: check environment variable override
# Truly non-interactive (CI/CD, no terminal): check environment variable override
if [[ "${OBOL_MODIFY_PATH:-no}" == "yes" ]]; then
add_to_profile "$profile"
log_info "Will be available in new shell sessions"
Expand All @@ -1234,8 +1234,8 @@ print_instructions() {
fi
echo ""

# Check if terminal is interactive for bootstrap prompt
if [[ -t 0 ]] && [[ -f "$OBOL_BIN_DIR/obol" ]]; then
# Check if we can prompt the user for bootstrap (works with curl | bash via /dev/tty)
if [[ -c /dev/tty ]] && [[ -f "$OBOL_BIN_DIR/obol" ]]; then
echo ""
log_info "Would you like to start the cluster now?"
echo ""
Expand All @@ -1246,7 +1246,7 @@ print_instructions() {
echo ""

local choice
read -p "Start cluster now? [y/N]: " choice
read -p "Start cluster now? [y/N]: " choice </dev/tty

case "$choice" in
[Yy]*)
Expand Down Expand Up @@ -1281,7 +1281,7 @@ print_instructions() {
;;
esac
else
# Non-interactive or no binary - show manual instructions
# Truly non-interactive (CI/CD, no terminal) or no binary - show manual instructions
echo "Verify installation:"
echo ""
echo " obol version"
Expand Down