<commit_before>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/tywkeene/autobd/api"
	"github.com/tywkeene/autobd/node"
	"github.com/tywkeene/autobd/options"
	"github.com/tywkeene/autobd/version"
	"net/http"
	"os"
	"runtime"
)

var (
	CommitHash string
	ServerVer  string
)

func init() {
	version.Set(CommitHash, ServerVer)
	version.Print()
	options.GetOptions()
	if options.Config.RunNode == false {
		api.SetupRoutes()
	}
}

func printLogo() {
	const node = `
 █████╗ ██╗   ██╗████████╗ ██████╗ ██████╗ ██████╗       ███╗   ██╗ ██████╗ ██████╗ ███████╗
██╔══██╗██║   ██║╚══██╔══╝██╔═══██╗██╔══██╗██╔══██╗      ████╗  ██║██╔═══██╗██╔══██╗██╔════╝
███████║██║   ██║   ██║   ██║   ██║██████╔╝██║  ██║█████╗██╔██╗ ██║██║   ██║██║  ██║█████╗
██╔══██║██║   ██║   ██║   ██║   ██║██╔══██╗██║  ██║╚════╝██║╚██╗██║██║   ██║██║  ██║██╔══╝
██║  ██║╚██████╔╝   ██║   ╚██████╔╝██████╔╝██████╔╝      ██║ ╚████║╚██████╔╝██████╔╝███████╗
╚═╝  ╚═╝ ╚═════╝    ╚═╝    ╚═════╝ ╚═════╝ ╚═════╝       ╚═╝  ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝
	`
	const server = `
 █████╗ ██╗   ██╗████████╗ ██████╗ ██████╗ ██████╗       ███████╗███████╗██████╗ ██╗   ██╗███████╗██████╗
██╔══██╗██║   ██║╚══██╔══╝██╔═══██╗██╔══██╗██╔══██╗      ██╔════╝██╔════╝██╔══██╗██║   ██║██╔════╝██╔══██╗
███████║██║   ██║   ██║   ██║   ██║██████╔╝██║  ██║█████╗███████╗█████╗  ██████╔╝██║   ██║█████╗  ██████╔╝
██╔══██║██║   ██║   ██║   ██║   ██║██╔══██╗██║  ██║╚════╝╚════██║██╔══╝  ██╔══██╗╚██╗ ██╔╝██╔══╝  ██╔══██╗
██║  ██║╚██████╔╝   ██║   ╚██████╔╝██████╔╝██████╔╝      ███████║███████╗██║  ██║ ╚████╔╝ ███████╗██║  ██║
╚═╝  ╚═╝ ╚═════╝    ╚═╝    ╚═════╝ ╚═════╝ ╚═════╝       ╚══════╝╚══════╝╚═╝  ╚═╝  ╚═══╝  ╚══════╝╚═╝  ╚═╝
	`
	if options.Config.RunNode == true {
		fmt.Println(node)
	} else {
		fmt.Println(server)
	}
}

func main() {
	if err := os.Chdir(options.Config.Root); err != nil {
		panic(err)
	}
	printLogo()
	if options.Config.RunNode == true {
		localNode := node.InitNode(options.Config.NodeConfig)
		if err := localNode.UpdateLoop(); err != nil {
			panic(err)
		}
	}
	if options.Config.Cores > runtime.NumCPU() {
		log.Error("Requested processor value greater than number of actual processors, using default")
	} else {
		runtime.GOMAXPROCS(options.Config.Cores)
	}
	log.Printf("Serving '%s' on port %s", options.Config.Root, options.Config.ApiPort)
	if options.Config.Ssl == true {
		log.Info("Using certificate (%s) and key (%s) for SSL\n", options.Config.Cert, options.Config.Key)
		log.Panic(http.ListenAndServeTLS(":"+options.Config.ApiPort, options.Config.Cert, options.Config.Key, nil))
	} else {
		log.Panic(http.ListenAndServe(":"+options.Config.ApiPort, nil))
	}
}
<commit_msg>General cleanup. Forgot the good old slogan for reasons because I can. Deal w/ it<commit_after>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/tywkeene/autobd/api"
	"github.com/tywkeene/autobd/node"
	"github.com/tywkeene/autobd/options"
	"github.com/tywkeene/autobd/version"
	"net/http"
	"os"
	"runtime"
)

var (
	CommitHash string
	ServerVer  string
)

func init() {
	version.Set(CommitHash, ServerVer)
	version.Print()
	options.GetOptions()
	if options.Config.RunNode == false {
		api.SetupRoutes()
	}
}

func printLogo() {
	const node = `
 █████╗ ██╗   ██╗████████╗ ██████╗ ██████╗ ██████╗       ███╗   ██╗ ██████╗ ██████╗ ███████╗
██╔══██╗██║   ██║╚══██╔══╝██╔═══██╗██╔══██╗██╔══██╗      ████╗  ██║██╔═══██╗██╔══██╗██╔════╝
███████║██║   ██║   ██║   ██║   ██║██████╔╝██║  ██║█████╗██╔██╗ ██║██║   ██║██║  ██║█████╗
██╔══██║██║   ██║   ██║   ██║   ██║██╔══██╗██║  ██║╚════╝██║╚██╗██║██║   ██║██║  ██║██╔══╝
██║  ██║╚██████╔╝   ██║   ╚██████╔╝██████╔╝██████╔╝      ██║ ╚████║╚██████╔╝██████╔╝███████╗
╚═╝  ╚═╝ ╚═════╝    ╚═╝    ╚═════╝ ╚═════╝ ╚═════╝       ╚═╝  ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝
Backing you up since whenever...
	`
	const server = `
 █████╗ ██╗   ██╗████████╗ ██████╗ ██████╗ ██████╗       ███████╗███████╗██████╗ ██╗   ██╗███████╗██████╗
██╔══██╗██║   ██║╚══██╔══╝██╔═══██╗██╔══██╗██╔══██╗      ██╔════╝██╔════╝██╔══██╗██║   ██║██╔════╝██╔══██╗
███████║██║   ██║   ██║   ██║   ██║██████╔╝██║  ██║█████╗███████╗█████╗  ██████╔╝██║   ██║█████╗  ██████╔╝
██╔══██║██║   ██║   ██║   ██║   ██║██╔══██╗██║  ██║╚════╝╚════██║██╔══╝  ██╔══██╗╚██╗ ██╔╝██╔══╝  ██╔══██╗
██║  ██║╚██████╔╝   ██║   ╚██████╔╝██████╔╝██████╔╝      ███████║███████╗██║  ██║ ╚████╔╝ ███████╗██║  ██║
╚═╝  ╚═╝ ╚═════╝    ╚═╝    ╚═════╝ ╚═════╝ ╚═════╝       ╚══════╝╚══════╝╚═╝  ╚═╝  ╚═══╝  ╚══════╝╚═╝  ╚═╝
Backing you up since right now...
	`
	if options.Config.RunNode == true {
		fmt.Println(node)
	} else {
		fmt.Println(server)
	}
}

