Skip to content

Commit

Permalink
Merge pull request #58 from danger/internals
Browse files Browse the repository at this point in the history
WIP to allow Peril to run the Executor
  • Loading branch information
orta committed Dec 30, 2016
2 parents 1f9a5c2 + 5913e6c commit 8e2b30e
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"distribution/": true
},
"files.associations": {
"*.js": "javascriptreact"
"**/*.js": "javascriptreact"
}
}
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

// Add your own contribution below

* Fixes to the shipped Flow/TS definitions - orta
* Adds more functions the the internal Danger GitHub client - orta
* Infrastructure work to allow Peril to run a Dangerfile - orta
* Upgrade outdated ESLint packages - macklinu
* Enhance Windows OS compatibility - kwonoj

Expand All @@ -17,8 +20,10 @@
* You can build and run in vscode using your own custom `env/development.env` file. This is useful because you can use the debugger against a real PR. See `env/development.env.example` for syntax. - orta

* Uses `jest-transform` and `jest-runtime` to eval and apply babel transforms.

This does two things, makes it feasible to do [hosted-danger](https://github.com/danger/peril) and
makes it possible to write your Dangerfile in a way that's consistent with the rest of your JavaScript. - orta

* Add tests directory to .npmignore - macklinu
* Update to Jest 18 - macklinu

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"homepage": "https://github.com/danger/danger-js#readme",
"devDependencies": {
"@types/commander": "^2.3.31",
"@types/node-fetch": "^1.6.5",
"@types/node-fetch": "^1.6.6",
"babel-cli": "^6.16.0",
"babel-eslint": "^7.1.1",
"babel-plugin-syntax-async-functions": "^6.13.0",
Expand Down
9 changes: 8 additions & 1 deletion scripts/create-flow-typed-export.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,14 @@ declare module "danger" {
const inlineFlow = flowTyped.replace('declare module "danger" {', "")
const inlineDefinition = inlineFlow.replace(/declare/g, "")
const withoutLastBrace = inlineDefinition.substring(0, inlineDefinition.lastIndexOf("}"))
fs.writeFileSync("distribution/danger.js.flow", withoutLastBrace)

// Replace all lines with versions that fake the function `{}` bit
const finalFlow = withoutLastBrace.split("\n").map((line) => {
if (!line.includes("function")) { return line }
else { return line.replace(";", " {};") }
}).join("\n")

fs.writeFileSync("distribution/danger.js.flow", finalFlow)
console.log("Awesome - shipped to distribution/danger.js.flow")
console.log("This will get sent off with the npm modile.")
})
39 changes: 39 additions & 0 deletions source/api/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import fetch from "node-fetch"

/**
* Adds logging to every fetch request if a global var for `verbose` is set to true
*
* @param {(string | fetch.Request)} url the request
* @param {fetch.RequestInit} [init] the usual options
* @returns {Promise<fetch.Response>} network-y promise
*/
export default function api(url: string | fetch.Request, init?: fetch.RequestInit): Promise<fetch.Response> {
if (global.verbose && global.verbose === true) {
const output = ["curl", "-i"]

if (init.method) {
output.push(`-X ${init.method}`)
}

if (init.headers) {
for (const prop in init.headers) {
if (init.headers.hasOwnProperty(prop)) {
output.push("-H", `"${prop}: ${init.headers[prop]}"`)
}
}
}

if (init.method === "POST") {
// const body:string = init.body
// output.concat([init.body])
}

if (typeof url === "string") {
output.push(url)
}

console.log(output.join(" ")) // tslint:disable-line
}

return fetch(url, init)
}
2 changes: 1 addition & 1 deletion source/commands/danger-run.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ if (source && source.isPR) {
console.log(`OK, looks good ${source.name} on ${platform.name}`)
try {
const exec = new Executor(source, platform)
exec.runDanger()
exec.setupAndRunDanger("dangerfile.js")
} catch (error) {
process.exitCode = 1
console.error(error.message)
Expand Down
File renamed without changes.
52 changes: 44 additions & 8 deletions source/platforms/GitHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { GitDSL } from "../dsl/GitDSL"
import type { CISource } from "../ci_source/ci_source"
import parseDiff from "parse-diff"

import fetch from "node-fetch"
import fetch from "../api/fetch"
import "babel-polyfill"

import os from "os"
Expand All @@ -15,17 +15,25 @@ import os from "os"

export type APIToken = string;

// TODO: Cache PR JSON

/** This represent the GitHub API, and conforming to the Platform Interface */

export class GitHub {
token: APIToken
ciSource: CISource
name: string
// A fetch-api shaped function
fetch: any

constructor(token: APIToken, ciSource: CISource) {
this.token = token
this.ciSource = ciSource
this.name = "GitHub"

// This allows Peril to DI in a new Fetch function
// which can handle unique API edge-cases around integrations
this.fetch = fetch
}

/**
Expand Down Expand Up @@ -122,6 +130,26 @@ export class GitHub {
return commentID !== null
}

/**
* Grabs the contents of an individual file on GitHub
*
* @param {string} path path to the file
* @param {string} [ref] an optional sha
* @returns {Promise<string>} text contents
*
*/
async fileContents(path: string, ref?: string): Promise<string> {
// Use head of PR (current state of PR) if no ref passed
if (!ref) {
const prJSON = await this.getReviewInfo()
ref = prJSON.head.ref
}
const fileMetadata = await this.getFileContents(path, ref)
const data = await fileMetadata.json()
const buffer = new Buffer(data.content, "base64")
return buffer.toString()
}

// The above is the API for Platform

async getDangerCommentID(): Promise<?number> {
Expand Down Expand Up @@ -157,7 +185,7 @@ export class GitHub {
})
}

getPullRequestInfo(): Promise<Response> {
getPullRequestInfo(): Promise <Response> {
const repo = this.ciSource.repoSlug
const prID = this.ciSource.pullRequestID
return this.get(`repos/${repo}/pulls/${prID}`)
Expand All @@ -183,37 +211,45 @@ export class GitHub {
})
}

getFileContents(path: string, ref: string): Promise<Response> {
const repo = this.ciSource.repoSlug
return this.get(`repos/${repo}/contents/${path}?ref=${ref}`, {})
}

// maybe this can move into the stuff below
post(path: string, headers: any = {}, body: any = {}, method: string = "POST"): Promise<Response> {
return fetch(`https://api.github.com/${path}`, {
return this.fetch(`https://api.github.com/${path}`, {
method: method,
body: JSON.stringify(body),
headers: {
"Authorization": `token ${this.token}`,
"Content-Type": "application/json",
...headers }
...headers
}
})
}

get(path: string, headers: any = {}, body: any = {}, method: string = "GET"): Promise<Response> {
return fetch(`https://api.github.com/${path}`, {
return this.fetch(`https://api.github.com/${path}`, {
method: method,
body: body,
headers: {
"Authorization": `token ${this.token}`,
"Content-Type": "application/json",
...headers }
...headers
}
})
}

patch(path: string, headers: any = {}, body: any = {}, method: string = "PATCH"): Promise<Response> {
return fetch(`https://api.github.com/${path}`, {
return this.fetch(`https://api.github.com/${path}`, {
method: method,
body: JSON.stringify(body),
headers: {
"Authorization": `token ${this.token}`,
"Content-Type": "application/json",
...headers }
...headers
}
})
}
}
2 changes: 1 addition & 1 deletion source/runner/Dangerfile.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type { DangerResults } from "./DangerResults"
import type { DangerResults } from "../dsl/DangerResults"
import type { DangerDSLType } from "../dsl/DangerDSL"
import type { MarkdownString } from "../dsl/Aliases"

Expand Down
70 changes: 24 additions & 46 deletions source/runner/DangerfileRunner.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,21 @@
// @flow

type Path = string

interface ResolverConfig {
browser?: boolean,
defaultPlatform: ?string,
extensions: Array<string>,
hasCoreModules: boolean,
moduleDirectories: Array<string>,
moduleNameMapper: ?{[key: string]: RegExp},
modulePaths: Array<Path>,
platforms?: Array<string>,
};

interface HasteConfig {
providesModuleNodeModules: Array<string>,
defaultPlatform?: ?string,
platforms?: Array<string>,
};

interface Environment {
constructor(config: any): void;
dispose(): void;
runScript(script: any): any;
global: any;
fakeTimers: {
clearAllTimers(): void;
runAllImmediates(): void;
runAllTicks(): void;
runAllTimers(): void;
runTimersToTime(): void;
runOnlyPendingTimers(): void;
runWithRealTimers(callback: any): void;
useFakeTimers(): void;
useRealTimers(): void;
};
testFilePath: string;
moduleMocker: any;
};

import Runtime from "jest-runtime"
import NodeEnvironment from "jest-environment-node"
import os from "os"

import type { DangerResults } from "../runner/DangerResults"
import type { DangerResults } from "../dsl/DangerResults"
import type { DangerContext } from "../runner/Dangerfile"
import type { DangerfileRuntimeEnv, Environment, Path } from "./types"

/**
* Executes a Dangerfile at a specific path, with a context.
* The values inside a Danger context are applied as globals to the Dangerfiles runtime.
*
* @param {string} filename the file path for the dangerfile
* @param {DangerContext} dangerfileContext the gloabl danger context
* @returns {DangerResults} the results of the run
* @returns {any} the results of the run
*/
export async function runDangerfile(filename: string, dangerfileContext: DangerContext): Promise<DangerResults> {
export async function createDangerfileRuntimeEnvironment(dangerfileContext: DangerContext): Promise<DangerfileRuntimeEnv> {
const config = {
cacheDirectory: os.tmpdir(),
setupFiles: [],
Expand All @@ -71,7 +32,8 @@ export async function runDangerfile(filename: string, dangerfileContext: DangerC
testPathDirs: [process.cwd()]
}

const environment:Environment = new NodeEnvironment(config)
const environment: Environment = new NodeEnvironment(config)

const runnerGlobal = environment.global
const context = dangerfileContext

Expand All @@ -89,9 +51,25 @@ export async function runDangerfile(filename: string, dangerfileContext: DangerC
const resolver = Runtime.createResolver(config, hasteMap.moduleMap)
const runtime = new Runtime(config, environment, resolver)

return {
context,
environment,
runtime
}
}

/**
* Executes a Dangerfile at a specific path, with a context.
* The values inside a Danger context are applied as globals to the Dangerfiles runtime.
*
* @param {string} filename the file path for the dangerfile
* @param {any} environment the results of createDangerfileRuntimeEnvironment
* @returns {DangerResults} the results of the run
*/
export async function runDangerfileEnvironment(filename: Path, environment: DangerfileRuntimeEnv): Promise<DangerResults> {
const runtime = environment.runtime
// Require our dangerfile
// TODO: This needs to be able to support arbitrary strings for Peril
runtime.requireModule(filename)

return context.results
return environment.context.results
}
33 changes: 27 additions & 6 deletions source/runner/Executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { contextForDanger } from "../runner/Dangerfile"
import { DangerDSL } from "../dsl/DangerDSL"
import type { CISource } from "../ci_source/ci_source"
import { Platform } from "../platforms/platform"
import type { DangerResults } from "../runner/DangerResults"
import type { DangerResults } from "../dsl/DangerResults"
import githubResultsTemplate from "./templates/github-issue-template"
import { runDangerfile } from "./DangerfileRunner"
import { createDangerfileRuntimeEnvironment, runDangerfileEnvironment } from "./DangerfileRunner"
import type { DangerfileRuntimeEnv } from "./types"

// This is still badly named, maybe it really should just be runner?

Expand All @@ -19,14 +20,34 @@ export default class Executor {
this.platform = platform
}

/** Mainly just a dumb helper because I can't do
* async functions in danger-run.js
* @param {string} file the path to run Danger from
* @returns {void} It's a promise, so a void promise
*/
async setupAndRunDanger(file: string) {
const runtimeEnv = await this.setupDanger()
await this.runDanger(file, runtimeEnv)
}

/**
* Runs all of the operations for a running just Danger
* @returns {void} It's a promise, so a void promise
* @returns {DangerfileRuntimeEnv} A runtime environment to run Danger in
*/
async runDanger() {
async setupDanger(): DangerfileRuntimeEnv {
const dsl = await this.dslForDanger()
const context = contextForDanger(dsl)
const results = await runDangerfile("dangerfile.js", context)
return await createDangerfileRuntimeEnvironment(context)
}

/**
* Runs all of the operations for a running just Danger
* @param {string} file the filepath to the Dangerfile
* @returns {void} It's a promise, so a void promise
*/

async runDanger(file: string, runtime: DangerfileRuntimeEnv) {
const results = await runDangerfileEnvironment(file, runtime)
await this.handleResults(results)
}

Expand All @@ -41,8 +62,8 @@ export default class Executor {

/**
* Handle the messaing aspects of running a Dangerfile
* @returns {void} It's a promise, so a void promise
* @param {DangerResults} results a JSON representation of the end-state for a Danger run
* @returns {void} It's a promise, so a void promise
*/
async handleResults(results: DangerResults) {
// Ensure process fails if there are fails
Expand Down

0 comments on commit 8e2b30e

Please sign in to comment.