← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/git-ref-url into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/git-ref-url into lp:launchpad with lp:~cjwatson/launchpad/git-ref-scanner as a prerequisite.

Commit message:
Add basic navigation support for Git references.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-ref-url/+merge/252900

Add basic navigation support for Git references.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-ref-url into lp:launchpad.
=== modified file 'lib/lp/app/browser/configure.zcml'
--- lib/lp/app/browser/configure.zcml	2014-11-29 01:33:59 +0000
+++ lib/lp/app/browser/configure.zcml	2015-03-13 14:20:26 +0000
@@ -1,4 +1,4 @@
-<!-- Copyright 2009-2014 Canonical Ltd.  This software is licensed under the
+<!-- Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
      GNU Affero General Public License version 3 (see the file LICENSE).
 -->
 
@@ -729,6 +729,13 @@
       />
 
   <adapter
+      for="lp.code.interfaces.gitrepository.IGitRepository"
+      provides="zope.traversing.interfaces.IPathAdapter"
+      factory="lp.app.browser.tales.GitRepositoryFormatterAPI"
+      name="fmt"
+      />
+
+  <adapter
       for="lp.bugs.interfaces.bugbranch.IBugBranch"
       provides="zope.traversing.interfaces.IPathAdapter"
       factory="lp.app.browser.tales.BugBranchFormatterAPI"

=== modified file 'lib/lp/app/browser/tales.py'
--- lib/lp/app/browser/tales.py	2014-11-27 05:01:51 +0000
+++ lib/lp/app/browser/tales.py	2015-03-13 14:20:26 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2014 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Implementation of the lp: htmlform: fmt: namespaces in TALES."""
@@ -1674,6 +1674,15 @@
             }
 
 
+class GitRepositoryFormatterAPI(CustomizableFormatter):
+    """Adapter for IGitRepository objects to a formatted string."""
+
+    _link_summary_template = '%(display_name)s'
+
+    def _link_summary_values(self):
+        return {'display_name': self._context.display_name}
+
+
 class BugBranchFormatterAPI(CustomizableFormatter):
     """Adapter providing fmt support for BugBranch objects"""
 

=== modified file 'lib/lp/app/doc/tales.txt'
--- lib/lp/app/doc/tales.txt	2014-12-31 13:24:43 +0000
+++ lib/lp/app/doc/tales.txt	2015-03-13 14:20:26 +0000
@@ -394,6 +394,7 @@
 
   * people / teams
   * branches
+  * Git repositories
   * bugs
   * bug subscriptions
   * bug tasks
@@ -520,6 +521,20 @@
     <a href=".../~eric/fooix/bar" class="sprite branch">lp://dev/fooix</a>
 
 
+Git repositories
+................
+
+For Git repositories, fmt:link links to the branch page.
+
+    >>> from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
+    >>> from lp.services.features.testing import FeatureFixture
+    >>> with FeatureFixture({GIT_FEATURE_FLAG: 'on'}):
+    ...     repository = factory.makeGitRepository(
+    ...         owner=eric, target=fooix, name=u'bar')
+    ...     print test_tales("repository/fmt:link", repository=repository)
+    <a href=".../~eric/fooix/+git/bar">lp:~eric/fooix/+git/bar</a>
+
+
 Bugs
 ....
 

=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml	2015-03-05 16:23:26 +0000
+++ lib/lp/code/browser/configure.zcml	2015-03-13 14:20:26 +0000
@@ -771,6 +771,9 @@
     <browser:url
         for="lp.code.interfaces.gitrepository.IGitRepository"
         urldata="lp.code.browser.gitrepository.GitRepositoryURL"/>
+    <browser:navigation
+        module="lp.code.browser.gitrepository"
+        classes="GitRepositoryNavigation"/>
     <browser:menus
         module="lp.code.browser.gitrepository"
         classes="
@@ -795,6 +798,21 @@
         factory="lp.code.browser.gitrepository.GitRepositoryBreadcrumb"
         permission="zope.Public"/>
 
+    <browser:defaultView
+        for="lp.code.interfaces.gitref.IGitRef"
+        name="+index"/>
+    <browser:url
+        for="lp.code.interfaces.gitref.IGitRef"
+        path_expression="string:+ref/${path}"
+        attribute_to_parent="repository"
+        rootsite="code"/>
+    <browser:page
+        for="lp.code.interfaces.gitref.IGitRef"
+        class="lp.code.browser.gitref.GitRefView"
+        permission="launchpad.View"
+        name="+index"
+        template="../templates/gitref-index.pt"/>
+
     <browser:menus
         classes="ProductBranchesMenu"
         module="lp.code.browser.branchlisting"/>

=== added file 'lib/lp/code/browser/gitref.py'
--- lib/lp/code/browser/gitref.py	1970-01-01 00:00:00 +0000
+++ lib/lp/code/browser/gitref.py	2015-03-13 14:20:26 +0000
@@ -0,0 +1,19 @@
+# Copyright 2015 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Git reference views."""
+
+__metaclass__ = type
+
+__all__ = [
+    'GitRefView',
+    ]
+
+from lp.services.webapp import LaunchpadView
+
+
+class GitRefView(LaunchpadView):
+
+    @property
+    def label(self):
+        return self.context.display_name

