##// END OF EJS Templates
Fixed archivals by passing baseui to scm get method for hg....
marcink -
r715:066af351 beta
parent child Browse files
Show More
@@ -1,230 +1,230 b''
1 1 .. _setup:
2 2
3 3 Setup
4 4 =====
5 5
6 6
7 7 Setting up the application
8 8 --------------------------
9 9
10 10 ::
11 11
12 12 paster make-config RhodeCode production.ini
13 13
14 14 - This will create `production.ini` config inside the directory
15 this config contains various settings for rhodecode, e.g proxy port,
15 this config contains various settings for RhodeCode, e.g proxy port,
16 16 email settings,static files, cache and logging.
17 17
18 18 ::
19 19
20 20 paster setup-app production.ini
21 21
22 22 - This command will create all needed tables and an admin account.
23 23 When asked for a path You can either use a new location of one with already
24 24 existing ones. RhodeCode will simply add all new found repositories to
25 25 it's database. Also make sure You specify correct path to repositories.
26 26 - Remember that the given path for mercurial_ repositories must be write
27 27 accessible for the application. It's very important since RhodeCode web interface
28 28 will work even without such an access but, when trying to do a push it'll
29 29 eventually fail with permission denied errors.
30 30 - Run
31 31
32 32 ::
33 33
34 34 paster serve production.ini
35 35
36 - This command runs the rhodecode server the app should be available at the
36 - This command runs the RhodeCode server the app should be available at the
37 37 127.0.0.1:5000. This ip and port is configurable via the production.ini
38 38 file created in previous step
39 39 - Use admin account you created to login.
40 40 - Default permissions on each repository is read, and owner is admin. So
41 41 remember to update these if needed.
42 42
43 43
44 44 Setting up Whoosh full text search
45 45 ----------------------------------
46 46
47 47 Index for whoosh can be build starting from version 1.1 using paster command
48 48 passing repo locations to index, as well as Your config file that stores
49 49 whoosh index files locations. There is possible to pass `-f` to the options
50 50 to enable full index rebuild. Without that indexing will run always in in
51 51 incremental mode.
52 52
53 53 ::
54 54
55 55 paster make-index --repo-location=<location for repos> production.ini
56 56
57 57 for full index rebuild You can use
58 58
59 59 ::
60 60
61 61 paster make-index -f --repo-location=<location for repos> production.ini
62 62
63 63 - For full text search You can either put crontab entry for
64 64
65 65 This command can be run even from crontab in order to do periodical
66 66 index builds and keep Your index always up to date. An example entry might
67 67 look like this
68 68
69 69 ::
70 70
71 71 /path/to/python/bin/paster --repo-location=<location for repos> /path/to/rhodecode/production.ini
72 72
73 73 When using incremental(default) mode whoosh will check last modification date
74 74 of each file and add it to reindex if newer file is available. Also indexing
75 75 daemon checks for removed files and removes them from index.
76 76
77 77 Sometime You might want to rebuild index from scratch. You can do that using
78 78 the `-f` flag passed to paster command or, in admin panel You can check
79 79 `build from scratch` flag.
80 80
81 81
82 82 Setting up LDAP support
83 83 -----------------------
84 84
85 85
86 86 RhodeCode starting from version 1.1 supports ldap authentication. In order
87 87 to use ldap, You have to install ldap-python package. This package is available
88 88 via pypi, so You can install it by running
89 89
90 90 ::
91 91
92 92 easy_install ldap-python
93 93
94 94 ::
95 95
96 96 pip install ldap-python
97 97
98 98
99 99 ldap-python requires some certain libs on Your system, so before installing it
100 100 check that You have at least `openldap`, and `sasl` libraries.
101 101
102 102 ldap settings are located in admin->permissions section,
103 103
104 104 Here's a typical ldap setup::
105 105
106 106 Enable ldap = checked #controlls if ldap access is enabled
107 107 Host = host.domain.org #acctuall ldap server to connect
108 108 Port = 389 or 689 for ldaps #ldap server ports
109 109 Enable LDAPS = unchecked #enable disable ldaps
110 110 Account = <account> #access for ldap server(if required)
111 111 Password = <password> #password for ldap server(if required)
112 112 Base DN = CN=users,DC=host,DC=domain,DC=org
113 113
114 114
115 115 `Account` and `Password` are optional, and used for two-phase ldap
116 116 authentication so those are credentials to access Your ldap, if it doesn't
117 117 support anonymous search/user lookups.
118 118
119 119 If all data are entered correctly, and `ldap-python` is properly installed
120 Users should be granted to access rhodecode wit theire ldap accounts. When
121 logging at the first time an special ldap account is created inside rhodecode,
120 Users should be granted to access RhodeCode wit theire ldap accounts. When
121 logging at the first time an special ldap account is created inside RhodeCode,
122 122 so You can control over permissions even on ldap users. If such user exists
123 already in rhodecode database ldap user with the same username would be not
124 able to access rhodecode.
123 already in RhodeCode database ldap user with the same username would be not
124 able to access RhodeCode.
125 125
126 126 If You have problems with ldap access and believe You entered correct
127 information check out the rhodecode logs,any error messages sent from
127 information check out the RhodeCode logs,any error messages sent from
128 128 ldap will be saved there.
129 129
130 130
131 131 Nginx virtual host example
132 132 --------------------------
133 133
134 134 Sample config for nginx using proxy::
135 135
136 136 server {
137 137 listen 80;
138 138 server_name hg.myserver.com;
139 139 access_log /var/log/nginx/rhodecode.access.log;
140 140 error_log /var/log/nginx/rhodecode.error.log;
141 141 location / {
142 142 root /var/www/rhodecode/rhodecode/public/;
143 143 if (!-f $request_filename){
144 144 proxy_pass http://127.0.0.1:5000;
145 145 }
146 146 #this is important for https !!!
147 147 proxy_set_header X-Url-Scheme $scheme;
148 148 include /etc/nginx/proxy.conf;
149 149 }
150 150 }
151 151
152 152 Here's the proxy.conf. It's tuned so it'll not timeout on long
153 153 pushes and also on large pushes::
154 154
155 155 proxy_redirect off;
156 156 proxy_set_header Host $host;
157 157 proxy_set_header X-Host $http_host;
158 158 proxy_set_header X-Real-IP $remote_addr;
159 159 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
160 160 proxy_set_header Proxy-host $proxy_host;
161 161 client_max_body_size 400m;
162 162 client_body_buffer_size 128k;
163 163 proxy_buffering off;
164 164 proxy_connect_timeout 3600;
165 165 proxy_send_timeout 3600;
166 166 proxy_read_timeout 3600;
167 167 proxy_buffer_size 8k;
168 168 proxy_buffers 8 32k;
169 169 proxy_busy_buffers_size 64k;
170 170 proxy_temp_file_write_size 64k;
171 171
172 172 Also when using root path with nginx You might set the static files to false
173 173 in production.ini file::
174 174
175 175 [app:main]
176 176 use = egg:rhodecode
177 177 full_stack = true
178 178 static_files = false
179 179 lang=en
180 180 cache_dir = %(here)s/data
181 181
182 182 To not have the statics served by the application. And improve speed.
183 183
184 184
185 185
186 186 Apache's example FCGI config
187 187 ----------------------------
188 188
189 189 TODO !
190 190
191 191 Other configuration files
192 192 -------------------------
193 193
194 194 Some extra configuration files and examples can be found here:
195 195 http://hg.python-works.com/rhodecode/files/tip/init.d
196 196
197 197 and also an celeryconfig file can be use from here:
198 198 http://hg.python-works.com/rhodecode/files/tip/celeryconfig.py
199 199
200 200 Troubleshooting
201 201 ---------------
202 202
203 203 - missing static files ?
204 204
205 205 - make sure either to set the `static_files = true` in the .ini file or
206 206 double check the root path for Your http setup. It should point to
207 207 for example:
208 208 /home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
209 209
210 210 - can't install celery/rabbitmq
211 211
212 212 - don't worry RhodeCode works without them too. No extra setup required
213 213
214 214
215 215 - long lasting push timeouts ?
216 216
217 217 - make sure You set a longer timeouts in Your proxy/fcgi settings, timeouts
218 are caused by https server and not rhodecode
218 are caused by https server and not RhodeCode
219 219
220 220 - large pushes timeouts ?
221 221
222 222 - make sure You set a proper max_body_size for the http server
223 223
224 224
225 225
226 226 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
227 227 .. _python: http://www.python.org/
228 228 .. _mercurial: http://mercurial.selenic.com/
229 229 .. _celery: http://celeryproject.org/
230 230 .. _rabbitmq: http://www.rabbitmq.com/ No newline at end of file
@@ -1,242 +1,243 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 9, 2010
22 22 Model for RhodeCode
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region, region_invalidate
26 26 from mercurial import ui
27 27 from rhodecode import BACKENDS
28 28 from rhodecode.lib import helpers as h
29 29 from rhodecode.lib.auth import HasRepoPermissionAny
30 30 from rhodecode.lib.utils import get_repos
31 31 from rhodecode.model import meta
32 32 from rhodecode.model.db import Repository, User, RhodeCodeUi, CacheInvalidation
33 33 from rhodecode.model.caching_query import FromCache
34 34 from sqlalchemy.orm import joinedload
35 35 from sqlalchemy.orm.session import make_transient
36 36 from vcs import get_backend
37 37 from vcs.utils.helpers import get_scm
38 38 from vcs.exceptions import RepositoryError, VCSError
39 39 from vcs.utils.lazy import LazyProperty
40 40 import traceback
41 41 import logging
42 42 import os
43 43 import time
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47 class ScmModel(object):
48 48 """
49 49 Mercurial Model
50 50 """
51 51
52 52 def __init__(self):
53 53 self.sa = meta.Session()
54 54
55 55 @LazyProperty
56 56 def repos_path(self):
57 57 """
58 58 Get's the repositories root path from database
59 59 """
60 60 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
61 61
62 62 return q.ui_value
63 63
64 64 def repo_scan(self, repos_path, baseui, initial=False):
65 65 """
66 66 Listing of repositories in given path. This path should not be a
67 67 repository itself. Return a dictionary of repository objects
68 68
69 69 :param repos_path: path to directory containing repositories
70 70 :param baseui
71 71 :param initial: initial scan
72 72 """
73 73 log.info('scanning for repositories in %s', repos_path)
74 74
75 75 if not isinstance(baseui, ui.ui):
76 76 baseui = ui.ui()
77 77 repos_list = {}
78 78
79 79 for name, path in get_repos(repos_path):
80 80 try:
81 81 if repos_list.has_key(name):
82 82 raise RepositoryError('Duplicate repository name %s '
83 83 'found in %s' % (name, path))
84 84 else:
85 85
86 86 klass = get_backend(path[0])
87 87
88 88 if path[0] == 'hg' and path[0] in BACKENDS.keys():
89 89 repos_list[name] = klass(path[1], baseui=baseui)
90 90
91 91 if path[0] == 'git' and path[0] in BACKENDS.keys():
92 92 repos_list[name] = klass(path[1])
93 93 except OSError:
94 94 continue
95 95
96 96 return repos_list
97 97
98 98 def get_repos(self, all_repos=None):
99 99 """
100 100 Get all repos from db and for each repo create it's backend instance.
101 101 and fill that backed with information from database
102 102
103 103 :param all_repos: give specific repositories list, good for filtering
104 104 """
105 105 if not all_repos:
106 106 all_repos = self.sa.query(Repository)\
107 107 .order_by(Repository.repo_name).all()
108 108
109 109 for r in all_repos:
110 110
111 111 repo = self.get(r.repo_name)
112 112
113 113 if repo is not None:
114 114 last_change = repo.last_change
115 115 tip = h.get_changeset_safe(repo, 'tip')
116 116
117 117 tmp_d = {}
118 118 tmp_d['name'] = repo.name
119 119 tmp_d['name_sort'] = tmp_d['name'].lower()
120 120 tmp_d['description'] = repo.dbrepo.description
121 121 tmp_d['description_sort'] = tmp_d['description']
122 122 tmp_d['last_change'] = last_change
123 123 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
124 124 tmp_d['tip'] = tip.raw_id
125 125 tmp_d['tip_sort'] = tip.revision
126 126 tmp_d['rev'] = tip.revision
127 127 tmp_d['contact'] = repo.dbrepo.user.full_contact
128 128 tmp_d['contact_sort'] = tmp_d['contact']
129 129 tmp_d['repo_archives'] = list(repo._get_archives())
130 130 tmp_d['last_msg'] = tip.message
131 131 tmp_d['repo'] = repo
132 132 yield tmp_d
133 133
134 134 def get_repo(self, repo_name):
135 135 return self.get(repo_name)
136 136
137 137 def get(self, repo_name):
138 138 """
139 139 Get's repository from given name, creates BackendInstance and
140 140 propagates it's data from database with all additional information
141 141 :param repo_name:
142 142 """
143 143 if not HasRepoPermissionAny('repository.read', 'repository.write',
144 144 'repository.admin')(repo_name, 'get repo check'):
145 145 return
146 146
147 147 @cache_region('long_term')
148 148 def _get_repo(repo_name):
149 149
150 150 repo_path = os.path.join(self.repos_path, repo_name)
151 151 alias = get_scm(repo_path)[0]
152 152
153 153 log.debug('Creating instance of %s repository', alias)
154 154 backend = get_backend(alias)
155 155
156 156 #TODO: get the baseui from somewhere for this
157 157 if alias == 'hg':
158 repo = backend(repo_path, create=False, baseui=None)
158 from pylons import app_globals as g
159 repo = backend(repo_path, create=False, baseui=g.baseui)
159 160 #skip hidden web repository
160 161 if repo._get_hidden():
161 162 return
162 163 else:
163 164 repo = backend(repo_path, create=False)
164 165
165 166 dbrepo = self.sa.query(Repository)\
166 167 .options(joinedload(Repository.fork))\
167 168 .options(joinedload(Repository.user))\
168 169 .filter(Repository.repo_name == repo_name)\
169 170 .scalar()
170 171 make_transient(dbrepo)
171 172 repo.dbrepo = dbrepo
172 173 return repo
173 174
174 175 invalidate = self._should_invalidate(repo_name)
175 176 if invalidate:
176 177 log.info('invalidating cache for repository %s', repo_name)
177 178 region_invalidate(_get_repo, None, repo_name)
178 179 self._mark_invalidated(invalidate)
179 180
180 181 return _get_repo(repo_name)
181 182
182 183
183 184
184 185 def mark_for_invalidation(self, repo_name):
185 186 """
186 187 Puts cache invalidation task into db for
187 188 further global cache invalidation
188 189
189 190 :param repo_name: this repo that should invalidation take place
190 191 """
191 192 log.debug('marking %s for invalidation', repo_name)
192 193 cache = self.sa.query(CacheInvalidation)\
193 194 .filter(CacheInvalidation.cache_key == repo_name).scalar()
194 195
195 196 if cache:
196 197 #mark this cache as inactive
197 198 cache.cache_active = False
198 199 else:
199 200 log.debug('cache key not found in invalidation db -> creating one')
200 201 cache = CacheInvalidation(repo_name)
201 202
202 203 try:
203 204 self.sa.add(cache)
204 205 self.sa.commit()
205 206 except:
206 207 log.error(traceback.format_exc())
207 208 self.sa.rollback()
208 209
209 210
210 211
211 212
212 213
213 214 def _should_invalidate(self, repo_name):
214 215 """
215 216 Looks up database for invalidation signals for this repo_name
216 217 :param repo_name:
217 218 """
218 219
219 220 ret = self.sa.query(CacheInvalidation)\
220 221 .options(FromCache('sql_cache_short',
221 222 'get_invalidation_%s' % repo_name))\
222 223 .filter(CacheInvalidation.cache_key == repo_name)\
223 224 .filter(CacheInvalidation.cache_active == False)\
224 225 .scalar()
225 226
226 227 return ret
227 228
228 229 def _mark_invalidated(self, cache_key):
229 230 """
230 231 Marks all occurences of cache to invaldation as already invalidated
231 232 @param repo_name:
232 233 """
233 234 if cache_key:
234 235 log.debug('marking %s as already invalidated', cache_key)
235 236 try:
236 237 cache_key.cache_active = True
237 238 self.sa.add(cache_key)
238 239 self.sa.commit()
239 240 except:
240 241 log.error(traceback.format_exc())
241 242 self.sa.rollback()
242 243
General Comments 0
You need to be logged in to leave comments. Login now