Credo v1.1.0 released
TL;DR Credo is a static code analysis tool for the Elixir language with a focus on code consistency and teaching. It just got support for plugins, so that it can go beyond static in its analysis! Get it via Hex and on GitHub.
Why plugins?
Credo’s focus has always been the static analysis of Elixir source code. That is its domain and purpose.
The decision to focus on static analysis alone, of course, kept us from implementing several features the community asked for over the years (Dialyzer integration and Autofixing for Credo Issues come to mind).
Credo Plugins will give plugin authors all the tools Credo itself uses to prepare & handle the analysis and facilitate & output its results. This means two things:
First, the community can now integrate other tools into Credo’s analysis. Want to incorporate Dialyzer results into your report? You can write a plugin for that.
Second, people will now be able to not only implement custom checks (which has been possible since Credo v0.4.0
), but also create very specific integrations around their project/company/team.
Let me give you an example
Your team has a convention that GitHub usernames have to be mentioned in TODO
comments like this
# @rrrene TODO: Find a better example!!!
Now you want to write a check that ensures that these people still work for you (e.g. are in your GitHub org). This is easy to do: Just write a custom check and poll the GitHub API with each found username and see if they are part of your org.
The only problem is that each of your projects has around 500 source files containing an average of one TODO
comment.
If you want to validate the org-affiliation for each of these, you will make a lot of API requests.
With Credo Plugins, you can now create a plugin that contacts the GitHub API when the plugin is initialized and puts all the usernames into Credo’s Execution
token, where your custom check can find it.
Plugins can attach themselves to each phase of Credo’s execution. They can add and override commands, provide their own command line switches, modify Credo’s default config and of course add new checks, which can add their own issues, with their own categories.
Using plugins
Plugins are just modules. Most of the time, a Credo plugin will be published on Hex. You include it as a dependency:
{:credo_demo_plugin, "~> 0.1.0"},
Plugins, like checks, are just modules and functions.
They can be included by listing them under the :plugins
field in Credo’s configuration file.
%{
configs: [
%{
name: "default",
plugins: [
{CredoDemoPlugin, []}
]
}
]
}
Configuring plugins
Plugins can be configured via params, just like checks. Each entry consists of a two-element tuple: the plugin’s module and a keyword list of parameters, which can be used to configure the plugin itself.
%{
configs: [
%{
name: "default",
plugins: [
{CredoDemoPlugin, [castle: "Grayskull"]}
]
}
]
}
Plugins can be deactivated by setting the second tuple element to false
.
%{
configs: [
%{
name: "default",
plugins: [
{CredoDemoPlugin, []},
{CredoYetAnotherPlugin, false} # <-- don't load this for now
]
}
]
}
Creating a plugin
A plugin is basically just a module that provides an init/1
callback.
defmodule CredoDemoPlugin do
def init(exec) do
# but what do we do here??
exec
end
end
The Credo.Plugin
module provides a number of functions for extending Credo’s core features.
defmodule CredoDemoPlugin do
@config_file File.read!(".credo.exs")
import Credo.Plugin
def init(exec) do
exec
|> register_default_config(@config_file)
|> register_command("demo", CredoDemoPlugin.DemoCommand)
|> register_cli_switch(:castle, :string, :X)
|> append_task(:convert_cli_options_to_config, CredoDemoPlugin.ConvertCliSwitchesToPluginParams)
|> prepend_task(:set_default_command, CredoDemoPlugin.SetDemoAsDefaultCommand)
end
end
With these few functions, you can achieve everything decribed above.
For a more detailed description on how those functions work, take a look at Credo’s README.