Application icon

github_api

Gem Version Build Status Dependency Status Code Climate Coverage Status

Website | Wiki | RDocs

A Ruby client for the official GitHub API.

Supports all the API methods. It's built in a modular way. You can either instantiate the whole API wrapper Github.new or use parts of it i.e. Github::Client::Repos.new if working solely with repositories is your main concern. Intuitive query methods allow you easily call API endpoints.

Features

Installation

Install the gem by running

gem install github_api

or put it in your Gemfile and run bundle install

gem "github_api"

Contents

1 Usage

To start using the gem, you can either perform direct calls on Github

Github.repos.list user: 'wycats'

or create a new client instance

github = Github.new

1.1 Options

At this stage, you can also supply various configuration parameters, such as adapter # http client used for performing requests auto_pagination # false by default, set to true to traverse requests page links oauth_token # oauth authorization token basic_auth # login:password string client_id # oauth client id client_secret # oauth client secret user # global user used in requests if none provided repo # global repository used in requests in none provided org # global organization used in requests if none provided endpoint # enterprise API endpoint site # enterprise API web endpoint ssl # SSL settings per_page # number of items per page- max of 100 user_agent # custom user agent name, 'Github API' by default which are used throughout the API. These can be passed directly as hash options:

github = Github.new oauth_token: 'token'

Alternatively, you can configure the GitHub settings by passing a block, for instance, for a custom enterprise endpoint and website like

github = Github.new do |config|
  config.endpoint    = 'https://github.company.com/api/v3'
  config.site        = 'https://github.company.com'
  config.oauth_token = 'token'
  config.adapter     = :net_http
  config.ssl         = {:verify => false}
end

You can authenticate either using OAuth authentication convenience methods (see OAuth section) or through basic authentication by passing your login and password credentials

github = Github.new login:'peter-murach', password:'...'

or using a convenience method:

github = Github.new basic_auth: 'login:password'

1.2 API navigation

This gem closely mirrors the GitHub API hierarchy i.e. if you want to create a download resource, look up the GitHub API spec and issue the request as in github.repos.downloads.create

For example to interact with GitHub Repositories API, issue the following calls that correspond directly to the GitHub API hierarchy

github.repos.commits.all  'user-name', 'repo-name'
github.repos.hooks.create 'user-name', 'repo-name', name: "web", active: true
github.repos.keys.get     'user-name', 'repo-name'

1.3 Modularity

The code base is modular and allows for you to work specifically with a given part of GitHub API e.g. blobs

blobs = Github::GitData::Blobs.new
blobs.create 'peter-murach', 'github', content: 'Blob content'

1.4 Response querying

The response is of type [Github::ResponseWrapper] which allows traversing all the json response attributes like method calls i.e.

repos = Github::Repos.new user: 'peter-murach', repo: 'github'
repos.branches do |branch|
  puts branch.name
end

2 Arguments & Parameters

The GithubAPI library allows for flexible argument parsing. Therefore, arguments can be passed during instance creation:

  issues = Github::Issues.new user: 'peter-murach', repo: 'github'
  issues.milestones.list state: 'open'

Further, arguments can be passed directly inside the method called, but then the order of parameters matters and hence please consult the method documentation or GitHub specification. For instance:

  issues = Github::Issues.new
  issues.milestones.list 'peter-murach', 'github', state: 'open'

Similarly, the arguments for the request can be passed inside the current scope such as:

  issues = Github::Issues.new
  issues.milestones(user: 'peter-murach', repo: 'github').list

But why limit ourselves? You can mix and match arguments, for example:

  issues = Github::Issues.new user: 'peter-murach'
  issues.milestones(repo: 'github').list
  issues.milestones(repo: 'tty').list

You can also use a bit of syntactic sugar common among Ruby libraries whereby "username/repository" can be passed as well:

  issues = Github::Issues.new
  issues.milestones('peter-murach/github').list
  issues.milestones.list 'peter-murach/github'

