From ce4658abfc128ec3451e0a04da9a84dafd2a41ad Mon Sep 17 00:00:00 2001 From: mpl Date: Tue, 6 Mar 2018 22:39:14 +0100 Subject: [PATCH] cmd: rename camput to pk-put, and make "pk put" call it A new "put" mode is added to the pk command, so that the "pk put" command can be used to create and upload blobs. What this command does is actually just call the previously named "camput" executable, which is renamed to "pk-put" in this change. This involves adding a new way to register a mode in cmdmain, when such a mode is just meant to call an external binary. To emphasize the distinction, the existing func (to register a sub-command, or a mode) is renamed from RegisterCommand to RegisterMode, and RegisterCommand is now the name of the new func/way. Updates #981 Updates #1056 Change-Id: Ief954c17aa88a376f551df7de4b4e9fe41ad96d1 --- cmd/pk-deploy/gce.go | 2 +- cmd/{camput => pk-put}/.gitignore | 0 cmd/{camput => pk-put}/androidx.go | 2 +- cmd/{camput => pk-put}/attr.go | 4 +- cmd/{camput => pk-put}/blobs.go | 4 +- cmd/{camput => pk-put}/cache.go | 0 cmd/{camput => pk-put}/camput.go | 2 +- cmd/{camput => pk-put}/camput_test.go | 8 +- cmd/{camput => pk-put}/delete.go | 4 +- cmd/{camput => pk-put}/discard.go | 0 cmd/{camput => pk-put}/doc.go | 42 ++++---- cmd/{camput => pk-put}/files.go | 8 +- cmd/{camput => pk-put}/init.go | 12 +-- cmd/{camput => pk-put}/kvcache.go | 8 +- cmd/{camput => pk-put}/permanode.go | 4 +- cmd/{camput => pk-put}/rawobj.go | 4 +- cmd/{camput => pk-put}/remove.go | 4 +- cmd/{camput => pk-put}/share.go | 4 +- cmd/{camput => pk-put}/stat_darwin.go | 0 cmd/{camput => pk-put}/stat_linux.go | 0 cmd/{camput => pk-put}/uploader.go | 0 cmd/pk/claims.go | 2 +- cmd/pk/dbinit.go | 2 +- cmd/pk/debug.go | 2 +- cmd/pk/describe.go | 2 +- cmd/pk/disco.go | 2 +- cmd/pk/dp_idx_rebuild.go | 2 +- cmd/pk/dumpconfig.go | 2 +- cmd/pk/env.go | 2 +- cmd/pk/googinit.go | 2 +- cmd/pk/index.go | 2 +- cmd/pk/list.go | 2 +- cmd/pk/makestatic.go | 2 +- cmd/pk/packblobs.go | 2 +- cmd/pk/put.go | 74 +++++++++++++++ cmd/pk/search.go | 2 +- cmd/pk/searchdoc.go | 2 +- cmd/pk/searchnames.go | 4 +- cmd/pk/sync.go | 2 +- cmd/pk/whoami.go | 2 +- dev/devcam/camget.go | 2 +- dev/devcam/camput.go | 14 +-- dev/devcam/doc.go | 4 +- dev/devcam/hook.go | 2 +- dev/devcam/pk.go | 2 +- dev/devcam/pkmount.go | 2 +- dev/devcam/review.go | 2 +- dev/devcam/server.go | 2 +- dev/devcam/test.go | 2 +- doc/README.md | 2 +- doc/client-config.md | 10 +- doc/environment-vars.md | 4 +- doc/server-config.md | 2 +- doc/sharing.md | 2 +- doc/status.md | 4 +- doc/terms.md | 2 +- doc/uses.md | 2 +- make.go | 4 +- misc/docker/dock.go | 2 +- pkg/client/android/androidx.go | 4 +- pkg/client/client.go | 8 +- pkg/client/config.go | 8 +- pkg/client/upload.go | 4 +- pkg/cmdmain/cmdmain.go | 121 +++++++++++++++++++++++- pkg/cmdmain/exec.go | 27 ++++++ pkg/importer/twitter/twitter.go | 4 +- pkg/test/integration/camget_test.go | 10 +- pkg/test/integration/camlistore_test.go | 22 ++--- pkg/test/integration/camput_test.go | 26 ++--- pkg/test/integration/diskpacked_test.go | 2 +- pkg/test/integration/named_test.go | 14 +-- pkg/test/integration/non-utf8_test.go | 10 +- pkg/test/integration/share_test.go | 4 +- pkg/test/world.go | 16 ++-- pkg/types/camtypes/errors.go | 2 +- pkg/types/clientconfig/config.go | 2 +- 76 files changed, 397 insertions(+), 177 deletions(-) rename cmd/{camput => pk-put}/.gitignore (100%) rename cmd/{camput => pk-put}/androidx.go (94%) rename cmd/{camput => pk-put}/attr.go (94%) rename cmd/{camput => pk-put}/blobs.go (96%) rename cmd/{camput => pk-put}/cache.go (100%) rename cmd/{camput => pk-put}/camput.go (98%) rename cmd/{camput => pk-put}/camput_test.go (96%) rename cmd/{camput => pk-put}/delete.go (90%) rename cmd/{camput => pk-put}/discard.go (100%) rename cmd/{camput => pk-put}/doc.go (60%) rename cmd/{camput => pk-put}/files.go (99%) rename cmd/{camput => pk-put}/init.go (94%) rename cmd/{camput => pk-put}/kvcache.go (97%) rename cmd/{camput => pk-put}/permanode.go (95%) rename cmd/{camput => pk-put}/rawobj.go (92%) rename cmd/{camput => pk-put}/remove.go (88%) rename cmd/{camput => pk-put}/share.go (95%) rename cmd/{camput => pk-put}/stat_darwin.go (100%) rename cmd/{camput => pk-put}/stat_linux.go (100%) rename cmd/{camput => pk-put}/uploader.go (100%) create mode 100644 cmd/pk/put.go create mode 100644 pkg/cmdmain/exec.go diff --git a/cmd/pk-deploy/gce.go b/cmd/pk-deploy/gce.go index d08a7ed14..742cc2de4 100644 --- a/cmd/pk-deploy/gce.go +++ b/cmd/pk-deploy/gce.go @@ -44,7 +44,7 @@ type gceCmd struct { } func init() { - cmdmain.RegisterCommand("gce", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("gce", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(gceCmd) flags.StringVar(&cmd.project, "project", "", "Name of Project.") flags.StringVar(&cmd.zone, "zone", gce.DefaultRegion, "GCE zone or region. If a region is given, a random zone in that region is selected. See https://cloud.google.com/compute/docs/zones") diff --git a/cmd/camput/.gitignore b/cmd/pk-put/.gitignore similarity index 100% rename from cmd/camput/.gitignore rename to cmd/pk-put/.gitignore diff --git a/cmd/camput/androidx.go b/cmd/pk-put/androidx.go similarity index 94% rename from cmd/camput/androidx.go rename to cmd/pk-put/androidx.go index c98d30d69..e7d63914b 100644 --- a/cmd/camput/androidx.go +++ b/cmd/pk-put/androidx.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Hacks for running camput as a child process on Android. +// Hacks for running pk-put as a child process on Android. package main diff --git a/cmd/camput/attr.go b/cmd/pk-put/attr.go similarity index 94% rename from cmd/camput/attr.go rename to cmd/pk-put/attr.go index 0abc666eb..020a49afd 100644 --- a/cmd/camput/attr.go +++ b/cmd/pk-put/attr.go @@ -31,7 +31,7 @@ type attrCmd struct { } func init() { - cmdmain.RegisterCommand("attr", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("attr", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(attrCmd) flags.BoolVar(&cmd.add, "add", false, `Adds attribute (e.g. "tag")`) flags.BoolVar(&cmd.del, "del", false, "Deletes named attribute [value]") @@ -44,7 +44,7 @@ func (c *attrCmd) Describe() string { } func (c *attrCmd) Usage() { - cmdmain.Errorf("Usage: camput [globalopts] attr [attroption] ") + cmdmain.Errorf("Usage: pk-put [globalopts] attr [attroption] ") } func (c *attrCmd) Examples() []string { diff --git a/cmd/camput/blobs.go b/cmd/pk-put/blobs.go similarity index 96% rename from cmd/camput/blobs.go rename to cmd/pk-put/blobs.go index 5d5c3eb70..76fa7c166 100644 --- a/cmd/camput/blobs.go +++ b/cmd/pk-put/blobs.go @@ -44,7 +44,7 @@ type blobCmd struct { } func init() { - cmdmain.RegisterCommand("blob", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("blob", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(blobCmd) flags.BoolVar(&cmd.makePermanode, "permanode", false, "Create and associate a new permanode for the blob.") flags.StringVar(&cmd.title, "title", "", "Optional title attribute to set on permanode when using -permanode.") @@ -59,7 +59,7 @@ func (c *blobCmd) Describe() string { } func (c *blobCmd) Usage() { - fmt.Fprintf(cmdmain.Stderr, "Usage: camput [globalopts] blob \n camput [globalopts] blob -\n") + fmt.Fprintf(cmdmain.Stderr, "Usage: pk-put [globalopts] blob \n pk-put [globalopts] blob -\n") } func (c *blobCmd) Examples() []string { diff --git a/cmd/camput/cache.go b/cmd/pk-put/cache.go similarity index 100% rename from cmd/camput/cache.go rename to cmd/pk-put/cache.go diff --git a/cmd/camput/camput.go b/cmd/pk-put/camput.go similarity index 98% rename from cmd/camput/camput.go rename to cmd/pk-put/camput.go index ffd4722bb..62c3835c1 100644 --- a/cmd/camput/camput.go +++ b/cmd/pk-put/camput.go @@ -93,7 +93,7 @@ func init() { } } - // So multiple cmd/camput TestFoo funcs run, each with + // So multiple cmd/pk-put TestFoo funcs run, each with // an fresh (and not previously closed) Uploader: uploader = nil uploaderOnce = sync.Once{} diff --git a/cmd/camput/camput_test.go b/cmd/pk-put/camput_test.go similarity index 96% rename from cmd/camput/camput_test.go rename to cmd/pk-put/camput_test.go index e3fd41bef..5d1898257 100644 --- a/cmd/camput/camput_test.go +++ b/cmd/pk-put/camput_test.go @@ -30,7 +30,7 @@ import ( "perkeep.org/pkg/cmdmain" ) -// env is the environment that a camput test runs within. +// env is the environment that a pk-put test runs within. type env struct { // stdin is the standard input, or /dev/null if nil stdin io.Reader @@ -88,7 +88,7 @@ func TestUsageOnNoargs(t *testing.T) { if len(out) != 0 { t.Errorf("wanted nothing on stdout; got:\n%s", out) } - if !bytes.Contains(err, []byte("Usage: camput")) { + if !bytes.Contains(err, []byte("Usage: pk-put")) { t.Errorf("stderr doesn't contain usage. Got:\n%s", err) } } @@ -113,8 +113,8 @@ func TestCommandUsage(t *testing.T) { func TestUploadingChangingDirectory(t *testing.T) { // TODO(bradfitz): // $ mkdir /tmp/somedir - // $ cp dev-camput /tmp/somedir - // $ ./dev-camput -file /tmp/somedir/ 2>&1 | tee /tmp/somedir/log + // $ cp dev-pk-put /tmp/somedir + // $ ./dev-pk-put -file /tmp/somedir/ 2>&1 | tee /tmp/somedir/log // ... verify it doesn't hang. t.Logf("TODO") } diff --git a/cmd/camput/delete.go b/cmd/pk-put/delete.go similarity index 90% rename from cmd/camput/delete.go rename to cmd/pk-put/delete.go index c8643cf78..72fbfbcf2 100644 --- a/cmd/camput/delete.go +++ b/cmd/pk-put/delete.go @@ -28,7 +28,7 @@ import ( type deleteCmd struct{} func init() { - cmdmain.RegisterCommand("delete", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("delete", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(deleteCmd) return cmd }) @@ -39,7 +39,7 @@ func (c *deleteCmd) Describe() string { } func (c *deleteCmd) Usage() { - cmdmain.Errorf("Usage: camput [globalopts] delete [blobref2]...") + cmdmain.Errorf("Usage: pk-put [globalopts] delete [blobref2]...") } func (c *deleteCmd) RunCommand(args []string) error { diff --git a/cmd/camput/discard.go b/cmd/pk-put/discard.go similarity index 100% rename from cmd/camput/discard.go rename to cmd/pk-put/discard.go diff --git a/cmd/camput/doc.go b/cmd/pk-put/doc.go similarity index 60% rename from cmd/camput/doc.go rename to cmd/pk-put/doc.go index ef134045e..19cc595fe 100644 --- a/cmd/camput/doc.go +++ b/cmd/pk-put/doc.go @@ -14,20 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ +// TODO(mpl): this doc is not in sync with what the pk-put help outputs. But it should be. + /* -The camput tool mainly pushes blobs, files, and directories. It can also perform various related tasks, such as setting tags, creating permanodes, and creating share blobs. +The pk-put tool mainly pushes blobs, files, and directories. It can also perform various related tasks, such as setting tags, creating permanodes, and creating share blobs. Usage: - camput [globalopts] [commandopts] [commandargs] + pk-put [globalopts] [commandopts] [commandargs] Modes: delete: Create and upload a delete claim. attr: Add, set, or delete a permanode's attribute. file: Upload file(s). - init: Initialize the camput configuration file. With no option, it tries to use the GPG key found in the default identity secret ring. + init: Initialize the pk-put configuration file. With no option, it tries to use the GPG key found in the default identity secret ring. permanode: Create and upload a permanode. rawobj: Upload a custom JSON schema blob. share: Grant access to a resource by making a "share" blob. @@ -35,31 +37,31 @@ Modes: Examples: - camput file [opts] (raw, without any metadata) - camput blob --permanode --title='My Blob' --tag=backup,my_blob - camput blob - (read from stdin) + pk-put blob (raw, without any metadata) + pk-put blob --permanode --title='My Blob' --tag=backup,my_blob + pk-put blob - (read from stdin) - camput permanode (create a new permanode) - camput permanode --title="Some Name" --tag=foo,bar (with attributes added) + pk-put permanode (create a new permanode) + pk-put permanode --title="Some Name" --tag=foo,bar (with attributes added) - camput init - camput init --gpgkey=XXXXX + pk-put init + pk-put init --gpgkey=XXXXX - camput share [opts] + pk-put share [opts] - camput rawobj (debug command) + pk-put rawobj (debug command) - camput attr Set attribute - camput attr --add Adds attribute (e.g. "tag") - camput attr --del [] Deletes named attribute [value + pk-put attr Set attribute + pk-put attr --add Adds attribute (e.g. "tag") + pk-put attr --del [] Deletes named attribute [value For mode-specific help: - camput -help + pk-put -help Global options: -help=false: print usage @@ -72,4 +74,4 @@ Global options: -verbose_http=false: show HTTP request summaries -version=false: show version */ -package main // import "perkeep.org/cmd/camput" +package main // import "perkeep.org/cmd/pk-put" diff --git a/cmd/camput/files.go b/cmd/pk-put/files.go similarity index 99% rename from cmd/camput/files.go rename to cmd/pk-put/files.go index 2e0d92227..f398b67e6 100644 --- a/cmd/camput/files.go +++ b/cmd/pk-put/files.go @@ -74,7 +74,7 @@ var ( ) func init() { - cmdmain.RegisterCommand("file", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("file", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(fileCmd) flags.BoolVar(&cmd.makePermanode, "permanode", false, "Create and associate a new permanode for the uploaded file or directory.") flags.BoolVar(&cmd.filePermanodes, "filenodes", false, "Create (if necessary) content-based permanodes for each uploaded file.") @@ -115,7 +115,7 @@ func (c *fileCmd) Describe() string { } func (c *fileCmd) Usage() { - fmt.Fprintf(cmdmain.Stderr, "Usage: camput [globalopts] file [fileopts] \n") + fmt.Fprintf(cmdmain.Stderr, "Usage: pk-put [globalopts] file [fileopts] \n") } func (c *fileCmd) Examples() []string { @@ -395,7 +395,7 @@ func (up *Uploader) uploadNode(ctx context.Context, n *node) (*client.PutResult, case mode&os.ModeNamedPipe != 0: // fifo bb.SetType("fifo") default: - return nil, fmt.Errorf("camput.files: unsupported file type %v for file %v", mode, n.fullPath) + return nil, fmt.Errorf("pk-put.files: unsupported file type %v for file %v", mode, n.fullPath) case fi.IsDir(): ss, err := n.directoryStaticSet() if err != nil { @@ -436,7 +436,7 @@ func (up *Uploader) statReceiver(n *node) blobserver.StatReceiver { statReceiver := up.altStatReceiver if statReceiver == nil { // TODO(mpl): simplify the altStatReceiver situation as well, - // see TODO in cmd/camput/uploader.go + // see TODO in cmd/pk-put/uploader.go statReceiver = up.Client } if android.IsChild() && n != nil && n.fi.Mode()&os.ModeType == 0 { diff --git a/cmd/camput/init.go b/cmd/pk-put/init.go similarity index 94% rename from cmd/camput/init.go rename to cmd/pk-put/init.go index e0a97e729..d2899fbe1 100644 --- a/cmd/camput/init.go +++ b/cmd/pk-put/init.go @@ -46,7 +46,7 @@ type initCmd struct { } func init() { - cmdmain.RegisterCommand("init", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("init", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(initCmd) flags.BoolVar(&cmd.newKey, "newkey", false, "Automatically generate a new identity in a new secret ring at the default location (~/.config/camlistore/identity-secring.gpg on linux).") @@ -59,11 +59,11 @@ func init() { } func (c *initCmd) Describe() string { - return "Initialize the camput configuration file. With no option, it tries to use the GPG key found in the default identity secret ring." + return "Initialize the pk-put configuration file. With no option, it tries to use the GPG key found in the default identity secret ring." } func (c *initCmd) Usage() { - usage := "Usage: camput [--server host] init [opts]\n\nExamples:\n" + usage := "Usage: pk-put [--server host] init [opts]\n\nExamples:\n" for _, v := range c.usageExamples() { usage += v + "\n" } @@ -73,10 +73,10 @@ func (c *initCmd) Usage() { func (c *initCmd) usageExamples() []string { var examples []string for _, v := range c.Examples() { - examples = append(examples, "camput init "+v) + examples = append(examples, "pk-put init "+v) } return append(examples, - "camput --server=https://localhost:3179 init --userpass=foo:bar --insecure=true") + "pk-put --server=https://localhost:3179 init --userpass=foo:bar --insecure=true") } func (c *initCmd) Examples() []string { @@ -106,7 +106,7 @@ func (c *initCmd) initSecretRing() error { c.secretRing = osutil.SecretRingFile() } if _, err := os.Stat(c.secretRing); err != nil { - hint := "\nA GPG key is required, please use 'camput init --newkey'.\n\nOr if you know what you're doing, you can set the global camput flag --secret-keyring, or the CAMLI_SECRET_RING env var, to use your own GPG ring. And --gpgkey= or GPGKEY to select which key ID to use." + hint := "\nA GPG key is required, please use 'pk-put init --newkey'.\n\nOr if you know what you're doing, you can set the global pk-put flag --secret-keyring, or the CAMLI_SECRET_RING env var, to use your own GPG ring. And --gpgkey= or GPGKEY to select which key ID to use." return fmt.Errorf("Could not use secret ring file %v: %v.\n%v", c.secretRing, err, hint) } return nil diff --git a/cmd/camput/kvcache.go b/cmd/pk-put/kvcache.go similarity index 97% rename from cmd/camput/kvcache.go rename to cmd/pk-put/kvcache.go index ff6541c00..9138ea5f5 100644 --- a/cmd/camput/kvcache.go +++ b/cmd/pk-put/kvcache.go @@ -55,7 +55,7 @@ type KvHaveCache struct { func NewKvHaveCache(gen string) *KvHaveCache { cleanCacheDir() - fullPath := filepath.Join(osutil.CacheDir(), "camput.havecache."+escapeGen(gen)+".leveldb") + fullPath := filepath.Join(osutil.CacheDir(), "pk-put.havecache."+escapeGen(gen)+".leveldb") db, err := leveldb.OpenFile(fullPath, nil) if err != nil { log.Fatalf("Could not create/open new have cache at %v, %v", fullPath, err) @@ -152,7 +152,7 @@ type KvStatCache struct { } func NewKvStatCache(gen string) *KvStatCache { - fullPath := filepath.Join(osutil.CacheDir(), "camput.statcache."+escapeGen(gen)+".leveldb") + fullPath := filepath.Join(osutil.CacheDir(), "pk-put.statcache."+escapeGen(gen)+".leveldb") db, err := leveldb.OpenFile(fullPath, nil) if err != nil { log.Fatalf("Could not create/open new stat cache at %v, %v", fullPath, err) @@ -376,11 +376,11 @@ func cleanCacheDir() { } for _, fi := range fis { - if strings.HasPrefix(fi.Name(), "camput.havecache.") { + if strings.HasPrefix(fi.Name(), "pk-put.havecache.") { haveCache = append(haveCache, fi) continue } - if strings.HasPrefix(fi.Name(), "camput.statcache.") { + if strings.HasPrefix(fi.Name(), "pk-put.statcache.") { statCache = append(statCache, fi) continue } diff --git a/cmd/camput/permanode.go b/cmd/pk-put/permanode.go similarity index 95% rename from cmd/camput/permanode.go rename to cmd/pk-put/permanode.go index 89cf03475..668b77021 100644 --- a/cmd/camput/permanode.go +++ b/cmd/pk-put/permanode.go @@ -36,7 +36,7 @@ type permanodeCmd struct { } func init() { - cmdmain.RegisterCommand("permanode", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("permanode", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(permanodeCmd) flags.StringVar(&cmd.title, "title", "", "Optional 'title' attribute to set on new permanode") flags.StringVar(&cmd.tag, "tag", "", "Optional tag(s) to set on new permanode; comma separated.") @@ -51,7 +51,7 @@ func (c *permanodeCmd) Describe() string { } func (c *permanodeCmd) Usage() { - cmdmain.Errorf("Usage: camput [globalopts] permanode [permanodeopts]\n") + cmdmain.Errorf("Usage: pk-put [globalopts] permanode [permanodeopts]\n") } func (c *permanodeCmd) Examples() []string { diff --git a/cmd/camput/rawobj.go b/cmd/pk-put/rawobj.go similarity index 92% rename from cmd/camput/rawobj.go rename to cmd/pk-put/rawobj.go index dcbbe5782..c61345129 100644 --- a/cmd/camput/rawobj.go +++ b/cmd/pk-put/rawobj.go @@ -31,7 +31,7 @@ type rawCmd struct { } func init() { - cmdmain.RegisterCommand("rawobj", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("rawobj", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(rawCmd) flags.StringVar(&cmd.vals, "vals", "", "Pipe-separated key=value properties") flags.BoolVar(&cmd.signed, "signed", true, "whether to sign the JSON object") @@ -44,7 +44,7 @@ func (c *rawCmd) Describe() string { } func (c *rawCmd) Usage() { - cmdmain.Errorf("Usage: camput [globalopts] rawobj [rawopts]\n") + cmdmain.Errorf("Usage: pk-put [globalopts] rawobj [rawopts]\n") } func (c *rawCmd) Examples() []string { diff --git a/cmd/camput/remove.go b/cmd/pk-put/remove.go similarity index 88% rename from cmd/camput/remove.go rename to cmd/pk-put/remove.go index a3db5d737..5fd95f50d 100644 --- a/cmd/camput/remove.go +++ b/cmd/pk-put/remove.go @@ -27,14 +27,14 @@ import ( type removeCmd struct{} func init() { - cmdmain.RegisterCommand("remove", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("remove", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(removeCmd) return cmd }) } func (c *removeCmd) Usage() { - fmt.Fprintf(cmdmain.Stderr, `Usage: camput remove + fmt.Fprintf(cmdmain.Stderr, `Usage: pk-put remove This command is for debugging only. You're not expected to use it in practice. `) diff --git a/cmd/camput/share.go b/cmd/pk-put/share.go similarity index 95% rename from cmd/camput/share.go rename to cmd/pk-put/share.go index 15c7d7d28..31c7308cd 100644 --- a/cmd/camput/share.go +++ b/cmd/pk-put/share.go @@ -35,7 +35,7 @@ type shareCmd struct { } func init() { - cmdmain.RegisterCommand("share", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("share", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(shareCmd) flags.StringVar(&cmd.search, "search", "", "share a search result, rather than a single blob. Should be the JSON representation of a search.SearchQuery (see https://perkeep.org/pkg/search/#SearchQuery for details). Exclusive with, and overrides the parameter.") flags.BoolVar(&cmd.transitive, "transitive", false, "share everything reachable from the given blobref") @@ -49,7 +49,7 @@ func (c *shareCmd) Describe() string { } func (c *shareCmd) Usage() { - fmt.Fprintf(cmdmain.Stderr, `Usage: camput share [opts] [] # blobRef of a file or directory + fmt.Fprintf(cmdmain.Stderr, `Usage: pk-put share [opts] [] # blobRef of a file or directory `) } diff --git a/cmd/camput/stat_darwin.go b/cmd/pk-put/stat_darwin.go similarity index 100% rename from cmd/camput/stat_darwin.go rename to cmd/pk-put/stat_darwin.go diff --git a/cmd/camput/stat_linux.go b/cmd/pk-put/stat_linux.go similarity index 100% rename from cmd/camput/stat_linux.go rename to cmd/pk-put/stat_linux.go diff --git a/cmd/camput/uploader.go b/cmd/pk-put/uploader.go similarity index 100% rename from cmd/camput/uploader.go rename to cmd/pk-put/uploader.go diff --git a/cmd/pk/claims.go b/cmd/pk/claims.go index c033568bb..c3c034e82 100644 --- a/cmd/pk/claims.go +++ b/cmd/pk/claims.go @@ -33,7 +33,7 @@ type claimsCmd struct { } func init() { - cmdmain.RegisterCommand("claims", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("claims", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(claimsCmd) flags.StringVar(&cmd.server, "server", "", "Server to fetch claims from. "+serverFlagHelp) flags.StringVar(&cmd.attr, "attr", "", "Filter claims about a specific attribute. If empty, all claims are returned.") diff --git a/cmd/pk/dbinit.go b/cmd/pk/dbinit.go index 06fc88764..62d9ba09c 100644 --- a/cmd/pk/dbinit.go +++ b/cmd/pk/dbinit.go @@ -51,7 +51,7 @@ type dbinitCmd struct { } func init() { - cmdmain.RegisterCommand("dbinit", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("dbinit", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(dbinitCmd) flags.StringVar(&cmd.user, "user", "root", "Admin user.") flags.StringVar(&cmd.password, "password", "", "Admin password.") diff --git a/cmd/pk/debug.go b/cmd/pk/debug.go index 44999ec99..261d2e67a 100644 --- a/cmd/pk/debug.go +++ b/cmd/pk/debug.go @@ -48,7 +48,7 @@ type debugSubMode struct { type debugCmd struct{} func init() { - cmdmain.RegisterCommand("debug", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("debug", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(debugCmd) }) } diff --git a/cmd/pk/describe.go b/cmd/pk/describe.go index c55779735..b33f74f2d 100644 --- a/cmd/pk/describe.go +++ b/cmd/pk/describe.go @@ -37,7 +37,7 @@ type desCmd struct { } func init() { - cmdmain.RegisterCommand("describe", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("describe", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(desCmd) flags.StringVar(&cmd.server, "server", "", "Server to query. "+serverFlagHelp) flags.StringVar(&cmd.at, "at", "", "Describe at what point in time. RFC3339 only for now. Empty string means current time.") diff --git a/cmd/pk/disco.go b/cmd/pk/disco.go index 77498329d..e1b2fb7fd 100644 --- a/cmd/pk/disco.go +++ b/cmd/pk/disco.go @@ -31,7 +31,7 @@ type discoCmd struct { } func init() { - cmdmain.RegisterCommand("discovery", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("discovery", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(discoCmd) flags.StringVar(&cmd.server, "server", "", "Server to do discovery against. "+serverFlagHelp) flags.BoolVar(&cmd.httpVer, "httpversion", false, "discover the HTTP version") diff --git a/cmd/pk/dp_idx_rebuild.go b/cmd/pk/dp_idx_rebuild.go index 811180ad8..20ec019f5 100644 --- a/cmd/pk/dp_idx_rebuild.go +++ b/cmd/pk/dp_idx_rebuild.go @@ -35,7 +35,7 @@ type reindexdpCmd struct { } func init() { - cmdmain.RegisterCommand("reindex-diskpacked", + cmdmain.RegisterMode("reindex-diskpacked", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(reindexdpCmd) flags.BoolVar(&cmd.overwrite, "overwrite", false, diff --git a/cmd/pk/dumpconfig.go b/cmd/pk/dumpconfig.go index 9cb4c0b8d..ed2ea3e60 100644 --- a/cmd/pk/dumpconfig.go +++ b/cmd/pk/dumpconfig.go @@ -31,7 +31,7 @@ import ( type dumpconfigCmd struct{} func init() { - cmdmain.RegisterCommand("dumpconfig", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("dumpconfig", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(dumpconfigCmd) }) } diff --git a/cmd/pk/env.go b/cmd/pk/env.go index 27d419736..620063686 100644 --- a/cmd/pk/env.go +++ b/cmd/pk/env.go @@ -38,7 +38,7 @@ var envMap = map[string]func() string{ type envCmd struct{} func init() { - cmdmain.RegisterCommand("env", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("env", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(envCmd) }) } diff --git a/cmd/pk/googinit.go b/cmd/pk/googinit.go index d2bef0ef4..fa0d86b4c 100644 --- a/cmd/pk/googinit.go +++ b/cmd/pk/googinit.go @@ -37,7 +37,7 @@ type googinitCmd struct { } func init() { - cmdmain.RegisterCommand("googinit", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("googinit", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(googinitCmd) flags.StringVar(&cmd.storageType, "type", "", "Storage type: drive or cloud") return cmd diff --git a/cmd/pk/index.go b/cmd/pk/index.go index 3d80e0b67..055350f25 100644 --- a/cmd/pk/index.go +++ b/cmd/pk/index.go @@ -32,7 +32,7 @@ type indexCmd struct { } func init() { - cmdmain.RegisterCommand("index", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("index", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(indexCmd) flags.BoolVar(&cmd.wipe, "wipe", false, "Erase and recreate all discovered indexes. NOOP for now.") if debug, _ := strconv.ParseBool(os.Getenv("CAMLI_DEBUG")); debug { diff --git a/cmd/pk/list.go b/cmd/pk/list.go index 8ac1ff8c2..f0efc5c0a 100644 --- a/cmd/pk/list.go +++ b/cmd/pk/list.go @@ -39,7 +39,7 @@ type listCmd struct { } func init() { - cmdmain.RegisterCommand("list", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("list", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := &listCmd{ syncCmd: &syncCmd{ dest: "stdout", diff --git a/cmd/pk/makestatic.go b/cmd/pk/makestatic.go index a5b0e6185..4ba33972a 100644 --- a/cmd/pk/makestatic.go +++ b/cmd/pk/makestatic.go @@ -34,7 +34,7 @@ type makeStaticCmd struct { } func init() { - cmdmain.RegisterCommand("makestatic", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("makestatic", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(makeStaticCmd) flags.StringVar(&cmd.server, "server", "", "Server to search. "+serverFlagHelp) return cmd diff --git a/cmd/pk/packblobs.go b/cmd/pk/packblobs.go index 2f33902e8..bdf662648 100644 --- a/cmd/pk/packblobs.go +++ b/cmd/pk/packblobs.go @@ -33,7 +33,7 @@ type packBlobsCmd struct { } func init() { - cmdmain.RegisterCommand("packblobs", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("packblobs", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(packBlobsCmd) flags.StringVar(&cmd.server, "server", "", "Server to search. "+serverFlagHelp) return cmd diff --git a/cmd/pk/put.go b/cmd/pk/put.go new file mode 100644 index 000000000..bdfd9ccb0 --- /dev/null +++ b/cmd/pk/put.go @@ -0,0 +1,74 @@ +/* +Copyright 2018 The Perkeep Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + + "perkeep.org/internal/osutil" + "perkeep.org/pkg/cmdmain" +) + +type putCmd struct { + binName string // the executable that is actually called, i.e. "pk-put". +} + +func init() { + cmdmain.RegisterCommand("put", func(flags *flag.FlagSet) cmdmain.CommandRunner { + return &putCmd{ + binName: "pk-put", + } + // TODO(mpl): do cmdmain.ExtraFlagRegistration = client.AddFlags somewhere, so + // pk has same global flags as pk-put? (which I think it already does anyway). + }) +} + +func (c *putCmd) Describe() string { + return "Create and upload blobs to a server." +} + +func (c *putCmd) Usage() { + panic("pk put Usage should never get called, as we should always end up calling either pk's or pk-put's usage") +} + +func (c *putCmd) RunCommand(args []string) error { + // RunCommand is only implemented to satisfy the CommandRunner interface. + panic("pk put RunCommand should never get called, as pk is supposed to invoke pk-put instead.") +} + +// LookPath returns the full path to the executable that "pk put" actually +// calls, i.e. "pk-put". +func (c *putCmd) LookPath() (string, error) { + fullPath, err := exec.LookPath(c.binName) + if err == nil { + return fullPath, nil + } + // If not in PATH, also try in bin dir of Perkeep source tree + perkeepPath, err := osutil.GoPackagePath("perkeep.org") + if err != nil { + return "", fmt.Errorf("full path for %v command was not found in either PATH or the bin dir of the source tree", c.binName) + } + fullPath = filepath.Join(perkeepPath, "bin", c.binName) + if _, err := os.Stat(fullPath); err != nil { + return "", fmt.Errorf("full path for %v command was not found in either PATH or %v", c.binName, filepath.Join(perkeepPath, "bin")) + } + return fullPath, nil +} diff --git a/cmd/pk/search.go b/cmd/pk/search.go index 48fd3dd62..2db681820 100644 --- a/cmd/pk/search.go +++ b/cmd/pk/search.go @@ -41,7 +41,7 @@ type searchCmd struct { } func init() { - cmdmain.RegisterCommand("search", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("search", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(searchCmd) flags.StringVar(&cmd.server, "server", "", "Server to search. "+serverFlagHelp) flags.IntVar(&cmd.limit, "limit", 0, "Limit number of results. 0 is default. Negative means no limit.") diff --git a/cmd/pk/searchdoc.go b/cmd/pk/searchdoc.go index f5d97ff55..0fc66d2d2 100644 --- a/cmd/pk/searchdoc.go +++ b/cmd/pk/searchdoc.go @@ -31,7 +31,7 @@ import ( type searchDocCmd struct{} func init() { - cmdmain.RegisterCommand("searchdoc", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("searchdoc", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(searchDocCmd) }) } diff --git a/cmd/pk/searchnames.go b/cmd/pk/searchnames.go index 9be60eeba..b57cdd1ea 100644 --- a/cmd/pk/searchnames.go +++ b/cmd/pk/searchnames.go @@ -36,10 +36,10 @@ type searchNamesSetCmd struct{} func init() { osutil.AddSecretRingFlag() - cmdmain.RegisterCommand("named-search-get", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("named-search-get", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(searchNamesGetCmd) }) - cmdmain.RegisterCommand("named-search-set", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("named-search-set", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(searchNamesSetCmd) }) } diff --git a/cmd/pk/sync.go b/cmd/pk/sync.go index 3e1d416e4..693a4f00c 100644 --- a/cmd/pk/sync.go +++ b/cmd/pk/sync.go @@ -55,7 +55,7 @@ type syncCmd struct { } func init() { - cmdmain.RegisterCommand("sync", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("sync", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(syncCmd) flags.StringVar(&cmd.src, "src", "", "Source blobserver. "+serverFlagHelp) flags.StringVar(&cmd.dest, "dest", "", "Destination blobserver (same format as src), or 'stdout' to just enumerate the --src blobs to stdout.") diff --git a/cmd/pk/whoami.go b/cmd/pk/whoami.go index 3f9240ec8..ae3689c04 100644 --- a/cmd/pk/whoami.go +++ b/cmd/pk/whoami.go @@ -32,7 +32,7 @@ import ( type whoamiCmd struct{} func init() { - cmdmain.RegisterCommand("whoami", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("whoami", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(whoamiCmd) }) } diff --git a/dev/devcam/camget.go b/dev/devcam/camget.go index 3ea394fa9..8aa24811e 100644 --- a/dev/devcam/camget.go +++ b/dev/devcam/camget.go @@ -41,7 +41,7 @@ type getCmd struct { } func init() { - cmdmain.RegisterCommand("get", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("get", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := &getCmd{ env: NewCopyEnv(), } diff --git a/dev/devcam/camput.go b/dev/devcam/camput.go index 79bf5f887..b3a5de954 100644 --- a/dev/devcam/camput.go +++ b/dev/devcam/camput.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// This file adds the "put" subcommand to devcam, to run camput against the dev server. +// This file adds the "put" subcommand to devcam, to run pk-put against the dev server. package main @@ -40,7 +40,7 @@ type putCmd struct { } func init() { - cmdmain.RegisterCommand("put", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("put", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := &putCmd{ env: NewCopyEnv(), } @@ -53,7 +53,7 @@ func init() { } func (c *putCmd) Usage() { - fmt.Fprintf(cmdmain.Stderr, "Usage: devcam put [put_opts] camput_args\n") + fmt.Fprintf(cmdmain.Stderr, "Usage: devcam put [put_opts] pk-put_args\n") } func (c *putCmd) Examples() []string { @@ -63,7 +63,7 @@ func (c *putCmd) Examples() []string { } func (c *putCmd) Describe() string { - return "run camput in dev mode." + return "run pk-put in dev mode." } func (c *putCmd) RunCommand(args []string) error { @@ -72,8 +72,8 @@ func (c *putCmd) RunCommand(args []string) error { return cmdmain.UsageError(fmt.Sprint(err)) } if !*noBuild { - if err := build(filepath.Join("cmd", "camput")); err != nil { - return fmt.Errorf("Could not build camput: %v", err) + if err := build(filepath.Join("cmd", "pk-put")); err != nil { + return fmt.Errorf("Could not build pk-put: %v", err) } } c.env.SetCamdevVars(c.altkey) @@ -88,7 +88,7 @@ func (c *putCmd) RunCommand(args []string) error { blobserver = strings.Replace(blobserver, "http://", "https://", 1) } - cmdBin := filepath.Join("bin", "camput") + cmdBin := filepath.Join("bin", "pk-put") cmdArgs := []string{ "-verbose=" + strconv.FormatBool(*cmdmain.FlagVerbose || !quiet), "-server=" + blobserver, diff --git a/dev/devcam/doc.go b/dev/devcam/doc.go index 57a15a80f..f0ae34989 100644 --- a/dev/devcam/doc.go +++ b/dev/devcam/doc.go @@ -16,7 +16,7 @@ limitations under the License. /* The devcam tool is a collection of wrappers around the camlistore programs -(camistored, camput, pk...) which take care of setup and configuration, +(camistored, pk-put, pk...) which take care of setup and configuration, so they can be used by developers to ease hacking on camlistore. Usage: @@ -26,7 +26,7 @@ Usage: Modes: get: run camget in dev mode. - put: run camput in dev mode. + put: run pk-put in dev mode. server: run the stand-alone camlistored in dev mode. Examples: diff --git a/dev/devcam/hook.go b/dev/devcam/hook.go index c35ee9efb..b66fab8e6 100644 --- a/dev/devcam/hook.go +++ b/dev/devcam/hook.go @@ -85,7 +85,7 @@ type hookCmd struct { } func init() { - cmdmain.RegisterCommand("hook", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("hook", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := &hookCmd{} flags.BoolVar(&cmd.verbose, "verbose", false, "Be verbose.") // TODO(mpl): "-w" flag to run gofmt -w and devcam fixv -w. for now just print instruction. diff --git a/dev/devcam/pk.go b/dev/devcam/pk.go index 4d0c906a6..5c4a85d30 100644 --- a/dev/devcam/pk.go +++ b/dev/devcam/pk.go @@ -36,7 +36,7 @@ type toolCmd struct { } func init() { - cmdmain.RegisterCommand("tool", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("tool", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := &toolCmd{ env: NewCopyEnv(), } diff --git a/dev/devcam/pkmount.go b/dev/devcam/pkmount.go index 854b0401d..7c1ea8300 100644 --- a/dev/devcam/pkmount.go +++ b/dev/devcam/pkmount.go @@ -46,7 +46,7 @@ type mountCmd struct { const mountpoint = "/tmp/pk-mount-dir" func init() { - cmdmain.RegisterCommand("mount", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("mount", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := &mountCmd{ env: NewCopyEnv(), } diff --git a/dev/devcam/review.go b/dev/devcam/review.go index 4c6253f26..a917e1293 100644 --- a/dev/devcam/review.go +++ b/dev/devcam/review.go @@ -38,7 +38,7 @@ var ( type reviewCmd struct{} func init() { - cmdmain.RegisterCommand("review", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("review", func(flags *flag.FlagSet) cmdmain.CommandRunner { return new(reviewCmd) }) } diff --git a/dev/devcam/server.go b/dev/devcam/server.go index 3d6c3197d..4a9500055 100644 --- a/dev/devcam/server.go +++ b/dev/devcam/server.go @@ -87,7 +87,7 @@ type serverCmd struct { } func init() { - cmdmain.RegisterCommand("server", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("server", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := &serverCmd{ env: NewCopyEnv(), } diff --git a/dev/devcam/test.go b/dev/devcam/test.go index 9714c92d9..b59061915 100644 --- a/dev/devcam/test.go +++ b/dev/devcam/test.go @@ -39,7 +39,7 @@ type testCmd struct { } func init() { - cmdmain.RegisterCommand("test", func(flags *flag.FlagSet) cmdmain.CommandRunner { + cmdmain.RegisterMode("test", func(flags *flag.FlagSet) cmdmain.CommandRunner { cmd := new(testCmd) flags.BoolVar(&cmd.short, "short", false, "Use '-short' with go test.") flags.BoolVar(&cmd.precommit, "precommit", true, "Run the pre-commit githook as part of tests.") diff --git a/doc/README.md b/doc/README.md index 35d5957a7..93064834b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -62,4 +62,4 @@ on the [mailing list](https://groups.google.com/group/camlistore). ## Video tutorials {#tutorials} * 2014-03, [Getting started with Camlistore](https://www.youtube.com/watch?v=RUv-8PhnNp8) -* 2014-03, [Getting started with camput and the Camlistore client tools](https://www.youtube.com/watch?v=DdccwBFc5ZI) +* 2014-03, [Getting started with pk put and the Camlistore client tools](https://www.youtube.com/watch?v=DdccwBFc5ZI) diff --git a/doc/client-config.md b/doc/client-config.md index 227db6195..9a6418019 100644 --- a/doc/client-config.md +++ b/doc/client-config.md @@ -1,6 +1,6 @@ # Configuring a client -The various clients (camput, camget, pk-mount...) use a common JSON config +The various clients (pk put, camget, pk-mount...) use a common JSON config file. This page documents the configuration parameters in that file. Run `pk env clientconfig` to see the default location for that file (**$HOME/.config/camlistore/client-config.json** on linux). In the following @@ -8,7 +8,7 @@ let **$CONFIGDIR** be the location returned by `pk env configdir`. ## Generating a default config file -Run `camput init`. +Run `pk put init`. On unix, @@ -35,14 +35,14 @@ should look something like: ### Top-level keys -* `identity`: your GPG fingerprint. Run `camput init` for help on how to +* `identity`: your GPG fingerprint. Run `pk put init` for help on how to generate a new keypair. * `identitySecretRing`: Optional. If non-empty, it specifies the location of your GPG secret keyring. Defaults to **$CONFIGDIR/identity-secring.gpg**. Run - `camput init` for help on how to generate a new keypair. + `pk put init` for help on how to generate a new keypair. -* `ignoredFiles`: Optional. The list of of files that camput should ignore and +* `ignoredFiles`: Optional. The list of of files that pk put should ignore and not try to upload. ### Servers diff --git a/doc/environment-vars.md b/doc/environment-vars.md index ccc0bb577..ede734c83 100644 --- a/doc/environment-vars.md +++ b/doc/environment-vars.md @@ -43,7 +43,7 @@ valid. launched camlistored. `CAMLI_DEBUG` (bool) -: Used by camlistored and camput to enable additional commandline options. +: Used by camlistored and pk put to enable additional commandline options. Used in pkg/schema to enable additional logging. `CAMLI_DEBUG_CONFIG` (bool) @@ -174,7 +174,7 @@ files to be ignored by [pkg/client](/pkg/client) when uploading. `CAMLI_NO_FILE_DUP_SEARCH` (bool) : This will cause the search-for-exists-before-upload step to be skipped when - camput is uploading files. + pk put is uploading files. `CAMLI_PPROF_START` (string) : Filename base to write a ".cpu" and ".mem" profile out diff --git a/doc/server-config.md b/doc/server-config.md index 335d68a77..ff5a1f287 100644 --- a/doc/server-config.md +++ b/doc/server-config.md @@ -164,7 +164,7 @@ See the [serverconfig.Publish](https://perkeep.org/pkg/types/serverconfig/#Publish) type for all the configuration parameters. -One can create any permanode with camput or the UI, and set its camliRoot +One can create any permanode with pk put or the UI, and set its camliRoot attribute to the value set in the config, to use it as the root permanode for publishing. diff --git a/doc/sharing.md b/doc/sharing.md index c196bb997..fa84c3b71 100644 --- a/doc/sharing.md +++ b/doc/sharing.md @@ -44,7 +44,7 @@ in your [server config](/doc/server-config.md). Now I want to share Hi.txt with you, so I create a share blob: - camput share --transitive sha1-0e5e60f367cc8156ae48198c496b2b2ebdf5313d + pk put share --transitive sha1-0e5e60f367cc8156ae48198c496b2b2ebdf5313d I've created this, and its name is `sha1-102758fb54521cb6540d256098e7c0f1625b33e3` diff --git a/doc/status.md b/doc/status.md index 32e5c1e94..90d2cc8e3 100644 --- a/doc/status.md +++ b/doc/status.md @@ -38,14 +38,14 @@ pieces and use cases, and where they're at.

