diff --git a/Readme.md b/Readme.md index 4204e18..6376a60 100644 --- a/Readme.md +++ b/Readme.md @@ -129,6 +129,13 @@ 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. +* `gitio`/`process.env.HUBOT_GITIO`: + If truthy, use [Git.io][] to shorten commit links when formatting. + +* `oneline`/`process.env.HUBOT_COMMIT_ONELINE`: + If truthy, truncate commit messages to the first line when + formatting. + ## Bespoke API access ## Mostly a work in progress, but here's a taste of what I have in mind: @@ -180,6 +187,66 @@ gh.branches( "foo/bar" ).delete "my_radical_feature", -> console.log "Deleted my branch!" ``` +### Commits ### + +#### Get commit details #### + +```coffeescript +gh.commits "iangreenleaf/githubot", "63bf7f86c67066158a2ee24d0fcf41177e54a21d", (commit) -> + console.log commit +``` + +GitHub's commit objects vary depending on the endpoint. This one is +just a wrapper around [GET +/repos/:owner/:repo/commits/:sha][get-commit]. + +#### Format commit objects #### + +Besides retrieving commit messages with `commits`, you can call +`commits` without arguments to access the data-massaging `formatter` +method. This is useful for pretty-printing commit messages, and you +can use the default output: + +```coffeescript +gh.commits "iangreenleaf/githubot", "63bf7f86c67066158a2ee24d0fcf41177e54a21d", (commit) -> + gh.commits().format commit, (commit) -> + console.log "#{commit.message} (#{commit.html_url})" +``` + +``` +Better paths to package in tests + +Fixes test errors in 0.6.x (https://github.com/iangreenleaf/githubot/commit/63bf7f86c67066158a2ee24d0fcf41177e54a21d) +``` + +Or You can also use the `gitio` and `oneline` [options](#options) to +adjust the `message` and `html_url` attributes: + +```coffeescript +gh.commits "iangreenleaf/githubot", "63bf7f86c67066158a2ee24d0fcf41177e54a21d", (commit) -> + gh.withOptions( + gitio: true + oneline: true + ) + .commits().format commit, (commit) -> + console.log "#{commit.message} (#{commit.html_url})" +``` + +``` +Better paths to package in tests (http://git.io/l5mwmQ) +``` + +The formatter can take input commit objects from the following +endpoints: + +* [GET /repos/:owner/:repo/commits/:sha][get-commit] +* [GET /repos/:owner/:repo/git/commits/:sha][get-commit] +* The [push event][push-event] + +Which all store data in slightly different ways. In any case, the +object passed to the `format` callback will have `commit.message` and +`commit.html_url`. + ### Deployments ### *Note*: These methods are smart and automatically use the @@ -231,3 +298,7 @@ ask and I can probably point you in the right direction. [Build Status]: https://travis-ci.org/iangreenleaf/githubot.png?branch=master +[Git.io]: http://git.io/ +[get-commit]: https://developer.github.com/v3/repos/commits/#get-a-single-commit +[get-git-commit]: https://developer.github.com/v3/git/commits/#get-a-commit +[push-event]: https://developer.github.com/v3/activity/events/types/#pushevent diff --git a/package.json b/package.json index e3b361d..3434e1c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ }, "dependencies": { "scoped-http-client": ">= 0.9.8", - "async": "0.2.x" + "async": "0.2.x", + "gitio2": ">= 2.0.0" }, "devDependencies": { "mocha": "1.x", diff --git a/src/commits.coffee b/src/commits.coffee new file mode 100644 index 0000000..237527d --- /dev/null +++ b/src/commits.coffee @@ -0,0 +1,40 @@ +gitio = require "gitio2" + +module.exports = (repo, commit, cb) -> + if cb? + if commit? + @get "repos/#{@qualified_repo repo}/commits/#{commit}", cb + else + @get "repos/#{@qualified_repo repo}/commits", cb + else + format: (commit, cb) => + if @_opt "oneline" + if commit.message? + commit.message = commit.message.split("\n")[0] + else if commit.commit? and commit.commit.message? + commit.message = commit.commit.message.split("\n")[0] + else if commit.commit? and commit.commit.message? and not commit.message? + commit.message = commit.commit.message + if not commit.html_url? + regex = new RegExp "^#{@_opt "apiRoot"}/(api/#{@_opt "apiVersion"}/)?repos/([^/]+)/([^/]+)/(?:git/)?commits/([a-f0-9]+)$" + match = commit.url.match regex + if match? + api = match[1] + owner = match[2] + repo = match[3] + sha = match[4] + if api? + base_url = @_opt "apiRoot" + else + base_url = "https://github.com" + commit.html_url = "#{base_url}/#{owner}/#{repo}/commit/#{sha}" + else + console.log "Unmatched commit URL: #{commit.url}" + commit.html_url = commit.url + if @_opt "gitio" + gitio commit.html_url, (err, data) -> + if not err + commit.html_url = data + cb commit + else + cb commit diff --git a/src/githubot.coffee b/src/githubot.coffee index f4bcb6f..63edd16 100644 --- a/src/githubot.coffee +++ b/src/githubot.coffee @@ -101,6 +101,8 @@ class Github branches: require './branches' + commits: require './commits' + deployments: require './deployments' _opt: (optName) -> @@ -120,6 +122,10 @@ class Github process.env.HUBOT_GITHUB_API ? "https://api.github.com" when "apiVersion" process.env.HUBOT_GITHUB_API_VERSION ? "v3" + when "gitio" + process.env.HUBOT_GITIO + when "oneline" + process.env.HUBOT_COMMIT_ONELINE else null module.exports = github = (robot, options = {}) -> diff --git a/test/commits.coffee b/test/commits.coffee new file mode 100644 index 0000000..ef3531c --- /dev/null +++ b/test/commits.coffee @@ -0,0 +1,42 @@ +[ gh, assert, nock, mock_robot ] = require "./test_helper" + +describe "commit utilities", -> + describe "formatting", -> + summary = "message summary" + commit = + sha: "abcdeg" + url: "https://github.com/foo/bar/commit/abcdeg" + message: summary + "\n\nmessage body" + gitio_short = "yyy" + gitio_url = "http://git.io/" + gitio_short + network = null + success = (done) -> + network.done() + done() + it "formats commits with gitio", (done) -> + network = nock("http://git.io") + .post("/create") + .reply(201, gitio_short) + gh.withOptions( + gitio: true + ) + .commits().format commit, (commit) -> + assert.deepEqual commit.html_url, gitio_url + success done + it "formats commits without gitio", (done) -> + original_url = commit.html_url + gh.commits().format commit, (commit) -> + assert.deepEqual commit.html_url, original_url + done() + it "formats commits with oneline", (done) -> + gh.withOptions( + oneline: true + ) + .commits().format commit, (commit) -> + assert.deepEqual commit.message, summary + done() + it "formats commits without oneline", (done) -> + original_message = commit.message + gh.commits().format commit, (commit) -> + assert.deepEqual commit.message, original_message + done() diff --git a/test/repo.coffee b/test/repo.coffee index d2cd4c9..41dd2fa 100644 --- a/test/repo.coffee +++ b/test/repo.coffee @@ -228,3 +228,40 @@ describe "repo api", -> gh.deployments("foo/bar").status @statusId, (status) -> network.done() done() + + describe "commits", -> + response = [ { sha: "abcdeg", url: "xxx" } ] + network = null + success = (done) -> + (body) -> + network.done() + done() + beforeEach -> + network = nock("https://api.github.com") + .get("/repos/foo/bar/commits") + .reply(200, response) + it "accepts a full repo with a commit hash", (done) -> + network = nock("https://api.github.com") + .get("/repos/foo/bar/commits/abcdeg") + .reply(200, response) + gh.commits "foo/bar", "abcdeg", success done + it "accepts a full repo without a commit hash", (done) -> + gh.commits "foo/bar", null, success done + it "accepts an unqualified repo", (done) -> + process.env.HUBOT_GITHUB_USER = "foo" + gh.commits "bar", null, success done + delete process.env.HUBOT_GITHUB_USER + it "returns json", (done) -> + gh.commits "foo/bar", null, (data) -> + assert.deepEqual response, data + done() + it "allows per-request overrides", (done) -> + network = nock("https://special.api.dev") + .get("/repos/bar/baz/commits") + .reply(200, response) + gh.withOptions( + apiRoot: "https://special.api.dev" + defaultUser: "bar" + defaultRepo: "baz" + ) + .commits null, null, success done