Finally, use the with scope to clearly denote your requests

  issues = Github::Issues.new
  issues.milestones.with(user: 'peter-murach', repo: 'github').list

Some API methods apart from required parameters such as username, repository name or organisation name, allow you to switch the way the data is returned to you, for instance

github = Github.new
github.git_data.trees.get 'peter-murach', 'github', 'c18647b75d72f19c1e0cc8af031e5d833b7f12ea'
# => gets a tree

github.git_data.trees.get 'peter-murach', 'github', 'c18647b75d72f19c1e0cc8af031e5d833b7f12ea',
  recursive: true # => gets a whole tree recursively

by passing a block you can iterate over the file tree

github.git_data.trees.get 'peter-murach', 'github', 'c18647b75d72f19c1e0cc8af031e5d833b7f12ea',
  recursive: true do |file|
    puts file.path
end

3 Advanced Configuration

The github_api gem will use the default middleware stack which is exposed by calling stack on a client instance. However, this stack can be freely modified with methods such as insert, insert_after, delete and swap. For instance, to add your CustomMiddleware do

github = Github.new do |config|
  config.stack.insert_after Github::Response::Helpers, CustomMiddleware
end

Furthermore, you can build your entire custom stack and specify other connection options such as adapter

github = Github.new do |config|
  config.adapter :excon

  config.stack do |builder|
    builder.use Github::Response::Helpers
    builder.use Github::Response::Jsonize
  end
end

4 Authentication

4.1 Basic

To start making requests as authenticated user you can use your GitHub username and password like so

Github.new basic_auth: 'login:password'

Though this method is convenient you should strongly consider using OAuth for improved security reasons.

4.2 Application OAuth access

In order to authenticate your app through OAuth2 on GitHub you need to

You can use convenience methods to help you achieve this using GithubAPI gem:

github = Github.new client_id: '...', client_secret: '...'
github.authorize_url redirect_uri: 'http://localhost', scope: 'repo'
# => "https://github.com/login/oauth/authorize?scope=repo&response_type=code&client_id='...'&redirect_uri=http%3A%2F%2Flocalhost"

After you get your authorization code, call to receive your access_token

token = github.get_token( authorization_code )

Once you have your access token, configure your github instance following instructions under Configuration.

Note: If you are working locally (i.e. your app URL and callback URL are localhost), do not specify a :redirect_uri otherwise you will get a redirect_uri_mismatch error.

4.3 Authorizations API

4.3.1 For an User

To create an access token through the GitHub Authrizations API, you are required to pass your basic credentials and scopes you wish to have for the authentication token.

github = Github.new basic_auth: 'login:password'
github.oauth.create scopes: ['repo']

You can add more than one scope from the user, public_repo, repo, gist or leave the scopes parameter out, in which case, the default read-only access will be assumed (includes public user profile info, public repo info, and gists).

4.3.2 For an App

Furthermore, to create auth token for an application you need to pass :app argument together with :client_id and :client_secret parameters.

github = Github.new basic_auth: 'login:password'
github.oauth.app.create 'client-id', scopes: ['repo']

In order to revoke auth token(s) for an application you must use basic authentication with client_id as login and client_secret as password.

github = Github.new basic_auth: "client_id:client_secret"
github.oauth.app.delete 'client-id'

Revoke a specific app token.

github.oauth.app.delete 'client-id', 'access-token'

4.4 Scopes

You can check OAuth scopes you have by:

  github = Github.new :oauth_token => 'token'
  github.scopes.list    # => ['repo']

To list the scopes that the particular GitHub API action checks for do:

  repos = Github::Repos.new
  res = repos.list :user => 'peter-murach'
  res.headers.accepted_oauth_scopes    # => ['delete_repo', 'repo', 'public_repo', 'repo:status']

To understand what each scope means refer to documentation

5 SSL

By default requests over SSL are set to OpenSSL::SSL::VERIFY_PEER. However, you can turn off peer verification by

  Github.new ssl: { verify: false }

