##// END OF EJS Templates
archives: optimize performance of repo archive option by delegating all logic to vcsserver....
milka -
r4536:2e292c98 default
parent child Browse files
Show More
@@ -402,8 +402,8 b' class RepoFilesView(RepoAppView):'
402 archive_dir_name = response_archive_name[:-len(ext)]
402 archive_dir_name = response_archive_name[:-len(ext)]
403
403
404 use_cached_archive = False
404 use_cached_archive = False
405 archive_cache_enabled = CONFIG.get(
405 archive_cache_dir = CONFIG.get('archive_cache_dir')
406 'archive_cache_dir') and not self.request.GET.get('no_cache')
406 archive_cache_enabled = archive_cache_dir and not self.request.GET.get('no_cache')
407 cached_archive_path = None
407 cached_archive_path = None
408
408
409 if archive_cache_enabled:
409 if archive_cache_enabled:
@@ -421,13 +421,13 b' class RepoFilesView(RepoAppView):'
421
421
422 # generate new archive, as previous was not found in the cache
422 # generate new archive, as previous was not found in the cache
423 if not use_cached_archive:
423 if not use_cached_archive:
424
424 _dir = os.path.abspath(archive_cache_dir) if archive_cache_dir else None
425 fd, archive = tempfile.mkstemp()
425 fd, archive = tempfile.mkstemp(dir=_dir)
426 log.debug('Creating new temp archive in %s', archive)
426 log.debug('Creating new temp archive in %s', archive)
427 try:
427 try:
428 commit.archive_repo(archive, archive_dir_name=archive_dir_name,
428 commit.archive_repo(archive, archive_dir_name=archive_dir_name,
429 kind=fileformat, subrepos=subrepos,
429 kind=fileformat, subrepos=subrepos,
430 archive_at_path=at_path, with_hash=with_hash)
430 archive_at_path=at_path)
431 except ImproperArchiveTypeError:
431 except ImproperArchiveTypeError:
432 return _('Unknown archive type')
432 return _('Unknown archive type')
433 if archive_cache_enabled:
433 if archive_cache_enabled:
@@ -915,8 +915,9 b' class BaseCommit(object):'
915 list of parent commits
915 list of parent commits
916
916
917 """
917 """
918 repository = None
919 branch = None
918
920
919 branch = None
920 """
921 """
921 Depending on the backend this should be set to the branch name of the
922 Depending on the backend this should be set to the branch name of the
922 commit. Backends not supporting branches on commits should leave this
923 commit. Backends not supporting branches on commits should leave this
@@ -1193,7 +1194,7 b' class BaseCommit(object):'
1193
1194
1194 def archive_repo(self, archive_dest_path, kind='tgz', subrepos=None,
1195 def archive_repo(self, archive_dest_path, kind='tgz', subrepos=None,
1195 archive_dir_name=None, write_metadata=False, mtime=None,
1196 archive_dir_name=None, write_metadata=False, mtime=None,
1196 archive_at_path='/', with_hash=True):
1197 archive_at_path='/'):
1197 """
1198 """
1198 Creates an archive containing the contents of the repository.
1199 Creates an archive containing the contents of the repository.
1199
1200
@@ -1216,29 +1217,12 b' class BaseCommit(object):'
1216 (kind, allowed_kinds))
1217 (kind, allowed_kinds))
1217
1218
1218 archive_dir_name = self._validate_archive_prefix(archive_dir_name)
1219 archive_dir_name = self._validate_archive_prefix(archive_dir_name)
1219
1220 mtime = mtime is not None or time.mktime(self.date.timetuple())
1220 mtime = mtime is not None or time.mktime(self.date.timetuple())
1221
1221 commit_id = self.raw_id
1222 file_info = []
1223 cur_rev = self.repository.get_commit(commit_id=self.raw_id)
1224 for _r, _d, files in cur_rev.walk(archive_at_path):
1225 for f in files:
1226 f_path = os.path.join(archive_dir_name, f.path)
1227 file_info.append(
1228 (f_path, f.mode, f.is_link(), f.raw_bytes))
1229
1222
1230 if write_metadata:
1223 return self.repository._remote.archive_repo(
1231 metadata = [
1224 archive_dest_path, kind, mtime, archive_at_path,
1232 ('repo_name', self.repository.name),
1225 archive_dir_name, commit_id)
1233 ('commit_id', self.raw_id),
1234 ('mtime', mtime),
1235 ('branch', self.branch),
1236 ('tags', ','.join(self.tags)),
1237 ]
1238 meta = ["%s:%s" % (f_name, value) for f_name, value in metadata]
1239 file_info.append(('.archival.txt', 0o644, False, '\n'.join(meta)))
1240
1241 connection.Hg.archive_repo(archive_dest_path, mtime, file_info, kind)
1242
1226
1243 def _validate_archive_prefix(self, archive_dir_name):
1227 def _validate_archive_prefix(self, archive_dir_name):
1244 if archive_dir_name is None:
1228 if archive_dir_name is None:
@@ -264,7 +264,6 b' input[type="button"] {'
264 margin-left: -1px;
264 margin-left: -1px;
265 padding-left: 2px;
265 padding-left: 2px;
266 padding-right: 2px;
266 padding-right: 2px;
267 border-left: 1px solid @grey3;
268 }
267 }
269 }
268 }
270
269
@@ -29,7 +29,7 b''
29 </a>
29 </a>
30
30
31 <div class="btn-action-switcher-container right-align">
31 <div class="btn-action-switcher-container right-align">
32 <ul class="btn-action-switcher" role="menu" style="min-width: 200px">
32 <ul class="btn-action-switcher" role="menu" style="min-width: 200px; width: max-content">
33 <li>
33 <li>
34 <a class="action_button" href="${h.route_path('repo_files_upload_file',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">
34 <a class="action_button" href="${h.route_path('repo_files_upload_file',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">
35 <i class="icon-upload"></i>
35 <i class="icon-upload"></i>
@@ -44,18 +44,41 b''
44 % endif
44 % endif
45
45
46 % if c.enable_downloads:
46 % if c.enable_downloads:
47 <% at_path = '{}'.format(request.GET.get('at') or c.commit.raw_id[:6]) %>
47 <%
48 <div class="btn btn-default new-file">
48 at_path = '{}'.format(request.GET.get('at') or c.commit.raw_id[:6])
49 % if c.f_path == '/':
49 if c.f_path == '/':
50 <a href="${h.route_path('repo_archivefile',repo_name=c.repo_name, fname='{}.zip'.format(c.commit.raw_id))}">
50 label = _('Full tree as {}')
51 ${_('Download full tree ZIP')}
51 _query = {'with_hash': '1'}
52 else:
53 label = _('This tree as {}')
54 _query = {'at_path':c.f_path, 'with_hash': '1'}
55 %>
56
57 <div class="btn-group btn-group-actions new-file">
58 <a class="archive_link btn btn-default" data-ext=".zip" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name, fname='{}{}'.format(c.commit.raw_id, '.zip'), _query=_query)}">
59 <i class="icon-download"></i>
60 ${label.format('.zip')}
52 </a>
61 </a>
53 % else:
62
54 <a href="${h.route_path('repo_archivefile',repo_name=c.repo_name, fname='{}.zip'.format(c.commit.raw_id), _query={'at_path':c.f_path, 'with_hash': '1'})}">
63 <a class="tooltip btn btn-default btn-more-option" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more download options')}">
55 ${_('Download this tree ZIP')}
64 <i class="icon-down"></i>
56 </a>
65 </a>
57 % endif
66
58 </div>
67 <div class="btn-action-switcher-container left-align">
68 <ul class="btn-action-switcher" role="menu" style="min-width: 200px; width: max-content">
69 % for a_type, content_type, extension in h.ARCHIVE_SPECS:
70 % if extension not in ['.zip']:
71 <li>
72 <a class="archive_link" data-ext="${extension}" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name, fname='{}{}'.format(c.commit.raw_id, extension), _query=_query)}">
73 <i class="icon-download"></i>
74 ${label.format(extension)}
75 </a>
76 </li>
77 % endif
78 % endfor
79 </ul>
80 </div>
81 </div>
59 % endif
82 % endif
60
83
61 <div class="files-quick-filter">
84 <div class="files-quick-filter">
@@ -198,7 +198,7 b''
198 </a>
198 </a>
199
199
200 <div class="btn-action-switcher-container left-align">
200 <div class="btn-action-switcher-container left-align">
201 <ul class="btn-action-switcher" role="menu" style="min-width: 200px">
201 <ul class="btn-action-switcher" role="menu" style="min-width: 200px; width: max-content">
202 % for a_type, content_type, extension in h.ARCHIVE_SPECS:
202 % for a_type, content_type, extension in h.ARCHIVE_SPECS:
203 % if extension not in ['.zip']:
203 % if extension not in ['.zip']:
204 <li>
204 <li>
@@ -49,23 +49,32 b' class TestArchives(BackendTestMixin):'
49 @classmethod
49 @classmethod
50 def _get_commits(cls):
50 def _get_commits(cls):
51 start_date = datetime.datetime(2010, 1, 1, 20)
51 start_date = datetime.datetime(2010, 1, 1, 20)
52 yield {
53 'message': 'Initial Commit',
54 'author': 'Joe Doe <joe.doe@example.com>',
55 'date': start_date + datetime.timedelta(hours=12),
56 'added': [
57 FileNode('executable_0o100755', '...', mode=0o100755),
58 FileNode('executable_0o100500', '...', mode=0o100500),
59 FileNode('not_executable', '...', mode=0o100644),
60 ],
61 }
52 for x in range(5):
62 for x in range(5):
53 yield {
63 yield {
54 'message': 'Commit %d' % x,
64 'message': 'Commit %d' % x,
55 'author': 'Joe Doe <joe.doe@example.com>',
65 'author': 'Joe Doe <joe.doe@example.com>',
56 'date': start_date + datetime.timedelta(hours=12 * x),
66 'date': start_date + datetime.timedelta(hours=12 * x),
57 'added': [
67 'added': [
58 FileNode(
68 FileNode('%d/file_%d.txt' % (x, x), content='Foobar %d' % x),
59 '%d/file_%d.txt' % (x, x), content='Foobar %d' % x),
60 ],
69 ],
61 }
70 }
62
71
63 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
72 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
64 def test_archive_tar(self, compressor):
73 def test_archive_tar(self, compressor):
65 self.tip.archive_repo(
74 self.tip.archive_repo(
66 self.temp_file, kind='t' + compressor, prefix='repo')
75 self.temp_file, kind='t{}'.format(compressor), archive_dir_name='repo')
67 out_dir = tempfile.mkdtemp()
76 out_dir = tempfile.mkdtemp()
68 out_file = tarfile.open(self.temp_file, 'r|' + compressor)
77 out_file = tarfile.open(self.temp_file, 'r|{}'.format(compressor))
69 out_file.extractall(out_dir)
78 out_file.extractall(out_dir)
70 out_file.close()
79 out_file.close()
71
80
@@ -77,8 +86,24 b' class TestArchives(BackendTestMixin):'
77
86
78 shutil.rmtree(out_dir)
87 shutil.rmtree(out_dir)
79
88
89 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
90 def test_archive_tar_symlink(self, compressor):
91 return False
92
93 @pytest.mark.parametrize('compressor', ['gz', 'bz2'])
94 def test_archive_tar_file_modes(self, compressor):
95 self.tip.archive_repo(
96 self.temp_file, kind='t{}'.format(compressor), archive_dir_name='repo')
97 out_dir = tempfile.mkdtemp()
98 out_file = tarfile.open(self.temp_file, 'r|{}'.format(compressor))
99 out_file.extractall(out_dir)
100 out_file.close()
101 dest = lambda inp: os.path.join(out_dir, 'repo/' + inp)
102
103 assert oct(os.stat(dest('not_executable')).st_mode) == '0100644'
104
80 def test_archive_zip(self):
105 def test_archive_zip(self):
81 self.tip.archive_repo(self.temp_file, kind='zip', prefix='repo')
106 self.tip.archive_repo(self.temp_file, kind='zip', archive_dir_name='repo')
82 out = zipfile.ZipFile(self.temp_file)
107 out = zipfile.ZipFile(self.temp_file)
83
108
84 for x in range(5):
109 for x in range(5):
@@ -91,10 +116,10 b' class TestArchives(BackendTestMixin):'
91
116
92 def test_archive_zip_with_metadata(self):
117 def test_archive_zip_with_metadata(self):
93 self.tip.archive_repo(self.temp_file, kind='zip',
118 self.tip.archive_repo(self.temp_file, kind='zip',
94 prefix='repo', write_metadata=True)
119 archive_dir_name='repo', write_metadata=True)
95
120
96 out = zipfile.ZipFile(self.temp_file)
121 out = zipfile.ZipFile(self.temp_file)
97 metafile = out.read('.archival.txt')
122 metafile = out.read('repo/.archival.txt')
98
123
99 raw_id = self.tip.raw_id
124 raw_id = self.tip.raw_id
100 assert 'commit_id:%s' % raw_id in metafile
125 assert 'commit_id:%s' % raw_id in metafile
General Comments 0
You need to be logged in to leave comments. Login now