@@ -20,7 +20,6 @@ import (
2020const (
2121 idleTimeout = 5 * time .Minute
2222 dialTimeout = 2 * time .Second
23- startPoll = 10 * time .Second
2423)
2524
2625// daemonRequest is the JSON wire format for client→daemon messages.
@@ -281,73 +280,55 @@ func (c *daemonClient) Close() {
281280 c .conn .Close () //nolint:errcheck
282281}
283282
284- // ensureDaemon connects to an existing daemon or spawns a new one.
285- // Returns a connected daemonClient, or an error if the daemon cannot be reached.
286- func ensureDaemon (gitRoot string ) (* daemonClient , error ) {
283+ // connectDaemon tries to connect to a running daemon.
284+ // Returns a connected daemonClient, or an error if no daemon is reachable.
285+ // Does NOT spawn a new daemon — use spawnDaemon for that.
286+ func connectDaemon (gitRoot string ) (* daemonClient , error ) {
287287 sock := socketPath (gitRoot )
288288
289- // Try connecting to existing daemon.
290- if conn , err := net . DialTimeout ( "unix" , sock , dialTimeout ); err = = nil {
291- dc := & daemonClient { conn : conn }
292- if err := dc . ping (); err == nil {
293- return dc , nil
294- }
289+ conn , err := net . DialTimeout ( "unix" , sock , dialTimeout )
290+ if err ! = nil {
291+ return nil , fmt . Errorf ( "nomic: no daemon running" )
292+ }
293+ dc := & daemonClient { conn : conn }
294+ if err := dc . ping (); err != nil {
295295 dc .Close ()
296+ return nil , fmt .Errorf ("nomic: daemon not responding: %w" , err )
296297 }
298+ return dc , nil
299+ }
300+
301+ // spawnDaemon launches a daemon process in the background.
302+ // It does not wait for the daemon to become ready — callers should
303+ // fall back to in-process embedding and benefit from the daemon on
304+ // subsequent invocations.
305+ func spawnDaemon (gitRoot string ) {
306+ sock := socketPath (gitRoot )
297307
298- // Stale socket/pid — clean up .
308+ // Clean up stale socket/pid.
299309 os .Remove (sock ) //nolint:errcheck
300310 os .Remove (pidPath (gitRoot )) //nolint:errcheck
301311
302- // Spawn daemon process.
303312 exe , err := os .Executable ()
304313 if err != nil {
305- return nil , fmt .Errorf ("nomic: resolve executable: %w" , err )
314+ return
315+ }
316+
317+ // Don't spawn from test binaries — they can't serve the daemon command.
318+ if strings .HasSuffix (exe , ".test" ) || strings .Contains (exe , "/_test/" ) {
319+ return
306320 }
307321
308322 cmd := exec .Command (exe , "_nomic-daemon" , "--git-root" , gitRoot )
309323 cmd .Stdout = nil
310324 cmd .Stderr = nil
311325 cmd .Stdin = nil
312- // Detach from parent process group.
313326 setSysProcAttr (cmd )
314327 if err := cmd .Start (); err != nil {
315- return nil , fmt . Errorf ( "nomic: start daemon: %w" , err )
328+ return
316329 }
317-
318- // Wait in background so we can detect early exit.
319- waitCh := make (chan error , 1 )
320- go func () { waitCh <- cmd .Wait () }()
321-
322- // Poll for socket readiness.
323- deadline := time .Now ().Add (startPoll )
324- for time .Now ().Before (deadline ) {
325- // Check if daemon already exited (failed to start).
326- select {
327- case err := <- waitCh :
328- if err != nil {
329- return nil , fmt .Errorf ("nomic: daemon exited: %w" , err )
330- }
331- return nil , fmt .Errorf ("nomic: daemon exited unexpectedly" )
332- default :
333- }
334-
335- time .Sleep (100 * time .Millisecond )
336- conn , err := net .DialTimeout ("unix" , sock , dialTimeout )
337- if err != nil {
338- continue
339- }
340- dc := & daemonClient {conn : conn }
341- if err := dc .ping (); err == nil {
342- return dc , nil
343- }
344- dc .Close ()
345- }
346-
347- // Timed out — kill the daemon process to avoid leaked goroutines.
348- cmd .Process .Kill () //nolint:errcheck
349- <- waitCh
350- return nil , fmt .Errorf ("nomic: daemon did not start within %v" , startPoll )
330+ // Fully detach — don't wait on the child.
331+ go cmd .Wait () //nolint:errcheck
351332}
352333
353334// NewDaemonCmd returns the hidden _nomic-daemon cobra command.
0 commit comments