Add template functionality
This commit is contained in:
parent
87f1a4e15c
commit
a4b355ffbd
32
README.md
32
README.md
@ -223,10 +223,34 @@ shown below:
|
||||
|
||||
Homemaker will process the dependency tasks before processing the task itself.
|
||||
|
||||
In addition to creating links, Homemaker is capable of executing commands on a per-task basis. Commands should be
|
||||
defined in an array called `cmds`, split into an item per each command line argument. All of the commands are executed
|
||||
with `dest` as the working directory (as mentioned previously, this defaults to your home directory). If any command
|
||||
returns a nonzero exit code, Homemaker will display an error message and prompt the user to determine if it should
|
||||
Sometimes, just linking a config file is not enough, because the content of the configuration file needs to be adapted
|
||||
to the target and we do not want to maintain several different versions of the same file. For such use cases, Homemaker
|
||||
supports templates. The configuration syntax for templates is the same as for links.
|
||||
|
||||
```
|
||||
[tasks.template]
|
||||
templates = [
|
||||
[".gitconfig"]
|
||||
]
|
||||
```
|
||||
|
||||
In the template file, the [go templating systax](https://godoc.org/text/template) is used for the customization of the
|
||||
config file. With the `.Env` prefix, all environment variables are available. Template example:
|
||||
|
||||
```
|
||||
[user]
|
||||
name = John Doe
|
||||
{{ if eq .Env.USER "john" }}
|
||||
email = john@doe.me
|
||||
{{ else }}
|
||||
email = john.doe@work.com
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
In addition to creating links and processing templates, Homemaker is capable of executing commands on a per-task basis.
|
||||
Commands should be defined in an array called `cmds`, split into an item per each command line argument. All of the commands
|
||||
are executed with `dest` as the working directory (as mentioned previously, this defaults to your home directory). If any
|
||||
command returns a nonzero exit code, Homemaker will display an error message and prompt the user to determine if it should
|
||||
*abort*, *retry*, or *cancel*. Additionally, if you must have explicit control of whether commands execute before or
|
||||
after the linking phase, you can use the `cmdspre` and `cmdspost` arrays which have similar behavior.
|
||||
|
||||
|
@ -36,6 +36,7 @@ const (
|
||||
flagVerbose
|
||||
flagNoCmds
|
||||
flagNoLinks
|
||||
flagNoTemplates
|
||||
flagNoMacro
|
||||
flagUnlink = flagNoCmds | (1 << iota)
|
||||
)
|
||||
@ -55,6 +56,7 @@ func main() {
|
||||
verbose := flag.Bool("verbose", false, "verbose output")
|
||||
nocmds := flag.Bool("nocmds", false, "don't execute commands")
|
||||
nolinks := flag.Bool("nolinks", false, "don't create links")
|
||||
notemplates := flag.Bool("notemplates", false, "don't process templates")
|
||||
variant := flag.String("variant", "", "execution variant for tasks and macros")
|
||||
unlink := flag.Bool("unlink", false, "remove existing links instead of creating them")
|
||||
|
||||
@ -77,6 +79,9 @@ func main() {
|
||||
if *nolinks {
|
||||
flags |= flagNoLinks
|
||||
}
|
||||
if *notemplates {
|
||||
flags |= flagNoTemplates
|
||||
}
|
||||
if *unlink {
|
||||
flags |= flagUnlink
|
||||
}
|
||||
|
25
task.go
25
task.go
@ -28,14 +28,15 @@ import (
|
||||
)
|
||||
|
||||
type task struct {
|
||||
Deps []string
|
||||
Links [][]string
|
||||
CmdsPre [][]string
|
||||
Cmds [][]string
|
||||
CmdsPost [][]string
|
||||
Envs [][]string
|
||||
Accepts [][]string
|
||||
Rejects [][]string
|
||||
Deps []string
|
||||
Links [][]string
|
||||
CmdsPre [][]string
|
||||
Cmds [][]string
|
||||
CmdsPost [][]string
|
||||
Envs [][]string
|
||||
Accepts [][]string
|
||||
Rejects [][]string
|
||||
Templates [][]string
|
||||
}
|
||||
|
||||
func (t *task) deps(conf *config) []string {
|
||||
@ -85,6 +86,14 @@ func (t *task) process(conf *config) error {
|
||||
}
|
||||
}
|
||||
|
||||
if conf.flags&flagNoTemplates == 0 {
|
||||
for _, currTmpl := range t.Templates {
|
||||
if err := processTemplate(currTmpl, conf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if conf.flags&flagNoCmds == 0 {
|
||||
for _, currCmd := range t.CmdsPost {
|
||||
if err := processCmd(currCmd, true, conf); err != nil {
|
||||
|
123
template.go
Normal file
123
template.go
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
|
||||
* Author: Alex Yatskov <alex@foosoft.net>, Lucas Bremgartner <lucas@bremis.ch>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type context struct {
|
||||
}
|
||||
|
||||
func (c *context) Env() map[string]string {
|
||||
env := make(map[string]string)
|
||||
for _, i := range os.Environ() {
|
||||
sep := strings.Index(i, "=")
|
||||
env[i[0:sep]] = i[sep+1:]
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
func parseTemplate(params []string) (srcPath, dstPath string, mode os.FileMode, err error) {
|
||||
length := len(params)
|
||||
if length < 1 || length > 3 {
|
||||
err = fmt.Errorf("invalid template statement")
|
||||
return
|
||||
}
|
||||
|
||||
if length > 2 {
|
||||
var parsed uint64
|
||||
parsed, err = strconv.ParseUint(params[2], 0, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mode = os.FileMode(parsed)
|
||||
} else {
|
||||
mode = 0755
|
||||
}
|
||||
|
||||
dstPath = os.ExpandEnv(params[0])
|
||||
srcPath = dstPath
|
||||
if length > 1 {
|
||||
srcPath = os.ExpandEnv(params[1])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func processTemplate(params []string, conf *config) (err error) {
|
||||
srcPath, dstPath, mode, err := parseTemplate(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcPathAbs := srcPath
|
||||
if !path.IsAbs(srcPathAbs) {
|
||||
srcPathAbs = path.Join(conf.srcDir, srcPath)
|
||||
}
|
||||
|
||||
dstPathAbs := dstPath
|
||||
if !path.IsAbs(dstPathAbs) {
|
||||
dstPathAbs = path.Join(conf.dstDir, dstPath)
|
||||
}
|
||||
|
||||
if _, err = os.Stat(srcPathAbs); os.IsNotExist(err) {
|
||||
return fmt.Errorf("source path %s does not exist in filesystem", srcPathAbs)
|
||||
}
|
||||
|
||||
if err = try(func() error { return createPath(dstPathAbs, conf.flags, mode) }); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = try(func() error { return cleanPath(dstPathAbs, conf.flags) }); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.flags&flagVerbose != 0 {
|
||||
log.Printf("process template %s to %s", srcPathAbs, dstPathAbs)
|
||||
}
|
||||
|
||||
t, err := template.New(srcPath).ParseFiles(srcPathAbs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(dstPathAbs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = f.Close()
|
||||
}()
|
||||
|
||||
return try(func() error {
|
||||
return t.Execute(f, &context{})
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user