Skip to content

Commit a3bccea

Browse files
feat(golang-rewrite): add support for shim templates resolution (#2076)
Co-authored-by: Augusto Moura <[email protected]>
1 parent c21e274 commit a3bccea

File tree

3 files changed

+99
-5
lines changed

3 files changed

+99
-5
lines changed

internal/plugins/plugins.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ func (e NoCallbackError) Error() string {
5656
return fmt.Sprintf(hasNoCallbackMsg, e.plugin, e.callback)
5757
}
5858

59+
// NoShimTemplateError is an error returned by ShimTemplatePath when an shim
60+
// template was not found in the plugin shims directory, or the file is not executable
61+
type NoShimTemplateError struct {
62+
shimName string
63+
plugin string
64+
}
65+
66+
func (e NoShimTemplateError) Error() string {
67+
return fmt.Sprintf(hasNoCallbackMsg, e.plugin, e.shimName)
68+
}
69+
5970
// NoCommandError is an error returned by ExtensionCommandPath when an extension
6071
// command with the given name does not exist
6172
type NoCommandError struct {
@@ -73,6 +84,7 @@ const (
7384
pluginAlreadyExistsMsg = "Plugin named %s already added"
7485
pluginMissingMsg = "Plugin named %s not installed"
7586
hasNoCallbackMsg = "Plugin named %s does not have a callback named %s"
87+
hasNoShimTemplateMsg = "Plugin named %s does not have a shim template named %s"
7688
hasNoCommandMsg = "Plugin named %s does not have a extension command named %s"
7789
)
7890

@@ -191,6 +203,20 @@ func (p Plugin) CallbackPath(name string) (string, error) {
191203
return path, nil
192204
}
193205

206+
// ShimTemplatePath returns the full file path to a shim, if it exists
207+
func (p Plugin) ShimTemplatePath(shimName string) (string, error) {
208+
const executableMode = 0o111
209+
210+
path := filepath.Join(p.Dir, "shims", shimName)
211+
stat, err := os.Stat(path)
212+
213+
if errors.Is(err, os.ErrNotExist) || stat.Mode()&executableMode == 0 {
214+
return "", NoShimTemplateError{shimName: shimName, plugin: p.Name}
215+
}
216+
217+
return path, nil
218+
}
219+
194220
// GetExtensionCommands returns a slice of strings representing all available
195221
// extension commands for the plugin.
196222
func (p Plugin) GetExtensionCommands() ([]string, error) {

internal/shims/shims.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func (e NoExecutableForPluginError) Error() string {
5656

5757
// FindExecutable takes a shim name and a current directory and returns the path
5858
// to the executable that the shim resolves to.
59-
func FindExecutable(conf config.Config, shimName, currentDirectory string) (string, plugins.Plugin, string, bool, error) {
59+
func FindExecutable(conf config.Config, shimName, currentDirectory string) (path string, plugin plugins.Plugin, version string, found bool, err error) {
6060
shimPath := Path(conf, shimName)
6161

6262
if _, err := os.Stat(shimPath); err != nil {
@@ -74,6 +74,11 @@ func FindExecutable(conf config.Config, shimName, currentDirectory string) (stri
7474
for _, shimToolVersion := range toolVersions {
7575
plugin := plugins.New(conf, shimToolVersion.Name)
7676
if plugin.Exists() == nil {
77+
// If a shim template is found, we can return it before looping through versions
78+
shimTemplate, err := plugin.ShimTemplatePath(shimName)
79+
if err == nil {
80+
return shimTemplate, plugin, "", true, nil
81+
}
7782

7883
versions, found, err := resolve.Version(conf, plugin, currentDirectory)
7984

@@ -368,18 +373,23 @@ func ToolExecutables(conf config.Config, plugin plugins.Plugin, version toolvers
368373

369374
// ExecutablePaths returns a slice of absolute directory paths that tool
370375
// executables are contained in.
371-
func ExecutablePaths(conf config.Config, plugin plugins.Plugin, version toolversions.Version) ([]string, error) {
376+
func ExecutablePaths(conf config.Config, plugin plugins.Plugin, version toolversions.Version) (paths []string, err error) {
372377
dirs, err := ExecutableDirs(plugin)
373378
if err != nil {
374379
return []string{}, err
375380
}
376381

382+
shimsDir, err := os.Stat(path.Join(plugin.Dir, "shims"))
383+
if err == nil && shimsDir.IsDir() {
384+
paths = append(paths, path.Join(plugin.Dir, "shims"))
385+
}
386+
377387
installPath := installs.InstallPath(conf, plugin, version)
378-
return dirsToPaths(dirs, installPath), nil
388+
return append(paths, dirsToPaths(dirs, installPath)...), nil
379389
}
380390

381-
// ExecutableDirs returns a slice of directory names that tool executables are
382-
// contained in
391+
// ExecutableDirs returns a slice of relative directory names that tool executables are
392+
// contained in, *inside installs*
383393
func ExecutableDirs(plugin plugins.Plugin) ([]string, error) {
384394
var stdOut strings.Builder
385395
var stdErr strings.Builder

internal/shims/shims_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,27 @@ func TestFindExecutable(t *testing.T) {
113113
assert.Equal(t, filepath.Base(executable), "dummy")
114114
assert.True(t, strings.HasPrefix(executable, dir))
115115
})
116+
117+
t.Run("returns string containing path to executable when shim template in plugin is set", func(t *testing.T) {
118+
// write a version file
119+
data := []byte("lua 1.1.0")
120+
assert.Nil(t, os.WriteFile(filepath.Join(currentDir, ".tool-versions"), data, 0o666))
121+
122+
// write a shim template to the plugin shims dir
123+
setupShimTemplate(t, plugin, "dummy", "echo 'shim template'")
124+
125+
executable, gotPlugin, version, found, err := FindExecutable(conf, "dummy", currentDir)
126+
assert.Nil(t, err)
127+
128+
relativePath, err := filepath.Rel(conf.DataDir, executable)
129+
assert.Nil(t, err)
130+
131+
assert.Equal(t, "plugins/lua/shims/dummy", relativePath)
132+
assert.Equal(t, "dummy", filepath.Base(executable))
133+
assert.Equal(t, plugin, gotPlugin)
134+
assert.Equal(t, "", version)
135+
assert.True(t, found)
136+
})
116137
}
117138

118139
func TestFindExecutable_Ref(t *testing.T) {
@@ -443,6 +464,27 @@ func TestExecutablePaths(t *testing.T) {
443464
assert.Equal(t, filepath.Base(path1), "foo")
444465
assert.Equal(t, filepath.Base(path2), "bar")
445466
})
467+
468+
t.Run("returns list of executable paths for tool version containing shim templates", func(t *testing.T) {
469+
data := []byte("echo 'foo bar'")
470+
err := os.WriteFile(filepath.Join(plugin.Dir, "bin", "list-bin-paths"), data, 0o777)
471+
assert.Nil(t, err)
472+
473+
// write a shim template to the plugin shims dir
474+
setupShimTemplate(t, plugin, "dummy", "echo 'shim template'")
475+
476+
executables, err := ExecutablePaths(conf, plugin, toolversions.Version{Type: "version", Value: "1.2.3"})
477+
assert.Nil(t, err)
478+
path1 := executables[0]
479+
path2 := executables[1]
480+
path3 := executables[2]
481+
assert.Equal(t, "foo", filepath.Base(path2))
482+
assert.Equal(t, "bar", filepath.Base(path3))
483+
484+
relativePath, err := filepath.Rel(conf.DataDir, path1)
485+
assert.Nil(t, err)
486+
assert.Equal(t, "plugins/lua/shims", relativePath)
487+
})
446488
}
447489

448490
func TestExecutableDirs(t *testing.T) {
@@ -511,3 +553,19 @@ func installVersion(t *testing.T, conf config.Config, plugin plugins.Plugin, ver
511553
err := installtest.InstallOneVersion(conf, plugin, "version", version)
512554
assert.Nil(t, err)
513555
}
556+
557+
func setupShimTemplate(t *testing.T, plugin plugins.Plugin, shimName string, script string) {
558+
t.Helper()
559+
shimsDirPath := filepath.Join(plugin.Dir, "shims")
560+
os.MkdirAll(shimsDirPath, 0o777)
561+
562+
shimPath := filepath.Join(shimsDirPath, shimName)
563+
contents := fmt.Sprintf("#!/usr/bin/env bash\n%s\n", script)
564+
err := os.WriteFile(shimPath, []byte(contents), 0o777)
565+
assert.Nil(t, err)
566+
567+
t.Cleanup(func() {
568+
err := os.Remove(shimPath)
569+
assert.Nil(t, err)
570+
})
571+
}

0 commit comments

Comments
 (0)