docs/tutorials/dependencies.html.md
This tutorial continues after "Getting Started" - so you should have seen Danger comment on your PRs.
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.
The simplest rule, which we can evolve, is that any time your package.json changes you probably want a change to the
yarn.lock or shrinkwrap.json 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:
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")
}
This works, and for a while, this is enough. Time passes and you hear about a node module with a
CVE 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:
dependencies (for noted 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.
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:
{
"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:
{
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.
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.
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 with child-process and execSync.
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:
JSONDiffForFile so that it only warns on dependencies or
devDependencies.@types/[module] to dependencies but only into devDependencies.yarn.lock file, to say how many transitive dependencies are added on every new dependency.