Plugging in to the Mattermost CLI
An overview of the Mattermost command line interface (CLI) and how I added plugin support to the CLI.
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 firstname.lastname@example.org --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:
We create a new Command struct called PluginAddCmd. The fields in the struct define the usage and description of the
- 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.
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
$ mattermost plugin --help
Now let’s find out how to use the
$ mattermost plugin add --help
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
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 functions
GetPlugins returned an error of the same type. I was wrong.
InitDBCommandContextCobra returns two values of the following type:
GetPlugins returns two values of the following type:
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!
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:
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
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!
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