Пример #1
0
class GithubPlugin(Component):
    implements(IRequestHandler)
    
    def __init__(self):
        self.hook = CommitHook(self.env)
    
    # IRequestHandler methods
    def match_request(self, req):
        return req.path_info.rstrip('/') == ('/github/%s' % GITHUB_KEY) and req.method == 'POST'
    
    def process_request(self, req):
        try:
            data = json.read(req.read())

            for sha1, commit in data['commits'].items():
                self.hook.process(commit)
            req.send_response(200)
            req.send_header('Content-Type', 'text/plain')
            req.end_headers()
            req.write('Hello world!')
        except json.ReadException, e:
            req.send_response(400)
            req.send_header('Content-type', 'text/plain')
            req.end_headers()
            req.write(e.message)
Пример #2
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter)
    
    
    key = Option('github', 'apitoken', '', doc="""Your GitHub API Token found here: https://github.com/account, """)
    closestatus = Option('github', 'closestatus', '', doc="""This is the status used to close a ticket. It defaults to closed.""")
    username = Option('github', 'username', '', doc="""your github user or organisation name""")
    autofetch = Option('github', 'autofetch', '', doc="""Should we auto fetch the repo when we get a commit hook from GitHub.""")
    repo = Option('trac', 'repository_dir' '', doc="""This is your repository dir""")
    gitreposdir = Option('github', 'gitreposdir' '', doc="""This is the path where all your git repo are""")

    def __init__(self):
        self.hook = CommitHook(self.env)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Username: %s" % self.username)
        self.processHook = False

    
    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip('/') == ('/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve
    
    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)
        
        req.send_response(204)
        req.send_header('Content-Length', 0)
        req.write('')
        raise RequestDone

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
	if not self.username:
		return handler

	isBrowser = req.path_info.startswith('/browser/')
	if isBrowser: 
		uri = req.path_info.split('/');
		projectName = uri[2]
		repoType = "%s" % self.env.get_repository(projectName)
		isGitRepo = False
		if repoType.find('Git') != -1:
			isGitRepo = True
		self.env.log.debug("Handle Pre-Request /browser: %s" % isGitRepo)
		if isGitRepo:
			self.processBrowserURL(req, projectName)

	isChangeset = req.path_info.startswith('/changeset')
	self.env.log.debug("REQUEST %s" % req.path_info)
	if isChangeset:	    
		info = req.path_info.split('/');
		rev=info[2]
		projectName=info[3]
		self.env.log.debug("Handle Pre-Request /changeset: %s" % info)
		if not rev.isdigit(): # digit = svn, not digit = sha1 = git
			self.processChangesetURL(req, projectName, rev)

        return handler


    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)


    def processChangesetURL(self, req, projectName, rev):
        self.env.log.debug("processChangesetURL")
        
        url = req.path_info.replace('/changeset/', '')
        if not url:
            url = ''

        redirect = 'https://github.com/%s/%s/commit/%s' % (self.username, projectName, rev)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)


    def processBrowserURL(self, req, projectName):
        self.env.log.debug("processBrowserURL")
        rev = req.args.get('rev')
        self.env.log.debug("rev %s" % rev)
        url = req.path_info.replace('/browser', '')
        if not rev:
            rev = ''
        self.env.log.debug("url %s" % url)
        redirect = 'https://github.com/%s/%s' % (self.username, projectName)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)
        

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'
	
        data = req.args.get('payload')
	jsondata = simplejson.loads(data)
		
	repoName = jsondata['repository']['name']

        if self.autofetch:
	    if data:
	    	jsondata = simplejson.loads(data)
		self.env.log.debug(jsondata['repository']['name']);
	        repo = Git(self.gitreposdir+repoName+"/.git")

            try:
              self.env.log.debug("Fetching repo %s" % self.repo)
              repo.execute(['git', 'fetch'])
              try:
                self.env.log.debug("Resyncing local repo")
                self.env.get_repository(repoName).sync()
              except:
                self.env.log.error("git sync failed!")
            except:
              self.env.log.error("git fetch failed!")

        jsondata = simplejson.loads(data)

         
        if jsondata:
            if jsondata['ref'] == "refs/heads/master" or re.search('-stable$', jsondata['ref']):
                for i in jsondata['commits']:
                    self.hook.process(i, status)