func main() {
	if err := os.Chdir(options.Config.Root); err != nil {
		log.Panic(err)
	}
	printLogo()
	if options.Config.RunNode == true {
		localNode := node.InitNode(options.Config.NodeConfig)
		if err := localNode.UpdateLoop(); err != nil {
			log.Panic(err)
		}
	}
	if options.Config.Cores > runtime.NumCPU() {
		log.Error("Requested processor value greater than number of actual processors, using default")
	} else {
		runtime.GOMAXPROCS(options.Config.Cores)
	}
	log.Printf("Serving '%s' on port %s", options.Config.Root, options.Config.ApiPort)
	if options.Config.Ssl == true {
		log.Infof("Using certificate (%s) and key (%s) for SSL\n", options.Config.Cert, options.Config.Key)
		log.Panic(http.ListenAndServeTLS(":"+options.Config.ApiPort, options.Config.Cert, options.Config.Key, nil))
	} else {
		log.Panic(http.ListenAndServe(":"+options.Config.ApiPort, nil))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/fsnotify/fsnotify"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"syscall" // TODO(zacsh) important to use x/syscall/unix explicitly?
	"time"
)

const defaultWaitTime time.Duration = 2 * time.Second

type featureFlag int

const (
	flgAutoIgnore featureFlag = 1 + iota
	// Particularly useful for VIM flury of events, see:
	//   https://stackoverflow.com/q/10300835/287374
	flgDebugOutput
	flgClobberCommands
)

func (flg featureFlag) String() string {
	switch flg {
	case flgAutoIgnore:
		return "flgAutoIgnore"
	case flgDebugOutput:
		return "flgDebugOutput"
	default:
		panic(fmt.Sprintf("unexpected flag, '%d'", int(flg)))
	}
}

type exitReason int

const (
	exCommandline exitReason = 1 + iota
	exWatcher
	exFsevent
)

type matcher struct {
	Expr     *regexp.Regexp
	IsIgnore bool
}

type runDirective struct {
	Shell        string
	Command      string
	WatchTargets []string
	Patterns     []matcher
	Features     map[featureFlag]bool

	LastRun time.Time
	RunMux  sync.Mutex
	Cmd     *exec.Cmd
	Birth   chan os.Process
	Living  *os.Process
	Death   chan error
	LastFin time.Time
}

func (m *matcher) String() string {
	status := "RESTR"
	if m.IsIgnore {
		status = "IGNOR"
	}
	return fmt.Sprintf("[%s]: %v", status, *m.Expr)
}

func (run *runDirective) maybeRun(stdOut bool) (bool, error) {
	run.RunMux.Lock()
	defer run.RunMux.Unlock()

	if run.isRecent(defaultWaitTime) {
		return false, nil
	}

	run.Birth = make(chan os.Process, 1)
	run.Death = make(chan error, 1)
	run.LastRun = time.Now()
	run.LastFin = time.Time{}
	if run.Features[flgClobberCommands] {
		return run.runAsync(stdOut), nil
	} else {
		return run.runSync(stdOut)
	}
}

func (run *runDirective) runSync(stdOut bool) (bool, error) {
	run.execAsync(stdOut)
	e := <-run.Death // block
	close(run.Birth)
	return true, e
}

// Tries to kill any existant COMMANDs still running
// returns indication of whether attempt was made and its errors:
//   true if any existed (ie: any cleanup was necessary)
//   error if cleanup failed
func (run *runDirective) cleanupExistant(wait bool) (existed bool, fail error) {
	existed = run.Living != nil
	if !existed {
		return
	}

	if fail = syscall.Kill(-run.Living.Pid, syscall.SIGKILL); fail != nil {
		fmt.Fprintf(os.Stderr, "failed to kill exec's pgroup: %s\n", fail)
		return
	}

	if wait {
		// TODO(zacsh) utilize os.Process.Wait() method
		<-run.Death
	}
	return
}

func (run *runDirective) runAsync(stdOut bool) bool {
	if _, e := run.cleanupExistant(true /*wait*/); e != nil {
		return false
	}

	go run.execAsync(stdOut)
	select {
	case p := <-run.Birth:
		run.Living = &p
		close(run.Birth)
		return true
	}
}

func (run *runDirective) execAsync(msgStdout bool) {
	if msgStdout {
		fmt.Printf("\n%s\t: `%s`\n",
			color.YellowString("running"),
			color.HiRedString(run.Command))
	}

	// TODO(zacsh) find out a shell-agnostic way to run comands (eg: *bash*
	// specifically takes a "-c" flag)
	run.Cmd = exec.Command(run.Shell, "-c", run.Command)
	run.Cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	run.Cmd.Stdout = os.Stdout
	run.Cmd.Stderr = os.Stderr

	go func() {
		e := run.Cmd.Run()
		run.LastFin = time.Now()
		if msgStdout {
			run.messageDeath(e)
		}
		run.Death <- e
	}()

	for {
		if run.Cmd != nil && run.Cmd.Process != nil {
			run.Birth <- *run.Cmd.Process
			break
		}
	}
}

func (run *runDirective) isRecent(since time.Duration) bool {
	if run.Features[flgClobberCommands] {
		since *= 2
	}

	return time.Since(run.LastRun) <= since ||
		time.Since(run.LastFin) <= since
}

func (run *runDirective) messageDeath(e error) {
	var maybeLn string
	if run.Features[flgClobberCommands] {
		maybeLn = "\n"
	}

	var maybeErr string
	if e != nil {
		maybeErr = fmt.Sprintf(
			"\t:  %s",
			color.New(color.Bold, color.FgRed).Sprintf(e.Error()))
	}

	fmt.Printf("%s%s in %v.%s\n",
		maybeLn,
		color.YellowString("done"),
		run.LastFin.Sub(run.LastRun),
		maybeErr)
}

func usage() string {
	return fmt.Sprintf(`Runs a command everytime some filesystem events happen.
  Usage:  COMMAND -c  [-i|-r FILE_PATTERN] [DIR_TO_WATCH, ...]

  If -c is passed, then long-running COMMAND will be killed when newer
  triggering events are received.

  Regular expressions can be used to match against files whose events have been
  as described by the next two flags:

  -i FILE_PATTERN: only run COMMAND if match is not made (invert/ignore)
  -r FILE_PATTERN: only run COMMAND if match is made

    This program watches filesystem events. Thus, when an event occurs, there
    is an associated file that causes that event. FILE_PATTERN tries to match
    that file's basename to FILE_PATTERN. The result of that match is as
    described by the flag preceding FILE_PATTERN, explained above.

    Valid FILE_PATTERN strings are those accepted by:
      https://golang.org/pkg/regexp/#Compile

  DIR_TO_WATCH defaults to just one: the current working directory. Multiple
  directories can be passed.
`)
}

func die(reason exitReason, e error) {
	var reasonStr string
	switch reason {
	case exCommandline:
		reasonStr = "usage"
	case exWatcher:
		reasonStr = "watcher"
	case exFsevent:
		reasonStr = "event"
	}

	fmt.Fprintf(os.Stderr, "%s error: %s\n", reasonStr, e)
	os.Exit(int(reason))
}

func (c *runDirective) debugStr() string {
	matchStr := "n/a"
	if len(c.Patterns) > 0 {
		matchStr = fmt.Sprintf("'%v',", c.Patterns[0])
		for _, p := range c.Patterns[1:] {
			matchStr = fmt.Sprintf("%s '%v',", matchStr, p)
		}

		matchStr = fmt.Sprintf(
			"%s", // close off bracket
			matchStr[:len(matchStr)-1 /*chop off trailing comma*/])
	}

	var features string
	for k, v := range c.Features {
		if v {
			var sep string
			if len(features) > 0 {
				sep = ", "
			}
			features = fmt.Sprintf("%s%s%s", features, sep, k.String())
		}
	}

	return fmt.Sprintf(`
  run.Command:                "%s"
  run.WatchTargets' Name()s:  [%s]
  run.FilePatterns:           [%s]
  run.Shell:                  "%s"
  run.Features:                %s
  `, c.Command,
		fmt.Sprintf("\n\t%s\n\t", strings.Join(c.WatchTargets, ",\n\t")),
		matchStr,
		c.Shell,
		features)
}

func (run *runDirective) isRejected(chain []matcher, e fsnotify.Event) bool {
	if len(chain) == 0 {
		return false
	}

	for i, p := range chain {
		if p.IsIgnore {
			if p.Expr.MatchString(filepath.Base(e.Name)) {
				if run.Features[flgDebugOutput] {
					fmt.Fprintf(os.Stderr, "IGNR[%d]\n", i)
				} else {
					fmt.Fprintf(os.Stderr, "-")
				}
				return true
			}
		} else {
			if !p.Expr.MatchString(filepath.Base(e.Name)) {
				if run.Features[flgDebugOutput] {
					fmt.Fprintf(os.Stderr, "MISS[%d]\n", i)
				} else {
					fmt.Fprintf(os.Stderr, "_")
				}
				return true
			}
		}
	}
	return false
}

func main() {
	magicFileRegexp := regexp.MustCompile(`^(\.\w.*sw[a-z]|4913)$`)

	run, perr := parseCli()
	if perr != nil {
		if perr.Stage == psHelp {
			fmt.Printf(usage())
			os.Exit(0)
		}

		die(exCommandline, perr)
	}

	watcher, e := fsnotify.NewWatcher()
	if e != nil {
		die(exCommandline, e)
	}
	defer watcher.Close()

	if run.Features[flgDebugOutput] {
		fmt.Fprintf(os.Stderr,
			"[debug] here's what you asked for:\n%s\n",
			run.debugStr())
	}

	haveActionableEvent := make(chan fsnotify.Event)
	go func() {
		for {
			select {
			case e := <-watcher.Events:
				if run.Features[flgDebugOutput] {
					fmt.Fprintf(os.Stderr, "[debug] [%s] %s\n", e.Op.String(), e.Name)
				}

				if run.Features[flgAutoIgnore] {
					if magicFileRegexp.MatchString(filepath.Base(e.Name)) {
						continue
					}
				}

				if run.isRejected(run.Patterns, e) {
					continue
				}

				haveActionableEvent <- e
			case err := <-watcher.Errors:
				die(exFsevent, err)
			}
		}
	}()

	go func() {
		for {
			select {
			case <-run.Death:
				run.Living = nil
				if !run.Features[flgClobberCommands] {
					continue
				}
				fmt.Fprintf(os.Stderr,
					"\t%s: death was unprovoked\n",
					color.New(color.Bold, color.FgBlue).Sprintf("warning"))

			case <-haveActionableEvent:
				if ran, _ := run.maybeRun(true /*msgStdout*/); !ran {
					fmt.Fprintf(os.Stderr, ".")
				}
			}
		}
	}()

	for _, t := range run.WatchTargets {
		if err := watcher.Add(t); err != nil {
			die(exWatcher, e)
		}
	}

	fmt.Printf("%s `%s`\n",
		color.HiGreenString("Watching"),
		strings.Join(run.WatchTargets, ", "))
	run.maybeRun(true /*msgStdout*/)

	kills := make(chan os.Signal, 1)
	signal.Notify(kills, os.Interrupt)
	for sig := range kills {
		fmt.Fprintf(os.Stderr, "\nCaught %v (%d); Cleaning up... ", sig, sig)
		ex := 0
		if _, e := run.cleanupExistant(false /*wait*/); e != nil {
			ex = 1
			fmt.Fprintf(os.Stderr, "Failed: %v\n", e)
		} else {
			fmt.Fprintf(os.Stderr, "Done\n")
		}
		os.Exit(ex)
	}
}
<commit_msg>nit(ui) clarify when we're in clobber mode<commit_after>package main

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/fsnotify/fsnotify"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"syscall" // TODO(zacsh) important to use x/syscall/unix explicitly?
	"time"
)

const defaultWaitTime time.Duration = 2 * time.Second

type featureFlag int

const (
	flgAutoIgnore featureFlag = 1 + iota
	// Particularly useful for VIM flury of events, see:
	//   https://stackoverflow.com/q/10300835/287374
	flgDebugOutput
	flgClobberCommands
)

func (flg featureFlag) String() string {
	switch flg {
	case flgAutoIgnore:
		return "flgAutoIgnore"
	case flgDebugOutput:
		return "flgDebugOutput"
	default:
		panic(fmt.Sprintf("unexpected flag, '%d'", int(flg)))
	}
}

type exitReason int

const (
	exCommandline exitReason = 1 + iota
	exWatcher
	exFsevent
)

type matcher struct {
	Expr     *regexp.Regexp
	IsIgnore bool
}

type runDirective struct {
	Shell        string
	Command      string
	WatchTargets []string
	Patterns     []matcher
	Features     map[featureFlag]bool

	LastRun time.Time
	RunMux  sync.Mutex
	Cmd     *exec.Cmd
	Birth   chan os.Process
	Living  *os.Process
	Death   chan error
	LastFin time.Time
}

func (m *matcher) String() string {
	status := "RESTR"
	if m.IsIgnore {
		status = "IGNOR"
	}
	return fmt.Sprintf("[%s]: %v", status, *m.Expr)
}

func (run *runDirective) maybeRun(stdOut bool) (bool, error) {
	run.RunMux.Lock()
	defer run.RunMux.Unlock()

	if run.isRecent(defaultWaitTime) {
		return false, nil
	}

	run.Birth = make(chan os.Process, 1)
	run.Death = make(chan error, 1)
	run.LastRun = time.Now()
	run.LastFin = time.Time{}
	if run.Features[flgClobberCommands] {
		return run.runAsync(stdOut), nil
	} else {
		return run.runSync(stdOut)
	}
}

func (run *runDirective) runSync(stdOut bool) (bool, error) {
	run.execAsync(stdOut)
	e := <-run.Death // block
	close(run.Birth)
	return true, e
}

// Tries to kill any existant COMMANDs still running
// returns indication of whether attempt was made and its errors:
//   true if any existed (ie: any cleanup was necessary)
//   error if cleanup failed
func (run *runDirective) cleanupExistant(wait bool) (existed bool, fail error) {
	existed = run.Living != nil
	if !existed {
		return
	}

	if fail = syscall.Kill(-run.Living.Pid, syscall.SIGKILL); fail != nil {
		fmt.Fprintf(os.Stderr, "failed to kill exec's pgroup: %s\n", fail)
		return
	}

	if wait {
		// TODO(zacsh) utilize os.Process.Wait() method
		<-run.Death
	}
	return
}

func (run *runDirective) runAsync(stdOut bool) bool {
	if _, e := run.cleanupExistant(true /*wait*/); e != nil {
		return false
	}

	go run.execAsync(stdOut)
	select {
	case p := <-run.Birth:
		run.Living = &p
		close(run.Birth)
		return true
	}
}

func (run *runDirective) execAsync(msgStdout bool) {
	if msgStdout {
		fmt.Printf("\n%s\t: `%s`\n",
			color.YellowString("running"),
			color.HiRedString(run.Command))
	}

	// TODO(zacsh) find out a shell-agnostic way to run comands (eg: *bash*
	// specifically takes a "-c" flag)
	run.Cmd = exec.Command(run.Shell, "-c", run.Command)
	run.Cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	run.Cmd.Stdout = os.Stdout
	run.Cmd.Stderr = os.Stderr

	go func() {
		e := run.Cmd.Run()
		run.LastFin = time.Now()
		if msgStdout {
			run.messageDeath(e)
		}
		run.Death <- e
	}()

	for {
		if run.Cmd != nil && run.Cmd.Process != nil {
			run.Birth <- *run.Cmd.Process
			break
		}
	}
}

func (run *runDirective) isRecent(since time.Duration) bool {
	if run.Features[flgClobberCommands] {
		since *= 2
	}

	return time.Since(run.LastRun) <= since ||
		time.Since(run.LastFin) <= since
}

func (run *runDirective) messageDeath(e error) {
	var maybeLn string
	if run.Features[flgClobberCommands] {
		maybeLn = "\n"
	}

	var maybeErr string
	if e != nil {
		maybeErr = fmt.Sprintf(
			"\t:  %s",
			color.New(color.Bold, color.FgRed).Sprintf(e.Error()))
	}

	fmt.Printf("%s%s in %v.%s\n",
		maybeLn,
		color.YellowString("done"),
		run.LastFin.Sub(run.LastRun),
		maybeErr)
}

func usage() string {
	return fmt.Sprintf(`Runs a command everytime some filesystem events happen.
  Usage:  COMMAND -c  [-i|-r FILE_PATTERN] [DIR_TO_WATCH, ...]

  If -c is passed, then long-running COMMAND will be killed when newer
  triggering events are received.

  Regular expressions can be used to match against files whose events have been
  as described by the next two flags:

  -i FILE_PATTERN: only run COMMAND if match is not made (invert/ignore)
  -r FILE_PATTERN: only run COMMAND if match is made

    This program watches filesystem events. Thus, when an event occurs, there
    is an associated file that causes that event. FILE_PATTERN tries to match
    that file's basename to FILE_PATTERN. The result of that match is as
    described by the flag preceding FILE_PATTERN, explained above.

    Valid FILE_PATTERN strings are those accepted by:
      https://golang.org/pkg/regexp/#Compile

  DIR_TO_WATCH defaults to just one: the current working directory. Multiple
  directories can be passed.
`)
}

func die(reason exitReason, e error) {
	var reasonStr string
	switch reason {
	case exCommandline:
		reasonStr = "usage"
	case exWatcher:
		reasonStr = "watcher"
	case exFsevent:
		reasonStr = "event"
	}

	fmt.Fprintf(os.Stderr, "%s error: %s\n", reasonStr, e)
	os.Exit(int(reason))
}

