Danger logo hero@2x

What is Danger JS?

Danger runs during your CI process, and gives teams the chance to automate common code review chores.

This provides another logical step in your build, through this Danger can help lint your rote tasks in daily code review.

You can use Danger to codify your teams norms. Leaving humans to think about harder problems.

This happens by Danger leaving messages inside your PRs based on rules that you create with JavaScript or TypeScript.

Over time, as rules are adhered to, the message is amended to reflect the current state of the code review.

after Created with Sketch. PUSH CODE LINT TESTS DANGER HUMAN REVIEWS

How does it work?

Danger is a NPM module that evals a Dangerfile. You set up a Dangerfile per-project. The Dangerfile contains a collection of home-grown rules specific to your project as a dangerfile.js or dangerfile.ts.

Danger can be installed via NPM or Yarn.

Use yarn add danger -D to add it to your package.json.

You can integrate Danger into your own project using AppCenter, Bamboo, BitbucketPipelines, Bitrise, BuddyBuild, BuddyWorks, Buildkite, Circle, Cirrus, CodeBuild, Codefresh, Codemagic, Codeship, Concourse, DockerCloud, Drone, GitHubActions, GitLabCI, Jenkins, Netlify, Nevercode, Screwdriver, Semaphore, Surf, TeamCity, Travis, VSTS.

You would then need to generate a GitHub access token or a BitBucket Server user, and expose some credentials as ENV vars. Then add `yarn danger ci` to your CI test run.

The Getting Started guide covers this in more detail.

 

// Import the feedback functions
import { message, warn, fail, markdown } from "danger"

// Add a message to the table
message("You have added 2 more modules to the app")

//  Adds a warning to the table
warn("You have not included a CHANGELOG entry.")

// Declares a blocking 
fail(`ESLint has failed with ${fails} fails.`)

// Show markdown under the table:
markdown("## New module Danger" + dangerYarnInfo)
Get Started

What does this Look like?

// Add a CHANGELOG entry for app changes
const hasChangelog = danger.git.modified_files.includes("changelog.md")
const isTrivial = (danger.github.pr.body + danger.github.pr.title).includes("#trivial")

if (!hasChangelog && !isTrivial) {
  warn("Please add a changelog entry for your changes.")
}
const packageChanged = danger.git.modified_files.includes('package.json');
const lockfileChanged = danger.git.modified_files.includes('yarn.lock');
if (packageChanged && !lockfileChanged) {
  const message = 'Changes were made to package.json, but not to yarn.lock';
  const idea = 'Perhaps you need to run `yarn install`?';
  warn(`${message} - <i>${idea}</i>`);
}
var bigPRThreshold = 600;
if (danger.github.pr.additions + danger.github.pr.deletions > bigPRThreshold) {
  warn(':exclamation: Big PR (' + ++errorCount + ')');
  markdown('> (' + errorCount + ') : Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.');
}
// Always ensure we assign someone, so that our Slackbot can do its work correctly
if (pr.assignee === null) {
  fail("Please assign someone to merge this PR, and optionally include people who should review.")
}
const hasAppChanges = modifiedAppFiles.length > 0;

const testChanges = modifiedAppFiles.filter(filepath =>
  filepath.includes('test'),
);
const hasTestChanges = testChanges.length > 0;

// Warn if there are library changes, but not tests
if (hasAppChanges && !hasTestChanges) {
  warn(
    "There are library changes, but not tests. That's OK as long as you're refactoring existing code",
  );
}
// Check that every file touched has a corresponding test file
const correspondingTestsForAppFiles = touchedAppOnlyFiles.map(f => {
  const newPath = path.dirname(f)
  const name = path.basename(f).replace(".ts", "-tests.ts")
  return `${newPath}/__tests__/${name}`
})

