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 }