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/ </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'])