diff --git a/pkg/plugins/plugin.go b/pkg/plugins/plugin.go index 92aba9832..2ef1c1c4e 100644 --- a/pkg/plugins/plugin.go +++ b/pkg/plugins/plugin.go @@ -3,7 +3,6 @@ package plugins import ( "bytes" "context" - "encoding/json" "fmt" "io" "io/ioutil" @@ -181,7 +180,7 @@ func NewPlugin(command string) *Plugin { // Use the context for cancelation. func (p *Plugin) Run(ctx context.Context) { var err error - p.Variables, err = p.loadVariablesFromJSONFile() + p.Variables, err = p.loadVariablesAsEnvVars() if err != nil { p.Debugf("ERR: %s", err) p.OnErr(err) @@ -316,34 +315,6 @@ func (p *Plugin) refresh(ctx context.Context) error { return nil } -func (p *Plugin) loadVariablesFromJSONFile() ([]string, error) { - variablesJSONFilename := p.Command + variableJSONFileExt - f, err := os.Open(variablesJSONFilename) - if err != nil && os.IsNotExist(err) { - // no .vars.json file - no probs - p.Debugf("(skipping) no variable file: %s", variablesJSONFilename) - return nil, nil - } else if err != nil { - p.Debugf("ERR: %s", variablesJSONFilename, err) - p.OnErr(err) - } - defer f.Close() - b, err := io.ReadAll(io.LimitReader(f, 1_000_000)) - if err != nil { - return nil, err - } - p.Debugf("%s: %s", variablesJSONFilename, string(b)) - var varmap map[string]interface{} - if err := json.Unmarshal(b, &varmap); err != nil { - return nil, errors.Wrap(err, "json.Unmarshal") - } - var vars []string - for k, v := range varmap { - vars = append(vars, fmt.Sprintf("%s=%v", k, v)) - } - return vars, nil -} - // OnErr is called when something has gone wrong at some point. func (p *Plugin) OnErr(err error) { p.Items.CycleItems = []*Item{ diff --git a/pkg/plugins/testdata/vars-test/plugin.sh b/pkg/plugins/testdata/vars-test/plugin.sh index 07cc9aea9..188b80536 100755 --- a/pkg/plugins/testdata/vars-test/plugin.sh +++ b/pkg/plugins/testdata/vars-test/plugin.sh @@ -1,3 +1,9 @@ #!/bin/bash + +# string(XBAR_TEST_EXPLICIT_VAR): An explicit variable +# string(XBAR_TEST_DEFAULT_VAR="default-value"): A default variable (from metadata) +# string(XBAR_TEST_SET_IN_VARS_JSON): A variable set in the JSON + echo "XBAR_TEST_EXPLICIT_VAR=${XBAR_TEST_EXPLICIT_VAR}" echo "XBAR_TEST_SET_IN_VARS_JSON=${XBAR_TEST_SET_IN_VARS_JSON}" +echo "XBAR_TEST_DEFAULT_VAR=${XBAR_TEST_DEFAULT_VAR}" diff --git a/pkg/plugins/variables.go b/pkg/plugins/variables.go index 0a61ea05c..36274770b 100644 --- a/pkg/plugins/variables.go +++ b/pkg/plugins/variables.go @@ -2,11 +2,14 @@ package plugins import ( "encoding/json" + "fmt" "io" "io/ioutil" "os" "path/filepath" + "sync" + "github.com/matryer/xbar/pkg/metadata" "github.com/pkg/errors" ) @@ -50,3 +53,93 @@ func LoadVariableValues(pluginDir, installedPluginPath string) (map[string]inter } return values, nil } + +func (p *Plugin) loadVariablesAsEnvVars() ([]string, error) { + vars, err := p.loadVariables() + if err != nil { + return nil, errors.Wrap(err, "loadVariables") + } + envvars := make([]string, 0, len(vars)) + for k, v := range vars { + envvars = append(envvars, fmt.Sprintf("%s=%v", k, v)) + } + return envvars, nil +} + +func (p *Plugin) loadVariables() (map[string]interface{}, error) { + var wg sync.WaitGroup + var defaultVars, jsonFileVars map[string]interface{} + var defaultVarsErr, jsonFileVarsErr error + wg.Add(1) + go func() { + defaultVars, defaultVarsErr = p.loadVariablesFromPluginMetadata() + wg.Done() + }() + wg.Add(1) + go func() { + jsonFileVars, jsonFileVarsErr = p.loadVariablesFromJSONFile() + wg.Done() + }() + wg.Wait() + if defaultVarsErr != nil { + return nil, errors.Wrap(defaultVarsErr, "load default vars") + } + if jsonFileVarsErr != nil { + return nil, errors.Wrap(jsonFileVarsErr, "load json file vars") + } + // add the json file vars to the defaults, + // and return them. + for k, v := range jsonFileVars { + defaultVars[k] = v + } + return defaultVars, nil +} + +// loadVariablesFromJSONFile gets a list of environment variable friendly +// key=value pairs. +func (p *Plugin) loadVariablesFromJSONFile() (map[string]interface{}, error) { + variablesJSONFilename := p.Command + variableJSONFileExt + f, err := os.Open(variablesJSONFilename) + if err != nil && os.IsNotExist(err) { + // no .vars.json file - no probs + return nil, nil + } else if err != nil { + return nil, errors.Wrap(err, "open vars json file") + } + defer f.Close() + b, err := io.ReadAll(io.LimitReader(f, 1_000_000)) + if err != nil { + return nil, err + } + var vars map[string]interface{} + if err := json.Unmarshal(b, &vars); err != nil { + return nil, errors.Wrap(err, "json.Unmarshal") + } + return vars, nil +} + +func (p *Plugin) loadVariablesFromPluginMetadata() (map[string]interface{}, error) { + // read the plugin metadata for default values + pluginFile, err := os.Open(p.Command) + if err != nil { + return nil, errors.Wrap(err, "open plugin source") + } + defer pluginFile.Close() + pluginFileB, err := io.ReadAll(io.LimitReader(pluginFile, 1_000_000)) + if err != nil { + return nil, errors.Wrap(err, "read plugin source") + } + pluginMetadata, err := metadata.Parse(metadata.DebugFunc(p.Debugf), p.CleanFilename(), string(pluginFileB)) + if err != nil { + return nil, errors.Wrap(err, "metadata.Parse") + } + vars := make(map[string]interface{}) + for _, pluginVar := range pluginMetadata.Vars { + if pluginVar.Default == "" { + // skip values with no default + continue + } + vars[pluginVar.Name] = pluginVar.DefaultValue() + } + return vars, nil +} diff --git a/pkg/plugins/variables_test.go b/pkg/plugins/variables_test.go index 057dc2d83..e6a9d40d4 100644 --- a/pkg/plugins/variables_test.go +++ b/pkg/plugins/variables_test.go @@ -34,9 +34,10 @@ func TestEnvironmentVariables(t *testing.T) { } p.Run(ctx) - is.Equal(len(p.Items.CycleItems), 2) - is.Equal(p.Items.CycleItems[0].Text, `XBAR_TEST_EXPLICIT_VAR=explicit`) // inherited - is.Equal(p.Items.CycleItems[1].Text, `XBAR_TEST_SET_IN_VARS_JSON=json`) // in vars.json file + is.Equal(len(p.Items.CycleItems), 3) + is.Equal(p.Items.CycleItems[0].Text, `XBAR_TEST_EXPLICIT_VAR=explicit`) // inherited + is.Equal(p.Items.CycleItems[1].Text, `XBAR_TEST_SET_IN_VARS_JSON=json`) // in vars.json file + is.Equal(p.Items.CycleItems[2].Text, `XBAR_TEST_DEFAULT_VAR=default-value`) // from plugin metadata }