Пример #3
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter, IEnvironmentSetupParticipant,
               IWikiSyntaxProvider)

    key = Option(
        'github',
        'apitoken',
        '',
        doc="""Your GitHub API Token found here: https://github.com/account, """
    )
    closestatus = Option(
        'github',
        'closestatus',
        '',
        doc=
        """This is the status used to close a ticket. It defaults to closed."""
    )
    browser = Option(
        'github',
        'browser',
        '',
        doc=
        """Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub."""
    )
    autofetch = Option(
        'github',
        'autofetch',
        '',
        doc=
        """Should we auto fetch the repo when we get a commit hook from GitHub."""
    )
    # TODO: Removed following line, obsolete
    # repo = Option('trac', 'repository_dir' '', doc="""This is your repository dir""")
    revmap = Option(
        'github',
        'svn_revmap',
        '',
        doc="""a plaintext file mapping svn revisions to git hashes""")
    enable_revmap = Option(
        'github',
        'enable_revmap',
        0,
        doc=
        """use the svn->git map when a request looks like a svn changeset """)
    long_tooltips = Option('github',
                           'long_tooltips',
                           0,
                           doc="""don't shorten tooltips""")

    SCHEMA = [
        Table('svn_revmap',
              key=('svn_rev', 'git_hash'))[Column('svn_rev', type='int'),
                                           Column('git_hash'),
                                           Column('commit_msg'),
                                           Index(['svn_rev', 'git_hash']), ]
    ]

    def __init__(self):
        self.hook = CommitHook(self.env)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.processHook = False

    # IEnvironmentSetupParticpant methods
    def environment_created(self):
        if int(self.enable_revmap):
            self._upgrade_db(self.env.get_db_cnx())

    #return true if the db table doesn't exist or needs to be updated
    def environment_needs_upgrade(self, db):
        if int(self.enable_revmap) == 0:
            return False
        cursor = db.cursor()
        try:
            cursor.execute("SELECT COUNT(*) FROM svn_revmap")
            row = cursor.fetchone()
            #if there's one or more rows, assume everything's ok
            if row[0] > 0:
                return False
            return True
        except Exception:
            db.rollback()
            return True

    def upgrade_environment(self, db):
        if int(self.enable_revmap):
            self._upgrade_db(db)

    def _upgrade_db(self, db):
        #open the revision map
        if int(self.enable_revmap) == 0:
            return 0
        try:
            revmap_fd = open(self.revmap, 'rb')
        except IOError:
            raise ResourceNotFound(
                _("revision map '%(revmap)s' not found", revmap=self.revmap))
        cursor = db.cursor()
        try:
            cursor.execute("DROP TABLE svn_revmap;")
        except Exception:
            db.rollback()

        db_backend, _unused = DatabaseManager(self.env)._get_connector()
        cursor = db.cursor()
        for table in self.SCHEMA:
            for stmt in db_backend.to_sql(table):
                self.env.log.debug(stmt)
                cursor.execute(stmt)

        insert_count = 0
        prev_rev = 0
        git_hash = revmap_fd.readline()[0:-1]
        while 1:
            #make sure this line is the hash
            if not re.match(r'[0-9a-f]{40}', git_hash):
                raise Exception("expecting hash, found '%s'" % git_hash)
            line = revmap_fd.readline()[0:-1]

            if line.startswith('git-svn-id:'):
                commit_msg = '<no commit message>'
            else:
                #slurp lines into the commit messsages until there's a blank line, a line starting with git-svn-id or a hash
                commit_msg = ''
                while not re.match(
                        r'[0-9a-f]{40}',
                        line) and not line.startswith('git-svn-id:'):
                    if len(line) > 0:
                        if not commit_msg:
                            commit_msg = line
                        else:
                            commit_msg = commit_msg + " " + line
                    line = revmap_fd.readline()[0:-1]

            if not line.startswith('git-svn-id:'):
                raise Exception("expected git-svn-id, got '%s'" % line)

            svn_rev_match = re.match(r'^git-svn-id:.*@(\d+) ', line)
            svn_rev = int(svn_rev_match.group(1))

            insert_query = "INSERT INTO svn_revmap (svn_rev, git_hash, commit_msg) VALUES (%s, %s, %s);"
            self.env.log.debug(insert_query % (svn_rev, git_hash, commit_msg))
            cursor.execute(insert_query,
                           (svn_rev, git_hash, commit_msg.decode('utf-8')))

            if prev_rev - 1 != svn_rev:
                self.env.log.debug("found a gap between r%d and r%d" %
                                   (prev_rev, svn_rev))
            prev_rev = svn_rev

            insert_count += 1
            if svn_rev == 1:
                break
            git_hash = revmap_fd.readline()[0:-1]
            while len(git_hash) == 0:
                git_hash = revmap_fd.readline()[0:-1]

        self.env.log.debug("inserted %d mappings into svn_revmap" %
                           insert_count)

    # IWikiSyntaxProvider methods
    def get_wiki_syntax(self):
        yield (
            r"\br[1-9]\d*\b",  #svn revision links ("r1432")
            lambda formatter, ns, match: self._format_changeset_link(
                formatter, ns, match))
        yield (
            r"\b[0-9a-fA-F]{5,40}\b",  #git hashes ("eb390eca04394")
            lambda formatter, ns, match: self._format_changeset_link(
                formatter, ns, match))

    #pre_process_request deals with link resolution
    def get_link_resolvers(self):
        return []

    def _format_changeset_link(self, formatter, ns, match):
        self.env.log.debug("format changeset link")
        if int(self.enable_revmap) == 0:
            self.env.log.debug("revmap disabled, skipping thingy")
            return match.group(0)
        self.env.log.debug("revmap enabled: formatting links")
        commit_data = self._get_commit_data(match.group(0))
        if len(commit_data) == 1:
            self.env.log.debug(commit_data)
            if int(self.long_tooltips):
                title = commit_data[0]['msg']
            else:
                title = shorten_line(commit_data[0]['msg'])
            return tag.a(match.group(0),
                         href="%s/%s" %
                         (formatter.href.changeset(), commit_data[0]['id']),
                         title=title,
                         class_="changeset")
        elif len(commit_data) > 1:
            #try to figure out something better when an id is ambiguous
            return match.group(0)

        return match.group(0)

    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip('/') == (
            '/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve

    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)
        # TODO: Verify this code (and redirect in hook.py also)
        req.send_response(204)
        req.send_header('Content-Length', 0)
        req.write('')
        raise RequestDone

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith('/browser')
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith('/changeset')
            try:
                repoinfo = req.path_info.replace('/changeset/',
                                                 '').partition("/")
            except AttributeError:
                repoinfo = req.path_info.replace('/changeset/', '')
                partition = repoinfo.split('/')
                repoinfo = [partition[0]]
                if len(partition) > 1:
                    repoinfo.append('/')
                    repoinfo.append('/'.join(partition[1:]))
                else:
                    repoinfo.append('')
                    repoinfo.append('')
            repo = self.env.get_repository(repoinfo[2])
            if repo.__class__.__name__ == "GitRepository":
                self.env.log.debug("Handle Pre-Request /changeset: %s" %
                                   serve2)
            if serve2:
                self.processChangesetURL(req)

        return handler

    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)

    def _get_commit_data(self, commit_id):
        if int(self.enable_revmap) == 0:
            return False
        self.env.log.debug("looking up commit: %s" % commit_id)
        cursor = self.env.get_db_cnx().cursor()
        if commit_id.startswith('r'):
            commit_id = commit_id[1:]
            self.env.log.debug(
                "running query: SELECT git_hash, commit_msg FROM svn_revmap WHERE svn_rev = %s"
                % commit_id)
            cursor.execute(
                "SELECT git_hash, commit_msg FROM svn_revmap WHERE svn_rev = %s",
                (commit_id, ))
            rows = cursor.fetchmany(5)
        else:
            self.env.log.debug(
                "running query: SELECT git_hash, commit_msg FROM svn_revmap WHERE git_hash LIKE '%s%%'"
                % commit_id)
            cursor.execute(
                "SELECT git_hash, commit_msg FROM svn_revmap WHERE git_hash LIKE '%s%%'"
                % (commit_id, ))
            rows = cursor.fetchmany(5)
        results = []
        for row in rows:
            #hash is what's in the db, id is the string the user used (usually not the full hash)
            d = {
                'hash': row[0],
                'msg': row[1],
                'id': commit_id,
            }
            results.append(d)
        return results

    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace('/tree/master', '/commit/')

        try:
            commitinfo = req.path_info.replace('/changeset/',
                                               '').partition("/")
        except AttributeError:
            commitinfo = req.path_info.replace('/changeset/', '')
            partition = commitinfo.split('/')
            commitinfo = [partition[0]]
            if len(partition) > 1:
                commitinfo.append('/')
                commitinfo.append('/'.join(partition[1:]))
            else:
                commitinfo.append('')
                commitinfo.append('')

        url = "/%s" % (commitinfo[2] + commitinfo[1] + "commit" +
                       commitinfo[1] + commitinfo[0])
        if not url:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        _out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        rev = req.args.get('rev')
        if rev:
            browser = self.browser.replace('/master', '/')
        else:
            rev = ''
            browser = self.browser

        url = req.path_info.replace('/browser', '')

        redirect = '%s%s%s' % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        _out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'

        if self.autofetch:
            repodir = RepositoryManager(self.env).repository_dir
            if not os.path.isabs(repodir):
                repodir = os.path.join(self.env.path, repodir)
            # TODO: This was the previous code, the repo options is probably unecessary now.
            # repodir = "%s/%s" % (self.repo, reponame)
            self.env.log.debug("Autofetching: %s" % repodir)
            repo = Git(repodir)

            try:
                self.env.log.debug("Fetching repo %s" % self.repo)
                repo.execute(['git', 'fetch'])
                try:
                    self.env.log.debug("Resyncing local repo")
                    self.env.get_repository('').sync()
                except Exception:
                    self.env.log.error("git sync failed!")
            except Exception:
                self.env.log.error("git fetch failed!")

        data = req.args.get('payload')

        if data:
            jsondata = simplejson.loads(data)
            reponame = jsondata['repository']['name']

            for i in jsondata['commits']:
                self.hook.process(i, status, self.enable_revmap, reponame)

        self.env.log.debug("Redirect URL: %s" % req)
        req.redirect(self.browser)
