Skip to content

Commit

Permalink
Merge pull request #13 from danger/local
Browse files Browse the repository at this point in the history
Add support for evaling and running Danger on CI
  • Loading branch information
orta authored Oct 16, 2016
2 parents e4267cd + aa9ef9a commit 9577a35
Show file tree
Hide file tree
Showing 17 changed files with 205 additions and 50 deletions.
9 changes: 9 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@
"flowtype"
],
"presets": ["es2015"],

"rules": {
"valid-jsdoc": 1,
"require-jsdoc": ["error", {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": false,
"ClassDeclaration": false
}
}],
"space-before-function-paren": ["error", "never"],
"quotes": [1, "double", "avoid-escape"],
"flowtype/define-flow-type": 1,
Expand Down
7 changes: 6 additions & 1 deletion dangerfile.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// @flow

// import danger from "danger"
import danger from "./source/danger"

// warn on changes in Package.json and not in shrinkwrap

// warn on changelog
// console.log(danger)
// console.log(danger.git)
// console.log(danger.git.created_files)

console.log("Eval'd:", danger.git)

danger.git.created_files.join(" ")
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
"babel-plugin-transform-flow-strip-types": "^6.8.0",
"babel-polyfill": "^6.16.0",
"babel-preset-es2015": "^6.16.0",
"babel-plugin-syntax-async-functions": "^6.13.0",
"babel-plugin-transform-regenerator": "^6.16.1",
"babel-preset-stage-3": "^6.17.0",
"eslint": "^3.3.1",
"eslint-config-standard": "^6.0.0-beta.3",
Expand All @@ -55,8 +57,6 @@
"jest-cli": "^16.0.0"
},
"dependencies": {
"babel-plugin-syntax-async-functions": "^6.13.0",
"babel-plugin-transform-regenerator": "^6.16.1",
"commander": "^2.9.0",
"node-fetch": "^1.6.3",
"parse-diff": "^0.4.0"
Expand Down
24 changes: 24 additions & 0 deletions source/ci_source/ci_source.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// @flow
"strict mode"

/** A json object that represents the outer ENV */
export type Env = any;

/** The shape of an object that represents an individual CI */

export interface CISource {
/** The name, mainly for showing errors */
env: string,

/** The hash of environment variables */
env: Env,

Expand All @@ -27,3 +31,23 @@ export interface CISource {
/** What is the URL for the repo */
repoURL: string,
}

import Travis from "./travis"
import Fake from "./fake"

/**
* Gets a CI Source form the current environment, by asking all known
* sources if they can be represented in this environment.
* @param {Env} env The environment.
* @returns {?CISource} a CI source if it's OK, otherwise Danger can't run.
*/
export function getCISourceForEnv(env: Env) : ?CISource {
// Fake is what I'm using during dev for the minute
let travis = new Travis(env)
if (travis.isCI) {
return travis
} else {
return new Fake()
}
}

14 changes: 12 additions & 2 deletions source/ci_source/ci_source_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@

import type { Env } from "./ci_source"

/** Validates that all ENV keys exist and have a length */
/**
* Validates that all ENV keys exist and have a length
* @param {Env} env The environment.
* @param {[string]} keys Keys to ensure existence of
* @returns {bool} true if they exist, false if not
*/
export function ensureEnvKeysExist(env: Env, keys: string[]) : boolean {
let hasKeys = keys.map((key: string) : boolean => {
return env.hasOwnProperty(key) && env[key].length > 0
})
return !hasKeys.includes(false)
}

/** Validates that all ENV keys exist and can be turned into ints */
/**
* Validates that all ENV keys exist and can be turned into ints
* @param {Env} env The environment.
* @param {[string]} keys Keys to ensure existence and number-ness of
* @returns {bool} true if they are all good, false if not
*/
export function ensureEnvKeysAreInt(env: Env, keys: string[]) : boolean {
let hasKeys = keys.map((key: string) : boolean => {
return env.hasOwnProperty(key) && !isNaN(parseInt(env.TRAVIS_PULL_REQUEST))
Expand Down
11 changes: 0 additions & 11 deletions source/ci_source/ci_source_selector.js
Original file line number Diff line number Diff line change
@@ -1,11 +0,0 @@
// @flow
"strict mode"

import type { Env, CISource } from "./ci_source"
import Travis from "./travis"

/** Here is some docs for the function */
export function getCISourceForEnv(env: Env) : ?CISource {
let travis = new Travis(env)
return travis
}
1 change: 1 addition & 0 deletions source/ci_source/fake.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { Env } from "./ci_source"
export default class FakeCI {
env: Env
constructor(env: Env) { this.env = env }
get name(): string { return "Fake Testing CI" }

get isCI() : boolean { return true }
get isPR() : boolean { return true }
Expand Down
2 changes: 2 additions & 0 deletions source/ci_source/travis.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export default class Travis {
env: Env
constructor(env: Env) { this.env = env }

get name(): string { return "Travis CI" }

get isCI() : boolean {
return ensureEnvKeysExist(this.env, ["HAS_JOSH_K_SEAL_OF_APPROVAL"])
}
Expand Down
38 changes: 18 additions & 20 deletions source/commands/danger-run.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,32 @@ import "babel-polyfill"

var program = require("commander")

import { getCISourceForEnv } from "../ci_source/ci_source_selector"
import { GitHub } from "../platforms/github"

// import type { Platform } from "../platforms/platform"
// import type { CISource } from "../ci_source/ci_source"
import FakeCI from "../ci_source/fake"
import { getCISourceForEnv } from "../ci_source/ci_source"
import { getPlatformForEnv } from "../platforms/platform"
import Executor from "../runner/Executor"

program
.option("-h, --head [commitish]", "TODO: Set the head commitish")
.option("-b, --base [commitish]", "TODO: Set the base commitish")
.option("-f, --fail-on-errors", "TODO: Fail on errors")
.parse(process.argv)

// function setupPlatformWithSource(platform:Platform, source: CISource): void {

// }

let source = getCISourceForEnv(process.env)
let fake = new FakeCI(process.env)
let github = new GitHub("OK", fake)
github.getInfo()
if (!source) {
console.log("Could not find a CI source for this run")
process.exitCode = 1
}

if (source) {
console.log("OK?")
console.log(source.isCI)
console.log("Is PR?")
console.log(source.isPR)
} else {
console.log("Could not find a CI source for this run")
process.exit(0)
const platform = getPlatformForEnv(process.env, source)
if (!platform) {
console.log(`Could not find a source code hosting platform for ${source.name}`)
process.exitCode = 1
}

if (platform) {
console.log(`OK, looks good ${source.name} on ${platform.name}`)
const exec = new Executor(source, platform)
exec.run()
}
}
8 changes: 4 additions & 4 deletions source/commands/danger.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#! /usr/bin/env node

console.log("OK")

// @flow
// This is needed so that other files can use async funcs
import "babel-polyfill"
Expand All @@ -14,7 +17,4 @@ program
.command("run", "Runs danger on your local system", {isDefault: true})
.command("init", "Creates a new Dangerfile.js")
.command("local", "Runs your changes against ")

console.log("pre?")
program.parse(process.argv)
console.log("post?")
.parse(process.argv)
4 changes: 2 additions & 2 deletions source/danger.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
// This file represents the module that is exposed as the danger API
import "babel-polyfill"

type DangerGit = {
type GitDSL = {
modified_files: string[],
created_files: string[],
deleted_files: string[]
}

type DangerDSL = {
git: DangerGit
git: GitDSL
}

const danger: DangerDSL = {
Expand Down
15 changes: 15 additions & 0 deletions source/dsl/DangerDSL.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @flow
"use strict"

// import type { Platform } from "../platforms/platform"
import type { GitDSL } from "../dsl/Git"

export default class DangerDSL {
git: GitDSL
pr: any

constructor(pr: any, git: GitDSL) {
this.git = git
this.pr = pr
}
}
6 changes: 6 additions & 0 deletions source/dsl/git.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface GitDSL {
modified_files: string[],
created_files: string[],
deleted_files: string[]
}

26 changes: 19 additions & 7 deletions source/platforms/github.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow
"use strict"

import type { GitDSL } from "../danger"
import type { CISource } from "../ci_source/ci_source"
import parseDiff from "parse-diff"

Expand All @@ -12,6 +13,8 @@ export type APIToken = string;
export type GraphQLQuery = string;
export type GraphQLResponse = any;

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

export class GitHub {
token: APIToken
ciSource: CISource
Expand All @@ -21,24 +24,33 @@ export class GitHub {
this.ciSource = ciSource
}

async getInfo() : void {
name: "GitHub"

async getReviewInfo() : Promise<any> {
let deets = await this.getPullRequestInfo()
let pr = await deets.json()
console.log(pr)
return await deets.json()
}

async getReviewDiff() : Promise<GitDSL> {
let diffReq = await this.getPullRequestDiff()
let diff = await diffReq.text()

// Worth trying to add a flow-typed for this as a tester?
let fileDiffs: [any] = parseDiff(diff)

let addedDiffs = fileDiffs.filter((diff: any) => diff["new"])
let removedDiffs = fileDiffs.filter((diff: any) => diff["deleted"])
let modified = fileDiffs.filter((diff: any) => !addedDiffs.includes(diff) && !removedDiffs.includes(diff))
let modifiedDiffs = fileDiffs.filter((diff: any) => !addedDiffs.includes(diff) && !removedDiffs.includes(diff))

console.log("Added: ", addedDiffs)
console.log("Removed: ", removedDiffs)
console.log("Modified: ", modified)
return {
modified_files: modifiedDiffs.map((d: any) => d.to),
created_files: addedDiffs.map((d: any) => d.to),
deleted_files: removedDiffs.map((d: any) => d.from)
}
}

// The above is the API for Platform

getUserInfo(): Promise<Response> {
return this.get("user")
}
Expand Down
23 changes: 22 additions & 1 deletion source/platforms/platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ export type Comment = {
}

export interface Platform {
ciSource: CISource
/** Mainly for logging and error reporting */
name: String,
ciSource: CISource,
/** Pulls in the Code Review Metadata for inspection */
getReviewInfo: () => Promise<any>,
/** Pulls in the Code Review Diff, and offers a succinct user-API for it */
fetchDiffInfo: () => Promise<GitDSL>
}

// envVars: () => string[];
// optionalEnvVars: () => string[];

Expand All @@ -32,3 +39,17 @@ export interface Platform {
// /** Edit an existing comment */
// async editComment: (comment: Comment, newBody: string) => Promise<boolean>;
// }

import { GitHub } from "./github"

/**
* Pulls out a platform for Danger to communicate on based on the environment
* @param {Env} env The environment.
* @param {CISource} source The existing source, to ensure they can run against each other
* @returns {?Platform} returns a platform if it can be supported
*/
export function getPlatformForEnv(env: Env, source: CISource) : ?Platform {
let github = new GitHub(env["DANGER_GITHUB_API_TOKEN"], source)
return github
}

40 changes: 40 additions & 0 deletions source/runner/Dangerfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// https://nodejs.org/api/vm.html
// https://60devs.com/executing-js-code-with-nodes-vm-module.html

import fs from "fs"
import vm from "vm"
import DangerDSL from "../dsl/DangerDSL"

export default class Dangerfile {
dsl: DangerDSL
constructor(dsl: DangerDSL) { this.dsl = dsl }

run(file: string) {
fs.readFile(file, "utf8", (err: Error, data: string) => {
if (err) { return console.error(err.message) }

// comment out imports of 'danger'
// e.g `import danger from`
// then user get typed data, and we fill it in
// via the VM context

const cleaned = data.replace(/import danger from/gi, "// import danger from")

const script = new vm.Script(cleaned, {
filename: file,
lineOffset: 1,
columnOffset: 1,
displayErrors: true,
timeout: 1000 // ms
})

const context: any = {
console,
danger: this.dsl
}

script.runInNewContext(context)
})
}
}

Loading

0 comments on commit 9577a35

Please sign in to comment.