From a7f29a716d6723432ed68414bd0b55970eaeff73 Mon Sep 17 00:00:00 2001 From: Mike Gray Date: Wed, 6 Jul 2016 15:38:25 -0400 Subject: [PATCH] noms as one command line application, with version and help (#1874) --- cmd/noms-diff/.gitignore | 1 - cmd/noms-diff/noms_diff.go | 72 ---------- cmd/noms-ds/.gitignore | 1 - cmd/noms-log/.gitignore | 1 - cmd/noms-serve/.gitignore | 1 - cmd/noms-serve/noms_serve.go | 57 -------- cmd/noms-show/.gitignore | 1 - cmd/noms-show/noms_show.go | 62 --------- cmd/noms-sync/.gitignore | 1 - cmd/noms-sync/noms_sync.go | 58 -------- cmd/{noms-serve => noms}/README.md | 4 - cmd/{noms-log => noms}/commit_iterator.go | 0 cmd/{noms-diff => noms}/diff/diff.go | 0 cmd/{noms-diff => noms}/diff/diff_test.go | 0 cmd/{noms-diff => noms}/diff/queue.go | 0 cmd/{noms-diff => noms}/diff/queue_test.go | 0 cmd/{noms-log => noms}/max_line_writer.go | 3 +- cmd/noms/noms.go | 152 ++++----------------- cmd/noms/noms_command.go | 67 +++++++++ cmd/noms/noms_diff.go | 66 +++++++++ cmd/{noms-ds => noms}/noms_ds.go | 37 ++--- cmd/{noms-ds => noms}/noms_ds_test.go | 24 ++-- cmd/noms/noms_help.go | 85 ++++++++++++ cmd/{noms-log => noms}/noms_log.go | 88 ++++++------ cmd/{noms-log => noms}/noms_log_test.go | 72 ++++++---- cmd/noms/noms_serve.go | 58 ++++++++ cmd/noms/noms_show.go | 56 ++++++++ cmd/{noms-show => noms}/noms_show_test.go | 15 +- cmd/noms/noms_sync.go | 60 ++++++++ cmd/{noms-sync => noms}/noms_sync_test.go | 10 +- cmd/noms/noms_version.go | 32 +++++ cmd/noms/noms_version_test.go | 28 ++++ go/chunks/leveldb_store.go | 6 +- go/constants/version.go | 2 + go/perf/codec-perf-rig/main.go | 1 + go/spec/dataspec.go | 5 +- go/util/clienttest/client_test_suite.go | 26 +++- go/util/outputpager/page_output.go | 10 +- go/util/profile/profile.go | 28 ++-- samples/go/counter/counter.go | 3 + samples/go/counter/counter_test.go | 12 +- samples/go/csv/csv-export/exporter.go | 3 +- samples/go/csv/csv-export/exporter_test.go | 5 +- samples/go/csv/csv-import/importer.go | 3 +- samples/go/csv/csv-import/importer_test.go | 20 +-- samples/go/demo-server/main.go | 2 +- samples/go/hr/main_test.go | 28 ++-- samples/go/json-import/json_importer.go | 2 +- samples/go/url-fetch/fetch.go | 2 +- samples/go/xml-import/xml_importer.go | 3 +- 50 files changed, 728 insertions(+), 545 deletions(-) delete mode 100644 cmd/noms-diff/.gitignore delete mode 100644 cmd/noms-diff/noms_diff.go delete mode 100644 cmd/noms-ds/.gitignore delete mode 100644 cmd/noms-log/.gitignore delete mode 100644 cmd/noms-serve/.gitignore delete mode 100644 cmd/noms-serve/noms_serve.go delete mode 100644 cmd/noms-show/.gitignore delete mode 100644 cmd/noms-show/noms_show.go delete mode 100644 cmd/noms-sync/.gitignore delete mode 100644 cmd/noms-sync/noms_sync.go rename cmd/{noms-serve => noms}/README.md (89%) rename cmd/{noms-log => noms}/commit_iterator.go (100%) rename cmd/{noms-diff => noms}/diff/diff.go (100%) rename cmd/{noms-diff => noms}/diff/diff_test.go (100%) rename cmd/{noms-diff => noms}/diff/queue.go (100%) rename cmd/{noms-diff => noms}/diff/queue_test.go (100%) rename cmd/{noms-log => noms}/max_line_writer.go (97%) create mode 100644 cmd/noms/noms_command.go create mode 100644 cmd/noms/noms_diff.go rename cmd/{noms-ds => noms}/noms_ds.go (52%) rename cmd/{noms-ds => noms}/noms_ds_test.go (76%) create mode 100644 cmd/noms/noms_help.go rename cmd/{noms-log => noms}/noms_log.go (74%) rename cmd/{noms-log => noms}/noms_log_test.go (86%) create mode 100644 cmd/noms/noms_serve.go create mode 100644 cmd/noms/noms_show.go rename cmd/{noms-show => noms}/noms_show_test.go (85%) create mode 100644 cmd/noms/noms_sync.go rename cmd/{noms-sync => noms}/noms_sync_test.go (86%) create mode 100644 cmd/noms/noms_version.go create mode 100644 cmd/noms/noms_version_test.go diff --git a/cmd/noms-diff/.gitignore b/cmd/noms-diff/.gitignore deleted file mode 100644 index 27dc359415..0000000000 --- a/cmd/noms-diff/.gitignore +++ /dev/null @@ -1 +0,0 @@ -noms-diff \ No newline at end of file diff --git a/cmd/noms-diff/noms_diff.go b/cmd/noms-diff/noms_diff.go deleted file mode 100644 index 6e670cf3c5..0000000000 --- a/cmd/noms-diff/noms_diff.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2016 Attic Labs, Inc. All rights reserved. -// Licensed under the Apache License, version 2.0: -// http://www.apache.org/licenses/LICENSE-2.0 - -package main - -import ( - "bufio" - "errors" - "flag" - "fmt" - "os" - - "github.com/attic-labs/noms/cmd/noms-diff/diff" - "github.com/attic-labs/noms/go/d" - "github.com/attic-labs/noms/go/spec" - "github.com/attic-labs/noms/go/util/outputpager" -) - -const ( - addPrefix = "+ " - subPrefix = "- " -) - -var ( - showHelp = flag.Bool("help", false, "show help text") -) - -func main() { - flag.Usage = func() { - fmt.Fprintln(os.Stderr, "Shows the difference between two objects\n") - fmt.Fprintln(os.Stderr, "Usage: noms diff \n") - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\nSee \"Spelling Objects\" at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object argument.\n\n") - } - - flag.Parse() - if *showHelp { - flag.Usage() - return - } - - if len(flag.Args()) != 2 { - d.CheckErrorNoUsage(errors.New("Expected exactly two arguments")) - } - - db1, value1, err := spec.GetPath(flag.Arg(0)) - d.CheckErrorNoUsage(err) - if value1 == nil { - d.CheckErrorNoUsage(fmt.Errorf("Object not found: %s", flag.Arg(0))) - } - defer db1.Close() - - db2, value2, err := spec.GetPath(flag.Arg(1)) - d.CheckErrorNoUsage(err) - if value2 == nil { - d.CheckErrorNoUsage(fmt.Errorf("Object not found: %s", flag.Arg(1))) - } - defer db2.Close() - - waitChan := outputpager.PageOutput(!*outputpager.NoPager) - - w := bufio.NewWriter(os.Stdout) - diff.Diff(w, value1, value2) - fmt.Fprintf(w, "\n") - w.Flush() - - if waitChan != nil { - os.Stdout.Close() - <-waitChan - } -} diff --git a/cmd/noms-ds/.gitignore b/cmd/noms-ds/.gitignore deleted file mode 100644 index fd4deef3a5..0000000000 --- a/cmd/noms-ds/.gitignore +++ /dev/null @@ -1 +0,0 @@ -noms-ds \ No newline at end of file diff --git a/cmd/noms-log/.gitignore b/cmd/noms-log/.gitignore deleted file mode 100644 index bd15576702..0000000000 --- a/cmd/noms-log/.gitignore +++ /dev/null @@ -1 +0,0 @@ -noms-log \ No newline at end of file diff --git a/cmd/noms-serve/.gitignore b/cmd/noms-serve/.gitignore deleted file mode 100644 index 887aecdd54..0000000000 --- a/cmd/noms-serve/.gitignore +++ /dev/null @@ -1 +0,0 @@ -noms-serve diff --git a/cmd/noms-serve/noms_serve.go b/cmd/noms-serve/noms_serve.go deleted file mode 100644 index 98f5067c05..0000000000 --- a/cmd/noms-serve/noms_serve.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016 Attic Labs, Inc. All rights reserved. -// Licensed under the Apache License, version 2.0: -// http://www.apache.org/licenses/LICENSE-2.0 - -package main - -import ( - "flag" - "fmt" - "os" - "os/signal" - "syscall" - - "github.com/attic-labs/noms/go/d" - "github.com/attic-labs/noms/go/datas" - "github.com/attic-labs/noms/go/spec" - "github.com/attic-labs/noms/go/util/profile" -) - -var ( - port = flag.Int("port", 8000, "") -) - -func main() { - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Serves a Noms database over HTTP\n\n") - fmt.Fprintf(os.Stderr, "Usage: noms serve \n") - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\nFor detailed information on spelling databases, see: at https://github.com/attic-labs/noms/blob/master/doc/spelling.md.\n\n") - } - - spec.RegisterDatabaseFlags() - flag.Parse() - - if len(flag.Args()) != 1 { - flag.Usage() - return - } - - cs, err := spec.GetChunkStore(flag.Arg(0)) - d.CheckError(err) - server := datas.NewRemoteDatabaseServer(cs, *port) - - // Shutdown server gracefully so that profile may be written - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) - signal.Notify(c, syscall.SIGTERM) - go func() { - <-c - server.Stop() - }() - - d.Try(func() { - defer profile.MaybeStartProfile().Stop() - server.Run() - }) -} diff --git a/cmd/noms-show/.gitignore b/cmd/noms-show/.gitignore deleted file mode 100644 index 41bd1d92bf..0000000000 --- a/cmd/noms-show/.gitignore +++ /dev/null @@ -1 +0,0 @@ -noms-show \ No newline at end of file diff --git a/cmd/noms-show/noms_show.go b/cmd/noms-show/noms_show.go deleted file mode 100644 index ce747df6c6..0000000000 --- a/cmd/noms-show/noms_show.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2016 Attic Labs, Inc. All rights reserved. -// Licensed under the Apache License, version 2.0: -// http://www.apache.org/licenses/LICENSE-2.0 - -package main - -import ( - "bufio" - "errors" - "flag" - "fmt" - "os" - - "github.com/attic-labs/noms/go/d" - "github.com/attic-labs/noms/go/spec" - "github.com/attic-labs/noms/go/types" - "github.com/attic-labs/noms/go/util/outputpager" -) - -var ( - showHelp = flag.Bool("help", false, "show help text") -) - -func main() { - flag.Usage = func() { - fmt.Fprintln(os.Stderr, "Shows a serialization of a Noms object\n") - fmt.Fprintln(os.Stderr, "Usage: noms show \n") - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\nSee \"Spelling Objects\" at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object argument.\n\n") - } - - flag.Parse() - if *showHelp { - flag.Usage() - return - } - - if len(flag.Args()) != 1 { - d.CheckErrorNoUsage(errors.New("expected exactly one argument")) - } - - database, value, err := spec.GetPath(flag.Arg(0)) - d.CheckErrorNoUsage(err) - - if value == nil { - fmt.Fprintf(os.Stderr, "Object not found: %s\n", flag.Arg(0)) - return - } - - waitChan := outputpager.PageOutput(!*outputpager.NoPager) - - w := bufio.NewWriter(os.Stdout) - types.WriteEncodedValueWithTags(w, value) - fmt.Fprintf(w, "\n") - w.Flush() - database.Close() - - if waitChan != nil { - os.Stdout.Close() - <-waitChan - } -} diff --git a/cmd/noms-sync/.gitignore b/cmd/noms-sync/.gitignore deleted file mode 100644 index fb8b3300ae..0000000000 --- a/cmd/noms-sync/.gitignore +++ /dev/null @@ -1 +0,0 @@ -noms-sync \ No newline at end of file diff --git a/cmd/noms-sync/noms_sync.go b/cmd/noms-sync/noms_sync.go deleted file mode 100644 index 9a9c1bd34b..0000000000 --- a/cmd/noms-sync/noms_sync.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2016 Attic Labs, Inc. All rights reserved. -// Licensed under the Apache License, version 2.0: -// http://www.apache.org/licenses/LICENSE-2.0 - -package main - -import ( - "errors" - "flag" - "fmt" - "log" - "os" - - "github.com/attic-labs/noms/go/d" - "github.com/attic-labs/noms/go/spec" - "github.com/attic-labs/noms/go/types" - "github.com/attic-labs/noms/go/util/profile" -) - -var ( - p = flag.Uint("p", 512, "parallelism") -) - -func main() { - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Moves datasets between or within databases\n\n") - fmt.Fprintf(os.Stderr, "noms sync [options] \n\n") - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\nFor detailed information on spelling objects and datasets, see: at https://github.com/attic-labs/noms/blob/master/doc/spelling.md.\n\n") - } - - spec.RegisterDatabaseFlags() - flag.Parse() - - if flag.NArg() != 2 { - d.CheckError(errors.New("expected a source object and destination dataset")) - } - - sourceStore, sourceObj, err := spec.GetPath(flag.Arg(0)) - d.CheckError(err) - defer sourceStore.Close() - - sinkDataset, err := spec.GetDataset(flag.Arg(1)) - d.CheckError(err) - defer sinkDataset.Database().Close() - - err = d.Try(func() { - defer profile.MaybeStartProfile().Stop() - - var err error - sinkDataset, err = sinkDataset.Pull(sourceStore, types.NewRef(sourceObj), int(*p)) - d.PanicIfError(err) - }) - - if err != nil { - log.Fatal(err) - } -} diff --git a/cmd/noms-serve/README.md b/cmd/noms/README.md similarity index 89% rename from cmd/noms-serve/README.md rename to cmd/noms/README.md index 41a04ed2b1..ac3c2ce823 100644 --- a/cmd/noms-serve/README.md +++ b/cmd/noms/README.md @@ -1,7 +1,3 @@ -# noms-serve - -noms-serve implements a noms database over HTTP. - ## Example ``` diff --git a/cmd/noms-log/commit_iterator.go b/cmd/noms/commit_iterator.go similarity index 100% rename from cmd/noms-log/commit_iterator.go rename to cmd/noms/commit_iterator.go diff --git a/cmd/noms-diff/diff/diff.go b/cmd/noms/diff/diff.go similarity index 100% rename from cmd/noms-diff/diff/diff.go rename to cmd/noms/diff/diff.go diff --git a/cmd/noms-diff/diff/diff_test.go b/cmd/noms/diff/diff_test.go similarity index 100% rename from cmd/noms-diff/diff/diff_test.go rename to cmd/noms/diff/diff_test.go diff --git a/cmd/noms-diff/diff/queue.go b/cmd/noms/diff/queue.go similarity index 100% rename from cmd/noms-diff/diff/queue.go rename to cmd/noms/diff/queue.go diff --git a/cmd/noms-diff/diff/queue_test.go b/cmd/noms/diff/queue_test.go similarity index 100% rename from cmd/noms-diff/diff/queue_test.go rename to cmd/noms/diff/queue_test.go diff --git a/cmd/noms-log/max_line_writer.go b/cmd/noms/max_line_writer.go similarity index 97% rename from cmd/noms-log/max_line_writer.go rename to cmd/noms/max_line_writer.go index 91b2b9a518..ee5741b21f 100644 --- a/cmd/noms-log/max_line_writer.go +++ b/cmd/noms/max_line_writer.go @@ -24,6 +24,7 @@ type maxLineWriter struct { node LogNode dest io.Writer needsPrefix bool + showGraph bool } func (w *maxLineWriter) writeMax(data []byte, enforceMax bool) (byteCnt int, err error) { @@ -31,7 +32,7 @@ func (w *maxLineWriter) writeMax(data []byte, enforceMax bool) (byteCnt int, err if w.needsPrefix { w.needsPrefix = false w.numLines++ - if *showGraph { + if w.showGraph { s := genGraph(w.node, w.numLines) _, err = w.dest.Write([]byte(s)) } diff --git a/cmd/noms/noms.go b/cmd/noms/noms.go index 4cf4c0832a..1fcbd9d503 100644 --- a/cmd/noms/noms.go +++ b/cmd/noms/noms.go @@ -8,147 +8,51 @@ import ( "flag" "fmt" "os" - "os/exec" - "path" - "sort" - "strings" - "syscall" ) -const ( - cmdPrefix = "noms-" -) - -func usage() { - fmt.Fprintf(os.Stderr, "usage: %s command [command-args]\n\n", path.Base(os.Args[0])) - if hasDefinedFlags(flag.CommandLine) { - fmt.Fprintf(os.Stderr, "Flags:\n\n") - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\n") - } - fmt.Fprintf(os.Stderr, "Commands:\n\n") - fmt.Fprintf(os.Stderr, " %s\n", strings.Join(listCmds(), "\n ")) - fmt.Fprintf(os.Stderr, "\nSee noms -h for information on each available command.\n\n") +var commands = []*nomsCommand{ + nomsDiff, + nomsDs, + nomsLog, + nomsServe, + nomsShow, + nomsSync, + nomsVersion, } func main() { flag.Usage = usage flag.Parse() - if flag.NArg() == 0 || flag.Arg(0) == "help" { + args := flag.Args() + if len(args) < 1 { usage() - os.Exit(1) - } - - cmd := findCmd(flag.Arg(0)) - if cmd == "" { - fmt.Fprintf(os.Stderr, "error: %s is not an available command\n", flag.Arg(0)) - usage() - os.Exit(1) - } - - executeCmd(cmd) -} - -func hasDefinedFlags(fs *flag.FlagSet) (hasFlags bool) { - fs.VisitAll(func(*flag.Flag) { - hasFlags = true - }) - return -} - -func findCmd(name string) (cmd string) { - nomsName := cmdPrefix + name - forEachDir(func(dir *os.File) (stop bool) { - if isNomsExecutable(dir, nomsName) { - cmd = path.Join(dir.Name(), nomsName) - stop = true - } return - }) - return -} + } -func listCmds() []string { - cmds := []string{} - - forEachDir(func(dir *os.File) (stop bool) { - // dir.Readdirnames may return an error, but |names| may still contain valid files. - names, _ := dir.Readdirnames(0) - for _, n := range names { - if isNomsExecutable(dir, n) { - cmds = append(cmds, n[len(cmdPrefix):]) - } - } + if args[0] == "help" { + help(args[1:]) return - }) - - sort.Strings(cmds) - return cmds -} - -func forEachDir(cb func(dir *os.File) bool) { - lookups := []struct { - Env string - Suffix string - }{ - {"PATH", ""}, - {"GOPATH", "bin"}, } - seen := map[string]bool{} + for _, cmd := range commands { + if cmd.Name() == args[0] { + flags := cmd.Flags() + flags.Usage = cmd.Usage - for _, lookup := range lookups { - env := os.Getenv(lookup.Env) - if env == "" { - continue - } - - paths := strings.Split(env, string(os.PathListSeparator)) - for _, p := range paths { - p := path.Join(p, lookup.Suffix) - - if seen[p] { - continue + flags.Parse(args[1:]) + args = flags.Args() + if cmd.Nargs != 0 && len(args) < cmd.Nargs { + cmd.Usage() } - - seen[p] = true - - if dir, err := os.Open(p); err == nil && cb(dir) { - return + exitCode := cmd.Run(args) + if exitCode != 0 { + os.Exit(exitCode) } + return } } -} - -func executeCmd(executable string) { - args := flag.Args()[1:] - if len(args) == 0 { - args = append(args, "-help") - } - nomsCmd := exec.Command(executable, args...) - nomsCmd.Stdin = os.Stdin - nomsCmd.Stdout = os.Stdout - nomsCmd.Stderr = os.Stderr - - err := nomsCmd.Run() - if err != nil { - switch t := err.(type) { - case *exec.ExitError: - status := t.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() - os.Exit(status) - default: - fmt.Fprintf(os.Stderr, "error: %s\n", err) - os.Exit(-1) - } - } -} - -func isNomsExecutable(dir *os.File, name string) bool { - if !strings.HasPrefix(name, cmdPrefix) || len(name) == len(cmdPrefix) { - return false - } - - fi, err := os.Stat(path.Join(dir.Name(), name)) - return err == nil && !fi.IsDir() && fi.Mode()&0111 != 0 + + fmt.Fprintf(os.Stderr, "noms: unknown command %q\n", args[0]) + usage() } diff --git a/cmd/noms/noms_command.go b/cmd/noms/noms_command.go new file mode 100644 index 0000000000..91d0b1bd42 --- /dev/null +++ b/cmd/noms/noms_command.go @@ -0,0 +1,67 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "flag" + "fmt" + "os" + "strings" +) + +type nomsCommand struct { + // Run runs the command. + // The args are the arguments after the command name. + Run func(args []string) int + + // UsageLine is the one-line usage message. + // The first word in the line is taken to be the command name. + UsageLine string + + // Short is the short description shown in the 'noms help' output. + Short string + + // Long is the long message shown in the 'noms help ' output. + Long string + + // Flag is a set of flags specific to this command. + Flags func() *flag.FlagSet + + // Nargs is the minimum number of arguments expected after flags, specific to this command. + Nargs int +} + +// Name returns the command's name: the first word in the usage line. +func (nc *nomsCommand) Name() string { + name := nc.UsageLine + i := strings.Index(name, " ") + if i >= 0 { + name = name[:i] + } + return name +} + +func countFlags(flags *flag.FlagSet) int { + if flags == nil { + return 0 + } else { + n := 0 + flags.VisitAll(func(f *flag.Flag) { + n++ + }) + return n + } +} + +func (nc *nomsCommand) Usage() { + fmt.Fprintf(os.Stderr, "usage: %s\n\n", nc.UsageLine) + fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(nc.Long)) + flags := nc.Flags() + if countFlags(flags) > 0 { + fmt.Fprintf(os.Stderr, "\noptions:\n") + flags.PrintDefaults() + } + os.Exit(1) +} diff --git a/cmd/noms/noms_diff.go b/cmd/noms/noms_diff.go new file mode 100644 index 0000000000..79374b028c --- /dev/null +++ b/cmd/noms/noms_diff.go @@ -0,0 +1,66 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + + "github.com/attic-labs/noms/cmd/noms/diff" + "github.com/attic-labs/noms/go/d" + "github.com/attic-labs/noms/go/spec" + "github.com/attic-labs/noms/go/util/outputpager" +) + +const ( + addPrefix = "+ " + subPrefix = "- " +) + +var nomsDiff = &nomsCommand{ + Run: runDiff, + UsageLine: "diff ", + Short: "Shows the difference between two objects", + Long: "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object arguments.", + Flags: setupDiffFlags, + Nargs: 2, +} + +func setupDiffFlags() *flag.FlagSet { + diffFlagSet := flag.NewFlagSet("diff", flag.ExitOnError) + outputpager.RegisterOutputpagerFlags(diffFlagSet) + return diffFlagSet +} + +func runDiff(args []string) int { + db1, value1, err := spec.GetPath(args[0]) + d.CheckErrorNoUsage(err) + if value1 == nil { + d.CheckErrorNoUsage(fmt.Errorf("Object not found: %s", args[0])) + } + defer db1.Close() + + db2, value2, err := spec.GetPath(args[1]) + d.CheckErrorNoUsage(err) + if value2 == nil { + d.CheckErrorNoUsage(fmt.Errorf("Object not found: %s", args[1])) + } + defer db2.Close() + + waitChan := outputpager.PageOutput(!outputpager.NoPager) + + w := bufio.NewWriter(os.Stdout) + diff.Diff(w, value1, value2) + fmt.Fprintf(w, "\n") + w.Flush() + + if waitChan != nil { + os.Stdout.Close() + <-waitChan + } + return 0 +} diff --git a/cmd/noms-ds/noms_ds.go b/cmd/noms/noms_ds.go similarity index 52% rename from cmd/noms-ds/noms_ds.go rename to cmd/noms/noms_ds.go index e07cfa25f2..ee63e532c0 100644 --- a/cmd/noms-ds/noms_ds.go +++ b/cmd/noms/noms_ds.go @@ -7,27 +7,31 @@ package main import ( "flag" "fmt" - "os" "github.com/attic-labs/noms/go/d" "github.com/attic-labs/noms/go/spec" "github.com/attic-labs/noms/go/types" ) -func main() { - toDelete := flag.String("d", "", "dataset to delete") +var toDelete string - flag.Usage = func() { - fmt.Fprintln(os.Stderr, "Noms dataset management\n") - fmt.Fprintln(os.Stderr, "Usage: noms ds [ | -d ]") - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\nFor detailed information on spelling datastores and datasets, see: at https://github.com/attic-labs/noms/blob/master/doc/spelling.md.\n\n") - } +var nomsDs = &nomsCommand{ + Run: runDs, + UsageLine: "ds [ | -d ]", + Short: "Noms dataset management", + Long: "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the database and dataset arguments.", + Flags: setupDsFlags, +} - flag.Parse() +func setupDsFlags() *flag.FlagSet { + dsFlagSet := flag.NewFlagSet("ds", flag.ExitOnError) + dsFlagSet.StringVar(&toDelete, "d", "", "dataset to delete") + return dsFlagSet +} - if *toDelete != "" { - set, err := spec.GetDataset(*toDelete) +func runDs(args []string) int { + if toDelete != "" { + set, err := spec.GetDataset(toDelete) d.CheckError(err) oldCommitRef, errBool := set.MaybeHeadRef() @@ -41,12 +45,11 @@ func main() { fmt.Printf("Deleted dataset %v (was %v)\n\n", set.ID(), oldCommitRef.TargetHash().String()) } else { - if flag.NArg() != 1 { - flag.Usage() - return + if len(args) != 1 { + d.CheckError(fmt.Errorf("Database arg missing")) } - store, err := spec.GetDatabase(flag.Arg(0)) + store, err := spec.GetDatabase(args[0]) d.CheckError(err) defer store.Close() @@ -54,5 +57,5 @@ func main() { fmt.Println(k) }) } - + return 0 } diff --git a/cmd/noms-ds/noms_ds_test.go b/cmd/noms/noms_ds_test.go similarity index 76% rename from cmd/noms-ds/noms_ds_test.go rename to cmd/noms/noms_ds_test.go index 93406c1ef3..38f3f3c542 100644 --- a/cmd/noms-ds/noms_ds_test.go +++ b/cmd/noms/noms_ds_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/attic-labs/noms/go/chunks" + "github.com/attic-labs/noms/go/d" "github.com/attic-labs/noms/go/datas" "github.com/attic-labs/noms/go/dataset" "github.com/attic-labs/noms/go/spec" @@ -17,14 +18,15 @@ import ( ) func TestDs(t *testing.T) { - suite.Run(t, &testSuite{}) + d.UtilExiter = testExiter{} + suite.Run(t, &nomsDsTestSuite{}) } -type testSuite struct { +type nomsDsTestSuite struct { clienttest.ClientTestSuite } -func (s *testSuite) TestEmptyNomsDs() { +func (s *nomsDsTestSuite) TestEmptyNomsDs() { dir := s.LdbDir cs := chunks.NewLevelDBStore(dir+"/name", "", 24, false) @@ -33,11 +35,11 @@ func (s *testSuite) TestEmptyNomsDs() { ds.Close() dbSpec := spec.CreateDatabaseSpecString("ldb", dir+"/name") - rtnVal := s.Run(main, []string{dbSpec}) + rtnVal, _ := s.Run(main, []string{"ds", dbSpec}) s.Equal("", rtnVal) } -func (s *testSuite) TestNomsDs() { +func (s *nomsDsTestSuite) TestNomsDs() { dir := s.LdbDir cs := chunks.NewLevelDBStore(dir+"/name", "", 24, false) @@ -61,26 +63,26 @@ func (s *testSuite) TestNomsDs() { dataset2Name := spec.CreateValueSpecString("ldb", dir+"/name", id2) // both datasets show up - rtnVal := s.Run(main, []string{dbSpec}) + rtnVal, _ := s.Run(main, []string{"ds", dbSpec}) s.Equal(id+"\n"+id2+"\n", rtnVal) // both datasets again, to make sure printing doesn't change them - rtnVal = s.Run(main, []string{dbSpec}) + rtnVal, _ = s.Run(main, []string{"ds", dbSpec}) s.Equal(id+"\n"+id2+"\n", rtnVal) // delete one dataset, print message at delete - rtnVal = s.Run(main, []string{"-d", datasetName}) + rtnVal, _ = s.Run(main, []string{"ds", "-d", datasetName}) s.Equal("Deleted dataset "+id+" (was sha1-d54b79552cda9ebe8e446eeb19aab0e69b6ceee3)\n\n", rtnVal) // print datasets, just one left - rtnVal = s.Run(main, []string{dbSpec}) + rtnVal, _ = s.Run(main, []string{"ds", dbSpec}) s.Equal(id2+"\n", rtnVal) // delete the second dataset - rtnVal = s.Run(main, []string{"-d", dataset2Name}) + rtnVal, _ = s.Run(main, []string{"ds", "-d", dataset2Name}) s.Equal("Deleted dataset "+id2+" (was sha1-7b75b0ebfc2a0815ba6fb2b31d03c8f9976ae530)\n\n", rtnVal) // print datasets, none left - rtnVal = s.Run(main, []string{dbSpec}) + rtnVal, _ = s.Run(main, []string{"ds", dbSpec}) s.Equal("", rtnVal) } diff --git a/cmd/noms/noms_help.go b/cmd/noms/noms_help.go new file mode 100644 index 0000000000..62f61c9557 --- /dev/null +++ b/cmd/noms/noms_help.go @@ -0,0 +1,85 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + "text/template" +) + +var usageTemplate = `Noms is a tool for iterating with Noms data. + +Usage: + + noms command [arguments] + +The commands are: +{{range .}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}} + +Use "noms help [command]" for more information about a command. + +` + +var helpTemplate = `usage: noms {{.UsageLine}} + +{{.Long | trim}} +` + +// tmpl executes the given template text on data, writing the result to w. +func tmpl(w io.Writer, text string, data interface{}) { + t := template.New("top") + t.Funcs(template.FuncMap{"trim": strings.TrimSpace}) + template.Must(t.Parse(text)) + if err := t.Execute(w, data); err != nil { + panic(err) + } +} + +func printUsage(w io.Writer) { + bw := bufio.NewWriter(w) + tmpl(bw, usageTemplate, commands) + bw.Flush() +} + +func usage() { + printUsage(os.Stderr) + os.Exit(1) +} + +// help implements the 'help' command. +func help(args []string) { + if len(args) == 0 { + printUsage(os.Stdout) + // not exit 2: succeeded at 'noms help'. + return + } + if len(args) != 1 { + fmt.Fprintf(os.Stderr, "usage: noms help command\n\nToo many arguments given.\n") + os.Exit(1) // failed at 'noms help' + } + + arg := args[0] + + for _, cmd := range commands { + if cmd.Name() == arg { + tmpl(os.Stdout, helpTemplate, cmd) + flags := cmd.Flags() + if countFlags(flags) > 0 { + fmt.Fprintf(os.Stdout, "\noptions:\n") + flags.PrintDefaults() + } + // not exit 2: succeeded at 'noms help cmd'. + return + } + } + + fmt.Fprintf(os.Stderr, "Unknown help topic %#q\n", arg) + usage() // failed at 'noms help cmd' +} diff --git a/cmd/noms-log/noms_log.go b/cmd/noms/noms_log.go similarity index 74% rename from cmd/noms-log/noms_log.go rename to cmd/noms/noms_log.go index 400ee18522..28e40c0c32 100644 --- a/cmd/noms-log/noms_log.go +++ b/cmd/noms/noms_log.go @@ -7,7 +7,6 @@ package main import ( "bufio" "bytes" - "errors" "flag" "fmt" "io" @@ -15,7 +14,7 @@ import ( "os" "strings" - "github.com/attic-labs/noms/cmd/noms-diff/diff" + "github.com/attic-labs/noms/cmd/noms/diff" "github.com/attic-labs/noms/go/d" "github.com/attic-labs/noms/go/datas" "github.com/attic-labs/noms/go/spec" @@ -26,62 +25,60 @@ import ( ) var ( - color, maxLines, maxCommits *int - showHelp, showGraph, showValue *bool - useColor = false + useColor = false + color int + maxLines int + maxCommits int + showGraph bool + showValue bool ) const parallelism = 16 -func main() { - color = flag.Int("color", -1, "value of 1 forces color on, 2 forces color off") - maxLines = flag.Int("max-lines", 10, "max number of lines to show per commit (-1 for all lines)") - maxCommits = flag.Int("n", 0, "max number of commits to display (0 for all commits)") - showHelp = flag.Bool("help", false, "show help text") - showGraph = flag.Bool("graph", false, "show ascii-based commit hierarcy on left side of output") - showValue = flag.Bool("show-value", false, "show commit value rather than diff information -- this is temporary") +var nomsLog = &nomsCommand{ + Run: runLog, + UsageLine: "log [options] ", + Short: "Displays the history of a Noms dataset", + Long: "commitObject must be a dataset or object spec that refers to a commit. See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details.", + Flags: setupLogFlags, + Nargs: 1, +} - flag.Usage = func() { - fmt.Fprintln(os.Stderr, "Displays the history of a Noms dataset\n") - fmt.Fprintln(os.Stderr, "Usage: noms log ") - fmt.Fprintln(os.Stderr, "commitObject must be a dataset or object spec that refers to a commit.") - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\nSee \"Spelling Objects\" at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object argument.\n\n") - } - - flag.Parse() - if *showHelp { - flag.Usage() - return - } - - if len(flag.Args()) != 1 { - d.CheckError(errors.New("expected exactly one argument")) - } +func setupLogFlags() *flag.FlagSet { + logFlagSet := flag.NewFlagSet("log", flag.ExitOnError) + logFlagSet.IntVar(&color, "color", -1, "value of 1 forces color on, 2 forces color off") + logFlagSet.IntVar(&maxLines, "max-lines", 10, "max number of lines to show per commit (-1 for all lines)") + logFlagSet.IntVar(&maxCommits, "n", 0, "max number of commits to display (0 for all commits)") + logFlagSet.BoolVar(&showGraph, "graph", false, "show ascii-based commit hierarcy on left side of output") + logFlagSet.BoolVar(&showValue, "show-value", false, "show commit value rather than diff information -- this is temporary") + outputpager.RegisterOutputpagerFlags(logFlagSet) + return logFlagSet +} +func runLog(args []string) int { useColor = shouldUseColor() - database, value, err := spec.GetPath(flag.Arg(0)) + database, value, err := spec.GetPath(args[0]) if err != nil { d.CheckErrorNoUsage(err) } defer database.Close() if value == nil { - d.CheckErrorNoUsage(fmt.Errorf("Object not found: %s", flag.Arg(0))) + d.CheckErrorNoUsage(fmt.Errorf("Object not found: %s", args[0])) } - waitChan := outputpager.PageOutput(!*outputpager.NoPager) + waitChan := outputpager.PageOutput(!outputpager.NoPager) origCommit, ok := value.(types.Struct) if !ok || !origCommit.Type().Equals(datas.CommitType()) { - d.CheckError(fmt.Errorf("%s does not reference a Commit object", flag.Arg(0))) + d.CheckError(fmt.Errorf("%s does not reference a Commit object", args[0])) } iter := NewCommitIterator(database, origCommit) displayed := 0 - if *maxCommits <= 0 { - *maxCommits = math.MaxInt32 + if maxCommits <= 0 { + maxCommits = math.MaxInt32 } inChan := make(chan interface{}, parallelism) @@ -92,7 +89,7 @@ func main() { }, parallelism) go func() { - for ln, ok := iter.Next(); ok && displayed < *maxCommits; ln, ok = iter.Next() { + for ln, ok := iter.Next(); ok && displayed < maxCommits; ln, ok = iter.Next() { inChan <- ln displayed++ } @@ -109,6 +106,7 @@ func main() { os.Stdout.Close() <-waitChan } + return 0 } // Prints the information for one commit in the log, including ascii graph on left side of commits if @@ -134,12 +132,12 @@ func printCommit(node LogNode, w io.Writer, db datas.Database) (err error) { } else { fmt.Fprintf(w, "%sParent: None\n", genGraph(node, lineno)) } - if *maxLines != 0 { + if maxLines != 0 { var n int - if *showValue { - n, err = writeCommitLines(node, *maxLines, lineno, w) + if showValue { + n, err = writeCommitLines(node, maxLines, lineno, w) } else { - n, err = writeDiffLines(node, db, *maxLines, lineno, w) + n, err = writeDiffLines(node, db, maxLines, lineno, w) } lineno += n } @@ -148,7 +146,7 @@ func printCommit(node LogNode, w io.Writer, db datas.Database) (err error) { // Generates ascii graph chars to display on the left side of the commit info if -graph arg is true. func genGraph(node LogNode, lineno int) string { - if !*showGraph { + if !showGraph { return "" } @@ -200,7 +198,7 @@ func genGraph(node LogNode, lineno int) string { } func writeCommitLines(node LogNode, maxLines, lineno int, w io.Writer) (lineCnt int, err error) { - mlw := &maxLineWriter{numLines: lineno, maxLines: maxLines, node: node, dest: w, needsPrefix: true} + mlw := &maxLineWriter{numLines: lineno, maxLines: maxLines, node: node, dest: w, needsPrefix: true, showGraph: showGraph} err = types.WriteEncodedValueWithTags(mlw, node.commit.Get(datas.ValueField)) d.PanicIfNotType(err, MaxLinesErr) if err != nil { @@ -216,7 +214,7 @@ func writeCommitLines(node LogNode, maxLines, lineno int, w io.Writer) (lineCnt } func writeDiffLines(node LogNode, db datas.Database, maxLines, lineno int, w io.Writer) (lineCnt int, err error) { - mlw := &maxLineWriter{numLines: lineno, maxLines: maxLines, node: node, dest: w, needsPrefix: true} + mlw := &maxLineWriter{numLines: lineno, maxLines: maxLines, node: node, dest: w, needsPrefix: true, showGraph: showGraph} parents := node.commit.Get(datas.ParentsField).(types.Set) var parent types.Value = nil if parents.Len() > 0 { @@ -242,10 +240,10 @@ func writeDiffLines(node LogNode, db datas.Database, maxLines, lineno int, w io. } func shouldUseColor() bool { - if *color != 1 && *color != 0 { + if color != 1 && color != 0 { return outputpager.IsStdoutTty() } - return *color == 1 + return color == 1 } func max(i, j int) int { diff --git a/cmd/noms-log/noms_log_test.go b/cmd/noms/noms_log_test.go similarity index 86% rename from cmd/noms-log/noms_log_test.go rename to cmd/noms/noms_log_test.go index ea9abdbcd4..e9a2e6b735 100644 --- a/cmd/noms-log/noms_log_test.go +++ b/cmd/noms/noms_log_test.go @@ -30,33 +30,34 @@ func (testExiter) Exit(code int) { panic(exitError{code}) } -func TestNomsShow(t *testing.T) { +func TestNomsLog(t *testing.T) { d.UtilExiter = testExiter{} - suite.Run(t, &nomsShowTestSuite{}) + suite.Run(t, &nomsLogTestSuite{}) } -type nomsShowTestSuite struct { +type nomsLogTestSuite struct { clienttest.ClientTestSuite } -func testCommitInResults(s *nomsShowTestSuite, str string, i int) { +func testCommitInResults(s *nomsLogTestSuite, str string, i int) { ds, err := spec.GetDataset(str) s.NoError(err) ds, err = ds.Commit(types.Number(i)) s.NoError(err) commit := ds.Head() ds.Database().Close() - s.Contains(s.Run(main, []string{str}), commit.Hash().String()) + res, _ := s.Run(main, []string{"log", str}) + s.Contains(res, commit.Hash().String()) } -func (s *nomsShowTestSuite) TestNomsLog() { +func (s *nomsLogTestSuite) TestNomsLog() { datasetName := "dsTest" str := spec.CreateValueSpecString("ldb", s.LdbDir, datasetName) ds, err := spec.GetDataset(str) s.NoError(err) ds.Database().Close() - s.Panics(func() { s.Run(main, []string{str}) }) + s.Panics(func() { s.Run(main, []string{"log", str}) }) testCommitInResults(s, str, 1) testCommitInResults(s, str, 2) @@ -78,7 +79,7 @@ func mergeDatasets(ds1, ds2 dataset.Dataset, v string) (dataset.Dataset, error) return ds1.CommitWithParents(types.String(v), types.NewSet(ds1.HeadRef(), ds2.HeadRef())) } -func (s *nomsShowTestSuite) TestNArg() { +func (s *nomsLogTestSuite) TestNArg() { str := spec.CreateDatabaseSpecString("ldb", s.LdbDir) dsName := "nArgTest" db, err := spec.GetDatabase(str) @@ -98,21 +99,23 @@ func (s *nomsShowTestSuite) TestNArg() { db.Close() dsSpec := spec.CreateValueSpecString("ldb", s.LdbDir, dsName) - s.NotContains(s.Run(main, []string{"-n=1", dsSpec}), h1.String()) - res := s.Run(main, []string{"-n=0", dsSpec}) + res, _ := s.Run(main, []string{"log", "-n=1", dsSpec}) + s.NotContains(res, h1.String()) + res, _ = s.Run(main, []string{"log", "-n=0", dsSpec}) s.Contains(res, h3.String()) s.Contains(res, h2.String()) s.Contains(res, h1.String()) vSpec := spec.CreateValueSpecString("ldb", s.LdbDir, "#"+h3.String()) - s.NotContains(s.Run(main, []string{"-n=1", vSpec}), h1.String()) - res = s.Run(main, []string{"-n=0", vSpec}) + res, _ = s.Run(main, []string{"log", "-n=1", vSpec}) + s.NotContains(res, h1.String()) + res, _ = s.Run(main, []string{"log", "-n=0", vSpec}) s.Contains(res, h3.String()) s.Contains(res, h2.String()) s.Contains(res, h1.String()) } -func (s *nomsShowTestSuite) TestNomsGraph1() { +func (s *nomsLogTestSuite) TestNomsGraph1() { str := spec.CreateDatabaseSpecString("ldb", s.LdbDir) db, err := spec.GetDatabase(str) s.NoError(err) @@ -159,11 +162,13 @@ func (s *nomsShowTestSuite) TestNomsGraph1() { s.NoError(err) b1.Database().Close() - s.Equal(graphRes1, s.Run(main, []string{"-graph", "-show-value=true", spec.CreateValueSpecString("ldb", s.LdbDir, "b1")})) - s.Equal(diffRes1, s.Run(main, []string{"-graph", "-show-value=false", spec.CreateValueSpecString("ldb", s.LdbDir, "b1")})) + res, _ := s.Run(main, []string{"log", "-graph", "-show-value=true", spec.CreateValueSpecString("ldb", s.LdbDir, "b1")}) + s.Equal(graphRes1, res) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=false", spec.CreateValueSpecString("ldb", s.LdbDir, "b1")}) + s.Equal(diffRes1, res) } -func (s *nomsShowTestSuite) TestNomsGraph2() { +func (s *nomsLogTestSuite) TestNomsGraph2() { str := spec.CreateDatabaseSpecString("ldb", s.LdbDir) db, err := spec.GetDatabase(str) s.NoError(err) @@ -188,11 +193,14 @@ func (s *nomsShowTestSuite) TestNomsGraph2() { s.NoError(err) db.Close() - s.Equal(graphRes2, s.Run(main, []string{"-graph", "-show-value=true", spec.CreateValueSpecString("ldb", s.LdbDir, "ba")})) - s.Equal(diffRes2, s.Run(main, []string{"-graph", "-show-value=false", spec.CreateValueSpecString("ldb", s.LdbDir, "ba")})) + + res, _ := s.Run(main, []string{"log", "-graph", "-show-value=true", spec.CreateValueSpecString("ldb", s.LdbDir, "ba")}) + s.Equal(graphRes2, res) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=false", spec.CreateValueSpecString("ldb", s.LdbDir, "ba")}) + s.Equal(diffRes2, res) } -func (s *nomsShowTestSuite) TestNomsGraph3() { +func (s *nomsLogTestSuite) TestNomsGraph3() { str := spec.CreateDatabaseSpecString("ldb", s.LdbDir) db, err := spec.GetDatabase(str) s.NoError(err) @@ -227,11 +235,13 @@ func (s *nomsShowTestSuite) TestNomsGraph3() { s.NoError(err) db.Close() - s.Equal(graphRes3, s.Run(main, []string{"-graph", "-show-value=true", spec.CreateValueSpecString("ldb", s.LdbDir, "w")})) - s.Equal(diffRes3, s.Run(main, []string{"-graph", "-show-value=false", spec.CreateValueSpecString("ldb", s.LdbDir, "w")})) + res, _ := s.Run(main, []string{"log", "-graph", "-show-value=true", spec.CreateValueSpecString("ldb", s.LdbDir, "w")}) + s.Equal(graphRes3, res) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=false", spec.CreateValueSpecString("ldb", s.LdbDir, "w")}) + s.Equal(diffRes3, res) } -func (s *nomsShowTestSuite) TestTruncation() { +func (s *nomsLogTestSuite) TestTruncation() { toNomsList := func(l []string) types.List { nv := []types.Value{} for _, v := range l { @@ -255,14 +265,20 @@ func (s *nomsShowTestSuite) TestTruncation() { db.Close() dsSpec := spec.CreateValueSpecString("ldb", s.LdbDir, "truncate") - s.Equal(truncRes1, s.Run(main, []string{"-graph", "-show-value=true", dsSpec})) - s.Equal(diffTrunc1, s.Run(main, []string{"-graph", "-show-value=false", dsSpec})) + res, _ := s.Run(main, []string{"log", "-graph", "-show-value=true", dsSpec}) + s.Equal(truncRes1, res) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=false", dsSpec}) + s.Equal(diffTrunc1, res) - s.Equal(truncRes2, s.Run(main, []string{"-graph", "-show-value=true", "-max-lines=-1", dsSpec})) - s.Equal(diffTrunc2, s.Run(main, []string{"-graph", "-show-value=false", "-max-lines=-1", dsSpec})) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=true", "-max-lines=-1", dsSpec}) + s.Equal(truncRes2, res) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=false", "-max-lines=-1", dsSpec}) + s.Equal(diffTrunc2, res) - s.Equal(truncRes3, s.Run(main, []string{"-graph", "-show-value=true", "-max-lines=0", dsSpec})) - s.Equal(diffTrunc3, s.Run(main, []string{"-graph", "-show-value=false", "-max-lines=0", dsSpec})) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=true", "-max-lines=0", dsSpec}) + s.Equal(truncRes3, res) + res, _ = s.Run(main, []string{"log", "-graph", "-show-value=false", "-max-lines=0", dsSpec}) + s.Equal(diffTrunc3, res) } func TestBranchlistSplice(t *testing.T) { diff --git a/cmd/noms/noms_serve.go b/cmd/noms/noms_serve.go new file mode 100644 index 0000000000..2b700727dc --- /dev/null +++ b/cmd/noms/noms_serve.go @@ -0,0 +1,58 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "flag" + "os" + "os/signal" + "syscall" + + "github.com/attic-labs/noms/go/d" + "github.com/attic-labs/noms/go/datas" + "github.com/attic-labs/noms/go/spec" + "github.com/attic-labs/noms/go/util/profile" +) + +var ( + port int +) + +var nomsServe = &nomsCommand{ + Run: runServe, + UsageLine: "serve [options] ", + Short: "Serves a Noms database over HTTP", + Long: "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the database argument.", + Flags: setupServeFlags, + Nargs: 1, +} + +func setupServeFlags() *flag.FlagSet { + serveFlagSet := flag.NewFlagSet("serve", flag.ExitOnError) + serveFlagSet.IntVar(&port, "port", 8000, "port to listen on for HTTP requests") + spec.RegisterDatabaseFlags(serveFlagSet) + return serveFlagSet +} + +func runServe(args []string) int { + cs, err := spec.GetChunkStore(args[0]) + d.CheckError(err) + server := datas.NewRemoteDatabaseServer(cs, port) + + // Shutdown server gracefully so that profile may be written + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + signal.Notify(c, syscall.SIGTERM) + go func() { + <-c + server.Stop() + }() + + d.Try(func() { + defer profile.MaybeStartProfile().Stop() + server.Run() + }) + return 0 +} diff --git a/cmd/noms/noms_show.go b/cmd/noms/noms_show.go new file mode 100644 index 0000000000..3106d47e31 --- /dev/null +++ b/cmd/noms/noms_show.go @@ -0,0 +1,56 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "bufio" + "flag" + "fmt" + "os" + + "github.com/attic-labs/noms/go/d" + "github.com/attic-labs/noms/go/spec" + "github.com/attic-labs/noms/go/types" + "github.com/attic-labs/noms/go/util/outputpager" +) + +var nomsShow = &nomsCommand{ + Run: runShow, + UsageLine: "show ", + Short: "Shows a serialization of a Noms object", + Long: "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object argument.", + Flags: setupShowFlags, + Nargs: 1, +} + +func setupShowFlags() *flag.FlagSet { + showFlagSet := flag.NewFlagSet("show", flag.ExitOnError) + outputpager.RegisterOutputpagerFlags(showFlagSet) + return showFlagSet +} + +func runShow(args []string) int { + database, value, err := spec.GetPath(args[0]) + d.CheckErrorNoUsage(err) + + if value == nil { + fmt.Fprintf(os.Stderr, "Object not found: %s\n", args[0]) + return 0 + } + + waitChan := outputpager.PageOutput(!outputpager.NoPager) + + w := bufio.NewWriter(os.Stdout) + types.WriteEncodedValueWithTags(w, value) + fmt.Fprintf(w, "\n") + w.Flush() + database.Close() + + if waitChan != nil { + os.Stdout.Close() + <-waitChan + } + return 0 +} diff --git a/cmd/noms-show/noms_show_test.go b/cmd/noms/noms_show_test.go similarity index 85% rename from cmd/noms-show/noms_show_test.go rename to cmd/noms/noms_show_test.go index f8b3e53687..759f2bd7b8 100644 --- a/cmd/noms-show/noms_show_test.go +++ b/cmd/noms/noms_show_test.go @@ -49,18 +49,23 @@ func (s *nomsShowTestSuite) TestNomsShow() { s1 := types.String("test string") r := writeTestData(str, s1) - s.Equal(res1, s.Run(main, []string{str})) + res, _ := s.Run(main, []string{"show", str}) + s.Equal(res1, res) str1 := spec.CreateValueSpecString("ldb", s.LdbDir, "#"+r.TargetHash().String()) - s.Equal(res2, s.Run(main, []string{str1})) + res, _ = s.Run(main, []string{"show", str1}) + s.Equal(res2, res) list := types.NewList(types.String("elem1"), types.Number(2), types.String("elem3")) r = writeTestData(str, list) - s.Equal(res3, s.Run(main, []string{str})) + res, _ = s.Run(main, []string{"show", str}) + s.Equal(res3, res) str1 = spec.CreateValueSpecString("ldb", s.LdbDir, "#"+r.TargetHash().String()) - s.Equal(res4, s.Run(main, []string{str1})) + res, _ = s.Run(main, []string{"show", str1}) + s.Equal(res4, res) _ = writeTestData(str, s1) - s.Equal(res5, s.Run(main, []string{str})) + res, _ = s.Run(main, []string{"show", str}) + s.Equal(res5, res) } diff --git a/cmd/noms/noms_sync.go b/cmd/noms/noms_sync.go new file mode 100644 index 0000000000..c72e4197f5 --- /dev/null +++ b/cmd/noms/noms_sync.go @@ -0,0 +1,60 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "flag" + "log" + + "github.com/attic-labs/noms/go/d" + "github.com/attic-labs/noms/go/spec" + "github.com/attic-labs/noms/go/types" + "github.com/attic-labs/noms/go/util/profile" +) + +var ( + p int +) + +var nomsSync = &nomsCommand{ + Run: runSync, + UsageLine: "sync [options] ", + Short: "Moves datasets between or within databases", + Long: "See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object and dataset arguments.", + Flags: setupSyncFlags, + Nargs: 2, +} + +func setupSyncFlags() *flag.FlagSet { + syncFlagSet := flag.NewFlagSet("sync", flag.ExitOnError) + syncFlagSet.IntVar(&p, "p", 512, "parallelism") + spec.RegisterDatabaseFlags(syncFlagSet) + profile.RegisterProfileFlags(syncFlagSet) + return syncFlagSet +} + +func runSync(args []string) int { + + sourceStore, sourceObj, err := spec.GetPath(args[0]) + d.CheckError(err) + defer sourceStore.Close() + + sinkDataset, err := spec.GetDataset(args[1]) + d.CheckError(err) + defer sinkDataset.Database().Close() + + err = d.Try(func() { + defer profile.MaybeStartProfile().Stop() + + var err error + sinkDataset, err = sinkDataset.Pull(sourceStore, types.NewRef(sourceObj), p) + d.PanicIfError(err) + }) + + if err != nil { + log.Fatal(err) + } + return 0 +} diff --git a/cmd/noms-sync/noms_sync_test.go b/cmd/noms/noms_sync_test.go similarity index 86% rename from cmd/noms-sync/noms_sync_test.go rename to cmd/noms/noms_sync_test.go index 6e7cfc85b2..f45eeff0cd 100644 --- a/cmd/noms-sync/noms_sync_test.go +++ b/cmd/noms/noms_sync_test.go @@ -18,14 +18,14 @@ import ( ) func TestSync(t *testing.T) { - suite.Run(t, &testSuite{}) + suite.Run(t, &nomsSyncTestSuite{}) } -type testSuite struct { +type nomsSyncTestSuite struct { clienttest.ClientTestSuite } -func (s *testSuite) TestSync() { +func (s *nomsSyncTestSuite) TestSync() { source1 := dataset.NewDataset(datas.NewDatabase(chunks.NewLevelDBStore(s.LdbDir, "", 1, false)), "foo") source1, err := source1.Commit(types.Number(42)) s.NoError(err) @@ -37,7 +37,7 @@ func (s *testSuite) TestSync() { sourceSpec := spec.CreateValueSpecString("ldb", s.LdbDir, "#"+source1HeadRef.String()) ldb2dir := path.Join(s.TempDir, "ldb2") sinkDatasetSpec := spec.CreateValueSpecString("ldb", ldb2dir, "bar") - out := s.Run(main, []string{sourceSpec, sinkDatasetSpec}) + out, _ := s.Run(main, []string{"sync", sourceSpec, sinkDatasetSpec}) s.Equal("", out) dest := dataset.NewDataset(datas.NewDatabase(chunks.NewLevelDBStore(ldb2dir, "", 1, false)), "bar") @@ -45,7 +45,7 @@ func (s *testSuite) TestSync() { dest.Database().Close() sourceDataset := spec.CreateValueSpecString("ldb", s.LdbDir, "foo") - out = s.Run(main, []string{sourceDataset, sinkDatasetSpec}) + out, _ = s.Run(main, []string{"sync", sourceDataset, sinkDatasetSpec}) s.Equal("", out) dest = dataset.NewDataset(datas.NewDatabase(chunks.NewLevelDBStore(ldb2dir, "", 1, false)), "bar") diff --git a/cmd/noms/noms_version.go b/cmd/noms/noms_version.go new file mode 100644 index 0000000000..26c4aa8860 --- /dev/null +++ b/cmd/noms/noms_version.go @@ -0,0 +1,32 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/attic-labs/noms/go/constants" +) + +var nomsVersion = &nomsCommand{ + Run: runVersion, + UsageLine: "version ", + Short: "Display noms version", + Long: "version prints the Noms data version and build identifier", + Flags: setupVersionFlags, + Nargs: 0, +} + +func setupVersionFlags() *flag.FlagSet { + return flag.NewFlagSet("version", flag.ExitOnError) +} + +func runVersion(args []string) int { + fmt.Fprintf(os.Stdout, "version: %v\n", constants.NomsVersion) + fmt.Fprintf(os.Stdout, "built from %v\n", constants.NomsGitSHA) + return 0 +} diff --git a/cmd/noms/noms_version_test.go b/cmd/noms/noms_version_test.go new file mode 100644 index 0000000000..c33c0e63d0 --- /dev/null +++ b/cmd/noms/noms_version_test.go @@ -0,0 +1,28 @@ +// Copyright 2016 Attic Labs, Inc. All rights reserved. +// Licensed under the Apache License, version 2.0: +// http://www.apache.org/licenses/LICENSE-2.0 + +package main + +import ( + "fmt" + "testing" + + "github.com/attic-labs/noms/go/constants" + "github.com/attic-labs/noms/go/util/clienttest" + "github.com/attic-labs/testify/suite" +) + +func TestVersion(t *testing.T) { + suite.Run(t, &nomsVersionTestSuite{}) +} + +type nomsVersionTestSuite struct { + clienttest.ClientTestSuite +} + +func (s *nomsVersionTestSuite) TestVersion() { + val, _ := s.Run(main, []string{"version"}) + expectedVal := fmt.Sprintf("version: %v\nbuilt from %v\n", constants.NomsVersion, constants.NomsGitSHA) + s.Equal(val, expectedVal) +} diff --git a/go/chunks/leveldb_store.go b/go/chunks/leveldb_store.go index 9f0ee82f27..0895add268 100644 --- a/go/chunks/leveldb_store.go +++ b/go/chunks/leveldb_store.go @@ -36,11 +36,11 @@ var ( flagsRegistered = false ) -func RegisterLevelDBFlags() { +func RegisterLevelDBFlags(flags *flag.FlagSet) { if !flagsRegistered { flagsRegistered = true - flag.IntVar(&ldbFlags.maxFileHandles, "ldb-max-file-handles", 24, "max number of open file handles") - flag.BoolVar(&ldbFlags.dumpStats, "ldb-dump-stats", false, "print get/has/put counts on close") + flags.IntVar(&ldbFlags.maxFileHandles, "ldb-max-file-handles", 24, "max number of open file handles") + flags.BoolVar(&ldbFlags.dumpStats, "ldb-dump-stats", false, "print get/has/put counts on close") } } diff --git a/go/constants/version.go b/go/constants/version.go index ae8ef524ec..dc0fb3d8c4 100644 --- a/go/constants/version.go +++ b/go/constants/version.go @@ -6,3 +6,5 @@ package constants // TODO: generate this from some central thing with go generate, so that JS and Go can be easily kept in sync const NomsVersion = "2" + +var NomsGitSHA = "Developer Mode" diff --git a/go/perf/codec-perf-rig/main.go b/go/perf/codec-perf-rig/main.go index 307a2b648c..8d3f024373 100644 --- a/go/perf/codec-perf-rig/main.go +++ b/go/perf/codec-perf-rig/main.go @@ -32,6 +32,7 @@ const boolSize = uint64(1) const structSize = uint64(64) func main() { + profile.RegisterProfileFlags(flag.CommandLine) flag.Parse() buildCount := *count diff --git a/go/spec/dataspec.go b/go/spec/dataspec.go index 1ea45206dd..470808d7cd 100644 --- a/go/spec/dataspec.go +++ b/go/spec/dataspec.go @@ -5,6 +5,7 @@ package spec import ( + "flag" "fmt" "net/url" "regexp" @@ -220,8 +221,8 @@ func (spec pathSpec) Value() (db datas.Database, val types.Value, err error) { return } -func RegisterDatabaseFlags() { - chunks.RegisterLevelDBFlags() +func RegisterDatabaseFlags(flags *flag.FlagSet) { + chunks.RegisterLevelDBFlags(flags) } // Utility functions to create specs diff --git a/go/util/clienttest/client_test_suite.go b/go/util/clienttest/client_test_suite.go index d78730a422..dcb74af5bf 100644 --- a/go/util/clienttest/client_test_suite.go +++ b/go/util/clienttest/client_test_suite.go @@ -19,31 +19,37 @@ type ClientTestSuite struct { TempDir string LdbDir string out *os.File + err *os.File } func (suite *ClientTestSuite) SetupSuite() { dir, err := ioutil.TempDir(os.TempDir(), "nomstest") d.Chk.NoError(err) - out, err := ioutil.TempFile(dir, "out") + stdOutput, err := ioutil.TempFile(dir, "out") + d.Chk.NoError(err) + errOutput, err := ioutil.TempFile(dir, "err") d.Chk.NoError(err) suite.TempDir = dir suite.LdbDir = path.Join(dir, "ldb") - suite.out = out + suite.out = stdOutput + suite.err = errOutput } func (suite *ClientTestSuite) TearDownSuite() { suite.out.Close() + suite.err.Close() defer d.Chk.NoError(os.RemoveAll(suite.TempDir)) } -func (suite *ClientTestSuite) Run(m func(), args []string) string { +func (suite *ClientTestSuite) Run(m func(), args []string) (stdout string, stderr string) { origArgs := os.Args origOut := os.Stdout origErr := os.Stderr os.Args = append([]string{"cmd"}, args...) os.Stdout = suite.out + os.Stderr = suite.err defer func() { os.Args = origArgs @@ -56,7 +62,7 @@ func (suite *ClientTestSuite) Run(m func(), args []string) string { _, err := suite.out.Seek(0, 0) d.Chk.NoError(err) - b, err := ioutil.ReadAll(os.Stdout) + capturedOut, err := ioutil.ReadAll(os.Stdout) d.Chk.NoError(err) _, err = suite.out.Seek(0, 0) @@ -64,5 +70,15 @@ func (suite *ClientTestSuite) Run(m func(), args []string) string { err = suite.out.Truncate(0) d.Chk.NoError(err) - return string(b) + _, err = suite.err.Seek(0, 0) + d.Chk.NoError(err) + capturedErr, err := ioutil.ReadAll(os.Stderr) + d.Chk.NoError(err) + + _, err = suite.err.Seek(0, 0) + d.Chk.NoError(err) + err = suite.err.Truncate(0) + d.Chk.NoError(err) + + return string(capturedOut), string(capturedErr) } diff --git a/go/util/outputpager/page_output.go b/go/util/outputpager/page_output.go index e2ee53153b..38eb964096 100644 --- a/go/util/outputpager/page_output.go +++ b/go/util/outputpager/page_output.go @@ -14,9 +14,17 @@ import ( ) var ( - NoPager = flag.Bool("no-pager", false, "suppress paging functionality") + NoPager bool + flagsRegistered = false ) +func RegisterOutputpagerFlags(flags *flag.FlagSet) { + if !flagsRegistered { + flagsRegistered = true + flags.BoolVar(&NoPager, "no-pager", false, "suppress paging functionality") + } +} + func PageOutput(usePager bool) <-chan struct{} { if !usePager || !IsStdoutTty() { return nil diff --git a/go/util/profile/profile.go b/go/util/profile/profile.go index cf4e7dacee..b20e9a1979 100644 --- a/go/util/profile/profile.go +++ b/go/util/profile/profile.go @@ -15,30 +15,40 @@ import ( ) var ( - cpuProfile = flag.String("cpuprofile", "", "write cpu profile to file") - memProfile = flag.String("memprofile", "", "write memory profile to this file") - blockProfile = flag.String("blockprofile", "", "write block profile to this file") + cpuProfile string + memProfile string + blockProfile string + flagsRegistered = false ) +func RegisterProfileFlags(flags *flag.FlagSet) { + if !flagsRegistered { + flagsRegistered = true + flags.StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file") + flags.StringVar(&memProfile, "memprofile", "", "write memory profile to this file") + flags.StringVar(&blockProfile, "blockprofile", "", "write block profile to this file") + } +} + // MaybeStartProfile checks the -blockProfile, -cpuProfile, and -memProfile flag and, for each that is set, attempts to start gathering profiling data into the appropriate files. It returns an object with one method, Stop(), that must be called in order to flush profile data to disk before the process terminates. func MaybeStartProfile() interface { Stop() } { p := &prof{} - if *blockProfile != "" { - f, err := os.Create(*blockProfile) + if blockProfile != "" { + f, err := os.Create(blockProfile) d.PanicIfError(err) runtime.SetBlockProfileRate(1) p.bp = f } - if *cpuProfile != "" { - f, err := os.Create(*cpuProfile) + if cpuProfile != "" { + f, err := os.Create(cpuProfile) d.PanicIfError(err) pprof.StartCPUProfile(f) p.cpu = f } - if *memProfile != "" { - f, err := os.Create(*memProfile) + if memProfile != "" { + f, err := os.Create(memProfile) d.PanicIfError(err) p.mem = f } diff --git a/samples/go/counter/counter.go b/samples/go/counter/counter.go index f14ea06416..24ce477b25 100644 --- a/samples/go/counter/counter.go +++ b/samples/go/counter/counter.go @@ -18,6 +18,9 @@ func main() { fmt.Fprintf(os.Stderr, "usage: %s [options] \n", os.Args[0]) flag.PrintDefaults() } + + spec.RegisterDatabaseFlags(flag.CommandLine) + flag.Parse() if flag.NArg() != 1 { diff --git a/samples/go/counter/counter_test.go b/samples/go/counter/counter_test.go index 5f610a1aa5..57a230e8c5 100644 --- a/samples/go/counter/counter_test.go +++ b/samples/go/counter/counter_test.go @@ -23,7 +23,13 @@ type counterTestSuite struct { func (s *counterTestSuite) TestCounter() { spec := spec.CreateValueSpecString("ldb", s.LdbDir, "counter") args := []string{spec} - s.Equal("1\n", s.Run(main, args)) - s.Equal("2\n", s.Run(main, args)) - s.Equal("3\n", s.Run(main, args)) + stdout, stderr := s.Run(main, args) + s.Equal("1\n", stdout) + s.Equal("", stderr) + stdout, stderr = s.Run(main, args) + s.Equal("2\n", stdout) + s.Equal("", stderr) + stdout, stderr = s.Run(main, args) + s.Equal("3\n", stdout) + s.Equal("", stderr) } diff --git a/samples/go/csv/csv-export/exporter.go b/samples/go/csv/csv-export/exporter.go index 507c493f5c..15ab03d6d9 100644 --- a/samples/go/csv/csv-export/exporter.go +++ b/samples/go/csv/csv-export/exporter.go @@ -23,7 +23,8 @@ var ( ) func main() { - spec.RegisterDatabaseFlags() + spec.RegisterDatabaseFlags(flag.CommandLine) + profile.RegisterProfileFlags(flag.CommandLine) flag.Usage = func() { fmt.Fprintln(os.Stderr, "Usage: csv-export [options] dataset > filename") diff --git a/samples/go/csv/csv-export/exporter_test.go b/samples/go/csv/csv-export/exporter_test.go index b9cbcbd2de..b5435f61d8 100644 --- a/samples/go/csv/csv-export/exporter_test.go +++ b/samples/go/csv/csv-export/exporter_test.go @@ -67,10 +67,11 @@ func (s *testSuite) TestCSVExporter() { // Run exporter dataspec := spec.CreateValueSpecString("ldb", s.LdbDir, setName) - out := s.Run(main, []string{dataspec}) + stdout, stderr := s.Run(main, []string{dataspec}) + s.Equal("", stderr) // Verify output - csvReader := csv.NewReader(strings.NewReader(out)) + csvReader := csv.NewReader(strings.NewReader(stdout)) row, err := csvReader.Read() d.Chk.NoError(err) diff --git a/samples/go/csv/csv-import/importer.go b/samples/go/csv/csv-import/importer.go index e9bf28af96..b98aaeef8d 100644 --- a/samples/go/csv/csv-import/importer.go +++ b/samples/go/csv/csv-import/importer.go @@ -43,7 +43,8 @@ func main() { destTypePattern = regexp.MustCompile("^(list|map):(\\d+)$") ) - spec.RegisterDatabaseFlags() + spec.RegisterDatabaseFlags(flag.CommandLine) + profile.RegisterProfileFlags(flag.CommandLine) flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: csv-import [options] \n\n") diff --git a/samples/go/csv/csv-import/importer_test.go b/samples/go/csv/csv-import/importer_test.go index 2ffd53fdb2..bdb7b14598 100644 --- a/samples/go/csv/csv-import/importer_test.go +++ b/samples/go/csv/csv-import/importer_test.go @@ -45,8 +45,9 @@ func (s *testSuite) TestCSVImporter() { setName := "csv" dataspec := spec.CreateValueSpecString("ldb", s.LdbDir, setName) - out := s.Run(main, []string{"-no-progress", "-column-types", "String,Number", dataspec, input.Name()}) - s.Equal("", out) + stdout, stderr := s.Run(main, []string{"-no-progress", "-column-types", "String,Number", dataspec, input.Name()}) + s.Equal("", stdout) + s.Equal("", stderr) cs := chunks.NewLevelDBStore(s.LdbDir, "", 1, false) ds := dataset.NewDataset(datas.NewDatabase(cs), setName) @@ -83,8 +84,9 @@ func (s *testSuite) TestCSVImporterToMap() { setName := "csv" dataspec := spec.CreateValueSpecString("ldb", s.LdbDir, setName) - out := s.Run(main, []string{"-no-progress", "-column-types", "String,Number,Number", "-dest-type", "map:1", dataspec, input.Name()}) - s.Equal("", out) + stdout, stderr := s.Run(main, []string{"-no-progress", "-column-types", "String,Number,Number", "-dest-type", "map:1", dataspec, input.Name()}) + s.Equal("", stdout) + s.Equal("", stderr) cs := chunks.NewLevelDBStore(s.LdbDir, "", 1, false) ds := dataset.NewDataset(datas.NewDatabase(cs), setName) @@ -113,8 +115,9 @@ func (s *testSuite) TestCSVImporterWithPipe() { setName := "csv" dataspec := spec.CreateValueSpecString("ldb", s.LdbDir, setName) - out := s.Run(main, []string{"-no-progress", "-column-types", "String,Number", "-delimiter", "|", dataspec, input.Name()}) - s.Equal("", out) + stdout, stderr := s.Run(main, []string{"-no-progress", "-column-types", "String,Number", "-delimiter", "|", dataspec, input.Name()}) + s.Equal("", stdout) + s.Equal("", stderr) cs := chunks.NewLevelDBStore(s.LdbDir, "", 1, false) ds := dataset.NewDataset(datas.NewDatabase(cs), setName) @@ -140,8 +143,9 @@ func (s *testSuite) TestCSVImporterWithExternalHeader() { setName := "csv" dataspec := spec.CreateValueSpecString("ldb", s.LdbDir, setName) - out := s.Run(main, []string{"-no-progress", "-column-types", "String,Number", "-header", "x,y", dataspec, input.Name()}) - s.Equal("", out) + stdout, stderr := s.Run(main, []string{"-no-progress", "-column-types", "String,Number", "-header", "x,y", dataspec, input.Name()}) + s.Equal("", stdout) + s.Equal("", stderr) cs := chunks.NewLevelDBStore(s.LdbDir, "", 1, false) ds := dataset.NewDataset(datas.NewDatabase(cs), setName) diff --git a/samples/go/demo-server/main.go b/samples/go/demo-server/main.go index c40190410c..aa5c0db53f 100644 --- a/samples/go/demo-server/main.go +++ b/samples/go/demo-server/main.go @@ -23,7 +23,7 @@ func usage() { } func main() { - chunks.RegisterLevelDBFlags() + chunks.RegisterLevelDBFlags(flag.CommandLine) dynFlags := chunks.DynamoFlags("") flag.Usage = usage diff --git a/samples/go/hr/main_test.go b/samples/go/hr/main_test.go index fd87319fcc..037bd7f6ec 100644 --- a/samples/go/hr/main_test.go +++ b/samples/go/hr/main_test.go @@ -26,19 +26,24 @@ type testSuite struct { func (s *testSuite) TestRoundTrip() { spec := fmt.Sprintf("ldb:%s::hr", s.LdbDir) - out := s.Run(main, []string{"-ds", spec, "list-persons"}) - s.Equal("No people found\n", out) + stdout, stderr := s.Run(main, []string{"-ds", spec, "list-persons"}) + s.Equal("No people found\n", stdout) + s.Equal("", stderr) - out = s.Run(main, []string{"-ds", spec, "add-person", "42", "Benjamin Kalman", "Programmer, Barista"}) - s.Equal("", out) + stdout, stderr = s.Run(main, []string{"-ds", spec, "add-person", "42", "Benjamin Kalman", "Programmer, Barista"}) + s.Equal("", stdout) + s.Equal("", stderr) - out = s.Run(main, []string{"-ds", spec, "add-person", "43", "Abigail Boodman", "Chief Architect"}) - s.Equal("", out) + stdout, stderr = s.Run(main, []string{"-ds", spec, "add-person", "43", "Abigail Boodman", "Chief Architect"}) + s.Equal("", stdout) + s.Equal("", stderr) - out = s.Run(main, []string{"-ds", spec, "list-persons"}) + stdout, stderr = s.Run(main, []string{"-ds", spec, "list-persons"}) s.Equal(`Benjamin Kalman (id: 42, title: Programmer, Barista) Abigail Boodman (id: 43, title: Chief Architect) -`, out) +`, stdout) + s.Equal("", stderr) + } func (s *testSuite) TestReadCanned() { @@ -49,13 +54,14 @@ func (s *testSuite) TestReadCanned() { // Have to copy the canned data elsewhere because just reading the database modifies it. _, err = exec.Command("cp", "-r", p, dst).Output() s.NoError(err) - out := s.Run(main, []string{"-ds", fmt.Sprintf("ldb:%s/test-data::hr", dst), "list-persons"}) + stdout, stderr := s.Run(main, []string{"-ds", fmt.Sprintf("ldb:%s/test-data::hr", dst), "list-persons"}) s.Equal(`Aaron Boodman (id: 7, title: Chief Evangelism Officer) Samuel Boodman (id: 13, title: VP, Culture) -`, out) +`, stdout) + s.Equal("", stderr) } func (s *testSuite) TestInvalidDatasetSpec() { // Should not crash - _ = s.Run(main, []string{"-ds", "invalid-dataset", "list-persons"}) + _, _ = s.Run(main, []string{"-ds", "invalid-dataset", "list-persons"}) } diff --git a/samples/go/json-import/json_importer.go b/samples/go/json-import/json_importer.go index 261fa6c576..a1134206e3 100644 --- a/samples/go/json-import/json_importer.go +++ b/samples/go/json-import/json_importer.go @@ -24,7 +24,7 @@ func main() { flag.PrintDefaults() } - spec.RegisterDatabaseFlags() + spec.RegisterDatabaseFlags(flag.CommandLine) flag.Parse() if len(flag.Args()) != 2 { diff --git a/samples/go/url-fetch/fetch.go b/samples/go/url-fetch/fetch.go index 763f45a6f3..5c67074e86 100644 --- a/samples/go/url-fetch/fetch.go +++ b/samples/go/url-fetch/fetch.go @@ -33,7 +33,7 @@ func main() { flag.PrintDefaults() } - spec.RegisterDatabaseFlags() + spec.RegisterDatabaseFlags(flag.CommandLine) flag.Parse() if flag.NArg() != 2 { diff --git a/samples/go/xml-import/xml_importer.go b/samples/go/xml-import/xml_importer.go index b5ead6deb6..4bbd154382 100644 --- a/samples/go/xml-import/xml_importer.go +++ b/samples/go/xml-import/xml_importer.go @@ -51,7 +51,8 @@ func (a refIndexList) Less(i, j int) bool { return a[i].index < a[j].index } func main() { err := d.Try(func() { - spec.RegisterDatabaseFlags() + spec.RegisterDatabaseFlags(flag.CommandLine) + profile.RegisterProfileFlags(flag.CommandLine) flag.Usage = customUsage flag.Parse()