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