Skip to content
This repository has been archived by the owner on May 6, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1 from talal/cleanup
Browse files Browse the repository at this point in the history
add init command and minor cleanup
  • Loading branch information
talal authored Feb 6, 2019
2 parents 60aa382 + a3544fb commit 52caf7e
Show file tree
Hide file tree
Showing 29 changed files with 720 additions and 288 deletions.
9 changes: 2 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
ifeq ($(shell uname -s),Darwin)
PREFIX := /usr/local
else
PREFIX := /usr
endif
PREFIX := /usr/local
PKG = github.com/talal/bonclay
VERSION := $(shell util/find_version.sh)

Expand All @@ -27,8 +23,7 @@ build/bonclay: FORCE
$(GO) install $(BUILD_FLAGS) -ldflags '$(LD_FLAGS)' '$(PKG)/cmd/bonclay'

install: FORCE all
install -d -m 0755 "$(DESTDIR)$(PREFIX)/bin"
install -m 0755 build/bonclay "$(DESTDIR)$(PREFIX)/bin/bonclay"
install -D build/bonclay "$(DESTDIR)$(PREFIX)/bin/bonclay"

ifeq ($(GOOS),windows)
release: FORCE release/$(BINARY64)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Bonclay

[![GitHub release](https://img.shields.io/github/release/talal/bonclay.svg)](https://github.com/talal/bonclay/releases/latest)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Build Status](https://travis-ci.org/talal/bonclay.svg?branch=master)](https://travis-ci.org/talal/bonclay)
[![Go Report Card](https://goreportcard.com/badge/github.com/talal/bonclay)](https://goreportcard.com/report/github.com/talal/bonclay)
[![GoDoc](https://godoc.org/github.com/talal/bonclay?status.svg)](https://godoc.org/github.com/talal/bonclay)
[![GitHub Release](https://img.shields.io/github/release/talal/bonclay.svg?style=flat-square)](https://github.com/talal/bonclay/releases/latest)
[![Build Status](https://img.shields.io/travis/talal/bonclay/master.svg?style=flat-square)](https://travis-ci.org/talal/bonclay)
[![Go Report Card](https://goreportcard.com/badge/github.com/talal/bonclay?style=flat-square)](https://goreportcard.com/report/github.com/talal/bonclay)
[![Software License](https://img.shields.io/github/license/talal/bonclay.svg?style=flat-square)](LICENSE)


Bonclay is a fast and minimal backup tool.

Expand Down
105 changes: 46 additions & 59 deletions cmd/bonclay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,86 +5,73 @@ import (
"fmt"
"os"
"strings"
"text/template"

"github.com/talal/bonclay/pkg/mistertwo"
"github.com/talal/bonclay/pkg/tasks"
"github.com/talal/bonclay/pkg/commands"
"github.com/talal/go-bits/cli"
)

// set by the Makefile at linking time
var version string

const usageInfo = `
Usage:
bonclay <command> <config-file>
const appUsageTemplate = `
Usage: bonclay [OPTIONS] <COMMAND> [COMMAND ARGS...]
Flags:
-h, --help
Show usage information.
-v, --version
Show application version.
Bonclay is a fast and minimal backup tool.
Commands:
sync <config-file>
Sync creates symbolic links between 'source:target' pairs defined in the
configuration spec.
Options:{{range .Options}}
{{printf "%-12s %s" .Name .Desc}}{{end}}
backup <config-file>
Backup uses the 'source:target' pairs defined in the configuration spec
to copy the sources to the targets.
restore <config-file>
Restore is the reverse of backup, it uses the 'source:target' pairs defined
in the configuration spec to copy the targets to the sources.
`

const errMsg = `
error: The following required arguments were not provided:
<command> <config-file>
Usage:
bonclay <command> <config-file>
For more information try --help
Commands:{{range .Commands}}
{{printf "%-12s %s" .Name .Desc}}{{end}}
{{"\n"}}
`

var versionFlag bool
var commandRegistry = make(cli.CommandRegistry)

func init() {
flag.BoolVar(&versionFlag, "version", false, "Show application version.")
flag.BoolVar(&versionFlag, "v", false, "Show application version.")
commandRegistry.Add(commands.Backup)
commandRegistry.Add(commands.Restore)
commandRegistry.Add(commands.Sync)
commandRegistry.Add(commands.Init)
}

func main() {
flag.Usage = func() { printAndExit(strings.TrimSpace(usageInfo), 0) }
flag.Parse()

if versionFlag {
printAndExit("bonclay "+version, 0)
fs := flag.NewFlagSet("bonclay", flag.ExitOnError)
versionFlag := fs.Bool("version", false, "Show application version.")

fs.Usage = func() {
var data struct {
Commands []cli.Command
Options []cli.Command
}
data.Commands = commandRegistry.Commands()
data.Options = []cli.Command{
{Name: "--help", Desc: "Show this screen."},
{Name: "--version", Desc: "Show application version."},
}

tmpl := template.Must(template.New("appUsage").Parse(strings.TrimSpace(appUsageTemplate)))
tmpl.Execute(os.Stdout, data)
}

args := flag.Args()
fs.Parse(os.Args[1:])

if len(args) != 2 {
printAndExit(strings.TrimSpace(errMsg), 1)
if *versionFlag {
fmt.Println("bonclay " + version)
return
}

switch args[0] {
case "sync":
c := mistertwo.NewConfiguration(args[1])
tasks.Sync(c)
case "backup":
c := mistertwo.NewConfiguration(args[1])
tasks.Backup(c)
case "restore":
c := mistertwo.NewConfiguration(args[1])
tasks.Restore(c)
default:
printAndExit(fmt.Sprintf(
"error: '%s' is not a valid command, try '--help' for more information", args[0]), 1)
args := fs.Args()
if len(args) == 0 {
fs.Usage()
os.Exit(1)
}
}

func printAndExit(str string, exitCode int) {
fmt.Println(str)
os.Exit(exitCode)
if cmd, ok := commandRegistry[args[0]]; !ok {
fmt.Fprintf(os.Stderr, "bonclay: error: unknown command: %s\n", args[0])
os.Exit(1)
} else {
cmd.Action(args[1:])
}
}
13 changes: 7 additions & 6 deletions doc/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Bonclay requires a configuration file in [YAML format](http://yaml.org). A minim

```yaml
backup:
overwrite: true
overwrite: false

restore:
overwrite: false
Expand All @@ -14,9 +14,9 @@ sync:
overwrite: true

spec:
~/file: testfile
~/example/directory: test-dir
~/example/dir with space in name/file: test-dir2/testfile2
~/examplefile: file
~/exampledir: dir
~/dir/another one: another one
```
The configuration file contains:
Expand All @@ -25,12 +25,13 @@ The configuration file contains:

You can use multiple configuration files for different tasks.

## Operations
## Commands

Bonclay has three operations:
Bonclay has four operations:
- Sync
- Backup
- Restore
- Init

### Sync

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module github.com/talal/bonclay

require (
github.com/talal/go-bits v0.0.0-20190112130027-6c329ec8a3e2
github.com/talal/go-bits/cli v0.0.0-20190206232352-5b28e4a5144d
github.com/talal/go-bits/color v0.0.0-20190206232352-5b28e4a5144d
gopkg.in/yaml.v2 v2.2.2
)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/talal/go-bits v0.0.0-20190112130027-6c329ec8a3e2 h1:RugS4dg5Qio894yMikQ6RbsenApo3YMPN+wllQH4uQI=
github.com/talal/go-bits v0.0.0-20190112130027-6c329ec8a3e2/go.mod h1:HGF/PnMf8971sLP+5fp0PMYV5EN3fwhhDJQmgisBGLQ=
github.com/talal/go-bits/cli v0.0.0-20190206232352-5b28e4a5144d h1:qcxaWV+wvpPFxV3my+FLyktUumbpvmMUNY3VdtzvsyE=
github.com/talal/go-bits/cli v0.0.0-20190206232352-5b28e4a5144d/go.mod h1:kFx4bJmukhKFkDqZSjiZD23Iq+7Y2jmqpNIrcc2Ah28=
github.com/talal/go-bits/color v0.0.0-20190206232352-5b28e4a5144d h1:0QPpMG+hvF/0xUyb3LePTyILUF7LDPquoa9xB0QBMqY=
github.com/talal/go-bits/color v0.0.0-20190206232352-5b28e4a5144d/go.mod h1:U+jqC1rjMV5j4UPOFjhh9fnBY22Tw5UXhbsXzuLhsvI=
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.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
Expand Down
71 changes: 71 additions & 0 deletions pkg/commands/backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package commands

import (
"flag"
"fmt"
"os"
"strings"

"github.com/talal/bonclay/pkg/file"
"github.com/talal/bonclay/pkg/mistertwo"
"github.com/talal/bonclay/pkg/write"
"github.com/talal/go-bits/cli"
)

const backupDesc = `Backup files/directories to their target location.`

const backupUsage = `
Usage: bonclay backup <config-file>
Backup uses the 'source:target' pairs defined in the configuration spec
to copy the sources to the targets.
`

// Backup represents the backup command.
var Backup = makeBackupCmd()

func makeBackupCmd() cli.Command {
fs := flag.NewFlagSet("bonclay backup", flag.ExitOnError)

fs.Usage = func() {
fmt.Println(strings.TrimSpace(backupUsage))
}

return cli.Command{
Name: "backup",
Desc: backupDesc,
Action: func(args []string) {
fs.Parse(args)
args = fs.Args()

if len(args) != 1 {
fs.Usage()
os.Exit(1)
}

cfg := mistertwo.NewConfiguration(args[0])
backupTask(cfg)
},
}
}

// backupTask uses 'source:target' pairs defined in the configuration spec to copy
// the sources to the targets.
func backupTask(config *mistertwo.Configuration) {
write.TaskHeader("backup")

errors := make([]string, 0, len(config.Spec))
for src, dst := range config.Spec {
err := file.Copy(src, dst, config.Backup.Overwrite)
if err != nil {
errors = append(errors, err.Error())
write.TaskFailure(src, dst)
continue
}

write.TaskSuccess(src, dst)
}

write.TaskFooter("backup", len(errors) == 0)
write.TaskErrors(errors)
}
99 changes: 99 additions & 0 deletions pkg/commands/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package commands

import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/talal/go-bits/cli"
)

const initDesc = `Create a new config file in the current directory.`

const initUsage = `
Usage: bonclay init
Create a new config file in the current directory with sane defaults and
example spec section.
`

const exampleConfig = `
# For a detailed description on the different options, take a look at the
# user guide: https://github.com/talal/bonclay/blob/master/doc/guide.md
backup:
overwrite: false
restore:
overwrite: false
sync:
clean: true
overwrite: true
spec:
# ~/examplefile: file
# ~/exampledir: dir
# ~/dir/another one: another one
`

// Init represents the init command.
var Init = makeInitCmd()

func makeInitCmd() cli.Command {
fs := flag.NewFlagSet("bonclay init", flag.ExitOnError)

fs.Usage = func() {
fmt.Println(strings.TrimSpace(initUsage))
}

return cli.Command{
Name: "init",
Desc: initDesc,
Action: func(args []string) {
fs.Parse(args)
args = fs.Args()

if len(args) > 0 {
fs.Usage()
os.Exit(1)
}

initTask()
},
}
}

// initTask creates a sample config file (bonclay.conf.yaml) in the current
// directory.
func initTask() {
cwd, err := os.Getwd()
if err != nil {
cwd = os.Getenv("PWD")
}

cfgPath := filepath.Join(filepath.Clean(cwd), "bonclay.conf.yaml")

_, err = os.Lstat(cfgPath)
if err == nil {
fmt.Fprintf(os.Stderr, "bonclay: error: config file already exists: %s\n", cfgPath)
os.Exit(1)
}

f, err := os.Create(cfgPath)
if err != nil {
fmt.Fprintf(os.Stderr, "bonclay: error: could not create config file: %s\n", cfgPath)
os.Exit(1)
}
defer f.Close()

_, err = f.WriteString(strings.TrimSpace(exampleConfig))
if err != nil {
fmt.Fprintf(os.Stderr, "bonclay: error: could not create config file: %s\n", cfgPath)
os.Exit(1)
}
f.Sync()

fmt.Printf("bonclay: config file created: %s\n", cfgPath)
}
Loading

0 comments on commit 52caf7e

Please sign in to comment.