Пример #4
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter)
    
    
    key = Option('github', 'apitoken', '', doc="""Your GitHub API Token found here: https://github.com/account, """)
    closestatus = Option('github', 'closestatus', '', doc="""This is the status used to close a ticket. It defaults to closed.""")
    browser = Option('github', 'browser', '', doc="""Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub.""")
    autofetch = Option('github', 'autofetch', '', doc="""Should we auto fetch the repo when we get a commit hook from GitHub.""")
    branches = Option('github', 'branches', "all", doc="""Restrict commit hook to these branches. """
        """Defaults to special value 'all', do not restrict commit hook""")
    comment_template = Option('github', 'comment_template', "Changeset: {commit[id]}", doc="""This will be appended to your commit message and used as trac comment""")
    repo = Option('trac', 'repository_dir', '', doc="""This is your repository dir""")

    def __init__(self):
        self.hook = CommitHook(self.env, self.comment_template)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.processHook = False

    
    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip('/') == ('/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve
    
    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith('/browser')
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith('/changeset')
            self.env.log.debug("Handle Pre-Request /changeset: %s" % serve2)
            if serve2:
                self.processChangesetURL(req)

        return handler


    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)


    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace('/tree/master', '/commit/')
        
        url = req.path_info.replace('/changeset/', '')
        if not url:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)


    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        browser = self.browser.replace('/master', '/')
        rev = req.args.get('rev')
        
        url = req.path_info.replace('/browser', '')
        if not rev:
            rev = ''

        redirect = '%s%s%s' % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

        

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'

        data = req.args.get('payload')
        branches = (parse_qs(req.query_string).get('branches') or self.branches).split(',')
        self.env.log.debug("Using branches: %s", branches)

        if data:
            jsondata = simplejson.loads(data)
            ref = jsondata['ref'].split('/')[-1]

            if ref in branches or 'all' in branches:
                for i in jsondata['commits']:
                    self.hook.process(i, status, jsondata)
            else:
                self.env.log.debug("Not running hook, ref %s is not in %s", ref, branches)

        if self.autofetch:
            repo = Git(self.repo)

            try:
              repo.execute(['git', 'fetch'])
            except:
              self.env.log.debug("git fetch failed!")
