From acaf5cac89df5fe12d400faa6114e6bbcfe84d1e Mon Sep 17 00:00:00 2001
From: Nic Wolfe <nic@wolfeden.ca>
Date: Thu, 5 Apr 2012 20:52:09 -0600
Subject: [PATCH] Removed the old github lib and switched to the v3 API for
 version checking.

---
 lib/pygithub/__init__.py    |  25 --
 lib/pygithub/ghsearch.py    |  50 ----
 lib/pygithub/github.py      | 520 ------------------------------------
 lib/pygithub/githubsync.py  |  88 ------
 lib/pygithub/githubtest.py  | 493 ----------------------------------
 sickbeard/gh_api.py         |  59 ++++
 sickbeard/versionChecker.py |   8 +-
 7 files changed, 63 insertions(+), 1180 deletions(-)
 delete mode 100644 lib/pygithub/__init__.py
 delete mode 100644 lib/pygithub/ghsearch.py
 delete mode 100644 lib/pygithub/github.py
 delete mode 100644 lib/pygithub/githubsync.py
 delete mode 100644 lib/pygithub/githubtest.py
 create mode 100644 sickbeard/gh_api.py

diff --git a/lib/pygithub/__init__.py b/lib/pygithub/__init__.py
deleted file mode 100644
index ad56e0873..000000000
--- a/lib/pygithub/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2005-2008  Dustin Sallings <dustin@spy.net>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-#
-# <http://www.opensource.org/licenses/mit-license.php>
-"""
-github module.
-"""
-__all__ = ['github','ghsearch','githubsync']
diff --git a/lib/pygithub/ghsearch.py b/lib/pygithub/ghsearch.py
deleted file mode 100644
index 586e502b1..000000000
--- a/lib/pygithub/ghsearch.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2005-2008  Dustin Sallings <dustin@spy.net>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-#
-# <http://www.opensource.org/licenses/mit-license.php>
-"""
-Search script.
-"""
-
-import sys
-
-import github
-
-def usage():
-    """display the usage and exit"""
-    print "Usage:  %s keyword [keyword...]" % (sys.argv[0])
-    sys.exit(1)
-
-def mk_url(repo):
-    return "http://github.com/%s/%s" % (repo.username, repo.name)
-
-if __name__ == '__main__':
-    g = github.GitHub()
-    if len(sys.argv) < 2:
-        usage()
-    res = g.repos.search(' '.join(sys.argv[1:]))
-
-    for repo in res:
-        try:
-            print "Found %s at %s" % (repo.name, mk_url(repo))
-        except AttributeError:
-            print "Bug: Couldn't format %s" % repo.__dict__
diff --git a/lib/pygithub/github.py b/lib/pygithub/github.py
deleted file mode 100644
index c0bfad778..000000000
--- a/lib/pygithub/github.py
+++ /dev/null
@@ -1,520 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2005-2008  Dustin Sallings <dustin@spy.net>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-#
-# <http://www.opensource.org/licenses/mit-license.php>
-"""
-Interface to github's API (v2).
-
-Basic usage:
-
-g = GitHub()
-
-for r in g.user.search('dustin'):
-    print r.name
-
-See the GitHub docs or README.markdown for more usage.
-
-Copyright (c) 2007  Dustin Sallings <dustin@spy.net>
-"""
-
-# GAE friendly URL detection (theoretically)
-try:
-    import urllib2
-    default_fetcher = urllib2.urlopen
-except LoadError:
-    pass
-
-import urllib
-import xml
-import xml.dom.minidom
-
-def _string_parser(x):
-    """Extract the data from the first child of the input."""
-    return x.firstChild.data
-
-_types = {
-    'string': _string_parser,
-    'integer': lambda x: int(_string_parser(x)),
-    'float': lambda x: float(_string_parser(x)),
-    'datetime': _string_parser,
-    'boolean': lambda x: _string_parser(x) == 'true'
-}
-
-def _parse(el):
-    """Generic response parser."""
-
-    type = 'string'
-    if el.attributes and 'type' in el.attributes.keys():
-        type = el.attributes['type'].value
-    elif el.localName in _types:
-        type = el.localName
-    elif len(el.childNodes) > 1:
-        # This is a container, find the child type
-        type = None
-        ch = el.firstChild
-        while ch and not type:
-            if ch.localName == 'type':
-                type = ch.firstChild.data
-            ch = ch.nextSibling
-
-    if not type:
-        raise Exception("Can't parse %s, known: %s"
-                        % (el.toxml(), repr(_types.keys())))
-
-    return _types[type](el)
-
-def parses(t):
-    """Parser for a specific type in the github response."""
-    def f(orig):
-        orig.parses = t
-        return orig
-    return f
-
-def with_temporary_mappings(m):
-    """Allow temporary localized altering of type mappings."""
-    def f(orig):
-        def every(self, *args):
-            global _types
-            o = _types.copy()
-            for k,v in m.items():
-                if v:
-                    _types[k] = v
-                else:
-                    del _types[k]
-            try:
-                return orig(self, *args)
-            finally:
-                _types = o
-        return every
-    return f
-
-@parses('array')
-def _parseArray(el):
-    rv = []
-    ch = el.firstChild
-    while ch:
-        if ch.nodeType != xml.dom.Node.TEXT_NODE and ch.firstChild:
-            rv.append(_parse(ch))
-        ch=ch.nextSibling
-    return rv
-
-class BaseResponse(object):
-    """Base class for XML Response Handling."""
-
-    def __init__(self, el):
-        ch = el.firstChild
-        while ch:
-            if ch.nodeType != xml.dom.Node.TEXT_NODE and ch.firstChild:
-                ln = ch.localName.replace('-', '_')
-                self.__dict__[ln] = _parse(ch)
-            ch=ch.nextSibling
-
-    def __repr__(self):
-        return "<<%s>>" % str(self.__class__)
-
-class User(BaseResponse):
-    """A github user."""
-
-    parses = 'user'
-
-    def __repr__(self):
-        return "<<User %s>>" % self.name
-
-class Plan(BaseResponse):
-    """A github plan."""
-
-    parses = 'plan'
-
-    def __repr__(self):
-        return "<<Plan %s>>" % self.name
-
-class Repository(BaseResponse):
-    """A repository."""
-
-    parses = 'repository'
-
-    @property
-    def owner_name(self):
-        if hasattr(self, 'owner'):
-            return self.owner
-        else:
-            return self.username
-
-    def __repr__(self):
-        return "<<Repository %s/%s>>" % (self.owner_name, self.name)
-
-class PublicKey(BaseResponse):
-    """A public key."""
-
-    parses = 'public-key'
-    title = 'untitled'
-
-    def __repr__(self):
-        return "<<Public key %s>>" % self.title
-
-class Commit(BaseResponse):
-    """A commit."""
-
-    parses = 'commit'
-
-    def __repr__(self):
-        return "<<Commit: %s>>" % self.id
-
-class Parent(Commit):
-    """A commit parent."""
-
-    parses = 'parent'
-
-class Author(User):
-    """A commit author."""
-
-    parses = 'author'
-
-class Committer(User):
-    """A commit committer."""
-
-    parses = 'committer'
-
-class Issue(BaseResponse):
-    """An issue within the issue tracker."""
-
-    parses = 'issue'
-
-    def __repr__(self):
-        return "<<Issue #%d>>" % self.number
-
-class Label(BaseResponse):
-    """A Label within the issue tracker."""
-    parses = 'label'
-
-    def __repr__(self):
-        return "<<Label $%d>>" % self.number
-
-class Tree(BaseResponse):
-    """A Tree object."""
-
-    # Parsing is scoped to objects...
-    def __repr__(self):
-        return "<<Tree: %s>>" % self.name
-
-class Blob(BaseResponse):
-    """A Blob object."""
-
-    # Parsing is scoped to objects...
-    def __repr__(self):
-        return "<<Blob: %s>>" % self.name
-
-class Modification(BaseResponse):
-    """A modification object."""
-
-    # Parsing is scoped to usage
-    def __repr__(self):
-        return "<<Modification of %s>>" % self.filename
-
-class Network(BaseResponse):
-    """A network entry."""
-
-    parses = 'network'
-
-    def __repr__(self):
-        return "<<Network of %s/%s>>" % (self.owner, self.name)
-
-# Load the known types.
-for __t in (t for t in globals().values() if hasattr(t, 'parses')):
-    _types[__t.parses] = __t
-
-class BaseEndpoint(object):
-
-    BASE_URL = 'https://github.com/api/v2/xml/'
-
-    def __init__(self, user, token, fetcher):
-        self.user = user
-        self.token = token
-        self.fetcher = fetcher
-
-    def _raw_fetch(self, path):
-        p = self.BASE_URL + path
-        args = ''
-        if self.user and self.token:
-            params = '&'.join(['login=' + urllib.quote(self.user),
-                               'token=' + urllib.quote(self.token)])
-            if '?' in path:
-                p += params
-            else:
-                p += '?' + params
-        return self.fetcher(p).read()
-
-    def _fetch(self, path):
-        return xml.dom.minidom.parseString(self._raw_fetch(path))
-
-    def _post(self, path, **kwargs):
-        p = {'login': self.user, 'token': self.token}
-        p.update(kwargs)
-        return self.fetcher(self.BASE_URL + path, urllib.urlencode(p)).read()
-
-    def _parsed(self, path):
-        doc = self._fetch(path)
-        return _parse(doc.documentElement)
-
-    def _posted(self,path,**kwargs):
-        stuff = self._post(path,**kwargs)
-        doc = xml.dom.minidom.parseString(stuff)
-        return _parse(doc.documentElement)
-
-class UserEndpoint(BaseEndpoint):
-
-    def search(self, query):
-        """Search for a user."""
-        return self._parsed('user/search/' + query)
-
-    def show(self, username):
-        """Get the info for a user."""
-        return self._parsed('user/show/' + username)
-
-    def keys(self):
-        """Get the public keys for a user."""
-        return self._parsed('user/keys')
-
-    def removeKey(self, keyId):
-        """Remove the key with the given ID (as retrieved from keys)"""
-        self._post('user/key/remove', id=keyId)
-
-    def addKey(self, name, key):
-        """Add an ssh key."""
-        self._post('user/key/add', name=name, key=key)
-
-class RepositoryEndpoint(BaseEndpoint):
-
-    def forUser(self, username):
-        """Get the repositories for the given user."""
-        return self._parsed('repos/show/' + username)
-
-    def branches(self, user, repo):
-        """List the branches for a repo."""
-        doc = self._fetch("repos/show/" + user + "/" + repo + "/branches")
-        rv = {}
-        for c in doc.documentElement.childNodes:
-            if c.nodeType != xml.dom.Node.TEXT_NODE:
-                rv[c.localName] = str(c.firstChild.data)
-        return rv
-
-    def search(self, term):
-        """Search for repositories."""
-        return self._parsed('repos/search/' + urllib.quote_plus(term))
-
-    def show(self, user, repo):
-        """Lookup an individual repository."""
-        return self._parsed('/'.join(['repos', 'show', user, repo]))
-
-    def watch(self, user, repo):
-        """Watch a repository."""
-        self._post('repos/watch/' + user + '/' + repo)
-
-    def unwatch(self, user, repo):
-        """Stop watching a repository."""
-        self._post('repos/unwatch/' + user + '/' + repo)
-
-    def watched(self, user):
-        """Get watched repositories of a user."""
-        return self._parsed('repos/watched/' + user)
-
-    def network(self, user, repo):
-        """Get the network for a given repo."""
-        return self._parsed('repos/show/' + user + '/' + repo + '/network')
-
-    def setVisible(self, repo, public=True):
-        """Set the visibility of the given repository (owned by the current user)."""
-        if public:
-            path = 'repos/set/public/' + repo
-        else:
-            path = 'repos/set/private/' + repo
-        self._post(path)
-
-    def create(self, name, description='', homepage='', public=1):
-        """Create a new repository."""
-        self._post('repos/create', name=name, description=description,
-                   homepage=homepage, public=str(public))
-
-    def delete(self, repo):
-        """Delete a repository."""
-        self._post('repos/delete/' + repo)
-
-    def fork(self, user, repo):
-        """Fork a user's repo."""
-        self._post('repos/fork/' + user + '/' + repo)
-
-    def collaborators(self, user, repo):
-        """Find all of the collaborators of one of your repositories."""
-        return self._parsed('repos/show/%s/%s/collaborators' % (user, repo))
-
-    def addCollaborator(self, repo, username):
-        """Add a collaborator to one of your repositories."""
-        self._post('repos/collaborators/' + repo + '/add/' + username)
-
-    def removeCollaborator(self, repo, username):
-        """Remove a collaborator from one of your repositories."""
-        self._post('repos/collaborators/' + repo + '/remove/' + username)
-
-    def collaborators_all(self):
-        """Find all of the collaborators of every of your repositories.
-
-        Returns a dictionary with reponame as key and a list of collaborators as value."""
-        ret = {}
-        for reponame in (rp.name for rp in self.forUser(self.user)):
-            ret[reponame] = self.collaborators(self.user, reponame)
-        return ret
-
-    def addCollaborator_all(self, username):
-        """Add a collaborator to all of your repositories."""
-        for reponame in (rp.name for rp in self.forUser(self.user)):
-            self.addCollaborator(reponame, username)
-
-    def removeCollaborator_all(self, username):
-        """Remove a collaborator from all of your repositories."""
-        for reponame in (rp.name for rp in self.forUser(self.user)):
-            self.removeCollaborator(reponame, username)
-
-    def deployKeys(self, repo):
-        """List the deploy keys for the given repository.
-
-        The repository must be owned by the current user."""
-        return self._parsed('repos/keys/' + repo)
-
-    def addDeployKey(self, repo, title, key):
-        """Add a deploy key to a repository."""
-        self._post('repos/key/' + repo + '/add', title=title, key=key)
-
-    def removeDeployKey(self, repo, keyId):
-        """Remove a deploy key."""
-        self._post('repos/key/' + repo + '/remove', id=keyId)
-
-class CommitEndpoint(BaseEndpoint):
-
-    def forBranch(self, user, repo, branch='master'):
-        """Get the commits for the given branch."""
-        return self._parsed('/'.join(['commits', 'list', user, repo, branch]))
-
-    def forFile(self, user, repo, path, branch='master'):
-        """Get the commits for the given file within the given branch."""
-        return self._parsed('/'.join(['commits', 'list', user, repo, branch, path]))
-
-    @with_temporary_mappings({'removed': _parseArray,
-                              'added': _parseArray,
-                              'modified': Modification,
-                              'diff': _string_parser,
-                              'filename': _string_parser})
-    def show(self, user, repo, sha):
-        """Get an individual commit."""
-        c = self._parsed('/'.join(['commits', 'show', user, repo, sha]))
-        # Some fixup due to weird XML structure
-        if hasattr(c, 'removed'):
-            c.removed = [i[0] for i in c.removed]
-        if hasattr(c, 'added'):
-            c.added = [i[0] for i in c.added]
-        return c
-
-class IssuesEndpoint(BaseEndpoint):
-
-    @with_temporary_mappings({'user': None})
-    def list(self, user, repo, state='open'):
-        """Get the list of issues for the given repo in the given state."""
-        return self._parsed('/'.join(['issues', 'list', user, repo, state]))
-
-    @with_temporary_mappings({'user': None})
-    def show(self, user, repo, issue_id):
-        """Show an individual issue."""
-        return self._parsed('/'.join(['issues', 'show', user, repo, str(issue_id)]))
-
-    def add_label(self, user, repo, issue_id, label):
-        """Add a label to an issue."""
-        self._post('issues/label/add/' + user + '/'
-                       + repo + '/' + label + '/' + str(issue_id))
-
-    def remove_label(self, user, repo, issue_id, label):
-        """Remove a label from an issue."""
-        self._post('issues/label/remove/' + user + '/'
-                   + repo + '/' + label + '/' + str(issue_id))
-
-    def close(self, user, repo, issue_id):
-        """Close an issue."""
-        self._post('/'.join(['issues', 'close', user, repo, str(issue_id)]))
-
-    def reopen(self, user, repo, issue_id):
-        """Reopen an issue."""
-        self._post('/'.join(['issues', 'reopen', user, repo, str(issue_id)]))
-
-    def new(self, user, repo, title, body=''):
-        """Create a new issue."""
-        return self._posted('/'.join(['issues', 'open', user, repo]),
-                            title=title, body=body)
-
-    def edit(self, user, repo, issue_id, title, body):
-        """Create a new issue."""
-        self._post('/'.join(['issues', 'edit', user, repo, str(issue_id)]),
-                   title=title, body=body)
-
-class ObjectsEndpoint(BaseEndpoint):
-
-    @with_temporary_mappings({'tree': Tree, 'type': _string_parser})
-    def tree(self, user, repo, t):
-        """Get the given tree from the given repo."""
-        tl = self._parsed('/'.join(['tree', 'show', user, repo, t]))
-        return dict([(t.name, t) for t in tl])
-
-    @with_temporary_mappings({'blob': Blob})
-    def blob(self, user, repo, t, fn):
-        return self._parsed('/'.join(['blob', 'show', user, repo, t, fn]))
-
-    def raw_blob(self, user, repo, sha):
-        """Get a raw blob from a repo."""
-        path = 'blob/show/%s/%s/%s' % (user, repo, sha)
-        return self._raw_fetch(path)
-
-class GitHub(object):
-    """Interface to github."""
-
-    def __init__(self, user=None, token=None, fetcher=default_fetcher):
-        self.user = user
-        self.token = token
-        self.fetcher = fetcher
-
-    @property
-    def users(self):
-        """Get access to the user API."""
-        return UserEndpoint(self.user, self.token, self.fetcher)
-
-    @property
-    def repos(self):
-        """Get access to the user API."""
-        return RepositoryEndpoint(self.user, self.token, self.fetcher)
-
-    @property
-    def commits(self):
-        return CommitEndpoint(self.user, self.token, self.fetcher)
-
-    @property
-    def issues(self):
-        return IssuesEndpoint(self.user, self.token, self.fetcher)
-
-    @property
-    def objects(self):
-        return ObjectsEndpoint(self.user, self.token, self.fetcher)
diff --git a/lib/pygithub/githubsync.py b/lib/pygithub/githubsync.py
deleted file mode 100644
index 50d70a076..000000000
--- a/lib/pygithub/githubsync.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2005-2008  Dustin Sallings <dustin@spy.net>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-#
-# <http://www.opensource.org/licenses/mit-license.php>
-"""
-Grab all of a user's projects from github.
-"""
-
-import os
-import sys
-import subprocess
-
-import github
-
-def check_for_old_format(path, url):
-    p = subprocess.Popen(['git', '--git-dir=' + path, 'config',
-        'remote.origin.fetch'], stdout = subprocess.PIPE)
-    stdout, stderr = p.communicate()
-    if stdout.strip() != '+refs/*:refs/*':
-        print "Not properly configured for mirroring, repairing."
-        subprocess.call(['git', '--git-dir=' + path, 'remote', 'rm', 'origin'])
-        add_mirror(path, url)
-
-def add_mirror(path, url):
-    subprocess.call(['git', '--git-dir=' + path, 'remote', 'add', '--mirror',
-            'origin', url])
-
-def sync(path, url, repo_name):
-    p = os.path.join(path, repo_name) + ".git"
-    print "Syncing %s -> %s" % (repo_name, p)
-    if not os.path.exists(p):
-        subprocess.call(['git', 'clone', '--bare', url, p])
-        add_mirror(p, url)
-    check_for_old_format(p, url)
-    subprocess.call(['git', '--git-dir=' + p, 'fetch', '-f'])
-
-def sync_user_repo(path, repo):
-    sync(path, "git://github.com/%s/%s" % (repo.owner, repo.name), repo.name)
-
-def usage():
-    sys.stderr.write("Usage:  %s username destination_url\n" % sys.argv[0])
-    sys.stderr.write(
-        """Ensures you've got the latest stuff for the given user.
-
-Also, if the file $HOME/.github-private exists, it will be read for
-additional projects.
-
-Each line must be a simple project name (e.g. py-github), a tab character,
-and a git URL.
-""")
-
-if __name__ == '__main__':
-    try:
-        user, path = sys.argv[1:]
-    except ValueError:
-        usage()
-        exit(1)
-
-    privfile = os.path.join(os.getenv("HOME"), ".github-private")
-    if os.path.exists(privfile):
-        f = open(privfile)
-        for line in f:
-            name, url = line.strip().split("\t")
-            sync(path, url, name)
-
-    gh = github.GitHub()
-
-    for repo in gh.repos.forUser(user):
-        sync_user_repo(path, repo)
diff --git a/lib/pygithub/githubtest.py b/lib/pygithub/githubtest.py
deleted file mode 100644
index bfb73cd89..000000000
--- a/lib/pygithub/githubtest.py
+++ /dev/null
@@ -1,493 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2005-2008  Dustin Sallings <dustin@spy.net>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-#
-# <http://www.opensource.org/licenses/mit-license.php>
-"""
-Defines and runs unittests.
-"""
-
-import urllib
-import hashlib
-import unittest
-
-import github
-
-class BaseCase(unittest.TestCase):
-
-    def _gh(self, expUrl, filename):
-
-        def opener(url):
-            self.assertEquals(expUrl, url)
-            return open(filename)
-        return github.GitHub(fetcher=opener)
-
-    def _agh(self, expUrl, u, t, filename):
-
-        def opener(url):
-            self.assertEquals(expUrl, url + '?login=' + u + '&token=' + t)
-            return open(filename)
-        return github.GitHub(fetcher=opener)
-
-    def _ghp(self, expUrl, u, t, **kv):
-
-        def opener(url, data):
-            h = {'login': u, 'token': t}
-            h.update(kv)
-            self.assertEquals(github.BaseEndpoint.BASE_URL + expUrl, url)
-            self.assertEquals(sorted(data.split('&')),
-                              sorted(urllib.urlencode(h).split('&')))
-        return github.GitHub(u, t, fetcher=opener)
-
-class UserTest(BaseCase):
-
-    def __loadUserSearch(self):
-        return self._gh('http://github.com/api/v2/xml/user/search/dustin',
-            'data/user.search.xml').users.search('dustin')
-
-    def __loadUser(self, which, u=None, p=None):
-        if u:
-            return self._agh('http://github.com/api/v2/xml/user/show/dustin'
-                              + '?login=' + u + '&token=' + p,
-                              u, p, 'data/' + which).users.show('dustin')
-
-        else:
-            return self._gh('http://github.com/api/v2/xml/user/show/dustin',
-                             'data/' + which).users.show('dustin')
-
-    def testUserSearch(self):
-        """Test the base properties of the user object."""
-        u = self.__loadUserSearch()[0]
-        self.assertEquals("Dustin Sallings", u.fullname)
-        self.assertEquals("dustin", u.name)
-        self.assertEquals("dustin@spy.net", u.email)
-        self.assertEquals("Santa Clara, CA", u.location)
-        self.assertEquals("Ruby", u.language)
-        self.assertEquals(35, u.actions)
-        self.assertEquals(77, u.repos)
-        self.assertEquals(78, u.followers)
-        self.assertEquals('user-1779', u.id)
-        self.assertAlmostEquals(12.231684, u.score)
-        self.assertEquals('user', u.type)
-        self.assertEquals('2008-02-29T17:59:09Z', u.created)
-        self.assertEquals('2009-03-19T09:15:24.663Z', u.pushed)
-        self.assertEquals("<<User dustin>>", repr(u))
-
-    def testUserPublic(self):
-        """Test the user show API with no authentication."""
-        u = self.__loadUser('user.public.xml')
-        self.assertEquals("Dustin Sallings", u.name)
-        # self.assertEquals(None, u.company)
-        self.assertEquals(10, u.following_count)
-        self.assertEquals(21, u.public_gist_count)
-        self.assertEquals(81, u.public_repo_count)
-        self.assertEquals('http://bleu.west.spy.net/~dustin/', u.blog)
-        self.assertEquals(1779, u.id)
-        self.assertEquals(82, u.followers_count)
-        self.assertEquals('dustin', u.login)
-        self.assertEquals('Santa Clara, CA', u.location)
-        self.assertEquals('dustin@spy.net', u.email)
-        self.assertEquals('2008-02-29T09:59:09-08:00', u.created_at)
-
-    def testUserPrivate(self):
-        """Test the user show API with extra info from auth."""
-        u = self.__loadUser('user.private.xml', 'dustin', 'blahblah')
-        self.assertEquals("Dustin Sallings", u.name)
-        # self.assertEquals(None, u.company)
-        self.assertEquals(10, u.following_count)
-        self.assertEquals(21, u.public_gist_count)
-        self.assertEquals(81, u.public_repo_count)
-        self.assertEquals('http://bleu.west.spy.net/~dustin/', u.blog)
-        self.assertEquals(1779, u.id)
-        self.assertEquals(82, u.followers_count)
-        self.assertEquals('dustin', u.login)
-        self.assertEquals('Santa Clara, CA', u.location)
-        self.assertEquals('dustin@spy.net', u.email)
-        self.assertEquals('2008-02-29T09:59:09-08:00', u.created_at)
-
-        # Begin private data
-
-        self.assertEquals("micro", u.plan.name)
-        self.assertEquals(1, u.plan.collaborators)
-        self.assertEquals(614400, u.plan.space)
-        self.assertEquals(5, u.plan.private_repos)
-        self.assertEquals(155191, u.disk_usage)
-        self.assertEquals(6, u.collaborators)
-        self.assertEquals(4, u.owned_private_repo_count)
-        self.assertEquals(5, u.total_private_repo_count)
-        self.assertEquals(0, u.private_gist_count)
-
-    def testKeysList(self):
-        """Test key listing."""
-        kl = self._agh('http://github.com/api/v2/xml/user/keys?login=dustin&token=blahblah',
-                       'dustin', 'blahblah', 'data/keys.xml').users.keys()
-        self.assertEquals(7, len(kl))
-        k = kl[0]
-
-        self.assertEquals('some key', k.title)
-        self.assertEquals(2181, k.id)
-        self.assertEquals(549, k.key.find('cdEXwCSjAIFp8iRqh3GOkxGyFSc25qv/MuOBg=='))
-
-    def testRemoveKey(self):
-        """Remove a key."""
-        self._ghp('user/key/remove',
-                  'dustin', 'p', id=828).users.removeKey(828)
-
-    def testAddKey(self):
-        """Add a key."""
-        self._ghp('user/key/add',
-                  'dustin', 'p', name='my key', key='some key').users.addKey(
-            'my key', 'some key')
-
-class RepoTest(BaseCase):
-
-    def __loadUserRepos(self):
-        return self._gh('http://github.com/api/v2/xml/repos/show/verbal',
-            'data/repos.xml').repos.forUser('verbal')
-
-    def testUserRepoList(self):
-        """Get a list of repos for a user."""
-        rs = self.__loadUserRepos()
-        self.assertEquals(10, len(rs))
-        r = rs[0]
-        self.assertEquals('A beanstalk client for the twisted network framework.',
-                          r.description)
-        self.assertEquals(2, r.watchers)
-        self.assertEquals(0, r.forks)
-        self.assertEquals('beanstalk-client-twisted', r.name)
-        self.assertEquals(False, r.private)
-        self.assertEquals('http://github.com/verbal/beanstalk-client-twisted',
-                          r.url)
-        self.assertEquals(True, r.fork)
-        self.assertEquals('verbal', r.owner)
-        # XXX:  Can't parse empty elements.  :(
-        # self.assertEquals('', r.homepage)
-
-    def testRepoSearch(self):
-        """Test searching a repository."""
-        rl = self._gh('http://github.com/api/v2/xml/repos/search/ruby+testing',
-                      'data/repos.search.xml').repos.search('ruby testing')
-        self.assertEquals(12, len(rl))
-
-        r = rl[0]
-        self.assertEquals('synthesis', r.name)
-        self.assertAlmostEquals(0.3234576, r.score, 4)
-        self.assertEquals(4656, r.actions)
-        self.assertEquals(2048, r.size)
-        self.assertEquals('Ruby', r.language)
-        self.assertEquals(26, r.followers)
-        self.assertEquals('gmalamid', r.username)
-        self.assertEquals('repo', r.type)
-        self.assertEquals('repo-3555', r.id)
-        self.assertEquals(1, r.forks)
-        self.assertFalse(r.fork)
-        self.assertEquals('Ruby test code analysis tool employing a '
-                          '"Synthesized Testing" strategy, aimed to reduce '
-                          'the volume of slower, coupled, complex wired tests.',
-                          r.description)
-        self.assertEquals('2009-01-08T13:45:06Z', r.pushed)
-        self.assertEquals('2008-03-11T23:38:04Z', r.created)
-
-    def testBranchList(self):
-        """Test branch listing for a repo."""
-        bl = self._gh('http://github.com/api/v2/xml/repos/show/schacon/ruby-git/branches',
-                      'data/repos.branches.xml').repos.branches('schacon', 'ruby-git')
-        self.assertEquals(4, len(bl))
-        self.assertEquals('ee90922f3da3f67ef19853a0759c1d09860fe3b3', bl['master'])
-
-    def testGetOneRepo(self):
-        """Fetch an individual repository."""
-        r = self._gh('http://github.com/api/v2/xml/repos/show/schacon/grit',
-                     'data/repo.xml').repos.show('schacon', 'grit')
-
-        self.assertEquals('Grit is a Ruby library for extracting information from a '
-                          'git repository in an object oriented manner - this fork '
-                          'tries to intergrate as much pure-ruby functionality as possible',
-                          r.description)
-        self.assertEquals(68, r.watchers)
-        self.assertEquals(4, r.forks)
-        self.assertEquals('grit', r.name)
-        self.assertFalse(r.private)
-        self.assertEquals('http://github.com/schacon/grit', r.url)
-        self.assertTrue(r.fork)
-        self.assertEquals('schacon', r.owner)
-        self.assertEquals('http://grit.rubyforge.org/', r.homepage)
-
-    def testGetRepoNetwork(self):
-        """Test network fetching."""
-        nl = self._gh('http://github.com/api/v2/xml/repos/show/dustin/py-github/network',
-                      'data/network.xml').repos.network('dustin', 'py-github')
-        self.assertEquals(5, len(nl))
-
-        n = nl[0]
-        self.assertEquals('Python interface for talking to the github API',
-                          n.description)
-        self.assertEquals('py-github', n.name)
-        self.assertFalse(n.private)
-        self.assertEquals('http://github.com/dustin/py-github', n.url)
-        self.assertEquals(30, n.watchers)
-        self.assertEquals(4, n.forks)
-        self.assertFalse(n.fork)
-        self.assertEquals('dustin', n.owner)
-        self.assertEquals('http://dustin.github.com/2008/12/29/github-sync.html',
-                          n.homepage)
-
-    def testSetPublic(self):
-        """Test setting a repo visible."""
-        self._ghp('repos/set/public/py-github', 'dustin', 'p').repos.setVisible(
-            'py-github')
-
-    def testSetPrivate(self):
-        """Test setting a repo to private."""
-        self._ghp('repos/set/private/py-github', 'dustin', 'p').repos.setVisible(
-            'py-github', False)
-
-    def testCreateRepository(self):
-        """Test creating a repository."""
-        self._ghp('repos/create', 'dustin', 'p',
-                  name='testrepo',
-                  description='woo',
-                  homepage='',
-                  public='1').repos.create(
-            'testrepo', description='woo')
-
-    def testDeleteRepo(self):
-        """Test setting a repo to private."""
-        self._ghp('repos/delete/mytest', 'dustin', 'p').repos.delete('mytest')
-
-    def testFork(self):
-        """Test forking'"""
-        self._ghp('repos/fork/someuser/somerepo', 'dustin', 'p').repos.fork(
-            'someuser', 'somerepo')
-
-    def testAddCollaborator(self):
-        """Adding a collaborator."""
-        self._ghp('repos/collaborators/memcached/add/trondn',
-                  'dustin', 'p').repos.addCollaborator('memcached', 'trondn')
-
-    def testRemoveCollaborator(self):
-        """Removing a collaborator."""
-        self._ghp('repos/collaborators/memcached/remove/trondn',
-                  'dustin', 'p').repos.removeCollaborator('memcached', 'trondn')
-
-    def testAddDeployKey(self):
-        """Add a deploy key."""
-        self._ghp('repos/key/blah/add', 'dustin', 'p',
-                  title='title', key='key').repos.addDeployKey('blah', 'title', 'key')
-
-    def testRemoveDeployKey(self):
-        """Remove a deploy key."""
-        self._ghp('repos/key/blah/remove', 'dustin', 'p',
-                  id=5).repos.removeDeployKey('blah', 5)
-
-class CommitTest(BaseCase):
-
-    def testCommitList(self):
-        """Test commit list."""
-        cl = self._gh('http://github.com/api/v2/xml/commits/list/mojombo/grit/master',
-                      'data/commits.xml').commits.forBranch('mojombo', 'grit')
-        self.assertEquals(30, len(cl))
-
-        c = cl[0]
-        self.assertEquals("Regenerated gemspec for version 1.1.1", c.message)
-        self.assertEquals('4ac4acab7fd9c7fd4c0e0f4ff5794b0347baecde', c.id)
-        self.assertEquals('94490563ebaf733cbb3de4ad659eb58178c2e574', c.tree)
-        self.assertEquals('2009-03-31T09:54:51-07:00', c.committed_date)
-        self.assertEquals('2009-03-31T09:54:51-07:00', c.authored_date)
-        self.assertEquals('http://github.com/mojombo/grit/commit/4ac4acab7fd9c7fd4c0e0f4ff5794b0347baecde',
-                          c.url)
-        self.assertEquals(1, len(c.parents))
-        self.assertEquals('5071bf9fbfb81778c456d62e111440fdc776f76c', c.parents[0].id)
-        self.assertEquals('Tom Preston-Werner', c.author.name)
-        self.assertEquals('tom@mojombo.com', c.author.email)
-        self.assertEquals('Tom Preston-Werner', c.committer.name)
-        self.assertEquals('tom@mojombo.com', c.committer.email)
-
-    def testCommitListForFile(self):
-        """Test commit list for a file."""
-        cl = self._gh('http://github.com/api/v2/xml/commits/list/mojombo/grit/master/grit.gemspec',
-                      'data/commits.xml').commits.forFile('mojombo', 'grit', 'grit.gemspec')
-        self.assertEquals(30, len(cl))
-
-        c = cl[0]
-        self.assertEquals("Regenerated gemspec for version 1.1.1", c.message)
-        self.assertEquals('4ac4acab7fd9c7fd4c0e0f4ff5794b0347baecde', c.id)
-        self.assertEquals('94490563ebaf733cbb3de4ad659eb58178c2e574', c.tree)
-        self.assertEquals('2009-03-31T09:54:51-07:00', c.committed_date)
-        self.assertEquals('2009-03-31T09:54:51-07:00', c.authored_date)
-        self.assertEquals('http://github.com/mojombo/grit/commit/4ac4acab7fd9c7fd4c0e0f4ff5794b0347baecde',
-                          c.url)
-        self.assertEquals(1, len(c.parents))
-        self.assertEquals('5071bf9fbfb81778c456d62e111440fdc776f76c', c.parents[0].id)
-        self.assertEquals('Tom Preston-Werner', c.author.name)
-        self.assertEquals('tom@mojombo.com', c.author.email)
-        self.assertEquals('Tom Preston-Werner', c.committer.name)
-        self.assertEquals('tom@mojombo.com', c.committer.email)
-
-    def testIndividualCommit(self):
-        """Grab a single commit."""
-        h = '4c86fa592fcc7cb685c6e9d8b6aebe8dcbac6b3e'
-        c = self._gh('http://github.com/api/v2/xml/commits/show/dustin/memcached/' + h,
-                     'data/commit.xml').commits.show('dustin', 'memcached', h)
-        self.assertEquals(['internal_tests.c'], c.removed)
-        self.assertEquals(set(['cache.c', 'cache.h', 'testapp.c']), set(c.added))
-        self.assertEquals('Create a generic cache for objects of same size\n\n'
-                          'The suffix pool could be thread-local and use the generic cache',
-                          c.message)
-
-        self.assertEquals(6, len(c.modified))
-        self.assertEquals('.gitignore', c.modified[0].filename)
-        self.assertEquals(140, len(c.modified[0].diff))
-
-        self.assertEquals(['ee0c3d5ae74d0862b4d9990e2ad13bc79f8c34df'],
-                          [p.id for p in c.parents])
-        self.assertEquals('http://github.com/dustin/memcached/commit/' + h, c.url)
-        self.assertEquals('Trond Norbye', c.author.name)
-        self.assertEquals('Trond.Norbye@sun.com', c.author.email)
-        self.assertEquals(h, c.id)
-        self.assertEquals('2009-04-17T16:15:52-07:00', c.committed_date)
-        self.assertEquals('2009-03-27T10:30:16-07:00', c.authored_date)
-        self.assertEquals('94b644163f6381a9930e2d7c583fae023895b903', c.tree)
-        self.assertEquals('Dustin Sallings', c.committer.name)
-        self.assertEquals('dustin@spy.net', c.committer.email)
-
-    def testWatchRepo(self):
-        """Test watching a repo."""
-        self._ghp('repos/watch/dustin/py-github', 'dustin', 'p').repos.watch(
-            'dustin', 'py-github')
-
-    def testWatchRepo(self):
-        """Test watching a repo."""
-        self._ghp('repos/unwatch/dustin/py-github', 'dustin', 'p').repos.unwatch(
-            'dustin', 'py-github')
-
-class IssueTest(BaseCase):
-
-    def testListIssues(self):
-        """Test listing issues."""
-        il = self._gh('http://github.com/api/v2/xml/issues/list/schacon/simplegit/open',
-                      'data/issues.list.xml').issues.list('schacon', 'simplegit')
-        self.assertEquals(1, len(il))
-        i = il[0]
-
-        self.assertEquals('schacon', i.user)
-        self.assertEquals('2009-04-17T16:19:02-07:00', i.updated_at)
-        self.assertEquals('something', i.body)
-        self.assertEquals('new', i.title)
-        self.assertEquals(2, i.number)
-        self.assertEquals(0, i.votes)
-        self.assertEquals(1.0, i.position)
-        self.assertEquals('2009-04-17T16:18:50-07:00', i.created_at)
-        self.assertEquals('open', i.state)
-
-    def testShowIssue(self):
-        """Show an individual issue."""
-        i = self._gh('http://github.com/api/v2/xml/issues/show/dustin/py-github/1',
-                     'data/issues.show.xml').issues.show('dustin', 'py-github', 1)
-
-        self.assertEquals('dustin', i.user)
-        self.assertEquals('2009-04-17T18:37:04-07:00', i.updated_at)
-        self.assertEquals('http://develop.github.com/p/general.html', i.body)
-        self.assertEquals('Add auth tokens', i.title)
-        self.assertEquals(1, i.number)
-        self.assertEquals(0, i.votes)
-        self.assertEquals(1.0, i.position)
-        self.assertEquals('2009-04-17T17:00:58-07:00', i.created_at)
-        self.assertEquals('closed', i.state)
-
-    def testAddLabel(self):
-        """Adding a label to an issue."""
-        self._ghp('issues/label/add/dustin/py-github/todo/33', 'd', 'pw').issues.add_label(
-            'dustin', 'py-github', 33, 'todo')
-
-    def testRemoveLabel(self):
-        """Removing a label from an issue."""
-        self._ghp('issues/label/remove/dustin/py-github/todo/33',
-                  'd', 'pw').issues.remove_label(
-            'dustin', 'py-github', 33, 'todo')
-
-    def testCloseIssue(self):
-        """Closing an issue."""
-        self._ghp('issues/close/dustin/py-github/1', 'd', 'pw').issues.close(
-            'dustin', 'py-github', 1)
-
-    def testReopenIssue(self):
-        """Reopening an issue."""
-        self._ghp('issues/reopen/dustin/py-github/1', 'd', 'pw').issues.reopen(
-            'dustin', 'py-github', 1)
-
-    def testCreateIssue(self):
-        """Creating an issue."""
-        self._ghp('issues/open/dustin/py-github', 'd', 'pw',
-                  title='test title', body='').issues.new(
-            'dustin', 'py-github', title='test title')
-
-    def testEditIssue(self):
-        """Editing an existing issue."""
-        self._ghp('issues/edit/dustin/py-github/1', 'd', 'pw',
-                  title='new title', body='new body').issues.edit(
-            'dustin', 'py-github', 1, 'new title', 'new body')
-
-class ObjectTest(BaseCase):
-
-    def testTree(self):
-        """Test tree fetching."""
-        h = '1ddd3f99f0b96019042239375b3ad4d45796ffba'
-        tl = self._gh('http://github.com/api/v2/xml/tree/show/dustin/py-github/' + h,
-                      'data/tree.xml').objects.tree('dustin', 'py-github', h)
-        self.assertEquals(8, len(tl))
-        self.assertEquals('setup.py', tl['setup.py'].name)
-        self.assertEquals('6e290379ec58fa00ac9d1c2a78f0819a21397445',
-                          tl['setup.py'].sha)
-        self.assertEquals('100755', tl['setup.py'].mode)
-        self.assertEquals('blob', tl['setup.py'].type)
-
-        self.assertEquals('src', tl['src'].name)
-        self.assertEquals('5fb9175803334c82b3fd66f1b69502691b91cf4f',
-                          tl['src'].sha)
-        self.assertEquals('040000', tl['src'].mode)
-        self.assertEquals('tree', tl['src'].type)
-
-    def testBlob(self):
-        """Test blob fetching."""
-        h = '1ddd3f99f0b96019042239375b3ad4d45796ffba'
-        blob = self._gh('http://github.com/api/v2/xml/blob/show/dustin/py-github/'
-                        + h + '/setup.py',
-                        'data/blob.xml').objects.blob('dustin', 'py-github', h, 'setup.py')
-        self.assertEquals('setup.py', blob.name)
-        self.assertEquals(1842, blob.size)
-        self.assertEquals('6e290379ec58fa00ac9d1c2a78f0819a21397445', blob.sha)
-        self.assertEquals('100755', blob.mode)
-        self.assertEquals('text/plain', blob.mime_type)
-        self.assertEquals(1842, len(blob.data))
-        self.assertEquals(1641, blob.data.index('Production/Stable'))
-
-    def testRawBlob(self):
-        """Test raw blob fetching."""
-        h = '6e290379ec58fa00ac9d1c2a78f0819a21397445'
-        blob = self._gh('http://github.com/api/v2/xml/blob/show/dustin/py-github/' + h,
-                        'data/setup.py').objects.raw_blob('dustin', 'py-github', h)
-        self.assertEquals('e2dc8aea9ae8961f4f5923f9febfdd0a',
-                          hashlib.md5(blob).hexdigest())
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/sickbeard/gh_api.py b/sickbeard/gh_api.py
new file mode 100644
index 000000000..32bb9cec8
--- /dev/null
+++ b/sickbeard/gh_api.py
@@ -0,0 +1,59 @@
+# Author: Nic Wolfe <nic@wolfeden.ca>
+# URL: http://code.google.com/p/sickbeard/
+#
+# This file is part of Sick Beard.
+#
+# Sick Beard is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Sick Beard is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Sick Beard.  If not, see <http://www.gnu.org/licenses/>.
+
+try:
+    import json
+except ImportError:
+    from lib import simplejson as json
+
+import urllib
+
+class GitHub(object):
+    """
+    Simple api wrapper for the Github API v3. Currently only supports the small thing that SB
+    needs it for - list of cimmots.
+    """
+    
+    def _access_API(self, path, params=None):
+        """
+        Access the API at the path given and with the optional params given.
+        
+        path: A list of the path elements to use (eg. ['repos', 'midgetspy', 'Sick-Beard', 'commits'])
+        params: Optional dict of name/value pairs for extra params to send. (eg. {'per_page': 10})
+        
+        Returns a deserialized json object of the result. Doesn't do any error checking (hope it works).
+        """
+        
+        url = 'https://api.github.com/' + '/'.join(path)
+        
+        if params and type(params) is dict:
+            url += '?' + '&'.join([str(x) + '=' + str(params[x]) for x in params.keys()])
+        
+        return json.load(urllib.urlopen(url)) 
+    
+    def commits(self, user, repo, branch='master'):
+        """
+        Uses the API to get a list of the 100 most recent commits from the specified user/repo/branch, starting from HEAD.
+        
+        user: The github username of the person whose repo you're querying
+        repo: The repo name to query
+        branch: Optional, the branch name to show commits from
+        
+        Returns a deserialized json object containing the commit info. See http://developer.github.com/v3/repos/commits/
+        """
+        return self._access_API(['repos', user, repo, 'commits'], {'per_page': 100, 'branch': branch})
diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py
index f4f040f80..a0ef58b1a 100644
--- a/sickbeard/versionChecker.py
+++ b/sickbeard/versionChecker.py
@@ -28,7 +28,7 @@ import urllib, urllib2
 import zipfile, tarfile
 
 from urllib2 import URLError
-from lib.pygithub import github
+import gh_api as github
 
 class CheckVersion():
     """
@@ -297,13 +297,13 @@ class GitUpdateManager(UpdateManager):
         gh = github.GitHub()
 
         # find newest commit
-        for curCommit in gh.commits.forBranch('midgetspy', 'Sick-Beard', version.SICKBEARD_VERSION):
+        for curCommit in gh.commits('midgetspy', 'Sick-Beard', version.SICKBEARD_VERSION):
             if not self._newest_commit_hash:
-                self._newest_commit_hash = curCommit.id
+                self._newest_commit_hash = curCommit['sha']
                 if not self._cur_commit_hash:
                     break
 
-            if curCommit.id == self._cur_commit_hash:
+            if curCommit['sha'] == self._cur_commit_hash:
                 break
 
             self._num_commits_behind += 1
-- 
GitLab