diff options
| -rw-r--r-- | config.go | 2 | ||||
| -rw-r--r-- | docs/content/config.md | 10 | ||||
| -rw-r--r-- | docs/content/usage.md | 10 | ||||
| -rw-r--r-- | internal/cmd/config.go | 4 | ||||
| -rw-r--r-- | internal/cmd/config_test.go | 15 | ||||
| -rw-r--r-- | internal/config/configfile.go | 5 | ||||
| -rw-r--r-- | internal/iohelpers/writers.go | 10 | ||||
| -rw-r--r-- | internal/tests/integration/basic_test.go | 3 | ||||
| -rw-r--r-- | internal/tests/integration/gomplateignore_test.go | 136 | ||||
| -rw-r--r-- | render.go | 4 | ||||
| -rw-r--r-- | template.go | 37 | ||||
| -rw-r--r-- | template_test.go | 35 |
12 files changed, 119 insertions, 152 deletions
@@ -136,7 +136,7 @@ func (o *Config) toNewConfig() (*config.Config, error) { LDelim: o.LDelim, RDelim: o.RDelim, Stdin: os.Stdin, - Stdout: &iohelpers.NopCloser{Writer: o.Out}, + Stdout: iohelpers.NopCloser(o.Out), Stderr: os.Stderr, } err := cfg.ParsePluginFlags(o.Plugins) diff --git a/docs/content/config.md b/docs/content/config.md index a260c2d2..a0eaf485 100644 --- a/docs/content/config.md +++ b/docs/content/config.md @@ -447,16 +447,6 @@ Overrides the right template delimiter. rightDelim: '))' ``` -## `suppressEmpty` - -See _[Suppressing empty output](../usage/#suppressing-empty-output)_ - -Suppresses empty output (i.e. output consisting of only whitespace). Can also be set with the `GOMPLATE_SUPPRESS_EMPTY` environment variable. - -```yaml -suppressEmpty: true -``` - ## `templates` See [`--template`/`-t`](../usage/#template-t). diff --git a/docs/content/usage.md b/docs/content/usage.md index 1ac2ca96..3d390978 100644 --- a/docs/content/usage.md +++ b/docs/content/usage.md @@ -424,16 +424,10 @@ hello world See also [`--exec-pipe`](#exec-pipe) for piping output directly into the post-exec command. -## Suppressing empty output +## Empty output -Sometimes it can be desirable to suppress empty output (i.e. output consisting of only whitespace). To do so, set `suppressEmpty: true` in your [config](../config/#suppressempty) file, or `GOMPLATE_SUPPRESS_EMPTY=true` in your environment: +If the template renders to an empty file (i.e. output consisting of only whitespace), gomplate will not write the output. -```console -$ export GOMPLATE_SUPPRESS_EMPTY=true -$ gomplate -i '{{ print " \n" }}' -o out -$ cat out -cat: out: No such file or directory -``` [default context]: ../syntax/#the-context [context]: ../syntax/#the-context diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 62777061..fc64f8e4 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -250,10 +250,6 @@ func applyEnvVars(_ context.Context, cfg *config.Config) (*config.Config, error) cfg.PluginTimeout = t } - if !cfg.SuppressEmpty && conv.ToBool(env.Getenv("GOMPLATE_SUPPRESS_EMPTY", "false")) { - cfg.SuppressEmpty = true - } - if !cfg.Experimental && conv.ToBool(env.Getenv("GOMPLATE_EXPERIMENTAL", "false")) { cfg.Experimental = true } diff --git a/internal/cmd/config_test.go b/internal/cmd/config_test.go index a3ff4935..1c9adfe5 100644 --- a/internal/cmd/config_test.go +++ b/internal/cmd/config_test.go @@ -221,21 +221,6 @@ func TestApplyEnvVars(t *testing.T) { }, { &config.Config{}, - &config.Config{SuppressEmpty: false}, - "GOMPLATE_SUPPRESS_EMPTY", "bogus", - }, - { - &config.Config{}, - &config.Config{SuppressEmpty: true}, - "GOMPLATE_SUPPRESS_EMPTY", "true", - }, - { - &config.Config{SuppressEmpty: true}, - &config.Config{SuppressEmpty: true}, - "GOMPLATE_SUPPRESS_EMPTY", "false", - }, - { - &config.Config{}, &config.Config{Experimental: false}, "GOMPLATE_EXPERIMENTAL", "bogus", }, diff --git a/internal/config/configfile.go b/internal/config/configfile.go index 76fc5306..d255a36c 100644 --- a/internal/config/configfile.go +++ b/internal/config/configfile.go @@ -69,9 +69,8 @@ type Config struct { PluginTimeout time.Duration `yaml:"pluginTimeout,omitempty"` - ExecPipe bool `yaml:"execPipe,omitempty"` - SuppressEmpty bool `yaml:"suppressEmpty,omitempty"` - Experimental bool `yaml:"experimental,omitempty"` + ExecPipe bool `yaml:"execPipe,omitempty"` + Experimental bool `yaml:"experimental,omitempty"` } type experimentalCtxKey struct{} diff --git a/internal/iohelpers/writers.go b/internal/iohelpers/writers.go index 870fa4cb..543c050b 100644 --- a/internal/iohelpers/writers.go +++ b/internal/iohelpers/writers.go @@ -82,17 +82,21 @@ func allWhitespace(p []byte) bool { // NopCloser returns a WriteCloser with a no-op Close method wrapping // the provided io.Writer. -type NopCloser struct { +func NopCloser(w io.Writer) io.WriteCloser { + return &nopCloser{Writer: w} +} + +type nopCloser struct { io.Writer } // Close - implements io.Closer -func (n *NopCloser) Close() error { +func (n *nopCloser) Close() error { return nil } var ( - _ io.WriteCloser = (*NopCloser)(nil) + _ io.WriteCloser = (*nopCloser)(nil) _ io.WriteCloser = (*emptySkipper)(nil) _ io.WriteCloser = (*sameSkipper)(nil) ) diff --git a/internal/tests/integration/basic_test.go b/internal/tests/integration/basic_test.go index 1e61646f..d2c4fa35 100644 --- a/internal/tests/integration/basic_test.go +++ b/internal/tests/integration/basic_test.go @@ -178,8 +178,7 @@ func TestBasic_EmptyOutputSuppression(t *testing.T) { tmpDir := setupBasicTest(t) out := tmpDir.Join("out") o, e, err := cmd(t, "-i", `{{print "\t \n\n\r\n\t\t \v\n"}}`, - "-o", out). - withEnv("GOMPLATE_SUPPRESS_EMPTY", "true").run() + "-o", out).run() assertSuccess(t, o, e, err, "") _, err = os.Stat(out) diff --git a/internal/tests/integration/gomplateignore_test.go b/internal/tests/integration/gomplateignore_test.go index a49d41ed..6679a8b4 100644 --- a/internal/tests/integration/gomplateignore_test.go +++ b/internal/tests/integration/gomplateignore_test.go @@ -70,8 +70,8 @@ func TestGomplateignore_Simple(t *testing.T) { files, err := execute(t, `# all dot files .* *.log`, - tfs.WithFile("empty.log", ""), - tfs.WithFile("rain.txt", "")) + tfs.WithFile("foo.log", "..."), + tfs.WithFile("rain.txt", "...")) require.NoError(t, err) assert.Equal(t, []string{"rain.txt"}, files) @@ -91,12 +91,12 @@ f[o]o/bar tfs.WithDir("foo", tfs.WithDir("bar", tfs.WithDir("tool", - tfs.WithFile("lex.txt", ""), + tfs.WithFile("lex.txt", "..."), ), - tfs.WithFile("1.txt", ""), + tfs.WithFile("1.txt", "..."), ), tfs.WithDir("tar", - tfs.WithFile("2.txt", ""), + tfs.WithFile("2.txt", "..."), ), ), ) @@ -110,10 +110,10 @@ func TestGomplateignore_Root(t *testing.T) { files, err := execute(t, `.gomplateignore /1.txt`, tfs.WithDir("sub", - tfs.WithFile("1.txt", ""), - tfs.WithFile("2.txt", ""), + tfs.WithFile("1.txt", "..."), + tfs.WithFile("2.txt", "..."), ), - tfs.WithFile("1.txt", ""), + tfs.WithFile("1.txt", "..."), ) require.NoError(t, err) @@ -127,14 +127,14 @@ func TestGomplateignore_Exclusion(t *testing.T) { !/e2.txt en/e3.txt !`, - tfs.WithFile("!", ""), - tfs.WithFile("e1.txt", ""), - tfs.WithFile("e2.txt", ""), - tfs.WithFile("e3.txt", ""), + tfs.WithFile("!", "xxx"), + tfs.WithFile("e1.txt", "xxx"), + tfs.WithFile("e2.txt", "xxx"), + tfs.WithFile("e3.txt", "xxx"), tfs.WithDir("en", - tfs.WithFile("e1.txt", ""), - tfs.WithFile("e2.txt", ""), - tfs.WithFile("e3.txt", ""), + tfs.WithFile("e1.txt", "xxx"), + tfs.WithFile("e2.txt", "xxx"), + tfs.WithFile("e3.txt", "xxx"), ), ) @@ -148,13 +148,13 @@ func TestGomplateignore_Nested(t *testing.T) { tfs.WithDir("inner", tfs.WithDir("inner2", tfs.WithFile(".gomplateignore", "moss.ini\n!/jess.ini"), - tfs.WithFile("jess.ini", ""), - tfs.WithFile("moss.ini", "")), + tfs.WithFile("jess.ini", "xxx"), + tfs.WithFile("moss.ini", "xxx")), tfs.WithFile(".gomplateignore", "*.lst\njess.ini"), - tfs.WithFile("2.lst", ""), - tfs.WithFile("foo.md", ""), + tfs.WithFile("2.lst", "xxx"), + tfs.WithFile("foo.md", "xxx"), ), - tfs.WithFile("1.txt", ""), + tfs.WithFile("1.txt", "xxx"), ) require.NoError(t, err) @@ -170,17 +170,17 @@ world.txt`, tfs.WithDir("aa", tfs.WithDir("a1", tfs.WithDir("a2", - tfs.WithFile("hello.txt", ""), - tfs.WithFile("world.txt", "")), - tfs.WithFile("hello.txt", ""), - tfs.WithFile("world.txt", "")), - tfs.WithFile("hello.txt", ""), - tfs.WithFile("world.txt", "")), + tfs.WithFile("hello.txt", "..."), + tfs.WithFile("world.txt", "...")), + tfs.WithFile("hello.txt", "..."), + tfs.WithFile("world.txt", "...")), + tfs.WithFile("hello.txt", "..."), + tfs.WithFile("world.txt", "...")), tfs.WithDir("bb", - tfs.WithFile("hello.txt", ""), - tfs.WithFile("world.txt", "")), - tfs.WithFile("hello.txt", ""), - tfs.WithFile("world.txt", ""), + tfs.WithFile("hello.txt", "..."), + tfs.WithFile("world.txt", "...")), + tfs.WithFile("hello.txt", "..."), + tfs.WithFile("world.txt", "..."), ) require.NoError(t, err) @@ -195,11 +195,11 @@ loss.txt !2.log `, tfs.WithDir("loss.txt", - tfs.WithFile("1.log", ""), - tfs.WithFile("2.log", "")), + tfs.WithFile("1.log", "xxx"), + tfs.WithFile("2.log", "xxx")), tfs.WithDir("foo", - tfs.WithFile("loss.txt", ""), - tfs.WithFile("bare.txt", "")), + tfs.WithFile("loss.txt", "xxx"), + tfs.WithFile("bare.txt", "xxx")), ) require.NoError(t, err) @@ -215,11 +215,11 @@ func TestGomplateignore_LeadingSpace(t *testing.T) { ! dart.log `, tfs.WithDir("inner", - tfs.WithFile(" what.txt", ""), - tfs.WithFile(" dart.log", "")), + tfs.WithFile(" what.txt", "xxx"), + tfs.WithFile(" dart.log", "xxx")), tfs.WithDir("inner2", - tfs.WithFile(" what.txt", "")), - tfs.WithFile(" what.txt", ""), + tfs.WithFile(" what.txt", "xxx")), + tfs.WithFile(" what.txt", "xxx"), ) require.NoError(t, err) @@ -236,19 +236,19 @@ func TestGomplateignore_WithExcludes(t *testing.T) { "--exclude", "sprites/*.ini", }, tfs.WithDir("logs", - tfs.WithFile("archive.zip", ""), - tfs.WithFile("engine.log", ""), - tfs.WithFile("skills.log", "")), + tfs.WithFile("archive.zip", "x"), + tfs.WithFile("engine.log", "x"), + tfs.WithFile("skills.log", "x")), tfs.WithDir("rules", - tfs.WithFile("index.csv", ""), - tfs.WithFile("fire.txt", ""), - tfs.WithFile("earth.txt", "")), + tfs.WithFile("index.csv", "x"), + tfs.WithFile("fire.txt", "x"), + tfs.WithFile("earth.txt", "x")), tfs.WithDir("sprites", - tfs.WithFile("human.csv", ""), - tfs.WithFile("demon.xml", ""), - tfs.WithFile("alien.ini", "")), - tfs.WithFile("manifest.json", ""), - tfs.WithFile("crash.bin", ""), + tfs.WithFile("human.csv", "x"), + tfs.WithFile("demon.xml", "x"), + tfs.WithFile("alien.ini", "x")), + tfs.WithFile("manifest.json", "x"), + tfs.WithFile("crash.bin", "x"), ) require.NoError(t, err) @@ -265,15 +265,15 @@ func TestGomplateignore_WithIncludes(t *testing.T) { "--exclude", "rules/*.txt", }, tfs.WithDir("logs", - tfs.WithFile("archive.zip", ""), - tfs.WithFile("engine.log", ""), - tfs.WithFile("skills.log", "")), + tfs.WithFile("archive.zip", "x"), + tfs.WithFile("engine.log", "x"), + tfs.WithFile("skills.log", "x")), tfs.WithDir("rules", - tfs.WithFile("index.csv", ""), - tfs.WithFile("fire.txt", ""), - tfs.WithFile("earth.txt", "")), - tfs.WithFile("manifest.json", ""), - tfs.WithFile("crash.bin", ""), + tfs.WithFile("index.csv", "x"), + tfs.WithFile("fire.txt", "x"), + tfs.WithFile("earth.txt", "x")), + tfs.WithFile("manifest.json", "x"), + tfs.WithFile("crash.bin", "x"), ) require.NoError(t, err) @@ -290,19 +290,19 @@ func TestGomplateignore_WithExcludeProcessing(t *testing.T) { "--exclude", "sprites/*.ini", }, tfs.WithDir("logs", - tfs.WithFile("archive.zip", ""), - tfs.WithFile("engine.log", ""), - tfs.WithFile("skills.log", "")), + tfs.WithFile("archive.zip", "xxx"), + tfs.WithFile("engine.log", "xxx"), + tfs.WithFile("skills.log", "xxx")), tfs.WithDir("rules", - tfs.WithFile("index.csv", ""), - tfs.WithFile("fire.txt", ""), - tfs.WithFile("earth.txt", "")), + tfs.WithFile("index.csv", "xxx"), + tfs.WithFile("fire.txt", "xxx"), + tfs.WithFile("earth.txt", "xxx")), tfs.WithDir("sprites", - tfs.WithFile("human.csv", ""), - tfs.WithFile("demon.xml", ""), - tfs.WithFile("alien.ini", "")), - tfs.WithFile("manifest.json", ""), - tfs.WithFile("crash.bin", ""), + tfs.WithFile("human.csv", "xxx"), + tfs.WithFile("demon.xml", "xxx"), + tfs.WithFile("alien.ini", "xxx")), + tfs.WithFile("manifest.json", "xxx"), + tfs.WithFile("crash.bin", "xxx"), ) require.NoError(t, err) @@ -6,7 +6,6 @@ import ( "io" "net/http" "net/url" - "os" "sync" "text/template" "time" @@ -244,8 +243,7 @@ func (t *Renderer) renderTemplatesWithData(ctx context.Context, templates []Temp func (t *Renderer) renderTemplate(ctx context.Context, template Template, f template.FuncMap, tmplctx interface{}) error { if template.Writer != nil { - wr, ok := template.Writer.(io.Closer) - if ok && wr != os.Stdout { + if wr, ok := template.Writer.(io.Closer); ok { defer wr.Close() } } diff --git a/template.go b/template.go index dbdabf36..812aafe4 100644 --- a/template.go +++ b/template.go @@ -192,7 +192,7 @@ func gatherTemplates(ctx context.Context, cfg *config.Config, outFileNamer func( case cfg.Input != "": // open the output file - no need to close it, as it will be closed by the // caller later - target, oerr := openOutFile(ctx, cfg.OutputFiles[0], 0o755, mode, modeOverride, cfg.Stdout, cfg.SuppressEmpty) + target, oerr := openOutFile(ctx, cfg.OutputFiles[0], 0o755, mode, modeOverride, cfg.Stdout) if oerr != nil { return nil, fmt.Errorf("openOutFile: %w", oerr) } @@ -367,7 +367,7 @@ func readInFile(ctx context.Context, cfg *config.Config, inFile string, mode os. func getOutfileHandler(ctx context.Context, cfg *config.Config, outFile string, mode os.FileMode, modeOverride bool) (io.Writer, error) { // open the output file - no need to close it, as it will be closed by the // caller later - target, err := openOutFile(ctx, outFile, 0o755, mode, modeOverride, cfg.Stdout, cfg.SuppressEmpty) + target, err := openOutFile(ctx, outFile, 0o755, mode, modeOverride, cfg.Stdout) if err != nil { return nil, fmt.Errorf("openOutFile: %w", err) } @@ -417,29 +417,20 @@ func fileToTemplate(ctx context.Context, cfg *config.Config, inFile, outFile str // openOutFile returns a writer for the given file, creating the file if it // doesn't exist yet, and creating the parent directories if necessary. Will -// defer actual opening until the first write (or the first non-empty write if -// 'suppressEmpty' is true). If the file already exists, it will not be -// overwritten until the first difference is encountered. +// defer actual opening until the first non-empty write. If the file already +// exists, it will not be overwritten until the first difference is encountered. // -// TODO: the 'suppressEmpty' behaviour should be always enabled, in the next -// major release (v4.x). +// TODO: dirMode is always called with 0o755 - should either remove or make it configurable // -//nolint:unparam // TODO: dirMode is always called with 0o755 - should either remove or make it configurable -func openOutFile(ctx context.Context, filename string, dirMode, mode os.FileMode, modeOverride bool, stdout io.Writer, suppressEmpty bool) (out io.Writer, err error) { - if suppressEmpty { - out = iohelpers.NewEmptySkipper(func() (io.Writer, error) { - if filename == "-" { - return stdout, nil - } - return createOutFile(ctx, filename, dirMode, mode, modeOverride) - }) - return out, nil - } - - if filename == "-" { - return stdout, nil - } - return createOutFile(ctx, filename, dirMode, mode, modeOverride) +//nolint:unparam +func openOutFile(ctx context.Context, filename string, dirMode, mode os.FileMode, modeOverride bool, stdout io.Writer) (out io.Writer, err error) { + out = iohelpers.NewEmptySkipper(func() (io.Writer, error) { + if filename == "-" { + return iohelpers.NopCloser(stdout), nil + } + return createOutFile(ctx, filename, dirMode, mode, modeOverride) + }) + return out, nil } func createOutFile(ctx context.Context, filename string, dirMode, mode os.FileMode, modeOverride bool) (out io.WriteCloser, err error) { diff --git a/template_test.go b/template_test.go index 1ac5150f..60607b2f 100644 --- a/template_test.go +++ b/template_test.go @@ -29,8 +29,10 @@ func TestOpenOutFile(t *testing.T) { ctx := datafs.ContextWithFSProvider(context.Background(), datafs.WrappedFSProvider(fsys, "file")) - cfg := &config.Config{Stdout: &bytes.Buffer{}} - f, err := openOutFile(ctx, "/tmp/foo", 0o755, 0o644, false, nil, false) + f, err := openOutFile(ctx, "/tmp/foo", 0o755, 0o644, false, nil) + require.NoError(t, err) + + _, err = f.Write([]byte("hello world")) require.NoError(t, err) wc, ok := f.(io.WriteCloser) @@ -44,9 +46,12 @@ func TestOpenOutFile(t *testing.T) { out := &bytes.Buffer{} - f, err = openOutFile(ctx, "-", 0o755, 0o644, false, out, false) + f, err = openOutFile(ctx, "-", 0o755, 0o644, false, out) require.NoError(t, err) - assert.Equal(t, cfg.Stdout, f) + + _, err = f.Write([]byte("hello world")) + require.NoError(t, err) + assert.Equal(t, "hello world", out.String()) } func TestGatherTemplates(t *testing.T) { @@ -76,16 +81,20 @@ func TestGatherTemplates(t *testing.T) { require.NoError(t, err) assert.Len(t, templates, 1) + buf := &bytes.Buffer{} cfg = &config.Config{ Input: "foo", - Stdout: &bytes.Buffer{}, + Stdout: buf, } cfg.ApplyDefaults() templates, err = gatherTemplates(ctx, cfg, nil) require.NoError(t, err) assert.Len(t, templates, 1) assert.Equal(t, "foo", templates[0].Text) - assert.Equal(t, cfg.Stdout, templates[0].Writer) + + _, err = templates[0].Writer.Write([]byte("hello world")) + require.NoError(t, err) + assert.Equal(t, "hello world", buf.String()) templates, err = gatherTemplates(ctx, &config.Config{ Input: "foo", @@ -106,41 +115,43 @@ func TestGatherTemplates(t *testing.T) { assert.Equal(t, iohelpers.NormalizeFileMode(0o644), info.Mode()) _ = hackpadfs.Remove(fsys, "out") + buf = &bytes.Buffer{} cfg = &config.Config{ InputFiles: []string{"foo"}, OutputFiles: []string{"out"}, - Stdout: &bytes.Buffer{}, + Stdout: buf, } templates, err = gatherTemplates(ctx, cfg, nil) require.NoError(t, err) assert.Len(t, templates, 1) assert.Equal(t, "bar", templates[0].Text) - assert.NotEqual(t, cfg.Stdout, templates[0].Writer) - // assert.Equal(t, os.FileMode(0o600), templates[0].mode) _, err = templates[0].Writer.Write([]byte("hello world")) require.NoError(t, err) + // negative test - we should not be writing to stdout + assert.NotEqual(t, "hello world", buf.String()) info, err = hackpadfs.Stat(fsys, "out") require.NoError(t, err) assert.Equal(t, iohelpers.NormalizeFileMode(0o600), info.Mode()) hackpadfs.Remove(fsys, "out") + buf = &bytes.Buffer{} cfg = &config.Config{ InputFiles: []string{"foo"}, OutputFiles: []string{"out"}, OutMode: "755", - Stdout: &bytes.Buffer{}, + Stdout: buf, } templates, err = gatherTemplates(ctx, cfg, nil) require.NoError(t, err) assert.Len(t, templates, 1) assert.Equal(t, "bar", templates[0].Text) - assert.NotEqual(t, cfg.Stdout, templates[0].Writer) - // assert.Equal(t, iohelpers.NormalizeFileMode(0o755), templates[0].mode) _, err = templates[0].Writer.Write([]byte("hello world")) require.NoError(t, err) + // negative test - we should not be writing to stdout + assert.NotEqual(t, "hello world", buf.String()) info, err = hackpadfs.Stat(fsys, "out") require.NoError(t, err) |