- + diff --git a/doc/terms.md b/doc/terms.md index aa9e45125..691d0272a 100644 --- a/doc/terms.md +++ b/doc/terms.md @@ -115,7 +115,7 @@ starts with { and is valid JSON in i on mutating an object (by all creating new, signed mutation schema blobs), the owner ultimately decides the policies on how the mutations are respected.

-

Example permanode blob: (as generated with camput --permanode)

+

Example permanode blob: (as generated with pk put --permanode)

{"camliVersion": 1,
   "camliSigner": "sha1-c4da9d771661563a27704b91b67989e7ea1e50b8",
diff --git a/doc/uses.md b/doc/uses.md
index 67a760c5d..86663f50f 100644
--- a/doc/uses.md
+++ b/doc/uses.md
@@ -7,7 +7,7 @@ are various stages of implementation (as of 2013-06-12).
 
 * **Filesystem backups**: easy initial use case.  Since you can easily put
 [files & directories and such](/doc/schema/) in Perkeep with
-[camput](/cmd/camput), you can use Perkeep for your backups.  Incremental
+[pk put](/cmd/pk put), you can use Perkeep for your backups.  Incremental
 backups are basically free.
 
 * **Efficient remote filesystem**: should be easy to do an aggressively caching
diff --git a/make.go b/make.go
index 41f3f3262..44a2231a9 100644
--- a/make.go
+++ b/make.go
@@ -54,7 +54,7 @@ var (
 	all            = flag.Bool("all", false, "Force rebuild of everything (go install -a)")
 	race           = flag.Bool("race", false, "Build race-detector version of binaries (they will run slowly)")
 	verbose        = flag.Bool("v", strings.Contains(os.Getenv("CAMLI_DEBUG_X"), "makego"), "Verbose mode")
-	targets        = flag.String("targets", "", "Optional comma-separated list of targets (i.e go packages) to build and install. '*' builds everything.  Empty builds defaults for this platform. Example: perkeep.org/server/camlistored,perkeep.org/cmd/camput")
+	targets        = flag.String("targets", "", "Optional comma-separated list of targets (i.e go packages) to build and install. '*' builds everything.  Empty builds defaults for this platform. Example: perkeep.org/server/camlistored,perkeep.org/cmd/pk-put")
 	quiet          = flag.Bool("quiet", false, "Don't print anything unless there's a failure.")
 	buildARCH      = flag.String("arch", runtime.GOARCH, "Architecture to build for.")
 	buildOS        = flag.String("os", runtime.GOOS, "Operating system to build for.")
@@ -107,7 +107,7 @@ func main() {
 	targs := []string{
 		"perkeep.org/dev/devcam",
 		"perkeep.org/cmd/camget",
-		"perkeep.org/cmd/camput",
+		"perkeep.org/cmd/pk-put",
 		"perkeep.org/cmd/pk",
 		"perkeep.org/cmd/pk-deploy",
 		"perkeep.org/server/camlistored",
diff --git a/misc/docker/dock.go b/misc/docker/dock.go
index 730b04894..e176c6ede 100644
--- a/misc/docker/dock.go
+++ b/misc/docker/dock.go
@@ -425,7 +425,7 @@ func packBinaries(ctxDir string) {
 	binaries := map[string]bool{
 		exeName("camlistored"): false,
 		exeName("camget"):      false,
-		exeName("camput"):      false,
+		exeName("pk-put"):      false,
 		exeName("pk"):          false,
 		exeName("publisher"):   false,
 	}
diff --git a/pkg/client/android/androidx.go b/pkg/client/android/androidx.go
index 203737c34..aa182547c 100644
--- a/pkg/client/android/androidx.go
+++ b/pkg/client/android/androidx.go
@@ -286,7 +286,7 @@ func TLSConfig() (*tls.Config, error) {
 	return cfg, nil
 }
 
-// NoteFileUploaded is a hook for camput to report that a file
+// NoteFileUploaded is a hook for pk-put to report that a file
 // was uploaded.  TODO: move this to pkg/client/android probably.
 func NoteFileUploaded(fullPath string, uploaded bool) {
 	if !IsChild() {
@@ -302,7 +302,7 @@ func NoteFileUploaded(fullPath string, uploaded bool) {
 
 // StatusReceiver is a blobserver.StatReceiver wrapper that
 // reports the full filename path and size of uploaded blobs.
-// The android app wrapping camput watches stdout for this, for progress bars.
+// The android app wrapping pk-put watches stdout for this, for progress bars.
 type StatusReceiver struct {
 	Sr   blobserver.StatReceiver
 	Path string
diff --git a/pkg/client/client.go b/pkg/client/client.go
index 60b8b65f2..343a081d6 100644
--- a/pkg/client/client.go
+++ b/pkg/client/client.go
@@ -114,8 +114,8 @@ type Client struct {
 	insecureAnyTLSCert bool
 
 	initIgnoredFilesOnce sync.Once
-	// list of files that camput should ignore.
-	// Defaults to empty, but camput init creates a config with a non
+	// list of files that pk-put should ignore.
+	// Defaults to empty, but pk-put init creates a config with a non
 	// empty list.
 	// See IsIgnoredFile for the matching rules.
 	ignoredFiles  []string
@@ -255,7 +255,7 @@ func (c *Client) NewPathClient(path string) (*Client, error) {
 // TransportConfig contains options for how HTTP requests are made.
 type TransportConfig struct {
 	// Proxy optionally specifies the Proxy for the transport. Useful with
-	// camput for debugging even localhost requests.
+	// pk-put for debugging even localhost requests.
 	Proxy   func(*http.Request) (*url.URL, error)
 	Verbose bool // Verbose enables verbose logging of HTTP requests.
 }
@@ -1487,7 +1487,7 @@ func (c *Client) UploadPlannedPermanode(ctx context.Context, key string, sigTime
 	return c.uploadString(ctx, signed, true)
 }
 
-// IsIgnoredFile returns whether the file at fullpath should be ignored by camput.
+// IsIgnoredFile returns whether the file at fullpath should be ignored by pk-put.
 // The fullpath is checked against the ignoredFiles list, trying the following rules in this order:
 // 1) star-suffix style matching (.e.g *.jpg).
 // 2) Shell pattern match as done by http://golang.org/pkg/path/filepath/#Match
diff --git a/pkg/client/config.go b/pkg/client/config.go
index 40e605f78..4df67c3b8 100644
--- a/pkg/client/config.go
+++ b/pkg/client/config.go
@@ -93,9 +93,9 @@ func (c *Client) parseConfig() {
 		if c != nil && c.isSharePrefix {
 			return
 		}
-		errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'camput init' to generate it.", configPath)
+		errMsg := fmt.Sprintf("Client configuration file %v does not exist. See 'pk-put init' to generate it.", configPath)
 		if keyID := serverKeyId(); keyID != "" {
-			hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'camput init -gpgkey %v'", keyID, osutil.UserServerConfigPath(), keyID)
+			hint := fmt.Sprintf("\nThe key id %v was found in the server config %v, so you might want:\n'pk-put init -gpgkey %v'", keyID, osutil.UserServerConfigPath(), keyID)
 			errMsg += hint
 		}
 		log.Fatal(errMsg)
@@ -220,7 +220,7 @@ func printConfigChangeHelp(conf jsonconfig.Obj) {
 		}
 	}
 	if oldConfig {
-		configChangedMsg += "Please see https://perkeep.org/doc/client-config, or use camput init to recreate a default one."
+		configChangedMsg += "Please see https://perkeep.org/doc/client-config, or use pk-put init to recreate a default one."
 		log.Print(configChangedMsg)
 	}
 }
@@ -414,7 +414,7 @@ func (c *Client) initSignerPublicKeyBlobref() {
 		configOnce.Do(parseConfig)
 		keyID = config.Identity
 		if keyID == "" {
-			log.Fatalf("No 'identity' key in JSON configuration file %q; have you run \"camput init\"?", osutil.UserClientConfigPath())
+			log.Fatalf("No 'identity' key in JSON configuration file %q; have you run \"pk-put init\"?", osutil.UserClientConfigPath())
 		}
 	}
 	keyRing := c.SecretRingFile()
diff --git a/pkg/client/upload.go b/pkg/client/upload.go
index 21ccbcd7a..a566c629d 100644
--- a/pkg/client/upload.go
+++ b/pkg/client/upload.go
@@ -447,7 +447,7 @@ type FileUploadOptions struct {
 // the server, they're not uploaded.
 //
 // Note: this method is still a work in progress, and might change to accommodate
-// the needs of camput file.
+// the needs of pk-put file.
 func (c *Client) UploadFile(ctx context.Context, filename string, contents io.Reader, opts *FileUploadOptions) (blob.Ref, error) {
 	fileMap := schema.NewFileMap(filename)
 	if opts != nil && opts.FileInfo != nil {
@@ -483,7 +483,7 @@ func (c *Client) UploadFile(ctx context.Context, filename string, contents io.Re
 	return schema.WriteFileMap(ctx, c, fileMap, contents)
 }
 
-// TODO(mpl): replace up.wholeFileDigest in camput with c.wholeRef maybe.
+// TODO(mpl): replace up.wholeFileDigest in pk-put with c.wholeRef maybe.
 
 // wholeRef returns the blob ref(s) of the regular file's contents
 // as if it were one entire blob (ignoring blob size limits).
diff --git a/pkg/cmdmain/cmdmain.go b/pkg/cmdmain/cmdmain.go
index cede5c692..ffcd13abd 100644
--- a/pkg/cmdmain/cmdmain.go
+++ b/pkg/cmdmain/cmdmain.go
@@ -24,8 +24,10 @@ import (
 	"io"
 	"log"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"sort"
+	"strings"
 	"sync"
 
 	"perkeep.org/pkg/buildinfo"
@@ -68,6 +70,9 @@ var (
 	modeCommand = make(map[string]CommandRunner)
 	modeFlags   = make(map[string]*flag.FlagSet)
 	wantHelp    = make(map[string]*bool)
+	// asNewCommand stores whether the mode should actually be run as a new
+	// independent command.
+	asNewCommand = make(map[string]bool)
 
 	// Indirections for replacement by tests
 	Stderr io.Writer = os.Stderr
@@ -93,6 +98,13 @@ type CommandRunner interface {
 	RunCommand(args []string) error
 }
 
+// ExecRunner is the type that a command mode should implement when that mode
+// just calls a new executable that will run as a new command.
+type ExecRunner interface {
+	CommandRunner
+	LookPath() (string, error)
+}
+
 type exampler interface {
 	Examples() []string
 }
@@ -101,9 +113,9 @@ type describer interface {
 	Describe() string
 }
 
-// RegisterCommand adds a mode to the list of modes for the main command.
+// RegisterMode adds a mode to the list of modes for the main command.
 // It is meant to be called in init() for each subcommand.
-func RegisterCommand(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunner) {
+func RegisterMode(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunner) {
 	if _, dup := modeCommand[mode]; dup {
 		log.Fatalf("duplicate command %q registered", mode)
 	}
@@ -117,6 +129,15 @@ func RegisterCommand(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunne
 	modeCommand[mode] = makeCmd(flags)
 }
 
+// RegisterCommand adds a mode to the list of modes for the main command, and
+// also specifies that this mode is just another executable that runs as a new
+// cmdmain command. The executable to run is determined by the LookPath implementation
+// for this mode.
+func RegisterCommand(mode string, makeCmd func(Flags *flag.FlagSet) CommandRunner) {
+	RegisterMode(mode, makeCmd)
+	asNewCommand[mode] = true
+}
+
 func hasFlags(flags *flag.FlagSet) bool {
 	any := false
 	flags.VisitAll(func(*flag.Flag) {
@@ -249,10 +270,22 @@ func Main() {
 		usage(fmt.Sprintf("Unknown mode %q", mode))
 	}
 
+	if _, ok := asNewCommand[mode]; ok {
+		runAsNewCommand(cmd, mode)
+		return
+	}
+
 	cmdFlags := modeFlags[mode]
 	cmdFlags.SetOutput(Stderr)
 	err := cmdFlags.Parse(args[1:])
 	if err != nil {
+		// We want -h to behave as -help, but without having to define another flag for
+		// it, so we handle it here.
+		// TODO(mpl): maybe even remove -help and just let them both be handled here?
+		if err == flag.ErrHelp {
+			help(mode)
+			return
+		}
 		err = ErrUsage
 	} else {
 		if *wantHelp[mode] {
@@ -285,6 +318,39 @@ func Main() {
 	}
 }
 
+// runAsNewCommand runs the executable specified by cmd's LookPath, which means
+// cmd must implement the ExecRunner interface. The executable must be a binary of
+// a program that runs Main.
+func runAsNewCommand(cmd CommandRunner, mode string) {
+	execCmd, ok := cmd.(ExecRunner)
+	if !ok {
+		panic(fmt.Sprintf("%v does not implement ExecRunner", mode))
+	}
+	cmdPath, err := execCmd.LookPath()
+	if err != nil {
+		Errorf("Error: %v\n", err)
+		Exit(2)
+	}
+	allArgs := shiftFlags(mode)
+	if err := runExec(cmdPath, allArgs, newCopyEnv()); err != nil {
+		panic(fmt.Sprintf("running %v should have ended with an os.Exit, and not leave us with that error: %v", cmdPath, err))
+	}
+}
+
+// shiftFlags prepends all the arguments (global flags) passed before the given
+// mode to the list of arguments after that mode, and returns that list.
+func shiftFlags(mode string) []string {
+	modePos := 0
+	for k, v := range os.Args {
+		if v == mode {
+			modePos = k
+			break
+		}
+	}
+	globalFlags := os.Args[1:modePos]
+	return append(globalFlags, os.Args[modePos+1:]...)
+}
+
 // Errorf prints to Stderr, regardless of FlagVerbose.
 func Errorf(format string, args ...interface{}) {
 	fmt.Fprintf(Stderr, format, args...)
@@ -304,3 +370,54 @@ func Logf(format string, v ...interface{}) {
 	}
 	logger.Printf(format, v...)
 }
+
+// sysExec is set to syscall.Exec on platforms that support it.
+var sysExec func(argv0 string, argv []string, envv []string) (err error)
+
+// runExec execs bin. If the platform doesn't support exec, it runs it and waits
+// for it to finish.
+func runExec(bin string, args []string, e *env) error {
+	if sysExec != nil {
+		sysExec(bin, append([]string{filepath.Base(bin)}, args...), e.flat())
+	}
+
+	cmd := exec.Command(bin, args...)
+	cmd.Env = e.flat()
+	cmd.Stdout = Stdout
+	cmd.Stderr = Stderr
+	return cmd.Run()
+}
+
+type env struct {
+	m     map[string]string
+	order []string
+}
+
+func (e *env) set(k, v string) {
+	_, dup := e.m[k]
+	e.m[k] = v
+	if !dup {
+		e.order = append(e.order, k)
+	}
+}
+
+func (e *env) flat() []string {
+	vv := make([]string, 0, len(e.order))
+	for _, k := range e.order {
+		if v, ok := e.m[k]; ok {
+			vv = append(vv, k+"="+v)
+		}
+	}
+	return vv
+}
+
+func newCopyEnv() *env {
+	e := &env{make(map[string]string), nil}
+	for _, kv := range os.Environ() {
+		eq := strings.Index(kv, "=")
+		if eq > 0 {
+			e.set(kv[:eq], kv[eq+1:])
+		}
+	}
+	return e
+}
diff --git a/pkg/cmdmain/exec.go b/pkg/cmdmain/exec.go
new file mode 100644
index 000000000..f974e7d5c
--- /dev/null
+++ b/pkg/cmdmain/exec.go
@@ -0,0 +1,27 @@
+// +build !windows
+
+/*
+Copyright 2018 The Perkeep Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cmdmain
+
+import (
+	"syscall"
+)
+
+func init() {
+	sysExec = syscall.Exec
+}
diff --git a/pkg/importer/twitter/twitter.go b/pkg/importer/twitter/twitter.go
index ec92073fb..4af6884e6 100644
--- a/pkg/importer/twitter/twitter.go
+++ b/pkg/importer/twitter/twitter.go
@@ -69,9 +69,9 @@ const (
 	// If set, it should be of a "file" schema blob referencing the tweets.zip
 	// file that Twitter makes available for the full archive download.
 	// The Twitter API doesn't go back forever in time, so if you started using
-	// the Perkeep importer too late, you need to "camput file tweets.zip"
+	// the Perkeep importer too late, you need to "pk-put file tweets.zip"
 	// once downloading it from Twitter, and then:
-	//   $ camput attr  twitterArchiveZipFileRef 
+	//   $ pk-put attr  twitterArchiveZipFileRef 
 	// ... and re-do an import.
 	acctAttrTweetZip = "twitterArchiveZipFileRef"
 
diff --git a/pkg/test/integration/camget_test.go b/pkg/test/integration/camget_test.go
index bb44d01e3..bcb4427e7 100644
--- a/pkg/test/integration/camget_test.go
+++ b/pkg/test/integration/camget_test.go
@@ -63,9 +63,9 @@ func TestCamgetSymlink(t *testing.T) {
 		t.Fatalf("os.Symlink(): %v", err)
 	}
 
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", srcDir))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", srcDir))
 	// TODO(mpl): rm call and delete pkg.
-	asserts.ExpectBool(t, true, out != "", "camput")
+	asserts.ExpectBool(t, true, out != "", "pk-put")
 	br := strings.Split(out, "\n")[0]
 	dstDir, err := ioutil.TempDir("", "camget-test-")
 	if err != nil {
@@ -106,7 +106,7 @@ func TestCamgetFIFO(t *testing.T) {
 
 	// Upload the fifo
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", fifo))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", fifo))
 	br := strings.Split(out, "\n")[0]
 
 	// Try and get it back
@@ -139,7 +139,7 @@ func TestCamgetSocket(t *testing.T) {
 
 	// Upload the socket
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", socket))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", socket))
 	br := strings.Split(out, "\n")[0]
 
 	// Try and get it back
@@ -189,7 +189,7 @@ func TestCamgetFile(t *testing.T) {
 	}
 
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", filename))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", filename))
 
 	br := strings.Split(out, "\n")[0]
 	_ = test.MustRunCmd(t, w.Cmd("camget", "-o", outDir, "-contents", br))
diff --git a/pkg/test/integration/camlistore_test.go b/pkg/test/integration/camlistore_test.go
index 1b2d687a6..413644789 100644
--- a/pkg/test/integration/camlistore_test.go
+++ b/pkg/test/integration/camlistore_test.go
@@ -34,7 +34,7 @@ import (
 )
 
 // Test that running:
-//   $ camput permanode
+//   $ pk-put permanode
 // ... creates and uploads a permanode, and that we can camget it back.
 func TestCamputPermanode(t *testing.T) {
 	w := test.GetWorld(t)
@@ -58,7 +58,7 @@ func TestCamputPermanode(t *testing.T) {
 func TestWebsocketQuery(t *testing.T) {
 	w := test.GetWorld(t)
 	pn := w.NewPermanode(t)
-	test.MustRunCmd(t, w.Cmd("camput", "attr", pn.String(), "tag", "foo"))
+	test.MustRunCmd(t, w.Cmd("pk-put", "attr", pn.String(), "tag", "foo"))
 
 	check := func(err error) {
 		if err != nil {
@@ -123,10 +123,10 @@ func TestWebsocketQuery(t *testing.T) {
 func TestInternalHandler(t *testing.T) {
 	w := test.GetWorld(t)
 	tests := map[string]int{
-		"/no-http-storage/":                                                    401,
-		"/no-http-handler/":                                                    401,
-		"/good-status/":                                                        200,
-		"/bs-and-maybe-also-index/camli":                                       400,
+		"/no-http-storage/":              401,
+		"/no-http-handler/":              401,
+		"/good-status/":                  200,
+		"/bs-and-maybe-also-index/camli": 400,
 		"/bs/camli/sha1-b2201302e129a4396a323cb56283cddeef11bbe8":              404,
 		"/no-http-storage/camli/sha1-b2201302e129a4396a323cb56283cddeef11bbe8": 401,
 	}
@@ -178,8 +178,8 @@ func mustWriteFile(t *testing.T, path, contents string) {
 	}
 }
 
-// Run camput in the environment it runs in under the Android app.
-// This matches how camput is used in UploadThread.java.
+// Run pk-put in the environment it runs in under the Android app.
+// This matches how pk-put is used in UploadThread.java.
 func TestAndroidCamputFile(t *testing.T) {
 	w := test.GetWorld(t)
 	// UploadThread.java sets:
@@ -193,7 +193,7 @@ func TestAndroidCamputFile(t *testing.T) {
 		"CAMPUT_ANDROID_OUTPUT=1",
 		"CAMLI_CACHE_DIR=" + cacheDir,
 	}
-	cmd := w.CmdWithEnv("camput",
+	cmd := w.CmdWithEnv("pk-put",
 		env,
 		"--server="+w.ServerBaseURL(),
 		"file",
@@ -259,10 +259,10 @@ func TestAndroidCamputFile(t *testing.T) {
 	}()
 	select {
 	case <-time.After(5 * time.Second):
-		t.Fatal("timeout waiting for camput to end")
+		t.Fatal("timeout waiting for pk-put to end")
 	case err := <-waitc:
 		if err != nil {
-			t.Errorf("camput exited uncleanly: %v", err)
+			t.Errorf("pk-put exited uncleanly: %v", err)
 		}
 	}
 }
diff --git a/pkg/test/integration/camput_test.go b/pkg/test/integration/camput_test.go
index c1810d258..a1e2c3c6e 100644
--- a/pkg/test/integration/camput_test.go
+++ b/pkg/test/integration/camput_test.go
@@ -50,7 +50,7 @@ func mkTmpFIFO(t *testing.T) (path string, cleanup func()) {
 	return
 }
 
-// Test that `camput' can upload fifos correctly.
+// Test that `pk-put' can upload fifos correctly.
 func TestCamputFIFO(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		t.SkipNow()
@@ -61,7 +61,7 @@ func TestCamputFIFO(t *testing.T) {
 
 	// Can we successfully upload a fifo?
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", fifo))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", fifo))
 
 	br := strings.Split(out, "\n")[0]
 	out = test.MustRunCmd(t, w.Cmd("camget", br))
@@ -88,7 +88,7 @@ func mkTmpSocket(t *testing.T) (path string, cleanup func()) {
 	return
 }
 
-// Test that `camput' can upload sockets correctly.
+// Test that `pk-put' can upload sockets correctly.
 func TestCamputSocket(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		t.SkipNow()
@@ -99,45 +99,45 @@ func TestCamputSocket(t *testing.T) {
 
 	// Can we successfully upload a socket?
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", socket))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", socket))
 
 	br := strings.Split(out, "\n")[0]
 	out = test.MustRunCmd(t, w.Cmd("camget", br))
 	t.Logf("Retrieved stored socket schema: %s", out)
 }
 
-// Test that camput twice on the same file only uploads once.
+// Test that pk-put twice on the same file only uploads once.
 func TestCamputUploadOnce(t *testing.T) {
 	w := test.GetWorld(t)
 
-	camputCmd := func() *exec.Cmd {
+	pkputCmd := func() *exec.Cmd {
 		// Use --contents_only because if test is run from devcam,
 		// server-config.json is going to be the one from within the fake gopath,
 		// hence with a different cTime and with a different blobRef everytime.
 		// Also, CAMLI_DEBUG is needed for --contents_only flag.
-		return w.CmdWithEnv("camput", append(os.Environ(), "CAMLI_DEBUG=1"), "file", "--contents_only=true", filepath.FromSlash("../testdata/server-config.json"))
+		return w.CmdWithEnv("pk-put", append(os.Environ(), "CAMLI_DEBUG=1"), "file", "--contents_only=true", filepath.FromSlash("../testdata/server-config.json"))
 	}
 	wantBlobRef := "sha1-381c42a63078ef49a2f1808318dbbafbb31a81d5"
-	cmd := camputCmd()
+	cmd := pkputCmd()
 	out := test.MustRunCmd(t, cmd)
 	out = strings.TrimSpace(out)
 	if out != wantBlobRef {
-		t.Fatalf("wrong camput output; wanted %v, got %v", wantBlobRef, out)
+		t.Fatalf("wrong pk-put output; wanted %v, got %v", wantBlobRef, out)
 	}
 
-	cmd = camputCmd()
+	cmd = pkputCmd()
 	var stderr bytes.Buffer
 	cmd.Stderr = &stderr
 	output, err := cmd.Output()
 	if err != nil {
-		t.Fatalf("second camput failed: %v, stdout: %v, stderr: %v", err, output, stderr.String())
+		t.Fatalf("second pk-put failed: %v, stdout: %v, stderr: %v", err, output, stderr.String())
 	}
 	out = strings.TrimSpace(string(output))
 	if out != wantBlobRef {
-		t.Fatalf("wrong 2nd camput output; wanted %v, got %v", wantBlobRef, out)
+		t.Fatalf("wrong 2nd pk-put output; wanted %v, got %v", wantBlobRef, out)
 	}
 	wantStats := `[uploadRequests=[blobs=0 bytes=0] uploads=[blobs=0 bytes=0]]`
 	if !strings.Contains(stderr.String(), wantStats) {
-		t.Fatalf("Wrong stats for 2nd camput upload; wanted %v, got %v", wantStats, out)
+		t.Fatalf("Wrong stats for 2nd pk-put upload; wanted %v, got %v", wantStats, out)
 	}
 }
diff --git a/pkg/test/integration/diskpacked_test.go b/pkg/test/integration/diskpacked_test.go
index 9c2e42c80..335b8b4e1 100644
--- a/pkg/test/integration/diskpacked_test.go
+++ b/pkg/test/integration/diskpacked_test.go
@@ -55,7 +55,7 @@ func benchmarkWrite(b *testing.B, cfg string) {
 			b.Fatalf("could not start server for config: %v\nError: %v", cfg, err)
 		}
 		b.StartTimer()
-		test.MustRunCmd(b, w.Cmd("camput", "file", testFile))
+		test.MustRunCmd(b, w.Cmd("pk-put", "file", testFile))
 		b.StopTimer()
 		w.Stop()
 	}
diff --git a/pkg/test/integration/named_test.go b/pkg/test/integration/named_test.go
index 34ce6c7f3..915c7e2b7 100644
--- a/pkg/test/integration/named_test.go
+++ b/pkg/test/integration/named_test.go
@@ -45,7 +45,7 @@ func parseJSON(s string) map[string]interface{} {
 func TestSetNamed(t *testing.T) {
 	w := test.GetWorld(t)
 	// Needed to upload the owner public key
-	runCmd(t, w, "camput", "permanode")
+	runCmd(t, w, "pk-put", "permanode")
 
 	runCmd(t, w, "pk", "named-search-set", "bar", "is:image and tag:bar")
 	gno := runCmd(t, w, "pk", "named-search-get", "bar")
@@ -58,16 +58,16 @@ func TestSetNamed(t *testing.T) {
 func TestGetNamed(t *testing.T) {
 	w := test.GetWorld(t)
 
-	putExprCmd := w.Cmd("camput", "blob", "-")
+	putExprCmd := w.Cmd("pk-put", "blob", "-")
 	putExprCmd.Stdin = strings.NewReader("is:pano")
 	ref, err := test.RunCmd(putExprCmd)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	pn := runCmd(t, w, "camput", "permanode")
-	runCmd(t, w, "camput", "attr", strings.TrimSpace(pn), "camliNamedSearch", "foo")
-	runCmd(t, w, "camput", "attr", strings.TrimSpace(pn), "camliContent", strings.TrimSpace(ref))
+	pn := runCmd(t, w, "pk-put", "permanode")
+	runCmd(t, w, "pk-put", "attr", strings.TrimSpace(pn), "camliNamedSearch", "foo")
+	runCmd(t, w, "pk-put", "attr", strings.TrimSpace(pn), "camliContent", strings.TrimSpace(ref))
 	gno := runCmd(t, w, "pk", "named-search-get", "foo")
 	gnr := parseJSON(gno)
 	if gnr["named"] != "foo" || gnr["substitute"] != "is:pano" {
@@ -79,7 +79,7 @@ func TestNamedSearch(t *testing.T) {
 	w := test.GetWorld(t)
 
 	runCmd(t, w, "pk", "named-search-set", "favorite", "tag:cats")
-	pn := runCmd(t, w, "camput", "permanode", "-title", "Felix", "-tag", "cats")
+	pn := runCmd(t, w, "pk-put", "permanode", "-title", "Felix", "-tag", "cats")
 	_, lines, err := bufio.ScanLines([]byte(pn), false)
 	if err != nil {
 		t.Fatal(err)
@@ -97,7 +97,7 @@ func TestNestedNamedSearch(t *testing.T) {
 
 	runCmd(t, w, "pk", "named-search-set", "favorite", "tag:cats")
 	runCmd(t, w, "pk", "named-search-set", "mybest", "named:favorite")
-	pn := runCmd(t, w, "camput", "permanode", "-title", "Felix", "-tag", "cats")
+	pn := runCmd(t, w, "pk-put", "permanode", "-title", "Felix", "-tag", "cats")
 	_, lines, err := bufio.ScanLines([]byte(pn), false)
 	if err != nil {
 		t.Fatal(err)
diff --git a/pkg/test/integration/non-utf8_test.go b/pkg/test/integration/non-utf8_test.go
index a8bf3d531..c25fc45e5 100644
--- a/pkg/test/integration/non-utf8_test.go
+++ b/pkg/test/integration/non-utf8_test.go
@@ -45,7 +45,7 @@ func tempDir(t *testing.T) (path string, cleanup func()) {
 	return
 }
 
-// Test that we can camput and camget a file whose name is not utf8,
+// Test that we can pk-put and camget a file whose name is not utf8,
 // that we don't panic in the process and that the results are
 // correct.
 func TestNonUTF8FileName(t *testing.T) {
@@ -69,10 +69,10 @@ func TestNonUTF8FileName(t *testing.T) {
 	fd.Close()
 
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", fd.Name()))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", fd.Name()))
 	br := strings.Split(out, "\n")[0]
 
-	// camput was a success. Can we get the file back in another directory?
+	// pk-put was a success. Can we get the file back in another directory?
 	dstDir, cleanup := tempDir(t)
 	defer cleanup()
 
@@ -84,7 +84,7 @@ func TestNonUTF8FileName(t *testing.T) {
 	}
 }
 
-// Test that we can camput and camget a symbolic link whose target is
+// Test that we can pk-put and camget a symbolic link whose target is
 // not utf8, that we do no panic in the process and that the results
 // are correct.
 func TestNonUTF8SymlinkTarget(t *testing.T) {
@@ -113,7 +113,7 @@ func TestNonUTF8SymlinkTarget(t *testing.T) {
 	}
 
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", filepath.Join(srcDir, "link")))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", filepath.Join(srcDir, "link")))
 	br := strings.Split(out, "\n")[0]
 
 	// See if we can camget it back correctly
diff --git a/pkg/test/integration/share_test.go b/pkg/test/integration/share_test.go
index 66d812241..9de064db2 100644
--- a/pkg/test/integration/share_test.go
+++ b/pkg/test/integration/share_test.go
@@ -43,10 +43,10 @@ func TestDirSharing(t *testing.T) {
 
 func share(t *testing.T, file string) {
 	w := test.GetWorld(t)
-	out := test.MustRunCmd(t, w.Cmd("camput", "file", file))
+	out := test.MustRunCmd(t, w.Cmd("pk-put", "file", file))
 	fileRef := strings.Split(out, "\n")[0]
 
-	out = test.MustRunCmd(t, w.Cmd("camput", "share", "-transitive", fileRef))
+	out = test.MustRunCmd(t, w.Cmd("pk-put", "share", "-transitive", fileRef))
 	shareRef := strings.Split(out, "\n")[0]
 
 	testDir, err := ioutil.TempDir("", "camli-share-test-")
diff --git a/pkg/test/world.go b/pkg/test/world.go
index df259aed7..80180d02f 100644
--- a/pkg/test/world.go
+++ b/pkg/test/world.go
@@ -41,7 +41,7 @@ import (
 // World defines an integration test world.
 //
 // It's used to run the actual Perkeep binaries (camlistored,
-// camput, camget, pk, etc) together in large tests, including
+// pk-put, camget, pk, etc) together in large tests, including
 // building them, finding them, and wiring them up in an isolated way.
 type World struct {
 	srcRoot  string // typically $GOPATH[0]/src/perkeep.org
@@ -249,10 +249,10 @@ func (w *World) NewPermanode(t *testing.T) blob.Ref {
 	if err := w.Ping(); err != nil {
 		t.Fatal(err)
 	}
-	out := MustRunCmd(t, w.Cmd("camput", "permanode"))
+	out := MustRunCmd(t, w.Cmd("pk-put", "permanode"))
 	br, ok := blob.Parse(strings.TrimSpace(out))
 	if !ok {
-		t.Fatalf("Expected permanode in camput stdout; got %q", out)
+		t.Fatalf("Expected permanode in pk-put stdout; got %q", out)
 	}
 	return br
 }
@@ -272,10 +272,10 @@ func (w *World) CmdWithEnv(binary string, env []string, args ...string) *exec.Cm
 	}
 	var cmd *exec.Cmd
 	switch binary {
-	case "camget", "camput", "pk", "pk-mount":
-		// TODO(mpl): lift the camput restriction when we have a unified logging mechanism
-		if binary == "camput" && !hasVerbose() {
-			// camput and pk are the only ones to have a -verbose flag through cmdmain
+	case "camget", "pk-put", "pk", "pk-mount":
+		// TODO(mpl): lift the pk-put restriction when we have a unified logging mechanism
+		if binary == "pk-put" && !hasVerbose() {
+			// pk-put and pk are the only ones to have a -verbose flag through cmdmain
 			// but pk is never used. (and pk-mount does not even have a -verbose).
 			args = append([]string{"-verbose"}, args...)
 		}
@@ -326,7 +326,7 @@ func GetWorldMaybe(t *testing.T) *World {
 }
 
 // RunCmd runs c (which is assumed to be something short-lived, like a
-// camput or camget command), capturing its stdout for return, and
+// pk-put or camget command), capturing its stdout for return, and
 // also capturing its stderr, just in the case of errors.
 // If there's an error, the return error fully describes the command and
 // all output.
diff --git a/pkg/types/camtypes/errors.go b/pkg/types/camtypes/errors.go
index 79bad27eb..ea86f1bb2 100644
--- a/pkg/types/camtypes/errors.go
+++ b/pkg/types/camtypes/errors.go
@@ -31,7 +31,7 @@ var (
 	ErrClientNoServer = addCamError("client-no-server", funcStr(func() string {
 		return fmt.Sprintf("No valid server defined. It can be set with the CAMLI_SERVER environment variable, or the --server flag, or in the \"servers\" section of %q (see https://camlistore.org/doc/client-config).", osutil.UserClientConfigPath())
 	}))
-	ErrClientNoPublicKey = addCamError("client-no-public-key", str("No public key configured: see 'camput init'."))
+	ErrClientNoPublicKey = addCamError("client-no-public-key", str("No public key configured: see 'pk-put init'."))
 )
 
 type str string
diff --git a/pkg/types/clientconfig/config.go b/pkg/types/clientconfig/config.go
index b179e6c72..d8d189900 100644
--- a/pkg/types/clientconfig/config.go
+++ b/pkg/types/clientconfig/config.go
@@ -34,7 +34,7 @@ type Config struct {
 	Servers            map[string]*Server `json:"servers"`                      // maps server alias to server config.
 	Identity           string             `json:"identity"`                     // GPG identity.
 	IdentitySecretRing string             `json:"identitySecretRing,omitempty"` // location of the secret ring file.
-	IgnoredFiles       []string           `json:"ignoredFiles,omitempty"`       // list of files that camput should ignore.
+	IgnoredFiles       []string           `json:"ignoredFiles,omitempty"`       // list of files that pk-put should ignore.
 }
 
 // Server holds the values specific to each server found in the JSON client
ItemStatusNotes
camput99%the kitchen sink tool to inject content into a blobserver. Quite good now. Also used by the Android client, as a child process.
pk put99%the kitchen sink tool to inject content into a blobserver. Quite good now. Also used by the Android client, as a child process.
camget10%tool to retrieve content from a blobserver.
pk-mountread-onlyFUSE mounting. Works on Linux and OS X.
Android Uploader90%UI is kinda ugly in spots but it works and -optionally backs up your SD card (photos, etc) to your blob server. Uses camput. +optionally backs up your SD card (photos, etc) to your blob server. Uses pk put. Can also work in "Share with Perkeep" mode, one resource at a time.