From 38980d814c5725eb1082b6e7aaa3e5f8a589133f Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 13 Jul 2012 16:53:09 -0700 Subject: [PATCH 01/76] Doc cleanup --- Readme.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index ab2fe59..c2dc745 100644 --- a/Readme.md +++ b/Readme.md @@ -89,11 +89,17 @@ This will happen with the bespoke methods as well: gh.branches "githubot", (branches) -> ``` -If `process.env.HUBOT_GITHUB_API` exists, it will override the default API URL. This is useful for Enterprise Github installations. +### Options ### - HUBOT_GITHUB_API='http://myprivate.github.int' +* `HUBOT_GITHUB_TOKEN`: GitHub API token. Required to perform authenticated actions. -`process.env.HUBOT_CONCURRENT_REQUESTS` limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. +* `HUBOT_GITHUB_USER`: Default GitHub username to use if one is not given. + +* `HUBOT_GITHUB_API`: The base API URL. This is useful for Enterprise Github installations. + + For example, `HUBOT_GITHUB_API='http://myprivate.github.int'` + +* `HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. ## [Contributors](https://github.com/iangreenleaf/githubot/contributors) ## From 34e09ddd9605143c770f6d65165d872536ebe64b Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 14 Jul 2012 22:27:22 -0700 Subject: [PATCH 02/76] Work around nock error on node 0.4 --- test/windowing.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/windowing.coffee b/test/windowing.coffee index 28612fb..d6edaaa 100644 --- a/test/windowing.coffee +++ b/test/windowing.coffee @@ -1,4 +1,7 @@ -[ gh, assert, nock, mock_robot ] = require "./test_helper" +# For some reason nock doesn't play nice with localhost in node 0.4, so +# avoid test_helper.coffee altogether here. +mock_robot = logger: { error: (->), debug: (->) }, clean: (->) +assert = require "assert" http = require "http" describe "concurrent requests", -> From be749789c17dcf9721899d416469aeaa9b765a53 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 14 Jul 2012 23:34:27 -0700 Subject: [PATCH 03/76] Dirtier workaround for 0.4 issues --- test/windowing.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/windowing.coffee b/test/windowing.coffee index d6edaaa..98fde30 100644 --- a/test/windowing.coffee +++ b/test/windowing.coffee @@ -1,9 +1,10 @@ -# For some reason nock doesn't play nice with localhost in node 0.4, so -# avoid test_helper.coffee altogether here. -mock_robot = logger: { error: (->), debug: (->) }, clean: (->) -assert = require "assert" +[ gh, assert, nock, mock_robot ] = require "./test_helper" http = require "http" +# nock screws up the parsed URL options in node 0.4, and I can't be bothered +# to deal with the problem. +return if process.versions.node < "0.5.0" + describe "concurrent requests", -> it "are limited", (done) -> maxRequests = 0 From fa610aeee34c2479c5cc42ec5ab566685faa9f07 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 14 Jul 2012 23:37:28 -0700 Subject: [PATCH 04/76] 0.8 compatibility --- .travis.yml | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f1d0f13..2a9b9e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,3 +2,4 @@ language: node_js node_js: - 0.4 - 0.6 + - 0.8 diff --git a/package.json b/package.json index 090898b..10ceda5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "test": "make test" }, "engines": { - "node": ">=0.4.7 <0.7.0" + "node": ">=0.4.7" }, "dependencies": { "scoped-http-client": "https://github.com/iangreenleaf/node-scoped-http-client/tarball/content_length_published", From 48f630e0addf9dabc6bed6a417a53fd33bde981e Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 14 Jul 2012 23:41:56 -0700 Subject: [PATCH 05/76] Looser mocha dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 10ceda5..c2ba193 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "async": "~0.1.22" }, "devDependencies": { - "mocha": "~1.1", + "mocha": "~1", "coffee-script": "~1.1.3", "nock": "~0.13.3" } From d43b03061a1f7d102d6a4108b1831a5d780b5ea2 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 14 Jul 2012 23:39:59 -0700 Subject: [PATCH 06/76] This makes more sense in acceptance tests --- test/{ => acceptance}/windowing.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename test/{ => acceptance}/windowing.coffee (90%) diff --git a/test/windowing.coffee b/test/acceptance/windowing.coffee similarity index 90% rename from test/windowing.coffee rename to test/acceptance/windowing.coffee index 98fde30..c0900b6 100644 --- a/test/windowing.coffee +++ b/test/acceptance/windowing.coffee @@ -1,4 +1,4 @@ -[ gh, assert, nock, mock_robot ] = require "./test_helper" +[ gh, assert, nock, mock_robot ] = require "../test_helper" http = require "http" # nock screws up the parsed URL options in node 0.4, and I can't be bothered @@ -24,7 +24,7 @@ describe "concurrent requests", -> process.env.HUBOT_GITHUB_API = "http://localhost:#{port}" process.env.HUBOT_CONCURRENT_REQUESTS = 35 - gh = require("..") mock_robot + gh = require("../..") mock_robot for i in [1..remain] gh.request "GET", "/repos/foo/bar/branches/#{i}", -> if --remain is 0 From 7f1153bac46ec3250b93c7305d132c7e18335a44 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 14 Jul 2012 23:44:11 -0700 Subject: [PATCH 07/76] v0.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c2ba193..2e6de71 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "0.2.1", + "version": "0.3.0", "homepage": "https://github.com/iangreenleaf/githubot", "repository": { "type": "git", From 898d4b1a3b1769b9659363684f4a366c8eeaeeaf Mon Sep 17 00:00:00 2001 From: Ian Young Date: Mon, 16 Jul 2012 23:00:17 -0700 Subject: [PATCH 08/76] Use specific version of Mocha to dodge regression See https://github.com/visionmedia/mocha/issues/489 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e6de71..c8eb424 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "async": "~0.1.22" }, "devDependencies": { - "mocha": "~1", + "mocha": "1.2.1", "coffee-script": "~1.1.3", "nock": "~0.13.3" } From 0c3e162d606b8c1d563dae9435ce905c7decf413 Mon Sep 17 00:00:00 2001 From: Ryan Rinaldi Date: Tue, 18 Dec 2012 14:22:56 -0600 Subject: [PATCH 09/76] Added merge api support --- githubot.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/githubot.coffee b/githubot.coffee index fcfe547..9cb8b82 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -55,9 +55,15 @@ class Github @request "GET", url, cb post: (url, data, cb) -> @request "POST", url, data, cb + merge: (repo, base, head, cb) -> + msg= + base: base + head: head + @post("https://api.github.com/repos/#{@qualified_repo repo}/merges", + msg, cb) branches: (repo, cb) -> if cb? - @get("https://api.github.com/repos/#{@qualified_repo repo}/branches", cb) + @get("https://api.github.com/repos/#{@qualified_repo repo}/merges", cb) else create: (branchName, opts, cb) => [opts,cb] = [{},opts] unless cb? From e1c44f54c9a5cdd4d6a33b838bfd0ae2bc6356ea Mon Sep 17 00:00:00 2001 From: Ryan Rinaldi Date: Tue, 18 Dec 2012 14:25:36 -0600 Subject: [PATCH 10/76] Fixed stupid typo --- githubot.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/githubot.coffee b/githubot.coffee index 9cb8b82..494c4b6 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -63,7 +63,7 @@ class Github msg, cb) branches: (repo, cb) -> if cb? - @get("https://api.github.com/repos/#{@qualified_repo repo}/merges", cb) + @get("https://api.github.com/repos/#{@qualified_repo repo}/branches", cb) else create: (branchName, opts, cb) => [opts,cb] = [{},opts] unless cb? From 9dcc2a76b0ae09184f33a6128734a320feaea02b Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 18 Dec 2012 16:08:00 -0800 Subject: [PATCH 11/76] Add information on contributing to the Readme --- Readme.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index c2dc745..ba092f7 100644 --- a/Readme.md +++ b/Readme.md @@ -101,7 +101,25 @@ gh.branches "githubot", (branches) -> * `HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. -## [Contributors](https://github.com/iangreenleaf/githubot/contributors) ## +## Contributing ## + +Install the dependencies: + + npm install + +Run the tests: + + make test + make test-all # Runs additional slower "integration" style tests, generally not necessary + +**Pull requests encouraged!** + +I'm vastly more likely to merge code that comes with tests. If you're confused by the testing process, +ask and I can probably point you in the right direction. + +## Thanks ## + +[These lovely people have contributed to githubot](https://github.com/iangreenleaf/githubot/contributors). From 5cce9422d09b5b5ef3040361a8b0cab52e3f53f5 Mon Sep 17 00:00:00 2001 From: jhubert Date: Fri, 1 Feb 2013 05:48:35 -0800 Subject: [PATCH 12/76] Get and export the current version from package.json --- test/test_helper.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_helper.coffee b/test/test_helper.coffee index cacf796..9dcd15a 100644 --- a/test/test_helper.coffee +++ b/test/test_helper.coffee @@ -11,7 +11,8 @@ mock_robot = gh = require("..") mock_robot nock = require("nock") -module.exports = [ gh, require("assert"), nock, mock_robot ] +package_version = require("../package.json")["version"] +module.exports = [ gh, require("assert"), nock, mock_robot, package_version ] beforeEach -> nock.cleanAll() From e9d34eb4d73f3950ce077fb6fe44676dc8dae02d Mon Sep 17 00:00:00 2001 From: jhubert Date: Fri, 1 Feb 2013 05:52:52 -0800 Subject: [PATCH 13/76] Test for the User-Agent header, including current version --- test/request.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/request.coffee b/test/request.coffee index a8c7d5c..1c987fc 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -1,4 +1,4 @@ -[ gh, assert, nock, mock_robot ] = require "./test_helper" +[ gh, assert, nock, mock_robot, package_version ] = require "./test_helper" describe "github api", -> describe "general purpose", -> @@ -27,6 +27,9 @@ describe "github api", -> it "includes accept header", (done) -> network.matchHeader('Accept', 'application/vnd.github.beta+json') gh.request "GET", "repos/foo/bar/branches", success done + it "includes User-Agent header", (done) -> + network.matchHeader('User-Agent', "GitHubot/#{package_version}") + gh.request "GET", "repos/foo/bar/branches", success done it "returns parsed json", (done) -> gh.request "GET", "repos/foo/bar/branches", (data) -> assert.deepEqual response, data From 7f3aa48455159e7679fafbd685faf5bfb27a8ffa Mon Sep 17 00:00:00 2001 From: jhubert Date: Fri, 1 Feb 2013 05:58:04 -0800 Subject: [PATCH 14/76] Add a User-Agent header including the current GitHubot version This may need to be changed in the near term to be unique to the current install of GitHubot so that the user isn't rate limited in the same bucket as all other users. per http://developer.github.com/changes/2012-10-14-rate-limit-changes/ --- githubot.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/githubot.coffee b/githubot.coffee index fcfe547..9861613 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -2,6 +2,8 @@ http = require "scoped-http-client" async = require "async" querystring = require "querystring" +version = require("./package.json")["version"] + process.env.HUBOT_CONCURRENT_REQUESTS ?= 20 class Github @@ -30,6 +32,7 @@ class Github url = "/#{url}" unless url[0] is "/" url = "#{url_api_base}#{url}" req = http.create(url).header("Accept", "application/vnd.github.beta+json") + req = req.header("User-Agent", "GitHubot/#{version}") req = req.header("Authorization", "token #{oauth_token}") if (oauth_token = process.env.HUBOT_GITHUB_TOKEN)? args = [] args.push JSON.stringify data if data? From afc261dac5ad33f5b4b52c7346293094131762b5 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 1 Feb 2013 23:23:44 -0800 Subject: [PATCH 15/76] v0.3.1 * Set custom user agent to meet updated GitHub API policies. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c8eb424..45f4f3e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "0.3.0", + "version": "0.3.1", "homepage": "https://github.com/iangreenleaf/githubot", "repository": { "type": "git", From 5d7fdbb2b2c83899ee76048fe96817501cd64dc9 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Wed, 27 Feb 2013 16:07:51 -0800 Subject: [PATCH 16/76] Specify the planned new merge interface --- test/repo.coffee | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/repo.coffee b/test/repo.coffee index 7b7c32a..2098851 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -99,3 +99,58 @@ describe "repo api", -> gh.branches( "foo/bar" ).delete @branchName, "anotherBranch", -> network.done() done() + + describe "merge", -> + beforeEach -> + @branchName = "newbranch" + @sha = "deadbeef" + + it "succeeds", (done) -> + network = nock("https://api.github.com") + .post("/repos/foo/bar/merges", + base: "master", head: @branchName) + .reply( + 201 + , commit: { sha: @sha, url: "xyz", commit: { message: "commit message" } } + ) + gh.branches("foo/bar").merge @branchName, (commit) => + assert.deepEqual commit, + sha: @sha, message: "commit message", url: "xyz" + network.done() + done() + + context "with base specified", (done) -> + beforeEach -> + @base = "targetbranch" + network = nock("https://api.github.com") + .post("/repos/foo/bar/merges", + base: @base, head: @branchName) + .reply( + 201 + , commit: { sha: @sha, url: "xyz", commit: { message: "commit message" } } + ) + @cb = (done) => (commit) => + assert.deepEqual commit, + sha: @sha, message: "commit message", url: "xyz" + network.done() + done() + + it "as 'into'", (done) -> + gh.branches("foo/bar").merge @branchName, {into: @base}, @cb(done) + it "as 'base'", (done) -> + gh.branches("foo/bar").merge @branchName, {base: @base}, @cb(done) + + it "with commit message specified", (done) -> + @message = "An awesome merge!" + network = nock("https://api.github.com") + .post("/repos/foo/bar/merges", + base: "master", head: @branchName, commit_message: @message) + .reply( + 201 + , commit: { sha: @sha, url: "xyz", commit: { message: @message } } + ) + gh.branches("foo/bar").merge @branchName, message: @message, (commit) => + assert.deepEqual commit, + sha: @sha, message: @message, url: "xyz" + network.done() + done() From 79b5daf0fa980c63c1c518ef6dfd0ecc9e85c0b7 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Wed, 27 Feb 2013 16:23:28 -0800 Subject: [PATCH 17/76] Move `merge` interface into branches namespace --- githubot.coffee | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 3058e88..4010022 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -58,12 +58,6 @@ class Github @request "GET", url, cb post: (url, data, cb) -> @request "POST", url, data, cb - merge: (repo, base, head, cb) -> - msg= - base: base - head: head - @post("https://api.github.com/repos/#{@qualified_repo repo}/merges", - msg, cb) branches: (repo, cb) -> if cb? @get("https://api.github.com/repos/#{@qualified_repo repo}/branches", cb) @@ -84,6 +78,15 @@ class Github actions.push (done) => @request "DELETE", "https://api.github.com/repos/#{@qualified_repo repo}/git/refs/heads/#{branchName}", done async.parallel actions, cb + merge: (head, opts, cb) => + [opts,cb] = [{},opts] unless cb? + body = + base: opts.base ? opts.into ? "master" + head: head + if opts.message? + body.commit_message = opts.message + @post "https://api.github.com/repos/#{@qualified_repo repo}/merges", body, (data) -> + cb sha: data.commit.sha, message: data.commit.commit.message, url: data.commit.url module.exports = github = (robot) -> new Github robot.logger From d65ef502b1c9ba1e696dc772d5f892caf977fc9d Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 28 Feb 2013 15:12:05 -0800 Subject: [PATCH 18/76] Document merge API --- Readme.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Readme.md b/Readme.md index ba092f7..439e8d8 100644 --- a/Readme.md +++ b/Readme.md @@ -53,11 +53,15 @@ Enter your Github password when prompted. When you get a response, look for the Mostly a work in progress, but here's a taste of what I have in mind: +### List branches ### + ```coffeescript gh.branches "foo/bar", (branches) -> console.log branches[0].name ``` +### Create a branch ### + ```coffeescript # Branch from master gh.branches( "foo/bar" ).create "my_radical_feature", (branch) -> @@ -68,6 +72,28 @@ gh.branches( "foo/bar" ).create "even_more_radical", from: "my_radical_feature", console.log branch.sha ``` +### Merge a branch ### + +```coffeescript +# Merge a branch into master +gh.branches( "foo/bar" ).merge "my_radical_feature", (mergeCommit) -> + console.log mergeCommit.message + +# Merge a branch into a different target +gh.branches( "foo/bar" ).merge "my_radical_feature", into: "hotfixes", (mergeCommit) -> + console.log mergeCommit.message + +# `base` is an alias for `into` +gh.branches( "foo/bar" ).merge "my_radical_feature", base: "hotfixes", (mergeCommit) -> + console.log mergeCommit.message + +# Provide your own commit message +gh.branches( "foo/bar" ).merge "my_radical_feature", message: "Merge my radical feature!", (mergeCommit) -> + console.log mergeCommit.sha +``` + +### Delete a branch ### + ```coffeescript gh.branches( "foo/bar" ).delete "my_radical_feature", -> console.log "Deleted my branch!" From 8474f9b3fc1ac43425b6f5dfda2c0c3f42bf82d8 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 28 Feb 2013 16:30:47 -0800 Subject: [PATCH 19/76] Handle empty response when nothing to merge --- githubot.coffee | 10 ++++++---- test/repo.coffee | 12 ++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 4010022..0b99792 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -42,14 +42,14 @@ class Github return @logger.error err if err? try - data = JSON.parse body if body + responseData = JSON.parse body if body catch e return @logger.error "Could not parse response: #{body}" if (200 <= res.statusCode < 300) - cb data + cb responseData else - @logger.error "#{res.statusCode} #{data.message}" + @logger.error "#{res.statusCode} #{responseData.message}" get: (url, data, cb) -> unless cb? [cb, data] = [data, null] @@ -85,7 +85,9 @@ class Github head: head if opts.message? body.commit_message = opts.message - @post "https://api.github.com/repos/#{@qualified_repo repo}/merges", body, (data) -> + @post "https://api.github.com/repos/#{@qualified_repo repo}/merges", body, (data) => + unless data? + return @logger.error "Nothing to merge" cb sha: data.commit.sha, message: data.commit.commit.message, url: data.commit.url module.exports = github = (robot) -> diff --git a/test/repo.coffee b/test/repo.coffee index 2098851..0954e78 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -154,3 +154,15 @@ describe "repo api", -> sha: @sha, message: @message, url: "xyz" network.done() done() + + it "notifies about no-op", (done) -> + network = nock("https://api.github.com") + .post("/repos/foo/bar/merges", + base: "master", head: @branchName) + .reply(204) + gh.branches("foo/bar").merge @branchName, -> + assert.fail null, null, "Should not call callback" + mock_robot.onError = (msg) -> + assert.ok /nothing to merge/i.exec msg + network.done() + done() From f0098f770e7adc3b59b61c41cd74611872166447 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 28 Feb 2013 17:08:06 -0800 Subject: [PATCH 20/76] GitHub developer docs are incorrect *tsk* --- githubot.coffee | 2 +- test/repo.coffee | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 0b99792..4a3c0fe 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -88,7 +88,7 @@ class Github @post "https://api.github.com/repos/#{@qualified_repo repo}/merges", body, (data) => unless data? return @logger.error "Nothing to merge" - cb sha: data.commit.sha, message: data.commit.commit.message, url: data.commit.url + cb sha: data.sha, message: data.commit.message, url: data.url module.exports = github = (robot) -> new Github robot.logger diff --git a/test/repo.coffee b/test/repo.coffee index 0954e78..452311c 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -111,7 +111,7 @@ describe "repo api", -> base: "master", head: @branchName) .reply( 201 - , commit: { sha: @sha, url: "xyz", commit: { message: "commit message" } } + , sha: @sha, url: "xyz", commit: { message: "commit message" } ) gh.branches("foo/bar").merge @branchName, (commit) => assert.deepEqual commit, @@ -127,7 +127,7 @@ describe "repo api", -> base: @base, head: @branchName) .reply( 201 - , commit: { sha: @sha, url: "xyz", commit: { message: "commit message" } } + , sha: @sha, url: "xyz", commit: { message: "commit message" } ) @cb = (done) => (commit) => assert.deepEqual commit, @@ -147,7 +147,7 @@ describe "repo api", -> base: "master", head: @branchName, commit_message: @message) .reply( 201 - , commit: { sha: @sha, url: "xyz", commit: { message: @message } } + , sha: @sha, url: "xyz", commit: { message: @message } ) gh.branches("foo/bar").merge @branchName, message: @message, (commit) => assert.deepEqual commit, From 0be7d14faafbfb9e73db64229a07424d1ddc2f39 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 28 Feb 2013 17:09:48 -0800 Subject: [PATCH 21/76] Remove root URL from calls These will now work with alternate URLs, as intended --- githubot.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 4a3c0fe..2942d82 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -60,14 +60,14 @@ class Github @request "POST", url, data, cb branches: (repo, cb) -> if cb? - @get("https://api.github.com/repos/#{@qualified_repo repo}/branches", cb) + @get("repos/#{@qualified_repo repo}/branches", cb) else create: (branchName, opts, cb) => [opts,cb] = [{},opts] unless cb? opts.from ?= "master" - @get "https://api.github.com/repos/#{@qualified_repo repo}/git/refs/heads/#{opts.from}", (json) => + @get "repos/#{@qualified_repo repo}/git/refs/heads/#{opts.from}", (json) => sha = json.object.sha - @post "https://api.github.com/repos/#{@qualified_repo repo}/git/refs", + @post "repos/#{@qualified_repo repo}/git/refs", ref: "refs/heads/#{branchName}", sha: sha , (data) -> cb name: branchName, commit: { sha: data.object.sha, url: data.object.url } @@ -76,7 +76,7 @@ class Github for branchName in branchNames do (branchName) => actions.push (done) => - @request "DELETE", "https://api.github.com/repos/#{@qualified_repo repo}/git/refs/heads/#{branchName}", done + @request "DELETE", "repos/#{@qualified_repo repo}/git/refs/heads/#{branchName}", done async.parallel actions, cb merge: (head, opts, cb) => [opts,cb] = [{},opts] unless cb? @@ -85,7 +85,7 @@ class Github head: head if opts.message? body.commit_message = opts.message - @post "https://api.github.com/repos/#{@qualified_repo repo}/merges", body, (data) => + @post "repos/#{@qualified_repo repo}/merges", body, (data) => unless data? return @logger.error "Nothing to merge" cb sha: data.sha, message: data.commit.message, url: data.url From d426b2075a785323dc630347262f33f2c4a803d3 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 28 Feb 2013 17:15:35 -0800 Subject: [PATCH 22/76] Use official release of http client --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 45f4f3e..998f996 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "node": ">=0.4.7" }, "dependencies": { - "scoped-http-client": "https://github.com/iangreenleaf/node-scoped-http-client/tarball/content_length_published", + "scoped-http-client": "~0.9.8", "async": "~0.1.22" }, "devDependencies": { From 0572a6271a220a9be27bd678dfe0754995d22ce6 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 1 Mar 2013 14:22:18 -0800 Subject: [PATCH 23/76] Update and relax dependencies --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 998f996..796e4d7 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,12 @@ "node": ">=0.4.7" }, "dependencies": { - "scoped-http-client": "~0.9.8", - "async": "~0.1.22" + "scoped-http-client": ">= 0.9.8", + "async": "0.2.x" }, "devDependencies": { - "mocha": "1.2.1", - "coffee-script": "~1.1.3", - "nock": "~0.13.3" + "mocha": "1.x", + "coffee-script": "1.x", + "nock": "0.x" } } From ceeb32318de9e608f32541f77b854e6499f1d0cb Mon Sep 17 00:00:00 2001 From: Ian Young Date: Wed, 20 Mar 2013 00:09:58 -0700 Subject: [PATCH 24/76] Don't worry about specific version in tests Now that nock accepted my pull request for regexps in header matchers, we can use that instead of requiring in the version number. --- package.json | 2 +- test/request.coffee | 4 ++-- test/test_helper.coffee | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 796e4d7..d26986e 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,6 @@ "devDependencies": { "mocha": "1.x", "coffee-script": "1.x", - "nock": "0.x" + "nock": ">= 0.15.0" } } diff --git a/test/request.coffee b/test/request.coffee index 1c987fc..f0ca344 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -1,4 +1,4 @@ -[ gh, assert, nock, mock_robot, package_version ] = require "./test_helper" +[ gh, assert, nock, mock_robot ] = require "./test_helper" describe "github api", -> describe "general purpose", -> @@ -28,7 +28,7 @@ describe "github api", -> network.matchHeader('Accept', 'application/vnd.github.beta+json') gh.request "GET", "repos/foo/bar/branches", success done it "includes User-Agent header", (done) -> - network.matchHeader('User-Agent', "GitHubot/#{package_version}") + network.matchHeader('User-Agent', /GitHubot\/\d+\.\d+\.\d+/) gh.request "GET", "repos/foo/bar/branches", success done it "returns parsed json", (done) -> gh.request "GET", "repos/foo/bar/branches", (data) -> diff --git a/test/test_helper.coffee b/test/test_helper.coffee index 9dcd15a..cacf796 100644 --- a/test/test_helper.coffee +++ b/test/test_helper.coffee @@ -11,8 +11,7 @@ mock_robot = gh = require("..") mock_robot nock = require("nock") -package_version = require("../package.json")["version"] -module.exports = [ gh, require("assert"), nock, mock_robot, package_version ] +module.exports = [ gh, require("assert"), nock, mock_robot ] beforeEach -> nock.cleanAll() From cc0ccbd9f2e80cee5c7d305e7be6c21bf51a2a06 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 6 Apr 2013 17:34:22 -0700 Subject: [PATCH 25/76] Add @errorHandler to deal with errors manually Fixes #9. --- githubot.coffee | 11 +++++++-- test/request.coffee | 59 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 2942d82..11efd67 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -39,17 +39,24 @@ class Github args.push "" if verb is "DELETE" and not data? task = run: (cb) -> req[verb.toLowerCase()](args...) cb @requestQueue.push task, (err, res, body) => - return @logger.error err if err? + if err? + @logger.error err + @errorHandler?(statusCode: res?.statusCode, error: err) + return try responseData = JSON.parse body if body catch e - return @logger.error "Could not parse response: #{body}" + @logger.error "Could not parse response: #{body}" + @errorHandler?(statusCode: res.statusCode, body: body) + return if (200 <= res.statusCode < 300) cb responseData else @logger.error "#{res.statusCode} #{responseData.message}" + @errorHandler?(statusCode: res.statusCode, message: responseData.message, body: body) + get: (url, data, cb) -> unless cb? [cb, data] = [data, null] diff --git a/test/request.coffee b/test/request.coffee index f0ca344..d6522a5 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -93,6 +93,20 @@ describe "github api", -> assert.fail(null, null, "Success callback should not be invoked") beforeEach -> network = nock("https://api.github.com").get("/foo") + http = require "scoped-http-client" + kablooie = -> + mock = { + header: -> mock, + get: () -> (cb) -> + cb new Error "Kablooie!" + } + http._old_create = http.create + http.create = -> mock + afterEach -> + if http._old_create? + http.create = http._old_create + http._old_create = null + it "complains about failed response", (done) -> network.reply(401, message: "Bad credentials") mock_robot.onError = (msg) -> @@ -106,19 +120,46 @@ describe "github api", -> done() gh.get "/foo", never_called it "complains about client errors", (done) -> - mock = { - header: -> mock, - get: () -> (cb) -> - cb new Error "Kablooie!" - } - http = require "scoped-http-client" - http._old_create = http.create - http.create = -> mock + kablooie() mock_robot.onError = (msg) -> assert.ok /kablooie/i.exec msg done() gh.get "/foo", never_called - http.create = http._old_create + + describe "with error handler", -> + beforeEach -> + network = nock("https://api.github.com") + .get("/foo") + afterEach -> + gh.errorHandler = null + it "calls handler on error", (done) -> + network.reply(406, message: "I hate you!") + gh.errorHandler = (response) -> + assert.equal 406, response.statusCode + assert.equal "I hate you!", response.message + assert.equal '{"message":"I hate you!"}', response.body + done() + gh.get "/foo", never_called + + it "doesn't call handler on success", (done) -> + network.reply(201, message: "Hooray!") + gh.errorHandler = never_called + gh.get "/foo", -> done() + + it "passes body if can't parse response", (done) -> + network.reply(500, "WTF$$%@! SERVER VOMIT") + gh.errorHandler = (response) -> + assert.equal 500, response.statusCode + assert.equal "WTF$$%@! SERVER VOMIT", response.body + done() + gh.get "/foo", never_called + + it "passes error if request failed", (done) -> + kablooie() + gh.errorHandler = (response) -> + assert.ok /kablooie/i.exec response.error + done() + gh.get "/foo", never_called describe "without robot given", -> before -> From f0db0fdcaf3ea5f94ba4ccd5b86c476486a8b84a Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 6 Apr 2013 18:32:55 -0700 Subject: [PATCH 26/76] Slight adjustment to error handler interface Now uses @handleErrors to register instead of setting property directly. See #9. --- githubot.coffee | 34 ++++++++++++++++++++++++++-------- test/request.coffee | 28 +++++++++++++++++++++------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 11efd67..095e11c 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -40,22 +40,26 @@ class Github task = run: (cb) -> req[verb.toLowerCase()](args...) cb @requestQueue.push task, (err, res, body) => if err? - @logger.error err - @errorHandler?(statusCode: res?.statusCode, error: err) - return + return @_errorHandler + statusCode: res?.statusCode + body: res?.body + error: err try responseData = JSON.parse body if body catch e - @logger.error "Could not parse response: #{body}" - @errorHandler?(statusCode: res.statusCode, body: body) - return + return @_errorHandler + statusCode: res.statusCode + body: body + error: "Could not parse response: #{body}" if (200 <= res.statusCode < 300) cb responseData else - @logger.error "#{res.statusCode} #{responseData.message}" - @errorHandler?(statusCode: res.statusCode, message: responseData.message, body: body) + @_errorHandler + statusCode: res.statusCode + body: body + error: responseData.message get: (url, data, cb) -> unless cb? @@ -65,6 +69,20 @@ class Github @request "GET", url, cb post: (url, data, cb) -> @request "POST", url, data, cb + + handleErrors: (callback) -> + @_errorHandler = (response) => + callback response + @_loggerErrorHandler response + + _loggerErrorHandler: (response) -> + message = response.error + message = "#{response.statusCode} #{message}" if response.statusCode? + @logger.error message + + _errorHandler: (response) -> + @_loggerErrorHandler response + branches: (repo, cb) -> if cb? @get("repos/#{@qualified_repo repo}/branches", cb) diff --git a/test/request.coffee b/test/request.coffee index d6522a5..ab7116d 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -89,11 +89,13 @@ describe "github api", -> describe "errors", -> network = null + http = require "scoped-http-client" never_called = -> assert.fail(null, null, "Success callback should not be invoked") beforeEach -> network = nock("https://api.github.com").get("/foo") - http = require "scoped-http-client" + afterEach -> + mock_robot.onError = null kablooie = -> mock = { header: -> mock, @@ -127,28 +129,30 @@ describe "github api", -> gh.get "/foo", never_called describe "with error handler", -> + defaultHandler = gh._errorHandler beforeEach -> network = nock("https://api.github.com") .get("/foo") afterEach -> - gh.errorHandler = null + gh._errorHandler = defaultHandler + it "calls handler on error", (done) -> network.reply(406, message: "I hate you!") - gh.errorHandler = (response) -> + gh.handleErrors (response) -> assert.equal 406, response.statusCode - assert.equal "I hate you!", response.message + assert.equal "I hate you!", response.error assert.equal '{"message":"I hate you!"}', response.body done() gh.get "/foo", never_called it "doesn't call handler on success", (done) -> network.reply(201, message: "Hooray!") - gh.errorHandler = never_called + gh.handleErrors never_called gh.get "/foo", -> done() it "passes body if can't parse response", (done) -> network.reply(500, "WTF$$%@! SERVER VOMIT") - gh.errorHandler = (response) -> + gh.handleErrors (response) -> assert.equal 500, response.statusCode assert.equal "WTF$$%@! SERVER VOMIT", response.body done() @@ -156,11 +160,21 @@ describe "github api", -> it "passes error if request failed", (done) -> kablooie() - gh.errorHandler = (response) -> + gh.handleErrors (response) -> assert.ok /kablooie/i.exec response.error done() gh.get "/foo", never_called + it "still logs errors", (done) -> + network.reply(406, message: "I hate you!") + expected = 2 + cb = -> + expected -= 1 + done() if expected is 0 + mock_robot.onError = cb + gh.handleErrors cb + gh.get "/foo", never_called + describe "without robot given", -> before -> gh = require("..") From 72ccc5b213ba99c9e7c95d29ca2cc7f4b5d4b1b6 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 6 Apr 2013 18:35:29 -0700 Subject: [PATCH 27/76] Move docs around --- Readme.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Readme.md b/Readme.md index 439e8d8..c675eb8 100644 --- a/Readme.md +++ b/Readme.md @@ -49,6 +49,34 @@ If you don't have a token yet, run this: Enter your Github password when prompted. When you get a response, look for the "token" value. +## Helpful Hubot ## + +Hubot will log errors if a request fails. + +If `process.env.HUBOT_GITHUB_USER` is present, we can help you guess a repo's full name: + +```coffeescript +github.qualified_repo "githubot" # => "iangreenleaf/githubot" +``` + +This will happen with the bespoke methods as well: + +```coffeescript +gh.branches "githubot", (branches) -> +``` + +### Options ### + +* `HUBOT_GITHUB_TOKEN`: GitHub API token. Required to perform authenticated actions. + +* `HUBOT_GITHUB_USER`: Default GitHub username to use if one is not given. + +* `HUBOT_GITHUB_API`: The base API URL. This is useful for Enterprise Github installations. + + For example, `HUBOT_GITHUB_API='http://myprivate.github.int'` + +* `HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. + ## Bespoke API access ## Mostly a work in progress, but here's a taste of what I have in mind: @@ -99,34 +127,6 @@ gh.branches( "foo/bar" ).delete "my_radical_feature", -> console.log "Deleted my branch!" ``` -## Helpful Hubot ## - -Hubot will log errors if a request fails. - -If `process.env.HUBOT_GITHUB_USER` is present, we can help you guess a repo's full name: - -```coffeescript -github.qualified_repo "githubot" # => "iangreenleaf/githubot" -``` - -This will happen with the bespoke methods as well: - -```coffeescript -gh.branches "githubot", (branches) -> -``` - -### Options ### - -* `HUBOT_GITHUB_TOKEN`: GitHub API token. Required to perform authenticated actions. - -* `HUBOT_GITHUB_USER`: Default GitHub username to use if one is not given. - -* `HUBOT_GITHUB_API`: The base API URL. This is useful for Enterprise Github installations. - - For example, `HUBOT_GITHUB_API='http://myprivate.github.int'` - -* `HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. - ## Contributing ## Install the dependencies: From df087e87686d6b35307ba9f60c9c63aaf415044d Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 6 Apr 2013 18:41:36 -0700 Subject: [PATCH 28/76] Docs for error handling, see #9. --- Readme.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index c675eb8..c43d716 100644 --- a/Readme.md +++ b/Readme.md @@ -49,9 +49,24 @@ If you don't have a token yet, run this: Enter your Github password when prompted. When you get a response, look for the "token" value. -## Helpful Hubot ## +## Handling errors ## + +GitHubot will log errors automatically if it has a logger. Used with Hubot, these will go to the Hubot logger. + +If your script would like to catch errors as well, define an extra callback: -Hubot will log errors if a request fails. +```coffeescript +github.handleErrors (response) -> + console.log "Oh no! #{response.statusCode}!" +``` + +The callback takes a `response` argument with the following keys: + +* `error`: The error message. +* `statusCode`: The status code of the API response, if present. +* `body`: The body of the API response, if present. + +## Helpful Hubot ## If `process.env.HUBOT_GITHUB_USER` is present, we can help you guess a repo's full name: From 8d0f9c3a2fa324ef6c3765b6948b0576f475f348 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sun, 7 Apr 2013 00:31:53 -0700 Subject: [PATCH 29/76] v0.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d26986e..2a85f9a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "0.3.1", + "version": "0.4.0", "homepage": "https://github.com/iangreenleaf/githubot", "repository": { "type": "git", From 3f44e322e13c5c1d49bb201ba1cae91370137cbb Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 25 Jun 2013 16:09:05 -0600 Subject: [PATCH 30/76] Update TravisCI image URL --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index c43d716..68af873 100644 --- a/Readme.md +++ b/Readme.md @@ -165,4 +165,4 @@ ask and I can probably point you in the right direction. -[Build Status]: https://secure.travis-ci.org/iangreenleaf/githubot.png?branch=master +[Build Status]: https://travis-ci.org/iangreenleaf/githubot.png?branch=master From 8a416c35d5d20ef58db0863fd80f6d88532470f8 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 25 Jun 2013 16:23:04 -0600 Subject: [PATCH 31/76] Get with the times, man --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2a9b9e6..58f2371 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - 0.4 - 0.6 - 0.8 + - 0.10 From e94834968d4e6f2049440b9eb72f071182b89384 Mon Sep 17 00:00:00 2001 From: Tomas Varaneckas Date: Tue, 23 Jul 2013 20:35:35 +0300 Subject: [PATCH 32/76] Add HUBOT_GITHUB_API_VERSION option This allows switching from the default "beta" API to "preview" or something else. --- Readme.md | 2 ++ githubot.coffee | 3 ++- test/request.coffee | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 68af873..7bd8d01 100644 --- a/Readme.md +++ b/Readme.md @@ -90,6 +90,8 @@ gh.branches "githubot", (branches) -> For example, `HUBOT_GITHUB_API='http://myprivate.github.int'` +* `HUBOT_GITHUB_API_VERSION`: Version of the API, i.e. "v3", "beta" or "preview". Defaults to "beta". + * `HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. ## Bespoke API access ## diff --git a/githubot.coffee b/githubot.coffee index 095e11c..124ba21 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -27,11 +27,12 @@ class Github [cb, data] = [data, null] url_api_base = process.env.HUBOT_GITHUB_API || "https://api.github.com" + api_version = process.env.HUBOT_GITHUB_API_VERSION || "beta" if url[0..3] isnt "http" url = "/#{url}" unless url[0] is "/" url = "#{url_api_base}#{url}" - req = http.create(url).header("Accept", "application/vnd.github.beta+json") + req = http.create(url).header("Accept", "application/vnd.github.#{api_version}+json") req = req.header("User-Agent", "GitHubot/#{version}") req = req.header("Authorization", "token #{oauth_token}") if (oauth_token = process.env.HUBOT_GITHUB_TOKEN)? args = [] diff --git a/test/request.coffee b/test/request.coffee index ab7116d..e2b70ea 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -27,6 +27,11 @@ describe "github api", -> it "includes accept header", (done) -> network.matchHeader('Accept', 'application/vnd.github.beta+json') gh.request "GET", "repos/foo/bar/branches", success done + it "allows setting API version", (done) -> + process.env.HUBOT_GITHUB_API_VERSION = "preview" + network.matchHeader('Accept', 'application/vnd.github.preview+json') + gh.request "GET", "repos/foo/bar/branches", success done + delete process.env.HUBOT_GITHUB_API_VERSION it "includes User-Agent header", (done) -> network.matchHeader('User-Agent', /GitHubot\/\d+\.\d+\.\d+/) gh.request "GET", "repos/foo/bar/branches", success done From 38959d45b631e55b088bcbbcc4fe399914c6ff06 Mon Sep 17 00:00:00 2001 From: Tomas Varaneckas Date: Tue, 23 Jul 2013 21:00:28 +0300 Subject: [PATCH 33/76] Update Travis configuration --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 58f2371..ed05f88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: node_js node_js: - - 0.6 - - 0.8 - - 0.10 + - "0.11" + - "0.10" + - "0.8" + - "0.6" From ca61cbc005608f449c20f76123aaaa29c9fae07e Mon Sep 17 00:00:00 2001 From: Tomas Varaneckas Date: Tue, 23 Jul 2013 21:41:19 +0300 Subject: [PATCH 34/76] Give a little time to finish mock calls in async spec --- test/repo.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/repo.coffee b/test/repo.coffee index 452311c..e369287 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -97,8 +97,10 @@ describe "repo api", -> network.delete("/repos/foo/bar/git/refs/heads/anotherBranch") .reply(204, {}) gh.branches( "foo/bar" ).delete @branchName, "anotherBranch", -> - network.done() - done() + setTimeout -> + network.done() + done() + , 1 describe "merge", -> beforeEach -> From c40012ace1303e1d7edc5947f9d41d5cc9f8dabe Mon Sep 17 00:00:00 2001 From: Tomas Varaneckas Date: Thu, 25 Jul 2013 06:58:23 +0300 Subject: [PATCH 35/76] Specify version via constructor rather than environment var --- Readme.md | 9 +++++++-- githubot.coffee | 9 ++++----- test/request.coffee | 5 ++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Readme.md b/Readme.md index 7bd8d01..089ebce 100644 --- a/Readme.md +++ b/Readme.md @@ -15,6 +15,13 @@ module.exports = (robot) -> github = require('githubot')(robot) ``` +Specify Github API version: + +```coffeescripts +module.exports = (robot) -> + github = require('githubot')(robot, apiVersion: 'preview') +``` + Or use it on its own: ```coffeescript @@ -90,8 +97,6 @@ gh.branches "githubot", (branches) -> For example, `HUBOT_GITHUB_API='http://myprivate.github.int'` -* `HUBOT_GITHUB_API_VERSION`: Version of the API, i.e. "v3", "beta" or "preview". Defaults to "beta". - * `HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. ## Bespoke API access ## diff --git a/githubot.coffee b/githubot.coffee index 124ba21..9d034d3 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -7,7 +7,7 @@ version = require("./package.json")["version"] process.env.HUBOT_CONCURRENT_REQUESTS ?= 20 class Github - constructor: (@logger) -> + constructor: (@logger, @apiVersion) -> @requestQueue = async.queue (task, cb) => task.run cb , process.env.HUBOT_CONCURRENT_REQUESTS @@ -27,12 +27,11 @@ class Github [cb, data] = [data, null] url_api_base = process.env.HUBOT_GITHUB_API || "https://api.github.com" - api_version = process.env.HUBOT_GITHUB_API_VERSION || "beta" if url[0..3] isnt "http" url = "/#{url}" unless url[0] is "/" url = "#{url_api_base}#{url}" - req = http.create(url).header("Accept", "application/vnd.github.#{api_version}+json") + req = http.create(url).header("Accept", "application/vnd.github.#{@apiVersion}+json") req = req.header("User-Agent", "GitHubot/#{version}") req = req.header("Authorization", "token #{oauth_token}") if (oauth_token = process.env.HUBOT_GITHUB_TOKEN)? args = [] @@ -116,8 +115,8 @@ class Github return @logger.error "Nothing to merge" cb sha: data.sha, message: data.commit.message, url: data.url -module.exports = github = (robot) -> - new Github robot.logger +module.exports = github = (robot, options = apiVersion: 'beta') -> + new Github robot.logger, options.apiVersion github[method] = func for method,func of Github.prototype diff --git a/test/request.coffee b/test/request.coffee index e2b70ea..596535c 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -28,10 +28,9 @@ describe "github api", -> network.matchHeader('Accept', 'application/vnd.github.beta+json') gh.request "GET", "repos/foo/bar/branches", success done it "allows setting API version", (done) -> - process.env.HUBOT_GITHUB_API_VERSION = "preview" + ghPreview = require("..") mock_robot, apiVersion: 'preview' network.matchHeader('Accept', 'application/vnd.github.preview+json') - gh.request "GET", "repos/foo/bar/branches", success done - delete process.env.HUBOT_GITHUB_API_VERSION + ghPreview.request "GET", "repos/foo/bar/branches", success done it "includes User-Agent header", (done) -> network.matchHeader('User-Agent', /GitHubot\/\d+\.\d+\.\d+/) gh.request "GET", "repos/foo/bar/branches", success done From 450408d9b66360a6382dcad0824092dbb66a2975 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 26 Jul 2013 00:16:24 -0700 Subject: [PATCH 36/76] Use nextTick for extra Nodeyness --- test/repo.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/repo.coffee b/test/repo.coffee index e369287..049c403 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -97,10 +97,9 @@ describe "repo api", -> network.delete("/repos/foo/bar/git/refs/heads/anotherBranch") .reply(204, {}) gh.branches( "foo/bar" ).delete @branchName, "anotherBranch", -> - setTimeout -> + nextTick -> network.done() done() - , 1 describe "merge", -> beforeEach -> From da8977764c7eb0ac463b26d37ef96383f013bd9f Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 26 Jul 2013 00:18:01 -0700 Subject: [PATCH 37/76] Tweak the Readme --- Readme.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Readme.md b/Readme.md index 089ebce..9e850d3 100644 --- a/Readme.md +++ b/Readme.md @@ -15,19 +15,19 @@ module.exports = (robot) -> github = require('githubot')(robot) ``` -Specify Github API version: - -```coffeescripts -module.exports = (robot) -> - github = require('githubot')(robot, apiVersion: 'preview') -``` - Or use it on its own: ```coffeescript github = require('githubot') ``` +Specify a different Github API version (default is `beta`): + +```coffeescripts +module.exports = (robot) -> + github = require('githubot')(robot, apiVersion: 'preview') +``` + ## Use ## Make any call to the Github v3 API, get the parsed JSON response: From 63fbb12b2fe4c297f1867e5f1c4cd8030d78a42d Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 26 Jul 2013 00:19:00 -0700 Subject: [PATCH 38/76] Tag v0.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a85f9a..a33554f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "0.4.0", + "version": "0.4.1", "homepage": "https://github.com/iangreenleaf/githubot", "repository": { "type": "git", From 1ef76ca0622c8a6ccae164533704d09a2f115b46 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 26 Jul 2013 00:24:23 -0700 Subject: [PATCH 39/76] Rookie mistake --- test/repo.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/repo.coffee b/test/repo.coffee index 049c403..1978b30 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -97,7 +97,7 @@ describe "repo api", -> network.delete("/repos/foo/bar/git/refs/heads/anotherBranch") .reply(204, {}) gh.branches( "foo/bar" ).delete @branchName, "anotherBranch", -> - nextTick -> + process.nextTick -> network.done() done() From 06c142f8d823fd4de15c1e067885f77068a1c7ef Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 31 Dec 2013 10:04:21 -0800 Subject: [PATCH 40/76] Workaround to build on 0.6 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ed05f88..c411fd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,5 @@ node_js: - "0.10" - "0.8" - "0.6" +before_install: + - '[ "${TRAVIS_NODE_VERSION}" != "0.6" ] || npm conf set strict-ssl false' From 069cbd79a6d8597454f9e3154c2b90cb8d41b1f4 Mon Sep 17 00:00:00 2001 From: Tomas Varaneckas Date: Fri, 27 Dec 2013 09:25:14 +0200 Subject: [PATCH 41/76] Add PUT, PATCH and DELETE wrappers around `request` Closes #14 --- Readme.md | 3 +++ githubot.coffee | 10 ++++++++++ test/request.coffee | 24 ++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 9e850d3..2078768 100644 --- a/Readme.md +++ b/Readme.md @@ -42,6 +42,9 @@ github.get "users/foo/repos", {type: "owner"}, (repos) -> data = { description: "A test gist", public: true, files: { "abc.txt": { content: "abcdefg" } } } github.post "gists", data, (gist) -> console.log gist.url + +github.patch "repos/my/repo/issues/11", {status: "closed"}, (issue) -> + console.log issue.html_url ``` ## Authentication ## diff --git a/githubot.coffee b/githubot.coffee index 9d034d3..1b75265 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -67,9 +67,19 @@ class Github if data? url += "?" + querystring.stringify data @request "GET", url, cb + post: (url, data, cb) -> @request "POST", url, data, cb + delete: (url, data, cb) -> + @request "DELETE", url, data, cb + + put: (url, data, cb) -> + @request "PUT", url, data, cb + + patch: (url, data, cb) -> + @request "PATCH", url, data, cb + handleErrors: (callback) -> @_errorHandler = (response) => callback response diff --git a/test/request.coffee b/test/request.coffee index 596535c..d503e5e 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -78,18 +78,38 @@ describe "github api", -> it "sends request", (done) -> gh.post "gists", data, success done + describe "put", -> + data = description: "A test gist", public: true, files: { "abc.txt": { content: "abcdefg" } } + response = url: "http://api.github.com/gists/1", id: 1 + beforeEach -> + network = nock("https://api.github.com") + .put("/gists", data) + .reply(201, response) + it "sends request", (done) -> + gh.put "gists", data, success done + + describe "patch", -> + data = description: "A test gist", public: true, files: { "abc.txt": { content: "abcdefg" } } + response = url: "http://api.github.com/gists/1", id: 1 + beforeEach -> + network = nock("https://api.github.com") + .patch("/gists", data) + .reply(201, response) + it "sends request", (done) -> + gh.patch "gists", data, success done + describe "delete", -> it "sends request", (done) -> network = nock("https://api.github.com") .delete("/gists/345") .reply(204) - gh.request "DELETE", "gists/345", success done + gh.delete "gists/345", success done it "includes empty body", (done) -> network = nock("https://api.github.com") .delete("/gists/345", "") .matchHeader("Content-Length", 0) .reply(204) - gh.request "DELETE", "gists/345", success done + gh.delete "gists/345", success done describe "errors", -> network = null From 265d0319b4883469d10dee7d0e0d72b472dba13f Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 31 Dec 2013 10:10:24 -0800 Subject: [PATCH 42/76] Remove data param from DELETE (see #17) --- githubot.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 1b75265..0252162 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -71,8 +71,8 @@ class Github post: (url, data, cb) -> @request "POST", url, data, cb - delete: (url, data, cb) -> - @request "DELETE", url, data, cb + delete: (url, cb) -> + @request "DELETE", url, null, cb put: (url, data, cb) -> @request "PUT", url, data, cb From 31c129d859e5727da498a17bce70041296364109 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 31 Dec 2013 10:21:49 -0800 Subject: [PATCH 43/76] Allow 0.11 to fail It's unstable, after all. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index c411fd1..6df6425 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,6 @@ node_js: - "0.6" before_install: - '[ "${TRAVIS_NODE_VERSION}" != "0.6" ] || npm conf set strict-ssl false' +matrix: + allow_failures: + - node_js: "0.11" From 70f430f8573554581255ba3afdc6bf7347116d26 Mon Sep 17 00:00:00 2001 From: Stephen Yeargin Date: Sat, 18 Jan 2014 15:57:37 -0600 Subject: [PATCH 44/76] First hack at adding deployment method. --- githubot.coffee | 12 ++++++++++++ test/repo.coffee | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/githubot.coffee b/githubot.coffee index 0252162..65bf16b 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -124,6 +124,18 @@ class Github unless data? return @logger.error "Nothing to merge" cb sha: data.sha, message: data.commit.message, url: data.url + deploy: (ref, pl, desc, cb) => + [opts,cb] = [{},opts] unless cb? + body = + ref: ref ? ref ? "master" + if pl? + body.payload = pl + if desc? + body.description = desc + @post "repos/#{@qualified_repo repo}/deployments", body, (data) => + unless data? + return @logger.error "Nothing to merge" + cb sha: data.sha, description: data.description, url: data.url module.exports = github = (robot, options = apiVersion: 'beta') -> new Github robot.logger, options.apiVersion diff --git a/test/repo.coffee b/test/repo.coffee index 1978b30..e89ac02 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -120,6 +120,26 @@ describe "repo api", -> network.done() done() + describe "deploy", -> + beforeEach -> + @branchName = "newbranch" + @payload = '{"environment":"production","deploy_user":"atmos","room_id":123456}' + @description = "deploying my sweet branch" + + it "succeeds", (done) -> + network = nock("https://api.github.com") + .post("/repos/foo/bar/deployments", + payload: @payload, ref: @branchName, description: @description) + .reply( + 201 + , sha: @branchName, url: "xyz/1", description: "abc" + ) + gh.branches("foo/bar").deploy @branchName, @payload, @description, (status) => + assert.deepEqual status, + sha: @branchName, description: "abc", url: "xyz/1" + network.done() + done() + context "with base specified", (done) -> beforeEach -> @base = "targetbranch" From 35938a80bf26ae7d34a788220e1b9bdc9f0900f8 Mon Sep 17 00:00:00 2001 From: Stephen Yeargin Date: Sun, 19 Jan 2014 00:16:43 -0600 Subject: [PATCH 45/76] Fixup --- githubot.coffee | 23 +++++++++++++++-------- test/repo.coffee | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 65bf16b..9047ddd 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -124,17 +124,24 @@ class Github unless data? return @logger.error "Nothing to merge" cb sha: data.sha, message: data.commit.message, url: data.url - deploy: (ref, pl, desc, cb) => + + deployments: (repo, cb) -> + if cb? + @get("repos/#{@qualified_repo repo}/deployments", cb) + else + create: (branchName, opts, cb) => [opts,cb] = [{},opts] unless cb? body = - ref: ref ? ref ? "master" - if pl? - body.payload = pl - if desc? - body.description = desc + ref: branchName ? branchName ? "master" + if opts.force? + body.force = opts.force + if opts.payload? + body.payload = opts.payload + if opts.auto_merge? + body.auto_merge = opts.auto_merge + if opts.description? + body.description = opts.description @post "repos/#{@qualified_repo repo}/deployments", body, (data) => - unless data? - return @logger.error "Nothing to merge" cb sha: data.sha, description: data.description, url: data.url module.exports = github = (robot, options = apiVersion: 'beta') -> diff --git a/test/repo.coffee b/test/repo.coffee index e89ac02..a28b56b 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -134,7 +134,7 @@ describe "repo api", -> 201 , sha: @branchName, url: "xyz/1", description: "abc" ) - gh.branches("foo/bar").deploy @branchName, @payload, @description, (status) => + gh.deployments("foo/bar").create @branchName, {payload: @payload, description: @description}, (status) => assert.deepEqual status, sha: @branchName, description: "abc", url: "xyz/1" network.done() From dba2e6c4ca96d546dcadadf4b0eb2d3083367bfd Mon Sep 17 00:00:00 2001 From: Stephen Yeargin Date: Mon, 20 Jan 2014 00:38:39 -0600 Subject: [PATCH 46/76] Adds method to deployments --- githubot.coffee | 2 ++ test/repo.coffee | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/githubot.coffee b/githubot.coffee index 9047ddd..4b837e6 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -143,6 +143,8 @@ class Github body.description = opts.description @post "repos/#{@qualified_repo repo}/deployments", body, (data) => cb sha: data.sha, description: data.description, url: data.url + status: (id, cb) => + @get("repos/#{@qualified_repo repo}/deployments/#{id}/statuses", cb) module.exports = github = (robot, options = apiVersion: 'beta') -> new Github robot.logger, options.apiVersion diff --git a/test/repo.coffee b/test/repo.coffee index a28b56b..22cf69a 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -120,7 +120,7 @@ describe "repo api", -> network.done() done() - describe "deploy", -> + describe "create deployment", -> beforeEach -> @branchName = "newbranch" @payload = '{"environment":"production","deploy_user":"atmos","room_id":123456}' @@ -140,6 +140,23 @@ describe "repo api", -> network.done() done() + describe "get deployment status", -> + beforeEach -> + @statusId = "123" + + it "succeeds", (done) -> + network = nock("https://api.github.com") + .get("/repos/foo/bar/deployments/#{@statusId}/statuses") + .reply( + 201 + , [{id: @statusId, state: "success", url: "xyz/1", description: "abc"}] + ) + gh.deployments("foo/bar").status @statusId, (status) => + assert.deepEqual status[0], + id: @statusId, state: "success", url: "xyz/1", description: "abc" + network.done() + done() + context "with base specified", (done) -> beforeEach -> @base = "targetbranch" From 55a10a27f317c497f062417550e7338db8882d30 Mon Sep 17 00:00:00 2001 From: Stephen Yeargin Date: Wed, 22 Jan 2014 01:08:37 -0600 Subject: [PATCH 47/76] Cleanup --- githubot.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/githubot.coffee b/githubot.coffee index 4b837e6..13d0df5 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -132,7 +132,7 @@ class Github create: (branchName, opts, cb) => [opts,cb] = [{},opts] unless cb? body = - ref: branchName ? branchName ? "master" + ref: branchName ? "master" if opts.force? body.force = opts.force if opts.payload? From 449c4d4ba757a9aa6a01e24097f4f244e4d97bf5 Mon Sep 17 00:00:00 2001 From: adamjacobbecker Date: Fri, 24 Jan 2014 13:11:27 -0800 Subject: [PATCH 48/76] add ability to override oauth token --- githubot.coffee | 7 ++++++- test/request.coffee | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/githubot.coffee b/githubot.coffee index 0252162..690b5c2 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -33,7 +33,12 @@ class Github url = "#{url_api_base}#{url}" req = http.create(url).header("Accept", "application/vnd.github.#{@apiVersion}+json") req = req.header("User-Agent", "GitHubot/#{version}") - req = req.header("Authorization", "token #{oauth_token}") if (oauth_token = process.env.HUBOT_GITHUB_TOKEN)? + if data?.token + oauth_token = data.token + delete data.token + else + oauth_token = process.env.HUBOT_GITHUB_TOKEN + req = req.header("Authorization", "token #{oauth_token}") if oauth_token? args = [] args.push JSON.stringify data if data? args.push "" if verb is "DELETE" and not data? diff --git a/test/request.coffee b/test/request.coffee index d503e5e..2c777b9 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -24,6 +24,11 @@ describe "github api", -> network.matchHeader("Authorization", "token 789abc") gh.request "GET", "repos/foo/bar/branches", success done delete process.env.HUBOT_GITHUB_TOKEN + it "overrides the oauth token manually", (done) -> + process.env.HUBOT_GITHUB_TOKEN = "789abc" + network.matchHeader("Authorization", "token abc") + gh.request "GET", "repos/foo/bar/branches", token: 'abc', success done + delete process.env.HUBOT_GITHUB_TOKEN it "includes accept header", (done) -> network.matchHeader('Accept', 'application/vnd.github.beta+json') gh.request "GET", "repos/foo/bar/branches", success done From 7298864e4ede7203b7e064cc40c413e6b2c15006 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 24 Jan 2014 18:11:01 -0800 Subject: [PATCH 49/76] Move tests to separate section --- test/repo.coffee | 76 +++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/test/repo.coffee b/test/repo.coffee index 22cf69a..7d27d3f 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -120,43 +120,6 @@ describe "repo api", -> network.done() done() - describe "create deployment", -> - beforeEach -> - @branchName = "newbranch" - @payload = '{"environment":"production","deploy_user":"atmos","room_id":123456}' - @description = "deploying my sweet branch" - - it "succeeds", (done) -> - network = nock("https://api.github.com") - .post("/repos/foo/bar/deployments", - payload: @payload, ref: @branchName, description: @description) - .reply( - 201 - , sha: @branchName, url: "xyz/1", description: "abc" - ) - gh.deployments("foo/bar").create @branchName, {payload: @payload, description: @description}, (status) => - assert.deepEqual status, - sha: @branchName, description: "abc", url: "xyz/1" - network.done() - done() - - describe "get deployment status", -> - beforeEach -> - @statusId = "123" - - it "succeeds", (done) -> - network = nock("https://api.github.com") - .get("/repos/foo/bar/deployments/#{@statusId}/statuses") - .reply( - 201 - , [{id: @statusId, state: "success", url: "xyz/1", description: "abc"}] - ) - gh.deployments("foo/bar").status @statusId, (status) => - assert.deepEqual status[0], - id: @statusId, state: "success", url: "xyz/1", description: "abc" - network.done() - done() - context "with base specified", (done) -> beforeEach -> @base = "targetbranch" @@ -204,3 +167,42 @@ describe "repo api", -> assert.ok /nothing to merge/i.exec msg network.done() done() + + describe "deployments", -> + describe "create deployment", -> + beforeEach -> + @branchName = "newbranch" + @payload = '{"environment":"production","deploy_user":"atmos","room_id":123456}' + @description = "deploying my sweet branch" + + it "succeeds", (done) -> + network = nock("https://api.github.com") + .post("/repos/foo/bar/deployments", + payload: @payload, ref: @branchName, description: @description) + .reply( + 201 + , sha: @branchName, url: "xyz/1", description: "abc" + ) + gh.deployments("foo/bar").create @branchName, {payload: @payload, description: @description}, (status) => + assert.deepEqual status, + sha: @branchName, description: "abc", url: "xyz/1" + network.done() + done() + + describe "get deployment status", -> + beforeEach -> + @statusId = "123" + + it "succeeds", (done) -> + network = nock("https://api.github.com") + .get("/repos/foo/bar/deployments/#{@statusId}/statuses") + .reply( + 201 + , [{id: @statusId, state: "success", url: "xyz/1", description: "abc"}] + ) + gh.deployments("foo/bar").status @statusId, (status) => + assert.deepEqual status[0], + id: @statusId, state: "success", url: "xyz/1", description: "abc" + network.done() + done() + From dca5b48ab4ef0b8555c79d516dd524a6e912b925 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 24 Jan 2014 18:13:55 -0800 Subject: [PATCH 50/76] New nock dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a33554f..47287c9 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,6 @@ "devDependencies": { "mocha": "1.x", "coffee-script": "1.x", - "nock": ">= 0.15.0" + "nock": ">= 0.27.1" } } From f979b0db08d0ce63759006c2f6c623e763c96787 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 10:42:53 -0800 Subject: [PATCH 51/76] Add `withOptions` for per-request overrides --- githubot.coffee | 27 +++++++++++++++++++-------- test/request.coffee | 25 ++++++++++++++++++++----- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index fe1b7fb..130318d 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -7,10 +7,17 @@ version = require("./package.json")["version"] process.env.HUBOT_CONCURRENT_REQUESTS ?= 20 class Github - constructor: (@logger, @apiVersion) -> + constructor: (@logger, @options) -> @requestQueue = async.queue (task, cb) => task.run cb , process.env.HUBOT_CONCURRENT_REQUESTS + withOptions: (specialOptions) -> + newOpts = {} + newOpts[k] = v for k,v of @options + newOpts[k] = v for k,v of specialOptions + g = new @constructor @logger, newOpts + g.requestQueue = @requestQueue + g qualified_repo: (repo) -> unless repo? unless (repo = process.env.HUBOT_GITHUB_REPO)? @@ -31,13 +38,9 @@ class Github if url[0..3] isnt "http" url = "/#{url}" unless url[0] is "/" url = "#{url_api_base}#{url}" - req = http.create(url).header("Accept", "application/vnd.github.#{@apiVersion}+json") + req = http.create(url).header("Accept", "application/vnd.github.#{@_opt "apiVersion"}+json") req = req.header("User-Agent", "GitHubot/#{version}") - if data?.token - oauth_token = data.token - delete data.token - else - oauth_token = process.env.HUBOT_GITHUB_TOKEN + oauth_token = @_opt "token" req = req.header("Authorization", "token #{oauth_token}") if oauth_token? args = [] args.push JSON.stringify data if data? @@ -151,8 +154,16 @@ class Github status: (id, cb) => @get("repos/#{@qualified_repo repo}/deployments/#{id}/statuses", cb) + _opt: (optName) -> + @options ?= {} + @options[optName] ? @_optFromEnv(optName) + _optFromEnv: (optName) -> + switch optName + when "token" then process.env.HUBOT_GITHUB_TOKEN + else null + module.exports = github = (robot, options = apiVersion: 'beta') -> - new Github robot.logger, options.apiVersion + new Github robot.logger, options github[method] = func for method,func of Github.prototype diff --git a/test/request.coffee b/test/request.coffee index 2c777b9..2f4df86 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -24,11 +24,6 @@ describe "github api", -> network.matchHeader("Authorization", "token 789abc") gh.request "GET", "repos/foo/bar/branches", success done delete process.env.HUBOT_GITHUB_TOKEN - it "overrides the oauth token manually", (done) -> - process.env.HUBOT_GITHUB_TOKEN = "789abc" - network.matchHeader("Authorization", "token abc") - gh.request "GET", "repos/foo/bar/branches", token: 'abc', success done - delete process.env.HUBOT_GITHUB_TOKEN it "includes accept header", (done) -> network.matchHeader('Accept', 'application/vnd.github.beta+json') gh.request "GET", "repos/foo/bar/branches", success done @@ -36,6 +31,26 @@ describe "github api", -> ghPreview = require("..") mock_robot, apiVersion: 'preview' network.matchHeader('Accept', 'application/vnd.github.preview+json') ghPreview.request "GET", "repos/foo/bar/branches", success done + it "allows setting API version for single request", (done) -> + network.matchHeader('Accept', 'application/vnd.github.special+json') + gh.withOptions(apiVersion: 'special').request "GET", "repos/foo/bar/branches", success done + it "allows setting the oauth token for single request", (done) -> + process.env.HUBOT_GITHUB_TOKEN = "789xyz" + network.matchHeader("Authorization", "token abc") + gh.withOptions(token: 'abc').request "GET", "repos/foo/bar/branches", success done + delete process.env.HUBOT_GITHUB_TOKEN + it "doesn't persist per-request options", (done) -> + network.matchHeader('Accept', 'application/vnd.github.special+json') + gh.withOptions(apiVersion: 'special').request "GET", "repos/foo/bar/branches", -> + network.done() + network2 = nock("https://api.github.com") + .get("/repos/baz/bar/branches") + .matchHeader('Accept', 'application/vnd.github.beta+json') + .reply(200, response) + # Should revert to the defaults on this request + gh.request "GET", "repos/baz/bar/branches", -> + network2.done() + done() it "includes User-Agent header", (done) -> network.matchHeader('User-Agent', /GitHubot\/\d+\.\d+\.\d+/) gh.request "GET", "repos/foo/bar/branches", success done From 1f3e569016d0978d0c75e75ea79f1591e5ef378c Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 10:46:59 -0800 Subject: [PATCH 52/76] Use preview API version for deployments, see #19 --- githubot.coffee | 8 +++++--- test/repo.coffee | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index 130318d..a972b13 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -134,8 +134,10 @@ class Github cb sha: data.sha, message: data.commit.message, url: data.url deployments: (repo, cb) -> + # These features are in preview mode + self = @withOptions apiVersion: 'cannonball-preview' if cb? - @get("repos/#{@qualified_repo repo}/deployments", cb) + self.get("repos/#{self.qualified_repo repo}/deployments", cb) else create: (branchName, opts, cb) => [opts,cb] = [{},opts] unless cb? @@ -149,10 +151,10 @@ class Github body.auto_merge = opts.auto_merge if opts.description? body.description = opts.description - @post "repos/#{@qualified_repo repo}/deployments", body, (data) => + self.post "repos/#{self.qualified_repo repo}/deployments", body, (data) => cb sha: data.sha, description: data.description, url: data.url status: (id, cb) => - @get("repos/#{@qualified_repo repo}/deployments/#{id}/statuses", cb) + self.get("repos/#{self.qualified_repo repo}/deployments/#{id}/statuses", cb) _opt: (optName) -> @options ?= {} diff --git a/test/repo.coffee b/test/repo.coffee index 7d27d3f..3c2dc92 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -206,3 +206,14 @@ describe "repo api", -> network.done() done() + it "uses special version header", (done) -> + network = nock("https://api.github.com") + .get("/repos/foo/bar/deployments/#{@statusId}/statuses") + .matchHeader('Accept', 'application/vnd.github.cannonball-preview+json') + .reply( + 201 + , [{id: @statusId, state: "success", url: "xyz/1", description: "abc"}] + ) + gh.deployments("foo/bar").status @statusId, (status) -> + network.done() + done() From ca6d497387b0525858dc8b826e371201f63d2879 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 11:38:42 -0800 Subject: [PATCH 53/76] Allow more options to be configured --- githubot.coffee | 23 +++++++++++++++-------- test/repo.coffee | 11 +++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/githubot.coffee b/githubot.coffee index a972b13..def2b74 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -4,13 +4,11 @@ querystring = require "querystring" version = require("./package.json")["version"] -process.env.HUBOT_CONCURRENT_REQUESTS ?= 20 - class Github constructor: (@logger, @options) -> @requestQueue = async.queue (task, cb) => task.run cb - , process.env.HUBOT_CONCURRENT_REQUESTS + , @_opt "concurrentRequests" withOptions: (specialOptions) -> newOpts = {} newOpts[k] = v for k,v of @options @@ -20,12 +18,12 @@ class Github g qualified_repo: (repo) -> unless repo? - unless (repo = process.env.HUBOT_GITHUB_REPO)? + unless (repo = @_opt "defaultRepo")? @logger.error "Default Github repo not specified" return null repo = repo.toLowerCase() return repo unless repo.indexOf("/") is -1 - unless (user = process.env.HUBOT_GITHUB_USER)? + unless (user = @_opt "defaultUser")? @logger.error "Default Github user not specified" return repo "#{user}/#{repo}" @@ -33,7 +31,7 @@ class Github unless cb? [cb, data] = [data, null] - url_api_base = process.env.HUBOT_GITHUB_API || "https://api.github.com" + url_api_base = @_opt("apiRoot") if url[0..3] isnt "http" url = "/#{url}" unless url[0] is "/" @@ -161,7 +159,16 @@ class Github @options[optName] ? @_optFromEnv(optName) _optFromEnv: (optName) -> switch optName - when "token" then process.env.HUBOT_GITHUB_TOKEN + when "token" + process.env.HUBOT_GITHUB_TOKEN + when "concurrentRequests" + process.env.HUBOT_CONCURRENT_REQUESTS ? 20 + when "defaultRepo" + process.env.HUBOT_GITHUB_REPO + when "defaultUser" + process.env.HUBOT_GITHUB_USER + when "apiRoot" + process.env.HUBOT_GITHUB_API ? "https://api.github.com" else null module.exports = github = (robot, options = apiVersion: 'beta') -> @@ -178,4 +185,4 @@ github.logger = { github.requestQueue = async.queue (task, cb) => task.run cb -, process.env.HUBOT_CONCURRENT_REQUESTS +, process.env.HUBOT_CONCURRENT_REQUESTS ? 20 diff --git a/test/repo.coffee b/test/repo.coffee index 3c2dc92..d2cd4c9 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -22,6 +22,17 @@ describe "repo api", -> gh.branches "foo/bar", (data) -> assert.deepEqual response, data done() + it "allows per-request overrides", (done) -> + network = nock("https://special.api.dev") + .get("/repos/bar/baz/branches") + .reply(200, response) + gh.withOptions( + apiRoot: "https://special.api.dev" + defaultUser: "bar" + defaultRepo: "baz" + ) + .branches null, success done + delete process.env.HUBOT_GITHUB_USER describe "create", -> beforeEach -> From 9695c4ef5bb79b07e09491454476bd0ac87cf0c1 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 12:27:20 -0800 Subject: [PATCH 54/76] Lock CoffeeScript version Something changed in the interaction with Mocha, and it started failing on newer versions. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 47287c9..ef9ea5a 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "mocha": "1.x", - "coffee-script": "1.x", + "coffee-script": "1.1.3", "nock": ">= 0.27.1" } } From b09676abfa0ff38465546bf3661cf9a9b70d7fbd Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 12:30:51 -0800 Subject: [PATCH 55/76] Updating Readme to explain options better --- Readme.md | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/Readme.md b/Readme.md index 2078768..e73872a 100644 --- a/Readme.md +++ b/Readme.md @@ -21,12 +21,7 @@ Or use it on its own: github = require('githubot') ``` -Specify a different Github API version (default is `beta`): - -```coffeescripts -module.exports = (robot) -> - github = require('githubot')(robot, apiVersion: 'preview') -``` +You can pass additional [options](#options) to the constructor if needed. ## Use ## @@ -90,17 +85,45 @@ This will happen with the bespoke methods as well: gh.branches "githubot", (branches) -> ``` -### Options ### +## Options ## + +### Passing options ### + +Options may be passed to githubot in three different ways, +in increasing order of precedence: + +1. Through shell environment variables. +2. Through the constructor: + + ```coffeescript + github = require('githubot')(robot, apiVersion: 'preview') + ``` +3. Using `withOptions`, which lets you pass options to only some requests: + + ```coffeescript + github = require('githubot')(robot) + preview = github.withOptions(apiVersion: 'preview') + # Uses preview API + preview.get '/preview/feature', -> # ... + # Uses regular API + github.get '/regular/feature', -> # ... + ``` + +### Available options ### -* `HUBOT_GITHUB_TOKEN`: GitHub API token. Required to perform authenticated actions. +* `token`/`process.env.HUBOT_GITHUB_TOKEN`: + GitHub API token. Required to perform authenticated actions. -* `HUBOT_GITHUB_USER`: Default GitHub username to use if one is not given. +* `defaultUser`/`process.env.HUBOT_GITHUB_USER`: + Default GitHub username to use if one is not given. -* `HUBOT_GITHUB_API`: The base API URL. This is useful for Enterprise Github installations. +* `apiRoot`/`process.env.HUBOT_GITHUB_API`: + The base API URL. This is useful for Enterprise Github installations. - For example, `HUBOT_GITHUB_API='http://myprivate.github.int'` + For example, `HUBOT_GITHUB_API='https://myprivate.github.int'` -* `HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. +* `concurrentRequests`/`process.env.HUBOT_CONCURRENT_REQUESTS`: + Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. ## Bespoke API access ## From d6622b2454ccef850e8e45d3b6bb7b5cc8ed5d1f Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 12:40:46 -0800 Subject: [PATCH 56/76] Promote apiVersion to a full-fledged option --- Readme.md | 4 ++++ githubot.coffee | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index e73872a..e104575 100644 --- a/Readme.md +++ b/Readme.md @@ -114,6 +114,10 @@ in increasing order of precedence: * `token`/`process.env.HUBOT_GITHUB_TOKEN`: GitHub API token. Required to perform authenticated actions. +* `apiVersion`/`process.env.HUBOT_GITHUB_API_VERSION`: + [Version of the API](http://developer.github.com/v3/versions/) + to access. Defaults to 'beta'. + * `defaultUser`/`process.env.HUBOT_GITHUB_USER`: Default GitHub username to use if one is not given. diff --git a/githubot.coffee b/githubot.coffee index def2b74..ba93bd0 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -169,9 +169,11 @@ class Github process.env.HUBOT_GITHUB_USER when "apiRoot" process.env.HUBOT_GITHUB_API ? "https://api.github.com" + when "apiVersion" + process.env.HUBOT_GITHUB_API_VERSION ? "beta" else null -module.exports = github = (robot, options = apiVersion: 'beta') -> +module.exports = github = (robot, options = {}) -> new Github robot.logger, options github[method] = func for method,func of Github.prototype From 576c386cb0214fbedb99deee2c195e7cc735d2bd Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 12:44:26 -0800 Subject: [PATCH 57/76] v0.5.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ef9ea5a..f615b3f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "0.4.1", + "version": "0.5.0", "homepage": "https://github.com/iangreenleaf/githubot", "repository": { "type": "git", @@ -13,7 +13,7 @@ "test": "make test" }, "engines": { - "node": ">=0.4.7" + "node": ">=0.6.0" }, "dependencies": { "scoped-http-client": ">= 0.9.8", From ff9e5fb785d3ea6636ebbf52441ca570ba13e40f Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 31 Jan 2014 13:20:36 -0800 Subject: [PATCH 58/76] Document deployments methods --- Readme.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index e104575..aef31f7 100644 --- a/Readme.md +++ b/Readme.md @@ -133,14 +133,15 @@ in increasing order of precedence: Mostly a work in progress, but here's a taste of what I have in mind: -### List branches ### +### Branches ### +#### List branches #### ```coffeescript gh.branches "foo/bar", (branches) -> console.log branches[0].name ``` -### Create a branch ### +#### Create a branch #### ```coffeescript # Branch from master @@ -152,7 +153,7 @@ gh.branches( "foo/bar" ).create "even_more_radical", from: "my_radical_feature", console.log branch.sha ``` -### Merge a branch ### +#### Merge a branch #### ```coffeescript # Merge a branch into master @@ -172,13 +173,40 @@ gh.branches( "foo/bar" ).merge "my_radical_feature", message: "Merge my radical console.log mergeCommit.sha ``` -### Delete a branch ### +#### Delete a branch #### ```coffeescript gh.branches( "foo/bar" ).delete "my_radical_feature", -> console.log "Deleted my branch!" ``` +### Deployments ### + +*Note*: These methods are smart and automatically use the +`cannonball-preview` version header. No intervention needed! + +#### List deployments #### + +```coffeescript +gh.deployments "foo/bar", (deploys) -> + console.log deploys.length +``` + +#### Create a deployment #### + +```coffeescript +gh.deployments("foo/bar") + .create 'my-branch', payload: {env: 'staging'}, description: "Ship it!", (deploy) => + console.log deploy.url +``` + +#### Deployment statuses #### + +```coffeescript +gh.deployments("foo/bar").status deployId, (deploys) -> + console.log deploys[0].state +``` + ## Contributing ## Install the dependencies: From d5e4b30112dbd2ed1bc69481e1d0c02060d3d214 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 12 Apr 2014 18:25:03 -0700 Subject: [PATCH 59/76] Use `v3` media type by default, fixes #21 --- Readme.md | 2 +- githubot.coffee | 2 +- test/request.coffee | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index aef31f7..4204e18 100644 --- a/Readme.md +++ b/Readme.md @@ -116,7 +116,7 @@ in increasing order of precedence: * `apiVersion`/`process.env.HUBOT_GITHUB_API_VERSION`: [Version of the API](http://developer.github.com/v3/versions/) - to access. Defaults to 'beta'. + to access. Defaults to 'v3'. * `defaultUser`/`process.env.HUBOT_GITHUB_USER`: Default GitHub username to use if one is not given. diff --git a/githubot.coffee b/githubot.coffee index ba93bd0..e212ef7 100644 --- a/githubot.coffee +++ b/githubot.coffee @@ -170,7 +170,7 @@ class Github when "apiRoot" process.env.HUBOT_GITHUB_API ? "https://api.github.com" when "apiVersion" - process.env.HUBOT_GITHUB_API_VERSION ? "beta" + process.env.HUBOT_GITHUB_API_VERSION ? "v3" else null module.exports = github = (robot, options = {}) -> diff --git a/test/request.coffee b/test/request.coffee index 2f4df86..35f99b9 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -25,7 +25,7 @@ describe "github api", -> gh.request "GET", "repos/foo/bar/branches", success done delete process.env.HUBOT_GITHUB_TOKEN it "includes accept header", (done) -> - network.matchHeader('Accept', 'application/vnd.github.beta+json') + network.matchHeader('Accept', 'application/vnd.github.v3+json') gh.request "GET", "repos/foo/bar/branches", success done it "allows setting API version", (done) -> ghPreview = require("..") mock_robot, apiVersion: 'preview' @@ -45,7 +45,7 @@ describe "github api", -> network.done() network2 = nock("https://api.github.com") .get("/repos/baz/bar/branches") - .matchHeader('Accept', 'application/vnd.github.beta+json') + .matchHeader('Accept', 'application/vnd.github.v3+json') .reply(200, response) # Should revert to the defaults on this request gh.request "GET", "repos/baz/bar/branches", -> From e774076042f7cf3754841c30a8610ad2a2823412 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 12 Apr 2014 18:35:19 -0700 Subject: [PATCH 60/76] Simple refactor Split custom api methods into own files. Everything else is pretty much the same. --- package.json | 2 +- src/branches.coffee | 33 ++++++++++++++ src/deployments.coffee | 22 ++++++++++ githubot.coffee => src/githubot.coffee | 59 ++------------------------ test/test_helper.coffee | 2 +- 5 files changed, 61 insertions(+), 57 deletions(-) create mode 100644 src/branches.coffee create mode 100644 src/deployments.coffee rename githubot.coffee => src/githubot.coffee (63%) diff --git a/package.json b/package.json index f615b3f..b9a3519 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "type": "git", "url": "git@github.com:iangreenleaf/githubot.git" }, - "main": "./githubot", + "main": "./src/githubot", "scripts": { "test": "make test" }, diff --git a/src/branches.coffee b/src/branches.coffee new file mode 100644 index 0000000..a7f4c29 --- /dev/null +++ b/src/branches.coffee @@ -0,0 +1,33 @@ +async = require "async" + +module.exports = (repo, cb) -> + if cb? + @get("repos/#{@qualified_repo repo}/branches", cb) + else + create: (branchName, opts, cb) => + [opts,cb] = [{},opts] unless cb? + opts.from ?= "master" + @get "repos/#{@qualified_repo repo}/git/refs/heads/#{opts.from}", (json) => + sha = json.object.sha + @post "repos/#{@qualified_repo repo}/git/refs", + ref: "refs/heads/#{branchName}", sha: sha + , (data) -> + cb name: branchName, commit: { sha: data.object.sha, url: data.object.url } + delete: (branchNames..., cb) => + actions = [] + for branchName in branchNames + do (branchName) => + actions.push (done) => + @request "DELETE", "repos/#{@qualified_repo repo}/git/refs/heads/#{branchName}", done + async.parallel actions, cb + merge: (head, opts, cb) => + [opts,cb] = [{},opts] unless cb? + body = + base: opts.base ? opts.into ? "master" + head: head + if opts.message? + body.commit_message = opts.message + @post "repos/#{@qualified_repo repo}/merges", body, (data) => + unless data? + return @logger.error "Nothing to merge" + cb sha: data.sha, message: data.commit.message, url: data.url diff --git a/src/deployments.coffee b/src/deployments.coffee new file mode 100644 index 0000000..548901a --- /dev/null +++ b/src/deployments.coffee @@ -0,0 +1,22 @@ +module.exports = (repo, cb) -> + # These features are in preview mode + self = @withOptions apiVersion: 'cannonball-preview' + if cb? + self.get("repos/#{self.qualified_repo repo}/deployments", cb) + else + create: (branchName, opts, cb) => + [opts,cb] = [{},opts] unless cb? + body = + ref: branchName ? "master" + if opts.force? + body.force = opts.force + if opts.payload? + body.payload = opts.payload + if opts.auto_merge? + body.auto_merge = opts.auto_merge + if opts.description? + body.description = opts.description + self.post "repos/#{self.qualified_repo repo}/deployments", body, (data) => + cb sha: data.sha, description: data.description, url: data.url + status: (id, cb) => + self.get("repos/#{self.qualified_repo repo}/deployments/#{id}/statuses", cb) diff --git a/githubot.coffee b/src/githubot.coffee similarity index 63% rename from githubot.coffee rename to src/githubot.coffee index e212ef7..f4bcb6f 100644 --- a/githubot.coffee +++ b/src/githubot.coffee @@ -2,7 +2,7 @@ http = require "scoped-http-client" async = require "async" querystring = require "querystring" -version = require("./package.json")["version"] +version = require("../package.json")["version"] class Github constructor: (@logger, @options) -> @@ -99,60 +99,9 @@ class Github _errorHandler: (response) -> @_loggerErrorHandler response - branches: (repo, cb) -> - if cb? - @get("repos/#{@qualified_repo repo}/branches", cb) - else - create: (branchName, opts, cb) => - [opts,cb] = [{},opts] unless cb? - opts.from ?= "master" - @get "repos/#{@qualified_repo repo}/git/refs/heads/#{opts.from}", (json) => - sha = json.object.sha - @post "repos/#{@qualified_repo repo}/git/refs", - ref: "refs/heads/#{branchName}", sha: sha - , (data) -> - cb name: branchName, commit: { sha: data.object.sha, url: data.object.url } - delete: (branchNames..., cb) => - actions = [] - for branchName in branchNames - do (branchName) => - actions.push (done) => - @request "DELETE", "repos/#{@qualified_repo repo}/git/refs/heads/#{branchName}", done - async.parallel actions, cb - merge: (head, opts, cb) => - [opts,cb] = [{},opts] unless cb? - body = - base: opts.base ? opts.into ? "master" - head: head - if opts.message? - body.commit_message = opts.message - @post "repos/#{@qualified_repo repo}/merges", body, (data) => - unless data? - return @logger.error "Nothing to merge" - cb sha: data.sha, message: data.commit.message, url: data.url - - deployments: (repo, cb) -> - # These features are in preview mode - self = @withOptions apiVersion: 'cannonball-preview' - if cb? - self.get("repos/#{self.qualified_repo repo}/deployments", cb) - else - create: (branchName, opts, cb) => - [opts,cb] = [{},opts] unless cb? - body = - ref: branchName ? "master" - if opts.force? - body.force = opts.force - if opts.payload? - body.payload = opts.payload - if opts.auto_merge? - body.auto_merge = opts.auto_merge - if opts.description? - body.description = opts.description - self.post "repos/#{self.qualified_repo repo}/deployments", body, (data) => - cb sha: data.sha, description: data.description, url: data.url - status: (id, cb) => - self.get("repos/#{self.qualified_repo repo}/deployments/#{id}/statuses", cb) + branches: require './branches' + + deployments: require './deployments' _opt: (optName) -> @options ?= {} diff --git a/test/test_helper.coffee b/test/test_helper.coffee index cacf796..d1ec6d0 100644 --- a/test/test_helper.coffee +++ b/test/test_helper.coffee @@ -9,7 +9,7 @@ mock_robot = mock_robot.onError = null mock_robot.logs = { error: [], debug: [] } -gh = require("..") mock_robot +gh = require("../src/githubot") mock_robot nock = require("nock") module.exports = [ gh, require("assert"), nock, mock_robot ] From 6d7bb5e15e30fa85b90b08abb2843a92afe2eb95 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 12 Apr 2014 19:16:06 -0700 Subject: [PATCH 61/76] Compile to JS before publishing Hooray, I'm finally not an abomination in the eyes of @isaacs! --- .gitignore | 4 ++++ package.json | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3c3629e..1a4f804 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ node_modules +lib + +# We auto-generate .npmignore from this file +.npmignore diff --git a/package.json b/package.json index b9a3519..620678c 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,15 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "0.5.0", + "version": "1.0.0-beta2", "homepage": "https://github.com/iangreenleaf/githubot", "repository": { "type": "git", "url": "git@github.com:iangreenleaf/githubot.git" }, - "main": "./src/githubot", + "main": "./lib/githubot", "scripts": { + "prepublish": "coffee -o lib -c src && cat .gitignore | grep -v '^lib$' > .npmignore", "test": "make test" }, "engines": { From 63bf7f86c67066158a2ee24d0fcf41177e54a21d Mon Sep 17 00:00:00 2001 From: Ian Young Date: Sat, 12 Apr 2014 22:27:29 -0700 Subject: [PATCH 62/76] Better paths to package in tests Fixes test errors in 0.6.x --- test/request.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/request.coffee b/test/request.coffee index 35f99b9..8325165 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -28,7 +28,7 @@ describe "github api", -> network.matchHeader('Accept', 'application/vnd.github.v3+json') gh.request "GET", "repos/foo/bar/branches", success done it "allows setting API version", (done) -> - ghPreview = require("..") mock_robot, apiVersion: 'preview' + ghPreview = require("../src/githubot") mock_robot, apiVersion: 'preview' network.matchHeader('Accept', 'application/vnd.github.preview+json') ghPreview.request "GET", "repos/foo/bar/branches", success done it "allows setting API version for single request", (done) -> @@ -221,7 +221,7 @@ describe "github api", -> describe "without robot given", -> before -> - gh = require("..") + gh = require("../src/githubot") it "complains to stderr", (done) -> util = require "util" util._old_error = util.error From 5f7825cb69480ef247e735c4d1df3de0f2899083 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Tue, 10 Jun 2014 16:22:25 -0700 Subject: [PATCH 63/76] Lock nock version for breaking older Nodes Node 0.8 apparently can't handle `^x.x.x` SemVer ranges. Nock includes that in a dependency in newer versions, so it's out until we drop 0.8 support. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 620678c..e3b361d 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,6 @@ "devDependencies": { "mocha": "1.x", "coffee-script": "1.1.3", - "nock": ">= 0.27.1" + "nock": "~0.28.3" } } From 1e6288b8b6d4b9de7206e189cb706b6b2a7a8ca0 Mon Sep 17 00:00:00 2001 From: Dan Poltawski Date: Wed, 8 Oct 2014 07:44:08 +0100 Subject: [PATCH 64/76] Update 'Acquire a token' instructions As note is now required: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 4204e18..eb294c2 100644 --- a/Readme.md +++ b/Readme.md @@ -50,7 +50,7 @@ If `process.env.HUBOT_GITHUB_TOKEN` is present, you're automatically authenticat If you don't have a token yet, run this: - curl -i https://api.github.com/authorizations -d '{"scopes":["repo"]}' -u "yourusername" + curl -i https://api.github.com/authorizations -d '{"note":"githubot","scopes":["repo"]}' -u "yourusername" Enter your Github password when prompted. When you get a response, look for the "token" value. From 07b1bf52b86edf79f8dd600bd1db7336c5ef55c7 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 12 Mar 2015 11:31:36 -0700 Subject: [PATCH 65/76] Allow error handling as a standard option Fixes #28. --- src/githubot.coffee | 5 ++--- test/request.coffee | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/githubot.coffee b/src/githubot.coffee index f4bcb6f..7b2c017 100644 --- a/src/githubot.coffee +++ b/src/githubot.coffee @@ -87,9 +87,7 @@ class Github @request "PATCH", url, data, cb handleErrors: (callback) -> - @_errorHandler = (response) => - callback response - @_loggerErrorHandler response + @options.errorHandler = callback _loggerErrorHandler: (response) -> message = response.error @@ -97,6 +95,7 @@ class Github @logger.error message _errorHandler: (response) -> + @options.errorHandler?(response) @_loggerErrorHandler response branches: require './branches' diff --git a/test/request.coffee b/test/request.coffee index 8325165..7b26325 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -219,6 +219,21 @@ describe "github api", -> gh.handleErrors cb gh.get "/foo", never_called + it "works in combination with withOptions", (done) -> + network.matchHeader('Accept', 'application/vnd.github.special+json') + network.reply(406, message: "I hate you!") + gh.handleErrors (response) -> + assert.equal 406, response.statusCode + done() + gh.withOptions(apiVersion: 'special').get "/foo", never_called + + it "can be passed as withOptions", (done) -> + network.reply(406, message: "I hate you!") + errHandler = (response) -> + assert.equal 406, response.statusCode + done() + gh.withOptions(errorHandler: errHandler).get "/foo", never_called + describe "without robot given", -> before -> gh = require("../src/githubot") From 9cae2f5ac438f3df8b94266aca52d2a380af804a Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 12 Mar 2015 11:57:07 -0700 Subject: [PATCH 66/76] Update Readme for new error handling options --- Readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Readme.md b/Readme.md index eb294c2..6cca975 100644 --- a/Readme.md +++ b/Readme.md @@ -71,6 +71,8 @@ The callback takes a `response` argument with the following keys: * `statusCode`: The status code of the API response, if present. * `body`: The body of the API response, if present. +You can also pass an error handler in [the options](#available-options) instead. + ## Helpful Hubot ## If `process.env.HUBOT_GITHUB_USER` is present, we can help you guess a repo's full name: @@ -129,6 +131,9 @@ in increasing order of precedence: * `concurrentRequests`/`process.env.HUBOT_CONCURRENT_REQUESTS`: Limits the allowed number of concurrent requests to the GitHub API. Defaults to 20. +* `errorHandler`: + Function for custom error handling logic. See [handling errors](#handling-errors) for more details. + ## Bespoke API access ## Mostly a work in progress, but here's a taste of what I have in mind: From 1414bc273baca60c978b13d5e213dbba47fdc47e Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 12 Mar 2015 11:58:42 -0700 Subject: [PATCH 67/76] Replace util.error with console.error `util.error` is deprecated, because random Node is random. --- src/githubot.coffee | 3 +-- test/request.coffee | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/githubot.coffee b/src/githubot.coffee index 7b2c017..6072f39 100644 --- a/src/githubot.coffee +++ b/src/githubot.coffee @@ -128,8 +128,7 @@ github[method] = func for method,func of Github.prototype github.logger = { error: (msg) -> - util = require "util" - util.error "ERROR: #{msg}" + console.error "ERROR: #{msg}" debug: -> } diff --git a/test/request.coffee b/test/request.coffee index 7b26325..3e9ec58 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -238,13 +238,12 @@ describe "github api", -> before -> gh = require("../src/githubot") it "complains to stderr", (done) -> - util = require "util" - util._old_error = util.error - util.error = (msg) -> + console._old_error = console.error + console.error = (msg) -> if msg.match /bad credentials/i - util.error = util._old_error + console.error = @_old_error done() else @_old_error.call process.stderr, msg - network.reply(401, message: "Bad credentials") + network.reply(401, message: "Bad credentials!") gh.get "/foo", never_called From 118fcdc9d726f8964635c82ff2bd0a5906ea2c5c Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 12 Mar 2015 12:16:24 -0700 Subject: [PATCH 68/76] Updating CoffeeScript/Mocha --- package.json | 4 ++-- test/mocha.opts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 test/mocha.opts diff --git a/package.json b/package.json index e3b361d..c64f49d 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,8 @@ "async": "0.2.x" }, "devDependencies": { - "mocha": "1.x", - "coffee-script": "1.1.3", + "mocha": "~2.2", + "coffee-script": "~1.9.1", "nock": "~0.28.3" } } diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..8c1102b --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,2 @@ +--require coffee-script/register +--compilers coffee:coffee-script/register From d0bb0af1894b1d2ff302f260818db16ea7755e5d Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 12 Mar 2015 12:02:08 -0700 Subject: [PATCH 69/76] v1.0.0 * Use stable `v3` API version by default * Publish JavaScript, not CoffeeScript * Add `errorHandler` option and fix bug combining error handling with options. * Replace deprecated `util.error` calls --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c64f49d..be410a4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "1.0.0-beta2", + "version": "1.0.0", "homepage": "https://github.com/iangreenleaf/githubot", "repository": { "type": "git", From ebcb56b4fecc9c0ea67a9cb5ce521bf2933c2096 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Sun, 17 Jul 2016 16:08:09 -0700 Subject: [PATCH 70/76] Add instructions for acquiring a token with 2FA --- Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Readme.md b/Readme.md index 6cca975..4bb1de6 100644 --- a/Readme.md +++ b/Readme.md @@ -54,6 +54,8 @@ If you don't have a token yet, run this: Enter your Github password when prompted. When you get a response, look for the "token" value. +If you have two-factor authentication enabled, you'll have to append `-H 'X-GitHub-OTP: 123456'` to the end of the above command, or you'll receive HTTP 401 Unauthorized instead of your token. + ## Handling errors ## GitHubot will log errors automatically if it has a logger. Used with Hubot, these will go to the Hubot logger. From d92187d74b5f703e23c50360f12b758e32de9989 Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 28 Jul 2016 14:29:46 -0500 Subject: [PATCH 71/76] Treat empty merge like other errors --- src/branches.coffee | 3 ++- test/repo.coffee | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/branches.coffee b/src/branches.coffee index a7f4c29..4b7b222 100644 --- a/src/branches.coffee +++ b/src/branches.coffee @@ -29,5 +29,6 @@ module.exports = (repo, cb) -> body.commit_message = opts.message @post "repos/#{@qualified_repo repo}/merges", body, (data) => unless data? - return @logger.error "Nothing to merge" + return @_errorHandler + error: "Nothing to merge" cb sha: data.sha, message: data.commit.message, url: data.url diff --git a/test/repo.coffee b/test/repo.coffee index d2cd4c9..600dba7 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -179,6 +179,19 @@ describe "repo api", -> network.done() done() + it "notifies custom error handler about no-op", (done) -> + network = nock("https://api.github.com") + .post("/repos/foo/bar/merges", + base: "master", head: @branchName) + .reply(204) + errHandler = (response) -> + assert.ok /nothing to merge/i.exec response.error + network.done() + done() + gh.withOptions(errorHandler: errHandler).branches("foo/bar").merge @branchName, -> + assert.fail null, null, "Should not call callback" + + describe "deployments", -> describe "create deployment", -> beforeEach -> From 44f15c5252f704ef1dee84422692f5e17d7fba5e Mon Sep 17 00:00:00 2001 From: Ian Young Date: Thu, 28 Jul 2016 14:32:55 -0500 Subject: [PATCH 72/76] Refactor tests --- test/repo.coffee | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/test/repo.coffee b/test/repo.coffee index 600dba7..0840e59 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -167,30 +167,27 @@ describe "repo api", -> network.done() done() - it "notifies about no-op", (done) -> - network = nock("https://api.github.com") - .post("/repos/foo/bar/merges", - base: "master", head: @branchName) - .reply(204) - gh.branches("foo/bar").merge @branchName, -> - assert.fail null, null, "Should not call callback" - mock_robot.onError = (msg) -> - assert.ok /nothing to merge/i.exec msg - network.done() - done() - - it "notifies custom error handler about no-op", (done) -> - network = nock("https://api.github.com") - .post("/repos/foo/bar/merges", - base: "master", head: @branchName) - .reply(204) - errHandler = (response) -> - assert.ok /nothing to merge/i.exec response.error - network.done() - done() - gh.withOptions(errorHandler: errHandler).branches("foo/bar").merge @branchName, -> - assert.fail null, null, "Should not call callback" + context "when no-op", (done) -> + beforeEach -> + network = nock("https://api.github.com") + .post("/repos/foo/bar/merges", + base: "master", head: @branchName) + .reply(204) + + it "errors", (done) -> + gh.branches("foo/bar").merge @branchName, -> + assert.fail null, null, "Should not call callback" + mock_robot.onError = (msg) -> + assert.ok /nothing to merge/i.exec msg + network.done() + done() + it "notifies custom error handler", (done) -> + errHandler = (response) -> + assert.ok /nothing to merge/i.exec response.error + network.done() + done() + gh.withOptions(errorHandler: errHandler).branches("foo/bar").merge @branchName describe "deployments", -> describe "create deployment", -> From 77f9aad0aaf19d0b65102b8cc9fc3d841bc9c67c Mon Sep 17 00:00:00 2001 From: Ian Young Date: Fri, 29 Jul 2016 11:04:28 -0500 Subject: [PATCH 73/76] Update Node versions --- .travis.yml | 12 ++++-------- package.json | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6df6425..9c72688 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,7 @@ language: node_js node_js: - - "0.11" + - "6" + - "5" + - "4" + - "0.12" - "0.10" - - "0.8" - - "0.6" -before_install: - - '[ "${TRAVIS_NODE_VERSION}" != "0.6" ] || npm conf set strict-ssl false' -matrix: - allow_failures: - - node_js: "0.11" diff --git a/package.json b/package.json index be410a4..f675947 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "test": "make test" }, "engines": { - "node": ">=0.6.0" + "node": ">=0.10.0" }, "dependencies": { "scoped-http-client": ">= 0.9.8", From 09b199308a30f04bf46fca91b7c1ad3ede42a8d7 Mon Sep 17 00:00:00 2001 From: Redouane Boutchacha Date: Sun, 21 Aug 2016 19:50:10 +0200 Subject: [PATCH 74/76] Add license field --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f675947..883af10 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Hubot-compatible Github API wrapper for Node.js", "version": "1.0.0", "homepage": "https://github.com/iangreenleaf/githubot", + "license": "MIT", "repository": { "type": "git", "url": "git@github.com:iangreenleaf/githubot.git" From 994320b83a71eab38b3db64eb173b8baebac6705 Mon Sep 17 00:00:00 2001 From: Morgan Delagrange Date: Wed, 14 Sep 2016 10:28:55 -0700 Subject: [PATCH 75/76] Fix withOptions when used without a robot --- src/githubot.coffee | 2 +- test/request.coffee | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/githubot.coffee b/src/githubot.coffee index 6072f39..0804b15 100644 --- a/src/githubot.coffee +++ b/src/githubot.coffee @@ -13,7 +13,7 @@ class Github newOpts = {} newOpts[k] = v for k,v of @options newOpts[k] = v for k,v of specialOptions - g = new @constructor @logger, newOpts + g = new Github @logger, newOpts g.requestQueue = @requestQueue g qualified_repo: (repo) -> diff --git a/test/request.coffee b/test/request.coffee index 3e9ec58..94c878f 100644 --- a/test/request.coffee +++ b/test/request.coffee @@ -34,6 +34,10 @@ describe "github api", -> it "allows setting API version for single request", (done) -> network.matchHeader('Accept', 'application/vnd.github.special+json') gh.withOptions(apiVersion: 'special').request "GET", "repos/foo/bar/branches", success done + it "allows setting an API version for single request without robot", (done) -> + noRobot = require("../src/githubot") + newGh = noRobot.withOptions('Accept', 'application/vnd.github.preview+json') + newGh.request "GET", "repos/foo/bar/branches", success done it "allows setting the oauth token for single request", (done) -> process.env.HUBOT_GITHUB_TOKEN = "789xyz" network.matchHeader("Authorization", "token abc") From afc9326f76c8e9f6c8401ffde6c649143627463b Mon Sep 17 00:00:00 2001 From: Ian Young Date: Wed, 14 Sep 2016 23:33:27 -0500 Subject: [PATCH 76/76] Release 1.0.1 Fixes a bug in the constructor options in standalone mode. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 883af10..2d7c6d0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Ian Young ", "name": "githubot", "description": "Hubot-compatible Github API wrapper for Node.js", - "version": "1.0.0", + "version": "1.0.1", "homepage": "https://github.com/iangreenleaf/githubot", "license": "MIT", "repository": {