corda/docs/source/cli-ux-guidelines.rst
Anthony Keenan 0ab644783e CORDA-1838: Add subcommands to node (#4091)
* Tidy up

* Add install-shell-extensions command

* Make cli tests use same version of picocli as everything else

* Remove initLogging from NodeStartup, it is ran earlier by CordaCLIWrapper

* Use picocli snapshot for testing

* Use RunLast() parser to invoke correct subcommands

* Deprecate old clear-network-map-cache parameter

* Restructure NodeStartup for commands

* Get rid of -c option since the flag method has been deprecated and that didn't exist in last release

* Update documentation

* Update backwards compatibility test

* Get all subcommands working

* Refactor sub commands into seperate classes

* Update docs and fix some tests

* Docs changes

* Fix merge conflicts with master

* Fix renamed parameters

* Fix test failure

* Fix compatibility tests

* Add missing compatibility test for blob inspector

* Remove blob inspector compatibility test as there are import conflicts

* Assorted doc fixes

* Addressing review comments

* More review comments

* Couple more bits

* Fix broken tests

* Fix compilation error

* More merge conflicts

* Make startup logging function a bit more sensible

* Fix broken shell extensions

* Make shell extensions work with subcommands

* Make sure parameters for deprecated options are carried through

* More review comments

* Adding some s's

* One last go

* Fix compilation error on Windows

* Revert logging changes

* Revert docs back to their original imperatively moody state
2018-10-24 13:58:19 +01:00

7.1 KiB
Raw Blame History

CLI UX Guide

Command line options

Command line utilities should use picocli (http://picocli.info) to provide a unified interface and follow the conventions in the picocli documentation, some of the more important of which are repeated below.

Option names

  • Options should be specified on the command line using a double dash, e.g. --parameter.
  • Options that consist of multiple words should be separated via hyphens e.g. --my-multiple-word-parameter-name.

Short names

  • Where possible a POSIX style short option should be provided for ease of use (see http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02).
    • These should be prefixed with a single hyphen.
    • For example -V for --verbose, -d for --dev-mode.
    • Consider adding short options for commands that would be ran regularly as part of troubleshooting/operational processes.
    • Short options should not be used for commands that would be used just once, for example initialising/registration type tasks.
  • The picocli interface allows combinations of options without parameters, for example, `-v and `-d can be combined as -vd.

Positional parameters

  • Parameters specified without an option should ideally all be part of a list.
    • For example, in java -jar test.jar file1 file2 file3, the parameters file1, file2 and file3 should be a list of files that are all acted on together (e.g. a list of CorDapps).
  • Avoid using positional parameters to mean different things, which involves someone remembering in which order things need to be specified.
    • For example, avoid java -jar test.jar configfile1 cordapp1 cordapp2 where parameter 1 is the config file and any subsequent parameters are the CorDapps.
    • Use java -jar test.jar cordapp1 cordapp2 --config-file configfile1 instead.

Standard options

  • A --help option should be provided which details all possible options with a brief description and any short name equivalents. A -h short option should also be provided.
  • A --version option that should output the version number of the software. A -V short option should also be provided.
  • A --logging-level option should be provided which specifies the logging level to be used in any logging files. Acceptable values should be DEBUG, TRACE, INFO, WARN and ERROR.
  • --verbose and --log-to-console options should be provided (both equivalent) which specifies that logging output should be displayed in the console. A -v short option should also be provided.

Standard subcommands

  • An install-shell-extensions subcommand should be provided that creates and installs a bash completion file.

Defaults

  • Flags should have sensible defaults.
  • Boolean flags should always default to false. Specifying the flag without a parameter should set it to true. For example --use-something` should be equal to--use-something=trueand no option should be equal to--my-flag=false``.
  • Do a bit of work to figure out reasonable defaults. Nobody likes having to set a dozen flags before the tool will cooperate.

Adding a new option

  • Boolean options should start with is, has or with. For example, --is-cheesy, --with-cheese, --has-cheese-on.
  • Any new options must be documented in the docsite and via the --help screen.
  • Never use acronyms in option names and try and make them as descriptive as possible.

Parameter stability

  • Avoid removing parameters. If, for some reason, a parameter needs to be renamed, add a new parameter with the new name and deprecate the old parameter, or alternatively keep both versions of the parameter.

The CordaCliWrapper base class

The CordaCliWrapper base class from the cliutils module should be used as a base where practicable, this will provide a set of default options out of the box. In order to use it, create a class containing your command line options using the syntax provided at (see the picocli website for more information)

import net.corda.cliutils.ExitCodes
import net.corda.cliutils.CordaCliWrapper

class UsefulUtilityExitCodes: ExitCodes {
    companion object {
        val APPLICATION_SPECIFIC_ERROR_CODE: Int = 100
    }
}

class UsefulUtility : CordaCliWrapper(
    "useful-utility", // the alias to be used for this utility in bash. When install-shell-extensions is run
                      // you will be able to invoke this command by running <useful-utility --opts> from the command line
    "A command line utility that is super useful!" // A description of this utility to be displayed when --help is run
) {
    @Option(names = ["--extra-usefulness", "-e"], // A list of the different ways this option can be referenced
            description = ["Use this option to add extra usefulness"] // Help description to be displayed for this option
    )
    private var extraUsefulness: Boolean = false // This default option will be shown in the help output

    override fun runProgram(): Int { // override this function to run the actual program
        try {
            // do some stuff
        } catch (KnownException: ex) {
            return UsefulUtilityExitCodes.APPLICATION_SPECIFIC_ERROR_CODE // return a special exit code for known exceptions
        }

        return UsefulUtilityExitCodes.SUCCESS // this is the exit code to be returned to the system inherited from the ExitCodes base class
    }
}

Then in your main() method:

import net.corda.cliutils.start

fun main(args: Array<String>) {
    UsefulUtility().start(args)
}

Application behavior

  • Set exit codes using exitProcess.
    • Zero means success.
    • Other numbers mean errors.
  • Setting a unique error code (starting from 1) for each thing that can conceivably break makes your tool shell-scripting friendly.
  • Make sure all exit codes are documented with recommended remedies where applicable.
  • Your --help text or other docs should ideally include examples. Writing examples is also a good way to find out if your program requires a dozen flags to do anything.
  • Dont print logging output to the console unless the user requested it via a -verbose flag (conventionally shortened to -v). Logs should be either suppressed or saved to a text file during normal usage, except for errors, which are always OK to print.
  • Don't print stack traces to the console. Stack traces can be added to logging files, but the user should see as meaningful error description as possible.