func (c *runDirective) debugStr() string {
	matchStr := "n/a"
	if len(c.Patterns) > 0 {
		matchStr = fmt.Sprintf("'%v',", c.Patterns[0])
		for _, p := range c.Patterns[1:] {
			matchStr = fmt.Sprintf("%s '%v',", matchStr, p)
		}

		matchStr = fmt.Sprintf(
			"%s", // close off bracket
			matchStr[:len(matchStr)-1 /*chop off trailing comma*/])
	}

	var features string
	for k, v := range c.Features {
		if v {
			var sep string
			if len(features) > 0 {
				sep = ", "
			}
			features = fmt.Sprintf("%s%s%s", features, sep, k.String())
		}
	}

	return fmt.Sprintf(`
  run.Command:                "%s"
  run.WatchTargets' Name()s:  [%s]
  run.FilePatterns:           [%s]
  run.Shell:                  "%s"
  run.Features:                %s
  `, c.Command,
		fmt.Sprintf("\n\t%s\n\t", strings.Join(c.WatchTargets, ",\n\t")),
		matchStr,
		c.Shell,
		features)
}

func (run *runDirective) isRejected(chain []matcher, e fsnotify.Event) bool {
	if len(chain) == 0 {
		return false
	}

	for i, p := range chain {
		if p.IsIgnore {
			if p.Expr.MatchString(filepath.Base(e.Name)) {
				if run.Features[flgDebugOutput] {
					fmt.Fprintf(os.Stderr, "IGNR[%d]\n", i)
				} else {
					fmt.Fprintf(os.Stderr, "-")
				}
				return true
			}
		} else {
			if !p.Expr.MatchString(filepath.Base(e.Name)) {
				if run.Features[flgDebugOutput] {
					fmt.Fprintf(os.Stderr, "MISS[%d]\n", i)
				} else {
					fmt.Fprintf(os.Stderr, "_")
				}
				return true
			}
		}
	}
	return false
}

func main() {
	magicFileRegexp := regexp.MustCompile(`^(\.\w.*sw[a-z]|4913)$`)

	run, perr := parseCli()
	if perr != nil {
		if perr.Stage == psHelp {
			fmt.Printf(usage())
			os.Exit(0)
		}

		die(exCommandline, perr)
	}

	watcher, e := fsnotify.NewWatcher()
	if e != nil {
		die(exCommandline, e)
	}
	defer watcher.Close()

	if run.Features[flgDebugOutput] {
		fmt.Fprintf(os.Stderr,
			"[debug] here's what you asked for:\n%s\n",
			run.debugStr())
	}

	haveActionableEvent := make(chan fsnotify.Event)
	go func() {
		for {
			select {
			case e := <-watcher.Events:
				if run.Features[flgDebugOutput] {
					fmt.Fprintf(os.Stderr, "[debug] [%s] %s\n", e.Op.String(), e.Name)
				}

				if run.Features[flgAutoIgnore] {
					if magicFileRegexp.MatchString(filepath.Base(e.Name)) {
						continue
					}
				}

				if run.isRejected(run.Patterns, e) {
					continue
				}

				haveActionableEvent <- e
			case err := <-watcher.Errors:
				die(exFsevent, err)
			}
		}
	}()

	go func() {
		for {
			select {
			case <-run.Death:
				run.Living = nil
				if !run.Features[flgClobberCommands] {
					continue
				}
				fmt.Fprintf(os.Stderr,
					"\t%s: death was unprovoked\n",
					color.New(color.Bold, color.FgBlue).Sprintf("warning"))

			case <-haveActionableEvent:
				if ran, _ := run.maybeRun(true /*msgStdout*/); !ran {
					fmt.Fprintf(os.Stderr, ".")
				}
			}
		}
	}()

	for _, t := range run.WatchTargets {
		if err := watcher.Add(t); err != nil {
			die(exWatcher, e)
		}
	}

	var clobberMode string
	if run.Features[flgClobberCommands] {
		clobberMode = fmt.Sprintf(" (in %s mode)", color.RedString("clobber"))
	}
	fmt.Printf("%s%s `%s`\n",
		color.HiGreenString("Watching"),
		clobberMode,
		strings.Join(run.WatchTargets, ", "))
	run.maybeRun(true /*msgStdout*/)

	kills := make(chan os.Signal, 1)
	signal.Notify(kills, os.Interrupt)
	for sig := range kills {
		fmt.Fprintf(os.Stderr, "\nCaught %v (%d); Cleaning up... ", sig, sig)
		ex := 0
		if _, e := run.cleanupExistant(false /*wait*/); e != nil {
			ex = 1
			fmt.Fprintf(os.Stderr, "Failed: %v\n", e)
		} else {
			fmt.Fprintf(os.Stderr, "Done\n")
		}
		os.Exit(ex)
	}
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  "log"
  "os"
  "os/exec"
  "time"
  "syscall"
  "strings"
)

func main() {
  command := os.Args[1:]
  loop(1 * time.Second, func () {
    reset()
    status := run(measure(command))
    if(status != 0) {
      os.Exit(status)
    }
  })
}

func reset() {
  run([]string {"clear"})
}

func measure(command []string) []string {
  return append([]string {"time"}, command...)
}

func run(command []string) int {
  cmd := exec.Command(getShell(), wrapForShell(buildArgs(command))...)
  cmd.Env = os.Environ()
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  cmd.Run()
  status, _ := cmd.ProcessState.Sys().(syscall.WaitStatus)
  return status.ExitStatus()
}

func safe(err error) {
  if err != nil {
    log.Fatal(err)
  }
}

func buildArgs(command []string) string {
  return strings.Join(command, " ")
}

func wrapForShell(command string) []string {
  return append([]string {"-c"}, fmt.Sprintf("%s; eval %s", sourceFiles(), command))
}

func sourceFiles() string {
  return fmt.Sprintf("%s %s", getShellSourceCmd(), getSourceFilePath())
}

func getSourceFilePath() string {
  return "~/.zshrc"
}

func getShellSourceCmd() string {
  return "."
}

func getShell() string {
  bin, found := syscall.Getenv("SHELL")
  if found == false {
    bin, _ = exec.LookPath("sh")
  }
  return bin
}

func loop(d time.Duration, fn func()) {
  select {
  case <- time.After(d):
    fn()
    loop(d, fn)
  }
}
<commit_msg>Work on #2<commit_after>package main

import (
  "fmt"
  "log"
  "flag"
  "os"
  "os/exec"
  "time"
  "syscall"
  "strings"
)

func main() {
  var interval int
  flag.IntVar(&interval, "i", 1, "interval")

  var interrupt bool
  flag.BoolVar(&interrupt, "x", false, "interrupt")

  flag.Parse()

  command := flag.Args()

  loop(intervalToTime(interval), func () {
    reset()
    status := run(measure(command))
    if(interrupt && status != 0) {
      os.Exit(status)
    }
  })
}

func intervalToTime(i int) time.Duration {
  return time.Duration(i) * time.Second
}

func reset() {
  run([]string {"clear"})
}

func measure(command []string) []string {
  return append([]string {"time"}, command...)
}

func run(command []string) int {
  cmd := exec.Command(getShell(), wrapForShell(buildArgs(command))...)
  cmd.Env = os.Environ()
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  cmd.Run()
  status, _ := cmd.ProcessState.Sys().(syscall.WaitStatus)
  return status.ExitStatus()
}

func safe(err error) {
  if err != nil {
    log.Fatal(err)
  }
}

func buildArgs(command []string) string {
  return strings.Join(command, " ")
}

func wrapForShell(command string) []string {
  return append([]string {"-c"}, fmt.Sprintf("%s; eval %s", sourceFiles(), command))
}

func sourceFiles() string {
  return fmt.Sprintf("%s %s", getShellSourceCmd(), getSourceFilePath())
}

func getSourceFilePath() string {
  return "~/.zshrc"
}

func getShellSourceCmd() string {
  return "."
}

func getShell() string {
  bin, found := syscall.Getenv("SHELL")
  if found == false {
    bin, _ = exec.LookPath("sh")
  }
  return bin
}

func loop(d time.Duration, fn func()) {
  fn()
  select {
  case <- time.After(d):
    loop(d, fn)
  }
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"os/user"
	"sort"
)

type Config struct {
	Book string
}

type Note map[string][]string

const configFilename = ".dnoterc"
const dnoteFilename = ".dnote"

func getConfigPath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%s/%s", usr.HomeDir, configFilename), nil
}

func getDnotePath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%s/%s", usr.HomeDir, dnoteFilename), nil
}

func generateConfigFile() error {
	content := []byte("book: general\n")
	configPath, err := getConfigPath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(configPath, content, 0644)
	return err
}

func touchDnoteFile() error {
	dnotePath, err := getDnotePath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(dnotePath, []byte{}, 0644)
	return err
}

// initDnote creates a config file if one does not exist
func initDnote() error {
	configPath, err := getConfigPath()
	if err != nil {
		return err
	}
	dnotePath, err := getDnotePath()
	if err != nil {
		return err
	}

	if !checkFileExists(configPath) {
		err := generateConfigFile()
		if err != nil {
			return err
		}
	}
	if !checkFileExists(dnotePath) {
		err := touchDnoteFile()
		if err != nil {
			return err
		}
	}

	return nil
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func readConfig() (Config, error) {
	var ret Config

	configPath, err := getConfigPath()
	if err != nil {
		return ret, err
	}

	b, err := ioutil.ReadFile(configPath)
	if err != nil {
		return ret, err
	}

	err = yaml.Unmarshal(b, &ret)
	if err != nil {
		return ret, err
	}

	return ret, nil
}

func getCurrentBook() (string, error) {
	config, err := readConfig()
	if err != nil {
		return "", err
	}

	return config.Book, nil
}

func writeConfig(config Config) error {
	d, err := yaml.Marshal(config)
	if err != nil {
		return err
	}

	configPath, err := getConfigPath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(configPath, d, 0644)
	if err != nil {
		return err
	}

	return nil
}

// changeBook replaces the book name in the dnote config file
func changeBook(bookName string) error {
	config, err := readConfig()
	if err != nil {
		return err
	}

	config.Book = bookName

	err = writeConfig(config)
	if err != nil {
		return err
	}

	return nil
}

func readNote() (Note, error) {
	ret := Note{}

	notePath, err := getDnotePath()
	if err != nil {
		return ret, err
	}

	b, err := ioutil.ReadFile(notePath)
	if err != nil {
		return ret, nil
	}

	err = yaml.Unmarshal(b, &ret)
	if err != nil {
		return ret, err
	}

	return ret, nil
}

func writeNote(content string) error {
	note, err := readNote()
	if err != nil {
		return err
	}

	book, err := getCurrentBook()
	if err != nil {
		return err
	}

	if _, ok := note[book]; ok {
		note[book] = append(note[book], content)
	} else {
		note[book] = []string{content}
	}

	d, err := yaml.Marshal(note)
	if err != nil {
		return err
	}

	notePath, err := getDnotePath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(notePath, d, 0644)
	if err != nil {
		return err
	}

	return nil
}

func getBooks() ([]string, error) {
	note, err := readNote()
	if err != nil {
		return nil, err
	}

	books := make([]string, 0, len(note))
	for k := range note {
		books = append(books, k)
	}

	sort.Strings(books)

	return books, nil
}

func checkFileExists(filepath string) bool {
	_, err := os.Stat(filepath)
	return !os.IsNotExist(err)
}

func main() {
	err := initDnote()
	check(err)

	if len(os.Args) < 2 {
		fmt.Println("Dnote - A command line tool to spontaneously record new learnings\n")
		fmt.Println("Commands:")
		fmt.Println("  use [u] - choose the book")
		fmt.Println("  new [n] - write a new note")
		fmt.Println("  ls [l] - show books")
		os.Exit(0)
	}

	command := os.Args[1]

	switch command {
	case "use", "u":
		book := os.Args[2]
		err := changeBook(book)
		check(err)
	case "new", "n":
		note := os.Args[2]
		fmt.Println(note)
		err := writeNote(note)
		check(err)
	case "ls", "l", "books":
		currentBook, err := getCurrentBook()
		check(err)
		books, err := getBooks()
		check(err)

		for _, book := range books {
			if book == currentBook {
				fmt.Printf("* %v\n", book)
			} else {
				fmt.Printf("  %v\n", book)
			}
		}
	default:
		break
	}

}
<commit_msg>Check update<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"sort"
)

type Config struct {
	Book string
}

type Note map[string][]string

const configFilename = ".dnoterc"
const dnoteFilename = ".dnote"
const version = "v0.0.1"

func getConfigPath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%s/%s", usr.HomeDir, configFilename), nil
}

func getDnotePath() (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%s/%s", usr.HomeDir, dnoteFilename), nil
}

