-
Notifications
You must be signed in to change notification settings - Fork 931
CF CLI Style Guide
The purpose of this document is to promote a shared understanding of the user experience for the Cloud Foundry Command Line Interface (cf CLI), so that contributors to the CLI can provide a simple and consistent user experience. The audience for this document includes:
-
Developers contributing cf CLI pull requests
-
CF/PCF development teams
-
cf CLI plug-in authors
-
The cf CLI team
The cf CLI embodies the diversity, friendliness, and openness of the Cloud Foundry Community. The CLI strives to provide a consistent and predictable experience for end users of CF.
The following section describes the patterns we consider to be best practices for the cf CLI user experience. While not every existing command follows all of these patterns, our intent is to revise existing commands and create new commands so that they do.
-
When we add functionality to enhance or change existing commands, we must first ensure that functionality is backwards compatible. Do not add functionality that will break existing users without contacting the cf CLI team. Check the latest supported version. Per Semantic Versioning, backwards compatibility should be maintained for minor releases.
-
Any updates or new commands must be optional, additive, or fully transparent to the user with all changes behind the scenes. They must also provide system feedback.
-
Idempotent (true for all commands except for create command)
-
All creates, deletes, lists, etc should act in a similar fashion; see more information below
-
Each command should only perform one function; for example, if it is a linking command, it should only create a link, it should not create additional objects. There may be exceptions in cases of creating and uploading a droplet, for example.
-
Targeting requirements should be consistent across new commands
We intend to provide users with the following interactions on cf CLI commands:
-
Feedback about initiating a process
-
Feedback when a process completes
-
Quick access to the state of an object
-
Quick access to the state process
The following sequence of commands and output shows examples of how the cf CLI delivers the four interactions mentioned above.
Commands which act on resources that make API calls first give the user feedback that they are about to start something:
-
The action about to take place;
-
The resource it is taken on (when applicable and in cyan flavor text);
-
The org and space the resource is in (when applicable and in cyan flavor text);
-
The identity (username) assumed on the remote system for the API call (when applicable and in cyan flavor text);
-
A final "..." to indicate more output is coming.
-
For "Create"/"delete" and "update" commands a short "OK", after which the command exits with exit code 0.
-
Output to the user to indicate a subsequent process or step that is about to/should be initiated to confirm the state of the operation.
-
Optionally, a
TIP
to indicate which commands to run next and/or a list command that can be retrieved in a subsequent system call in a table and/or key-value display. Add aTIP
if the operation, for some reason, is not as transparent to the user as it should be - an example of this is theupdate-buildpack
command, which unbeknownst to the user, can create a buildpack resource with a nil stack.
Note
- For commands that do not act on a resource, (
cf login
, for example), they do not need to display initial feedback text or echo the resource, org/space and user details. However, they do require the final "..." ending and anOK
to confirm the operation completed successfully.
-
The output that follows feedback text depends on the action of the command and the API response:
-
"List" commands display a table with the retrieved information. They follow the table design guidelines defined elsewhere in this guide.
-
"Get" commands display a key-value list with the retrieved information. For resources that include collections within, they may also display a table. For example,
app
displays a key-value list specific to an app followed by tables with instance details for each process. The key-list design guidelines are defined elsewhere in this guide. -
"Create"/”delete” and “update” commands display “OK” to clarify the action completed successfully:
-
"Delete" commands on a resource that could not be found display a “Resource 'X' does not exist” warning message together with the “OK” status message that the desired state is considered reached (the resource is gone) but brings attention to the user that they may want to double check the right resource was specified to the command - the command exits with
0
for the same reason. -
"Create" or “update” commands may display additional information that the user would need to proceed. For example,
create-package
should display the package information so the user can proceed to set a droplet for the app.
-
-
-
Commands that require additional action to take effect on a resource should say so. For example,
set-health-check
displays a tip that an app restart is required for the change to take effect. -
Commands that orchestrate others (e.g.
push
) should display new feedback text for each action it takes, to help the user investigate or report how far the command proceeded and what it was doing in case the output stalls or the command fails. -
The user should be given feedback again when the process completes. That can either be (For non-list or informational commands):
-
An error message, (eventually) leading to the command aborting with
FAILED
and exit code1
. We display theFAILED
message on the last line of the output. If there is aTIP
, then it is outputted after theFAILED
, with a blank line in between. Note as a general rule, we check flag options validity first before we make api calls. If there is an error, we should print out the reason for the error. -
A short
OK
, possibly with a tip or warning message, after which the command exits with exit code0
. Tips should only be used if the transaction was non-transparent or if there's an additional action the user might want to take on the resource. -
All errors and warnings should go to STDERR, and should be in plain text
-
-
Outside of flavor text (see above), we avoid displaying colors. In some critical cases, we display color: for example, if you have an app that's down we show status and instances in color.
-
Fields, rows, headers, should not disappear unless it is a major bump of the CLI - if it exists, it should always display (if we decide to add a column, for example) - for scripting reasons we should stay consistent
-
Any idempotent command that doesn't actually run an operation should output a warning (deleting a space that doesn't exist, for example, should complete successfully but should output a warning to say that the delete did not occur)
-
When two flag combinations cannot be used together, we like to fail fast, however there are cases in which we can't: for example if
path
and--assign-stack
flags are used together the first validation (because we use go flags) is done to check if thepath
provided is valid, then the command will fail on the flag validation. -
For some commands, we check for minimum API versions and display an error when the user's version of the API is below the minimum required to use a specific feature. See below for more information.
-
Command output is first and foremost optimized for human readers.
-
Some commands are optimized for scripting (e.g.
ssh-code
) or support flags that optimize output for scripting (e.g.app myapp --guid
). -
Colors can be disabled using a
cf config
flag or environment variable, and are disabled when the CLI detects it is not in a tty session. -
The v6 CLI strived for the following: information displayed in tables should not change in order or have new columns inserted, so scripts can use tools such as
awk
to parse its output.
Avoid removing lines just because they don't have an associated value. Adding/Removing attributes from a key/value table only because it's empty adversely affects a developer's ability to learn about the attributes of an object (e.g., an app). Perhaps knowing the value is empty gives the developer an opportunity to troubleshoot or rectify an issue because they can see an attribute is not defined. Perhaps a developer can discover a feature because the attribute is presented, which gives them an opportunity to explore its purpose.
Arguments are used to specify the resource on which the action is performed. If there is no ambiguity what resource the action is intended for, or if there is a reasonable default, there is no need for an argument. All other required information should be provided using options with reasonable defaults.
-
Args versus flags
-
Args are required (For example, bind-service - you always need the app name and service name)
-
flags are optional/supplementary to an arg (For bind-service, you could also provide a
-c
(for json), or--binding-name
flags. -
if you are not 100% sure that args are not going to be required in the future, then make them flags - even if they are required right now
-
Flags are tab completed; args do not do tab completion
-
-
Raw Inputs versus Files
- Input file versus raw command line (for
cf bind-service
, it takes both a valid JSON object either in-line or in a file.create-security-group
, on the other hand, only takes a file that contains json). Always make it a file for simplicity.
- Input file versus raw command line (for
Commands may have prerequisites - for example, the user is required to be logged in, an API endpoint, org, and space must be targeted, or an API endpoint of a certain minimum version.
The user should be notified immediately when a prerequisite is not satisfied. Where possible, the command should abort with an error message clarifying which prerequisite was not met and what the user can do about it. The command shall abort with exit code 1
. Some examples follow below:
-
Scenario: user is not logged in. Most commands require a user to be logged in, with few exceptions such as
cf marketplace
. The user should be immediately informed that they areNot logged in. Use 'cf login' or cf login --sso to log in.
-
Scenario: commands that require the user to be targeting an org and space. When multiple subsequent prerequisites are not met, the user should be provided with that feedback. Most commands require an
org
andspace
to be targeted (Examples of exceptions:feature-flags
,security-groups
). In the case where users are required to be targeted to anorg
orspace
, they should be told to target an org and space - not first be told to target an org, and on the subsequent execution fail again and be told to target a space: -
Where possible, if not all arguments are provided (requirements for running commands), aborting should happen before the initial command feedback text is displayed. For example:
Most CF CLI commands fall into five types. To give users a predictable and consistent experience, follow these patterns for each command type.
-
Create/Add: Examples: create-buildpack, create-space, create-user, create-service
-
Patterns:
-
Display the name of the new item to confirm it was created.
-
If there are additional actions required after the resource is created, add a tip at the end of the output.
-
If the resource already exists, the command should return output to say the resource already exists.
-
-
Example: Single resource happy path:
Example: Multi-step resource creation (creating and uploading):
Example: The resource already exists:
-
Delete:
-
Patterns:
- Provide a prompt to ask the user to confirm the deletion if the deletion is destructive: it recursively deletes resources. If the user enters 'no' at the prompt, confirm to the user that the resource was not deleted.
- Display the name of the item to be deleted.
-
Examples: delete-service, unbind-service, unshare-service, delete-space
-
-
List:
-
Patterns:
-
Display the name of the item to be displayed.
-
A
OK
is not required since the returned list or table is confirmation of a successful status -
See the flavor text output below and the conventions for styling: the headers are lowercase. The list is alphabetized.
-
-
Examples: apps, marketplace, services, orgs, spaces
-
There are table modifiers and exceptions to the above list rules which are important to discuss.
-
cf routes --orglevel
will show routes for multiple spaces, and the order of that table is listed by space, not alphabetically. -
cf marketplace --no-plans
hides theplans
column -
cf org-users -a
removes the role-related rows and lists all users in the organization including the OrgUser role
-
-
Summary:
-
Patterns:
-
Simple key values are displayed for one to one or one to many relationships. For many to many relationships, a table is displayed.
-
Key values are aligned to the longest key name (first column) plus three spaces. Tables are consistent with the section below. Also see the Key/Value Pair Output section for more information.
-
-
Examples: app, service
-
Anti-patterns:
target
,api
- where we are not listing a resource.
-
-
Rename/Update:
-
Patterns:
-
Input is old value then new value.
-
OK should always be displayed when the update is successful. If not,
FAILED
should be displayed.
-
-
Examples: rename (app), rename-service, rename-org, update-service, update-buildpack
-
-
Link: Examples: bind-service, share-service
-
Patterns:
- See the Command Naming Conventions below for VERB-NOUN imperatives. Consistency in naming should be adhered to.
- Linking commands should only create a link, it should not create additional objects.
-
Generally, cf CLI’s commands are named in VERB-NOUN, or ACTION-RESOURCE order.
This allows them to be read like an imperative or request: "Computer, start app!", “Delete app helloworld, please”, “Set space role of user@example.com in myorg and myspace to SpaceManager”, etc.
For CRUD related to resources, we include the resource name in the commmand -buildpack
, e.g., cf create-buildpack
. (This also ensures command tab completion for cf cr<tab>
takes the user to the long list of "create" commands without needing to enter an additional -[tab]
step.).
Exceptions:
-
The noun
app
is omitted from command names when the action is on an app, as the main purpose of the cf CLI is to push and manage apps. The exception is withcreate-app
. -
For commands that list resources, or show details of a particular resource, the verb is omitted.
-
A few common commands to set up the local environment are single-noun for quick configuration:
api
,target
,config
. -
list-plugin-repos
does not omit "list". Maybe to disambiguate withrepo-plugins
, or simply by error.
The command’s verb should match the action taken.
To make it easy for experienced users to remember command names, new commands should not unnecessarily introduce new verbs when an existing command with a suitable synonymous verb exists.
Commands with opposite actions should use antonyms to make it easy for the user to predict the name of the one command from seeing the other. For example, start
andstop
, bind-service
and unbind-service
, map-route
and unmap-route
, install-plugin
and uninstall-plugin
, etc.
Some flexibility is allowed when a slight variation of the antonym better matches the action, or consequence of an action. For example, reset-space-isolation-segment
clears a previously set isolation segment, which causes app placement to default to the org level configured segment.
When the cf
command is run without arguments, or with the help
subcommand or -h
, --help
, cf h -a
options, an overview of common app developer commands is returned.
Every CLI command has its own help page, retrievable using cf cmd -h
, cf help cmd
, cf cmd --help
). Guidelines for this help page are here. They are inspired by man page conventions.
-
The first line returns the command name and version, followed by its short description.
-
The second line returns its Usage. Thereafter, the commands are grouped in sections (Examples, Alias (if applicable), Options, See Also). Each section has a header title displayed in bold and all caps, ending with a colon.
Common global environment variables (if any) are displayed last, for example, see the push
help text.
If required, you may also display a TIP after the USAGE information (see update-buildpack
) if there are important gotchas that the user should be aware of.
In rare occasions, a WARNING section may appear - see cf login -h
- to alert users of any security-related information they should be aware of.
In cases where a required argument is not provided, if too many args are provided for a command, the CLI will exit immediately and return the help text for the command.
The cf CLI has a policy regarding supporting a range of CF deployment versions here.
Some commands require a minimum version of an API endpoint; these include newer commands such as bind-service
, and assigning a stack to update-buildpack
.
Users are notified when executing a command for a feature not available in their targeted CF endpoint. The CLI aborts with Failed
and exit code 1
, and states the required API version for the command, flag or value specified, and if available and relevant, the current API version. For example, Using FEATURE requires CF API version 3.25.0 or higher. Your target is 3.4.0.
(See buildpacks
functionality for cf push app -b ruby_buildpack -b python_buildpack
for an example of the message.)
Environment variables are only used in select cases, and used sparingly - at times only passwords are set as environment variables. For example, you can set export CF_USERNAME="$USERNAME"
and export CF_PASSWORD="$PASSWORD"
and run cf auth
to log in.
Make sure when not in a tty session, do not output colors. All other output should be identical. Moving forward, we will attempt to make this easier to parse.
Guidance:
Simple key values are displayed for one to one or one to many relationships. For many to many relationships, a table is displayed.
-
Use tables when returning attributes for multiple objects.
-
Use key/value pairs when returning attributes for one object.
Guidance:
When displaying a table:
-
Column headers are bold and lowercase (top row)
-
Columns are separated by at least three spaces
-
If there isn’t a value for a field, it’s left empty
-
If there are no values for a table, it’s replaced with a text explanation.
Guidance:
When displaying a key/value pair:
-
Key value (in left column) is always lowercase
-
The value of every field is aligned; columns are separated by at least three spaces from the longest key (such as "requested state")
-
If there isn’t a value for a field, the key is displayed but the value is left empty (such as "isolation segment" and "space quota")
current time formats cf version 6.23.1+a70deb38f.2017-01-13
The format for displaying time depends on how the user expects to use this information.
Guideline
Location | Purpose | Desired format | Example |
"Last timestamp" in `cf app myapp` | Was the app pushed recently? Was it last Friday morning or last month? | - CLI user’s TZ - Localized - Include weekday | Thu 03 May 16:24:50 PDT 2018 |
`cf logs myapp` | Compare log time entries, correlate with other logs | - Compact - Consistent width - App/Server TZ | 2018-05-08T15:43:12.41-0700 |
The CF CLI design team reviewed flavor text and provided guidance below after a round of research for accessibility.
- Don't add flavor text to anything outside of the following convention:
- intro output text -
resource
to be acted upon (app name, for example),org name
,space name
,role
(admin, for example),user name
are in cyan -
OK
is green -
Failed
is red -
Down
apps in thecf app
display in red flavor text - table output - headers are in white and bold
- Accessibility is important
Example of color blind friendly colors as described above:
Warnings, error messages, TIP
are in plain text.
For information regarding developing plugins, see Developing cf CLI Plugins.