Compare commits
No commits in common. "0682a0d24931a9ba4f96a460e6122e1e0b3fbd6d" and "0066db548728068cb761084a0bb8f255e9525837" have entirely different histories.
0682a0d249
...
0066db5487
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,7 +4,6 @@
|
||||
*.so
|
||||
*.dylib
|
||||
homemaker
|
||||
build
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
50
Makefile
50
Makefile
@ -1,50 +0,0 @@
|
||||
appname := homemaker
|
||||
sources := $(wildcard *.go)
|
||||
|
||||
build = GOOS=$(1) GOARCH=$(2) go build -o build/$(appname)$(3)
|
||||
tar = cd build && tar -cvzf $(appname)_$(1)_$(2).tar.gz $(appname)$(3) && rm $(appname)$(3)
|
||||
zip = cd build && zip $(appname)_$(1)_$(2).zip $(appname)$(3) && rm $(appname)$(3)
|
||||
|
||||
.PHONY: all windows darwin linux clean
|
||||
|
||||
all: windows darwin linux
|
||||
|
||||
clean:
|
||||
rm -rf build/
|
||||
|
||||
# linux builds
|
||||
linux: build/$(appname)_linux_arm.tar.gz build/$(appname)_linux_arm64.tar.gz build/$(appname)_linux_386.tar.gz build/$(appname)_linux_amd64.tar.gz
|
||||
|
||||
build/$(appname)_linux_386.tar.gz: $(sources)
|
||||
$(call build,linux,386,)
|
||||
$(call tar,linux,386)
|
||||
|
||||
build/$(appname)_linux_amd64.tar.gz: $(sources)
|
||||
$(call build,linux,amd64,)
|
||||
$(call tar,linux,amd64)
|
||||
|
||||
build/$(appname)_linux_arm.tar.gz: $(sources)
|
||||
$(call build,linux,arm,)
|
||||
$(call tar,linux,arm)
|
||||
|
||||
build/$(appname)_linux_arm64.tar.gz: $(sources)
|
||||
$(call build,linux,arm64,)
|
||||
$(call tar,linux,arm64)
|
||||
|
||||
# darwin builds
|
||||
darwin: build/$(appname)_darwin_amd64.tar.gz
|
||||
|
||||
build/$(appname)_darwin_amd64.tar.gz: $(sources)
|
||||
$(call build,darwin,amd64,)
|
||||
$(call tar,darwin,amd64)
|
||||
|
||||
# windows builds
|
||||
windows: build/$(appname)_windows_386.zip build/$(appname)_windows_amd64.zip
|
||||
|
||||
build/$(appname)_windows_386.zip: $(sources)
|
||||
$(call build,windows,386,.exe)
|
||||
$(call zip,windows,386,.exe)
|
||||
|
||||
build/$(appname)_windows_amd64.zip: $(sources)
|
||||
$(call build,windows,amd64,.exe)
|
||||
$(call zip,windows,amd64,.exe)
|
14
README.md
14
README.md
@ -7,7 +7,7 @@ installation, has no dependencies and makes use of simple configuration file str
|
||||
[make](https://en.wikipedia.org/wiki/Make_%28software%29) to generate symlinks and execute system commands to aid in
|
||||
configuring a new system for use.
|
||||
|
||||
![](img/demo.gif)
|
||||
![](https://foosoft.net/projects/homemaker/img/demo.gif)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -68,10 +68,18 @@ your needs.
|
||||
If you already have the Go environment and toolchain set up, you can get the latest version by running:
|
||||
|
||||
```
|
||||
go install foosoft.net/projects/homemaker@latest
|
||||
$ go get github.com/FooSoft/homemaker
|
||||
```
|
||||
|
||||
Otherwise, you can use the [pre-built binaries](https://github.com/FooSoft/homemaker/releases) from the project page.
|
||||
Otherwise, you can use the pre-built binaries for the platforms below:
|
||||
|
||||
* [homemaker\_darwin\_386.tar.gz](https://foosoft.net/projects/homemaker/dl/homemaker_darwin_386.tar.gz)
|
||||
* [homemaker\_darwin\_amd64.tar.gz](https://foosoft.net/projects/homemaker/dl/homemaker_darwin_amd64.tar.gz)
|
||||
* [homemaker\_linux\_386.tar.gz](https://foosoft.net/projects/homemaker/dl/homemaker_linux_386.tar.gz)
|
||||
* [homemaker\_linux\_amd64.tar.gz](https://foosoft.net/projects/homemaker/dl/homemaker_linux_amd64.tar.gz)
|
||||
* [homemaker\_linux\_arm.tar.gz](https://foosoft.net/projects/homemaker/dl/homemaker_linux_arm.tar.gz)
|
||||
* [homemaker\_windows\_386.tar.gz](https://foosoft.net/projects/homemaker/dl/homemaker_windows_386.tar.gz)
|
||||
* [homemaker\_windows\_amd64.tar.gz](https://foosoft.net/projects/homemaker/dl/homemaker_windows_amd64.tar.gz)
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"path"
|
||||
|
||||
"github.com/naoina/toml"
|
||||
"gopkg.in/yaml.v2"
|
||||
@ -50,7 +50,7 @@ func newConfig(filename string) (*config, error) {
|
||||
}
|
||||
|
||||
conf := &config{handled: make(map[string]bool)}
|
||||
switch filepath.Ext(filename) {
|
||||
switch path.Ext(filename) {
|
||||
case ".json":
|
||||
if err := json.Unmarshal(bytes, &conf); err != nil {
|
||||
return nil, err
|
||||
|
12
go.mod
12
go.mod
@ -1,13 +1,7 @@
|
||||
module foosoft.net/projects/homemaker
|
||||
|
||||
go 1.18
|
||||
module github.com/FooSoft/homemaker
|
||||
|
||||
require (
|
||||
github.com/naoina/toml v0.1.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/naoina/go-stringutil v0.1.0 // indirect
|
||||
github.com/naoina/toml v0.1.1
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
||||
|
7
go.sum
7
go.sum
@ -1,10 +1,7 @@
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8=
|
||||
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
11
homemaker.go
11
homemaker.go
@ -27,8 +27,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"path"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -43,7 +42,7 @@ const (
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [options] conf src\n", filepath.Base(os.Args[0]))
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [options] conf src\n", path.Base(os.Args[0]))
|
||||
fmt.Fprintf(os.Stderr, "https://foosoft.net/projects/homemaker/\n\n")
|
||||
fmt.Fprintf(os.Stderr, "Parameters:\n")
|
||||
flag.PrintDefaults()
|
||||
@ -51,7 +50,7 @@ func usage() {
|
||||
|
||||
func main() {
|
||||
taskName := flag.String("task", "default", "name of task to execute")
|
||||
dstDir := flag.String("dest", "", "target directory for tasks")
|
||||
dstDir := flag.String("dest", os.Getenv("HOME"), "target directory for tasks")
|
||||
force := flag.Bool("force", true, "create parent directories to target")
|
||||
clobber := flag.Bool("clobber", false, "delete files and directories at target")
|
||||
verbose := flag.Bool("verbose", false, "verbose output")
|
||||
@ -95,10 +94,6 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(*dstDir) == "" {
|
||||
*dstDir, _ = os.UserHomeDir()
|
||||
}
|
||||
|
||||
conf.srcDir = makeAbsPath(flag.Arg(1))
|
||||
conf.dstDir = makeAbsPath(*dstDir)
|
||||
conf.variant = *variant
|
||||
|
BIN
img/demo.gif
BIN
img/demo.gif
Binary file not shown.
Before Width: | Height: | Size: 647 KiB |
60
link.go
60
link.go
@ -26,10 +26,51 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func cleanPath(loc string, flags int) error {
|
||||
if info, _ := os.Lstat(loc); info != nil {
|
||||
if info.Mode()&os.ModeSymlink == 0 {
|
||||
if flags&flagClobber != 0 || prompt("clobber path", loc) {
|
||||
if flags&flagVerbose != 0 {
|
||||
log.Printf("clobbering path: %s", loc)
|
||||
}
|
||||
if err := os.RemoveAll(loc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if flags&flagVerbose != 0 {
|
||||
log.Printf("removing symlink: %s", loc)
|
||||
}
|
||||
if err := os.Remove(loc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createPath(loc string, flags int, mode os.FileMode) error {
|
||||
parentDir := path.Dir(loc)
|
||||
|
||||
if _, err := os.Stat(parentDir); os.IsNotExist(err) {
|
||||
if flags&flagForce != 0 || prompt("force create path", parentDir) {
|
||||
if flags&flagVerbose != 0 {
|
||||
log.Printf("force creating path: %s", parentDir)
|
||||
}
|
||||
if err := os.MkdirAll(parentDir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLink(params []string) (srcPath, dstPath string, mode os.FileMode, err error) {
|
||||
length := len(params)
|
||||
if length < 1 || length > 3 {
|
||||
@ -65,13 +106,13 @@ func processLink(params []string, conf *config) error {
|
||||
}
|
||||
|
||||
srcPathAbs := srcPath
|
||||
if !filepath.IsAbs(srcPathAbs) {
|
||||
srcPathAbs = filepath.Join(conf.srcDir, srcPath)
|
||||
if !path.IsAbs(srcPathAbs) {
|
||||
srcPathAbs = path.Join(conf.srcDir, srcPath)
|
||||
}
|
||||
|
||||
dstPathAbs := dstPath
|
||||
if !filepath.IsAbs(dstPathAbs) {
|
||||
dstPathAbs = filepath.Join(conf.dstDir, dstPath)
|
||||
if !path.IsAbs(dstPathAbs) {
|
||||
dstPathAbs = path.Join(conf.dstDir, dstPath)
|
||||
}
|
||||
|
||||
if conf.flags&flagUnlink != flagUnlink {
|
||||
@ -83,13 +124,9 @@ func processLink(params []string, conf *config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
pathCleaned, err := cleanPath(dstPathAbs, conf.flags)
|
||||
if err != nil {
|
||||
if err := try(func() error { return cleanPath(dstPathAbs, conf.flags) }); err != nil {
|
||||
return err
|
||||
}
|
||||
if !pathCleaned {
|
||||
return nil
|
||||
}
|
||||
|
||||
if conf.flags&flagVerbose != 0 {
|
||||
log.Printf("linking %s to %s", srcPathAbs, dstPathAbs)
|
||||
@ -104,7 +141,6 @@ func processLink(params []string, conf *config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = cleanPath(dstPathAbs, conf.flags)
|
||||
return err
|
||||
return try(func() error { return cleanPath(dstPathAbs, conf.flags) })
|
||||
}
|
||||
}
|
||||
|
16
template.go
16
template.go
@ -26,7 +26,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
@ -79,13 +79,13 @@ func processTemplate(params []string, conf *config) (err error) {
|
||||
}
|
||||
|
||||
srcPathAbs := srcPath
|
||||
if !filepath.IsAbs(srcPathAbs) {
|
||||
srcPathAbs = filepath.Join(conf.srcDir, srcPath)
|
||||
if !path.IsAbs(srcPathAbs) {
|
||||
srcPathAbs = path.Join(conf.srcDir, srcPath)
|
||||
}
|
||||
|
||||
dstPathAbs := dstPath
|
||||
if !filepath.IsAbs(dstPathAbs) {
|
||||
dstPathAbs = filepath.Join(conf.dstDir, dstPath)
|
||||
if !path.IsAbs(dstPathAbs) {
|
||||
dstPathAbs = path.Join(conf.dstDir, dstPath)
|
||||
}
|
||||
|
||||
if _, err = os.Stat(srcPathAbs); os.IsNotExist(err) {
|
||||
@ -96,13 +96,9 @@ func processTemplate(params []string, conf *config) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
pathCleaned, err := cleanPath(dstPathAbs, conf.flags)
|
||||
if err != nil {
|
||||
if err = try(func() error { return cleanPath(dstPathAbs, conf.flags) }); err != nil {
|
||||
return err
|
||||
}
|
||||
if !pathCleaned {
|
||||
return nil
|
||||
}
|
||||
|
||||
if conf.flags&flagVerbose != 0 {
|
||||
log.Printf("process template %s to %s", srcPathAbs, dstPathAbs)
|
||||
|
46
util.go
46
util.go
@ -47,52 +47,6 @@ func makeAbsPath(path string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
func cleanPath(loc string, flags int) (bool, error) {
|
||||
if info, _ := os.Lstat(loc); info != nil {
|
||||
if info.Mode()&os.ModeSymlink == 0 {
|
||||
shouldContinue := false
|
||||
if flags&flagClobber == 0 {
|
||||
shouldContinue = prompt("clobber path", loc)
|
||||
}
|
||||
if flags&flagClobber != 0 || shouldContinue {
|
||||
if flags&flagVerbose != 0 {
|
||||
log.Printf("clobbering path: %s", loc)
|
||||
}
|
||||
if err := try(func() error { return os.RemoveAll(loc) }) ; err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
} else {
|
||||
if flags&flagVerbose != 0 {
|
||||
log.Printf("removing symlink: %s", loc)
|
||||
}
|
||||
if err := try(func() error { return os.Remove(loc) }); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func createPath(loc string, flags int, mode os.FileMode) error {
|
||||
parentDir := filepath.Dir(loc)
|
||||
|
||||
if _, err := os.Stat(parentDir); os.IsNotExist(err) {
|
||||
if flags&flagForce != 0 || prompt("force create path", parentDir) {
|
||||
if flags&flagVerbose != 0 {
|
||||
log.Printf("force creating path: %s", parentDir)
|
||||
}
|
||||
if err := os.MkdirAll(parentDir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeVariantNames(name, variant string) []string {
|
||||
if nameParts := strings.Split(name, "__"); len(nameParts) > 1 {
|
||||
variant = nameParts[len(nameParts)-1]
|
||||
|
Loading…
Reference in New Issue
Block a user