From 93c64553a0a3d33bcc7e36ae5e3884fcc755ba15 Mon Sep 17 00:00:00 2001 From: "Calvin A. Allen" Date: Fri, 12 Dec 2025 16:41:16 -0500 Subject: [PATCH] feat(install): auto-set global version when installing first version When installing the first version of a runtime (no global version configured), automatically set it as the global version. This improves the first-time user experience by eliminating the need to manually run `dtvem global ` after the first install. - Adds `autoSetGlobalIfNeeded()` helper function - Applies to both single installs and bulk installs - Shows informational message: "Set as global version (first install)" - Does not change existing global version if one is already set Fixes #112 --- src/cmd/install.go | 24 +++++++++ src/cmd/install_test.go | 112 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 src/cmd/install_test.go diff --git a/src/cmd/install.go b/src/cmd/install.go index c738417..10f6cde 100644 --- a/src/cmd/install.go +++ b/src/cmd/install.go @@ -70,6 +70,28 @@ func installSingle(runtimeName, version string) { } ui.Success("Successfully installed %s %s", provider.DisplayName(), version) + + // Auto-set global version if no global version is currently configured + autoSetGlobalIfNeeded(provider, version) +} + +// autoSetGlobalIfNeeded sets the installed version as global if no global version exists +func autoSetGlobalIfNeeded(provider runtime.Provider, version string) { + currentGlobal, err := provider.GlobalVersion() + if err != nil || currentGlobal != "" { + // Either an error occurred or a global version is already set + ui.Debug("Global version check: current=%q, err=%v", currentGlobal, err) + return + } + + // No global version configured, auto-set it + if err := provider.SetGlobalVersion(version); err != nil { + ui.Debug("Failed to auto-set global version: %v", err) + ui.Warning("Could not auto-set global version: %v", err) + return + } + + ui.Info("Set as global version (first install)") } // installBulk installs all runtimes from .dtvem/runtimes.json @@ -173,6 +195,8 @@ func executeInstalls(tasks []installTask) (success, failures int, failureList [] } else { ui.Success("Installed %s %s", task.provider.DisplayName(), task.version) success++ + // Auto-set global version if needed + autoSetGlobalIfNeeded(task.provider, task.version) } } diff --git a/src/cmd/install_test.go b/src/cmd/install_test.go new file mode 100644 index 0000000..2ecc0f6 --- /dev/null +++ b/src/cmd/install_test.go @@ -0,0 +1,112 @@ +package cmd + +import ( + "testing" + + "github.com/dtvem/dtvem/src/internal/runtime" +) + +// mockProvider implements runtime.Provider for testing +type mockProvider struct { + name string + displayName string + globalVersion string + globalSetError error + setGlobalCalls []string +} + +func (m *mockProvider) Name() string { return m.name } +func (m *mockProvider) DisplayName() string { return m.displayName } +func (m *mockProvider) Shims() []string { return []string{m.name} } +func (m *mockProvider) ExecutablePath(version string) (string, error) { return "", nil } +func (m *mockProvider) IsInstalled(version string) (bool, error) { return false, nil } +func (m *mockProvider) ShouldReshimAfter(shimName string, args []string) bool { return false } +func (m *mockProvider) Install(version string) error { return nil } +func (m *mockProvider) Uninstall(version string) error { return nil } +func (m *mockProvider) ListInstalled() ([]runtime.InstalledVersion, error) { + return nil, nil +} +func (m *mockProvider) ListAvailable() ([]runtime.AvailableVersion, error) { + return nil, nil +} +func (m *mockProvider) InstallPath(version string) (string, error) { return "", nil } +func (m *mockProvider) LocalVersion() (string, error) { return "", nil } +func (m *mockProvider) SetLocalVersion(version string) error { return nil } +func (m *mockProvider) CurrentVersion() (string, error) { return "", nil } +func (m *mockProvider) DetectInstalled() ([]runtime.DetectedVersion, error) { + return nil, nil +} +func (m *mockProvider) GlobalPackages(installPath string) ([]string, error) { + return nil, nil +} +func (m *mockProvider) InstallGlobalPackages(version string, packages []string) error { + return nil +} +func (m *mockProvider) ManualPackageInstallCommand(packages []string) string { + return "" +} + +func (m *mockProvider) GlobalVersion() (string, error) { + return m.globalVersion, nil +} + +func (m *mockProvider) SetGlobalVersion(version string) error { + m.setGlobalCalls = append(m.setGlobalCalls, version) + return m.globalSetError +} + +func TestAutoSetGlobalIfNeeded_NoGlobalVersion(t *testing.T) { + provider := &mockProvider{ + name: "test", + displayName: "Test", + globalVersion: "", // No global version set + } + + autoSetGlobalIfNeeded(provider, "1.0.0") + + if len(provider.setGlobalCalls) != 1 { + t.Errorf("Expected SetGlobalVersion to be called once, got %d calls", len(provider.setGlobalCalls)) + } + if len(provider.setGlobalCalls) > 0 && provider.setGlobalCalls[0] != "1.0.0" { + t.Errorf("Expected SetGlobalVersion called with '1.0.0', got %q", provider.setGlobalCalls[0]) + } +} + +func TestAutoSetGlobalIfNeeded_GlobalVersionAlreadySet(t *testing.T) { + provider := &mockProvider{ + name: "test", + displayName: "Test", + globalVersion: "2.0.0", // Global version already set + } + + autoSetGlobalIfNeeded(provider, "1.0.0") + + if len(provider.setGlobalCalls) != 0 { + t.Errorf("Expected SetGlobalVersion to not be called when global already set, got %d calls", len(provider.setGlobalCalls)) + } +} + +func TestAutoSetGlobalIfNeeded_MultipleInstalls(t *testing.T) { + provider := &mockProvider{ + name: "test", + displayName: "Test", + globalVersion: "", // No global version initially + } + + // First install - should set global + autoSetGlobalIfNeeded(provider, "1.0.0") + + if len(provider.setGlobalCalls) != 1 { + t.Fatalf("Expected first install to set global, got %d calls", len(provider.setGlobalCalls)) + } + + // Simulate that global is now set + provider.globalVersion = "1.0.0" + + // Second install - should NOT change global + autoSetGlobalIfNeeded(provider, "2.0.0") + + if len(provider.setGlobalCalls) != 1 { + t.Errorf("Expected second install to not change global, got %d calls total", len(provider.setGlobalCalls)) + } +}