Пример #5
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter)

    key = Option(
        'github',
        'apitoken',
        '',
        doc="""Your GitHub API Token found here: https://github.com/account, """
    )
    closestatus = Option(
        'github',
        'closestatus',
        '',
        doc=
        """This is the status used to close a ticket. It defaults to closed."""
    )
    browser = Option(
        'github',
        'browser',
        '',
        doc=
        """Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub."""
    )
    autofetch = Option(
        'github',
        'autofetch',
        '',
        doc=
        """Should we auto fetch the repo when we get a commit hook from GitHub."""
    )
    branches = Option(
        'github',
        'branches',
        "all",
        doc="""Restrict commit hook to these branches. """
        """Defaults to special value 'all', do not restrict commit hook""")
    comment_template = Option(
        'github',
        'comment_template',
        "Changeset: {commit[id]}",
        doc=
        """This will be appended to your commit message and used as trac comment"""
    )
    repo = Option('trac',
                  'repository_dir',
                  '',
                  doc="""This is your repository dir""")

    def __init__(self):
        self.hook = CommitHook(self.env, self.comment_template)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.processHook = False
        self.env.log.debug("Match Request")

    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request: %s, key: %s" %
                           (req.path_info, self.key))
        serve = req.path_info.rstrip('/') == (
            '/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve

    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith('/browser')
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith('/changeset')
            self.env.log.debug("Handle Pre-Request /changeset: %s" % serve2)
            if serve2:
                self.processChangesetURL(req)

        return handler

    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)

    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace('/tree/master', '/commit/')

        url = req.path_info.replace('/changeset/', '')
        if not url:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        browser = self.browser.replace('/master', '/')
        rev = req.args.get('rev')

        url = req.path_info.replace('/browser', '')
        if not rev:
            rev = ''

        redirect = '%s%s%s' % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

    def parse_query_string(self, query_string):
        """Parse a query string into a _RequestArgs."""
        args = _RequestArgs()
        for arg in query_string.split('&'):
            nv = arg.split('=', 1)
            if len(nv) == 2:
                (name, value) = nv
            else:
                (name, value) = (nv[0], '')
            name = urllib2.unquote(name.replace('+', ' '))
            if isinstance(name, unicode):
                name = name.encode('utf-8')
            value = urllib2.unquote(value.replace('+', ' '))
            if not isinstance(value, unicode):
                value = unicode(value, 'utf-8')
            if name in args:
                if isinstance(args[name], list):
                    args[name].append(value)
                else:
                    args[name] = [args[name], value]
            else:
                args[name] = value
        return args

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'

        data = req.args.get('payload')
        branches = (self.parse_query_string(req.query_string).get('branches')
                    or self.branches).split(',')
        self.env.log.debug("Using branches: %s", branches)

        if data:
            jsondata = simplejson.loads(data)
            ref = jsondata['ref'].split('/')[-1]

            if ref in branches or 'all' in branches:
                for i in jsondata['commits']:
                    self.hook.process(i, status, jsondata)
            else:
                self.env.log.debug("Not running hook, ref %s is not in %s",
                                   ref, branches)

        if self.autofetch:
            repo = Git(self.repo)

            try:
                repo.execute(['git', 'fetch'])
            except:
                self.env.log.debug("git fetch failed!")
