Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a Danger Process command that outputs the DSL as a JSON object #341

Merged
merged 5 commits into from
Aug 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor clarification: JSON file may be more accurate as JSON document if I'm understanding correctly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct, I've just updated the changelog: d0fc1eb

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()