summaryrefslogtreecommitdiff
path: root/morphlib/sourcemanager.py
diff options
context:
space:
mode:
Diffstat (limited to 'morphlib/sourcemanager.py')
-rw-r--r--morphlib/sourcemanager.py286
1 files changed, 0 insertions, 286 deletions
diff --git a/morphlib/sourcemanager.py b/morphlib/sourcemanager.py
deleted file mode 100644
index a1a350ca..00000000
--- a/morphlib/sourcemanager.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# Copyright (C) 2012 Codethink Limited
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-
-import os
-import urlparse
-import urllib2
-import shutil
-import string
-
-import morphlib
-
-
-gitscheme=["git",]
-urlparse.uses_relative.extend(gitscheme)
-urlparse.uses_netloc.extend(gitscheme)
-urlparse.uses_params.extend(gitscheme)
-urlparse.uses_query.extend(gitscheme)
-urlparse.uses_fragment.extend(gitscheme)
-
-
-def quote_url(url):
- ''' Convert URIs to strings that only contain digits, letters, % and _.
-
- NOTE: When changing the code of this function, make sure to also apply
- the same to the quote_url() function of lorry. Otherwise the git bundles
- generated by lorry may no longer be found by morph.
-
- '''
- valid_chars = string.digits + string.letters + '%_'
- transl = lambda x: x if x in valid_chars else '_'
- return ''.join([transl(x) for x in url])
-
-
-class RepositoryUpdateError(Exception): # pragma: no cover
-
- def __init__(self, repo, ref, error):
- Exception.__init__(self, 'Failed to update %s:%s: %s' %
- (repo, ref, error))
-
-
-class RepositoryFetchError(Exception):
-
- def __init__(self, repo):
- Exception.__init__(self, 'Failed to fetch %s' % repo)
-
-
-class SourceManager(object):
-
- def __init__(self, app, cachedir=None, update=True):
- self.real_msg = app.msg
- self.settings = app.settings
- self.cached_treeishes = {}
- self.cache_dir = cachedir
- self.update = update
- if not self.cache_dir:
- self.cache_dir = os.path.join(app.settings['cachedir'], 'gits')
- self.indent = 0
-
- def indent_more(self):
- self.indent += 1
-
- def indent_less(self):
- self.indent -= 1
-
- def msg(self, text):
- spaces = ' ' * self.indent
- self.real_msg('%s%s' % (spaces, text))
-
- def _wget(self, url, filename): # pragma: no cover
- # the following doesn't work during bootstrapping
- # ex = morphlib.execute.Execute(self.cache_dir, msg=self.msg)
- # ex.runv(['wget', '-c', url])
- # so we do it poorly in pure Python instead
- source_handle = urllib2.urlopen(url)
- target_handle = open(filename, 'wb')
-
- data = source_handle.read(4096)
- while data:
- target_handle.write(data)
- data = source_handle.read(4096)
-
- source_handle.close()
- target_handle.close()
-
- return filename
-
- def _cache_repo_from_bundle(self, server, repo_url):
- quoted_url = quote_url(repo_url)
- cached_repo = os.path.join(self.cache_dir, quoted_url)
- bundle_name = '%s.bndl' % quoted_url
- bundle_url = server + bundle_name
- bundle = os.path.join(self.cache_dir, bundle_name)
- self.msg('Trying to fetch bundle %s' % bundle_url)
- request = urllib2.Request(bundle_url)
- try:
- urllib2.urlopen(request)
- try:
- self._wget(bundle_url, bundle)
- self.msg('Extracting bundle %s into %s' %
- (bundle, cached_repo))
- try:
- os.mkdir(cached_repo)
- morphlib.git.extract_bundle(cached_repo, bundle,
- self.msg)
- self.msg('Setting origin to %s' % repo_url)
- morphlib.git.set_remote(cached_repo, 'origin',
- repo_url, self.msg)
- return cached_repo, None
- except morphlib.execute.CommandFailure, e: # pragma: no cover
- if os.path.exists(cached_repo):
- shutil.rmtree(cached_repo)
- self.msg('Unable to extract bundle %s' % bundle)
- return None, 'Unable to extract bundle %s: %s' % (bundle,
- e)
- finally:
- if os.path.exists(bundle):
- os.remove(bundle)
- except morphlib.execute.CommandFailure, e: # pragma: no cover
- return None, 'Unable to fetch bundle %s: %s' % (bundle, e)
- except urllib2.URLError, e:
- return None, 'Unable to fetch bundle %s: %s' % (bundle_url, e)
-
- def _cache_repo_from_url(self, repo_url):
- # quote the URL and calculate the location for the cached repo
- quoted_url = quote_url(repo_url)
- cached_repo = os.path.join(self.cache_dir, quoted_url)
-
- if os.path.exists(cached_repo): # pragma: no cover
- # the cache location exists, assume this is what we want
- self.msg('Using cached clone %s of %s' % (cached_repo, repo_url))
- return cached_repo, None
- else:
- # bundle server did not have a bundle for the repo
- self.msg('Trying to clone %s into %s' % (repo_url, cached_repo))
- try:
- morphlib.git.clone(cached_repo, repo_url, self.msg)
- return cached_repo, None
- except morphlib.execute.CommandFailure, e:
- if os.path.exists(cached_repo): # pragma: no cover
- shutil.rmtree(cached_repo)
- return None, 'Unable to clone from %s: %s' % (repo_url, e)
-
- def _cache_repo_from_base_urls(self, repo, ref):
- self.msg('Checking repository %s' % repo)
- self.indent_more()
-
- def fixup_url(url):
- return (url if url.endswith('/') else url + '/')
-
- # create absolute repo URLs
- repo_urls = [urlparse.urljoin(fixup_url(x), repo)
- for x in self.settings['git-base-url']]
-
- orig_url = None
- cached_repo = None
- errors = []
-
- # check if we have a cached version of the repo
- for repo_url in repo_urls:
- quoted_url = quote_url(repo_url)
- cached_repo_dirname = os.path.join(self.cache_dir, quoted_url)
- if os.path.exists(cached_repo_dirname):
- orig_url = repo_url
- cached_repo = cached_repo_dirname
- break
-
- # first pass, try all base URLs with the bundle server
- if not cached_repo and self.settings['bundle-server']:
- server = fixup_url(self.settings['bundle-server'])
-
- for repo_url in repo_urls:
- cached_repo, error = self._cache_repo_from_bundle(server,
- repo_url)
- if cached_repo:
- orig_url = repo_url
- break
- else:
- errors.append(error)
-
- # second pass, try cloning from base URLs directly
- if not cached_repo:
- # try all URLs to find or obtain a cached clone of the repo
- for repo_url in repo_urls:
- cached_repo, error = self._cache_repo_from_url(repo_url)
- if cached_repo:
- orig_url = repo_url
- break
- else:
- errors.append(error)
-
- if cached_repo:
- # we have a cached version of the repo now
- if self.update:
- # we are supposed to update 'origin', so do that now
- try:
- self.msg('Updating %s' % cached_repo)
- morphlib.git.update_remote(cached_repo, 'origin',
- self.msg)
- except morphlib.execute.CommandFailure, e: # pragma: no cover
- self.msg('Failed to update origin: %s' % e)
- self.indent_less()
- # ignore remote update failures during bootstrap
- if not self.settings['bootstrap']:
- raise RepositoryUpdateError(repo, ref, e)
- else: # pragma: no cover
- self.msg('Assuming cached repository %s is up to date' %
- cached_repo)
- else: # pragma: no cover
- # cloning using all individual base URLs failed
-
- # print all the errors at once to give the user an overview
- # over what went wrong in which order
- for error in errors:
- self.msg(error)
-
- self.indent_less()
- raise RepositoryFetchError(repo)
-
- # we should have a cached version of the repo now, return a treeish
- # for the repo and ref tuple
- treeish = morphlib.git.Treeish(cached_repo, orig_url, ref, self.msg)
- self.indent_less()
- return treeish
-
- def _resolve_submodules(self, treeish): # pragma: no cover
- self.indent_more()
-
- # resolve submodules
- treeish.submodules = morphlib.git.Submodules(treeish.repo,
- treeish.ref,
- self.msg)
- try:
- # load submodules from .gitmodules
- treeish.submodules.load()
-
- # resolve the tree-ishes for all submodules recursively
- for submodule in treeish.submodules: # pragma: no cover
- submodule.treeish = self.get_treeish(submodule.url,
- submodule.commit)
- except morphlib.git.NoModulesFileError:
- # this is not really an error, the repository simply
- # does not specify any git submodules
- pass
-
- self.indent_less()
-
- def get_treeish(self, repo, ref):
- '''Returns a Treeish for a URL or repo name with a given reference.
-
- If the source hasn't been cloned yet, this will fetch it, either using
- clone or by fetching a bundle.
-
- Raises morphlib.git.InvalidReferenceError if the reference cannot be
- found. Raises morphlib.sourcemanager.RepositoryUpdateError if the
- repository cannot be cloned or updated.
-
- '''
-
- if (repo, ref) not in self.cached_treeishes: # pragma: no cover
- # load the corresponding treeish on demand
- treeish = self._cache_repo_from_base_urls(repo, ref)
-
- # have a treeish now, cache it to avoid loading it twice
- self.cached_treeishes[(repo, ref)] = treeish
-
- # load tree-ishes for submodules, if necessary and desired
- if self.settings['ignore-submodules']:
- treeish.submodules = []
- else:
- self._resolve_submodules(treeish)
-
- # we should now have a cached treeish to use now
- return self.cached_treeishes[(repo, ref)] # pragma: no cover