summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Henderson <dhenderson@gmail.com>2019-04-09 16:31:05 -0700
committerGitHub <noreply@github.com>2019-04-09 16:31:05 -0700
commit67e97968fdb190cbdbbe511c8d6f816ff65eb124 (patch)
treede763083c0eeebe58ab679f595d4e95214534c84
parent32235c3c65d9c5fdc7171077f603b203197088a2 (diff)
parentac0012f4f6d2cde52490294e1fb262770f95ba9f (diff)
Merge pull request #529 from hairyhenderson/output-file-template-288
Adding --output-map argument for templating output paths
-rw-r--r--cmd/gomplate/main.go11
-rw-r--r--cmd/gomplate/main_test.go70
-rw-r--r--config.go3
-rw-r--r--config_test.go26
-rw-r--r--docs/content/usage.md50
-rw-r--r--gomplate.go52
-rw-r--r--gomplate_test.go26
-rw-r--r--template.go18
-rw-r--r--template_test.go12
-rw-r--r--template_unix_test.go4
-rw-r--r--template_windows_test.go4
-rw-r--r--tests/integration/basic_test.go6
-rw-r--r--tests/integration/inputdir_test.go85
13 files changed, 340 insertions, 27 deletions
diff --git a/cmd/gomplate/main.go b/cmd/gomplate/main.go
index 20b1e5a2..3c125c7d 100644
--- a/cmd/gomplate/main.go
+++ b/cmd/gomplate/main.go
@@ -23,6 +23,7 @@ var (
opts gomplate.Config
)
+// nolint: gocyclo
func validateOpts(cmd *cobra.Command, args []string) error {
if cmd.Flag("in").Changed && cmd.Flag("file").Changed {
return errors.New("--in and --file may not be used together")
@@ -44,6 +45,15 @@ func validateOpts(cmd *cobra.Command, args []string) error {
return errors.New("--input-dir must be set when --output-dir is set")
}
}
+
+ if cmd.Flag("output-map").Changed {
+ if cmd.Flag("out").Changed || cmd.Flag("output-dir").Changed {
+ return errors.New("--output-map can not be used together with --out or --output-dir")
+ }
+ if !cmd.Flag("input-dir").Changed {
+ return errors.New("--input-dir must be set when --output-map is set")
+ }
+ }
return nil
}
@@ -140,6 +150,7 @@ func initFlags(command *cobra.Command) {
command.Flags().StringArrayVarP(&opts.OutputFiles, "out", "o", []string{"-"}, "output `file` name. Omit to use standard output.")
command.Flags().StringArrayVarP(&opts.Templates, "template", "t", []string{}, "Additional template file(s)")
command.Flags().StringVar(&opts.OutputDir, "output-dir", ".", "`directory` to store the processed templates. Only used for --input-dir")
+ command.Flags().StringVar(&opts.OutputMap, "output-map", "", "Template `string` to map the input file to an output path")
command.Flags().StringVar(&opts.OutMode, "chmod", "", "set the mode for output file(s). Omit to inherit from input file(s)")
ldDefault := env.Getenv("GOMPLATE_LEFT_DELIM", "{{")
diff --git a/cmd/gomplate/main_test.go b/cmd/gomplate/main_test.go
new file mode 100644
index 00000000..c8670853
--- /dev/null
+++ b/cmd/gomplate/main_test.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/spf13/cobra"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestValidateOpts(t *testing.T) {
+ err := validateOpts(parseFlags(), nil)
+ assert.NoError(t, err)
+
+ cmd := parseFlags("-i=foo", "-f", "bar")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags("-i=foo", "-o=bar", "-o=baz")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags("-i=foo", "--input-dir=baz")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags("--input-dir=foo", "-f=bar")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags("--output-dir=foo", "-o=bar")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags("--output-dir=foo")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags("--output-map", "bar")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags("-o", "foo", "--output-map", "bar")
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags(
+ "--input-dir", "in",
+ "--output-dir", "foo",
+ "--output-map", "bar",
+ )
+ err = validateOpts(cmd, nil)
+ assert.Error(t, err)
+
+ cmd = parseFlags(
+ "--input-dir", "in",
+ "--output-map", "bar",
+ )
+ err = validateOpts(cmd, nil)
+ assert.NoError(t, err)
+}
+
+func parseFlags(flags ...string) *cobra.Command {
+ cmd := &cobra.Command{}
+ initFlags(cmd)
+ err := cmd.ParseFlags(flags)
+ if err != nil {
+ panic(err)
+ }
+ return cmd
+}
diff --git a/config.go b/config.go
index 26f48ee7..def7dabb 100644
--- a/config.go
+++ b/config.go
@@ -15,6 +15,7 @@ type Config struct {
ExcludeGlob []string
OutputFiles []string
OutputDir string
+ OutputMap string
OutMode string
DataSources []string
@@ -81,6 +82,8 @@ func (o *Config) String() string {
c += "\noutput: "
if o.InputDir != "" && o.OutputDir != "." {
c += o.OutputDir
+ } else if o.OutputMap != "" {
+ c += o.OutputMap
} else {
c += strings.Join(o.OutputFiles, ", ")
}
diff --git a/config_test.go b/config_test.go
index d916e7e9..91c2b1a9 100644
--- a/config_test.go
+++ b/config_test.go
@@ -15,17 +15,37 @@ output: -`
assert.Equal(t, expected, c.String())
c = &Config{
- LDelim: "{{",
- RDelim: "}}",
- Input: "{{ foo }}",
+ LDelim: "L",
+ RDelim: "R",
+ Input: "foo",
OutputFiles: []string{"-"},
Templates: []string{"foo=foo.t", "bar=bar.t"},
}
expected = `input: <arg>
output: -
+left_delim: L
+right_delim: R
templates: foo=foo.t, bar=bar.t`
assert.Equal(t, expected, c.String())
+
+ c = &Config{
+ InputDir: "in/",
+ OutputDir: "out/",
+ }
+ expected = `input: in/
+output: out/`
+
+ assert.Equal(t, expected, c.String())
+
+ c = &Config{
+ InputDir: "in/",
+ OutputMap: "{{ .in }}",
+ }
+ expected = `input: in/
+output: {{ .in }}`
+
+ assert.Equal(t, expected, c.String())
}
func TestGetMode(t *testing.T) {
diff --git a/docs/content/usage.md b/docs/content/usage.md
index 82870b72..46f133b0 100644
--- a/docs/content/usage.md
+++ b/docs/content/usage.md
@@ -33,18 +33,56 @@ You can specify multiple `--file` and `--out` arguments. The same number of each
### `--input-dir` and `--output-dir`
-For processing multiple templates in a directory you can use `--input-dir` and `--output-dir` together. In this case all files in input directory will be processed as templates and the resulting files stored in `--output-dir`. The output directory will be created if it does not exist and the directory structure of the input directory will be preserved.
+For processing multiple templates in a directory you can use `--input-dir` and `--output-dir` together. In this case all files in input directory will be processed as templates and the resulting files stored in `--output-dir`. The output directory will be created if it does not exist and the directory structure of the input directory will be preserved.
You can use `.gomplateignore` to ignore some files in the input directory, with similar syntax and behaviour to [.gitignore](https://git-scm.com/docs/gitignore) files.
Example:
```bash
- # Process all files in directory "templates" with the datasource given
- # and store the files with the same directory structure in "config"
+# Process all files in directory "templates" with the datasource given
+# and store the files with the same directory structure in "config"
gomplate --input-dir=templates --output-dir=config --datasource config=config.yaml
```
+### `--output-map`
+
+Sometimes a 1-to-1 mapping betwen input filenames and output filenames is not desirable. For these cases, you can supply a template string as the argument to `--output-map`. The template string is interpreted as a regular gomplate template, and all datasources and external nested templates are available to the output map template.
+
+A new [context][] is provided, with the input filename is available at `.in`, and the original context is available at `.ctx`. For convenience, any context keys not conflicting with `in` or `ctx` are also copied.
+
+All whitespace on the left or right sides of the output is trimmed.
+
+For example, given an input directory `in/` containing files with the extension `.yaml.tmpl`, if we want to rename those to `.yaml`:
+
+```console
+$ gomplate --input-dir=in/ --output-map='out/{{ .in | strings.ReplaceAll ".yaml.tmpl" ".yaml" }}'
+```
+
+#### Referencing complex output map template files
+
+It may be useful to store more complex output map templates in a file. This can be done with [external templates][].
+
+Consider a template `out.t`:
+
+```
+{{- /* .in may contain a directory name - we want to preserve that */ -}}
+{{ $f := filepath.Base .in -}}
+out/{{ .in | strings.ReplaceAll $f (index .filemap $f) }}.out
+```
+
+And a datasource `filemap.json`:
+
+```json
+{ "eins.txt": "uno", "deux.txt": "dos" }
+```
+
+We can blend these two together:
+
+```console
+$ gomplate -t out=out.t -c filemap.json --input-dir=in --output-map='{{ template "out" }}'
+```
+
### Ignorefile
You can use ignore file `.gomplateignore` to ignore some files, have the similar behavior to the [.gitignore](https://git-scm.com/docs/gitignore) file.
@@ -104,7 +142,7 @@ Use `--left-delim`/`--right-delim` or set `$GOMPLATE_LEFT_DELIM`/`$GOMPLATE_RIGH
### `--template`/`-t`
-Add a nested template that can be referenced by the main input template(s) with the [`template`](https://golang.org/pkg/text/template/#hdr-Actions) built-in. Specify multiple times to add multiple template references.
+Add a nested template that can be referenced by the main input template(s) with the [`template`](https://golang.org/pkg/text/template/#hdr-Actions) built-in or the functions in the [`tmpl`](../functions/tmpl/) namespace. Specify multiple times to add multiple template references.
A few different forms are valid:
@@ -164,3 +202,7 @@ $ gomplate -i '{{ print " \n" }}' -o out
$ cat out
cat: out: No such file or directory
```
+
+[default context]: ../syntax/#the-context
+[context]: ../syntax/#the-context
+[external templates]: ../syntax/#external-templates
diff --git a/gomplate.go b/gomplate.go
index 0e08f0ce..4f7ef146 100644
--- a/gomplate.go
+++ b/gomplate.go
@@ -1,14 +1,17 @@
package gomplate
import (
+ "bytes"
"io"
"os"
"path"
+ "path/filepath"
"strings"
"text/template"
"time"
"github.com/hairyhenderson/gomplate/data"
+ "github.com/pkg/errors"
"github.com/spf13/afero"
)
@@ -127,7 +130,7 @@ func RunTemplates(o *Config) error {
func (g *gomplate) runTemplates(o *Config) error {
start := time.Now()
- tmpl, err := gatherTemplates(o)
+ tmpl, err := gatherTemplates(o, chooseNamer(o, g))
Metrics.GatherDuration = time.Since(start)
if err != nil {
Metrics.Errors++
@@ -148,3 +151,50 @@ func (g *gomplate) runTemplates(o *Config) error {
}
return nil
}
+
+func chooseNamer(o *Config, g *gomplate) func(string) (string, error) {
+ if o.OutputMap == "" {
+ return simpleNamer(o.OutputDir)
+ }
+ return mappingNamer(o.OutputMap, g)
+}
+
+func simpleNamer(outDir string) func(inPath string) (string, error) {
+ return func(inPath string) (string, error) {
+ outPath := filepath.Join(outDir, inPath)
+ return filepath.Clean(outPath), nil
+ }
+}
+
+func mappingNamer(outMap string, g *gomplate) func(string) (string, error) {
+ return func(inPath string) (string, error) {
+ out := &bytes.Buffer{}
+ t := &tplate{
+ name: "<OutputMap>",
+ contents: outMap,
+ target: out,
+ }
+ tpl, err := t.toGoTemplate(g)
+ if err != nil {
+ return "", err
+ }
+ ctx := &context{}
+ switch c := g.context.(type) {
+ case *context:
+ for k, v := range *c {
+ if k != "in" && k != "ctx" {
+ (*ctx)[k] = v
+ }
+ }
+ }
+ (*ctx)["ctx"] = g.context
+ (*ctx)["in"] = inPath
+
+ err = tpl.Execute(t.target, ctx)
+ if err != nil {
+ return "", errors.Wrapf(err, "failed to render outputMap with ctx %+v and inPath %s", ctx, inPath)
+ }
+
+ return filepath.Clean(strings.TrimSpace(out.String())), nil
+ }
+}
diff --git a/gomplate_test.go b/gomplate_test.go
index 5bb7c69d..83e47adc 100644
--- a/gomplate_test.go
+++ b/gomplate_test.go
@@ -5,6 +5,7 @@ import (
"io"
"net/http/httptest"
"os"
+ "path/filepath"
"testing"
"github.com/spf13/afero"
@@ -238,3 +239,28 @@ func TestParseTemplateArgs(t *testing.T) {
_, err = parseTemplateArgs([]string{"bogus.t"})
assert.Error(t, err)
}
+
+func TestSimpleNamer(t *testing.T) {
+ n := simpleNamer("out/")
+ out, err := n("file")
+ assert.NoError(t, err)
+ expected := filepath.FromSlash("out/file")
+ assert.Equal(t, expected, out)
+}
+
+func TestMappingNamer(t *testing.T) {
+ g := &gomplate{funcMap: map[string]interface{}{
+ "foo": func() string { return "foo" },
+ }}
+ n := mappingNamer("out/{{ .in }}", g)
+ out, err := n("file")
+ assert.NoError(t, err)
+ expected := filepath.FromSlash("out/file")
+ assert.Equal(t, expected, out)
+
+ n = mappingNamer("out/{{ foo }}{{ .in }}", g)
+ out, err = n("file")
+ assert.NoError(t, err)
+ expected = filepath.FromSlash("out/foofile")
+ assert.Equal(t, expected, out)
+}
diff --git a/template.go b/template.go
index 7697cef2..78736b52 100644
--- a/template.go
+++ b/template.go
@@ -96,7 +96,7 @@ func (t *tplate) addTarget() (err error) {
// gatherTemplates - gather and prepare input template(s) and output file(s) for rendering
// nolint: gocyclo
-func gatherTemplates(o *Config) (templates []*tplate, err error) {
+func gatherTemplates(o *Config, outFileNamer func(string) (string, error)) (templates []*tplate, err error) {
o.defaults()
mode, modeOverride, err := o.getMode()
if err != nil {
@@ -112,11 +112,9 @@ func gatherTemplates(o *Config) (templates []*tplate, err error) {
modeOverride: modeOverride,
targetPath: o.OutputFiles[0],
}}
- }
-
- // input dirs presume output dirs are set too
- if o.InputDir != "" {
- templates, err = walkDir(o.InputDir, o.OutputDir, o.ExcludeGlob, mode, modeOverride)
+ } else if o.InputDir != "" {
+ // input dirs presume output dirs are set too
+ templates, err = walkDir(o.InputDir, outFileNamer, o.ExcludeGlob, mode, modeOverride)
if err != nil {
return nil, err
}
@@ -150,9 +148,8 @@ func processTemplates(templates []*tplate) ([]*tplate, error) {
// walkDir - given an input dir `dir` and an output dir `outDir`, and a list
// of .gomplateignore and exclude globs (if any), walk the input directory and create a list of
// tplate objects, and an error, if any.
-func walkDir(dir, outDir string, excludeGlob []string, mode os.FileMode, modeOverride bool) ([]*tplate, error) {
+func walkDir(dir string, outFileNamer func(string) (string, error), excludeGlob []string, mode os.FileMode, modeOverride bool) ([]*tplate, error) {
dir = filepath.Clean(dir)
- outDir = filepath.Clean(outDir)
dirStat, err := fs.Stat(dir)
if err != nil {
@@ -175,7 +172,10 @@ func walkDir(dir, outDir string, excludeGlob []string, mode os.FileMode, modeOve
files := matches.UnmatchedFiles
for _, file := range files {
nextInPath := filepath.Join(dir, file)
- nextOutPath := filepath.Join(outDir, file)
+ nextOutPath, err := outFileNamer(file)
+ if err != nil {
+ return nil, err
+ }
if mode == 0 {
stat, perr := fs.Stat(nextInPath)
diff --git a/template_test.go b/template_test.go
index 123eb58a..14fe6ed3 100644
--- a/template_test.go
+++ b/template_test.go
@@ -92,13 +92,13 @@ func TestGatherTemplates(t *testing.T) {
afero.WriteFile(fs, "in/2", []byte("bar"), 0644)
afero.WriteFile(fs, "in/3", []byte("baz"), 0644)
- templates, err := gatherTemplates(&Config{})
+ templates, err := gatherTemplates(&Config{}, nil)
assert.NoError(t, err)
assert.Len(t, templates, 1)
templates, err = gatherTemplates(&Config{
Input: "foo",
- })
+ }, nil)
assert.NoError(t, err)
assert.Len(t, templates, 1)
assert.Equal(t, "foo", templates[0].contents)
@@ -107,7 +107,7 @@ func TestGatherTemplates(t *testing.T) {
templates, err = gatherTemplates(&Config{
Input: "foo",
OutputFiles: []string{"out"},
- })
+ }, nil)
assert.NoError(t, err)
assert.Len(t, templates, 1)
assert.Equal(t, "out", templates[0].targetPath)
@@ -120,7 +120,7 @@ func TestGatherTemplates(t *testing.T) {
templates, err = gatherTemplates(&Config{
InputFiles: []string{"foo"},
OutputFiles: []string{"out"},
- })
+ }, nil)
assert.NoError(t, err)
assert.Len(t, templates, 1)
assert.Equal(t, "bar", templates[0].contents)
@@ -135,7 +135,7 @@ func TestGatherTemplates(t *testing.T) {
InputFiles: []string{"foo"},
OutputFiles: []string{"out"},
OutMode: "755",
- })
+ }, nil)
assert.NoError(t, err)
assert.Len(t, templates, 1)
assert.Equal(t, "bar", templates[0].contents)
@@ -149,7 +149,7 @@ func TestGatherTemplates(t *testing.T) {
templates, err = gatherTemplates(&Config{
InputDir: "in",
OutputDir: "out",
- })
+ }, simpleNamer("out"))
assert.NoError(t, err)
assert.Len(t, templates, 3)
assert.Equal(t, "foo", templates[0].contents)
diff --git a/template_unix_test.go b/template_unix_test.go
index 2e7ddc70..930b2835 100644
--- a/template_unix_test.go
+++ b/template_unix_test.go
@@ -15,7 +15,7 @@ func TestWalkDir(t *testing.T) {
defer func() { fs = origfs }()
fs = afero.NewMemMapFs()
- _, err := walkDir("/indir", "/outdir", nil, 0, false)
+ _, err := walkDir("/indir", simpleNamer("/outdir"), nil, 0, false)
assert.Error(t, err)
_ = fs.MkdirAll("/indir/one", 0777)
@@ -24,7 +24,7 @@ func TestWalkDir(t *testing.T) {
afero.WriteFile(fs, "/indir/one/bar", []byte("bar"), 0644)
afero.WriteFile(fs, "/indir/two/baz", []byte("baz"), 0644)
- templates, err := walkDir("/indir", "/outdir", []string{"*/two"}, 0, false)
+ templates, err := walkDir("/indir", simpleNamer("/outdir"), []string{"*/two"}, 0, false)
assert.NoError(t, err)
expected := []*tplate{
diff --git a/template_windows_test.go b/template_windows_test.go
index 52357f8a..824019b8 100644
--- a/template_windows_test.go
+++ b/template_windows_test.go
@@ -15,7 +15,7 @@ func TestWalkDir(t *testing.T) {
defer func() { fs = origfs }()
fs = afero.NewMemMapFs()
- _, err := walkDir(`C:\indir`, `C:\outdir`, nil, 0, false)
+ _, err := walkDir(`C:\indir`, simpleNamer(`C:\outdir`), nil, 0, false)
assert.Error(t, err)
_ = fs.MkdirAll(`C:\indir\one`, 0777)
@@ -24,7 +24,7 @@ func TestWalkDir(t *testing.T) {
afero.WriteFile(fs, `C:\indir\one\bar`, []byte("bar"), 0644)
afero.WriteFile(fs, `C:\indir\two\baz`, []byte("baz"), 0644)
- templates, err := walkDir(`C:\indir`, `C:\outdir`, []string{`*\two`}, 0, false)
+ templates, err := walkDir(`C:\indir`, simpleNamer(`C:\outdir`), []string{`*\two`}, 0, false)
assert.NoError(t, err)
expected := []*tplate{
diff --git a/tests/integration/basic_test.go b/tests/integration/basic_test.go
index 9789716a..31018806 100644
--- a/tests/integration/basic_test.go
+++ b/tests/integration/basic_test.go
@@ -140,6 +140,12 @@ func (s *BasicSuite) TestFlagRules(c *C) {
ExitCode: 1,
Err: "--output-dir can not be used together with --out",
})
+
+ result = icmd.RunCommand(GomplateBin, "--output-map", ".", "--out", "param")
+ result.Assert(c, icmd.Expected{
+ ExitCode: 1,
+ Err: "--output-map can not be used together with --out or --output-dir",
+ })
}
func (s *BasicSuite) TestDelimsChangedThroughOpts(c *C) {
diff --git a/tests/integration/inputdir_test.go b/tests/integration/inputdir_test.go
index a9df9823..341aab57 100644
--- a/tests/integration/inputdir_test.go
+++ b/tests/integration/inputdir_test.go
@@ -24,6 +24,11 @@ var _ = Suite(&InputDirSuite{})
func (s *InputDirSuite) SetUpTest(c *C) {
s.tmpDir = fs.NewDir(c, "gomplate-inttests",
fs.WithFile("config.yml", "one: eins\ntwo: deux\n"),
+ fs.WithFile("filemap.json", `{"eins.txt":"uno","deux.txt":"dos"}`),
+ fs.WithFile("out.t", `{{- /* .in may contain a directory name - we want to preserve that */ -}}
+{{ $f := filepath.Base .in -}}
+out/{{ .in | strings.ReplaceAll $f (index .filemap $f) }}.out
+`),
fs.WithDir("in",
fs.WithFile("eins.txt", `{{ (ds "config").one }}`, fs.WithMode(0644)),
fs.WithDir("inner",
@@ -116,6 +121,86 @@ func (s *InputDirSuite) TestInputDirWithModeOverride(c *C) {
}
}
+func (s *InputDirSuite) TestOutputMapInline(c *C) {
+ result := icmd.RunCmd(icmd.Command(GomplateBin,
+ "--input-dir", s.tmpDir.Join("in"),
+ "--output-map", `OUT/{{ strings.ToUpper .in }}`,
+ "-d", "config.yml",
+ ), func(c *icmd.Cmd) {
+ c.Dir = s.tmpDir.Path()
+ })
+ result.Assert(c, icmd.Success)
+
+ files, err := ioutil.ReadDir(s.tmpDir.Join("OUT"))
+ assert.NilError(c, err)
+ tassert.Len(c, files, 2)
+
+ files, err = ioutil.ReadDir(s.tmpDir.Join("OUT", "INNER"))
+ assert.NilError(c, err)
+ tassert.Len(c, files, 1)
+
+ testdata := []struct {
+ path string
+ mode os.FileMode
+ content string
+ }{
+ {s.tmpDir.Join("OUT", "EINS.TXT"), 0644, "eins"},
+ {s.tmpDir.Join("OUT", "INNER", "DEUX.TXT"), 0644, "deux"},
+ }
+ for _, v := range testdata {
+ info, err := os.Stat(v.path)
+ assert.NilError(c, err)
+ // chmod support on Windows is pretty weak for now
+ if runtime.GOOS != "windows" {
+ assert.Equal(c, v.mode, info.Mode())
+ }
+ content, err := ioutil.ReadFile(v.path)
+ assert.NilError(c, err)
+ assert.Equal(c, v.content, string(content))
+ }
+}
+
+func (s *InputDirSuite) TestOutputMapExternal(c *C) {
+ result := icmd.RunCmd(icmd.Command(GomplateBin,
+ "--input-dir", s.tmpDir.Join("in"),
+ "--output-map", `{{ template "out" . }}`,
+ "-t", "out=out.t",
+ "-c", "filemap.json",
+ "-d", "config.yml",
+ ), func(c *icmd.Cmd) {
+ c.Dir = s.tmpDir.Path()
+ })
+ result.Assert(c, icmd.Success)
+
+ files, err := ioutil.ReadDir(s.tmpDir.Join("out"))
+ assert.NilError(c, err)
+ tassert.Len(c, files, 2)
+
+ files, err = ioutil.ReadDir(s.tmpDir.Join("out", "inner"))
+ assert.NilError(c, err)
+ tassert.Len(c, files, 1)
+
+ testdata := []struct {
+ path string
+ mode os.FileMode
+ content string
+ }{
+ {s.tmpDir.Join("out", "uno.out"), 0644, "eins"},
+ {s.tmpDir.Join("out", "inner", "dos.out"), 0644, "deux"},
+ }
+ for _, v := range testdata {
+ info, err := os.Stat(v.path)
+ assert.NilError(c, err)
+ // chmod support on Windows is pretty weak for now
+ if runtime.GOOS != "windows" {
+ assert.Equal(c, v.mode, info.Mode())
+ }
+ content, err := ioutil.ReadFile(v.path)
+ assert.NilError(c, err)
+ assert.Equal(c, v.content, string(content))
+ }
+}
+
func (s *InputDirSuite) TestDefaultOutputDir(c *C) {
result := icmd.RunCmd(icmd.Command(GomplateBin,
"--input-dir", s.tmpDir.Join("in"),