Rubocop has landed on Discourse 👮‍♀️ 👮

What is Rubocop?

In short,

RuboCop is a Ruby static code analyzer. Out of the box it will enforce many of the guidelines outlined in the community Ruby Style Guide.

Why do we need it?

We’ve started to notice a divergent in the coding styles used across our Ruby files as our code base continues to grow and new inconsistencies are constantly being introduced. Without using a linter, it is hard for us to maintain and communicate the coding style that the Discourse team would like to follow. With Rubocop, the coding style is being policed automatically by these set of rules defined in .rubocop.yml.

How do I run it?

In the root directory of your Discourse folder, simply run

bundle exec rubocop


bundle exec rubocop --parallel

for faster execution.

Integrations with your code editor


Sublime Text


Visual Studio Code


Maybe I’m the only one who uses the One True Editor, but there is also an emacs Rubocop package.


A note for Atom users.

The only way I’ve found to get linter-rubocop in Atom working for a non-core plugin is to copy the entire core .rubocop.yml and add it to the plugin repo.

The same applies to linter-eslint, i.e. you need to add .eslintrc to the root of your plugin repo, duplicating the core .eslintrc.

Even if your plugin is in your discourse/plugins folder locally, Atom will not consider it part of your “project” and will not run the relevant linter in the files in the plugin (the files in core Discourse will lint just fine).

If anyone else is using Atom to build non-core plugins and has a way to support linter-rubocop and linter-eslint without replicating the core linter config in each plugin repo I’d be interested.


I just checked and I have linting errors in my current discourse-chronos project. I have done nothing special, my workflow is to symlink the plugin folder and not copying it:

huh interesting. Using symlinks works for me too. Moving a plugin folder inside plugins definitely prevents it from working though. As I work with many different combinations of non-core plugins I hadn’t been bothered to create symlinks every time I moved stuff around, but it looks like I should.

Would you mind trying something for me? Remove the symlink and copy the discourse-chronos folder into discourse/plugins. Do the linters still pick up the core config in discourse-chronos files? Maybe it’s my Atom config.

** edit ** It’s probably because non-core plugin files are .gitignored in core, which prevents the core linter from running, but if the folder is symlinked it is not ignored, even though the link is inside a folder that is ignored. Somewhat confusing, but makes some sense.

** edit 2 ** Ah but if you just symlink, how do you run plugin tests locally? bundle exec rake plugin:spec['discourse-elections'] doesn’t work.

** edit 3 ** Solution: if you want to keep your non-core plugin files in the Discourse tree itself (i.e. actual folder, not a symlink), the way to get the linter in non-core plugin files to work is to uncheck Exclude VCS Ignored Paths in the Atom core config. The base linter package inherits this setting. The downside is that all other ignored files will be linted as well.