func generateConfigFile() error {
	content := []byte("book: general\n")
	configPath, err := getConfigPath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(configPath, content, 0644)
	return err
}

func touchDnoteFile() error {
	dnotePath, err := getDnotePath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(dnotePath, []byte{}, 0644)
	return err
}

// initDnote creates a config file if one does not exist
func initDnote() error {
	configPath, err := getConfigPath()
	if err != nil {
		return err
	}
	dnotePath, err := getDnotePath()
	if err != nil {
		return err
	}

	if !checkFileExists(configPath) {
		err := generateConfigFile()
		if err != nil {
			return err
		}
	}
	if !checkFileExists(dnotePath) {
		err := touchDnoteFile()
		if err != nil {
			return err
		}
	}

	return nil
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func readConfig() (Config, error) {
	var ret Config

	configPath, err := getConfigPath()
	if err != nil {
		return ret, err
	}

	b, err := ioutil.ReadFile(configPath)
	if err != nil {
		return ret, err
	}

	err = yaml.Unmarshal(b, &ret)
	if err != nil {
		return ret, err
	}

	return ret, nil
}

func getCurrentBook() (string, error) {
	config, err := readConfig()
	if err != nil {
		return "", err
	}

	return config.Book, nil
}

func writeConfig(config Config) error {
	d, err := yaml.Marshal(config)
	if err != nil {
		return err
	}

	configPath, err := getConfigPath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(configPath, d, 0644)
	if err != nil {
		return err
	}

	return nil
}

// changeBook replaces the book name in the dnote config file
func changeBook(bookName string) error {
	config, err := readConfig()
	if err != nil {
		return err
	}

	config.Book = bookName

	err = writeConfig(config)
	if err != nil {
		return err
	}

	return nil
}

func readNote() (Note, error) {
	ret := Note{}

	notePath, err := getDnotePath()
	if err != nil {
		return ret, err
	}

	b, err := ioutil.ReadFile(notePath)
	if err != nil {
		return ret, nil
	}

	err = yaml.Unmarshal(b, &ret)
	if err != nil {
		return ret, err
	}

	return ret, nil
}

func writeNote(content string) error {
	note, err := readNote()
	if err != nil {
		return err
	}

	book, err := getCurrentBook()
	if err != nil {
		return err
	}

	if _, ok := note[book]; ok {
		note[book] = append(note[book], content)
	} else {
		note[book] = []string{content}
	}

	d, err := yaml.Marshal(note)
	if err != nil {
		return err
	}

	notePath, err := getDnotePath()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(notePath, d, 0644)
	if err != nil {
		return err
	}

	return nil
}

func getBooks() ([]string, error) {
	note, err := readNote()
	if err != nil {
		return nil, err
	}

	books := make([]string, 0, len(note))
	for k := range note {
		books = append(books, k)
	}

	sort.Strings(books)

	return books, nil
}

func checkFileExists(filepath string) bool {
	_, err := os.Stat(filepath)
	return !os.IsNotExist(err)
}

func checkUpdates() error {
	endpoint := "https://api.github.com/repos/dnote-io/cli/releases"
	resp, err := http.Get(endpoint)
	if err != nil {
		return err
	}

	defer resp.Body.Close()

	var x []map[string]interface{}
	err = json.NewDecoder(resp.Body).Decode(&x)
	if err != nil {
		return err
	}

	if len(x) == 0 {
		return nil
	}

	latestRelease := x[0]
	latestVersion := latestRelease["tag_name"]

	if version != latestVersion {
		fmt.Println("==")
		fmt.Printf("Update available: %s\n", latestVersion)
		fmt.Printf("See: %s\n", x[0]["html_url"])
		fmt.Println("==\n")
	}

	return nil
}

func main() {
	err := initDnote()
	check(err)
	err = checkUpdates()
	check(err)

	if len(os.Args) < 2 {
		fmt.Println("Dnote - Spontaneously capture new engineering lessons\n")
		fmt.Println("Commands:")
		fmt.Println("  use [u] - choose the book")
		fmt.Println("  new [n] - write a new note")
		fmt.Println("  books [b] - show books")
		os.Exit(0)
	}

	command := os.Args[1]

	switch command {
	case "use", "u":
		book := os.Args[2]
		err := changeBook(book)
		check(err)
	case "new", "n":
		note := os.Args[2]
		fmt.Println(note)
		err := writeNote(note)
		check(err)
	case "books", "b":
		currentBook, err := getCurrentBook()
		check(err)
		books, err := getBooks()
		check(err)

		for _, book := range books {
			if book == currentBook {
				fmt.Printf("* %v\n", book)
			} else {
				fmt.Printf("  %v\n", book)
			}
		}
	default:
		break
	}

}
<|endoftext|>


<commit_before>package main

import (
  "flag"
  "fmt"
  "log"
  "os"
  "os/exec"
  "strings"
  "syscall"
  "time"
)

func main() {
  var interval int
  flag.IntVar(&interval, "i", 1, "interval")

  var interrupt bool
  flag.BoolVar(&interrupt, "x", false, "interrupt")

  flag.Parse()

  command := flag.Args()

  loop(intervalToTime(interval), func () {
    reset()
    status := run(measure(command))
    if(interrupt && status != 0) {
      os.Exit(status)
    }
  })
}

func intervalToTime(i int) time.Duration {
  return time.Duration(i) * time.Second
}

func reset() {
  run([]string {"clear"})
}

func measure(command []string) []string {
  return append([]string {"time"}, command...)
}

func run(command []string) int {
  cmd := exec.Command(getShell(), wrapForShell(buildArgs(command))...)
  cmd.Env = os.Environ()
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  cmd.Run()
  status, _ := cmd.ProcessState.Sys().(syscall.WaitStatus)
  return status.ExitStatus()
}

func safe(err error) {
  if err != nil {
    log.Fatal(err)
  }
}

func buildArgs(command []string) string {
  return strings.Join(command, " ")
}

func wrapForShell(command string) []string {
  return append([]string {"-c"}, fmt.Sprintf("%s; eval %s", sourceFiles(), command))
}

func sourceFiles() string {
  return fmt.Sprintf("%s %s", getShellSourceCmd(), getSourceFilePath())
}

func getSourceFilePath() string {
  return "~/.zshrc"
}

func getShellSourceCmd() string {
  return "."
}

func getShell() string {
  bin, found := syscall.Getenv("SHELL")
  if found == false {
    bin, _ = exec.LookPath("sh")
  }
  return bin
}

func loop(d time.Duration, fn func()) {
  fn()
  select {
  case <- time.After(d):
    loop(d, fn)
  }
}
<commit_msg>Adds -v<commit_after>package main

import (
  "flag"
  "fmt"
  "log"
  "os"
  "os/exec"
  "strings"
  "syscall"
  "time"
)

const VERSION="v0.0.1"

func main() {
  var interval int
  flag.IntVar(&interval, "i", 1, "interval")

  var interrupt bool
  flag.BoolVar(&interrupt, "x", false, "interrupt")

  var version bool
  flag.BoolVar(&version, "v", false, "version")

  flag.Parse()

  if version {
    fmt.Println(VERSION)
    os.Exit(0)
  }

  command := flag.Args()

  loop(intervalToTime(interval), func () {
    reset()
    status := run(measure(command))
    if(interrupt && status != 0) {
      os.Exit(status)
    }
  })
}

func intervalToTime(i int) time.Duration {
  return time.Duration(i) * time.Second
}

func reset() {
  run([]string {"clear"})
}

func measure(command []string) []string {
  return append([]string {"time"}, command...)
}

func run(command []string) int {
  cmd := exec.Command(getShell(), wrapForShell(buildArgs(command))...)
  cmd.Env = os.Environ()
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  cmd.Run()
  status, _ := cmd.ProcessState.Sys().(syscall.WaitStatus)
  return status.ExitStatus()
}

func safe(err error) {
  if err != nil {
    log.Fatal(err)
  }
}

func buildArgs(command []string) string {
  return strings.Join(command, " ")
}

func wrapForShell(command string) []string {
  return append([]string {"-c"}, fmt.Sprintf("%s; eval %s", sourceFiles(), command))
}

func sourceFiles() string {
  return fmt.Sprintf("%s %s", getShellSourceCmd(), getSourceFilePath())
}

func getSourceFilePath() string {
  return "~/.zshrc"
}

func getShellSourceCmd() string {
  return "."
}

func getShell() string {
  bin, found := syscall.Getenv("SHELL")
  if found == false {
    bin, _ = exec.LookPath("sh")
  }
  return bin
}

func loop(d time.Duration, fn func()) {
  fn()
  select {
  case <- time.After(d):
    loop(d, fn)
  }
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"

	"github.com/docker/swarm/discovery"
	_ "github.com/docker/swarm/discovery/consul"
	_ "github.com/docker/swarm/discovery/etcd"
	_ "github.com/docker/swarm/discovery/file"
	_ "github.com/docker/swarm/discovery/nodes"
	"github.com/docker/swarm/discovery/token"
	_ "github.com/docker/swarm/discovery/zookeeper"
)

func main() {
	app := cli.NewApp()
	app.Name = "swarm"
	app.Usage = "docker clustering"
	app.Version = "0.0.1"
	app.Author = ""
	app.Email = ""

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:   "debug",
			Usage:  "debug mode",
			EnvVar: "DEBUG",
		},
	}

	// logs
	app.Before = func(c *cli.Context) error {
		log.SetOutput(os.Stderr)
		if c.Bool("debug") {
			log.SetLevel(log.DebugLevel)
		}
		return nil
	}

	app.Commands = []cli.Command{
		{
			Name:      "create",
			ShortName: "c",
			Usage:     "create a cluster",
			Action: func(c *cli.Context) {
				discovery := &token.TokenDiscoveryService{}
				discovery.Initialize("", 0)
				token, err := discovery.CreateCluster()
				if err != nil {
					log.Fatal(err)
				}
				fmt.Println(token)
			},
		},
		{
			Name:      "list",
			ShortName: "l",
			Usage:     "list nodes in a cluster",
			Flags:     []cli.Flag{flDiscovery},
			Action: func(c *cli.Context) {
				if c.String("discovery") == "" {
					log.Fatal("--discovery required to list a cluster")
				}

				d, err := discovery.New(c.String("discovery"), 0)
				if err != nil {
					log.Fatal(err)
				}

				nodes, err := d.Fetch()
				if err != nil {
					log.Fatal(err)
				}
				for _, node := range nodes {
					fmt.Println(node)
				}
			},
		},
		{
			Name:      "manage",
			ShortName: "m",
			Usage:     "manage a docker cluster",
			Flags: []cli.Flag{
				flDiscovery,
				flStore,
				flStrategy, flFilter,
				flHosts, flHeartBeat, flOverCommit,
				flTls, flTlsCaCert, flTlsCert, flTlsKey, flTlsVerify,
				flEnableCors},
			Action: manage,
		},
		{
			Name:      "join",
			ShortName: "j",
			Usage:     "join a docker cluster",
			Flags:     []cli.Flag{flDiscovery, flAddr, flHeartBeat},
			Action:    join,
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Bump version to 0.1.0<commit_after>package main

import (
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"

	"github.com/docker/swarm/discovery"
	_ "github.com/docker/swarm/discovery/consul"
	_ "github.com/docker/swarm/discovery/etcd"
	_ "github.com/docker/swarm/discovery/file"
	_ "github.com/docker/swarm/discovery/nodes"
	"github.com/docker/swarm/discovery/token"
	_ "github.com/docker/swarm/discovery/zookeeper"
)

func main() {
	app := cli.NewApp()
	app.Name = "swarm"
	app.Usage = "docker clustering"
	app.Version = "0.1.0"
	app.Author = ""
	app.Email = ""

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:   "debug",
			Usage:  "debug mode",
			EnvVar: "DEBUG",
		},
	}

	// logs
	app.Before = func(c *cli.Context) error {
		log.SetOutput(os.Stderr)
		if c.Bool("debug") {
			log.SetLevel(log.DebugLevel)
		}
		return nil
	}

	app.Commands = []cli.Command{
		{
			Name:      "create",
			ShortName: "c",
			Usage:     "create a cluster",
			Action: func(c *cli.Context) {
				discovery := &token.TokenDiscoveryService{}
				discovery.Initialize("", 0)
				token, err := discovery.CreateCluster()
				if err != nil {
					log.Fatal(err)
				}
				fmt.Println(token)
			},
		},
		{
			Name:      "list",
			ShortName: "l",
			Usage:     "list nodes in a cluster",
			Flags:     []cli.Flag{flDiscovery},
			Action: func(c *cli.Context) {
				if c.String("discovery") == "" {
					log.Fatal("--discovery required to list a cluster")
				}

				d, err := discovery.New(c.String("discovery"), 0)
				if err != nil {
					log.Fatal(err)
				}

				nodes, err := d.Fetch()
				if err != nil {
					log.Fatal(err)
				}
				for _, node := range nodes {
					fmt.Println(node)
				}
			},
		},
		{
			Name:      "manage",
			ShortName: "m",
			Usage:     "manage a docker cluster",
			Flags: []cli.Flag{
				flDiscovery,
				flStore,
				flStrategy, flFilter,
				flHosts, flHeartBeat, flOverCommit,
				flTls, flTlsCaCert, flTlsCert, flTlsKey, flTlsVerify,
				flEnableCors},
			Action: manage,
		},
		{
			Name:      "join",
			ShortName: "j",
			Usage:     "join a docker cluster",
			Flags:     []cli.Flag{flDiscovery, flAddr, flHeartBeat},
			Action:    join,
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"
)

type publickey struct {
	algorithm   string
	key         string
	fingerprint string
	name        string
}

type unixuser struct {
	name string
	home string
}

type access struct {
	ts   time.Time
	user string
	ip   string
}

func main() {
	allkeys, err := getAuthorizedKeysForAllUsers()
	if err != nil {
		fmt.Println(err)
		return
	}

	logs, err := parseAllLogFiles()
	if err != nil {
		fmt.Println(err)
		return
	}

	now := time.Now()

	var usernames []string
	for k := range allkeys {
		usernames = append(usernames, k)
	}
	sort.Strings(usernames)

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
	fmt.Fprintf(w, "USER\tNAME\tALG\tUSAGE\tCOUNT\tFINGERPRINT\n")

	for _, user := range usernames {
		for _, key := range allkeys[user] {
			found, lastUse, count := findLog(logs, key.fingerprint, user)

			var lastUseStr string
			if found {
				lastUseStr = durationAsString(now.Sub(lastUse))
			} else {
				lastUseStr = "never"
			}
			var countStr string
			if count > 0 {
				countStr = fmt.Sprintf("%5d", count)
			} else {
				countStr = "    -"
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", user, key.name,
				key.algorithm, lastUseStr, countStr, key.fingerprint)
		}
	}

	w.Flush()
}

func getAuthorizedKeys(homedir string) ([]publickey, error) {
	// open ~/.ssh/authorized_keys
	file, err := os.Open(path.Join(homedir, ".ssh", "authorized_keys"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// every public key is in its own line
	var keys []publickey
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		splits := strings.SplitN(scanner.Text(), " ", 3)
		keys = append(keys, publickey{
			algorithm:   splits[0],
			key:         splits[1],
			fingerprint: computeFingerprint(splits[1]),
			name:        splits[2],
		})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return keys, nil
}

func computeFingerprint(pubkey string) string {
	pubkeyBytes, err := base64.StdEncoding.DecodeString(pubkey)
	if err != nil {
		return ""
	}

	hasher := md5.New()
	hasher.Write(pubkeyBytes)
	hash := hex.EncodeToString(hasher.Sum(nil))

	var colonhash bytes.Buffer
	for pos, ch := range hash {
		colonhash.WriteRune(ch)
		if pos%2 == 1 && pos < len(hash)-1 {
			colonhash.WriteString(":")
		}
	}
	return colonhash.String()
}

func parseLogFile(path string, accesses map[string][]access) error {
	info, err := os.Stat(path)
	if err != nil {
		return err
	}
	logFileYear := info.ModTime().Year()
	logFileTimezone := info.ModTime().Location()

	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	var scanner *bufio.Scanner
	if strings.HasSuffix(path, ".gz") {
		gr, err := gzip.NewReader(file)
		if err != nil {
			return err
		}
		defer gr.Close()

		scanner = bufio.NewScanner(gr)
	} else {
		scanner = bufio.NewScanner(file)
	}

	pattern := regexp.MustCompile("([A-Za-z]+ [ 0-9][0-9] [0-9]+:[0-9]+:[0-9]+) [^ ]* sshd\\[[0-9]+\\]: Accepted publickey for (.+) from ([0-9a-f.:]+) port [0-9]+ ssh2: RSA ([0-9a-f:]+)")

	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "Accepted publickey for") {
			m := pattern.FindStringSubmatch(line)
			if len(m) != 5 {
				continue
			}
			fingerprint := m[4]
			ts, err := time.ParseInLocation("2006 Jan 2 15:04:05", strconv.Itoa(logFileYear)+" "+m[1], logFileTimezone)
			if err != nil {
				continue
			}
			accesses[fingerprint] = append(accesses[fingerprint],
				access{ts: ts, user: m[2], ip: m[3]})
		}
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	return nil
}

// Read /etc/passwd and return all users and their corresponding home directory
func getAllUsers() ([]unixuser, error) {
	file, err := os.Open("/etc/passwd")
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var users []unixuser

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fs := strings.Split(scanner.Text(), ":")
		if len(fs) != 7 {
			continue
		}
		users = append(users, unixuser{name: fs[0], home: fs[5]})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return users, nil
}

func getAuthorizedKeysForAllUsers() (map[string][]publickey, error) {
	keys := make(map[string][]publickey)

	users, err := getAllUsers()
	if err != nil {
		return nil, err
	}

	for _, user := range users {
		userkeys, err := getAuthorizedKeys(user.home)
		if err != nil {
			continue
		}
		keys[user.name] = userkeys
	}

	return keys, nil
}

func durationAsString(dur time.Duration) string {
	var count int
	var unit string

	if dur.Minutes() < 1 {
		return "just now"
	} else if dur.Hours() < 1 {
		count = int(dur.Minutes())
		unit = "minute"
	} else if dur.Hours() < 24 {
		count = int(dur.Hours())
		unit = "hour"
	} else {
		count = int(dur.Hours() / 24)
		unit = "day"
	}

	if count != 1 {
		// add plural 's'
		unit += "s"
	}

	return fmt.Sprintf("%d %s ago", count, unit)
}

func getLogFiles() ([]string, error) {
	const logDir = "/var/log"
	files, err := ioutil.ReadDir(logDir)
	if err != nil {
		return nil, err
	}

	logFiles := []string{}
	for _, f := range files {
		if matched, err := filepath.Match("auth.log*", f.Name()); err == nil && matched {
			logFiles = append(logFiles, filepath.Join(logDir, f.Name()))
		}
	}

	return logFiles, nil
}

func parseAllLogFiles() (map[string][]access, error) {
	allfiles, err := getLogFiles()
	if err != nil {
		return nil, err
	}

	logs := make(map[string][]access)
	for _, file := range allfiles {
		err := parseLogFile(file, logs)
		if err != nil {
			return nil, err
		}
	}

	return logs, nil
}

// Find the last log entry for (fingerprint, user) in the given logs mapping
// Returns a tuple of (found, lastUse, count)
// lastUse.IsZero() == true iff found == false
func findLog(logs map[string][]access, fingerprint string, user string) (bool, time.Time, int) {
	found := false
	var lastUse time.Time
	count := 0

	for _, log := range logs[fingerprint] {
		if log.user != user {
			continue
		}
		found = true
		count++
		if lastUse.IsZero() || log.ts.After(lastUse) {
			lastUse = log.ts
		}
	}

	return found, lastUse, count
}
<commit_msg>Add sanity check<commit_after>package main

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"
	"time"
)

type publickey struct {
	algorithm   string
	key         string
	fingerprint string
	name        string
}

type unixuser struct {
	name string
	home string
}

type access struct {
	ts   time.Time
	user string
	ip   string
}

func main() {
	allkeys, err := getAuthorizedKeysForAllUsers()
	if err != nil {
		fmt.Println(err)
		return
	}

	logs, err := parseAllLogFiles()
	if err != nil {
		fmt.Println(err)
		return
	}

	now := time.Now()

	var usernames []string
	for k := range allkeys {
		usernames = append(usernames, k)
	}
	sort.Strings(usernames)

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
	fmt.Fprintf(w, "USER\tNAME\tALG\tUSAGE\tCOUNT\tFINGERPRINT\n")

	for _, user := range usernames {
		for _, key := range allkeys[user] {
			found, lastUse, count := findLog(logs, key.fingerprint, user)

			var lastUseStr string
			if found {
				lastUseStr = durationAsString(now.Sub(lastUse))
			} else {
				lastUseStr = "never"
			}
			var countStr string
			if count > 0 {
				countStr = fmt.Sprintf("%5d", count)
			} else {
				countStr = "    -"
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", user, key.name,
				key.algorithm, lastUseStr, countStr, key.fingerprint)
		}
	}

	w.Flush()
}

func getAuthorizedKeys(homedir string) ([]publickey, error) {
	// open ~/.ssh/authorized_keys
	file, err := os.Open(path.Join(homedir, ".ssh", "authorized_keys"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// every public key is in its own line
	var keys []publickey
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		splits := strings.SplitN(scanner.Text(), " ", 3)
		if len(splits) != 3 {
			continue
		}
		keys = append(keys, publickey{
			algorithm:   splits[0],
			key:         splits[1],
			fingerprint: computeFingerprint(splits[1]),
			name:        splits[2],
		})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return keys, nil
}

func computeFingerprint(pubkey string) string {
	pubkeyBytes, err := base64.StdEncoding.DecodeString(pubkey)
	if err != nil {
		return ""
	}

	hasher := md5.New()
	hasher.Write(pubkeyBytes)
	hash := hex.EncodeToString(hasher.Sum(nil))

	var colonhash bytes.Buffer
	for pos, ch := range hash {
		colonhash.WriteRune(ch)
		if pos%2 == 1 && pos < len(hash)-1 {
			colonhash.WriteString(":")
		}
	}
	return colonhash.String()
}

func parseLogFile(path string, accesses map[string][]access) error {
	info, err := os.Stat(path)
	if err != nil {
		return err
	}
	logFileYear := info.ModTime().Year()
	logFileTimezone := info.ModTime().Location()

	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	var scanner *bufio.Scanner
	if strings.HasSuffix(path, ".gz") {
		gr, err := gzip.NewReader(file)
		if err != nil {
			return err
		}
		defer gr.Close()

		scanner = bufio.NewScanner(gr)
	} else {
		scanner = bufio.NewScanner(file)
	}

	pattern := regexp.MustCompile("([A-Za-z]+ [ 0-9][0-9] [0-9]+:[0-9]+:[0-9]+) [^ ]* sshd\\[[0-9]+\\]: Accepted publickey for (.+) from ([0-9a-f.:]+) port [0-9]+ ssh2: RSA ([0-9a-f:]+)")

	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "Accepted publickey for") {
			m := pattern.FindStringSubmatch(line)
			if len(m) != 5 {
				continue
			}
			fingerprint := m[4]
			ts, err := time.ParseInLocation("2006 Jan 2 15:04:05", strconv.Itoa(logFileYear)+" "+m[1], logFileTimezone)
			if err != nil {
				continue
			}
			accesses[fingerprint] = append(accesses[fingerprint],
				access{ts: ts, user: m[2], ip: m[3]})
		}
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	return nil
}

// Read /etc/passwd and return all users and their corresponding home directory
func getAllUsers() ([]unixuser, error) {
	file, err := os.Open("/etc/passwd")
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var users []unixuser

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fs := strings.Split(scanner.Text(), ":")
		if len(fs) != 7 {
			continue
		}
		users = append(users, unixuser{name: fs[0], home: fs[5]})
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return users, nil
}

func getAuthorizedKeysForAllUsers() (map[string][]publickey, error) {
	keys := make(map[string][]publickey)

	users, err := getAllUsers()
	if err != nil {
		return nil, err
	}

	for _, user := range users {
		userkeys, err := getAuthorizedKeys(user.home)
		if err != nil {
			continue
		}
		keys[user.name] = userkeys
	}

	return keys, nil
}

func durationAsString(dur time.Duration) string {
	var count int
	var unit string

	if dur.Minutes() < 1 {
		return "just now"
	} else if dur.Hours() < 1 {
		count = int(dur.Minutes())
		unit = "minute"
	} else if dur.Hours() < 24 {
		count = int(dur.Hours())
		unit = "hour"
	} else {
		count = int(dur.Hours() / 24)
		unit = "day"
	}

	if count != 1 {
		// add plural 's'
		unit += "s"
	}

	return fmt.Sprintf("%d %s ago", count, unit)
}

func getLogFiles() ([]string, error) {
	const logDir = "/var/log"
	files, err := ioutil.ReadDir(logDir)
	if err != nil {
		return nil, err
	}

	logFiles := []string{}
	for _, f := range files {
		if matched, err := filepath.Match("auth.log*", f.Name()); err == nil && matched {
			logFiles = append(logFiles, filepath.Join(logDir, f.Name()))
		}
	}

	return logFiles, nil
}

func parseAllLogFiles() (map[string][]access, error) {
	allfiles, err := getLogFiles()
	if err != nil {
		return nil, err
	}

	logs := make(map[string][]access)
	for _, file := range allfiles {
		err := parseLogFile(file, logs)
		if err != nil {
			return nil, err
		}
	}

	return logs, nil
}

// Find the last log entry for (fingerprint, user) in the given logs mapping
// Returns a tuple of (found, lastUse, count)
// lastUse.IsZero() == true iff found == false
func findLog(logs map[string][]access, fingerprint string, user string) (bool, time.Time, int) {
	found := false
	var lastUse time.Time
	count := 0

	for _, log := range logs[fingerprint] {
		if log.user != user {
			continue
		}
		found = true
		count++
		if lastUse.IsZero() || log.ts.After(lastUse) {
			lastUse = log.ts
		}
	}

	return found, lastUse, count
}
<|endoftext|>


<commit_before>// Benchwrap automates running and analysing Go benchmarks.
//
// Usage:
//
//	usage: benchwrap rev.old [rev.new] [rev.more ...]
//
// Benchwrap runs a set of benchmarks n times for one or more git revisions. It
// feeds the collected benchmark results to `benchstat`, which in turn analyzes
// the data and prints it to stdout. Each input rev must be a valid git commit
// or reference, e.g. a hash, tag or branch. Options to `go test` and
// `benchstat` can be given by using the appropriate flags.
//
// Options:
//
//	  -bench regexp
//	        regexp denoting benchmarks to run (go test -bench) (default ".")
//	  -delta-test test
//	        forward test to benchstat -delta-test flag
//	  -gt-flags string
//	        forward quoted string of flags to go test
//	  -html
//	        invoke benchstat with -html flag
//	  -n number
//	        number of go test invocations per git revision (default 10)
//	  -pkgs string
//	        packages to test (go test [packages]) (default ".")
//	  -v    print verbose output to stderr
//
// Dependencies:
//
// 	go get [-u] rsc.io/benchstat
//
// Example
//
// In a git repository, run all `Foo` benchmarks 10 times each for git tag
// `v0.42`, commit `cdd48c8a` and branch master, and analyse results with
// benchstat:
//
// 	$ benchwrap -n 10 -bench=Foo v0.42 cdd48c8a master
//
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"unicode/utf8"
)

var (
	bench   = flag.String("bench", ".", "`regexp` denoting benchmarks to run (go test -bench)")
	nflag   = flag.Int("n", 10, "`number` of go test invocations per git revision")
	gtpkgs  = flag.String("pkgs", ".", "packages to test (go test [packages])")
	gtflags = flag.String("gt-flags", "", "forward quoted `string` of flags to go test")
	bsdelta = flag.String("delta-test", "", "forward `test` to benchstat -delta-test flag")
	bshtml  = flag.Bool("html", false, "invoke benchstat with -html flag")
	verbose = flag.Bool("v", false, "print verbose output to stderr")
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: benchwrap rev.old [rev.new] [rev.more ...]\n")
	fmt.Fprintf(os.Stderr, "\noptions:\n")
	flag.PrintDefaults()
	os.Exit(2)
}

type rev struct {
	bytes.Buffer
	name      string
	sha1      string
	sha1Short string
	fpath     string
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		flag.Usage()
	}

	_, err := exec.LookPath("benchstat")
	if err != nil {
		fmt.Fprint(os.Stderr, "no benchstat binary in $PATH\n")
		fmt.Fprint(os.Stderr, "go get [-u] rsc.io/benchstat\n")
		os.Exit(2)
	}

	setupLogging()

	var (
		revs    []*rev
		tmpdir  string
		nmaxlen int
		bsargs  []string
		bsout   []byte
		out     bytes.Buffer
	)

	currentRevName, err := gitNameRev("HEAD")
	if err != nil {
		goto err
	}

	for i := 0; i < flag.NArg(); i++ {
		f := flag.Arg(i)
		r := &rev{}
		r.name = f
		r.sha1, err = gitRevParseVerify(f)
		if err != nil {
			goto err
		}
		r.sha1Short = shortSHA1(r.sha1)
		revs = append(revs, r)
	}

	for _, rev := range revs {
		err = gitCheckout(rev.sha1)
		if err != nil {
			goto err
		}
		for i := 0; i < *nflag; i++ {
			var tmp []byte
			var args []string
			args = append(
				args,
				"test",
				*gtpkgs,
				"-run=NONE",
				"-bench="+*bench,
			)
			if *gtflags != "" {
				fs := strings.Fields(*gtflags)
				args = append(args, fs...)
			}
			tmp, err = run("go", args...)
			if err != nil {
				goto err
			}
			log.Printf("%s\n", tmp)
			rev.Write(tmp)
		}
	}

	tmpdir, err = ioutil.TempDir("", "bw")
	if err != nil {
		goto err
	}

	for _, rev := range revs {
		rev.fpath = filepath.Join(tmpdir, rev.sha1Short)
		err = ioutil.WriteFile(
			rev.fpath,
			rev.Bytes(),
			0644,
		)
		if err != nil {
			goto err
		}
	}

	if *bshtml {
		bsargs = append(bsargs, "-html")
	}
	if *bsdelta != "" {
		bsargs = append(bsargs, "-delta-test", *bsdelta)
	}
	for _, rev := range revs {
		bsargs = append(bsargs, rev.fpath)
	}
	bsout, err = run(
		"benchstat",
		bsargs...,
	)
	if err != nil {
		goto err
	}

	for _, rev := range revs {
		n := utf8.RuneCountInString(rev.name)
		if n > nmaxlen {
			nmaxlen = n
		}
	}

	switch len(revs) {
	case 1:
		out.WriteString(fmt.Sprintf("%s: %s\n", revs[0].name, revs[0].sha1))
	case 2:
		out.WriteString(fmt.Sprintf("%s:\t%s\n", "old", revs[0].sha1))
		out.WriteString(fmt.Sprintf("%s:\t%s\n", "new", revs[1].sha1))
	default:
		for _, rev := range revs {
			out.WriteString(fmt.Sprintf("%*s", -nmaxlen, rev.name))
			out.WriteString(fmt.Sprintf("\t%s\n", rev.sha1))
		}
	}

	out.WriteByte('\n')
	out.Write(bsout)
	out.WriteByte('\n')

	os.Stdout.Write(out.Bytes())

	gitCheckout(currentRevName)
	if tmpdir != "" {
		err = os.RemoveAll(tmpdir)
		if err != nil {
			log.Println(err)
		}
	}
	os.Exit(0)

err:
	gitCheckout(currentRevName)
	if tmpdir != "" {
		os.RemoveAll(tmpdir)
	}
	fmt.Fprintf(os.Stderr, "benchwrap: %v\n", err)
	os.Exit(2)
}

func gitNameRev(rev string) (name string, err error) {
	out, err := run("git", "name-rev", "--name-only", rev)
	return string(out), err
}

func gitRevParseVerify(rev string) (sha1 string, err error) {
	out, err := run("git", "rev-parse", "--verify", rev)
	return string(out), err
}

func gitCheckout(sha1 string) error {
	_, err := run("git", "checkout", sha1)
	return err
}

func run(command string, args ...string) ([]byte, error) {
	log.Println(strings.Join(append([]string{command}, args...), " "))
	cmd := exec.Command(command, args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("error: %v\n%s\n", err, out)
	}
	return bytes.TrimSuffix(out, []byte{'\n'}), nil
}

func shortSHA1(sha1 string) string {
	if len(sha1) < 5 {
		return sha1
	}
	return sha1[:5]
}

func setupLogging() {
	log.SetPrefix("benchwrap: ")
	log.SetFlags(0)
	if !*verbose {
		log.SetOutput(ioutil.Discard)
	}
}
<commit_msg>benchwrap: add -h-vs-h1 flag<commit_after>// Benchwrap automates running and analysing Go benchmarks.
//
// Usage:
//
//	usage: benchwrap rev.old [rev.new] [rev.more ...]
//
// Benchwrap runs a set of benchmarks n times for one or more git revisions. It
// feeds the collected benchmark results to `benchstat`, which in turn analyzes
// the data and prints it to stdout. Each input rev must be a valid git commit
// or reference, e.g. a hash, tag or branch. Options to `go test` and
// `benchstat` can be given by using the appropriate flags.
//
// Options:
//
//	  -bench regexp
//	        regexp denoting benchmarks to run (go test -bench) (default ".")
//	  -delta-test test
//	        forward test to benchstat -delta-test flag
//	  -gt-flags string
//	        forward quoted string of flags to go test
//	  -html
//	        invoke benchstat with -html flag
//	  -n number
//	        number of go test invocations per git revision (default 10)
//	  -pkgs string
//	        packages to test (go test [packages]) (default ".")
//	  -v    print verbose output to stderr
//
// Dependencies:
//
// 	go get [-u] rsc.io/benchstat
//
// Example
//
// In a git repository, run all `Foo` benchmarks 10 times each for git tag
// `v0.42`, commit `cdd48c8a` and branch master, and analyse results with
// benchstat:
//
// 	$ benchwrap -n 10 -bench=Foo v0.42 cdd48c8a master
//
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"unicode/utf8"
)

