DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,155 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for CLI version: a collection of handyΒ tips
Mateusz Szostok
Mateusz Szostok

Posted on • Originally published at szostok.io

CLI version: a collection of handyΒ tips

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.

Pretty version

source: version.szostok.io

Flag or Command?

Before we discuss what we should display, let's talk about how our user can get the CLI version.

flags-or-commands

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.


bubbles

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).

chat

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
Enter fullscreen mode Exit fullscreen mode

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?


diamond

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:

  1. 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
    
  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.
    
  3. Terraform CLI. The upgrade notice is always enabled, however, when the JSON format is used, it only shows the terraform_outdated property set to true.

    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!

Follow @m_szostok on Twitter to get the latest news.

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.

preview

Top comments (0)

πŸŒ™ Dark Mode?!

Turn it on in Settings