Пример #6
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter)

    key = Option("github", "apitoken", "", doc="""Your GitHub API Token found here: https://github.com/account, """)
    closestatus = Option(
        "github", "closestatus", "", doc="""This is the status used to close a ticket. It defaults to closed."""
    )
    browser = Option(
        "github",
        "browser",
        "",
        doc="""Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub.""",
    )
    autofetch = Option(
        "github", "autofetch", "", doc="""Should we auto fetch the repo when we get a commit hook from GitHub."""
    )
    repo = Option("trac", "repository_dir" "", doc="""This is your repository dir""")

    def __init__(self):
        self.hook = CommitHook(self.env)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.processHook = False

    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip("/") == ("/github/%s" % self.key) and req.method == "POST"
        if serve:
            self.processHook = True
            # This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve

    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith("/browser")
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith("/changeset")
            self.env.log.debug("Handle Pre-Request /changeset: %s" % serve2)
            if serve2:
                self.processChangesetURL(req)

        return handler

    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)

    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace("/tree/master", "/commit/")

        url = req.path_info.replace("/changeset/", "")
        if not url:
            browser = self.browser
            url = ""

        redirect = "%s%s" % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = "Going to GitHub: %s" % redirect

        req.redirect(redirect)

    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        browser = self.browser.replace("/master", "/")
        rev = req.args.get("rev")

        url = req.path_info.replace("/browser", "")
        if not rev:
            rev = ""

        redirect = "%s%s%s" % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = "Going to GitHub: %s" % redirect

        req.redirect(redirect)

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = "closed"

        data = req.args.get("payload")

        if data:
            jsondata = simplejson.loads(data)

            branch = jsondata["ref"][11:]
            #            if (branch == 'master') or branch.startswith('fixes/'):
            for i in jsondata["commits"]:
                self.hook.process(i, status, branch)

        if self.autofetch:
            repo = Git(self.repo)

            try:
                repo.execute(["git", "fetch"])
            except:
                self.env.log.debug("git fetch failed!")
