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.
|
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
|
Sometimes, just linking a config file is not enough, because the content of the configuration file needs to be adapted
|
||||||
defined in an array called `cmds`, split into an item per each command line argument. All of the commands are executed
|
to the target and we do not want to maintain several different versions of the same file. For such use cases, Homemaker
|
||||||
with `dest` as the working directory (as mentioned previously, this defaults to your home directory). If any command
|
supports templates. The configuration syntax for templates is the same as for links.
|
||||||
returns a nonzero exit code, Homemaker will display an error message and prompt the user to determine if it should
|
|
||||||
|
```
|
||||||
|
[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
|
*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.
|
after the linking phase, you can use the `cmdspre` and `cmdspost` arrays which have similar behavior.
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ const (
|
|||||||
flagVerbose
|
flagVerbose
|
||||||
flagNoCmds
|
flagNoCmds
|
||||||
flagNoLinks
|
flagNoLinks
|
||||||
|
flagNoTemplates
|
||||||
flagNoMacro
|
flagNoMacro
|
||||||
flagUnlink = flagNoCmds | (1 << iota)
|
flagUnlink = flagNoCmds | (1 << iota)
|
||||||
)
|
)
|
||||||
@ -55,6 +56,7 @@ func main() {
|
|||||||
verbose := flag.Bool("verbose", false, "verbose output")
|
verbose := flag.Bool("verbose", false, "verbose output")
|
||||||
nocmds := flag.Bool("nocmds", false, "don't execute commands")
|
nocmds := flag.Bool("nocmds", false, "don't execute commands")
|
||||||
nolinks := flag.Bool("nolinks", false, "don't create links")
|
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")
|
variant := flag.String("variant", "", "execution variant for tasks and macros")
|
||||||
unlink := flag.Bool("unlink", false, "remove existing links instead of creating them")
|
unlink := flag.Bool("unlink", false, "remove existing links instead of creating them")
|
||||||
|
|
||||||
@ -77,6 +79,9 @@ func main() {
|
|||||||
if *nolinks {
|
if *nolinks {
|
||||||
flags |= flagNoLinks
|
flags |= flagNoLinks
|
||||||
}
|
}
|
||||||
|
if *notemplates {
|
||||||
|
flags |= flagNoTemplates
|
||||||
|
}
|
||||||
if *unlink {
|
if *unlink {
|
||||||
flags |= flagUnlink
|
flags |= flagUnlink
|
||||||
}
|
}
|
||||||
|
25
task.go
25
task.go
@ -28,14 +28,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type task struct {
|
type task struct {
|
||||||
Deps []string
|
Deps []string
|
||||||
Links [][]string
|
Links [][]string
|
||||||
CmdsPre [][]string
|
CmdsPre [][]string
|
||||||
Cmds [][]string
|
Cmds [][]string
|
||||||
CmdsPost [][]string
|
CmdsPost [][]string
|
||||||
Envs [][]string
|
Envs [][]string
|
||||||
Accepts [][]string
|
Accepts [][]string
|
||||||
Rejects [][]string
|
Rejects [][]string
|
||||||
|
Templates [][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *task) deps(conf *config) []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 {
|
if conf.flags&flagNoCmds == 0 {
|
||||||
for _, currCmd := range t.CmdsPost {
|
for _, currCmd := range t.CmdsPost {
|
||||||
if err := processCmd(currCmd, true, conf); err != nil {
|
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