提交 29c6a53e 作者: Juan Batiz-Benet

Merge pull request #1139 from AtnNn/flags

Improve command line parsing
...@@ -93,6 +93,12 @@ func main() { ...@@ -93,6 +93,12 @@ func main() {
fmt.Fprintf(w, "Use 'ipfs %s --help' for information about this command\n", cmdPath) fmt.Fprintf(w, "Use 'ipfs %s --help' for information about this command\n", cmdPath)
} }
// Handle `ipfs help'
if len(os.Args) == 2 && os.Args[1] == "help" {
printHelp(false, os.Stdout)
os.Exit(0)
}
// parse the commandline into a command invocation // parse the commandline into a command invocation
parseErr := invoc.Parse(ctx, os.Args[1:]) parseErr := invoc.Parse(ctx, os.Args[1:])
...@@ -110,13 +116,6 @@ func main() { ...@@ -110,13 +116,6 @@ func main() {
} }
} }
// here we handle the cases where
// - commands with no Run func are invoked directly.
// - the main command is invoked.
if invoc.cmd == nil || invoc.cmd.Run == nil {
printHelp(false, os.Stdout)
os.Exit(0)
}
// ok now handle parse error (which means cli input was wrong, // ok now handle parse error (which means cli input was wrong,
// e.g. incorrect number of args, or nonexistent subcommand) // e.g. incorrect number of args, or nonexistent subcommand)
...@@ -132,6 +131,14 @@ func main() { ...@@ -132,6 +131,14 @@ func main() {
os.Exit(1) os.Exit(1)
} }
// here we handle the cases where
// - commands with no Run func are invoked directly.
// - the main command is invoked.
if invoc.cmd == nil || invoc.cmd.Run == nil {
printHelp(false, os.Stdout)
os.Exit(0)
}
// ok, finally, run the command invocation. // ok, finally, run the command invocation.
intrh, ctx := invoc.SetupInterruptHandler(ctx) intrh, ctx := invoc.SetupInterruptHandler(ctx)
defer intrh.Close() defer intrh.Close()
......
...@@ -14,7 +14,8 @@ const ( ...@@ -14,7 +14,8 @@ const (
requiredArg = "<%v>" requiredArg = "<%v>"
optionalArg = "[<%v>]" optionalArg = "[<%v>]"
variadicArg = "%v..." variadicArg = "%v..."
optionFlag = "-%v" shortFlag = "-%v"
longFlag = "--%v"
optionType = "(%v)" optionType = "(%v)"
whitespace = "\r\n\t " whitespace = "\r\n\t "
...@@ -219,6 +220,14 @@ func argumentText(cmd *cmds.Command) []string { ...@@ -219,6 +220,14 @@ func argumentText(cmd *cmds.Command) []string {
return lines return lines
} }
func optionFlag(flag string) string {
if len(flag) == 1 {
return fmt.Sprintf(shortFlag, flag)
} else {
return fmt.Sprintf(longFlag, flag)
}
}
func optionText(cmd ...*cmds.Command) []string { func optionText(cmd ...*cmds.Command) []string {
// get a slice of the options we want to list out // get a slice of the options we want to list out
options := make([]cmds.Option, 0) options := make([]cmds.Option, 0)
...@@ -241,7 +250,7 @@ func optionText(cmd ...*cmds.Command) []string { ...@@ -241,7 +250,7 @@ func optionText(cmd ...*cmds.Command) []string {
names := sortByLength(opt.Names()) names := sortByLength(opt.Names())
if len(names) >= j+1 { if len(names) >= j+1 {
lines[i] += fmt.Sprintf(optionFlag, names[j]) lines[i] += optionFlag(names[j])
} }
if len(names) > j+1 { if len(names) > j+1 {
lines[i] += ", " lines[i] += ", "
......
...@@ -2,7 +2,6 @@ package cli ...@@ -2,7 +2,6 @@ package cli
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
...@@ -13,20 +12,12 @@ import ( ...@@ -13,20 +12,12 @@ import (
u "github.com/ipfs/go-ipfs/util" u "github.com/ipfs/go-ipfs/util"
) )
// ErrInvalidSubcmd signals when the parse error is not found
var ErrInvalidSubcmd = errors.New("subcommand not found")
// Parse parses the input commandline string (cmd, flags, and args). // Parse parses the input commandline string (cmd, flags, and args).
// returns the corresponding command Request object. // returns the corresponding command Request object.
func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) { func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) {
path, input, cmd := parsePath(input, root) path, opts, stringVals, cmd, err := parseOpts(input, root)
if len(path) == 0 {
return nil, nil, path, ErrInvalidSubcmd
}
opts, stringVals, err := parseOptions(input)
if err != nil { if err != nil {
return nil, cmd, path, err return nil, nil, path, err
} }
optDefs, err := root.GetOptions(path) optDefs, err := root.GetOptions(path)
...@@ -34,14 +25,6 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c ...@@ -34,14 +25,6 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
return nil, cmd, path, err return nil, cmd, path, err
} }
// check to make sure there aren't any undefined options
for k := range opts {
if _, found := optDefs[k]; !found {
err = fmt.Errorf("Unrecognized option: -%s", k)
return nil, cmd, path, err
}
}
req, err := cmds.NewRequest(path, opts, nil, nil, cmd, optDefs) req, err := cmds.NewRequest(path, opts, nil, nil, cmd, optDefs)
if err != nil { if err != nil {
return nil, cmd, path, err return nil, cmd, path, err
...@@ -75,67 +58,142 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c ...@@ -75,67 +58,142 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
return req, cmd, path, nil return req, cmd, path, nil
} }
// parsePath separates the command path and the opts and args from a command string // Parse a command line made up of sub-commands, short arguments, long arguments and positional arguments
// returns command path slice, rest slice, and the corresponding *cmd.Command func parseOpts(args []string, root *cmds.Command) (
func parsePath(input []string, root *cmds.Command) ([]string, []string, *cmds.Command) { path []string,
cmd := root opts map[string]interface{},
path := make([]string, 0, len(input)) stringVals []string,
input2 := make([]string, 0, len(input)) cmd *cmds.Command,
err error,
) {
path = make([]string, 0, len(args))
stringVals = make([]string, 0, len(args))
optDefs := map[string]cmds.Option{}
opts = map[string]interface{}{}
cmd = root
// parseFlag checks that a flag is valid and saves it into opts
// Returns true if the optional second argument is used
parseFlag := func(name string, arg *string, mustUse bool) (bool, error) {
if _, ok := opts[name]; ok {
return false, fmt.Errorf("Duplicate values for option '%s'", name)
}
for i, blob := range input { optDef, found := optDefs[name]
if strings.HasPrefix(blob, "-") { if !found {
input2 = append(input2, blob) err = fmt.Errorf("Unrecognized option '%s'", name)
continue return false, err
} }
sub := cmd.Subcommand(blob) if optDef.Type() == cmds.Bool {
if sub == nil { if mustUse {
input2 = append(input2, input[i:]...) return false, fmt.Errorf("Option '%s' takes no arguments, but was passed '%s'", name, *arg)
break }
opts[name] = ""
return false, nil
} else {
if arg == nil {
return true, fmt.Errorf("Missing argument for option '%s'", name)
}
opts[name] = *arg
return true, nil
} }
cmd = sub
path = append(path, blob)
} }
return path, input2, cmd optDefs, err = root.GetOptions(path)
} if err != nil {
return
// parseOptions parses the raw string values of the given options }
// returns the parsed options as strings, along with the CLI args
func parseOptions(input []string) (map[string]interface{}, []string, error) {
opts := make(map[string]interface{})
args := []string{}
for i := 0; i < len(input); i++ {
blob := input[i]
if strings.HasPrefix(blob, "-") { consumed := false
name := blob[1:] for i, arg := range args {
value := "" switch {
case consumed:
// arg was already consumed by the preceding flag
consumed = false
continue
// support single and double dash case arg == "--":
if strings.HasPrefix(name, "-") { // treat all remaining arguments as positional arguments
name = name[1:] stringVals = append(stringVals, args[i+1:]...)
return
case strings.HasPrefix(arg, "--"):
// arg is a long flag, with an optional argument specified
// using `=' or in args[i+1]
var slurped bool
var next *string
split := strings.SplitN(arg, "=", 2)
if len(split) == 2 {
slurped = false
arg = split[0]
next = &split[1]
} else {
slurped = true
if i+1 < len(args) {
next = &args[i+1]
} else {
next = nil
}
} }
consumed, err = parseFlag(arg[2:], next, len(split) == 2)
if strings.Contains(name, "=") { if err != nil {
split := strings.SplitN(name, "=", 2) return
name = split[0]
value = split[1]
} }
if !slurped {
if _, ok := opts[name]; ok { consumed = false
return nil, nil, fmt.Errorf("Duplicate values for option '%s'", name)
} }
opts[name] = value case strings.HasPrefix(arg, "-") && arg != "-":
// args is one or more flags in short form, followed by an optional argument
// all flags except the last one have type bool
for arg = arg[1:]; len(arg) != 0; arg = arg[1:] {
var rest *string
var slurped bool
mustUse := false
if len(arg) > 1 {
slurped = false
str := arg[1:]
if len(str) > 0 && str[0] == '=' {
str = str[1:]
mustUse = true
}
rest = &str
} else {
slurped = true
if i+1 < len(args) {
rest = &args[i+1]
} else {
rest = nil
}
}
var end bool
end, err = parseFlag(arg[0:1], rest, mustUse)
if err != nil {
return
}
if end {
consumed = slurped
break
}
}
} else { default:
args = append(args, blob) // arg is a sub-command or a positional argument
sub := cmd.Subcommand(arg)
if sub != nil {
cmd = sub
path = append(path, arg)
optDefs, err = root.GetOptions(path)
if err != nil {
return
}
} else {
stringVals = append(stringVals, arg)
}
} }
} }
return
return opts, args, nil
} }
func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool) ([]string, []files.File, error) { func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursive bool) ([]string, []files.File, error) {
...@@ -171,7 +229,7 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi ...@@ -171,7 +229,7 @@ func parseArgs(inputs []string, stdin *os.File, argDefs []cmds.Argument, recursi
// and the last arg definition is not variadic (or there are no definitions), return an error // and the last arg definition is not variadic (or there are no definitions), return an error
notVariadic := len(argDefs) == 0 || !argDefs[len(argDefs)-1].Variadic notVariadic := len(argDefs) == 0 || !argDefs[len(argDefs)-1].Variadic
if notVariadic && numInputs > len(argDefs) { if notVariadic && numInputs > len(argDefs) {
return nil, nil, fmt.Errorf("Expected %v arguments, got %v", len(argDefs), numInputs) return nil, nil, fmt.Errorf("Expected %v arguments, got %v: %v", len(argDefs), numInputs, inputs)
} }
stringArgs := make([]string, 0, numInputs) stringArgs := make([]string, 0, numInputs)
......
package cli package cli
import ( import (
//"fmt" "strings"
"testing" "testing"
"github.com/ipfs/go-ipfs/commands" "github.com/ipfs/go-ipfs/commands"
...@@ -11,43 +11,79 @@ func TestOptionParsing(t *testing.T) { ...@@ -11,43 +11,79 @@ func TestOptionParsing(t *testing.T) {
subCmd := &commands.Command{} subCmd := &commands.Command{}
cmd := &commands.Command{ cmd := &commands.Command{
Options: []commands.Option{ Options: []commands.Option{
commands.StringOption("b", "some option"), commands.StringOption("string", "s", "a string"),
commands.BoolOption("bool", "b", "a bool"),
}, },
Subcommands: map[string]*commands.Command{ Subcommands: map[string]*commands.Command{
"test": subCmd, "test": subCmd,
}, },
} }
opts, input, err := parseOptions([]string{"--beep", "-boop=lol", "test2", "-c", "beep", "--foo=5"}) type kvs map[string]interface{}
/*for k, v := range opts { type words []string
fmt.Printf("%s: %s\n", k, v)
} sameWords := func(a words, b words) bool {
fmt.Printf("%s\n", input)*/ for i, w := range a {
if err != nil { if w != b[i] {
t.Error("Should have passed") return false
} }
if len(opts) != 4 || opts["beep"] != "" || opts["boop"] != "lol" || opts["c"] != "" || opts["foo"] != "5" { }
t.Errorf("Returned options were defferent than expected: %v", opts) return true
}
if len(input) != 2 || input[0] != "test2" || input[1] != "beep" {
t.Errorf("Returned input was different than expected: %v", input)
} }
_, _, err = parseOptions([]string{"-beep=1", "-boop=2", "-beep=3"}) sameKVs := func(a kvs, b kvs) bool {
if err == nil { if len(a) != len(b) {
t.Error("Should have failed (duplicate option name)") return false
}
for k, v := range a {
if v != b[k] {
return false
}
}
return true
} }
path, args, sub := parsePath([]string{"test", "beep", "boop"}, cmd) testHelper := func(args string, expectedOpts kvs, expectedWords words, expectErr bool) {
if len(path) != 1 || path[0] != "test" { _, opts, input, _, err := parseOpts(strings.Split(args, " "), cmd)
t.Errorf("Returned path was defferent than expected: %v", path) if expectErr {
if err == nil {
t.Errorf("Command line '%v' parsing should have failed", args)
}
} else if err != nil {
t.Errorf("Command line '%v' failed to parse: %v", args, err)
} else if !sameWords(input, expectedWords) || !sameKVs(opts, expectedOpts) {
t.Errorf("Command line '%v':\n parsed as %v %v\n instead of %v %v",
args, opts, input, expectedOpts, expectedWords)
}
} }
if len(args) != 2 || args[0] != "beep" || args[1] != "boop" {
t.Errorf("Returned args were different than expected: %v", args) testFail := func(args string) {
testHelper(args, kvs{}, words{}, true)
} }
if sub != subCmd {
t.Errorf("Returned command was different than expected") test := func(args string, expectedOpts kvs, expectedWords words) {
testHelper(args, expectedOpts, expectedWords, false)
} }
test("-", kvs{}, words{"-"})
testFail("-b -b")
test("beep boop", kvs{}, words{"beep", "boop"})
test("test beep boop", kvs{}, words{"beep", "boop"})
testFail("-s")
test("-s foo", kvs{"s": "foo"}, words{})
test("-sfoo", kvs{"s": "foo"}, words{})
test("-s=foo", kvs{"s": "foo"}, words{})
test("-b", kvs{"b": ""}, words{})
test("-bs foo", kvs{"b": "", "s": "foo"}, words{})
test("-sb", kvs{"s": "b"}, words{})
test("-b foo", kvs{"b": ""}, words{"foo"})
test("--bool foo", kvs{"bool": ""}, words{"foo"})
testFail("--bool=foo")
testFail("--string")
test("--string foo", kvs{"string": "foo"}, words{})
test("--string=foo", kvs{"string": "foo"}, words{})
test("-- -b", kvs{}, words{"-b"})
test("foo -b", kvs{"b": ""}, words{"foo"})
} }
func TestArgumentParsing(t *testing.T) { func TestArgumentParsing(t *testing.T) {
......
...@@ -40,7 +40,7 @@ Please run the ipfs migration tool before continuing. ...@@ -40,7 +40,7 @@ Please run the ipfs migration tool before continuing.
` + migrationInstructions ` + migrationInstructions
var ( var (
ErrNoRepo = errors.New("no ipfs repo found. please run: ipfs init") ErrNoRepo = func (path string) error { return fmt.Errorf("no ipfs repo found in '%s'. please run: ipfs init ", path) }
ErrNoVersion = errors.New("no version file found, please run 0-to-1 migration tool.\n" + migrationInstructions) ErrNoVersion = errors.New("no version file found, please run 0-to-1 migration tool.\n" + migrationInstructions)
ErrOldRepo = errors.New("ipfs repo found in old '~/.go-ipfs' location, please run migration tool.\n" + migrationInstructions) ErrOldRepo = errors.New("ipfs repo found in old '~/.go-ipfs' location, please run migration tool.\n" + migrationInstructions)
) )
...@@ -172,7 +172,7 @@ func checkInitialized(path string) error { ...@@ -172,7 +172,7 @@ func checkInitialized(path string) error {
if isInitializedUnsynced(alt) { if isInitializedUnsynced(alt) {
return ErrOldRepo return ErrOldRepo
} }
return ErrNoRepo return ErrNoRepo(path)
} }
return nil return nil
} }
......
...@@ -105,7 +105,7 @@ test_wait_open_tcp_port_10_sec() { ...@@ -105,7 +105,7 @@ test_wait_open_tcp_port_10_sec() {
# was setting really weird things and am not sure why. # was setting really weird things and am not sure why.
test_config_set() { test_config_set() {
# grab flags (like -bool in "ipfs config -bool") # grab flags (like --bool in "ipfs config --bool")
test_cfg_flags="" # unset in case. test_cfg_flags="" # unset in case.
test "$#" = 3 && { test_cfg_flags=$1; shift; } test "$#" = 3 && { test_cfg_flags=$1; shift; }
...@@ -184,7 +184,7 @@ test_config_ipfs_gateway_writable() { ...@@ -184,7 +184,7 @@ test_config_ipfs_gateway_writable() {
test_config_ipfs_gateway_readonly $1 test_config_ipfs_gateway_readonly $1
test_expect_success "prepare config -- gateway writable" ' test_expect_success "prepare config -- gateway writable" '
test_config_set -bool Gateway.Writable true || test_config_set --bool Gateway.Writable true ||
test_fsh cat "\"$IPFS_PATH/config\"" test_fsh cat "\"$IPFS_PATH/config\""
' '
} }
......
...@@ -7,7 +7,7 @@ test_description="Test config command" ...@@ -7,7 +7,7 @@ test_description="Test config command"
# we use a function so that we can run it both offline + online # we use a function so that we can run it both offline + online
test_config_cmd_set() { test_config_cmd_set() {
# flags (like -bool in "ipfs config -bool") # flags (like --bool in "ipfs config --bool")
cfg_flags="" # unset in case. cfg_flags="" # unset in case.
test "$#" = 3 && { cfg_flags=$1; shift; } test "$#" = 3 && { cfg_flags=$1; shift; }
...@@ -41,8 +41,8 @@ test_config_cmd() { ...@@ -41,8 +41,8 @@ test_config_cmd() {
test_config_cmd_set "beep" "boop" test_config_cmd_set "beep" "boop"
test_config_cmd_set "beep1" "boop2" test_config_cmd_set "beep1" "boop2"
test_config_cmd_set "beep1" "boop2" test_config_cmd_set "beep1" "boop2"
test_config_cmd_set "-bool" "beep2" "true" test_config_cmd_set "--bool" "beep2" "true"
test_config_cmd_set "-bool" "beep2" "false" test_config_cmd_set "--bool" "beep2" "false"
} }
......
...@@ -17,7 +17,7 @@ test_expect_success "'ipfs add afile' succeeds" ' ...@@ -17,7 +17,7 @@ test_expect_success "'ipfs add afile' succeeds" '
' '
test_expect_success "added file was pinned" ' test_expect_success "added file was pinned" '
ipfs pin ls -type=recursive >actual && ipfs pin ls --type=recursive >actual &&
grep "$HASH" actual grep "$HASH" actual
' '
...@@ -49,7 +49,7 @@ test_expect_success "file no longer pinned" ' ...@@ -49,7 +49,7 @@ test_expect_success "file no longer pinned" '
echo "$HASH_WELCOME_DOCS" >expected2 && echo "$HASH_WELCOME_DOCS" >expected2 &&
ipfs refs -r "$HASH_WELCOME_DOCS" >>expected2 && ipfs refs -r "$HASH_WELCOME_DOCS" >>expected2 &&
echo QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn >> expected2 && echo QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn >> expected2 &&
ipfs pin ls -type=recursive >actual2 && ipfs pin ls --type=recursive >actual2 &&
test_sort_cmp expected2 actual2 test_sort_cmp expected2 actual2
' '
...@@ -102,10 +102,10 @@ test_expect_success "adding multiblock random file succeeds" ' ...@@ -102,10 +102,10 @@ test_expect_success "adding multiblock random file succeeds" '
MBLOCKHASH=`ipfs add -q multiblock` MBLOCKHASH=`ipfs add -q multiblock`
' '
test_expect_success "'ipfs pin ls -type=indirect' is correct" ' test_expect_success "'ipfs pin ls --type=indirect' is correct" '
ipfs refs "$MBLOCKHASH" >refsout && ipfs refs "$MBLOCKHASH" >refsout &&
ipfs refs -r "$HASH_WELCOME_DOCS" >>refsout && ipfs refs -r "$HASH_WELCOME_DOCS" >>refsout &&
ipfs pin ls -type=indirect >indirectpins && ipfs pin ls --type=indirect >indirectpins &&
test_sort_cmp refsout indirectpins test_sort_cmp refsout indirectpins
' '
...@@ -121,27 +121,27 @@ test_expect_success "pin something directly" ' ...@@ -121,27 +121,27 @@ test_expect_success "pin something directly" '
test_cmp expected10 actual10 test_cmp expected10 actual10
' '
test_expect_success "'ipfs pin ls -type=direct' is correct" ' test_expect_success "'ipfs pin ls --type=direct' is correct" '
echo "$DIRECTPIN" >directpinexpected && echo "$DIRECTPIN" >directpinexpected &&
ipfs pin ls -type=direct >directpinout && ipfs pin ls --type=direct >directpinout &&
test_sort_cmp directpinexpected directpinout test_sort_cmp directpinexpected directpinout
' '
test_expect_success "'ipfs pin ls -type=recursive' is correct" ' test_expect_success "'ipfs pin ls --type=recursive' is correct" '
echo "$MBLOCKHASH" >rp_expected && echo "$MBLOCKHASH" >rp_expected &&
echo "$HASH_WELCOME_DOCS" >>rp_expected && echo "$HASH_WELCOME_DOCS" >>rp_expected &&
echo QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn >>rp_expected && echo QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn >>rp_expected &&
ipfs refs -r "$HASH_WELCOME_DOCS" >>rp_expected && ipfs refs -r "$HASH_WELCOME_DOCS" >>rp_expected &&
ipfs pin ls -type=recursive >rp_actual && ipfs pin ls --type=recursive >rp_actual &&
test_sort_cmp rp_expected rp_actual test_sort_cmp rp_expected rp_actual
' '
test_expect_success "'ipfs pin ls -type=all' is correct" ' test_expect_success "'ipfs pin ls --type=all' is correct" '
cat directpinout >allpins && cat directpinout >allpins &&
cat rp_actual >>allpins && cat rp_actual >>allpins &&
cat indirectpins >>allpins && cat indirectpins >>allpins &&
cat allpins | sort | uniq >> allpins_uniq && cat allpins | sort | uniq >> allpins_uniq &&
ipfs pin ls -type=all >actual_allpins && ipfs pin ls --type=all >actual_allpins &&
test_sort_cmp allpins_uniq actual_allpins test_sort_cmp allpins_uniq actual_allpins
' '
......
...@@ -143,7 +143,7 @@ test_expect_success "added dir was NOT pinned indirectly" ' ...@@ -143,7 +143,7 @@ test_expect_success "added dir was NOT pinned indirectly" '
' '
test_expect_success "nothing is pinned directly" ' test_expect_success "nothing is pinned directly" '
ipfs pin ls -type=direct >actual4 && ipfs pin ls --type=direct >actual4 &&
test_must_be_empty actual4 test_must_be_empty actual4
' '
......
...@@ -13,7 +13,7 @@ test_init_ipfs ...@@ -13,7 +13,7 @@ test_init_ipfs
# test publishing a hash # test publishing a hash
test_expect_success "'ipfs name publish' succeeds" ' test_expect_success "'ipfs name publish' succeeds" '
PEERID=`ipfs id -format="<id>"` && PEERID=`ipfs id --format="<id>"` &&
ipfs name publish "$HASH_WELCOME_DOCS" >publish_out ipfs name publish "$HASH_WELCOME_DOCS" >publish_out
' '
...@@ -34,7 +34,7 @@ test_expect_success "resolve output looks good" ' ...@@ -34,7 +34,7 @@ test_expect_success "resolve output looks good" '
# now test with a path # now test with a path
test_expect_success "'ipfs name publish' succeeds" ' test_expect_success "'ipfs name publish' succeeds" '
PEERID=`ipfs id -format="<id>"` && PEERID=`ipfs id --format="<id>"` &&
ipfs name publish "/ipfs/$HASH_WELCOME_DOCS/help" >publish_out ipfs name publish "/ipfs/$HASH_WELCOME_DOCS/help" >publish_out
' '
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论