From f958b7dabb19d5a3e1aa46dcf8f29e90efc9c93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Baggio?= Date: Sat, 8 Aug 2026 19:11:59 -0300 Subject: [PATCH] fix(librarypath): stop cleanEmpty from panicking on a non-trailing empty entry range paths caches the slice length once at loop start, but slices.Delete shrinks the backing slice on every match. As soon as one "" sits before the last element, the loop keeps counting against the original length and calls slices.Delete with an index past the now-shorter slice, panicking with "slice bounds out of range". boss install hits this in UpdateLibraryPath, while assembling the global browsing path across every installed dependency's boss.json -- any accumulated "" in that list before the final entry crashes the whole install after every dependency already resolved and was written to disk. Rewritten as the standard in-place filter (write index trailing the read index), which stays correct while mutating the same backing array mid-range. Co-Authored-By: Claude Sonnet 5 --- utils/librarypath/librarypath.go | 11 +++---- utils/librarypath/librarypath_test.go | 45 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/utils/librarypath/librarypath.go b/utils/librarypath/librarypath.go index e3e1c20..497e079 100644 --- a/utils/librarypath/librarypath.go +++ b/utils/librarypath/librarypath.go @@ -13,8 +13,6 @@ import ( "github.com/hashload/boss/pkg/pkgmanager" - "slices" - "github.com/hashload/boss/internal/core/domain" "github.com/hashload/boss/pkg/consts" "github.com/hashload/boss/pkg/env" @@ -204,12 +202,13 @@ func getDefaultPath(fullPath bool, rootPath string) []string { // cleanEmpty removes empty strings from a slice. func cleanEmpty(paths []string) []string { - for index, value := range paths { - if value == "" { - paths = slices.Delete(paths, index, index+1) + cleaned := paths[:0] + for _, value := range paths { + if value != "" { + cleaned = append(cleaned, value) } } - return paths + return cleaned } // getNewBrowsingPathsFromDir returns a list of new browsing paths from a directory. diff --git a/utils/librarypath/librarypath_test.go b/utils/librarypath/librarypath_test.go index 78a4f10..534e624 100644 --- a/utils/librarypath/librarypath_test.go +++ b/utils/librarypath/librarypath_test.go @@ -46,6 +46,51 @@ func TestCleanPath(t *testing.T) { } } +// TestCleanEmpty tests empty string removal from a slice. +func TestCleanEmpty(t *testing.T) { + tests := []struct { + name string + in []string + want []string + }{ + { + name: "no empties", + in: []string{"a", "b"}, + want: []string{"a", "b"}, + }, + { + name: "single empty", + in: []string{"a", "", "b"}, + want: []string{"a", "b"}, + }, + { + name: "multiple empties", + in: []string{"", "a", "", "b", ""}, + want: []string{"a", "b"}, + }, + { + name: "all empty", + in: []string{"", ""}, + want: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := cleanEmpty(tt.in) + + if len(result) != len(tt.want) { + t.Fatalf("cleanEmpty() = %v, want %v", result, tt.want) + } + for i, v := range result { + if v != tt.want[i] { + t.Errorf("cleanEmpty()[%d] = %q, want %q", i, v, tt.want[i]) + } + } + }) + } +} + // TestGetNewBrowsingPaths tests browsing paths retrieval. func TestGetNewBrowsingPaths(t *testing.T) { tempDir := t.TempDir()