Skip to content

Commit

Permalink
Add templatectl
Browse files Browse the repository at this point in the history
  • Loading branch information
fatih committed Oct 2, 2019
1 parent 526e3d7 commit e2b5ab8
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
cmd/templatectl/templatectl
23 changes: 23 additions & 0 deletions README.md
@@ -1,2 +1,25 @@
# templatectl
Simple templating CLI

## Install

```
go get github.com/fatih/templatectl/cmd/templatectl@latest
```

## Usage


```sh
# By default templatectl prints to stdout
echo 'This is {{ env "ENV_FOO" }}' > input.tmpl
export ENV_FOO="foo"
$ templatectl --input input.tmpl
This is foo

# Or output to a file
$ templatectl --input input.tmpl --output templated.txt
$ cat templated.txt
This is foo
```

50 changes: 50 additions & 0 deletions cmd/templatectl/main.go
@@ -0,0 +1,50 @@
package main

import (
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"os"

"github.com/fatih/templatectl/internal/template"
)

func main() {
if err := realMain(); err != nil {
log.Fatalln(err)
}
}

func realMain() error {
input := flag.String("input", "", "File path to process")
output := flag.String("output", "", "Path to save the processed template file(optional)")
flag.Parse()

if *input == "" {
return errors.New("usage: templatectl --input file.tmpl --output exported.txt")
}

t := template.NewTemplate()
buf, err := t.ExecuteFile(*input)
if err != nil {
return fmt.Errorf("error executing file %q template: %s", *input, err)
}

if *output != "" {
info, err := os.Stat(*input)
if err != nil {
return fmt.Errorf("error retrieving file info %q: %s", *output, err)
}

err = ioutil.WriteFile(*output, []byte(buf), info.Mode())
if err != nil {
return fmt.Errorf("error saving processed file %q: %s", *output, err)
}
} else {
fmt.Print(buf)
}

return nil
}
3 changes: 3 additions & 0 deletions go.mod
@@ -0,0 +1,3 @@
module github.com/fatih/templatectl

go 1.13
48 changes: 48 additions & 0 deletions internal/template/template.go
@@ -0,0 +1,48 @@
package template

import (
"bytes"
"io/ioutil"
"os"
"text/template"
)

// NewTemplate returns a new template instance
func NewTemplate() *Template {
return &Template{}
}

// Template is responsible for producing files from the given files
type Template struct{}

// ExecuteFile executes the given template file and returns the final result
func (t *Template) ExecuteFile(file string) (string, error) {
f, err := ioutil.ReadFile(file)
if err != nil {
return "", err
}

return t.Execute(string(f))
}

// Execute executes the given template and returns the final result
func (t *Template) Execute(in string) (string, error) {
funcMap := template.FuncMap{
"env": os.Getenv,
}

tmpl, err := template.New("file").
Funcs(funcMap).
Parse(string(in))
if err != nil {
return "", err
}

var buf bytes.Buffer
err = tmpl.Execute(&buf, nil)
if err != nil {
return "", err
}

return buf.String(), nil
}
46 changes: 46 additions & 0 deletions internal/template/template_test.go
@@ -0,0 +1,46 @@
package template

import (
"os"
"testing"
)

func TestTemplate_Execute(t *testing.T) {
test := []struct {
name string
in string
out string
envs map[string]string
}{
{
name: "template with environment variable",
envs: map[string]string{
"SOME_ENV_VARIABLE_FOO": "foo",
},
in: `This is: {{ env "SOME_ENV_VARIABLE_FOO" }}`,
out: `This is: foo`,
},
}

for _, ts := range test {
t.Run(ts.name, func(t *testing.T) {
for key, val := range ts.envs {
if err := os.Setenv(key, val); err != nil {
t.Fatal(err)
}
}

tmpl := NewTemplate()
out, err := tmpl.Execute(ts.in)
if err != nil {
t.Fatal(err)
}

if ts.out != out {
t.Errorf("test case: %+v \n===== WANT =====\n\n%+v\n\t==== GOT ====\n\n%+v",
ts.name, ts.out, out)
}
})
}

}

0 comments on commit e2b5ab8

Please sign in to comment.