Skip to content

Commit

Permalink
Merge pull request #6 from JasonEtco/circle-ci
Browse files Browse the repository at this point in the history
Support Circle CI
  • Loading branch information
JasonEtco authored Dec 25, 2017
2 parents 4058e05 + ebd3e82 commit cd13559
Show file tree
Hide file tree
Showing 11 changed files with 714 additions and 8 deletions.
18 changes: 13 additions & 5 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
const { Travis } = require('./providers')
const { Travis, Circle } = require('./providers')

module.exports = robot => {
robot.log('App has started!')

robot.on('status', async context => {
// Only trigger on failed statuses
if (context.payload.state === 'failure') {
let issue
let serializer

const { context: statusContext } = context.payload

// Only trigger on PR (for now, will eventually support Push statuses)
if (statusContext === Travis.ctx) {
const travis = new Travis(context)
issue = await travis.serialize()
serializer = new Travis(context)
} else if (statusContext === Circle.ctx) {
serializer = new Circle(context)
} else {
robot.log(`ctx does not exist: ${statusContext}`)
return
}

return context.github.issues.createComment(context.repo(issue))
// Will return false if something borks
const issue = await serializer.serialize()
if (issue) {
return context.github.issues.createComment(context.repo(issue))
}
}
})
}
68 changes: 68 additions & 0 deletions lib/providers/Circle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const request = require('request-promise-native')

class Circle {
constructor (context) {
this.context = context
}

static get ctx () {
return 'ci/circleci'
}

buildUri ({ owner, repo }, build) { return `https://circleci.com/api/v1.1/project/github/${owner}/${repo}/${build}` }

parseLog (log) {
// sp00ky RegExp to start the extraction
const start = new RegExp(/\r\n|\n/g)

// The first line can be either \n or \r\n, so
// we need to know how to offset it.
const offset = start.exec(log)[0].length

const end = 'Test failed. See above for more details.'

// Get the content between the test and end strings
let content = log.substring(log.search(start) + offset, log.indexOf(end))

// Remove the last line, it's usually extra
content = content.substring(0, content.lastIndexOf('\n'))

return content
}

async serialize () {
// target_url looks like
// https://circleci.com/gh/:owner/:repo/:number (see tests/fixtures/circle/build.json)
const { target_url: targetUrl } = this.context.payload
const build = /https:\/\/circleci\.com\/gh\/(?:.+?\/){2}(\d+)/g.exec(targetUrl)[1]

const buildJson = JSON.parse(await request({
uri: this.buildUri(this.context.repo(), build),
headers: { Accept: 'application/json' }
}))

if (buildJson.pull_requests) {
const prUrl = buildJson.pull_requests[0].url
const number = prUrl.substr(prUrl.lastIndexOf('/')).substr(1)

const ret = { number: parseInt(number, 10) }

const npmTestStep = buildJson.steps.find(step => step.name === 'npm test')

const res = JSON.parse(await request({
uri: npmTestStep.actions[0].output_url,
gzip: true,
headers: { Accept: 'application/json' }
}))

const content = this.parseLog(res[0].message)
ret.body = `:sparkles: Good work on this PR so far! :sparkles: Unfortunately, the [Circle CI build](${targetUrl}) is failing. Here's the output:\n\`\`\`\n${content}\n\`\`\`\nI'm sure you can fix it! If you need help, don't hesitate to ask a maintainer of the project!`

return ret
} else {
return false
}
}
}

