Skip to content

Commit

Permalink
Apply filter on relevant branches to the timeline.
Browse files Browse the repository at this point in the history
Fix trac-hacks#1.

Thanks Alexey Boriskin for providing the initial version of this patch.
  • Loading branch information
aaugustin committed Oct 13, 2012
1 parent cbbdc9a commit 4fff491
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 44 deletions.
45 changes: 36 additions & 9 deletions runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,22 @@ def stopTracd(cls):
cls.tracd.wait()

@staticmethod
def makeGitCommit(repo, path, content, message='edit'):
def makeGitBranch(repo, branch):
subprocess.check_output(['git', '--git-dir=%s/.git' % repo, 'branch', branch])

@staticmethod
def makeGitCommit(repo, path, content, message='edit', branch='master'):
if branch != 'master':
subprocess.check_output(['git', '--git-dir=%s/.git' % repo, 'checkout', branch],
stderr=subprocess.PIPE)
path = os.path.join(repo, path)
with open(path, 'wb') as fp:
fp.write(content)
subprocess.check_output(['git', '--git-dir=%s/.git' % repo, 'add', path])
subprocess.check_output(['git', '--git-dir=%s/.git' % repo, 'commit', '-m', message])
if branch != 'master':
subprocess.check_output(['git', '--git-dir=%s/.git' % repo, 'checkout', 'master'],
stderr=subprocess.PIPE)

@staticmethod
def openGitHubHook(n=1, reponame=''):
Expand Down Expand Up @@ -229,6 +239,27 @@ def testBadUrl(self):
with self.assertRaisesRegexp(urllib2.HTTPError, r'^HTTP Error 404: Not Found$'):
urllib2.urlopen(URL + 'changesetnosuchurl')

def testTimelineFiltering(self):
self.makeGitBranch(GIT, 'stable/2.0')
self.makeGitBranch(GIT, 'unstable/2.0')
self.makeGitBranch(ALTGIT, 'stable/2.0')
self.makeGitBranch(ALTGIT, 'unstable/2.0')
self.makeGitCommit(GIT, 'myfile', 'timeline 1\n', 'msg 1')
self.makeGitCommit(GIT, 'myfile', 'timeline 2\n', 'msg 2', 'stable/2.0')
self.makeGitCommit(GIT, 'myfile', 'timeline 3\n', 'msg 3', 'unstable/2.0')
self.makeGitCommit(ALTGIT, 'myfile', 'timeline 4\n', 'msg 4')
self.makeGitCommit(ALTGIT, 'myfile', 'timeline 5\n', 'msg 5', 'stable/2.0')
self.makeGitCommit(ALTGIT, 'myfile', 'timeline 6\n', 'msg 6', 'unstable/2.0')
self.openGitHubHook(3)
self.openGitHubHook(3, 'alt')
html = urllib2.urlopen(URL + 'timeline').read()
self.assertTrue('msg 1' in html)
self.assertTrue('msg 2' in html)
self.assertTrue('msg 3' in html)
self.assertTrue('msg 4' in html)
self.assertTrue('msg 5' in html)
self.assertFalse('msg 6' in html)


class GitHubPostCommitHookTests(TracGitHubTests):

Expand Down Expand Up @@ -262,14 +293,10 @@ def testMultipleCommits(self):
r"\* Adding commits [0-9a-f]{40}, [0-9a-f]{40}\n")

