Free continuous Integration for modern Android apps with CircleCI

    Free continuous Integration for modern Android apps with CircleCI
    Cover image: “Skyfall” by Edoardo Brotto — on flickr

    Most developers nowadays see the value in having a continuous integration (CI) environment, but many projects still don’t take advantage of it. Get a nice setup  —  for free!  —  for your projects using CircleCI and a set of free and open source tools.

    Is CI attainable on a zero budget project?

    Most developers will agree that for our craft, the ideal scenario is having your project being attached to a CI machine, and have that generating a nice report for every commit you and your team push.

    For example, at Novoda we have a Jenkins instance running our CI jobs, that gathers useful information in the build page details:

    Ruh roh, somebody upset Checkstyle!

    That’s very useful, and the great bit about it is that Jenkins is free and open source, so anybody can set it up and run it as their CI and get the same results. Unfortunately, for most developers working on hobby projects in their (usually very scarce) spare time, and for many solo developers, Jenkins has a few drawbacks.

    First of all, it takes a lot of time and skills to set it up and keep it running, up-to-date and secure. And you need a fast computer too, that is always on and connected to a fast network. It’s not something most people have or could get for their side projects. The result is that many hobby and open source projects get no CI love.

    That’s exactly the situation I faced when I started working on Squanchy. Back then, I did what I usually do when I want to work on a nontrivial open source project, which is to take advantage of the free tier the awesome people at Travis CI offer for open source projects. For a GitHub project, it’s a very simple set up process too. The great thing about Travis is that it’s free, easy to set up, and it’s zero maintenance.

    Why not just using Travis then?

    I have used Travis, and I like it, but there’s some tradeoffs you need to be ok with:

    • It’s very barebones and streamlined (which is not a bad thing)
    • Because of its streamlined and easy to use nature, it’s not very flexible and might be limiting you in what you can achieve
    • Has no way to generate report pages and, to the best of my knowledge, has no artifact archive for past builds
    • Builds on Travis are very slow, and their caching doesn’t help much either (plus, they explicitly recommend not using it for the worst offenders, such as the Android SDK)

    At the end of the day, whether those are a concern for you or not depends on what your goals are. I made do for a few months on Squanchy, because having some CI was better than having no CI, and because we had no time to improve the situation.

    The Squanchy project was very much time-constrained: our hard deadline was the week before Droidcon Italy 2017, at the beginning of April, and we had begun working in January. Because of that, our build until very recently was simply checking that the codebase would build correctly, and the (shamefully scant) unit test suite was passing.

    Still, the setup we had had always nagged me; the total lack of static analysis was making me very nervous about the quality of the code we were shipping — and, turns out, rightly so. Adding static analysis to the CI build uncovered some subtle bugs we had missed.

    We could’ve just enabled static analysis, kept the Travis setup as it was, and called it a day. Instead, curious because of the awesome reputation CircleCI got from its users, and aggravated by the slow Travis builds that had been a bane from day one, I decided to give CircleCI a spin. After all, adding static analysis on top of the already slow basic build we had on Travis was only going to make it worse.

    On top of that, CircleCI offers up to 4 free Linux containers for Open Source projects (up to 2 macOS containers if you need to do iOS/macOS). If your project is hosted on a public GitHub or Bitbucket repo, this is a great improvement over the 1 free Linux container they offer for private repos. In case you were wondering, a container is, in a nutshell, a VM that can run a build, or part of it — we’ll go into details in the next part of the series.

    Static analysis, a developer’s best friend

    Before jumping on CircleCI, I realised I needed to beef up the Squanchy build to include static analysis tools.

    Static analysis tools are very, very important; they perform a series of analyses on the sources and the bytecode, and spit out a series of code style violations, potential bugs, improvements and best practices to apply. This means that by running static analysis tools you can increase the confidence that your code is in tip-top shape and avoid bugs.

    Every Android developer knows at least a static analysis tool: the almighty Android Lint. It checks your code and resources, looking for places where you might be misusing Android APIs, have unused resources, risk crashes, etc. But Lint is hardly the only static analysis tool an Android developer should have in their toolbox.

    Fun fact: Lint gets its name from the homonymous tool that was introduced with Unix v7 in 1979, and checked C sources for suspicious code.
    source

    There’s several very well known static analysis tools for Java. The most widely used are Checkstyle, PMD and Findbugs. There’s plenty more, including some, like ErrorProne, that replace the javac compiler altogether and checks at compile time. In this series, we’ll focus on the most common trifecta: Checkstyle, PMD and Findbugs.

    For Squanchy, I have used the usual ruleset we apply in Novoda for those tools. Using the Novoda Gradle static analysis plugin it takes very little effort to set up all these tools. In fact, everything is done in the static-analysis.gradle file. You can check out the rules we use by looking into the team-props/static-analysis folder in the Squanchy repo.

    What about Kotlin?

    The eagle-eyed amongst you will notice that I explicitly specified that those static analysis tools are for Java. That was indeed a necessary point to make, since they (mostly) don’t work with Kotlin, and that’s a shame because after this year’s Google I/O there is literally no excuse not to use Kotlin anymore.

    Java source code checkers such as Checkstyle usually don’t have a parser for Kotlin and won’t be able to build an AST, which in turn means they won’t be able to perform any check. Bytecode checkers might, or might not, work, depending on what JVM bytecode the Kotlin compiler produces. While it’s always valid JVM code, the optimised bytecode kotlinc produces can at times be assumed to come from poorly written Java by those checkers.

    Unfortunately, there is not much to do there other than making sure you exclude all Kotlin code from those tool’s checks. For example, in Squanchy, we have these exclusions in our static-analysis config folder:

    <!DOCTYPE module PUBLIC
      "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
      "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
    
    <module name="Checker">
      <!--  ...  -->
      
      <!-- Use suppressions from checkstyle-suppressions.xml -->
      <module name="SuppressionFilter">
        <property name="file" value="team-props/static-analysis/checkstyle-suppressions.xml" />
      </module>
      
      <!-- ... -->
    </module>
    static-analysis/checkstyle-modules.xml
    <?xml version="1.0"?>
    
    <!DOCTYPE suppressions PUBLIC
      "-//Puppy Crawl//DTD Suppressions 1.1//EN"
      "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
    
    <suppressions>
    
      <!-- Exclude all Kotlin files -->
      <suppress checks=".*" files=".*\.kt" />
    
      <!-- ... -->
    
    </suppressions>
    static-analysis/checkstyle-suppressions.xml
    <FindBugsFilter>
    
      <!-- Exclude all Kotlin files -->
      <Match>
        <Source name="~.*\.kt" />
      </Match>
    
      <!-- ... -->
    
    </FindBugsFilter>
    static-analysis/findbugs-excludes.xml
    <ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      name="Squanchy PMD rules"
      xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
      xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
    
      <description>PMD rules for the Squanchy codebase.</description>
    
      <!-- Exclude all Kotlin files -->
      <exclude-pattern>.*\.kt</exclude-pattern>
      
      <!-- ... -->
      
    </ruleset>
    static-analysis/pmd-rules.xml

    This leaves us without any static analysis for Kotlin sources, though. Luckily, there are some static analysis tools for Kotlin (although still very much in their infancy). The one that seems to be the most active and the most mature is Detekt. At the time of writing it has not reached version 1.0 yet but it’s burning fast through milestone builds, heading towards the first stable build.

    Detekt offers both a static analysis checker, and a code formatter. The latter is inspired by and shares its rules with KtLint, but Detekt also offers the ability to use IDEA’s CLI interface to perform the formatting, as of 1.0.0.M11.

    Detekt was not supported by the Novoda Gradle static analysis plugin before v0.5, and there is still no first party integration by Gradle. This could be a problem, but luckily it has a convenient Gradle plugin that makes it very easy to integrate in your builds:

    plugins {
        id "io.gitlab.arturbosch.detekt" version "1.0.0.M11"
    }
    
    detekt {
        input = "./src/main/java"
        config = "$rootDir/team-props/static-analysis/detekt-config.yml"
        filters = ".*test.*, .*/resources/.*"
        output = true   // The default is to only output to the command line!
        report = "$project.projectDir/build/reports"
        
        // Note: even though it’s the recommended way to format code as of 1.0.0.M11,
        // I did not include the idea task configuration as it won’t work on the CI
        // we have set up anyway given there is no IDEA/Android Studio installed there.
    }
    app/build.gradle

    You can check out Squanchy’s Detekt configuration here. It’s a slightly modified version of the default configuration that the Detekt author provides.

    With this in place, we can be confident that all our code is indeed covered by static analysis, both in Java and Kotlin.

    Next up: configuring CircleCI

    Now that we have all our (code)bases covered  —  sorry about the bad pun  —  we can perform a full check of static analysis and tests in our project; you can verify from the console output that it is actually running all the checks:

    $ ./gradlew check
    ...
    :app:detektCheck
    Analyzing 36 kotlin files: ....................................
    ...
    detekt run within 2002 ms
    ...
    :app:checkstyleDebug
    :app:collectCheckstyleDebugViolations
    :app:checkstyleMain
    :app:collectCheckstyleMainViolations
    :app:findbugsDebug
    ...
    :app:generateFindbugsDebugHtmlReport
    :app:collectFindbugsDebugViolations
    :app:pmdDebug
    :app:collectPmdDebugViolations
    :app:pmdMain
    :app:collectPmdMainViolations
    :app:evaluateViolations
    ...
    :app:lint
    Ran lint on variant release: 0 issues found
    Ran lint on variant debug: 0 issues found
    ...
    :app:test
    :app:check
    :check
    
    BUILD SUCCESSFUL

    In the next part of this series, we’ll dive into the simple process of setting up, and the not-so-simple process of configuring our CI jobs on CircleCI.

    Thanks to Lorenzo Quiroli, Benoit Quenaudon and Francesco Pontillo for proofreading.

    Sebastiano Poggi

    Sebastiano Poggi

    “It depends” 🤷‍♂️ — Google Developer Expert for Android, Flutter and Identity. A geek 🤓 who has a serious thing for good design ✨ and for emojis 🤟working at JetBrains (opinions my own)

    London and elsewhere https://sebastiano.dev