var (
	bench   = flag.String("bench", ".", "`regexp` denoting benchmarks to run (go test -bench)")
	nflag   = flag.Int("n", 10, "`number` of go test invocations per git revision")
	hvsh1   = flag.Bool("h-vs-h1", false, "use HEAD~1 as rev.old and HEAD as rev.new")
	gtpkgs  = flag.String("pkgs", ".", "packages to test (go test [packages])")
	gtflags = flag.String("gt-flags", "", "forward quoted `string` of flags to go test")
	bsdelta = flag.String("delta-test", "", "forward `test` to benchstat -delta-test flag")
	bshtml  = flag.Bool("html", false, "invoke benchstat with -html flag")
	verbose = flag.Bool("v", false, "print verbose output to stderr")
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: benchwrap rev.old [rev.new] [rev.more ...]\n")
	fmt.Fprintf(os.Stderr, "\noptions:\n")
	flag.PrintDefaults()
	os.Exit(2)
}

type rev struct {
	bytes.Buffer
	name      string
	sha1      string
	sha1Short string
	fpath     string
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 && !*hvsh1 {
		flag.Usage()
	}

	_, err := exec.LookPath("benchstat")
	if err != nil {
		fmt.Fprint(os.Stderr, "no benchstat binary in $PATH\n")
		fmt.Fprint(os.Stderr, "go get [-u] rsc.io/benchstat\n")
		os.Exit(2)
	}

	setupLogging()

	var (
		args    []string
		revs    []*rev
		tmpdir  string
		nmaxlen int
		bsargs  []string
		bsout   []byte
		out     bytes.Buffer
	)

	currentRevName, err := gitNameRev("HEAD")
	if err != nil {
		goto err
	}

	if *hvsh1 {
		args = []string{"HEAD~1", "HEAD"}
	} else {
		args = flag.Args()
	}

	for _, arg := range args {
		r := &rev{}
		r.name = arg
		r.sha1, err = gitRevParseVerify(arg)
		if err != nil {
			goto err
		}
		r.sha1Short = shortSHA1(r.sha1)
		revs = append(revs, r)
	}

	for _, rev := range revs {
		err = gitCheckout(rev.sha1)
		if err != nil {
			goto err
		}
		for i := 0; i < *nflag; i++ {
			var tmp []byte
			var args []string
			args = append(
				args,
				"test",
				*gtpkgs,
				"-run=NONE",
				"-bench="+*bench,
			)
			if *gtflags != "" {
				fs := strings.Fields(*gtflags)
				args = append(args, fs...)
			}
			tmp, err = run("go", args...)
			if err != nil {
				goto err
			}
			log.Printf("%s\n", tmp)
			rev.Write(tmp)
		}
	}

	tmpdir, err = ioutil.TempDir("", "bw")
	if err != nil {
		goto err
	}

	for _, rev := range revs {
		rev.fpath = filepath.Join(tmpdir, rev.sha1Short)
		err = ioutil.WriteFile(
			rev.fpath,
			rev.Bytes(),
			0644,
		)
		if err != nil {
			goto err
		}
	}

	if *bshtml {
		bsargs = append(bsargs, "-html")
	}
	if *bsdelta != "" {
		bsargs = append(bsargs, "-delta-test", *bsdelta)
	}
	for _, rev := range revs {
		bsargs = append(bsargs, rev.fpath)
	}
	bsout, err = run(
		"benchstat",
		bsargs...,
	)
	if err != nil {
		goto err
	}

	for _, rev := range revs {
		n := utf8.RuneCountInString(rev.name)
		if n > nmaxlen {
			nmaxlen = n
		}
	}

	switch len(revs) {
	case 1:
		out.WriteString(fmt.Sprintf("%s: %s\n", revs[0].name, revs[0].sha1))
	case 2:
		out.WriteString(fmt.Sprintf("%s:\t%s\n", "old", revs[0].sha1))
		out.WriteString(fmt.Sprintf("%s:\t%s\n", "new", revs[1].sha1))
	default:
		for _, rev := range revs {
			out.WriteString(fmt.Sprintf("%*s", -nmaxlen, rev.name))
			out.WriteString(fmt.Sprintf("\t%s\n", rev.sha1))
		}
	}

	out.WriteByte('\n')
	out.Write(bsout)
	out.WriteByte('\n')

	os.Stdout.Write(out.Bytes())

	gitCheckout(currentRevName)
	if tmpdir != "" {
		err = os.RemoveAll(tmpdir)
		if err != nil {
			log.Println(err)
		}
	}
	os.Exit(0)

err:
	gitCheckout(currentRevName)
	if tmpdir != "" {
		os.RemoveAll(tmpdir)
	}
	fmt.Fprintf(os.Stderr, "benchwrap: %v\n", err)
	os.Exit(2)
}

