Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pkg/desktop/transport/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,10 @@ func TestFallbackTransport_NonSocketErrorDoesNotDisableProxy(t *testing.T) {
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://example.invalid/", http.NoBody)
require.NoError(t, err)

resp, err := ft.RoundTrip(req) //nolint:bodyclose // resp is nil on error, checked below
resp, err := ft.RoundTrip(req)
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
require.Error(t, err)
require.Nil(t, resp)
assert.True(t, errors.Is(err, upstreamErr) || err.Error() == upstreamErr.Error())
Expand Down
14 changes: 6 additions & 8 deletions pkg/selfupdate/exec_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package selfupdate

import (
"context"
"errors"
"fmt"
"os"
"os/exec"
Expand All @@ -27,9 +29,9 @@ func swapBinary(dst, src string) error {
if cpErr := atomicWriteFromFile(dst, src); cpErr != nil {
// Roll back so we never leave the install without a binary.
if rbErr := os.Rename(old, dst); rbErr != nil {
return fmt.Errorf("installing new binary: %w (copy fallback failed: %v; rollback also failed: %v)", err, cpErr, rbErr)
return fmt.Errorf("installing new binary: %w (copy fallback failed: %w; rollback also failed: %w)", err, cpErr, rbErr)
}
return fmt.Errorf("installing new binary: %w (copy fallback failed: %v)", err, cpErr)
return fmt.Errorf("installing new binary: %w (copy fallback failed: %w)", err, cpErr)
}
_ = os.Remove(src)
}
Expand All @@ -48,7 +50,7 @@ func reExecProcess(path string, args, env []string) error {
childArgs = args[1:]
}

cmd := exec.Command(path, childArgs...) //nolint:gosec // path is our own freshly installed binary
cmd := exec.CommandContext(context.Background(), path, childArgs...)
cmd.Env = env
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
Expand All @@ -69,9 +71,5 @@ func reExecProcess(path string, args, env []string) error {
// asExitError is a tiny helper kept separate so exec_unix.go does not need to
// import errors solely for this Windows branch.
func asExitError(err error, target **exec.ExitError) bool {
if e, ok := err.(*exec.ExitError); ok { //nolint:errorlint // direct type assertion is intentional here
*target = e
return true
}
return false
return errors.As(err, target)
}
4 changes: 2 additions & 2 deletions pkg/tools/builtin/backgroundjobs/cmd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ func createProcessGroup(proc *os.Process) (*processGroup, error) {
if _, err := windows.SetInformationJobObject(
job,
windows.JobObjectExtendedLimitInformation,
uintptr(unsafe.Pointer(&info)),
uintptr(unsafe.Pointer(&info)), //nolint:gosec // Windows API requires unsafe.Pointer
uint32(unsafe.Sizeof(info))); err != nil {
_ = windows.CloseHandle(job)
return nil, err
}

handle, err := windows.OpenProcess(windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE, false, uint32(proc.Pid))
handle, err := windows.OpenProcess(windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE, false, uint32(proc.Pid)) //nolint:gosec // proc.Pid fits in uint32 on Windows
if err != nil {
_ = windows.CloseHandle(job)
return nil, err
Expand Down
19 changes: 16 additions & 3 deletions pkg/tools/builtin/rag/rag.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type ToolSet struct {
manager *rag.Manager
toolName string
eventCallback EventCallback
cancelWatcher context.CancelFunc
watcherDone chan struct{}
}

// Verify interface compliance.
Expand Down Expand Up @@ -84,18 +86,25 @@ func (t *ToolSet) Start(ctx context.Context) error {
return nil
}

// We create a child context so we can explicitly cancel the watcher and event goroutines
// when Stop() is called, preventing goroutine leaks if the parent context outlives this toolset.
watchCtx, cancel := context.WithCancel(ctx)
t.cancelWatcher = cancel
t.watcherDone = make(chan struct{})

// Forward RAG manager events if a callback is set.
if t.eventCallback != nil {
go t.forwardEvents(ctx)
go t.forwardEvents(watchCtx)
}

if err := t.manager.Initialize(ctx); err != nil {
return fmt.Errorf("failed to initialize RAG manager %q: %w", t.toolName, err)
}

go func() {
if err := t.manager.StartFileWatcher(ctx); err != nil {
slog.ErrorContext(ctx, "Failed to start RAG file watcher", "tool", t.toolName, "error", err)
defer close(t.watcherDone)
if err := t.manager.StartFileWatcher(watchCtx); err != nil && !errors.Is(err, context.Canceled) {
slog.ErrorContext(watchCtx, "Failed to start RAG file watcher", "tool", t.toolName, "error", err)
}
}()
return nil
Expand All @@ -106,6 +115,10 @@ func (t *ToolSet) Stop(_ context.Context) error {
if t.manager == nil {
return nil
}
if t.cancelWatcher != nil {
t.cancelWatcher()
<-t.watcherDone
}
return t.manager.Close()
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/tools/builtin/shell/cmd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ func createProcessGroup(proc *os.Process) (*processGroup, error) {
if _, err := windows.SetInformationJobObject(
job,
windows.JobObjectExtendedLimitInformation,
uintptr(unsafe.Pointer(&info)),
uintptr(unsafe.Pointer(&info)), //nolint:gosec // Windows API requires unsafe.Pointer
uint32(unsafe.Sizeof(info))); err != nil {
_ = windows.CloseHandle(job)
return nil, err
}

handle, err := windows.OpenProcess(windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE, false, uint32(proc.Pid))
handle, err := windows.OpenProcess(windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE, false, uint32(proc.Pid)) //nolint:gosec // proc.Pid fits in uint32 on Windows
if err != nil {
_ = windows.CloseHandle(job)
return nil, err
Expand Down
6 changes: 5 additions & 1 deletion pkg/tools/builtin/shell/script_shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,11 @@ func (t *ScriptToolSet) execute(ctx context.Context, rt tools.Runtime, toolConfi
// stay literal because env values may legitimately contain $ (issue
// #2615).
for _, key := range slices.Sorted(maps.Keys(toolConfig.Env)) {
envCopy = append(envCopy, key+"="+path.ExpandEnvRefs(toolConfig.Env[key]))
val := path.ExpandEnvRefs(toolConfig.Env[key])
if strings.ContainsRune(val, 0) {
return tools.ResultError(fmt.Sprintf("configured environment variable %q contains a NUL byte", key)), nil
}
envCopy = append(envCopy, key+"="+val)
}
for key, value := range params {
if value == nil {
Expand Down
Loading