##// END OF EJS Templates
Fixed bug in repos group discovery, when inner folder of bare git repos were detected as a group
marcink -
r2497:5a8c19c4 beta
parent child Browse files
Show More
@@ -1,315 +1,317 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos_groups
3 rhodecode.controllers.admin.repos_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories groups controller for RhodeCode
6 Repositories groups controller for RhodeCode
7
7
8 :created_on: Mar 23, 2010
8 :created_on: Mar 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31
31
32 from pylons import request, tmpl_context as c, url
32 from pylons import request, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 from sqlalchemy.exc import IntegrityError
36 from sqlalchemy.exc import IntegrityError
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
40 HasReposGroupPermissionAnyDecorator
40 HasReposGroupPermissionAnyDecorator
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.model.db import RepoGroup
42 from rhodecode.model.db import RepoGroup
43 from rhodecode.model.repos_group import ReposGroupModel
43 from rhodecode.model.repos_group import ReposGroupModel
44 from rhodecode.model.forms import ReposGroupForm
44 from rhodecode.model.forms import ReposGroupForm
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
47 from webob.exc import HTTPInternalServerError
47 from webob.exc import HTTPInternalServerError, HTTPNotFound
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class ReposGroupsController(BaseController):
52 class ReposGroupsController(BaseController):
53 """REST Controller styled on the Atom Publishing Protocol"""
53 """REST Controller styled on the Atom Publishing Protocol"""
54 # To properly map this controller, ensure your config/routing.py
54 # To properly map this controller, ensure your config/routing.py
55 # file has a resource setup:
55 # file has a resource setup:
56 # map.resource('repos_group', 'repos_groups')
56 # map.resource('repos_group', 'repos_groups')
57
57
58 @LoginRequired()
58 @LoginRequired()
59 def __before__(self):
59 def __before__(self):
60 super(ReposGroupsController, self).__before__()
60 super(ReposGroupsController, self).__before__()
61
61
62 def __load_defaults(self):
62 def __load_defaults(self):
63 c.repo_groups = RepoGroup.groups_choices()
63 c.repo_groups = RepoGroup.groups_choices()
64 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
64 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
65
65
66 repo_model = RepoModel()
66 repo_model = RepoModel()
67 c.users_array = repo_model.get_users_js()
67 c.users_array = repo_model.get_users_js()
68 c.users_groups_array = repo_model.get_users_groups_js()
68 c.users_groups_array = repo_model.get_users_groups_js()
69
69
70 def __load_data(self, group_id):
70 def __load_data(self, group_id):
71 """
71 """
72 Load defaults settings for edit, and update
72 Load defaults settings for edit, and update
73
73
74 :param group_id:
74 :param group_id:
75 """
75 """
76 self.__load_defaults()
76 self.__load_defaults()
77
77
78 repo_group = RepoGroup.get_or_404(group_id)
78 repo_group = RepoGroup.get_or_404(group_id)
79
79
80 data = repo_group.get_dict()
80 data = repo_group.get_dict()
81
81
82 data['group_name'] = repo_group.name
82 data['group_name'] = repo_group.name
83
83
84 # fill repository users
84 # fill repository users
85 for p in repo_group.repo_group_to_perm:
85 for p in repo_group.repo_group_to_perm:
86 data.update({'u_perm_%s' % p.user.username:
86 data.update({'u_perm_%s' % p.user.username:
87 p.permission.permission_name})
87 p.permission.permission_name})
88
88
89 # fill repository groups
89 # fill repository groups
90 for p in repo_group.users_group_to_perm:
90 for p in repo_group.users_group_to_perm:
91 data.update({'g_perm_%s' % p.users_group.users_group_name:
91 data.update({'g_perm_%s' % p.users_group.users_group_name:
92 p.permission.permission_name})
92 p.permission.permission_name})
93
93
94 return data
94 return data
95
95
96 @HasPermissionAnyDecorator('hg.admin')
96 @HasPermissionAnyDecorator('hg.admin')
97 def index(self, format='html'):
97 def index(self, format='html'):
98 """GET /repos_groups: All items in the collection"""
98 """GET /repos_groups: All items in the collection"""
99 # url('repos_groups')
99 # url('repos_groups')
100 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
100 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
101 c.groups = sorted(RepoGroup.query().all(), key=sk)
101 c.groups = sorted(RepoGroup.query().all(), key=sk)
102 return render('admin/repos_groups/repos_groups_show.html')
102 return render('admin/repos_groups/repos_groups_show.html')
103
103
104 @HasPermissionAnyDecorator('hg.admin')
104 @HasPermissionAnyDecorator('hg.admin')
105 def create(self):
105 def create(self):
106 """POST /repos_groups: Create a new item"""
106 """POST /repos_groups: Create a new item"""
107 # url('repos_groups')
107 # url('repos_groups')
108 self.__load_defaults()
108 self.__load_defaults()
109 repos_group_form = ReposGroupForm(available_groups =
109 repos_group_form = ReposGroupForm(available_groups =
110 c.repo_groups_choices)()
110 c.repo_groups_choices)()
111 try:
111 try:
112 form_result = repos_group_form.to_python(dict(request.POST))
112 form_result = repos_group_form.to_python(dict(request.POST))
113 ReposGroupModel().create(
113 ReposGroupModel().create(
114 group_name=form_result['group_name'],
114 group_name=form_result['group_name'],
115 group_description=form_result['group_description'],
115 group_description=form_result['group_description'],
116 parent=form_result['group_parent_id']
116 parent=form_result['group_parent_id']
117 )
117 )
118 Session.commit()
118 Session.commit()
119 h.flash(_('created repos group %s') \
119 h.flash(_('created repos group %s') \
120 % form_result['group_name'], category='success')
120 % form_result['group_name'], category='success')
121 #TODO: in futureaction_logger(, '', '', '', self.sa)
121 #TODO: in futureaction_logger(, '', '', '', self.sa)
122 except formencode.Invalid, errors:
122 except formencode.Invalid, errors:
123
123
124 return htmlfill.render(
124 return htmlfill.render(
125 render('admin/repos_groups/repos_groups_add.html'),
125 render('admin/repos_groups/repos_groups_add.html'),
126 defaults=errors.value,
126 defaults=errors.value,
127 errors=errors.error_dict or {},
127 errors=errors.error_dict or {},
128 prefix_error=False,
128 prefix_error=False,
129 encoding="UTF-8")
129 encoding="UTF-8")
130 except Exception:
130 except Exception:
131 log.error(traceback.format_exc())
131 log.error(traceback.format_exc())
132 h.flash(_('error occurred during creation of repos group %s') \
132 h.flash(_('error occurred during creation of repos group %s') \
133 % request.POST.get('group_name'), category='error')
133 % request.POST.get('group_name'), category='error')
134
134
135 return redirect(url('repos_groups'))
135 return redirect(url('repos_groups'))
136
136
137 @HasPermissionAnyDecorator('hg.admin')
137 @HasPermissionAnyDecorator('hg.admin')
138 def new(self, format='html'):
138 def new(self, format='html'):
139 """GET /repos_groups/new: Form to create a new item"""
139 """GET /repos_groups/new: Form to create a new item"""
140 # url('new_repos_group')
140 # url('new_repos_group')
141 self.__load_defaults()
141 self.__load_defaults()
142 return render('admin/repos_groups/repos_groups_add.html')
142 return render('admin/repos_groups/repos_groups_add.html')
143
143
144 @HasPermissionAnyDecorator('hg.admin')
144 @HasPermissionAnyDecorator('hg.admin')
145 def update(self, id):
145 def update(self, id):
146 """PUT /repos_groups/id: Update an existing item"""
146 """PUT /repos_groups/id: Update an existing item"""
147 # Forms posted to this method should contain a hidden field:
147 # Forms posted to this method should contain a hidden field:
148 # <input type="hidden" name="_method" value="PUT" />
148 # <input type="hidden" name="_method" value="PUT" />
149 # Or using helpers:
149 # Or using helpers:
150 # h.form(url('repos_group', id=ID),
150 # h.form(url('repos_group', id=ID),
151 # method='put')
151 # method='put')
152 # url('repos_group', id=ID)
152 # url('repos_group', id=ID)
153
153
154 self.__load_defaults()
154 self.__load_defaults()
155 c.repos_group = RepoGroup.get(id)
155 c.repos_group = RepoGroup.get(id)
156
156
157 repos_group_form = ReposGroupForm(
157 repos_group_form = ReposGroupForm(
158 edit=True,
158 edit=True,
159 old_data=c.repos_group.get_dict(),
159 old_data=c.repos_group.get_dict(),
160 available_groups=c.repo_groups_choices
160 available_groups=c.repo_groups_choices
161 )()
161 )()
162 try:
162 try:
163 form_result = repos_group_form.to_python(dict(request.POST))
163 form_result = repos_group_form.to_python(dict(request.POST))
164 ReposGroupModel().update(id, form_result)
164 ReposGroupModel().update(id, form_result)
165 Session.commit()
165 Session.commit()
166 h.flash(_('updated repos group %s') \
166 h.flash(_('updated repos group %s') \
167 % form_result['group_name'], category='success')
167 % form_result['group_name'], category='success')
168 #TODO: in futureaction_logger(, '', '', '', self.sa)
168 #TODO: in futureaction_logger(, '', '', '', self.sa)
169 except formencode.Invalid, errors:
169 except formencode.Invalid, errors:
170
170
171 return htmlfill.render(
171 return htmlfill.render(
172 render('admin/repos_groups/repos_groups_edit.html'),
172 render('admin/repos_groups/repos_groups_edit.html'),
173 defaults=errors.value,
173 defaults=errors.value,
174 errors=errors.error_dict or {},
174 errors=errors.error_dict or {},
175 prefix_error=False,
175 prefix_error=False,
176 encoding="UTF-8")
176 encoding="UTF-8")
177 except Exception:
177 except Exception:
178 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
179 h.flash(_('error occurred during update of repos group %s') \
179 h.flash(_('error occurred during update of repos group %s') \
180 % request.POST.get('group_name'), category='error')
180 % request.POST.get('group_name'), category='error')
181
181
182 return redirect(url('repos_groups'))
182 return redirect(url('repos_groups'))
183
183
184 @HasPermissionAnyDecorator('hg.admin')
184 @HasPermissionAnyDecorator('hg.admin')
185 def delete(self, id):
185 def delete(self, id):
186 """DELETE /repos_groups/id: Delete an existing item"""
186 """DELETE /repos_groups/id: Delete an existing item"""
187 # Forms posted to this method should contain a hidden field:
187 # Forms posted to this method should contain a hidden field:
188 # <input type="hidden" name="_method" value="DELETE" />
188 # <input type="hidden" name="_method" value="DELETE" />
189 # Or using helpers:
189 # Or using helpers:
190 # h.form(url('repos_group', id=ID),
190 # h.form(url('repos_group', id=ID),
191 # method='delete')
191 # method='delete')
192 # url('repos_group', id=ID)
192 # url('repos_group', id=ID)
193
193
194 gr = RepoGroup.get(id)
194 gr = RepoGroup.get(id)
195 repos = gr.repositories.all()
195 repos = gr.repositories.all()
196 if repos:
196 if repos:
197 h.flash(_('This group contains %s repositores and cannot be '
197 h.flash(_('This group contains %s repositores and cannot be '
198 'deleted' % len(repos)),
198 'deleted' % len(repos)),
199 category='error')
199 category='error')
200 return redirect(url('repos_groups'))
200 return redirect(url('repos_groups'))
201
201
202 try:
202 try:
203 ReposGroupModel().delete(id)
203 ReposGroupModel().delete(id)
204 Session.commit()
204 Session.commit()
205 h.flash(_('removed repos group %s' % gr.group_name), category='success')
205 h.flash(_('removed repos group %s' % gr.group_name), category='success')
206 #TODO: in future action_logger(, '', '', '', self.sa)
206 #TODO: in future action_logger(, '', '', '', self.sa)
207 except IntegrityError, e:
207 except IntegrityError, e:
208 if e.message.find('groups_group_parent_id_fkey') != -1:
208 if e.message.find('groups_group_parent_id_fkey') != -1:
209 log.error(traceback.format_exc())
209 log.error(traceback.format_exc())
210 h.flash(_('Cannot delete this group it still contains '
210 h.flash(_('Cannot delete this group it still contains '
211 'subgroups'),
211 'subgroups'),
212 category='warning')
212 category='warning')
213 else:
213 else:
214 log.error(traceback.format_exc())
214 log.error(traceback.format_exc())
215 h.flash(_('error occurred during deletion of repos '
215 h.flash(_('error occurred during deletion of repos '
216 'group %s' % gr.group_name), category='error')
216 'group %s' % gr.group_name), category='error')
217
217
218 except Exception:
218 except Exception:
219 log.error(traceback.format_exc())
219 log.error(traceback.format_exc())
220 h.flash(_('error occurred during deletion of repos '
220 h.flash(_('error occurred during deletion of repos '
221 'group %s' % gr.group_name), category='error')
221 'group %s' % gr.group_name), category='error')
222
222
223 return redirect(url('repos_groups'))
223 return redirect(url('repos_groups'))
224
224
225 @HasReposGroupPermissionAnyDecorator('group.admin')
225 @HasReposGroupPermissionAnyDecorator('group.admin')
226 def delete_repos_group_user_perm(self, group_name):
226 def delete_repos_group_user_perm(self, group_name):
227 """
227 """
228 DELETE an existing repositories group permission user
228 DELETE an existing repositories group permission user
229
229
230 :param group_name:
230 :param group_name:
231 """
231 """
232
232
233 try:
233 try:
234 ReposGroupModel().revoke_user_permission(
234 ReposGroupModel().revoke_user_permission(
235 repos_group=group_name, user=request.POST['user_id']
235 repos_group=group_name, user=request.POST['user_id']
236 )
236 )
237 Session.commit()
237 Session.commit()
238 except Exception:
238 except Exception:
239 log.error(traceback.format_exc())
239 log.error(traceback.format_exc())
240 h.flash(_('An error occurred during deletion of group user'),
240 h.flash(_('An error occurred during deletion of group user'),
241 category='error')
241 category='error')
242 raise HTTPInternalServerError()
242 raise HTTPInternalServerError()
243
243
244 @HasReposGroupPermissionAnyDecorator('group.admin')
244 @HasReposGroupPermissionAnyDecorator('group.admin')
245 def delete_repos_group_users_group_perm(self, group_name):
245 def delete_repos_group_users_group_perm(self, group_name):
246 """
246 """
247 DELETE an existing repositories group permission users group
247 DELETE an existing repositories group permission users group
248
248
249 :param group_name:
249 :param group_name:
250 """
250 """
251
251
252 try:
252 try:
253 ReposGroupModel().revoke_users_group_permission(
253 ReposGroupModel().revoke_users_group_permission(
254 repos_group=group_name,
254 repos_group=group_name,
255 group_name=request.POST['users_group_id']
255 group_name=request.POST['users_group_id']
256 )
256 )
257 Session.commit()
257 Session.commit()
258 except Exception:
258 except Exception:
259 log.error(traceback.format_exc())
259 log.error(traceback.format_exc())
260 h.flash(_('An error occurred during deletion of group'
260 h.flash(_('An error occurred during deletion of group'
261 ' users groups'),
261 ' users groups'),
262 category='error')
262 category='error')
263 raise HTTPInternalServerError()
263 raise HTTPInternalServerError()
264
264
265 def show_by_name(self, group_name):
265 def show_by_name(self, group_name):
266 """
266 """
267 This is a proxy that does a lookup group_name -> id, and shows
267 This is a proxy that does a lookup group_name -> id, and shows
268 the group by id view instead
268 the group by id view instead
269 """
269 """
270 group_name = group_name.rstrip('/')
270 group_name = group_name.rstrip('/')
271 id_ = RepoGroup.get_by_group_name(group_name).group_id
271 id_ = RepoGroup.get_by_group_name(group_name)
272 return self.show(id_)
272 if id_:
273 return self.show(id_.group_id)
274 raise HTTPNotFound
273
275
274 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
276 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
275 'group.admin')
277 'group.admin')
276 def show(self, id, format='html'):
278 def show(self, id, format='html'):
277 """GET /repos_groups/id: Show a specific item"""
279 """GET /repos_groups/id: Show a specific item"""
278 # url('repos_group', id=ID)
280 # url('repos_group', id=ID)
279
281
280 c.group = RepoGroup.get_or_404(id)
282 c.group = RepoGroup.get_or_404(id)
281
283
282 c.group_repos = c.group.repositories.all()
284 c.group_repos = c.group.repositories.all()
283
285
284 #overwrite our cached list with current filter
286 #overwrite our cached list with current filter
285 gr_filter = c.group_repos
287 gr_filter = c.group_repos
286 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
288 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
287
289
288 c.repos_list = c.cached_repo_list
290 c.repos_list = c.cached_repo_list
289
291
290 c.repo_cnt = 0
292 c.repo_cnt = 0
291
293
292 c.groups = self.sa.query(RepoGroup).order_by(RepoGroup.group_name)\
294 c.groups = self.sa.query(RepoGroup).order_by(RepoGroup.group_name)\
293 .filter(RepoGroup.group_parent_id == id).all()
295 .filter(RepoGroup.group_parent_id == id).all()
294
296
295 return render('admin/repos_groups/repos_groups.html')
297 return render('admin/repos_groups/repos_groups.html')
296
298
297 @HasPermissionAnyDecorator('hg.admin')
299 @HasPermissionAnyDecorator('hg.admin')
298 def edit(self, id, format='html'):
300 def edit(self, id, format='html'):
299 """GET /repos_groups/id/edit: Form to edit an existing item"""
301 """GET /repos_groups/id/edit: Form to edit an existing item"""
300 # url('edit_repos_group', id=ID)
302 # url('edit_repos_group', id=ID)
301
303
302 id_ = int(id)
304 id_ = int(id)
303
305
304 c.repos_group = RepoGroup.get(id_)
306 c.repos_group = RepoGroup.get(id_)
305 defaults = self.__load_data(id_)
307 defaults = self.__load_data(id_)
306
308
307 # we need to exclude this group from the group list for editing
309 # we need to exclude this group from the group list for editing
308 c.repo_groups = filter(lambda x: x[0] != id_, c.repo_groups)
310 c.repo_groups = filter(lambda x: x[0] != id_, c.repo_groups)
309
311
310 return htmlfill.render(
312 return htmlfill.render(
311 render('admin/repos_groups/repos_groups_edit.html'),
313 render('admin/repos_groups/repos_groups_edit.html'),
312 defaults=defaults,
314 defaults=defaults,
313 encoding="UTF-8",
315 encoding="UTF-8",
314 force_defaults=False
316 force_defaults=False
315 )
317 )
@@ -1,677 +1,686 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 from os.path import abspath
35 from os.path import abspath
36 from os.path import dirname as dn, join as jn
36 from os.path import dirname as dn, join as jn
37
37
38 from paste.script.command import Command, BadCommand
38 from paste.script.command import Command, BadCommand
39
39
40 from mercurial import ui, config
40 from mercurial import ui, config
41
41
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
43
43
44 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs import get_backend
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.utils.helpers import get_scm
47 from rhodecode.lib.vcs.utils.helpers import get_scm
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49
49
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
54 UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm,\
54 UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm,\
55 CacheInvalidation
55 CacheInvalidation
56 from rhodecode.model.meta import Session
56 from rhodecode.model.meta import Session
57 from rhodecode.model.repos_group import ReposGroupModel
57 from rhodecode.model.repos_group import ReposGroupModel
58 from rhodecode.lib.utils2 import safe_str, safe_unicode
58 from rhodecode.lib.utils2 import safe_str, safe_unicode
59 from rhodecode.lib.vcs.utils.fakemod import create_module
59 from rhodecode.lib.vcs.utils.fakemod import create_module
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
63 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
64
64
65
65
66 def recursive_replace(str_, replace=' '):
66 def recursive_replace(str_, replace=' '):
67 """
67 """
68 Recursive replace of given sign to just one instance
68 Recursive replace of given sign to just one instance
69
69
70 :param str_: given string
70 :param str_: given string
71 :param replace: char to find and replace multiple instances
71 :param replace: char to find and replace multiple instances
72
72
73 Examples::
73 Examples::
74 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
74 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
75 'Mighty-Mighty-Bo-sstones'
75 'Mighty-Mighty-Bo-sstones'
76 """
76 """
77
77
78 if str_.find(replace * 2) == -1:
78 if str_.find(replace * 2) == -1:
79 return str_
79 return str_
80 else:
80 else:
81 str_ = str_.replace(replace * 2, replace)
81 str_ = str_.replace(replace * 2, replace)
82 return recursive_replace(str_, replace)
82 return recursive_replace(str_, replace)
83
83
84
84
85 def repo_name_slug(value):
85 def repo_name_slug(value):
86 """
86 """
87 Return slug of name of repository
87 Return slug of name of repository
88 This function is called on each creation/modification
88 This function is called on each creation/modification
89 of repository to prevent bad names in repo
89 of repository to prevent bad names in repo
90 """
90 """
91
91
92 slug = remove_formatting(value)
92 slug = remove_formatting(value)
93 slug = strip_tags(slug)
93 slug = strip_tags(slug)
94
94
95 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
95 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
96 slug = slug.replace(c, '-')
96 slug = slug.replace(c, '-')
97 slug = recursive_replace(slug, '-')
97 slug = recursive_replace(slug, '-')
98 slug = collapse(slug, '-')
98 slug = collapse(slug, '-')
99 return slug
99 return slug
100
100
101
101
102 def get_repo_slug(request):
102 def get_repo_slug(request):
103 _repo = request.environ['pylons.routes_dict'].get('repo_name')
103 _repo = request.environ['pylons.routes_dict'].get('repo_name')
104 if _repo:
104 if _repo:
105 _repo = _repo.rstrip('/')
105 _repo = _repo.rstrip('/')
106 return _repo
106 return _repo
107
107
108
108
109 def get_repos_group_slug(request):
109 def get_repos_group_slug(request):
110 _group = request.environ['pylons.routes_dict'].get('group_name')
110 _group = request.environ['pylons.routes_dict'].get('group_name')
111 if _group:
111 if _group:
112 _group = _group.rstrip('/')
112 _group = _group.rstrip('/')
113 return _group
113 return _group
114
114
115
115
116 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
116 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
117 """
117 """
118 Action logger for various actions made by users
118 Action logger for various actions made by users
119
119
120 :param user: user that made this action, can be a unique username string or
120 :param user: user that made this action, can be a unique username string or
121 object containing user_id attribute
121 object containing user_id attribute
122 :param action: action to log, should be on of predefined unique actions for
122 :param action: action to log, should be on of predefined unique actions for
123 easy translations
123 easy translations
124 :param repo: string name of repository or object containing repo_id,
124 :param repo: string name of repository or object containing repo_id,
125 that action was made on
125 that action was made on
126 :param ipaddr: optional ip address from what the action was made
126 :param ipaddr: optional ip address from what the action was made
127 :param sa: optional sqlalchemy session
127 :param sa: optional sqlalchemy session
128
128
129 """
129 """
130
130
131 if not sa:
131 if not sa:
132 sa = meta.Session
132 sa = meta.Session
133
133
134 try:
134 try:
135 if hasattr(user, 'user_id'):
135 if hasattr(user, 'user_id'):
136 user_obj = user
136 user_obj = user
137 elif isinstance(user, basestring):
137 elif isinstance(user, basestring):
138 user_obj = User.get_by_username(user)
138 user_obj = User.get_by_username(user)
139 else:
139 else:
140 raise Exception('You have to provide user object or username')
140 raise Exception('You have to provide user object or username')
141
141
142 if hasattr(repo, 'repo_id'):
142 if hasattr(repo, 'repo_id'):
143 repo_obj = Repository.get(repo.repo_id)
143 repo_obj = Repository.get(repo.repo_id)
144 repo_name = repo_obj.repo_name
144 repo_name = repo_obj.repo_name
145 elif isinstance(repo, basestring):
145 elif isinstance(repo, basestring):
146 repo_name = repo.lstrip('/')
146 repo_name = repo.lstrip('/')
147 repo_obj = Repository.get_by_repo_name(repo_name)
147 repo_obj = Repository.get_by_repo_name(repo_name)
148 else:
148 else:
149 repo_obj = None
149 repo_obj = None
150 repo_name = ''
150 repo_name = ''
151
151
152 user_log = UserLog()
152 user_log = UserLog()
153 user_log.user_id = user_obj.user_id
153 user_log.user_id = user_obj.user_id
154 user_log.action = safe_unicode(action)
154 user_log.action = safe_unicode(action)
155
155
156 user_log.repository = repo_obj
156 user_log.repository = repo_obj
157 user_log.repository_name = repo_name
157 user_log.repository_name = repo_name
158
158
159 user_log.action_date = datetime.datetime.now()
159 user_log.action_date = datetime.datetime.now()
160 user_log.user_ip = ipaddr
160 user_log.user_ip = ipaddr
161 sa.add(user_log)
161 sa.add(user_log)
162
162
163 log.info(
163 log.info(
164 'Adding user %s, action %s on %s' % (user_obj, action,
164 'Adding user %s, action %s on %s' % (user_obj, action,
165 safe_unicode(repo))
165 safe_unicode(repo))
166 )
166 )
167 if commit:
167 if commit:
168 sa.commit()
168 sa.commit()
169 except:
169 except:
170 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
171 raise
171 raise
172
172
173
173
174 def get_repos(path, recursive=False):
174 def get_repos(path, recursive=False):
175 """
175 """
176 Scans given path for repos and return (name,(type,path)) tuple
176 Scans given path for repos and return (name,(type,path)) tuple
177
177
178 :param path: path to scan for repositories
178 :param path: path to scan for repositories
179 :param recursive: recursive search and return names with subdirs in front
179 :param recursive: recursive search and return names with subdirs in front
180 """
180 """
181
181
182 # remove ending slash for better results
182 # remove ending slash for better results
183 path = path.rstrip(os.sep)
183 path = path.rstrip(os.sep)
184
184
185 def _get_repos(p):
185 def _get_repos(p):
186 if not os.access(p, os.W_OK):
186 if not os.access(p, os.W_OK):
187 return
187 return
188 for dirpath in os.listdir(p):
188 for dirpath in os.listdir(p):
189 if os.path.isfile(os.path.join(p, dirpath)):
189 if os.path.isfile(os.path.join(p, dirpath)):
190 continue
190 continue
191 cur_path = os.path.join(p, dirpath)
191 cur_path = os.path.join(p, dirpath)
192 try:
192 try:
193 scm_info = get_scm(cur_path)
193 scm_info = get_scm(cur_path)
194 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
194 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
195 except VCSError:
195 except VCSError:
196 if not recursive:
196 if not recursive:
197 continue
197 continue
198 #check if this dir containts other repos for recursive scan
198 #check if this dir containts other repos for recursive scan
199 rec_path = os.path.join(p, dirpath)
199 rec_path = os.path.join(p, dirpath)
200 if os.path.isdir(rec_path):
200 if os.path.isdir(rec_path):
201 for inner_scm in _get_repos(rec_path):
201 for inner_scm in _get_repos(rec_path):
202 yield inner_scm
202 yield inner_scm
203
203
204 return _get_repos(path)
204 return _get_repos(path)
205
205
206
206
207 def is_valid_repo(repo_name, base_path):
207 def is_valid_repo(repo_name, base_path):
208 """
208 """
209 Returns True if given path is a valid repository False otherwise
209 Returns True if given path is a valid repository False otherwise
210
210
211 :param repo_name:
211 :param repo_name:
212 :param base_path:
212 :param base_path:
213
213
214 :return True: if given path is a valid repository
214 :return True: if given path is a valid repository
215 """
215 """
216 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
216 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
217
217
218 try:
218 try:
219 get_scm(full_path)
219 get_scm(full_path)
220 return True
220 return True
221 except VCSError:
221 except VCSError:
222 return False
222 return False
223
223
224
224
225 def is_valid_repos_group(repos_group_name, base_path):
225 def is_valid_repos_group(repos_group_name, base_path):
226 """
226 """
227 Returns True if given path is a repos group False otherwise
227 Returns True if given path is a repos group False otherwise
228
228
229 :param repo_name:
229 :param repo_name:
230 :param base_path:
230 :param base_path:
231 """
231 """
232 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
232 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
233
233
234 # check if it's not a repo
234 # check if it's not a repo
235 if is_valid_repo(repos_group_name, base_path):
235 if is_valid_repo(repos_group_name, base_path):
236 return False
236 return False
237
237
238 try:
239 # we need to check bare git repos at higher level
240 # since we might match branches/hooks/info/objects or possible
241 # other things inside bare git repo
242 get_scm(os.path.dirname(full_path))
243 return False
244 except VCSError:
245 pass
246
238 # check if it's a valid path
247 # check if it's a valid path
239 if os.path.isdir(full_path):
248 if os.path.isdir(full_path):
240 return True
249 return True
241
250
242 return False
251 return False
243
252
244
253
245 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
254 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
246 while True:
255 while True:
247 ok = raw_input(prompt)
256 ok = raw_input(prompt)
248 if ok in ('y', 'ye', 'yes'):
257 if ok in ('y', 'ye', 'yes'):
249 return True
258 return True
250 if ok in ('n', 'no', 'nop', 'nope'):
259 if ok in ('n', 'no', 'nop', 'nope'):
251 return False
260 return False
252 retries = retries - 1
261 retries = retries - 1
253 if retries < 0:
262 if retries < 0:
254 raise IOError
263 raise IOError
255 print complaint
264 print complaint
256
265
257 #propagated from mercurial documentation
266 #propagated from mercurial documentation
258 ui_sections = ['alias', 'auth',
267 ui_sections = ['alias', 'auth',
259 'decode/encode', 'defaults',
268 'decode/encode', 'defaults',
260 'diff', 'email',
269 'diff', 'email',
261 'extensions', 'format',
270 'extensions', 'format',
262 'merge-patterns', 'merge-tools',
271 'merge-patterns', 'merge-tools',
263 'hooks', 'http_proxy',
272 'hooks', 'http_proxy',
264 'smtp', 'patch',
273 'smtp', 'patch',
265 'paths', 'profiling',
274 'paths', 'profiling',
266 'server', 'trusted',
275 'server', 'trusted',
267 'ui', 'web', ]
276 'ui', 'web', ]
268
277
269
278
270 def make_ui(read_from='file', path=None, checkpaths=True):
279 def make_ui(read_from='file', path=None, checkpaths=True):
271 """
280 """
272 A function that will read python rc files or database
281 A function that will read python rc files or database
273 and make an mercurial ui object from read options
282 and make an mercurial ui object from read options
274
283
275 :param path: path to mercurial config file
284 :param path: path to mercurial config file
276 :param checkpaths: check the path
285 :param checkpaths: check the path
277 :param read_from: read from 'file' or 'db'
286 :param read_from: read from 'file' or 'db'
278 """
287 """
279
288
280 baseui = ui.ui()
289 baseui = ui.ui()
281
290
282 # clean the baseui object
291 # clean the baseui object
283 baseui._ocfg = config.config()
292 baseui._ocfg = config.config()
284 baseui._ucfg = config.config()
293 baseui._ucfg = config.config()
285 baseui._tcfg = config.config()
294 baseui._tcfg = config.config()
286
295
287 if read_from == 'file':
296 if read_from == 'file':
288 if not os.path.isfile(path):
297 if not os.path.isfile(path):
289 log.debug('hgrc file is not present at %s skipping...' % path)
298 log.debug('hgrc file is not present at %s skipping...' % path)
290 return False
299 return False
291 log.debug('reading hgrc from %s' % path)
300 log.debug('reading hgrc from %s' % path)
292 cfg = config.config()
301 cfg = config.config()
293 cfg.read(path)
302 cfg.read(path)
294 for section in ui_sections:
303 for section in ui_sections:
295 for k, v in cfg.items(section):
304 for k, v in cfg.items(section):
296 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
305 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
297 baseui.setconfig(section, k, v)
306 baseui.setconfig(section, k, v)
298
307
299 elif read_from == 'db':
308 elif read_from == 'db':
300 sa = meta.Session
309 sa = meta.Session
301 ret = sa.query(RhodeCodeUi)\
310 ret = sa.query(RhodeCodeUi)\
302 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
311 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
303 .all()
312 .all()
304
313
305 hg_ui = ret
314 hg_ui = ret
306 for ui_ in hg_ui:
315 for ui_ in hg_ui:
307 if ui_.ui_active:
316 if ui_.ui_active:
308 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
317 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
309 ui_.ui_key, ui_.ui_value)
318 ui_.ui_key, ui_.ui_value)
310 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
319 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
311
320
312 meta.Session.remove()
321 meta.Session.remove()
313 return baseui
322 return baseui
314
323
315
324
316 def set_rhodecode_config(config):
325 def set_rhodecode_config(config):
317 """
326 """
318 Updates pylons config with new settings from database
327 Updates pylons config with new settings from database
319
328
320 :param config:
329 :param config:
321 """
330 """
322 hgsettings = RhodeCodeSetting.get_app_settings()
331 hgsettings = RhodeCodeSetting.get_app_settings()
323
332
324 for k, v in hgsettings.items():
333 for k, v in hgsettings.items():
325 config[k] = v
334 config[k] = v
326
335
327
336
328 def invalidate_cache(cache_key, *args):
337 def invalidate_cache(cache_key, *args):
329 """
338 """
330 Puts cache invalidation task into db for
339 Puts cache invalidation task into db for
331 further global cache invalidation
340 further global cache invalidation
332 """
341 """
333
342
334 from rhodecode.model.scm import ScmModel
343 from rhodecode.model.scm import ScmModel
335
344
336 if cache_key.startswith('get_repo_cached_'):
345 if cache_key.startswith('get_repo_cached_'):
337 name = cache_key.split('get_repo_cached_')[-1]
346 name = cache_key.split('get_repo_cached_')[-1]
338 ScmModel().mark_for_invalidation(name)
347 ScmModel().mark_for_invalidation(name)
339
348
340
349
341 class EmptyChangeset(BaseChangeset):
350 class EmptyChangeset(BaseChangeset):
342 """
351 """
343 An dummy empty changeset. It's possible to pass hash when creating
352 An dummy empty changeset. It's possible to pass hash when creating
344 an EmptyChangeset
353 an EmptyChangeset
345 """
354 """
346
355
347 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
356 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
348 alias=None):
357 alias=None):
349 self._empty_cs = cs
358 self._empty_cs = cs
350 self.revision = -1
359 self.revision = -1
351 self.message = ''
360 self.message = ''
352 self.author = ''
361 self.author = ''
353 self.date = ''
362 self.date = ''
354 self.repository = repo
363 self.repository = repo
355 self.requested_revision = requested_revision
364 self.requested_revision = requested_revision
356 self.alias = alias
365 self.alias = alias
357
366
358 @LazyProperty
367 @LazyProperty
359 def raw_id(self):
368 def raw_id(self):
360 """
369 """
361 Returns raw string identifying this changeset, useful for web
370 Returns raw string identifying this changeset, useful for web
362 representation.
371 representation.
363 """
372 """
364
373
365 return self._empty_cs
374 return self._empty_cs
366
375
367 @LazyProperty
376 @LazyProperty
368 def branch(self):
377 def branch(self):
369 return get_backend(self.alias).DEFAULT_BRANCH_NAME
378 return get_backend(self.alias).DEFAULT_BRANCH_NAME
370
379
371 @LazyProperty
380 @LazyProperty
372 def short_id(self):
381 def short_id(self):
373 return self.raw_id[:12]
382 return self.raw_id[:12]
374
383
375 def get_file_changeset(self, path):
384 def get_file_changeset(self, path):
376 return self
385 return self
377
386
378 def get_file_content(self, path):
387 def get_file_content(self, path):
379 return u''
388 return u''
380
389
381 def get_file_size(self, path):
390 def get_file_size(self, path):
382 return 0
391 return 0
383
392
384
393
385 def map_groups(path):
394 def map_groups(path):
386 """
395 """
387 Given a full path to a repository, create all nested groups that this
396 Given a full path to a repository, create all nested groups that this
388 repo is inside. This function creates parent-child relationships between
397 repo is inside. This function creates parent-child relationships between
389 groups and creates default perms for all new groups.
398 groups and creates default perms for all new groups.
390
399
391 :param paths: full path to repository
400 :param paths: full path to repository
392 """
401 """
393 sa = meta.Session
402 sa = meta.Session
394 groups = path.split(Repository.url_sep())
403 groups = path.split(Repository.url_sep())
395 parent = None
404 parent = None
396 group = None
405 group = None
397
406
398 # last element is repo in nested groups structure
407 # last element is repo in nested groups structure
399 groups = groups[:-1]
408 groups = groups[:-1]
400 rgm = ReposGroupModel(sa)
409 rgm = ReposGroupModel(sa)
401 for lvl, group_name in enumerate(groups):
410 for lvl, group_name in enumerate(groups):
402 group_name = '/'.join(groups[:lvl] + [group_name])
411 group_name = '/'.join(groups[:lvl] + [group_name])
403 group = RepoGroup.get_by_group_name(group_name)
412 group = RepoGroup.get_by_group_name(group_name)
404 desc = '%s group' % group_name
413 desc = '%s group' % group_name
405
414
406 # skip folders that are now removed repos
415 # skip folders that are now removed repos
407 if REMOVED_REPO_PAT.match(group_name):
416 if REMOVED_REPO_PAT.match(group_name):
408 break
417 break
409
418
410 if group is None:
419 if group is None:
411 log.debug('creating group level: %s group_name: %s' % (lvl,
420 log.debug('creating group level: %s group_name: %s' % (lvl,
412 group_name))
421 group_name))
413 group = RepoGroup(group_name, parent)
422 group = RepoGroup(group_name, parent)
414 group.group_description = desc
423 group.group_description = desc
415 sa.add(group)
424 sa.add(group)
416 rgm._create_default_perms(group)
425 rgm._create_default_perms(group)
417 sa.flush()
426 sa.flush()
418 parent = group
427 parent = group
419 return group
428 return group
420
429
421
430
422 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
431 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
423 """
432 """
424 maps all repos given in initial_repo_list, non existing repositories
433 maps all repos given in initial_repo_list, non existing repositories
425 are created, if remove_obsolete is True it also check for db entries
434 are created, if remove_obsolete is True it also check for db entries
426 that are not in initial_repo_list and removes them.
435 that are not in initial_repo_list and removes them.
427
436
428 :param initial_repo_list: list of repositories found by scanning methods
437 :param initial_repo_list: list of repositories found by scanning methods
429 :param remove_obsolete: check for obsolete entries in database
438 :param remove_obsolete: check for obsolete entries in database
430 """
439 """
431 from rhodecode.model.repo import RepoModel
440 from rhodecode.model.repo import RepoModel
432 sa = meta.Session
441 sa = meta.Session
433 rm = RepoModel()
442 rm = RepoModel()
434 user = sa.query(User).filter(User.admin == True).first()
443 user = sa.query(User).filter(User.admin == True).first()
435 if user is None:
444 if user is None:
436 raise Exception('Missing administrative account !')
445 raise Exception('Missing administrative account !')
437 added = []
446 added = []
438
447
439 for name, repo in initial_repo_list.items():
448 for name, repo in initial_repo_list.items():
440 group = map_groups(name)
449 group = map_groups(name)
441 if not rm.get_by_repo_name(name, cache=False):
450 if not rm.get_by_repo_name(name, cache=False):
442 log.info('repository %s not found creating default' % name)
451 log.info('repository %s not found creating default' % name)
443 added.append(name)
452 added.append(name)
444 form_data = {
453 form_data = {
445 'repo_name': name,
454 'repo_name': name,
446 'repo_name_full': name,
455 'repo_name_full': name,
447 'repo_type': repo.alias,
456 'repo_type': repo.alias,
448 'description': repo.description \
457 'description': repo.description \
449 if repo.description != 'unknown' else '%s repository' % name,
458 if repo.description != 'unknown' else '%s repository' % name,
450 'private': False,
459 'private': False,
451 'group_id': getattr(group, 'group_id', None),
460 'group_id': getattr(group, 'group_id', None),
452 'landing_rev': repo.DEFAULT_BRANCH_NAME
461 'landing_rev': repo.DEFAULT_BRANCH_NAME
453 }
462 }
454 rm.create(form_data, user, just_db=True)
463 rm.create(form_data, user, just_db=True)
455 sa.commit()
464 sa.commit()
456 removed = []
465 removed = []
457 if remove_obsolete:
466 if remove_obsolete:
458 # remove from database those repositories that are not in the filesystem
467 # remove from database those repositories that are not in the filesystem
459 for repo in sa.query(Repository).all():
468 for repo in sa.query(Repository).all():
460 if repo.repo_name not in initial_repo_list.keys():
469 if repo.repo_name not in initial_repo_list.keys():
461 log.debug("Removing non existing repository found in db %s" %
470 log.debug("Removing non existing repository found in db %s" %
462 repo.repo_name)
471 repo.repo_name)
463 removed.append(repo.repo_name)
472 removed.append(repo.repo_name)
464 sa.delete(repo)
473 sa.delete(repo)
465 sa.commit()
474 sa.commit()
466
475
467 # clear cache keys
476 # clear cache keys
468 log.debug("Clearing cache keys now...")
477 log.debug("Clearing cache keys now...")
469 CacheInvalidation.clear_cache()
478 CacheInvalidation.clear_cache()
470 sa.commit()
479 sa.commit()
471 return added, removed
480 return added, removed
472
481
473
482
474 # set cache regions for beaker so celery can utilise it
483 # set cache regions for beaker so celery can utilise it
475 def add_cache(settings):
484 def add_cache(settings):
476 cache_settings = {'regions': None}
485 cache_settings = {'regions': None}
477 for key in settings.keys():
486 for key in settings.keys():
478 for prefix in ['beaker.cache.', 'cache.']:
487 for prefix in ['beaker.cache.', 'cache.']:
479 if key.startswith(prefix):
488 if key.startswith(prefix):
480 name = key.split(prefix)[1].strip()
489 name = key.split(prefix)[1].strip()
481 cache_settings[name] = settings[key].strip()
490 cache_settings[name] = settings[key].strip()
482 if cache_settings['regions']:
491 if cache_settings['regions']:
483 for region in cache_settings['regions'].split(','):
492 for region in cache_settings['regions'].split(','):
484 region = region.strip()
493 region = region.strip()
485 region_settings = {}
494 region_settings = {}
486 for key, value in cache_settings.items():
495 for key, value in cache_settings.items():
487 if key.startswith(region):
496 if key.startswith(region):
488 region_settings[key.split('.')[1]] = value
497 region_settings[key.split('.')[1]] = value
489 region_settings['expire'] = int(region_settings.get('expire',
498 region_settings['expire'] = int(region_settings.get('expire',
490 60))
499 60))
491 region_settings.setdefault('lock_dir',
500 region_settings.setdefault('lock_dir',
492 cache_settings.get('lock_dir'))
501 cache_settings.get('lock_dir'))
493 region_settings.setdefault('data_dir',
502 region_settings.setdefault('data_dir',
494 cache_settings.get('data_dir'))
503 cache_settings.get('data_dir'))
495
504
496 if 'type' not in region_settings:
505 if 'type' not in region_settings:
497 region_settings['type'] = cache_settings.get('type',
506 region_settings['type'] = cache_settings.get('type',
498 'memory')
507 'memory')
499 beaker.cache.cache_regions[region] = region_settings
508 beaker.cache.cache_regions[region] = region_settings
500
509
501
510
502 def load_rcextensions(root_path):
511 def load_rcextensions(root_path):
503 import rhodecode
512 import rhodecode
504 from rhodecode.config import conf
513 from rhodecode.config import conf
505
514
506 path = os.path.join(root_path, 'rcextensions', '__init__.py')
515 path = os.path.join(root_path, 'rcextensions', '__init__.py')
507 if os.path.isfile(path):
516 if os.path.isfile(path):
508 rcext = create_module('rc', path)
517 rcext = create_module('rc', path)
509 EXT = rhodecode.EXTENSIONS = rcext
518 EXT = rhodecode.EXTENSIONS = rcext
510 log.debug('Found rcextensions now loading %s...' % rcext)
519 log.debug('Found rcextensions now loading %s...' % rcext)
511
520
512 # Additional mappings that are not present in the pygments lexers
521 # Additional mappings that are not present in the pygments lexers
513 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
522 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
514
523
515 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
524 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
516
525
517 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
526 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
518 log.debug('settings custom INDEX_EXTENSIONS')
527 log.debug('settings custom INDEX_EXTENSIONS')
519 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
528 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
520
529
521 #ADDITIONAL MAPPINGS
530 #ADDITIONAL MAPPINGS
522 log.debug('adding extra into INDEX_EXTENSIONS')
531 log.debug('adding extra into INDEX_EXTENSIONS')
523 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
532 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
524
533
525
534
526 #==============================================================================
535 #==============================================================================
527 # TEST FUNCTIONS AND CREATORS
536 # TEST FUNCTIONS AND CREATORS
528 #==============================================================================
537 #==============================================================================
529 def create_test_index(repo_location, config, full_index):
538 def create_test_index(repo_location, config, full_index):
530 """
539 """
531 Makes default test index
540 Makes default test index
532
541
533 :param config: test config
542 :param config: test config
534 :param full_index:
543 :param full_index:
535 """
544 """
536
545
537 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
546 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
538 from rhodecode.lib.pidlock import DaemonLock, LockHeld
547 from rhodecode.lib.pidlock import DaemonLock, LockHeld
539
548
540 repo_location = repo_location
549 repo_location = repo_location
541
550
542 index_location = os.path.join(config['app_conf']['index_dir'])
551 index_location = os.path.join(config['app_conf']['index_dir'])
543 if not os.path.exists(index_location):
552 if not os.path.exists(index_location):
544 os.makedirs(index_location)
553 os.makedirs(index_location)
545
554
546 try:
555 try:
547 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
556 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
548 WhooshIndexingDaemon(index_location=index_location,
557 WhooshIndexingDaemon(index_location=index_location,
549 repo_location=repo_location)\
558 repo_location=repo_location)\
550 .run(full_index=full_index)
559 .run(full_index=full_index)
551 l.release()
560 l.release()
552 except LockHeld:
561 except LockHeld:
553 pass
562 pass
554
563
555
564
556 def create_test_env(repos_test_path, config):
565 def create_test_env(repos_test_path, config):
557 """
566 """
558 Makes a fresh database and
567 Makes a fresh database and
559 install test repository into tmp dir
568 install test repository into tmp dir
560 """
569 """
561 from rhodecode.lib.db_manage import DbManage
570 from rhodecode.lib.db_manage import DbManage
562 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
571 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
563
572
564 # PART ONE create db
573 # PART ONE create db
565 dbconf = config['sqlalchemy.db1.url']
574 dbconf = config['sqlalchemy.db1.url']
566 log.debug('making test db %s' % dbconf)
575 log.debug('making test db %s' % dbconf)
567
576
568 # create test dir if it doesn't exist
577 # create test dir if it doesn't exist
569 if not os.path.isdir(repos_test_path):
578 if not os.path.isdir(repos_test_path):
570 log.debug('Creating testdir %s' % repos_test_path)
579 log.debug('Creating testdir %s' % repos_test_path)
571 os.makedirs(repos_test_path)
580 os.makedirs(repos_test_path)
572
581
573 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
582 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
574 tests=True)
583 tests=True)
575 dbmanage.create_tables(override=True)
584 dbmanage.create_tables(override=True)
576 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
585 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
577 dbmanage.create_default_user()
586 dbmanage.create_default_user()
578 dbmanage.admin_prompt()
587 dbmanage.admin_prompt()
579 dbmanage.create_permissions()
588 dbmanage.create_permissions()
580 dbmanage.populate_default_permissions()
589 dbmanage.populate_default_permissions()
581 Session.commit()
590 Session.commit()
582 # PART TWO make test repo
591 # PART TWO make test repo
583 log.debug('making test vcs repositories')
592 log.debug('making test vcs repositories')
584
593
585 idx_path = config['app_conf']['index_dir']
594 idx_path = config['app_conf']['index_dir']
586 data_path = config['app_conf']['cache_dir']
595 data_path = config['app_conf']['cache_dir']
587
596
588 #clean index and data
597 #clean index and data
589 if idx_path and os.path.exists(idx_path):
598 if idx_path and os.path.exists(idx_path):
590 log.debug('remove %s' % idx_path)
599 log.debug('remove %s' % idx_path)
591 shutil.rmtree(idx_path)
600 shutil.rmtree(idx_path)
592
601
593 if data_path and os.path.exists(data_path):
602 if data_path and os.path.exists(data_path):
594 log.debug('remove %s' % data_path)
603 log.debug('remove %s' % data_path)
595 shutil.rmtree(data_path)
604 shutil.rmtree(data_path)
596
605
597 #CREATE DEFAULT TEST REPOS
606 #CREATE DEFAULT TEST REPOS
598 cur_dir = dn(dn(abspath(__file__)))
607 cur_dir = dn(dn(abspath(__file__)))
599 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
608 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
600 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
609 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
601 tar.close()
610 tar.close()
602
611
603 cur_dir = dn(dn(abspath(__file__)))
612 cur_dir = dn(dn(abspath(__file__)))
604 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
613 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
605 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
614 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
606 tar.close()
615 tar.close()
607
616
608 #LOAD VCS test stuff
617 #LOAD VCS test stuff
609 from rhodecode.tests.vcs import setup_package
618 from rhodecode.tests.vcs import setup_package
610 setup_package()
619 setup_package()
611
620
612
621
613 #==============================================================================
622 #==============================================================================
614 # PASTER COMMANDS
623 # PASTER COMMANDS
615 #==============================================================================
624 #==============================================================================
616 class BasePasterCommand(Command):
625 class BasePasterCommand(Command):
617 """
626 """
618 Abstract Base Class for paster commands.
627 Abstract Base Class for paster commands.
619
628
620 The celery commands are somewhat aggressive about loading
629 The celery commands are somewhat aggressive about loading
621 celery.conf, and since our module sets the `CELERY_LOADER`
630 celery.conf, and since our module sets the `CELERY_LOADER`
622 environment variable to our loader, we have to bootstrap a bit and
631 environment variable to our loader, we have to bootstrap a bit and
623 make sure we've had a chance to load the pylons config off of the
632 make sure we've had a chance to load the pylons config off of the
624 command line, otherwise everything fails.
633 command line, otherwise everything fails.
625 """
634 """
626 min_args = 1
635 min_args = 1
627 min_args_error = "Please provide a paster config file as an argument."
636 min_args_error = "Please provide a paster config file as an argument."
628 takes_config_file = 1
637 takes_config_file = 1
629 requires_config_file = True
638 requires_config_file = True
630
639
631 def notify_msg(self, msg, log=False):
640 def notify_msg(self, msg, log=False):
632 """Make a notification to user, additionally if logger is passed
641 """Make a notification to user, additionally if logger is passed
633 it logs this action using given logger
642 it logs this action using given logger
634
643
635 :param msg: message that will be printed to user
644 :param msg: message that will be printed to user
636 :param log: logging instance, to use to additionally log this message
645 :param log: logging instance, to use to additionally log this message
637
646
638 """
647 """
639 if log and isinstance(log, logging):
648 if log and isinstance(log, logging):
640 log(msg)
649 log(msg)
641
650
642 def run(self, args):
651 def run(self, args):
643 """
652 """
644 Overrides Command.run
653 Overrides Command.run
645
654
646 Checks for a config file argument and loads it.
655 Checks for a config file argument and loads it.
647 """
656 """
648 if len(args) < self.min_args:
657 if len(args) < self.min_args:
649 raise BadCommand(
658 raise BadCommand(
650 self.min_args_error % {'min_args': self.min_args,
659 self.min_args_error % {'min_args': self.min_args,
651 'actual_args': len(args)})
660 'actual_args': len(args)})
652
661
653 # Decrement because we're going to lob off the first argument.
662 # Decrement because we're going to lob off the first argument.
654 # @@ This is hacky
663 # @@ This is hacky
655 self.min_args -= 1
664 self.min_args -= 1
656 self.bootstrap_config(args[0])
665 self.bootstrap_config(args[0])
657 self.update_parser()
666 self.update_parser()
658 return super(BasePasterCommand, self).run(args[1:])
667 return super(BasePasterCommand, self).run(args[1:])
659
668
660 def update_parser(self):
669 def update_parser(self):
661 """
670 """
662 Abstract method. Allows for the class's parser to be updated
671 Abstract method. Allows for the class's parser to be updated
663 before the superclass's `run` method is called. Necessary to
672 before the superclass's `run` method is called. Necessary to
664 allow options/arguments to be passed through to the underlying
673 allow options/arguments to be passed through to the underlying
665 celery command.
674 celery command.
666 """
675 """
667 raise NotImplementedError("Abstract Method.")
676 raise NotImplementedError("Abstract Method.")
668
677
669 def bootstrap_config(self, conf):
678 def bootstrap_config(self, conf):
670 """
679 """
671 Loads the pylons configuration.
680 Loads the pylons configuration.
672 """
681 """
673 from pylons import config as pylonsconfig
682 from pylons import config as pylonsconfig
674
683
675 self.path_to_ini_file = os.path.realpath(conf)
684 self.path_to_ini_file = os.path.realpath(conf)
676 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
685 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
677 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
686 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now