Skip to content

Commit e2670c1

Browse files
rubysclaude
andcommitted
Standardize framework configuration and enhance startup logging
- Simplify FrameworkConfig to use consistent command/args pattern like hooks and managed processes - Remove complex fallback logic for runtime_executable, server_executable, server_command - Add comprehensive error logging for application startup failures - Log framework configuration, command execution, and detailed error diagnostics - Check for missing executables and directories when startup fails BREAKING CHANGE: Framework configuration now uses: framework: command: ruby args: ["bin/rails", "server", "-p", "${port}"] Instead of: framework: runtime_executable: ruby server_executable: bin/rails server_command: server server_args: ["-p", "${port}"] 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 79f3542 commit e2670c1

2 files changed

Lines changed: 57 additions & 43 deletions

File tree

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,8 @@ applications:
9999
timeout: 5m # App process idle timeout (duration format)
100100
start_port: 4000
101101
framework:
102-
runtime_executable: ruby
103-
server_executable: bin/rails
104-
server_command: server
105-
server_args: ["-p", "${port}"]
102+
command: ruby # Command to execute
103+
args: ["bin/rails", "server", "-p", "${port}"] # Arguments array
106104
app_directory: /rails
107105
port_env_var: PORT
108106
startup_delay: 5

cmd/navigator/main.go

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,11 @@ type WebApp struct {
178178

179179
// FrameworkConfig represents framework-specific configuration
180180
type FrameworkConfig struct {
181-
RuntimeExecutable string `yaml:"runtime_executable"` // e.g., "ruby", "node", "python"
182-
ServerExecutable string `yaml:"server_executable"` // e.g., "bin/rails", "server.js"
183-
ServerCommand string `yaml:"server_command"` // e.g., "server"
184-
ServerArgs []string `yaml:"server_args"` // e.g., ["-p", "4000"]
185-
AppDirectory string `yaml:"app_directory"` // e.g., "/rails", "/app"
186-
PortEnvVar string `yaml:"port_env_var"` // e.g., "PORT"
187-
StartupDelay int `yaml:"startup_delay"` // seconds to wait before marking ready
181+
Command string `yaml:"command"` // e.g., "ruby", "node", "python", "bin/rails"
182+
Args []string `yaml:"args"` // e.g., ["server", "-p", "${port}"]
183+
AppDirectory string `yaml:"app_directory"` // e.g., "/rails", "/app"
184+
PortEnvVar string `yaml:"port_env_var"` // e.g., "PORT"
185+
StartupDelay int `yaml:"startup_delay"` // seconds to wait before marking ready
188186
}
189187

190188
// Tenant represents a tenant configuration with optional framework overrides
@@ -1741,43 +1739,38 @@ func (m *AppManager) startApp(app *WebApp) {
17411739
}
17421740

17431741
// Build command using framework configuration
1744-
var cmd *exec.Cmd
1742+
if framework.Command == "" {
1743+
slog.Error("Framework command not configured", "path", app.Location.Path)
1744+
return // Cannot start without command configured
1745+
}
17451746

1746-
// Expand server args with port substitution
1747-
serverArgs := make([]string, len(framework.ServerArgs))
1748-
for i, arg := range framework.ServerArgs {
1747+
// Log the framework configuration being used
1748+
slog.Info("Starting application with framework config",
1749+
"path", app.Location.Path,
1750+
"command", framework.Command,
1751+
"args", framework.Args,
1752+
"appDirectory", appDir,
1753+
"port", app.Port)
1754+
1755+
// Expand args with port substitution
1756+
args := make([]string, len(framework.Args))
1757+
for i, arg := range framework.Args {
17491758
if arg == "${port}" {
1750-
serverArgs[i] = strconv.Itoa(app.Port)
1759+
args[i] = strconv.Itoa(app.Port)
17511760
} else {
1752-
serverArgs[i] = arg
1761+
args[i] = arg
17531762
}
17541763
}
17551764

1756-
// Check if server executable exists in app directory
1757-
serverPath := filepath.Join(appDir, framework.ServerExecutable)
1758-
if _, err := os.Stat(serverPath); err == nil {
1759-
// Use the server executable directly
1760-
if framework.ServerCommand != "" {
1761-
// Add server command as first argument
1762-
args := append([]string{framework.ServerCommand}, serverArgs...)
1763-
cmd = exec.CommandContext(ctx, serverPath, args...)
1764-
} else {
1765-
cmd = exec.CommandContext(ctx, serverPath, serverArgs...)
1766-
}
1767-
} else {
1768-
// Fallback to runtime executable with server executable as argument
1769-
runtime := framework.RuntimeExecutable
1770-
if runtime == "" {
1771-
return // Cannot start without runtime executable configured
1772-
}
1773-
if framework.ServerCommand != "" {
1774-
args := append([]string{framework.ServerExecutable, framework.ServerCommand}, serverArgs...)
1775-
cmd = exec.CommandContext(ctx, runtime, args...)
1776-
} else {
1777-
args := append([]string{framework.ServerExecutable}, serverArgs...)
1778-
cmd = exec.CommandContext(ctx, runtime, args...)
1779-
}
1780-
}
1765+
// Log the final command that will be executed
1766+
slog.Info("Executing command",
1767+
"path", app.Location.Path,
1768+
"command", framework.Command,
1769+
"expandedArgs", args,
1770+
"workingDir", appDir)
1771+
1772+
// Create command with framework command and args
1773+
cmd := exec.CommandContext(ctx, framework.Command, args...)
17811774

17821775
cmd.Dir = appDir
17831776
cmd.Env = env
@@ -1844,7 +1837,30 @@ func (m *AppManager) startApp(app *WebApp) {
18441837
"directory", appDir)
18451838

18461839
if err := cmd.Start(); err != nil {
1847-
slog.Error("Failed to start web app", "path", app.Location.Path, "error", err)
1840+
slog.Error("Failed to start web app",
1841+
"path", app.Location.Path,
1842+
"command", framework.Command,
1843+
"args", args,
1844+
"workingDir", appDir,
1845+
"port", app.Port,
1846+
"error", err)
1847+
1848+
// Check if the command executable exists
1849+
if _, statErr := os.Stat(framework.Command); statErr != nil {
1850+
slog.Error("Command executable not found",
1851+
"path", app.Location.Path,
1852+
"command", framework.Command,
1853+
"statError", statErr)
1854+
}
1855+
1856+
// Check if working directory exists
1857+
if _, statErr := os.Stat(appDir); statErr != nil {
1858+
slog.Error("Working directory not found",
1859+
"path", app.Location.Path,
1860+
"workingDir", appDir,
1861+
"statError", statErr)
1862+
}
1863+
18481864
app.mutex.Lock()
18491865
app.Starting = false
18501866
app.mutex.Unlock()

0 commit comments

Comments
 (0)