summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJannis Pohlmann <jannis.pohlmann@codethink.co.uk>2012-04-17 15:29:51 +0100
committerJannis Pohlmann <jannis.pohlmann@codethink.co.uk>2012-04-17 15:29:51 +0100
commit9ba282863b41bea8d9fe990a9aba92677e2f4501 (patch)
treec2067aed613ee82879179483f421c37f64188715
downloadmorph-cache-server-9ba282863b41bea8d9fe990a9aba92677e2f4501.tar.gz
Initial import implementing /files and /sha1s.
-rw-r--r--README21
-rwxr-xr-xmorph-cache-server83
-rw-r--r--morphcacheserver/__init__.py17
-rw-r--r--morphcacheserver/repocache.py92
-rw-r--r--setup.py46
5 files changed, 259 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..a57e9a0
--- /dev/null
+++ b/README
@@ -0,0 +1,21 @@
+README for morph-cache-server
+=============================
+
+Legalese
+--------
+
+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.
+
diff --git a/morph-cache-server b/morph-cache-server
new file mode 100755
index 0000000..777e627
--- /dev/null
+++ b/morph-cache-server
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+#
+# 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 cliapp
+import logging
+import urllib
+
+from bottle import Bottle, request, response, run
+from morphcacheserver.repocache import RepoCache
+
+
+defaults = {
+ 'repo-dir': '/var/cache/morph-cache-server/gits',
+}
+
+
+class MorphCacheServer(cliapp.Application):
+
+ def add_settings(self):
+ self.settings.string(['repo-dir'],
+ 'path to the repository cache directory',
+ metavar='PATH',
+ default=defaults['repo-dir'])
+
+ def process_args(self, args):
+ app = Bottle()
+
+ repo_cache = RepoCache(self, self.settings['repo-dir'])
+
+ @app.get('/sha1s')
+ def sha1():
+ repo = self._unescape_parameter(request.query.repo)
+ ref = self._unescape_parameter(request.query.ref)
+ try:
+ sha1 = repo_cache.resolve_ref(repo, ref)
+ return {
+ 'repo': '%s' % repo,
+ 'ref': '%s' % ref,
+ 'sha1': '%s' % sha1
+ }
+ except Exception, e:
+ response.status = 404
+ logging.debug('%s' % e)
+
+ @app.get('/files')
+ def file():
+ repo = self._unescape_parameter(request.query.repo)
+ ref = self._unescape_parameter(request.query.ref)
+ filename = self._unescape_parameter(request.query.filename)
+ try:
+ content = repo_cache.cat_file(repo, ref, filename)
+ response.set_header('Content-Type', 'application/octet-stream')
+ return content
+ except Exception, e:
+ response.status = 404
+ logging.debug('%s' % e)
+
+ root = Bottle()
+ root.mount(app, '/1.0')
+
+ run(root, host='0.0.0.0', port=8080, reloader=True)
+
+ def _unescape_parameter(self, param):
+ return urllib.unquote(param)
+
+
+if __name__ == '__main__':
+ MorphCacheServer().run()
diff --git a/morphcacheserver/__init__.py b/morphcacheserver/__init__.py
new file mode 100644
index 0000000..9ad5a30
--- /dev/null
+++ b/morphcacheserver/__init__.py
@@ -0,0 +1,17 @@
+# 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 repocache
diff --git a/morphcacheserver/repocache.py b/morphcacheserver/repocache.py
new file mode 100644
index 0000000..49b8200
--- /dev/null
+++ b/morphcacheserver/repocache.py
@@ -0,0 +1,92 @@
+# 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 cliapp
+import os
+import string
+
+
+class InvalidReferenceError(cliapp.AppException):
+
+ def __init__(self, repo, ref):
+ cliapp.AppException.__init__(
+ self, 'Ref %s is an invalid reference for repo %s' %
+ (ref, repo))
+
+
+class UnresolvedNamedReferenceError(cliapp.AppException):
+
+ def __init__(self, repo, ref):
+ cliapp.AppException.__init__(
+ self, 'Ref %s is not a SHA1 ref for repo %s' %
+ (ref, repo))
+
+
+class RepoCache(object):
+
+ def __init__(self, app, dirname):
+ self.app = app
+ self.dirname = dirname
+
+ def resolve_ref(self, repo_url, ref):
+ quoted_url = self._quote_url(repo_url)
+ repo_dir = os.path.join(self.dirname, quoted_url)
+ try:
+ refs = self._show_ref(repo_dir, ref).split('\n')
+ refs = [x.split() for x in refs if 'origin' in x]
+ return refs[0][0]
+ except cliapp.AppException:
+ pass
+ if not self._is_valid_sha1(ref):
+ raise InvalidReferenceError(repo_url, ref)
+ try:
+ return self._rev_list(ref).strip()
+ except:
+ raise InvalidReferenceError(repo_url, ref)
+
+ def cat_file(self, repo_url, ref, filename):
+ quoted_url = self._quote_url(repo_url)
+ repo_dir = os.path.join(self.dirname, quoted_url)
+
+ if not self._is_valid_sha1(ref):
+ raise UnresolvedNamedReferenceError(repo_url, ref)
+ try:
+ sha1 = self._rev_list(repo_dir, ref).strip()
+ except:
+ raise InvalidReferenceError(repo_url, ref)
+
+ return self._cat_file(repo_dir, sha1, filename)
+
+ def _quote_url(self, url):
+ valid_chars = string.digits + string.letters + '%_'
+ transl = lambda x: x if x in valid_chars else '_'
+ return ''.join([transl(x) for x in url])
+
+ def _show_ref(self, repo_dir, ref):
+ return self.app.runcmd(['git', 'show-ref', ref], cwd=repo_dir)
+
+ def _rev_list(self, repo_dir, ref):
+ return self.app.runcmd(
+ ['git', 'rev-list', '--no-walk', ref], cwd=repo_dir)
+
+ def _cat_file(self, repo_dir, sha1, filename):
+ return self.app.runcmd(
+ ['git', 'cat-file', 'blob', '%s:%s' % (sha1, filename)],
+ cwd=repo_dir)
+
+ def _is_valid_sha1(self, ref):
+ valid_chars = 'abcdefABCDEF0123456789'
+ return len(ref) == 40 and all([x in valid_chars for x in ref])
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..e861f39
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+#
+# 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.
+
+
+from distutils.core import setup
+
+
+setup(name='morph-cache-server',
+ description='FIXME',
+ long_description='''\
+FIXME
+''',
+ classifiers=[
+ 'Development Status :: 2 - Pre-Alpha',
+ 'Environment :: Console',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Operating System :: POSIX :: Linux',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Build Tools',
+ 'Topic :: Software Development :: Embedded Systems',
+ 'Topic :: System :: Archiving :: Packaging',
+ 'Topic :: System :: Software Distribution',
+ ],
+ author='Jannis Pohlmann',
+ author_email='jannis.pohlmann@codethink.co.uk',
+ url='http://www.baserock.org/',
+ scripts=['morph-cache-server'],
+ packages=['morphcacheserver'],
+ )