Files
terraform-docs/internal/plugin/discovery.go
Pascal Hofmann dd450a5352 chore: fix golangci-lint issues
Signed-off-by: Pascal Hofmann <mail@pascalhofmann.de>
2025-12-12 14:43:23 +01:00

119 lines
2.6 KiB
Go

/*
Copyright 2021 The terraform-docs Authors.
Licensed under the MIT license (the "License"); you may not
use this file except in compliance with the License.
You may obtain a copy of the License at the LICENSE file in
the root directory of this source tree.
*/
package plugin
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
goplugin "github.com/hashicorp/go-plugin"
"github.com/mitchellh/go-homedir"
pluginsdk "github.com/terraform-docs/terraform-docs/plugin"
)
// Discover plugins and registers them. The lookup priority of plugins is as
// follow:
//
// 1. `TFDOCS_PLUGIN_DIR` environment variable (if it's set)
// 2. Current directory (./.tfdocs.d/plugins)
// 3. Home directory (~/.tfdocs.d/plugins)
//
// Files under these directories that satisfy the "tfdocs-format-*" naming
// convention are treated as plugins.
func Discover() (*List, error) {
if dir := os.Getenv("TFDOCS_PLUGIN_DIR"); dir != "" {
return findPlugins(dir)
}
if _, err := os.Stat(localPluginsRoot); !os.IsNotExist(err) {
return findPlugins(localPluginsRoot)
}
dir, err := homedir.Expand(homePluginsRoot)
if err != nil {
return nil, err
}
return findPlugins(dir)
}
// findPlugins finds plugins in a given 'dir' and registers them.
func findPlugins(dir string) (*List, error) {
clients := map[string]*goplugin.Client{}
formatters := map[string]*pluginsdk.Client{}
files, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
for _, f := range files {
name := strings.ReplaceAll(f.Name(), namePrefix, "")
path, err := getPluginPath(dir, name)
if err != nil {
return nil, err
}
// Accepting variables here is intentional; we need to determine the
// path on the fly per directory.
//
// nolint:gosec
cmd := exec.CommandContext(context.TODO(), path)
client := pluginsdk.NewClient(&pluginsdk.ClientOpts{
Cmd: cmd,
})
rpcClient, err := client.Client()
if err != nil {
return nil, err
}
raw, err := rpcClient.Dispense("formatter")
if err != nil {
return nil, err
}
formatter := raw.(*pluginsdk.Client)
if _, ok := clients[name]; ok {
return nil, fmt.Errorf("plugin %s is already registered", name)
}
clients[name] = client
formatters[name] = formatter
}
return &List{formatters: formatters, clients: clients}, nil
}
func getPluginPath(dir string, name string) (string, error) {
suffix := ""
if runtime.GOOS == "windows" {
suffix += ".exe"
}
path := filepath.Join(dir, fmt.Sprintf("%s%s%s", namePrefix, name, suffix))
if _, err := os.Stat(path); os.IsNotExist(err) {
return "", os.ErrNotExist
}
return path, nil
}