Skip to content

Commit 99e0810

Browse files
committed
feat(reshim): add detailed output with per-runtime progress
- Show 'Regenerating shims for <Runtime>...' for each runtime - Display summary table with runtime names and their shims - Show total count of shims and runtimes created - Add RehashWithCallback for progress reporting
1 parent 2acb78f commit 99e0810

3 files changed

Lines changed: 128 additions & 17 deletions

File tree

src/cmd/reshim.go

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package cmd
22

33
import (
4+
"fmt"
5+
"sort"
6+
"strings"
7+
48
"github.com/dtvem/dtvem/src/internal/config"
9+
"github.com/dtvem/dtvem/src/internal/runtime"
510
"github.com/dtvem/dtvem/src/internal/shim"
11+
"github.com/dtvem/dtvem/src/internal/tui"
612
"github.com/dtvem/dtvem/src/internal/ui"
713
"github.com/spf13/cobra"
814
)
@@ -18,8 +24,6 @@ Run this command after installing new versions or if shims become corrupted.
1824
Example:
1925
dtvem reshim`,
2026
Run: func(cmd *cobra.Command, args []string) {
21-
ui.Header("Regenerating shims...")
22-
2327
// Ensure directories exist
2428
if err := config.EnsureDirectories(); err != nil {
2529
ui.Error("Failed to create directories: %v", err)
@@ -34,17 +38,59 @@ Example:
3438
return
3539
}
3640

37-
// Regenerate shims with spinner
38-
spinner := ui.NewSpinner("Regenerating shims...")
39-
spinner.Start()
41+
// Collect display names as we process
42+
displayNames := make(map[string]string)
4043

41-
if err := manager.Rehash(); err != nil {
42-
spinner.Error("Failed to regenerate shims")
44+
// Regenerate shims with per-runtime progress
45+
result, err := manager.RehashWithCallback(func(runtimeName, displayName string) {
46+
displayNames[runtimeName] = displayName
47+
ui.Info("Regenerating shims for %s...", displayName)
48+
})
49+
50+
if err != nil {
51+
fmt.Println()
4352
ui.Error("%v", err)
4453
return
4554
}
4655

47-
spinner.Success("Shims regenerated successfully")
56+
fmt.Println()
57+
58+
// Display results in a table
59+
table := tui.NewTable("Runtime", "Shims")
60+
table.SetTitle("Shims Created")
61+
62+
// Sort runtime names for consistent output
63+
runtimeNames := make([]string, 0, len(result.ShimsByRuntime))
64+
for name := range result.ShimsByRuntime {
65+
runtimeNames = append(runtimeNames, name)
66+
}
67+
sort.Strings(runtimeNames)
68+
69+
for _, runtimeName := range runtimeNames {
70+
shims := result.ShimsByRuntime[runtimeName]
71+
sort.Strings(shims)
72+
73+
// Get display name from provider
74+
displayName := runtimeName
75+
if provider, err := runtime.Get(runtimeName); err == nil {
76+
displayName = provider.DisplayName()
77+
} else if len(displayName) > 0 {
78+
// Capitalize first letter as fallback
79+
displayName = strings.ToUpper(displayName[:1]) + displayName[1:]
80+
}
81+
82+
// Format shims list (truncate if too long)
83+
shimList := strings.Join(shims, ", ")
84+
if len(shimList) > 60 {
85+
shimList = shimList[:57] + "..."
86+
}
87+
88+
table.AddRow(displayName, shimList)
89+
}
90+
91+
fmt.Println(table.Render())
92+
fmt.Println()
93+
ui.Success("Created %d shims for %d runtime(s)", result.TotalShims, len(result.ShimsByRuntime))
4894
},
4995
}
5096

src/cmd/uninstall.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Examples:
108108
shimSpinner.Warning("Could not regenerate shims")
109109
ui.Warning("You may need to run 'dtvem reshim' manually")
110110
} else {
111-
if err := manager.Rehash(); err != nil {
111+
if _, err := manager.Rehash(); err != nil {
112112
shimSpinner.Warning("Could not regenerate shims")
113113
ui.Warning("You may need to run 'dtvem reshim' manually")
114114
} else {

src/internal/shim/manager.go

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,42 @@ func (m *Manager) ListShims() ([]string, error) {
130130
return shims, nil
131131
}
132132

133-
// Rehash regenerates all shims by scanning installed versions
134-
func (m *Manager) Rehash() error {
133+
// RehashResult contains the results of a reshim operation
134+
type RehashResult struct {
135+
// ShimsByRuntime maps runtime names to their shim names
136+
ShimsByRuntime map[string][]string
137+
// TotalShims is the total number of shims created
138+
TotalShims int
139+
}
140+
141+
// RuntimeShimInfo contains shim information for a single runtime
142+
type RuntimeShimInfo struct {
143+
RuntimeName string
144+
Shims []string
145+
}
146+
147+
// RehashCallback is called before processing each runtime
148+
// runtimeName is the internal name, displayName is the user-friendly name
149+
type RehashCallback func(runtimeName, displayName string)
150+
151+
// RehashWithCallback regenerates all shims, calling the callback before each runtime
152+
func (m *Manager) RehashWithCallback(callback RehashCallback) (*RehashResult, error) {
135153
paths := config.DefaultPaths()
136154
versionsDir := paths.Versions
137155

138156
// Read all runtime directories
139157
entries, err := os.ReadDir(versionsDir)
140158
if err != nil {
141159
if os.IsNotExist(err) {
142-
return fmt.Errorf("no versions directory found - no runtimes installed yet")
160+
return nil, fmt.Errorf("no versions directory found - no runtimes installed yet")
143161
}
144-
return fmt.Errorf("failed to read versions directory: %w", err)
162+
return nil, fmt.Errorf("failed to read versions directory: %w", err)
145163
}
146164

147165
// Collect shim-to-runtime mappings (shim name -> runtime name)
148166
shimMap := make(ShimMap)
167+
// Also track shims by runtime for reporting
168+
shimsByRuntime := make(map[string][]string)
149169

150170
for _, entry := range entries {
151171
if !entry.IsDir() {
@@ -161,6 +181,29 @@ func (m *Manager) Rehash() error {
161181
continue
162182
}
163183

184+
// Skip if no versions installed
185+
hasVersions := false
186+
for _, ve := range versionEntries {
187+
if ve.IsDir() {
188+
hasVersions = true
189+
break
190+
}
191+
}
192+
if !hasVersions {
193+
continue
194+
}
195+
196+
// Get display name from provider
197+
displayName := runtimeName
198+
if provider, err := runtimepkg.Get(runtimeName); err == nil {
199+
displayName = provider.DisplayName()
200+
}
201+
202+
// Call the callback before processing this runtime
203+
if callback != nil {
204+
callback(runtimeName, displayName)
205+
}
206+
164207
// For each installed version, scan for executables
165208
for _, versionEntry := range versionEntries {
166209
if !versionEntry.IsDir() {
@@ -173,13 +216,15 @@ func (m *Manager) Rehash() error {
173216
coreShims := RuntimeShims(runtimeName)
174217
for _, shimName := range coreShims {
175218
shimMap[shimName] = runtimeName
219+
shimsByRuntime[runtimeName] = appendUnique(shimsByRuntime[runtimeName], shimName)
176220
}
177221

178222
// Then, scan bin directory for globally installed packages
179223
binDir := filepath.Join(versionDir, "bin")
180224
if execs, err := findExecutables(binDir); err == nil {
181225
for _, exec := range execs {
182226
shimMap[exec] = runtimeName
227+
shimsByRuntime[runtimeName] = appendUnique(shimsByRuntime[runtimeName], exec)
183228
}
184229
}
185230

@@ -189,36 +234,56 @@ func (m *Manager) Rehash() error {
189234
if execs, err := findExecutables(versionDir); err == nil {
190235
for _, exec := range execs {
191236
shimMap[exec] = runtimeName
237+
shimsByRuntime[runtimeName] = appendUnique(shimsByRuntime[runtimeName], exec)
192238
}
193239
}
194240
// Check Scripts directory for Python pip packages
195241
scriptsDir := filepath.Join(versionDir, "Scripts")
196242
if execs, err := findExecutables(scriptsDir); err == nil {
197243
for _, exec := range execs {
198244
shimMap[exec] = runtimeName
245+
shimsByRuntime[runtimeName] = appendUnique(shimsByRuntime[runtimeName], exec)
199246
}
200247
}
201248
}
202249
}
203250
}
204251

205252
if len(shimMap) == 0 {
206-
return fmt.Errorf("no runtimes installed - nothing to reshim")
253+
return nil, fmt.Errorf("no runtimes installed - nothing to reshim")
207254
}
208255

209256
// Save the shim map cache
210257
if err := SaveShimMap(shimMap); err != nil {
211-
return fmt.Errorf("failed to save shim map cache: %w", err)
258+
return nil, fmt.Errorf("failed to save shim map cache: %w", err)
212259
}
213260

214261
// Create all shims
215262
for shimName := range shimMap {
216263
if err := m.CreateShim(shimName); err != nil {
217-
return err
264+
return nil, err
218265
}
219266
}
220267

221-
return nil
268+
return &RehashResult{
269+
ShimsByRuntime: shimsByRuntime,
270+
TotalShims: len(shimMap),
271+
}, nil
272+
}
273+
274+
// Rehash regenerates all shims by scanning installed versions (no progress callback)
275+
func (m *Manager) Rehash() (*RehashResult, error) {
276+
return m.RehashWithCallback(nil)
277+
}
278+
279+
// appendUnique appends a string to a slice only if it's not already present
280+
func appendUnique(slice []string, s string) []string {
281+
for _, existing := range slice {
282+
if existing == s {
283+
return slice
284+
}
285+
}
286+
return append(slice, s)
222287
}
223288

224289
// copyFile copies a file from src to dst

0 commit comments

Comments
 (0)