Stop saying “you forgot to …” in code review (Danger Dart Edition)

HelloCore
6 min readApr 5, 2022

Have you ever forgotten something in your pull request, what if I told you there is a tool helping with that?

I’d like to present Danger (Dart Edition)

Danger is a tool that runs on CI to validate specified rules on Pull Request. There are multiple versions of Danger, for example, Ruby, JS, Kotlin, Swift, Python, and Dart.

Danger runs as the last step of CI build for Pull Request. It reads your dangerfile, processes it, and comments (or inline-comment) on the pull request.

Personally, I’ve been using Danger for a while (JS version for 2 years, and Dart version for a year), and I found that there are common rules I used in multiple projects.

  • Lint check (Display analyze results on the comment)
  • Code Formatter
  • Display golden image changes (previously, on Github, I need to click on view file to view image changes, so having all modified images in the comment is pretty useful)
  • Prefer clock.now() instead of DateTime.now()
  • Prefer testGoldensWithClock() instead of testGoldens()
  • Display test results (especially failure cases)
  • Display code coverage
  • Display code coverage compared with target branch (manually implement, inspired by codecov.io)

We can validate whatever we wanted as long as it can be described in the code.

Fully functional Danger Dart on Bitbucket Cloud

How to use Danger Dart

  1. You need to install Danger JS because Danger Dart uses Danger JS under the hood.
$ npm install -g danger

2. Activate Danger Dart on pub

$ pub global activate danger_dart

3. Add danger_core in dev_dependencies

dev_dependencies:
danger_core:

4. Create dangerfile.dart on the root project, this will be the main file for Danger Dart

// @dart=2.10
import 'package:danger_core/danger_core.dart';

void main() {
if (danger.github.pr.title.contains('WIP')) {
warn('PR is considered WIP');
}
}

5. Try with your pull request

$ danger_dart pr https://your_pull_request_link

As expected, it shouldn’t work, because you need environment variables for Danger to know your repository.

For Github, all you need is GITHUB_TOKEN (can be DANGER_GITHUB_API_TOKEN as well). You can create one here.

$ export DANGER_GITHUB_API_TOKEN='blablabla'

For BitbucketCloud, you have 2 options (read more)

1. DANGER_BITBUCKETCLOUD_USERNAME and DANGER_BITBUCKETCLOUD_PASSWORD . The username you can get from the profile page, and you can create App Password for this tool (Requires Read Pull Requests and Read Account permissions)

$ export DANGER_BITBUCKETCLOUD_USERNAME='blablabla'
$ export DANGER_BITBUCKETCLOUD_PASSWORD='blablabla'

2. DANGER_BITBUCKETCLOUD_OAUTH_KEY and DANGER_BITBUCKETCLOUD_OAUTH_SECRET . It can be created by going to Settings > OAuth > Add consumer and put https://bitbucket.org/site/oauth2/authorize on Callback URL (Also requires Read Pull Requests and Read Account permissions)

$ export DANGER_BITBUCKETCLOUD_OAUTH_KEY='blablabla'
$ export DANGER_BITBUCKETCLOUD_OAUTH_SECRET='blablabla'

For GitLab, it requires either DANGER_GITLAB_API_TOKEN or DANGER_GITLAB_API_OAUTH_TOKEN . Please refer to this.

$ export DANGER_GITLAB_API_TOKEN='blablabla'

For Bitbucket Server, it’s not supported yet (because I don’t personally use it), but it won’t take much time to implement as DangerJS is already supported (can be requested here).

After you have needed environment variables, you can run the previous command. It will not comment anything on the pull request because pr is the command for testing your dangerfile, so it will only print messages on your console.

Next, you need to config it on CI, there are multiple supported CI. You can see the list here.

For example, with Github Actions, you can easily integrate with the following steps. (GITHUB_TOKEN is already provided for Github Actions, so you don’t need to manually create it)

- uses: actions/setup-node@v1- uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603- name: Install Danger-js
run: npm install -g danger
- name: Activate command
run: dart pub global activate
- name: Run danger ci
run: danger_dart ci
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

For other CI, you can manually install and run it. This is what I used on Bitrise.

#!/usr/bin/env bash
npm install -g danger
echo "Danger JS Version"
danger --version
if ! command -v danger_dart &> /dev/null
then
echo "Install danger_dart"
dart pub global activate danger_dart
else
echo "Skip installing danger_dart"
fi
export PATH="$PATH":"$HOME/.pub-cache/bin"

danger_dart ci --verbose

After you create a pull request and let your CI builds, you will see a comment from Danger.

A comment from Danger Dart on Github

Now, you can add rules as many as you wanted.

How to write Danger rules

In dangerfile.dart , after importing import 'package:danger_core/danger_core.dart' , you will be able to access danger , it will be a singleton object that provides metadata of the pull request.

For example, danger.git.modifiedFiles , this will give you access to the list of all modified file paths in this pull request.

danger.github.pr.title , this will be the title of this pull request. (only available if you run on Github)

For more information, please check https://github.com/danger/dart/blob/master/packages/danger_core/lib/src/models/danger_dsl.dart

When you want to comment on the pull request, you can call functions message , warn , fail , or markdown . Each type has a different icon, for example, fail will be ❌, or warn will be ⚠️ (and it can be customized)

Moreover, if you use fail , it will make this step on the CI fail, so it’s pretty useful if you want to prevent the pull request from merging.

For inline comment, you need to give parameters file and line .

// @dart=2.10
import 'dart:io';

import 'package:danger_core/danger_core.dart';
void main() {
message('Hello World');
fail('Please fix this', file: 'README.md', line: 1);
}

Plugin

You can use rules from others or create a reusable rule and share it, some rules are already implemented.

DangerPluginDartTest

This is a plugin to read, parse the test results, and display them on the pull request, but you need to run tests and keep test results in JSON format.

$ flutter test --reporter json | tee your_test_results.json

After that, you need to add danger_plugin_dart_test to dev_dependencies , and call DangerPluginDartTest.processFile

// @dart=2.10
import 'dart:io';

import 'package:danger_core/danger_core.dart';
import 'package:danger_plugin_dart_test/danger_plugin_dart_test.dart';

void main() {
final testResultFile = File('your_test_results.json');
DangerPluginDartTest.processFile(testResultFile);
}

After Danger found failure cases, it will comment on the pull request like this.

Failure test case report on Bitbucket Cloud

Display Code Coverage

I haven’t created a plugin for this yet due to my laziness (could be in the future).

To use this, you need to run tests with --coverage

$ flutter test --coverage --reporter json | tee your_test_results.json

Add lcov_dart to dev_dependencies

Copy this file to tools/danger_coverage_report.dart

And inside dangerfile.dart you will be able to call DangerCodeCov.process

// @dart=2.10
import 'dart:io';

import 'package:danger_core/danger_core.dart';
import 'tools/danger_coverage_report.dart';

void main() {
DangerCodeCov.process(File('./coverage/lcov.info'));
}
Test coverage report on Github

PS.1. This code only supports Github, if you want to use it with another provider, you need to modify the link (line 31)

PS.2. This code will display only 30 least code coverage files, you can modify it to display only changed files. (File list can be filtered by danger.git.createdFiles and danger.git.modifiedFiles on line 106)

Conclusion

Danger is a tool to allow creating rules to validate pull requests, it reduces code review time.

Rules can be reused in other projects, or shared with other people.

Danger also supports multiple languages. For Danger Dart, you can create an issue here if you find a bug or any improvement.

PS. if you like this project, please don’t forget to click the ⭐️ button on Github

--

--

HelloCore

Just a bear who loves coding. Everything Developer, General Bae(เบ๊).