module.exports = Circle
3 changes: 2 additions & 1 deletion lib/providers/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module.exports = {
Travis: require('./Travis')
Travis: require('./Travis'),
Circle: require('./Circle')
}
264 changes: 264 additions & 0 deletions tests/fixtures/circle/build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
{
"compare" : "https://github.com/JasonEtco/todo/compare/36eeffbb45a7...f485a7dcc619",
"previous_successful_build" : {
"build_num" : 3,
"status" : "success",
"build_time_millis" : 25854
},
"build_parameters" : null,
"oss" : true,
"all_commit_details_truncated" : false,
"committer_date" : "2017-12-25T11:09:32-05:00",
"steps" : [ {
"name" : "Spin up Environment",
"actions" : [ {
"truncated" : false,
"index" : 0,
"parallel" : true,
"failed" : null,
"infrastructure_fail" : null,
"name" : "Spin up Environment",
"bash_command" : null,
"status" : "success",
"timedout" : null,
"continue" : null,
"end_time" : "2017-12-25T16:09:42.393Z",
"type" : "test",
"allocation_id" : "5a4122a4c9e77c0001868fc8-0-build/76A07AA6",
"output_url" : "https://circle-production-action-output.s3.amazonaws.com/0a90ba100046945f5c2214a5-JasonEtco-todo-0-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20171225T214612Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20171225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=1f94fba0599ca0aa40f1f098310cf0b49590d05b17fec2d87d4583f992fb4dc9",
"start_time" : "2017-12-25T16:09:41.435Z",
"background" : false,
"exit_code" : null,
"insignificant" : false,
"canceled" : null,
"step" : 0,
"run_time_millis" : 958,
"has_output" : true
} ]
}, {
"name" : "Checkout code",
"actions" : [ {
"truncated" : false,
"index" : 0,
"parallel" : true,
"failed" : null,
"infrastructure_fail" : null,
"name" : "Checkout code",
"bash_command" : "#!/bin/sh\nset -e\n\n# Workaround old docker images with incorrect $HOME\n# check https://github.com/docker/docker/issues/2968 for details\nif [ \"${HOME}\" = \"/\" ]\nthen\n export HOME=$(getent passwd $(id -un) | cut -d: -f6)\nfi\n\nmkdir -p ~/.ssh\n\necho 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\nbitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==\n' >> ~/.ssh/known_hosts\n\n(umask 077; touch ~/.ssh/id_rsa)\nchmod 0600 ~/.ssh/id_rsa\n(cat <<EOF > ~/.ssh/id_rsa\n$CHECKOUT_KEY\nEOF\n)\n\n# use git+ssh instead of https\ngit config --global url.\"ssh://[email protected]\".insteadOf \"https://github.com\" || true\n\nif [ -e /home/circleci/repo/.git ]\nthen\n cd /home/circleci/repo\n git remote set-url origin \"$CIRCLE_REPOSITORY_URL\" || true\nelse\n mkdir -p /home/circleci/repo\n cd /home/circleci/repo\n git clone \"$CIRCLE_REPOSITORY_URL\" .\nfi\n\nif [ -n \"$CIRCLE_TAG\" ]\nthen\n git fetch --force origin \"refs/tags/${CIRCLE_TAG}\"\nelse\n git fetch --force origin \"cleanup:remotes/origin/cleanup\"\nfi\n\n\nif [ -n \"$CIRCLE_TAG\" ]\nthen\n git reset --hard \"$CIRCLE_SHA1\"\n git checkout -q \"$CIRCLE_TAG\"\nelif [ -n \"$CIRCLE_BRANCH\" ]\nthen\n git reset --hard \"$CIRCLE_SHA1\"\n git checkout -q -B \"$CIRCLE_BRANCH\"\nfi\n\ngit reset --hard \"$CIRCLE_SHA1\"",
"status" : "success",
"timedout" : null,
"continue" : null,
"end_time" : "2017-12-25T16:09:43.101Z",
"type" : "test",
"allocation_id" : "5a4122a4c9e77c0001868fc8-0-build/76A07AA6",
"output_url" : "https://circle-production-action-output.s3.amazonaws.com/1a90ba100046945f6c2214a5-JasonEtco-todo-101-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20171225T214612Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20171225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=382b4296d6e86081577ae760aa9a1f70eff0ff1d0df9f35bc34db6af0417cc18",
"start_time" : "2017-12-25T16:09:42.595Z",
"background" : false,
"exit_code" : 0,
"insignificant" : false,
"canceled" : null,
"step" : 101,
"run_time_millis" : 506,
"has_output" : true
} ]
}, {
"name" : "Restoring Cache",
"actions" : [ {
"truncated" : false,
"index" : 0,
"parallel" : true,
"failed" : null,
"infrastructure_fail" : null,
"name" : "Restoring Cache",
"bash_command" : null,
"status" : "success",
"timedout" : null,
"continue" : null,
"end_time" : "2017-12-25T16:09:45.725Z",
"type" : "test",
"allocation_id" : "5a4122a4c9e77c0001868fc8-0-build/76A07AA6",
"output_url" : "https://circle-production-action-output.s3.amazonaws.com/2a90ba100046945f7c2214a5-JasonEtco-todo-102-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20171225T214612Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20171225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=ce365dadecd660629ba8347804ea76f09696f179598c3845d740e535606ea0c2",
"start_time" : "2017-12-25T16:09:43.107Z",
"background" : false,
"exit_code" : null,
"insignificant" : false,
"canceled" : null,
"step" : 102,
"run_time_millis" : 2618,
"has_output" : true
} ]
}, {
"name" : "npm install",
"actions" : [ {
"truncated" : false,
"index" : 0,
"parallel" : true,
"failed" : null,
"infrastructure_fail" : null,
"name" : "npm install",
"bash_command" : "#!/bin/bash -eo pipefail\nnpm install",
"status" : "success",
"timedout" : null,
"continue" : null,
"end_time" : "2017-12-25T16:09:56.236Z",
"type" : "test",
"allocation_id" : "5a4122a4c9e77c0001868fc8-0-build/76A07AA6",
"output_url" : "https://circle-production-action-output.s3.amazonaws.com/3a90ba100046945f9c2214a5-JasonEtco-todo-103-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20171225T214612Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20171225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=a4a35677822cf60ef2f5fb9cfb1fe8a21ea3711fe19b91eb45dfcd7b934951cf",
"start_time" : "2017-12-25T16:09:45.731Z",
"background" : false,
"exit_code" : 0,
"insignificant" : false,
"canceled" : null,
"step" : 103,
"run_time_millis" : 10505,
"has_output" : true
} ]
}, {
"name" : "Saving Cache",
"actions" : [ {
"truncated" : false,
"index" : 0,
"parallel" : true,
"failed" : null,
"infrastructure_fail" : null,
"name" : "Saving Cache",
"bash_command" : null,
"status" : "success",
"timedout" : null,
"continue" : null,
"end_time" : "2017-12-25T16:09:56.277Z",
"type" : "test",
"allocation_id" : "5a4122a4c9e77c0001868fc8-0-build/76A07AA6",
"output_url" : "https://circle-production-action-output.s3.amazonaws.com/8a90ba100046945f4d2214a5-JasonEtco-todo-104-0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20171225T214612Z&X-Amz-SignedHeaders=host&X-Amz-Expires=431999&X-Amz-Credential=AKIAIJNI6FA5RIAFFQ7Q%2F20171225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=9a131a034e82a2d7003d81f4447ae53e0e5c15008eb8c6ca18b84414337b8014",
"start_time" : "2017-12-25T16:09:56.241Z",
"background" : false,
"exit_code" : null,
"insignificant" : false,
"canceled" : null,
"step" : 104,
"run_time_millis" : 36,
"has_output" : true
} ]
}, {
"name" : "npm test",
"actions" : [ {
"truncated" : false,
"index" : 0,
"parallel" : true,
"failed" : null,
"infrastructure_fail" : null,
"name" : "npm test",
"bash_command" : "#!/bin/bash -eo pipefail\nnpm test",
"status" : "success",
"timedout" : null,
"continue" : null,
"end_time" : "2017-12-25T16:10:07.280Z",
"type" : "test",
"allocation_id" : "5a4122a4c9e77c0001868fc8-0-build/76A07AA6",
"output_url" : "https://circleci.com/fake-output-url",
"start_time" : "2017-12-25T16:09:56.282Z",
"background" : false,
"exit_code" : 0,
"insignificant" : false,
"canceled" : null,
"step" : 105,
"run_time_millis" : 10998,
"has_output" : true
} ]
} ],
"body" : "",
"usage_queued_at" : "2017-12-25T16:09:08.975Z",
"fail_reason" : null,
"retry_of" : null,
"reponame" : "todo",
"ssh_users" : [ ],
"build_url" : "https://circleci.com/gh/JasonEtco/todo/5",
"parallel" : 1,
"failed" : false,
"branch" : "cleanup",
"username" : "JasonEtco",
"author_date" : "2017-12-25T11:09:32-05:00",
"why" : "github",
"user" : {
"is_user" : true,
"login" : "JasonEtco",
"avatar_url" : "https://avatars1.githubusercontent.com/u/10660468?v=4",
"name" : "Jason Etcovitch",
"vcs_type" : "github",
"id" : 10660468
},
"vcs_revision" : "f485a7dcc6193a33ef1090ef6b6bc5cc8dde75d2",
"owners" : [ "JasonEtco" ],
"vcs_tag" : null,
"pull_requests" : [ {
"head_sha" : "f485a7dcc6193a33ef1090ef6b6bc5cc8dde75d2",
"url" : "https://github.com/JasonEtco/todo/pull/1"
} ],
"build_num" : 5,
"infrastructure_fail" : false,
"committer_email" : "[email protected]",
"has_artifacts" : true,
"previous" : {
"build_num" : 4,
"status" : "failed",
"build_time_millis" : 23348
},
"status" : "fixed",
"committer_name" : "Jason Etcovitch",
"retries" : null,
"subject" : "Fix linter",
"vcs_type" : "github",
"timedout" : false,
"dont_build" : null,
"lifecycle" : "finished",
"no_dependency_cache" : false,
"stop_time" : "2017-12-25T16:10:07.286Z",
"ssh_disabled" : true,
"build_time_millis" : 25884,
"picard" : {
"build_agent" : {
"image" : null,
"properties" : {
"executor" : "docker",
"build_agent" : "0.0.4606-189b121"
}
},
"resource_class" : {
"cpu" : 2.0,
"ram" : 4096,
"class" : "medium"
},
"executor" : "docker"
},
"circle_yml" : {
"string" : "version: 2\njobs:\n build:\n docker:\n - image: circleci/node:8.9.1\n\n working_directory: ~/repo\n\n steps:\n - checkout\n\n # Download and cache dependencies\n - restore_cache:\n keys:\n - v1-dependencies-{{ checksum \"package.json\" }}\n - v1-dependencies-\n\n - run: npm install\n\n - save_cache:\n paths:\n - node_modules\n key: v1-dependencies-{{ checksum \"package.json\" }}\n\n - run: npm test\n"
},
"messages" : [ ],
"is_first_green_build" : false,
"job_name" : null,
"start_time" : "2017-12-25T16:09:41.402Z",
"canceler" : null,
"all_commit_details" : [ {
"committer_date" : "2017-12-25T11:09:32-05:00",
"body" : "",
"branch" : "cleanup",
"author_date" : "2017-12-25T11:09:32-05:00",
"committer_email" : "[email protected]",
"commit" : "f485a7dcc6193a33ef1090ef6b6bc5cc8dde75d2",
"committer_login" : "JasonEtco",
"committer_name" : "Jason Etcovitch",
"subject" : "Fix linter",
"commit_url" : "https://github.com/JasonEtco/todo/commit/f485a7dcc6193a33ef1090ef6b6bc5cc8dde75d2",
"author_login" : "JasonEtco",
"author_name" : "Jason Etcovitch",
"author_email" : "[email protected]"
} ],
"platform" : "2.0",
"outcome" : "success",
"vcs_url" : "https://github.com/JasonEtco/todo",
"author_name" : "Jason Etcovitch",
"node" : null,
"queued_at" : "2017-12-25T16:09:37.419Z",
"canceled" : false,
"author_email" : "[email protected]"
}
Loading

0 comments on commit cd13559

Please sign in to comment.