detached 682c64f

fix ci on windows: split detach helper into posix/windows variants

syscall.SysProcAttr.Setsid is posix-only — unknown field on windows.
Extracted the detach-on-start logic into a detachChild function
variable, implemented in botcmd_unix.go (Setsid) and botcmd_windows.go
(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP creation flags).
commit
682c64f494f3ae272adb98a7557f1b61e39901c9
author
patriceckhart · · 2026-04-18 08:58 UTC
parent
2158c27
signature
unsigned
3 files · +49 -2
M internal/agent/botcmd.go +11 -2
@@ -19,6 +19,13 @@ import (
1919 "github.com/patriceckhart/zot/internal/core"
2020 )
2121
22+// detachChild configures cmd to run in its own process group so tty
23+// signals sent to the parent (SIGINT, SIGHUP on logout) don't also
24+// reach the detached bot. Platform-specific: setsid on unix, a noop
25+// on windows (Go's spawn path already detaches when no console is
26+// inherited). See botcmd_unix.go and botcmd_windows.go.
27+var detachChild func(cmd *exec.Cmd)
28+
2229 // runBotCommand dispatches `zot telegram-bot ...` subcommands. The
2330 // short alias "tg" is also accepted. Returns true if rawArgs begins
2431 // with a recognised subcommand, false otherwise.
@@ -230,8 +237,10 @@ func botStart(rawTail []string) error {
230237 cmd.Stdout = logFile
231238 cmd.Stderr = logFile
232239 cmd.Stdin = nil
233- // Detach: new session so terminal signals don't reach the child.
234- cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
240+ // Detach: new session / new process group so terminal signals
241+ // don't reach the child. Impl lives in botcmd_unix.go /
242+ // botcmd_windows.go because Setsid is posix-only.
243+ detachChild(cmd)
235244
236245 if err := cmd.Start(); err != nil {
237246 return fmt.Errorf("spawn: %w", err)
@@ -19,6 +19,13 @@ import (
19 "github.com/patriceckhart/zot/internal/core"19 "github.com/patriceckhart/zot/internal/core"
20 )20 )
21 21
22+// detachChild configures cmd to run in its own process group so tty
23+// signals sent to the parent (SIGINT, SIGHUP on logout) don't also
24+// reach the detached bot. Platform-specific: setsid on unix, a noop
25+// on windows (Go's spawn path already detaches when no console is
26+// inherited). See botcmd_unix.go and botcmd_windows.go.
27+var detachChild func(cmd *exec.Cmd)
28+
22 // runBotCommand dispatches `zot telegram-bot ...` subcommands. The29 // runBotCommand dispatches `zot telegram-bot ...` subcommands. The
23 // short alias "tg" is also accepted. Returns true if rawArgs begins30 // short alias "tg" is also accepted. Returns true if rawArgs begins
24 // with a recognised subcommand, false otherwise.31 // with a recognised subcommand, false otherwise.
@@ -230,8 +237,10 @@ func botStart(rawTail []string) error {
230 cmd.Stdout = logFile237 cmd.Stdout = logFile
231 cmd.Stderr = logFile238 cmd.Stderr = logFile
232 cmd.Stdin = nil239 cmd.Stdin = nil
233- // Detach: new session so terminal signals don't reach the child.240+ // Detach: new session / new process group so terminal signals
234- cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}241+ // don't reach the child. Impl lives in botcmd_unix.go /
242+ // botcmd_windows.go because Setsid is posix-only.
243+ detachChild(cmd)
235 244
236 if err := cmd.Start(); err != nil {245 if err := cmd.Start(); err != nil {
237 return fmt.Errorf("spawn: %w", err)246 return fmt.Errorf("spawn: %w", err)
A internal/agent/botcmd_unix.go +17 -0
@@ -0,0 +1,17 @@
1+//go:build !windows
2+
3+package agent
4+
5+import (
6+ "os/exec"
7+ "syscall"
8+)
9+
10+// detachChild runs the bot in a new session so tty signals sent to
11+// the parent shell (SIGINT when the terminal closes, SIGHUP on
12+// logout) don't propagate to the detached bot.
13+func init() {
14+ detachChild = func(cmd *exec.Cmd) {
15+ cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
16+ }
17+}
@@ -0,0 +1,17 @@
1+//go:build !windows
2+
3+package agent
4+
5+import (
6+ "os/exec"
7+ "syscall"
8+)
9+
10+// detachChild runs the bot in a new session so tty signals sent to
11+// the parent shell (SIGINT when the terminal closes, SIGHUP on
12+// logout) don't propagate to the detached bot.
13+func init() {
14+ detachChild = func(cmd *exec.Cmd) {
15+ cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
16+ }
17+}
A internal/agent/botcmd_windows.go +21 -0
@@ -0,0 +1,21 @@
1+//go:build windows
2+
3+package agent
4+
5+import (
6+ "os/exec"
7+ "syscall"
8+)
9+
10+// detachChild spawns the bot in its own process group via the
11+// DETACHED_PROCESS + CREATE_NEW_PROCESS_GROUP creation flags. Windows
12+// has no direct setsid equivalent; these two flags combined give the
13+// child a clean console-less group that ctrl+c on the parent won't
14+// reach.
15+func init() {
16+ detachChild = func(cmd *exec.Cmd) {
17+ cmd.SysProcAttr = &syscall.SysProcAttr{
18+ CreationFlags: 0x00000008 | 0x00000200, // DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP
19+ }
20+ }
21+}
@@ -0,0 +1,21 @@
1+//go:build windows
2+
3+package agent
4+
5+import (
6+ "os/exec"
7+ "syscall"
8+)
9+
10+// detachChild spawns the bot in its own process group via the
11+// DETACHED_PROCESS + CREATE_NEW_PROCESS_GROUP creation flags. Windows
12+// has no direct setsid equivalent; these two flags combined give the
13+// child a clean console-less group that ctrl+c on the parent won't
14+// reach.
15+func init() {
16+ detachChild = func(cmd *exec.Cmd) {
17+ cmd.SysProcAttr = &syscall.SysProcAttr{
18+ CreationFlags: 0x00000008 | 0x00000200, // DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP
19+ }
20+ }
21+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close