You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by tv...@apache.org on 2013/07/23 03:30:10 UTC

git commit: [#6463] Google Code repo importer

Updated Branches:
  refs/heads/tv/6463 [created] 87877b33b


[#6463] Google Code repo importer

Signed-off-by: Tim Van Steenburgh <tv...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/87877b33
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/87877b33
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/87877b33

Branch: refs/heads/tv/6463
Commit: 87877b33ba030b6301ec7f466a06037bfc761127
Parents: ec7edd8
Author: Tim Van Steenburgh <tv...@gmail.com>
Authored: Tue Jul 23 01:28:02 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Tue Jul 23 01:28:02 2013 +0000

----------------------------------------------------------------------
 .../forgeimporters/google/__init__.py           |   5 +-
 ForgeImporters/forgeimporters/google/code.py    | 122 +++++++++++++++++++
 .../google/templates/code/index.html            |  24 ++++
 .../forgeimporters/google/tests/test_code.py    | 122 +++++++++++++++++++
 4 files changed, 271 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/87877b33/ForgeImporters/forgeimporters/google/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/__init__.py b/ForgeImporters/forgeimporters/google/__init__.py
index 0feb64a..cebf1f9 100644
--- a/ForgeImporters/forgeimporters/google/__init__.py
+++ b/ForgeImporters/forgeimporters/google/__init__.py
@@ -28,6 +28,7 @@ from allura import model as M
 class GoogleCodeProjectExtractor(object):
     PAGE_MAP = {
             'project_info': 'http://code.google.com/p/%s/',
+            'source_browse': 'http://code.google.com/p/%s/source/browse/',
         }
 
     LICENSE_MAP = defaultdict(lambda:'Other/Proprietary License', {
@@ -45,9 +46,9 @@ class GoogleCodeProjectExtractor(object):
 
     def __init__(self, project, page='project_info'):
         gc_project_name = project.get_tool_data('google-code', 'project_name')
-        page = urllib2.urlopen(self.PAGE_MAP[page] % urllib.quote(gc_project_name))
+        self.url = self.PAGE_MAP[page] % urllib.quote(gc_project_name)
         self.project = project
-        self.page = BeautifulSoup(page)
+        self.page = BeautifulSoup(urllib2.urlopen(self.url))
 
     def get_short_description(self):
         self.project.short_description = str(self.page.find(itemprop='description')).strip()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/87877b33/ForgeImporters/forgeimporters/google/code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/code.py b/ForgeImporters/forgeimporters/google/code.py
new file mode 100644
index 0000000..3c2e484
--- /dev/null
+++ b/ForgeImporters/forgeimporters/google/code.py
@@ -0,0 +1,122 @@
+#       Licensed to the Apache Software Foundation (ASF) under one
+#       or more contributor license agreements.  See the NOTICE file
+#       distributed with this work for additional information
+#       regarding copyright ownership.  The ASF licenses this file
+#       to you under the Apache License, Version 2.0 (the
+#       "License"); you may not use this file except in compliance
+#       with the License.  You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#       Unless required by applicable law or agreed to in writing,
+#       software distributed under the License is distributed on an
+#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#       KIND, either express or implied.  See the License for the
+#       specific language governing permissions and limitations
+#       under the License.
+
+import re
+
+import formencode as fe
+from formencode import validators as fev
+
+from pylons import tmpl_context as c
+from tg import (
+        expose,
+        redirect,
+        validate,
+        )
+from tg.decorators import (
+        with_trailing_slash,
+        without_trailing_slash,
+        )
+
+from allura.controllers import BaseController
+from allura.lib.decorators import require_post
+
+from forgehg.hg_main import ForgeHgApp
+from forgegit.git_main import ForgeGitApp
+from forgesvn.svn_main import ForgeSVNApp
+
+from forgeimporters.base import ToolImporter
+from forgeimporters.google import GoogleCodeProjectExtractor
+
+
+RE_REPO_TYPE = re.compile(r'(svn|hg|git)')
+REPO_URLS = {
+    'svn': 'http://{0}.googlecode.com/svn/trunk/',
+    'git': 'https://code.google.com/p/{0}/',
+    'hg': 'https://code.google.com/p/{0}/',
+}
+REPO_ENTRY_POINTS = {
+    'svn': 'SVN',
+    'git': 'Git',
+    'hg': 'Hg',
+}
+
+
+def get_repo_url(project_name, type_):
+    return REPO_URLS[type_].format(project_name)
+
+
+def get_repo_type(extractor):
+    repo_type = extractor.page.find(id="crumb_root")
+    if not repo_type:
+        raise Exception("Couldn't detect repo type: no #crumb_root in "
+                "{0}".format(extractor.url))
+    re_match = RE_REPO_TYPE.match(repo_type.text.lower())
+    if re_match:
+        return re_match.group(0)
+    else:
+        raise Exception("Unknown repo type: {0}".format(repo_type.text))
+
+
+class GoogleRepoImportSchema(fe.Schema):
+    gc_project_name = fev.UnicodeString(not_empty=True)
+    mount_point = fev.UnicodeString()
+    mount_label = fev.UnicodeString()
+
+
+class GoogleRepoImportController(BaseController):
+    @with_trailing_slash
+    @expose('jinja:forgeimporters.google:templates/code/index.html')
+    def index(self, **kw):
+        return {}
+
+    @without_trailing_slash
+    @expose()
+    @require_post()
+    @validate(GoogleRepoImportSchema(), error_handler=index)
+    def create(self, gc_project_name, mount_point, mount_label, **kw):
+        c.project.set_tool_data('google-code', project_name=gc_project_name)
+        app = GoogleRepoImporter.import_tool(c.project,
+                mount_point=mount_point,
+                mount_label=mount_label)
+        redirect(app.url())
+
+
+class GoogleRepoImporter(ToolImporter):
+    target_app = (ForgeHgApp, ForgeGitApp, ForgeSVNApp)
+    source = 'Google Code'
+    controller = GoogleRepoImportController
+    tool_label = 'Google Code Source Importer'
+    tool_description = 'Import your SVN, Git, or Hg repo from Google Code'
+
+    def import_tool(self, project=None, mount_point=None, mount_label=None):
+        """ Import a Google Code repo into a new SVN, Git, or Hg Allura tool.
+
+        """
+        if not project:
+            raise Exception("You must supply a project")
+        if not project.get_tool_data('google-code', 'project_name'):
+            raise Exception("Missing Google Code project name")
+        extractor = GoogleCodeProjectExtractor(project, page='source_browse')
+        repo_type = get_repo_type(extractor)
+        repo_url = get_repo_url(project.get_tool_data('google-code',
+            'project_name'), repo_type)
+        return project.install_app(
+                REPO_ENTRY_POINTS[repo_type],
+                mount_point=mount_point or 'source',
+                mount_label=mount_label or 'Source',
+                init_from_url=repo_url,
+                )

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/87877b33/ForgeImporters/forgeimporters/google/templates/code/index.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/templates/code/index.html b/ForgeImporters/forgeimporters/google/templates/code/index.html
new file mode 100644
index 0000000..7eb1e47
--- /dev/null
+++ b/ForgeImporters/forgeimporters/google/templates/code/index.html
@@ -0,0 +1,24 @@
+{% extends g.theme.master %}
+
+{% block title %}
+{{c.project.name}} / Import Google Code Repo
+{% endblock %}
+
+{% block header %}
+Import a Repo from a Google Code project
+{% endblock %}
+
+{% block content %}
+<form action="create" method="post" class="pad">
+  <label for="gc_project_name">Google Code project name (as it appears in a URL)</label>
+  <input name="gc_project_name" />
+
+  <label for="mount_label">Label</label>
+  <input name="mount_label" value="Source" />
+
+  <label for="mount_point">Mount Point</label>
+  <input name="mount_point" value="source" />
+
+  <input type="submit" />
+</form>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/87877b33/ForgeImporters/forgeimporters/google/tests/test_code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tests/test_code.py b/ForgeImporters/forgeimporters/google/tests/test_code.py
new file mode 100644
index 0000000..1459290
--- /dev/null
+++ b/ForgeImporters/forgeimporters/google/tests/test_code.py
@@ -0,0 +1,122 @@
+from unittest import TestCase
+from mock import Mock, patch
+
+from allura.tests import TestController
+from forgesvn.tests import with_svn
+
+from forgeimporters.google.code import (
+        get_repo_url,
+        get_repo_type,
+        GoogleRepoImporter,
+        GoogleRepoImportController,
+        )
+
+
+class TestGetRepoUrl(TestCase):
+
+    def test_svn(self):
+        r = get_repo_url('projname', 'svn')
+        self.assertEqual(r, 'http://projname.googlecode.com/svn/trunk/')
+
+    def test_git(self):
+        r = get_repo_url('projname', 'git')
+        self.assertEqual(r, 'https://code.google.com/p/projname/')
+
+    def test_hg(self):
+        r = get_repo_url('projname', 'hg')
+        self.assertEqual(r, 'https://code.google.com/p/projname/')
+
+
+class TestRepoType(TestCase):
+
+    def _make_extractor(self, html):
+        from BeautifulSoup import BeautifulSoup
+        return Mock(page=BeautifulSoup(html),
+                url="http://test/source/browse")
+
+    def test_happy_path(self):
+        extractor = self._make_extractor(
+                '<span id="crumb_root">\nsvn/&nbsp;</span>')
+        self.assertEqual('svn', get_repo_type(extractor))
+
+    def test_no_crumb_root(self):
+        extractor = self._make_extractor('')
+        with self.assertRaises(Exception) as cm:
+            get_repo_type(extractor)
+        self.assertEqual(str(cm.exception),
+                "Couldn't detect repo type: no #crumb_root in "
+                "http://test/source/browse")
+
+    def test_unknown_repo_type(self):
+        extractor = self._make_extractor(
+                '<span id="crumb_root">cvs</span>')
+        with self.assertRaises(Exception) as cm:
+            get_repo_type(extractor)
+        self.assertEqual(str(cm.exception), "Unknown repo type: cvs")
+
+
+class TestGoogleRepoImporter(TestCase):
+
+    def _make_project(self, gc_proj_name=None):
+        project = Mock()
+        project.get_tool_data.side_effect = lambda *args: gc_proj_name
+        return project
+
+    @patch('forgeimporters.google.code.get_repo_type')
+    @patch('forgeimporters.google.code.get_repo_url')
+    def test_import_tool_happy_path(self, get_repo_url, get_repo_type):
+        get_repo_type.return_value = 'git'
+        get_repo_url.return_value = 'http://remote/clone/url/'
+        p = self._make_project(gc_proj_name='myproject')
+        GoogleRepoImporter().import_tool(p)
+        p.install_app.assert_called_once_with('Git',
+                mount_point='source',
+                mount_label='Source',
+                init_from_url='http://remote/clone/url/',
+                )
+
+    def test_no_project(self):
+        with self.assertRaises(Exception) as cm:
+            GoogleRepoImporter().import_tool()
+        self.assertEqual(str(cm.exception), "You must supply a project")
+
+    def test_no_google_code_project_name(self):
+        p = self._make_project()
+        with self.assertRaises(Exception) as cm:
+            GoogleRepoImporter().import_tool(p)
+        self.assertEqual(str(cm.exception), "Missing Google Code project name")
+
+
+class TestGoogleRepoImportController(TestController, TestCase):
+    def setUp(self):
+        """Mount Google Code importer on the SVN admin controller"""
+        super(TestGoogleRepoImportController, self).setUp()
+        from forgesvn.svn_main import SVNRepoAdminController
+        SVNRepoAdminController._importer = GoogleRepoImportController()
+
+    @with_svn
+    def test_index(self):
+        r = self.app.get('/p/test/admin/src/_importer/')
+        self.assertIsNotNone(r.html.find(attrs=dict(name="gc_project_name")))
+        self.assertIsNotNone(r.html.find(attrs=dict(name="mount_label")))
+        self.assertIsNotNone(r.html.find(attrs=dict(name="mount_point")))
+
+    @with_svn
+    @patch('forgeimporters.google.code.GoogleRepoImporter')
+    def test_create(self, gri):
+        from allura import model as M
+        gri.import_tool.return_value = Mock()
+        gri.import_tool.return_value.url.return_value = '/p/test/mymount'
+        params = dict(gc_project_name='poop',
+                mount_label='mylabel',
+                mount_point='mymount',
+                )
+        r = self.app.post('/p/test/admin/src/_importer/create', params,
+                status=302)
+        project = M.Project.query.get(shortname='test')
+        self.assertEqual(r.location, 'http://localhost/p/test/mymount')
+        self.assertEqual(project.get_tool_data('google-code', 'project_name'),
+                'poop')
+        self.assertEqual(project._id, gri.import_tool.call_args[0][0]._id)
+        self.assertEqual(u'mymount', gri.import_tool.call_args[1]['mount_point'])
+        self.assertEqual(u'mylabel', gri.import_tool.call_args[1]['mount_label'])