github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/cmd/noms/noms.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package main
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"log"
    28  	"math/rand"
    29  	"os"
    30  	"strings"
    31  	"time"
    32  
    33  	"github.com/attic-labs/kingpin"
    34  	flag "github.com/juju/gnuflag"
    35  
    36  	"github.com/dolthub/dolt/go/store/cmd/noms/util"
    37  	"github.com/dolthub/dolt/go/store/util/exit"
    38  	"github.com/dolthub/dolt/go/store/util/profile"
    39  	"github.com/dolthub/dolt/go/store/util/verbose"
    40  )
    41  
    42  var commands = []*util.Command{
    43  	nomsConfig,
    44  	nomsDs,
    45  	nomsRoot,
    46  	nomsShow,
    47  	nomsManifest,
    48  	nomsCat,
    49  	nomsWalk,
    50  }
    51  
    52  var kingpinCommands = []util.KingpinCommand{
    53  	nomsBlob,
    54  	nomsStats,
    55  }
    56  
    57  var actions = []string{
    58  	"interacting with",
    59  	"poking at",
    60  	"goofing with",
    61  	"dancing with",
    62  	"playing with",
    63  	"contemplation of",
    64  	"showing off",
    65  	"jiggerypokery of",
    66  	"singing to",
    67  	"nomming on",
    68  	"fighting with",
    69  }
    70  
    71  func usageString() string {
    72  	i := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(actions))
    73  	return fmt.Sprintf(`Noms is a tool for %s Noms data.`, actions[i])
    74  }
    75  
    76  func main() {
    77  	// allow short (-h) help
    78  	kingpin.EnableFileExpansion = false
    79  	kingpin.CommandLine.HelpFlag.Short('h')
    80  	noms := kingpin.New("noms", usageString())
    81  
    82  	// global flags
    83  	cpuProfileVal := noms.Flag("cpuprofile", "write cpu profile to file").String()
    84  	memProfileVal := noms.Flag("memprofile", "write memory profile to file").String()
    85  	blockProfileVal := noms.Flag("blockprofile", "write block profile to file").String()
    86  	verboseVal := noms.Flag("verbose", "show more").Short('v').Bool()
    87  
    88  	// set up docs for non-kingpin commands
    89  	addNomsDocs(noms)
    90  
    91  	handlers := map[string]util.KingpinHandler{}
    92  
    93  	// install kingpin handlers
    94  	for _, cmdFunction := range kingpinCommands {
    95  		command, handler := cmdFunction(context.Background(), noms)
    96  		handlers[command.FullCommand()] = handler
    97  	}
    98  
    99  	input := kingpin.MustParse(noms.Parse(os.Args[1:]))
   100  
   101  	// apply global flags
   102  	profile.ApplyProfileFlags(cpuProfileVal, memProfileVal, blockProfileVal)
   103  	verbose.SetVerbose(*verboseVal)
   104  
   105  	if handler := handlers[strings.Split(input, " ")[0]]; handler != nil {
   106  		handler(input)
   107  	}
   108  
   109  	// fall back to previous (non-kingpin) noms commands
   110  
   111  	flag.Parse(false)
   112  
   113  	args := flag.Args()
   114  
   115  	// Don't prefix log messages with timestamp when running interactively
   116  	log.SetFlags(0)
   117  
   118  	for _, cmd := range commands {
   119  		if cmd.Name() == args[0] {
   120  			flags := cmd.Flags()
   121  			flags.Usage = cmd.Usage
   122  
   123  			flags.Parse(true, args[1:])
   124  			args = flags.Args()
   125  			if cmd.Nargs != 0 && len(args) < cmd.Nargs {
   126  				cmd.Usage()
   127  			}
   128  			exitCode := cmd.Run(context.Background(), args)
   129  			if exitCode != 0 {
   130  				exit.Exit(exitCode)
   131  			}
   132  			return
   133  		}
   134  	}
   135  }
   136  
   137  // addDatabaseArg adds a "database" arg to the passed command
   138  func addDatabaseArg(cmd *kingpin.CmdClause) (arg *string) {
   139  	return cmd.Arg("database", "a noms database path").Required().String() // TODO: custom parser for noms db URL?
   140  }
   141  
   142  // addNomsDocs - adds documentation (docs only, not commands) for existing (pre-kingpin) commands.
   143  func addNomsDocs(noms *kingpin.Application) {
   144  	// commmit
   145  	commit := noms.Command("commit", `Commits a specified value as head of the dataset
   146  If absolute-path is not provided, then it is read from stdin. See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the dataset and absolute-path arguments.
   147  `)
   148  	commit.Flag("allow-dupe", "creates a new commit, even if it would be identical (modulo metadata and parents) to the existing HEAD.").Default("0").Int()
   149  	commit.Flag("date", "alias for -meta 'date=<date>'. '<date>' must be iso8601-formatted. If '<date>' is empty, it defaults to the current date.").String()
   150  	commit.Flag("message", "alias for -meta 'message=<message>'").String()
   151  	commit.Flag("meta", "'<key>=<value>' - creates a metadata field called 'key' set to 'value'. Value should be human-readable encoded.").String()
   152  	commit.Flag("meta-p", "'<key>=<path>' - creates a metadata field called 'key' set to the value at <path>").String()
   153  	commit.Arg("absolute-path", "the path to read data from").String()
   154  	// TODO: this should be required, but kingpin does not allow required args after non-required ones. Perhaps a custom type would fix that?
   155  	commit.Arg("database", "a noms database path").String()
   156  
   157  	// config
   158  	noms.Command("config", "Prints the active configuration if a .nomsconfig file is present")
   159  
   160  	// diff
   161  	diff := noms.Command("diff", `Shows the difference between two objects
   162  See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object arguments.
   163  `)
   164  	diff.Flag("stat", "Writes a summary of the changes instead").Short('s').Bool()
   165  	diff.Arg("object1", "").Required().String()
   166  	diff.Arg("object2", "").Required().String()
   167  
   168  	// ds
   169  	ds := noms.Command("ds", `Noms dataset management
   170  See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the database argument.
   171  `)
   172  	ds.Flag("delete", "dataset to delete").Short('d').String()
   173  	ds.Arg("database", "a noms database path").String()
   174  
   175  	// log
   176  	log := noms.Command("log", `Displays the history of a path
   177  See Spelling Values at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the <path-spec> parameter.
   178  `)
   179  	log.Flag("color", "value of 1 forces color on, 0 forces color off").Default("-1").Int()
   180  	log.Flag("max-lines", "max number of lines to show per commit (-1 for all lines)").Default("9").Int()
   181  	log.Flag("max-commits", "max number of commits to display (0 for all commits)").Short('n').Default("0").Int()
   182  	log.Flag("oneline", "show a summary of each commit on a single line").Bool()
   183  	log.Flag("graph", "show ascii-based commit hierarchy on left side of output").Bool()
   184  	log.Flag("show-value", "show commit value rather than diff information").Bool()
   185  	log.Flag("tz", "display formatted date comments in specified timezone, must be: local or utc").Enum("local", "utc")
   186  	log.Arg("path-spec", "").Required().String()
   187  
   188  	// merge
   189  	merge := noms.Command("merge", `Merges and commits the head values of two named datasets
   190  See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the database argument.
   191  You must provide a working database and the names of two Datasets you want to merge. The values at the heads of these Datasets will be merged, put into a new Commit object, and set as the Head of the third provided Dataset name.
   192  `)
   193  	merge.Flag("policy", "conflict resolution policy for merging. Defaults to 'n', which means no resolution strategy will be applied. Supported values are 'l' (left), 'r' (right) and 'p' (prompt). 'prompt' will bring up a simple command-line prompt allowing you to resolve conflicts by choosing between 'l' or 'r' on a case-by-case basis.").Default("n").Enum("n", "r", "l", "p")
   194  	addDatabaseArg(merge)
   195  	merge.Arg("left-dataset-name", "a dataset").Required().String()
   196  	merge.Arg("right-dataset-name", "a dataset").Required().String()
   197  	merge.Arg("output-dataset-name", "a dataset").Required().String()
   198  
   199  	// root
   200  	root := noms.Command("root", `Get or set the current root hash of the entire database
   201  See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the database argument.
   202  `)
   203  	root.Flag("update", "Replaces the entire database with the one with the given hash").String()
   204  	addDatabaseArg(root)
   205  
   206  	// show
   207  	show := noms.Command("show", `Shows a serialization of a Noms object
   208  See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object argument.
   209  `)
   210  	show.Flag("raw", "If true, dumps the raw binary version of the data").Bool()
   211  	show.Flag("stats", "If true, reports statistics related to the value").Bool()
   212  	show.Flag("tz", "display formatted date comments in specified timezone, must be: local or utc").Enum("local", "utc")
   213  	show.Arg("object", "a noms object").Required().String()
   214  
   215  	// walk
   216  	walk := noms.Command("walk", `Walks references contained in an object.
   217  See Spelling Objects at https://github.com/attic-labs/noms/blob/master/doc/spelling.md for details on the object argument.
   218  `)
   219  	walk.Arg("object", "a noms object").String()
   220  	walk.Flag("quiet", "If true, prints only dangling refs, not the paths of all refs").Bool()
   221  
   222  	// version
   223  	noms.Command("version", "Print the noms version")
   224  
   225  	//manifest
   226  	manifest := noms.Command("manifest", `Prints a database's manifest in a more readable format`)
   227  	addDatabaseArg(manifest)
   228  
   229  	//cat
   230  	cat := noms.Command("cat", `Prints the contents of an nbs file`)
   231  	cat.Arg("nbs-file", "nbs file").Required().String()
   232  	cat.Flag("raw", "If true, includes the raw binary version of each chunk in the nbs file").Bool()
   233  	cat.Flag("decompressed", "If true, includes the decompressed binary version of each chunk in the nbs file").Bool()
   234  	cat.Flag("no-show", "If true, skips printing of the value").Bool()
   235  	cat.Flag("no-refs", "If true, skips printing of the refs").Bool()
   236  	cat.Flag("hashes-only", "If true, only prints the b32 hashes").Bool()
   237  }