diff --git a/changelog.md b/changelog.md index 92aa60bd1..971a588ba 100644 --- a/changelog.md +++ b/changelog.md @@ -2,12 +2,18 @@ // Add your own contribution below +### 0.6.0 + +* omits flow requirement for new test files +* adds support for circleci +* defines CISource properties in flow as read-only + ### 0.5.0 * `danger.pr` -> `danger.github.pr`, I've also created interfaces for them - orta * `warn`, `message`, `markdown` are all ported over to DangerJS - orta * Shows a HTML table for Danger message - orta -* Now offers a Flow-typed definition file, it's not shipped to their repo yet, you can make it by `npm run export-flowtype` - orta +* Now offers a Flow-typed definition file, it's not shipped to their repo yet, you can make it by `npm run export-flowtype` - orta * Started turning this into a real project by adding tests - orta ### 0.0.5-0.0.10 @@ -22,7 +28,7 @@ * Danger will post a comment on a GitHub PR with any Fails - orta -### 0.0.2 +### 0.0.2 OK, first usable for others version. Only supports GitHub and Travis CI. @@ -30,7 +36,7 @@ You can run by doing: ```sh danger -``` +``` Make sure you set a `DANGER_GITHUB_API_TOKEN` on your CI - [see the Ruby guide](http://danger.systems/guides/getting_started.html#setting-up-danger-to-run-on-your-ci) for that. @@ -42,13 +48,13 @@ git fail(message: string) ``` -`pr` _probably_ won't be sticking around for the long run, but if you're using a `0.0.2` release, you should be OK with that. It's the full metadata of the PR, so [this JSON file](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/github_api/pr_response.json). +`pr` _probably_ won't be sticking around for the long run, but if you're using a `0.0.2` release, you should be OK with that. It's the full metadata of the PR, so [this JSON file](https://raw.githubusercontent.com/danger/danger/master/spec/fixtures/github_api/pr_response.json). `git` currently has: ```sh git.modified_file git.created_files -git.deleted_files +git.deleted_files ``` which are string arrays of files. @@ -66,10 +72,10 @@ if (!hasChangelog) { } ``` -That should do ya. I think. This doens't support babel, and I haven't explored using other modules etc, so +That should do ya. I think. This doens't support babel, and I haven't explored using other modules etc, so ./ ### 0.0.1 -Not usable for others, only stubs of classes etc. \ No newline at end of file +Not usable for others, only stubs of classes etc. diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..3db508af2 --- /dev/null +++ b/circle.yml @@ -0,0 +1,12 @@ +machine: + node: + version: 6.1 + +test: + override: + - npm run build + - npm run link + - danger + post: + - npm run flow + - npm run lint diff --git a/dangerfile.js b/dangerfile.js index e0b315dca..90f35a092 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -12,10 +12,12 @@ if (!hasChangelog) { const jsFiles = danger.git.created_files.filter(path => path.endsWith("js")) // new js files should have `@flow` at the top -const unFlowedFiles = jsFiles.filter(filepath => { - const content = fs.readFileSync(filepath) - return !content.includes("@flow") -}) +// but exclude tests from being flow-ey +const unFlowedFiles = jsFiles.filter(path => !path.endsWith("test.js")) + .filter(filepath => { + const content = fs.readFileSync(filepath) + return !content.includes("@flow") + }) if (unFlowedFiles.length > 0) { warn(`These new JS files do not have Flow enabled: ${unFlowedFiles.join(", ")}`) diff --git a/source/ci_source/Circle.js b/source/ci_source/Circle.js new file mode 100644 index 000000000..540b3e6f2 --- /dev/null +++ b/source/ci_source/Circle.js @@ -0,0 +1,53 @@ +// @flow +"use strict" + +import type { Env } from "./ci_source" +import { ensureEnvKeysExist, ensureEnvKeysAreInt } from "./ci_source_helpers" +export default class Circle { + env: Env + constructor(env: Env) { this.env = env } + + get name(): string { return "Circle CI" } + + get isCI(): boolean { + return ensureEnvKeysExist(this.env, ["CIRCLE_BUILD_NUM"]) + } + + get isPR(): boolean { + if (ensureEnvKeysExist(this.env, ["CI_PULL_REQUEST"])) { + return true + } + + const mustHave = ["CIRCLE_CI_API_TOKEN", "CIRCLE_PROJECT_USERNAME", "CIRCLE_PROJECT_REPONAME", "CIRCLE_BUILD_NUM"] + return ensureEnvKeysExist(this.env, mustHave) && ensureEnvKeysAreInt(this.env, ["CIRCLE_PR_NUMBER"]) + } + + _prParseURL(): {owner?: string, reponame?: string, id?: string} { + const prUrl = this.env.CI_PULL_REQUEST || "" + const splitSlug = prUrl.split("/") + if (splitSlug.length === 7) { + const owner = splitSlug[3] + const reponame = splitSlug[4] + const id = splitSlug[6] + return {owner, reponame, id} + } + return {} + } + + get pullRequestID(): string { + if (this.env.CIRCLE_PR_NUMBER) { + return this.env.CIRCLE_PR_NUMBER + } else { + const {id} = this._prParseURL() + return id || "" + } + } + + get repoSlug(): string { + const {owner, reponame} = this._prParseURL() + return (owner && reponame) ? `${owner}/${reponame}` : "" + } + + get repoURL(): string { return this.env.CIRCLE_REPOSITORY_URL } + get supportedPlatforms(): string[] { return ["github"] } +} diff --git a/source/ci_source/_tests/_circle.test.js b/source/ci_source/_tests/_circle.test.js new file mode 100644 index 000000000..4abf258f0 --- /dev/null +++ b/source/ci_source/_tests/_circle.test.js @@ -0,0 +1,83 @@ +import Circle from "../Circle" + +const correctEnv = { + "CIRCLE_CI_API_TOKEN": "xxx", + "CIRCLE_PROJECT_USERNAME": "circle_org", + "CIRCLE_PROJECT_REPONAME": "someproject", + "CIRCLE_BUILD_NUM": "1501", + "CIRCLE_PR_NUMBER": "800", + "CI_PULL_REQUEST": "https://github.com/artsy/eigen/pull/800" +} + +describe(".isCI", () => { + test("validates when all Circle environment vars are set", () => { + const circle = new Circle(correctEnv) + expect(circle.isCI).toBeTruthy() + }) + + test("does not validate without josh", () => { + const circle = new Circle({}) + expect(circle.isCI).toBeFalsy() + }) +}) + +describe(".isPR", () => { + test("validates when all circle environment vars are set", () => { + const circle = new Circle(correctEnv) + expect(circle.isPR).toBeTruthy() + }) + + test("does not validate outside of circle", () => { + const circle = new Circle({}) + expect(circle.isPR).toBeFalsy() + }) + + const envs = ["CIRCLE_CI_API_TOKEN", "CIRCLE_PROJECT_USERNAME", "CIRCLE_PROJECT_REPONAME", "CIRCLE_BUILD_NUM"] + envs.forEach((key: string) => { + var env = { + "CIRCLE_CI_API_TOKEN": "xxx", + "CIRCLE_PROJECT_USERNAME": "circle_org", + "CIRCLE_PROJECT_REPONAME": "someproject", + "CIRCLE_BUILD_NUM": "1501", + "CIRCLE_PR_NUMBER": "800", + "CI_PULL_REQUEST": "https://github.com/artsy/eigen/pull/800" + } + env[key] = null + + test(`does not validate when ${key} is missing`, () => { + const circle = new Circle({}) + expect(circle.isPR).toBeFalsy() + }) + }) + + it("needs to have a PR number", () => { + var env = { + "CIRCLE_PR_NUMBER": "asdasd", + "CIRCLE_REPO_SLUG": "artsy/eigen" + } + const circle = new Circle(env) + expect(circle.isPR).toBeFalsy() + }) +}) + +describe(".pullReuestID", () => { + it("pulls it out of the env", () => { + const circle = new Circle({CIRCLE_PR_NUMBER: "800"}) + expect(circle.pullRequestID).toEqual("800") + }) + + it("can derive it from PR Url", () => { + var env = { + "CI_PULL_REQUEST": "https://github.com/artsy/eigen/pull/23" + } + const circle = new Circle(env) + expect(circle.pullRequestID).toEqual("23") + }) +}) + +describe(".repoSlug", () => { + it("derives it from the PR Url", () => { + const circle = new Circle(correctEnv) + expect(circle.repoSlug).toEqual("artsy/eigen") + }) +}) diff --git a/source/ci_source/ci_source.js b/source/ci_source/ci_source.js index 0c3dc6601..4c02b169d 100644 --- a/source/ci_source/ci_source.js +++ b/source/ci_source/ci_source.js @@ -35,6 +35,7 @@ export interface CISource { } import Travis from "./Travis" +import Circle from "./Circle" import Fake from "./Fake" /** @@ -46,8 +47,12 @@ import Fake from "./Fake" export function getCISourceForEnv(env: Env) : ?CISource { // Fake is what I'm using during dev for the minute const travis = new Travis(env) + const circle = new Circle(env) + if (travis.isCI) { return travis + } else if (circle.isCI) { + return circle } else { return new Fake() }