From 1c388240e0f056fe3392ceb973cd04efb7bdb722 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Mon, 13 Apr 2026 22:14:46 +0200 Subject: [PATCH 1/3] Use `slices` and `cmp` packages instead of `sort` Migrate all `sort.Slice`, `sort.SliceStable`, and `sort.Strings` calls to their standard library equivalents introduced in Go 1.21: `slices.SortFunc`, `slices.SortStableFunc`, and `slices.Sort`. Add `forbidigo` lint rules to prevent reintroduction. See https://go.dev/doc/go1.21#slices Co-authored-by: Isaac --- .golangci.yaml | 10 +++++++ acceptance/acceptance_test.go | 3 +- acceptance/internal/config.go | 3 +- bundle/config/loader/process_include.go | 12 ++++---- .../mutator/resourcemutator/apply_presets.go | 6 ++-- bundle/config/validate/enum.go | 12 ++++---- bundle/config/validate/required.go | 12 ++++---- .../config/validate/unique_resource_keys.go | 22 +++++++------- bundle/configsync/format.go | 6 ++-- bundle/configsync/patch.go | 7 +++-- bundle/configsync/resolve.go | 29 ++++++++++--------- bundle/deploy/terraform/tfdyn/convert_job.go | 23 ++++++++------- bundle/docsgen/nodes.go | 11 +++---- bundle/internal/validation/enum.go | 9 +++--- bundle/internal/validation/required.go | 9 +++--- bundle/permissions/permission_diagnostics.go | 4 +-- bundle/phases/telemetry.go | 6 ++-- bundle/render/render_text_output.go | 11 +++---- bundle/run/output/job.go | 7 +++-- cmd/apps/import.go | 18 +++++++----- cmd/bundle/debug/refschema.go | 4 +-- cmd/fs/ls.go | 7 +++-- cmd/labs/project/interpreters.go | 15 +++++----- experimental/aitools/cmd/list.go | 4 +-- .../aitools/lib/installer/installer.go | 4 +-- experimental/aitools/lib/installer/update.go | 4 +-- libs/apps/manifest/manifest.go | 11 +++---- libs/dagrun/dagrun_test.go | 3 +- libs/databrickscfg/cfgpickers/warehouses.go | 27 +++++++++-------- libs/dyn/dynloc/locations.go | 3 +- libs/dyn/merge/elements_by_key.go | 4 +-- libs/dyn/yamlsaver/saver.go | 7 +++-- libs/filer/dbfs_client.go | 4 +-- libs/filer/fake_filer.go | 5 ++-- libs/filer/files_client.go | 4 +-- libs/filer/workspace_files_client.go | 4 +-- libs/process/opts_test.go | 4 +-- libs/structs/structdiff/diff.go | 3 +- libs/structs/structwalk/walk.go | 3 +- libs/sync/dirset.go | 4 +-- libs/template/renderer.go | 6 ++-- libs/template/writer.go | 7 +++-- libs/testdiff/replacement.go | 6 ++-- libs/testserver/jobs.go | 5 ++-- libs/testserver/serving_endpoints.go | 4 +-- libs/utils/utils.go | 4 +-- 46 files changed, 198 insertions(+), 178 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index e64590f365..1690eb7703 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -46,6 +46,16 @@ linters: msg: Use env.UserHomeDir(ctx) from libs/env instead. - pattern: 'os\.Getenv' msg: Use env.Get(ctx) from the libs/env package instead of os.Getenv. + - pattern: 'sort\.Slice' + msg: Use slices.SortFunc from the standard library instead. + - pattern: 'sort\.SliceStable' + msg: Use slices.SortStableFunc from the standard library instead. + - pattern: 'sort\.Strings' + msg: Use slices.Sort from the standard library instead. + - pattern: 'sort\.Ints' + msg: Use slices.Sort from the standard library instead. + - pattern: 'sort\.Float64s' + msg: Use slices.Sort from the standard library instead. analyze-types: true copyloopvar: check-alias: true diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index 7cbc5d55d8..b46ac462b7 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -17,7 +17,6 @@ import ( "regexp" "runtime" "slices" - "sort" "strconv" "strings" "sync" @@ -486,7 +485,7 @@ func getTests(t *testing.T) []string { }) require.NoError(t, err) - sort.Strings(testDirs) + slices.Sort(testDirs) return testDirs } diff --git a/acceptance/internal/config.go b/acceptance/internal/config.go index e14bddae68..48bbd11c3f 100644 --- a/acceptance/internal/config.go +++ b/acceptance/internal/config.go @@ -6,7 +6,6 @@ import ( "path/filepath" "reflect" "slices" - "sort" "strings" "testing" "time" @@ -350,7 +349,7 @@ func ExpandEnvMatrix(matrix, exclude map[string][]string, extraVars []string) [] for key := range filteredMatrix { keys = append(keys, key) } - sort.Strings(keys) + slices.Sort(keys) // Build an expansion of all combinations. // At each step we look at a given key and append each possible value to each diff --git a/bundle/config/loader/process_include.go b/bundle/config/loader/process_include.go index 12be8bb83f..3a64297814 100644 --- a/bundle/config/loader/process_include.go +++ b/bundle/config/loader/process_include.go @@ -1,10 +1,10 @@ package loader import ( + "cmp" "context" "fmt" "slices" - "sort" "strings" "github.com/databricks/cli/bundle" @@ -98,7 +98,7 @@ func validateSingleResourceDefined(configRoot dyn.Value, ext, typ string) diag.D lines = append(lines, fmt.Sprintf(" - %s (%s)\n", r.key, r.typ)) } // Sort the lines to print to make the output deterministic. - sort.Strings(lines) + slices.Sort(lines) // Compact the lines before writing them to the message to remove any duplicate lines. // This is needed because we do not dedup earlier when gathering the resources // and it's valid to define the same resource in both the resources and targets block. @@ -114,11 +114,11 @@ func validateSingleResourceDefined(configRoot dyn.Value, ext, typ string) diag.D paths = append(paths, rr.path) } // Sort the locations and paths to make the output deterministic. - sort.Slice(locations, func(i, j int) bool { - return locations[i].String() < locations[j].String() + slices.SortFunc(locations, func(a, b dyn.Location) int { + return cmp.Compare(a.String(), b.String()) }) - sort.Slice(paths, func(i, j int) bool { - return paths[i].String() < paths[j].String() + slices.SortFunc(paths, func(a, b dyn.Path) int { + return cmp.Compare(a.String(), b.String()) }) return diag.Diagnostics{ diff --git a/bundle/config/mutator/resourcemutator/apply_presets.go b/bundle/config/mutator/resourcemutator/apply_presets.go index 8749103b47..dd6625633c 100644 --- a/bundle/config/mutator/resourcemutator/apply_presets.go +++ b/bundle/config/mutator/resourcemutator/apply_presets.go @@ -1,10 +1,10 @@ package resourcemutator import ( + "cmp" "context" "path" "slices" - "sort" "strings" "github.com/databricks/cli/bundle" @@ -315,8 +315,8 @@ func toTagArray(tags map[string]string) []Tag { for key, value := range tags { tagArray = append(tagArray, Tag{Key: key, Value: value}) } - sort.Slice(tagArray, func(i, j int) bool { - return tagArray[i].Key < tagArray[j].Key + slices.SortFunc(tagArray, func(a, b Tag) int { + return cmp.Compare(a.Key, b.Key) }) return tagArray } diff --git a/bundle/config/validate/enum.go b/bundle/config/validate/enum.go index 033f7474ec..e6266163bc 100644 --- a/bundle/config/validate/enum.go +++ b/bundle/config/validate/enum.go @@ -1,10 +1,10 @@ package validate import ( + "cmp" "context" "fmt" "slices" - "sort" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/internal/validation/generated" @@ -86,16 +86,14 @@ func (f *enum) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { } // Sort diagnostics to make them deterministic - sort.Slice(diags, func(i, j int) bool { + slices.SortFunc(diags, func(a, b diag.Diagnostic) int { // First sort by summary - if diags[i].Summary != diags[j].Summary { - return diags[i].Summary < diags[j].Summary + if n := cmp.Compare(a.Summary, b.Summary); n != 0 { + return n } // Then sort by locations as a tie breaker if summaries are the same. - iLocs := fmt.Sprintf("%v", diags[i].Locations) - jLocs := fmt.Sprintf("%v", diags[j].Locations) - return iLocs < jLocs + return cmp.Compare(fmt.Sprintf("%v", a.Locations), fmt.Sprintf("%v", b.Locations)) }) return diags diff --git a/bundle/config/validate/required.go b/bundle/config/validate/required.go index 82b02ed8b8..6f886caab4 100644 --- a/bundle/config/validate/required.go +++ b/bundle/config/validate/required.go @@ -1,10 +1,10 @@ package validate import ( + "cmp" "context" "fmt" "slices" - "sort" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/internal/validation/generated" @@ -69,16 +69,14 @@ func warnForMissingFields(ctx context.Context, b *bundle.Bundle) diag.Diagnostic } // Sort diagnostics to make them deterministic - sort.Slice(diags, func(i, j int) bool { + slices.SortFunc(diags, func(a, b diag.Diagnostic) int { // First sort by summary - if diags[i].Summary != diags[j].Summary { - return diags[i].Summary < diags[j].Summary + if n := cmp.Compare(a.Summary, b.Summary); n != 0 { + return n } // Finally sort by locations as a tie breaker if summaries are the same. - iLocs := fmt.Sprintf("%v", diags[i].Locations) - jLocs := fmt.Sprintf("%v", diags[j].Locations) - return iLocs < jLocs + return cmp.Compare(fmt.Sprintf("%v", a.Locations), fmt.Sprintf("%v", b.Locations)) }) return diags diff --git a/bundle/config/validate/unique_resource_keys.go b/bundle/config/validate/unique_resource_keys.go index 3a227e5c1e..12c13fd1a8 100644 --- a/bundle/config/validate/unique_resource_keys.go +++ b/bundle/config/validate/unique_resource_keys.go @@ -1,8 +1,9 @@ package validate import ( + "cmp" "context" - "sort" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" @@ -99,20 +100,17 @@ func (m *uniqueResourceKeys) Apply(ctx context.Context, b *bundle.Bundle) diag.D // Sort the locations and paths for consistent error messages. This helps // with unit testing. - sort.Slice(v.locations, func(i, j int) bool { - l1 := v.locations[i] - l2 := v.locations[j] - - if l1.File != l2.File { - return l1.File < l2.File + slices.SortFunc(v.locations, func(a, b dyn.Location) int { + if n := cmp.Compare(a.File, b.File); n != 0 { + return n } - if l1.Line != l2.Line { - return l1.Line < l2.Line + if n := cmp.Compare(a.Line, b.Line); n != 0 { + return n } - return l1.Column < l2.Column + return cmp.Compare(a.Column, b.Column) }) - sort.Slice(v.paths, func(i, j int) bool { - return v.paths[i].String() < v.paths[j].String() + slices.SortFunc(v.paths, func(a, b dyn.Path) int { + return cmp.Compare(a.String(), b.String()) }) // If there are multiple resources with the same key, report an error. diff --git a/bundle/configsync/format.go b/bundle/configsync/format.go index a4671039b8..1ad54db5a1 100644 --- a/bundle/configsync/format.go +++ b/bundle/configsync/format.go @@ -2,7 +2,7 @@ package configsync import ( "fmt" - "sort" + "slices" "strings" ) @@ -21,7 +21,7 @@ func FormatTextOutput(changes Changes) string { for key := range changes { resourceKeys = append(resourceKeys, key) } - sort.Strings(resourceKeys) + slices.Sort(resourceKeys) for _, resourceKey := range resourceKeys { resourceChanges := changes[resourceKey] @@ -31,7 +31,7 @@ func FormatTextOutput(changes Changes) string { for path := range resourceChanges { paths = append(paths, path) } - sort.Strings(paths) + slices.Sort(paths) for _, path := range paths { configChange := resourceChanges[path] diff --git a/bundle/configsync/patch.go b/bundle/configsync/patch.go index 9853641e6b..06bdb381db 100644 --- a/bundle/configsync/patch.go +++ b/bundle/configsync/patch.go @@ -2,12 +2,13 @@ package configsync import ( "bytes" + "cmp" "context" "errors" "fmt" "os" "regexp" - "sort" + "slices" "strconv" "strings" @@ -62,8 +63,8 @@ func ApplyChangesToYAML(ctx context.Context, b *bundle.Bundle, fieldChanges []Fi }) } - sort.Slice(result, func(i, j int) bool { - return result[i].Path < result[j].Path + slices.SortFunc(result, func(a, b FileChange) int { + return cmp.Compare(a.Path, b.Path) }) return result, nil diff --git a/bundle/configsync/resolve.go b/bundle/configsync/resolve.go index 396480111f..f6c91a9dfa 100644 --- a/bundle/configsync/resolve.go +++ b/bundle/configsync/resolve.go @@ -1,11 +1,12 @@ package configsync import ( + "cmp" "context" "fmt" "io/fs" "path/filepath" - "sort" + "slices" "strings" "github.com/databricks/cli/bundle" @@ -174,7 +175,7 @@ func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes for resourceKey := range configChanges { resourceKeys = append(resourceKeys, resourceKey) } - sort.Strings(resourceKeys) + slices.Sort(resourceKeys) for _, resourceKey := range resourceKeys { resourceChanges := configChanges[resourceKey] @@ -187,25 +188,25 @@ func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes } // Sort field paths by depth (deeper first), then operation type (removals before adds), then alphabetically - sort.SliceStable(fieldPaths, func(i, j int) bool { - depthI := fieldPathsDepths[fieldPaths[i]] - depthJ := fieldPathsDepths[fieldPaths[j]] + slices.SortStableFunc(fieldPaths, func(a, b string) int { + depthA := fieldPathsDepths[a] + depthB := fieldPathsDepths[b] - if depthI != depthJ { - return depthI > depthJ + if depthA != depthB { + return cmp.Compare(depthB, depthA) } - opI := resourceChanges[fieldPaths[i]].Operation - opJ := resourceChanges[fieldPaths[j]].Operation + opA := resourceChanges[a].Operation + opB := resourceChanges[b].Operation - if opI == OperationRemove && opJ != OperationRemove { - return true + if opA == OperationRemove && opB != OperationRemove { + return -1 } - if opI != OperationRemove && opJ == OperationRemove { - return false + if opA != OperationRemove && opB == OperationRemove { + return 1 } - return fieldPaths[i] < fieldPaths[j] + return cmp.Compare(a, b) }) // Create indices map for this resource, path -> indices, that we could use to replace with added elements diff --git a/bundle/deploy/terraform/tfdyn/convert_job.go b/bundle/deploy/terraform/tfdyn/convert_job.go index 83683f5e89..c9f7e8219a 100644 --- a/bundle/deploy/terraform/tfdyn/convert_job.go +++ b/bundle/deploy/terraform/tfdyn/convert_job.go @@ -1,10 +1,10 @@ package tfdyn import ( + "cmp" "context" "fmt" "slices" - "sort" "strings" "github.com/databricks/cli/bundle/internal/tf/schema" @@ -101,7 +101,7 @@ func patchApplyPolicyDefaultValues(_ dyn.Path, v dyn.Value) (dyn.Value, error) { } } - sort.Strings(paths) + slices.Sort(paths) valList := make([]dyn.Value, len(paths)) for i, s := range paths { valList[i] = dyn.V(s) @@ -132,19 +132,22 @@ func convertJobResource(ctx context.Context, vin dyn.Value) (dyn.Value, error) { var err error tasks, ok := vin.Get("tasks").AsSequence() if ok { - sort.Slice(tasks, func(i, j int) bool { + slices.SortFunc(tasks, func(a, b dyn.Value) int { // We sort the tasks by their task key. Tasks without task keys are ordered // before tasks with task keys. We do not error for those tasks // since presence of a task_key is validated for in the Jobs backend. - tk1, ok := tasks[i].Get("task_key").AsString() - if !ok { - return true + tk1, ok1 := a.Get("task_key").AsString() + tk2, ok2 := b.Get("task_key").AsString() + if !ok1 && ok2 { + return -1 } - tk2, ok := tasks[j].Get("task_key").AsString() - if !ok { - return false + if ok1 && !ok2 { + return 1 } - return tk1 < tk2 + if !ok1 && !ok2 { + return 0 + } + return cmp.Compare(tk1, tk2) }) vout, err = dyn.Set(vin, "tasks", dyn.V(tasks)) if err != nil { diff --git a/bundle/docsgen/nodes.go b/bundle/docsgen/nodes.go index 41d37f338c..8c651b2556 100644 --- a/bundle/docsgen/nodes.go +++ b/bundle/docsgen/nodes.go @@ -1,7 +1,8 @@ package main import ( - "sort" + "cmp" + "slices" "strings" "github.com/databricks/cli/libs/jsonschema" @@ -130,8 +131,8 @@ func buildNodes(s jsonschema.Schema, refs map[string]*jsonschema.Schema, ownFiel } } - sort.Slice(nodes, func(i, j int) bool { - return nodes[i].Title < nodes[j].Title + slices.SortFunc(nodes, func(a, b rootNode) int { + return cmp.Compare(a.Title, b.Title) }) return nodes } @@ -193,8 +194,8 @@ func getAttributes(props, refs map[string]*jsonschema.Schema, ownFields map[stri Link: reference, }) } - sort.Slice(attributes, func(i, j int) bool { - return attributes[i].Title < attributes[j].Title + slices.SortFunc(attributes, func(a, b attributeNode) int { + return cmp.Compare(a.Title, b.Title) }) return attributes } diff --git a/bundle/internal/validation/enum.go b/bundle/internal/validation/enum.go index d0c201e909..ca2e821da5 100644 --- a/bundle/internal/validation/enum.go +++ b/bundle/internal/validation/enum.go @@ -2,13 +2,14 @@ package main import ( "bytes" + "cmp" "errors" "fmt" "go/format" "os" "path/filepath" "reflect" - "sort" + "slices" "text/template" "github.com/databricks/cli/bundle/config" @@ -185,7 +186,7 @@ func sortGroupedPatternsEnum(groupedPatterns map[string][]EnumPatternInfo) [][]E for key := range groupedPatterns { groupKeys = append(groupKeys, key) } - sort.Strings(groupKeys) + slices.Sort(groupKeys) // Build sorted result result := make([][]EnumPatternInfo, 0, len(groupKeys)) @@ -193,8 +194,8 @@ func sortGroupedPatternsEnum(groupedPatterns map[string][]EnumPatternInfo) [][]E patterns := groupedPatterns[key] // Sort patterns within each group by pattern - sort.Slice(patterns, func(i, j int) bool { - return patterns[i].Pattern < patterns[j].Pattern + slices.SortFunc(patterns, func(a, b EnumPatternInfo) int { + return cmp.Compare(a.Pattern, b.Pattern) }) result = append(result, patterns) diff --git a/bundle/internal/validation/required.go b/bundle/internal/validation/required.go index 584f63ba8d..10c0c0eb5d 100644 --- a/bundle/internal/validation/required.go +++ b/bundle/internal/validation/required.go @@ -2,12 +2,13 @@ package main import ( "bytes" + "cmp" "fmt" "go/format" "os" "path/filepath" "reflect" - "sort" + "slices" "strings" "text/template" @@ -141,7 +142,7 @@ func sortGroupedPatterns(groupedPatterns map[string][]RequiredPatternInfo) [][]R for key := range groupedPatterns { groupKeys = append(groupKeys, key) } - sort.Strings(groupKeys) + slices.Sort(groupKeys) // Build sorted result result := make([][]RequiredPatternInfo, 0, len(groupKeys)) @@ -149,8 +150,8 @@ func sortGroupedPatterns(groupedPatterns map[string][]RequiredPatternInfo) [][]R patterns := groupedPatterns[key] // Sort patterns within each group by parent path - sort.Slice(patterns, func(i, j int) bool { - return patterns[i].Parent < patterns[j].Parent + slices.SortFunc(patterns, func(a, b RequiredPatternInfo) int { + return cmp.Compare(a.Parent, b.Parent) }) result = append(result, patterns) diff --git a/bundle/permissions/permission_diagnostics.go b/bundle/permissions/permission_diagnostics.go index 21d997f1b7..e25ccd5e72 100644 --- a/bundle/permissions/permission_diagnostics.go +++ b/bundle/permissions/permission_diagnostics.go @@ -3,7 +3,7 @@ package permissions import ( "context" "fmt" - "sort" + "slices" "strings" "github.com/databricks/cli/bundle" @@ -112,7 +112,7 @@ func analyzeBundlePermissions(b *bundle.Bundle) (bool, string) { assistance := "For assistance, contact the owners of this project." if otherManagers.Size() > 0 { list := otherManagers.Values() - sort.Strings(list) + slices.Sort(list) assistance = fmt.Sprintf( "For assistance, users or groups with appropriate permissions may include: %s.", strings.Join(list, ", "), diff --git a/bundle/phases/telemetry.go b/bundle/phases/telemetry.go index 5478ddb2a1..bb9a7d7e6b 100644 --- a/bundle/phases/telemetry.go +++ b/bundle/phases/telemetry.go @@ -1,9 +1,9 @@ package phases import ( + "cmp" "context" "slices" - "sort" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" @@ -18,8 +18,8 @@ func getExecutionTimes(b *bundle.Bundle) []protos.IntMapEntry { executionTimes := b.Metrics.ExecutionTimes // Sort the execution times in descending order. - sort.Slice(executionTimes, func(i, j int) bool { - return executionTimes[i].Value > executionTimes[j].Value + slices.SortFunc(executionTimes, func(a, b protos.IntMapEntry) int { + return cmp.Compare(b.Value, a.Value) }) // Keep only the top 250 execution times. This keeps the telemetry event diff --git a/bundle/render/render_text_output.go b/bundle/render/render_text_output.go index 58d77170d6..4b892ff219 100644 --- a/bundle/render/render_text_output.go +++ b/bundle/render/render_text_output.go @@ -1,10 +1,11 @@ package render import ( + "cmp" "context" "fmt" "io" - "sort" + "slices" "strings" "text/template" @@ -188,12 +189,12 @@ func RenderSummary(ctx context.Context, out io.Writer, b *bundle.Bundle) error { // Helper function to sort and render resource groups using the template func renderResourcesTemplate(out io.Writer, resourceGroups []ResourceGroup) error { // Sort everything to ensure consistent output - sort.Slice(resourceGroups, func(i, j int) bool { - return resourceGroups[i].GroupName < resourceGroups[j].GroupName + slices.SortFunc(resourceGroups, func(a, b ResourceGroup) int { + return cmp.Compare(a.GroupName, b.GroupName) }) for _, group := range resourceGroups { - sort.Slice(group.Resources, func(i, j int) bool { - return group.Resources[i].Key < group.Resources[j].Key + slices.SortFunc(group.Resources, func(a, b ResourceInfo) int { + return cmp.Compare(a.Key, b.Key) }) } diff --git a/bundle/run/output/job.go b/bundle/run/output/job.go index 2ac974cd57..7dce689719 100644 --- a/bundle/run/output/job.go +++ b/bundle/run/output/job.go @@ -1,9 +1,10 @@ package output import ( + "cmp" "context" "fmt" - "sort" + "slices" "strings" "github.com/databricks/databricks-sdk-go" @@ -34,8 +35,8 @@ func (out *JobOutput) String() (string, error) { } result := strings.Builder{} result.WriteString("Output:\n") - sort.Slice(out.TaskOutputs, func(i, j int) bool { - return out.TaskOutputs[i].EndTime < out.TaskOutputs[j].EndTime + slices.SortFunc(out.TaskOutputs, func(a, b TaskOutput) int { + return cmp.Compare(a.EndTime, b.EndTime) }) for _, v := range out.TaskOutputs { if v.Output == nil { diff --git a/cmd/apps/import.go b/cmd/apps/import.go index aae658676d..5922b1431e 100644 --- a/cmd/apps/import.go +++ b/cmd/apps/import.go @@ -2,12 +2,13 @@ package apps import ( "bufio" + "cmp" "context" "errors" "fmt" "os" "path/filepath" - "sort" + "slices" "strings" "go.yaml.in/yaml/v3" @@ -117,13 +118,16 @@ Examples: } // Sort apps: owned by current user first - sort.Slice(appList, func(i, j int) bool { - iOwned := strings.ToLower(appList[i].Creator) == currentUserEmail - jOwned := strings.ToLower(appList[j].Creator) == currentUserEmail - if iOwned != jOwned { - return iOwned + slices.SortFunc(appList, func(a, b apps.App) int { + aOwned := strings.ToLower(a.Creator) == currentUserEmail + bOwned := strings.ToLower(b.Creator) == currentUserEmail + if aOwned != bOwned { + if aOwned { + return -1 + } + return 1 } - return appList[i].Name < appList[j].Name + return cmp.Compare(a.Name, b.Name) }) // Build selection map diff --git a/cmd/bundle/debug/refschema.go b/cmd/bundle/debug/refschema.go index ba22d28ae1..0b7b164e86 100644 --- a/cmd/bundle/debug/refschema.go +++ b/cmd/bundle/debug/refschema.go @@ -4,7 +4,7 @@ import ( "fmt" "io" "reflect" - "sort" + "slices" "strings" "github.com/databricks/cli/bundle/direct/dresources" @@ -112,7 +112,7 @@ func dumpRemoteSchemas(out io.Writer) error { } } - sort.Strings(lines) + slices.Sort(lines) for _, l := range lines { fmt.Fprint(out, l) } diff --git a/cmd/fs/ls.go b/cmd/fs/ls.go index d7eac513a5..1e856a35e8 100644 --- a/cmd/fs/ls.go +++ b/cmd/fs/ls.go @@ -1,9 +1,10 @@ package fs import ( + "cmp" "io/fs" "path" - "sort" + "slices" "time" "github.com/databricks/cli/cmd/root" @@ -72,8 +73,8 @@ func newLsCommand() *cobra.Command { } jsonDirEntries[i] = *jsonDirEntry } - sort.Slice(jsonDirEntries, func(i, j int) bool { - return jsonDirEntries[i].Name < jsonDirEntries[j].Name + slices.SortFunc(jsonDirEntries, func(a, b jsonDirEntry) int { + return cmp.Compare(a.Name, b.Name) }) // Use template for long mode if the flag is set diff --git a/cmd/labs/project/interpreters.go b/cmd/labs/project/interpreters.go index 7bde3d6093..e02a8612d9 100644 --- a/cmd/labs/project/interpreters.go +++ b/cmd/labs/project/interpreters.go @@ -1,6 +1,7 @@ package project import ( + "cmp" "context" "errors" "fmt" @@ -8,7 +9,7 @@ import ( "os" "path/filepath" "runtime" - "sort" + "slices" "strings" "github.com/databricks/cli/libs/env" @@ -100,14 +101,12 @@ func DetectInterpreters(ctx context.Context) (allInterpreters, error) { if len(found) == 0 { return nil, ErrNoPythonInterpreters } - sort.Slice(found, func(i, j int) bool { - a := found[i].Version - b := found[j].Version - cmp := semver.Compare(a, b) - if cmp != 0 { - return cmp < 0 + slices.SortFunc(found, func(a, b Interpreter) int { + c := semver.Compare(a.Version, b.Version) + if c != 0 { + return c } - return a < b + return cmp.Compare(a.Version, b.Version) }) return found, nil } diff --git a/experimental/aitools/cmd/list.go b/experimental/aitools/cmd/list.go index 0774a0d584..7c7144bd03 100644 --- a/experimental/aitools/cmd/list.go +++ b/experimental/aitools/cmd/list.go @@ -3,7 +3,7 @@ package aitools import ( "errors" "fmt" - "sort" + "slices" "strings" "text/tabwriter" @@ -84,7 +84,7 @@ func defaultListSkills(cmd *cobra.Command, scope string) error { for name := range manifest.Skills { names = append(names, name) } - sort.Strings(names) + slices.Sort(names) version := strings.TrimPrefix(ref, "v") cmdio.LogString(ctx, "Available skills (v"+version+"):") diff --git a/experimental/aitools/lib/installer/installer.go b/experimental/aitools/lib/installer/installer.go index 82d1364b04..4a3f363254 100644 --- a/experimental/aitools/lib/installer/installer.go +++ b/experimental/aitools/lib/installer/installer.go @@ -8,7 +8,7 @@ import ( "net/http" "os" "path/filepath" - "sort" + "slices" "strings" "time" @@ -164,7 +164,7 @@ func InstallSkillsForAgents(ctx context.Context, src ManifestSource, targetAgent for name := range targetSkills { skillNames = append(skillNames, name) } - sort.Strings(skillNames) + slices.Sort(skillNames) for _, name := range skillNames { meta := targetSkills[name] diff --git a/experimental/aitools/lib/installer/update.go b/experimental/aitools/lib/installer/update.go index f92de2f6d3..9e5ea2ddaf 100644 --- a/experimental/aitools/lib/installer/update.go +++ b/experimental/aitools/lib/installer/update.go @@ -6,7 +6,7 @@ import ( "fmt" "os" "path/filepath" - "sort" + "slices" "strings" "time" @@ -236,7 +236,7 @@ func sortedKeys[V any](m map[string]V) []string { for k := range m { keys = append(keys, k) } - sort.Strings(keys) + slices.Sort(keys) return keys } diff --git a/libs/apps/manifest/manifest.go b/libs/apps/manifest/manifest.go index 84f3018164..8d3cd12e27 100644 --- a/libs/apps/manifest/manifest.go +++ b/libs/apps/manifest/manifest.go @@ -1,11 +1,12 @@ package manifest import ( + "cmp" "encoding/json" "fmt" "os" "path/filepath" - "sort" + "slices" "strings" ) @@ -58,7 +59,7 @@ func (r Resource) FieldNames() []string { for k := range r.Fields { names = append(names, k) } - sort.Strings(names) + slices.Sort(names) return names } @@ -122,8 +123,8 @@ func (m *Manifest) GetPlugins() []Plugin { } plugins = append(plugins, p) } - sort.Slice(plugins, func(i, j int) bool { - return plugins[i].Name < plugins[j].Name + slices.SortFunc(plugins, func(a, b Plugin) int { + return cmp.Compare(a.Name, b.Name) }) return plugins } @@ -174,7 +175,7 @@ func (m *Manifest) GetPluginNames() []string { for name := range m.Plugins { names = append(names, name) } - sort.Strings(names) + slices.Sort(names) return names } diff --git a/libs/dagrun/dagrun_test.go b/libs/dagrun/dagrun_test.go index 0c6488bec2..9977e162ad 100644 --- a/libs/dagrun/dagrun_test.go +++ b/libs/dagrun/dagrun_test.go @@ -3,7 +3,6 @@ package dagrun import ( "fmt" "slices" - "sort" "sync" "testing" @@ -194,7 +193,7 @@ func runTestCase(t *testing.T, tc testCase, g *Graph, p int) { if tc.seen != nil { assert.Equal(t, tc.seen, seen) } else if tc.seenSorted != nil { - sort.Strings(seen) + slices.Sort(seen) assert.Equal(t, tc.seenSorted, seen) } else { assert.Empty(t, seen) diff --git a/libs/databrickscfg/cfgpickers/warehouses.go b/libs/databrickscfg/cfgpickers/warehouses.go index 73c60dc08e..91fdadaa91 100644 --- a/libs/databrickscfg/cfgpickers/warehouses.go +++ b/libs/databrickscfg/cfgpickers/warehouses.go @@ -1,10 +1,11 @@ package cfgpickers import ( + "cmp" "context" "errors" "fmt" - "sort" + "slices" "strings" "github.com/databricks/cli/libs/cmdio" @@ -86,12 +87,11 @@ func sortWarehousesByState(all []sql.EndpointInfo) []sql.EndpointInfo { sql.StateStopped: 3, sql.StateStopping: 4, } - sort.Slice(warehouses, func(i, j int) bool { - pi, pj := priorities[warehouses[i].State], priorities[warehouses[j].State] - if pi != pj { - return pi < pj + slices.SortFunc(warehouses, func(a, b sql.EndpointInfo) int { + if n := cmp.Compare(priorities[a.State], priorities[b.State]); n != 0 { + return n } - return strings.ToLower(warehouses[i].Name) < strings.ToLower(warehouses[j].Name) + return cmp.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) }) return warehouses @@ -187,13 +187,16 @@ func SelectWarehouse(ctx context.Context, w *databricks.WorkspaceClient, descrip defaultId := warehouses[0].Id // Sort by running state first, then alphabetically for display - sort.Slice(warehouses, func(i, j int) bool { - iRunning := warehouses[i].State == sql.StateRunning - jRunning := warehouses[j].State == sql.StateRunning - if iRunning != jRunning { - return iRunning + slices.SortFunc(warehouses, func(a, b sql.EndpointInfo) int { + aRunning := a.State == sql.StateRunning + bRunning := b.State == sql.StateRunning + if aRunning != bRunning { + if aRunning { + return -1 + } + return 1 } - return strings.ToLower(warehouses[i].Name) < strings.ToLower(warehouses[j].Name) + return cmp.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) }) // Build options for the picker (● = running, ○ = not running) diff --git a/libs/dyn/dynloc/locations.go b/libs/dyn/dynloc/locations.go index 47612a3ce3..ec0d7e7c95 100644 --- a/libs/dyn/dynloc/locations.go +++ b/libs/dyn/dynloc/locations.go @@ -5,7 +5,6 @@ import ( "maps" "path/filepath" "slices" - "sort" "github.com/databricks/cli/libs/dyn" ) @@ -94,7 +93,7 @@ func (l *Locations) registerFileNames(locs []dyn.Location) error { } l.Files = slices.Collect(maps.Values(cache)) - sort.Strings(l.Files) + slices.Sort(l.Files) // Build the file-to-index map. for i, file := range l.Files { diff --git a/libs/dyn/merge/elements_by_key.go b/libs/dyn/merge/elements_by_key.go index 51772da0ae..6bf71d41ae 100644 --- a/libs/dyn/merge/elements_by_key.go +++ b/libs/dyn/merge/elements_by_key.go @@ -1,7 +1,7 @@ package merge import ( - "sort" + "slices" "github.com/databricks/cli/libs/dyn" ) @@ -48,7 +48,7 @@ func (e elementsByKey) doMap(_ dyn.Path, v dyn.Value, mergeFunc func(a, b dyn.Va } if e.sortKeys { - sort.Strings(keys) + slices.Sort(keys) } // Gather resulting elements in natural order. diff --git a/libs/dyn/yamlsaver/saver.go b/libs/dyn/yamlsaver/saver.go index e63cd03b34..4c302e26f0 100644 --- a/libs/dyn/yamlsaver/saver.go +++ b/libs/dyn/yamlsaver/saver.go @@ -1,11 +1,12 @@ package yamlsaver import ( + "cmp" "fmt" "io" "os" "path/filepath" - "sort" + "slices" "strconv" "github.com/databricks/cli/libs/dyn" @@ -79,8 +80,8 @@ func (s *saver) toYamlNodeWithStyle(v dyn.Value, style yaml.Style) (*yaml.Node, // The location is set when we convert API response struct to config.Value representation // See convert.convertMap for details pairs := m.Pairs() - sort.SliceStable(pairs, func(i, j int) bool { - return pairs[i].Value.Location().Line < pairs[j].Value.Location().Line + slices.SortStableFunc(pairs, func(a, b dyn.Pair) int { + return cmp.Compare(a.Value.Location().Line, b.Value.Location().Line) }) var content []*yaml.Node diff --git a/libs/filer/dbfs_client.go b/libs/filer/dbfs_client.go index d7d79207fd..761f279036 100644 --- a/libs/filer/dbfs_client.go +++ b/libs/filer/dbfs_client.go @@ -1,6 +1,7 @@ package filer import ( + "cmp" "context" "errors" "io" @@ -8,7 +9,6 @@ import ( "net/http" "path" "slices" - "sort" "strings" "time" @@ -273,7 +273,7 @@ func (w *DbfsClient) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, e } // Sort by name for parity with os.ReadDir. - sort.Slice(info, func(i, j int) bool { return info[i].Name() < info[j].Name() }) + slices.SortFunc(info, func(a, b fs.DirEntry) int { return cmp.Compare(a.Name(), b.Name()) }) return info, nil } diff --git a/libs/filer/fake_filer.go b/libs/filer/fake_filer.go index f6eb5955ee..954399b8c6 100644 --- a/libs/filer/fake_filer.go +++ b/libs/filer/fake_filer.go @@ -1,12 +1,13 @@ package filer import ( + "cmp" "context" "errors" "io" "io/fs" "path" - "sort" + "slices" "strings" "github.com/databricks/cli/libs/fakefs" @@ -54,7 +55,7 @@ func (f *FakeFiler) ReadDir(ctx context.Context, p string) ([]fs.DirEntry, error out = append(out, fakefs.DirEntry{FileInfo: v}) } - sort.Slice(out, func(i, j int) bool { return out[i].Name() < out[j].Name() }) + slices.SortFunc(out, func(a, b fs.DirEntry) int { return cmp.Compare(a.Name(), b.Name()) }) return out, nil } diff --git a/libs/filer/files_client.go b/libs/filer/files_client.go index 4160b9b209..f996f16fee 100644 --- a/libs/filer/files_client.go +++ b/libs/filer/files_client.go @@ -1,6 +1,7 @@ package filer import ( + "cmp" "context" "errors" "fmt" @@ -10,7 +11,6 @@ import ( "net/url" "path" "slices" - "sort" "strings" "time" @@ -381,7 +381,7 @@ func (w *FilesClient) ReadDir(ctx context.Context, name string) ([]fs.DirEntry, } // Sort by name for parity with os.ReadDir. - sort.Slice(entries, func(i, j int) bool { return entries[i].Name() < entries[j].Name() }) + slices.SortFunc(entries, func(a, b fs.DirEntry) int { return cmp.Compare(a.Name(), b.Name()) }) return entries, nil } diff --git a/libs/filer/workspace_files_client.go b/libs/filer/workspace_files_client.go index eb09d532e3..9f75d3ca2e 100644 --- a/libs/filer/workspace_files_client.go +++ b/libs/filer/workspace_files_client.go @@ -2,6 +2,7 @@ package filer import ( "bytes" + "cmp" "context" "errors" "fmt" @@ -12,7 +13,6 @@ import ( "path" "regexp" "slices" - "sort" "strings" "time" @@ -43,7 +43,7 @@ func wsfsDirEntriesFromObjectInfos(objects []workspace.ObjectInfo) []fs.DirEntry } // Sort by name for parity with os.ReadDir. - sort.Slice(info, func(i, j int) bool { return info[i].Name() < info[j].Name() }) + slices.SortFunc(info, func(a, b fs.DirEntry) int { return cmp.Compare(a.Name(), b.Name()) }) return info } diff --git a/libs/process/opts_test.go b/libs/process/opts_test.go index 15c4f94b01..94838f4a7e 100644 --- a/libs/process/opts_test.go +++ b/libs/process/opts_test.go @@ -3,7 +3,7 @@ package process import ( "os/exec" "runtime" - "sort" + "slices" "testing" "github.com/databricks/cli/internal/testutil" @@ -38,7 +38,7 @@ func TestWorksWithLibsEnv(t *testing.T) { assert.NoError(t, err) vars := cmd.Environ() - sort.Strings(vars) + slices.Sort(vars) assert.GreaterOrEqual(t, len(vars), 2) assert.Equal(t, "CCC=DDD", vars[0]) diff --git a/libs/structs/structdiff/diff.go b/libs/structs/structdiff/diff.go index 7f2a047d24..c63c845563 100644 --- a/libs/structs/structdiff/diff.go +++ b/libs/structs/structdiff/diff.go @@ -4,7 +4,6 @@ import ( "fmt" "reflect" "slices" - "sort" "strings" "github.com/databricks/cli/libs/structs/structaccess" @@ -272,7 +271,7 @@ func diffMapStringKey(ctx *diffContext, path *structpath.PathNode, m1, m2 reflec for s := range keySet { keys = append(keys, s) } - sort.Strings(keys) + slices.Sort(keys) for _, ks := range keys { k := keySet[ks] diff --git a/libs/structs/structwalk/walk.go b/libs/structs/structwalk/walk.go index d5430855fc..352f5f86ca 100644 --- a/libs/structs/structwalk/walk.go +++ b/libs/structs/structwalk/walk.go @@ -4,7 +4,6 @@ import ( "errors" "reflect" "slices" - "sort" "github.com/databricks/cli/libs/structs/structaccess" "github.com/databricks/cli/libs/structs/structpath" @@ -90,7 +89,7 @@ func walkValue(path *structpath.PathNode, val reflect.Value, field *reflect.Stru for _, k := range val.MapKeys() { keys = append(keys, k.String()) } - sort.Strings(keys) + slices.Sort(keys) for _, ks := range keys { v := val.MapIndex(reflect.ValueOf(ks)) node := structpath.NewBracketString(path, ks) diff --git a/libs/sync/dirset.go b/libs/sync/dirset.go index 33b85cb8e1..dc1b819cf5 100644 --- a/libs/sync/dirset.go +++ b/libs/sync/dirset.go @@ -2,7 +2,7 @@ package sync import ( "path" - "sort" + "slices" ) // DirSet is a set of directories. @@ -37,7 +37,7 @@ func (dirset DirSet) Slice() []string { for dir := range dirset { out = append(out, dir) } - sort.Strings(out) + slices.Sort(out) return out } diff --git a/libs/template/renderer.go b/libs/template/renderer.go index 4416211b19..c1403fa071 100644 --- a/libs/template/renderer.go +++ b/libs/template/renderer.go @@ -1,6 +1,7 @@ package template import ( + "cmp" "context" "errors" "fmt" @@ -9,7 +10,6 @@ import ( "path" "regexp" "slices" - "sort" "strings" "text/template" @@ -288,8 +288,8 @@ func (r *renderer) walk() error { return err } // Sort by name to ensure deterministic ordering - sort.Slice(entries, func(i, j int) bool { - return entries[i].Name() < entries[j].Name() + slices.SortFunc(entries, func(a, b fs.DirEntry) int { + return cmp.Compare(a.Name(), b.Name()) }) for _, entry := range entries { if entry.IsDir() { diff --git a/libs/template/writer.go b/libs/template/writer.go index 37e3fec0e7..358731f6a2 100644 --- a/libs/template/writer.go +++ b/libs/template/writer.go @@ -1,9 +1,10 @@ package template import ( + "cmp" "context" "path/filepath" - "sort" + "slices" "strconv" "strings" @@ -198,8 +199,8 @@ func (tmpl *writerWithFullTelemetry) LogTelemetry(ctx context.Context) { } // Sort the arguments by key for deterministic telemetry logging - sort.Slice(args, func(i, j int) bool { - return args[i].Key < args[j].Key + slices.SortFunc(args, func(a, b protos.BundleInitTemplateEnumArg) int { + return cmp.Compare(a.Key, b.Key) }) telemetry.Log(ctx, protos.DatabricksCliLog{ diff --git a/libs/testdiff/replacement.go b/libs/testdiff/replacement.go index e1b4f8ea6d..188a623cc8 100644 --- a/libs/testdiff/replacement.go +++ b/libs/testdiff/replacement.go @@ -1,12 +1,12 @@ package testdiff import ( + "cmp" "encoding/json" "path/filepath" "regexp" "runtime" "slices" - "sort" "strconv" "strings" @@ -51,8 +51,8 @@ func (r *ReplacementsContext) Replace(s string) string { // Sort replacements stably by Order to guarantee deterministic application sequence. // A cloned slice is used to avoid mutating the original order held in the context. repls := slices.Clone(r.Repls) - sort.SliceStable(repls, func(i, j int) bool { - return repls[i].Order < repls[j].Order + slices.SortStableFunc(repls, func(a, b Replacement) int { + return cmp.Compare(a.Order, b.Order) }) for _, repl := range repls { if !repl.Distinct { diff --git a/libs/testserver/jobs.go b/libs/testserver/jobs.go index ca03196b06..15800341de 100644 --- a/libs/testserver/jobs.go +++ b/libs/testserver/jobs.go @@ -1,6 +1,7 @@ package testserver import ( + "cmp" "encoding/json" "errors" "fmt" @@ -8,7 +9,7 @@ import ( "os/exec" "path/filepath" "runtime" - "sort" + "slices" "strconv" "strings" @@ -223,7 +224,7 @@ func (s *FakeWorkspace) JobsList() Response { list = append(list, baseJob) } - sort.Slice(list, func(i, j int) bool { return list[i].JobId < list[j].JobId }) + slices.SortFunc(list, func(a, b jobs.BaseJob) int { return cmp.Compare(a.JobId, b.JobId) }) return Response{Body: jobs.ListJobsResponse{Jobs: list}} } diff --git a/libs/testserver/serving_endpoints.go b/libs/testserver/serving_endpoints.go index c0531d5ef1..49e72d3df2 100644 --- a/libs/testserver/serving_endpoints.go +++ b/libs/testserver/serving_endpoints.go @@ -3,7 +3,7 @@ package testserver import ( "encoding/json" "fmt" - "sort" + "slices" "github.com/databricks/databricks-sdk-go/service/serving" ) @@ -298,7 +298,7 @@ func (s *FakeWorkspace) ServingEndpointPatchTags(req Request, name string) Respo for key := range tagMap { keys = append(keys, key) } - sort.Strings(keys) + slices.Sort(keys) for _, key := range keys { tags = append(tags, serving.EndpointTag{Key: key, Value: tagMap[key]}) } diff --git a/libs/utils/utils.go b/libs/utils/utils.go index 1ffca44f68..6f3d553157 100644 --- a/libs/utils/utils.go +++ b/libs/utils/utils.go @@ -2,7 +2,7 @@ package utils import ( "reflect" - "sort" + "slices" ) func SortedKeys[T any](m map[string]T) []string { @@ -10,7 +10,7 @@ func SortedKeys[T any](m map[string]T) []string { for k := range m { keys = append(keys, k) } - sort.Strings(keys) + slices.Sort(keys) return keys } From 8e43e61c9ae09b5f88aba2d4f890e6d0f8dd38e5 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Mon, 13 Apr 2026 22:51:48 +0200 Subject: [PATCH 2/3] Use `slices.Sorted(maps.Keys(...))` to replace collect-keys-then-sort pattern Go 1.23 added `slices.Sorted` which accepts an iterator and returns a sorted slice. Combined with `maps.Keys`, this replaces the common three-step pattern of allocating a slice, collecting keys, then sorting. This removes the `utils.SortedKeys` helper and two package-local duplicates (`sortedKeys`, `sortKeys`), inlining all call sites with the stdlib one-liner. Co-authored-by: Isaac --- acceptance/acceptance_test.go | 4 ++-- acceptance/internal/config.go | 7 ++----- bundle/artifacts/build.go | 5 +++-- bundle/artifacts/prepare.go | 5 +++-- bundle/config/validate/scripts.go | 5 +++-- bundle/configsync/format.go | 13 +++---------- bundle/configsync/resolve.go | 7 ++----- bundle/direct/bundle_plan.go | 3 +-- bundle/direct/graph.go | 5 +++-- bundle/internal/schema/annotations.go | 8 ++------ bundle/internal/tf/codegen/generator/generator.go | 6 ++++-- bundle/internal/tf/codegen/generator/util.go | 13 ------------- bundle/internal/tf/codegen/generator/walker.go | 5 +++-- bundle/internal/validation/enum.go | 7 ++----- bundle/internal/validation/required.go | 7 ++----- bundle/libraries/remote_path.go | 5 +++-- bundle/libraries/switch_to_patched_wheels.go | 9 +++++---- bundle/libraries/upload.go | 5 +++-- bundle/phases/bind.go | 5 +++-- bundle/tests/include_test.go | 7 ++++--- cmd/bundle/debug/refschema.go | 10 +++++----- cmd/pipelines/deploy.go | 5 +++-- experimental/aitools/cmd/list.go | 7 ++----- experimental/aitools/lib/installer/installer.go | 7 ++----- experimental/aitools/lib/installer/update.go | 14 +++----------- libs/apps/manifest/manifest.go | 15 +++------------ libs/dyn/dynloc/locations.go | 3 +-- libs/dyn/dynvar/resolve.go | 4 ++-- libs/structs/structdiff/diff.go | 7 ++----- libs/sync/diff_test.go | 12 ++++++------ libs/sync/dirset.go | 8 ++------ libs/testserver/serving_endpoints.go | 7 ++----- libs/utils/utils.go | 10 ---------- 33 files changed, 86 insertions(+), 154 deletions(-) delete mode 100644 bundle/internal/tf/codegen/generator/util.go diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index b46ac462b7..4e4d721217 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -16,6 +16,7 @@ import ( "path/filepath" "regexp" "runtime" + "maps" "slices" "strconv" "strings" @@ -31,7 +32,6 @@ import ( "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/testdiff" "github.com/databricks/cli/libs/testserver" - "github.com/databricks/cli/libs/utils" "github.com/stretchr/testify/require" ) @@ -817,7 +817,7 @@ func buildTestEnv(configEnv map[string]string, customEnv []string) []string { env := make([]string, 0, len(configEnv)+len(customEnv)) // Add config.Env first (but skip keys that exist in customEnv) - for _, key := range utils.SortedKeys(configEnv) { + for _, key := range slices.Sorted(maps.Keys(configEnv)) { if hasKey(customEnv, key) { continue } diff --git a/acceptance/internal/config.go b/acceptance/internal/config.go index 48bbd11c3f..10192524e0 100644 --- a/acceptance/internal/config.go +++ b/acceptance/internal/config.go @@ -2,6 +2,7 @@ package internal import ( "hash/fnv" + "maps" "os" "path/filepath" "reflect" @@ -345,11 +346,7 @@ func ExpandEnvMatrix(matrix, exclude map[string][]string, extraVars []string) [] return result } - keys := make([]string, 0, len(filteredMatrix)) - for key := range filteredMatrix { - keys = append(keys, key) - } - slices.Sort(keys) + keys := slices.Sorted(maps.Keys(filteredMatrix)) // Build an expansion of all combinations. // At each step we look at a given key and append each possible value to each diff --git a/bundle/artifacts/build.go b/bundle/artifacts/build.go index 6d89b8c07c..2d1b1e4d74 100644 --- a/bundle/artifacts/build.go +++ b/bundle/artifacts/build.go @@ -3,8 +3,10 @@ package artifacts import ( "context" "fmt" + "maps" "os" "path/filepath" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" @@ -15,7 +17,6 @@ import ( "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/logdiag" "github.com/databricks/cli/libs/patchwheel" - "github.com/databricks/cli/libs/utils" ) func Build() bundle.Mutator { @@ -37,7 +38,7 @@ func (m *build) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { }) } - for _, artifactName := range utils.SortedKeys(b.Config.Artifacts) { + for _, artifactName := range slices.Sorted(maps.Keys(b.Config.Artifacts)) { a := b.Config.Artifacts[artifactName] if a.BuildCommand != "" { diff --git a/bundle/artifacts/prepare.go b/bundle/artifacts/prepare.go index 84e2eefd0e..041669ad11 100644 --- a/bundle/artifacts/prepare.go +++ b/bundle/artifacts/prepare.go @@ -3,8 +3,10 @@ package artifacts import ( "context" "errors" + "maps" "os" "path/filepath" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" @@ -15,7 +17,6 @@ import ( "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/logdiag" "github.com/databricks/cli/libs/python" - "github.com/databricks/cli/libs/utils" ) func Prepare() bundle.Mutator { @@ -34,7 +35,7 @@ func (m *prepare) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics return diag.FromErr(err) } - for _, artifactName := range utils.SortedKeys(b.Config.Artifacts) { + for _, artifactName := range slices.Sorted(maps.Keys(b.Config.Artifacts)) { artifact := b.Config.Artifacts[artifactName] if artifact == nil { l := b.Config.GetLocation("artifacts." + artifactName) diff --git a/bundle/config/validate/scripts.go b/bundle/config/validate/scripts.go index 421ca593cb..04c6045bb4 100644 --- a/bundle/config/validate/scripts.go +++ b/bundle/config/validate/scripts.go @@ -3,12 +3,13 @@ package validate import ( "context" "fmt" + "maps" "regexp" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/utils" ) type validateScripts struct{} @@ -28,7 +29,7 @@ func (f *validateScripts) Apply(ctx context.Context, b *bundle.Bundle) diag.Diag // Sort the scripts to have a deterministic order for the // generated diagnostics. - scriptKeys := utils.SortedKeys(b.Config.Scripts) + scriptKeys := slices.Sorted(maps.Keys(b.Config.Scripts)) for _, k := range scriptKeys { script := b.Config.Scripts[k] diff --git a/bundle/configsync/format.go b/bundle/configsync/format.go index 1ad54db5a1..c6b1278cd3 100644 --- a/bundle/configsync/format.go +++ b/bundle/configsync/format.go @@ -2,6 +2,7 @@ package configsync import ( "fmt" + "maps" "slices" "strings" ) @@ -17,21 +18,13 @@ func FormatTextOutput(changes Changes) string { output.WriteString(fmt.Sprintf("Detected changes in %d resource(s):\n\n", len(changes))) - resourceKeys := make([]string, 0, len(changes)) - for key := range changes { - resourceKeys = append(resourceKeys, key) - } - slices.Sort(resourceKeys) + resourceKeys := slices.Sorted(maps.Keys(changes)) for _, resourceKey := range resourceKeys { resourceChanges := changes[resourceKey] output.WriteString(fmt.Sprintf("Resource: %s\n", resourceKey)) - paths := make([]string, 0, len(resourceChanges)) - for path := range resourceChanges { - paths = append(paths, path) - } - slices.Sort(paths) + paths := slices.Sorted(maps.Keys(resourceChanges)) for _, path := range paths { configChange := resourceChanges[path] diff --git a/bundle/configsync/resolve.go b/bundle/configsync/resolve.go index f6c91a9dfa..ce065d6ca8 100644 --- a/bundle/configsync/resolve.go +++ b/bundle/configsync/resolve.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io/fs" + "maps" "path/filepath" "slices" "strings" @@ -171,11 +172,7 @@ func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes var result []FieldChange targetName := b.Config.Bundle.Target - resourceKeys := make([]string, 0, len(configChanges)) - for resourceKey := range configChanges { - resourceKeys = append(resourceKeys, resourceKey) - } - slices.Sort(resourceKeys) + resourceKeys := slices.Sorted(maps.Keys(configChanges)) for _, resourceKey := range resourceKeys { resourceChanges := configChanges[resourceKey] diff --git a/bundle/direct/bundle_plan.go b/bundle/direct/bundle_plan.go index 10baf64d8a..15ccf2ac4e 100644 --- a/bundle/direct/bundle_plan.go +++ b/bundle/direct/bundle_plan.go @@ -23,7 +23,6 @@ import ( "github.com/databricks/cli/libs/structs/structdiff" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/structs/structvar" - "github.com/databricks/cli/libs/utils" "github.com/databricks/databricks-sdk-go" ) @@ -968,7 +967,7 @@ func (b *DeploymentBundle) getAdapterForKey(resourceKey string) (*dresources.Ada adapter, ok := b.Adapters[group] if !ok { - return nil, fmt.Errorf("resource type %q not supported, available: %s", group, strings.Join(utils.SortedKeys(b.Adapters), ", ")) + return nil, fmt.Errorf("resource type %q not supported, available: %s", group, strings.Join(slices.Sorted(maps.Keys(b.Adapters)), ", ")) } return adapter, nil diff --git a/bundle/direct/graph.go b/bundle/direct/graph.go index 433eb8dc57..386c590f53 100644 --- a/bundle/direct/graph.go +++ b/bundle/direct/graph.go @@ -2,17 +2,18 @@ package direct import ( "fmt" + "maps" + "slices" "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/libs/dagrun" - "github.com/databricks/cli/libs/utils" ) func makeGraph(plan *deployplan.Plan) (*dagrun.Graph, error) { g := dagrun.NewGraph() // Add all nodes first - for _, resourceKey := range utils.SortedKeys(plan.Plan) { + for _, resourceKey := range slices.Sorted(maps.Keys(plan.Plan)) { g.AddNode(resourceKey) } diff --git a/bundle/internal/schema/annotations.go b/bundle/internal/schema/annotations.go index 689d75ae2e..c57926e131 100644 --- a/bundle/internal/schema/annotations.go +++ b/bundle/internal/schema/annotations.go @@ -3,6 +3,7 @@ package main import ( "bytes" "fmt" + "maps" "os" "reflect" "regexp" @@ -183,12 +184,7 @@ func saveYamlWithStyle(outputPath string, annotations annotation.File) error { } func getAlphabeticalOrder[T any](mapping map[string]T) *yamlsaver.Order { - var order []string - for k := range mapping { - order = append(order, k) - } - slices.Sort(order) - return yamlsaver.NewOrder(order) + return yamlsaver.NewOrder(slices.Sorted(maps.Keys(mapping))) } func convertLinksToAbsoluteUrl(s string) string { diff --git a/bundle/internal/tf/codegen/generator/generator.go b/bundle/internal/tf/codegen/generator/generator.go index 47af677c00..37d9a7b7f7 100644 --- a/bundle/internal/tf/codegen/generator/generator.go +++ b/bundle/internal/tf/codegen/generator/generator.go @@ -4,8 +4,10 @@ import ( "context" "fmt" "log" + "maps" "os" "path/filepath" + "slices" "strings" "text/template" @@ -56,7 +58,7 @@ func (r *root) Generate(path string) error { func Run(ctx context.Context, schema *tfjson.ProviderSchema, checksums *schemapkg.ProviderChecksums, path string) error { // Generate types for resources var resources []*namedBlock - for _, k := range sortKeys(schema.ResourceSchemas) { + for _, k := range slices.Sorted(maps.Keys(schema.ResourceSchemas)) { // Skipping all plugin framework struct generation. // TODO: This is a temporary fix, generation should be fixed in the future. if strings.HasSuffix(k, "_pluginframework") { @@ -87,7 +89,7 @@ func Run(ctx context.Context, schema *tfjson.ProviderSchema, checksums *schemapk // Generate types for data sources. var dataSources []*namedBlock - for _, k := range sortKeys(schema.DataSourceSchemas) { + for _, k := range slices.Sorted(maps.Keys(schema.DataSourceSchemas)) { // Skipping all plugin framework struct generation. // TODO: This is a temporary fix, generation should be fixed in the future. if strings.HasSuffix(k, "_pluginframework") { diff --git a/bundle/internal/tf/codegen/generator/util.go b/bundle/internal/tf/codegen/generator/util.go deleted file mode 100644 index 4844cd870e..0000000000 --- a/bundle/internal/tf/codegen/generator/util.go +++ /dev/null @@ -1,13 +0,0 @@ -package generator - -import ( - "maps" - "slices" -) - -// sortKeys returns a sorted copy of the keys in the specified map. -func sortKeys[M ~map[K]V, K string, V any](m M) []K { - keys := slices.Collect(maps.Keys(m)) - slices.Sort(keys) - return keys -} diff --git a/bundle/internal/tf/codegen/generator/walker.go b/bundle/internal/tf/codegen/generator/walker.go index e08490fe52..bdcb325bc3 100644 --- a/bundle/internal/tf/codegen/generator/walker.go +++ b/bundle/internal/tf/codegen/generator/walker.go @@ -2,6 +2,7 @@ package generator import ( "fmt" + "maps" "slices" "strings" @@ -117,7 +118,7 @@ func processAttributeType(typ cty.Type, resourceName, attributePath string) stri } func nestedBlockKeys(block *tfjson.SchemaBlock) []string { - keys := sortKeys(block.NestedBlocks) + keys := slices.Sorted(maps.Keys(block.NestedBlocks)) // Remove TF specific "timeouts" block. if i := slices.Index(keys, "timeouts"); i != -1 { @@ -163,7 +164,7 @@ func (w *walker) walk(block *tfjson.SchemaBlock, name []string) error { } // Declare attributes. - for _, k := range sortKeys(block.Attributes) { + for _, k := range slices.Sorted(maps.Keys(block.Attributes)) { v := block.Attributes[k] // Assert the attribute type is always set. diff --git a/bundle/internal/validation/enum.go b/bundle/internal/validation/enum.go index ca2e821da5..276a3847de 100644 --- a/bundle/internal/validation/enum.go +++ b/bundle/internal/validation/enum.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "go/format" + "maps" "os" "path/filepath" "reflect" @@ -182,11 +183,7 @@ func filterTargetsAndEnvironmentsEnum(patterns map[string][]EnumPatternInfo) map // sortGroupedPatterns sorts patterns within each group and returns them as a sorted slice func sortGroupedPatternsEnum(groupedPatterns map[string][]EnumPatternInfo) [][]EnumPatternInfo { // Get sorted group keys - groupKeys := make([]string, 0, len(groupedPatterns)) - for key := range groupedPatterns { - groupKeys = append(groupKeys, key) - } - slices.Sort(groupKeys) + groupKeys := slices.Sorted(maps.Keys(groupedPatterns)) // Build sorted result result := make([][]EnumPatternInfo, 0, len(groupKeys)) diff --git a/bundle/internal/validation/required.go b/bundle/internal/validation/required.go index 10c0c0eb5d..ee327b4f9c 100644 --- a/bundle/internal/validation/required.go +++ b/bundle/internal/validation/required.go @@ -5,6 +5,7 @@ import ( "cmp" "fmt" "go/format" + "maps" "os" "path/filepath" "reflect" @@ -138,11 +139,7 @@ func filterTargetsAndEnvironments(patterns map[string][]RequiredPatternInfo) map // sortGroupedPatterns sorts patterns within each group and returns them as a sorted slice func sortGroupedPatterns(groupedPatterns map[string][]RequiredPatternInfo) [][]RequiredPatternInfo { // Get sorted group keys - groupKeys := make([]string, 0, len(groupedPatterns)) - for key := range groupedPatterns { - groupKeys = append(groupKeys, key) - } - slices.Sort(groupKeys) + groupKeys := slices.Sorted(maps.Keys(groupedPatterns)) // Build sorted result result := make([][]RequiredPatternInfo, 0, len(groupKeys)) diff --git a/bundle/libraries/remote_path.go b/bundle/libraries/remote_path.go index d24387653a..22784a6335 100644 --- a/bundle/libraries/remote_path.go +++ b/bundle/libraries/remote_path.go @@ -3,13 +3,14 @@ package libraries import ( "context" "fmt" + "maps" "path" "path/filepath" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/utils" ) // ReplaceWithRemotePath updates all the libraries paths to point to the remote location @@ -25,7 +26,7 @@ func ReplaceWithRemotePath(ctx context.Context, b *bundle.Bundle) (map[string][] return nil, diag.FromErr(err) } - sources := utils.SortedKeys(libs) + sources := slices.Sorted(maps.Keys(libs)) // Update all the config paths to point to the uploaded location err = b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { diff --git a/bundle/libraries/switch_to_patched_wheels.go b/bundle/libraries/switch_to_patched_wheels.go index d7f442bb58..0a9d184604 100644 --- a/bundle/libraries/switch_to_patched_wheels.go +++ b/bundle/libraries/switch_to_patched_wheels.go @@ -2,13 +2,14 @@ package libraries import ( "context" + "maps" "path/filepath" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/log" - "github.com/databricks/cli/libs/utils" ) type switchToPatchedWheels struct{} @@ -35,7 +36,7 @@ func (c switchToPatchedWheels) Apply(ctx context.Context, b *bundle.Bundle) diag log.Debugf(ctx, "Updating resources.jobs.%s.task[%d].libraries[%d].whl from %s to %s", jobName, taskInd, libInd, lib.Whl, repl) job.Tasks[taskInd].Libraries[libInd].Whl = repl } else { - log.Debugf(ctx, "Not updating resources.jobs.%s.task[%d].libraries[%d].whl from %s. Available replacements: %v", jobName, taskInd, libInd, lib.Whl, utils.SortedKeys(replacements)) + log.Debugf(ctx, "Not updating resources.jobs.%s.task[%d].libraries[%d].whl from %s. Available replacements: %v", jobName, taskInd, libInd, lib.Whl, slices.Sorted(maps.Keys(replacements))) } } @@ -49,7 +50,7 @@ func (c switchToPatchedWheels) Apply(ctx context.Context, b *bundle.Bundle) diag log.Debugf(ctx, "Updating resources.jobs.%s.task[%d].for_each_task.task.libraries[%d].whl from %s to %s", jobName, taskInd, libInd, lib.Whl, repl) foreachptr.Task.Libraries[libInd].Whl = repl } else { - log.Debugf(ctx, "Not updating resources.jobs.%s.task[%d].for_each_task.task.libraries[%d].whl from %s. Available replacements: %v", jobName, taskInd, libInd, lib.Whl, utils.SortedKeys(replacements)) + log.Debugf(ctx, "Not updating resources.jobs.%s.task[%d].for_each_task.task.libraries[%d].whl from %s. Available replacements: %v", jobName, taskInd, libInd, lib.Whl, slices.Sorted(maps.Keys(replacements))) } } } @@ -67,7 +68,7 @@ func (c switchToPatchedWheels) Apply(ctx context.Context, b *bundle.Bundle) diag log.Debugf(ctx, "Updating resources.jobs.%s.environments[%d].spec.dependencies[%d] from %s to %s", jobName, envInd, depInd, dep, repl) specptr.Dependencies[depInd] = repl } else { - log.Debugf(ctx, "Not updating resources.jobs.%s.environments[%d].spec.dependencies[%d] from %s. Available replacements: %v", jobName, envInd, depInd, dep, utils.SortedKeys(replacements)) + log.Debugf(ctx, "Not updating resources.jobs.%s.environments[%d].spec.dependencies[%d] from %s. Available replacements: %v", jobName, envInd, depInd, dep, slices.Sorted(maps.Keys(replacements))) } } } diff --git a/bundle/libraries/upload.go b/bundle/libraries/upload.go index 590adda4ff..cb3ff2faf0 100644 --- a/bundle/libraries/upload.go +++ b/bundle/libraries/upload.go @@ -4,8 +4,10 @@ import ( "context" "errors" "fmt" + "maps" "os" "path/filepath" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/cmdio" @@ -13,7 +15,6 @@ import ( "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/filer" "github.com/databricks/cli/libs/log" - "github.com/databricks/cli/libs/utils" "golang.org/x/sync/errgroup" ) @@ -58,7 +59,7 @@ func (u *upload) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { u.client = client } - sources := utils.SortedKeys(u.libs) + sources := slices.Sorted(maps.Keys(u.libs)) errs, errCtx := errgroup.WithContext(ctx) errs.SetLimit(maxFilesRequestsInFlight) diff --git a/bundle/phases/bind.go b/bundle/phases/bind.go index 0435d19a6d..fbed0aaef1 100644 --- a/bundle/phases/bind.go +++ b/bundle/phases/bind.go @@ -5,6 +5,8 @@ import ( "encoding/json" "errors" "fmt" + "maps" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/engine" @@ -15,7 +17,6 @@ import ( "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/logdiag" - "github.com/databricks/cli/libs/utils" ) func Bind(ctx context.Context, b *bundle.Bundle, opts *terraform.BindOptions, engine engine.EngineType) { @@ -55,7 +56,7 @@ func Bind(ctx context.Context, b *bundle.Bundle, opts *terraform.BindOptions, en if result.Plan != nil { if entry, ok := result.Plan.Plan[resourceKey]; ok && entry != nil && len(entry.Changes) > 0 { cmdio.LogString(ctx, "\nChanges detected:") - for _, field := range utils.SortedKeys(entry.Changes) { + for _, field := range slices.Sorted(maps.Keys(entry.Changes)) { change := entry.Changes[field] if change.Action != deployplan.Skip { cmdio.LogString(ctx, fmt.Sprintf(" ~ %s: %v -> %v", field, jsonDump(ctx, change.Remote, field), jsonDump(ctx, change.New, field))) diff --git a/bundle/tests/include_test.go b/bundle/tests/include_test.go index c5ad2d58a1..50bf177fdf 100644 --- a/bundle/tests/include_test.go +++ b/bundle/tests/include_test.go @@ -1,13 +1,14 @@ package config_tests import ( + "maps" "path/filepath" + "slices" "testing" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/phases" "github.com/databricks/cli/libs/logdiag" - "github.com/databricks/cli/libs/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -26,7 +27,7 @@ func TestIncludeInvalid(t *testing.T) { func TestIncludeWithGlob(t *testing.T) { b := load(t, "./include_with_glob") - keys := utils.SortedKeys(b.Config.Resources.Jobs) + keys := slices.Sorted(maps.Keys(b.Config.Resources.Jobs)) assert.Equal(t, []string{"my_job"}, keys) job := b.Config.Resources.Jobs["my_job"] @@ -46,7 +47,7 @@ func TestIncludeForMultipleMatches(t *testing.T) { b := load(t, "./include_multiple") // Test that both jobs were loaded. - keys := utils.SortedKeys(b.Config.Resources.Jobs) + keys := slices.Sorted(maps.Keys(b.Config.Resources.Jobs)) assert.Equal(t, []string{"my_first_job", "my_second_job"}, keys) first := b.Config.Resources.Jobs["my_first_job"] diff --git a/cmd/bundle/debug/refschema.go b/cmd/bundle/debug/refschema.go index 0b7b164e86..4ba1ae999f 100644 --- a/cmd/bundle/debug/refschema.go +++ b/cmd/bundle/debug/refschema.go @@ -3,6 +3,7 @@ package debug import ( "fmt" "io" + "maps" "reflect" "slices" "strings" @@ -11,7 +12,6 @@ import ( "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/structs/structwalk" - "github.com/databricks/cli/libs/utils" "github.com/spf13/cobra" ) @@ -42,7 +42,7 @@ func dumpRemoteSchemas(out io.Writer) error { return fmt.Errorf("failed to initialize adapters: %w", err) } - for _, resourceName := range utils.SortedKeys(adapters) { + for _, resourceName := range slices.Sorted(maps.Keys(adapters)) { adapter := adapters[resourceName] var resourcePrefix string @@ -100,9 +100,9 @@ func dumpRemoteSchemas(out io.Writer) error { } var lines []string - for _, p := range utils.SortedKeys(pathTypes) { + for _, p := range slices.Sorted(maps.Keys(pathTypes)) { byType := pathTypes[p] - for _, t := range utils.SortedKeys(byType) { + for _, t := range slices.Sorted(maps.Keys(byType)) { info := formatTags(byType[t]) sep := "." if strings.HasPrefix(p, "[") { @@ -125,5 +125,5 @@ func formatTags(sources map[string]struct{}) string { if len(sources) == 3 { return "ALL" } - return strings.Join(utils.SortedKeys(sources), "\t") + return strings.Join(slices.Sorted(maps.Keys(sources)), "\t") } diff --git a/cmd/pipelines/deploy.go b/cmd/pipelines/deploy.go index d966a962d3..2c8d27de14 100644 --- a/cmd/pipelines/deploy.go +++ b/cmd/pipelines/deploy.go @@ -4,6 +4,8 @@ package pipelines import ( "fmt" + "maps" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/mutator" @@ -11,7 +13,6 @@ import ( "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/logdiag" - libsutils "github.com/databricks/cli/libs/utils" "github.com/spf13/cobra" ) @@ -63,7 +64,7 @@ func deployCommand() *cobra.Command { } for _, group := range b.Config.Resources.AllResources() { - for _, resourceKey := range libsutils.SortedKeys(group.Resources) { + for _, resourceKey := range slices.Sorted(maps.Keys(group.Resources)) { resource := group.Resources[resourceKey] cmdio.LogString(ctx, fmt.Sprintf("View your %s %s here: %s", resource.ResourceDescription().SingularName, resourceKey, resource.GetURL())) } diff --git a/experimental/aitools/cmd/list.go b/experimental/aitools/cmd/list.go index 7c7144bd03..1be1538c9a 100644 --- a/experimental/aitools/cmd/list.go +++ b/experimental/aitools/cmd/list.go @@ -3,6 +3,7 @@ package aitools import ( "errors" "fmt" + "maps" "slices" "strings" "text/tabwriter" @@ -80,11 +81,7 @@ func defaultListSkills(cmd *cobra.Command, scope string) error { } // Build sorted list of skill names. - names := make([]string, 0, len(manifest.Skills)) - for name := range manifest.Skills { - names = append(names, name) - } - slices.Sort(names) + names := slices.Sorted(maps.Keys(manifest.Skills)) version := strings.TrimPrefix(ref, "v") cmdio.LogString(ctx, "Available skills (v"+version+"):") diff --git a/experimental/aitools/lib/installer/installer.go b/experimental/aitools/lib/installer/installer.go index 4a3f363254..8b10f0b9aa 100644 --- a/experimental/aitools/lib/installer/installer.go +++ b/experimental/aitools/lib/installer/installer.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "maps" "net/http" "os" "path/filepath" @@ -160,11 +161,7 @@ func InstallSkillsForAgents(ctx context.Context, src ManifestSource, targetAgent } // Install each skill in sorted order for determinism. - skillNames := make([]string, 0, len(targetSkills)) - for name := range targetSkills { - skillNames = append(skillNames, name) - } - slices.Sort(skillNames) + skillNames := slices.Sorted(maps.Keys(targetSkills)) for _, name := range skillNames { meta := targetSkills[name] diff --git a/experimental/aitools/lib/installer/update.go b/experimental/aitools/lib/installer/update.go index 9e5ea2ddaf..e2eeace4f0 100644 --- a/experimental/aitools/lib/installer/update.go +++ b/experimental/aitools/lib/installer/update.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "maps" "os" "path/filepath" "slices" @@ -84,7 +85,7 @@ func UpdateSkills(ctx context.Context, src ManifestSource, targetAgents []*agent if state.Release == latestTag && !opts.Force { cmdio.LogString(ctx, "Already up to date.") - return &UpdateResult{Unchanged: sortedKeys(state.Skills)}, nil + return &UpdateResult{Unchanged: slices.Sorted(maps.Keys(state.Skills))}, nil } manifest, err := src.FetchManifest(ctx, latestTag) @@ -105,7 +106,7 @@ func UpdateSkills(ctx context.Context, src ManifestSource, targetAgents []*agent isDev := strings.HasPrefix(cliVersion, build.DefaultSemver) // Sort skill names for deterministic output. - names := sortedKeys(skillSet) + names := slices.Sorted(maps.Keys(skillSet)) for _, name := range names { meta, inManifest := manifest.Skills[name] @@ -230,15 +231,6 @@ func hasLegacyInstall(ctx context.Context, globalDir string) bool { return hasSkillsOnDisk(filepath.Join(homeDir, ".databricks", "agent-skills")) } -// sortedKeys returns the keys of a map sorted alphabetically. -func sortedKeys[V any](m map[string]V) []string { - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - slices.Sort(keys) - return keys -} // FormatUpdateResult returns a human-readable summary of the update result. // When check is true, output uses "Would update/add" instead of "Updated/Added". diff --git a/libs/apps/manifest/manifest.go b/libs/apps/manifest/manifest.go index 8d3cd12e27..54423ada2e 100644 --- a/libs/apps/manifest/manifest.go +++ b/libs/apps/manifest/manifest.go @@ -4,6 +4,7 @@ import ( "cmp" "encoding/json" "fmt" + "maps" "os" "path/filepath" "slices" @@ -55,12 +56,7 @@ func (r Resource) HasFields() bool { // FieldNames returns the field names in sorted order for deterministic iteration. func (r Resource) FieldNames() []string { - names := make([]string, 0, len(r.Fields)) - for k := range r.Fields { - names = append(names, k) - } - slices.Sort(names) - return names + return slices.Sorted(maps.Keys(r.Fields)) } // Resources defines the required and optional resources for a plugin. @@ -171,12 +167,7 @@ func (m *Manifest) GetPluginByName(name string) *Plugin { // GetPluginNames returns a list of all plugin names. func (m *Manifest) GetPluginNames() []string { - names := make([]string, 0, len(m.Plugins)) - for name := range m.Plugins { - names = append(names, name) - } - slices.Sort(names) - return names + return slices.Sorted(maps.Keys(m.Plugins)) } // ValidatePluginNames checks that all provided plugin names exist in the manifest. diff --git a/libs/dyn/dynloc/locations.go b/libs/dyn/dynloc/locations.go index ec0d7e7c95..5c8e22f093 100644 --- a/libs/dyn/dynloc/locations.go +++ b/libs/dyn/dynloc/locations.go @@ -92,8 +92,7 @@ func (l *Locations) registerFileNames(locs []dyn.Location) error { cache[loc.File] = out } - l.Files = slices.Collect(maps.Values(cache)) - slices.Sort(l.Files) + l.Files = slices.Sorted(maps.Values(cache)) // Build the file-to-index map. for i, file := range l.Files { diff --git a/libs/dyn/dynvar/resolve.go b/libs/dyn/dynvar/resolve.go index b1366d93bb..1cfcc028b7 100644 --- a/libs/dyn/dynvar/resolve.go +++ b/libs/dyn/dynvar/resolve.go @@ -3,11 +3,11 @@ package dynvar import ( "errors" "fmt" + "maps" "slices" "strings" "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/utils" ) // Resolve resolves variable references in the given input value using the provided lookup function. @@ -101,7 +101,7 @@ func (r *resolver) resolveVariableReferences() (err error) { // We sort the keys here to ensure that we always resolve the same variable reference first. // This is done such that the cycle detection error is deterministic. If we did not do this, // we could enter the cycle at any point in the cycle and return varying errors. - keys := utils.SortedKeys(r.refs) + keys := slices.Sorted(maps.Keys(r.refs)) for _, key := range keys { v, err := r.resolveRef(r.refs[key], []string{key}) if err != nil { diff --git a/libs/structs/structdiff/diff.go b/libs/structs/structdiff/diff.go index c63c845563..61c909dfd1 100644 --- a/libs/structs/structdiff/diff.go +++ b/libs/structs/structdiff/diff.go @@ -2,6 +2,7 @@ package structdiff import ( "fmt" + "maps" "reflect" "slices" "strings" @@ -267,11 +268,7 @@ func diffMapStringKey(ctx *diffContext, path *structpath.PathNode, m1, m2 reflec keySet[ks] = k } - var keys []string - for s := range keySet { - keys = append(keys, s) - } - slices.Sort(keys) + keys := slices.Sorted(maps.Keys(keySet)) for _, ks := range keys { k := keySet[ks] diff --git a/libs/sync/diff_test.go b/libs/sync/diff_test.go index 94b6cc3754..d48223f775 100644 --- a/libs/sync/diff_test.go +++ b/libs/sync/diff_test.go @@ -87,7 +87,7 @@ func TestDiffComputationForRemovedFiles(t *testing.T) { expected := diff{ delete: []string{"foo/a/b/c"}, rmdir: []string{"foo", "foo/a", "foo/a/b"}, - mkdir: []string{}, + mkdir: nil, put: []string{}, } assert.Equal(t, expected, computeDiff(after, before)) @@ -121,8 +121,8 @@ func TestDiffComputationWhenRemoteNameIsChanged(t *testing.T) { expected := diff{ delete: []string{"foo/a/b/c"}, - rmdir: []string{}, - mkdir: []string{}, + rmdir: nil, + mkdir: nil, put: []string{"foo/a/b/c.py"}, } assert.Equal(t, expected, computeDiff(after, before)) @@ -143,7 +143,7 @@ func TestDiffComputationForNewFiles(t *testing.T) { expected := diff{ delete: []string{}, - rmdir: []string{}, + rmdir: nil, mkdir: []string{"foo", "foo/a", "foo/a/b"}, put: []string{"foo/a/b/c.py"}, } @@ -178,8 +178,8 @@ func TestDiffComputationForUpdatedFiles(t *testing.T) { expected := diff{ delete: []string{}, - rmdir: []string{}, - mkdir: []string{}, + rmdir: nil, + mkdir: nil, put: []string{"foo/a/b/c"}, } assert.Equal(t, expected, computeDiff(after, before)) diff --git a/libs/sync/dirset.go b/libs/sync/dirset.go index dc1b819cf5..c6d6622f41 100644 --- a/libs/sync/dirset.go +++ b/libs/sync/dirset.go @@ -1,6 +1,7 @@ package sync import ( + "maps" "path" "slices" ) @@ -33,12 +34,7 @@ func MakeDirSet(files []string) DirSet { // Slice returns a sorted copy of the dirset elements as a slice. func (dirset DirSet) Slice() []string { - out := make([]string, 0, len(dirset)) - for dir := range dirset { - out = append(out, dir) - } - slices.Sort(out) - return out + return slices.Sorted(maps.Keys(dirset)) } // Remove returns the set difference of two DirSets. diff --git a/libs/testserver/serving_endpoints.go b/libs/testserver/serving_endpoints.go index 49e72d3df2..cfe59e448e 100644 --- a/libs/testserver/serving_endpoints.go +++ b/libs/testserver/serving_endpoints.go @@ -3,6 +3,7 @@ package testserver import ( "encoding/json" "fmt" + "maps" "slices" "github.com/databricks/databricks-sdk-go/service/serving" @@ -294,11 +295,7 @@ func (s *FakeWorkspace) ServingEndpointPatchTags(req Request, name string) Respo // Convert back to slice sorted by key for stable output tags := make([]serving.EndpointTag, 0, len(tagMap)) - keys := make([]string, 0, len(tagMap)) - for key := range tagMap { - keys = append(keys, key) - } - slices.Sort(keys) + keys := slices.Sorted(maps.Keys(tagMap)) for _, key := range keys { tags = append(tags, serving.EndpointTag{Key: key, Value: tagMap[key]}) } diff --git a/libs/utils/utils.go b/libs/utils/utils.go index 6f3d553157..5d4dc62df9 100644 --- a/libs/utils/utils.go +++ b/libs/utils/utils.go @@ -2,18 +2,8 @@ package utils import ( "reflect" - "slices" ) -func SortedKeys[T any](m map[string]T) []string { - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - slices.Sort(keys) - return keys -} - // FilterFields creates a new slice with fields present only in the provided type, // excluding any fields specified in the excludeFields list. // We must use that when copying structs because JSON marshaller in SDK crashes if it sees unknown field. From 9f2e6e935ed695c98996437cf346c5f2dfb53bf6 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Mon, 13 Apr 2026 23:11:48 +0200 Subject: [PATCH 3/3] Fix import ordering and remove stray blank line Co-authored-by: Isaac --- acceptance/acceptance_test.go | 2 +- experimental/aitools/lib/installer/update.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index 4e4d721217..19df26d2ea 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -10,13 +10,13 @@ import ( "flag" "fmt" "io" + "maps" "net/http" "os" "os/exec" "path/filepath" "regexp" "runtime" - "maps" "slices" "strconv" "strings" diff --git a/experimental/aitools/lib/installer/update.go b/experimental/aitools/lib/installer/update.go index e2eeace4f0..663ad5e908 100644 --- a/experimental/aitools/lib/installer/update.go +++ b/experimental/aitools/lib/installer/update.go @@ -231,7 +231,6 @@ func hasLegacyInstall(ctx context.Context, globalDir string) bool { return hasSkillsOnDisk(filepath.Join(homeDir, ".databricks", "agent-skills")) } - // FormatUpdateResult returns a human-readable summary of the update result. // When check is true, output uses "Would update/add" instead of "Updated/Added". func FormatUpdateResult(result *UpdateResult, check bool) string {