Plugging in to the Mattermost CLI

An overview of the Mattermost command line interface (CLI) and how I added plugin support to the CLI.

Santos
Aug 24, 2018 · 5 min read
Source: https://mattermost.com/about-us/

What’s in a command? 🔌

I spend part of my day using the command line to push code to GitHub or to run go run main.go for my side projects. Did you know Visual Studio Code comes with a terminal right in the editor? You’re welcome.

Mattermost, an open source Slack alternative, does not come with a terminal 😞 but it does come with a CLI. The CLI has been very useful to me throughout my open source contributions to the project. For example, I can run the following command on my terminal:

$ mattermost user create --email user@example.com --username userexample --password Password1

Voila. I can now log in to Mattermost with the created user. No need to create users through the UI. No need to write code to call the POST method on the /users endpoint. The Mattermost CLI is useful for administrators to configure and customize their installation. For more information, check out the documentation.

Cobra line interface 🐍

Mattermost supports client and server plugins to further customize the system. Plugins allow users to customize user interfaces. Want to launch Zoom meetings from the channel you’re in? There’s a plugin for that. I recently worked on an issue to control plugins using the Mattermost CLI. Administrators can now install, delete, and list plugins as they would using the UI.

The CLI provided by Mattermost uses Cobra. Cobra is a library that makes it easy to build command line interfaces. Let’s take a look at the code to set up the add command to install plugins:

Setting up a Cobra command

We create a new Command struct called PluginAddCmd. The fields in the struct define the usage and description of the add command:

  • Use is the one-line usage message.
  • Short is the short description shown in the ‘help’ output.
  • Long is the long message shown in the ‘help add’ output.
  • Example is an example of how to use the command.
  • RunE is the function that contains our command logic.

When the add command executes, pluginAddCmdF, the function denoted by RunE, will run. It contains logic to open a compressed plugin file and upload it. You can find the code for this command here. These fields are helpful for users who want to know how to use commands. Let’s find out how we can manage plugins by including the --help flag:

$ mattermost plugin --help
Help output for plugins

Now let’s find out how to use the add command:

$ mattermost plugin add --help
Help output to add a plugin

err on the side of caution 🚧

Handling errors in Go is different than other programming languages I have encountered. In Go, we communicate if an error has occurred by returning a separate value. One thing to keep in mind when checking for errors are types.

The following was my first implementation of the list command:

Incorrect implementation

This command retrieves and displays the active and inactive plugins. On line 8, GetPlugins returns two values — the plugin response and an error value. If the error value is not equal to nil, an error has occurred inside of GetPlugins. We return a friendly message to the user. If the error value is nil, then no error has occurred. We can proceed to process the plugin response and list the active and inactive plugins to the user.

I use the short variable declaration := when assigning variables. This is very handy in Go but led me to pulling my hair out while working on this issue. I thought the functionsInitDBCommandContextCobra and GetPlugins returned an error of the same type. I was wrong.

InitDBCommandContextCobra returns two values of the following type:

  • *app.App
  • error

GetPlugins returns two values of the following type:

  • *model.PluginsResponse
  • *model.AppError

On line 2, we set err to have an error type. When we try to re-use the err variable on line 8, we get the following error message: invalid memory address or nil pointer dereference. This is because GetPlugins returns a different type of error. We can fix this by using a new variable called appErr. Easy!

Correct implementation

Idiomatic, you say? 🗣️

Although I’ve been around the Go programming language for over a year now, I am still new to writing “idiomatic” Go. Here’s an example of a Go idiom that I see all the time but never understood:

Idiomatic Go code

Both versions of the code are correct. Yet, the second version is idiomatic. If there are no errors, we can proceed with the happy path that handles listing plugins. It also reads well and there are no extra levels of indentation since it omits the else. Debugging the first version of the code would also demand more context (e.g. how did we end up in the else block?).

I’ve worked at companies where two parts of the code base had similar logic written in two different ways. There was no standardized way of knowing which convention was right as a newcomer. Following established conventions allows us to write programs that other programmers can understand!

Go forth and open source 🐿️

I hope this post has provided you with new information on the anatomy of Mattermost commands. Special thanks to two core committers, Joram and Jesús. They answered my questions and helped get my changes merged. If you’re reading this and want to get in to open source, you should contribute to the Mattermost project. They’re always looking for help!

Notes 📝

You will need to generate the mattermost command executable used in this article. From the mattermost-server directory, run the following make command (depending on your platform):

$ make build-osx$ make build-linux$ make build-windows

The executable is in your go/bin path.

Santos

Written by

Santos

"Hay más tiempo que vida."

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade