noms as one command line application, with version and help (#1874)

This commit is contained in:
Mike Gray
2016-07-06 15:38:25 -04:00
committed by GitHub
parent e08853070b
commit a7f29a716d
50 changed files with 728 additions and 545 deletions

View File

@@ -1 +0,0 @@
noms-diff

View File

@@ -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 <object1> <object2>\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
}
}

View File

@@ -1 +0,0 @@
noms-ds

View File

@@ -1 +0,0 @@
noms-log

View File

@@ -1 +0,0 @@
noms-serve

View File

@@ -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 <database>\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()
})
}

View File

@@ -1 +0,0 @@
noms-show

View File

@@ -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 <object>\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
}
}

View File

@@ -1 +0,0 @@
noms-sync

View File

@@ -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] <source-object> <dest-dataset>\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)
}
}

View File

@@ -1,7 +1,3 @@
# noms-serve
noms-serve implements a noms database over HTTP.
## Example
```

View File

@@ -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))
}

View File

@@ -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 <command> -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()
}

67
cmd/noms/noms_command.go Normal file
View File

@@ -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 <this-command>' 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)
}

66
cmd/noms/noms_diff.go Normal file
View File

@@ -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 <object1> <object2>",
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
}

View File

@@ -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 [<database> | -d <dataset>]")
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 [<database> | -d <dataset>]",
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
}

View File

@@ -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)
}

85
cmd/noms/noms_help.go Normal file
View File

@@ -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'
}

View File

@@ -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] <commitObject>",
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 <commitObject>")
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 {

View File

@@ -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) {

58
cmd/noms/noms_serve.go Normal file
View File

@@ -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] <database>",
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
}

56
cmd/noms/noms_show.go Normal file
View File

@@ -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 <object>",
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
}

View File

@@ -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)
}

60
cmd/noms/noms_sync.go Normal file
View File

@@ -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] <source-object> <dest-dataset>",
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
}

View File

@@ -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")

32
cmd/noms/noms_version.go Normal file
View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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")
}
}

View File

@@ -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"

View File

@@ -32,6 +32,7 @@ const boolSize = uint64(1)
const structSize = uint64(64)
func main() {
profile.RegisterProfileFlags(flag.CommandLine)
flag.Parse()
buildCount := *count

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -18,6 +18,9 @@ func main() {
fmt.Fprintf(os.Stderr, "usage: %s [options] <dataset>\n", os.Args[0])
flag.PrintDefaults()
}
spec.RegisterDatabaseFlags(flag.CommandLine)
flag.Parse()
if flag.NArg() != 1 {

View File

@@ -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)
}

View File

@@ -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")

View File

@@ -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)

View File

@@ -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] <dataset> <csvfile>\n\n")

View File

@@ -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)

View File

@@ -23,7 +23,7 @@ func usage() {
}
func main() {
chunks.RegisterLevelDBFlags()
chunks.RegisterLevelDBFlags(flag.CommandLine)
dynFlags := chunks.DynamoFlags("")
flag.Usage = usage

View File

@@ -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"})
}

View File

@@ -24,7 +24,7 @@ func main() {
flag.PrintDefaults()
}
spec.RegisterDatabaseFlags()
spec.RegisterDatabaseFlags(flag.CommandLine)
flag.Parse()
if len(flag.Args()) != 2 {

View File

@@ -33,7 +33,7 @@ func main() {
flag.PrintDefaults()
}
spec.RegisterDatabaseFlags()
spec.RegisterDatabaseFlags(flag.CommandLine)
flag.Parse()
if flag.NArg() != 2 {

View File

@@ -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()