Пример #7
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter, IEnvironmentSetupParticipant,
            IWikiSyntaxProvider)
    
    
    key = Option('github', 'apitoken', '', doc="""Your GitHub API Token found here: https://github.com/account, """)
    closestatus = Option('github', 'closestatus', '', doc="""This is the status used to close a ticket. It defaults to closed.""")
    browser = Option('github', 'browser', '', doc="""Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub.""")
    autofetch = Option('github', 'autofetch', '', doc="""Should we auto fetch the repo when we get a commit hook from GitHub.""")
    # TODO: Removed following line, obsolete
	# repo = Option('trac', 'repository_dir' '', doc="""This is your repository dir""")
    revmap        = Option('github', 'svn_revmap',    '', doc = """a plaintext file mapping svn revisions to git hashes""")
    enable_revmap = Option('github', 'enable_revmap',  0, doc = """use the svn->git map when a request looks like a svn changeset """)
    long_tooltips = Option('github', 'long_tooltips',  0, doc = """don't shorten tooltips""")

    SCHEMA = [
            Table('svn_revmap', key = ('svn_rev', 'git_hash'))[
                Column('svn_rev', type='int'),
                Column('git_hash'),
                Column('commit_msg'),
                Index(['svn_rev', 'git_hash']),
                ]
            ]


    def __init__(self):
        self.hook = CommitHook(self.env)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.processHook = False

    # IEnvironmentSetupParticpant methods
    def environment_created(self):
        if int(self.enable_revmap):
            self._upgrade_db(self.env.get_db_cnx())
    
    #return true if the db table doesn't exist or needs to be updated
    def environment_needs_upgrade(self, db):
        if int(self.enable_revmap) == 0:
            return False
        cursor = db.cursor()
        try:
            cursor.execute("SELECT COUNT(*) FROM svn_revmap")
            row = cursor.fetchone()
            #if there's one or more rows, assume everything's ok
            if row[0] > 0:
                return False
            return True
        except:
            db.rollback()
            return True

    def upgrade_environment(self, db):
        if int(self.enable_revmap):
            self._upgrade_db(db)

    def _upgrade_db(self, db):
        #open the revision map
        if int(self.enable_revmap) == 0:
            return 0
        try:
            revmap_fd = open(self.revmap, 'rb')
        except IOError:
            raise ResourceNotFound(_("revision map '%(revmap)s' not found", revmap=self.revmap))
        cursor = db.cursor()
        try:
            cursor.execute("DROP TABLE svn_revmap;")
        except:
            db.rollback()

        db_backend, unused = DatabaseManager(self.env)._get_connector()
        cursor = db.cursor()
        for table in self.SCHEMA:
            for stmt in db_backend.to_sql(table):
                self.env.log.debug(stmt)
                cursor.execute(stmt)

        insert_count = 0
        prev_rev = 0
        git_hash = revmap_fd.readline()[0:-1]
        while 1:
            #make sure this line is the hash
            if not re.match(r'[0-9a-f]{40}', git_hash):
                raise Exception("expecting hash, found '%s'" % git_hash)
            line = revmap_fd.readline()[0:-1]

            if line.startswith('git-svn-id:'):
                commit_msg = '<no commit message>'
            else:
                #slurp lines into the commit messsages until there's a blank line, a line starting with git-svn-id or a hash
                commit_msg = ''
                while not re.match(r'[0-9a-f]{40}', line) and not line.startswith('git-svn-id:'):
                    if len(line) > 0:
                        if not commit_msg:
                            commit_msg = line
                        else:
                            commit_msg = commit_msg + " " + line
                    line = revmap_fd.readline()[0:-1]

            if not line.startswith('git-svn-id:'):
                raise Exception("expected git-svn-id, got '%s'" % line)

            svn_rev_match = re.match(r'^git-svn-id:.*@(\d+) ', line)
            svn_rev = int(svn_rev_match.group(1))

            insert_query = "INSERT INTO svn_revmap (svn_rev, git_hash, commit_msg) VALUES (%s, %s, %s);"
            self.env.log.debug(insert_query % (svn_rev, git_hash, commit_msg))
            cursor.execute(insert_query, (svn_rev, git_hash, commit_msg.decode('utf-8')))

            if prev_rev - 1 != svn_rev:
                self.env.log.debug("found a gap between r%d and r%d" % (prev_rev, svn_rev))
            prev_rev = svn_rev

            insert_count += 1
            if svn_rev == 1:
                break
            git_hash = revmap_fd.readline()[0:-1]
            while len(git_hash) == 0:
                git_hash = revmap_fd.readline()[0:-1]

        self.env.log.debug("inserted %d mappings into svn_revmap" % insert_count)

    # IWikiSyntaxProvider methods
    def get_wiki_syntax(self):
        yield (r"\br[1-9]\d*\b",  #svn revision links ("r1432")
            lambda formatter, ns, match:
                self._format_changeset_link(formatter, ns, match))
        yield (r"\b[0-9a-fA-F]{5,40}\b", #git hashes ("eb390eca04394")
            lambda formatter, ns, match:
                self._format_changeset_link(formatter, ns, match))


    #pre_process_request deals with link resolution
    def get_link_resolvers(self):
        return []

    def _format_changeset_link(self, formatter, ns, match):
        self.env.log.debug("format changeset link")
        if int(self.enable_revmap) == 0:
            self.env.log.debug("revmap disabled, skipping thingy")
            return match.group(0)
        self.env.log.debug("revmap enabled: formatting links")
        commit_data = self._get_commit_data(match.group(0))
        if len(commit_data) == 1:
            self.env.log.debug(commit_data)
            if int(self.long_tooltips):
                title = commit_data[0]['msg']
            else:
                title = shorten_line(commit_data[0]['msg'])
            return tag.a(match.group(0), href="%s/%s" % (formatter.href.changeset(), commit_data[0]['id']),
                    title=title, class_="changeset")
        elif len(commit_data) > 1:
            #try to figure out something better when an id is ambiguous
            return match.group(0)

        return match.group(0)

    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip('/') == ('/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve
    
    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)
		# TODO: Verify this code (and redirect in hook.py also)
        req.send_response(204)
        req.send_header('Content-Length', 0)
        req.write('')
        raise RequestDone

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith('/browser')
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith('/changeset')
            repoinfo = req.path_info.replace('/changeset/', '').partition("/")
            repo = self.env.get_repository(repoinfo[2])
            if repo.__class__.__name__ == "GitRepository":
            self.env.log.debug("Handle Pre-Request /changeset: %s" % serve2)
            if serve2:
                self.processChangesetURL(req)

        return handler


    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)

    def _get_commit_data(self, commit_id):
        if int(self.enable_revmap) == 0:
            return False
        self.env.log.debug("looking up commit: %s" % commit_id)
        cursor = self.env.get_db_cnx().cursor()
        if commit_id.startswith('r'):
            commit_id = commit_id[1:]
            self.env.log.debug("running query: SELECT git_hash, commit_msg FROM svn_revmap WHERE svn_rev = %s" % commit_id)
            cursor.execute("SELECT git_hash, commit_msg FROM svn_revmap WHERE svn_rev = %s", (commit_id,))
            rows = cursor.fetchmany(5);
        else:
            self.env.log.debug("running query: SELECT git_hash, commit_msg FROM svn_revmap WHERE git_hash LIKE '%s%%'" % commit_id)
            cursor.execute("SELECT git_hash, commit_msg FROM svn_revmap WHERE git_hash LIKE '%s%%'" % (commit_id,))
            rows = cursor.fetchmany(5)
        results = []
        for row in rows:
            #hash is what's in the db, id is the string the user used (usually not the full hash)
            d = {'hash': row[0],
                 'msg' : row[1],
                 'id'  : commit_id,
            }
            results.append(d)
        return results

    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace('/tree/master', '/commit/')
        
        commitinfo = req.path_info.replace('/changeset/', '').partition("/")
        url = "/%s" % (commitinfo[2] + commitinfo[1] + "commit" + commitinfo[1] + commitinfo[0])
        if not url:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)


    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        rev = req.args.get('rev')
        if rev:
            browser = self.browser.replace('/master', '/')
        else:
            rev = ''
            browser = self.browser

        url = req.path_info.replace('/browser', '')

        redirect = '%s%s%s' % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

        

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'

        if self.autofetch:
            repodir = RepositoryManager(self.env).repository_dir
            if not os.path.isabs(repodir):
                repodir = os.path.join(self.env.path, repodir)
            # TODO: This was the previous code, the repo options is probably unecessary now.
			# repodir = "%s/%s" % (self.repo, reponame)
            self.env.log.debug("Autofetching: %s" % repodir)
            repo = Git(repodir)

            try:
              self.env.log.debug("Fetching repo %s" % self.repo)
              repo.execute(['git', 'fetch'])
              try:
                self.env.log.debug("Resyncing local repo")
                self.env.get_repository('').sync()
              except:
                self.env.log.error("git sync failed!")
            except:
              self.env.log.error("git fetch failed!")

        data = req.args.get('payload')
         
        if data:
            jsondata = simplejson.loads(data)
            reponame = jsondata['repository']['name']

            for i in jsondata['commits']:
                self.hook.process(i, status, self.enable_revmap,reponame)

       self.env.log.debug("Redirect URL: %s" % req)
       req.redirect(self.browser)