func gitNameRev(rev string) (name string, err error) {
	out, err := run("git", "name-rev", "--name-only", rev)
	return string(out), err
}

func gitRevParseVerify(rev string) (sha1 string, err error) {
	out, err := run("git", "rev-parse", "--verify", rev)
	return string(out), err
}

func gitCheckout(sha1 string) error {
	_, err := run("git", "checkout", sha1)
	return err
}

func run(command string, args ...string) ([]byte, error) {
	log.Println(strings.Join(append([]string{command}, args...), " "))
	cmd := exec.Command(command, args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("error: %v\n%s\n", err, out)
	}
	return bytes.TrimSuffix(out, []byte{'\n'}), nil
}

func shortSHA1(sha1 string) string {
	if len(sha1) < 5 {
		return sha1
	}
	return sha1[:5]
}

func setupLogging() {
	log.SetPrefix("benchwrap: ")
	log.SetFlags(0)
	if !*verbose {
		log.SetOutput(ioutil.Discard)
	}
}
<|endoftext|>


<commit_before>// Package main defines a command line interface for the sqlboiler package
package main

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

	"github.com/kat-co/vala"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/vattle/sqlboiler/bdb/drivers"
	"github.com/vattle/sqlboiler/boilingcore"
)

const sqlBoilerVersion = "2.2.0"