=== modified file 'lib/lp/code/browser/gitrepository.py'
--- lib/lp/code/browser/gitrepository.py	2015-03-04 16:49:42 +0000
+++ lib/lp/code/browser/gitrepository.py	2015-03-13 14:20:26 +0000
@@ -8,6 +8,7 @@
 __all__ = [
     'GitRepositoryBreadcrumb',
     'GitRepositoryContextMenu',
+    'GitRepositoryNavigation',
     'GitRepositoryURL',
     'GitRepositoryView',
     ]
@@ -16,12 +17,15 @@
 from zope.interface import implements
 
 from lp.app.browser.informationtype import InformationTypePortletMixin
+from lp.app.errors import NotFoundError
 from lp.code.interfaces.gitrepository import IGitRepository
 from lp.services.config import config
 from lp.services.webapp import (
     ContextMenu,
     LaunchpadView,
     Link,
+    Navigation,
+    stepto,
     )
 from lp.services.webapp.authorization import (
     check_permission,
@@ -54,6 +58,24 @@
         return self.context.unique_name.split("/")[-1]
 
 
+class GitRepositoryNavigation(Navigation):
+
+    usedfor = IGitRepository
+
+    @stepto("+ref")
+    def traverse_ref(self):
+        segments = list(self.request.getTraversalStack())
+        ref_segments = []
+        while segments:
+            ref_segments.append(segments.pop())
+            ref = self.context.getRefByPath("/".join(ref_segments))
+            if ref is not None:
+                for _ in range(len(ref_segments)):
+                    self.request.stepstogo.consume()
+                return ref
+        raise NotFoundError
+
+
 class GitRepositoryContextMenu(ContextMenu):
     """Context menu for `IGitRepository`."""
 

=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
--- lib/lp/code/browser/tests/test_gitrepository.py	2015-03-05 15:28:11 +0000
+++ lib/lp/code/browser/tests/test_gitrepository.py	2015-03-13 14:20:26 +0000
@@ -24,15 +24,36 @@
     login_person,
     logout,
     person_logged_in,
+    TestCaseWithFactory,
     )
 from lp.testing.layers import DatabaseFunctionalLayer
 from lp.testing.pages import (
     setupBrowser,
     setupBrowserForUser,
     )
+from lp.testing.publication import test_traverse
 from lp.testing.views import create_initialized_view
 
 