If your client fails to find CA certs, you can pass other SSL options to specify exactly how the information is sourced

  ssl: {
    client_cert: "/usr/local/www.example.com/client_cert.pem"
    client_key:  "/user/local/www.example.com/client_key.pem"
    ca_file:     "example.com.cert"
    ca_path:     "/etc/ssl/"
  }

For instance, download CA root certificates from Mozilla cacert and point ca_file at your certificate bundle location. This will allow the client to verify the github.com ssl certificate as authentic.

6 API

Main API methods are grouped into the following classes that can be instantiated on their own

Github         - full API access

Github::Gists           Github::GitData    Github::Repos             Github::Search
Github::Orgs            Github::Issues     Github::Authorizations
Github::PullRequests    Github::Users      Github::Activity

Some parts of GitHub API v3 require you to be authenticated, for instance the following are examples of APIs only for the authenticated user

Github::Users::Emails
Github::Users::Keys

All method calls form Ruby like sentences and allow for intuitive API navigation, for instance

github = Github.new :oauth_token => '...'
github.users.followers.following 'wycats'  # => returns users that 'wycats' is following
github.users.followers.following? 'wycats' # => returns true if following, otherwise false

For specifications on all available methods, go to http://developer.github.com/v3/ or read the rdoc. All methods are documented there with examples of usage.

Alternatively, you can find out which methods are supported by calling actions on a class instance in your irb:

>> Github::Repos.actions                    >> github.issues.actions
---                                         ---
|--> all                                    |--> all
|--> branches                               |--> comments
|--> collaborators                          |--> create
|--> commits                                |--> edit
|--> contribs                               |--> events
|--> contributors                           |--> find
|--> create                                 |--> get
|--> downloads                              |--> labels
|--> edit                                   |--> list
|--> find                                   |--> list_repo
|--> forks                                  |--> list_repository
|--> get                                    |--> milestones
|--> hooks                                  ...
...

7 Media Types

You can specify custom media types to choose the format of the data you wish to receive. To make things easier you can specify the following shortcuts json, blob, raw, text, html, full. For instance:

github = Github.new
github.issues.get 'peter-murach', 'github', 108, media: 'text'

This will be expanded into application/vnd.github.v3.text+json

If you wish to specify the version, pass media: 'beta.text' which will be converted to application/vnd/github.beta.text+json.

Finally, you can always pass the whole accept header like so

github.issues.get 'peter-murach', 'github', 108, accept: 'application/vnd.github.raw'

8 Hypermedia

TODO

9 Configuration

Certain methods require authentication. To get your GitHub OAuth v2 credentials, register an app at https://github.com/settings/applications/ You will need to be logged in to register the application.

Github.configure do |config|
  config.oauth_token   = YOUR_OAUTH_ACCESS_TOKEN
  config.basic_auth    = 'login:password'
end

or

Github.new(:oauth_token => YOUR_OAUTH_TOKEN)
Github.new(:basic_auth => 'login:password')

All parameters can be overwritten each method call by passing a parameters hash.

By default, no caching will be performed. In order to set the cache do... If no cache type is provided, a default memoization is done.

10 Pagination

Any request that returns multiple items will be paginated to 30 items by default. You can specify custom page and per_page query parameters to alter default behavior. For instance:

repos = Github::Repos.new
repos.list user: 'wycats', per_page: 10, page: 5

Then you can query the pagination information included in the link header by:

res.links.first  # Shows the URL of the first page of results.
res.links.next   # Shows the URL of the immediate next page of results.
res.links.prev   # Shows the URL of the immediate previous page of results.
res.links.last   # Shows the URL of the last page of results.

In order to iterate through the entire result set page by page, you can use convenience methods:

res.each_page do |page|
  page.each do |repo|
    puts repo.name
  end
end

or use has_next_page? and next_page like in the following:

while res.has_next_page?
  ... process response ...
  res.next_page
end

