-
-
Notifications
You must be signed in to change notification settings - Fork 364
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
249 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// @flow | ||
"use strict" | ||
|
||
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 } | ||
|
||
get pullRequestID(): string { return "327" } | ||
get repoSlug(): string { return "artsy/emission" } | ||
get repoURL(): string { return "maybe not needed?" } | ||
get supportedPlatforms() : string[] { return ["github"] } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// @flow | ||
"use strict" | ||
|
||
import type { Env } from "./ci_source" | ||
import { ensureEnvKeysExist, ensureEnvKeysAreInt } from "./ci_source_helpers" | ||
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"]) | ||
} | ||
|
||
get isPR() : boolean { | ||
const mustHave = ["HAS_JOSH_K_SEAL_OF_APPROVAL", "TRAVIS_PULL_REQUEST"] | ||
const mustBeInts = ["TRAVIS_REPO_SLUG"] | ||
return ensureEnvKeysExist(this.env, mustHave) && ensureEnvKeysAreInt(this.env, mustBeInts) | ||
} | ||
|
||
get pullRequestID(): string { return this.env.TRAVIS_PULL_REQUEST } | ||
get repoSlug(): string { return this.env.TRAVIS_REPO_SLUG } | ||
get repoURL(): string { return "maybe not needed?" } | ||
get supportedPlatforms() : string[] { return ["github"] } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
// @flow | ||
"use strict" | ||
|
||
import type { GitDSL } from "../dsl/git" | ||
import type { CISource } from "../ci_source/ci_source" | ||
import parseDiff from "parse-diff" | ||
|
||
import fetch from "node-fetch" | ||
import "babel-polyfill" | ||
|
||
// This pattern of re-typing specific strings has worked well for Artsy in Swift | ||
// so, I'm willing to give it a shot here. | ||
|
||
export type APIToken = string; | ||
|
||
/** This represent the GitHub API, and conforming to the Platform Interface */ | ||
|
||
export class GitHub { | ||
token: APIToken | ||
ciSource: CISource | ||
name: string | ||
|
||
constructor(token: APIToken, ciSource: CISource) { | ||
this.token = token | ||
this.ciSource = ciSource | ||
this.name = "GitHub" | ||
} | ||
|
||
/** | ||
* Get the Code Review description metadata | ||
* | ||
* @returns {Promise<any>} JSON representation | ||
*/ | ||
async getReviewInfo() : Promise<any> { | ||
const deets = await this.getPullRequestInfo() | ||
return await deets.json() | ||
} | ||
|
||
/** | ||
* Get the Code Review diff representation | ||
* | ||
* @returns {Promise<GitDSL>} the git DSL | ||
*/ | ||
async getReviewDiff() : Promise<GitDSL> { | ||
const diffReq = await this.getPullRequestDiff() | ||
const diff = await diffReq.text() | ||
|
||
// Worth trying to add a flow-typed for this as a tester? | ||
const fileDiffs: [any] = parseDiff(diff) | ||
|
||
const addedDiffs = fileDiffs.filter((diff: any) => diff["new"]) | ||
const removedDiffs = fileDiffs.filter((diff: any) => diff["deleted"]) | ||
const modifiedDiffs = fileDiffs.filter((diff: any) => !addedDiffs.includes(diff) && !removedDiffs.includes(diff)) | ||
|
||
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) | ||
} | ||
} | ||
|
||
async updateOrCreateComment(newComment: string): Promise<bool> { | ||
const commentID = await this.getDangerCommentID() | ||
if (commentID) { await this.updateCommentWithID(commentID, newComment) } | ||
else { await this.createComment(newComment) } | ||
return true | ||
} | ||
|
||
/** | ||
* Returns the response for the new comment | ||
* | ||
* @param {string} comment you want to post | ||
* @returns {Promise<any>} JSON response of new comment | ||
*/ | ||
async createComment(comment: string): Promise<any> { | ||
return this.postPRComment(comment) | ||
} | ||
|
||
// In Danger RB we support a danger_id property, | ||
// this should be handled at some point | ||
|
||
/** | ||
* Deletes the main Danger comment, used when you have | ||
* fixed all your failures. | ||
* | ||
* @returns {Promise<bool>} did it work? | ||
*/ | ||
async deleteMainComment(): Promise<bool> { | ||
const commentID = await this.getDangerCommentID() | ||
if (commentID) { await this.deleteCommentWithID(commentID) } | ||
return commentID !== null | ||
} | ||
|
||
/** | ||
* Updates the main Danger comment, when Danger has run | ||
* more than once | ||
* | ||
* @param {string} comment updated text | ||
* | ||
* @returns {Promise<bool>} did it work? | ||
*/ | ||
async editMainComment(comment: string): Promise<bool> { | ||
const commentID = await this.getDangerCommentID() | ||
if (commentID) { await this.updateCommentWithID(commentID, comment) } | ||
return commentID !== null | ||
} | ||
|
||
// The above is the API for Platform | ||
|
||
async getDangerCommentID(): Promise<?number> { | ||
const userID = await this.getUserID() | ||
const allCommentsResponse = await this.getPullRequestComments() | ||
const allComments: any[] = await allCommentsResponse.json() | ||
const dangerComment = allComments.find((comment: any) => comment.user.id === userID) | ||
return dangerComment ? dangerComment.id : null | ||
} | ||
|
||
async updateCommentWithID(id: number, comment: string): Promise<Response> { | ||
const repo = this.ciSource.repoSlug | ||
return this.patch(`repos/${repo}/issues/comments/${id}`, {}, { | ||
body: comment | ||
}) | ||
} | ||
|
||
async deleteCommentWithID(id: number): Promise<Response> { | ||
const repo = this.ciSource.repoSlug | ||
return this.get(`repos/${repo}/issues/comments/${id}`, {}, {}, "DELETE") | ||
} | ||
|
||
async getUserID(): Promise<number> { | ||
const info = await this.getUserInfo() | ||
return info.id | ||
} | ||
|
||
postPRComment(comment: string): Promise<Response> { | ||
const repo = this.ciSource.repoSlug | ||
const prID = this.ciSource.pullRequestID | ||
return this.post(`repos/${repo}/issues/${prID}/comments`, {}, { | ||
body: comment | ||
}) | ||
} | ||
|
||
getPullRequestInfo(): Promise<Response> { | ||
const repo = this.ciSource.repoSlug | ||
const prID = this.ciSource.pullRequestID | ||
return this.get(`repos/${repo}/pulls/${prID}`) | ||
} | ||
|
||
async getUserInfo(): Promise<any> { | ||
const response:Response = await this.get("user") | ||
return await response.json() | ||
} | ||
|
||
// TODO: This does not handle pagination | ||
getPullRequestComments(): Promise<Response> { | ||
const repo = this.ciSource.repoSlug | ||
const prID = this.ciSource.pullRequestID | ||
return this.get(`repos/${repo}/issues/${prID}/comments`) | ||
} | ||
|
||
getPullRequestDiff(): Promise<Response> { | ||
const repo = this.ciSource.repoSlug | ||
const prID = this.ciSource.pullRequestID | ||
return this.get(`repos/${repo}/pulls/${prID}`, { | ||
accept: "application/vnd.github.v3.diff" | ||
}) | ||
} | ||
|
||
// 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}`, { | ||
method: method, | ||
body: JSON.stringify(body), | ||
headers: { | ||
"Authorization": `token ${this.token}`, | ||
"Content-Type": "application/json", | ||
...headers } | ||
}) | ||
} | ||
|
||
get(path: string, headers: any = {}, body: any = {}, method: string = "GET"): Promise<Response> { | ||
return fetch(`https://api.github.com/${path}`, { | ||
method: method, | ||
body: body, | ||
headers: { | ||
"Authorization": `token ${this.token}`, | ||
"Content-Type": "application/json", | ||
...headers } | ||
}) | ||
} | ||
|
||
patch(path: string, headers: any = {}, body: any = {}, method: string = "PATCH"): Promise<Response> { | ||
return fetch(`https://api.github.com/${path}`, { | ||
method: method, | ||
body: JSON.stringify(body), | ||
headers: { | ||
"Authorization": `token ${this.token}`, | ||
"Content-Type": "application/json", | ||
...headers } | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters