Configuring sbt + JSLint with TeamCity

In this post I will shortly describe the configuration process of the JSLint SBT plugin and then the TeamCity build config that reacts on errors/warnings raised by this plugin.

Problem

In my current project we are working with rather non-standard technologies for a decent Java developer. Our backend runs on Scala and Scalatra and serves pure JSON to the frontend written in JavaScript and AngularJS. And as we are far from being seasoned JS developers, we want to have the quality of our JS codebase guarded by some automatic tool.

Toolbox

After some research it appeared that JSLint is for JS the same as FindBugs for Java so we decided to try it out in our project. Luckily there is a JSLint SBT plugin so we weren’t forced to carve our own solution.

Configuration

SBT-JSLint plugin

According to the documentation to configure the plugin we have to perform the two following steps:

  1. Add plugin declaration to Plugins.scala file

    object Plugins extends Build {
      lazy val plugins = Project(
        "plugins",
        file("."),
        settings = Defaults.defaultSettings ++
          Seq(addSbtPlugin("com.github.philcali" % "sbt-jslint" % "0.1.3"))
      )
    }
    
  2. Include plugin settings in Build.scala:

    import sbtjslint.Plugin._
    import sbtjslint.Plugin.LintKeys._
       
    // ...
      lazy val ui: Project = Project(
        "bootstrap-ui",
        file("bootstrap-ui"),
        settings = buildSettings ++ lintSettings
      )
    

But as usual these steps are not enough. First of all, when we run sbt jslint we will see that it scans some default directory looking for JS files to analyse. And of course our JavaScript lies in a completely different place. Secondly, we want to add the jslint step to the build process so it is executed every time. So simply, we need lintCustomSettings with declared custom JS location and the jslint step process added to the test step in our build:

val lintCustomSettings = lintSettings ++ inConfig(Test)(Seq(
    sourceDirectory in jslint <<= (baseDirectory)(_ / "src/main/webapp/app"),
    compile in Test <<= (compile in Test) dependsOn (jslint)
  ))

  lazy val ui: Project = Project(
    "bootstrap-ui",
    file("bootstrap-ui"),
    settings = buildSettings ++ lintCustomSettings
  )

Now after running sbt test we will see that JavaScript code is checked and the report is printed to the console and also written in target/jslint/results.xml file.

When you analyse report.xml you might be overwhelmed by the number of warnings raised by the plugin. Some of them are quite useful, but some might be ignored or, even better, disabled by custom flags. In our project we use something like:

flags in jslint ++= Seq("undef", "vars", "browser")

added to lintCustomSettings. For more flags and their descriptions please visit plugin documentation.

Team City config

Now, as we have our plugin running and generating report.xml, we could configure the TeamCity build to react on JS errors and warnings.

Simply go to Edit Configuration Settings of your build and then choose “Build Step: Command Line”:

tc-step1

And in the section Additional Build Features click “Add build feature”, choose “XML report processing”, then “JSLint” and specify the path to the report.xml file. Personally I also check “Verbose output” to see more details in the build log.

tc-step2

After saving we can go to the next build step named “Build failure conditions” and define two new rules there so we end up with something similar to the screen presented below:

tc-step3

And finally our build is fully configured to report and react on problems in JavaScript files:

tc-step4

Reference commits from my current project (no longer publicly available):

  • commit 1a2d764
  • commit c284aca (only the last modification in Build.scala)
Written by

Tomasz Dziurko

VP of Engineering at [SoftwareMill](https://softwaremill.com), a 100% remote software consultancy and part of the VirtusLab group. I build engineering organizations that scale without losing the people who make them work. [More about me]({{site.baseurl}}/about-me/)