var (
	cmdState  *boilingcore.State
	cmdConfig *boilingcore.Config
)

func main() {
	var err error

	// Too much happens between here and cobra's argument handling, for
	// something so simple just do it immediately.
	for _, arg := range os.Args {
		if arg == "--version" {
			fmt.Println("SQLBoiler v" + sqlBoilerVersion)
			return
		}
	}

	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "./"
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Ignore errors here, fallback to other validation methods.
	// Users can use environment variables if a config is not found.
	_ = viper.ReadInConfig()

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates an ORM tailored to your database schema.",
		Long: "SQL Boiler generates a Go ORM from template files, tailored to your database schema.\n" +
			`Complete documentation is available at http://github.com/vattle/sqlboiler`,
		Example:       `sqlboiler postgres`,
		PreRunE:       preRun,
		RunE:          run,
		PostRunE:      postRun,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("schema", "s", "public", "The name of your database schema, for databases that support real schemas")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")
	rootCmd.PersistentFlags().StringP("basedir", "", "", "The base directory has the templates and templates_test folders")
	rootCmd.PersistentFlags().StringSliceP("blacklist", "b", nil, "Do not include these tables in your generated package")
	rootCmd.PersistentFlags().StringSliceP("whitelist", "w", nil, "Only include these tables in your generated package")
	rootCmd.PersistentFlags().StringSliceP("tag", "t", nil, "Struct tags to be included on your models in addition to json, yaml, toml")
	rootCmd.PersistentFlags().StringSliceP("replace", "", nil, "Replace templates by directory: relpath/to_file.tpl:relpath/to_replacement.tpl")
	rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug mode prints stack traces on error")
	rootCmd.PersistentFlags().BoolP("no-tests", "", false, "Disable generated go test files")
	rootCmd.PersistentFlags().BoolP("no-hooks", "", false, "Disable hooks feature for your models")
	rootCmd.PersistentFlags().BoolP("no-auto-timestamps", "", false, "Disable automatic timestamps for created_at/updated_at")
	rootCmd.PersistentFlags().BoolP("version", "", false, "Print the version")
	rootCmd.PersistentFlags().BoolP("tinyint-as-bool", "", false, "Map MySQL tinyint(1) in Go to bool instead of int8")
	rootCmd.PersistentFlags().BoolP("wipe", "", false, "Delete the output folder (rm -rf) before generation to ensure sanity")

	// hide flags not recommended for use
	rootCmd.PersistentFlags().MarkHidden("replace")

	viper.SetDefault("postgres.sslmode", "require")
	viper.SetDefault("postgres.port", "5432")
	viper.SetDefault("mysql.sslmode", "true")
	viper.SetDefault("mysql.port", "3306")

	viper.BindPFlags(rootCmd.PersistentFlags())
	viper.AutomaticEnv()

	if err := rootCmd.Execute(); err != nil {
		if e, ok := err.(commandFailure); ok {
			fmt.Printf("Error: %v\n\n", string(e))
			rootCmd.Help()
		} else if !viper.GetBool("debug") {
			fmt.Printf("Error: %v\n", err)
		} else {
			fmt.Printf("Error: %+v\n", err)
		}

		os.Exit(1)
	}
}

type commandFailure string

