Skip to content

Commit

Permalink
New plugin args option
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Henderson <[email protected]>
  • Loading branch information
hairyhenderson committed Apr 4, 2023
1 parent cd3be0b commit 72194fe
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ COPY --from=build /bin/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT} /gompl

ENTRYPOINT [ "/gomplate" ]

FROM alpine:3.17 AS gomplate-alpine
FROM alpine:3.17.3 AS gomplate-alpine

ARG VCS_REF
ARG TARGETOS
Expand Down
37 changes: 34 additions & 3 deletions docs/content/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,15 +304,19 @@ See [`--plugin`](../usage/#plugin).
A map that configures custom functions for use in the templates. The key is the
name of the function, and the value configures the plugin. The value is a map
containing the command (`cmd`) and the options `pipe` (boolean) and `timeout`
(duration).
(duration). A list of optional arguments to always pass to the plugin can be set
with `args` (array of strings).

Alternatively, the value can be a string, which sets `cmd`.
Alternatively, the value can be a string, which sets only `cmd`.

```yaml
in: '{{ "hello world" | figlet | lolcat }}'
in: '{{ "world" | figlet | lolcat }}'
plugins:
figlet:
cmd: /usr/local/bin/figlet
args:
- oh
- hello
pipe: true
timeout: 1s
lolcat: /home/hairyhenderson/go/bin/lolcat
Expand All @@ -322,6 +326,33 @@ plugins:

The path to the plugin executable (or script) to run.

### `args`

An array of optional arguments to always pass to the plugin. These arguments
will be passed _before_ any arguments provided in the template.

For example:

```yaml
plugins:
echo:
cmd: /bin/echo
args:
- foo
- bar
```

With this template:
```
{{ echo "baz" }}
```

Will result the command being called like this:

```console
$ /bin/echo foo bar baz
```

### `pipe`

Whether to pipe the final argument of the template function to the plugin's
Expand Down
6 changes: 4 additions & 2 deletions internal/config/configfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,9 @@ func (d DataSource) mergeFrom(o DataSource) DataSource {

type PluginConfig struct {
Cmd string
Timeout time.Duration
Pipe bool
Args []string `yaml:"args,omitempty"`
Timeout time.Duration `yaml:"timeout,omitempty"`
Pipe bool `yaml:"pipe,omitempty"`
}

// UnmarshalYAML - satisfy the yaml.Umarshaler interface - plugin configs can
Expand All @@ -178,6 +179,7 @@ func (p *PluginConfig) UnmarshalYAML(value *yaml.Node) error {

type raw struct {
Cmd string
Args []string
Timeout time.Duration
Pipe bool
}
Expand Down
7 changes: 4 additions & 3 deletions internal/config/configfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,16 +626,17 @@ pluginTimeout: 500ms
c = &Config{
Plugins: map[string]PluginConfig{
"foo": {
Cmd: "bar",
Pipe: true,
Cmd: "bar",
Timeout: 1 * time.Second,
Pipe: true,
},
},
}
expected = `---
plugins:
foo:
cmd: bar
timeout: 0s
timeout: 1s
pipe: true
`

Expand Down
14 changes: 14 additions & 0 deletions internal/tests/integration/plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,17 @@ plugins:
assert.Equal(t, "", e)
assert.Equal(t, "=hi=thZrZ", o)
}

func TestPlugins_Args(t *testing.T) {
tmpDir := setupPluginsTest(t)

writeConfig(t, tmpDir, `in: '{{ echo "world" }}'
plugins:
echo:
cmd: echo
args: [ oh, hello ]
`)

o, e, err := cmd(t).withDir(tmpDir.Path()).run()
assertSuccess(t, o, e, err, "oh hello world\n")
}
8 changes: 8 additions & 0 deletions plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func bindPlugins(ctx context.Context, cfg *config.Config, funcMap template.FuncM
Timeout: timeout,
Pipe: v.Pipe,
Stderr: cfg.Stderr,
Args: v.Args,
})
}

Expand All @@ -49,6 +50,10 @@ type PluginOpts struct {
// Defaults to os.Stderr.
Stderr io.Writer

// Args are additional arguments to pass to the plugin. These precede any
// arguments passed to the plugin function at runtime.
Args []string

// Timeout is the maximum amount of time to wait for the plugin to complete.
// Defaults to 5 seconds.
Timeout time.Duration
Expand All @@ -74,6 +79,7 @@ func PluginFunc(ctx context.Context, cmd string, opts PluginOpts) func(...interf
plugin := &plugin{
ctx: ctx,
path: cmd,
args: opts.Args,
timeout: timeout,
pipe: opts.Pipe,
stderr: stderr,
Expand All @@ -87,6 +93,7 @@ type plugin struct {
ctx context.Context
stderr io.Writer
path string
args []string
timeout time.Duration
pipe bool
}
Expand Down Expand Up @@ -122,6 +129,7 @@ func findPowershell() string {

func (p *plugin) run(args ...interface{}) (interface{}, error) {
a := conv.ToStrings(args...)
a = append(p.args, a...)

name, a := p.buildCommand(a)

Expand Down
24 changes: 24 additions & 0 deletions plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,30 @@ func TestRun(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "", stderr.String())
assert.Equal(t, "foo", strings.TrimSpace(out.(string)))

p = &plugin{
ctx: ctx,
timeout: 500 * time.Millisecond,
stderr: stderr,
path: "echo",
args: []string{"foo", "bar"},
}
out, err = p.run()
require.NoError(t, err)
assert.Equal(t, "", stderr.String())
assert.Equal(t, "foo bar", strings.TrimSpace(out.(string)))

p = &plugin{
ctx: ctx,
timeout: 500 * time.Millisecond,
stderr: stderr,
path: "echo",
args: []string{"foo", "bar"},
}
out, err = p.run("baz", "qux")
require.NoError(t, err)
assert.Equal(t, "", stderr.String())
assert.Equal(t, "foo bar baz qux", strings.TrimSpace(out.(string)))
}

func ExamplePluginFunc() {
Expand Down

0 comments on commit 72194fe

Please sign in to comment.