Linting KMM Android Application with Danger

Linting KMM Android Application with Danger

A starter guide for adding the static code analyzer for a Kotlin Multiplatform Mobile Android application, with Danger.
Pisit Wetchayanwiwat
Pisit Wetchayanwiwat
July 06, 2023
Android Mobile

Table of Contents

Introduction

Static analysis is the action of analyzing code without execution. Lint is the most common form of static analysis. The results of lint are warnings and errors for code quality improvement. Linting has evolved to be a crucial part of continuous development.

In this article, we will walk through some concepts of static code analysis on Kotlin Multiplatform Mobile (KMM) Android, how to locally lint, how to integrate linting into a Continuous Integration(CI) flow (using GitHub Actions), and eventually, publish a summary report on every Pull Requests.

Danger on a pull request
Danger on a pull request

Why Lint

Linting is an efficient and quick way to ensure the code conforms to the team’s quality standards. Lint warnings are visible right on the code with supported IDEs, shown with Git Hooks, or automatically commented on the pull request.

Linting is blazingly fast, takes less than 10 seconds, and can be easily integrated into any workflow. The earlier we can find issues with code quality, the quicker we can fix the mistake and lessen the impact on the whole project.

How to Lint

Linting on a KMM project uses the built-in Gradle command:

./gradlew lint

This command will generate lint-results files, one for each module. The default modules in KMM are androidApp and shared, so two reports will be generated.

Lint report files
Lint report files

The settings for ./gradlew lint can be customized in a lint.xml file.

For example, to ignore the GradleVersion warnings from the template, create a lint.xml in the project’s root with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <issue id="GradlePluginVersion" severity="ignore" />
    <issue id="GradleDependency" severity="ignore" />
</lint>

Lint with Danger

What is Danger

Linting in continuous integration & continuous development (CI/CD), using just Gradle, will only show the output in the pipeline’s log. This is where Danger comes in. Danger is a tool for commenting on CI/CD’s outputs directly on a pull request’s code changes. Danger makes code review easier and encourages following code conventions more.

💡 Danger only works with CI services and can only post to version control (GitHub, Bitbucket & GitLab) or email. Feel free to skip Danger-related content if your team does not use CI.

Lint with Danger

Set up Danger Instruction

To start using Danger, include Danger in the project’s Gemfile, and create a file name Gemfile in the root of the project with the following content:

source "https://rubygems.org"

# Other gems
gem 'danger'
gem 'danger-android_lint'

💡 Danger uses Ruby, make sure the CI service has Ruby pre-installed.

Create a new Dangerfile at the root of the project. The Dangerfile contains Danger’s instructions on what to check and comment on.

Add the Android Lint report to the Dangerfile:

# Android Lint output check
lint_dir = '**/build/reports/lint-result-debug.xml'
Dir[lint_dir].each do |file_name|
  android_lint.skip_gradle_task = true
  android_lint.report_file = file_name
  android_lint.lint(inline_mode: true)
end

This script finds all generated lint-result-debug.xml in the project and publishes the Android Lint result.

💡 This is a minimum setup for Danger to work with Android Lint. For a comprehensive Danger setup with PR lint, please check out our Android Template for reference.

💡 For a more detailed setup guide, see the instructions on the Danger website.

Set up CI/CD for Danger

Include the following steps to your CI workflow:

  • Run Lint to generate the report ./gradlew lint.
  • Install Ruby and Danger bundle install.
  • Execute Danger bundle exec danger.

Make sure to execute Danger as the last step to ensure all outputs are ready.

The following example workflow is for GitHub Actions:

- name: Run Android Lint
  run: ./gradlew lint
- name: Set up Ruby
  uses: actions/setup-ruby@v1
  with:
    ruby-version: '2.7'
- name: Install Bundle and check environment versions
  run: bundle install
- name: Run Android Danger
  env:
    DANGER_GITHUB_API_TOKEN: $
  run: bundle exec danger

💡 DANGER_GITHUB_API_TOKEN is the token from a bot account or a user’s account. Danger will use the included account for commenting on the pull request. There is a detailed instruction in the Danger official tutorial on how to set up one.

This workflow will generate and output the lint report on the pull request.

Linting with Danger on a pull request
Linting with Danger on a pull request

How to Lint with detekt

What is detekt

detekt is a static code analyzer (linter) for Kotlin. detekt’s rules cover many rules that Android Lint lacks and can be considered the missing piece in a KMM project.

Using detekt

Add detekt to the project by adding the dependency inside ./build.gradle.kts.

buildscript {
    ...
    dependencies {
        ...
        classpath("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.21.0")
    }
}

In each of the Kotlin modules, such as shared and androidApp, include the detekt plugin in the build.gradle.kts.

plugins {
    ...
    id("io.gitlab.arturbosch.detekt")
}

At the end of the module’s build.gradle.kts, add the source files directories.

detekt {
    source = files(
        "./"
    )
    // config = files("./detekt-config.yml") // Use to define Detekt rules.
}

💡 See more detekt configurations from the document.

Run detekt with the Gradle command ./gradlew detekt. Each module will have detekt report files and directory.

detekt report files
detekt report files

detekt with Danger

Showing detekt report on the pull request is similar to showing Android Lint report with the support of another danger gem.

First, add the danger-kotlin_detekt gem to the Gemfile.

source "https://rubygems.org"

gem 'danger'
# Other gems
gem 'danger-kotlin_detekt'

Then add the detekt block to Dangerfile.

# Other blocks
# ...

# Detekt output check
detekt_dir = "**/build/reports/detekt/detekt.xml" 
Dir[detekt_dir].each do |file_name|
  kotlin_detekt.skip_gradle_task = true
  kotlin_detekt.report_file = file_name
  kotlin_detekt.detekt(inline_mode: true)
end

This new block will scan for build/reports/detekt/detekt.xml in each module and publish detekt’s generated reports on the pull request.

Ensure to include ./gradlew detekt in the CI workflow as in this GitHub Actions example.

- name: Run Detekt
  run: ./gradlew detekt
- name: Run Android Lint
  run: ./gradlew lint
...
- name: Run Android Danger
  env:
    DANGER_GITHUB_API_TOKEN: $
  run: bundle exec danger
detekt with Danger on a pull request
detekt with Danger on a pull request

💡 detekt’s default configuration can fail the CI pipeline when the number of errors exceeds the threshold. To allow detekt to run without failing the pipeline, configure the setting ignoreFailures = true.

// Disable fail on error threshold.
detekt {
    source = files(
        "./"
    )
    ignoreFailures = true
}

Conclusion

Linting and code coverage are simple tools that can drastically improve your CI/CD pipeline. But having a summary report for all of them would not be possible without Danger.

Using Danger to publish the lint and coverage report to a pull request helps reduce your team’s workload and ensures that the code’s structure follows the agreed format.

detekt and Danger are also easy to set up for both legacy and new projects. So if you have yet to try them out, give them a try.

To learn more about code coverage with Danger, make sure to read the upcoming KMM Android Code Coverage blog post or read about KMM iOS lint and KMM iOS code coverage.

References

Android Lint
detekt
Danger
GitHib Actions

If this is the kind of challenges you wanna tackle, Nimble is hiring awesome web and mobile developers to join our team in Bangkok, Thailand, Ho Chi Minh City, Vietnam, and Da Nang, Vietnam✌️

Join Us

Recommended Stories:

Accelerate your digital transformation.

Subscribe to our newsletter and get latest news and trends from Nimble