Skip to content

Commit 1aa8172

Browse files
committed
fix(env): inject provider specific environment variables into active shell
- refactored `unirtm env` to use config to determine active tools instead of database - updated `cmd/3.env.go` and `cmd/9.activate.go` to call `Provider.GetEnvVars()` - added `cmd/3.env_integration_test.go` to ensure GOROOT is exported correctly - ignore host GOROOT in shim mode execution
1 parent 2778e74 commit 1aa8172

4 files changed

Lines changed: 139 additions & 21 deletions

File tree

cmd/0.cmd.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ func invokeShimMode(exeName string) {
154154
os.Setenv(k, v)
155155
}
156156

157+
// Filter out host environment variables that might pollute the unirtm managed tools.
158+
// E.g., if the user has GOROOT set for a system Go installation, it will break unirtm's Go.
159+
if exeName == "go" || exeName == "gofmt" {
160+
os.Unsetenv("GOROOT")
161+
}
162+
157163
// Prepare for execution
158164
args := os.Args
159165
if len(args) > 0 {

cmd/3.env.go

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package cmd
55

66
import (
7-
"context"
87
"encoding/json"
98
"fmt"
109
"os"
@@ -15,9 +14,8 @@ import (
1514

1615
"github.com/pterm/pterm"
1716
"github.com/snowdreamtech/unirtm/internal/config"
18-
"github.com/snowdreamtech/unirtm/internal/database"
1917
"github.com/snowdreamtech/unirtm/internal/pkg/env"
20-
"github.com/snowdreamtech/unirtm/internal/repository/sqlite"
18+
"github.com/snowdreamtech/unirtm/internal/provider"
2119
"github.com/spf13/cobra"
2220
"golang.org/x/term"
2321
)
@@ -70,7 +68,6 @@ func runEnv(cmd *cobra.Command, args []string) error {
7068
return runEnvInfoWithStyle()
7169
}
7270

73-
ctx := context.Background()
7471
cfg, _ := config.LoadFull()
7572

7673
// Collect environment data
@@ -83,27 +80,78 @@ func runEnv(cmd *cobra.Command, args []string) error {
8380
var sources []string
8481
isRedacted := make(map[string]bool)
8582

86-
// Load tools from database
87-
dbPath := env.GetDatabasePath()
88-
db, err := database.Open(ctx, database.Config{Path: dbPath, WALMode: true})
89-
if err == nil {
90-
defer db.Close()
91-
installRepo, _ := sqlite.NewInstallationRepository(db.Conn())
92-
installations, _ := installRepo.List(ctx)
93-
94-
seen := make(map[string]bool)
95-
for _, inst := range installations {
96-
binDir := filepath.Join(installsDir, inst.Tool, inst.Version, "bin")
97-
if _, statErr := os.Stat(binDir); statErr == nil && !seen[binDir] {
98-
pathDirs = append(pathDirs, binDir)
99-
seen[binDir] = true
83+
// Get active tools from configuration
84+
toolVersions := make(map[string]string)
85+
if cfg != nil {
86+
for name, tc := range cfg.Tools {
87+
toolVersions[name] = tc.Version
88+
}
89+
}
90+
91+
registry := provider.DefaultRegistry
92+
seen := make(map[string]bool)
93+
providerEnvVars := make(map[string]string)
94+
95+
for toolNameKey, version := range toolVersions {
96+
toolName := toolNameKey
97+
backendName := ""
98+
99+
if idx := strings.Index(toolNameKey, ":"); idx != -1 {
100+
backendName = toolNameKey[:idx]
101+
toolName = toolNameKey[idx+1:]
102+
} else if strings.Contains(toolNameKey, "/") {
103+
backendName = "github"
104+
}
105+
106+
if backendName == "go" || strings.HasPrefix(toolNameKey, "go:") {
107+
backendName = "go-pkg"
108+
if strings.HasPrefix(toolNameKey, "go:") {
109+
toolName = strings.TrimPrefix(toolNameKey, "go:")
110+
}
111+
}
112+
113+
p := registry.GetWithBackend(toolName, backendName)
114+
if p == nil {
115+
continue
116+
}
117+
fsToolName := env.GetFSToolName(toolName, backendName)
118+
installPath := filepath.Join(installsDir, fsToolName, version)
119+
120+
// Add bin paths
121+
binPaths, err := p.GetBinPaths(toolName, installPath, version)
122+
if err == nil {
123+
for _, binDir := range binPaths {
124+
if _, statErr := os.Stat(binDir); statErr == nil && !seen[binDir] {
125+
pathDirs = append(pathDirs, binDir)
126+
seen[binDir] = true
127+
}
128+
}
129+
}
130+
131+
// Add version variables
132+
varName := "UNIRTM_" + strings.ToUpper(strings.ReplaceAll(fsToolName, "-", "_")) + "_VERSION"
133+
vars = append(vars, envVarEntry{Name: varName, Value: version, Source: "tool:" + toolNameKey})
134+
135+
// Add provider env vars
136+
toolEnvVars, err := p.GetEnvVars(toolName, installPath, version)
137+
if err == nil {
138+
for k, v := range toolEnvVars {
139+
if existing, exists := providerEnvVars[k]; !exists {
140+
providerEnvVars[k] = v
141+
} else if k == "NODE_PATH" {
142+
sep := string(os.PathListSeparator)
143+
if !strings.Contains(existing+sep, v+sep) {
144+
providerEnvVars[k] = existing + sep + v
145+
}
146+
}
100147
}
101-
// Add version variables
102-
varName := "UNIRTM_" + strings.ToUpper(strings.ReplaceAll(inst.Tool, "-", "_")) + "_VERSION"
103-
vars = append(vars, envVarEntry{Name: varName, Value: inst.Version, Source: "tool:" + inst.Tool})
104148
}
105149
}
106150

151+
for k, v := range providerEnvVars {
152+
vars = append(vars, envVarEntry{Name: k, Value: v, Source: "provider"})
153+
}
154+
107155
// Load config [env] variables
108156
if cfg != nil {
109157
resolved, src, redacted, err := cfg.ResolveEnvironment()

cmd/3.env_integration_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestEnvCommand_InjectsProviderEnvVars(t *testing.T) {
12+
// Create a temp directory for our mock project
13+
tempDir, err := os.MkdirTemp("", "unirtm-env-test-*")
14+
assert.NoError(t, err)
15+
defer os.RemoveAll(tempDir)
16+
17+
// Write a mock .unirtm.toml
18+
tomlContent := `
19+
[tools]
20+
go = "1.26.3"
21+
`
22+
err = os.WriteFile(filepath.Join(tempDir, ".unirtm.toml"), []byte(tomlContent), 0644)
23+
assert.NoError(t, err)
24+
25+
// Mock the installs directory so GetEnvVars doesn't panic or fail
26+
installsDir := filepath.Join(tempDir, "installs")
27+
os.Setenv("UNIRTM_INSTALLS_DIR", installsDir)
28+
defer os.Unsetenv("UNIRTM_INSTALLS_DIR")
29+
30+
// Change working directory to the temp dir so config.LoadFull picks it up
31+
origWd, _ := os.Getwd()
32+
os.Chdir(tempDir)
33+
defer os.Chdir(origWd)
34+
35+
// Temporarily set the database path to a dummy memory DB or temp file to avoid nil pointers
36+
os.Setenv("UNIRTM_DATABASE_PATH", filepath.Join(tempDir, "unirtm.db"))
37+
defer os.Unsetenv("UNIRTM_DATABASE_PATH")
38+
39+
out := captureStdoutFunc(t, func() {
40+
// Run the env command in shell "bash" mode
41+
rootCmd.SetArgs([]string{"env", "--shell", "bash"})
42+
_ = rootCmd.Execute()
43+
})
44+
45+
// Assert that GOROOT is present in the output
46+
assert.Contains(t, out, "GOROOT")
47+
assert.Contains(t, out, "1.26.3")
48+
}

cmd/9.activate.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,22 @@ func runActivate(cmd *cobra.Command, args []string) error {
290290
if err == nil {
291291
injectedPaths = append(injectedPaths, binPaths...)
292292
}
293+
294+
// Add provider-specific environment variables (e.g., GOROOT)
295+
toolEnvVars, err := p.GetEnvVars(toolName, installPath, version)
296+
if err == nil {
297+
for k, v := range toolEnvVars {
298+
if existing, exists := envVars[k]; !exists {
299+
envVars[k] = v
300+
} else if k == "NODE_PATH" {
301+
// Concatenate NODE_PATH for multiple npm tools
302+
sep := string(os.PathListSeparator)
303+
if !strings.Contains(existing+sep, v+sep) {
304+
envVars[k] = existing + sep + v
305+
}
306+
}
307+
}
308+
}
293309
}
294310
}
295311

0 commit comments

Comments
 (0)