launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #04098
[Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
Xaav has proposed merging lp:~xaav/loggerhead/export-tarball into lp:loggerhead.
Requested reviews:
Gavin Panella (allenap)
Martin Albisetti (beuno)
Vincent Ladeuil (vila)
Launchpad code reviewers (launchpad-reviewers): code
Robert Collins (lifeless)
Related bugs:
Bug #240580 in loggerhead: "Ability to download a tarball for a revision"
https://bugs.launchpad.net/loggerhead/+bug/240580
For more details, see:
https://code.launchpad.net/~xaav/loggerhead/export-tarball/+merge/66408
This branch **may** accomplish exporting the tarball using chunked transfer encoding. The code all looks to be correct, but I have not tested it, so I would like your opinion.
Thanks!
--
https://code.launchpad.net/~xaav/loggerhead/export-tarball/+merge/66408
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~xaav/loggerhead/export-tarball into lp:loggerhead.
=== modified file 'loggerhead/apps/branch.py'
--- loggerhead/apps/branch.py 2011-06-28 16:09:37 +0000
+++ loggerhead/apps/branch.py 2011-06-30 02:59:24 +0000
@@ -33,7 +33,7 @@
from loggerhead.controllers.atom_ui import AtomUI
from loggerhead.controllers.changelog_ui import ChangeLogUI
from loggerhead.controllers.diff_ui import DiffUI
-from loggerhead.controllers.download_ui import DownloadUI
+from loggerhead.controllers.download_ui import DownloadUI, DownloadTarballUI
from loggerhead.controllers.filediff_ui import FileDiffUI
from loggerhead.controllers.inventory_ui import InventoryUI
from loggerhead.controllers.revision_ui import RevisionUI
@@ -49,7 +49,7 @@
def __init__(self, branch, friendly_name=None, config={},
graph_cache=None, branch_link=None, is_root=False,
- served_url=_DEFAULT, use_cdn=False):
+ served_url=_DEFAULT, use_cdn=False, export_tarballs=True):
self.branch = branch
self._config = config
self.friendly_name = friendly_name
@@ -61,6 +61,7 @@
self.is_root = is_root
self.served_url = served_url
self.use_cdn = use_cdn
+ self.export_tarballs = export_tarballs
def get_history(self):
file_cache = None
@@ -126,6 +127,7 @@
'revision': RevisionUI,
'search': SearchUI,
'view': ViewUI,
+ 'tarball': DownloadTarballUI,
}
def last_updated(self):
@@ -176,11 +178,20 @@
self.branch.lock_read()
try:
try:
+<<<<<<< TREE
c = self.lookup_app(environ)
return c(environ, start_response)
+=======
+ c = cls(self, self.get_history)
+ to_ret = c(environ, start_response)
+>>>>>>> MERGE-SOURCE
except:
environ['exc_info'] = sys.exc_info()
environ['branch'] = self
raise
+ if type(to_ret) == type(httpexceptions.HTTPSeeOther('/')):
+ raise to_ret
+ else:
+ return to_ret
finally:
self.branch.unlock()
=== modified file 'loggerhead/config.py'
--- loggerhead/config.py 2010-05-12 14:38:05 +0000
+++ loggerhead/config.py 2011-06-30 02:59:24 +0000
@@ -36,6 +36,7 @@
use_cdn=False,
sql_dir=None,
allow_writes=False,
+ export_tarballs=True,
)
parser.add_option("--user-dirs", action="store_true",
help="Serve user directories as ~user.")
@@ -75,6 +76,8 @@
help="The directory to place the SQL cache in")
parser.add_option("--allow-writes", action="store_true",
help="Allow writing to the Bazaar server.")
+ parser.add_option("--export-tarballs", action="store_true",
+ help="Allow exporting revisions to tarballs.")
return parser
=== modified file 'loggerhead/controllers/__init__.py'
--- loggerhead/controllers/__init__.py 2011-06-28 16:09:37 +0000
+++ loggerhead/controllers/__init__.py 2011-06-30 02:59:24 +0000
@@ -21,7 +21,7 @@
import simplejson
import time
-from paste.httpexceptions import HTTPNotFound
+from paste.httpexceptions import HTTPNotFound, HTTPSeeOther
from paste.request import path_info_pop, parse_querystring
from loggerhead import util
=== modified file 'loggerhead/controllers/download_ui.py'
--- loggerhead/controllers/download_ui.py 2010-05-05 19:03:40 +0000
+++ loggerhead/controllers/download_ui.py 2011-06-30 02:59:24 +0000
@@ -19,46 +19,51 @@
import logging
import mimetypes
+import os
import urllib
from paste import httpexceptions
from paste.request import path_info_pop
from loggerhead.controllers import TemplatedBranchView
+from loggerhead.exporter import export_archive
log = logging.getLogger("loggerhead.controllers")
class DownloadUI (TemplatedBranchView):
- def __call__(self, environ, start_response):
- # /download/<rev_id>/<file_id>/[filename]
-
- h = self._history
-
+ def encode_filename(self, filename):
+
+ return urllib.quote(filename.encode('utf-8'))
+
+ def get_args(self, environ):
args = []
while True:
arg = path_info_pop(environ)
if arg is None:
break
args.append(arg)
+ return args
+ def __call__(self, environ, start_response):
+ # /download/<rev_id>/<file_id>/[filename]
+ h = self._history
+ args = self.get_args(environ)
if len(args) < 2:
raise httpexceptions.HTTPMovedPermanently(
self._branch.absolute_url('/changes'))
-
revid = h.fix_revid(args[0])
file_id = args[1]
path, filename, content = h.get_file(file_id, revid)
mime_type, encoding = mimetypes.guess_type(filename)
if mime_type is None:
mime_type = 'application/octet-stream'
-
self.log.info('/download %s @ %s (%d bytes)',
path,
h.get_revno(revid),
len(content))
- encoded_filename = urllib.quote(filename.encode('utf-8'))
+ encoded_filename = self.encode_filename(filename)
headers = [
('Content-Type', mime_type),
('Content-Length', str(len(content))),
@@ -67,3 +72,29 @@
]
start_response('200 OK', headers)
return [content]
+
+
+class DownloadTarballUI(DownloadUI):
+
+ def __call__(self, environ, start_response):
+ """Stream a tarball from a bazaar branch."""
+ # Tried to re-use code from downloadui, not very successful
+ format = "tar"
+ history = self._history
+ self.args = self.get_args(environ)
+ if len(self.args):
+ revid = history.fix_revid(self.args[0])
+ else:
+ revid = self.get_revid()
+ if self._branch.export_tarballs:
+ root = 'branch'
+ encoded_filename = self.encode_filename(root + format)
+ headers = [
+ ('Content-Type', 'application/octet-stream'),
+ ('Content-Disposition',
+ "attachment; filename*=utf-8''%s" % (encoded_filename,)),
+ ]
+ start_response('200 OK', headers)
+ return export_archive(history, root, revid, format)
+ else:
+ raise httpexceptions.HTTPSeeOther('/')
=== modified file 'loggerhead/controllers/revision_ui.py'
--- loggerhead/controllers/revision_ui.py 2011-06-27 17:11:24 +0000
+++ loggerhead/controllers/revision_ui.py 2011-06-30 02:59:24 +0000
@@ -136,9 +136,19 @@
self._branch.friendly_name,
self._branch.is_root,
'changes'))
+<<<<<<< TREE
values.update({
'history': self._history,
+=======
+ can_export = self._branch.export_tarballs
+ return {
+ 'branch': self._branch,
+ 'revid': revid,
+ 'change': change,
+ 'file_changes': file_changes,
+ 'diff_chunks': diff_chunks,
+>>>>>>> MERGE-SOURCE
'link_data': simplejson.dumps(link_data),
'json_specific_path': simplejson.dumps(path),
'path_to_id': simplejson.dumps(path_to_id),
@@ -149,6 +159,15 @@
'filter_file_id': filter_file_id,
'diff_chunks': diff_chunks,
'query': query,
+<<<<<<< TREE
'specific_path': path,
'start_revid': start_revid,
})
+=======
+ 'remember': remember,
+ 'compare_revid': compare_revid,
+ 'url': self._branch.context_url,
+ 'directory_breadcrumbs': directory_breadcrumbs,
+ 'can_export': can_export,
+ }
+>>>>>>> MERGE-SOURCE
=== added file 'loggerhead/exporter.py'
--- loggerhead/exporter.py 1970-01-01 00:00:00 +0000
+++ loggerhead/exporter.py 2011-06-30 02:59:24 +0000
@@ -0,0 +1,46 @@
+# Copyright (C) 2011 Canonical Ltd
+#
+# 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; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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.
+#
+"""Exports an archive from a bazaar branch"""
+
+from bzrlib.export import get_export_generator
+
+
+class ExporterFileObject(object):
+
+ def __init__(self):
+ self._buffer = []
+
+ def write(self, s):
+ self._buffer.append(s)
+
+ def get_buffer(self):
+ try:
+ return ''.join(self._buffer)
+ finally:
+ self._buffer = []
+
+
+def export_archive(history, root, revid, format=".tar.gz"):
+ """Export tree contents to an archive
+
+ :param history: Instance of history to export
+ :param root: Root location inside the archive.
+ :param revid: Revision to export
+ :param format: Format of the archive
+ """
+ fileobj = ExporterFileObject()
+ tree = history._branch.repository.revision_tree(revid)
+ for _ in get_export_generator(tree=tree, root=root, fileobj=fileobj, format=format):
+ yield fileobj.get_buffer()
+ # Might have additonal contents written
+ yield fileobj.get_buffer()
=== modified file 'loggerhead/history.py'
--- loggerhead/history.py 2011-03-25 13:09:10 +0000
+++ loggerhead/history.py 2011-06-30 02:59:24 +0000
@@ -33,6 +33,7 @@
import re
import textwrap
import threading
+import tarfile
from bzrlib import tag
import bzrlib.branch
@@ -44,6 +45,7 @@
from loggerhead import search
from loggerhead import util
from loggerhead.wholehistory import compute_whole_history_data
+from bzrlib.export.tar_exporter import export_tarball
def is_branch(folder):
@@ -816,4 +818,6 @@
renamed=sorted(reporter.renamed, key=lambda x: x.new_filename),
removed=sorted(reporter.removed, key=lambda x: x.filename),
modified=sorted(reporter.modified, key=lambda x: x.filename),
- text_changes=sorted(reporter.text_changes, key=lambda x: x.filename))
+ text_changes=sorted(reporter.text_changes,
+ key=lambda x: x.filename))
+
=== added directory 'loggerhead/static/downloads'
=== modified file 'loggerhead/templates/revision.pt'
--- loggerhead/templates/revision.pt 2011-06-28 16:51:55 +0000
+++ loggerhead/templates/revision.pt 2011-06-30 02:59:24 +0000
@@ -84,7 +84,10 @@
tal:attributes="href python:url(['/diff', change.revno], clear=1)">download diff</a>
<a tal:condition="python:compare_revid is not None"
tal:attributes="href python:url(['/diff', change.revno, history.get_revno(compare_revid)], clear=1)">download diff</a>
- </li>
+ </li>
+ <li tal:condition="python:can_export">
+ <a tal:attributes="href python:url(['/tarball', change.revno])">Download tarball</a>
+ </li>
<li id="last"><a tal:attributes="href python:url(['/changes', change.revno]);
title string:view history from revision ${change/revno}"
tal:content="string:view history from revision ${change/revno}"></a></li>
=== modified file 'loggerhead/tests/test_controllers.py'
--- loggerhead/tests/test_controllers.py 2011-06-28 16:06:12 +0000
+++ loggerhead/tests/test_controllers.py 2011-06-30 02:59:24 +0000
@@ -1,4 +1,14 @@
+<<<<<<< TREE
import simplejson
+=======
+from cStringIO import StringIO
+import logging
+import tarfile
+
+from paste.httpexceptions import HTTPServerError
+
+from bzrlib import errors
+>>>>>>> MERGE-SOURCE
from loggerhead.apps.branch import BranchWSGIApp
from loggerhead.controllers.annotate_ui import AnnotateUI
@@ -135,6 +145,7 @@
kwargs={'file_id': 'file_id'}, headers={})
annotated = annotate_info['annotated']
self.assertEqual(2, len(annotated))
+<<<<<<< TREE
self.assertEqual('2', annotated[1].change.revno)
self.assertEqual('1', annotated[2].change.revno)
@@ -204,3 +215,23 @@
revlog_ui = branch_app.lookup_app(env)
self.assertOkJsonResponse(revlog_ui, env)
+=======
+ self.assertEqual('2', annotated[0].change.revno)
+ self.assertEqual('1', annotated[1].change.revno)
+
+
+class TestDownloadTarballUI(BasicTests):
+
+ def setUp(self):
+ super(TestDownloadTarballUI, self).setUp()
+ self.createBranch()
+
+ def test_download_tarball(self):
+ app = self.setUpLoggerhead()
+ res = app.get('/tarball')
+ f = open('tarball', 'w')
+ f.write(res)
+ f.close()
+ self.failIf(not tarfile.is_tarfile('tarball'))
+ # Now check the content. TBC
+>>>>>>> MERGE-SOURCE
Follow ups
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-29
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-29
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-12
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-11
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-10
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Martin Pool, 2011-07-10
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-10
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Xaav, 2011-07-07
-
Re: [Merge] lp:~xaav/loggerhead/export-tarball into lp:loggerhead
From: Gavin Panella, 2011-07-07