Пример #8
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter)
    
    
    key = Option('github', 'apitoken', '', doc="""Your GitHub API Token found here: https://github.com/account, """)
    closestatus = Option('github', 'closestatus', '', doc="""This is the status used to close a ticket. It defaults to closed.""")
    browser = Option('github', 'browser', '', doc="""Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub.""")
    svnhashes = Option('github', 'svnhashes', '', doc="""If transitioning from SVN, this is the path where your hashes are located.""")
    autofetch = Option('github', 'autofetch', '', doc="""Should we auto fetch the repo when we get a commit hook from GitHub.""")
    repo = Option('trac', 'repository_dir' '', doc="""This is your repository dir""")

    def __init__(self):
        self.hook = CommitHook(self.env)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.env.log.debug("Hash Location: %s" % self.svnhashes)
        self.processHook = False

    
    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip('/') == ('/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve
    
    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith('/browser')
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith('/changeset/')
            if serve2:
                revision = req.path_info.replace('/changeset/', '')
                self.env.log.debug("Handle Pre-Request /changeset/: %s" % revision)
                if len(revision) < 6 and revision.isdigit():
                    self.processSubversionURL(req)
                else:
                    self.processChangesetURL(req)
        return handler


    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)


    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace('/tree/master', '/commit/')
        
        url = req.path_info.replace('/changeset/', '')
        if not url:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)


    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        browser = self.browser.replace('/master', '/')
        rev = req.args.get('rev')
        
        url = req.path_info.replace('/browser', '')
        if not rev:
            rev = ''

        redirect = '%s%s%s' % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

        

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'

        data = req.args.get('payload')
         
        if data:
            jsondata = simplejson.loads(data)

            for i in jsondata['commits']:
                self.hook.process(i, status)


        if self.autofetch:
            repo = Git(self.repo)

            try:
              repo.execute(['git', 'fetch'])
            except:
              self.env.log.debug("git fetch failed!")


    def processSubversionURL(self, req):
        self.env.log.debug("processSubversionURL")
        browser = self.browser.replace('/tree/master', '/commit/')

        svnrevision = req.path_info.replace('/changeset/', '')
        svnrevision = svnrevision.zfill(5)
        subfolder1 = svnrevision[:2]
        subfolder2 = svnrevision[2:3]
        filename = svnrevision[3:]

        self.env.log.debug("Redirect SVN: %s" % svnrevision)
        self.env.log.debug("Path: %s/%s/%s/%s" % (self.svnhashes, subfolder1, subfolder2, filename))
        try:
            filehandle = open(self.svnhashes+'/'+subfolder1+'/'+subfolder2+'/'+filename,"r")
            line = filehandle.readline()
            line = line.rstrip("\r\n")
            filehandle.close()
            url = req.path_info.replace('/changeset/'+svnrevision, line)
        except:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)
