Reopen the already-running setup wizard instead of failing on a busy port

This commit is contained in:
Emil
2026-08-03 14:26:17 +03:00
parent 9a90d59ecf
commit 81e854eb1d
+33
View File
@@ -10,11 +10,13 @@ import (
"errors"
"flag"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"time"
@@ -166,6 +168,16 @@ func runSetup(args []string) int {
})
listener, err := server.Listen()
if err != nil {
// A wizard may already be running on this port (left open, or a
// second terminal). If it is ours, opening the browser is the
// friendlier outcome than failing the command.
if existing := wizardAlreadyRunning(*port); existing != "" {
logger.Info("the setup wizard is already running", "url", existing)
if !*noOpen {
openBrowser(existing)
}
return 0
}
logger.Error("setup wizard could not bind the loopback port", "err", err)
return 1
}
@@ -199,3 +211,24 @@ func openBrowser(url string) {
return
}
}
// wizardAlreadyRunning probes the requested loopback port and returns its URL
// when it serves the setup wizard page, or "" when it does not (another
// process, or nothing at all).
func wizardAlreadyRunning(port int) string {
url := fmt.Sprintf("http://127.0.0.1:%d/", port)
client := http.Client{Timeout: 2 * time.Second}
resp, err := client.Get(url)
if err != nil {
return ""
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return ""
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<16))
if err != nil || !strings.Contains(string(body), "SciMesh Worker · Setup") {
return ""
}
return url
}