func (c commandFailure) Error() string {
	return string(c)
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return commandFailure("must provide a driver name")
	}

	driverName := args[0]

	cmdConfig = &boilingcore.Config{
		DriverName:       driverName,
		OutFolder:        viper.GetString("output"),
		Schema:           viper.GetString("schema"),
		PkgName:          viper.GetString("pkgname"),
		BaseDir:          viper.GetString("basedir"),
		Debug:            viper.GetBool("debug"),
		NoTests:          viper.GetBool("no-tests"),
		NoHooks:          viper.GetBool("no-hooks"),
		NoAutoTimestamps: viper.GetBool("no-auto-timestamps"),
		Wipe:             viper.GetBool("wipe"),
	}

	// BUG: https://github.com/spf13/viper/issues/200
	// Look up the value of blacklist, whitelist & tags directly from PFlags in Cobra if we
	// detect a malformed value coming out of viper.
	// Once the bug is fixed we'll be able to move this into the init above
	cmdConfig.BlacklistTables = viper.GetStringSlice("blacklist")
	if len(cmdConfig.BlacklistTables) == 1 && strings.ContainsRune(cmdConfig.BlacklistTables[0], ',') {
		cmdConfig.BlacklistTables, err = cmd.PersistentFlags().GetStringSlice("blacklist")
		if err != nil {
			return err
		}
	}

	cmdConfig.WhitelistTables = viper.GetStringSlice("whitelist")
	if len(cmdConfig.WhitelistTables) == 1 && strings.ContainsRune(cmdConfig.WhitelistTables[0], ',') {
		cmdConfig.WhitelistTables, err = cmd.PersistentFlags().GetStringSlice("whitelist")
		if err != nil {
			return err
		}
	}

	cmdConfig.Tags = viper.GetStringSlice("tag")
	if len(cmdConfig.Tags) == 1 && strings.ContainsRune(cmdConfig.Tags[0], ',') {
		cmdConfig.Tags, err = cmd.PersistentFlags().GetStringSlice("tag")
		if err != nil {
			return err
		}
	}

	cmdConfig.Replacements = viper.GetStringSlice("replace")
	if len(cmdConfig.Replacements) == 1 && strings.ContainsRune(cmdConfig.Replacements[0], ',') {
		cmdConfig.Replacements, err = cmd.PersistentFlags().GetStringSlice("replace")
		if err != nil {
			return err
		}
	}

	if driverName == "postgres" {
		cmdConfig.Postgres = boilingcore.PostgresConfig{
			User:    viper.GetString("postgres.user"),
			Pass:    viper.GetString("postgres.pass"),
			Host:    viper.GetString("postgres.host"),
			Port:    viper.GetInt("postgres.port"),
			DBName:  viper.GetString("postgres.dbname"),
			SSLMode: viper.GetString("postgres.sslmode"),
		}

		// BUG: https://github.com/spf13/viper/issues/71
		// Despite setting defaults, nested values don't get defaults
		// Set them manually
		if cmdConfig.Postgres.SSLMode == "" {
			cmdConfig.Postgres.SSLMode = "require"
			viper.Set("postgres.sslmode", cmdConfig.Postgres.SSLMode)
		}

		if cmdConfig.Postgres.Port == 0 {
			cmdConfig.Postgres.Port = 5432
			viper.Set("postgres.port", cmdConfig.Postgres.Port)
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.Postgres.User, "postgres.user"),
			vala.StringNotEmpty(cmdConfig.Postgres.Host, "postgres.host"),
			vala.Not(vala.Equals(cmdConfig.Postgres.Port, 0, "postgres.port")),
			vala.StringNotEmpty(cmdConfig.Postgres.DBName, "postgres.dbname"),
			vala.StringNotEmpty(cmdConfig.Postgres.SSLMode, "postgres.sslmode"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	}

	if driverName == "mysql" {
		cmdConfig.MySQL = boilingcore.MySQLConfig{
			User:    viper.GetString("mysql.user"),
			Pass:    viper.GetString("mysql.pass"),
			Host:    viper.GetString("mysql.host"),
			Port:    viper.GetInt("mysql.port"),
			DBName:  viper.GetString("mysql.dbname"),
			SSLMode: viper.GetString("mysql.sslmode"),
		}

		// Set MySQL TinyintAsBool global var. This flag only applies to MySQL.
		drivers.TinyintAsBool = viper.GetBool("tinyint-as-bool")

		// MySQL doesn't have schemas, just databases
		cmdConfig.Schema = cmdConfig.MySQL.DBName

		// BUG: https://github.com/spf13/viper/issues/71
		// Despite setting defaults, nested values don't get defaults
		// Set them manually
		if cmdConfig.MySQL.SSLMode == "" {
			cmdConfig.MySQL.SSLMode = "true"
			viper.Set("mysql.sslmode", cmdConfig.MySQL.SSLMode)
		}

		if cmdConfig.MySQL.Port == 0 {
			cmdConfig.MySQL.Port = 3306
			viper.Set("mysql.port", cmdConfig.MySQL.Port)
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.MySQL.User, "mysql.user"),
			vala.StringNotEmpty(cmdConfig.MySQL.Host, "mysql.host"),
			vala.Not(vala.Equals(cmdConfig.MySQL.Port, 0, "mysql.port")),
			vala.StringNotEmpty(cmdConfig.MySQL.DBName, "mysql.dbname"),
			vala.StringNotEmpty(cmdConfig.MySQL.SSLMode, "mysql.sslmode"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	}

	if driverName == "mssql" {
		cmdConfig.MSSQL = boilingcore.MSSQLConfig{
			User:    viper.GetString("mssql.user"),
			Pass:    viper.GetString("mssql.pass"),
			Host:    viper.GetString("mssql.host"),
			Port:    viper.GetInt("mssql.port"),
			DBName:  viper.GetString("mssql.dbname"),
			SSLMode: viper.GetString("mssql.sslmode"),
		}

		// BUG: https://github.com/spf13/viper/issues/71
		// Despite setting defaults, nested values don't get defaults
		// Set them manually
		if cmdConfig.MSSQL.SSLMode == "" {
			cmdConfig.MSSQL.SSLMode = "true"
			viper.Set("mssql.sslmode", cmdConfig.MSSQL.SSLMode)
		}

		if cmdConfig.MSSQL.Port == 0 {
			cmdConfig.MSSQL.Port = 3306
			viper.Set("mssql.port", cmdConfig.MSSQL.Port)
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.MSSQL.User, "mssql.user"),
			vala.StringNotEmpty(cmdConfig.MSSQL.Host, "mssql.host"),
			vala.Not(vala.Equals(cmdConfig.MSSQL.Port, 0, "mssql.port")),
			vala.StringNotEmpty(cmdConfig.MSSQL.DBName, "mssql.dbname"),
			vala.StringNotEmpty(cmdConfig.MSSQL.SSLMode, "mssql.sslmode"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	}

	cmdState, err = boilingcore.New(cmdConfig)
	return err
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run(true)
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}
<commit_msg>Fixed default port for MS SQL<commit_after>// Package main defines a command line interface for the sqlboiler package
package main

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

	"github.com/kat-co/vala"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/vattle/sqlboiler/bdb/drivers"
	"github.com/vattle/sqlboiler/boilingcore"
)

const sqlBoilerVersion = "2.2.0"

var (
	cmdState  *boilingcore.State
	cmdConfig *boilingcore.Config
)

func main() {
	var err error

	// Too much happens between here and cobra's argument handling, for
	// something so simple just do it immediately.
	for _, arg := range os.Args {
		if arg == "--version" {
			fmt.Println("SQLBoiler v" + sqlBoilerVersion)
			return
		}
	}

	viper.SetConfigName("sqlboiler")

	configHome := os.Getenv("XDG_CONFIG_HOME")
	homePath := os.Getenv("HOME")
	wd, err := os.Getwd()
	if err != nil {
		wd = "./"
	}

	configPaths := []string{wd}
	if len(configHome) > 0 {
		configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler"))
	} else {
		configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler"))
	}

	for _, p := range configPaths {
		viper.AddConfigPath(p)
	}

	// Ignore errors here, fallback to other validation methods.
	// Users can use environment variables if a config is not found.
	_ = viper.ReadInConfig()

	// Set up the cobra root command
	var rootCmd = &cobra.Command{
		Use:   "sqlboiler [flags] <driver>",
		Short: "SQL Boiler generates an ORM tailored to your database schema.",
		Long: "SQL Boiler generates a Go ORM from template files, tailored to your database schema.\n" +
			`Complete documentation is available at http://github.com/vattle/sqlboiler`,
		Example:       `sqlboiler postgres`,
		PreRunE:       preRun,
		RunE:          run,
		PostRunE:      postRun,
		SilenceErrors: true,
		SilenceUsage:  true,
	}

	// Set up the cobra root command flags
	rootCmd.PersistentFlags().StringP("output", "o", "models", "The name of the folder to output to")
	rootCmd.PersistentFlags().StringP("schema", "s", "public", "The name of your database schema, for databases that support real schemas")
	rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")
	rootCmd.PersistentFlags().StringP("basedir", "", "", "The base directory has the templates and templates_test folders")
	rootCmd.PersistentFlags().StringSliceP("blacklist", "b", nil, "Do not include these tables in your generated package")
	rootCmd.PersistentFlags().StringSliceP("whitelist", "w", nil, "Only include these tables in your generated package")
	rootCmd.PersistentFlags().StringSliceP("tag", "t", nil, "Struct tags to be included on your models in addition to json, yaml, toml")
	rootCmd.PersistentFlags().StringSliceP("replace", "", nil, "Replace templates by directory: relpath/to_file.tpl:relpath/to_replacement.tpl")
	rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug mode prints stack traces on error")
	rootCmd.PersistentFlags().BoolP("no-tests", "", false, "Disable generated go test files")
	rootCmd.PersistentFlags().BoolP("no-hooks", "", false, "Disable hooks feature for your models")
	rootCmd.PersistentFlags().BoolP("no-auto-timestamps", "", false, "Disable automatic timestamps for created_at/updated_at")
	rootCmd.PersistentFlags().BoolP("version", "", false, "Print the version")
	rootCmd.PersistentFlags().BoolP("tinyint-as-bool", "", false, "Map MySQL tinyint(1) in Go to bool instead of int8")
	rootCmd.PersistentFlags().BoolP("wipe", "", false, "Delete the output folder (rm -rf) before generation to ensure sanity")

	// hide flags not recommended for use
	rootCmd.PersistentFlags().MarkHidden("replace")

	viper.SetDefault("postgres.sslmode", "require")
	viper.SetDefault("postgres.port", "5432")
	viper.SetDefault("mysql.sslmode", "true")
	viper.SetDefault("mysql.port", "3306")

	viper.BindPFlags(rootCmd.PersistentFlags())
	viper.AutomaticEnv()

	if err := rootCmd.Execute(); err != nil {
		if e, ok := err.(commandFailure); ok {
			fmt.Printf("Error: %v\n\n", string(e))
			rootCmd.Help()
		} else if !viper.GetBool("debug") {
			fmt.Printf("Error: %v\n", err)
		} else {
			fmt.Printf("Error: %+v\n", err)
		}

		os.Exit(1)
	}
}

type commandFailure string

func (c commandFailure) Error() string {
	return string(c)
}

func preRun(cmd *cobra.Command, args []string) error {
	var err error

	if len(args) == 0 {
		return commandFailure("must provide a driver name")
	}

	driverName := args[0]

	cmdConfig = &boilingcore.Config{
		DriverName:       driverName,
		OutFolder:        viper.GetString("output"),
		Schema:           viper.GetString("schema"),
		PkgName:          viper.GetString("pkgname"),
		BaseDir:          viper.GetString("basedir"),
		Debug:            viper.GetBool("debug"),
		NoTests:          viper.GetBool("no-tests"),
		NoHooks:          viper.GetBool("no-hooks"),
		NoAutoTimestamps: viper.GetBool("no-auto-timestamps"),
		Wipe:             viper.GetBool("wipe"),
	}

	// BUG: https://github.com/spf13/viper/issues/200
	// Look up the value of blacklist, whitelist & tags directly from PFlags in Cobra if we
	// detect a malformed value coming out of viper.
	// Once the bug is fixed we'll be able to move this into the init above
	cmdConfig.BlacklistTables = viper.GetStringSlice("blacklist")
	if len(cmdConfig.BlacklistTables) == 1 && strings.ContainsRune(cmdConfig.BlacklistTables[0], ',') {
		cmdConfig.BlacklistTables, err = cmd.PersistentFlags().GetStringSlice("blacklist")
		if err != nil {
			return err
		}
	}

	cmdConfig.WhitelistTables = viper.GetStringSlice("whitelist")
	if len(cmdConfig.WhitelistTables) == 1 && strings.ContainsRune(cmdConfig.WhitelistTables[0], ',') {
		cmdConfig.WhitelistTables, err = cmd.PersistentFlags().GetStringSlice("whitelist")
		if err != nil {
			return err
		}
	}

	cmdConfig.Tags = viper.GetStringSlice("tag")
	if len(cmdConfig.Tags) == 1 && strings.ContainsRune(cmdConfig.Tags[0], ',') {
		cmdConfig.Tags, err = cmd.PersistentFlags().GetStringSlice("tag")
		if err != nil {
			return err
		}
	}

	cmdConfig.Replacements = viper.GetStringSlice("replace")
	if len(cmdConfig.Replacements) == 1 && strings.ContainsRune(cmdConfig.Replacements[0], ',') {
		cmdConfig.Replacements, err = cmd.PersistentFlags().GetStringSlice("replace")
		if err != nil {
			return err
		}
	}

	if driverName == "postgres" {
		cmdConfig.Postgres = boilingcore.PostgresConfig{
			User:    viper.GetString("postgres.user"),
			Pass:    viper.GetString("postgres.pass"),
			Host:    viper.GetString("postgres.host"),
			Port:    viper.GetInt("postgres.port"),
			DBName:  viper.GetString("postgres.dbname"),
			SSLMode: viper.GetString("postgres.sslmode"),
		}

		// BUG: https://github.com/spf13/viper/issues/71
		// Despite setting defaults, nested values don't get defaults
		// Set them manually
		if cmdConfig.Postgres.SSLMode == "" {
			cmdConfig.Postgres.SSLMode = "require"
			viper.Set("postgres.sslmode", cmdConfig.Postgres.SSLMode)
		}

		if cmdConfig.Postgres.Port == 0 {
			cmdConfig.Postgres.Port = 5432
			viper.Set("postgres.port", cmdConfig.Postgres.Port)
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.Postgres.User, "postgres.user"),
			vala.StringNotEmpty(cmdConfig.Postgres.Host, "postgres.host"),
			vala.Not(vala.Equals(cmdConfig.Postgres.Port, 0, "postgres.port")),
			vala.StringNotEmpty(cmdConfig.Postgres.DBName, "postgres.dbname"),
			vala.StringNotEmpty(cmdConfig.Postgres.SSLMode, "postgres.sslmode"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	}

	if driverName == "mysql" {
		cmdConfig.MySQL = boilingcore.MySQLConfig{
			User:    viper.GetString("mysql.user"),
			Pass:    viper.GetString("mysql.pass"),
			Host:    viper.GetString("mysql.host"),
			Port:    viper.GetInt("mysql.port"),
			DBName:  viper.GetString("mysql.dbname"),
			SSLMode: viper.GetString("mysql.sslmode"),
		}

		// Set MySQL TinyintAsBool global var. This flag only applies to MySQL.
		drivers.TinyintAsBool = viper.GetBool("tinyint-as-bool")

		// MySQL doesn't have schemas, just databases
		cmdConfig.Schema = cmdConfig.MySQL.DBName

		// BUG: https://github.com/spf13/viper/issues/71
		// Despite setting defaults, nested values don't get defaults
		// Set them manually
		if cmdConfig.MySQL.SSLMode == "" {
			cmdConfig.MySQL.SSLMode = "true"
			viper.Set("mysql.sslmode", cmdConfig.MySQL.SSLMode)
		}

		if cmdConfig.MySQL.Port == 0 {
			cmdConfig.MySQL.Port = 3306
			viper.Set("mysql.port", cmdConfig.MySQL.Port)
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.MySQL.User, "mysql.user"),
			vala.StringNotEmpty(cmdConfig.MySQL.Host, "mysql.host"),
			vala.Not(vala.Equals(cmdConfig.MySQL.Port, 0, "mysql.port")),
			vala.StringNotEmpty(cmdConfig.MySQL.DBName, "mysql.dbname"),
			vala.StringNotEmpty(cmdConfig.MySQL.SSLMode, "mysql.sslmode"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	}

	if driverName == "mssql" {
		cmdConfig.MSSQL = boilingcore.MSSQLConfig{
			User:    viper.GetString("mssql.user"),
			Pass:    viper.GetString("mssql.pass"),
			Host:    viper.GetString("mssql.host"),
			Port:    viper.GetInt("mssql.port"),
			DBName:  viper.GetString("mssql.dbname"),
			SSLMode: viper.GetString("mssql.sslmode"),
		}

		// BUG: https://github.com/spf13/viper/issues/71
		// Despite setting defaults, nested values don't get defaults
		// Set them manually
		if cmdConfig.MSSQL.SSLMode == "" {
			cmdConfig.MSSQL.SSLMode = "true"
			viper.Set("mssql.sslmode", cmdConfig.MSSQL.SSLMode)
		}

		if cmdConfig.MSSQL.Port == 0 {
			cmdConfig.MSSQL.Port = 1433
			viper.Set("mssql.port", cmdConfig.MSSQL.Port)
		}

		err = vala.BeginValidation().Validate(
			vala.StringNotEmpty(cmdConfig.MSSQL.User, "mssql.user"),
			vala.StringNotEmpty(cmdConfig.MSSQL.Host, "mssql.host"),
			vala.Not(vala.Equals(cmdConfig.MSSQL.Port, 0, "mssql.port")),
			vala.StringNotEmpty(cmdConfig.MSSQL.DBName, "mssql.dbname"),
			vala.StringNotEmpty(cmdConfig.MSSQL.SSLMode, "mssql.sslmode"),
		).Check()

		if err != nil {
			return commandFailure(err.Error())
		}
	}

	cmdState, err = boilingcore.New(cmdConfig)
	return err
}

func run(cmd *cobra.Command, args []string) error {
	return cmdState.Run(true)
}

func postRun(cmd *cobra.Command, args []string) error {
	return cmdState.Cleanup()
}
<|endoftext|>