Skip to content

Commit da5e4b0

Browse files
authored
[fix] isolate Unix-only process-group syscalls behind build tags (#247)
* [fix] isolate Unix-only process-group syscalls behind build tags Extract the Setpgid/SIGKILL process-group setup from hooks.go and manager.go into configureProcessGroup, implemented in proc_unix.go (unix build tag) and a no-op proc_windows.go (windows build tag). Fixes GOOS=windows cross-compilation: syscall.SysProcAttr.Setpgid and syscall.Kill are absent on Windows, causing the build to fail. Unix runtime behaviour is unchanged. * [ci] add Windows cross-compile guard to CI Add a build-cross job that cross-compiles for windows/amd64 and windows/arm64 on every push and PR. This mirrors the goreleaser release targets and ensures a Unix-only syscall regression cannot reach the release stage again. * [ci] set CGO_ENABLED=0 for Windows cross-compile steps Pure-Go cross-compilation requires CGO_ENABLED=0 on a Linux host without a Windows cross-compiler. Makes the intent explicit and prevents a latent failure if cgo is ever introduced. * [ci] extend build-cross matrix to all goreleaser targets Add linux/arm64, darwin/amd64, and darwin/arm64 to the build-cross CI job so it fully mirrors the goreleaser release matrix (not just Windows). Addresses reviewer feedback on PR #247.
1 parent 4139ba8 commit da5e4b0

5 files changed

Lines changed: 47 additions & 21 deletions

File tree

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ jobs:
8080
cache: true
8181
- run: make test-race
8282

83+
build-cross:
84+
runs-on: ubuntu-latest
85+
timeout-minutes: 15
86+
steps:
87+
- uses: actions/checkout@v6
88+
- uses: actions/setup-go@v6
89+
with:
90+
go-version-file: go.mod
91+
cache: true
92+
- run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build ./...
93+
- run: CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build ./...
94+
- run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build ./...
95+
- run: CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build ./...
96+
- run: CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build ./...
97+
8398
vuln:
8499
runs-on: ubuntu-latest
85100
timeout-minutes: 15

internal/deps/hooks.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"os/exec"
88
"strings"
9-
"syscall"
109

1110
"github.com/lugassawan/rimba/internal/progress"
1211
)
@@ -30,15 +29,7 @@ func RunPostCreateHooks(ctx context.Context, worktreeDir string, hooks []string,
3029

3130
cmd := exec.CommandContext(ctx, "sh", "-c", hook) //nolint:gosec // hook commands come from user config
3231
cmd.Dir = worktreeDir
33-
// Put the subprocess in its own process group so that cancellation kills
34-
// sh and all children it may have forked (e.g. sh -c sleep 30 on Linux).
35-
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
36-
cmd.Cancel = func() error {
37-
if cmd.Process != nil {
38-
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
39-
}
40-
return nil
41-
}
32+
configureProcessGroup(cmd)
4233

4334
var buf bytes.Buffer
4435
cmd.Stdout = &buf

internal/deps/manager.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"runtime"
1111
"strings"
1212
"sync/atomic"
13-
"syscall"
1413

1514
"github.com/lugassawan/rimba/internal/config"
1615
"github.com/lugassawan/rimba/internal/debug"
@@ -206,16 +205,7 @@ func runInstall(ctx context.Context, worktreePath string, mod Module) error {
206205

207206
cmd := exec.CommandContext(ctx, "sh", "-c", mod.InstallCmd) //nolint:gosec // install commands come from user config
208207
cmd.Dir = dir
209-
// Put the subprocess in its own process group so cancellation kills sh and
210-
// all children (e.g. npm spawns node children that would otherwise keep
211-
// stdout/stderr pipes open and block cmd.Wait).
212-
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
213-
cmd.Cancel = func() error {
214-
if cmd.Process != nil {
215-
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
216-
}
217-
return nil
218-
}
208+
configureProcessGroup(cmd)
219209

220210
var buf bytes.Buffer
221211
cmd.Stdout = &buf

internal/deps/proc_unix.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//go:build unix
2+
3+
package deps
4+
5+
import (
6+
"os/exec"
7+
"syscall"
8+
)
9+
10+
// configureProcessGroup puts the subprocess in its own process group so that
11+
// context cancellation kills the shell and all children it may have forked
12+
// (e.g. `sh -c "sleep 30"` or npm spawning node children).
13+
func configureProcessGroup(cmd *exec.Cmd) {
14+
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
15+
cmd.Cancel = func() error {
16+
if cmd.Process != nil {
17+
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
18+
}
19+
return nil
20+
}
21+
}

internal/deps/proc_windows.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build windows
2+
3+
package deps
4+
5+
import "os/exec"
6+
7+
// configureProcessGroup is a no-op on Windows, which has no POSIX process
8+
// groups. exec.CommandContext's default cancellation (Process.Kill) is used.
9+
func configureProcessGroup(cmd *exec.Cmd) {}

0 commit comments

Comments
 (0)