Hi Tim,
Cool to see this arrive! Sadly, I'm not sure it can land yet, because
of the new sourcecode branch. Let's talk to a LOSA about that.
> === modified file 'lib/lp/code/stories/codeimport/xx-create-codeimport.txt'
I think it would be good to create a Hg import in this test.
> --- lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-01-11 22:40:42 +0000
> +++ lib/lp/code/stories/codeimport/xx-create-codeimport.txt 2010-01-12 22:39:29 +0000
> @@ -32,6 +32,7 @@
>
> >>> print_radio_button_field(browser.contents, "rcs_type")
> ( ) Git
> + ( ) Mercurial
> (*) Subversion
> ( ) CVS
>
> @@ -66,21 +67,14 @@
> Requesting a Git import
> =======================
>
> -The default foreign VCS type is Subversion.
> -
> - >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
> - >>> print_radio_button_field(browser.contents, "rcs_type")
> - ( ) Git
> - (*) Subversion
> - ( ) CVS
> -
> The user is required to enter a project that the import is for,
> a name for the import branch, and a subversion branch location.
>
> + >>> browser.open("http://code.launchpad.dev/+code-imports/+new")
> >>> browser.getControl('Project').value = "firefox"
> >>> browser.getControl('Branch Name').value = "git-import"
> >>> browser.getControl('Git').click()
> - >>> browser.getControl('Repo URL').value = (
> + >>> browser.getControl('Repo URL', index=0).value = (
> ... "git://example.com/firefox.git")
> >>> browser.getControl('Request Import').click()
>
> === modified file 'lib/lp/code/templates/branch-import-details.pt'
> --- lib/lp/code/templates/branch-import-details.pt 2010-01-11 21:21:00 +0000
> +++ lib/lp/code/templates/branch-import-details.pt 2010-01-12 22:39:29 +0000
> @@ -45,6 +45,12 @@
>
>
>
> +
>
> This branch is an import of the
> === modified file 'lib/lp/codehosting/codeimport/tests/servers.py'
> --- lib/lp/codehosting/codeimport/tests/servers.py 2009-12-04 04:08:12 +0000
> +++ lib/lp/codehosting/codeimport/tests/servers.py 2010-01-12 22:39:29 +0000
> @@ -8,6 +8,7 @@
> __all__ = [
> 'CVSServer',
> 'GitServer',
> + 'MercurialServer',
> 'SubversionServer',
> ]
>
> @@ -210,3 +211,23 @@
> builder.finish()
> finally:
> os.chdir(wd)
> +
> +
> +class MercurialServer(Server):
> +
> + def __init__(self, repo_url):
> + super(MercurialServer, self).__init__()
> + self.repo_url = repo_url
> +
> + def makeRepo(self, tree_contents):
> + from mercurial.ui import ui
> + from mercurial.localrepo import localrepository
> + repo = localrepository(ui(), self.repo_url, create=1)
> + for filename, contents in tree_contents:
> + f = open(os.path.join(self.repo_url, filename), 'w')
> + try:
> + f.write(contents)
> + finally:
> + f.close()
> + repo.add([filename])
> + repo.commit(text='', user='jane Foo ')
> === modified file 'lib/lp/codehosting/codeimport/tests/test_worker.py'
> --- lib/lp/codehosting/codeimport/tests/test_worker.py 2010-01-11 23:39:19 +0000
> +++ lib/lp/codehosting/codeimport/tests/test_worker.py 2010-01-12 22:39:29 +0000
> @@ -32,10 +32,10 @@
> from lp.codehosting import load_optional_plugin
> from lp.codehosting.codeimport.worker import (
> BazaarBranchStore, BzrSvnImportWorker, CSCVSImportWorker,
> - ForeignTreeStore, GitImportWorker, ImportDataStore, ImportWorker,
> - get_default_bazaar_branch_store)
> + ForeignTreeStore, GitImportWorker, HgImportWorker, ImportDataStore,
> + ImportWorker, get_default_bazaar_branch_store)
> from lp.codehosting.codeimport.tests.servers import (
> - CVSServer, GitServer, SubversionServer)
> + CVSServer, GitServer, MercurialServer, SubversionServer)
> from lp.codehosting.tests.helpers import (
> create_branch_with_one_revision)
> from lp.testing.factory import LaunchpadObjectFactory
> @@ -822,7 +822,7 @@
> # import should be rejected.
> args = {'rcstype': self.rcstype}
> reference_url = self.createBranchReference()
> - if self.rcstype in ('git', 'bzr-svn'):
> + if self.rcstype in ('git', 'bzr-svn', 'hg'):
> args['url'] = reference_url
> else:
> raise AssertionError("unexpected rcs_type %r" % self.rcs_type)
> @@ -886,6 +886,57 @@
> rcstype='git', url=repository_path)
>
>
> +class TestMercurialImport(WorkerTest, TestActualImportMixin,
> + PullingImportWorkerTests):
> +
> + rcstype = 'hg'
> +
> + def setUp(self):
> + super(TestMercurialImport, self).setUp()
> + load_optional_plugin('hg')
> + self.setUpImport()
> +
> + def tearDown(self):
> + """Clear bzr-hg's cache of tdb connections.
They're still sqlite connections, right? Not tdb.
> + This is rather obscure: different test runs tend to re-use the same
> + paths on disk, which confuses bzr-hg as it keeps a cache that maps
> + paths to database connections, which happily returns the connection
> + that corresponds to a path that no longer exists.
> + """
> + from bzrlib.plugins.hg.idmap import mapdbs
> + mapdbs().clear()
> + WorkerTest.tearDown(self)
> +
> + def makeImportWorker(self, source_details):
> + """Make a new `ImportWorker`."""
> + return HgImportWorker(
> + source_details, self.get_transport('import_data'),
> + self.bazaar_store, logging.getLogger())
> +
> + def makeForeignCommit(self, source_details):
> + """Change the foreign tree, generating exactly one commit."""
> + from mercurial.ui import ui
> + from mercurial.localrepo import localrepository
> + repo = localrepository(ui(), source_details.url)
> + repo.commit(text="hello world!", user="Jane Random Hacker", force=1)
> + self.foreign_commit_count += 1
> +
> + def makeSourceDetails(self, branch_name, files):
> + """Make a Mercurial `CodeImportSourceDetails` pointing at a real repo.
> + """
> + repository_path = self.makeTemporaryDirectory()
> + hg_server = MercurialServer(repository_path)
> + hg_server.setUp()
> + self.addCleanup(hg_server.tearDown)
> +
> + hg_server.makeRepo(files)
> + self.foreign_commit_count = 1
> +
> + return self.factory.makeCodeImportSourceDetails(
> + rcstype='hg', url=repository_path)
> +
> +
> class TestBzrSvnImport(WorkerTest, SubversionImportHelpers,
> TestActualImportMixin, PullingImportWorkerTests):
>
> === modified file 'lib/lp/codehosting/codeimport/tests/test_workermonitor.py'
> --- lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-01-11 21:21:00 +0000
> +++ lib/lp/codehosting/codeimport/tests/test_workermonitor.py 2010-01-12 22:39:29 +0000
> @@ -43,7 +43,7 @@
> CodeImportWorkerMonitor, CodeImportWorkerMonitorProtocol, ExitQuietly,
> read_only_transaction)
> from lp.codehosting.codeimport.tests.servers import (
> - CVSServer, GitServer, SubversionServer)
> + CVSServer, GitServer, MercurialServer, SubversionServer)
> from lp.codehosting.codeimport.tests.test_worker import (
> clean_up_default_stores_for_import)
> from lp.testing import login, logout
> @@ -501,6 +501,18 @@
>
> return self.factory.makeCodeImport(git_repo_url=self.repo_path)
>
> + def makeHgCodeImport(self):
> + """Make a `CodeImport` that points to a real Git repository."""
It's the obligatory copy-pasted docstring! Thanks for leaving me one :-)
> + load_optional_plugin('hg')
> + self.hg_server = MercurialServer(self.repo_path)
> + self.hg_server.setUp()
> + self.addCleanup(self.hg_server.tearDown)
> +
> + self.hg_server.makeRepo([('README', 'contents')])
> + self.foreign_commit_count = 1
> +
> + return self.factory.makeCodeImport(hg_repo_url=self.repo_path)
> +
> def getStartedJobForImport(self, code_import):
> """Get a started `CodeImportJob` for `code_import`.
>
> @@ -592,6 +604,15 @@
> result = self.performImport(job_id)
> return result.addCallback(self.assertImported, code_import_id)
>
> + def test_import_hg(self):
> + # Create a Mercurial CodeImport and import it.
> + job = self.getStartedJobForImport(self.makeHgCodeImport())
> + code_import_id = job.code_import.id
> + job_id = job.id
> + self.layer.txn.commit()
> + result = self.performImport(job_id)
> + return result.addCallback(self.assertImported, code_import_id)
> +
> def test_import_bzrsvn(self):
> # Create a Subversion-via-bzr-svn CodeImport and import it.
> job = self.getStartedJobForImport(self.makeBzrSvnCodeImport())
> === modified file 'lib/lp/codehosting/codeimport/worker.py'
> --- lib/lp/codehosting/codeimport/worker.py 2010-01-12 03:58:38 +0000
> +++ lib/lp/codehosting/codeimport/worker.py 2010-01-12 22:39:29 +0000
> @@ -11,6 +11,7 @@
> 'CodeImportSourceDetails',
> 'ForeignTreeStore',
> 'GitImportWorker',
> + 'HgImportWorker',
> 'ImportWorker',
> 'get_default_bazaar_branch_store',
> ]
> @@ -111,7 +112,7 @@
> 'git'], None otherwise.
> :ivar cvs_root: The $CVSROOT if rcstype == 'cvs', None otherwise.
> :ivar cvs_module: The CVS module if rcstype == 'cvs', None otherwise.
> - """
> + """
A space went missing here?
> def __init__(self, branch_id, rcstype, url=None, cvs_root=None,
> cvs_module=None):
> @@ -126,7 +127,7 @@
> """Convert command line-style arguments to an instance."""
> branch_id = int(arguments.pop(0))
> rcstype = arguments.pop(0)
> - if rcstype in ['svn', 'bzr-svn', 'git']:
> + if rcstype in ['svn', 'bzr-svn', 'git', 'hg']:
> [url] = arguments
> cvs_root = cvs_module = None
> elif rcstype == 'cvs':
> @@ -151,6 +152,8 @@
> cvs_module=str(code_import.cvs_module))
> elif code_import.rcs_type == RevisionControlSystems.GIT:
> return cls(branch_id, 'git', str(code_import.url))
> + elif code_import.rcs_type == RevisionControlSystems.HG:
> + return cls(branch_id, 'hg', str(code_import.url))
> else:
> raise AssertionError("Unknown rcstype %r." % code_import.rcs_type)
>
> @@ -158,7 +161,7 @@
> """Return a list of arguments suitable for passing to a child process.
> """
> result = [str(self.branch_id), self.rcstype]
> - if self.rcstype in ['svn', 'bzr-svn', 'git']:
> + if self.rcstype in ['svn', 'bzr-svn', 'git', 'hg']:
> result.append(self.url)
> elif self.rcstype == 'cvs':
> result.append(self.cvs_root)
> @@ -544,6 +547,45 @@
> 'git.db', bazaar_tree.branch.repository._transport)
>
>
> +class HgImportWorker(PullingImportWorker):
> + """An import worker for Mercurial imports.
> +
> + The only behaviour we add is preserving the 'hg.db' map between runs.
Given it's not called hg.db after all, maybe the docstring should be
more generic.
> + """
> +
> + db_file = 'hg-v2.db'
The handling of db_file could be hoisted to the superclass perhaps?
Maybe not now.
> + @property
> + def format_classes(self):
> + """See `PullingImportWorker.opening_format`."""
> + # We only return HgLocalRepository for tests.
> + from bzrlib.plugins.hg import HgBzrDirFormat
> + return [HgBzrDirFormat]
> +
> + def getBazaarWorkingTree(self):
> + """See `ImportWorker.getBazaarWorkingTree`.
> +
> + In addition to the superclass' behaviour, we retrieve the 'hg.db'
> + map from the import data store and put it where bzr-hg will find
> + it in the Bazaar tree, that is at '.bzr/repository/hg.db'.
> + """
> + tree = PullingImportWorker.getBazaarWorkingTree(self)
> + self.import_data_store.fetch(
> + self.db_file, tree.branch.repository._transport)
> + return tree
> +
> + def pushBazaarWorkingTree(self, bazaar_tree):
> + """See `ImportWorker.pushBazaarWorkingTree`.
> +
> + In addition to the superclass' behaviour, we store the 'hg.db' shamap
> + that bzr-hg will have created at .bzr/repository/hg.db into the
> + import data store.
> + """
> + PullingImportWorker.pushBazaarWorkingTree(self, bazaar_tree)
> + self.import_data_store.put(
> + self.db_file, bazaar_tree.branch.repository._transport)
> +
> +
> class BzrSvnImportWorker(PullingImportWorker):
> """An import worker for importing Subversion via bzr-svn."""
>
> === modified file 'utilities/sourcedeps.conf'
> --- utilities/sourcedeps.conf 2009-12-02 02:46:05 +0000
> +++ utilities/sourcedeps.conf 2010-01-12 22:39:29 +0000
> @@ -1,4 +1,5 @@
> bzr-git lp:~launchpad-pqm/bzr-git/devel;revno=248
> +bzr-hg lp:bzr-hg
> bzr-loom lp:~launchpad-pqm/bzr-loom/trunk;revno=47
> bzr-svn lp:~launchpad-pqm/bzr-svn/devel;revno=2706
> cscvs lp:~launchpad-pqm/launchpad-cscvs/devel;revno=430
I'm not sure that tracking lp:bzr-hg directly is that good an idea --
what if the database filename changes again? We probably need to set
up a ~launchpad-pqm/bzr-hg/devel branch and merge to it frequently. I
think you also need to mangle the config-manager config which is in
the lp:lp-production-configs branch because I don't think deployment
is driven from the sourcedeps.conf file yet -- basically, we need to
talk to a LOSA about adding the new sourcecode branch :/
Cheers,
mwh