Edited by: Maja Szostok
Table Of Contents
Including version information in CLIs is as obvious as a shower after a 3-day music festival. But have you ever given it a second thought?
I researched 16 popular CLIs to find out how they approach this, and ultimately to come up with a solution that is both elegant and useful.
In this article, I present my findings and outline both the good and the bad of each approach.
Flag or Command?
Before we discuss what we should display, let's talk about how our user can get the CLI version.
Flags
Let's take a look at the most popular options:
Name | Description |
---|---|
-v |
Might be confusing, as -v is often used to enable the verbose mode. If you decide to use -v anyway, consider -d, --debug for more verbose output. |
-V |
Being a capital letter, it's a better alternative for -v as it's visually distinct. |
--version |
Is a self-descriptive alternative, mentioned by CLI guidelines as a common approach found in CLIs. |
However, flags come with one big problem. It's hard to combine them with additional functionality. The most popular ones are:
- Printing a help message with more information about the version itself.
- Printing only the version number.
- Printing only the client version when your CLI works with a server, like
kubectl
does.
There are some options to deal with that, but they have their own flaws:
-
<cli> -V --short
— it's hard to tell which flags work together -
A combination of function and
-version
suffix. For example:
<cli> --short-version
Unfortunately, this doesn't scale well. See:
<cli> --short-client-version <cli> --short-server-version <cli> --json-version <cli> --json-client-version <cli> --json-server-version
Command
An alternative solution is to have a dedicated <cli> version
command, which gives you an easy way to:
- Print a help message with
<cli> version -h
. - Combine it with other functionality. For example,
--short
,--json
,--client-only
, etc. - Introduce related sub-commands. For example,
<cli> version check-update
to check if there is a new release.
It requires more typing than <cli> -v
, but you can add aliases, such as <cli> ver
or even <cli> v
. However, this is not something I saw often 🤔.
Decision
I guess all of us want to have our cake and eat it too 😎. In this one case, it's possible. Personally, I would suggest having the --version
flag to print the short version, and the <cli> version
command to support more complex use cases.
What to collect
Version information is your CLI's DNA. The data that you collect and display is later useful for the CLI authors and its users. Users want to easily check if they're using the right version. Authors mostly want to find out what revision of the source code was used for a given CLI version, e.g. in case of a bug report.
Users are the most important part of our projects. Let's start with their needs first!
-
Get the CLI version — the best option is to use semantic versioning here.
- Get both the client and the server version, if available.
Note: Make sure that you have an option to print the client version only.
Be informed about using an outdated version or version skew.
Get the URL to the GitHub release and/or documentation.
For you as the author, displaying these details can come in handy:
- Git commit — the SHA for the commit that a given version was built from.
- Git state — for example,
clean
if there are no local code changes when this binary was built;dirty
if the binary was built from locally modified code. - Build date — the date when the binary was built.
- Programming language version — the version of the language that was used to compile your binary.
- Build user — the creator of the binary. It can be also the name of your automation, e.g.
goreleaser-1.11.2
. - Related components' versions — the versions of the binaries that are used under the hood, e.g. Git, Docker. A good example, of collecting such information is Minikube (example).
Output options
The final aspect is how to present the data to the user. The most popular option is to print a human-readable message if no flags are specified.
However, for doing version check, options such as short, YAML, and JSON are useful.
Name | Description |
---|---|
--output= json | yaml | short | Displays the version information in the JSON format. |
--client-only | Displays only the client version. |
As a result, on a CI pipeline, we can easily detect a mismatched version:
EXPECTED_VERSION="1.42.0"
GOT_VER=$(golangci-lint version --format=short 2>&1)
if [[ "${GOT_VER}" != "${GOLANGCI_LINT_VERSION}" ]]; then
echo "✗ golangci-lint version mismatch, expected ${EXPECTED_VERSION}, available ${GOT_VER}"
exit 1
fi
An unusual finding for me was that Helm CLI supports Go templates. For example, --template='Version: {{.Version}}'
outputs 'Version: v3.2.1'. I personally don't see how it adds value if you already support the JSON and/or YAML output format. Do you find it useful?
Bells and whistles
The below practices stood out as a nice touch for me as a user.
Upgrade notice
Inform your user when a newer version was released. Additionally, you can detect what option was used for installing your CLI, e.g. brew
, apt
, etc., and suggest a command ready to copy-paste.
Let me show you a few examples:
-
GitHub CLI. The upgrade notice is enabled by default. To disable it, you need to export the
GH_NO_UPDATE_NOTIFIER
environment variable. It checks for new releases once every 24 hours, and displays an upgrade notice on stderr error if a newer version was found. Here is an example output:
A new release of gh is available: 2.5.1 → v2.5.2 To upgrade, run: brew update && brew upgrade gh https://github.com/cli/cli/releases/tag/v2.5.2
-
OpenFaaS CLI. The upgrade notice is enabled by default. To disable it, you need to specify the
--warn-update=false
flag. Here is an example output:
Your faas-cli version (0.14.5) may be out of date. Version: 0.14.6 is now available on GitHub.
-
Terraform CLI. The upgrade notice is always enabled, however, when the JSON format is used, it only shows the
terraform_outdated
property set totrue
.
Terraform v1.2.8 on darwin_amd64 Your version of Terraform is out of date! The latest version is 1.2.9. You can update by downloading from https://www.terraform.io/downloads.html
Version skew warning
Print a warning message if the difference between the CLI and related components is greater than the supported version skew.
The related components include but are not limited to the server that your CLI connects to, and/or other binaries that are used under the hood, such as Git, Docker, etc.
Summary
You may think: printing CLI version… isn't that obvious? Don't you have better things to do instead of thinking how to implement it? And yet, we can find a lot of different approaches. It bothered me sooooo much that I decided to do this research and structure my knowledge in this area. I hope, that you found it useful, too. Thanks for reading!
Bonus
If you use Go to create your CLI, you don't need to reinvent the wheel - I got your back! You can use the version
package, which was made to remove repetitiveness in implementing the version command.
The details about the CLIs that I analyzed can be found at mszostok/cli-analysis.
Top comments (0)