mirror of
https://github.com/terraform-docs/terraform-docs.git
synced 2026-03-27 04:48:33 +07:00
119 lines
2.6 KiB
Go
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
|
|
}
|