Skip to content

Commit

Permalink
Merge pull request #341 from danger/danger-process
Browse files Browse the repository at this point in the history
Adds a Danger Process command that outputs the DSL as a JSON object
  • Loading branch information
orta committed Aug 26, 2017
2 parents 73b44e0 + d23d2ec commit fa3b711
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 0 deletions.
36 changes: 36 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@

// TODO

* Adds a `danger process` command, this command takes amn argument of a process to run which expects the Danger DSL as JSON in STDIN,
and will post a DangerResults object to it's STDOUT. This frees up another process to do whatever they want. So, others
can make their own Danger runner.

An example of this is [Danger Swift][danger-swift]. It takes a [JSON][swift-json] file via [STDIN][swift-stdin], [compiles
and evaluates][swift-eval] a [Swift file][swift-dangerfile] then passes the results back to `danger process` via [STDOUT][swift-stdout].

Another example is this simple Ruby script:

```ruby
#!/usr/bin/env ruby

require 'json'
dsl_json = STDIN.tty? ? 'Cannot read from STDIN' : $stdin.read
danger = JSON.parse(dsl_json)
results = { warnings: [], messages:[], fails: [], markdowns: [] }

if danger.github.pr.body.include? "Hello world"
results.messages << { message: "Hey there" }
end

require 'json'
STDOUT.write(results.to_json)
```

Which is basically Ruby Danger in ~10LOC. Lols.

This is the first release of the command, it's pretty untested, but it is usable IMO.

[danger-swift]: https://github.com/danger/danger-swift
[swift-json]: https://github.com/danger/danger-swift/blob/master/fixtures/eidolon_609.json
[swift-stdin]: https://github.com/danger/danger-swift/blob/1576e336e41698861456533463c8821675427258/Sources/Runner/main.swift#L9-L11
[swift-eval]: https://github.com/danger/danger-swift/blob/1576e336e41698861456533463c8821675427258/Sources/Runner/main.swift#L23-L40
[swift-dangerfile]: https://github.com/danger/danger-swift/blob/1576e336e41698861456533463c8821675427258/Dangerfile.swift
[swift-stdout]: https://github.com/danger/danger-swift/blob/1576e336e41698861456533463c8821675427258/Sources/Runner/main.swift#L48-L50

### 2.0.0-alpha.9

* Uses the Babel 7 alpha for all source compilation with JS, Flow+JS and TS. This worked without any changes to our
Expand Down
10 changes: 10 additions & 0 deletions scripts/danger_runner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env ruby

str = STDIN.tty? ? 'Cannot read from STDIN' : $stdin.read
exit(1) unless str

# Have a dumb fake response
require 'json'
results = { fails: [], warnings: [], messages: [], markdowns: [] }.to_json

STDOUT.write(results)
150 changes: 150 additions & 0 deletions source/commands/danger-process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Given the nature of this command, it can be tricky to test, so I use a command like this:
//
// env DANGER_GITHUB_API_TOKEN='xxx' DANGER_FAKE_CI="YEP" DANGER_TEST_REPO='artsy/eigen' DANGER_TEST_PR='2408'
// yarn ts-node -s -- source/commands/danger-process.ts ./scripts/danger_runner.rb
//
//

import { spawn } from "child_process"

import * as program from "commander"
import { getCISource } from "../ci_source/get_ci_source"
import { getPlatformForEnv } from "../platforms/platform"
import { Executor } from "../runner/Executor"
import { providers } from "../ci_source/providers"
import { sentence } from "../runner/DangerUtils"
import * as chalk from "chalk"

declare const global: any

let subprocessName: string | undefined

program
.usage("[options] <process_name>")
.description(
"Does a Danger run, but instead of handling the execution of a Dangerfile it will pass the DSL " +
"into another process expecting the process to eventually return results back as JSON. If you don't " +
"provide another process, then it will output to STDOUT."
)
.option("-v, --verbose", "Verbose output of files")
.option("-c, --external-ci-provider [modulePath]", "Specify custom CI provider")
.option("-t, --text-only", "Provide an STDOUT only interface, Danger will not post to your PR")
.action(process_name => (subprocessName = process_name))
.parse(process.argv)

// The dynamic nature of the program means typecasting a lot
// use this to work with dynamic propeties
const app = program as any

process.on("unhandledRejection", function(reason: string, _p: any) {
console.log(chalk.red("Error: "), reason)
process.exitCode = 1
})

// const encoding = "utf-8"
// let data = ""

// process.stdin.setEncoding(encoding)

// process.stdin.on("readable", function() {
// var chunk
// while ((chunk = process.stdin.read())) {
// data += chunk
// }
// })

// process.stdin.on("end", function() {
// // There will be a trailing \n from the user hitting enter. Get rid of it.
// data = data.replace(/\n$/, "")
// processIncomingResults(data)
// })

if (process.env["DANGER_VERBOSE"] || app.verbose) {
global.verbose = true
}

// const processIncomingResults = (response: string) => response

// a dirty wrapper to allow async functionality in the setup
async function run(): Promise<any> {
const source = getCISource(process.env, app.externalCiProvider || undefined)

if (!source) {
console.log("Could not find a CI source for this run. Does Danger support this CI service?")
console.log(`Danger supports: ${sentence(providers.map(p => p.name))}.`)

if (!process.env["CI"]) {
console.log("You may want to consider using `danger pr` to run Danger locally.")
}

process.exitCode = 1
}
// run the sources setup function, if it exists
if (source && source.setup) {
await source.setup()
}

if (source && !source.isPR) {
// This does not set a failing exit code
console.log("Skipping Danger due to not this run not executing on a PR.")
}

if (source && source.isPR) {
const platform = getPlatformForEnv(process.env, source)
if (!platform) {
console.log(chalk.red(`Could not find a source code hosting platform for ${source.name}.`))
console.log(
`Currently DangerJS only supports GitHub, if you want other platforms, consider the Ruby version or help out.`
)
process.exitCode = 1
}

if (platform) {
const config = {
stdoutOnly: app.textOnly,
verbose: app.verbose,
}

const exec = new Executor(source, platform, config)
const dangerDSL = await exec.dslForDanger()

// Remove this to reduce STDOUT spam
if (dangerDSL.github && dangerDSL.github.api) {
delete dangerDSL.github.api
// Add an API token?
}

const dslJSONString = JSON.stringify(dangerDSL, null, " ") + "\n"
if (!subprocessName) {
// Just pipe it out to the CLI
process.stdout.write(dslJSONString)
} else {
const child = spawn(subprocessName)

child.stdin.write(dslJSONString)
child.stdin.end()

child.stdout.on("data", async data => {
console.log(`stdout: ${data}`)

data = data.toString()
const trimmed = data.trim()
if (trimmed.startsWith("{") && trimmed.endsWith("}") && trimmed.includes("markdowns")) {
const results = JSON.parse(trimmed)
await exec.handleResults(results)
}
})

child.stderr.on("data", data => {
console.log(`stderr: ${data}`)
})

child.on("close", code => {
console.log(`child process exited with code ${code}`)
})
}
}
}
}

run()

0 comments on commit fa3b711

Please sign in to comment.