Skip to content

Commit c704472

Browse files
committed
feat: recursive sourcing of startup scripts
1 parent 5d9e5a8 commit c704472

File tree

4 files changed

+69
-19
lines changed

4 files changed

+69
-19
lines changed

common/nix_candidate_source.go

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,48 @@ func (s *NixCandidateSource) WhereSet(somePath string) *PathSetIn {
4040
}
4141

4242
func (s *NixCandidateSource) crawlKnownPaths() {
43-
ForEachKnownPath(func(originalSource, expandedSource string) {
44-
if s.expandedPathsAlreadyCrawled[expandedSource] {
45-
return
43+
ForEachKnownPath(s.crawlSource)
44+
}
45+
46+
func (s *NixCandidateSource) crawlSource(originalSource, expandedSource string) {
47+
if s.expandedPathsAlreadyCrawled[expandedSource] || isDir(expandedSource) {
48+
return
49+
}
50+
// do not crawl this file again
51+
s.expandedPathsAlreadyCrawled[expandedSource] = true
52+
53+
// try getting the contents
54+
input, err := readAllText(expandedSource)
55+
if err != nil {
56+
return
57+
}
58+
59+
ForEachVariableAssignment(s.key, input, func(value string) {
60+
harvestedPaths := s.harvestPaths(value)
61+
for _, harvestedPath := range harvestedPaths {
62+
s.tryUpdatePathMap(harvestedPath, originalSource, expandedSource)
4663
}
47-
// do not crawl this file again
48-
s.expandedPathsAlreadyCrawled[expandedSource] = true
64+
})
65+
66+
sourcedQueue := []string{}
4967

50-
// try getting the contents
51-
input, err := readAllText(expandedSource)
52-
if err != nil {
68+
ForEachSourcedScript(input, func(sourcedPath string) {
69+
normalizedSourcedPath := s.fs.GetAbsolutePath(sourcedPath)
70+
if s.expandedPathsAlreadyCrawled[normalizedSourcedPath] {
5371
return
5472
}
55-
56-
ForEachVariableAssignment(s.key, input, func(value string) {
57-
harvestedPaths := s.harvestPaths(value)
58-
for _, harvestedPath := range harvestedPaths {
59-
s.tryUpdatePathMap(harvestedPath, originalSource, expandedSource)
60-
}
61-
})
73+
sourcedQueue = append(sourcedQueue, sourcedPath)
6274
})
75+
76+
for {
77+
if len(sourcedQueue) == 0 {
78+
break
79+
}
80+
next := sourcedQueue[0]
81+
sourcedQueue = sourcedQueue[1:]
82+
s.crawlSource(next, s.fs.GetAbsolutePath(next))
83+
}
84+
6385
}
6486

6587
func (s *NixCandidateSource) tryUpdatePathMap(harvestedPath string, originalSource string, expandedSource string) {
@@ -93,14 +115,17 @@ func (s *NixCandidateSource) crawlPathLists() {
93115

94116
// input is some path definition
95117
func (s *NixCandidateSource) harvestPaths(input string) []string {
118+
input = fixHomeExpansion(input)
96119
input = strings.TrimSpace(input)
97120
input = strings.Trim(input, fmt.Sprintf("%v\"'", os.PathListSeparator))
98121
inputExpanded := expand.Expand(input, func(key string) (value string, ok bool) {
122+
switch key {
99123
// do not expand the desired variable itself
100-
if key == s.key {
124+
case s.key:
101125
return "", false
126+
default:
127+
return os.LookupEnv(key)
102128
}
103-
return os.LookupEnv(key)
104129
})
105130
all := strings.Split(inputExpanded, fmt.Sprintf("%c", os.PathListSeparator))
106131
res := hashset.New[string](uint64(len(all)), g.Equals[string], g.HashString)
@@ -141,3 +166,11 @@ func appendIfNotInSlice[T any](input []T, value T, eq func(T, T) bool) []T {
141166

142167
return res
143168
}
169+
170+
func isDir(p string) bool {
171+
f, err := os.Stat(p)
172+
if err != nil {
173+
return false
174+
}
175+
return f.IsDir()
176+
}

common/os_filesystem.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package common
33
import (
44
"os"
55
"path/filepath"
6+
"strings"
67

78
"github.com/mitchellh/go-homedir"
89
"go.spiff.io/expand"
@@ -12,6 +13,7 @@ type OsFilesystem struct{}
1213

1314
func (*OsFilesystem) GetAbsolutePath(path string) string {
1415
// homedir is assumed to work correctly
16+
path = fixHomeExpansion(path)
1517
expandedPath, err := homedir.Expand(path)
1618
if err == nil {
1719
path = expandedPath
@@ -23,6 +25,12 @@ func (*OsFilesystem) GetAbsolutePath(path string) string {
2325
return expand.Expand(path, os.LookupEnv)
2426
}
2527

28+
func fixHomeExpansion(path string) string {
29+
path = strings.ReplaceAll(path, "$HOME/", "~/")
30+
path = strings.ReplaceAll(path, "${HOME}/", "~/")
31+
return path
32+
}
33+
2634
// returns (exists, isDir)
2735
func (f *OsFilesystem) PathStatus(path string) (bool, bool) {
2836
path = f.GetAbsolutePath(path)

common/os_filesystem_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"path"
77
"runtime"
8+
"strings"
89
"testing"
910

1011
"github.com/stretchr/testify/assert"
@@ -72,3 +73,11 @@ func createTempFile(t *testing.T) *os.File {
7273
fmt.Println(tempFile.Name())
7374
return tempFile
7475
}
76+
77+
func Test_expanding_home_variable(t *testing.T) {
78+
if os.Getenv("HOME") == "" {
79+
os.Setenv("HOME", "/home")
80+
}
81+
expanded := sut.GetAbsolutePath("$HOME/.test/env")
82+
assert.True(t, strings.HasSuffix(expanded, "/.test/env"))
83+
}

common/script_parser.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func ForEachVariableAssignment(key, input string, fn func(string)) {
1515
syntax.Walk(f, func(node syntax.Node) bool {
1616
switch x := node.(type) {
1717
case *syntax.Assign:
18-
if key == x.Name.Value {
18+
if x.Name != nil && key == x.Name.Value && x.Value != nil {
1919
value := input[x.Value.Pos().Offset():x.Value.End().Offset()]
2020
fn(value)
2121
}
@@ -39,7 +39,7 @@ func ForEachSourcedScript(input string, fn func(string)) {
3939
syntax.Walk(f, func(node syntax.Node) bool {
4040
switch x := node.(type) {
4141
case *syntax.CallExpr:
42-
if len(x.Args) == 2 {
42+
if len(x.Args) >= 2 {
4343
cmd := input[x.Args[0].Pos().Offset():x.Args[0].End().Offset()]
4444
cmd = strings.TrimLeft(cmd, "\\")
4545

0 commit comments

Comments
 (0)