// New app files should get new test files
// Allow warning instead of failing if you say "Skip New Tests" inside the body, make it explicit.
const testFilesThatDontExist = correspondingTestsForAppFiles.filter(f => fs.existsSync(f))
if (testFilesThatDontExist.length > 0) {
  const callout = acceptedNoTests ? warn : fail
  const output = `Missing Test Files:
    ${testFilesThatDontExist.map(f => `  - [] \`${f}\``).join("\n")}
    If these files are supposed to not exist, please update your PR body to include "Skip New Tests".
  `
  callout(output)
}
var globalFile = 'Rx.js';
var minFile = 'Rx.min.js';

function sizeDiffBadge(name, value) {
  var color = 'lightgrey';
  if (value > 0) {
    color = 'red';
  } else if (value < 0) {
    color = 'green';
  }
  return 'https://img.shields.io/badge/' + name + '-' + getFormattedKB(getKB(value)) + 'KB-' + color + '.svg?style=flat-square';
}

//post size of build
schedule(new Promise(function (res) {
  getSize('./dist/cjs', function (e, result) {
    var localGlobalFile = path.resolve('./dist/global', globalFile);
    var localMinFile = path.resolve('./dist/global', minFile);

    //get sizes of PR build
    var global = fs.statSync(localGlobalFile);
    var global_gzip = gzipSize.sync(fs.readFileSync(localGlobalFile, 'utf8'));
    var min = fs.statSync(localMinFile);
    var min_gzip = gzipSize.sync(fs.readFileSync(localMinFile, 'utf8'));

    // [...]

    var sizeMessage = '<img src="https://img.shields.io/badge/Size%20Diff%20%28' + releaseVersion + '%29--lightgrey.svg?style=flat-square"/>  ';
    sizeMessage += '<img src="' + sizeDiffBadge('Global', global.size - bundleGlobal.size) + '"/> ';
    sizeMessage += '<img src="' + sizeDiffBadge('Global(gzip)', global_gzip - bundle_global_gzip) + '"/> ';
    sizeMessage += '<img src="' + sizeDiffBadge('Min', min.size - bundleMin.size) + '"/> ';
    sizeMessage += '<img src="' + sizeDiffBadge('Min (gzip)', min_gzip - bundle_min_gzip) + '"/> ';
    message(sizeMessage);

    markdown('> CJS: **' + getKB(result) +
      '**KB, global: **' + getKB(global.size) +
      '**KB (gzipped: **' + getKB(global_gzip) +
      '**KB), min: **' + getKB(min.size) +
      '**KB (gzipped: **' + getKB(min_gzip) + '**KB)');

    res();
  });
}));
const jsFiles = danger.git.created_files.filter(path => path.endsWith("js"));

function absolute (relPath) {
  return path.resolve(__dirname, relPath)
}

const flowIgnorePaths = [
  'node_modules','test', 'build', 'examples', 'doc', 'android', 'ios', 'bin', 'dist', 'flow-typed'
].map(function (rel) {
  return absolute(rel)
});

// new js files should have `@flow` at the top
const unFlowedFiles = jsFiles.filter(filepath => {
  let i = 0
  const len = flowIgnorePaths.length
  while (i < len) {
    const p = flowIgnorePaths[i]
    if (absolute(filepath).indexOf(p) > -1) {
      // ignore this file because it's in the flow-ignore-paths.
      return false;
    }
    i++
  }
  const content = fs.readFileSync(filepath);
  return !content.includes("@flow");
});

if (unFlowedFiles.length > 0) {
  warn(
    `These new JS files do not have Flow enabled: ${unFlowedFiles.join(", ")}`
  );
}
// Changes to these files may need SemVer bumps
const semverBumpFiles = ['ThemeProvider.js', 'StyledComponent.js', 'StyledNativeComponent.js']
semverBumpFiles.forEach(file => {
  if (jsModifiedFiles.includes(file)) {
    warn('Changes to #{file} might be SemVer major changes.')
  }
})

Lots of documentation

Getting Started with Danger JS

Step one on using Danger in your application. Start here.

Read more

CHANGELOG

What's been changing in the codebase.

Read more

Extending Danger

How to take your rules, and make them accessible to more people by writing a Danger plugin.

Read more

Danger in a Node App

An example where you work in a team on a node app, and some of the common Danger rules for working together.

Read more

Danger + Node Library

An example where you work on an OSS node library, so you're trying to improve overall contributions from light contributors.

Read more

About the Dangerfile

Step two on using Danger in your app, how to work locally and nuances around working with files.

Read more

Cultural Changes

Discussing introducing Danger into a team, how you can use it to provide positive feedback and encourage adoption of new rules.

Read more

Danger + Dependencies

An example of how can you use Danger to keep your dependencies in check.

Read more

Danger in my Language

How to use `danger process` to create a Danger runner for any language.

Read more

Danger + Transpilation

How Danger's TypeScript/Babel integration works.

Read more

FAQ

Common questions that come up in our GitHub issues.

Read more

Danger + BitBucket Cloud

An overview of using Danger with BitBucket Cloud, and some examples

Read more

Danger + BitBucket Server

An overview of using Danger with BitBucket Server, and some examples

Read more

Danger + GitLab

An overview of using Danger with GitLab, and some examples

Read more

Fast Feedback via Danger Local

How to use Danger to get per-commit feedback

Read more

Peril

When there's not enough Danger in your life

Read more

Awesome plugins