def testCommitOnBranch(self):
subprocess.check_output(['git', '--git-dir=%s/.git' % ALTGIT,
'checkout', '-b', 'stable/1.0'], stderr=subprocess.PIPE)
self.makeGitCommit(ALTGIT, 'stable', 'stable branch\n')
subprocess.check_output(['git', '--git-dir=%s/.git' % ALTGIT,
'checkout', '-b', 'unstable'], stderr=subprocess.PIPE)
self.makeGitCommit(ALTGIT, 'unstable', 'unstable branch\n')
subprocess.check_output(['git', '--git-dir=%s/.git' % ALTGIT,
'checkout', 'master'], stderr=subprocess.PIPE)
self.makeGitBranch(ALTGIT, 'stable/1.0')
self.makeGitCommit(ALTGIT, 'stable', 'stable branch\n', branch='stable/1.0')
self.makeGitBranch(ALTGIT, 'unstable/1.0')
self.makeGitCommit(ALTGIT, 'unstable', 'unstable branch\n', branch='unstable/1.0')
output = self.openGitHubHook(2, 'alt').read()
self.assertRegexpMatches(output, r"Running hook on alt\n"
r"\* Updating clone\n"
Expand Down
96 changes: 61 additions & 35 deletions tracext/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,33 @@
from trac.config import ListOption, Option
from trac.core import Component, implements
from trac.resource import ResourceNotFound
from trac.timeline.api import ITimelineEventProvider
from trac.util.translation import _
from trac.versioncontrol.api import is_default, NoSuchChangeset, RepositoryManager
from trac.versioncontrol.web_ui.changeset import ChangesetModule
from trac.web.api import IRequestHandler


class GitHubBrowser(ChangesetModule):
implements(IRequestHandler)
class GitHubMixin(object):

def get_gh_repo(self, reponame):
key = 'repository' if is_default(reponame) else '%s.repository' % reponame
return self.config.get('github', key)

def get_branches(self, reponame):
key = 'branches' if is_default(reponame) else '%s.branches' % reponame
return self.config.getlist('github', key, sep=' ')


class GitHubBrowser(GitHubMixin, ChangesetModule):
implements(IRequestHandler, ITimelineEventProvider)

repository = Option('github', 'repository', '',
doc="Repository name on GitHub (<user>/<project>)")

def match_request(self, req):
if not self.repository: # pragma: no cover
return super(GitHubBrowser, self).match_request(req)
# IRequestHandler methods

def match_request(self, req):
match = self._request_re.match(req.path_info)
if match:
rev, path = match.groups()
Expand All @@ -29,17 +40,12 @@ def match_request(self, req):
return True

def process_request(self, req):
if not self.repository: # pragma: no cover
return super(GitHubBrowser, self).process_request(req)

rev = req.args.get('rev')
path = req.args.get('path')

rm = RepositoryManager(self.env)
reponame, repos, path = rm.get_repository_by_path(path)

key = 'repository' if is_default(reponame) else '%s.repository' % reponame
gh_repo = self.config.get('github', key)
gh_repo = self.get_gh_repo(reponame)

try:
rev = repos.normalize_rev(rev)
Expand All @@ -54,8 +60,27 @@ def process_request(self, req):
url = 'https://github.com/%s/commit/%s' % (gh_repo, rev)
req.redirect(url)

# ITimelineEventProvider methods

def get_timeline_events(self, req, start, stop, filters):
rm = RepositoryManager(self.env)

class GitHubPostCommitHook(Component):
for event in super(GitHubBrowser, self).get_timeline_events(req, start, stop, filters):
assert event[0] == 'changeset'
viewable_changesets, show_location, show_files = event[3]
filtered_changesets = []
for cset, cset_resource, (reponame,) in viewable_changesets:
branches = self.get_branches(reponame)
repos = rm.get_repository(reponame)
if rev_in_branches(repos, cset.rev, branches):
filtered_changesets.append((cset, cset_resource, [reponame]))
if filtered_changesets:
cset = filtered_changesets[-1][0]
yield ('changeset', cset.date, cset.author,
(filtered_changesets, show_location, show_files))


class GitHubPostCommitHook(GitHubMixin, Component):
implements(IRequestHandler)

branches = ListOption('github', 'branches', sep=' ',
Expand Down Expand Up @@ -88,9 +113,6 @@ def process_request(self, req):
rm = RepositoryManager(self.env)
reponame, repos, path = rm.get_repository_by_path(path)

key = 'branches' if is_default(reponame) else '%s.branches' % reponame
branches = self.config.getlist('github', key, sep=' ')

if path != '/':
msg = u'No such repository (%s)\n' % path
self.log.warning(msg.rstrip('\n'))
Expand All @@ -109,35 +131,39 @@ def process_request(self, req):
self.log.warning(msg.rstrip('\n'))
req.send(msg.encode('utf-8'), 'text/plain', 400)

if branches:
added_revs, skipped_revs = [], []
for rev in revs:
rev_branches = repos.git.repo.branch('--contains', rev)
rev_branches = [l[2:] for l in rev_branches.splitlines()]
if any(fnmatch.fnmatchcase(rev_branch, branch)
for rev_branch in rev_branches
for branch in branches):
added_revs.append(rev)
else:
skipped_revs.append(rev)
else:
added_revs, skipped_revs = revs, []
branches = self.get_branches(reponame)
added_revs, skipped_revs = [], []
for rev in revs:
if rev_in_branches(repos, rev, branches):
added_revs.append(rev)
else:
skipped_revs.append(rev)

if added_revs:
output += u'* Adding %s\n' % self.describe_commits(added_revs)
output += u'* Adding %s\n' % describe_commits(added_revs)
# This is where Trac gets notified of the commits in the changeset
rm.notify('changeset_added', reponame, added_revs)

if skipped_revs:
output += u'* Skipping %s\n' % self.describe_commits(skipped_revs)
output += u'* Skipping %s\n' % describe_commits(skipped_revs)

for line in output.splitlines():
self.log.debug(line)

req.send(output.encode('utf-8'), 'text/plain', 200 if output else 204)

def describe_commits(self, revs):
if len(revs) == 1:
return u'commit %s' % revs[0]
else:
return u'commits %s' % u', '.join(revs)

def rev_in_branches(repos, rev, branches):
if not branches: # no branches filter configured
return True
rev_branches = repos.git.repo.branch('--contains', rev)
rev_branches = [l[2:] for l in rev_branches.splitlines()]
return any(fnmatch.fnmatchcase(rev_branch, branch)
for rev_branch in rev_branches for branch in branches)


def describe_commits(revs):
if len(revs) == 1:
return u'commit %s' % revs[0]
else:
return u'commits %s' % u', '.join(revs)

0 comments on commit 4fff491

Please sign in to comment.