fix(tui): resolve cross-platform root detection and fallback paths in pickers - #3795
Conversation
|
In relation to #3792 we should maybe have some cross platform integration tests |
Adding these tests would be a good move. |
|
@Sayt-0 can you verify if the windows support related fixes are addressing these problems too |
|
Checked against the Windows changes merged in #3866. #3866 adds a blocking native Windows test job, but it does not include the picker root detection and fallback changes from this PR. The current Windows suite does not exercise a drive root such as |
Add regression tests for root directory fallback and empty initial working directory as requested by maintainer. Also fix unrelated failing tests on Windows due to slash normalization, and resolve gosec, errorlint, and forbidigo lint issues.
…r-paths # Conflicts: # pkg/tui/dialog/session_browser_test.go # pkg/tui/dialog/skills_test.go
|
@Sayt-0 I have fixed the code in this PR too. There were some merge conflicts too. Please do let me know if anything needs to be changed in this pr. |
Sayt-0
left a comment
There was a problem hiding this comment.
The core fix is correct and useful: filepath.Dir(dir) != dir is the right cross-platform root check, and the . fallback matches the existing pattern in file_picker.go. Verified locally: build, go test ./pkg/tui/dialog, GOOS=windows build of the touched packages, and golangci-lint run all pass.
Blocking point: the PR mixes unrelated GOOS=windows lint fixes into the picker fix. Details in the inline comments.
| area | status |
|---|---|
root detection idiom (file_picker.go, working_dir_picker.go) |
good |
. fallback (working_dir_picker.go, tui.go) |
good |
| regression tests | good, two minor remarks |
selfupdate / cmd_windows.go lint changes |
out of scope, please split |
| 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) |
There was a problem hiding this comment.
These changes are unrelated to the picker fix. CI lint only runs with GOOS=linux (see Taskfile.yml), so _windows.go files are never linted in CI and this is not needed for the PR to pass. %v to %w also changes error-wrapping semantics: errors.Is/As now match cpErr and rbErr. Please move to a separate chore(lint) PR, together with the nolint changes below and in backgroundjobs/cmd_windows.go and shell/cmd_windows.go.
| } | ||
|
|
||
| cmd := exec.Command(path, childArgs...) //nolint:gosec // path is our own freshly installed binary | ||
| cmd := exec.Command(path, childArgs...) //nolint:noctx // path is our own freshly installed binary; no context needed for re-exec |
There was a problem hiding this comment.
Same scope concern. The swap itself is correct (G204 is globally excluded in .golangci.yml, so the gosec directive was unused and nolintlint flags it under GOOS=windows; noctx is the actual finding), but it belongs in the lint cleanup PR.
| 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 // Pid is safe to convert to uint32 on Windows |
There was a problem hiding this comment.
Out of scope for this PR, same as the selfupdate changes. The directives are justified (Windows API requires unsafe.Pointer; PID fits in uint32), but they should move to the lint cleanup PR. Same for the mirrored changes in pkg/tools/builtin/shell/cmd_windows.go.
| func TestWorkingDirPickerEmptyFallback(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| // Pass an empty string for the initial directory. | ||
| // NewWorkingDirPickerDialog should fall back to os.Getwd(). | ||
| d := NewWorkingDirPickerDialog(t.Context(), nil, nil, nil, "").(*workingDirPickerDialog) | ||
|
|
||
| cwd, err := os.Getwd() | ||
| require.NoError(t, err) | ||
|
|
||
| require.Equal(t, cwd, d.currentDir, "empty initial directory should fall back to current working directory") | ||
| } |
There was a problem hiding this comment.
The name suggests coverage of the new cwd = "." fallback, but this test exercises the pre-existing empty-string to os.Getwd() path (the . branch is only reachable when Getwd fails, which is hard to simulate). Suggested rename: TestWorkingDirPickerEmptyInitialDirUsesGetwd. The test is still worth keeping as it covers a previously untested path.
| for _, e := range d.entries { | ||
| if e.name == ".." { | ||
| t.Errorf("root directory should not have a parent dir entry, but got '..'") | ||
| } | ||
| } |
There was a problem hiding this comment.
The manual loop can reuse the existing helper:
assert.NotContains(t, entryNames(d.entries), "..")- Rename TestWorkingDirPickerEmptyFallback to TestWorkingDirPickerEmptyInitialDirUsesGetwd - Simplify file picker root test to use assert.NotContains
|
@Sayt-0 I have fixed the PR according to your review. |
Summary
This PR addresses two cross-platform path handling issues in the TUI file and directory picker components to ensure reliable behavior on Windows and POSIX systems.
1. Cross-Platform Root Directory Boundary Detection
d.currentDir != "/"to decide whether to append the..(parent directory) option.C:\). SinceC:\does not equal/, the TUI treatedC:\as a non-root folder and appended a..entry pointing back toC:\, causing an infinite navigation loop.filepath.Dir(d.currentDir) != d.currentDir. This leverages the standard library to verify if a directory is its own parent, which works reliably on Windows, Linux, and macOS.2. Cross-Platform Directory Fallback (
/to.)os.Getwd()returned an error or empty string, the TUI fell back toworkingDir = "/"./as a fallback causes invalid path resolutions on Windows systems when execution context is lost./to.. This aligns with the existing fallback pattern used infile_picker.goand ensures safe resolution relative to the process execution directory across all platforms.Verification
golangci-lint run(0 issues).go test ./pkg/tui/dialog.