Alternatively, you can retrieve all pages in one invocation by passing the auto_pagination option like so:

  github = Github.new auto_pagination: true

Depending at what stage you pass the auto_pagination it will affect all or only a single request:

  Github::Repos.new auto_pagination: true         # affects Repos part of API

  Github::Repos.new.list user: '...', auto_pagination: true  # affects a single request

One can also navigate straight to the specific page by:

res.count_pages  # Number of pages
res.page 5       # Requests given page if it exists, nil otherwise
res.first_page   # Get first page
res.next_page    # Get next page
res.prev_page    # Get previous page
res.last_page    # Get last page

11 Caching

Caching is supported through the faraday-http-cache gem.

Add the gem to your Gemfile:

gem 'faraday-http-cache'

You can now configure cache parameters as follows

Github.configure do |config|
  config.stack do |builder|
    builder.use Faraday::HttpCache, store: Rails.cache
  end
end

More details on the available options can be found in the gem's own documentation: https://github.com/plataformatec/faraday-http-cache#faraday-http-cache

12 Debugging

run with ENV['DEBUG'] flag or include middleware by passing debug flag

13 Error Handling

The generic error class Github::Error::GithubError will handle both the client (Github::Error::ClientError) and service (Github::Error::ServiceError) side errors. For instance in your code you can catch errors like

begin
  # Do something with github_api gem
rescue Github::Error::GithubError => e
  puts e.message

  if e.is_a? Github::Error::ServiceError
    # handle GitHub service errors such as 404
  elsif e.is_a? Github::Error::ClientError
    # handle client errors e.i. missing required parameter in request
  end
end

14 Response Message

Each response comes packaged with methods allowing for inspection of HTTP start line and headers. For example, to check for rate limits and status codes, call

res = Github::Repos.new.branches 'peter-murach', 'github'
res.headers.ratelimit_limit     # "5000"
res.headers.ratelimit_remaining # "4999"
res.headers.status              # "200"
res.headers.content_type        # "application/json; charset=utf-8"
res.headers.etag                # "\"2c5dfc54b3fe498779ef3a9ada9a0af9\""
res.headers.cache_control       # "public, max-age=60, s-maxage=60"

15 Examples

Some API methods require input parameters. These are simply added as a hash of properties, for instance

issues = Github::Issues.new user:'peter-murach', repo: 'github-api'
issues.milestones.list state: 'open', sort: 'due_date', direction: 'asc'

Other methods may require inputs as an array of strings

users = Github::Users.new oauth_token: 'token'
users.emails.add 'email1', 'email2', ..., 'emailn' # => Adds emails to the authenticated user

If a method returns a collection, you can iterate over it by supplying a block parameter,

events = Github::Activity::Events.new
events.public do |event|
  puts event.actor.
end

Query requests return boolean values instead of HTTP responses

github = Github.new
github.orgs.members.member? 'github', 'technoweenie', public: true # => true

15.1 Rails Example

A Rails controller that allows a user to authorize their GitHub account and then performs a request.

class GithubController < ApplicationController

  attr_accessor :github
  private :github

  def authorize
    github  = Github.new client_id: '...', client_secret: '...'
    address = github.authorize_url redirect_uri: 'http://...', scope: 'repo'
    redirect_to address
  end

  def callback
    authorization_code = params[:code]
    access_token = github.get_token authorization_code
    access_token.token   # => returns token value
  end
end

16 Testing

The test suite is split into two groups, live and mock.

The live tests are the ones in features folder and they simply exercise the GitHub API by making live requests and then being cached with VCR in directory named features\cassettes. For details on how to get set up, please navigate to the features folder.

The mock tests are in the spec directory and their primary concern is to test the gem internals without the hindrance of external calls.

Development

Questions or problems? Please post them on the issue tracker. You can contribute changes by forking the project and submitting a pull request. You can ensure the tests are passing by running bundle and rake.

Copyright

Copyright (c) 2011-2014 Piotr Murach. See LICENSE.txt for further details.