Skip to content

Commit

Permalink
Merge pull request #473 from danger/adam-moss-local-run
Browse files Browse the repository at this point in the history
Local execution for lint-staging like tests
  • Loading branch information
orta committed Jan 21, 2018
2 parents c4a1d99 + f822a17 commit f7a5287
Show file tree
Hide file tree
Showing 31 changed files with 644 additions and 255 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ matrix:
- echo "This is the real `danger ci` run on this repo"
- DEBUG="*" danger ci --verbose
- echo "Validating that danger pr works as expected"
- DEBUG="*" danger pr https://github.com/danger/danger-js/pull/465 --dangerfile dangerfile.circle.js --verbose
- DEBUG="*" danger pr https://github.com/danger/danger-js/pull/465 --dangerfile dangerfile.circle.js --verbose
- echo "Validating that danger local works as expected"
- DEBUG="*" danger local --dangerfile source/platforms/git/_tests/local_dangerfile_example.ts

# Create some fake projects at runtime
- node_js: '7'
Expand Down
12 changes: 8 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@

## Master

* Updates our API from "github" to "@octokit/rest"
* Adds a new command `danger local`

this looks like mainly an internal change, and should not affect any
existing dangerfiles. - [@fbartho][] [@orta][]
This command will look between the current branch and master
and use that to evaluate a dangerfile. This is aimed specifically at
tools like git commit hooks, and for people who don't do code review.

### 3.0.6
`danger.github` will be falsy in this context, so you could share a dangerfile
between your CI + code Review. - [@orta][]

### 3.0.5

* Added support for Bitrise as a CI Provider - [@tychota][]

Expand Down
31 changes: 31 additions & 0 deletions dangerfile.lite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as fs from "fs"

import { DangerDSLType } from "./source/dsl/DangerDSL"
declare var danger: DangerDSLType
declare function warn(params: string): void

const hasChangelog = danger.git.modified_files.includes("CHANGELOG.md")
if (!hasChangelog) {
warn("Please add a changelog entry for your changes.")
}

import dtsGenerator from "./scripts/danger-dts"
const currentDTS = dtsGenerator()
const savedDTS = fs.readFileSync("source/danger.d.ts").toString()
if (currentDTS !== savedDTS) {
const message = "There are changes to the Danger DSL which are not reflected in the current danger.d.ts."
const idea = "Please run <code>yarn declarations</code> and update this PR."
fail(`${message}\n - ${idea}`)
}

// Always ensure we name all CI providers in the README. These
// regularly get forgotten on a PR adding a new one.
const sentence = danger.utils.sentence

import { realProviders } from "./source/ci_source/providers"
const readme = fs.readFileSync("README.md").toString()
const names = realProviders.map(p => new p({}).name)
const missing = names.filter(n => !readme.includes(n))
if (missing.length) {
warn(`These providers are missing from the README: ${sentence(missing)}`)
}
17 changes: 9 additions & 8 deletions dangerfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,33 @@ declare function warn(params: string): void
declare function fail(params: string): void
// declare function message(params: string): void
// declare function markdown(params: string): void
declare function schedule(promise: Promise<any | void>): void
declare function schedule(promise: () => Promise<any | void>): void
declare function schedule(callback: (resolve: any) => void): void
// declare function schedule(promise: Promise<any | void>): void
// declare function schedule(promise: () => Promise<any | void>): void
// declare function schedule(callback: (resolve: any) => void): void

import * as fs from "fs"

schedule(async () => {
const checkREADME = async () => {
// Request a CHANGELOG entry if not declared #trivial
const hasChangelog = danger.git.modified_files.includes("CHANGELOG.md")
const isTrivial = (danger.github.pr.body + danger.github.pr.title).includes("#trivial")
const isGreenkeeper = danger.github.pr.user.login === "greenkeeper"

if (!hasChangelog && !isTrivial && !isGreenkeeper) {
warn("Please add a changelog entry for your changes.")
warn("Please add a changelog entry for your changes, by adding a note in the master section to `CHANGELOG.md`.")

// Politely ask for their name on the entry too
const changelogDiff = await danger.git.diffForFile("changelog.md")
const changelogDiff = await danger.git.diffForFile("CHANGELOG.md")
const contributorName = danger.github.pr.user.login
if (changelogDiff && changelogDiff.diff.includes(contributorName)) {
warn("Please add your GitHub name to the changelog entry, so we can attribute you correctly.")
}
}
})
}
checkREADME()

import yarn from "danger-plugin-yarn"
schedule(yarn())
yarn()

