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
27 changes: 25 additions & 2 deletions cmd/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/abiosoft/colima/cmd/root"
"github.com/abiosoft/colima/config/configmanager"
"github.com/abiosoft/colima/model"
"github.com/abiosoft/colima/util"
"github.com/abiosoft/colima/util/terminal"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -159,13 +160,35 @@ Press Ctrl-C to stop the server.
return err
}

// Determine the port to use
port := modelCmdArgs.ServePort
portExplicitlySet := cmd.Flags().Changed("port")

// If port was not explicitly set, find an available port starting from the default
const maxPortAttempts = 20
if !portExplicitlySet {
availablePort, found := util.FindAvailablePort(port, maxPortAttempts)
if !found {
return fmt.Errorf("no available port found in range %d-%d", port, port+maxPortAttempts-1)
}
if availablePort != port {
fmt.Printf("Port %d is in use, using port %d instead\n", port, availablePort)
}
port = availablePort
} else {
// User explicitly set the port, check if it's available
if _, found := util.FindAvailablePort(port, 1); !found {
return fmt.Errorf("port %d is already in use", port)
}
}

// Build header for alternate screen
separator := "────────────────────────────────────────"
header := fmt.Sprintf("Colima - Model Server (Ctrl-C to stop)\nWeb UI & API at http://localhost:%d\n%s", modelCmdArgs.ServePort, separator)
header := fmt.Sprintf("Colima - Model Server (Ctrl-C to stop)\nWeb UI & API at http://localhost:%d\n%s", port, separator)

// Run in alternate screen with header
return terminal.WithAltScreen(func() error {
return runner.Serve(normalizedModel, modelCmdArgs.ServePort)
return runner.Serve(normalizedModel, port)
}, header)
},
}
Expand Down
25 changes: 25 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,31 @@ func RandomAvailablePort() int {
return listener.Addr().(*net.TCPAddr).Port
}

// isPortAvailable checks if a specific port is available on the host.
func isPortAvailable(port int) bool {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
return false
}
if err := listener.Close(); err != nil {
return false
}
return true
}

// FindAvailablePort finds the first available port starting from startPort.
// It checks up to maxAttempts consecutive ports (startPort, startPort+1, ...).
// Returns the available port and true if found, or 0 and false if no port is available.
func FindAvailablePort(startPort, maxAttempts int) (int, bool) {
for i := range maxAttempts {
port := startPort + i
if isPortAvailable(port) {
return port, true
}
}
return 0, false
}

// HostIPAddresses returns all IPv4 addresses on the host.
func HostIPAddresses() []net.IP {
var addresses []net.IP
Expand Down