DEV Community

Ramu Narasinga
Ramu Narasinga

Posted on • Originally published at thinkthroo.com

dangerfile.ts in Storybook codebase.

In this article, we review dangerfile.ts in Storybook codebase. We will look at:

  1. What is dangerfile?

  2. dangerfile in Storybook.

I study patterns used in an open source project found on Github Trending. For this week, I reviewed some parts of Storybook codebase and wrote this article.

What is dangerfile?

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.

I also wrote another article about this, checkout dangerfile.ts in Twenty, the #1 open-source CRM.

dangerfile in Storybook

In storybook/scripts/dangerfile.ts, you will find the following functions definition:

  1. checkRequiredLabels

  2. checkPrTitle

checkRequiredLabels

At L26, you will find the following code:

const checkRequiredLabels = (labels: string[]) => {
  const forbiddenLabels = flatten([
    'ci: do not merge',
    'in progress',
    branchVersion !== Versions.MAJOR ? 'BREAKING CHANGE' : [],
    branchVersion === Versions.PATCH ? 'feature request' : [],
  ]);

  const requiredLabels = flatten([
    prLogConfig.skipLabels || [],
    (prLogConfig.validLabels || []).map((keyVal: string) => keyVal[0]),
  ]);

  const blockingLabels = intersection(forbiddenLabels, labels);
  if (!isEmpty(blockingLabels)) {
    fail(
      `PR is marked with ${blockingLabels.map((label: string) => `"${label}"`).join(', ')} label${
        blockingLabels.length > 1 ? 's' : ''
      }.`
    );
  }

  const foundRequiredLabels = intersection(requiredLabels, labels);
  if (isEmpty(foundRequiredLabels)) {
    fail(`PR is not labeled with one of: ${JSON.stringify(requiredLabels)}`);
  } else if (foundRequiredLabels.length > 1) {
    fail(`Please choose only one of these labels: ${JSON.stringify(foundRequiredLabels)}`);
  }

  const foundCILabels = intersection(ciLabels, labels);
  if (isEmpty(foundCILabels)) {
    fail(`PR is not labeled with one of: ${JSON.stringify(ciLabels)}`);
  } else if (foundCILabels.length > 1) {
    fail(`Please choose only one of these labels: ${JSON.stringify(foundCILabels)}`);
  }
};
Enter fullscreen mode Exit fullscreen mode

fail is imported as shown below at the top of the file

import { danger, fail } from 'danger';
Enter fullscreen mode Exit fullscreen mode

checkPrTitle

You will find the following definition at L63:

const checkPrTitle = (title: string) => {
  const match = title.match(/^[A-Z].+:\s[A-Z].+$/);
  if (!match) {
    fail(
      `PR title must be in the format of "Area: Summary", With both Area and Summary starting with a capital letter
Good examples:
- "Docs: Describe Canvas Doc Block"
- "Svelte: Support Svelte v4"
Bad examples:
- "add new api docs"
- "fix: Svelte 4 support"
- "Vue: improve docs"`
    );
  }
};
Enter fullscreen mode Exit fullscreen mode

Both these functions are invoked based on the following condition:

if (prLogConfig) {
  const { labels } = danger.github.issue;
  checkRequiredLabels(labels.map((l) => l.name));
  checkPrTitle(danger.github.pr.title);
}
Enter fullscreen mode Exit fullscreen mode

prLogConfig

const pkg = require('../code/package.json');
const prLogConfig = pkg['pr-log'];
Enter fullscreen mode Exit fullscreen mode

And this pr-log is defined in the code/package.json

"pr-log": {
    "skipLabels": [
      "cleanup"
    ],
    "validLabels": [
      [
        "BREAKING CHANGE",
        "Breaking Changes"
      ],
      [
        "feature request",
        "Features"
      ],
      [
        "bug",
        "Bug Fixes"
      ],
      [
        "documentation",
        "Documentation"
      ],
      [
        "maintenance",
        "Maintenance"
      ],
      [
        "build",
        "Build"
      ],
      [
        "dependencies",
        "Dependency Upgrades"
      ]
    ]
  }

Enter fullscreen mode Exit fullscreen mode

About me:

Hey, my name is Ramu Narasinga. I study codebase architecture in large open-source projects.

Email: ramu.narasinga@gmail.com

Study the patterns used in large OSS projects at Think Throo.

References:

  1. https://github.com/storybookjs/storybook/blob/next/scripts/dangerfile.ts#L25

  2. https://danger.systems/js/

  3. https://medium.com/@ramunarasinga/dangerfile-ts-in-twenty-the-1-open-source-crm-5660f366a6d8

  4. https://github.com/storybookjs/storybook/blob/next/code/package.json

Top comments (0)