import jest from "danger-plugin-jest"
jest()
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorials/transpilation.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ 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 the 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 passing .
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
* Whether you use `dangerfile.ts` or `dangerfile.js` is irrelevant, the environment matters more

### TypeScript gotchas

Expand All @@ -32,7 +32,7 @@ You might have a `src` folder where your actual source code is kept, and adding

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, you can manipulate the object to be whatever you want in tests:
You can use something like jest's module mocking system to fake it in tests, sp you can manipulate the object PR to look like whatever you want in tests:

```js
jest.mock("danger", () => jest.fn())
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"danger-pr": "distribution/commands/danger-pr.js",
"danger-process": "distribution/commands/danger-process.js",
"danger-ci": "distribution/commands/danger-ci.js",
"danger-runner": "distribution/commands/danger-runner.js",
"danger-init": "distribution/commands/danger-init.js"
"danger-init": "distribution/commands/danger-init.js",
"danger-local": "distribution/commands/danger-local.js"
},
"jest": {
"transform": {
Expand All @@ -36,12 +36,12 @@
},
"scripts": {
"precommit": "lint-staged",
"prepush": "yarn run build",
"prepush": "yarn build; yarn danger:prepush",
"test": "jest",
"test:watch": "jest --watch",
"lint": "tslint \"source/**/*.ts\"",
"lint:fix": "tslint \"source/**/*.ts\" --fix",
"prepublishOnly": "yarn run build && yarn declarations && yarn build:flow-types",
"prepublishOnly": "yarn build && yarn declarations && yarn build:flow-types",
"build": "shx rm -rf ./distribution && tsc -p tsconfig.production.json && madge ./distribution --circular",
"build:fast": "tsc -p tsconfig.production.json",
"build:flow-types":
Expand All @@ -53,7 +53,8 @@
"mkdir docs/docs_generate; cp source/danger.d.ts docs/docs_generate; cp node_modules/@octokit/rest/index.d.ts docs/docs_generate/github.d.ts",
"docs":
"yarn run docs:cp_defs; yarn typedoc --ignoreCompilerErrors --mode modules --json docs/js_ref_dsl_docs.json --includeDeclarations source",
"dts-lint": "yarn run declarations && yarn dtslint types"
"dts-lint": "yarn run declarations && yarn dtslint types",
"danger:prepush": "node distribution/commands/danger.js local --dangerfile dangerfile.lite.ts"
},
"repository": {
"type": "git",
Expand Down
34 changes: 34 additions & 0 deletions source/ci_source/providers/local-repo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Env, CISource } from "../ci_source"

export class LocalRepo implements CISource {
private readonly env: Env

constructor(env: Env) {
const defaults = {
repo: process.cwd(),
pr: undefined,
}

this.env = { ...env, ...defaults }
}
get name(): string {
return "local repo"
}

get isCI(): boolean {
return true
}
get isPR(): boolean {
return true
}

get pullRequestID(): string {
return ""
}
get repoSlug(): string {
return this.env.repo
}
get supportedPlatforms(): string[] {
return ["git"]
}
}
7 changes: 4 additions & 3 deletions source/commands/ci/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import { CISource } from "../../ci_source/ci_source"
const d = debug("danger:process_runner")

export interface RunnerConfig {
source: CISource
platform: Platform
source?: CISource
platform?: Platform
additionalArgs?: string[]
}

export const runRunner = async (app: SharedCLI, config?: RunnerConfig) => {
Expand All @@ -43,7 +44,7 @@ export const runRunner = async (app: SharedCLI, config?: RunnerConfig) => {
const dangerJSONDSL = await jsonDSLGenerator(platform)

const config: ExecutorOptions = {
stdoutOnly: app.textOnly,
stdoutOnly: !platform.supportsCommenting() || app.textOnly,
verbose: app.verbose,
jsonOnly: false,
dangerID: app.id || "default",
Expand Down
29 changes: 29 additions & 0 deletions source/commands/danger-local.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#! /usr/bin/env node

import * as program from "commander"

import setSharedArgs, { SharedCLI } from "./utils/sharedDangerfileArgs"
import { runRunner } from "./ci/runner"
import { LocalGit } from "../platforms/LocalGit"
import { FakeCI } from "../ci_source/providers/Fake"

interface App extends SharedCLI {
/** What should we compare against? */
base?: string
/** Should we run against current staged changes? */
staging?: boolean
}

program
.usage("[options]")
.option("-b, --base [base]", "Choose the base commit to work against.")
// TODO: this option
// .option("-s, --staging", "Just use staged changes.")
.description("Runs danger without PR metadata, useful for git hooks.")
setSharedArgs(program).parse(process.argv)

const app = (program as any) as App
const base = app.base || "master"
const localPlatform = new LocalGit({ base, staged: app.staging })
const fakeSource = new FakeCI(process.env)
runRunner(app, { source: fakeSource, platform: localPlatform, additionalArgs: ["--local"] })
3 changes: 2 additions & 1 deletion source/commands/danger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ program
.command("process", "Like `ci` but lets another process handle evaluating a Dangerfile")
.command("pr", "Runs your local Dangerfile against an existing GitHub PR. Will not post on the PR")
.command("runner", "Runs a dangerfile against a DSL passed in via STDIN [You probably don't need this]")
.command("local", "Runs danger standalone on a repo, useful for git hooks")
.on("--help", () => {
console.log("\n")
console.log(" Docs:")
Expand All @@ -37,7 +38,7 @@ const originalProcessArgV = process.argv
program.parse(process.argv)

const showUpgradeNotice =
process.env.CI && ["init", "ci", "process", "pr", "--help"].some(cmd => originalProcessArgV.includes(cmd))
process.env.CI && ["init", "ci", "process", "pr", "local", "--help"].some(cmd => originalProcessArgV.includes(cmd))

if (showUpgradeNotice) {
console.error("You may have updated from Danger 2.x -> 3.x without updating from `danger` to `danger ci`.")
Expand Down
4 changes: 4 additions & 0 deletions source/commands/utils/_tests/dangerRunToRunnerCLI.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ describe("it can handle the command", () => {
expect(dangerRunToRunnerCLI(["danger", "ci"])).toEqual("danger runner".split(" "))
})

it("`danger local`", () => {
expect(dangerRunToRunnerCLI(["danger", "local"])).toEqual("danger runner".split(" "))
})

it("`danger ci --dangerfile myDangerfile.ts`", () => {
expect(dangerRunToRunnerCLI(["danger", "ci", "--dangerfile", "myDangerfile.ts"])).toEqual(
"danger runner --dangerfile myDangerfile.ts".split(" ")
Expand Down
12 changes: 10 additions & 2 deletions source/commands/utils/dangerRunToRunnerCLI.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
const usesProcessSeparationCommands = ["ci", "pr", "local"]

const dangerRunToRunnerCLI = (argv: string[]) => {
let newCommand = []
newCommand.push(argv[0])

// e.g. node --inspect distribution/commands/danger-run-ci.js --dangerfile myDangerfile.ts
// or node distribution/commands/danger-pr.js --dangerfile myDangerfile.ts

if (argv.length === 1) {
return ["danger", "runner"]
} else if (argv[0].includes("node")) {
const newJSFile = argv[1].replace("-ci", "-runner").replace("-pr", "-runner")
// convert
let newJSFile = argv[1]
usesProcessSeparationCommands.forEach(name => {
newJSFile = newJSFile.replace("-" + name, "-runner")
})

newCommand.push(newJSFile)
for (let index = 2; index < argv.length; index++) {
newCommand.push(argv[index])
Expand All @@ -16,7 +24,7 @@ const dangerRunToRunnerCLI = (argv: string[]) => {
// e.g. danger ci --dangerfile
// if you do `danger run` start looking at args later
newCommand.push("runner")
let index = argv[1] === "ci" || argv[1] === "pr" ? 2 : 1
let index = usesProcessSeparationCommands.includes(argv[1]) ? 2 : 1
for (; index < argv.length; index++) {
newCommand.push(argv[index])
}
Expand Down
2 changes: 2 additions & 0 deletions source/commands/utils/file-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export function dangerfilePath(program: any): string {
if (program.dangerfile) {
return program.dangerfile
}

if (existsSync("dangerfile.ts")) {
return "dangerfile.ts"
}

if (existsSync("dangerfile.js")) {
return "dangerfile.js"
}
Expand Down
4 changes: 4 additions & 0 deletions source/platforms/FakePlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export class FakePlatform implements Platform {
}
}

supportsCommenting() {
return true
}

async updateOrCreateComment(_newComment: string): Promise<boolean> {
return true
}
Expand Down
4 changes: 4 additions & 0 deletions source/platforms/GitHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ export class GitHub {
}
}

supportsCommenting() {
return true
}

/**
* Returns the response for the new comment
*
Expand Down

0 comments on commit f7a5287

Please sign in to comment.