+class TestGitRepositoryNavigation(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestGitRepositoryNavigation, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
+    def test_traverse_ref(self):
+        [ref] = self.factory.makeGitRefs()
+        url = "%s/+ref/%s" % (canonical_url(ref.repository), ref.path)
+        self.assertEqual(ref, test_traverse(url)[0])
+
+    def test_traverse_ref_missing(self):
+        repository = self.factory.makeGitRepository()
+        url = "%s/+ref/refs/heads/master" % canonical_url(repository)
+        self.assertRaises(NotFound, test_traverse, url)
+
+
 class TestGitRepositoryView(BrowserTestCase):
 
     layer = DatabaseFunctionalLayer

=== modified file 'lib/lp/code/interfaces/gitref.py'
--- lib/lp/code/interfaces/gitref.py	2015-03-13 14:20:26 +0000
+++ lib/lp/code/interfaces/gitref.py	2015-03-13 14:20:26 +0000
@@ -41,3 +41,7 @@
     object_type = Choice(
         title=_("Object type"), required=True, readonly=True,
         vocabulary=GitObjectType)
+
+    display_name = TextLine(
+        title=_("Display name"), required=True, readonly=True,
+        description=_("Display name of the reference."))

=== modified file 'lib/lp/code/model/gitref.py'
--- lib/lp/code/model/gitref.py	2015-03-13 14:20:26 +0000
+++ lib/lp/code/model/gitref.py	2015-03-13 14:20:26 +0000
@@ -35,3 +35,7 @@
     commit_sha1 = Unicode(name='commit_sha1', allow_none=False)
 
     object_type = EnumCol(enum=GitObjectType, notNull=True)
+
+    @property
+    def display_name(self):
+        return self.path.split("/", 2)[-1]

=== added file 'lib/lp/code/model/tests/test_gitref.py'
--- lib/lp/code/model/tests/test_gitref.py	1970-01-01 00:00:00 +0000
+++ lib/lp/code/model/tests/test_gitref.py	2015-03-13 14:20:26 +0000
@@ -0,0 +1,24 @@
+# Copyright 2015 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for Git references."""
+
+__metaclass__ = type
+
+from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
+from lp.services.features.testing import FeatureFixture
+from lp.testing import TestCaseWithFactory
+from lp.testing.layers import DatabaseFunctionalLayer
+
+
+class TestGitRef(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def test_display_name(self):
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+        [master, personal] = self.factory.makeGitRefs(
+            paths=[u"refs/heads/master", u"refs/heads/people/foo/bar"])
+        self.assertEqual(
+            ["master", "people/foo/bar"],
+            [ref.display_name for ref in (master, personal)])

=== added file 'lib/lp/code/templates/gitref-index.pt'
--- lib/lp/code/templates/gitref-index.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/code/templates/gitref-index.pt	2015-03-13 14:20:26 +0000
@@ -0,0 +1,34 @@
+<html
+  xmlns="http://www.w3.org/1999/xhtml";
+  xmlns:tal="http://xml.zope.org/namespaces/tal";
+  xmlns:metal="http://xml.zope.org/namespaces/metal";
+  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+  metal:use-macro="view/macro:page/main_side"
+  i18n:domain="launchpad"
+>
+
+<body>
+
+<div metal:fill-slot="main">
+
+  <div class="yui-g">
+    <div id="ref-info" class="portlet">
+      <h2>Branch information</h2>
+      <div class="two-column-list">
+        <dl id="name">
+          <dt>Name:</dt>
+          <dd tal:content="context/display_name" />
+        </dl>
+
+        <dl id="repository">
+          <dt>Repository:</dt>
+          <dd tal:content="structure context/repository/fmt:link" />
+        </dl>
+      </div>
+    </div>
+  </div>
+
+</div>
+
+</body>
+</html>

=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py	2015-03-09 15:04:54 +0000
+++ lib/lp/registry/browser/person.py	2015-03-13 14:20:26 +0000
@@ -379,7 +379,8 @@
                 iter_segments, owner=self.context)
             if repository is None:
                 raise NotFoundError
-            for i in range(num_segments - len(list(iter_segments))):
+            # Subtract one because the pillar has already been traversed.
+            for _ in range(num_segments - len(list(iter_segments)) - 1):
                 self.request.stepstogo.consume()
 
             if IProduct.providedBy(target):


Follow ups