Commit 0a45ada4 authored by Stephen Whitmore's avatar Stephen Whitmore Committed by Jeromy Johnson

CLI: discoverability and consistency (#2542)

* reduces help indent from 4 to 2 spaces

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* reduces horz/vert space taken by "ipfs" cmd

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* show subcommands on shorthelp

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Drops colons at the end of cmd headings.

This makes command headings consistent with the output of 'ipfs', which
does not include colons.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* more consistent output between short-/long-help

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Puts DESCRIPTION before SUBCOMMANDS.

Users likely want to understand what a command does before worrying
about its subcommands.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Keeps ipfs cmd from outputting its subcmds twice.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes redundant synopsis from "file"

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes extra whitespace from longhelp

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Consistent spacing whether SUBCMDS or not.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes redundant SUBCMD output from ipfs object.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes redundant synopsis from "name"

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Newline after Description only if it exists.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes redundant synopsis from "bootstrap"

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes redundant synopsis from "swarm"

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes trailing newline in ping help.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Prints shorthelp on parse error.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* tiny comment fixes

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* updates README usage

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Don't include extra whitespace if no .MoreHelp

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* description improvements

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Hides the obscure 'file' subcommand.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Splits 'ipfs daemon' into Short and Long help.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes redundant synopsis from "config"

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Wraps lines to keep from going over 80.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* specify repo separately

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* s/structure/hierarchy

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* missing .

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes trailing colon from 'usage' test.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Updates sharness test error messages.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes trailing colon from 'usage' test.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Updates add-symlink to use /bin/sh.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Removes "hierarchy".

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>

* Updates "ipfs ping" synopsis.

* Updates t0040 with latest wording.

* Removes unnecessary daemon setup.

License: MIT
Signed-off-by: default avatarStephen Whitmore <noffle@ipfs.io>
parent 1b50a9d2
......@@ -122,56 +122,53 @@ or install it from source with `go get -u github.com/ipfs/ipfs-update`.
## Usage
```
USAGE:
ipfs - global p2p merkle-dag filesystem
ipfs [<flags>] <command> [<arg>] ...
BASIC COMMANDS
init Initialize ipfs local configuration
add <path> Add an object to ipfs
cat <ref> Show ipfs object data
get <ref> Download ipfs objects
ls <ref> List links from an object
refs <ref> List hashes of links from an object
DATA STRUCTURE COMMANDS
block Interact with raw blocks in the datastore
object Interact with raw dag nodes
file Interact with Unix filesystem objects
ADVANCED COMMANDS
daemon Start a long-running daemon process
mount Mount an ipfs read-only mountpoint
resolve Resolve any type of name
name Publish or resolve IPNS names
dns Resolve DNS links
pin Pin objects to local storage
repo gc Garbage collect unpinned objects
NETWORK COMMANDS
id Show info about ipfs peers
bootstrap Add or remove bootstrap peers
swarm Manage connections to the p2p network
dht Query the DHT for values or peers
ping Measure the latency of a connection
diag Print diagnostics
TOOL COMMANDS
config Manage configuration
version Show ipfs version information
update Download and apply go-ipfs updates
commands List all available commands
Use 'ipfs <command> --help' to learn more about each command.
ipfs - Global p2p merkle-dag filesystem.
ipfs [<flags>] <command> [<arg>] ...
SUBCOMMANDS
BASIC COMMANDS
init Initialize ipfs local configuration
add <path> Add a file to ipfs
cat <ref> Show ipfs object data
get <ref> Download ipfs objects
ls <ref> List links from an object
refs <ref> List hashes of links from an object
DATA STRUCTURE COMMANDS
block Interact with raw blocks in the datastore
object Interact with raw dag nodes
files Interact with objects as if they were a unix filesystem
ADVANCED COMMANDS
daemon Start a long-running daemon process
mount Mount an ipfs read-only mountpoint
resolve Resolve any type of name
name Publish or resolve IPNS names
dns Resolve DNS links
pin Pin objects to local storage
repo Manipulate an IPFS repository
NETWORK COMMANDS
id Show info about ipfs peers
bootstrap Add or remove bootstrap peers
swarm Manage connections to the p2p network
dht Query the DHT for values or peers
ping Measure the latency of a connection
diag Print diagnostics
TOOL COMMANDS
config Manage configuration
version Show ipfs version information
update Download and apply go-ipfs updates
commands List all available commands
Use 'ipfs <command> --help' to learn more about each command.
ipfs uses a repository in the local file system. By default, the repo is located
at ~/.ipfs. To change the repo location, set the $IPFS_PATH environment variable:
export IPFS_PATH=/path/to/ipfsrepo
```
## Getting Started
......
......@@ -55,7 +55,8 @@ over the network. Most applications that use IPFS will do so by
communicating with a daemon over the HTTP API. While the daemon is
running, calls to 'ipfs' commands will be sent over the network to
the daemon.
`,
LongDescription: `
The daemon will start listening on ports on the network, which are
documented in (and can be modified through) 'ipfs config Addresses'.
For example, to change the 'Gateway' port:
......
......@@ -24,8 +24,6 @@ var initCmd = &cmds.Command{
ShortDescription: `
Initializes IPFS configuration files and generates a new keypair.
IPFS_PATH environment variable
ipfs uses a repository in the local file system. By default, the repo is located
at ~/.ipfs. To change the repo location, set the $IPFS_PATH environment variable:
......
......@@ -132,7 +132,7 @@ func main() {
if invoc.cmd != nil {
// we need a newline space.
fmt.Fprintf(os.Stderr, "\n")
printMetaHelp(os.Stderr)
printHelp(false, os.Stderr)
}
os.Exit(1)
}
......
......@@ -20,7 +20,7 @@ const (
whitespace = "\r\n\t "
indentStr = " "
indentStr = " "
)
type helpFields struct {
......@@ -74,38 +74,38 @@ func (f *helpFields) IndentAll() {
const usageFormat = "{{if .Usage}}{{.Usage}}{{else}}{{.Path}}{{if .ArgUsage}} {{.ArgUsage}}{{end}} - {{.Tagline}}{{end}}"
const longHelpFormat = `
const longHelpFormat = `USAGE
{{.Indent}}{{template "usage" .}}
{{if .Arguments}}ARGUMENTS:
{{if .Arguments}}ARGUMENTS
{{.Arguments}}
{{end}}{{if .Options}}OPTIONS:
{{end}}{{if .Options}}OPTIONS
{{.Options}}
{{end}}{{if .Subcommands}}SUBCOMMANDS:
{{end}}{{if .Description}}DESCRIPTION
{{.Description}}
{{end}}{{if .Subcommands}}SUBCOMMANDS
{{.Subcommands}}
{{.Indent}}Use '{{.Path}} <subcmd> --help' for more information about each command.
{{end}}{{if .Description}}DESCRIPTION:
{{.Description}}
{{end}}
`
const shortHelpFormat = `USAGE:
const shortHelpFormat = `USAGE
{{.Indent}}{{template "usage" .}}
{{if .Synopsis}}
{{.Synopsis}}
{{end}}{{if .Description}}
{{.Description}}
{{end}}
{{if .MoreHelp}}Use '{{.Path}} --help' for more information about this command.
{{end}}{{if .Subcommands}}
SUBCOMMANDS
{{.Subcommands}}
{{end}}{{if .MoreHelp}}
Use '{{.Path}} --help' for more information about this command.
{{end}}
`
......@@ -119,7 +119,7 @@ func init() {
shortHelpTemplate = template.Must(usageTemplate.New("shortHelp").Parse(shortHelpFormat))
}
// LongHelp returns a formatted CLI helptext string, generated for the given command
// LongHelp writes a formatted CLI helptext string to a Writer for the given command
func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error {
cmd, err := root.Get(path)
if err != nil {
......@@ -169,7 +169,7 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer)
return longHelpTemplate.Execute(out, fields)
}
// ShortHelp returns a formatted CLI helptext string, generated for the given command
// ShortHelp writes a formatted CLI helptext string to a Writer for the given command
func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error {
cmd, err := root.Get(path)
if err != nil {
......@@ -193,10 +193,16 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer
Tagline: cmd.Helptext.Tagline,
Synopsis: cmd.Helptext.Synopsis,
Description: cmd.Helptext.ShortDescription,
Subcommands: cmd.Helptext.Subcommands,
Usage: cmd.Helptext.Usage,
MoreHelp: (cmd != root),
}
// autogen fields that are empty
if len(fields.Subcommands) == 0 {
fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n")
}
// trim the extra newlines (see TrimNewlines doc)
fields.TrimNewlines()
......
......@@ -30,11 +30,9 @@ const (
var AddCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Add a file to ipfs.",
Tagline: "Add a file or directory to ipfs.",
ShortDescription: `
Adds contents of <path> to ipfs. Use -r to add directories.
Note that directories are added recursively, to form the ipfs
MerkleDAG.
Adds contents of <path> to ipfs. Use -r to add directories (recursively).
`,
LongDescription: `
Adds contents of <path> to ipfs. Use -r to add directories.
......
......@@ -22,11 +22,6 @@ var peerOptionDesc = "A peer to add to the bootstrap list (in the format '<multi
var BootstrapCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Show or edit the list of bootstrap peers.",
Synopsis: `
ipfs bootstrap list - Show peers in the bootstrap list
ipfs bootstrap add <peer>... - Add peers to the bootstrap list
ipfs bootstrap rm <peer>... - Removes peers from the bootstrap list
`,
ShortDescription: `
Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'.
` + bootstrapSecurityWarning,
......
......@@ -25,13 +25,6 @@ type ConfigField struct {
var ConfigCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Get and set IPFS config values.",
Synopsis: `
ipfs config <key> - Get value of <key>
ipfs config <key> <value> - Set value of <key> to <value>
ipfs config show - Show config file
ipfs config edit - Edit config file in $EDITOR
ipfs config replace <file> - Replaces the config file with <file>
`,
ShortDescription: `
'ipfs config' controls configuration variables. It works like 'git config'.
The configuration values are stored in a config file inside your IPFS
......
......@@ -10,10 +10,6 @@ type IpnsEntry struct {
var NameCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "IPFS namespace (IPNS) tool.",
Synopsis: `
ipfs name publish [<name>] <ipfs-path> - Publish an object to IPNS
ipfs name resolve [<name>] - Gets the value currently published at an IPNS name
`,
ShortDescription: `
IPNS is a PKI namespace, where names are the hashes of public keys, and
the private key enables publishing new (signed) values. In both publish
......
......@@ -47,16 +47,6 @@ var ObjectCmd = &cmds.Command{
ShortDescription: `
'ipfs object' is a plumbing command used to manipulate DAG objects
directly.`,
Synopsis: `
ipfs object data <key> - Outputs raw bytes in an object
ipfs object diff <key1> <key2> - Diffs two given objects
ipfs object get <key> - Get the DAG node named by <key>
ipfs object links <key> - Outputs links pointed to by object
ipfs object new <template> - Create new ipfs objects
ipfs object patch <args> - Create new object from old ones
ipfs object put <data> - Stores input, outputs its key
ipfs object stat <key> - Outputs statistics of object
`,
},
Subcommands: map[string]*cmds.Command{
......
......@@ -94,7 +94,7 @@ var addPinCmd = &cmds.Command{
var rmPinCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Removes the pinned object from local storage. (By default, recursively. Use -r=false for direct pins).",
Tagline: "Removes the pinned object from local storage.",
ShortDescription: `
Removes the pin from the given object allowing it to be garbage
collected if needed. (By default, recursively. Use -r=false for direct pins)
......
......@@ -27,10 +27,8 @@ type PingResult struct {
var PingCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Send echo request packets to IPFS hosts.",
Synopsis: `
Send pings to a peer using the routing system to discover its address
`,
Tagline: "Send echo request packets to IPFS hosts.",
Synopsis: "Send pings to a peer to verify connectivity and determine latency.",
ShortDescription: `
'ipfs ping' is a tool to test sending data to other nodes. It finds nodes
via the routing system, sends pings, waits for pongs, and prints out round-
......
......@@ -35,7 +35,8 @@ var RefsCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Lists links (references) from an object.",
ShortDescription: `
Displays the link hashes an IPFS or IPNS object(s) contains, with the following format:
Lists the hashes of all the links an IPFS or IPNS object(s) contains,
with the following format:
<link base58 hash>
......
......@@ -23,55 +23,49 @@ var Root = &cmds.Command{
Synopsis: `
ipfs [<flags>] <command> [<arg>] ...
`,
ShortDescription: `
Subcommands: `
BASIC COMMANDS
init Initialize ipfs local configuration
add <path> Add a file to ipfs
cat <ref> Show ipfs object data
get <ref> Download ipfs objects
ls <ref> List links from an object
refs <ref> List hashes of links from an object
init Initialize ipfs local configuration
add <path> Add a file to ipfs
cat <ref> Show ipfs object data
get <ref> Download ipfs objects
ls <ref> List links from an object
refs <ref> List hashes of links from an object
DATA STRUCTURE COMMANDS
block Interact with raw blocks in the datastore
object Interact with raw dag nodes
files Interact with objects as if they were a unix filesystem
file Interact with Unix filesystem objects
block Interact with raw blocks in the datastore
object Interact with raw dag nodes
files Interact with objects as if they were a unix filesystem
ADVANCED COMMANDS
daemon Start a long-running daemon process
mount Mount an ipfs read-only mountpoint
resolve Resolve any type of name
name Publish or resolve IPNS names
dns Resolve DNS links
pin Pin objects to local storage
repo gc Garbage collect unpinned objects
daemon Start a long-running daemon process
mount Mount an ipfs read-only mountpoint
resolve Resolve any type of name
name Publish or resolve IPNS names
dns Resolve DNS links
pin Pin objects to local storage
repo Manipulate the IPFS repository
NETWORK COMMANDS
id Show info about ipfs peers
bootstrap Add or remove bootstrap peers
swarm Manage connections to the p2p network
dht Query the DHT for values or peers
ping Measure the latency of a connection
diag Print diagnostics
id Show info about ipfs peers
bootstrap Add or remove bootstrap peers
swarm Manage connections to the p2p network
dht Query the DHT for values or peers
ping Measure the latency of a connection
diag Print diagnostics
TOOL COMMANDS
config Manage configuration
version Show ipfs version information
update Download and apply go-ipfs updates
commands List all available commands
config Manage configuration
version Show ipfs version information
update Download and apply go-ipfs updates
commands List all available commands
Use 'ipfs <command> --help' to learn more about each command.
ipfs uses a repository in the local file system. By default, the repo is located
at ~/.ipfs. To change the repo location, set the $IPFS_PATH environment variable:
export IPFS_PATH=/path/to/ipfsrepo
export IPFS_PATH=/path/to/ipfsrepo
`,
},
Options: []cmds.Option{
......
......@@ -28,13 +28,6 @@ type addrMap struct {
var SwarmCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Swarm inspection tool.",
Synopsis: `
ipfs swarm addrs - List known addresses. Useful for debugging.
ipfs swarm connect <address> - Open connection to a given address
ipfs swarm disconnect <address> - Close connection to a given address
ipfs swarm filters - Manipulate filters addresses
ipfs swarm peers - List peers with open connections
`,
ShortDescription: `
'ipfs swarm' is a tool to manipulate the network swarm. The swarm is the
component that opens, listens for, and maintains connections to other
......
......@@ -9,9 +9,6 @@ var UnixFSCmd = &cmds.Command{
'ipfs file' provides a familiar interface to file systems represented
by IPFS objects, which hides IPFS implementation details like layout
objects (e.g. fanout and chunking).
`,
Synopsis: `
ipfs file ls <path>... - List directory contents for <path>...
`,
},
......
......@@ -26,7 +26,7 @@ test_expect_success "ipfs help succeeds" '
'
test_expect_success "ipfs help output looks good" '
egrep -i "^Usage:" help.txt >/dev/null &&
egrep -i "^Usage" help.txt >/dev/null &&
egrep "ipfs .* <command>" help.txt >/dev/null ||
test_fsh cat help.txt
'
......
......@@ -8,8 +8,15 @@ test_description="Test add and cat commands"
. lib/test-lib.sh
client_err() {
printf "$@\n\nUse 'ipfs add --help' for information about this command\n"
client_err_add() {
printf "$@\n\n"
echo 'USAGE
ipfs add <path>... - Add a file or directory to ipfs.
Adds contents of <path> to ipfs. Use -r to add directories (recursively).
Use '"'"'ipfs add --help'"'"' for more information about this command.
'
}
test_add_cat_file() {
......@@ -156,7 +163,7 @@ test_add_named_pipe() {
test_expect_success "useful error message when adding a named pipe" '
mkfifo named-pipe &&
test_expect_code 1 ipfs add named-pipe 2>actual &&
client_err "Error: Unrecognized file type for named-pipe: $(generic_stat named-pipe)" >expected &&
client_err_add "Error: Unrecognized file type for named-pipe: $(generic_stat named-pipe)" >expected &&
rm named-pipe &&
test_cmp expected actual
'
......
#!/bin/bash
#!/bin/sh
#
# Copyright (c) 2014 Christian Couder
# MIT Licensed; see the LICENSE file in this repository.
......
......@@ -69,7 +69,7 @@ test_expect_success "ipfs help succeeds" '
'
test_expect_success "ipfs help output looks good" '
egrep -i "^Usage:" help.txt >/dev/null &&
egrep -i "^Usage" help.txt >/dev/null &&
egrep "ipfs .* <command>" help.txt >/dev/null ||
test_fsh cat help.txt
'
......
......@@ -9,7 +9,6 @@ test_description="test http requests made by cli"
. lib/test-lib.sh
test_init_ipfs
test_launch_ipfs_daemon
test_expect_success "can make http request against nc server" '
go-sleep 0.5s | nc -l 5005 > nc_out &
......@@ -28,6 +27,4 @@ test_expect_success "api flag does not appear in request" '
test_expect_code 1 grep "api=/ip4" nc_out
'
test_kill_ipfs_daemon
test_done
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment