[chore](dev): keep one Delve server across hot reloads

Replace Air's kill-and-rerun loop in the debug image with the pilot
persistent-delve supervisor: src/dev/dlv-watch.sh owns one headless
`dlv exec --multiclient --continue` server, rebuilds ./tmp/main on save and
restarts only the debug target through the tiny rpc2 client in
src/dev/dlv-restart. An attached VS Code session (and its breakpoints) now
survives saves instead of reconnecting after every reload.

The debug compose builds locally with `pull_policy: build` and without
`build.network: host`: compose cannot grant the network.host entitlement, so
a cold `up` previously failed with "additional privileges requested"; the
default bridge reaches the dev Gitea for module resolution.
This commit is contained in:
2026-10-19 09:26:50 +01:00
parent 2ef8d30b47
commit c58e193038
6 changed files with 308 additions and 6 deletions
+62
View File
@@ -0,0 +1,62 @@
// Copyright (C) 2026 Dcntrlized
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// dlv-restart is a minimal JSON-RPC client (delve service/rpc2) that restarts
// the debug target of a persistent headless dlv server and resumes it.
//
// Why not `printf 'restart\n' | dlv connect ...`? `dlv connect` is an
// interactive terminal client: its stdout/stderr is distinct from the debug
// target's, it always reads go-ahead from the same stream, and its stdin EOF
// makes it drop into the (dlv) prompt and exit with a "kill the headless
// instance?" prompt — dumping the debugee's full state and racing the attached
// vscode-go client. This helper speaks rpc2 directly, so it only issues the
// RPCs we want, prints nothing, and exits without disturbing the target or the
// VS Code session.
//
// Usage: dlv-restart [addr] (addr defaults to 127.0.0.1:2345)
package main
import (
"fmt"
"os"
"github.com/go-delve/delve/service/rpc2"
)
func main() {
addr := "127.0.0.1:2345"
if len(os.Args) > 1 {
addr = os.Args[1]
}
client := rpc2.NewClient(addr)
discarded, err := client.RestartFrom(false, "", false, nil, [3]string{}, false)
if err != nil {
fmt.Fprintf(os.Stderr, "dlv-restart: Restart failed: %v\n", err)
os.Exit(1)
}
for _, d := range discarded {
fmt.Fprintf(os.Stderr, "discarded %s: %v\n", d.Breakpoint.Name, d.Reason)
}
// The restarted process begins halted, so resume it with Continue, then
// close the connection. --accept-multiclient keeps the server and target
// running while our short-lived client disconnects; an attached vscode-go
// client is undisturbed.
if err := client.Disconnect(true); err != nil {
fmt.Fprintf(os.Stderr, "dlv-restart: disconnect failed: %v\n", err)
}
}