- Installation
- Getting started
- Commands
- Options
- Arguments
- Scripts
- Configuration
- Internal commands
- Help
- Autocompletion
Root level commands are defined in the commands
section of the manifest file (centry.yaml
).
To define a command, two properties
are required. The name
is what you will be calling when using your CLI and the path
points to the script file where the command function lives.
Here's how you would define a root level command called get
:
// file: centry.yaml
commands:
- name: get
path: ./get.sh
In the script file, create a function matching the name
property.
// file: get.sh
#!/usr/bin/env bash
get() {
echo "getting stuff for the fun of it"
}
There are additional properties that may be set for a command (see Command properties
).
You may also choose to specify some of the properties using annotations (see Command annotations
).
What strategy you choose is entirely up to you but the root level commands must always be partially specified in the manifest file.
Sub commands are exclusively defined in scripts. Creating a sub command is as easy as including the special character colon (:
) in a script function name. Let's say you have already defined a root level command called get
but wanted to define two commands that have get
as their parent. Simply create two functions named get:
and suffix it with the desired name of the sub command.
// file: centry.yaml
commands:
- name: get
path: ./get.sh
// file: get.sh
#!/usr/bin/env bash
get:data() {
echo "getting the latest data..."
}
get:time() {
echo "the time is $(date +"%T")"
}
The script above defins two subcommands, data
and time
. The can now be executed by calling your CLI like below.
mycli get data
mycli get time
Adding annotations for sub commands works in the same way as for root level commands. Here's an example adding a description for the two commands created above. Note that the full function name must be used in the annotation.
// file: get.sh
#!/usr/bin/env bash
# centry.cmd[get:data]/description=Get's you data
get:data() {
echo "getting the latest data..."
}
# centry.cmd[get:time]/description=Displays the current time
get:time() {
echo "the time is $(date +"%T")"
}
Property | Description | YAML key | Type | Required |
---|---|---|---|---|
Name | The name of the command | name |
string | true |
Path | Relative path to the script containing the command | path |
string | true |
Description | Description of the command, displayed in help output | description |
string | false |
Help | Usage example for the command | help |
string | false |
Hidden | When true, hides the command from help output | hidden |
boolean | false |
Command annotations are used to associate metadata with a command. Annotations are defined using regular comments in bash (a line starting with #
). They may be placed anywhere inside the script file and in any order you want. It is however recommended that you keep it close to your functions to act as documentation when changing your commands.
Property | Format |
---|---|
Description | # centry.cmd[<command>]/description=<value> |
Help | # centry.cmd[<command>]/help=<value> |
Hidden | # centry.cmd[<command>]/hidden=<value> |
Options (aka flags) are used to pass named arguments to commands. When used, centry
will export a variable for you with the value of the option set.
Option values are made available to your commands as environment variables. Given an option named filter
, centry sets the environment variable FILTER
to the value provided by the option or to it's default value. The environment variable name that is used for an option can be changed by setting the EnvName
property (see Option properties).
Global options are made available for all commands. They are often used to to provide context for the commands you are executing. Global options are defined in the options
section of the manifest file (centry.yaml
). To define a global option, two properties are required. The name of the option and it's type. In general you should only specify a global option if it makes sense in the context of all commands provided by your cli.
Here's how you would define the global option --verbose
:
// file: centry.yaml
options:
- name: verbose
type: bool
description: Use verbose logging
Using a global option requires you to specify the option before the name of any command. This is by design and helps drive home the fact that global options are available for all commands.
mycli --verbose command1
mycli --verbose command2
Command options are, as the name suggests, scoped to commands. Therefore there is no way to define these in the manifest file. Instead you will be using annotations
to define command options. For a full list of available annotations, see Option annotations.
Here's an example defining a filter
option for the get files
command:
// file: get.sh
#!/usr/bin/env bash
# centry.cmd[get:files].option[filter]/description=List only files matching the specified filter
get:files() {
echo "listing files in the current directory (filter=${FILTER:-})..."
if [[ "${FILTER:-}" != "" ]]; then
ls . | grep "${FILTER:?}"
else
ls .
fi
}
Options have a type
property that defines it's behavior and possible values. The currently supported option types are:
String options are the most common type to use. It has a name
and it's type
set to string
. In addition to the required properties, it is quite common to use the default
property to set a default value for the option. See Option properties for the full list of available properties.
Example
// file: centry.yaml
options:
- name: filter
type: string
description: Filters output of the command
Usage: --<option_name> <value>
or --<option_name>=<value>
Boolean options can be used to provide a switch for behaviors in a command. As an example it could be used to turning debug logging on or off. A bool option have a value of false
by default (this can be changed but it is not recommended). Using the default value of false
, providing the option to your cli will tell centry to toggle that value to true
.
Example
// file: centry.yaml
options:
- name: verbose
type: bool
description: Turn on verbose logging
// file: get.sh
#!/usr/bin/env bash
get:data() {
echo "getting data..."
if [[ ${VERBOSE} ]]; then
curl --verbose http://google.com
else
curl http://google.com
fi
}
Integer options can be used to pass numbers to your commands. Things like --max-retries=5
and --cluster-size=3
are great examples where you might want to use an integer option. Integer options have a default value of 0
but may be set to any integer value. Passing an integer option will override the default value to the value provided.
Example
// file: get.sh
#!/usr/bin/env bash
# centry.cmd[get:url].option[url]/required=true
# centry.cmd[get:url].option[max-retries]/type=integer
# centry.cmd[get:url].option[max-retries]/default=3
get:url() {
echo "Calling ${URL:?} a maximum of ${MAX_RETRIES:?} time(s)"
echo
local success=false
local attempts=0
until [[ ${success:?} == true ]]; do
((attempts++))
if ! curl "${URL:?}"; then
if [[ ${attempts:?} -ge ${MAX_RETRIES:?} ]]; then
echo
echo "Max retries reached... exiting!"
return 1
fi
sleep 1
else
success=true
fi
done
}
Usage: --<option_name>
or --<option_name>=<value>
Select options are a bit different. It is commonly used to have the user select one value from an array of predefined values. The user selects a value by using the matching option.
Let's dive into an example where we want the user to be able to select one of three AWS regions (eu-central-1, eu-west-1 and us-east-1). Here's how we would define that in our manifest.
select/v2 (introduced in v1.4.0)
// file: centry.yaml
options:
- name: region
type: select/v2
env_name: AWS_REGION
description: Set's the AWS region
values:
- name: eu-west-1
- name: eu-cenral-1
- name: us-east-1
In addition to name
, select/v2
values also support setting a short
name and providing a value
that will be set when selected.
select (deprecated since v1.4.0)
// file: centry.yaml
options:
- name: eu-central-1
type: select
env_name: AWS_REGION
description: Use eu-central-1 AWS region
- name: eu-west-1
type: select
env_name: AWS_REGION
description: Use eu-west-1 AWS region
- name: us-east-1
type: select
env_name: AWS_REGION
description: Use us-east-1 AWS region
On it's own, a select option provides no real value. The magic happens when we override the environment variable name that will have it's value set when a select option is provided. This essentially creates an array of valid values scoped to the specified environment variable name.
// file: get.sh
#!/usr/bin/env bash
get:lambdas() {
: [ ${AWS_REGION:?'An AWS region must be selected using one of the predefined options'} ]
echo "listing AWS lambda functions in region ${AWS_REGION:?}..."
aws lambda list-functions --region ${AWS_REGION:?}
}
With the above command defined we can now run the following to list lambdas in the region us-east-1:
mycli get lambdas --us-east-1
NOTE:
- As no default value can be specified for select options, it's name is instead used as it's value.
- If multiple select options with the same environment variable name is specified, the last one wins.
Property | Description | YAML | Type | Required |
---|---|---|---|---|
Type | Type of option | type |
OptionType (string/bool/select etc.) | true |
Name | Name of the option | name |
string | true |
Short | Short name of the option | short |
string | false |
EnvName | Name of environment variable set for the option | env_name |
string | false |
Default | Default value of the option | default |
string | false |
Description | Description of the option, displayed in help output | description |
string | false |
Hidden | When true, hides the option from help output | hidden |
boolean | false |
Required | When true, marks the option as required | required |
boolean | false |
Values | Used to set the valid values for select/v2 option |
values |
array of object{name,short,value} | - |
Option annotations are used to define options for a command. Annotations are defined using regular comments in bash (a line starting with #). They may be placed anywhere inside the script file and in any order you want. It is however recommended that you keep it close to your functions to double as documentation for the command/option.
Property | Format |
---|---|
Type | # centry.cmd[<command>].option[<option>]/type=<value> |
Short | # centry.cmd[<command>].option[<option>]/short=<value> |
EnvName | # centry.cmd[<command>].option[<option>]/envName=<value> |
Default | # centry.cmd[<command>].option[<option>]/default=<value> |
Description | # centry.cmd[<command>].option[<option>]/description=<value> |
Hidden | # centry.cmd[<command>].option[<option>]/hidden=<value> |
Required | # centry.cmd[<command>].option[<option>]/required=<value> |
Values | # centry.cmd[<command>].option[<option>]/values=[{"name":"<name>","short":"<short>","value":"<value>"}] |
Anything after the last specified command or option will be passed to your command as arguments.
mycli mycommand --myoption=foo bar baz
Assuming mycommand
have an option defined called myoption
, in the example above, bar
and baz
would be passed as arguments. The same is true when myoption
is left out.
In some cases it is useful for flags to be passed on as arguments to a command.
In the following command we have wrapped curl
but want to allow the use of the verbose flag, even though that is not the default behavior.
// file: get.sh
#!/usr/bin/env bash
# centry.cmd[get:data]/description=Get's data from a URL
# centry.cmd[get:data].option[url]/description=The URL to get data from
get:data() {
echo "getting the data from ${URL:?}..."
curl "${URL:?}" "$@"
}
The command below will fail since a verbose option is not defined.
mycli get data --url http://google.com --verbose
To achive the desired behaviour we need to tell centry to stop processing arguments. This can be done by adding a --
when calling the command.
mycli get data --url http://google.com -- --verbose
Before executing a command, centry can import helper functions and run common setup tasks for the environment the command executes in. This is done by specifying an array of file paths in the scripts
section that centry will source, in the specified order. This makes sharing functions across commands easier and more predictable while keeping things DRY.
Comman use-cases include:
- Sourcing of functions from script libraries and wrapper scripts
- Installing missing dependencies and downloading files
- Setting environment variables
- Authentication and authorization
Here's an example:
// file: centry.yaml
scripts:
- /usr/share/bash-commons-1.0.2/modules/bash-commons/src/log.sh
- scripts/helpers.sh
- scripts/init.sh
If you need something to run at the point of sourcing it, simply make it self executing like the init script below.
// file: scripts/init.sh
#!/usr/bin/env bash
init() {
echo 'initializing the environment'
}
init "$@"
NOTE: It is important to know that naming conflicts may occur. If multiple scripts are sourced, containing functions with the same name, only the last one would be available for commands to use.
The config
section of the manifest file allows you to override default values as well as describing your CLI.
The most common place to start is with the metadata properties that are used to describe your CLI to it's users. They are defined at the root of the config
section as shown below:
config:
name: mycli
description: does whatever I wan't it to
version: 1.0.0
Property | Description | Type | Default | Required |
---|---|---|---|---|
Name | Name of the CLI | string | - | true |
Description | Description of the CLI | string | A declarative cli built using centry | false |
Version | Version of the CLI | string | - | false |
In the config.log
section you may change things related to logging in centry. You may want to turn on debug logging or add a prefix to the log messages printed by centry.
config:
name: mycli
log:
level: debug
prefix: "[centry] "
Property | Description | Type | Default | Required |
---|---|---|---|---|
Level | Log level used by centry | LogLevel (debug/info/warn/error/panic) | info | true |
Prefix | Prefix applied to all centry log messages | string | - | false |
Also defined in the config
section are some properties that allow even more control of how centry works.
config:
name: mycli
environmentPrefix: MY_CLI_
hideInternalCommands: false
hideInternalOptions: false
helpMode: interactive
Property | Description | Type | Default | Required |
---|---|---|---|---|
EnvironmentPrefix | Prefix used when exporting environment variables in centry | string | - | false |
HideInternalCommands | Hides internal centry commands from help output | boolean | true | false |
HideInternalOptions | Hides internal centry options from help output | boolean | true | false |
HelpMode | Mode triggered when cli is invoked without arguments | string (default/interactive) | default | false |
The default mode prints a generated help section to stdout. It is contextual and will print available subcommands and options.
When the cli is invoked without arguments, the default help mode is triggered. By changing the help mode from default
to interactive
, centry will provide an interactive command builder that can help with exploring the cli. It allows for filtering commands and subcommands as well as prompting for option input or selection.
See advanced configuration for how change the default behavior.