diff --git a/docs/projects.rst b/docs/projects.rst index e364cb2e6..1ff43b02c 100644 --- a/docs/projects.rst +++ b/docs/projects.rst @@ -18,12 +18,12 @@ Git versioning can be enabled by creating a `local_settings.py` file in the `sly .. code-block:: python import os - + SPEC_FACTORY = { 'PROJECT_SPEC': 'slyd.gitstorage.projectspec.ProjectSpec', 'PROJECT_MANAGER': 'slyd.gitstorage.projects.ProjectsManager', 'PARAMS': { - 'storage_backend': 'dulwich.repo.Repo', + 'storage_backend': 'fs_repo.repo.FsRepo', 'location': os.environ.get('PORTIA_DATA_DIR', SPEC_DATA_DIR) }, 'CAPABILITIES': { diff --git a/portia_server/fs_repo/__init__.py b/portia_server/fs_repo/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/portia_server/fs_repo/repo.py b/portia_server/fs_repo/repo.py new file mode 100644 index 000000000..45edda353 --- /dev/null +++ b/portia_server/fs_repo/repo.py @@ -0,0 +1,73 @@ +import errno +import os +import six +import sys +from django.conf import settings +from dulwich.object_store import PACKDIR +from dulwich.repo import ( + Repo, BASE_DIRECTORIES, CONTROLDIR, DEFAULT_REF, OBJECTDIR +) + + +def root_path(name): + return os.path.join(settings.MEDIA_ROOT, name) + + +def maybe_mkdirs(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + six.reraise(*sys.exc_info()) + + +class FsRepo(Repo): + def __init__(self, name): + self._name = name + super(FsRepo, self).__init__(root_path(name or '')) + self.bare = True + + def head(self): + """Return the SHA1 pointed at by HEAD.""" + return self.refs['refs/heads/master'] + + @classmethod + def init_bare(cls, path): + return super(FsRepo, cls).init_bare( + os.path.join(root_path(path), CONTROLDIR)) + + @classmethod + def _init_maybe_bare(cls, path, bare): + for d in BASE_DIRECTORIES: + maybe_mkdirs(os.path.join(path, *d)) + objectdir_path = os.path.join(path, OBJECTDIR) + for d in ('info', PACKDIR): + maybe_mkdirs(os.path.join(objectdir_path, d)) + ret = cls(path) + ret.refs.set_symbolic_ref(b'HEAD', DEFAULT_REF) + ret._init_files(bare) + return ret + + @classmethod + def open(cls, name): + """Open an existing repository.""" + return cls(name) + + @classmethod + def repo_exists(cls, name): + """Check if a repository exists.""" + return ( + os.path.isdir(root_path(name)) and + os.path.isdir(os.path.join(root_path(name), CONTROLDIR)) + ) + + @classmethod + def list_repos(cls): + """List all repository names.""" + return [d for d in os.listdir(settings.MEDIA_ROOT) + if cls.repo_exists(d)] + + @classmethod + def delete_repo(cls, name): + """Delete a repository.""" + os.remove(root_path(name)) diff --git a/portia_server/storage/backends.py b/portia_server/storage/backends.py index cf9c95332..3eaca50b8 100644 --- a/portia_server/storage/backends.py +++ b/portia_server/storage/backends.py @@ -17,7 +17,7 @@ try: from dulwich.diff_tree import tree_changes from dulwich.objects import Blob, Tree - from dulwich.errors import ObjectMissing + from dulwich.errors import NotGitRepository, ObjectMissing except ImportError: pass # Dulwich not required when using FS backend from six import iteritems, text_type, string_types @@ -69,8 +69,8 @@ def init_project(self): def get_projects(cls, user): # return an OrderedDict of id => name pairs try: - dirs, _ = cls('').listdir('') - return OrderedDict((project, project) for project in dirs) + projects = Repoman.list_repos() + return OrderedDict((project, project) for project in projects) except OSError as ex: if ex.errno != errno.ENOENT: six.reraise(*sys.exc_info()) @@ -194,7 +194,10 @@ def __init__(self, name, author=None): return self._tree, self._working_tree = None, None self.author = author - repo = Repoman.open_repo(name, author) + try: + repo = Repoman.open_repo(name, author) + except NotGitRepository: + repo = Repoman.create_repo(name, author) self.repo = repo self.branch = branch = (author and author.username) or DEFAULT_USER self.checkout(branch=branch) @@ -244,7 +247,7 @@ def checkout(self, commit=None, branch=None, retry=True): @classmethod def setup(cls): Repoman.setup(getattr(settings, 'GITSTORAGE_REPO_BACKEND', - 'dulwich.repo.Repo')) + 'fs_repo.repo.FsRepo')) def _open(self, name, mode='rb'): name = self.path(name) diff --git a/portia_server/storage/repoman.py b/portia_server/storage/repoman.py index be083c43e..62e8845af 100644 --- a/portia_server/storage/repoman.py +++ b/portia_server/storage/repoman.py @@ -64,7 +64,7 @@ def setup(cls, storage_backend): def create_repo(cls, repo_name, author=None): """Create a new repository named repo_name.""" if cls.storage.repo_exists(repo_name): - raise NameError() + raise NameError('Project already exists with that name') repoman = cls(author) repoman._repo = cls.storage.init_bare(repo_name) tree = Tree()