## File: README.md ## What is Danger JS? Danger runs after your CI, automating your team's conventions surrounding code review. This provides another logical step in your process, through which Danger can help lint your rote tasks in daily code review. You can use Danger to codify your team's norms, leaving humans to think about harder problems. Danger JS works with GitHub, BitBucket Server, BitBucket Cloud for code review, then with: Travis CI, GitLab CI, Semaphore, Circle CI, GitHub Actions, Jenkins, Bamboo, Bitrise, surf-build, Codeship, Drone, Buildkite, Buddy.works, TeamCity, Visual Studio Team Services, Screwdriver, Concourse, Netlify, CodeBuild, Codefresh, AppCenter, BitBucket Pipelines, Cirrus CI, Codemagic or Xcode Cloud. [](https://www.npmjs.com/package/danger) [](https://travis-ci.org/danger/danger-js) [](https://ci.appveyor.com/project/orta/danger-js/branch/main) ## For example? You can: - Enforce CHANGELOGs - Enforce links to Trello/JIRA in PR/MR bodies - Enforce using descriptive labels - Look out for common anti-patterns - Highlight interesting build artifacts - Give warnings when specific files change Danger provides the glue to let _you_ build out the rules specific to your team's culture, offering useful metadata and a comprehensive plugin system to share common issues. ## Getting Started Alright. So, actually, you may be in the wrong place. From here on in, this README is going to be for people who are interested in working on and improving on Danger JS. We keep all of the end-user documentation at . Some quick links to get you started: - [Getting Started](http://danger.systems/js/guides/getting_started.html) - [Guides Index](http://danger.systems/js/guides.html) - [DSL Reference](http://danger.systems/js/reference.html) ## This thing is broken, I should help improve it! Awesommmmee. Everything you need is down below. You can also refer to [CONTRIBUTING](CONTRIBUTING.md) file where you'll find the same information listed below. ```sh git clone https://github.com/danger/danger-js.git cd danger-js # if you don't have yarn installed npm install yarn yarn install ``` You can then verify your install by running the tests, and the linters: ```sh yarn test yarn lint ``` The fixers for both tslint and prettier will be applied when you commit, and on a push your code will be verified that it compiles. You can run your dev copy of danger against a PR by running: ```sh yarn build; node --inspect distribution/commands/danger-pr.js https://github.com/danger/danger-js/pull/817 ``` ### How does Danger JS work? Check the [architecture doc](https://github.com/danger/danger-js/blob/main/docs/architecture.md). ### What is the TODO? Check the issues, I try and keep my short term perspective there. Long term is in the [VISION.md](VISION.md). ### Releasing a new version of Danger Following [this commit](https://github.com/danger/danger-js/commit/a26ac3b3bd4f002acd37f6a363c8e74c9d5039ab) as a model: - Checkout the `main` branch. Ensure your working tree is clean, and make sure you have the latest changes by running `git pull; yarn`. - Publish - `npm run release -- patch --ci --npm.otp=`. - This will trigger a CI run which updates homebrew for the native builds :ship: ## License, Contributor's Guidelines and Code of Conduct We try to keep as much discussion as possible in GitHub issues, but also have a pretty inactive Slack --- if you'd like an invite, ping [@Orta](https://twitter.com/orta/) a DM on Twitter with your email. It's mostly interesting if you want to stay on top of Danger without all the emails from GitHub. > This project is open source under the MIT license, which means you have full access to the source code and can modify > it to fit your own needs but don't have access to deploy. > > This project subscribes to the [Moya Contributors Guidelines](https://github.com/Moya/contributors) which TLDR: means > we give out push access easily and often. > > Contributors subscribe to the [Contributor Code of Conduct](http://contributor-covenant.org/version/1/3/0/) based on > the [Contributor Covenant](http://contributor-covenant.org) version 1.3.0. [emiss]: https://github.com/artsy/emission/blob/master/dangerfile.ts [danger-js]: https://github.com/danger/danger-js/blob/main/dangerfile.ts [meta]: https://github.com/artsy/metaphysics/blob/master/dangerfile.js [fbj]: https://github.com/facebook/jest/blob/master/dangerfile.js [sc]: https://github.com/styled-components/styled-components/blob/master/dangerfile.js [rxjs]: https://github.com/ReactiveX/rxjs/blob/master/dangerfile.js [setup]: http://danger.systems/guides/getting_started.html#creating-a-bot-account-for-danger-to-use [jest]: https://github.com/facebook/jest --- ## File: docs/guides/faq.html.md --- title: FAQ subtitle: Frequently Asked Questions layout: guide_js order: 3 blurb: Common questions that come up in our GitHub issues. --- ## Can I use the same Dangerfile across many repos? Ish, it's currently quite complex to set up, but work is on-going on [Danger/Peril][peril]. This is a hosted version of Danger which does not need to run on CI. Using Peril you can use Dangerfiles to reply to basically any GitHub webhook type. ## I only want to run Danger for internal contributors Let's say you run Danger on the same CI service that deploys your code. If that's open source, you don't want to be letting anyone pull out your private env vars. The work around for this is to not simply call Danger on every test run: ```sh '[ ! -z $DANGER_GITHUB_API_TOKEN ] && yarn danger ci || echo "Skipping Danger for External Contributor"' ``` This ensures that Danger only runs when you have the environment variables set up to run. This is how Danger works for a lot of the open source mobile projects in Artsy. ## Danger is not posting to GitHub PRs, but everything looks fine? Try logging in to the GitHub account that should be writing the messages, it's possible that your account has triggered the bot detection algorithm on GitHub. This means that messages are sent correctly, but do not show up for anyone except the sender. This makes it more or less impossible to detect from Danger's side. ## I'm not sure what Danger is doing If you run danger with `DEBUG="*"` prefixed, you'll get a lot of information about what's happening under the hood. E.g: ```sh DEBUG="*" DANGER_GITHUB_API_TOKEN=[123] yarn danger pr https://github.com/facebook/react/pull/11865 ``` or on the CI: ```sh DEBUG="*" yarn danger ci ``` This will print out a _lot_ of information. ## Circle CI doesnt run my build consistently Yeah... We're struggling with that one. It's something we keep taking stabs at improving, so [keep an eye on the issues][circle_issues]. Ideally this issue will get resolved and we'll get it [fixed for free][circle_pr]. What happens is that Circle triggers a CI build before the PR has been set up, and so Danger cannot get information about the corresponding repo and PR. Danger on Circle with use the Circle API to try and hook itself up to the right PR, so if you have `yarn danger ci` later on in the process, you'll have a better chance of them hooking up. This can be worked around by sending PRs from forks. [circle_issues]: https://github.com/danger/danger-js/search?q=circle&state=open&type=Issues&utf8=✓ [circle_pr]: https://discuss.circleci.com/t/pull-requests-not-triggering-build/1213 ## I want to help influence Danger's direction We'd recommend first becoming acquainted with the [VISION.md][] inside Danger, this is the long-term plan. Then there are two ways to start contributing today: - Opinions are extra welcome on issues marked as [Open For Discussion][open]. - Well defined work items like features or fixes are marked as [You Can Do This][you-can-do-this]. We keep comments in the public domain, there is a Slack, but it's very rarely used. If you're interested in joining, you can DM [orta][]. [77]: https://github.com/danger/danger-js/issues/77 [529]: https://github.com/danger/danger-js/issues/529 [vision.md]: https://github.com/danger/danger-js/blob/main/VISION.md [open]: https://github.com/danger/danger-js/issues?q=is%3Aissue+is%3Aopen+label%3A%22Open+for+Discussion%22 [you-can-do-this]: https://github.com/danger/danger-js/issues?q=is%3Aissue+is%3Aopen+label%3A%22You+Can+Do+This%22 [orta]: https://twitter.com/orta/ [peril]: https://github.com/danger/peril --- ## File: docs/guides/peril.html.md --- title: Peril subtitle: When there's not enough Danger in your life layout: guide_js order: 4 blurb: When there's not enough Danger in your life --- ## TLDR: Peril Peril is a hosted instance of Danger. So instead of running on CI, it will run on a server somewhere and can respond instantly to webhooks. This gives Danger the ability to respond instantly to PR changes, and to run on more than just PRs. A lot of the information on Peril can be found on the [Artsy blog: here](http://artsy.github.io/blog/2017/09/04/Introducing-Peril/) Today Peril is self-hosted via heroku. There is a walkthrough on the [Peril repo: here](https://github.com/danger/peril/blob/main/docs/setup_for_org.md). It's still a pretty fast moving project ever 6 months into deployment so expect to maybe fix your own problem occasionally. ## Dangerfile implications Two tricky problems in Peril today: - Async is weird. - Can't do relative `import`s. Today Peril runs by inline execution of a JavaScript script. This has a serious draw-back in that async behavior doesn't work how you think it does. Here are some patterns for handling that. - **Ignore Async.** - A Dangerfile is a script, the non-blocking aspect of the node API can be ignored. E.g. use `path.xSync` instead of `path.x` - **Scheduling** - The Dangerfile DSL includes a function called `schedule`, this can handle either a promise or a function with a callback arg. For example using `async/await`: ```js import { schedule, danger } from "danger" /// [... a bunch of functions] schedule(async () => { const packageDiff = await danger.git.JSONDiffForFile("package.json") checkForRelease(packageDiff) checkForNewDependencies(packageDiff) checkForLockfileDiff(packageDiff) checkForTypesInDeps(packageDiff) }) ``` In this case, the closure is queued up and Danger waits until all `schedule` functions/promises are finished before continuing, so make sure to not cause it to lock. ## Plugin implications A plugin that runs on Peril will also have to handle the above if it uses async code. For some examples of this, see [danger-plugin-spellcheck](https://github.com/orta/danger-plugin-spellcheck#danger-plugin-spellcheck). --- ## File: docs/guides/the_dangerfile.html.md --- title: About the Dangerfile subtitle: The Dangerfile layout: guide_js order: 1 blurb: Step two on using Danger in your app, how to work locally and nuances around working with files. --- ## Writing your Dangerfile The Danger JS DSL is fully typed via TypeScript. These definitions are shipped with the Danger module. If your text editor supports working with type definitions you will get inline-documentation and auto-completion after you import danger in your Dangerfile. [Visual Studio Code](https://code.visualstudio.com/) will do this by default for you. If you are using Babel in your project, your Dangerfile will use the same transpilation settings. If you're using TypeScript + Jest it will work out of the box too, however, if you don't, you should head over to the [transpilation guide][transpilation_guide]. ## Working on your Dangerfile There are two ways to locally work on your Dangerfile. These both rely on using the GitHub API locally, so you may hit the GitHub API rate-limit or need to have authenticated request for private repos. In which case you can use an access token to do authenticated requests by exposing a token to Danger. ```sh export DANGER_GITHUB_API_TOKEN='xxxx' # or for BitBucket by username and password export DANGER_BITBUCKETSERVER_HOST='xxxx' DANGER_BITBUCKETSERVER_USERNAME='yyyy' DANGER_BITBUCKETSERVER_PASSWORD='zzzz' # or for BitBucket by username and personal access token export DANGER_BITBUCKETSERVER_HOST='xxxx' DANGER_BITBUCKETSERVER_USERNAME='yyyy' DANGER_BITBUCKETSERVER_TOKEN='zzzz' # or for BitBucket Cloud by username (from Account Settings page), password (App-password with Read Pull requests, and Read Account Permissions) export DANGER_BITBUCKETCLOUD_USERNAME='xxxx' export DANGER_BITBUCKETCLOUD_PASSWORD='yyyy' # or for BitBucket Cloud by OAuth key, and OAuth secret # You can get OAuth key from Settings > OAuth > Add consumer, put `https://bitbucket.org/site/oauth2/authorize` for `Callback URL`, and enable Read Pull requests, and Read Account Permissions. export DANGER_BITBUCKETCLOUD_OAUTH_KEY='xxxx' export DANGER_BITBUCKETCLOUD_OAUTH_SECRET='yyyy' # or for BitBucket Cloud by Repository Access Token # You can get a Repository Access Token from Repo Settings > Security > Acesss Tokens and set Pull requests write scope. export DANGER_BITBUCKETCLOUD_REPO_ACCESSTOKEN='xxxx' ``` Then the danger CLI will use authenticated API calls, which don't get this by API limits. ### Using `danger pr` The command `danger pr` expects an argument of a PR url, e.g. `yarn danger pr https://github.com/danger/danger-js/pull/100`. This will use your local Dangerfile against the metadata of the linked PR. Danger will then output the results into your terminal, instead of inside the PR itself. This _will not_ post comments. It is for locally testing, see `yarn danger pr --help` for more info. ### Using `danger` and Faking being on a CI If you create an [appropriately scoped temporary api token](http://danger.systems/js/guides/getting_started.html#setting-up-an-access-token) for your GitHub account, this can be a good way to see if danger is suitable for you before integrating it into your CI system. You can manually trigger danger against a pull request on the command line by setting the following environmental variables: ```bash export DANGER_FAKE_CI="YEP" export DANGER_TEST_REPO='username/reponame' ``` Then you can run against a local branch that is attached to a pull-request, by running the following: ```bash git checkout branch-for-pr-1234 DANGER_TEST_PR='1234' yarn danger ci ``` Assuming that your local file-system matches up to that branch on GitHub, this will be a good approximation of how danger will work when you integrate it into your CI system. Note: this will leave a comment on the PR. ## Working with files Over time, we've found it easier to create up-front arrays of files you are interested in - then you can work with these potential arrays of files. For example: ```js import { danger } from "danger" const docs = danger.git.fileMatch("**/*.md") const app = danger.git.fileMatch("src/**/*.ts") const tests = danger.git.fileMatch("*/__tests__/*") if (docs.edited) { message("Thanks - We :heart: our [documentarians](http://www.writethedocs.org/)!") } if (app.modified && !tests.modified) { warn("You have app changes without tests.") } ``` ## Utils As the JavaScript library API is relatively limited, the Danger module includes utility functions for work which is common to most Dangerfiles. These functions can be found in both `utils`, and `github.utils` for GitHub specific functions. We're interested at growing this carefully. ## Finding more info The [CHANGELOG][changelog] for Danger is kept entirely end-user focused, so if there is an aspect of the Dangerfile that you do not know, or looks confusing and there is nothing in the documentation - [check the CHANGELOG][changelog]. This is where we write-up why a change happened, and how it can affect Danger users. ### Examples If you'd like to work with some reference material, here are some examples in the wild. JavaScript: - **Libraries** - [facebook/react-native][rn], and [facebook/react][r]. - **Docs** - [bamlab/dev-standards][bamlab] Some TypeScript examples: - **Apps** - [Artsy/Eigen][eigen] - **Libraries** [danger/danger-js][danger-js], [apollographql/apollo-client][apollo] [eigen]: https://github.com/artsy/eigen/blob/main/dangerfile.ts [danger-js]: https://github.com/danger/danger-js/blob/master/dangerfile.ts [meta]: https://github.com/artsy/metaphysics/blob/master/dangerfile.js [rn]: https://github.com/facebook/react-native/blob/main/packages/react-native-bots/dangerfile.js [r]: https://github.com/facebook/react/blob/master/dangerfile.js [setup]: http://danger.systems/guides/getting_started.html#creating-a-bot-account-for-danger-to-use [jest]: https://github.com/facebook/jest [transpilation_guide]: /docs/tutorials/transpilation.html.md [changelog]: http://danger.systems/js/changelog.html [apollo]: https://github.com/apollographql/apollo-cache-persist/blob/master/dangerfile.ts [bamlab]: https://github.com/bamlab/dev-standards/blob/master/dangerfile.js --- ## File: docs/tutorials/dependencies.html.md --- title: Danger + Dependencies subtitle: Danger + Dependencies layout: guide_js order: 2 blurb: An example of how can you use Danger to keep your dependencies in check. --- ## Before we get started This tutorial continues after "[Getting Started][started]" - so you should have seen Danger comment on your PRs. ## Keeping on top of your dependencies Building pretty-much anything in the node ecosystem involves using external dependencies. In an ideal situation you want to use as few dependencies as possible, and get the most use out of them. Remember that you are shipping your dependencies too, so you are responsible for them to your end-users. The numerical scale of dependencies can make it tough to feel like you own your entire stack. So let's try and use Danger to give us more insight into changes related to our dependencies. ## Lockfiles The simplest rule, which we can evolve, is that any time your `package.json` changes you probably want a change to the [`yarn.lock`][lockfile] or [`shrinkwrap.json`][shrinkwrap] file. Yes, not every change to the `package.json` represents a dependency update, but we're starting simple. You start off your `Dangerfile` like this: ```js import { danger, warn } from "danger" const packageJson = danger.git.fileMatch("package.json") const packageLock = danger.git.fileMatch("yarn.lock") if (packageJson.modified && !packageLock.modified) { warn("There are package.json changes with no corresponding lockfile changes") } ``` ### Vetting New Dependencies This works, and for a while, this is enough. Time passes and you hear about a node module with a [CVE](https://cve.mitre.org) against it, let's call it `"spaced-between"`, you want to ensure it isn't added as a dependency. There are two aspects that you consider: - Keeping track of changes to `dependencies` (for noted dependencies) - Reading the lockfile for the dependency (for transitive dependencies) ### Keeping track of changes to dependencies We can use `danger.git.JSONDiffForFile` to understand the changes to a JSON file during code review. Note: it returns a promise, so we'll need to use `schedule` to make sure it runs async code correctly in Peril. ```js const blacklist = "spaced-between" schedule(async () => { const packageDiff = await danger.git.JSONDiffForFile("package.json") if (packageDiff.dependencies) { const newDependencies = packageDiff.dependencies.added if (newDependencies.includes(blacklist)) { fail(`Do not add ${blacklist} to our dependencies, see CVE #23") } } }) ``` So for example with a diff of `package.json` where spaced-between is added: ```diff { "dependencies": { "commander": "^2.9.0", "debug": "^2.6.0" + "spaced-between": "^1.1.1", "typescript": "^2.2.1", }, } ``` `JSONDiffForFile` will return an object shaped like this: ```js { dependencies: { added: ["chalk"], removed: [], after: { commander: "^2.9.0", debug: "^2.6.0", "spaced-between": "^1.1.1", typescript: "^2.2.1" }, before: { commander: "^2.9.0", debug: "^2.6.0", typescript: "^2.2.1" }, } } ``` Danger can then look inside the added `keys` for your blacklisted module, and fail the build if it is included. ### Parsing the lockfile You can trust that this dependency is going to be added directly to your project without it being highlighted in code review, but you can't be sure that any updates to your dependency tree won't bring it in transitively. A transitive dependency is one that comes in as a dependency of a dependency, one which isn't added to `packages.json` but is in `node_modules`. So you're going to look at a simple rule that parses the text of the file for your blacklisted module. ```js import fs from "fs" const blacklist = "spaced-between" const lockfile = fs.readFileSync("yarn.lock").toString() if (lockfile.includes(blacklist)) { const message = `${blacklist} was added to our dependencies, see CVE #23` const hint = `To find out what introduced it, use \`yarn why ${blacklist}\`.` fail(`${message}${hint}`) } ``` Note the use of `readFileSync`, as Danger is running as a script you'll find it simpler to use the synchronous methods when possible. You could improve the above rule by making danger run `yarn why spaced-between` and outputting the text into the messages. We do this in the [danger repo][danger-why] with `child-process` and `execSync`. ### Building from here This should give you an idea on how to understand changes to your `node_modules`, from here you can create any rules you want using a mix of `JSONDiffForFile`, `fs.readFileSync` and `child_process.execSync`. Here are a few ideas to get you started: - Convert the check for the package and lockfile to use `JSONDiffForFile` so that it only warns on `dependencies` or `devDependencies`. - Ensure you never add `@types/[module]` to `dependencies` but only into `devDependencies`. - When a new dependency is added, use a web-service like [libraries.io][libs] to describe the module inline. - [Parse][yarn-parse] the `yarn.lock` file, to say how many transitive dependencies are added on every new dependency. - When a dependency is removed, and no other dependencies are added, do a thumbs up 👍. [started]: /js/guides/getting_started.html [lockfile]: https://yarnpkg.com/lang/en/docs/yarn-lock/ [shrinkwrap]: https://docs.npmjs.com/cli/shrinkwrap [danger-why]: https://github.com/danger/danger-js/blob/8fba6e7c301ac3459c2b0b93264bff7256efd8da/dangerfile.ts#L49 [libs]: https://libraries.io [yarn-parse]: https://www.npmjs.com/package/parse-yarn-lock --- ## File: docs/tutorials/fast-feedback.html.md --- title: Fast Feedback via Danger Local subtitle: Platformless layout: guide_js order: 4 blurb: How to use Danger to get per-commit feedback --- ## Before we get started This tutorial continues after "[Getting Started][started]" - it's not required that you have `danger ci` set up though. ## Locality With Danger, the typical flow is to help you can check rules on CI and get feedback inside your PR. With Peril you can move those rules to run on an external server making feedback instant. `danger local` provides a somewhat hybrid approach. `danger local` provides a way to run a Dangerfile based on git-hooks. This let's you run rules while you are still in the same context as your work as opposed to later during the code review. Personally, I find this most useful on projects when I ship 90% of the code to it. ## How it works Where `danger ci` uses information from the Pull Request to figure out what has changed, `danger local` naively uses the local differences in git from master to the current commit to derive the runtime environment. This is naive because if you don't keep your master branch sync, then it will be checking across potentially many branches. Inside a Dangerfile `danger.github` and `danger.bitbucket` will be falsy in this context, so you can share a Dangerfile between `danger local` and `danger ci` as long as you verify that these objects exist before using them. When I thought about how I wanted to use `danger local` on repos in the Danger org, I opted to make a separate Dangerfile for `danger local` and import this at the end of the main Dangerfile. This new Dangerfile only contains rules which can run with just `danger.git`, e.g. CHANGELOG/README checks. I called it `dangerfile.lite.ts`. ## Getting it set up You need to add both Danger and [husky](https://www.npmjs.com/package/husky) to your project: ```sh yarn add --dev danger husky ``` When husky is in your dependencies, git-hooks are set up to respond according to matching names in the `"scripts"` section of your `package.json`. We want to use [a pre-push](https://git-scm.com/docs/githooks#_pre_push) hook to let `danger local` run before code has been submitted. ```json "scripts": { "prepush": "yarn build; yarn danger:prepush", "danger:prepush": "yarn danger local --dangerfile dangerfile.lite.ts" // [...] ``` Yes, it's a `pre-push` hook and the script is `prepush`, husky [removes the dashes](https://github.com/typicode/husky/blob/master/HOOKS.md#hooks). If `master` isn't the branch which you want as a reference then you can use `--base dev` to change the comparison base. --- ## File: docs/tutorials/node-app.html.md --- title: Danger in a Node App subtitle: Danger + Node layout: guide_js order: 0 blurb: An example where you work in a team on a node app, and some of the common Danger rules for working together. --- ## Before we get started This tutorial continues after "[Getting Started][started]" - so you should have seen Danger comment on your PRs. ## "Node App" A node app could cover anything from an API, to a website, a native app or a hardware project. The rules on these projects tend to come from your larger dev team culture. In [Artsy][] a lot of our rules for applications come from trying to have a similar culture between all projects. ## Assignees We use [a slack bot][no-slacking] to let people know when they've been assigned to a PR, and so the first rule added to an app is a check that there are assignees to your PR. This is a really simple check: ```js import { danger, fail, warn } from "danger" if (!danger.github.pr.assignee) { fail("This pull request needs an assignee, and optionally include any reviewers.") } ``` The `danger.pr` object is the JSON provided by GitHub to [represent a pull request][pr]. So here we're pulling out the `assignee` key and validating that anything is inside it. We can make a small improvement to this rule, by allowing someone to declare that a PR is a "work in progress" Danger can allow the build to pass, but will provide feedback that there is no assignee. ```js import { danger, fail, warn } from "danger" if (!danger.github.pr.assignee) { const method = danger.github.pr.title.includes("WIP") ? warn : fail method("This pull request needs an assignee, and optionally include any reviewers.") } ``` Using a function as a variable we can determine whether to fail, or warn based on whether the PR's title includes the string `"WIP"`. ## PR Messages In a similar vein, we also want to encourage pull requests as a form of documentation. We can help push people in this direction by not allowing the body of a pull request to be less than a few characters long. ```js if (danger.github.pr.body.length < 10) { fail("This pull request needs a description.") } ``` This can be expanded to all sorts of checks for example: - Making sure every PR references an issue, or JIRA ticket. - Skipping particular rules based on what someone says inside the message. E.g. "This is a trivial PR." - in Artsy we allow particular hashtags to suppress feedback from danger. ## Results of CI Processes Let's assume you're using CI for running tests or linters. ```yaml script: - yarn lint - yarn test - yarn danger ci ``` If your tool does not have an extra log file output option, you can look at using [`tee`][tee] to copy the text output into a file for later reading ( so you'd change `- yarn lint` to `yarn lint | tee 'linter.log'` ) And here's a really simple check that it contains the word "Failed" and to post the logs into the PR. ```js import { danger, markdown } from "danger" import contains from "lodash-contains" import fs from "fs" const testFile = "tests-output.log" const linterOutput = fs.readFileSync(testFile).toString() if (contains(linterOutput, "Failed")) { markdown(`These changes failed to pass the linter: ${linterOutput} `) } ``` More mature tools may have a JSON output reporter, so you can parse that file and create your own report for danger to post. If you build something that is a generic wrapper around a specific linting tool, this is a great place to convert that code [into a plugin][plugin] so that anyone can use it. In this case, Danger effectively is a way of moving these messages into the code review session. For example: - [danger-plugin-jest][] - [danger-plugin-tslint][] [started]: /js/guides/getting_started.html [artsy]: http://artsy.github.io [no-slacking]: https://github.com/alloy/no-slacking-on-pull-requests-bot [pr]: https://developer.github.com/v3/pulls/#get-a-single-pull-request [tee]: http://linux.101hacks.com/unix/tee-command-examples/ [plugin]: /js/usage/extending-danger.html [danger-plugin-jest]: https://github.com/macklinu/danger-plugin-jest#danger-plugin-jest [danger-plugin-tslint]: https://github.com/macklinu/danger-plugin-tslint#readme --- ## File: docs/tutorials/node-library.html.md --- title: Danger + Node Library subtitle: Danger + Node Library layout: guide_js order: 1 blurb: An example where you work on an OSS node library, so you're trying to improve overall contributions from light contributors. --- ## Before we get started This tutorial continues after "[Getting Started][started]" - so you should have seen Danger comment on your PRs. ## Keeping on top of your library End-users want to understand what has changed between versions of your library, and a CHANGELOG is a great way to keep them up to date. However, it can be easy to forget to add a CHANGELOG entry to any changes to a library. So let's add a check that a CHANGELOG entry is added on every PR: ```js import { danger, fail, warn } from "danger" const changelogChanges = danger.git.fileMatch("CHANGELOG.md") if (!changelogChanges.modified) { warn("This pull request may need a CHANGELOG entry.") } ``` We went with `warn` here because there are a lot of legitimate reasons to not need a CHANGELOG entry (updating typos, CI and other infrastructure.) We can improve this though, let's _also_ check that there are changes to the source code for our library. ```js import { danger, fail, warn } from "danger" import first from "lodash.first" const changelogChanges = danger.git.fileMatch("CHANGELOG.md") const hasLibraryChanges = first(danger.git.modified_files, path => path.startsWith("lib/")) if (hasLibraryChanges && !changelogChanges.modified) { warn("This pull request may need a CHANGELOG entry.") } ``` This is a much more specific rule, now changes to the README won't warrant a CHANGELOG entry. ### Dependencies Any dependencies that you use are passed on to all of your library consumers, so you should consider using Danger to keep track of those as they evolve. For more information, see the tutorial on [Dependencies][deps]. ### Keep your README up to date An example from Danger itself, is that we want to ensure the README always shows what CI providers will work by default with Danger. As both the app, and Danger use JavaScript, we can import code from the app and use that to create a new rule. ```js import { danger, fail, warn } from "danger" import contains from "lodash.contains" // This is a list of all the CI providers import { realProviders } from "./source/ci_source/providers" const readme = fs.readFileSync("README.md", "utf8") const names = realProviders.map(ci => new ci({}).name) const missing = names.filter(name => !contains(readme, name)) if (missing.length) { warn(`These providers are missing from the README: ${sentence(missing)}`) } ``` Danger also uses a similar check to create our type definition files, if any of the public DSL changes then Danger checks that the type definitions have been updated, and recommends how to do so if not. These are rare chores which are really hard to remember to do, and impossible if you're not intimate with the codebase - so providing automated feedback here is really useful. [deps]: /js/tutorials/dependencies.html [started]: /js/guides/getting_started.html --- ## File: docs/tutorials/transpilation.html.md --- title: Danger + Transpilation subtitle: Danger + Transpilation layout: guide_js order: 3 blurb: How Danger's TypeScript/Babel integration works. --- ### Transpilation Danger tries to pick up either Babel or TypeScript up at runtime. It does this by `require`ing both dependencies, which will follow the standard [NodeJS require resolution](https://nodejs.org/api/modules.html#modules_all_together). If either don't exist, then the Dangerfile will be treated as not needing transpilation and passed directly to the node runtime. A few notes: - TypeScript is prioritized over Babel - Babel 7 support for TypeScript is supported - Whether you use `dangerfile.ts` or `dangerfile.js` is irrelevant, the environment matters more ### TypeScript gotchas You might have a `src` folder where your actual source code is kept, and adding a `dangerfile.ts` at the root which will break compilation. The answer to this is to add the dangerfile to the `"exclude"` section. Then to get inline errors working correct, add it to the `"include"`. It's a neat little trick. You can see it working in [artsy/eigen#tsconfig.json][tsconfig] ```json { "compilerOptions": {}, "include": ["src/**/*.ts", "src/**/*.tsx", "dangerfile.ts"], "exclude": ["dangerfile.ts", "node_modules"] } ``` ### The "danger" module The `danger` module is removed before evaluation, it's only there to fake your dev env into working correctly. In reality, all of the exports are added to the global environment. If you import `"danger"` in code that isn't evaluated inside Danger itself, it will raise an exception. You can use something like jest's module mocking system to fake it in tests, letting you manipulate the object `danger.github.pr` to look like whatever you want in tests: ```js jest.mock("danger", () => jest.fn()) import * as danger from "danger" const dm = danger as any import { rfc5 } from "../org/all-prs" beforeEach(() => { dm.fail = jest.fn() }) it("fails when there's no PR body", () => { dm.danger = { github: { pr: { body: "" } } } return rfc5().then(() => { expect(dm.fail).toHaveBeenCalledWith("Please add a description to your PR.") }) }) it("does nothing when there's a PR body", () => { dm.danger = { github: { pr: { body: "Hello world" } } } return rfc5().then(() => { expect(dm.fail).not.toHaveBeenCalled() }) }) ``` [tsconfig]: https://github.com/artsy/eigen/blob/main/tsconfig.json ### Disabling Transpilation You can force Danger to skip transpiling your Dangerfile by setting the environment variable `DANGER_DISABLE_TRANSPILATION="true"`. --- ## File: docs/usage/bitbucket_cloud.html.md --- title: Danger + BitBucket Cloud subtitle: Dangerous bits layout: guide_js order: 4 blurb: An overview of using Danger with BitBucket Cloud, and some examples --- To use Danger JS with BitBucket Cloud: you'll need to create a new account for Danger to use, then set the following environment variables on your CI: You could use either username with password, OAuth key with OAuth secret or repository access token. For username and password, you need to set. - `DANGER_BITBUCKETCLOUD_USERNAME` = The username for the account used to comment, as shown on https://bitbucket.org/account/ - `DANGER_BITBUCKETCLOUD_PASSWORD` = The password for the account used to comment, you could use [App passwords](https://confluence.atlassian.com/bitbucket/app-passwords-828781300.html#Apppasswords-Aboutapppasswords) with Read Pull Requests and Read Account Permissions. For OAuth key and OAuth secret, you can get them from. - Open [BitBucket Cloud Website](https://bitbucket.org) - Navigate to Settings > OAuth > Add consumer - Put `https://bitbucket.org/site/oauth2/authorize` for `Callback URL`, and enable Read Pull requests, and Read Account Permission. - `DANGER_BITBUCKETCLOUD_OAUTH_KEY` = The consumer key for the account used to comment, as show as `Key` on the website. - `DANGER_BITBUCKETCLOUD_OAUTH_SECRET` = The consumer secret for the account used to comment, as show as `Secret` on the website. For [repository access token](https://support.atlassian.com/bitbucket-cloud/docs/repository-access-tokens/), what you need to create one is: - Open your repository URL - Navigate to Settings > Security > Access Tokens > Create Repository Access Token - Give it a name and set Pull requests write scope Then in your Dangerfiles you will have a fully fleshed out `danger.bitbucket_cloud` object to work with. For example: ```ts import { danger, warn } from "danger" if (danger.bitbucket_cloud.pr.title.includes("WIP")) { warn("PR is considered WIP") } ``` The DSL is expansive, you can see all the details inside the [Danger JS Reference][ref], but the TLDR is: ```ts danger.bitbucket_cloud. /** The pull request and repository metadata */ metadata: RepoMetaData /** The PR metadata */ pr: BitBucketCloudPRDSL /** The commits associated with the pull request */ commits: BitBucketCloudCommit[] /** The comments on the pull request */ comments: BitBucketCloudPRComment[] /** The activities such as OPENING, COMMENTING, CLOSING, MERGING or UPDATING a pull request */ activities: BitBucketCloudPRActivity[] ```