dangerfile.ts in Storybook codebase.
In this article, we review dangerfile.ts in Storybook codebase. We will look at:
-
What is dangerfile?
-
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:
-
checkRequiredLabels
-
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)}`);
}
};
fail is imported as shown below at the top of the file
import { danger, fail } from 'danger';
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"`
);
}
};
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);
}
prLogConfig
const pkg = require('../code/package.json');
const prLogConfig = pkg['pr-log'];
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"
]
]
}
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:
-
https://github.com/storybookjs/storybook/blob/next/scripts/dangerfile.ts#L25
-
https://medium.com/@ramunarasinga/dangerfile-ts-in-twenty-the-1-open-source-crm-5660f366a6d8
-
https://github.com/storybookjs/storybook/blob/next/code/package.json