diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | apply.go | 77 | ||||
-rw-r--r-- | go.mod | 5 | ||||
-rw-r--r-- | go.sum | 2 | ||||
-rw-r--r-- | main.go | 28 | ||||
-rw-r--r-- | template.go | 54 |
6 files changed, 167 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32814ee --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +conf diff --git a/apply.go b/apply.go new file mode 100644 index 0000000..7ea5bf9 --- /dev/null +++ b/apply.go @@ -0,0 +1,77 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "text/template" +) + +func apply(args []string) error { + if len(args) != 0 { + return applyFiles(args) + } + names, err := getFileNames() + if err != nil { + return err + } + return applyFiles(names) +} + +func applyFiles(names []string) error { + for _, name := range names { + fmt.Printf("applying %s\n", name) + if err := applyFile(name); err != nil { + return fmt.Errorf("could not apply %s: %v", name, err) + } + } + return nil +} + +func applyFile(name string) error { + src := filepath.Join(base, "templates", name) + t, err := template.New(filepath.Base(name)).Funcs(buildFuncMap()).ParseFiles(src) + if err != nil { + return fmt.Errorf("could not build template: %v", err) + } + data, err := getTemplateData() + if err != nil { + return err + } + d := filepath.Join(dest, name) + if err = os.MkdirAll(filepath.Dir(d), 0700); err != nil { + return err + } + out, err := os.OpenFile(d, os.O_CREATE|os.O_WRONLY, 0600) + if err != nil { + return err + } + defer out.Close() + if err = t.Execute(out, data); err != nil { + return fmt.Errorf("could not execute template: %v", err) + } + return nil +} + +func getFileNames() ([]string, error) { + var names []string + d := filepath.Join(base, "templates") + err := filepath.Walk(d, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + name, err := filepath.Rel(d, path) + if err != nil { + return err + } + names = append(names, name) + return nil + }) + if err != nil { + return nil, err + } + return names, nil +} @@ -0,0 +1,5 @@ +module git.sr.ht/~benburwell/conf + +go 1.12 + +require github.com/BurntSushi/toml v0.3.1 @@ -0,0 +1,2 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "os" +) + +const ( + base = "/Users/ben/dotfiles" + dest = "/Users/ben" +) + +func main() { + if len(os.Args) < 2 { + fmt.Printf("usage: conf apply [files...]\n") + os.Exit(1) + } + switch os.Args[1] { + case "apply": + if err := apply(os.Args[2:]); err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } + default: + fmt.Printf("unrecognized command: %s\n", os.Args[1]) + os.Exit(1) + } +} diff --git a/template.go b/template.go new file mode 100644 index 0000000..cb6dc2e --- /dev/null +++ b/template.go @@ -0,0 +1,54 @@ +package main + +import ( + "bytes" + "os" + "os/exec" + "path/filepath" + "text/template" + + "github.com/BurntSushi/toml" +) + +type TemplateData struct { + Hostname string + Vars map[string]interface{} +} + +func buildFuncMap() template.FuncMap { + return map[string]interface{}{ + "pass": lookupPassword, + "env": os.Getenv, + } +} + +func lookupPassword(name string) (string, error) { + out, err := exec.Command("pass", name).CombinedOutput() + if err != nil { + return "", err + } + buf := bytes.NewBuffer(out) + line, err := buf.ReadBytes('\n') + if err != nil { + return "", err + } + return string(line), nil +} + +func getTemplateData() (*TemplateData, error) { + var data TemplateData + vars, err := readVars() + if err != nil { + return nil, err + } + data.Vars = vars + return &data, nil +} + +func readVars() (map[string]interface{}, error) { + var vars map[string]interface{} + if _, err := toml.DecodeFile(filepath.Join(base, "vars.toml"), &vars); err != nil { + return nil, err + } + return vars, nil +} |