Пример #9
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter)
    
    
    key = Option('github', 'apitoken', '', doc="""Your GitHub API Token found here: https://github.com/account, """)
    closestatus = Option('github', 'closestatus', '', doc="""This is the status used to close a ticket. It defaults to closed.""")
    browser = Option('github', 'browser', '', doc="""Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub.""")
    autofetch = Option('github', 'autofetch', '', doc="""Should we auto fetch the repo when we get a commit hook from GitHub.""")
    repo = Option('github', 'git_root' '', doc="""This is your root git dir""")

    def __init__(self):
        self.hook = CommitHook(self.env)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.processHook = False

    
    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip('/') == ('/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve
    
    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith('/browser')
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith('/changeset')
            repoinfo = req.path_info.replace('/changeset/', '').partition("/")
            repo = self.env.get_repository(repoinfo[2])
            if repo.__class__.__name__ == "GitRepository":
                self.env.log.debug("Handle Pre-Request /changeset: %s" % serve2)
                if serve2:
                    self.processChangesetURL(req)

        return handler


    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)


    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace('/tree/master', '/commit/')
        
        commitinfo = req.path_info.replace('/changeset/', '').partition("/")
        url = "/%s" % (commitinfo[2] + commitinfo[1] + "commit" + commitinfo[1] + commitinfo[0])
        if not url:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)


    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        browser = self.browser.replace('/master', '/')
        rev = req.args.get('rev')
        
        url = req.path_info.replace('/browser', '')
        if not rev:
            rev = ''

        redirect = '%s%s%s' % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

        

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'

        data = req.args.get('payload')
         
        if data:
            jsondata = simplejson.loads(data)
            reponame = jsondata['repository']['name']
            for i in jsondata['commits']:
                self.hook.process(i, status, reponame)


        if self.autofetch:
            repodir = "%s/%s" % (self.repo, reponame)
            self.env.log.debug("Autofetching: %s" % repodir)
            repo = Git(repodir)

            try:
              repo.execute(['git', 'fetch'])
            except:
              self.env.log.debug("git fetch failed!")
Пример #10
0
class GithubPlugin(Component):
    implements(IRequestHandler, IRequestFilter)

    key = Option(
        'github',
        'apitoken',
        '',
        doc="""Your GitHub API Token found here: https://github.com/account, """
    )
    closestatus = Option(
        'github',
        'closestatus',
        '',
        doc=
        """This is the status used to close a ticket. It defaults to closed."""
    )
    browser = Option(
        'github',
        'browser',
        '',
        doc=
        """Place your GitHub Source Browser URL here to have the /browser entry point redirect to GitHub."""
    )
    autofetch = Option(
        'github',
        'autofetch',
        '',
        doc=
        """Should we auto fetch the repo when we get a commit hook from GitHub."""
    )
    repo = Option('trac', 'repository_dir'
                  '',
                  doc="""This is your repository dir""")

    def __init__(self):
        self.hook = CommitHook(self.env)
        self.env.log.debug("API Token: %s" % self.key)
        self.env.log.debug("Browser: %s" % self.browser)
        self.processHook = False

    # IRequestHandler methods
    def match_request(self, req):
        self.env.log.debug("Match Request")
        serve = req.path_info.rstrip('/') == (
            '/github/%s' % self.key) and req.method == 'POST'
        if serve:
            self.processHook = True
            #This is hacky but it's the only way I found to let Trac post to this request
            #   without a valid form_token
            req.form_token = None

        self.env.log.debug("Handle Request: %s" % serve)
        return serve

    def process_request(self, req):
        if self.processHook:
            self.processCommitHook(req)

    # This has to be done via the pre_process_request handler
    # Seems that the /browser request doesn't get routed to match_request :(
    def pre_process_request(self, req, handler):
        if self.browser:
            serve = req.path_info.startswith('/browser')
            self.env.log.debug("Handle Pre-Request /browser: %s" % serve)
            if serve:
                self.processBrowserURL(req)

            serve2 = req.path_info.startswith('/changeset')
            self.env.log.debug("Handle Pre-Request /changeset: %s" % serve2)
            if serve2:
                self.processChangesetURL(req)

        return handler

    def post_process_request(self, req, template, data, content_type):
        return (template, data, content_type)

    def processChangesetURL(self, req):
        self.env.log.debug("processChangesetURL")
        browser = self.browser.replace('/tree/master', '/commit/')

        url = req.path_info.replace('/changeset/', '')
        if not url:
            browser = self.browser
            url = ''

        redirect = '%s%s' % (browser, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

    def processBrowserURL(self, req):
        self.env.log.debug("processBrowserURL")
        browser = self.browser.replace('/master', '/')
        rev = req.args.get('rev')

        url = req.path_info.replace('/browser', '')
        if not rev:
            rev = ''

        redirect = '%s%s%s' % (browser, rev, url)
        self.env.log.debug("Redirect URL: %s" % redirect)
        out = 'Going to GitHub: %s' % redirect

        req.redirect(redirect)

    def processCommitHook(self, req):
        self.env.log.debug("processCommitHook")
        status = self.closestatus
        if not status:
            status = 'closed'

        data = req.args.get('payload')

        if data:
            jsondata = simplejson.loads(data)

            for i in jsondata['commits']:
                self.hook.process(i, status)

        if self.autofetch:
            repo = Git(self.repo)

            try:
                repo.execute(['git', 'fetch'])
            except:
                self.env.log.debug("git fetch failed!")