##// END OF EJS Templates
feat(git lfs path and path to hg large files in *.ini files): moved git lfs path and path of hg large files to *.ini files.
ilin.s -
r5535:7350be7a default
parent child Browse files
Show More
@@ -1,899 +1,905 b''
1
1
2 ; #########################################
2 ; #########################################
3 ; RHODECODE COMMUNITY EDITION CONFIGURATION
3 ; RHODECODE COMMUNITY EDITION CONFIGURATION
4 ; #########################################
4 ; #########################################
5
5
6 [DEFAULT]
6 [DEFAULT]
7 ; Debug flag sets all loggers to debug, and enables request tracking
7 ; Debug flag sets all loggers to debug, and enables request tracking
8 debug = true
8 debug = true
9
9
10 ; ########################################################################
10 ; ########################################################################
11 ; EMAIL CONFIGURATION
11 ; EMAIL CONFIGURATION
12 ; These settings will be used by the RhodeCode mailing system
12 ; These settings will be used by the RhodeCode mailing system
13 ; ########################################################################
13 ; ########################################################################
14
14
15 ; prefix all emails subjects with given prefix, helps filtering out emails
15 ; prefix all emails subjects with given prefix, helps filtering out emails
16 #email_prefix = [RhodeCode]
16 #email_prefix = [RhodeCode]
17
17
18 ; email FROM address all mails will be sent
18 ; email FROM address all mails will be sent
19 #app_email_from = rhodecode-noreply@localhost
19 #app_email_from = rhodecode-noreply@localhost
20
20
21 #smtp_server = mail.server.com
21 #smtp_server = mail.server.com
22 #smtp_username =
22 #smtp_username =
23 #smtp_password =
23 #smtp_password =
24 #smtp_port =
24 #smtp_port =
25 #smtp_use_tls = false
25 #smtp_use_tls = false
26 #smtp_use_ssl = true
26 #smtp_use_ssl = true
27
27
28 [server:main]
28 [server:main]
29 ; COMMON HOST/IP CONFIG, This applies mostly to develop setup,
29 ; COMMON HOST/IP CONFIG, This applies mostly to develop setup,
30 ; Host port for gunicorn are controlled by gunicorn_conf.py
30 ; Host port for gunicorn are controlled by gunicorn_conf.py
31 host = 127.0.0.1
31 host = 127.0.0.1
32 port = 10020
32 port = 10020
33
33
34
34
35 ; ###########################
35 ; ###########################
36 ; GUNICORN APPLICATION SERVER
36 ; GUNICORN APPLICATION SERVER
37 ; ###########################
37 ; ###########################
38
38
39 ; run with gunicorn --config gunicorn_conf.py --paste rhodecode.ini
39 ; run with gunicorn --config gunicorn_conf.py --paste rhodecode.ini
40
40
41 ; Module to use, this setting shouldn't be changed
41 ; Module to use, this setting shouldn't be changed
42 use = egg:gunicorn#main
42 use = egg:gunicorn#main
43
43
44 ; Prefix middleware for RhodeCode.
44 ; Prefix middleware for RhodeCode.
45 ; recommended when using proxy setup.
45 ; recommended when using proxy setup.
46 ; allows to set RhodeCode under a prefix in server.
46 ; allows to set RhodeCode under a prefix in server.
47 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
47 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
48 ; And set your prefix like: `prefix = /custom_prefix`
48 ; And set your prefix like: `prefix = /custom_prefix`
49 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
49 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
50 ; to make your cookies only work on prefix url
50 ; to make your cookies only work on prefix url
51 [filter:proxy-prefix]
51 [filter:proxy-prefix]
52 use = egg:PasteDeploy#prefix
52 use = egg:PasteDeploy#prefix
53 prefix = /
53 prefix = /
54
54
55 [app:main]
55 [app:main]
56 ; The %(here)s variable will be replaced with the absolute path of parent directory
56 ; The %(here)s variable will be replaced with the absolute path of parent directory
57 ; of this file
57 ; of this file
58 ; Each option in the app:main can be override by an environmental variable
58 ; Each option in the app:main can be override by an environmental variable
59 ;
59 ;
60 ;To override an option:
60 ;To override an option:
61 ;
61 ;
62 ;RC_<KeyName>
62 ;RC_<KeyName>
63 ;Everything should be uppercase, . and - should be replaced by _.
63 ;Everything should be uppercase, . and - should be replaced by _.
64 ;For example, if you have these configuration settings:
64 ;For example, if you have these configuration settings:
65 ;rc_cache.repo_object.backend = foo
65 ;rc_cache.repo_object.backend = foo
66 ;can be overridden by
66 ;can be overridden by
67 ;export RC_CACHE_REPO_OBJECT_BACKEND=foo
67 ;export RC_CACHE_REPO_OBJECT_BACKEND=foo
68
68
69 use = egg:rhodecode-enterprise-ce
69 use = egg:rhodecode-enterprise-ce
70
70
71 ; enable proxy prefix middleware, defined above
71 ; enable proxy prefix middleware, defined above
72 #filter-with = proxy-prefix
72 #filter-with = proxy-prefix
73
73
74 ; #############
74 ; #############
75 ; DEBUG OPTIONS
75 ; DEBUG OPTIONS
76 ; #############
76 ; #############
77
77
78 pyramid.reload_templates = true
78 pyramid.reload_templates = true
79
79
80 # During development the we want to have the debug toolbar enabled
80 # During development the we want to have the debug toolbar enabled
81 pyramid.includes =
81 pyramid.includes =
82 pyramid_debugtoolbar
82 pyramid_debugtoolbar
83
83
84 debugtoolbar.hosts = 0.0.0.0/0
84 debugtoolbar.hosts = 0.0.0.0/0
85 debugtoolbar.exclude_prefixes =
85 debugtoolbar.exclude_prefixes =
86 /css
86 /css
87 /fonts
87 /fonts
88 /images
88 /images
89 /js
89 /js
90
90
91 ## RHODECODE PLUGINS ##
91 ## RHODECODE PLUGINS ##
92 rhodecode.includes =
92 rhodecode.includes =
93 rhodecode.api
93 rhodecode.api
94
94
95
95
96 # api prefix url
96 # api prefix url
97 rhodecode.api.url = /_admin/api
97 rhodecode.api.url = /_admin/api
98
98
99 ; enable debug style page
99 ; enable debug style page
100 debug_style = true
100 debug_style = true
101
101
102 ; #################
102 ; #################
103 ; END DEBUG OPTIONS
103 ; END DEBUG OPTIONS
104 ; #################
104 ; #################
105
105
106 ; encryption key used to encrypt social plugin tokens,
106 ; encryption key used to encrypt social plugin tokens,
107 ; remote_urls with credentials etc, if not set it defaults to
107 ; remote_urls with credentials etc, if not set it defaults to
108 ; `beaker.session.secret`
108 ; `beaker.session.secret`
109 #rhodecode.encrypted_values.secret =
109 #rhodecode.encrypted_values.secret =
110
110
111 ; decryption strict mode (enabled by default). It controls if decryption raises
111 ; decryption strict mode (enabled by default). It controls if decryption raises
112 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
112 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
113 #rhodecode.encrypted_values.strict = false
113 #rhodecode.encrypted_values.strict = false
114
114
115 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
115 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
116 ; fernet is safer, and we strongly recommend switching to it.
116 ; fernet is safer, and we strongly recommend switching to it.
117 ; Due to backward compatibility aes is used as default.
117 ; Due to backward compatibility aes is used as default.
118 #rhodecode.encrypted_values.algorithm = fernet
118 #rhodecode.encrypted_values.algorithm = fernet
119
119
120 ; Return gzipped responses from RhodeCode (static files/application)
120 ; Return gzipped responses from RhodeCode (static files/application)
121 gzip_responses = false
121 gzip_responses = false
122
122
123 ; Auto-generate javascript routes file on startup
123 ; Auto-generate javascript routes file on startup
124 generate_js_files = false
124 generate_js_files = false
125
125
126 ; System global default language.
126 ; System global default language.
127 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
127 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
128 lang = en
128 lang = en
129
129
130 ; Perform a full repository scan and import on each server start.
130 ; Perform a full repository scan and import on each server start.
131 ; Settings this to true could lead to very long startup time.
131 ; Settings this to true could lead to very long startup time.
132 startup.import_repos = false
132 startup.import_repos = false
133
133
134 ; URL at which the application is running. This is used for Bootstrapping
134 ; URL at which the application is running. This is used for Bootstrapping
135 ; requests in context when no web request is available. Used in ishell, or
135 ; requests in context when no web request is available. Used in ishell, or
136 ; SSH calls. Set this for events to receive proper url for SSH calls.
136 ; SSH calls. Set this for events to receive proper url for SSH calls.
137 app.base_url = http://rhodecode.local
137 app.base_url = http://rhodecode.local
138
138
139 ; Host at which the Service API is running.
139 ; Host at which the Service API is running.
140 app.service_api.host = http://rhodecode.local:10020
140 app.service_api.host = http://rhodecode.local:10020
141
141
142 ; Secret for Service API authentication.
142 ; Secret for Service API authentication.
143 app.service_api.token =
143 app.service_api.token =
144
144
145 ; Unique application ID. Should be a random unique string for security.
145 ; Unique application ID. Should be a random unique string for security.
146 app_instance_uuid = rc-production
146 app_instance_uuid = rc-production
147
147
148 ; Cut off limit for large diffs (size in bytes). If overall diff size on
148 ; Cut off limit for large diffs (size in bytes). If overall diff size on
149 ; commit, or pull request exceeds this limit this diff will be displayed
149 ; commit, or pull request exceeds this limit this diff will be displayed
150 ; partially. E.g 512000 == 512Kb
150 ; partially. E.g 512000 == 512Kb
151 cut_off_limit_diff = 512000
151 cut_off_limit_diff = 512000
152
152
153 ; Cut off limit for large files inside diffs (size in bytes). Each individual
153 ; Cut off limit for large files inside diffs (size in bytes). Each individual
154 ; file inside diff which exceeds this limit will be displayed partially.
154 ; file inside diff which exceeds this limit will be displayed partially.
155 ; E.g 128000 == 128Kb
155 ; E.g 128000 == 128Kb
156 cut_off_limit_file = 128000
156 cut_off_limit_file = 128000
157
157
158 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
158 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
159 vcs_full_cache = true
159 vcs_full_cache = true
160
160
161 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
161 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
162 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
162 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
163 force_https = false
163 force_https = false
164
164
165 ; use Strict-Transport-Security headers
165 ; use Strict-Transport-Security headers
166 use_htsts = false
166 use_htsts = false
167
167
168 ; Set to true if your repos are exposed using the dumb protocol
168 ; Set to true if your repos are exposed using the dumb protocol
169 git_update_server_info = false
169 git_update_server_info = false
170
170
171 ; RSS/ATOM feed options
171 ; RSS/ATOM feed options
172 rss_cut_off_limit = 256000
172 rss_cut_off_limit = 256000
173 rss_items_per_page = 10
173 rss_items_per_page = 10
174 rss_include_diff = false
174 rss_include_diff = false
175
175
176 ; gist URL alias, used to create nicer urls for gist. This should be an
176 ; gist URL alias, used to create nicer urls for gist. This should be an
177 ; url that does rewrites to _admin/gists/{gistid}.
177 ; url that does rewrites to _admin/gists/{gistid}.
178 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
178 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
179 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
179 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
180 gist_alias_url =
180 gist_alias_url =
181
181
182 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
182 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
183 ; used for access.
183 ; used for access.
184 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
184 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
185 ; came from the the logged in user who own this authentication token.
185 ; came from the the logged in user who own this authentication token.
186 ; Additionally @TOKEN syntax can be used to bound the view to specific
186 ; Additionally @TOKEN syntax can be used to bound the view to specific
187 ; authentication token. Such view would be only accessible when used together
187 ; authentication token. Such view would be only accessible when used together
188 ; with this authentication token
188 ; with this authentication token
189 ; list of all views can be found under `/_admin/permissions/auth_token_access`
189 ; list of all views can be found under `/_admin/permissions/auth_token_access`
190 ; The list should be "," separated and on a single line.
190 ; The list should be "," separated and on a single line.
191 ; Most common views to enable:
191 ; Most common views to enable:
192
192
193 # RepoCommitsView:repo_commit_download
193 # RepoCommitsView:repo_commit_download
194 # RepoCommitsView:repo_commit_patch
194 # RepoCommitsView:repo_commit_patch
195 # RepoCommitsView:repo_commit_raw
195 # RepoCommitsView:repo_commit_raw
196 # RepoCommitsView:repo_commit_raw@TOKEN
196 # RepoCommitsView:repo_commit_raw@TOKEN
197 # RepoFilesView:repo_files_diff
197 # RepoFilesView:repo_files_diff
198 # RepoFilesView:repo_archivefile
198 # RepoFilesView:repo_archivefile
199 # RepoFilesView:repo_file_raw
199 # RepoFilesView:repo_file_raw
200 # GistView:*
200 # GistView:*
201 api_access_controllers_whitelist =
201 api_access_controllers_whitelist =
202
202
203 ; Default encoding used to convert from and to unicode
203 ; Default encoding used to convert from and to unicode
204 ; can be also a comma separated list of encoding in case of mixed encodings
204 ; can be also a comma separated list of encoding in case of mixed encodings
205 default_encoding = UTF-8
205 default_encoding = UTF-8
206
206
207 ; instance-id prefix
207 ; instance-id prefix
208 ; a prefix key for this instance used for cache invalidation when running
208 ; a prefix key for this instance used for cache invalidation when running
209 ; multiple instances of RhodeCode, make sure it's globally unique for
209 ; multiple instances of RhodeCode, make sure it's globally unique for
210 ; all running RhodeCode instances. Leave empty if you don't use it
210 ; all running RhodeCode instances. Leave empty if you don't use it
211 instance_id =
211 instance_id =
212
212
213 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
213 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
214 ; of an authentication plugin also if it is disabled by it's settings.
214 ; of an authentication plugin also if it is disabled by it's settings.
215 ; This could be useful if you are unable to log in to the system due to broken
215 ; This could be useful if you are unable to log in to the system due to broken
216 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
216 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
217 ; module to log in again and fix the settings.
217 ; module to log in again and fix the settings.
218 ; Available builtin plugin IDs (hash is part of the ID):
218 ; Available builtin plugin IDs (hash is part of the ID):
219 ; egg:rhodecode-enterprise-ce#rhodecode
219 ; egg:rhodecode-enterprise-ce#rhodecode
220 ; egg:rhodecode-enterprise-ce#pam
220 ; egg:rhodecode-enterprise-ce#pam
221 ; egg:rhodecode-enterprise-ce#ldap
221 ; egg:rhodecode-enterprise-ce#ldap
222 ; egg:rhodecode-enterprise-ce#jasig_cas
222 ; egg:rhodecode-enterprise-ce#jasig_cas
223 ; egg:rhodecode-enterprise-ce#headers
223 ; egg:rhodecode-enterprise-ce#headers
224 ; egg:rhodecode-enterprise-ce#crowd
224 ; egg:rhodecode-enterprise-ce#crowd
225
225
226 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
226 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
227
227
228 ; Flag to control loading of legacy plugins in py:/path format
228 ; Flag to control loading of legacy plugins in py:/path format
229 auth_plugin.import_legacy_plugins = true
229 auth_plugin.import_legacy_plugins = true
230
230
231 ; alternative return HTTP header for failed authentication. Default HTTP
231 ; alternative return HTTP header for failed authentication. Default HTTP
232 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
232 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
233 ; handling that causing a series of failed authentication calls.
233 ; handling that causing a series of failed authentication calls.
234 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
234 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
235 ; This will be served instead of default 401 on bad authentication
235 ; This will be served instead of default 401 on bad authentication
236 auth_ret_code =
236 auth_ret_code =
237
237
238 ; use special detection method when serving auth_ret_code, instead of serving
238 ; use special detection method when serving auth_ret_code, instead of serving
239 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
239 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
240 ; and then serve auth_ret_code to clients
240 ; and then serve auth_ret_code to clients
241 auth_ret_code_detection = false
241 auth_ret_code_detection = false
242
242
243 ; locking return code. When repository is locked return this HTTP code. 2XX
243 ; locking return code. When repository is locked return this HTTP code. 2XX
244 ; codes don't break the transactions while 4XX codes do
244 ; codes don't break the transactions while 4XX codes do
245 lock_ret_code = 423
245 lock_ret_code = 423
246
246
247 ; Filesystem location were repositories should be stored
247 ; Filesystem location were repositories should be stored
248 repo_store.path = /var/opt/rhodecode_repo_store
248 repo_store.path = /var/opt/rhodecode_repo_store
249
249
250 ; allows to setup custom hooks in settings page
250 ; allows to setup custom hooks in settings page
251 allow_custom_hooks_settings = true
251 allow_custom_hooks_settings = true
252
252
253 ; Generated license token required for EE edition license.
253 ; Generated license token required for EE edition license.
254 ; New generated token value can be found in Admin > settings > license page.
254 ; New generated token value can be found in Admin > settings > license page.
255 license_token =
255 license_token =
256
256
257 ; This flag hides sensitive information on the license page such as token, and license data
257 ; This flag hides sensitive information on the license page such as token, and license data
258 license.hide_license_info = false
258 license.hide_license_info = false
259
259
260 ; supervisor connection uri, for managing supervisor and logs.
260 ; supervisor connection uri, for managing supervisor and logs.
261 supervisor.uri =
261 supervisor.uri =
262
262
263 ; supervisord group name/id we only want this RC instance to handle
263 ; supervisord group name/id we only want this RC instance to handle
264 supervisor.group_id = dev
264 supervisor.group_id = dev
265
265
266 ; Display extended labs settings
266 ; Display extended labs settings
267 labs_settings_active = true
267 labs_settings_active = true
268
268
269 ; Custom exception store path, defaults to TMPDIR
269 ; Custom exception store path, defaults to TMPDIR
270 ; This is used to store exception from RhodeCode in shared directory
270 ; This is used to store exception from RhodeCode in shared directory
271 #exception_tracker.store_path =
271 #exception_tracker.store_path =
272
272
273 ; Send email with exception details when it happens
273 ; Send email with exception details when it happens
274 #exception_tracker.send_email = false
274 #exception_tracker.send_email = false
275
275
276 ; Comma separated list of recipients for exception emails,
276 ; Comma separated list of recipients for exception emails,
277 ; e.g admin@rhodecode.com,devops@rhodecode.com
277 ; e.g admin@rhodecode.com,devops@rhodecode.com
278 ; Can be left empty, then emails will be sent to ALL super-admins
278 ; Can be left empty, then emails will be sent to ALL super-admins
279 #exception_tracker.send_email_recipients =
279 #exception_tracker.send_email_recipients =
280
280
281 ; optional prefix to Add to email Subject
281 ; optional prefix to Add to email Subject
282 #exception_tracker.email_prefix = [RHODECODE ERROR]
282 #exception_tracker.email_prefix = [RHODECODE ERROR]
283
283
284 ; NOTE: this setting IS DEPRECATED:
284 ; NOTE: this setting IS DEPRECATED:
285 ; file_store backend is always enabled
285 ; file_store backend is always enabled
286 #file_store.enabled = true
286 #file_store.enabled = true
287
287
288 ; NOTE: this setting IS DEPRECATED:
288 ; NOTE: this setting IS DEPRECATED:
289 ; file_store.backend = X -> use `file_store.backend.type = filesystem_v2` instead
289 ; file_store.backend = X -> use `file_store.backend.type = filesystem_v2` instead
290 ; Storage backend, available options are: local
290 ; Storage backend, available options are: local
291 #file_store.backend = local
291 #file_store.backend = local
292
292
293 ; NOTE: this setting IS DEPRECATED:
293 ; NOTE: this setting IS DEPRECATED:
294 ; file_store.storage_path = X -> use `file_store.filesystem_v2.storage_path = X` instead
294 ; file_store.storage_path = X -> use `file_store.filesystem_v2.storage_path = X` instead
295 ; path to store the uploaded binaries and artifacts
295 ; path to store the uploaded binaries and artifacts
296 #file_store.storage_path = /var/opt/rhodecode_data/file_store
296 #file_store.storage_path = /var/opt/rhodecode_data/file_store
297
297
298 ; Artifacts file-store, is used to store comment attachments and artifacts uploads.
298 ; Artifacts file-store, is used to store comment attachments and artifacts uploads.
299 ; file_store backend type: filesystem_v1, filesystem_v2 or objectstore (s3-based) are available as options
299 ; file_store backend type: filesystem_v1, filesystem_v2 or objectstore (s3-based) are available as options
300 ; filesystem_v1 is backwards compat with pre 5.1 storage changes
300 ; filesystem_v1 is backwards compat with pre 5.1 storage changes
301 ; new installations should choose filesystem_v2 or objectstore (s3-based), pick filesystem when migrating from
301 ; new installations should choose filesystem_v2 or objectstore (s3-based), pick filesystem when migrating from
302 ; previous installations to keep the artifacts without a need of migration
302 ; previous installations to keep the artifacts without a need of migration
303 #file_store.backend.type = filesystem_v2
303 #file_store.backend.type = filesystem_v2
304
304
305 ; filesystem options...
305 ; filesystem options...
306 #file_store.filesystem_v1.storage_path = /var/opt/rhodecode_data/artifacts_file_store
306 #file_store.filesystem_v1.storage_path = /var/opt/rhodecode_data/artifacts_file_store
307
307
308 ; filesystem_v2 options...
308 ; filesystem_v2 options...
309 #file_store.filesystem_v2.storage_path = /var/opt/rhodecode_data/artifacts_file_store
309 #file_store.filesystem_v2.storage_path = /var/opt/rhodecode_data/artifacts_file_store
310 #file_store.filesystem_v2.shards = 8
310 #file_store.filesystem_v2.shards = 8
311
311
312 ; objectstore options...
312 ; objectstore options...
313 ; url for s3 compatible storage that allows to upload artifacts
313 ; url for s3 compatible storage that allows to upload artifacts
314 ; e.g http://minio:9000
314 ; e.g http://minio:9000
315 #file_store.backend.type = objectstore
315 #file_store.backend.type = objectstore
316 #file_store.objectstore.url = http://s3-minio:9000
316 #file_store.objectstore.url = http://s3-minio:9000
317
317
318 ; a top-level bucket to put all other shards in
318 ; a top-level bucket to put all other shards in
319 ; objects will be stored in rhodecode-file-store/shard-N based on the bucket_shards number
319 ; objects will be stored in rhodecode-file-store/shard-N based on the bucket_shards number
320 #file_store.objectstore.bucket = rhodecode-file-store
320 #file_store.objectstore.bucket = rhodecode-file-store
321
321
322 ; number of sharded buckets to create to distribute archives across
322 ; number of sharded buckets to create to distribute archives across
323 ; default is 8 shards
323 ; default is 8 shards
324 #file_store.objectstore.bucket_shards = 8
324 #file_store.objectstore.bucket_shards = 8
325
325
326 ; key for s3 auth
326 ; key for s3 auth
327 #file_store.objectstore.key = s3admin
327 #file_store.objectstore.key = s3admin
328
328
329 ; secret for s3 auth
329 ; secret for s3 auth
330 #file_store.objectstore.secret = s3secret4
330 #file_store.objectstore.secret = s3secret4
331
331
332 ;region for s3 storage
332 ;region for s3 storage
333 #file_store.objectstore.region = eu-central-1
333 #file_store.objectstore.region = eu-central-1
334
334
335 ; Redis url to acquire/check generation of archives locks
335 ; Redis url to acquire/check generation of archives locks
336 archive_cache.locking.url = redis://redis:6379/1
336 archive_cache.locking.url = redis://redis:6379/1
337
337
338 ; Storage backend, only 'filesystem' and 'objectstore' are available now
338 ; Storage backend, only 'filesystem' and 'objectstore' are available now
339 archive_cache.backend.type = filesystem
339 archive_cache.backend.type = filesystem
340
340
341 ; url for s3 compatible storage that allows to upload artifacts
341 ; url for s3 compatible storage that allows to upload artifacts
342 ; e.g http://minio:9000
342 ; e.g http://minio:9000
343 archive_cache.objectstore.url = http://s3-minio:9000
343 archive_cache.objectstore.url = http://s3-minio:9000
344
344
345 ; key for s3 auth
345 ; key for s3 auth
346 archive_cache.objectstore.key = key
346 archive_cache.objectstore.key = key
347
347
348 ; secret for s3 auth
348 ; secret for s3 auth
349 archive_cache.objectstore.secret = secret
349 archive_cache.objectstore.secret = secret
350
350
351 ;region for s3 storage
351 ;region for s3 storage
352 archive_cache.objectstore.region = eu-central-1
352 archive_cache.objectstore.region = eu-central-1
353
353
354 ; number of sharded buckets to create to distribute archives across
354 ; number of sharded buckets to create to distribute archives across
355 ; default is 8 shards
355 ; default is 8 shards
356 archive_cache.objectstore.bucket_shards = 8
356 archive_cache.objectstore.bucket_shards = 8
357
357
358 ; a top-level bucket to put all other shards in
358 ; a top-level bucket to put all other shards in
359 ; objects will be stored in rhodecode-archive-cache/shard-N based on the bucket_shards number
359 ; objects will be stored in rhodecode-archive-cache/shard-N based on the bucket_shards number
360 archive_cache.objectstore.bucket = rhodecode-archive-cache
360 archive_cache.objectstore.bucket = rhodecode-archive-cache
361
361
362 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
362 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
363 archive_cache.objectstore.retry = false
363 archive_cache.objectstore.retry = false
364
364
365 ; number of seconds to wait for next try using retry
365 ; number of seconds to wait for next try using retry
366 archive_cache.objectstore.retry_backoff = 1
366 archive_cache.objectstore.retry_backoff = 1
367
367
368 ; how many tries do do a retry fetch from this backend
368 ; how many tries do do a retry fetch from this backend
369 archive_cache.objectstore.retry_attempts = 10
369 archive_cache.objectstore.retry_attempts = 10
370
370
371 ; Default is $cache_dir/archive_cache if not set
371 ; Default is $cache_dir/archive_cache if not set
372 ; Generated repo archives will be cached at this location
372 ; Generated repo archives will be cached at this location
373 ; and served from the cache during subsequent requests for the same archive of
373 ; and served from the cache during subsequent requests for the same archive of
374 ; the repository. This path is important to be shared across filesystems and with
374 ; the repository. This path is important to be shared across filesystems and with
375 ; RhodeCode and vcsserver
375 ; RhodeCode and vcsserver
376 archive_cache.filesystem.store_dir = /var/opt/rhodecode_data/archive_cache
376 archive_cache.filesystem.store_dir = /var/opt/rhodecode_data/archive_cache
377
377
378 ; The limit in GB sets how much data we cache before recycling last used, defaults to 10 gb
378 ; The limit in GB sets how much data we cache before recycling last used, defaults to 10 gb
379 archive_cache.filesystem.cache_size_gb = 1
379 archive_cache.filesystem.cache_size_gb = 1
380
380
381 ; Eviction policy used to clear out after cache_size_gb limit is reached
381 ; Eviction policy used to clear out after cache_size_gb limit is reached
382 archive_cache.filesystem.eviction_policy = least-recently-stored
382 archive_cache.filesystem.eviction_policy = least-recently-stored
383
383
384 ; By default cache uses sharding technique, this specifies how many shards are there
384 ; By default cache uses sharding technique, this specifies how many shards are there
385 ; default is 8 shards
385 ; default is 8 shards
386 archive_cache.filesystem.cache_shards = 8
386 archive_cache.filesystem.cache_shards = 8
387
387
388 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
388 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
389 archive_cache.filesystem.retry = false
389 archive_cache.filesystem.retry = false
390
390
391 ; number of seconds to wait for next try using retry
391 ; number of seconds to wait for next try using retry
392 archive_cache.filesystem.retry_backoff = 1
392 archive_cache.filesystem.retry_backoff = 1
393
393
394 ; how many tries do do a retry fetch from this backend
394 ; how many tries do do a retry fetch from this backend
395 archive_cache.filesystem.retry_attempts = 10
395 archive_cache.filesystem.retry_attempts = 10
396
396
397
397
398 ; #############
398 ; #############
399 ; CELERY CONFIG
399 ; CELERY CONFIG
400 ; #############
400 ; #############
401
401
402 ; manually run celery: /path/to/celery worker --task-events --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
402 ; manually run celery: /path/to/celery worker --task-events --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
403
403
404 use_celery = true
404 use_celery = true
405
405
406 ; path to store schedule database
406 ; path to store schedule database
407 #celerybeat-schedule.path =
407 #celerybeat-schedule.path =
408
408
409 ; connection url to the message broker (default redis)
409 ; connection url to the message broker (default redis)
410 celery.broker_url = redis://redis:6379/8
410 celery.broker_url = redis://redis:6379/8
411
411
412 ; results backend to get results for (default redis)
412 ; results backend to get results for (default redis)
413 celery.result_backend = redis://redis:6379/8
413 celery.result_backend = redis://redis:6379/8
414
414
415 ; rabbitmq example
415 ; rabbitmq example
416 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
416 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
417
417
418 ; maximum tasks to execute before worker restart
418 ; maximum tasks to execute before worker restart
419 celery.max_tasks_per_child = 20
419 celery.max_tasks_per_child = 20
420
420
421 ; tasks will never be sent to the queue, but executed locally instead.
421 ; tasks will never be sent to the queue, but executed locally instead.
422 celery.task_always_eager = false
422 celery.task_always_eager = false
423
423
424 ; #############
424 ; #############
425 ; DOGPILE CACHE
425 ; DOGPILE CACHE
426 ; #############
426 ; #############
427
427
428 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
428 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
429 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
429 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
430 cache_dir = /var/opt/rhodecode_data
430 cache_dir = /var/opt/rhodecode_data
431
431
432 ; *********************************************
432 ; *********************************************
433 ; `sql_cache_short` cache for heavy SQL queries
433 ; `sql_cache_short` cache for heavy SQL queries
434 ; Only supported backend is `memory_lru`
434 ; Only supported backend is `memory_lru`
435 ; *********************************************
435 ; *********************************************
436 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
436 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
437 rc_cache.sql_cache_short.expiration_time = 30
437 rc_cache.sql_cache_short.expiration_time = 30
438
438
439
439
440 ; *****************************************************
440 ; *****************************************************
441 ; `cache_repo_longterm` cache for repo object instances
441 ; `cache_repo_longterm` cache for repo object instances
442 ; Only supported backend is `memory_lru`
442 ; Only supported backend is `memory_lru`
443 ; *****************************************************
443 ; *****************************************************
444 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
444 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
445 ; by default we use 30 Days, cache is still invalidated on push
445 ; by default we use 30 Days, cache is still invalidated on push
446 rc_cache.cache_repo_longterm.expiration_time = 2592000
446 rc_cache.cache_repo_longterm.expiration_time = 2592000
447 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
447 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
448 rc_cache.cache_repo_longterm.max_size = 10000
448 rc_cache.cache_repo_longterm.max_size = 10000
449
449
450
450
451 ; *********************************************
451 ; *********************************************
452 ; `cache_general` cache for general purpose use
452 ; `cache_general` cache for general purpose use
453 ; for simplicity use rc.file_namespace backend,
453 ; for simplicity use rc.file_namespace backend,
454 ; for performance and scale use rc.redis
454 ; for performance and scale use rc.redis
455 ; *********************************************
455 ; *********************************************
456 rc_cache.cache_general.backend = dogpile.cache.rc.file_namespace
456 rc_cache.cache_general.backend = dogpile.cache.rc.file_namespace
457 rc_cache.cache_general.expiration_time = 43200
457 rc_cache.cache_general.expiration_time = 43200
458 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
458 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
459 #rc_cache.cache_general.arguments.filename = /tmp/cache_general_db
459 #rc_cache.cache_general.arguments.filename = /tmp/cache_general_db
460
460
461 ; alternative `cache_general` redis backend with distributed lock
461 ; alternative `cache_general` redis backend with distributed lock
462 #rc_cache.cache_general.backend = dogpile.cache.rc.redis
462 #rc_cache.cache_general.backend = dogpile.cache.rc.redis
463 #rc_cache.cache_general.expiration_time = 300
463 #rc_cache.cache_general.expiration_time = 300
464
464
465 ; redis_expiration_time needs to be greater then expiration_time
465 ; redis_expiration_time needs to be greater then expiration_time
466 #rc_cache.cache_general.arguments.redis_expiration_time = 7200
466 #rc_cache.cache_general.arguments.redis_expiration_time = 7200
467
467
468 #rc_cache.cache_general.arguments.host = localhost
468 #rc_cache.cache_general.arguments.host = localhost
469 #rc_cache.cache_general.arguments.port = 6379
469 #rc_cache.cache_general.arguments.port = 6379
470 #rc_cache.cache_general.arguments.db = 0
470 #rc_cache.cache_general.arguments.db = 0
471 #rc_cache.cache_general.arguments.socket_timeout = 30
471 #rc_cache.cache_general.arguments.socket_timeout = 30
472 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
472 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
473 #rc_cache.cache_general.arguments.distributed_lock = true
473 #rc_cache.cache_general.arguments.distributed_lock = true
474
474
475 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
475 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
476 #rc_cache.cache_general.arguments.lock_auto_renewal = true
476 #rc_cache.cache_general.arguments.lock_auto_renewal = true
477
477
478 ; *************************************************
478 ; *************************************************
479 ; `cache_perms` cache for permission tree, auth TTL
479 ; `cache_perms` cache for permission tree, auth TTL
480 ; for simplicity use rc.file_namespace backend,
480 ; for simplicity use rc.file_namespace backend,
481 ; for performance and scale use rc.redis
481 ; for performance and scale use rc.redis
482 ; *************************************************
482 ; *************************************************
483 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
483 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
484 rc_cache.cache_perms.expiration_time = 3600
484 rc_cache.cache_perms.expiration_time = 3600
485 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
485 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
486 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms_db
486 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms_db
487
487
488 ; alternative `cache_perms` redis backend with distributed lock
488 ; alternative `cache_perms` redis backend with distributed lock
489 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
489 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
490 #rc_cache.cache_perms.expiration_time = 300
490 #rc_cache.cache_perms.expiration_time = 300
491
491
492 ; redis_expiration_time needs to be greater then expiration_time
492 ; redis_expiration_time needs to be greater then expiration_time
493 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
493 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
494
494
495 #rc_cache.cache_perms.arguments.host = localhost
495 #rc_cache.cache_perms.arguments.host = localhost
496 #rc_cache.cache_perms.arguments.port = 6379
496 #rc_cache.cache_perms.arguments.port = 6379
497 #rc_cache.cache_perms.arguments.db = 0
497 #rc_cache.cache_perms.arguments.db = 0
498 #rc_cache.cache_perms.arguments.socket_timeout = 30
498 #rc_cache.cache_perms.arguments.socket_timeout = 30
499 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
499 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
500 #rc_cache.cache_perms.arguments.distributed_lock = true
500 #rc_cache.cache_perms.arguments.distributed_lock = true
501
501
502 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
502 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
503 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
503 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
504
504
505 ; ***************************************************
505 ; ***************************************************
506 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
506 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
507 ; for simplicity use rc.file_namespace backend,
507 ; for simplicity use rc.file_namespace backend,
508 ; for performance and scale use rc.redis
508 ; for performance and scale use rc.redis
509 ; ***************************************************
509 ; ***************************************************
510 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
510 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
511 rc_cache.cache_repo.expiration_time = 2592000
511 rc_cache.cache_repo.expiration_time = 2592000
512 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
512 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
513 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo_db
513 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo_db
514
514
515 ; alternative `cache_repo` redis backend with distributed lock
515 ; alternative `cache_repo` redis backend with distributed lock
516 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
516 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
517 #rc_cache.cache_repo.expiration_time = 2592000
517 #rc_cache.cache_repo.expiration_time = 2592000
518
518
519 ; redis_expiration_time needs to be greater then expiration_time
519 ; redis_expiration_time needs to be greater then expiration_time
520 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
520 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
521
521
522 #rc_cache.cache_repo.arguments.host = localhost
522 #rc_cache.cache_repo.arguments.host = localhost
523 #rc_cache.cache_repo.arguments.port = 6379
523 #rc_cache.cache_repo.arguments.port = 6379
524 #rc_cache.cache_repo.arguments.db = 1
524 #rc_cache.cache_repo.arguments.db = 1
525 #rc_cache.cache_repo.arguments.socket_timeout = 30
525 #rc_cache.cache_repo.arguments.socket_timeout = 30
526 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
526 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
527 #rc_cache.cache_repo.arguments.distributed_lock = true
527 #rc_cache.cache_repo.arguments.distributed_lock = true
528
528
529 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
529 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
530 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
530 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
531
531
532 ; ##############
532 ; ##############
533 ; BEAKER SESSION
533 ; BEAKER SESSION
534 ; ##############
534 ; ##############
535
535
536 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
536 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
537 ; types are file, ext:redis, ext:database, ext:memcached
537 ; types are file, ext:redis, ext:database, ext:memcached
538 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
538 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
539 #beaker.session.type = file
539 #beaker.session.type = file
540 #beaker.session.data_dir = %(here)s/data/sessions
540 #beaker.session.data_dir = %(here)s/data/sessions
541
541
542 ; Redis based sessions
542 ; Redis based sessions
543 beaker.session.type = ext:redis
543 beaker.session.type = ext:redis
544 beaker.session.url = redis://redis:6379/2
544 beaker.session.url = redis://redis:6379/2
545
545
546 ; DB based session, fast, and allows easy management over logged in users
546 ; DB based session, fast, and allows easy management over logged in users
547 #beaker.session.type = ext:database
547 #beaker.session.type = ext:database
548 #beaker.session.table_name = db_session
548 #beaker.session.table_name = db_session
549 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
549 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
550 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
550 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
551 #beaker.session.sa.pool_recycle = 3600
551 #beaker.session.sa.pool_recycle = 3600
552 #beaker.session.sa.echo = false
552 #beaker.session.sa.echo = false
553
553
554 beaker.session.key = rhodecode
554 beaker.session.key = rhodecode
555 beaker.session.secret = develop-rc-uytcxaz
555 beaker.session.secret = develop-rc-uytcxaz
556 beaker.session.lock_dir = /data_ramdisk/lock
556 beaker.session.lock_dir = /data_ramdisk/lock
557
557
558 ; Secure encrypted cookie. Requires AES and AES python libraries
558 ; Secure encrypted cookie. Requires AES and AES python libraries
559 ; you must disable beaker.session.secret to use this
559 ; you must disable beaker.session.secret to use this
560 #beaker.session.encrypt_key = key_for_encryption
560 #beaker.session.encrypt_key = key_for_encryption
561 #beaker.session.validate_key = validation_key
561 #beaker.session.validate_key = validation_key
562
562
563 ; Sets session as invalid (also logging out user) if it haven not been
563 ; Sets session as invalid (also logging out user) if it haven not been
564 ; accessed for given amount of time in seconds
564 ; accessed for given amount of time in seconds
565 beaker.session.timeout = 2592000
565 beaker.session.timeout = 2592000
566 beaker.session.httponly = true
566 beaker.session.httponly = true
567
567
568 ; Path to use for the cookie. Set to prefix if you use prefix middleware
568 ; Path to use for the cookie. Set to prefix if you use prefix middleware
569 #beaker.session.cookie_path = /custom_prefix
569 #beaker.session.cookie_path = /custom_prefix
570
570
571 ; Set https secure cookie
571 ; Set https secure cookie
572 beaker.session.secure = false
572 beaker.session.secure = false
573
573
574 ; default cookie expiration time in seconds, set to `true` to set expire
574 ; default cookie expiration time in seconds, set to `true` to set expire
575 ; at browser close
575 ; at browser close
576 #beaker.session.cookie_expires = 3600
576 #beaker.session.cookie_expires = 3600
577
577
578 ; #############################
578 ; #############################
579 ; SEARCH INDEXING CONFIGURATION
579 ; SEARCH INDEXING CONFIGURATION
580 ; #############################
580 ; #############################
581
581
582 ; Full text search indexer is available in rhodecode-tools under
582 ; Full text search indexer is available in rhodecode-tools under
583 ; `rhodecode-tools index` command
583 ; `rhodecode-tools index` command
584
584
585 ; WHOOSH Backend, doesn't require additional services to run
585 ; WHOOSH Backend, doesn't require additional services to run
586 ; it works good with few dozen repos
586 ; it works good with few dozen repos
587 search.module = rhodecode.lib.index.whoosh
587 search.module = rhodecode.lib.index.whoosh
588 search.location = %(here)s/data/index
588 search.location = %(here)s/data/index
589
589
590 ; ####################
590 ; ####################
591 ; CHANNELSTREAM CONFIG
591 ; CHANNELSTREAM CONFIG
592 ; ####################
592 ; ####################
593
593
594 ; channelstream enables persistent connections and live notification
594 ; channelstream enables persistent connections and live notification
595 ; in the system. It's also used by the chat system
595 ; in the system. It's also used by the chat system
596
596
597 channelstream.enabled = true
597 channelstream.enabled = true
598
598
599 ; server address for channelstream server on the backend
599 ; server address for channelstream server on the backend
600 channelstream.server = channelstream:9800
600 channelstream.server = channelstream:9800
601
601
602 ; location of the channelstream server from outside world
602 ; location of the channelstream server from outside world
603 ; use ws:// for http or wss:// for https. This address needs to be handled
603 ; use ws:// for http or wss:// for https. This address needs to be handled
604 ; by external HTTP server such as Nginx or Apache
604 ; by external HTTP server such as Nginx or Apache
605 ; see Nginx/Apache configuration examples in our docs
605 ; see Nginx/Apache configuration examples in our docs
606 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
606 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
607 channelstream.secret = ENV_GENERATED
607 channelstream.secret = ENV_GENERATED
608 channelstream.history.location = /var/opt/rhodecode_data/channelstream_history
608 channelstream.history.location = /var/opt/rhodecode_data/channelstream_history
609
609
610 ; Internal application path that Javascript uses to connect into.
610 ; Internal application path that Javascript uses to connect into.
611 ; If you use proxy-prefix the prefix should be added before /_channelstream
611 ; If you use proxy-prefix the prefix should be added before /_channelstream
612 channelstream.proxy_path = /_channelstream
612 channelstream.proxy_path = /_channelstream
613
613
614
614
615 ; ##############################
615 ; ##############################
616 ; MAIN RHODECODE DATABASE CONFIG
616 ; MAIN RHODECODE DATABASE CONFIG
617 ; ##############################
617 ; ##############################
618
618
619 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
619 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
620 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
620 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
621 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
621 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
622 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
622 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
623 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
623 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
624
624
625 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
625 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
626
626
627 ; see sqlalchemy docs for other advanced settings
627 ; see sqlalchemy docs for other advanced settings
628 ; print the sql statements to output
628 ; print the sql statements to output
629 sqlalchemy.db1.echo = false
629 sqlalchemy.db1.echo = false
630
630
631 ; recycle the connections after this amount of seconds
631 ; recycle the connections after this amount of seconds
632 sqlalchemy.db1.pool_recycle = 3600
632 sqlalchemy.db1.pool_recycle = 3600
633
633
634 ; the number of connections to keep open inside the connection pool.
634 ; the number of connections to keep open inside the connection pool.
635 ; 0 indicates no limit
635 ; 0 indicates no limit
636 ; the general calculus with gevent is:
636 ; the general calculus with gevent is:
637 ; if your system allows 500 concurrent greenlets (max_connections) that all do database access,
637 ; if your system allows 500 concurrent greenlets (max_connections) that all do database access,
638 ; then increase pool size + max overflow so that they add up to 500.
638 ; then increase pool size + max overflow so that they add up to 500.
639 #sqlalchemy.db1.pool_size = 5
639 #sqlalchemy.db1.pool_size = 5
640
640
641 ; The number of connections to allow in connection pool "overflow", that is
641 ; The number of connections to allow in connection pool "overflow", that is
642 ; connections that can be opened above and beyond the pool_size setting,
642 ; connections that can be opened above and beyond the pool_size setting,
643 ; which defaults to five.
643 ; which defaults to five.
644 #sqlalchemy.db1.max_overflow = 10
644 #sqlalchemy.db1.max_overflow = 10
645
645
646 ; Connection check ping, used to detect broken database connections
646 ; Connection check ping, used to detect broken database connections
647 ; could be enabled to better handle cases if MySQL has gone away errors
647 ; could be enabled to better handle cases if MySQL has gone away errors
648 #sqlalchemy.db1.ping_connection = true
648 #sqlalchemy.db1.ping_connection = true
649
649
650 ; ##########
650 ; ##########
651 ; VCS CONFIG
651 ; VCS CONFIG
652 ; ##########
652 ; ##########
653 vcs.server.enable = true
653 vcs.server.enable = true
654 vcs.server = vcsserver:10010
654 vcs.server = vcsserver:10010
655
655
656 ; Web server connectivity protocol, responsible for web based VCS operations
656 ; Web server connectivity protocol, responsible for web based VCS operations
657 ; Available protocols are:
657 ; Available protocols are:
658 ; `http` - use http-rpc backend (default)
658 ; `http` - use http-rpc backend (default)
659 vcs.server.protocol = http
659 vcs.server.protocol = http
660
660
661 ; Push/Pull operations protocol, available options are:
661 ; Push/Pull operations protocol, available options are:
662 ; `http` - use http-rpc backend (default)
662 ; `http` - use http-rpc backend (default)
663 vcs.scm_app_implementation = http
663 vcs.scm_app_implementation = http
664
664
665 ; Push/Pull operations hooks protocol, available options are:
665 ; Push/Pull operations hooks protocol, available options are:
666 ; `http` - use http-rpc backend (default)
666 ; `http` - use http-rpc backend (default)
667 ; `celery` - use celery based hooks
667 ; `celery` - use celery based hooks
668 #DEPRECATED:vcs.hooks.protocol = http
668 #DEPRECATED:vcs.hooks.protocol = http
669 vcs.hooks.protocol.v2 = celery
669 vcs.hooks.protocol.v2 = celery
670
670
671 ; Host on which this instance is listening for hooks. vcsserver will call this host to pull/push hooks so it should be
671 ; Host on which this instance is listening for hooks. vcsserver will call this host to pull/push hooks so it should be
672 ; accessible via network.
672 ; accessible via network.
673 ; Use vcs.hooks.host = "*" to bind to current hostname (for Docker)
673 ; Use vcs.hooks.host = "*" to bind to current hostname (for Docker)
674 vcs.hooks.host = *
674 vcs.hooks.host = *
675
675
676 ; Start VCSServer with this instance as a subprocess, useful for development
676 ; Start VCSServer with this instance as a subprocess, useful for development
677 vcs.start_server = false
677 vcs.start_server = false
678
678
679 ; List of enabled VCS backends, available options are:
679 ; List of enabled VCS backends, available options are:
680 ; `hg` - mercurial
680 ; `hg` - mercurial
681 ; `git` - git
681 ; `git` - git
682 ; `svn` - subversion
682 ; `svn` - subversion
683 vcs.backends = hg, git, svn
683 vcs.backends = hg, git, svn
684
684
685 ; Wait this number of seconds before killing connection to the vcsserver
685 ; Wait this number of seconds before killing connection to the vcsserver
686 vcs.connection_timeout = 3600
686 vcs.connection_timeout = 3600
687
687
688 ; Cache flag to cache vcsserver remote calls locally
688 ; Cache flag to cache vcsserver remote calls locally
689 ; It uses cache_region `cache_repo`
689 ; It uses cache_region `cache_repo`
690 vcs.methods.cache = true
690 vcs.methods.cache = true
691
691
692 ; Filesystem location where Git lfs objects should be stored
693 vcs.git.lfs.storage_location = /var/opt/rhodecode_repo_store/.cache/git_lfs_store
694
695 ; Filesystem location where Mercurial largefile objects should be stored
696 vcs.hg.largefiles.storage_location = /var/opt/rhodecode_repo_store/.cache/hg_largefiles_store
697
692 ; ####################################################
698 ; ####################################################
693 ; Subversion proxy support (mod_dav_svn)
699 ; Subversion proxy support (mod_dav_svn)
694 ; Maps RhodeCode repo groups into SVN paths for Apache
700 ; Maps RhodeCode repo groups into SVN paths for Apache
695 ; ####################################################
701 ; ####################################################
696
702
697 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
703 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
698 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
704 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
699 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
705 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
700 #vcs.svn.compatible_version = 1.8
706 #vcs.svn.compatible_version = 1.8
701
707
702 ; Redis connection settings for svn integrations logic
708 ; Redis connection settings for svn integrations logic
703 ; This connection string needs to be the same on ce and vcsserver
709 ; This connection string needs to be the same on ce and vcsserver
704 vcs.svn.redis_conn = redis://redis:6379/0
710 vcs.svn.redis_conn = redis://redis:6379/0
705
711
706 ; Enable SVN proxy of requests over HTTP
712 ; Enable SVN proxy of requests over HTTP
707 vcs.svn.proxy.enabled = true
713 vcs.svn.proxy.enabled = true
708
714
709 ; host to connect to running SVN subsystem
715 ; host to connect to running SVN subsystem
710 vcs.svn.proxy.host = http://svn:8090
716 vcs.svn.proxy.host = http://svn:8090
711
717
712 ; Enable or disable the config file generation.
718 ; Enable or disable the config file generation.
713 svn.proxy.generate_config = true
719 svn.proxy.generate_config = true
714
720
715 ; Generate config file with `SVNListParentPath` set to `On`.
721 ; Generate config file with `SVNListParentPath` set to `On`.
716 svn.proxy.list_parent_path = true
722 svn.proxy.list_parent_path = true
717
723
718 ; Set location and file name of generated config file.
724 ; Set location and file name of generated config file.
719 svn.proxy.config_file_path = /etc/rhodecode/conf/svn/mod_dav_svn.conf
725 svn.proxy.config_file_path = /etc/rhodecode/conf/svn/mod_dav_svn.conf
720
726
721 ; alternative mod_dav config template. This needs to be a valid mako template
727 ; alternative mod_dav config template. This needs to be a valid mako template
722 ; Example template can be found in the source code:
728 ; Example template can be found in the source code:
723 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
729 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
724 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
730 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
725
731
726 ; Used as a prefix to the `Location` block in the generated config file.
732 ; Used as a prefix to the `Location` block in the generated config file.
727 ; In most cases it should be set to `/`.
733 ; In most cases it should be set to `/`.
728 svn.proxy.location_root = /
734 svn.proxy.location_root = /
729
735
730 ; Command to reload the mod dav svn configuration on change.
736 ; Command to reload the mod dav svn configuration on change.
731 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
737 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
732 ; Make sure user who runs RhodeCode process is allowed to reload Apache
738 ; Make sure user who runs RhodeCode process is allowed to reload Apache
733 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
739 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
734
740
735 ; If the timeout expires before the reload command finishes, the command will
741 ; If the timeout expires before the reload command finishes, the command will
736 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
742 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
737 #svn.proxy.reload_timeout = 10
743 #svn.proxy.reload_timeout = 10
738
744
739 ; ####################
745 ; ####################
740 ; SSH Support Settings
746 ; SSH Support Settings
741 ; ####################
747 ; ####################
742
748
743 ; Defines if a custom authorized_keys file should be created and written on
749 ; Defines if a custom authorized_keys file should be created and written on
744 ; any change user ssh keys. Setting this to false also disables possibility
750 ; any change user ssh keys. Setting this to false also disables possibility
745 ; of adding SSH keys by users from web interface. Super admins can still
751 ; of adding SSH keys by users from web interface. Super admins can still
746 ; manage SSH Keys.
752 ; manage SSH Keys.
747 ssh.generate_authorized_keyfile = true
753 ssh.generate_authorized_keyfile = true
748
754
749 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
755 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
750 # ssh.authorized_keys_ssh_opts =
756 # ssh.authorized_keys_ssh_opts =
751
757
752 ; Path to the authorized_keys file where the generate entries are placed.
758 ; Path to the authorized_keys file where the generate entries are placed.
753 ; It is possible to have multiple key files specified in `sshd_config` e.g.
759 ; It is possible to have multiple key files specified in `sshd_config` e.g.
754 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
760 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
755 ssh.authorized_keys_file_path = /etc/rhodecode/conf/ssh/authorized_keys_rhodecode
761 ssh.authorized_keys_file_path = /etc/rhodecode/conf/ssh/authorized_keys_rhodecode
756
762
757 ; Command to execute the SSH wrapper. The binary is available in the
763 ; Command to execute the SSH wrapper. The binary is available in the
758 ; RhodeCode installation directory.
764 ; RhodeCode installation directory.
759 ; legacy: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
765 ; legacy: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
760 ; new rewrite: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
766 ; new rewrite: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
761 #DEPRECATED: ssh.wrapper_cmd = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
767 #DEPRECATED: ssh.wrapper_cmd = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
762 ssh.wrapper_cmd.v2 = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
768 ssh.wrapper_cmd.v2 = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
763
769
764 ; Allow shell when executing the ssh-wrapper command
770 ; Allow shell when executing the ssh-wrapper command
765 ssh.wrapper_cmd_allow_shell = false
771 ssh.wrapper_cmd_allow_shell = false
766
772
767 ; Enables logging, and detailed output send back to the client during SSH
773 ; Enables logging, and detailed output send back to the client during SSH
768 ; operations. Useful for debugging, shouldn't be used in production.
774 ; operations. Useful for debugging, shouldn't be used in production.
769 ssh.enable_debug_logging = true
775 ssh.enable_debug_logging = true
770
776
771 ; Paths to binary executable, by default they are the names, but we can
777 ; Paths to binary executable, by default they are the names, but we can
772 ; override them if we want to use a custom one
778 ; override them if we want to use a custom one
773 ssh.executable.hg = /usr/local/bin/rhodecode_bin/vcs_bin/hg
779 ssh.executable.hg = /usr/local/bin/rhodecode_bin/vcs_bin/hg
774 ssh.executable.git = /usr/local/bin/rhodecode_bin/vcs_bin/git
780 ssh.executable.git = /usr/local/bin/rhodecode_bin/vcs_bin/git
775 ssh.executable.svn = /usr/local/bin/rhodecode_bin/vcs_bin/svnserve
781 ssh.executable.svn = /usr/local/bin/rhodecode_bin/vcs_bin/svnserve
776
782
777 ; Enables SSH key generator web interface. Disabling this still allows users
783 ; Enables SSH key generator web interface. Disabling this still allows users
778 ; to add their own keys.
784 ; to add their own keys.
779 ssh.enable_ui_key_generator = true
785 ssh.enable_ui_key_generator = true
780
786
781 ; Statsd client config, this is used to send metrics to statsd
787 ; Statsd client config, this is used to send metrics to statsd
782 ; We recommend setting statsd_exported and scrape them using Prometheus
788 ; We recommend setting statsd_exported and scrape them using Prometheus
783 #statsd.enabled = false
789 #statsd.enabled = false
784 #statsd.statsd_host = 0.0.0.0
790 #statsd.statsd_host = 0.0.0.0
785 #statsd.statsd_port = 8125
791 #statsd.statsd_port = 8125
786 #statsd.statsd_prefix =
792 #statsd.statsd_prefix =
787 #statsd.statsd_ipv6 = false
793 #statsd.statsd_ipv6 = false
788
794
789 ; configure logging automatically at server startup set to false
795 ; configure logging automatically at server startup set to false
790 ; to use the below custom logging config.
796 ; to use the below custom logging config.
791 ; RC_LOGGING_FORMATTER
797 ; RC_LOGGING_FORMATTER
792 ; RC_LOGGING_LEVEL
798 ; RC_LOGGING_LEVEL
793 ; env variables can control the settings for logging in case of autoconfigure
799 ; env variables can control the settings for logging in case of autoconfigure
794
800
795 #logging.autoconfigure = true
801 #logging.autoconfigure = true
796
802
797 ; specify your own custom logging config file to configure logging
803 ; specify your own custom logging config file to configure logging
798 #logging.logging_conf_file = /path/to/custom_logging.ini
804 #logging.logging_conf_file = /path/to/custom_logging.ini
799
805
800 ; Dummy marker to add new entries after.
806 ; Dummy marker to add new entries after.
801 ; Add any custom entries below. Please don't remove this marker.
807 ; Add any custom entries below. Please don't remove this marker.
802 custom.conf = 1
808 custom.conf = 1
803
809
804
810
805 ; #####################
811 ; #####################
806 ; LOGGING CONFIGURATION
812 ; LOGGING CONFIGURATION
807 ; #####################
813 ; #####################
808
814
809 [loggers]
815 [loggers]
810 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
816 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
811
817
812 [handlers]
818 [handlers]
813 keys = console, console_sql
819 keys = console, console_sql
814
820
815 [formatters]
821 [formatters]
816 keys = generic, json, color_formatter, color_formatter_sql
822 keys = generic, json, color_formatter, color_formatter_sql
817
823
818 ; #######
824 ; #######
819 ; LOGGERS
825 ; LOGGERS
820 ; #######
826 ; #######
821 [logger_root]
827 [logger_root]
822 level = NOTSET
828 level = NOTSET
823 handlers = console
829 handlers = console
824
830
825 [logger_sqlalchemy]
831 [logger_sqlalchemy]
826 level = INFO
832 level = INFO
827 handlers = console_sql
833 handlers = console_sql
828 qualname = sqlalchemy.engine
834 qualname = sqlalchemy.engine
829 propagate = 0
835 propagate = 0
830
836
831 [logger_beaker]
837 [logger_beaker]
832 level = DEBUG
838 level = DEBUG
833 handlers =
839 handlers =
834 qualname = beaker.container
840 qualname = beaker.container
835 propagate = 1
841 propagate = 1
836
842
837 [logger_rhodecode]
843 [logger_rhodecode]
838 level = DEBUG
844 level = DEBUG
839 handlers =
845 handlers =
840 qualname = rhodecode
846 qualname = rhodecode
841 propagate = 1
847 propagate = 1
842
848
843 [logger_ssh_wrapper]
849 [logger_ssh_wrapper]
844 level = DEBUG
850 level = DEBUG
845 handlers =
851 handlers =
846 qualname = ssh_wrapper
852 qualname = ssh_wrapper
847 propagate = 1
853 propagate = 1
848
854
849 [logger_celery]
855 [logger_celery]
850 level = DEBUG
856 level = DEBUG
851 handlers =
857 handlers =
852 qualname = celery
858 qualname = celery
853
859
854
860
855 ; ########
861 ; ########
856 ; HANDLERS
862 ; HANDLERS
857 ; ########
863 ; ########
858
864
859 [handler_console]
865 [handler_console]
860 class = StreamHandler
866 class = StreamHandler
861 args = (sys.stderr, )
867 args = (sys.stderr, )
862 level = DEBUG
868 level = DEBUG
863 ; To enable JSON formatted logs replace 'generic/color_formatter' with 'json'
869 ; To enable JSON formatted logs replace 'generic/color_formatter' with 'json'
864 ; This allows sending properly formatted logs to grafana loki or elasticsearch
870 ; This allows sending properly formatted logs to grafana loki or elasticsearch
865 formatter = color_formatter
871 formatter = color_formatter
866
872
867 [handler_console_sql]
873 [handler_console_sql]
868 ; "level = DEBUG" logs SQL queries and results.
874 ; "level = DEBUG" logs SQL queries and results.
869 ; "level = INFO" logs SQL queries.
875 ; "level = INFO" logs SQL queries.
870 ; "level = WARN" logs neither. (Recommended for production systems.)
876 ; "level = WARN" logs neither. (Recommended for production systems.)
871 class = StreamHandler
877 class = StreamHandler
872 args = (sys.stderr, )
878 args = (sys.stderr, )
873 level = WARN
879 level = WARN
874 ; To enable JSON formatted logs replace 'generic/color_formatter_sql' with 'json'
880 ; To enable JSON formatted logs replace 'generic/color_formatter_sql' with 'json'
875 ; This allows sending properly formatted logs to grafana loki or elasticsearch
881 ; This allows sending properly formatted logs to grafana loki or elasticsearch
876 formatter = color_formatter_sql
882 formatter = color_formatter_sql
877
883
878 ; ##########
884 ; ##########
879 ; FORMATTERS
885 ; FORMATTERS
880 ; ##########
886 ; ##########
881
887
882 [formatter_generic]
888 [formatter_generic]
883 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
889 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
884 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
890 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
885 datefmt = %Y-%m-%d %H:%M:%S
891 datefmt = %Y-%m-%d %H:%M:%S
886
892
887 [formatter_color_formatter]
893 [formatter_color_formatter]
888 class = rhodecode.lib.logging_formatter.ColorFormatter
894 class = rhodecode.lib.logging_formatter.ColorFormatter
889 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
895 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
890 datefmt = %Y-%m-%d %H:%M:%S
896 datefmt = %Y-%m-%d %H:%M:%S
891
897
892 [formatter_color_formatter_sql]
898 [formatter_color_formatter_sql]
893 class = rhodecode.lib.logging_formatter.ColorFormatterSql
899 class = rhodecode.lib.logging_formatter.ColorFormatterSql
894 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
900 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
895 datefmt = %Y-%m-%d %H:%M:%S
901 datefmt = %Y-%m-%d %H:%M:%S
896
902
897 [formatter_json]
903 [formatter_json]
898 format = %(timestamp)s %(levelname)s %(name)s %(message)s %(req_id)s
904 format = %(timestamp)s %(levelname)s %(name)s %(message)s %(req_id)s
899 class = rhodecode.lib._vendor.jsonlogger.JsonFormatter
905 class = rhodecode.lib._vendor.jsonlogger.JsonFormatter
@@ -1,867 +1,873 b''
1
1
2 ; #########################################
2 ; #########################################
3 ; RHODECODE COMMUNITY EDITION CONFIGURATION
3 ; RHODECODE COMMUNITY EDITION CONFIGURATION
4 ; #########################################
4 ; #########################################
5
5
6 [DEFAULT]
6 [DEFAULT]
7 ; Debug flag sets all loggers to debug, and enables request tracking
7 ; Debug flag sets all loggers to debug, and enables request tracking
8 debug = false
8 debug = false
9
9
10 ; ########################################################################
10 ; ########################################################################
11 ; EMAIL CONFIGURATION
11 ; EMAIL CONFIGURATION
12 ; These settings will be used by the RhodeCode mailing system
12 ; These settings will be used by the RhodeCode mailing system
13 ; ########################################################################
13 ; ########################################################################
14
14
15 ; prefix all emails subjects with given prefix, helps filtering out emails
15 ; prefix all emails subjects with given prefix, helps filtering out emails
16 #email_prefix = [RhodeCode]
16 #email_prefix = [RhodeCode]
17
17
18 ; email FROM address all mails will be sent
18 ; email FROM address all mails will be sent
19 #app_email_from = rhodecode-noreply@localhost
19 #app_email_from = rhodecode-noreply@localhost
20
20
21 #smtp_server = mail.server.com
21 #smtp_server = mail.server.com
22 #smtp_username =
22 #smtp_username =
23 #smtp_password =
23 #smtp_password =
24 #smtp_port =
24 #smtp_port =
25 #smtp_use_tls = false
25 #smtp_use_tls = false
26 #smtp_use_ssl = true
26 #smtp_use_ssl = true
27
27
28 [server:main]
28 [server:main]
29 ; COMMON HOST/IP CONFIG, This applies mostly to develop setup,
29 ; COMMON HOST/IP CONFIG, This applies mostly to develop setup,
30 ; Host port for gunicorn are controlled by gunicorn_conf.py
30 ; Host port for gunicorn are controlled by gunicorn_conf.py
31 host = 127.0.0.1
31 host = 127.0.0.1
32 port = 10020
32 port = 10020
33
33
34
34
35 ; ###########################
35 ; ###########################
36 ; GUNICORN APPLICATION SERVER
36 ; GUNICORN APPLICATION SERVER
37 ; ###########################
37 ; ###########################
38
38
39 ; run with gunicorn --config gunicorn_conf.py --paste rhodecode.ini
39 ; run with gunicorn --config gunicorn_conf.py --paste rhodecode.ini
40
40
41 ; Module to use, this setting shouldn't be changed
41 ; Module to use, this setting shouldn't be changed
42 use = egg:gunicorn#main
42 use = egg:gunicorn#main
43
43
44 ; Prefix middleware for RhodeCode.
44 ; Prefix middleware for RhodeCode.
45 ; recommended when using proxy setup.
45 ; recommended when using proxy setup.
46 ; allows to set RhodeCode under a prefix in server.
46 ; allows to set RhodeCode under a prefix in server.
47 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
47 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
48 ; And set your prefix like: `prefix = /custom_prefix`
48 ; And set your prefix like: `prefix = /custom_prefix`
49 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
49 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
50 ; to make your cookies only work on prefix url
50 ; to make your cookies only work on prefix url
51 [filter:proxy-prefix]
51 [filter:proxy-prefix]
52 use = egg:PasteDeploy#prefix
52 use = egg:PasteDeploy#prefix
53 prefix = /
53 prefix = /
54
54
55 [app:main]
55 [app:main]
56 ; The %(here)s variable will be replaced with the absolute path of parent directory
56 ; The %(here)s variable will be replaced with the absolute path of parent directory
57 ; of this file
57 ; of this file
58 ; Each option in the app:main can be override by an environmental variable
58 ; Each option in the app:main can be override by an environmental variable
59 ;
59 ;
60 ;To override an option:
60 ;To override an option:
61 ;
61 ;
62 ;RC_<KeyName>
62 ;RC_<KeyName>
63 ;Everything should be uppercase, . and - should be replaced by _.
63 ;Everything should be uppercase, . and - should be replaced by _.
64 ;For example, if you have these configuration settings:
64 ;For example, if you have these configuration settings:
65 ;rc_cache.repo_object.backend = foo
65 ;rc_cache.repo_object.backend = foo
66 ;can be overridden by
66 ;can be overridden by
67 ;export RC_CACHE_REPO_OBJECT_BACKEND=foo
67 ;export RC_CACHE_REPO_OBJECT_BACKEND=foo
68
68
69 use = egg:rhodecode-enterprise-ce
69 use = egg:rhodecode-enterprise-ce
70
70
71 ; enable proxy prefix middleware, defined above
71 ; enable proxy prefix middleware, defined above
72 #filter-with = proxy-prefix
72 #filter-with = proxy-prefix
73
73
74 ; encryption key used to encrypt social plugin tokens,
74 ; encryption key used to encrypt social plugin tokens,
75 ; remote_urls with credentials etc, if not set it defaults to
75 ; remote_urls with credentials etc, if not set it defaults to
76 ; `beaker.session.secret`
76 ; `beaker.session.secret`
77 #rhodecode.encrypted_values.secret =
77 #rhodecode.encrypted_values.secret =
78
78
79 ; decryption strict mode (enabled by default). It controls if decryption raises
79 ; decryption strict mode (enabled by default). It controls if decryption raises
80 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
80 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
81 #rhodecode.encrypted_values.strict = false
81 #rhodecode.encrypted_values.strict = false
82
82
83 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
83 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
84 ; fernet is safer, and we strongly recommend switching to it.
84 ; fernet is safer, and we strongly recommend switching to it.
85 ; Due to backward compatibility aes is used as default.
85 ; Due to backward compatibility aes is used as default.
86 #rhodecode.encrypted_values.algorithm = fernet
86 #rhodecode.encrypted_values.algorithm = fernet
87
87
88 ; Return gzipped responses from RhodeCode (static files/application)
88 ; Return gzipped responses from RhodeCode (static files/application)
89 gzip_responses = false
89 gzip_responses = false
90
90
91 ; Auto-generate javascript routes file on startup
91 ; Auto-generate javascript routes file on startup
92 generate_js_files = false
92 generate_js_files = false
93
93
94 ; System global default language.
94 ; System global default language.
95 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
95 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
96 lang = en
96 lang = en
97
97
98 ; Perform a full repository scan and import on each server start.
98 ; Perform a full repository scan and import on each server start.
99 ; Settings this to true could lead to very long startup time.
99 ; Settings this to true could lead to very long startup time.
100 startup.import_repos = false
100 startup.import_repos = false
101
101
102 ; URL at which the application is running. This is used for Bootstrapping
102 ; URL at which the application is running. This is used for Bootstrapping
103 ; requests in context when no web request is available. Used in ishell, or
103 ; requests in context when no web request is available. Used in ishell, or
104 ; SSH calls. Set this for events to receive proper url for SSH calls.
104 ; SSH calls. Set this for events to receive proper url for SSH calls.
105 app.base_url = http://rhodecode.local
105 app.base_url = http://rhodecode.local
106
106
107 ; Host at which the Service API is running.
107 ; Host at which the Service API is running.
108 app.service_api.host = http://rhodecode.local:10020
108 app.service_api.host = http://rhodecode.local:10020
109
109
110 ; Secret for Service API authentication.
110 ; Secret for Service API authentication.
111 app.service_api.token =
111 app.service_api.token =
112
112
113 ; Unique application ID. Should be a random unique string for security.
113 ; Unique application ID. Should be a random unique string for security.
114 app_instance_uuid = rc-production
114 app_instance_uuid = rc-production
115
115
116 ; Cut off limit for large diffs (size in bytes). If overall diff size on
116 ; Cut off limit for large diffs (size in bytes). If overall diff size on
117 ; commit, or pull request exceeds this limit this diff will be displayed
117 ; commit, or pull request exceeds this limit this diff will be displayed
118 ; partially. E.g 512000 == 512Kb
118 ; partially. E.g 512000 == 512Kb
119 cut_off_limit_diff = 512000
119 cut_off_limit_diff = 512000
120
120
121 ; Cut off limit for large files inside diffs (size in bytes). Each individual
121 ; Cut off limit for large files inside diffs (size in bytes). Each individual
122 ; file inside diff which exceeds this limit will be displayed partially.
122 ; file inside diff which exceeds this limit will be displayed partially.
123 ; E.g 128000 == 128Kb
123 ; E.g 128000 == 128Kb
124 cut_off_limit_file = 128000
124 cut_off_limit_file = 128000
125
125
126 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
126 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
127 vcs_full_cache = true
127 vcs_full_cache = true
128
128
129 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
129 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
130 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
130 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
131 force_https = false
131 force_https = false
132
132
133 ; use Strict-Transport-Security headers
133 ; use Strict-Transport-Security headers
134 use_htsts = false
134 use_htsts = false
135
135
136 ; Set to true if your repos are exposed using the dumb protocol
136 ; Set to true if your repos are exposed using the dumb protocol
137 git_update_server_info = false
137 git_update_server_info = false
138
138
139 ; RSS/ATOM feed options
139 ; RSS/ATOM feed options
140 rss_cut_off_limit = 256000
140 rss_cut_off_limit = 256000
141 rss_items_per_page = 10
141 rss_items_per_page = 10
142 rss_include_diff = false
142 rss_include_diff = false
143
143
144 ; gist URL alias, used to create nicer urls for gist. This should be an
144 ; gist URL alias, used to create nicer urls for gist. This should be an
145 ; url that does rewrites to _admin/gists/{gistid}.
145 ; url that does rewrites to _admin/gists/{gistid}.
146 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
146 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
147 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
147 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
148 gist_alias_url =
148 gist_alias_url =
149
149
150 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
150 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
151 ; used for access.
151 ; used for access.
152 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
152 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
153 ; came from the the logged in user who own this authentication token.
153 ; came from the the logged in user who own this authentication token.
154 ; Additionally @TOKEN syntax can be used to bound the view to specific
154 ; Additionally @TOKEN syntax can be used to bound the view to specific
155 ; authentication token. Such view would be only accessible when used together
155 ; authentication token. Such view would be only accessible when used together
156 ; with this authentication token
156 ; with this authentication token
157 ; list of all views can be found under `/_admin/permissions/auth_token_access`
157 ; list of all views can be found under `/_admin/permissions/auth_token_access`
158 ; The list should be "," separated and on a single line.
158 ; The list should be "," separated and on a single line.
159 ; Most common views to enable:
159 ; Most common views to enable:
160
160
161 # RepoCommitsView:repo_commit_download
161 # RepoCommitsView:repo_commit_download
162 # RepoCommitsView:repo_commit_patch
162 # RepoCommitsView:repo_commit_patch
163 # RepoCommitsView:repo_commit_raw
163 # RepoCommitsView:repo_commit_raw
164 # RepoCommitsView:repo_commit_raw@TOKEN
164 # RepoCommitsView:repo_commit_raw@TOKEN
165 # RepoFilesView:repo_files_diff
165 # RepoFilesView:repo_files_diff
166 # RepoFilesView:repo_archivefile
166 # RepoFilesView:repo_archivefile
167 # RepoFilesView:repo_file_raw
167 # RepoFilesView:repo_file_raw
168 # GistView:*
168 # GistView:*
169 api_access_controllers_whitelist =
169 api_access_controllers_whitelist =
170
170
171 ; Default encoding used to convert from and to unicode
171 ; Default encoding used to convert from and to unicode
172 ; can be also a comma separated list of encoding in case of mixed encodings
172 ; can be also a comma separated list of encoding in case of mixed encodings
173 default_encoding = UTF-8
173 default_encoding = UTF-8
174
174
175 ; instance-id prefix
175 ; instance-id prefix
176 ; a prefix key for this instance used for cache invalidation when running
176 ; a prefix key for this instance used for cache invalidation when running
177 ; multiple instances of RhodeCode, make sure it's globally unique for
177 ; multiple instances of RhodeCode, make sure it's globally unique for
178 ; all running RhodeCode instances. Leave empty if you don't use it
178 ; all running RhodeCode instances. Leave empty if you don't use it
179 instance_id =
179 instance_id =
180
180
181 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
181 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
182 ; of an authentication plugin also if it is disabled by it's settings.
182 ; of an authentication plugin also if it is disabled by it's settings.
183 ; This could be useful if you are unable to log in to the system due to broken
183 ; This could be useful if you are unable to log in to the system due to broken
184 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
184 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
185 ; module to log in again and fix the settings.
185 ; module to log in again and fix the settings.
186 ; Available builtin plugin IDs (hash is part of the ID):
186 ; Available builtin plugin IDs (hash is part of the ID):
187 ; egg:rhodecode-enterprise-ce#rhodecode
187 ; egg:rhodecode-enterprise-ce#rhodecode
188 ; egg:rhodecode-enterprise-ce#pam
188 ; egg:rhodecode-enterprise-ce#pam
189 ; egg:rhodecode-enterprise-ce#ldap
189 ; egg:rhodecode-enterprise-ce#ldap
190 ; egg:rhodecode-enterprise-ce#jasig_cas
190 ; egg:rhodecode-enterprise-ce#jasig_cas
191 ; egg:rhodecode-enterprise-ce#headers
191 ; egg:rhodecode-enterprise-ce#headers
192 ; egg:rhodecode-enterprise-ce#crowd
192 ; egg:rhodecode-enterprise-ce#crowd
193
193
194 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
194 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
195
195
196 ; Flag to control loading of legacy plugins in py:/path format
196 ; Flag to control loading of legacy plugins in py:/path format
197 auth_plugin.import_legacy_plugins = true
197 auth_plugin.import_legacy_plugins = true
198
198
199 ; alternative return HTTP header for failed authentication. Default HTTP
199 ; alternative return HTTP header for failed authentication. Default HTTP
200 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
200 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
201 ; handling that causing a series of failed authentication calls.
201 ; handling that causing a series of failed authentication calls.
202 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
202 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
203 ; This will be served instead of default 401 on bad authentication
203 ; This will be served instead of default 401 on bad authentication
204 auth_ret_code =
204 auth_ret_code =
205
205
206 ; use special detection method when serving auth_ret_code, instead of serving
206 ; use special detection method when serving auth_ret_code, instead of serving
207 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
207 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
208 ; and then serve auth_ret_code to clients
208 ; and then serve auth_ret_code to clients
209 auth_ret_code_detection = false
209 auth_ret_code_detection = false
210
210
211 ; locking return code. When repository is locked return this HTTP code. 2XX
211 ; locking return code. When repository is locked return this HTTP code. 2XX
212 ; codes don't break the transactions while 4XX codes do
212 ; codes don't break the transactions while 4XX codes do
213 lock_ret_code = 423
213 lock_ret_code = 423
214
214
215 ; Filesystem location were repositories should be stored
215 ; Filesystem location were repositories should be stored
216 repo_store.path = /var/opt/rhodecode_repo_store
216 repo_store.path = /var/opt/rhodecode_repo_store
217
217
218 ; allows to setup custom hooks in settings page
218 ; allows to setup custom hooks in settings page
219 allow_custom_hooks_settings = true
219 allow_custom_hooks_settings = true
220
220
221 ; Generated license token required for EE edition license.
221 ; Generated license token required for EE edition license.
222 ; New generated token value can be found in Admin > settings > license page.
222 ; New generated token value can be found in Admin > settings > license page.
223 license_token =
223 license_token =
224
224
225 ; This flag hides sensitive information on the license page such as token, and license data
225 ; This flag hides sensitive information on the license page such as token, and license data
226 license.hide_license_info = false
226 license.hide_license_info = false
227
227
228 ; supervisor connection uri, for managing supervisor and logs.
228 ; supervisor connection uri, for managing supervisor and logs.
229 supervisor.uri =
229 supervisor.uri =
230
230
231 ; supervisord group name/id we only want this RC instance to handle
231 ; supervisord group name/id we only want this RC instance to handle
232 supervisor.group_id = prod
232 supervisor.group_id = prod
233
233
234 ; Display extended labs settings
234 ; Display extended labs settings
235 labs_settings_active = true
235 labs_settings_active = true
236
236
237 ; Custom exception store path, defaults to TMPDIR
237 ; Custom exception store path, defaults to TMPDIR
238 ; This is used to store exception from RhodeCode in shared directory
238 ; This is used to store exception from RhodeCode in shared directory
239 #exception_tracker.store_path =
239 #exception_tracker.store_path =
240
240
241 ; Send email with exception details when it happens
241 ; Send email with exception details when it happens
242 #exception_tracker.send_email = false
242 #exception_tracker.send_email = false
243
243
244 ; Comma separated list of recipients for exception emails,
244 ; Comma separated list of recipients for exception emails,
245 ; e.g admin@rhodecode.com,devops@rhodecode.com
245 ; e.g admin@rhodecode.com,devops@rhodecode.com
246 ; Can be left empty, then emails will be sent to ALL super-admins
246 ; Can be left empty, then emails will be sent to ALL super-admins
247 #exception_tracker.send_email_recipients =
247 #exception_tracker.send_email_recipients =
248
248
249 ; optional prefix to Add to email Subject
249 ; optional prefix to Add to email Subject
250 #exception_tracker.email_prefix = [RHODECODE ERROR]
250 #exception_tracker.email_prefix = [RHODECODE ERROR]
251
251
252 ; NOTE: this setting IS DEPRECATED:
252 ; NOTE: this setting IS DEPRECATED:
253 ; file_store backend is always enabled
253 ; file_store backend is always enabled
254 #file_store.enabled = true
254 #file_store.enabled = true
255
255
256 ; NOTE: this setting IS DEPRECATED:
256 ; NOTE: this setting IS DEPRECATED:
257 ; file_store.backend = X -> use `file_store.backend.type = filesystem_v2` instead
257 ; file_store.backend = X -> use `file_store.backend.type = filesystem_v2` instead
258 ; Storage backend, available options are: local
258 ; Storage backend, available options are: local
259 #file_store.backend = local
259 #file_store.backend = local
260
260
261 ; NOTE: this setting IS DEPRECATED:
261 ; NOTE: this setting IS DEPRECATED:
262 ; file_store.storage_path = X -> use `file_store.filesystem_v2.storage_path = X` instead
262 ; file_store.storage_path = X -> use `file_store.filesystem_v2.storage_path = X` instead
263 ; path to store the uploaded binaries and artifacts
263 ; path to store the uploaded binaries and artifacts
264 #file_store.storage_path = /var/opt/rhodecode_data/file_store
264 #file_store.storage_path = /var/opt/rhodecode_data/file_store
265
265
266 ; Artifacts file-store, is used to store comment attachments and artifacts uploads.
266 ; Artifacts file-store, is used to store comment attachments and artifacts uploads.
267 ; file_store backend type: filesystem_v1, filesystem_v2 or objectstore (s3-based) are available as options
267 ; file_store backend type: filesystem_v1, filesystem_v2 or objectstore (s3-based) are available as options
268 ; filesystem_v1 is backwards compat with pre 5.1 storage changes
268 ; filesystem_v1 is backwards compat with pre 5.1 storage changes
269 ; new installations should choose filesystem_v2 or objectstore (s3-based), pick filesystem when migrating from
269 ; new installations should choose filesystem_v2 or objectstore (s3-based), pick filesystem when migrating from
270 ; previous installations to keep the artifacts without a need of migration
270 ; previous installations to keep the artifacts without a need of migration
271 #file_store.backend.type = filesystem_v2
271 #file_store.backend.type = filesystem_v2
272
272
273 ; filesystem options...
273 ; filesystem options...
274 #file_store.filesystem_v1.storage_path = /var/opt/rhodecode_data/artifacts_file_store
274 #file_store.filesystem_v1.storage_path = /var/opt/rhodecode_data/artifacts_file_store
275
275
276 ; filesystem_v2 options...
276 ; filesystem_v2 options...
277 #file_store.filesystem_v2.storage_path = /var/opt/rhodecode_data/artifacts_file_store
277 #file_store.filesystem_v2.storage_path = /var/opt/rhodecode_data/artifacts_file_store
278 #file_store.filesystem_v2.shards = 8
278 #file_store.filesystem_v2.shards = 8
279
279
280 ; objectstore options...
280 ; objectstore options...
281 ; url for s3 compatible storage that allows to upload artifacts
281 ; url for s3 compatible storage that allows to upload artifacts
282 ; e.g http://minio:9000
282 ; e.g http://minio:9000
283 #file_store.backend.type = objectstore
283 #file_store.backend.type = objectstore
284 #file_store.objectstore.url = http://s3-minio:9000
284 #file_store.objectstore.url = http://s3-minio:9000
285
285
286 ; a top-level bucket to put all other shards in
286 ; a top-level bucket to put all other shards in
287 ; objects will be stored in rhodecode-file-store/shard-N based on the bucket_shards number
287 ; objects will be stored in rhodecode-file-store/shard-N based on the bucket_shards number
288 #file_store.objectstore.bucket = rhodecode-file-store
288 #file_store.objectstore.bucket = rhodecode-file-store
289
289
290 ; number of sharded buckets to create to distribute archives across
290 ; number of sharded buckets to create to distribute archives across
291 ; default is 8 shards
291 ; default is 8 shards
292 #file_store.objectstore.bucket_shards = 8
292 #file_store.objectstore.bucket_shards = 8
293
293
294 ; key for s3 auth
294 ; key for s3 auth
295 #file_store.objectstore.key = s3admin
295 #file_store.objectstore.key = s3admin
296
296
297 ; secret for s3 auth
297 ; secret for s3 auth
298 #file_store.objectstore.secret = s3secret4
298 #file_store.objectstore.secret = s3secret4
299
299
300 ;region for s3 storage
300 ;region for s3 storage
301 #file_store.objectstore.region = eu-central-1
301 #file_store.objectstore.region = eu-central-1
302
302
303 ; Redis url to acquire/check generation of archives locks
303 ; Redis url to acquire/check generation of archives locks
304 archive_cache.locking.url = redis://redis:6379/1
304 archive_cache.locking.url = redis://redis:6379/1
305
305
306 ; Storage backend, only 'filesystem' and 'objectstore' are available now
306 ; Storage backend, only 'filesystem' and 'objectstore' are available now
307 archive_cache.backend.type = filesystem
307 archive_cache.backend.type = filesystem
308
308
309 ; url for s3 compatible storage that allows to upload artifacts
309 ; url for s3 compatible storage that allows to upload artifacts
310 ; e.g http://minio:9000
310 ; e.g http://minio:9000
311 archive_cache.objectstore.url = http://s3-minio:9000
311 archive_cache.objectstore.url = http://s3-minio:9000
312
312
313 ; key for s3 auth
313 ; key for s3 auth
314 archive_cache.objectstore.key = key
314 archive_cache.objectstore.key = key
315
315
316 ; secret for s3 auth
316 ; secret for s3 auth
317 archive_cache.objectstore.secret = secret
317 archive_cache.objectstore.secret = secret
318
318
319 ;region for s3 storage
319 ;region for s3 storage
320 archive_cache.objectstore.region = eu-central-1
320 archive_cache.objectstore.region = eu-central-1
321
321
322 ; number of sharded buckets to create to distribute archives across
322 ; number of sharded buckets to create to distribute archives across
323 ; default is 8 shards
323 ; default is 8 shards
324 archive_cache.objectstore.bucket_shards = 8
324 archive_cache.objectstore.bucket_shards = 8
325
325
326 ; a top-level bucket to put all other shards in
326 ; a top-level bucket to put all other shards in
327 ; objects will be stored in rhodecode-archive-cache/shard-N based on the bucket_shards number
327 ; objects will be stored in rhodecode-archive-cache/shard-N based on the bucket_shards number
328 archive_cache.objectstore.bucket = rhodecode-archive-cache
328 archive_cache.objectstore.bucket = rhodecode-archive-cache
329
329
330 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
330 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
331 archive_cache.objectstore.retry = false
331 archive_cache.objectstore.retry = false
332
332
333 ; number of seconds to wait for next try using retry
333 ; number of seconds to wait for next try using retry
334 archive_cache.objectstore.retry_backoff = 1
334 archive_cache.objectstore.retry_backoff = 1
335
335
336 ; how many tries do do a retry fetch from this backend
336 ; how many tries do do a retry fetch from this backend
337 archive_cache.objectstore.retry_attempts = 10
337 archive_cache.objectstore.retry_attempts = 10
338
338
339 ; Default is $cache_dir/archive_cache if not set
339 ; Default is $cache_dir/archive_cache if not set
340 ; Generated repo archives will be cached at this location
340 ; Generated repo archives will be cached at this location
341 ; and served from the cache during subsequent requests for the same archive of
341 ; and served from the cache during subsequent requests for the same archive of
342 ; the repository. This path is important to be shared across filesystems and with
342 ; the repository. This path is important to be shared across filesystems and with
343 ; RhodeCode and vcsserver
343 ; RhodeCode and vcsserver
344 archive_cache.filesystem.store_dir = /var/opt/rhodecode_data/archive_cache
344 archive_cache.filesystem.store_dir = /var/opt/rhodecode_data/archive_cache
345
345
346 ; The limit in GB sets how much data we cache before recycling last used, defaults to 10 gb
346 ; The limit in GB sets how much data we cache before recycling last used, defaults to 10 gb
347 archive_cache.filesystem.cache_size_gb = 40
347 archive_cache.filesystem.cache_size_gb = 40
348
348
349 ; Eviction policy used to clear out after cache_size_gb limit is reached
349 ; Eviction policy used to clear out after cache_size_gb limit is reached
350 archive_cache.filesystem.eviction_policy = least-recently-stored
350 archive_cache.filesystem.eviction_policy = least-recently-stored
351
351
352 ; By default cache uses sharding technique, this specifies how many shards are there
352 ; By default cache uses sharding technique, this specifies how many shards are there
353 ; default is 8 shards
353 ; default is 8 shards
354 archive_cache.filesystem.cache_shards = 8
354 archive_cache.filesystem.cache_shards = 8
355
355
356 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
356 ; if true, this cache will try to retry with retry_attempts=N times waiting retry_backoff time
357 archive_cache.filesystem.retry = false
357 archive_cache.filesystem.retry = false
358
358
359 ; number of seconds to wait for next try using retry
359 ; number of seconds to wait for next try using retry
360 archive_cache.filesystem.retry_backoff = 1
360 archive_cache.filesystem.retry_backoff = 1
361
361
362 ; how many tries do do a retry fetch from this backend
362 ; how many tries do do a retry fetch from this backend
363 archive_cache.filesystem.retry_attempts = 10
363 archive_cache.filesystem.retry_attempts = 10
364
364
365
365
366 ; #############
366 ; #############
367 ; CELERY CONFIG
367 ; CELERY CONFIG
368 ; #############
368 ; #############
369
369
370 ; manually run celery: /path/to/celery worker --task-events --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
370 ; manually run celery: /path/to/celery worker --task-events --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
371
371
372 use_celery = true
372 use_celery = true
373
373
374 ; path to store schedule database
374 ; path to store schedule database
375 #celerybeat-schedule.path =
375 #celerybeat-schedule.path =
376
376
377 ; connection url to the message broker (default redis)
377 ; connection url to the message broker (default redis)
378 celery.broker_url = redis://redis:6379/8
378 celery.broker_url = redis://redis:6379/8
379
379
380 ; results backend to get results for (default redis)
380 ; results backend to get results for (default redis)
381 celery.result_backend = redis://redis:6379/8
381 celery.result_backend = redis://redis:6379/8
382
382
383 ; rabbitmq example
383 ; rabbitmq example
384 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
384 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
385
385
386 ; maximum tasks to execute before worker restart
386 ; maximum tasks to execute before worker restart
387 celery.max_tasks_per_child = 20
387 celery.max_tasks_per_child = 20
388
388
389 ; tasks will never be sent to the queue, but executed locally instead.
389 ; tasks will never be sent to the queue, but executed locally instead.
390 celery.task_always_eager = false
390 celery.task_always_eager = false
391
391
392 ; #############
392 ; #############
393 ; DOGPILE CACHE
393 ; DOGPILE CACHE
394 ; #############
394 ; #############
395
395
396 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
396 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
397 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
397 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
398 cache_dir = /var/opt/rhodecode_data
398 cache_dir = /var/opt/rhodecode_data
399
399
400 ; *********************************************
400 ; *********************************************
401 ; `sql_cache_short` cache for heavy SQL queries
401 ; `sql_cache_short` cache for heavy SQL queries
402 ; Only supported backend is `memory_lru`
402 ; Only supported backend is `memory_lru`
403 ; *********************************************
403 ; *********************************************
404 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
404 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
405 rc_cache.sql_cache_short.expiration_time = 30
405 rc_cache.sql_cache_short.expiration_time = 30
406
406
407
407
408 ; *****************************************************
408 ; *****************************************************
409 ; `cache_repo_longterm` cache for repo object instances
409 ; `cache_repo_longterm` cache for repo object instances
410 ; Only supported backend is `memory_lru`
410 ; Only supported backend is `memory_lru`
411 ; *****************************************************
411 ; *****************************************************
412 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
412 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
413 ; by default we use 30 Days, cache is still invalidated on push
413 ; by default we use 30 Days, cache is still invalidated on push
414 rc_cache.cache_repo_longterm.expiration_time = 2592000
414 rc_cache.cache_repo_longterm.expiration_time = 2592000
415 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
415 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
416 rc_cache.cache_repo_longterm.max_size = 10000
416 rc_cache.cache_repo_longterm.max_size = 10000
417
417
418
418
419 ; *********************************************
419 ; *********************************************
420 ; `cache_general` cache for general purpose use
420 ; `cache_general` cache for general purpose use
421 ; for simplicity use rc.file_namespace backend,
421 ; for simplicity use rc.file_namespace backend,
422 ; for performance and scale use rc.redis
422 ; for performance and scale use rc.redis
423 ; *********************************************
423 ; *********************************************
424 rc_cache.cache_general.backend = dogpile.cache.rc.file_namespace
424 rc_cache.cache_general.backend = dogpile.cache.rc.file_namespace
425 rc_cache.cache_general.expiration_time = 43200
425 rc_cache.cache_general.expiration_time = 43200
426 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
426 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
427 #rc_cache.cache_general.arguments.filename = /tmp/cache_general_db
427 #rc_cache.cache_general.arguments.filename = /tmp/cache_general_db
428
428
429 ; alternative `cache_general` redis backend with distributed lock
429 ; alternative `cache_general` redis backend with distributed lock
430 #rc_cache.cache_general.backend = dogpile.cache.rc.redis
430 #rc_cache.cache_general.backend = dogpile.cache.rc.redis
431 #rc_cache.cache_general.expiration_time = 300
431 #rc_cache.cache_general.expiration_time = 300
432
432
433 ; redis_expiration_time needs to be greater then expiration_time
433 ; redis_expiration_time needs to be greater then expiration_time
434 #rc_cache.cache_general.arguments.redis_expiration_time = 7200
434 #rc_cache.cache_general.arguments.redis_expiration_time = 7200
435
435
436 #rc_cache.cache_general.arguments.host = localhost
436 #rc_cache.cache_general.arguments.host = localhost
437 #rc_cache.cache_general.arguments.port = 6379
437 #rc_cache.cache_general.arguments.port = 6379
438 #rc_cache.cache_general.arguments.db = 0
438 #rc_cache.cache_general.arguments.db = 0
439 #rc_cache.cache_general.arguments.socket_timeout = 30
439 #rc_cache.cache_general.arguments.socket_timeout = 30
440 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
440 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
441 #rc_cache.cache_general.arguments.distributed_lock = true
441 #rc_cache.cache_general.arguments.distributed_lock = true
442
442
443 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
443 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
444 #rc_cache.cache_general.arguments.lock_auto_renewal = true
444 #rc_cache.cache_general.arguments.lock_auto_renewal = true
445
445
446 ; *************************************************
446 ; *************************************************
447 ; `cache_perms` cache for permission tree, auth TTL
447 ; `cache_perms` cache for permission tree, auth TTL
448 ; for simplicity use rc.file_namespace backend,
448 ; for simplicity use rc.file_namespace backend,
449 ; for performance and scale use rc.redis
449 ; for performance and scale use rc.redis
450 ; *************************************************
450 ; *************************************************
451 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
451 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
452 rc_cache.cache_perms.expiration_time = 3600
452 rc_cache.cache_perms.expiration_time = 3600
453 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
453 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
454 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms_db
454 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms_db
455
455
456 ; alternative `cache_perms` redis backend with distributed lock
456 ; alternative `cache_perms` redis backend with distributed lock
457 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
457 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
458 #rc_cache.cache_perms.expiration_time = 300
458 #rc_cache.cache_perms.expiration_time = 300
459
459
460 ; redis_expiration_time needs to be greater then expiration_time
460 ; redis_expiration_time needs to be greater then expiration_time
461 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
461 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
462
462
463 #rc_cache.cache_perms.arguments.host = localhost
463 #rc_cache.cache_perms.arguments.host = localhost
464 #rc_cache.cache_perms.arguments.port = 6379
464 #rc_cache.cache_perms.arguments.port = 6379
465 #rc_cache.cache_perms.arguments.db = 0
465 #rc_cache.cache_perms.arguments.db = 0
466 #rc_cache.cache_perms.arguments.socket_timeout = 30
466 #rc_cache.cache_perms.arguments.socket_timeout = 30
467 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
467 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
468 #rc_cache.cache_perms.arguments.distributed_lock = true
468 #rc_cache.cache_perms.arguments.distributed_lock = true
469
469
470 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
470 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
471 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
471 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
472
472
473 ; ***************************************************
473 ; ***************************************************
474 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
474 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
475 ; for simplicity use rc.file_namespace backend,
475 ; for simplicity use rc.file_namespace backend,
476 ; for performance and scale use rc.redis
476 ; for performance and scale use rc.redis
477 ; ***************************************************
477 ; ***************************************************
478 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
478 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
479 rc_cache.cache_repo.expiration_time = 2592000
479 rc_cache.cache_repo.expiration_time = 2592000
480 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
480 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
481 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo_db
481 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo_db
482
482
483 ; alternative `cache_repo` redis backend with distributed lock
483 ; alternative `cache_repo` redis backend with distributed lock
484 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
484 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
485 #rc_cache.cache_repo.expiration_time = 2592000
485 #rc_cache.cache_repo.expiration_time = 2592000
486
486
487 ; redis_expiration_time needs to be greater then expiration_time
487 ; redis_expiration_time needs to be greater then expiration_time
488 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
488 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
489
489
490 #rc_cache.cache_repo.arguments.host = localhost
490 #rc_cache.cache_repo.arguments.host = localhost
491 #rc_cache.cache_repo.arguments.port = 6379
491 #rc_cache.cache_repo.arguments.port = 6379
492 #rc_cache.cache_repo.arguments.db = 1
492 #rc_cache.cache_repo.arguments.db = 1
493 #rc_cache.cache_repo.arguments.socket_timeout = 30
493 #rc_cache.cache_repo.arguments.socket_timeout = 30
494 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
494 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
495 #rc_cache.cache_repo.arguments.distributed_lock = true
495 #rc_cache.cache_repo.arguments.distributed_lock = true
496
496
497 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
497 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
498 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
498 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
499
499
500 ; ##############
500 ; ##############
501 ; BEAKER SESSION
501 ; BEAKER SESSION
502 ; ##############
502 ; ##############
503
503
504 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
504 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
505 ; types are file, ext:redis, ext:database, ext:memcached
505 ; types are file, ext:redis, ext:database, ext:memcached
506 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
506 ; Fastest ones are ext:redis and ext:database, DO NOT use memory type for session
507 #beaker.session.type = file
507 #beaker.session.type = file
508 #beaker.session.data_dir = %(here)s/data/sessions
508 #beaker.session.data_dir = %(here)s/data/sessions
509
509
510 ; Redis based sessions
510 ; Redis based sessions
511 beaker.session.type = ext:redis
511 beaker.session.type = ext:redis
512 beaker.session.url = redis://redis:6379/2
512 beaker.session.url = redis://redis:6379/2
513
513
514 ; DB based session, fast, and allows easy management over logged in users
514 ; DB based session, fast, and allows easy management over logged in users
515 #beaker.session.type = ext:database
515 #beaker.session.type = ext:database
516 #beaker.session.table_name = db_session
516 #beaker.session.table_name = db_session
517 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
517 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
518 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
518 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
519 #beaker.session.sa.pool_recycle = 3600
519 #beaker.session.sa.pool_recycle = 3600
520 #beaker.session.sa.echo = false
520 #beaker.session.sa.echo = false
521
521
522 beaker.session.key = rhodecode
522 beaker.session.key = rhodecode
523 beaker.session.secret = production-rc-uytcxaz
523 beaker.session.secret = production-rc-uytcxaz
524 beaker.session.lock_dir = /data_ramdisk/lock
524 beaker.session.lock_dir = /data_ramdisk/lock
525
525
526 ; Secure encrypted cookie. Requires AES and AES python libraries
526 ; Secure encrypted cookie. Requires AES and AES python libraries
527 ; you must disable beaker.session.secret to use this
527 ; you must disable beaker.session.secret to use this
528 #beaker.session.encrypt_key = key_for_encryption
528 #beaker.session.encrypt_key = key_for_encryption
529 #beaker.session.validate_key = validation_key
529 #beaker.session.validate_key = validation_key
530
530
531 ; Sets session as invalid (also logging out user) if it haven not been
531 ; Sets session as invalid (also logging out user) if it haven not been
532 ; accessed for given amount of time in seconds
532 ; accessed for given amount of time in seconds
533 beaker.session.timeout = 2592000
533 beaker.session.timeout = 2592000
534 beaker.session.httponly = true
534 beaker.session.httponly = true
535
535
536 ; Path to use for the cookie. Set to prefix if you use prefix middleware
536 ; Path to use for the cookie. Set to prefix if you use prefix middleware
537 #beaker.session.cookie_path = /custom_prefix
537 #beaker.session.cookie_path = /custom_prefix
538
538
539 ; Set https secure cookie
539 ; Set https secure cookie
540 beaker.session.secure = false
540 beaker.session.secure = false
541
541
542 ; default cookie expiration time in seconds, set to `true` to set expire
542 ; default cookie expiration time in seconds, set to `true` to set expire
543 ; at browser close
543 ; at browser close
544 #beaker.session.cookie_expires = 3600
544 #beaker.session.cookie_expires = 3600
545
545
546 ; #############################
546 ; #############################
547 ; SEARCH INDEXING CONFIGURATION
547 ; SEARCH INDEXING CONFIGURATION
548 ; #############################
548 ; #############################
549
549
550 ; Full text search indexer is available in rhodecode-tools under
550 ; Full text search indexer is available in rhodecode-tools under
551 ; `rhodecode-tools index` command
551 ; `rhodecode-tools index` command
552
552
553 ; WHOOSH Backend, doesn't require additional services to run
553 ; WHOOSH Backend, doesn't require additional services to run
554 ; it works good with few dozen repos
554 ; it works good with few dozen repos
555 search.module = rhodecode.lib.index.whoosh
555 search.module = rhodecode.lib.index.whoosh
556 search.location = %(here)s/data/index
556 search.location = %(here)s/data/index
557
557
558 ; ####################
558 ; ####################
559 ; CHANNELSTREAM CONFIG
559 ; CHANNELSTREAM CONFIG
560 ; ####################
560 ; ####################
561
561
562 ; channelstream enables persistent connections and live notification
562 ; channelstream enables persistent connections and live notification
563 ; in the system. It's also used by the chat system
563 ; in the system. It's also used by the chat system
564
564
565 channelstream.enabled = true
565 channelstream.enabled = true
566
566
567 ; server address for channelstream server on the backend
567 ; server address for channelstream server on the backend
568 channelstream.server = channelstream:9800
568 channelstream.server = channelstream:9800
569
569
570 ; location of the channelstream server from outside world
570 ; location of the channelstream server from outside world
571 ; use ws:// for http or wss:// for https. This address needs to be handled
571 ; use ws:// for http or wss:// for https. This address needs to be handled
572 ; by external HTTP server such as Nginx or Apache
572 ; by external HTTP server such as Nginx or Apache
573 ; see Nginx/Apache configuration examples in our docs
573 ; see Nginx/Apache configuration examples in our docs
574 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
574 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
575 channelstream.secret = ENV_GENERATED
575 channelstream.secret = ENV_GENERATED
576 channelstream.history.location = /var/opt/rhodecode_data/channelstream_history
576 channelstream.history.location = /var/opt/rhodecode_data/channelstream_history
577
577
578 ; Internal application path that Javascript uses to connect into.
578 ; Internal application path that Javascript uses to connect into.
579 ; If you use proxy-prefix the prefix should be added before /_channelstream
579 ; If you use proxy-prefix the prefix should be added before /_channelstream
580 channelstream.proxy_path = /_channelstream
580 channelstream.proxy_path = /_channelstream
581
581
582
582
583 ; ##############################
583 ; ##############################
584 ; MAIN RHODECODE DATABASE CONFIG
584 ; MAIN RHODECODE DATABASE CONFIG
585 ; ##############################
585 ; ##############################
586
586
587 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
587 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
588 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
588 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
589 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
589 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
590 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
590 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
591 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
591 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
592
592
593 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
593 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
594
594
595 ; see sqlalchemy docs for other advanced settings
595 ; see sqlalchemy docs for other advanced settings
596 ; print the sql statements to output
596 ; print the sql statements to output
597 sqlalchemy.db1.echo = false
597 sqlalchemy.db1.echo = false
598
598
599 ; recycle the connections after this amount of seconds
599 ; recycle the connections after this amount of seconds
600 sqlalchemy.db1.pool_recycle = 3600
600 sqlalchemy.db1.pool_recycle = 3600
601
601
602 ; the number of connections to keep open inside the connection pool.
602 ; the number of connections to keep open inside the connection pool.
603 ; 0 indicates no limit
603 ; 0 indicates no limit
604 ; the general calculus with gevent is:
604 ; the general calculus with gevent is:
605 ; if your system allows 500 concurrent greenlets (max_connections) that all do database access,
605 ; if your system allows 500 concurrent greenlets (max_connections) that all do database access,
606 ; then increase pool size + max overflow so that they add up to 500.
606 ; then increase pool size + max overflow so that they add up to 500.
607 #sqlalchemy.db1.pool_size = 5
607 #sqlalchemy.db1.pool_size = 5
608
608
609 ; The number of connections to allow in connection pool "overflow", that is
609 ; The number of connections to allow in connection pool "overflow", that is
610 ; connections that can be opened above and beyond the pool_size setting,
610 ; connections that can be opened above and beyond the pool_size setting,
611 ; which defaults to five.
611 ; which defaults to five.
612 #sqlalchemy.db1.max_overflow = 10
612 #sqlalchemy.db1.max_overflow = 10
613
613
614 ; Connection check ping, used to detect broken database connections
614 ; Connection check ping, used to detect broken database connections
615 ; could be enabled to better handle cases if MySQL has gone away errors
615 ; could be enabled to better handle cases if MySQL has gone away errors
616 #sqlalchemy.db1.ping_connection = true
616 #sqlalchemy.db1.ping_connection = true
617
617
618 ; ##########
618 ; ##########
619 ; VCS CONFIG
619 ; VCS CONFIG
620 ; ##########
620 ; ##########
621 vcs.server.enable = true
621 vcs.server.enable = true
622 vcs.server = vcsserver:10010
622 vcs.server = vcsserver:10010
623
623
624 ; Web server connectivity protocol, responsible for web based VCS operations
624 ; Web server connectivity protocol, responsible for web based VCS operations
625 ; Available protocols are:
625 ; Available protocols are:
626 ; `http` - use http-rpc backend (default)
626 ; `http` - use http-rpc backend (default)
627 vcs.server.protocol = http
627 vcs.server.protocol = http
628
628
629 ; Push/Pull operations protocol, available options are:
629 ; Push/Pull operations protocol, available options are:
630 ; `http` - use http-rpc backend (default)
630 ; `http` - use http-rpc backend (default)
631 vcs.scm_app_implementation = http
631 vcs.scm_app_implementation = http
632
632
633 ; Push/Pull operations hooks protocol, available options are:
633 ; Push/Pull operations hooks protocol, available options are:
634 ; `http` - use http-rpc backend (default)
634 ; `http` - use http-rpc backend (default)
635 ; `celery` - use celery based hooks
635 ; `celery` - use celery based hooks
636 #DEPRECATED:vcs.hooks.protocol = http
636 #DEPRECATED:vcs.hooks.protocol = http
637 vcs.hooks.protocol.v2 = celery
637 vcs.hooks.protocol.v2 = celery
638
638
639 ; Host on which this instance is listening for hooks. vcsserver will call this host to pull/push hooks so it should be
639 ; Host on which this instance is listening for hooks. vcsserver will call this host to pull/push hooks so it should be
640 ; accessible via network.
640 ; accessible via network.
641 ; Use vcs.hooks.host = "*" to bind to current hostname (for Docker)
641 ; Use vcs.hooks.host = "*" to bind to current hostname (for Docker)
642 vcs.hooks.host = *
642 vcs.hooks.host = *
643
643
644 ; Start VCSServer with this instance as a subprocess, useful for development
644 ; Start VCSServer with this instance as a subprocess, useful for development
645 vcs.start_server = false
645 vcs.start_server = false
646
646
647 ; List of enabled VCS backends, available options are:
647 ; List of enabled VCS backends, available options are:
648 ; `hg` - mercurial
648 ; `hg` - mercurial
649 ; `git` - git
649 ; `git` - git
650 ; `svn` - subversion
650 ; `svn` - subversion
651 vcs.backends = hg, git, svn
651 vcs.backends = hg, git, svn
652
652
653 ; Wait this number of seconds before killing connection to the vcsserver
653 ; Wait this number of seconds before killing connection to the vcsserver
654 vcs.connection_timeout = 3600
654 vcs.connection_timeout = 3600
655
655
656 ; Cache flag to cache vcsserver remote calls locally
656 ; Cache flag to cache vcsserver remote calls locally
657 ; It uses cache_region `cache_repo`
657 ; It uses cache_region `cache_repo`
658 vcs.methods.cache = true
658 vcs.methods.cache = true
659
659
660 ; Filesystem location where Git lfs objects should be stored
661 vcs.git.lfs.storage_location = /var/opt/rhodecode_repo_store/.cache/git_lfs_store
662
663 ; Filesystem location where Mercurial largefile objects should be stored
664 vcs.hg.largefiles.storage_location = /var/opt/rhodecode_repo_store/.cache/hg_largefiles_store
665
660 ; ####################################################
666 ; ####################################################
661 ; Subversion proxy support (mod_dav_svn)
667 ; Subversion proxy support (mod_dav_svn)
662 ; Maps RhodeCode repo groups into SVN paths for Apache
668 ; Maps RhodeCode repo groups into SVN paths for Apache
663 ; ####################################################
669 ; ####################################################
664
670
665 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
671 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
666 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
672 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
667 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
673 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
668 #vcs.svn.compatible_version = 1.8
674 #vcs.svn.compatible_version = 1.8
669
675
670 ; Redis connection settings for svn integrations logic
676 ; Redis connection settings for svn integrations logic
671 ; This connection string needs to be the same on ce and vcsserver
677 ; This connection string needs to be the same on ce and vcsserver
672 vcs.svn.redis_conn = redis://redis:6379/0
678 vcs.svn.redis_conn = redis://redis:6379/0
673
679
674 ; Enable SVN proxy of requests over HTTP
680 ; Enable SVN proxy of requests over HTTP
675 vcs.svn.proxy.enabled = true
681 vcs.svn.proxy.enabled = true
676
682
677 ; host to connect to running SVN subsystem
683 ; host to connect to running SVN subsystem
678 vcs.svn.proxy.host = http://svn:8090
684 vcs.svn.proxy.host = http://svn:8090
679
685
680 ; Enable or disable the config file generation.
686 ; Enable or disable the config file generation.
681 svn.proxy.generate_config = true
687 svn.proxy.generate_config = true
682
688
683 ; Generate config file with `SVNListParentPath` set to `On`.
689 ; Generate config file with `SVNListParentPath` set to `On`.
684 svn.proxy.list_parent_path = true
690 svn.proxy.list_parent_path = true
685
691
686 ; Set location and file name of generated config file.
692 ; Set location and file name of generated config file.
687 svn.proxy.config_file_path = /etc/rhodecode/conf/svn/mod_dav_svn.conf
693 svn.proxy.config_file_path = /etc/rhodecode/conf/svn/mod_dav_svn.conf
688
694
689 ; alternative mod_dav config template. This needs to be a valid mako template
695 ; alternative mod_dav config template. This needs to be a valid mako template
690 ; Example template can be found in the source code:
696 ; Example template can be found in the source code:
691 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
697 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
692 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
698 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
693
699
694 ; Used as a prefix to the `Location` block in the generated config file.
700 ; Used as a prefix to the `Location` block in the generated config file.
695 ; In most cases it should be set to `/`.
701 ; In most cases it should be set to `/`.
696 svn.proxy.location_root = /
702 svn.proxy.location_root = /
697
703
698 ; Command to reload the mod dav svn configuration on change.
704 ; Command to reload the mod dav svn configuration on change.
699 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
705 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
700 ; Make sure user who runs RhodeCode process is allowed to reload Apache
706 ; Make sure user who runs RhodeCode process is allowed to reload Apache
701 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
707 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
702
708
703 ; If the timeout expires before the reload command finishes, the command will
709 ; If the timeout expires before the reload command finishes, the command will
704 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
710 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
705 #svn.proxy.reload_timeout = 10
711 #svn.proxy.reload_timeout = 10
706
712
707 ; ####################
713 ; ####################
708 ; SSH Support Settings
714 ; SSH Support Settings
709 ; ####################
715 ; ####################
710
716
711 ; Defines if a custom authorized_keys file should be created and written on
717 ; Defines if a custom authorized_keys file should be created and written on
712 ; any change user ssh keys. Setting this to false also disables possibility
718 ; any change user ssh keys. Setting this to false also disables possibility
713 ; of adding SSH keys by users from web interface. Super admins can still
719 ; of adding SSH keys by users from web interface. Super admins can still
714 ; manage SSH Keys.
720 ; manage SSH Keys.
715 ssh.generate_authorized_keyfile = true
721 ssh.generate_authorized_keyfile = true
716
722
717 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
723 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
718 # ssh.authorized_keys_ssh_opts =
724 # ssh.authorized_keys_ssh_opts =
719
725
720 ; Path to the authorized_keys file where the generate entries are placed.
726 ; Path to the authorized_keys file where the generate entries are placed.
721 ; It is possible to have multiple key files specified in `sshd_config` e.g.
727 ; It is possible to have multiple key files specified in `sshd_config` e.g.
722 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
728 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
723 ssh.authorized_keys_file_path = /etc/rhodecode/conf/ssh/authorized_keys_rhodecode
729 ssh.authorized_keys_file_path = /etc/rhodecode/conf/ssh/authorized_keys_rhodecode
724
730
725 ; Command to execute the SSH wrapper. The binary is available in the
731 ; Command to execute the SSH wrapper. The binary is available in the
726 ; RhodeCode installation directory.
732 ; RhodeCode installation directory.
727 ; legacy: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
733 ; legacy: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
728 ; new rewrite: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
734 ; new rewrite: /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
729 #DEPRECATED: ssh.wrapper_cmd = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
735 #DEPRECATED: ssh.wrapper_cmd = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper
730 ssh.wrapper_cmd.v2 = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
736 ssh.wrapper_cmd.v2 = /usr/local/bin/rhodecode_bin/bin/rc-ssh-wrapper-v2
731
737
732 ; Allow shell when executing the ssh-wrapper command
738 ; Allow shell when executing the ssh-wrapper command
733 ssh.wrapper_cmd_allow_shell = false
739 ssh.wrapper_cmd_allow_shell = false
734
740
735 ; Enables logging, and detailed output send back to the client during SSH
741 ; Enables logging, and detailed output send back to the client during SSH
736 ; operations. Useful for debugging, shouldn't be used in production.
742 ; operations. Useful for debugging, shouldn't be used in production.
737 ssh.enable_debug_logging = false
743 ssh.enable_debug_logging = false
738
744
739 ; Paths to binary executable, by default they are the names, but we can
745 ; Paths to binary executable, by default they are the names, but we can
740 ; override them if we want to use a custom one
746 ; override them if we want to use a custom one
741 ssh.executable.hg = /usr/local/bin/rhodecode_bin/vcs_bin/hg
747 ssh.executable.hg = /usr/local/bin/rhodecode_bin/vcs_bin/hg
742 ssh.executable.git = /usr/local/bin/rhodecode_bin/vcs_bin/git
748 ssh.executable.git = /usr/local/bin/rhodecode_bin/vcs_bin/git
743 ssh.executable.svn = /usr/local/bin/rhodecode_bin/vcs_bin/svnserve
749 ssh.executable.svn = /usr/local/bin/rhodecode_bin/vcs_bin/svnserve
744
750
745 ; Enables SSH key generator web interface. Disabling this still allows users
751 ; Enables SSH key generator web interface. Disabling this still allows users
746 ; to add their own keys.
752 ; to add their own keys.
747 ssh.enable_ui_key_generator = true
753 ssh.enable_ui_key_generator = true
748
754
749 ; Statsd client config, this is used to send metrics to statsd
755 ; Statsd client config, this is used to send metrics to statsd
750 ; We recommend setting statsd_exported and scrape them using Prometheus
756 ; We recommend setting statsd_exported and scrape them using Prometheus
751 #statsd.enabled = false
757 #statsd.enabled = false
752 #statsd.statsd_host = 0.0.0.0
758 #statsd.statsd_host = 0.0.0.0
753 #statsd.statsd_port = 8125
759 #statsd.statsd_port = 8125
754 #statsd.statsd_prefix =
760 #statsd.statsd_prefix =
755 #statsd.statsd_ipv6 = false
761 #statsd.statsd_ipv6 = false
756
762
757 ; configure logging automatically at server startup set to false
763 ; configure logging automatically at server startup set to false
758 ; to use the below custom logging config.
764 ; to use the below custom logging config.
759 ; RC_LOGGING_FORMATTER
765 ; RC_LOGGING_FORMATTER
760 ; RC_LOGGING_LEVEL
766 ; RC_LOGGING_LEVEL
761 ; env variables can control the settings for logging in case of autoconfigure
767 ; env variables can control the settings for logging in case of autoconfigure
762
768
763 #logging.autoconfigure = true
769 #logging.autoconfigure = true
764
770
765 ; specify your own custom logging config file to configure logging
771 ; specify your own custom logging config file to configure logging
766 #logging.logging_conf_file = /path/to/custom_logging.ini
772 #logging.logging_conf_file = /path/to/custom_logging.ini
767
773
768 ; Dummy marker to add new entries after.
774 ; Dummy marker to add new entries after.
769 ; Add any custom entries below. Please don't remove this marker.
775 ; Add any custom entries below. Please don't remove this marker.
770 custom.conf = 1
776 custom.conf = 1
771
777
772
778
773 ; #####################
779 ; #####################
774 ; LOGGING CONFIGURATION
780 ; LOGGING CONFIGURATION
775 ; #####################
781 ; #####################
776
782
777 [loggers]
783 [loggers]
778 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
784 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
779
785
780 [handlers]
786 [handlers]
781 keys = console, console_sql
787 keys = console, console_sql
782
788
783 [formatters]
789 [formatters]
784 keys = generic, json, color_formatter, color_formatter_sql
790 keys = generic, json, color_formatter, color_formatter_sql
785
791
786 ; #######
792 ; #######
787 ; LOGGERS
793 ; LOGGERS
788 ; #######
794 ; #######
789 [logger_root]
795 [logger_root]
790 level = NOTSET
796 level = NOTSET
791 handlers = console
797 handlers = console
792
798
793 [logger_sqlalchemy]
799 [logger_sqlalchemy]
794 level = INFO
800 level = INFO
795 handlers = console_sql
801 handlers = console_sql
796 qualname = sqlalchemy.engine
802 qualname = sqlalchemy.engine
797 propagate = 0
803 propagate = 0
798
804
799 [logger_beaker]
805 [logger_beaker]
800 level = DEBUG
806 level = DEBUG
801 handlers =
807 handlers =
802 qualname = beaker.container
808 qualname = beaker.container
803 propagate = 1
809 propagate = 1
804
810
805 [logger_rhodecode]
811 [logger_rhodecode]
806 level = DEBUG
812 level = DEBUG
807 handlers =
813 handlers =
808 qualname = rhodecode
814 qualname = rhodecode
809 propagate = 1
815 propagate = 1
810
816
811 [logger_ssh_wrapper]
817 [logger_ssh_wrapper]
812 level = DEBUG
818 level = DEBUG
813 handlers =
819 handlers =
814 qualname = ssh_wrapper
820 qualname = ssh_wrapper
815 propagate = 1
821 propagate = 1
816
822
817 [logger_celery]
823 [logger_celery]
818 level = DEBUG
824 level = DEBUG
819 handlers =
825 handlers =
820 qualname = celery
826 qualname = celery
821
827
822
828
823 ; ########
829 ; ########
824 ; HANDLERS
830 ; HANDLERS
825 ; ########
831 ; ########
826
832
827 [handler_console]
833 [handler_console]
828 class = StreamHandler
834 class = StreamHandler
829 args = (sys.stderr, )
835 args = (sys.stderr, )
830 level = INFO
836 level = INFO
831 ; To enable JSON formatted logs replace 'generic/color_formatter' with 'json'
837 ; To enable JSON formatted logs replace 'generic/color_formatter' with 'json'
832 ; This allows sending properly formatted logs to grafana loki or elasticsearch
838 ; This allows sending properly formatted logs to grafana loki or elasticsearch
833 formatter = generic
839 formatter = generic
834
840
835 [handler_console_sql]
841 [handler_console_sql]
836 ; "level = DEBUG" logs SQL queries and results.
842 ; "level = DEBUG" logs SQL queries and results.
837 ; "level = INFO" logs SQL queries.
843 ; "level = INFO" logs SQL queries.
838 ; "level = WARN" logs neither. (Recommended for production systems.)
844 ; "level = WARN" logs neither. (Recommended for production systems.)
839 class = StreamHandler
845 class = StreamHandler
840 args = (sys.stderr, )
846 args = (sys.stderr, )
841 level = WARN
847 level = WARN
842 ; To enable JSON formatted logs replace 'generic/color_formatter_sql' with 'json'
848 ; To enable JSON formatted logs replace 'generic/color_formatter_sql' with 'json'
843 ; This allows sending properly formatted logs to grafana loki or elasticsearch
849 ; This allows sending properly formatted logs to grafana loki or elasticsearch
844 formatter = generic
850 formatter = generic
845
851
846 ; ##########
852 ; ##########
847 ; FORMATTERS
853 ; FORMATTERS
848 ; ##########
854 ; ##########
849
855
850 [formatter_generic]
856 [formatter_generic]
851 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
857 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
852 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
858 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
853 datefmt = %Y-%m-%d %H:%M:%S
859 datefmt = %Y-%m-%d %H:%M:%S
854
860
855 [formatter_color_formatter]
861 [formatter_color_formatter]
856 class = rhodecode.lib.logging_formatter.ColorFormatter
862 class = rhodecode.lib.logging_formatter.ColorFormatter
857 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
863 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
858 datefmt = %Y-%m-%d %H:%M:%S
864 datefmt = %Y-%m-%d %H:%M:%S
859
865
860 [formatter_color_formatter_sql]
866 [formatter_color_formatter_sql]
861 class = rhodecode.lib.logging_formatter.ColorFormatterSql
867 class = rhodecode.lib.logging_formatter.ColorFormatterSql
862 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
868 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
863 datefmt = %Y-%m-%d %H:%M:%S
869 datefmt = %Y-%m-%d %H:%M:%S
864
870
865 [formatter_json]
871 [formatter_json]
866 format = %(timestamp)s %(levelname)s %(name)s %(message)s %(req_id)s
872 format = %(timestamp)s %(levelname)s %(name)s %(message)s %(req_id)s
867 class = rhodecode.lib._vendor.jsonlogger.JsonFormatter
873 class = rhodecode.lib._vendor.jsonlogger.JsonFormatter
@@ -1,228 +1,231 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import os
19 import os
20 import tempfile
20 import tempfile
21 import logging
21 import logging
22
22
23 from pyramid.settings import asbool
23 from pyramid.settings import asbool
24
24
25 from rhodecode.config.settings_maker import SettingsMaker
25 from rhodecode.config.settings_maker import SettingsMaker
26 from rhodecode.config import utils as config_utils
26 from rhodecode.config import utils as config_utils
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 def sanitize_settings_and_apply_defaults(global_config, settings):
31 def sanitize_settings_and_apply_defaults(global_config, settings):
32 """
32 """
33 Applies settings defaults and does all type conversion.
33 Applies settings defaults and does all type conversion.
34
34
35 We would move all settings parsing and preparation into this place, so that
35 We would move all settings parsing and preparation into this place, so that
36 we have only one place left which deals with this part. The remaining parts
36 we have only one place left which deals with this part. The remaining parts
37 of the application would start to rely fully on well-prepared settings.
37 of the application would start to rely fully on well-prepared settings.
38
38
39 This piece would later be split up per topic to avoid a big fat monster
39 This piece would later be split up per topic to avoid a big fat monster
40 function.
40 function.
41 """
41 """
42 jn = os.path.join
42 jn = os.path.join
43
43
44 global_settings_maker = SettingsMaker(global_config)
44 global_settings_maker = SettingsMaker(global_config)
45 global_settings_maker.make_setting('debug', default=False, parser='bool')
45 global_settings_maker.make_setting('debug', default=False, parser='bool')
46 debug_enabled = asbool(global_config.get('debug'))
46 debug_enabled = asbool(global_config.get('debug'))
47
47
48 settings_maker = SettingsMaker(settings)
48 settings_maker = SettingsMaker(settings)
49
49
50 settings_maker.make_setting(
50 settings_maker.make_setting(
51 'logging.autoconfigure',
51 'logging.autoconfigure',
52 default=False,
52 default=False,
53 parser='bool')
53 parser='bool')
54
54
55 logging_conf = jn(os.path.dirname(global_config.get('__file__')), 'logging.ini')
55 logging_conf = jn(os.path.dirname(global_config.get('__file__')), 'logging.ini')
56 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
56 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
57
57
58 # Default includes, possible to change as a user
58 # Default includes, possible to change as a user
59 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
59 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
60 log.debug(
60 log.debug(
61 "Using the following pyramid.includes: %s",
61 "Using the following pyramid.includes: %s",
62 pyramid_includes)
62 pyramid_includes)
63
63
64 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
64 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
65 settings_maker.make_setting('rhodecode.edition_id', 'CE')
65 settings_maker.make_setting('rhodecode.edition_id', 'CE')
66
66
67 if 'mako.default_filters' not in settings:
67 if 'mako.default_filters' not in settings:
68 # set custom default filters if we don't have it defined
68 # set custom default filters if we don't have it defined
69 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
69 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
70 settings['mako.default_filters'] = 'h_filter'
70 settings['mako.default_filters'] = 'h_filter'
71
71
72 if 'mako.directories' not in settings:
72 if 'mako.directories' not in settings:
73 mako_directories = settings.setdefault('mako.directories', [
73 mako_directories = settings.setdefault('mako.directories', [
74 # Base templates of the original application
74 # Base templates of the original application
75 'rhodecode:templates',
75 'rhodecode:templates',
76 ])
76 ])
77 log.debug(
77 log.debug(
78 "Using the following Mako template directories: %s",
78 "Using the following Mako template directories: %s",
79 mako_directories)
79 mako_directories)
80
80
81 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
81 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
82 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
82 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
83 raw_url = settings['beaker.session.url']
83 raw_url = settings['beaker.session.url']
84 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
84 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
85 settings['beaker.session.url'] = 'redis://' + raw_url
85 settings['beaker.session.url'] = 'redis://' + raw_url
86
86
87 settings_maker.make_setting('__file__', global_config.get('__file__'))
87 settings_maker.make_setting('__file__', global_config.get('__file__'))
88
88
89 # TODO: johbo: Re-think this, usually the call to config.include
89 # TODO: johbo: Re-think this, usually the call to config.include
90 # should allow to pass in a prefix.
90 # should allow to pass in a prefix.
91 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
91 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
92
92
93 # Sanitize generic settings.
93 # Sanitize generic settings.
94 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
94 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
95 settings_maker.make_setting('gzip_responses', False, parser='bool')
95 settings_maker.make_setting('gzip_responses', False, parser='bool')
96 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
96 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
97
97
98 # statsd
98 # statsd
99 settings_maker.make_setting('statsd.enabled', False, parser='bool')
99 settings_maker.make_setting('statsd.enabled', False, parser='bool')
100 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
100 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
101 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
101 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
102 settings_maker.make_setting('statsd.statsd_prefix', '')
102 settings_maker.make_setting('statsd.statsd_prefix', '')
103 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
103 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
104
104
105 settings_maker.make_setting('vcs.svn.compatible_version', '')
105 settings_maker.make_setting('vcs.svn.compatible_version', '')
106 settings_maker.make_setting('vcs.svn.redis_conn', 'redis://redis:6379/0')
106 settings_maker.make_setting('vcs.svn.redis_conn', 'redis://redis:6379/0')
107 settings_maker.make_setting('vcs.svn.proxy.enabled', True, parser='bool')
107 settings_maker.make_setting('vcs.svn.proxy.enabled', True, parser='bool')
108 settings_maker.make_setting('vcs.svn.proxy.host', 'http://svn:8090', parser='string')
108 settings_maker.make_setting('vcs.svn.proxy.host', 'http://svn:8090', parser='string')
109 settings_maker.make_setting('vcs.hooks.protocol.v2', 'celery')
109 settings_maker.make_setting('vcs.hooks.protocol.v2', 'celery')
110 settings_maker.make_setting('vcs.hooks.host', '*')
110 settings_maker.make_setting('vcs.hooks.host', '*')
111 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
111 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
112 settings_maker.make_setting('vcs.server', '')
112 settings_maker.make_setting('vcs.server', '')
113 settings_maker.make_setting('vcs.server.protocol', 'http')
113 settings_maker.make_setting('vcs.server.protocol', 'http')
114 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
114 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
115 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
115 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
116 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
116 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
117 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
117 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
118 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
118 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
119 settings_maker.make_setting('vcs.git.lfs.storage_location', '/var/opt/rhodecode_repo_store/.cache/git_lfs_store')
120 settings_maker.make_setting('vcs.hg.largefiles.storage_location',
121 '/var/opt/rhodecode_repo_store/.cache/hg_largefiles_store')
119
122
120 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
123 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
121
124
122 # repo_store path
125 # repo_store path
123 settings_maker.make_setting('repo_store.path', '/var/opt/rhodecode_repo_store')
126 settings_maker.make_setting('repo_store.path', '/var/opt/rhodecode_repo_store')
124 # Support legacy values of vcs.scm_app_implementation. Legacy
127 # Support legacy values of vcs.scm_app_implementation. Legacy
125 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
128 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
126 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
129 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
127 scm_app_impl = settings['vcs.scm_app_implementation']
130 scm_app_impl = settings['vcs.scm_app_implementation']
128 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
131 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
129 settings['vcs.scm_app_implementation'] = 'http'
132 settings['vcs.scm_app_implementation'] = 'http'
130
133
131 settings_maker.make_setting('appenlight', False, parser='bool')
134 settings_maker.make_setting('appenlight', False, parser='bool')
132
135
133 temp_store = tempfile.gettempdir()
136 temp_store = tempfile.gettempdir()
134 tmp_cache_dir = jn(temp_store, 'rc_cache')
137 tmp_cache_dir = jn(temp_store, 'rc_cache')
135
138
136 # save default, cache dir, and use it for all backends later.
139 # save default, cache dir, and use it for all backends later.
137 default_cache_dir = settings_maker.make_setting(
140 default_cache_dir = settings_maker.make_setting(
138 'cache_dir',
141 'cache_dir',
139 default=tmp_cache_dir, default_when_empty=True,
142 default=tmp_cache_dir, default_when_empty=True,
140 parser='dir:ensured')
143 parser='dir:ensured')
141
144
142 # exception store cache
145 # exception store cache
143 settings_maker.make_setting(
146 settings_maker.make_setting(
144 'exception_tracker.store_path',
147 'exception_tracker.store_path',
145 default=jn(default_cache_dir, 'exc_store'), default_when_empty=True,
148 default=jn(default_cache_dir, 'exc_store'), default_when_empty=True,
146 parser='dir:ensured'
149 parser='dir:ensured'
147 )
150 )
148
151
149 settings_maker.make_setting(
152 settings_maker.make_setting(
150 'celerybeat-schedule.path',
153 'celerybeat-schedule.path',
151 default=jn(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
154 default=jn(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
152 parser='file:ensured'
155 parser='file:ensured'
153 )
156 )
154
157
155 # celery
158 # celery
156 broker_url = settings_maker.make_setting('celery.broker_url', 'redis://redis:6379/8')
159 broker_url = settings_maker.make_setting('celery.broker_url', 'redis://redis:6379/8')
157 settings_maker.make_setting('celery.result_backend', broker_url)
160 settings_maker.make_setting('celery.result_backend', broker_url)
158
161
159 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
162 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
160 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
163 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
161
164
162 # sessions, ensure file since no-value is memory
165 # sessions, ensure file since no-value is memory
163 settings_maker.make_setting('beaker.session.type', 'file')
166 settings_maker.make_setting('beaker.session.type', 'file')
164 settings_maker.make_setting('beaker.session.data_dir', jn(default_cache_dir, 'session_data'))
167 settings_maker.make_setting('beaker.session.data_dir', jn(default_cache_dir, 'session_data'))
165
168
166 # cache_general
169 # cache_general
167 settings_maker.make_setting('rc_cache.cache_general.backend', 'dogpile.cache.rc.file_namespace')
170 settings_maker.make_setting('rc_cache.cache_general.backend', 'dogpile.cache.rc.file_namespace')
168 settings_maker.make_setting('rc_cache.cache_general.expiration_time', 60 * 60 * 12, parser='int')
171 settings_maker.make_setting('rc_cache.cache_general.expiration_time', 60 * 60 * 12, parser='int')
169 settings_maker.make_setting('rc_cache.cache_general.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_general.db'))
172 settings_maker.make_setting('rc_cache.cache_general.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_general.db'))
170
173
171 # cache_perms
174 # cache_perms
172 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
175 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
173 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60 * 60, parser='int')
176 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60 * 60, parser='int')
174 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_perms_db'))
177 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_perms_db'))
175
178
176 # cache_repo
179 # cache_repo
177 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
180 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
178 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60 * 60 * 24 * 30, parser='int')
181 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60 * 60 * 24 * 30, parser='int')
179 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_repo_db'))
182 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_repo_db'))
180
183
181 # cache_license
184 # cache_license
182 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
185 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
183 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 60 * 5, parser='int')
186 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 60 * 5, parser='int')
184 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_license_db'))
187 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', jn(default_cache_dir, 'rhodecode_cache_license_db'))
185
188
186 # cache_repo_longterm memory, 96H
189 # cache_repo_longterm memory, 96H
187 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
190 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
188 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
191 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
189 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
192 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
190
193
191 # sql_cache_short
194 # sql_cache_short
192 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
195 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
193 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
196 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
194 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
197 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
195
198
196 # archive_cache
199 # archive_cache
197 settings_maker.make_setting('archive_cache.locking.url', 'redis://redis:6379/1')
200 settings_maker.make_setting('archive_cache.locking.url', 'redis://redis:6379/1')
198 settings_maker.make_setting('archive_cache.backend.type', 'filesystem')
201 settings_maker.make_setting('archive_cache.backend.type', 'filesystem')
199
202
200 settings_maker.make_setting('archive_cache.filesystem.store_dir', jn(default_cache_dir, 'archive_cache'), default_when_empty=True,)
203 settings_maker.make_setting('archive_cache.filesystem.store_dir', jn(default_cache_dir, 'archive_cache'), default_when_empty=True,)
201 settings_maker.make_setting('archive_cache.filesystem.cache_shards', 8, parser='int')
204 settings_maker.make_setting('archive_cache.filesystem.cache_shards', 8, parser='int')
202 settings_maker.make_setting('archive_cache.filesystem.cache_size_gb', 10, parser='float')
205 settings_maker.make_setting('archive_cache.filesystem.cache_size_gb', 10, parser='float')
203 settings_maker.make_setting('archive_cache.filesystem.eviction_policy', 'least-recently-stored')
206 settings_maker.make_setting('archive_cache.filesystem.eviction_policy', 'least-recently-stored')
204
207
205 settings_maker.make_setting('archive_cache.filesystem.retry', False, parser='bool')
208 settings_maker.make_setting('archive_cache.filesystem.retry', False, parser='bool')
206 settings_maker.make_setting('archive_cache.filesystem.retry_backoff', 1, parser='int')
209 settings_maker.make_setting('archive_cache.filesystem.retry_backoff', 1, parser='int')
207 settings_maker.make_setting('archive_cache.filesystem.retry_attempts', 10, parser='int')
210 settings_maker.make_setting('archive_cache.filesystem.retry_attempts', 10, parser='int')
208
211
209 settings_maker.make_setting('archive_cache.objectstore.url', 'http://s3-minio:9000', default_when_empty=True,)
212 settings_maker.make_setting('archive_cache.objectstore.url', 'http://s3-minio:9000', default_when_empty=True,)
210 settings_maker.make_setting('archive_cache.objectstore.key', '')
213 settings_maker.make_setting('archive_cache.objectstore.key', '')
211 settings_maker.make_setting('archive_cache.objectstore.secret', '')
214 settings_maker.make_setting('archive_cache.objectstore.secret', '')
212 settings_maker.make_setting('archive_cache.objectstore.region', 'eu-central-1')
215 settings_maker.make_setting('archive_cache.objectstore.region', 'eu-central-1')
213 settings_maker.make_setting('archive_cache.objectstore.bucket', 'rhodecode-archive-cache', default_when_empty=True,)
216 settings_maker.make_setting('archive_cache.objectstore.bucket', 'rhodecode-archive-cache', default_when_empty=True,)
214 settings_maker.make_setting('archive_cache.objectstore.bucket_shards', 8, parser='int')
217 settings_maker.make_setting('archive_cache.objectstore.bucket_shards', 8, parser='int')
215
218
216 settings_maker.make_setting('archive_cache.objectstore.cache_size_gb', 10, parser='float')
219 settings_maker.make_setting('archive_cache.objectstore.cache_size_gb', 10, parser='float')
217 settings_maker.make_setting('archive_cache.objectstore.eviction_policy', 'least-recently-stored')
220 settings_maker.make_setting('archive_cache.objectstore.eviction_policy', 'least-recently-stored')
218
221
219 settings_maker.make_setting('archive_cache.objectstore.retry', False, parser='bool')
222 settings_maker.make_setting('archive_cache.objectstore.retry', False, parser='bool')
220 settings_maker.make_setting('archive_cache.objectstore.retry_backoff', 1, parser='int')
223 settings_maker.make_setting('archive_cache.objectstore.retry_backoff', 1, parser='int')
221 settings_maker.make_setting('archive_cache.objectstore.retry_attempts', 10, parser='int')
224 settings_maker.make_setting('archive_cache.objectstore.retry_attempts', 10, parser='int')
222
225
223 settings_maker.env_expand()
226 settings_maker.env_expand()
224
227
225 # configure instance id
228 # configure instance id
226 config_utils.set_instance_id(settings)
229 config_utils.set_instance_id(settings)
227
230
228 return settings
231 return settings
@@ -1,827 +1,835 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 """
19 """
20 Utilities library for RhodeCode
20 Utilities library for RhodeCode
21 """
21 """
22
22
23 import datetime
23 import datetime
24
24
25 import decorator
25 import decorator
26 import logging
26 import logging
27 import os
27 import os
28 import re
28 import re
29 import sys
29 import sys
30 import shutil
30 import shutil
31 import socket
31 import socket
32 import tempfile
32 import tempfile
33 import traceback
33 import traceback
34 import tarfile
34 import tarfile
35
35
36 from functools import wraps
36 from functools import wraps
37 from os.path import join as jn
37 from os.path import join as jn
38
38
39 import paste
39 import paste
40 import pkg_resources
40 import pkg_resources
41 from webhelpers2.text import collapse, strip_tags, convert_accented_entities, convert_misc_entities
41 from webhelpers2.text import collapse, strip_tags, convert_accented_entities, convert_misc_entities
42
42
43 from mako import exceptions
43 from mako import exceptions
44
44
45 from rhodecode import ConfigGet
45 from rhodecode.lib.hash_utils import sha256_safe, md5, sha1
46 from rhodecode.lib.hash_utils import sha256_safe, md5, sha1
46 from rhodecode.lib.type_utils import AttributeDict
47 from rhodecode.lib.type_utils import AttributeDict
47 from rhodecode.lib.str_utils import safe_bytes, safe_str
48 from rhodecode.lib.str_utils import safe_bytes, safe_str
48 from rhodecode.lib.vcs.backends.base import Config
49 from rhodecode.lib.vcs.backends.base import Config
49 from rhodecode.lib.vcs.exceptions import VCSError
50 from rhodecode.lib.vcs.exceptions import VCSError
50 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
51 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
51 from rhodecode.lib.ext_json import sjson as json
52 from rhodecode.lib.ext_json import sjson as json
52 from rhodecode.model import meta
53 from rhodecode.model import meta
53 from rhodecode.model.db import (
54 from rhodecode.model.db import (
54 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
55 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
55 from rhodecode.model.meta import Session
56 from rhodecode.model.meta import Session
56
57
57
58
58 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
59
60
60 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
61 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
61
62
62 # String which contains characters that are not allowed in slug names for
63 # String which contains characters that are not allowed in slug names for
63 # repositories or repository groups. It is properly escaped to use it in
64 # repositories or repository groups. It is properly escaped to use it in
64 # regular expressions.
65 # regular expressions.
65 SLUG_BAD_CHARS = re.escape(r'`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
66 SLUG_BAD_CHARS = re.escape(r'`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
66
67
67 # Regex that matches forbidden characters in repo/group slugs.
68 # Regex that matches forbidden characters in repo/group slugs.
68 SLUG_BAD_CHAR_RE = re.compile(r'[{}\x00-\x08\x0b-\x0c\x0e-\x1f]'.format(SLUG_BAD_CHARS))
69 SLUG_BAD_CHAR_RE = re.compile(r'[{}\x00-\x08\x0b-\x0c\x0e-\x1f]'.format(SLUG_BAD_CHARS))
69
70
70 # Regex that matches allowed characters in repo/group slugs.
71 # Regex that matches allowed characters in repo/group slugs.
71 SLUG_GOOD_CHAR_RE = re.compile(r'[^{}]'.format(SLUG_BAD_CHARS))
72 SLUG_GOOD_CHAR_RE = re.compile(r'[^{}]'.format(SLUG_BAD_CHARS))
72
73
73 # Regex that matches whole repo/group slugs.
74 # Regex that matches whole repo/group slugs.
74 SLUG_RE = re.compile(r'[^{}]+'.format(SLUG_BAD_CHARS))
75 SLUG_RE = re.compile(r'[^{}]+'.format(SLUG_BAD_CHARS))
75
76
76 _license_cache = None
77 _license_cache = None
77
78
78
79
79 def adopt_for_celery(func):
80 def adopt_for_celery(func):
80 """
81 """
81 Decorator designed to adopt hooks (from rhodecode.lib.hooks_base)
82 Decorator designed to adopt hooks (from rhodecode.lib.hooks_base)
82 for further usage as a celery tasks.
83 for further usage as a celery tasks.
83 """
84 """
84 @wraps(func)
85 @wraps(func)
85 def wrapper(extras):
86 def wrapper(extras):
86 extras = AttributeDict(extras)
87 extras = AttributeDict(extras)
87 try:
88 try:
88 # HooksResponse implements to_json method which must be used there.
89 # HooksResponse implements to_json method which must be used there.
89 return func(extras).to_json()
90 return func(extras).to_json()
90 except Exception as e:
91 except Exception as e:
91 return {'status': 128, 'exception': type(e).__name__, 'exception_args': e.args}
92 return {'status': 128, 'exception': type(e).__name__, 'exception_args': e.args}
92 return wrapper
93 return wrapper
93
94
94
95
95 def repo_name_slug(value):
96 def repo_name_slug(value):
96 """
97 """
97 Return slug of name of repository
98 Return slug of name of repository
98 This function is called on each creation/modification
99 This function is called on each creation/modification
99 of repository to prevent bad names in repo
100 of repository to prevent bad names in repo
100 """
101 """
101
102
102 replacement_char = '-'
103 replacement_char = '-'
103
104
104 slug = strip_tags(value)
105 slug = strip_tags(value)
105 slug = convert_accented_entities(slug)
106 slug = convert_accented_entities(slug)
106 slug = convert_misc_entities(slug)
107 slug = convert_misc_entities(slug)
107
108
108 slug = SLUG_BAD_CHAR_RE.sub('', slug)
109 slug = SLUG_BAD_CHAR_RE.sub('', slug)
109 slug = re.sub(r'[\s]+', '-', slug)
110 slug = re.sub(r'[\s]+', '-', slug)
110 slug = collapse(slug, replacement_char)
111 slug = collapse(slug, replacement_char)
111
112
112 return slug
113 return slug
113
114
114
115
115 #==============================================================================
116 #==============================================================================
116 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
117 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
117 #==============================================================================
118 #==============================================================================
118 def get_repo_slug(request):
119 def get_repo_slug(request):
119 _repo = ''
120 _repo = ''
120
121
121 if hasattr(request, 'db_repo_name'):
122 if hasattr(request, 'db_repo_name'):
122 # if our requests has set db reference use it for name, this
123 # if our requests has set db reference use it for name, this
123 # translates the example.com/_<id> into proper repo names
124 # translates the example.com/_<id> into proper repo names
124 _repo = request.db_repo_name
125 _repo = request.db_repo_name
125 elif getattr(request, 'matchdict', None):
126 elif getattr(request, 'matchdict', None):
126 # pyramid
127 # pyramid
127 _repo = request.matchdict.get('repo_name')
128 _repo = request.matchdict.get('repo_name')
128
129
129 if _repo:
130 if _repo:
130 _repo = _repo.rstrip('/')
131 _repo = _repo.rstrip('/')
131 return _repo
132 return _repo
132
133
133
134
134 def get_repo_group_slug(request):
135 def get_repo_group_slug(request):
135 _group = ''
136 _group = ''
136 if hasattr(request, 'db_repo_group'):
137 if hasattr(request, 'db_repo_group'):
137 # if our requests has set db reference use it for name, this
138 # if our requests has set db reference use it for name, this
138 # translates the example.com/_<id> into proper repo group names
139 # translates the example.com/_<id> into proper repo group names
139 _group = request.db_repo_group.group_name
140 _group = request.db_repo_group.group_name
140 elif getattr(request, 'matchdict', None):
141 elif getattr(request, 'matchdict', None):
141 # pyramid
142 # pyramid
142 _group = request.matchdict.get('repo_group_name')
143 _group = request.matchdict.get('repo_group_name')
143
144
144 if _group:
145 if _group:
145 _group = _group.rstrip('/')
146 _group = _group.rstrip('/')
146 return _group
147 return _group
147
148
148
149
149 def get_user_group_slug(request):
150 def get_user_group_slug(request):
150 _user_group = ''
151 _user_group = ''
151
152
152 if hasattr(request, 'db_user_group'):
153 if hasattr(request, 'db_user_group'):
153 _user_group = request.db_user_group.users_group_name
154 _user_group = request.db_user_group.users_group_name
154 elif getattr(request, 'matchdict', None):
155 elif getattr(request, 'matchdict', None):
155 # pyramid
156 # pyramid
156 _user_group = request.matchdict.get('user_group_id')
157 _user_group = request.matchdict.get('user_group_id')
157 _user_group_name = request.matchdict.get('user_group_name')
158 _user_group_name = request.matchdict.get('user_group_name')
158 try:
159 try:
159 if _user_group:
160 if _user_group:
160 _user_group = UserGroup.get(_user_group)
161 _user_group = UserGroup.get(_user_group)
161 elif _user_group_name:
162 elif _user_group_name:
162 _user_group = UserGroup.get_by_group_name(_user_group_name)
163 _user_group = UserGroup.get_by_group_name(_user_group_name)
163
164
164 if _user_group:
165 if _user_group:
165 _user_group = _user_group.users_group_name
166 _user_group = _user_group.users_group_name
166 except Exception:
167 except Exception:
167 log.exception('Failed to get user group by id and name')
168 log.exception('Failed to get user group by id and name')
168 # catch all failures here
169 # catch all failures here
169 return None
170 return None
170
171
171 return _user_group
172 return _user_group
172
173
173
174
174 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
175 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
175 """
176 """
176 Scans given path for repos and return (name,(type,path)) tuple
177 Scans given path for repos and return (name,(type,path)) tuple
177
178
178 :param path: path to scan for repositories
179 :param path: path to scan for repositories
179 :param recursive: recursive search and return names with subdirs in front
180 :param recursive: recursive search and return names with subdirs in front
180 """
181 """
181
182
182 # remove ending slash for better results
183 # remove ending slash for better results
183 path = path.rstrip(os.sep)
184 path = path.rstrip(os.sep)
184 log.debug('now scanning in %s location recursive:%s...', path, recursive)
185 log.debug('now scanning in %s location recursive:%s...', path, recursive)
185
186
186 def _get_repos(p):
187 def _get_repos(p):
187 dirpaths = get_dirpaths(p)
188 dirpaths = get_dirpaths(p)
188 if not _is_dir_writable(p):
189 if not _is_dir_writable(p):
189 log.warning('repo path without write access: %s', p)
190 log.warning('repo path without write access: %s', p)
190
191
191 for dirpath in dirpaths:
192 for dirpath in dirpaths:
192 if os.path.isfile(os.path.join(p, dirpath)):
193 if os.path.isfile(os.path.join(p, dirpath)):
193 continue
194 continue
194 cur_path = os.path.join(p, dirpath)
195 cur_path = os.path.join(p, dirpath)
195
196
196 # skip removed repos
197 # skip removed repos
197 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
198 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
198 continue
199 continue
199
200
200 #skip .<somethin> dirs
201 #skip .<somethin> dirs
201 if dirpath.startswith('.'):
202 if dirpath.startswith('.'):
202 continue
203 continue
203
204
204 try:
205 try:
205 scm_info = get_scm(cur_path)
206 scm_info = get_scm(cur_path)
206 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
207 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
207 except VCSError:
208 except VCSError:
208 if not recursive:
209 if not recursive:
209 continue
210 continue
210 #check if this dir containts other repos for recursive scan
211 #check if this dir containts other repos for recursive scan
211 rec_path = os.path.join(p, dirpath)
212 rec_path = os.path.join(p, dirpath)
212 if os.path.isdir(rec_path):
213 if os.path.isdir(rec_path):
213 yield from _get_repos(rec_path)
214 yield from _get_repos(rec_path)
214
215
215 return _get_repos(path)
216 return _get_repos(path)
216
217
217
218
218 def get_dirpaths(p: str) -> list:
219 def get_dirpaths(p: str) -> list:
219 try:
220 try:
220 # OS-independable way of checking if we have at least read-only
221 # OS-independable way of checking if we have at least read-only
221 # access or not.
222 # access or not.
222 dirpaths = os.listdir(p)
223 dirpaths = os.listdir(p)
223 except OSError:
224 except OSError:
224 log.warning('ignoring repo path without read access: %s', p)
225 log.warning('ignoring repo path without read access: %s', p)
225 return []
226 return []
226
227
227 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
228 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
228 # decode paths and suddenly returns unicode objects itself. The items it
229 # decode paths and suddenly returns unicode objects itself. The items it
229 # cannot decode are returned as strings and cause issues.
230 # cannot decode are returned as strings and cause issues.
230 #
231 #
231 # Those paths are ignored here until a solid solution for path handling has
232 # Those paths are ignored here until a solid solution for path handling has
232 # been built.
233 # been built.
233 expected_type = type(p)
234 expected_type = type(p)
234
235
235 def _has_correct_type(item):
236 def _has_correct_type(item):
236 if type(item) is not expected_type:
237 if type(item) is not expected_type:
237 log.error(
238 log.error(
238 "Ignoring path %s since it cannot be decoded into str.",
239 "Ignoring path %s since it cannot be decoded into str.",
239 # Using "repr" to make sure that we see the byte value in case
240 # Using "repr" to make sure that we see the byte value in case
240 # of support.
241 # of support.
241 repr(item))
242 repr(item))
242 return False
243 return False
243 return True
244 return True
244
245
245 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
246 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
246
247
247 return dirpaths
248 return dirpaths
248
249
249
250
250 def _is_dir_writable(path):
251 def _is_dir_writable(path):
251 """
252 """
252 Probe if `path` is writable.
253 Probe if `path` is writable.
253
254
254 Due to trouble on Cygwin / Windows, this is actually probing if it is
255 Due to trouble on Cygwin / Windows, this is actually probing if it is
255 possible to create a file inside of `path`, stat does not produce reliable
256 possible to create a file inside of `path`, stat does not produce reliable
256 results in this case.
257 results in this case.
257 """
258 """
258 try:
259 try:
259 with tempfile.TemporaryFile(dir=path):
260 with tempfile.TemporaryFile(dir=path):
260 pass
261 pass
261 except OSError:
262 except OSError:
262 return False
263 return False
263 return True
264 return True
264
265
265
266
266 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
267 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
267 """
268 """
268 Returns True if given path is a valid repository False otherwise.
269 Returns True if given path is a valid repository False otherwise.
269 If expect_scm param is given also, compare if given scm is the same
270 If expect_scm param is given also, compare if given scm is the same
270 as expected from scm parameter. If explicit_scm is given don't try to
271 as expected from scm parameter. If explicit_scm is given don't try to
271 detect the scm, just use the given one to check if repo is valid
272 detect the scm, just use the given one to check if repo is valid
272
273
273 :param repo_name:
274 :param repo_name:
274 :param base_path:
275 :param base_path:
275 :param expect_scm:
276 :param expect_scm:
276 :param explicit_scm:
277 :param explicit_scm:
277 :param config:
278 :param config:
278
279
279 :return True: if given path is a valid repository
280 :return True: if given path is a valid repository
280 """
281 """
281 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
282 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
282 log.debug('Checking if `%s` is a valid path for repository. '
283 log.debug('Checking if `%s` is a valid path for repository. '
283 'Explicit type: %s', repo_name, explicit_scm)
284 'Explicit type: %s', repo_name, explicit_scm)
284
285
285 try:
286 try:
286 if explicit_scm:
287 if explicit_scm:
287 detected_scms = [get_scm_backend(explicit_scm)(
288 detected_scms = [get_scm_backend(explicit_scm)(
288 full_path, config=config).alias]
289 full_path, config=config).alias]
289 else:
290 else:
290 detected_scms = get_scm(full_path)
291 detected_scms = get_scm(full_path)
291
292
292 if expect_scm:
293 if expect_scm:
293 return detected_scms[0] == expect_scm
294 return detected_scms[0] == expect_scm
294 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
295 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
295 return True
296 return True
296 except VCSError:
297 except VCSError:
297 log.debug('path: %s is not a valid repo !', full_path)
298 log.debug('path: %s is not a valid repo !', full_path)
298 return False
299 return False
299
300
300
301
301 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
302 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
302 """
303 """
303 Returns True if a given path is a repository group, False otherwise
304 Returns True if a given path is a repository group, False otherwise
304
305
305 :param repo_group_name:
306 :param repo_group_name:
306 :param base_path:
307 :param base_path:
307 """
308 """
308 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
309 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
309 log.debug('Checking if `%s` is a valid path for repository group',
310 log.debug('Checking if `%s` is a valid path for repository group',
310 repo_group_name)
311 repo_group_name)
311
312
312 # check if it's not a repo
313 # check if it's not a repo
313 if is_valid_repo(repo_group_name, base_path):
314 if is_valid_repo(repo_group_name, base_path):
314 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
315 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
315 return False
316 return False
316
317
317 try:
318 try:
318 # we need to check bare git repos at higher level
319 # we need to check bare git repos at higher level
319 # since we might match branches/hooks/info/objects or possible
320 # since we might match branches/hooks/info/objects or possible
320 # other things inside bare git repo
321 # other things inside bare git repo
321 maybe_repo = os.path.dirname(full_path)
322 maybe_repo = os.path.dirname(full_path)
322 if maybe_repo == base_path:
323 if maybe_repo == base_path:
323 # skip root level repo check; we know root location CANNOT BE a repo group
324 # skip root level repo check; we know root location CANNOT BE a repo group
324 return False
325 return False
325
326
326 scm_ = get_scm(maybe_repo)
327 scm_ = get_scm(maybe_repo)
327 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
328 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
328 return False
329 return False
329 except VCSError:
330 except VCSError:
330 pass
331 pass
331
332
332 # check if it's a valid path
333 # check if it's a valid path
333 if skip_path_check or os.path.isdir(full_path):
334 if skip_path_check or os.path.isdir(full_path):
334 log.debug('path: %s is a valid repo group !', full_path)
335 log.debug('path: %s is a valid repo group !', full_path)
335 return True
336 return True
336
337
337 log.debug('path: %s is not a valid repo group !', full_path)
338 log.debug('path: %s is not a valid repo group !', full_path)
338 return False
339 return False
339
340
340
341
341 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
342 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
342 while True:
343 while True:
343 ok = input(prompt)
344 ok = input(prompt)
344 if ok.lower() in ('y', 'ye', 'yes'):
345 if ok.lower() in ('y', 'ye', 'yes'):
345 return True
346 return True
346 if ok.lower() in ('n', 'no', 'nop', 'nope'):
347 if ok.lower() in ('n', 'no', 'nop', 'nope'):
347 return False
348 return False
348 retries = retries - 1
349 retries = retries - 1
349 if retries < 0:
350 if retries < 0:
350 raise OSError
351 raise OSError
351 print(complaint)
352 print(complaint)
352
353
353 # propagated from mercurial documentation
354 # propagated from mercurial documentation
354 ui_sections = [
355 ui_sections = [
355 'alias', 'auth',
356 'alias', 'auth',
356 'decode/encode', 'defaults',
357 'decode/encode', 'defaults',
357 'diff', 'email',
358 'diff', 'email',
358 'extensions', 'format',
359 'extensions', 'format',
359 'merge-patterns', 'merge-tools',
360 'merge-patterns', 'merge-tools',
360 'hooks', 'http_proxy',
361 'hooks', 'http_proxy',
361 'smtp', 'patch',
362 'smtp', 'patch',
362 'paths', 'profiling',
363 'paths', 'profiling',
363 'server', 'trusted',
364 'server', 'trusted',
364 'ui', 'web', ]
365 'ui', 'web', ]
365
366
366
367
367 def config_data_from_db(clear_session=True, repo=None):
368 def prepare_config_data(clear_session=True, repo=None):
368 """
369 """
369 Read the configuration data from the database and return configuration
370 Read the configuration data from the database, *.ini files and return configuration
370 tuples.
371 tuples.
371 """
372 """
372 from rhodecode.model.settings import VcsSettingsModel
373 from rhodecode.model.settings import VcsSettingsModel
373
374
374 config = []
375 config = []
375
376
376 sa = meta.Session()
377 sa = meta.Session()
377 settings_model = VcsSettingsModel(repo=repo, sa=sa)
378 settings_model = VcsSettingsModel(repo=repo, sa=sa)
378
379
379 ui_settings = settings_model.get_ui_settings()
380 ui_settings = settings_model.get_ui_settings()
380
381
381 ui_data = []
382 ui_data = []
382 for setting in ui_settings:
383 for setting in ui_settings:
384 # Todo: remove this section once transition to *.ini files will be completed
385 if setting.section in ('largefiles', 'vcs_git_lfs'):
386 if setting.key != 'enabled':
387 continue
383 if setting.active:
388 if setting.active:
384 ui_data.append((setting.section, setting.key, setting.value))
389 ui_data.append((setting.section, setting.key, setting.value))
385 config.append((
390 config.append((
386 safe_str(setting.section), safe_str(setting.key),
391 safe_str(setting.section), safe_str(setting.key),
387 safe_str(setting.value)))
392 safe_str(setting.value)))
388 if setting.key == 'push_ssl':
393 if setting.key == 'push_ssl':
389 # force set push_ssl requirement to False, rhodecode
394 # force set push_ssl requirement to False, rhodecode
390 # handles that
395 # handles that
391 config.append((
396 config.append((
392 safe_str(setting.section), safe_str(setting.key), False))
397 safe_str(setting.section), safe_str(setting.key), False))
398 config_getter = ConfigGet()
399 config.append(('vcs_git_lfs', 'store_location', config_getter.get_str('vcs.git.lfs.storage_location')))
400 config.append(('largefiles', 'usercache', config_getter.get_str('vcs.hg.largefiles.storage_location')))
393 log.debug(
401 log.debug(
394 'settings ui from db@repo[%s]: %s',
402 'settings ui from db@repo[%s]: %s',
395 repo,
403 repo,
396 ','.join(['[{}] {}={}'.format(*s) for s in ui_data]))
404 ','.join(['[{}] {}={}'.format(*s) for s in ui_data]))
397 if clear_session:
405 if clear_session:
398 meta.Session.remove()
406 meta.Session.remove()
399
407
400 # TODO: mikhail: probably it makes no sense to re-read hooks information.
408 # TODO: mikhail: probably it makes no sense to re-read hooks information.
401 # It's already there and activated/deactivated
409 # It's already there and activated/deactivated
402 skip_entries = []
410 skip_entries = []
403 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
411 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
404 if 'pull' not in enabled_hook_classes:
412 if 'pull' not in enabled_hook_classes:
405 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
413 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
406 if 'push' not in enabled_hook_classes:
414 if 'push' not in enabled_hook_classes:
407 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
415 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
408 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
416 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
409 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
417 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
410
418
411 config = [entry for entry in config if entry[:2] not in skip_entries]
419 config = [entry for entry in config if entry[:2] not in skip_entries]
412
420
413 return config
421 return config
414
422
415
423
416 def make_db_config(clear_session=True, repo=None):
424 def make_db_config(clear_session=True, repo=None):
417 """
425 """
418 Create a :class:`Config` instance based on the values in the database.
426 Create a :class:`Config` instance based on the values in the database.
419 """
427 """
420 config = Config()
428 config = Config()
421 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
429 config_data = prepare_config_data(clear_session=clear_session, repo=repo)
422 for section, option, value in config_data:
430 for section, option, value in config_data:
423 config.set(section, option, value)
431 config.set(section, option, value)
424 return config
432 return config
425
433
426
434
427 def get_enabled_hook_classes(ui_settings):
435 def get_enabled_hook_classes(ui_settings):
428 """
436 """
429 Return the enabled hook classes.
437 Return the enabled hook classes.
430
438
431 :param ui_settings: List of ui_settings as returned
439 :param ui_settings: List of ui_settings as returned
432 by :meth:`VcsSettingsModel.get_ui_settings`
440 by :meth:`VcsSettingsModel.get_ui_settings`
433
441
434 :return: a list with the enabled hook classes. The order is not guaranteed.
442 :return: a list with the enabled hook classes. The order is not guaranteed.
435 :rtype: list
443 :rtype: list
436 """
444 """
437 enabled_hooks = []
445 enabled_hooks = []
438 active_hook_keys = [
446 active_hook_keys = [
439 key for section, key, value, active in ui_settings
447 key for section, key, value, active in ui_settings
440 if section == 'hooks' and active]
448 if section == 'hooks' and active]
441
449
442 hook_names = {
450 hook_names = {
443 RhodeCodeUi.HOOK_PUSH: 'push',
451 RhodeCodeUi.HOOK_PUSH: 'push',
444 RhodeCodeUi.HOOK_PULL: 'pull',
452 RhodeCodeUi.HOOK_PULL: 'pull',
445 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
453 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
446 }
454 }
447
455
448 for key in active_hook_keys:
456 for key in active_hook_keys:
449 hook = hook_names.get(key)
457 hook = hook_names.get(key)
450 if hook:
458 if hook:
451 enabled_hooks.append(hook)
459 enabled_hooks.append(hook)
452
460
453 return enabled_hooks
461 return enabled_hooks
454
462
455
463
456 def set_rhodecode_config(config):
464 def set_rhodecode_config(config):
457 """
465 """
458 Updates pyramid config with new settings from database
466 Updates pyramid config with new settings from database
459
467
460 :param config:
468 :param config:
461 """
469 """
462 from rhodecode.model.settings import SettingsModel
470 from rhodecode.model.settings import SettingsModel
463 app_settings = SettingsModel().get_all_settings()
471 app_settings = SettingsModel().get_all_settings()
464
472
465 for k, v in list(app_settings.items()):
473 for k, v in list(app_settings.items()):
466 config[k] = v
474 config[k] = v
467
475
468
476
469 def get_rhodecode_realm():
477 def get_rhodecode_realm():
470 """
478 """
471 Return the rhodecode realm from database.
479 Return the rhodecode realm from database.
472 """
480 """
473 from rhodecode.model.settings import SettingsModel
481 from rhodecode.model.settings import SettingsModel
474 realm = SettingsModel().get_setting_by_name('realm')
482 realm = SettingsModel().get_setting_by_name('realm')
475 return safe_str(realm.app_settings_value)
483 return safe_str(realm.app_settings_value)
476
484
477
485
478 def get_rhodecode_repo_store_path():
486 def get_rhodecode_repo_store_path():
479 """
487 """
480 Returns the base path. The base path is the filesystem path which points
488 Returns the base path. The base path is the filesystem path which points
481 to the repository store.
489 to the repository store.
482 """
490 """
483
491
484 import rhodecode
492 import rhodecode
485 return rhodecode.CONFIG['repo_store.path']
493 return rhodecode.CONFIG['repo_store.path']
486
494
487
495
488 def map_groups(path):
496 def map_groups(path):
489 """
497 """
490 Given a full path to a repository, create all nested groups that this
498 Given a full path to a repository, create all nested groups that this
491 repo is inside. This function creates parent-child relationships between
499 repo is inside. This function creates parent-child relationships between
492 groups and creates default perms for all new groups.
500 groups and creates default perms for all new groups.
493
501
494 :param paths: full path to repository
502 :param paths: full path to repository
495 """
503 """
496 from rhodecode.model.repo_group import RepoGroupModel
504 from rhodecode.model.repo_group import RepoGroupModel
497 sa = meta.Session()
505 sa = meta.Session()
498 groups = path.split(Repository.NAME_SEP)
506 groups = path.split(Repository.NAME_SEP)
499 parent = None
507 parent = None
500 group = None
508 group = None
501
509
502 # last element is repo in nested groups structure
510 # last element is repo in nested groups structure
503 groups = groups[:-1]
511 groups = groups[:-1]
504 rgm = RepoGroupModel(sa)
512 rgm = RepoGroupModel(sa)
505 owner = User.get_first_super_admin()
513 owner = User.get_first_super_admin()
506 for lvl, group_name in enumerate(groups):
514 for lvl, group_name in enumerate(groups):
507 group_name = '/'.join(groups[:lvl] + [group_name])
515 group_name = '/'.join(groups[:lvl] + [group_name])
508 group = RepoGroup.get_by_group_name(group_name)
516 group = RepoGroup.get_by_group_name(group_name)
509 desc = '%s group' % group_name
517 desc = '%s group' % group_name
510
518
511 # skip folders that are now removed repos
519 # skip folders that are now removed repos
512 if REMOVED_REPO_PAT.match(group_name):
520 if REMOVED_REPO_PAT.match(group_name):
513 break
521 break
514
522
515 if group is None:
523 if group is None:
516 log.debug('creating group level: %s group_name: %s',
524 log.debug('creating group level: %s group_name: %s',
517 lvl, group_name)
525 lvl, group_name)
518 group = RepoGroup(group_name, parent)
526 group = RepoGroup(group_name, parent)
519 group.group_description = desc
527 group.group_description = desc
520 group.user = owner
528 group.user = owner
521 sa.add(group)
529 sa.add(group)
522 perm_obj = rgm._create_default_perms(group)
530 perm_obj = rgm._create_default_perms(group)
523 sa.add(perm_obj)
531 sa.add(perm_obj)
524 sa.flush()
532 sa.flush()
525
533
526 parent = group
534 parent = group
527 return group
535 return group
528
536
529
537
530 def repo2db_mapper(initial_repo_list, remove_obsolete=False, force_hooks_rebuild=False):
538 def repo2db_mapper(initial_repo_list, remove_obsolete=False, force_hooks_rebuild=False):
531 """
539 """
532 maps all repos given in initial_repo_list, non existing repositories
540 maps all repos given in initial_repo_list, non existing repositories
533 are created, if remove_obsolete is True it also checks for db entries
541 are created, if remove_obsolete is True it also checks for db entries
534 that are not in initial_repo_list and removes them.
542 that are not in initial_repo_list and removes them.
535
543
536 :param initial_repo_list: list of repositories found by scanning methods
544 :param initial_repo_list: list of repositories found by scanning methods
537 :param remove_obsolete: check for obsolete entries in database
545 :param remove_obsolete: check for obsolete entries in database
538 """
546 """
539 from rhodecode.model.repo import RepoModel
547 from rhodecode.model.repo import RepoModel
540 from rhodecode.model.repo_group import RepoGroupModel
548 from rhodecode.model.repo_group import RepoGroupModel
541 from rhodecode.model.settings import SettingsModel
549 from rhodecode.model.settings import SettingsModel
542
550
543 sa = meta.Session()
551 sa = meta.Session()
544 repo_model = RepoModel()
552 repo_model = RepoModel()
545 user = User.get_first_super_admin()
553 user = User.get_first_super_admin()
546 added = []
554 added = []
547
555
548 # creation defaults
556 # creation defaults
549 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
557 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
550 enable_statistics = defs.get('repo_enable_statistics')
558 enable_statistics = defs.get('repo_enable_statistics')
551 enable_locking = defs.get('repo_enable_locking')
559 enable_locking = defs.get('repo_enable_locking')
552 enable_downloads = defs.get('repo_enable_downloads')
560 enable_downloads = defs.get('repo_enable_downloads')
553 private = defs.get('repo_private')
561 private = defs.get('repo_private')
554
562
555 for name, repo in list(initial_repo_list.items()):
563 for name, repo in list(initial_repo_list.items()):
556 group = map_groups(name)
564 group = map_groups(name)
557 str_name = safe_str(name)
565 str_name = safe_str(name)
558 db_repo = repo_model.get_by_repo_name(str_name)
566 db_repo = repo_model.get_by_repo_name(str_name)
559
567
560 # found repo that is on filesystem not in RhodeCode database
568 # found repo that is on filesystem not in RhodeCode database
561 if not db_repo:
569 if not db_repo:
562 log.info('repository `%s` not found in the database, creating now', name)
570 log.info('repository `%s` not found in the database, creating now', name)
563 added.append(name)
571 added.append(name)
564 desc = (repo.description
572 desc = (repo.description
565 if repo.description != 'unknown'
573 if repo.description != 'unknown'
566 else '%s repository' % name)
574 else '%s repository' % name)
567
575
568 db_repo = repo_model._create_repo(
576 db_repo = repo_model._create_repo(
569 repo_name=name,
577 repo_name=name,
570 repo_type=repo.alias,
578 repo_type=repo.alias,
571 description=desc,
579 description=desc,
572 repo_group=getattr(group, 'group_id', None),
580 repo_group=getattr(group, 'group_id', None),
573 owner=user,
581 owner=user,
574 enable_locking=enable_locking,
582 enable_locking=enable_locking,
575 enable_downloads=enable_downloads,
583 enable_downloads=enable_downloads,
576 enable_statistics=enable_statistics,
584 enable_statistics=enable_statistics,
577 private=private,
585 private=private,
578 state=Repository.STATE_CREATED
586 state=Repository.STATE_CREATED
579 )
587 )
580 sa.commit()
588 sa.commit()
581 # we added that repo just now, and make sure we updated server info
589 # we added that repo just now, and make sure we updated server info
582 if db_repo.repo_type == 'git':
590 if db_repo.repo_type == 'git':
583 git_repo = db_repo.scm_instance()
591 git_repo = db_repo.scm_instance()
584 # update repository server-info
592 # update repository server-info
585 log.debug('Running update server info')
593 log.debug('Running update server info')
586 git_repo._update_server_info(force=True)
594 git_repo._update_server_info(force=True)
587
595
588 db_repo.update_commit_cache(recursive=False)
596 db_repo.update_commit_cache(recursive=False)
589
597
590 config = db_repo._config
598 config = db_repo._config
591 config.set('extensions', 'largefiles', '')
599 config.set('extensions', 'largefiles', '')
592 repo = db_repo.scm_instance(config=config)
600 repo = db_repo.scm_instance(config=config)
593 repo.install_hooks(force=force_hooks_rebuild)
601 repo.install_hooks(force=force_hooks_rebuild)
594
602
595 removed = []
603 removed = []
596 if remove_obsolete:
604 if remove_obsolete:
597 # remove from database those repositories that are not in the filesystem
605 # remove from database those repositories that are not in the filesystem
598 for repo in sa.query(Repository).all():
606 for repo in sa.query(Repository).all():
599 if repo.repo_name not in list(initial_repo_list.keys()):
607 if repo.repo_name not in list(initial_repo_list.keys()):
600 log.debug("Removing non-existing repository found in db `%s`",
608 log.debug("Removing non-existing repository found in db `%s`",
601 repo.repo_name)
609 repo.repo_name)
602 try:
610 try:
603 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
611 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
604 sa.commit()
612 sa.commit()
605 removed.append(repo.repo_name)
613 removed.append(repo.repo_name)
606 except Exception:
614 except Exception:
607 # don't hold further removals on error
615 # don't hold further removals on error
608 log.error(traceback.format_exc())
616 log.error(traceback.format_exc())
609 sa.rollback()
617 sa.rollback()
610
618
611 def splitter(full_repo_name):
619 def splitter(full_repo_name):
612 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
620 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
613 gr_name = None
621 gr_name = None
614 if len(_parts) == 2:
622 if len(_parts) == 2:
615 gr_name = _parts[0]
623 gr_name = _parts[0]
616 return gr_name
624 return gr_name
617
625
618 initial_repo_group_list = [splitter(x) for x in
626 initial_repo_group_list = [splitter(x) for x in
619 list(initial_repo_list.keys()) if splitter(x)]
627 list(initial_repo_list.keys()) if splitter(x)]
620
628
621 # remove from database those repository groups that are not in the
629 # remove from database those repository groups that are not in the
622 # filesystem due to parent child relationships we need to delete them
630 # filesystem due to parent child relationships we need to delete them
623 # in a specific order of most nested first
631 # in a specific order of most nested first
624 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
632 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
625 def nested_sort(gr):
633 def nested_sort(gr):
626 return len(gr.split('/'))
634 return len(gr.split('/'))
627 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
635 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
628 if group_name not in initial_repo_group_list:
636 if group_name not in initial_repo_group_list:
629 repo_group = RepoGroup.get_by_group_name(group_name)
637 repo_group = RepoGroup.get_by_group_name(group_name)
630 if (repo_group.children.all() or
638 if (repo_group.children.all() or
631 not RepoGroupModel().check_exist_filesystem(
639 not RepoGroupModel().check_exist_filesystem(
632 group_name=group_name, exc_on_failure=False)):
640 group_name=group_name, exc_on_failure=False)):
633 continue
641 continue
634
642
635 log.info(
643 log.info(
636 'Removing non-existing repository group found in db `%s`',
644 'Removing non-existing repository group found in db `%s`',
637 group_name)
645 group_name)
638 try:
646 try:
639 RepoGroupModel(sa).delete(group_name, fs_remove=False)
647 RepoGroupModel(sa).delete(group_name, fs_remove=False)
640 sa.commit()
648 sa.commit()
641 removed.append(group_name)
649 removed.append(group_name)
642 except Exception:
650 except Exception:
643 # don't hold further removals on error
651 # don't hold further removals on error
644 log.exception(
652 log.exception(
645 'Unable to remove repository group `%s`',
653 'Unable to remove repository group `%s`',
646 group_name)
654 group_name)
647 sa.rollback()
655 sa.rollback()
648 raise
656 raise
649
657
650 return added, removed
658 return added, removed
651
659
652
660
653 def load_rcextensions(root_path):
661 def load_rcextensions(root_path):
654 import rhodecode
662 import rhodecode
655 from rhodecode.config import conf
663 from rhodecode.config import conf
656
664
657 path = os.path.join(root_path)
665 path = os.path.join(root_path)
658 sys.path.append(path)
666 sys.path.append(path)
659
667
660 try:
668 try:
661 rcextensions = __import__('rcextensions')
669 rcextensions = __import__('rcextensions')
662 except ImportError:
670 except ImportError:
663 if os.path.isdir(os.path.join(path, 'rcextensions')):
671 if os.path.isdir(os.path.join(path, 'rcextensions')):
664 log.warning('Unable to load rcextensions from %s', path)
672 log.warning('Unable to load rcextensions from %s', path)
665 rcextensions = None
673 rcextensions = None
666
674
667 if rcextensions:
675 if rcextensions:
668 log.info('Loaded rcextensions from %s...', rcextensions)
676 log.info('Loaded rcextensions from %s...', rcextensions)
669 rhodecode.EXTENSIONS = rcextensions
677 rhodecode.EXTENSIONS = rcextensions
670
678
671 # Additional mappings that are not present in the pygments lexers
679 # Additional mappings that are not present in the pygments lexers
672 conf.LANGUAGES_EXTENSIONS_MAP.update(
680 conf.LANGUAGES_EXTENSIONS_MAP.update(
673 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
681 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
674
682
675
683
676 def get_custom_lexer(extension):
684 def get_custom_lexer(extension):
677 """
685 """
678 returns a custom lexer if it is defined in rcextensions module, or None
686 returns a custom lexer if it is defined in rcextensions module, or None
679 if there's no custom lexer defined
687 if there's no custom lexer defined
680 """
688 """
681 import rhodecode
689 import rhodecode
682 from pygments import lexers
690 from pygments import lexers
683
691
684 # custom override made by RhodeCode
692 # custom override made by RhodeCode
685 if extension in ['mako']:
693 if extension in ['mako']:
686 return lexers.get_lexer_by_name('html+mako')
694 return lexers.get_lexer_by_name('html+mako')
687
695
688 # check if we didn't define this extension as other lexer
696 # check if we didn't define this extension as other lexer
689 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
697 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
690 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
698 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
691 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
699 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
692 return lexers.get_lexer_by_name(_lexer_name)
700 return lexers.get_lexer_by_name(_lexer_name)
693
701
694
702
695 #==============================================================================
703 #==============================================================================
696 # TEST FUNCTIONS AND CREATORS
704 # TEST FUNCTIONS AND CREATORS
697 #==============================================================================
705 #==============================================================================
698 def create_test_index(repo_location, config):
706 def create_test_index(repo_location, config):
699 """
707 """
700 Makes default test index.
708 Makes default test index.
701 """
709 """
702 try:
710 try:
703 import rc_testdata
711 import rc_testdata
704 except ImportError:
712 except ImportError:
705 raise ImportError('Failed to import rc_testdata, '
713 raise ImportError('Failed to import rc_testdata, '
706 'please make sure this package is installed from requirements_test.txt')
714 'please make sure this package is installed from requirements_test.txt')
707 rc_testdata.extract_search_index(
715 rc_testdata.extract_search_index(
708 'vcs_search_index', os.path.dirname(config['search.location']))
716 'vcs_search_index', os.path.dirname(config['search.location']))
709
717
710
718
711 def create_test_directory(test_path):
719 def create_test_directory(test_path):
712 """
720 """
713 Create test directory if it doesn't exist.
721 Create test directory if it doesn't exist.
714 """
722 """
715 if not os.path.isdir(test_path):
723 if not os.path.isdir(test_path):
716 log.debug('Creating testdir %s', test_path)
724 log.debug('Creating testdir %s', test_path)
717 os.makedirs(test_path)
725 os.makedirs(test_path)
718
726
719
727
720 def create_test_database(test_path, config):
728 def create_test_database(test_path, config):
721 """
729 """
722 Makes a fresh database.
730 Makes a fresh database.
723 """
731 """
724 from rhodecode.lib.db_manage import DbManage
732 from rhodecode.lib.db_manage import DbManage
725 from rhodecode.lib.utils2 import get_encryption_key
733 from rhodecode.lib.utils2 import get_encryption_key
726
734
727 # PART ONE create db
735 # PART ONE create db
728 dbconf = config['sqlalchemy.db1.url']
736 dbconf = config['sqlalchemy.db1.url']
729 enc_key = get_encryption_key(config)
737 enc_key = get_encryption_key(config)
730
738
731 log.debug('making test db %s', dbconf)
739 log.debug('making test db %s', dbconf)
732
740
733 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
741 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
734 tests=True, cli_args={'force_ask': True}, enc_key=enc_key)
742 tests=True, cli_args={'force_ask': True}, enc_key=enc_key)
735 dbmanage.create_tables(override=True)
743 dbmanage.create_tables(override=True)
736 dbmanage.set_db_version()
744 dbmanage.set_db_version()
737 # for tests dynamically set new root paths based on generated content
745 # for tests dynamically set new root paths based on generated content
738 dbmanage.create_settings(dbmanage.config_prompt(test_path))
746 dbmanage.create_settings(dbmanage.config_prompt(test_path))
739 dbmanage.create_default_user()
747 dbmanage.create_default_user()
740 dbmanage.create_test_admin_and_users()
748 dbmanage.create_test_admin_and_users()
741 dbmanage.create_permissions()
749 dbmanage.create_permissions()
742 dbmanage.populate_default_permissions()
750 dbmanage.populate_default_permissions()
743 Session().commit()
751 Session().commit()
744
752
745
753
746 def create_test_repositories(test_path, config):
754 def create_test_repositories(test_path, config):
747 """
755 """
748 Creates test repositories in the temporary directory. Repositories are
756 Creates test repositories in the temporary directory. Repositories are
749 extracted from archives within the rc_testdata package.
757 extracted from archives within the rc_testdata package.
750 """
758 """
751 import rc_testdata
759 import rc_testdata
752 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
760 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
753
761
754 log.debug('making test vcs repositories')
762 log.debug('making test vcs repositories')
755
763
756 idx_path = config['search.location']
764 idx_path = config['search.location']
757 data_path = config['cache_dir']
765 data_path = config['cache_dir']
758
766
759 # clean index and data
767 # clean index and data
760 if idx_path and os.path.exists(idx_path):
768 if idx_path and os.path.exists(idx_path):
761 log.debug('remove %s', idx_path)
769 log.debug('remove %s', idx_path)
762 shutil.rmtree(idx_path)
770 shutil.rmtree(idx_path)
763
771
764 if data_path and os.path.exists(data_path):
772 if data_path and os.path.exists(data_path):
765 log.debug('remove %s', data_path)
773 log.debug('remove %s', data_path)
766 shutil.rmtree(data_path)
774 shutil.rmtree(data_path)
767
775
768 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
776 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
769 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
777 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
770
778
771 # Note: Subversion is in the process of being integrated with the system,
779 # Note: Subversion is in the process of being integrated with the system,
772 # until we have a properly packed version of the test svn repository, this
780 # until we have a properly packed version of the test svn repository, this
773 # tries to copy over the repo from a package "rc_testdata"
781 # tries to copy over the repo from a package "rc_testdata"
774 svn_repo_path = rc_testdata.get_svn_repo_archive()
782 svn_repo_path = rc_testdata.get_svn_repo_archive()
775 with tarfile.open(svn_repo_path) as tar:
783 with tarfile.open(svn_repo_path) as tar:
776 tar.extractall(jn(test_path, SVN_REPO))
784 tar.extractall(jn(test_path, SVN_REPO))
777
785
778
786
779 def password_changed(auth_user, session):
787 def password_changed(auth_user, session):
780 # Never report password change in case of default user or anonymous user.
788 # Never report password change in case of default user or anonymous user.
781 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
789 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
782 return False
790 return False
783
791
784 password_hash = md5(safe_bytes(auth_user.password)) if auth_user.password else None
792 password_hash = md5(safe_bytes(auth_user.password)) if auth_user.password else None
785 rhodecode_user = session.get('rhodecode_user', {})
793 rhodecode_user = session.get('rhodecode_user', {})
786 session_password_hash = rhodecode_user.get('password', '')
794 session_password_hash = rhodecode_user.get('password', '')
787 return password_hash != session_password_hash
795 return password_hash != session_password_hash
788
796
789
797
790 def read_opensource_licenses():
798 def read_opensource_licenses():
791 global _license_cache
799 global _license_cache
792
800
793 if not _license_cache:
801 if not _license_cache:
794 licenses = pkg_resources.resource_string(
802 licenses = pkg_resources.resource_string(
795 'rhodecode', 'config/licenses.json')
803 'rhodecode', 'config/licenses.json')
796 _license_cache = json.loads(licenses)
804 _license_cache = json.loads(licenses)
797
805
798 return _license_cache
806 return _license_cache
799
807
800
808
801 def generate_platform_uuid():
809 def generate_platform_uuid():
802 """
810 """
803 Generates platform UUID based on it's name
811 Generates platform UUID based on it's name
804 """
812 """
805 import platform
813 import platform
806
814
807 try:
815 try:
808 uuid_list = [platform.platform()]
816 uuid_list = [platform.platform()]
809 return sha256_safe(':'.join(uuid_list))
817 return sha256_safe(':'.join(uuid_list))
810 except Exception as e:
818 except Exception as e:
811 log.error('Failed to generate host uuid: %s', e)
819 log.error('Failed to generate host uuid: %s', e)
812 return 'UNDEFINED'
820 return 'UNDEFINED'
813
821
814
822
815 def send_test_email(recipients, email_body='TEST EMAIL'):
823 def send_test_email(recipients, email_body='TEST EMAIL'):
816 """
824 """
817 Simple code for generating test emails.
825 Simple code for generating test emails.
818 Usage::
826 Usage::
819
827
820 from rhodecode.lib import utils
828 from rhodecode.lib import utils
821 utils.send_test_email()
829 utils.send_test_email()
822 """
830 """
823 from rhodecode.lib.celerylib import tasks, run_task
831 from rhodecode.lib.celerylib import tasks, run_task
824
832
825 email_body = email_body_plaintext = email_body
833 email_body = email_body_plaintext = email_body
826 subject = f'SUBJECT FROM: {socket.gethostname()}'
834 subject = f'SUBJECT FROM: {socket.gethostname()}'
827 tasks.send_email(recipients, subject, email_body_plaintext, email_body)
835 tasks.send_email(recipients, subject, email_body_plaintext, email_body)
@@ -1,669 +1,663 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 """
19 """
20 this is forms validation classes
20 this is forms validation classes
21 http://formencode.org/module-formencode.validators.html
21 http://formencode.org/module-formencode.validators.html
22 for list off all availible validators
22 for list off all availible validators
23
23
24 we can create our own validators
24 we can create our own validators
25
25
26 The table below outlines the options which can be used in a schema in addition to the validators themselves
26 The table below outlines the options which can be used in a schema in addition to the validators themselves
27 pre_validators [] These validators will be applied before the schema
27 pre_validators [] These validators will be applied before the schema
28 chained_validators [] These validators will be applied after the schema
28 chained_validators [] These validators will be applied after the schema
29 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
29 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
30 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
30 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
31 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
31 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
32 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
32 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
33
33
34
34
35 <name> = formencode.validators.<name of validator>
35 <name> = formencode.validators.<name of validator>
36 <name> must equal form name
36 <name> must equal form name
37 list=[1,2,3,4,5]
37 list=[1,2,3,4,5]
38 for SELECT use formencode.All(OneOf(list), Int())
38 for SELECT use formencode.All(OneOf(list), Int())
39
39
40 """
40 """
41
41
42 import deform
42 import deform
43 import logging
43 import logging
44 import formencode
44 import formencode
45
45
46 from pkg_resources import resource_filename
46 from pkg_resources import resource_filename
47 from formencode import All, Pipe
47 from formencode import All, Pipe
48
48
49 from pyramid.threadlocal import get_current_request
49 from pyramid.threadlocal import get_current_request
50
50
51 from rhodecode import BACKENDS
51 from rhodecode import BACKENDS
52 from rhodecode.lib import helpers
52 from rhodecode.lib import helpers
53 from rhodecode.model import validators as v
53 from rhodecode.model import validators as v
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 deform_templates = resource_filename('deform', 'templates')
58 deform_templates = resource_filename('deform', 'templates')
59 rhodecode_templates = resource_filename('rhodecode', 'templates/forms')
59 rhodecode_templates = resource_filename('rhodecode', 'templates/forms')
60 search_path = (rhodecode_templates, deform_templates)
60 search_path = (rhodecode_templates, deform_templates)
61
61
62
62
63 class RhodecodeFormZPTRendererFactory(deform.ZPTRendererFactory):
63 class RhodecodeFormZPTRendererFactory(deform.ZPTRendererFactory):
64 """ Subclass of ZPTRendererFactory to add rhodecode context variables """
64 """ Subclass of ZPTRendererFactory to add rhodecode context variables """
65 def __call__(self, template_name, **kw):
65 def __call__(self, template_name, **kw):
66 kw['h'] = helpers
66 kw['h'] = helpers
67 kw['request'] = get_current_request()
67 kw['request'] = get_current_request()
68 return self.load(template_name)(**kw)
68 return self.load(template_name)(**kw)
69
69
70
70
71 form_renderer = RhodecodeFormZPTRendererFactory(search_path)
71 form_renderer = RhodecodeFormZPTRendererFactory(search_path)
72 deform.Form.set_default_renderer(form_renderer)
72 deform.Form.set_default_renderer(form_renderer)
73
73
74
74
75 def LoginForm(localizer):
75 def LoginForm(localizer):
76 _ = localizer
76 _ = localizer
77
77
78 class _LoginForm(formencode.Schema):
78 class _LoginForm(formencode.Schema):
79 allow_extra_fields = True
79 allow_extra_fields = True
80 filter_extra_fields = True
80 filter_extra_fields = True
81 username = v.UnicodeString(
81 username = v.UnicodeString(
82 strip=True,
82 strip=True,
83 min=1,
83 min=1,
84 not_empty=True,
84 not_empty=True,
85 messages={
85 messages={
86 'empty': _('Please enter a login'),
86 'empty': _('Please enter a login'),
87 'tooShort': _('Enter a value %(min)i characters long or more')
87 'tooShort': _('Enter a value %(min)i characters long or more')
88 }
88 }
89 )
89 )
90
90
91 password = v.UnicodeString(
91 password = v.UnicodeString(
92 strip=False,
92 strip=False,
93 min=3,
93 min=3,
94 max=72,
94 max=72,
95 not_empty=True,
95 not_empty=True,
96 messages={
96 messages={
97 'empty': _('Please enter a password'),
97 'empty': _('Please enter a password'),
98 'tooShort': _('Enter %(min)i characters or more')}
98 'tooShort': _('Enter %(min)i characters or more')}
99 )
99 )
100
100
101 remember = v.StringBoolean(if_missing=False)
101 remember = v.StringBoolean(if_missing=False)
102
102
103 chained_validators = [v.ValidAuth(localizer)]
103 chained_validators = [v.ValidAuth(localizer)]
104 return _LoginForm
104 return _LoginForm
105
105
106
106
107 def TOTPForm(localizer, user, allow_recovery_code_use=False):
107 def TOTPForm(localizer, user, allow_recovery_code_use=False):
108 _ = localizer
108 _ = localizer
109
109
110 class _TOTPForm(formencode.Schema):
110 class _TOTPForm(formencode.Schema):
111 allow_extra_fields = True
111 allow_extra_fields = True
112 filter_extra_fields = False
112 filter_extra_fields = False
113 totp = v.Regex(r'^(?:\d{6}|[A-Z0-9]{32})$')
113 totp = v.Regex(r'^(?:\d{6}|[A-Z0-9]{32})$')
114 secret_totp = v.String()
114 secret_totp = v.String()
115
115
116 def to_python(self, value, state=None):
116 def to_python(self, value, state=None):
117 validation_checks = [user.is_totp_valid]
117 validation_checks = [user.is_totp_valid]
118 if allow_recovery_code_use:
118 if allow_recovery_code_use:
119 validation_checks.append(user.is_2fa_recovery_code_valid)
119 validation_checks.append(user.is_2fa_recovery_code_valid)
120 form_data = super().to_python(value, state)
120 form_data = super().to_python(value, state)
121 received_code = form_data['totp']
121 received_code = form_data['totp']
122 secret = form_data.get('secret_totp')
122 secret = form_data.get('secret_totp')
123
123
124 if not any(map(lambda func: func(received_code, secret), validation_checks)):
124 if not any(map(lambda func: func(received_code, secret), validation_checks)):
125 error_msg = _('Code is invalid. Try again!')
125 error_msg = _('Code is invalid. Try again!')
126 raise formencode.Invalid(error_msg, v, state, error_dict={'totp': error_msg})
126 raise formencode.Invalid(error_msg, v, state, error_dict={'totp': error_msg})
127 return form_data
127 return form_data
128
128
129 return _TOTPForm
129 return _TOTPForm
130
130
131
131
132 def WhitelistedVcsClientsForm(localizer):
132 def WhitelistedVcsClientsForm(localizer):
133 _ = localizer
133 _ = localizer
134
134
135 class _WhitelistedVcsClientsForm(formencode.Schema):
135 class _WhitelistedVcsClientsForm(formencode.Schema):
136 regexp = r'^(?:\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\*)\s*(?:,\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\s*\*\s*)*$'
136 regexp = r'^(?:\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\*)\s*(?:,\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\s*\*\s*)*$'
137 allow_extra_fields = True
137 allow_extra_fields = True
138 filter_extra_fields = True
138 filter_extra_fields = True
139 git = v.Regex(regexp)
139 git = v.Regex(regexp)
140 hg = v.Regex(regexp)
140 hg = v.Regex(regexp)
141 svn = v.Regex(regexp)
141 svn = v.Regex(regexp)
142
142
143 return _WhitelistedVcsClientsForm
143 return _WhitelistedVcsClientsForm
144
144
145
145
146 def UserForm(localizer, edit=False, available_languages=None, old_data=None):
146 def UserForm(localizer, edit=False, available_languages=None, old_data=None):
147 old_data = old_data or {}
147 old_data = old_data or {}
148 available_languages = available_languages or []
148 available_languages = available_languages or []
149 _ = localizer
149 _ = localizer
150
150
151 class _UserForm(formencode.Schema):
151 class _UserForm(formencode.Schema):
152 allow_extra_fields = True
152 allow_extra_fields = True
153 filter_extra_fields = True
153 filter_extra_fields = True
154 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
154 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
155 v.ValidUsername(localizer, edit, old_data))
155 v.ValidUsername(localizer, edit, old_data))
156 if edit:
156 if edit:
157 new_password = All(
157 new_password = All(
158 v.ValidPassword(localizer),
158 v.ValidPassword(localizer),
159 v.UnicodeString(strip=False, min=6, max=72, not_empty=False)
159 v.UnicodeString(strip=False, min=6, max=72, not_empty=False)
160 )
160 )
161 password_confirmation = All(
161 password_confirmation = All(
162 v.ValidPassword(localizer),
162 v.ValidPassword(localizer),
163 v.UnicodeString(strip=False, min=6, max=72, not_empty=False),
163 v.UnicodeString(strip=False, min=6, max=72, not_empty=False),
164 )
164 )
165 admin = v.StringBoolean(if_missing=False)
165 admin = v.StringBoolean(if_missing=False)
166 else:
166 else:
167 password = All(
167 password = All(
168 v.ValidPassword(localizer),
168 v.ValidPassword(localizer),
169 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
169 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
170 )
170 )
171 password_confirmation = All(
171 password_confirmation = All(
172 v.ValidPassword(localizer),
172 v.ValidPassword(localizer),
173 v.UnicodeString(strip=False, min=6, max=72, not_empty=False)
173 v.UnicodeString(strip=False, min=6, max=72, not_empty=False)
174 )
174 )
175
175
176 password_change = v.StringBoolean(if_missing=False)
176 password_change = v.StringBoolean(if_missing=False)
177 create_repo_group = v.StringBoolean(if_missing=False)
177 create_repo_group = v.StringBoolean(if_missing=False)
178
178
179 active = v.StringBoolean(if_missing=False)
179 active = v.StringBoolean(if_missing=False)
180 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
180 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
181 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
181 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
182 email = All(v.UniqSystemEmail(localizer, old_data), v.Email(not_empty=True))
182 email = All(v.UniqSystemEmail(localizer, old_data), v.Email(not_empty=True))
183 description = v.UnicodeString(strip=True, min=1, max=250, not_empty=False,
183 description = v.UnicodeString(strip=True, min=1, max=250, not_empty=False,
184 if_missing='')
184 if_missing='')
185 extern_name = v.UnicodeString(strip=True)
185 extern_name = v.UnicodeString(strip=True)
186 extern_type = v.UnicodeString(strip=True)
186 extern_type = v.UnicodeString(strip=True)
187 language = v.OneOf(available_languages, hideList=False,
187 language = v.OneOf(available_languages, hideList=False,
188 testValueList=True, if_missing=None)
188 testValueList=True, if_missing=None)
189 chained_validators = [v.ValidPasswordsMatch(localizer)]
189 chained_validators = [v.ValidPasswordsMatch(localizer)]
190 return _UserForm
190 return _UserForm
191
191
192
192
193 def UserGroupForm(localizer, edit=False, old_data=None, allow_disabled=False):
193 def UserGroupForm(localizer, edit=False, old_data=None, allow_disabled=False):
194 old_data = old_data or {}
194 old_data = old_data or {}
195 _ = localizer
195 _ = localizer
196
196
197 class _UserGroupForm(formencode.Schema):
197 class _UserGroupForm(formencode.Schema):
198 allow_extra_fields = True
198 allow_extra_fields = True
199 filter_extra_fields = True
199 filter_extra_fields = True
200
200
201 users_group_name = All(
201 users_group_name = All(
202 v.UnicodeString(strip=True, min=1, not_empty=True),
202 v.UnicodeString(strip=True, min=1, not_empty=True),
203 v.ValidUserGroup(localizer, edit, old_data)
203 v.ValidUserGroup(localizer, edit, old_data)
204 )
204 )
205 user_group_description = v.UnicodeString(strip=True, min=1,
205 user_group_description = v.UnicodeString(strip=True, min=1,
206 not_empty=False)
206 not_empty=False)
207
207
208 users_group_active = v.StringBoolean(if_missing=False)
208 users_group_active = v.StringBoolean(if_missing=False)
209
209
210 if edit:
210 if edit:
211 # this is user group owner
211 # this is user group owner
212 user = All(
212 user = All(
213 v.UnicodeString(not_empty=True),
213 v.UnicodeString(not_empty=True),
214 v.ValidRepoUser(localizer, allow_disabled))
214 v.ValidRepoUser(localizer, allow_disabled))
215 return _UserGroupForm
215 return _UserGroupForm
216
216
217
217
218 def RepoGroupForm(localizer, edit=False, old_data=None, available_groups=None,
218 def RepoGroupForm(localizer, edit=False, old_data=None, available_groups=None,
219 can_create_in_root=False, allow_disabled=False):
219 can_create_in_root=False, allow_disabled=False):
220 _ = localizer
220 _ = localizer
221 old_data = old_data or {}
221 old_data = old_data or {}
222 available_groups = available_groups or []
222 available_groups = available_groups or []
223
223
224 class _RepoGroupForm(formencode.Schema):
224 class _RepoGroupForm(formencode.Schema):
225 allow_extra_fields = True
225 allow_extra_fields = True
226 filter_extra_fields = False
226 filter_extra_fields = False
227
227
228 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
228 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
229 v.SlugifyName(localizer),)
229 v.SlugifyName(localizer),)
230 group_description = v.UnicodeString(strip=True, min=1,
230 group_description = v.UnicodeString(strip=True, min=1,
231 not_empty=False)
231 not_empty=False)
232 group_copy_permissions = v.StringBoolean(if_missing=False)
232 group_copy_permissions = v.StringBoolean(if_missing=False)
233
233
234 group_parent_id = v.OneOf(available_groups, hideList=False,
234 group_parent_id = v.OneOf(available_groups, hideList=False,
235 testValueList=True, not_empty=True)
235 testValueList=True, not_empty=True)
236 enable_locking = v.StringBoolean(if_missing=False)
236 enable_locking = v.StringBoolean(if_missing=False)
237 chained_validators = [
237 chained_validators = [
238 v.ValidRepoGroup(localizer, edit, old_data, can_create_in_root)]
238 v.ValidRepoGroup(localizer, edit, old_data, can_create_in_root)]
239
239
240 if edit:
240 if edit:
241 # this is repo group owner
241 # this is repo group owner
242 user = All(
242 user = All(
243 v.UnicodeString(not_empty=True),
243 v.UnicodeString(not_empty=True),
244 v.ValidRepoUser(localizer, allow_disabled))
244 v.ValidRepoUser(localizer, allow_disabled))
245 return _RepoGroupForm
245 return _RepoGroupForm
246
246
247
247
248 def RegisterForm(localizer, edit=False, old_data=None):
248 def RegisterForm(localizer, edit=False, old_data=None):
249 _ = localizer
249 _ = localizer
250 old_data = old_data or {}
250 old_data = old_data or {}
251
251
252 class _RegisterForm(formencode.Schema):
252 class _RegisterForm(formencode.Schema):
253 allow_extra_fields = True
253 allow_extra_fields = True
254 filter_extra_fields = True
254 filter_extra_fields = True
255 username = All(
255 username = All(
256 v.ValidUsername(localizer, edit, old_data),
256 v.ValidUsername(localizer, edit, old_data),
257 v.UnicodeString(strip=True, min=1, not_empty=True)
257 v.UnicodeString(strip=True, min=1, not_empty=True)
258 )
258 )
259 password = All(
259 password = All(
260 v.ValidPassword(localizer),
260 v.ValidPassword(localizer),
261 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
261 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
262 )
262 )
263 password_confirmation = All(
263 password_confirmation = All(
264 v.ValidPassword(localizer),
264 v.ValidPassword(localizer),
265 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
265 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
266 )
266 )
267 active = v.StringBoolean(if_missing=False)
267 active = v.StringBoolean(if_missing=False)
268 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
268 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
269 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
269 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
270 email = All(v.UniqSystemEmail(localizer, old_data), v.Email(not_empty=True))
270 email = All(v.UniqSystemEmail(localizer, old_data), v.Email(not_empty=True))
271
271
272 chained_validators = [v.ValidPasswordsMatch(localizer)]
272 chained_validators = [v.ValidPasswordsMatch(localizer)]
273 return _RegisterForm
273 return _RegisterForm
274
274
275
275
276 def PasswordResetForm(localizer):
276 def PasswordResetForm(localizer):
277 _ = localizer
277 _ = localizer
278
278
279 class _PasswordResetForm(formencode.Schema):
279 class _PasswordResetForm(formencode.Schema):
280 allow_extra_fields = True
280 allow_extra_fields = True
281 filter_extra_fields = True
281 filter_extra_fields = True
282 email = All(v.ValidSystemEmail(localizer), v.Email(not_empty=True))
282 email = All(v.ValidSystemEmail(localizer), v.Email(not_empty=True))
283 return _PasswordResetForm
283 return _PasswordResetForm
284
284
285
285
286 def RepoForm(localizer, edit=False, old_data=None, repo_groups=None, allow_disabled=False):
286 def RepoForm(localizer, edit=False, old_data=None, repo_groups=None, allow_disabled=False):
287 _ = localizer
287 _ = localizer
288 old_data = old_data or {}
288 old_data = old_data or {}
289 repo_groups = repo_groups or []
289 repo_groups = repo_groups or []
290 supported_backends = BACKENDS.keys()
290 supported_backends = BACKENDS.keys()
291
291
292 class _RepoForm(formencode.Schema):
292 class _RepoForm(formencode.Schema):
293 allow_extra_fields = True
293 allow_extra_fields = True
294 filter_extra_fields = False
294 filter_extra_fields = False
295 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
295 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
296 v.SlugifyName(localizer), v.CannotHaveGitSuffix(localizer))
296 v.SlugifyName(localizer), v.CannotHaveGitSuffix(localizer))
297 repo_group = All(v.CanWriteGroup(localizer, old_data),
297 repo_group = All(v.CanWriteGroup(localizer, old_data),
298 v.OneOf(repo_groups, hideList=True))
298 v.OneOf(repo_groups, hideList=True))
299 repo_type = v.OneOf(supported_backends, required=False,
299 repo_type = v.OneOf(supported_backends, required=False,
300 if_missing=old_data.get('repo_type'))
300 if_missing=old_data.get('repo_type'))
301 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
301 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
302 repo_private = v.StringBoolean(if_missing=False)
302 repo_private = v.StringBoolean(if_missing=False)
303 repo_copy_permissions = v.StringBoolean(if_missing=False)
303 repo_copy_permissions = v.StringBoolean(if_missing=False)
304 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
304 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
305
305
306 repo_enable_statistics = v.StringBoolean(if_missing=False)
306 repo_enable_statistics = v.StringBoolean(if_missing=False)
307 repo_enable_downloads = v.StringBoolean(if_missing=False)
307 repo_enable_downloads = v.StringBoolean(if_missing=False)
308 repo_enable_locking = v.StringBoolean(if_missing=False)
308 repo_enable_locking = v.StringBoolean(if_missing=False)
309
309
310 if edit:
310 if edit:
311 # this is repo owner
311 # this is repo owner
312 user = All(
312 user = All(
313 v.UnicodeString(not_empty=True),
313 v.UnicodeString(not_empty=True),
314 v.ValidRepoUser(localizer, allow_disabled))
314 v.ValidRepoUser(localizer, allow_disabled))
315 clone_uri_change = v.UnicodeString(
315 clone_uri_change = v.UnicodeString(
316 not_empty=False, if_missing=v.Missing)
316 not_empty=False, if_missing=v.Missing)
317
317
318 chained_validators = [v.ValidCloneUri(localizer),
318 chained_validators = [v.ValidCloneUri(localizer),
319 v.ValidRepoName(localizer, edit, old_data)]
319 v.ValidRepoName(localizer, edit, old_data)]
320 return _RepoForm
320 return _RepoForm
321
321
322
322
323 def RepoPermsForm(localizer):
323 def RepoPermsForm(localizer):
324 _ = localizer
324 _ = localizer
325
325
326 class _RepoPermsForm(formencode.Schema):
326 class _RepoPermsForm(formencode.Schema):
327 allow_extra_fields = True
327 allow_extra_fields = True
328 filter_extra_fields = False
328 filter_extra_fields = False
329 chained_validators = [v.ValidPerms(localizer, type_='repo')]
329 chained_validators = [v.ValidPerms(localizer, type_='repo')]
330 return _RepoPermsForm
330 return _RepoPermsForm
331
331
332
332
333 def RepoGroupPermsForm(localizer, valid_recursive_choices):
333 def RepoGroupPermsForm(localizer, valid_recursive_choices):
334 _ = localizer
334 _ = localizer
335
335
336 class _RepoGroupPermsForm(formencode.Schema):
336 class _RepoGroupPermsForm(formencode.Schema):
337 allow_extra_fields = True
337 allow_extra_fields = True
338 filter_extra_fields = False
338 filter_extra_fields = False
339 recursive = v.OneOf(valid_recursive_choices)
339 recursive = v.OneOf(valid_recursive_choices)
340 chained_validators = [v.ValidPerms(localizer, type_='repo_group')]
340 chained_validators = [v.ValidPerms(localizer, type_='repo_group')]
341 return _RepoGroupPermsForm
341 return _RepoGroupPermsForm
342
342
343
343
344 def UserGroupPermsForm(localizer):
344 def UserGroupPermsForm(localizer):
345 _ = localizer
345 _ = localizer
346
346
347 class _UserPermsForm(formencode.Schema):
347 class _UserPermsForm(formencode.Schema):
348 allow_extra_fields = True
348 allow_extra_fields = True
349 filter_extra_fields = False
349 filter_extra_fields = False
350 chained_validators = [v.ValidPerms(localizer, type_='user_group')]
350 chained_validators = [v.ValidPerms(localizer, type_='user_group')]
351 return _UserPermsForm
351 return _UserPermsForm
352
352
353
353
354 def RepoFieldForm(localizer):
354 def RepoFieldForm(localizer):
355 _ = localizer
355 _ = localizer
356
356
357 class _RepoFieldForm(formencode.Schema):
357 class _RepoFieldForm(formencode.Schema):
358 filter_extra_fields = True
358 filter_extra_fields = True
359 allow_extra_fields = True
359 allow_extra_fields = True
360
360
361 new_field_key = All(v.FieldKey(localizer),
361 new_field_key = All(v.FieldKey(localizer),
362 v.UnicodeString(strip=True, min=3, not_empty=True))
362 v.UnicodeString(strip=True, min=3, not_empty=True))
363 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
363 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
364 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
364 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
365 if_missing='str')
365 if_missing='str')
366 new_field_label = v.UnicodeString(not_empty=False)
366 new_field_label = v.UnicodeString(not_empty=False)
367 new_field_desc = v.UnicodeString(not_empty=False)
367 new_field_desc = v.UnicodeString(not_empty=False)
368 return _RepoFieldForm
368 return _RepoFieldForm
369
369
370
370
371 def RepoForkForm(localizer, edit=False, old_data=None,
371 def RepoForkForm(localizer, edit=False, old_data=None,
372 supported_backends=BACKENDS.keys(), repo_groups=None):
372 supported_backends=BACKENDS.keys(), repo_groups=None):
373 _ = localizer
373 _ = localizer
374 old_data = old_data or {}
374 old_data = old_data or {}
375 repo_groups = repo_groups or []
375 repo_groups = repo_groups or []
376
376
377 class _RepoForkForm(formencode.Schema):
377 class _RepoForkForm(formencode.Schema):
378 allow_extra_fields = True
378 allow_extra_fields = True
379 filter_extra_fields = False
379 filter_extra_fields = False
380 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
380 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
381 v.SlugifyName(localizer))
381 v.SlugifyName(localizer))
382 repo_group = All(v.CanWriteGroup(localizer, ),
382 repo_group = All(v.CanWriteGroup(localizer, ),
383 v.OneOf(repo_groups, hideList=True))
383 v.OneOf(repo_groups, hideList=True))
384 repo_type = All(v.ValidForkType(localizer, old_data), v.OneOf(supported_backends))
384 repo_type = All(v.ValidForkType(localizer, old_data), v.OneOf(supported_backends))
385 description = v.UnicodeString(strip=True, min=1, not_empty=True)
385 description = v.UnicodeString(strip=True, min=1, not_empty=True)
386 private = v.StringBoolean(if_missing=False)
386 private = v.StringBoolean(if_missing=False)
387 copy_permissions = v.StringBoolean(if_missing=False)
387 copy_permissions = v.StringBoolean(if_missing=False)
388 fork_parent_id = v.UnicodeString()
388 fork_parent_id = v.UnicodeString()
389 chained_validators = [v.ValidForkName(localizer, edit, old_data)]
389 chained_validators = [v.ValidForkName(localizer, edit, old_data)]
390 return _RepoForkForm
390 return _RepoForkForm
391
391
392
392
393 def ApplicationSettingsForm(localizer):
393 def ApplicationSettingsForm(localizer):
394 _ = localizer
394 _ = localizer
395
395
396 class _ApplicationSettingsForm(formencode.Schema):
396 class _ApplicationSettingsForm(formencode.Schema):
397 allow_extra_fields = True
397 allow_extra_fields = True
398 filter_extra_fields = False
398 filter_extra_fields = False
399 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
399 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
400 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
400 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
401 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
401 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
402 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
402 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
403 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
403 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
404 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
404 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
405 rhodecode_create_personal_repo_group = v.StringBoolean(if_missing=False)
405 rhodecode_create_personal_repo_group = v.StringBoolean(if_missing=False)
406 rhodecode_personal_repo_group_pattern = v.UnicodeString(strip=True, min=1, not_empty=False)
406 rhodecode_personal_repo_group_pattern = v.UnicodeString(strip=True, min=1, not_empty=False)
407 return _ApplicationSettingsForm
407 return _ApplicationSettingsForm
408
408
409
409
410 def ApplicationVisualisationForm(localizer):
410 def ApplicationVisualisationForm(localizer):
411 from rhodecode.model.db import Repository
411 from rhodecode.model.db import Repository
412 _ = localizer
412 _ = localizer
413
413
414 class _ApplicationVisualisationForm(formencode.Schema):
414 class _ApplicationVisualisationForm(formencode.Schema):
415 allow_extra_fields = True
415 allow_extra_fields = True
416 filter_extra_fields = False
416 filter_extra_fields = False
417 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
417 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
418 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
418 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
419 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
419 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
420
420
421 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
421 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
422 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
422 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
423 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
423 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
424 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
424 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
425 rhodecode_show_version = v.StringBoolean(if_missing=False)
425 rhodecode_show_version = v.StringBoolean(if_missing=False)
426 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
426 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
427 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
427 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
428 rhodecode_gravatar_url = v.UnicodeString(min=3)
428 rhodecode_gravatar_url = v.UnicodeString(min=3)
429 rhodecode_clone_uri_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI)
429 rhodecode_clone_uri_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI)
430 rhodecode_clone_uri_id_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI_ID)
430 rhodecode_clone_uri_id_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI_ID)
431 rhodecode_clone_uri_ssh_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI_SSH)
431 rhodecode_clone_uri_ssh_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI_SSH)
432 rhodecode_support_url = v.UnicodeString()
432 rhodecode_support_url = v.UnicodeString()
433 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
433 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
434 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
434 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
435 return _ApplicationVisualisationForm
435 return _ApplicationVisualisationForm
436
436
437
437
438 class _BaseVcsSettingsForm(formencode.Schema):
438 class _BaseVcsSettingsForm(formencode.Schema):
439
439
440 allow_extra_fields = True
440 allow_extra_fields = True
441 filter_extra_fields = False
441 filter_extra_fields = False
442 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
442 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
443 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
443 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
444 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
444 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
445
445
446 # PR/Code-review
446 # PR/Code-review
447 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
447 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
448 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
448 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
449
449
450 # hg
450 # hg
451 extensions_largefiles = v.StringBoolean(if_missing=False)
451 extensions_largefiles = v.StringBoolean(if_missing=False)
452 extensions_evolve = v.StringBoolean(if_missing=False)
452 extensions_evolve = v.StringBoolean(if_missing=False)
453 phases_publish = v.StringBoolean(if_missing=False)
453 phases_publish = v.StringBoolean(if_missing=False)
454
454
455 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
455 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
456 rhodecode_hg_close_branch_before_merging = v.StringBoolean(if_missing=False)
456 rhodecode_hg_close_branch_before_merging = v.StringBoolean(if_missing=False)
457
457
458 # git
458 # git
459 vcs_git_lfs_enabled = v.StringBoolean(if_missing=False)
459 vcs_git_lfs_enabled = v.StringBoolean(if_missing=False)
460 rhodecode_git_use_rebase_for_merging = v.StringBoolean(if_missing=False)
460 rhodecode_git_use_rebase_for_merging = v.StringBoolean(if_missing=False)
461 rhodecode_git_close_branch_before_merging = v.StringBoolean(if_missing=False)
461 rhodecode_git_close_branch_before_merging = v.StringBoolean(if_missing=False)
462
462
463 # cache
463 # cache
464 rhodecode_diff_cache = v.StringBoolean(if_missing=False)
464 rhodecode_diff_cache = v.StringBoolean(if_missing=False)
465
465
466
466
467 def ApplicationUiSettingsForm(localizer):
467 def ApplicationUiSettingsForm(localizer):
468 _ = localizer
468 _ = localizer
469
469
470 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
470 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
471 web_push_ssl = v.StringBoolean(if_missing=False)
471 web_push_ssl = v.StringBoolean(if_missing=False)
472 largefiles_usercache = All(
473 v.ValidPath(localizer),
474 v.UnicodeString(strip=True, min=2, not_empty=True))
475 vcs_git_lfs_store_location = All(
476 v.ValidPath(localizer),
477 v.UnicodeString(strip=True, min=2, not_empty=True))
478 extensions_hggit = v.StringBoolean(if_missing=False)
472 extensions_hggit = v.StringBoolean(if_missing=False)
479 new_svn_branch = v.ValidSvnPattern(localizer, section='vcs_svn_branch')
473 new_svn_branch = v.ValidSvnPattern(localizer, section='vcs_svn_branch')
480 new_svn_tag = v.ValidSvnPattern(localizer, section='vcs_svn_tag')
474 new_svn_tag = v.ValidSvnPattern(localizer, section='vcs_svn_tag')
481 return _ApplicationUiSettingsForm
475 return _ApplicationUiSettingsForm
482
476
483
477
484 def RepoVcsSettingsForm(localizer, repo_name):
478 def RepoVcsSettingsForm(localizer, repo_name):
485 _ = localizer
479 _ = localizer
486
480
487 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
481 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
488 inherit_global_settings = v.StringBoolean(if_missing=False)
482 inherit_global_settings = v.StringBoolean(if_missing=False)
489 new_svn_branch = v.ValidSvnPattern(localizer,
483 new_svn_branch = v.ValidSvnPattern(localizer,
490 section='vcs_svn_branch', repo_name=repo_name)
484 section='vcs_svn_branch', repo_name=repo_name)
491 new_svn_tag = v.ValidSvnPattern(localizer,
485 new_svn_tag = v.ValidSvnPattern(localizer,
492 section='vcs_svn_tag', repo_name=repo_name)
486 section='vcs_svn_tag', repo_name=repo_name)
493 return _RepoVcsSettingsForm
487 return _RepoVcsSettingsForm
494
488
495
489
496 def LabsSettingsForm(localizer):
490 def LabsSettingsForm(localizer):
497 _ = localizer
491 _ = localizer
498
492
499 class _LabSettingsForm(formencode.Schema):
493 class _LabSettingsForm(formencode.Schema):
500 allow_extra_fields = True
494 allow_extra_fields = True
501 filter_extra_fields = False
495 filter_extra_fields = False
502 return _LabSettingsForm
496 return _LabSettingsForm
503
497
504
498
505 def ApplicationPermissionsForm(
499 def ApplicationPermissionsForm(
506 localizer, register_choices, password_reset_choices,
500 localizer, register_choices, password_reset_choices,
507 extern_activate_choices):
501 extern_activate_choices):
508 _ = localizer
502 _ = localizer
509
503
510 class _DefaultPermissionsForm(formencode.Schema):
504 class _DefaultPermissionsForm(formencode.Schema):
511 allow_extra_fields = True
505 allow_extra_fields = True
512 filter_extra_fields = True
506 filter_extra_fields = True
513
507
514 anonymous = v.StringBoolean(if_missing=False)
508 anonymous = v.StringBoolean(if_missing=False)
515 default_register = v.OneOf(register_choices)
509 default_register = v.OneOf(register_choices)
516 default_register_message = v.UnicodeString()
510 default_register_message = v.UnicodeString()
517 default_password_reset = v.OneOf(password_reset_choices)
511 default_password_reset = v.OneOf(password_reset_choices)
518 default_extern_activate = v.OneOf(extern_activate_choices)
512 default_extern_activate = v.OneOf(extern_activate_choices)
519 return _DefaultPermissionsForm
513 return _DefaultPermissionsForm
520
514
521
515
522 def ObjectPermissionsForm(localizer, repo_perms_choices, group_perms_choices,
516 def ObjectPermissionsForm(localizer, repo_perms_choices, group_perms_choices,
523 user_group_perms_choices):
517 user_group_perms_choices):
524 _ = localizer
518 _ = localizer
525
519
526 class _ObjectPermissionsForm(formencode.Schema):
520 class _ObjectPermissionsForm(formencode.Schema):
527 allow_extra_fields = True
521 allow_extra_fields = True
528 filter_extra_fields = True
522 filter_extra_fields = True
529 overwrite_default_repo = v.StringBoolean(if_missing=False)
523 overwrite_default_repo = v.StringBoolean(if_missing=False)
530 overwrite_default_group = v.StringBoolean(if_missing=False)
524 overwrite_default_group = v.StringBoolean(if_missing=False)
531 overwrite_default_user_group = v.StringBoolean(if_missing=False)
525 overwrite_default_user_group = v.StringBoolean(if_missing=False)
532
526
533 default_repo_perm = v.OneOf(repo_perms_choices)
527 default_repo_perm = v.OneOf(repo_perms_choices)
534 default_group_perm = v.OneOf(group_perms_choices)
528 default_group_perm = v.OneOf(group_perms_choices)
535 default_user_group_perm = v.OneOf(user_group_perms_choices)
529 default_user_group_perm = v.OneOf(user_group_perms_choices)
536
530
537 return _ObjectPermissionsForm
531 return _ObjectPermissionsForm
538
532
539
533
540 def BranchPermissionsForm(localizer, branch_perms_choices):
534 def BranchPermissionsForm(localizer, branch_perms_choices):
541 _ = localizer
535 _ = localizer
542
536
543 class _BranchPermissionsForm(formencode.Schema):
537 class _BranchPermissionsForm(formencode.Schema):
544 allow_extra_fields = True
538 allow_extra_fields = True
545 filter_extra_fields = True
539 filter_extra_fields = True
546 overwrite_default_branch = v.StringBoolean(if_missing=False)
540 overwrite_default_branch = v.StringBoolean(if_missing=False)
547 default_branch_perm = v.OneOf(branch_perms_choices)
541 default_branch_perm = v.OneOf(branch_perms_choices)
548
542
549 return _BranchPermissionsForm
543 return _BranchPermissionsForm
550
544
551
545
552 def UserPermissionsForm(localizer, create_choices, create_on_write_choices,
546 def UserPermissionsForm(localizer, create_choices, create_on_write_choices,
553 repo_group_create_choices, user_group_create_choices,
547 repo_group_create_choices, user_group_create_choices,
554 fork_choices, inherit_default_permissions_choices):
548 fork_choices, inherit_default_permissions_choices):
555 _ = localizer
549 _ = localizer
556
550
557 class _DefaultPermissionsForm(formencode.Schema):
551 class _DefaultPermissionsForm(formencode.Schema):
558 allow_extra_fields = True
552 allow_extra_fields = True
559 filter_extra_fields = True
553 filter_extra_fields = True
560
554
561 anonymous = v.StringBoolean(if_missing=False)
555 anonymous = v.StringBoolean(if_missing=False)
562
556
563 default_repo_create = v.OneOf(create_choices)
557 default_repo_create = v.OneOf(create_choices)
564 default_repo_create_on_write = v.OneOf(create_on_write_choices)
558 default_repo_create_on_write = v.OneOf(create_on_write_choices)
565 default_user_group_create = v.OneOf(user_group_create_choices)
559 default_user_group_create = v.OneOf(user_group_create_choices)
566 default_repo_group_create = v.OneOf(repo_group_create_choices)
560 default_repo_group_create = v.OneOf(repo_group_create_choices)
567 default_fork_create = v.OneOf(fork_choices)
561 default_fork_create = v.OneOf(fork_choices)
568 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
562 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
569 return _DefaultPermissionsForm
563 return _DefaultPermissionsForm
570
564
571
565
572 def UserIndividualPermissionsForm(localizer):
566 def UserIndividualPermissionsForm(localizer):
573 _ = localizer
567 _ = localizer
574
568
575 class _DefaultPermissionsForm(formencode.Schema):
569 class _DefaultPermissionsForm(formencode.Schema):
576 allow_extra_fields = True
570 allow_extra_fields = True
577 filter_extra_fields = True
571 filter_extra_fields = True
578
572
579 inherit_default_permissions = v.StringBoolean(if_missing=False)
573 inherit_default_permissions = v.StringBoolean(if_missing=False)
580 return _DefaultPermissionsForm
574 return _DefaultPermissionsForm
581
575
582
576
583 def DefaultsForm(localizer, edit=False, old_data=None, supported_backends=BACKENDS.keys()):
577 def DefaultsForm(localizer, edit=False, old_data=None, supported_backends=BACKENDS.keys()):
584 _ = localizer
578 _ = localizer
585 old_data = old_data or {}
579 old_data = old_data or {}
586
580
587 class _DefaultsForm(formencode.Schema):
581 class _DefaultsForm(formencode.Schema):
588 allow_extra_fields = True
582 allow_extra_fields = True
589 filter_extra_fields = True
583 filter_extra_fields = True
590 default_repo_type = v.OneOf(supported_backends)
584 default_repo_type = v.OneOf(supported_backends)
591 default_repo_private = v.StringBoolean(if_missing=False)
585 default_repo_private = v.StringBoolean(if_missing=False)
592 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
586 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
593 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
587 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
594 default_repo_enable_locking = v.StringBoolean(if_missing=False)
588 default_repo_enable_locking = v.StringBoolean(if_missing=False)
595 return _DefaultsForm
589 return _DefaultsForm
596
590
597
591
598 def AuthSettingsForm(localizer):
592 def AuthSettingsForm(localizer):
599 _ = localizer
593 _ = localizer
600
594
601 class _AuthSettingsForm(formencode.Schema):
595 class _AuthSettingsForm(formencode.Schema):
602 allow_extra_fields = True
596 allow_extra_fields = True
603 filter_extra_fields = True
597 filter_extra_fields = True
604 auth_plugins = All(v.ValidAuthPlugins(localizer),
598 auth_plugins = All(v.ValidAuthPlugins(localizer),
605 v.UniqueListFromString(localizer)(not_empty=True))
599 v.UniqueListFromString(localizer)(not_empty=True))
606 return _AuthSettingsForm
600 return _AuthSettingsForm
607
601
608
602
609 def UserExtraEmailForm(localizer):
603 def UserExtraEmailForm(localizer):
610 _ = localizer
604 _ = localizer
611
605
612 class _UserExtraEmailForm(formencode.Schema):
606 class _UserExtraEmailForm(formencode.Schema):
613 email = All(v.UniqSystemEmail(localizer), v.Email(not_empty=True))
607 email = All(v.UniqSystemEmail(localizer), v.Email(not_empty=True))
614 return _UserExtraEmailForm
608 return _UserExtraEmailForm
615
609
616
610
617 def UserExtraIpForm(localizer):
611 def UserExtraIpForm(localizer):
618 _ = localizer
612 _ = localizer
619
613
620 class _UserExtraIpForm(formencode.Schema):
614 class _UserExtraIpForm(formencode.Schema):
621 ip = v.ValidIp(localizer)(not_empty=True)
615 ip = v.ValidIp(localizer)(not_empty=True)
622 return _UserExtraIpForm
616 return _UserExtraIpForm
623
617
624
618
625 def PullRequestForm(localizer, repo_id):
619 def PullRequestForm(localizer, repo_id):
626 _ = localizer
620 _ = localizer
627
621
628 class ReviewerForm(formencode.Schema):
622 class ReviewerForm(formencode.Schema):
629 user_id = v.Int(not_empty=True)
623 user_id = v.Int(not_empty=True)
630 reasons = All()
624 reasons = All()
631 rules = All(v.UniqueList(localizer, convert=int)())
625 rules = All(v.UniqueList(localizer, convert=int)())
632 mandatory = v.StringBoolean()
626 mandatory = v.StringBoolean()
633 role = v.String(if_missing='reviewer')
627 role = v.String(if_missing='reviewer')
634
628
635 class ObserverForm(formencode.Schema):
629 class ObserverForm(formencode.Schema):
636 user_id = v.Int(not_empty=True)
630 user_id = v.Int(not_empty=True)
637 reasons = All()
631 reasons = All()
638 rules = All(v.UniqueList(localizer, convert=int)())
632 rules = All(v.UniqueList(localizer, convert=int)())
639 mandatory = v.StringBoolean()
633 mandatory = v.StringBoolean()
640 role = v.String(if_missing='observer')
634 role = v.String(if_missing='observer')
641
635
642 class _PullRequestForm(formencode.Schema):
636 class _PullRequestForm(formencode.Schema):
643 allow_extra_fields = True
637 allow_extra_fields = True
644 filter_extra_fields = True
638 filter_extra_fields = True
645
639
646 common_ancestor = v.UnicodeString(strip=True, required=True)
640 common_ancestor = v.UnicodeString(strip=True, required=True)
647 source_repo = v.UnicodeString(strip=True, required=True)
641 source_repo = v.UnicodeString(strip=True, required=True)
648 source_ref = v.UnicodeString(strip=True, required=True)
642 source_ref = v.UnicodeString(strip=True, required=True)
649 target_repo = v.UnicodeString(strip=True, required=True)
643 target_repo = v.UnicodeString(strip=True, required=True)
650 target_ref = v.UnicodeString(strip=True, required=True)
644 target_ref = v.UnicodeString(strip=True, required=True)
651 revisions = All(#v.NotReviewedRevisions(localizer, repo_id)(),
645 revisions = All(#v.NotReviewedRevisions(localizer, repo_id)(),
652 v.UniqueList(localizer)(not_empty=True))
646 v.UniqueList(localizer)(not_empty=True))
653 review_members = formencode.ForEach(ReviewerForm())
647 review_members = formencode.ForEach(ReviewerForm())
654 observer_members = formencode.ForEach(ObserverForm())
648 observer_members = formencode.ForEach(ObserverForm())
655 pullrequest_title = v.UnicodeString(strip=True, required=True, min=1, max=255)
649 pullrequest_title = v.UnicodeString(strip=True, required=True, min=1, max=255)
656 pullrequest_desc = v.UnicodeString(strip=True, required=False)
650 pullrequest_desc = v.UnicodeString(strip=True, required=False)
657 description_renderer = v.UnicodeString(strip=True, required=False)
651 description_renderer = v.UnicodeString(strip=True, required=False)
658
652
659 return _PullRequestForm
653 return _PullRequestForm
660
654
661
655
662 def IssueTrackerPatternsForm(localizer):
656 def IssueTrackerPatternsForm(localizer):
663 _ = localizer
657 _ = localizer
664
658
665 class _IssueTrackerPatternsForm(formencode.Schema):
659 class _IssueTrackerPatternsForm(formencode.Schema):
666 allow_extra_fields = True
660 allow_extra_fields = True
667 filter_extra_fields = False
661 filter_extra_fields = False
668 chained_validators = [v.ValidPattern(localizer)]
662 chained_validators = [v.ValidPattern(localizer)]
669 return _IssueTrackerPatternsForm
663 return _IssueTrackerPatternsForm
@@ -1,902 +1,893 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import os
19 import os
20 import re
20 import re
21 import logging
21 import logging
22 import time
22 import time
23 import functools
23 import functools
24 from collections import namedtuple
24 from collections import namedtuple
25
25
26 from pyramid.threadlocal import get_current_request
26 from pyramid.threadlocal import get_current_request
27
27
28 from rhodecode.lib import rc_cache
28 from rhodecode.lib import rc_cache
29 from rhodecode.lib.hash_utils import sha1_safe
29 from rhodecode.lib.hash_utils import sha1_safe
30 from rhodecode.lib.html_filters import sanitize_html
30 from rhodecode.lib.html_filters import sanitize_html
31 from rhodecode.lib.utils2 import (
31 from rhodecode.lib.utils2 import (
32 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
32 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
33 from rhodecode.lib.vcs.backends import base
33 from rhodecode.lib.vcs.backends import base
34 from rhodecode.lib.statsd_client import StatsdClient
34 from rhodecode.lib.statsd_client import StatsdClient
35 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
36 from rhodecode.model.db import (
36 from rhodecode.model.db import (
37 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting)
37 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting)
38 from rhodecode.model.meta import Session
38 from rhodecode.model.meta import Session
39
39
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 UiSetting = namedtuple(
44 UiSetting = namedtuple(
45 'UiSetting', ['section', 'key', 'value', 'active'])
45 'UiSetting', ['section', 'key', 'value', 'active'])
46
46
47 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
47 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
48
48
49
49
50 class SettingNotFound(Exception):
50 class SettingNotFound(Exception):
51 def __init__(self, setting_id):
51 def __init__(self, setting_id):
52 msg = f'Setting `{setting_id}` is not found'
52 msg = f'Setting `{setting_id}` is not found'
53 super().__init__(msg)
53 super().__init__(msg)
54
54
55
55
56 class SettingsModel(BaseModel):
56 class SettingsModel(BaseModel):
57 BUILTIN_HOOKS = (
57 BUILTIN_HOOKS = (
58 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
58 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
59 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
59 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
60 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
60 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
61 RhodeCodeUi.HOOK_PUSH_KEY,)
61 RhodeCodeUi.HOOK_PUSH_KEY,)
62 HOOKS_SECTION = 'hooks'
62 HOOKS_SECTION = 'hooks'
63
63
64 def __init__(self, sa=None, repo=None):
64 def __init__(self, sa=None, repo=None):
65 self.repo = repo
65 self.repo = repo
66 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
66 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
67 self.SettingsDbModel = (
67 self.SettingsDbModel = (
68 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
68 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
69 super().__init__(sa)
69 super().__init__(sa)
70
70
71 def get_keyname(self, key_name, prefix='rhodecode_'):
71 def get_keyname(self, key_name, prefix='rhodecode_'):
72 return f'{prefix}{key_name}'
72 return f'{prefix}{key_name}'
73
73
74 def get_ui_by_key(self, key):
74 def get_ui_by_key(self, key):
75 q = self.UiDbModel.query()
75 q = self.UiDbModel.query()
76 q = q.filter(self.UiDbModel.ui_key == key)
76 q = q.filter(self.UiDbModel.ui_key == key)
77 q = self._filter_by_repo(RepoRhodeCodeUi, q)
77 q = self._filter_by_repo(RepoRhodeCodeUi, q)
78 return q.scalar()
78 return q.scalar()
79
79
80 def get_ui_by_section(self, section):
80 def get_ui_by_section(self, section):
81 q = self.UiDbModel.query()
81 q = self.UiDbModel.query()
82 q = q.filter(self.UiDbModel.ui_section == section)
82 q = q.filter(self.UiDbModel.ui_section == section)
83 q = self._filter_by_repo(RepoRhodeCodeUi, q)
83 q = self._filter_by_repo(RepoRhodeCodeUi, q)
84 return q.all()
84 return q.all()
85
85
86 def get_ui_by_section_and_key(self, section, key):
86 def get_ui_by_section_and_key(self, section, key):
87 q = self.UiDbModel.query()
87 q = self.UiDbModel.query()
88 q = q.filter(self.UiDbModel.ui_section == section)
88 q = q.filter(self.UiDbModel.ui_section == section)
89 q = q.filter(self.UiDbModel.ui_key == key)
89 q = q.filter(self.UiDbModel.ui_key == key)
90 q = self._filter_by_repo(RepoRhodeCodeUi, q)
90 q = self._filter_by_repo(RepoRhodeCodeUi, q)
91 return q.scalar()
91 return q.scalar()
92
92
93 def get_ui(self, section=None, key=None):
93 def get_ui(self, section=None, key=None):
94 q = self.UiDbModel.query()
94 q = self.UiDbModel.query()
95 q = self._filter_by_repo(RepoRhodeCodeUi, q)
95 q = self._filter_by_repo(RepoRhodeCodeUi, q)
96
96
97 if section:
97 if section:
98 q = q.filter(self.UiDbModel.ui_section == section)
98 q = q.filter(self.UiDbModel.ui_section == section)
99 if key:
99 if key:
100 q = q.filter(self.UiDbModel.ui_key == key)
100 q = q.filter(self.UiDbModel.ui_key == key)
101
101
102 # TODO: mikhail: add caching
102 # TODO: mikhail: add caching
103 result = [
103 result = [
104 UiSetting(
104 UiSetting(
105 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
105 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
106 value=safe_str(r.ui_value), active=r.ui_active
106 value=safe_str(r.ui_value), active=r.ui_active
107 )
107 )
108 for r in q.all()
108 for r in q.all()
109 ]
109 ]
110 return result
110 return result
111
111
112 def get_builtin_hooks(self):
112 def get_builtin_hooks(self):
113 q = self.UiDbModel.query()
113 q = self.UiDbModel.query()
114 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
114 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
115 return self._get_hooks(q)
115 return self._get_hooks(q)
116
116
117 def get_custom_hooks(self):
117 def get_custom_hooks(self):
118 q = self.UiDbModel.query()
118 q = self.UiDbModel.query()
119 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
119 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
120 return self._get_hooks(q)
120 return self._get_hooks(q)
121
121
122 def create_ui_section_value(self, section, val, key=None, active=True):
122 def create_ui_section_value(self, section, val, key=None, active=True):
123 new_ui = self.UiDbModel()
123 new_ui = self.UiDbModel()
124 new_ui.ui_section = section
124 new_ui.ui_section = section
125 new_ui.ui_value = val
125 new_ui.ui_value = val
126 new_ui.ui_active = active
126 new_ui.ui_active = active
127
127
128 repository_id = ''
128 repository_id = ''
129 if self.repo:
129 if self.repo:
130 repo = self._get_repo(self.repo)
130 repo = self._get_repo(self.repo)
131 repository_id = repo.repo_id
131 repository_id = repo.repo_id
132 new_ui.repository_id = repository_id
132 new_ui.repository_id = repository_id
133
133
134 if not key:
134 if not key:
135 # keys are unique so they need appended info
135 # keys are unique so they need appended info
136 if self.repo:
136 if self.repo:
137 key = sha1_safe(f'{section}{val}{repository_id}')
137 key = sha1_safe(f'{section}{val}{repository_id}')
138 else:
138 else:
139 key = sha1_safe(f'{section}{val}')
139 key = sha1_safe(f'{section}{val}')
140
140
141 new_ui.ui_key = key
141 new_ui.ui_key = key
142
142
143 Session().add(new_ui)
143 Session().add(new_ui)
144 return new_ui
144 return new_ui
145
145
146 def create_or_update_hook(self, key, value):
146 def create_or_update_hook(self, key, value):
147 ui = (
147 ui = (
148 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
148 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
149 self.UiDbModel())
149 self.UiDbModel())
150 ui.ui_section = self.HOOKS_SECTION
150 ui.ui_section = self.HOOKS_SECTION
151 ui.ui_active = True
151 ui.ui_active = True
152 ui.ui_key = key
152 ui.ui_key = key
153 ui.ui_value = value
153 ui.ui_value = value
154
154
155 if self.repo:
155 if self.repo:
156 repo = self._get_repo(self.repo)
156 repo = self._get_repo(self.repo)
157 repository_id = repo.repo_id
157 repository_id = repo.repo_id
158 ui.repository_id = repository_id
158 ui.repository_id = repository_id
159
159
160 Session().add(ui)
160 Session().add(ui)
161 return ui
161 return ui
162
162
163 def delete_ui(self, id_):
163 def delete_ui(self, id_):
164 ui = self.UiDbModel.get(id_)
164 ui = self.UiDbModel.get(id_)
165 if not ui:
165 if not ui:
166 raise SettingNotFound(id_)
166 raise SettingNotFound(id_)
167 Session().delete(ui)
167 Session().delete(ui)
168
168
169 def get_setting_by_name(self, name):
169 def get_setting_by_name(self, name):
170 q = self._get_settings_query()
170 q = self._get_settings_query()
171 q = q.filter(self.SettingsDbModel.app_settings_name == name)
171 q = q.filter(self.SettingsDbModel.app_settings_name == name)
172 return q.scalar()
172 return q.scalar()
173
173
174 def create_or_update_setting(
174 def create_or_update_setting(
175 self, name, val: Optional | str = Optional(''), type_: Optional | str = Optional('unicode')):
175 self, name, val: Optional | str = Optional(''), type_: Optional | str = Optional('unicode')):
176 """
176 """
177 Creates or updates RhodeCode setting. If updates are triggered, it will
177 Creates or updates RhodeCode setting. If updates are triggered, it will
178 only update parameters that are explicitly set Optional instance will
178 only update parameters that are explicitly set Optional instance will
179 be skipped
179 be skipped
180
180
181 :param name:
181 :param name:
182 :param val:
182 :param val:
183 :param type_:
183 :param type_:
184 :return:
184 :return:
185 """
185 """
186
186
187 res = self.get_setting_by_name(name)
187 res = self.get_setting_by_name(name)
188 repo = self._get_repo(self.repo) if self.repo else None
188 repo = self._get_repo(self.repo) if self.repo else None
189
189
190 if not res:
190 if not res:
191 val = Optional.extract(val)
191 val = Optional.extract(val)
192 type_ = Optional.extract(type_)
192 type_ = Optional.extract(type_)
193
193
194 args = (
194 args = (
195 (repo.repo_id, name, val, type_)
195 (repo.repo_id, name, val, type_)
196 if repo else (name, val, type_))
196 if repo else (name, val, type_))
197 res = self.SettingsDbModel(*args)
197 res = self.SettingsDbModel(*args)
198
198
199 else:
199 else:
200 if self.repo:
200 if self.repo:
201 res.repository_id = repo.repo_id
201 res.repository_id = repo.repo_id
202
202
203 res.app_settings_name = name
203 res.app_settings_name = name
204 if not isinstance(type_, Optional):
204 if not isinstance(type_, Optional):
205 # update if set
205 # update if set
206 res.app_settings_type = type_
206 res.app_settings_type = type_
207 if not isinstance(val, Optional):
207 if not isinstance(val, Optional):
208 # update if set
208 # update if set
209 res.app_settings_value = val
209 res.app_settings_value = val
210
210
211 Session().add(res)
211 Session().add(res)
212 return res
212 return res
213
213
214 def get_cache_region(self):
214 def get_cache_region(self):
215 repo = self._get_repo(self.repo) if self.repo else None
215 repo = self._get_repo(self.repo) if self.repo else None
216 cache_key = f"repo.v1.{repo.repo_id}" if repo else "repo.v1.ALL"
216 cache_key = f"repo.v1.{repo.repo_id}" if repo else "repo.v1.ALL"
217 cache_namespace_uid = f'cache_settings.{cache_key}'
217 cache_namespace_uid = f'cache_settings.{cache_key}'
218 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
218 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
219 return region, cache_namespace_uid
219 return region, cache_namespace_uid
220
220
221 def invalidate_settings_cache(self, hard=False):
221 def invalidate_settings_cache(self, hard=False):
222 region, namespace_key = self.get_cache_region()
222 region, namespace_key = self.get_cache_region()
223 log.debug('Invalidation cache [%s] region %s for cache_key: %s',
223 log.debug('Invalidation cache [%s] region %s for cache_key: %s',
224 'invalidate_settings_cache', region, namespace_key)
224 'invalidate_settings_cache', region, namespace_key)
225
225
226 # we use hard cleanup if invalidation is sent
226 # we use hard cleanup if invalidation is sent
227 rc_cache.clear_cache_namespace(region, namespace_key, method=rc_cache.CLEAR_DELETE)
227 rc_cache.clear_cache_namespace(region, namespace_key, method=rc_cache.CLEAR_DELETE)
228
228
229 def get_cache_call_method(self, cache=True):
229 def get_cache_call_method(self, cache=True):
230 region, cache_key = self.get_cache_region()
230 region, cache_key = self.get_cache_region()
231
231
232 @region.conditional_cache_on_arguments(condition=cache)
232 @region.conditional_cache_on_arguments(condition=cache)
233 def _get_all_settings(name, key):
233 def _get_all_settings(name, key):
234 q = self._get_settings_query()
234 q = self._get_settings_query()
235 if not q:
235 if not q:
236 raise Exception('Could not get application settings !')
236 raise Exception('Could not get application settings !')
237
237
238 settings = {
238 settings = {
239 self.get_keyname(res.app_settings_name): res.app_settings_value
239 self.get_keyname(res.app_settings_name): res.app_settings_value
240 for res in q
240 for res in q
241 }
241 }
242 return settings
242 return settings
243 return _get_all_settings
243 return _get_all_settings
244
244
245 def get_all_settings(self, cache=False, from_request=True):
245 def get_all_settings(self, cache=False, from_request=True):
246 # defines if we use GLOBAL, or PER_REPO
246 # defines if we use GLOBAL, or PER_REPO
247 repo = self._get_repo(self.repo) if self.repo else None
247 repo = self._get_repo(self.repo) if self.repo else None
248
248
249 # initially try the request context; this is the fastest
249 # initially try the request context; this is the fastest
250 # we only fetch global config, NOT for repo-specific
250 # we only fetch global config, NOT for repo-specific
251 if from_request and not repo:
251 if from_request and not repo:
252 request = get_current_request()
252 request = get_current_request()
253
253
254 if request and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
254 if request and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
255 rc_config = request.call_context.rc_config
255 rc_config = request.call_context.rc_config
256 if rc_config:
256 if rc_config:
257 return rc_config
257 return rc_config
258
258
259 _region, cache_key = self.get_cache_region()
259 _region, cache_key = self.get_cache_region()
260 _get_all_settings = self.get_cache_call_method(cache=cache)
260 _get_all_settings = self.get_cache_call_method(cache=cache)
261
261
262 start = time.time()
262 start = time.time()
263 result = _get_all_settings('rhodecode_settings', cache_key)
263 result = _get_all_settings('rhodecode_settings', cache_key)
264 compute_time = time.time() - start
264 compute_time = time.time() - start
265 log.debug('cached method:%s took %.4fs', _get_all_settings.__name__, compute_time)
265 log.debug('cached method:%s took %.4fs', _get_all_settings.__name__, compute_time)
266
266
267 statsd = StatsdClient.statsd
267 statsd = StatsdClient.statsd
268 if statsd:
268 if statsd:
269 elapsed_time_ms = round(1000.0 * compute_time) # use ms only
269 elapsed_time_ms = round(1000.0 * compute_time) # use ms only
270 statsd.timing("rhodecode_settings_timing.histogram", elapsed_time_ms,
270 statsd.timing("rhodecode_settings_timing.histogram", elapsed_time_ms,
271 use_decimals=False)
271 use_decimals=False)
272
272
273 log.debug('Fetching app settings for key: %s took: %.4fs: cache: %s', cache_key, compute_time, cache)
273 log.debug('Fetching app settings for key: %s took: %.4fs: cache: %s', cache_key, compute_time, cache)
274
274
275 return result
275 return result
276
276
277 def get_auth_settings(self):
277 def get_auth_settings(self):
278 q = self._get_settings_query()
278 q = self._get_settings_query()
279 q = q.filter(
279 q = q.filter(
280 self.SettingsDbModel.app_settings_name.startswith('auth_'))
280 self.SettingsDbModel.app_settings_name.startswith('auth_'))
281 rows = q.all()
281 rows = q.all()
282 auth_settings = {
282 auth_settings = {
283 row.app_settings_name: row.app_settings_value for row in rows}
283 row.app_settings_name: row.app_settings_value for row in rows}
284 return auth_settings
284 return auth_settings
285
285
286 def get_auth_plugins(self):
286 def get_auth_plugins(self):
287 auth_plugins = self.get_setting_by_name("auth_plugins")
287 auth_plugins = self.get_setting_by_name("auth_plugins")
288 return auth_plugins.app_settings_value
288 return auth_plugins.app_settings_value
289
289
290 def get_default_repo_settings(self, strip_prefix=False):
290 def get_default_repo_settings(self, strip_prefix=False):
291 q = self._get_settings_query()
291 q = self._get_settings_query()
292 q = q.filter(
292 q = q.filter(
293 self.SettingsDbModel.app_settings_name.startswith('default_'))
293 self.SettingsDbModel.app_settings_name.startswith('default_'))
294 rows = q.all()
294 rows = q.all()
295
295
296 result = {}
296 result = {}
297 for row in rows:
297 for row in rows:
298 key = row.app_settings_name
298 key = row.app_settings_name
299 if strip_prefix:
299 if strip_prefix:
300 key = remove_prefix(key, prefix='default_')
300 key = remove_prefix(key, prefix='default_')
301 result.update({key: row.app_settings_value})
301 result.update({key: row.app_settings_value})
302 return result
302 return result
303
303
304 def get_repo(self):
304 def get_repo(self):
305 repo = self._get_repo(self.repo)
305 repo = self._get_repo(self.repo)
306 if not repo:
306 if not repo:
307 raise Exception(
307 raise Exception(
308 f'Repository `{self.repo}` cannot be found inside the database')
308 f'Repository `{self.repo}` cannot be found inside the database')
309 return repo
309 return repo
310
310
311 def _filter_by_repo(self, model, query):
311 def _filter_by_repo(self, model, query):
312 if self.repo:
312 if self.repo:
313 repo = self.get_repo()
313 repo = self.get_repo()
314 query = query.filter(model.repository_id == repo.repo_id)
314 query = query.filter(model.repository_id == repo.repo_id)
315 return query
315 return query
316
316
317 def _get_hooks(self, query):
317 def _get_hooks(self, query):
318 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
318 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
319 query = self._filter_by_repo(RepoRhodeCodeUi, query)
319 query = self._filter_by_repo(RepoRhodeCodeUi, query)
320 return query.all()
320 return query.all()
321
321
322 def _get_settings_query(self):
322 def _get_settings_query(self):
323 q = self.SettingsDbModel.query()
323 q = self.SettingsDbModel.query()
324 return self._filter_by_repo(RepoRhodeCodeSetting, q)
324 return self._filter_by_repo(RepoRhodeCodeSetting, q)
325
325
326 def list_enabled_social_plugins(self, settings):
326 def list_enabled_social_plugins(self, settings):
327 enabled = []
327 enabled = []
328 for plug in SOCIAL_PLUGINS_LIST:
328 for plug in SOCIAL_PLUGINS_LIST:
329 if str2bool(settings.get(f'rhodecode_auth_{plug}_enabled')):
329 if str2bool(settings.get(f'rhodecode_auth_{plug}_enabled')):
330 enabled.append(plug)
330 enabled.append(plug)
331 return enabled
331 return enabled
332
332
333
333
334 def assert_repo_settings(func):
334 def assert_repo_settings(func):
335 @functools.wraps(func)
335 @functools.wraps(func)
336 def _wrapper(self, *args, **kwargs):
336 def _wrapper(self, *args, **kwargs):
337 if not self.repo_settings:
337 if not self.repo_settings:
338 raise Exception('Repository is not specified')
338 raise Exception('Repository is not specified')
339 return func(self, *args, **kwargs)
339 return func(self, *args, **kwargs)
340 return _wrapper
340 return _wrapper
341
341
342
342
343 class IssueTrackerSettingsModel(object):
343 class IssueTrackerSettingsModel(object):
344 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
344 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
345 SETTINGS_PREFIX = 'issuetracker_'
345 SETTINGS_PREFIX = 'issuetracker_'
346
346
347 def __init__(self, sa=None, repo=None):
347 def __init__(self, sa=None, repo=None):
348 self.global_settings = SettingsModel(sa=sa)
348 self.global_settings = SettingsModel(sa=sa)
349 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
349 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
350
350
351 @property
351 @property
352 def inherit_global_settings(self):
352 def inherit_global_settings(self):
353 if not self.repo_settings:
353 if not self.repo_settings:
354 return True
354 return True
355 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
355 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
356 return setting.app_settings_value if setting else True
356 return setting.app_settings_value if setting else True
357
357
358 @inherit_global_settings.setter
358 @inherit_global_settings.setter
359 def inherit_global_settings(self, value):
359 def inherit_global_settings(self, value):
360 if self.repo_settings:
360 if self.repo_settings:
361 settings = self.repo_settings.create_or_update_setting(
361 settings = self.repo_settings.create_or_update_setting(
362 self.INHERIT_SETTINGS, value, type_='bool')
362 self.INHERIT_SETTINGS, value, type_='bool')
363 Session().add(settings)
363 Session().add(settings)
364
364
365 def _get_keyname(self, key, uid, prefix='rhodecode_'):
365 def _get_keyname(self, key, uid, prefix='rhodecode_'):
366 return f'{prefix}{self.SETTINGS_PREFIX}{key}_{uid}'
366 return f'{prefix}{self.SETTINGS_PREFIX}{key}_{uid}'
367
367
368 def _make_dict_for_settings(self, qs):
368 def _make_dict_for_settings(self, qs):
369 prefix_match = self._get_keyname('pat', '',)
369 prefix_match = self._get_keyname('pat', '',)
370
370
371 issuetracker_entries = {}
371 issuetracker_entries = {}
372 # create keys
372 # create keys
373 for k, v in qs.items():
373 for k, v in qs.items():
374 if k.startswith(prefix_match):
374 if k.startswith(prefix_match):
375 uid = k[len(prefix_match):]
375 uid = k[len(prefix_match):]
376 issuetracker_entries[uid] = None
376 issuetracker_entries[uid] = None
377
377
378 def url_cleaner(input_str):
378 def url_cleaner(input_str):
379 input_str = input_str.replace('"', '').replace("'", '')
379 input_str = input_str.replace('"', '').replace("'", '')
380 input_str = sanitize_html(input_str, strip=True)
380 input_str = sanitize_html(input_str, strip=True)
381 return input_str
381 return input_str
382
382
383 # populate
383 # populate
384 for uid in issuetracker_entries:
384 for uid in issuetracker_entries:
385 url_data = qs.get(self._get_keyname('url', uid))
385 url_data = qs.get(self._get_keyname('url', uid))
386
386
387 pat = qs.get(self._get_keyname('pat', uid))
387 pat = qs.get(self._get_keyname('pat', uid))
388 try:
388 try:
389 pat_compiled = re.compile(r'%s' % pat)
389 pat_compiled = re.compile(r'%s' % pat)
390 except re.error:
390 except re.error:
391 pat_compiled = None
391 pat_compiled = None
392
392
393 issuetracker_entries[uid] = AttributeDict({
393 issuetracker_entries[uid] = AttributeDict({
394 'pat': pat,
394 'pat': pat,
395 'pat_compiled': pat_compiled,
395 'pat_compiled': pat_compiled,
396 'url': url_cleaner(
396 'url': url_cleaner(
397 qs.get(self._get_keyname('url', uid)) or ''),
397 qs.get(self._get_keyname('url', uid)) or ''),
398 'pref': sanitize_html(
398 'pref': sanitize_html(
399 qs.get(self._get_keyname('pref', uid)) or ''),
399 qs.get(self._get_keyname('pref', uid)) or ''),
400 'desc': qs.get(
400 'desc': qs.get(
401 self._get_keyname('desc', uid)),
401 self._get_keyname('desc', uid)),
402 })
402 })
403
403
404 return issuetracker_entries
404 return issuetracker_entries
405
405
406 def get_global_settings(self, cache=False):
406 def get_global_settings(self, cache=False):
407 """
407 """
408 Returns list of global issue tracker settings
408 Returns list of global issue tracker settings
409 """
409 """
410 defaults = self.global_settings.get_all_settings(cache=cache)
410 defaults = self.global_settings.get_all_settings(cache=cache)
411 settings = self._make_dict_for_settings(defaults)
411 settings = self._make_dict_for_settings(defaults)
412 return settings
412 return settings
413
413
414 def get_repo_settings(self, cache=False):
414 def get_repo_settings(self, cache=False):
415 """
415 """
416 Returns list of issue tracker settings per repository
416 Returns list of issue tracker settings per repository
417 """
417 """
418 if not self.repo_settings:
418 if not self.repo_settings:
419 raise Exception('Repository is not specified')
419 raise Exception('Repository is not specified')
420 all_settings = self.repo_settings.get_all_settings(cache=cache)
420 all_settings = self.repo_settings.get_all_settings(cache=cache)
421 settings = self._make_dict_for_settings(all_settings)
421 settings = self._make_dict_for_settings(all_settings)
422 return settings
422 return settings
423
423
424 def get_settings(self, cache=False):
424 def get_settings(self, cache=False):
425 if self.inherit_global_settings:
425 if self.inherit_global_settings:
426 return self.get_global_settings(cache=cache)
426 return self.get_global_settings(cache=cache)
427 else:
427 else:
428 return self.get_repo_settings(cache=cache)
428 return self.get_repo_settings(cache=cache)
429
429
430 def delete_entries(self, uid):
430 def delete_entries(self, uid):
431 if self.repo_settings:
431 if self.repo_settings:
432 all_patterns = self.get_repo_settings()
432 all_patterns = self.get_repo_settings()
433 settings_model = self.repo_settings
433 settings_model = self.repo_settings
434 else:
434 else:
435 all_patterns = self.get_global_settings()
435 all_patterns = self.get_global_settings()
436 settings_model = self.global_settings
436 settings_model = self.global_settings
437 entries = all_patterns.get(uid, [])
437 entries = all_patterns.get(uid, [])
438
438
439 for del_key in entries:
439 for del_key in entries:
440 setting_name = self._get_keyname(del_key, uid, prefix='')
440 setting_name = self._get_keyname(del_key, uid, prefix='')
441 entry = settings_model.get_setting_by_name(setting_name)
441 entry = settings_model.get_setting_by_name(setting_name)
442 if entry:
442 if entry:
443 Session().delete(entry)
443 Session().delete(entry)
444
444
445 Session().commit()
445 Session().commit()
446
446
447 def create_or_update_setting(
447 def create_or_update_setting(
448 self, name, val=Optional(''), type_=Optional('unicode')):
448 self, name, val=Optional(''), type_=Optional('unicode')):
449 if self.repo_settings:
449 if self.repo_settings:
450 setting = self.repo_settings.create_or_update_setting(
450 setting = self.repo_settings.create_or_update_setting(
451 name, val, type_)
451 name, val, type_)
452 else:
452 else:
453 setting = self.global_settings.create_or_update_setting(
453 setting = self.global_settings.create_or_update_setting(
454 name, val, type_)
454 name, val, type_)
455 return setting
455 return setting
456
456
457
457
458 class VcsSettingsModel(object):
458 class VcsSettingsModel(object):
459
459
460 INHERIT_SETTINGS = 'inherit_vcs_settings'
460 INHERIT_SETTINGS = 'inherit_vcs_settings'
461 GENERAL_SETTINGS = (
461 GENERAL_SETTINGS = (
462 'use_outdated_comments',
462 'use_outdated_comments',
463 'pr_merge_enabled',
463 'pr_merge_enabled',
464 'hg_use_rebase_for_merging',
464 'hg_use_rebase_for_merging',
465 'hg_close_branch_before_merging',
465 'hg_close_branch_before_merging',
466 'git_use_rebase_for_merging',
466 'git_use_rebase_for_merging',
467 'git_close_branch_before_merging',
467 'git_close_branch_before_merging',
468 'diff_cache',
468 'diff_cache',
469 )
469 )
470
470
471 HOOKS_SETTINGS = (
471 HOOKS_SETTINGS = (
472 ('hooks', 'changegroup.repo_size'),
472 ('hooks', 'changegroup.repo_size'),
473 ('hooks', 'changegroup.push_logger'),
473 ('hooks', 'changegroup.push_logger'),
474 ('hooks', 'outgoing.pull_logger'),
474 ('hooks', 'outgoing.pull_logger'),
475 )
475 )
476 HG_SETTINGS = (
476 HG_SETTINGS = (
477 ('extensions', 'largefiles'),
477 ('extensions', 'largefiles'),
478 ('phases', 'publish'),
478 ('phases', 'publish'),
479 ('extensions', 'evolve'),
479 ('extensions', 'evolve'),
480 ('extensions', 'topic'),
480 ('extensions', 'topic'),
481 ('experimental', 'evolution'),
481 ('experimental', 'evolution'),
482 ('experimental', 'evolution.exchange'),
482 ('experimental', 'evolution.exchange'),
483 )
483 )
484 GIT_SETTINGS = (
484 GIT_SETTINGS = (
485 ('vcs_git_lfs', 'enabled'),
485 ('vcs_git_lfs', 'enabled'),
486 )
486 )
487 GLOBAL_HG_SETTINGS = (
487 GLOBAL_HG_SETTINGS = (
488 ('extensions', 'largefiles'),
488 ('extensions', 'largefiles'),
489 ('largefiles', 'usercache'),
490 ('phases', 'publish'),
489 ('phases', 'publish'),
491 ('extensions', 'evolve'),
490 ('extensions', 'evolve'),
492 ('extensions', 'topic'),
491 ('extensions', 'topic'),
493 ('experimental', 'evolution'),
492 ('experimental', 'evolution'),
494 ('experimental', 'evolution.exchange'),
493 ('experimental', 'evolution.exchange'),
495 )
494 )
496
495
497 GLOBAL_GIT_SETTINGS = (
496 GLOBAL_GIT_SETTINGS = (
498 ('vcs_git_lfs', 'enabled'),
497 ('vcs_git_lfs', 'enabled'),
499 ('vcs_git_lfs', 'store_location')
500 )
498 )
501
499
502 SVN_BRANCH_SECTION = 'vcs_svn_branch'
500 SVN_BRANCH_SECTION = 'vcs_svn_branch'
503 SVN_TAG_SECTION = 'vcs_svn_tag'
501 SVN_TAG_SECTION = 'vcs_svn_tag'
504 SSL_SETTING = ('web', 'push_ssl')
502 SSL_SETTING = ('web', 'push_ssl')
505 PATH_SETTING = ('paths', '/')
503 PATH_SETTING = ('paths', '/')
506
504
507 def __init__(self, sa=None, repo=None):
505 def __init__(self, sa=None, repo=None):
508 self.global_settings = SettingsModel(sa=sa)
506 self.global_settings = SettingsModel(sa=sa)
509 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
507 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
510 self._ui_settings = (
508 self._ui_settings = (
511 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
509 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
512 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
510 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
513
511
514 @property
512 @property
515 @assert_repo_settings
513 @assert_repo_settings
516 def inherit_global_settings(self):
514 def inherit_global_settings(self):
517 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
515 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
518 return setting.app_settings_value if setting else True
516 return setting.app_settings_value if setting else True
519
517
520 @inherit_global_settings.setter
518 @inherit_global_settings.setter
521 @assert_repo_settings
519 @assert_repo_settings
522 def inherit_global_settings(self, value):
520 def inherit_global_settings(self, value):
523 self.repo_settings.create_or_update_setting(
521 self.repo_settings.create_or_update_setting(
524 self.INHERIT_SETTINGS, value, type_='bool')
522 self.INHERIT_SETTINGS, value, type_='bool')
525
523
526 def get_keyname(self, key_name, prefix='rhodecode_'):
524 def get_keyname(self, key_name, prefix='rhodecode_'):
527 return f'{prefix}{key_name}'
525 return f'{prefix}{key_name}'
528
526
529 def get_global_svn_branch_patterns(self):
527 def get_global_svn_branch_patterns(self):
530 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
528 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
531
529
532 @assert_repo_settings
530 @assert_repo_settings
533 def get_repo_svn_branch_patterns(self):
531 def get_repo_svn_branch_patterns(self):
534 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
532 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
535
533
536 def get_global_svn_tag_patterns(self):
534 def get_global_svn_tag_patterns(self):
537 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
535 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
538
536
539 @assert_repo_settings
537 @assert_repo_settings
540 def get_repo_svn_tag_patterns(self):
538 def get_repo_svn_tag_patterns(self):
541 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
539 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
542
540
543 def get_global_settings(self):
541 def get_global_settings(self):
544 return self._collect_all_settings(global_=True)
542 return self._collect_all_settings(global_=True)
545
543
546 @assert_repo_settings
544 @assert_repo_settings
547 def get_repo_settings(self):
545 def get_repo_settings(self):
548 return self._collect_all_settings(global_=False)
546 return self._collect_all_settings(global_=False)
549
547
550 @assert_repo_settings
548 @assert_repo_settings
551 def get_repo_settings_inherited(self):
549 def get_repo_settings_inherited(self):
552 global_settings = self.get_global_settings()
550 global_settings = self.get_global_settings()
553 global_settings.update(self.get_repo_settings())
551 global_settings.update(self.get_repo_settings())
554 return global_settings
552 return global_settings
555
553
556 @assert_repo_settings
554 @assert_repo_settings
557 def create_or_update_repo_settings(
555 def create_or_update_repo_settings(
558 self, data, inherit_global_settings=False):
556 self, data, inherit_global_settings=False):
559 from rhodecode.model.scm import ScmModel
557 from rhodecode.model.scm import ScmModel
560
558
561 self.inherit_global_settings = inherit_global_settings
559 self.inherit_global_settings = inherit_global_settings
562
560
563 repo = self.repo_settings.get_repo()
561 repo = self.repo_settings.get_repo()
564 if not inherit_global_settings:
562 if not inherit_global_settings:
565 if repo.repo_type == 'svn':
563 if repo.repo_type == 'svn':
566 self.create_repo_svn_settings(data)
564 self.create_repo_svn_settings(data)
567 else:
565 else:
568 self.create_or_update_repo_hook_settings(data)
566 self.create_or_update_repo_hook_settings(data)
569 self.create_or_update_repo_pr_settings(data)
567 self.create_or_update_repo_pr_settings(data)
570
568
571 if repo.repo_type == 'hg':
569 if repo.repo_type == 'hg':
572 self.create_or_update_repo_hg_settings(data)
570 self.create_or_update_repo_hg_settings(data)
573
571
574 if repo.repo_type == 'git':
572 if repo.repo_type == 'git':
575 self.create_or_update_repo_git_settings(data)
573 self.create_or_update_repo_git_settings(data)
576
574
577 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
575 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
578
576
579 @assert_repo_settings
577 @assert_repo_settings
580 def create_or_update_repo_hook_settings(self, data):
578 def create_or_update_repo_hook_settings(self, data):
581 for section, key in self.HOOKS_SETTINGS:
579 for section, key in self.HOOKS_SETTINGS:
582 data_key = self._get_form_ui_key(section, key)
580 data_key = self._get_form_ui_key(section, key)
583 if data_key not in data:
581 if data_key not in data:
584 raise ValueError(
582 raise ValueError(
585 f'The given data does not contain {data_key} key')
583 f'The given data does not contain {data_key} key')
586
584
587 active = data.get(data_key)
585 active = data.get(data_key)
588 repo_setting = self.repo_settings.get_ui_by_section_and_key(
586 repo_setting = self.repo_settings.get_ui_by_section_and_key(
589 section, key)
587 section, key)
590 if not repo_setting:
588 if not repo_setting:
591 global_setting = self.global_settings.\
589 global_setting = self.global_settings.\
592 get_ui_by_section_and_key(section, key)
590 get_ui_by_section_and_key(section, key)
593 self.repo_settings.create_ui_section_value(
591 self.repo_settings.create_ui_section_value(
594 section, global_setting.ui_value, key=key, active=active)
592 section, global_setting.ui_value, key=key, active=active)
595 else:
593 else:
596 repo_setting.ui_active = active
594 repo_setting.ui_active = active
597 Session().add(repo_setting)
595 Session().add(repo_setting)
598
596
599 def update_global_hook_settings(self, data):
597 def update_global_hook_settings(self, data):
600 for section, key in self.HOOKS_SETTINGS:
598 for section, key in self.HOOKS_SETTINGS:
601 data_key = self._get_form_ui_key(section, key)
599 data_key = self._get_form_ui_key(section, key)
602 if data_key not in data:
600 if data_key not in data:
603 raise ValueError(
601 raise ValueError(
604 f'The given data does not contain {data_key} key')
602 f'The given data does not contain {data_key} key')
605 active = data.get(data_key)
603 active = data.get(data_key)
606 repo_setting = self.global_settings.get_ui_by_section_and_key(
604 repo_setting = self.global_settings.get_ui_by_section_and_key(
607 section, key)
605 section, key)
608 repo_setting.ui_active = active
606 repo_setting.ui_active = active
609 Session().add(repo_setting)
607 Session().add(repo_setting)
610
608
611 @assert_repo_settings
609 @assert_repo_settings
612 def create_or_update_repo_pr_settings(self, data):
610 def create_or_update_repo_pr_settings(self, data):
613 return self._create_or_update_general_settings(
611 return self._create_or_update_general_settings(
614 self.repo_settings, data)
612 self.repo_settings, data)
615
613
616 def create_or_update_global_pr_settings(self, data):
614 def create_or_update_global_pr_settings(self, data):
617 return self._create_or_update_general_settings(
615 return self._create_or_update_general_settings(
618 self.global_settings, data)
616 self.global_settings, data)
619
617
620 @assert_repo_settings
618 @assert_repo_settings
621 def create_repo_svn_settings(self, data):
619 def create_repo_svn_settings(self, data):
622 return self._create_svn_settings(self.repo_settings, data)
620 return self._create_svn_settings(self.repo_settings, data)
623
621
624 def _set_evolution(self, settings, is_enabled):
622 def _set_evolution(self, settings, is_enabled):
625 if is_enabled:
623 if is_enabled:
626 # if evolve is active set evolution=all
624 # if evolve is active set evolution=all
627
625
628 self._create_or_update_ui(
626 self._create_or_update_ui(
629 settings, *('experimental', 'evolution'), value='all',
627 settings, *('experimental', 'evolution'), value='all',
630 active=True)
628 active=True)
631 self._create_or_update_ui(
629 self._create_or_update_ui(
632 settings, *('experimental', 'evolution.exchange'), value='yes',
630 settings, *('experimental', 'evolution.exchange'), value='yes',
633 active=True)
631 active=True)
634 # if evolve is active set topics server support
632 # if evolve is active set topics server support
635 self._create_or_update_ui(
633 self._create_or_update_ui(
636 settings, *('extensions', 'topic'), value='',
634 settings, *('extensions', 'topic'), value='',
637 active=True)
635 active=True)
638
636
639 else:
637 else:
640 self._create_or_update_ui(
638 self._create_or_update_ui(
641 settings, *('experimental', 'evolution'), value='',
639 settings, *('experimental', 'evolution'), value='',
642 active=False)
640 active=False)
643 self._create_or_update_ui(
641 self._create_or_update_ui(
644 settings, *('experimental', 'evolution.exchange'), value='no',
642 settings, *('experimental', 'evolution.exchange'), value='no',
645 active=False)
643 active=False)
646 self._create_or_update_ui(
644 self._create_or_update_ui(
647 settings, *('extensions', 'topic'), value='',
645 settings, *('extensions', 'topic'), value='',
648 active=False)
646 active=False)
649
647
650 @assert_repo_settings
648 @assert_repo_settings
651 def create_or_update_repo_hg_settings(self, data):
649 def create_or_update_repo_hg_settings(self, data):
652 largefiles, phases, evolve = \
650 largefiles, phases, evolve = \
653 self.HG_SETTINGS[:3]
651 self.HG_SETTINGS[:3]
654 largefiles_key, phases_key, evolve_key = \
652 largefiles_key, phases_key, evolve_key = \
655 self._get_settings_keys(self.HG_SETTINGS[:3], data)
653 self._get_settings_keys(self.HG_SETTINGS[:3], data)
656
654
657 self._create_or_update_ui(
655 self._create_or_update_ui(
658 self.repo_settings, *largefiles, value='',
656 self.repo_settings, *largefiles, value='',
659 active=data[largefiles_key])
657 active=data[largefiles_key])
660 self._create_or_update_ui(
658 self._create_or_update_ui(
661 self.repo_settings, *evolve, value='',
659 self.repo_settings, *evolve, value='',
662 active=data[evolve_key])
660 active=data[evolve_key])
663 self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
661 self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
664
662
665 self._create_or_update_ui(
663 self._create_or_update_ui(
666 self.repo_settings, *phases, value=safe_str(data[phases_key]))
664 self.repo_settings, *phases, value=safe_str(data[phases_key]))
667
665
668 def create_or_update_global_hg_settings(self, data):
666 def create_or_update_global_hg_settings(self, data):
669 opts_len = 4
667 opts_len = 3
670 largefiles, largefiles_store, phases, evolve \
668 largefiles, phases, evolve \
671 = self.GLOBAL_HG_SETTINGS[:opts_len]
669 = self.GLOBAL_HG_SETTINGS[:opts_len]
672 largefiles_key, largefiles_store_key, phases_key, evolve_key \
670 largefiles_key, phases_key, evolve_key \
673 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:opts_len], data)
671 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:opts_len], data)
674
672
675 self._create_or_update_ui(
673 self._create_or_update_ui(
676 self.global_settings, *largefiles, value='',
674 self.global_settings, *largefiles, value='',
677 active=data[largefiles_key])
675 active=data[largefiles_key])
678 self._create_or_update_ui(
676 self._create_or_update_ui(
679 self.global_settings, *largefiles_store, value=data[largefiles_store_key])
680 self._create_or_update_ui(
681 self.global_settings, *phases, value=safe_str(data[phases_key]))
677 self.global_settings, *phases, value=safe_str(data[phases_key]))
682 self._create_or_update_ui(
678 self._create_or_update_ui(
683 self.global_settings, *evolve, value='',
679 self.global_settings, *evolve, value='',
684 active=data[evolve_key])
680 active=data[evolve_key])
685 self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
681 self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
686
682
687 def create_or_update_repo_git_settings(self, data):
683 def create_or_update_repo_git_settings(self, data):
688 # NOTE(marcink): # comma makes unpack work properly
684 # NOTE(marcink): # comma makes unpack work properly
689 lfs_enabled, \
685 lfs_enabled, \
690 = self.GIT_SETTINGS
686 = self.GIT_SETTINGS
691
687
692 lfs_enabled_key, \
688 lfs_enabled_key, \
693 = self._get_settings_keys(self.GIT_SETTINGS, data)
689 = self._get_settings_keys(self.GIT_SETTINGS, data)
694
690
695 self._create_or_update_ui(
691 self._create_or_update_ui(
696 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
692 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
697 active=data[lfs_enabled_key])
693 active=data[lfs_enabled_key])
698
694
699 def create_or_update_global_git_settings(self, data):
695 def create_or_update_global_git_settings(self, data):
700 lfs_enabled, lfs_store_location \
696 lfs_enabled = self.GLOBAL_GIT_SETTINGS[0]
701 = self.GLOBAL_GIT_SETTINGS
697 lfs_enabled_key = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)[0]
702 lfs_enabled_key, lfs_store_location_key \
703 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
704
698
705 self._create_or_update_ui(
699 self._create_or_update_ui(
706 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
700 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
707 active=data[lfs_enabled_key])
701 active=data[lfs_enabled_key])
708 self._create_or_update_ui(
709 self.global_settings, *lfs_store_location,
710 value=data[lfs_store_location_key])
711
702
712 def create_or_update_global_svn_settings(self, data):
703 def create_or_update_global_svn_settings(self, data):
713 # branch/tags patterns
704 # branch/tags patterns
714 self._create_svn_settings(self.global_settings, data)
705 self._create_svn_settings(self.global_settings, data)
715
706
716 def update_global_ssl_setting(self, value):
707 def update_global_ssl_setting(self, value):
717 self._create_or_update_ui(
708 self._create_or_update_ui(
718 self.global_settings, *self.SSL_SETTING, value=value)
709 self.global_settings, *self.SSL_SETTING, value=value)
719
710
720 @assert_repo_settings
711 @assert_repo_settings
721 def delete_repo_svn_pattern(self, id_):
712 def delete_repo_svn_pattern(self, id_):
722 ui = self.repo_settings.UiDbModel.get(id_)
713 ui = self.repo_settings.UiDbModel.get(id_)
723 if ui and ui.repository.repo_name == self.repo_settings.repo:
714 if ui and ui.repository.repo_name == self.repo_settings.repo:
724 # only delete if it's the same repo as initialized settings
715 # only delete if it's the same repo as initialized settings
725 self.repo_settings.delete_ui(id_)
716 self.repo_settings.delete_ui(id_)
726 else:
717 else:
727 # raise error as if we wouldn't find this option
718 # raise error as if we wouldn't find this option
728 self.repo_settings.delete_ui(-1)
719 self.repo_settings.delete_ui(-1)
729
720
730 def delete_global_svn_pattern(self, id_):
721 def delete_global_svn_pattern(self, id_):
731 self.global_settings.delete_ui(id_)
722 self.global_settings.delete_ui(id_)
732
723
733 @assert_repo_settings
724 @assert_repo_settings
734 def get_repo_ui_settings(self, section=None, key=None):
725 def get_repo_ui_settings(self, section=None, key=None):
735 global_uis = self.global_settings.get_ui(section, key)
726 global_uis = self.global_settings.get_ui(section, key)
736 repo_uis = self.repo_settings.get_ui(section, key)
727 repo_uis = self.repo_settings.get_ui(section, key)
737
728
738 filtered_repo_uis = self._filter_ui_settings(repo_uis)
729 filtered_repo_uis = self._filter_ui_settings(repo_uis)
739 filtered_repo_uis_keys = [
730 filtered_repo_uis_keys = [
740 (s.section, s.key) for s in filtered_repo_uis]
731 (s.section, s.key) for s in filtered_repo_uis]
741
732
742 def _is_global_ui_filtered(ui):
733 def _is_global_ui_filtered(ui):
743 return (
734 return (
744 (ui.section, ui.key) in filtered_repo_uis_keys
735 (ui.section, ui.key) in filtered_repo_uis_keys
745 or ui.section in self._svn_sections)
736 or ui.section in self._svn_sections)
746
737
747 filtered_global_uis = [
738 filtered_global_uis = [
748 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
739 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
749
740
750 return filtered_global_uis + filtered_repo_uis
741 return filtered_global_uis + filtered_repo_uis
751
742
752 def get_global_ui_settings(self, section=None, key=None):
743 def get_global_ui_settings(self, section=None, key=None):
753 return self.global_settings.get_ui(section, key)
744 return self.global_settings.get_ui(section, key)
754
745
755 def get_ui_settings_as_config_obj(self, section=None, key=None):
746 def get_ui_settings_as_config_obj(self, section=None, key=None):
756 config = base.Config()
747 config = base.Config()
757
748
758 ui_settings = self.get_ui_settings(section=section, key=key)
749 ui_settings = self.get_ui_settings(section=section, key=key)
759
750
760 for entry in ui_settings:
751 for entry in ui_settings:
761 config.set(entry.section, entry.key, entry.value)
752 config.set(entry.section, entry.key, entry.value)
762
753
763 return config
754 return config
764
755
765 def get_ui_settings(self, section=None, key=None):
756 def get_ui_settings(self, section=None, key=None):
766 if not self.repo_settings or self.inherit_global_settings:
757 if not self.repo_settings or self.inherit_global_settings:
767 return self.get_global_ui_settings(section, key)
758 return self.get_global_ui_settings(section, key)
768 else:
759 else:
769 return self.get_repo_ui_settings(section, key)
760 return self.get_repo_ui_settings(section, key)
770
761
771 def get_svn_patterns(self, section=None):
762 def get_svn_patterns(self, section=None):
772 if not self.repo_settings:
763 if not self.repo_settings:
773 return self.get_global_ui_settings(section)
764 return self.get_global_ui_settings(section)
774 else:
765 else:
775 return self.get_repo_ui_settings(section)
766 return self.get_repo_ui_settings(section)
776
767
777 @assert_repo_settings
768 @assert_repo_settings
778 def get_repo_general_settings(self):
769 def get_repo_general_settings(self):
779 global_settings = self.global_settings.get_all_settings()
770 global_settings = self.global_settings.get_all_settings()
780 repo_settings = self.repo_settings.get_all_settings()
771 repo_settings = self.repo_settings.get_all_settings()
781 filtered_repo_settings = self._filter_general_settings(repo_settings)
772 filtered_repo_settings = self._filter_general_settings(repo_settings)
782 global_settings.update(filtered_repo_settings)
773 global_settings.update(filtered_repo_settings)
783 return global_settings
774 return global_settings
784
775
785 def get_global_general_settings(self):
776 def get_global_general_settings(self):
786 return self.global_settings.get_all_settings()
777 return self.global_settings.get_all_settings()
787
778
788 def get_general_settings(self):
779 def get_general_settings(self):
789 if not self.repo_settings or self.inherit_global_settings:
780 if not self.repo_settings or self.inherit_global_settings:
790 return self.get_global_general_settings()
781 return self.get_global_general_settings()
791 else:
782 else:
792 return self.get_repo_general_settings()
783 return self.get_repo_general_settings()
793
784
794 def _filter_ui_settings(self, settings):
785 def _filter_ui_settings(self, settings):
795 filtered_settings = [
786 filtered_settings = [
796 s for s in settings if self._should_keep_setting(s)]
787 s for s in settings if self._should_keep_setting(s)]
797 return filtered_settings
788 return filtered_settings
798
789
799 def _should_keep_setting(self, setting):
790 def _should_keep_setting(self, setting):
800 keep = (
791 keep = (
801 (setting.section, setting.key) in self._ui_settings or
792 (setting.section, setting.key) in self._ui_settings or
802 setting.section in self._svn_sections)
793 setting.section in self._svn_sections)
803 return keep
794 return keep
804
795
805 def _filter_general_settings(self, settings):
796 def _filter_general_settings(self, settings):
806 keys = [self.get_keyname(key) for key in self.GENERAL_SETTINGS]
797 keys = [self.get_keyname(key) for key in self.GENERAL_SETTINGS]
807 return {
798 return {
808 k: settings[k]
799 k: settings[k]
809 for k in settings if k in keys}
800 for k in settings if k in keys}
810
801
811 def _collect_all_settings(self, global_=False):
802 def _collect_all_settings(self, global_=False):
812 settings = self.global_settings if global_ else self.repo_settings
803 settings = self.global_settings if global_ else self.repo_settings
813 result = {}
804 result = {}
814
805
815 for section, key in self._ui_settings:
806 for section, key in self._ui_settings:
816 ui = settings.get_ui_by_section_and_key(section, key)
807 ui = settings.get_ui_by_section_and_key(section, key)
817 result_key = self._get_form_ui_key(section, key)
808 result_key = self._get_form_ui_key(section, key)
818
809
819 if ui:
810 if ui:
820 if section in ('hooks', 'extensions'):
811 if section in ('hooks', 'extensions'):
821 result[result_key] = ui.ui_active
812 result[result_key] = ui.ui_active
822 elif result_key in ['vcs_git_lfs_enabled']:
813 elif result_key in ['vcs_git_lfs_enabled']:
823 result[result_key] = ui.ui_active
814 result[result_key] = ui.ui_active
824 else:
815 else:
825 result[result_key] = ui.ui_value
816 result[result_key] = ui.ui_value
826
817
827 for name in self.GENERAL_SETTINGS:
818 for name in self.GENERAL_SETTINGS:
828 setting = settings.get_setting_by_name(name)
819 setting = settings.get_setting_by_name(name)
829 if setting:
820 if setting:
830 result_key = self.get_keyname(name)
821 result_key = self.get_keyname(name)
831 result[result_key] = setting.app_settings_value
822 result[result_key] = setting.app_settings_value
832
823
833 return result
824 return result
834
825
835 def _get_form_ui_key(self, section, key):
826 def _get_form_ui_key(self, section, key):
836 return '{section}_{key}'.format(
827 return '{section}_{key}'.format(
837 section=section, key=key.replace('.', '_'))
828 section=section, key=key.replace('.', '_'))
838
829
839 def _create_or_update_ui(
830 def _create_or_update_ui(
840 self, settings, section, key, value=None, active=None):
831 self, settings, section, key, value=None, active=None):
841 ui = settings.get_ui_by_section_and_key(section, key)
832 ui = settings.get_ui_by_section_and_key(section, key)
842 if not ui:
833 if not ui:
843 active = True if active is None else active
834 active = True if active is None else active
844 settings.create_ui_section_value(
835 settings.create_ui_section_value(
845 section, value, key=key, active=active)
836 section, value, key=key, active=active)
846 else:
837 else:
847 if active is not None:
838 if active is not None:
848 ui.ui_active = active
839 ui.ui_active = active
849 if value is not None:
840 if value is not None:
850 ui.ui_value = value
841 ui.ui_value = value
851 Session().add(ui)
842 Session().add(ui)
852
843
853 def _create_svn_settings(self, settings, data):
844 def _create_svn_settings(self, settings, data):
854 svn_settings = {
845 svn_settings = {
855 'new_svn_branch': self.SVN_BRANCH_SECTION,
846 'new_svn_branch': self.SVN_BRANCH_SECTION,
856 'new_svn_tag': self.SVN_TAG_SECTION
847 'new_svn_tag': self.SVN_TAG_SECTION
857 }
848 }
858 for key in svn_settings:
849 for key in svn_settings:
859 if data.get(key):
850 if data.get(key):
860 settings.create_ui_section_value(svn_settings[key], data[key])
851 settings.create_ui_section_value(svn_settings[key], data[key])
861
852
862 def _create_or_update_general_settings(self, settings, data):
853 def _create_or_update_general_settings(self, settings, data):
863 for name in self.GENERAL_SETTINGS:
854 for name in self.GENERAL_SETTINGS:
864 data_key = self.get_keyname(name)
855 data_key = self.get_keyname(name)
865 if data_key not in data:
856 if data_key not in data:
866 raise ValueError(
857 raise ValueError(
867 f'The given data does not contain {data_key} key')
858 f'The given data does not contain {data_key} key')
868 setting = settings.create_or_update_setting(
859 setting = settings.create_or_update_setting(
869 name, data[data_key], 'bool')
860 name, data[data_key], 'bool')
870 Session().add(setting)
861 Session().add(setting)
871
862
872 def _get_settings_keys(self, settings, data):
863 def _get_settings_keys(self, settings, data):
873 data_keys = [self._get_form_ui_key(*s) for s in settings]
864 data_keys = [self._get_form_ui_key(*s) for s in settings]
874 for data_key in data_keys:
865 for data_key in data_keys:
875 if data_key not in data:
866 if data_key not in data:
876 raise ValueError(
867 raise ValueError(
877 f'The given data does not contain {data_key} key')
868 f'The given data does not contain {data_key} key')
878 return data_keys
869 return data_keys
879
870
880 def create_largeobjects_dirs_if_needed(self, repo_store_path):
871 def create_largeobjects_dirs_if_needed(self, repo_store_path):
881 """
872 """
882 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
873 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
883 does a repository scan if enabled in the settings.
874 does a repository scan if enabled in the settings.
884 """
875 """
885
876
886 from rhodecode.lib.vcs.backends.hg import largefiles_store
877 from rhodecode.lib.vcs.backends.hg import largefiles_store
887 from rhodecode.lib.vcs.backends.git import lfs_store
878 from rhodecode.lib.vcs.backends.git import lfs_store
888
879
889 paths = [
880 paths = [
890 largefiles_store(repo_store_path),
881 largefiles_store(repo_store_path),
891 lfs_store(repo_store_path)]
882 lfs_store(repo_store_path)]
892
883
893 for path in paths:
884 for path in paths:
894 if os.path.isdir(path):
885 if os.path.isdir(path):
895 continue
886 continue
896 if os.path.isfile(path):
887 if os.path.isfile(path):
897 continue
888 continue
898 # not a file nor dir, we try to create it
889 # not a file nor dir, we try to create it
899 try:
890 try:
900 os.makedirs(path)
891 os.makedirs(path)
901 except Exception:
892 except Exception:
902 log.warning('Failed to create largefiles dir:%s', path)
893 log.warning('Failed to create largefiles dir:%s', path)
@@ -1,345 +1,323 b''
1 ## snippet for displaying vcs settings
1 ## snippet for displaying vcs settings
2 ## usage:
2 ## usage:
3 ## <%namespace name="vcss" file="/base/vcssettings.mako"/>
3 ## <%namespace name="vcss" file="/base/vcssettings.mako"/>
4 ## ${vcss.vcs_settings_fields()}
4 ## ${vcss.vcs_settings_fields()}
5
5
6 <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, **kwargs)">
6 <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, **kwargs)">
7 % if display_globals:
7 % if display_globals:
8 <div class="panel panel-default">
8 <div class="panel panel-default">
9 <div class="panel-heading" id="general">
9 <div class="panel-heading" id="general">
10 <h3 class="panel-title">${_('General')}<a class="permalink" href="#general"> ΒΆ</a></h3>
10 <h3 class="panel-title">${_('General')}<a class="permalink" href="#general"> ΒΆ</a></h3>
11 </div>
11 </div>
12 <div class="panel-body">
12 <div class="panel-body">
13 <div class="field">
13 <div class="field">
14 <div class="checkbox">
14 <div class="checkbox">
15 ${h.checkbox('web_push_ssl' + suffix, 'True')}
15 ${h.checkbox('web_push_ssl' + suffix, 'True')}
16 <label for="web_push_ssl${suffix}">${_('Require SSL for vcs operations')}</label>
16 <label for="web_push_ssl${suffix}">${_('Require SSL for vcs operations')}</label>
17 </div>
17 </div>
18 <div class="label">
18 <div class="label">
19 <span class="help-block">${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')}</span>
19 <span class="help-block">${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')}</span>
20 </div>
20 </div>
21 </div>
21 </div>
22 </div>
22 </div>
23 </div>
23 </div>
24 % endif
24 % endif
25
25
26 % if display_globals or repo_type in ['git', 'hg']:
26 % if display_globals or repo_type in ['git', 'hg']:
27 <div class="panel panel-default">
27 <div class="panel panel-default">
28 <div class="panel-heading" id="vcs-hooks-options">
28 <div class="panel-heading" id="vcs-hooks-options">
29 <h3 class="panel-title">${_('Internal Hooks')}<a class="permalink" href="#vcs-hooks-options"> ΒΆ</a></h3>
29 <h3 class="panel-title">${_('Internal Hooks')}<a class="permalink" href="#vcs-hooks-options"> ΒΆ</a></h3>
30 </div>
30 </div>
31 <div class="panel-body">
31 <div class="panel-body">
32 <div class="field">
32 <div class="field">
33 <div class="checkbox">
33 <div class="checkbox">
34 ${h.checkbox('hooks_changegroup_repo_size' + suffix, 'True', **kwargs)}
34 ${h.checkbox('hooks_changegroup_repo_size' + suffix, 'True', **kwargs)}
35 <label for="hooks_changegroup_repo_size${suffix}">${_('Show repository size after push')}</label>
35 <label for="hooks_changegroup_repo_size${suffix}">${_('Show repository size after push')}</label>
36 </div>
36 </div>
37
37
38 <div class="label">
38 <div class="label">
39 <span class="help-block">${_('Trigger a hook that calculates repository size after each push.')}</span>
39 <span class="help-block">${_('Trigger a hook that calculates repository size after each push.')}</span>
40 </div>
40 </div>
41 <div class="checkbox">
41 <div class="checkbox">
42 ${h.checkbox('hooks_changegroup_push_logger' + suffix, 'True', **kwargs)}
42 ${h.checkbox('hooks_changegroup_push_logger' + suffix, 'True', **kwargs)}
43 <label for="hooks_changegroup_push_logger${suffix}">${_('Execute pre/post push hooks')}</label>
43 <label for="hooks_changegroup_push_logger${suffix}">${_('Execute pre/post push hooks')}</label>
44 </div>
44 </div>
45 <div class="label">
45 <div class="label">
46 <span class="help-block">${_('Execute Built in pre/post push hooks. This also executes rcextensions hooks.')}</span>
46 <span class="help-block">${_('Execute Built in pre/post push hooks. This also executes rcextensions hooks.')}</span>
47 </div>
47 </div>
48 <div class="checkbox">
48 <div class="checkbox">
49 ${h.checkbox('hooks_outgoing_pull_logger' + suffix, 'True', **kwargs)}
49 ${h.checkbox('hooks_outgoing_pull_logger' + suffix, 'True', **kwargs)}
50 <label for="hooks_outgoing_pull_logger${suffix}">${_('Execute pre/post pull hooks')}</label>
50 <label for="hooks_outgoing_pull_logger${suffix}">${_('Execute pre/post pull hooks')}</label>
51 </div>
51 </div>
52 <div class="label">
52 <div class="label">
53 <span class="help-block">${_('Execute Built in pre/post pull hooks. This also executes rcextensions hooks.')}</span>
53 <span class="help-block">${_('Execute Built in pre/post pull hooks. This also executes rcextensions hooks.')}</span>
54 </div>
54 </div>
55 </div>
55 </div>
56 </div>
56 </div>
57 </div>
57 </div>
58 % endif
58 % endif
59
59
60 % if display_globals or repo_type in ['hg']:
60 % if display_globals or repo_type in ['hg']:
61 <div class="panel panel-default">
61 <div class="panel panel-default">
62 <div class="panel-heading" id="vcs-hg-options">
62 <div class="panel-heading" id="vcs-hg-options">
63 <h3 class="panel-title">${_('Mercurial Settings')}<a class="permalink" href="#vcs-hg-options"> ΒΆ</a></h3>
63 <h3 class="panel-title">${_('Mercurial Settings')}<a class="permalink" href="#vcs-hg-options"> ΒΆ</a></h3>
64 </div>
64 </div>
65 <div class="panel-body">
65 <div class="panel-body">
66 <div class="checkbox">
66 <div class="checkbox">
67 ${h.checkbox('extensions_largefiles' + suffix, 'True', **kwargs)}
67 ${h.checkbox('extensions_largefiles' + suffix, 'True', **kwargs)}
68 <label for="extensions_largefiles${suffix}">${_('Enable largefiles extension')}</label>
68 <label for="extensions_largefiles${suffix}">${_('Enable largefiles extension')}</label>
69 </div>
69 </div>
70 <div class="label">
70 <div class="label">
71 % if display_globals:
71 % if display_globals:
72 <span class="help-block">${_('Enable Largefiles extensions for all repositories.')}</span>
72 <span class="help-block">${_('Enable Largefiles extensions for all repositories.')}</span>
73 % else:
73 % else:
74 <span class="help-block">${_('Enable Largefiles extensions for this repository.')}</span>
74 <span class="help-block">${_('Enable Largefiles extensions for this repository.')}</span>
75 % endif
75 % endif
76 </div>
76 </div>
77
77
78 % if display_globals:
79 <div class="field">
80 <div class="input">
81 ${h.text('largefiles_usercache' + suffix, size=59)}
82 </div>
83 </div>
84 <div class="label">
85 <span class="help-block">${_('Filesystem location where Mercurial largefile objects should be stored.')}</span>
86 </div>
87 % endif
88
89 <div class="checkbox">
78 <div class="checkbox">
90 ${h.checkbox('phases_publish' + suffix, 'True', **kwargs)}
79 ${h.checkbox('phases_publish' + suffix, 'True', **kwargs)}
91 <label for="phases_publish${suffix}">${_('Set repositories as publishing') if display_globals else _('Set repository as publishing')}</label>
80 <label for="phases_publish${suffix}">${_('Set repositories as publishing') if display_globals else _('Set repository as publishing')}</label>
92 </div>
81 </div>
93 <div class="label">
82 <div class="label">
94 <span class="help-block">${_('When this is enabled all commits in the repository are seen as public commits by clients.')}</span>
83 <span class="help-block">${_('When this is enabled all commits in the repository are seen as public commits by clients.')}</span>
95 </div>
84 </div>
96
85
97 <div class="checkbox">
86 <div class="checkbox">
98 ${h.checkbox('extensions_evolve' + suffix, 'True', **kwargs)}
87 ${h.checkbox('extensions_evolve' + suffix, 'True', **kwargs)}
99 <label for="extensions_evolve${suffix}">${_('Enable Evolve and Topic extension')}</label>
88 <label for="extensions_evolve${suffix}">${_('Enable Evolve and Topic extension')}</label>
100 </div>
89 </div>
101 <div class="label">
90 <div class="label">
102 % if display_globals:
91 % if display_globals:
103 <span class="help-block">${_('Enable Evolve and Topic extensions for all repositories.')}</span>
92 <span class="help-block">${_('Enable Evolve and Topic extensions for all repositories.')}</span>
104 % else:
93 % else:
105 <span class="help-block">${_('Enable Evolve and Topic extensions for this repository.')}</span>
94 <span class="help-block">${_('Enable Evolve and Topic extensions for this repository.')}</span>
106 % endif
95 % endif
107 </div>
96 </div>
108
97
109 </div>
98 </div>
110 </div>
99 </div>
111 % endif
100 % endif
112
101
113 % if display_globals or repo_type in ['git']:
102 % if display_globals or repo_type in ['git']:
114 <div class="panel panel-default">
103 <div class="panel panel-default">
115 <div class="panel-heading" id="vcs-git-options">
104 <div class="panel-heading" id="vcs-git-options">
116 <h3 class="panel-title">${_('Git Settings')}<a class="permalink" href="#vcs-git-options"> ΒΆ</a></h3>
105 <h3 class="panel-title">${_('Git Settings')}<a class="permalink" href="#vcs-git-options"> ΒΆ</a></h3>
117 </div>
106 </div>
118 <div class="panel-body">
107 <div class="panel-body">
119 <div class="checkbox">
108 <div class="checkbox">
120 ${h.checkbox('vcs_git_lfs_enabled' + suffix, 'True', **kwargs)}
109 ${h.checkbox('vcs_git_lfs_enabled' + suffix, 'True', **kwargs)}
121 <label for="vcs_git_lfs_enabled${suffix}">${_('Enable lfs extension')}</label>
110 <label for="vcs_git_lfs_enabled${suffix}">${_('Enable lfs extension')}</label>
122 </div>
111 </div>
123 <div class="label">
112 <div class="label">
124 % if display_globals:
113 % if display_globals:
125 <span class="help-block">${_('Enable lfs extensions for all repositories.')}</span>
114 <span class="help-block">${_('Enable lfs extensions for all repositories.')}</span>
126 % else:
115 % else:
127 <span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
116 <span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
128 % endif
117 % endif
129 </div>
118 </div>
130
131 % if display_globals:
132 <div class="field">
133 <div class="input">
134 ${h.text('vcs_git_lfs_store_location' + suffix, size=59)}
135 </div>
136 </div>
137 <div class="label">
138 <span class="help-block">${_('Filesystem location where Git lfs objects should be stored.')}</span>
139 </div>
140 % endif
141 </div>
119 </div>
142 </div>
120 </div>
143 % endif
121 % endif
144
122
145 % if display_globals or repo_type in ['svn']:
123 % if display_globals or repo_type in ['svn']:
146 <div class="panel panel-default">
124 <div class="panel panel-default">
147 <div class="panel-heading" id="vcs-svn-options">
125 <div class="panel-heading" id="vcs-svn-options">
148 <h3 class="panel-title">${_('Subversion Settings')}<a class="permalink" href="#vcs-svn-options"> ΒΆ</a></h3>
126 <h3 class="panel-title">${_('Subversion Settings')}<a class="permalink" href="#vcs-svn-options"> ΒΆ</a></h3>
149 </div>
127 </div>
150 <div class="panel-body">
128 <div class="panel-body">
151 % if display_globals:
129 % if display_globals:
152 <div class="field">
130 <div class="field">
153 <div class="content" >
131 <div class="content" >
154 <label>${_('mod_dav config')}</label><br/>
132 <label>${_('mod_dav config')}</label><br/>
155 <code>path: ${c.svn_config_path}</code>
133 <code>path: ${c.svn_config_path}</code>
156 </div>
134 </div>
157 <br/>
135 <br/>
158
136
159 <div>
137 <div>
160
138
161 % if c.svn_generate_config:
139 % if c.svn_generate_config:
162 <span class="buttons">
140 <span class="buttons">
163 <button class="btn btn-primary" id="vcs_svn_generate_cfg">${_('Re-generate Apache Config')}</button>
141 <button class="btn btn-primary" id="vcs_svn_generate_cfg">${_('Re-generate Apache Config')}</button>
164 </span>
142 </span>
165 % endif
143 % endif
166 </div>
144 </div>
167 </div>
145 </div>
168 % endif
146 % endif
169
147
170 <div class="field">
148 <div class="field">
171 <div class="content" >
149 <div class="content" >
172 <label>${_('Repository patterns')}</label><br/>
150 <label>${_('Repository patterns')}</label><br/>
173 </div>
151 </div>
174 </div>
152 </div>
175 <div class="label">
153 <div class="label">
176 <span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span>
154 <span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span>
177 </div>
155 </div>
178
156
179 <div class="field branch_patterns">
157 <div class="field branch_patterns">
180 <div class="input" >
158 <div class="input" >
181 <label>${_('Branches')}:</label><br/>
159 <label>${_('Branches')}:</label><br/>
182 </div>
160 </div>
183 % if svn_branch_patterns:
161 % if svn_branch_patterns:
184 % for branch in svn_branch_patterns:
162 % for branch in svn_branch_patterns:
185 <div class="input adjacent" id="${'id%s' % branch.ui_id}">
163 <div class="input adjacent" id="${'id%s' % branch.ui_id}">
186 ${h.hidden('branch_ui_key' + suffix, branch.ui_key)}
164 ${h.hidden('branch_ui_key' + suffix, branch.ui_key)}
187 ${h.text('branch_value_%d' % branch.ui_id + suffix, branch.ui_value, size=59, readonly="readonly", class_='disabled')}
165 ${h.text('branch_value_%d' % branch.ui_id + suffix, branch.ui_value, size=59, readonly="readonly", class_='disabled')}
188 % if kwargs.get('disabled') != 'disabled':
166 % if kwargs.get('disabled') != 'disabled':
189 <span class="btn btn-x" onclick="ajaxDeletePattern(${branch.ui_id},'${'id%s' % branch.ui_id}')">
167 <span class="btn btn-x" onclick="ajaxDeletePattern(${branch.ui_id},'${'id%s' % branch.ui_id}')">
190 ${_('Delete')}
168 ${_('Delete')}
191 </span>
169 </span>
192 % endif
170 % endif
193 </div>
171 </div>
194 % endfor
172 % endfor
195 %endif
173 %endif
196 </div>
174 </div>
197 % if kwargs.get('disabled') != 'disabled':
175 % if kwargs.get('disabled') != 'disabled':
198 <div class="field branch_patterns">
176 <div class="field branch_patterns">
199 <div class="input" >
177 <div class="input" >
200 ${h.text('new_svn_branch',size=59,placeholder='New branch pattern')}
178 ${h.text('new_svn_branch',size=59,placeholder='New branch pattern')}
201 </div>
179 </div>
202 </div>
180 </div>
203 % endif
181 % endif
204 <div class="field tag_patterns">
182 <div class="field tag_patterns">
205 <div class="input" >
183 <div class="input" >
206 <label>${_('Tags')}:</label><br/>
184 <label>${_('Tags')}:</label><br/>
207 </div>
185 </div>
208 % if svn_tag_patterns:
186 % if svn_tag_patterns:
209 % for tag in svn_tag_patterns:
187 % for tag in svn_tag_patterns:
210 <div class="input" id="${'id%s' % tag.ui_id + suffix}">
188 <div class="input" id="${'id%s' % tag.ui_id + suffix}">
211 ${h.hidden('tag_ui_key' + suffix, tag.ui_key)}
189 ${h.hidden('tag_ui_key' + suffix, tag.ui_key)}
212 ${h.text('tag_ui_value_new_%d' % tag.ui_id + suffix, tag.ui_value, size=59, readonly="readonly", class_='disabled tag_input')}
190 ${h.text('tag_ui_value_new_%d' % tag.ui_id + suffix, tag.ui_value, size=59, readonly="readonly", class_='disabled tag_input')}
213 % if kwargs.get('disabled') != 'disabled':
191 % if kwargs.get('disabled') != 'disabled':
214 <span class="btn btn-x" onclick="ajaxDeletePattern(${tag.ui_id},'${'id%s' % tag.ui_id}')">
192 <span class="btn btn-x" onclick="ajaxDeletePattern(${tag.ui_id},'${'id%s' % tag.ui_id}')">
215 ${_('Delete')}
193 ${_('Delete')}
216 </span>
194 </span>
217 %endif
195 %endif
218 </div>
196 </div>
219 % endfor
197 % endfor
220 % endif
198 % endif
221 </div>
199 </div>
222 % if kwargs.get('disabled') != 'disabled':
200 % if kwargs.get('disabled') != 'disabled':
223 <div class="field tag_patterns">
201 <div class="field tag_patterns">
224 <div class="input" >
202 <div class="input" >
225 ${h.text('new_svn_tag' + suffix, size=59, placeholder='New tag pattern')}
203 ${h.text('new_svn_tag' + suffix, size=59, placeholder='New tag pattern')}
226 </div>
204 </div>
227 </div>
205 </div>
228 %endif
206 %endif
229 </div>
207 </div>
230 </div>
208 </div>
231 % else:
209 % else:
232 ${h.hidden('new_svn_branch' + suffix, '')}
210 ${h.hidden('new_svn_branch' + suffix, '')}
233 ${h.hidden('new_svn_tag' + suffix, '')}
211 ${h.hidden('new_svn_tag' + suffix, '')}
234 % endif
212 % endif
235
213
236
214
237 % if display_globals or repo_type in ['hg', 'git']:
215 % if display_globals or repo_type in ['hg', 'git']:
238 <div class="panel panel-default">
216 <div class="panel panel-default">
239 <div class="panel-heading" id="vcs-pull-requests-options">
217 <div class="panel-heading" id="vcs-pull-requests-options">
240 <h3 class="panel-title">${_('Pull Request Settings')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
218 <h3 class="panel-title">${_('Pull Request Settings')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
241 </div>
219 </div>
242 <div class="panel-body">
220 <div class="panel-body">
243 <div class="checkbox">
221 <div class="checkbox">
244 ${h.checkbox('rhodecode_pr_merge_enabled' + suffix, 'True', **kwargs)}
222 ${h.checkbox('rhodecode_pr_merge_enabled' + suffix, 'True', **kwargs)}
245 <label for="rhodecode_pr_merge_enabled${suffix}">${_('Enable server-side merge for pull requests')}</label>
223 <label for="rhodecode_pr_merge_enabled${suffix}">${_('Enable server-side merge for pull requests')}</label>
246 </div>
224 </div>
247 <div class="label">
225 <div class="label">
248 <span class="help-block">${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}</span>
226 <span class="help-block">${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}</span>
249 </div>
227 </div>
250 <div class="checkbox">
228 <div class="checkbox">
251 ${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)}
229 ${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)}
252 <label for="rhodecode_use_outdated_comments${suffix}">${_('Invalidate and relocate inline comments during update')}</label>
230 <label for="rhodecode_use_outdated_comments${suffix}">${_('Invalidate and relocate inline comments during update')}</label>
253 </div>
231 </div>
254 <div class="label">
232 <div class="label">
255 <span class="help-block">${_('During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden.')}</span>
233 <span class="help-block">${_('During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden.')}</span>
256 </div>
234 </div>
257 </div>
235 </div>
258 </div>
236 </div>
259 % endif
237 % endif
260
238
261 % if display_globals or repo_type in ['hg', 'git', 'svn']:
239 % if display_globals or repo_type in ['hg', 'git', 'svn']:
262 <div class="panel panel-default">
240 <div class="panel panel-default">
263 <div class="panel-heading" id="vcs-pull-requests-options">
241 <div class="panel-heading" id="vcs-pull-requests-options">
264 <h3 class="panel-title">${_('Diff cache')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
242 <h3 class="panel-title">${_('Diff cache')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
265 </div>
243 </div>
266 <div class="panel-body">
244 <div class="panel-body">
267 <div class="checkbox">
245 <div class="checkbox">
268 ${h.checkbox('rhodecode_diff_cache' + suffix, 'True', **kwargs)}
246 ${h.checkbox('rhodecode_diff_cache' + suffix, 'True', **kwargs)}
269 <label for="rhodecode_diff_cache${suffix}">${_('Enable caching diffs for pull requests cache and commits')}</label>
247 <label for="rhodecode_diff_cache${suffix}">${_('Enable caching diffs for pull requests cache and commits')}</label>
270 </div>
248 </div>
271 </div>
249 </div>
272 </div>
250 </div>
273 % endif
251 % endif
274
252
275 % if display_globals or repo_type in ['hg',]:
253 % if display_globals or repo_type in ['hg',]:
276 <div class="panel panel-default">
254 <div class="panel panel-default">
277 <div class="panel-heading" id="vcs-pull-requests-options">
255 <div class="panel-heading" id="vcs-pull-requests-options">
278 <h3 class="panel-title">${_('Mercurial Pull Request Settings')}<a class="permalink" href="#vcs-hg-pull-requests-options"> ΒΆ</a></h3>
256 <h3 class="panel-title">${_('Mercurial Pull Request Settings')}<a class="permalink" href="#vcs-hg-pull-requests-options"> ΒΆ</a></h3>
279 </div>
257 </div>
280 <div class="panel-body">
258 <div class="panel-body">
281 ## Specific HG settings
259 ## Specific HG settings
282 <div class="checkbox">
260 <div class="checkbox">
283 ${h.checkbox('rhodecode_hg_use_rebase_for_merging' + suffix, 'True', **kwargs)}
261 ${h.checkbox('rhodecode_hg_use_rebase_for_merging' + suffix, 'True', **kwargs)}
284 <label for="rhodecode_hg_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
262 <label for="rhodecode_hg_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
285 </div>
263 </div>
286 <div class="label">
264 <div class="label">
287 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
265 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
288 </div>
266 </div>
289
267
290 <div class="checkbox">
268 <div class="checkbox">
291 ${h.checkbox('rhodecode_hg_close_branch_before_merging' + suffix, 'True', **kwargs)}
269 ${h.checkbox('rhodecode_hg_close_branch_before_merging' + suffix, 'True', **kwargs)}
292 <label for="rhodecode_hg_close_branch_before_merging{suffix}">${_('Close branch before merging it')}</label>
270 <label for="rhodecode_hg_close_branch_before_merging{suffix}">${_('Close branch before merging it')}</label>
293 </div>
271 </div>
294 <div class="label">
272 <div class="label">
295 <span class="help-block">${_('Close branch before merging it into destination branch. No effect when rebase strategy is use.')}</span>
273 <span class="help-block">${_('Close branch before merging it into destination branch. No effect when rebase strategy is use.')}</span>
296 </div>
274 </div>
297
275
298
276
299 </div>
277 </div>
300 </div>
278 </div>
301 % endif
279 % endif
302
280
303 % if display_globals or repo_type in ['git']:
281 % if display_globals or repo_type in ['git']:
304 <div class="panel panel-default">
282 <div class="panel panel-default">
305 <div class="panel-heading" id="vcs-pull-requests-options">
283 <div class="panel-heading" id="vcs-pull-requests-options">
306 <h3 class="panel-title">${_('Git Pull Request Settings')}<a class="permalink" href="#vcs-git-pull-requests-options"> ΒΆ</a></h3>
284 <h3 class="panel-title">${_('Git Pull Request Settings')}<a class="permalink" href="#vcs-git-pull-requests-options"> ΒΆ</a></h3>
307 </div>
285 </div>
308 <div class="panel-body">
286 <div class="panel-body">
309 ## <div class="checkbox">
287 ## <div class="checkbox">
310 ## ${h.checkbox('rhodecode_git_use_rebase_for_merging' + suffix, 'True', **kwargs)}
288 ## ${h.checkbox('rhodecode_git_use_rebase_for_merging' + suffix, 'True', **kwargs)}
311 ## <label for="rhodecode_git_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
289 ## <label for="rhodecode_git_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
312 ## </div>
290 ## </div>
313 ## <div class="label">
291 ## <div class="label">
314 ## <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
292 ## <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
315 ## </div>
293 ## </div>
316
294
317 <div class="checkbox">
295 <div class="checkbox">
318 ${h.checkbox('rhodecode_git_close_branch_before_merging' + suffix, 'True', **kwargs)}
296 ${h.checkbox('rhodecode_git_close_branch_before_merging' + suffix, 'True', **kwargs)}
319 <label for="rhodecode_git_close_branch_before_merging{suffix}">${_('Delete branch after merging it')}</label>
297 <label for="rhodecode_git_close_branch_before_merging{suffix}">${_('Delete branch after merging it')}</label>
320 </div>
298 </div>
321 <div class="label">
299 <div class="label">
322 <span class="help-block">${_('Delete branch after merging it into destination branch.')}</span>
300 <span class="help-block">${_('Delete branch after merging it into destination branch.')}</span>
323 </div>
301 </div>
324 </div>
302 </div>
325 </div>
303 </div>
326 % endif
304 % endif
327
305
328 <script type="text/javascript">
306 <script type="text/javascript">
329
307
330 $(document).ready(function() {
308 $(document).ready(function() {
331 /* On click handler for the `Generate Apache Config` button. It sends a
309 /* On click handler for the `Generate Apache Config` button. It sends a
332 POST request to trigger the (re)generation of the mod_dav_svn config. */
310 POST request to trigger the (re)generation of the mod_dav_svn config. */
333 $('#vcs_svn_generate_cfg').on('click', function(event) {
311 $('#vcs_svn_generate_cfg').on('click', function(event) {
334 event.preventDefault();
312 event.preventDefault();
335 var url = "${h.route_path('admin_settings_vcs_svn_generate_cfg')}";
313 var url = "${h.route_path('admin_settings_vcs_svn_generate_cfg')}";
336 var jqxhr = $.post(url, {'csrf_token': CSRF_TOKEN});
314 var jqxhr = $.post(url, {'csrf_token': CSRF_TOKEN});
337 jqxhr.done(function(data) {
315 jqxhr.done(function(data) {
338 $.Topic('/notifications').publish(data);
316 $.Topic('/notifications').publish(data);
339 });
317 });
340 });
318 });
341 });
319 });
342
320
343 </script>
321 </script>
344 </%def>
322 </%def>
345
323
@@ -1,489 +1,490 b''
1
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
6 # (only), as published by the Free Software Foundation.
6 # (only), as published by the Free Software Foundation.
7 #
7 #
8 # This program is distributed in the hope that it will be useful,
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
11 # GNU General Public License for more details.
12 #
12 #
13 # You should have received a copy of the GNU Affero General Public License
13 # You should have received a copy of the GNU Affero General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #
15 #
16 # This program is dual-licensed. If you wish to learn more about the
16 # This program is dual-licensed. If you wish to learn more about the
17 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19
19
20 import multiprocessing
20 import multiprocessing
21 import os
21 import os
22
22
23 import mock
23 import mock
24 import py
24 import py
25 import pytest
25 import pytest
26
26
27 from rhodecode.lib import caching_query
27 from rhodecode.lib import caching_query
28 from rhodecode.lib import utils
28 from rhodecode.lib import utils
29 from rhodecode.lib.str_utils import safe_bytes
29 from rhodecode.lib.str_utils import safe_bytes
30 from rhodecode.model import settings
30 from rhodecode.model import settings
31 from rhodecode.model import db
31 from rhodecode.model import db
32 from rhodecode.model import meta
32 from rhodecode.model import meta
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
34 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.repo_group import RepoGroupModel
35 from rhodecode.model.settings import UiSetting, SettingsModel
35 from rhodecode.model.settings import UiSetting, SettingsModel
36 from rhodecode.tests.fixture import Fixture
36 from rhodecode.tests.fixture import Fixture
37 from rhodecode_tools.lib.hash_utils import md5_safe
37 from rhodecode_tools.lib.hash_utils import md5_safe
38 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.ext_json import json
39
39
40 fixture = Fixture()
40 fixture = Fixture()
41
41
42
42
43 def extract_hooks(config):
43 def extract_hooks(config):
44 """Return a dictionary with the hook entries of the given config."""
44 """Return a dictionary with the hook entries of the given config."""
45 hooks = {}
45 hooks = {}
46 config_items = config.serialize()
46 config_items = config.serialize()
47 for section, name, value in config_items:
47 for section, name, value in config_items:
48 if section != 'hooks':
48 if section != 'hooks':
49 continue
49 continue
50 hooks[name] = value
50 hooks[name] = value
51
51
52 return hooks
52 return hooks
53
53
54
54
55 def disable_hooks(request, hooks):
55 def disable_hooks(request, hooks):
56 """Disables the given hooks from the UI settings."""
56 """Disables the given hooks from the UI settings."""
57 session = meta.Session()
57 session = meta.Session()
58
58
59 model = SettingsModel()
59 model = SettingsModel()
60 for hook_key in hooks:
60 for hook_key in hooks:
61 sett = model.get_ui_by_key(hook_key)
61 sett = model.get_ui_by_key(hook_key)
62 sett.ui_active = False
62 sett.ui_active = False
63 session.add(sett)
63 session.add(sett)
64
64
65 # Invalidate cache
65 # Invalidate cache
66 ui_settings = session.query(db.RhodeCodeUi).options(
66 ui_settings = session.query(db.RhodeCodeUi).options(
67 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
67 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
68
68
69 meta.cache.invalidate(
69 meta.cache.invalidate(
70 ui_settings, {},
70 ui_settings, {},
71 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
71 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
72
72
73 ui_settings = session.query(db.RhodeCodeUi).options(
73 ui_settings = session.query(db.RhodeCodeUi).options(
74 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
74 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
75
75
76 meta.cache.invalidate(
76 meta.cache.invalidate(
77 ui_settings, {},
77 ui_settings, {},
78 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
78 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
79
79
80 @request.addfinalizer
80 @request.addfinalizer
81 def rollback():
81 def rollback():
82 session.rollback()
82 session.rollback()
83
83
84
84
85 HOOK_PRE_PUSH = db.RhodeCodeUi.HOOK_PRE_PUSH
85 HOOK_PRE_PUSH = db.RhodeCodeUi.HOOK_PRE_PUSH
86 HOOK_PRETX_PUSH = db.RhodeCodeUi.HOOK_PRETX_PUSH
86 HOOK_PRETX_PUSH = db.RhodeCodeUi.HOOK_PRETX_PUSH
87 HOOK_PUSH = db.RhodeCodeUi.HOOK_PUSH
87 HOOK_PUSH = db.RhodeCodeUi.HOOK_PUSH
88 HOOK_PRE_PULL = db.RhodeCodeUi.HOOK_PRE_PULL
88 HOOK_PRE_PULL = db.RhodeCodeUi.HOOK_PRE_PULL
89 HOOK_PULL = db.RhodeCodeUi.HOOK_PULL
89 HOOK_PULL = db.RhodeCodeUi.HOOK_PULL
90 HOOK_REPO_SIZE = db.RhodeCodeUi.HOOK_REPO_SIZE
90 HOOK_REPO_SIZE = db.RhodeCodeUi.HOOK_REPO_SIZE
91 HOOK_PUSH_KEY = db.RhodeCodeUi.HOOK_PUSH_KEY
91 HOOK_PUSH_KEY = db.RhodeCodeUi.HOOK_PUSH_KEY
92
92
93 HG_HOOKS = frozenset(
93 HG_HOOKS = frozenset(
94 (HOOK_PRE_PULL, HOOK_PULL, HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH,
94 (HOOK_PRE_PULL, HOOK_PULL, HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH,
95 HOOK_REPO_SIZE, HOOK_PUSH_KEY))
95 HOOK_REPO_SIZE, HOOK_PUSH_KEY))
96
96
97
97
98 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
98 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
99 ([], HG_HOOKS),
99 ([], HG_HOOKS),
100 (HG_HOOKS, []),
100 (HG_HOOKS, []),
101
101
102 ([HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_REPO_SIZE, HOOK_PUSH_KEY], [HOOK_PRE_PULL, HOOK_PULL, HOOK_PUSH]),
102 ([HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_REPO_SIZE, HOOK_PUSH_KEY], [HOOK_PRE_PULL, HOOK_PULL, HOOK_PUSH]),
103
103
104 # When a pull/push hook is disabled, its pre-pull/push counterpart should
104 # When a pull/push hook is disabled, its pre-pull/push counterpart should
105 # be disabled too.
105 # be disabled too.
106 ([HOOK_PUSH], [HOOK_PRE_PULL, HOOK_PULL, HOOK_REPO_SIZE]),
106 ([HOOK_PUSH], [HOOK_PRE_PULL, HOOK_PULL, HOOK_REPO_SIZE]),
107 ([HOOK_PULL], [HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH, HOOK_REPO_SIZE,
107 ([HOOK_PULL], [HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH, HOOK_REPO_SIZE,
108 HOOK_PUSH_KEY]),
108 HOOK_PUSH_KEY]),
109 ])
109 ])
110 def test_make_db_config_hg_hooks(baseapp, request, disabled_hooks,
110 def test_make_db_config_hg_hooks(baseapp, request, disabled_hooks,
111 expected_hooks):
111 expected_hooks):
112 disable_hooks(request, disabled_hooks)
112 disable_hooks(request, disabled_hooks)
113
113
114 config = utils.make_db_config()
114 config = utils.make_db_config()
115 hooks = extract_hooks(config)
115 hooks = extract_hooks(config)
116
116
117 assert set(hooks.keys()).intersection(HG_HOOKS) == set(expected_hooks)
117 assert set(hooks.keys()).intersection(HG_HOOKS) == set(expected_hooks)
118
118
119
119
120 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
120 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
121 ([], ['pull', 'push']),
121 ([], ['pull', 'push']),
122 ([HOOK_PUSH], ['pull']),
122 ([HOOK_PUSH], ['pull']),
123 ([HOOK_PULL], ['push']),
123 ([HOOK_PULL], ['push']),
124 ([HOOK_PULL, HOOK_PUSH], []),
124 ([HOOK_PULL, HOOK_PUSH], []),
125 ])
125 ])
126 def test_get_enabled_hook_classes(disabled_hooks, expected_hooks):
126 def test_get_enabled_hook_classes(disabled_hooks, expected_hooks):
127 hook_keys = (HOOK_PUSH, HOOK_PULL)
127 hook_keys = (HOOK_PUSH, HOOK_PULL)
128 ui_settings = [
128 ui_settings = [
129 ('hooks', key, 'some value', key not in disabled_hooks)
129 ('hooks', key, 'some value', key not in disabled_hooks)
130 for key in hook_keys]
130 for key in hook_keys]
131
131
132 result = utils.get_enabled_hook_classes(ui_settings)
132 result = utils.get_enabled_hook_classes(ui_settings)
133 assert sorted(result) == expected_hooks
133 assert sorted(result) == expected_hooks
134
134
135
135
136 def test_get_filesystem_repos_finds_repos(tmpdir, baseapp):
136 def test_get_filesystem_repos_finds_repos(tmpdir, baseapp):
137 _stub_git_repo(tmpdir.ensure('repo', dir=True))
137 _stub_git_repo(tmpdir.ensure('repo', dir=True))
138 repos = list(utils.get_filesystem_repos(str(tmpdir)))
138 repos = list(utils.get_filesystem_repos(str(tmpdir)))
139 assert repos == [('repo', ('git', tmpdir.join('repo')))]
139 assert repos == [('repo', ('git', tmpdir.join('repo')))]
140
140
141
141
142 def test_get_filesystem_repos_skips_directories(tmpdir, baseapp):
142 def test_get_filesystem_repos_skips_directories(tmpdir, baseapp):
143 tmpdir.ensure('not-a-repo', dir=True)
143 tmpdir.ensure('not-a-repo', dir=True)
144 repos = list(utils.get_filesystem_repos(str(tmpdir)))
144 repos = list(utils.get_filesystem_repos(str(tmpdir)))
145 assert repos == []
145 assert repos == []
146
146
147
147
148 def test_get_filesystem_repos_skips_directories_with_repos(tmpdir, baseapp):
148 def test_get_filesystem_repos_skips_directories_with_repos(tmpdir, baseapp):
149 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
149 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
150 repos = list(utils.get_filesystem_repos(str(tmpdir)))
150 repos = list(utils.get_filesystem_repos(str(tmpdir)))
151 assert repos == []
151 assert repos == []
152
152
153
153
154 def test_get_filesystem_repos_finds_repos_in_subdirectories(tmpdir, baseapp):
154 def test_get_filesystem_repos_finds_repos_in_subdirectories(tmpdir, baseapp):
155 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
155 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
156 repos = list(utils.get_filesystem_repos(str(tmpdir), recursive=True))
156 repos = list(utils.get_filesystem_repos(str(tmpdir), recursive=True))
157 assert repos == [('subdir/repo', ('git', tmpdir.join('subdir', 'repo')))]
157 assert repos == [('subdir/repo', ('git', tmpdir.join('subdir', 'repo')))]
158
158
159
159
160 def test_get_filesystem_repos_skips_names_starting_with_dot(tmpdir):
160 def test_get_filesystem_repos_skips_names_starting_with_dot(tmpdir):
161 _stub_git_repo(tmpdir.ensure('.repo', dir=True))
161 _stub_git_repo(tmpdir.ensure('.repo', dir=True))
162 repos = list(utils.get_filesystem_repos(str(tmpdir)))
162 repos = list(utils.get_filesystem_repos(str(tmpdir)))
163 assert repos == []
163 assert repos == []
164
164
165
165
166 def test_get_filesystem_repos_skips_files(tmpdir):
166 def test_get_filesystem_repos_skips_files(tmpdir):
167 tmpdir.ensure('test-file')
167 tmpdir.ensure('test-file')
168 repos = list(utils.get_filesystem_repos(str(tmpdir)))
168 repos = list(utils.get_filesystem_repos(str(tmpdir)))
169 assert repos == []
169 assert repos == []
170
170
171
171
172 def test_get_filesystem_repos_skips_removed_repositories(tmpdir):
172 def test_get_filesystem_repos_skips_removed_repositories(tmpdir):
173 removed_repo_name = 'rm__00000000_000000_000000__.stub'
173 removed_repo_name = 'rm__00000000_000000_000000__.stub'
174 assert utils.REMOVED_REPO_PAT.match(removed_repo_name)
174 assert utils.REMOVED_REPO_PAT.match(removed_repo_name)
175 _stub_git_repo(tmpdir.ensure(removed_repo_name, dir=True))
175 _stub_git_repo(tmpdir.ensure(removed_repo_name, dir=True))
176 repos = list(utils.get_filesystem_repos(str(tmpdir)))
176 repos = list(utils.get_filesystem_repos(str(tmpdir)))
177 assert repos == []
177 assert repos == []
178
178
179
179
180 def _stub_git_repo(repo_path):
180 def _stub_git_repo(repo_path):
181 """
181 """
182 Make `repo_path` look like a Git repository.
182 Make `repo_path` look like a Git repository.
183 """
183 """
184 repo_path.ensure('.git', dir=True)
184 repo_path.ensure('.git', dir=True)
185
185
186
186
187 def test_get_dirpaths_returns_all_paths_on_str(tmpdir):
187 def test_get_dirpaths_returns_all_paths_on_str(tmpdir):
188 tmpdir.ensure('test-file')
188 tmpdir.ensure('test-file')
189 tmpdir.ensure('test-file-1')
189 tmpdir.ensure('test-file-1')
190 tmp_path = str(tmpdir)
190 tmp_path = str(tmpdir)
191 dirpaths = utils.get_dirpaths(tmp_path)
191 dirpaths = utils.get_dirpaths(tmp_path)
192 assert list(sorted(dirpaths)) == ['test-file', 'test-file-1']
192 assert list(sorted(dirpaths)) == ['test-file', 'test-file-1']
193
193
194
194
195 def test_get_dirpaths_returns_all_paths_on_bytes(tmpdir):
195 def test_get_dirpaths_returns_all_paths_on_bytes(tmpdir):
196 tmpdir.ensure('test-file-bytes')
196 tmpdir.ensure('test-file-bytes')
197 tmp_path = str(tmpdir)
197 tmp_path = str(tmpdir)
198 dirpaths = utils.get_dirpaths(safe_bytes(tmp_path))
198 dirpaths = utils.get_dirpaths(safe_bytes(tmp_path))
199 assert list(sorted(dirpaths)) == [b'test-file-bytes']
199 assert list(sorted(dirpaths)) == [b'test-file-bytes']
200
200
201
201
202 def test_get_dirpaths_returns_all_paths_bytes(
202 def test_get_dirpaths_returns_all_paths_bytes(
203 tmpdir, platform_encodes_filenames):
203 tmpdir, platform_encodes_filenames):
204 if platform_encodes_filenames:
204 if platform_encodes_filenames:
205 pytest.skip("This platform seems to encode filenames.")
205 pytest.skip("This platform seems to encode filenames.")
206 tmpdir.ensure('repo-a-umlaut-\xe4')
206 tmpdir.ensure('repo-a-umlaut-\xe4')
207 dirpaths = utils.get_dirpaths(str(tmpdir))
207 dirpaths = utils.get_dirpaths(str(tmpdir))
208 assert dirpaths == ['repo-a-umlaut-\xe4']
208 assert dirpaths == ['repo-a-umlaut-\xe4']
209
209
210
210
211 def test_get_dirpaths_skips_paths_it_cannot_decode(
211 def test_get_dirpaths_skips_paths_it_cannot_decode(
212 tmpdir, platform_encodes_filenames):
212 tmpdir, platform_encodes_filenames):
213 if platform_encodes_filenames:
213 if platform_encodes_filenames:
214 pytest.skip("This platform seems to encode filenames.")
214 pytest.skip("This platform seems to encode filenames.")
215 path_with_latin1 = 'repo-a-umlaut-\xe4'
215 path_with_latin1 = 'repo-a-umlaut-\xe4'
216 tmp_path = str(tmpdir.ensure(path_with_latin1))
216 tmp_path = str(tmpdir.ensure(path_with_latin1))
217 dirpaths = utils.get_dirpaths(tmp_path)
217 dirpaths = utils.get_dirpaths(tmp_path)
218 assert dirpaths == []
218 assert dirpaths == []
219
219
220
220
221 @pytest.fixture(scope='session')
221 @pytest.fixture(scope='session')
222 def platform_encodes_filenames():
222 def platform_encodes_filenames():
223 """
223 """
224 Boolean indicator if the current platform changes filename encodings.
224 Boolean indicator if the current platform changes filename encodings.
225 """
225 """
226 path_with_latin1 = 'repo-a-umlaut-\xe4'
226 path_with_latin1 = 'repo-a-umlaut-\xe4'
227 tmpdir = py.path.local.mkdtemp()
227 tmpdir = py.path.local.mkdtemp()
228 tmpdir.ensure(path_with_latin1)
228 tmpdir.ensure(path_with_latin1)
229 read_path = tmpdir.listdir()[0].basename
229 read_path = tmpdir.listdir()[0].basename
230 tmpdir.remove()
230 tmpdir.remove()
231 return path_with_latin1 != read_path
231 return path_with_latin1 != read_path
232
232
233
233
234 def test_repo2db_mapper_groups(repo_groups):
234 def test_repo2db_mapper_groups(repo_groups):
235 session = meta.Session()
235 session = meta.Session()
236 zombie_group, parent_group, child_group = repo_groups
236 zombie_group, parent_group, child_group = repo_groups
237 zombie_path = os.path.join(
237 zombie_path = os.path.join(
238 RepoGroupModel().repos_path, zombie_group.full_path)
238 RepoGroupModel().repos_path, zombie_group.full_path)
239 os.rmdir(zombie_path)
239 os.rmdir(zombie_path)
240
240
241 # Avoid removing test repos when calling repo2db_mapper
241 # Avoid removing test repos when calling repo2db_mapper
242 repo_list = {
242 repo_list = {
243 repo.repo_name: 'test' for repo in session.query(db.Repository).all()
243 repo.repo_name: 'test' for repo in session.query(db.Repository).all()
244 }
244 }
245 utils.repo2db_mapper(repo_list, remove_obsolete=True)
245 utils.repo2db_mapper(repo_list, remove_obsolete=True)
246
246
247 groups_in_db = session.query(db.RepoGroup).all()
247 groups_in_db = session.query(db.RepoGroup).all()
248 assert child_group in groups_in_db
248 assert child_group in groups_in_db
249 assert parent_group in groups_in_db
249 assert parent_group in groups_in_db
250 assert zombie_path not in groups_in_db
250 assert zombie_path not in groups_in_db
251
251
252
252
253 def test_repo2db_mapper_enables_largefiles(backend):
253 def test_repo2db_mapper_enables_largefiles(backend):
254 repo = backend.create_repo()
254 repo = backend.create_repo()
255 repo_list = {repo.repo_name: 'test'}
255 repo_list = {repo.repo_name: 'test'}
256 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm_mock:
256 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm_mock:
257 utils.repo2db_mapper(repo_list, remove_obsolete=False)
257 utils.repo2db_mapper(repo_list, remove_obsolete=False)
258 _, kwargs = scm_mock.call_args
258 _, kwargs = scm_mock.call_args
259 assert kwargs['config'].get('extensions', 'largefiles') == ''
259 assert kwargs['config'].get('extensions', 'largefiles') == ''
260
260
261
261
262 @pytest.mark.backends("git", "svn")
262 @pytest.mark.backends("git", "svn")
263 def test_repo2db_mapper_installs_hooks_for_repos_in_db(backend):
263 def test_repo2db_mapper_installs_hooks_for_repos_in_db(backend):
264 repo = backend.create_repo()
264 repo = backend.create_repo()
265 repo_list = {repo.repo_name: 'test'}
265 repo_list = {repo.repo_name: 'test'}
266 utils.repo2db_mapper(repo_list, remove_obsolete=False)
266 utils.repo2db_mapper(repo_list, remove_obsolete=False)
267
267
268
268
269 @pytest.mark.backends("git", "svn")
269 @pytest.mark.backends("git", "svn")
270 def test_repo2db_mapper_installs_hooks_for_newly_added_repos(backend):
270 def test_repo2db_mapper_installs_hooks_for_newly_added_repos(backend):
271 repo = backend.create_repo()
271 repo = backend.create_repo()
272 RepoModel().delete(repo, fs_remove=False)
272 RepoModel().delete(repo, fs_remove=False)
273 meta.Session().commit()
273 meta.Session().commit()
274 repo_list = {repo.repo_name: repo.scm_instance()}
274 repo_list = {repo.repo_name: repo.scm_instance()}
275 utils.repo2db_mapper(repo_list, remove_obsolete=False)
275 utils.repo2db_mapper(repo_list, remove_obsolete=False)
276
276
277
277
278 class TestPasswordChanged(object):
278 class TestPasswordChanged(object):
279
279
280 def setup_method(self):
280 def setup_method(self):
281 self.session = {
281 self.session = {
282 'rhodecode_user': {
282 'rhodecode_user': {
283 'password': '0cc175b9c0f1b6a831c399e269772661'
283 'password': '0cc175b9c0f1b6a831c399e269772661'
284 }
284 }
285 }
285 }
286 self.auth_user = mock.Mock()
286 self.auth_user = mock.Mock()
287 self.auth_user.userame = 'test'
287 self.auth_user.userame = 'test'
288 self.auth_user.password = 'abc123'
288 self.auth_user.password = 'abc123'
289
289
290 def test_returns_false_for_default_user(self):
290 def test_returns_false_for_default_user(self):
291 self.auth_user.username = db.User.DEFAULT_USER
291 self.auth_user.username = db.User.DEFAULT_USER
292 result = utils.password_changed(self.auth_user, self.session)
292 result = utils.password_changed(self.auth_user, self.session)
293 assert result is False
293 assert result is False
294
294
295 def test_returns_false_if_password_was_not_changed(self):
295 def test_returns_false_if_password_was_not_changed(self):
296 self.session['rhodecode_user']['password'] = md5_safe(
296 self.session['rhodecode_user']['password'] = md5_safe(
297 self.auth_user.password)
297 self.auth_user.password)
298 result = utils.password_changed(self.auth_user, self.session)
298 result = utils.password_changed(self.auth_user, self.session)
299 assert result is False
299 assert result is False
300
300
301 def test_returns_true_if_password_was_changed(self):
301 def test_returns_true_if_password_was_changed(self):
302 result = utils.password_changed(self.auth_user, self.session)
302 result = utils.password_changed(self.auth_user, self.session)
303 assert result is True
303 assert result is True
304
304
305 def test_returns_true_if_auth_user_password_is_empty(self):
305 def test_returns_true_if_auth_user_password_is_empty(self):
306 self.auth_user.password = None
306 self.auth_user.password = None
307 result = utils.password_changed(self.auth_user, self.session)
307 result = utils.password_changed(self.auth_user, self.session)
308 assert result is True
308 assert result is True
309
309
310 def test_returns_true_if_session_password_is_empty(self):
310 def test_returns_true_if_session_password_is_empty(self):
311 self.session['rhodecode_user'].pop('password')
311 self.session['rhodecode_user'].pop('password')
312 result = utils.password_changed(self.auth_user, self.session)
312 result = utils.password_changed(self.auth_user, self.session)
313 assert result is True
313 assert result is True
314
314
315
315
316 class TestReadOpenSourceLicenses(object):
316 class TestReadOpenSourceLicenses(object):
317 def test_success(self):
317 def test_success(self):
318 utils._license_cache = None
318 utils._license_cache = None
319 json_data = '''
319 json_data = '''
320 {
320 {
321 "python2.7-pytest-2.7.1": {"UNKNOWN": null},
321 "python2.7-pytest-2.7.1": {"UNKNOWN": null},
322 "python2.7-Markdown-2.6.2": {
322 "python2.7-Markdown-2.6.2": {
323 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
323 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
324 }
324 }
325 }
325 }
326 '''
326 '''
327 resource_string_patch = mock.patch.object(
327 resource_string_patch = mock.patch.object(
328 utils.pkg_resources, 'resource_string', return_value=json_data)
328 utils.pkg_resources, 'resource_string', return_value=json_data)
329 with resource_string_patch:
329 with resource_string_patch:
330 result = utils.read_opensource_licenses()
330 result = utils.read_opensource_licenses()
331 assert result == json.loads(json_data)
331 assert result == json.loads(json_data)
332
332
333 def test_caching(self):
333 def test_caching(self):
334 utils._license_cache = {
334 utils._license_cache = {
335 "python2.7-pytest-2.7.1": {
335 "python2.7-pytest-2.7.1": {
336 "UNKNOWN": None
336 "UNKNOWN": None
337 },
337 },
338 "python2.7-Markdown-2.6.2": {
338 "python2.7-Markdown-2.6.2": {
339 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
339 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
340 }
340 }
341 }
341 }
342 resource_patch = mock.patch.object(
342 resource_patch = mock.patch.object(
343 utils.pkg_resources, 'resource_string', side_effect=Exception)
343 utils.pkg_resources, 'resource_string', side_effect=Exception)
344 json_patch = mock.patch.object(
344 json_patch = mock.patch.object(
345 utils.json, 'loads', side_effect=Exception)
345 utils.json, 'loads', side_effect=Exception)
346
346
347 with resource_patch as resource_mock, json_patch as json_mock:
347 with resource_patch as resource_mock, json_patch as json_mock:
348 result = utils.read_opensource_licenses()
348 result = utils.read_opensource_licenses()
349
349
350 assert resource_mock.call_count == 0
350 assert resource_mock.call_count == 0
351 assert json_mock.call_count == 0
351 assert json_mock.call_count == 0
352 assert result == utils._license_cache
352 assert result == utils._license_cache
353
353
354 def test_licenses_file_contains_no_unknown_licenses(self):
354 def test_licenses_file_contains_no_unknown_licenses(self):
355 utils._license_cache = None
355 utils._license_cache = None
356 result = utils.read_opensource_licenses()
356 result = utils.read_opensource_licenses()
357
357
358 for license_data in result:
358 for license_data in result:
359 if isinstance(license_data["license"], list):
359 if isinstance(license_data["license"], list):
360 for lic_data in license_data["license"]:
360 for lic_data in license_data["license"]:
361 assert 'UNKNOWN' not in lic_data["fullName"]
361 assert 'UNKNOWN' not in lic_data["fullName"]
362 else:
362 else:
363 full_name = license_data.get("fullName") or license_data
363 full_name = license_data.get("fullName") or license_data
364 assert 'UNKNOWN' not in full_name
364 assert 'UNKNOWN' not in full_name
365
365
366
366
367 class TestMakeDbConfig(object):
367 class TestMakeDbConfig(object):
368 def test_data_from_config_data_from_db_returned(self):
368 def test_data_from_config_data_from_db_returned(self):
369 test_data = [
369 test_data = [
370 ('section1', 'option1', 'value1'),
370 ('section1', 'option1', 'value1'),
371 ('section2', 'option2', 'value2'),
371 ('section2', 'option2', 'value2'),
372 ('section3', 'option3', 'value3'),
372 ('section3', 'option3', 'value3'),
373 ]
373 ]
374 with mock.patch.object(utils, 'config_data_from_db') as config_mock:
374 with mock.patch.object(utils, 'prepare_config_data') as config_mock:
375 config_mock.return_value = test_data
375 config_mock.return_value = test_data
376 kwargs = {'clear_session': False, 'repo': 'test_repo'}
376 kwargs = {'clear_session': False, 'repo': 'test_repo'}
377 result = utils.make_db_config(**kwargs)
377 result = utils.make_db_config(**kwargs)
378 config_mock.assert_called_once_with(**kwargs)
378 config_mock.assert_called_once_with(**kwargs)
379 for section, option, expected_value in test_data:
379 for section, option, expected_value in test_data:
380 value = result.get(section, option)
380 value = result.get(section, option)
381 assert value == expected_value
381 assert value == expected_value
382
382
383
383
384 class TestConfigDataFromDb(object):
384 class TestPrepareConfigData(object):
385 def test_config_data_from_db_returns_active_settings(self):
385 def test_prepare_config_data_returns_active_settings(self):
386 test_data = [
386 test_data = [
387 UiSetting('section1', 'option1', 'value1', True),
387 UiSetting('section1', 'option1', 'value1', True),
388 UiSetting('section2', 'option2', 'value2', True),
388 UiSetting('section2', 'option2', 'value2', True),
389 UiSetting('section3', 'option3', 'value3', False),
389 UiSetting('section3', 'option3', 'value3', False),
390 ]
390 ]
391 repo_name = 'test_repo'
391 repo_name = 'test_repo'
392
392
393 model_patch = mock.patch.object(settings, 'VcsSettingsModel')
393 model_patch = mock.patch.object(settings, 'VcsSettingsModel')
394 hooks_patch = mock.patch.object(
394 hooks_patch = mock.patch.object(
395 utils, 'get_enabled_hook_classes',
395 utils, 'get_enabled_hook_classes',
396 return_value=['pull', 'push', 'repo_size'])
396 return_value=['pull', 'push', 'repo_size'])
397 with model_patch as model_mock, hooks_patch:
397 with model_patch as model_mock, hooks_patch:
398 instance_mock = mock.Mock()
398 instance_mock = mock.Mock()
399 model_mock.return_value = instance_mock
399 model_mock.return_value = instance_mock
400 instance_mock.get_ui_settings.return_value = test_data
400 instance_mock.get_ui_settings.return_value = test_data
401 result = utils.config_data_from_db(
401 result = utils.prepare_config_data(
402 clear_session=False, repo=repo_name)
402 clear_session=False, repo=repo_name)
403
403
404 self._assert_repo_name_passed(model_mock, repo_name)
404 self._assert_repo_name_passed(model_mock, repo_name)
405
405
406 expected_result = [
406 expected_result = [
407 ('section1', 'option1', 'value1'),
407 ('section1', 'option1', 'value1'),
408 ('section2', 'option2', 'value2'),
408 ('section2', 'option2', 'value2'),
409 ]
409 ]
410 assert result == expected_result
410 # We have extra config items returned, so we're ignoring two last items
411 assert result[:2] == expected_result
411
412
412 def _assert_repo_name_passed(self, model_mock, repo_name):
413 def _assert_repo_name_passed(self, model_mock, repo_name):
413 assert model_mock.call_count == 1
414 assert model_mock.call_count == 1
414 call_args, call_kwargs = model_mock.call_args
415 call_args, call_kwargs = model_mock.call_args
415 assert call_kwargs['repo'] == repo_name
416 assert call_kwargs['repo'] == repo_name
416
417
417
418
418 class TestIsDirWritable(object):
419 class TestIsDirWritable(object):
419 def test_returns_false_when_not_writable(self):
420 def test_returns_false_when_not_writable(self):
420 with mock.patch('builtins.open', side_effect=OSError):
421 with mock.patch('builtins.open', side_effect=OSError):
421 assert not utils._is_dir_writable('/stub-path')
422 assert not utils._is_dir_writable('/stub-path')
422
423
423 def test_returns_true_when_writable(self, tmpdir):
424 def test_returns_true_when_writable(self, tmpdir):
424 assert utils._is_dir_writable(str(tmpdir))
425 assert utils._is_dir_writable(str(tmpdir))
425
426
426 def test_is_safe_against_race_conditions(self, tmpdir):
427 def test_is_safe_against_race_conditions(self, tmpdir):
427 workers = multiprocessing.Pool()
428 workers = multiprocessing.Pool()
428 directories = [str(tmpdir)] * 10
429 directories = [str(tmpdir)] * 10
429 workers.map(utils._is_dir_writable, directories)
430 workers.map(utils._is_dir_writable, directories)
430
431
431
432
432 class TestGetEnabledHooks(object):
433 class TestGetEnabledHooks(object):
433 def test_only_active_hooks_are_enabled(self):
434 def test_only_active_hooks_are_enabled(self):
434 ui_settings = [
435 ui_settings = [
435 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
436 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
436 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
437 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
437 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', False)
438 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', False)
438 ]
439 ]
439 result = utils.get_enabled_hook_classes(ui_settings)
440 result = utils.get_enabled_hook_classes(ui_settings)
440 assert result == ['push', 'repo_size']
441 assert result == ['push', 'repo_size']
441
442
442 def test_all_hooks_are_enabled(self):
443 def test_all_hooks_are_enabled(self):
443 ui_settings = [
444 ui_settings = [
444 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
445 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
445 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
446 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
446 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', True)
447 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', True)
447 ]
448 ]
448 result = utils.get_enabled_hook_classes(ui_settings)
449 result = utils.get_enabled_hook_classes(ui_settings)
449 assert result == ['push', 'repo_size', 'pull']
450 assert result == ['push', 'repo_size', 'pull']
450
451
451 def test_no_enabled_hooks_when_no_hook_settings_are_found(self):
452 def test_no_enabled_hooks_when_no_hook_settings_are_found(self):
452 ui_settings = []
453 ui_settings = []
453 result = utils.get_enabled_hook_classes(ui_settings)
454 result = utils.get_enabled_hook_classes(ui_settings)
454 assert result == []
455 assert result == []
455
456
456
457
457 def test_obfuscate_url_pw():
458 def test_obfuscate_url_pw():
458 from rhodecode.lib.utils2 import obfuscate_url_pw
459 from rhodecode.lib.utils2 import obfuscate_url_pw
459 engine = u'/home/repos/malmΓΆ'
460 engine = u'/home/repos/malmΓΆ'
460 assert obfuscate_url_pw(engine)
461 assert obfuscate_url_pw(engine)
461
462
462
463
463 @pytest.mark.parametrize("test_ua, expected", [
464 @pytest.mark.parametrize("test_ua, expected", [
464 ("", ""),
465 ("", ""),
465 ('"quoted"', 'quoted'),
466 ('"quoted"', 'quoted'),
466 ('internal-merge', 'internal-merge'),
467 ('internal-merge', 'internal-merge'),
467 ('hg/internal-merge', 'hg/internal-merge'),
468 ('hg/internal-merge', 'hg/internal-merge'),
468 ('git/internal-merge', 'git/internal-merge'),
469 ('git/internal-merge', 'git/internal-merge'),
469
470
470 # git
471 # git
471 ('git/2.10.1 (Apple Git-78)', 'git/2.10.1'),
472 ('git/2.10.1 (Apple Git-78)', 'git/2.10.1'),
472 ('GiT/2.37.2.windows.2', 'git/2.37.2'),
473 ('GiT/2.37.2.windows.2', 'git/2.37.2'),
473 ('git/2.35.1 (Microsoft Windows NT 10.0.19044.0; Win32NT x64) CLR/4.0.30319 VS16/16.0.0', 'git/2.35.1'),
474 ('git/2.35.1 (Microsoft Windows NT 10.0.19044.0; Win32NT x64) CLR/4.0.30319 VS16/16.0.0', 'git/2.35.1'),
474 ('ssh-user-agent', 'ssh-user-agent'),
475 ('ssh-user-agent', 'ssh-user-agent'),
475 ('git/ssh-user-agent', 'git/ssh-user-agent'),
476 ('git/ssh-user-agent', 'git/ssh-user-agent'),
476
477
477
478
478 # hg
479 # hg
479 ('mercurial/proto-1.0 (Mercurial 4.2)', 'mercurial/4.2'),
480 ('mercurial/proto-1.0 (Mercurial 4.2)', 'mercurial/4.2'),
480 ('mercurial/proto-1.0', ''),
481 ('mercurial/proto-1.0', ''),
481 ('mercurial/proto-1.0 (Mercurial 3.9.2)', 'mercurial/3.9.2'),
482 ('mercurial/proto-1.0 (Mercurial 3.9.2)', 'mercurial/3.9.2'),
482 ('mercurial/ssh-user-agent', 'mercurial/ssh-user-agent'),
483 ('mercurial/ssh-user-agent', 'mercurial/ssh-user-agent'),
483 ('mercurial/proto-1.0 (Mercurial 5.8rc0)', 'mercurial/5.8rc0'),
484 ('mercurial/proto-1.0 (Mercurial 5.8rc0)', 'mercurial/5.8rc0'),
484
485
485
486
486 ])
487 ])
487 def test_user_agent_normalizer(test_ua, expected):
488 def test_user_agent_normalizer(test_ua, expected):
488 from rhodecode.lib.utils2 import user_agent_normalizer
489 from rhodecode.lib.utils2 import user_agent_normalizer
489 assert user_agent_normalizer(test_ua, safe=False) == expected
490 assert user_agent_normalizer(test_ua, safe=False) == expected
@@ -1,1114 +1,1108 b''
1
1
2 # Copyright (C) 2010-2023 RhodeCode GmbH
2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 #
3 #
4 # This program is free software: you can redistribute it and/or modify
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License, version 3
5 # it under the terms of the GNU Affero General Public License, version 3
6 # (only), as published by the Free Software Foundation.
6 # (only), as published by the Free Software Foundation.
7 #
7 #
8 # This program is distributed in the hope that it will be useful,
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
11 # GNU General Public License for more details.
12 #
12 #
13 # You should have received a copy of the GNU Affero General Public License
13 # You should have received a copy of the GNU Affero General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #
15 #
16 # This program is dual-licensed. If you wish to learn more about the
16 # This program is dual-licensed. If you wish to learn more about the
17 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19
19
20 import mock
20 import mock
21 import pytest
21 import pytest
22
22
23 from rhodecode.lib.utils2 import str2bool
23 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.settings import VcsSettingsModel, UiSetting
25 from rhodecode.model.settings import VcsSettingsModel, UiSetting
26
26
27
27
28 HOOKS_FORM_DATA = {
28 HOOKS_FORM_DATA = {
29 'hooks_changegroup_repo_size': True,
29 'hooks_changegroup_repo_size': True,
30 'hooks_changegroup_push_logger': True,
30 'hooks_changegroup_push_logger': True,
31 'hooks_outgoing_pull_logger': True
31 'hooks_outgoing_pull_logger': True
32 }
32 }
33
33
34 SVN_FORM_DATA = {
34 SVN_FORM_DATA = {
35 'new_svn_branch': 'test-branch',
35 'new_svn_branch': 'test-branch',
36 'new_svn_tag': 'test-tag'
36 'new_svn_tag': 'test-tag'
37 }
37 }
38
38
39 GENERAL_FORM_DATA = {
39 GENERAL_FORM_DATA = {
40 'rhodecode_pr_merge_enabled': True,
40 'rhodecode_pr_merge_enabled': True,
41 'rhodecode_use_outdated_comments': True,
41 'rhodecode_use_outdated_comments': True,
42 'rhodecode_hg_use_rebase_for_merging': True,
42 'rhodecode_hg_use_rebase_for_merging': True,
43 'rhodecode_hg_close_branch_before_merging': True,
43 'rhodecode_hg_close_branch_before_merging': True,
44 'rhodecode_git_use_rebase_for_merging': True,
44 'rhodecode_git_use_rebase_for_merging': True,
45 'rhodecode_git_close_branch_before_merging': True,
45 'rhodecode_git_close_branch_before_merging': True,
46 'rhodecode_diff_cache': True,
46 'rhodecode_diff_cache': True,
47 }
47 }
48
48
49
49
50 class TestInheritGlobalSettingsProperty(object):
50 class TestInheritGlobalSettingsProperty(object):
51 def test_get_raises_exception_when_repository_not_specified(self):
51 def test_get_raises_exception_when_repository_not_specified(self):
52 model = VcsSettingsModel()
52 model = VcsSettingsModel()
53 with pytest.raises(Exception) as exc_info:
53 with pytest.raises(Exception) as exc_info:
54 model.inherit_global_settings
54 model.inherit_global_settings
55 assert str(exc_info.value) == 'Repository is not specified'
55 assert str(exc_info.value) == 'Repository is not specified'
56
56
57 def test_true_is_returned_when_value_is_not_found(self, repo_stub):
57 def test_true_is_returned_when_value_is_not_found(self, repo_stub):
58 model = VcsSettingsModel(repo=repo_stub.repo_name)
58 model = VcsSettingsModel(repo=repo_stub.repo_name)
59 assert model.inherit_global_settings is True
59 assert model.inherit_global_settings is True
60
60
61 def test_value_is_returned(self, repo_stub, settings_util):
61 def test_value_is_returned(self, repo_stub, settings_util):
62 model = VcsSettingsModel(repo=repo_stub.repo_name)
62 model = VcsSettingsModel(repo=repo_stub.repo_name)
63 settings_util.create_repo_rhodecode_setting(
63 settings_util.create_repo_rhodecode_setting(
64 repo_stub, VcsSettingsModel.INHERIT_SETTINGS, False, 'bool')
64 repo_stub, VcsSettingsModel.INHERIT_SETTINGS, False, 'bool')
65 assert model.inherit_global_settings is False
65 assert model.inherit_global_settings is False
66
66
67 def test_value_is_set(self, repo_stub):
67 def test_value_is_set(self, repo_stub):
68 model = VcsSettingsModel(repo=repo_stub.repo_name)
68 model = VcsSettingsModel(repo=repo_stub.repo_name)
69 model.inherit_global_settings = False
69 model.inherit_global_settings = False
70 setting = model.repo_settings.get_setting_by_name(
70 setting = model.repo_settings.get_setting_by_name(
71 VcsSettingsModel.INHERIT_SETTINGS)
71 VcsSettingsModel.INHERIT_SETTINGS)
72 try:
72 try:
73 assert setting.app_settings_type == 'bool'
73 assert setting.app_settings_type == 'bool'
74 assert setting.app_settings_value is False
74 assert setting.app_settings_value is False
75 finally:
75 finally:
76 Session().delete(setting)
76 Session().delete(setting)
77 Session().commit()
77 Session().commit()
78
78
79 def test_set_raises_exception_when_repository_not_specified(self):
79 def test_set_raises_exception_when_repository_not_specified(self):
80 model = VcsSettingsModel()
80 model = VcsSettingsModel()
81 with pytest.raises(Exception) as exc_info:
81 with pytest.raises(Exception) as exc_info:
82 model.inherit_global_settings = False
82 model.inherit_global_settings = False
83 assert str(exc_info.value) == 'Repository is not specified'
83 assert str(exc_info.value) == 'Repository is not specified'
84
84
85
85
86 class TestVcsSettingsModel(object):
86 class TestVcsSettingsModel(object):
87 def test_global_svn_branch_patterns(self):
87 def test_global_svn_branch_patterns(self):
88 model = VcsSettingsModel()
88 model = VcsSettingsModel()
89 expected_result = {'test': 'test'}
89 expected_result = {'test': 'test'}
90 with mock.patch.object(model, 'global_settings') as settings_mock:
90 with mock.patch.object(model, 'global_settings') as settings_mock:
91 get_settings = settings_mock.get_ui_by_section
91 get_settings = settings_mock.get_ui_by_section
92 get_settings.return_value = expected_result
92 get_settings.return_value = expected_result
93 settings_mock.return_value = expected_result
93 settings_mock.return_value = expected_result
94 result = model.get_global_svn_branch_patterns()
94 result = model.get_global_svn_branch_patterns()
95
95
96 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
96 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
97 assert expected_result == result
97 assert expected_result == result
98
98
99 def test_repo_svn_branch_patterns(self):
99 def test_repo_svn_branch_patterns(self):
100 model = VcsSettingsModel()
100 model = VcsSettingsModel()
101 expected_result = {'test': 'test'}
101 expected_result = {'test': 'test'}
102 with mock.patch.object(model, 'repo_settings') as settings_mock:
102 with mock.patch.object(model, 'repo_settings') as settings_mock:
103 get_settings = settings_mock.get_ui_by_section
103 get_settings = settings_mock.get_ui_by_section
104 get_settings.return_value = expected_result
104 get_settings.return_value = expected_result
105 settings_mock.return_value = expected_result
105 settings_mock.return_value = expected_result
106 result = model.get_repo_svn_branch_patterns()
106 result = model.get_repo_svn_branch_patterns()
107
107
108 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
108 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
109 assert expected_result == result
109 assert expected_result == result
110
110
111 def test_repo_svn_branch_patterns_raises_exception_when_repo_is_not_set(
111 def test_repo_svn_branch_patterns_raises_exception_when_repo_is_not_set(
112 self):
112 self):
113 model = VcsSettingsModel()
113 model = VcsSettingsModel()
114 with pytest.raises(Exception) as exc_info:
114 with pytest.raises(Exception) as exc_info:
115 model.get_repo_svn_branch_patterns()
115 model.get_repo_svn_branch_patterns()
116 assert str(exc_info.value) == 'Repository is not specified'
116 assert str(exc_info.value) == 'Repository is not specified'
117
117
118 def test_global_svn_tag_patterns(self):
118 def test_global_svn_tag_patterns(self):
119 model = VcsSettingsModel()
119 model = VcsSettingsModel()
120 expected_result = {'test': 'test'}
120 expected_result = {'test': 'test'}
121 with mock.patch.object(model, 'global_settings') as settings_mock:
121 with mock.patch.object(model, 'global_settings') as settings_mock:
122 get_settings = settings_mock.get_ui_by_section
122 get_settings = settings_mock.get_ui_by_section
123 get_settings.return_value = expected_result
123 get_settings.return_value = expected_result
124 settings_mock.return_value = expected_result
124 settings_mock.return_value = expected_result
125 result = model.get_global_svn_tag_patterns()
125 result = model.get_global_svn_tag_patterns()
126
126
127 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
127 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
128 assert expected_result == result
128 assert expected_result == result
129
129
130 def test_repo_svn_tag_patterns(self):
130 def test_repo_svn_tag_patterns(self):
131 model = VcsSettingsModel()
131 model = VcsSettingsModel()
132 expected_result = {'test': 'test'}
132 expected_result = {'test': 'test'}
133 with mock.patch.object(model, 'repo_settings') as settings_mock:
133 with mock.patch.object(model, 'repo_settings') as settings_mock:
134 get_settings = settings_mock.get_ui_by_section
134 get_settings = settings_mock.get_ui_by_section
135 get_settings.return_value = expected_result
135 get_settings.return_value = expected_result
136 settings_mock.return_value = expected_result
136 settings_mock.return_value = expected_result
137 result = model.get_repo_svn_tag_patterns()
137 result = model.get_repo_svn_tag_patterns()
138
138
139 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
139 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
140 assert expected_result == result
140 assert expected_result == result
141
141
142 def test_repo_svn_tag_patterns_raises_exception_when_repo_is_not_set(self):
142 def test_repo_svn_tag_patterns_raises_exception_when_repo_is_not_set(self):
143 model = VcsSettingsModel()
143 model = VcsSettingsModel()
144 with pytest.raises(Exception) as exc_info:
144 with pytest.raises(Exception) as exc_info:
145 model.get_repo_svn_tag_patterns()
145 model.get_repo_svn_tag_patterns()
146 assert str(exc_info.value) == 'Repository is not specified'
146 assert str(exc_info.value) == 'Repository is not specified'
147
147
148 def test_get_global_settings(self):
148 def test_get_global_settings(self):
149 expected_result = {'test': 'test'}
149 expected_result = {'test': 'test'}
150 model = VcsSettingsModel()
150 model = VcsSettingsModel()
151 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
151 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
152 collect_mock.return_value = expected_result
152 collect_mock.return_value = expected_result
153 result = model.get_global_settings()
153 result = model.get_global_settings()
154
154
155 collect_mock.assert_called_once_with(global_=True)
155 collect_mock.assert_called_once_with(global_=True)
156 assert result == expected_result
156 assert result == expected_result
157
157
158 def test_get_repo_settings(self, repo_stub):
158 def test_get_repo_settings(self, repo_stub):
159 model = VcsSettingsModel(repo=repo_stub.repo_name)
159 model = VcsSettingsModel(repo=repo_stub.repo_name)
160 expected_result = {'test': 'test'}
160 expected_result = {'test': 'test'}
161 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
161 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
162 collect_mock.return_value = expected_result
162 collect_mock.return_value = expected_result
163 result = model.get_repo_settings()
163 result = model.get_repo_settings()
164
164
165 collect_mock.assert_called_once_with(global_=False)
165 collect_mock.assert_called_once_with(global_=False)
166 assert result == expected_result
166 assert result == expected_result
167
167
168 @pytest.mark.parametrize('settings, global_', [
168 @pytest.mark.parametrize('settings, global_', [
169 ('global_settings', True),
169 ('global_settings', True),
170 ('repo_settings', False)
170 ('repo_settings', False)
171 ])
171 ])
172 def test_collect_all_settings(self, settings, global_):
172 def test_collect_all_settings(self, settings, global_):
173 model = VcsSettingsModel()
173 model = VcsSettingsModel()
174 result_mock = self._mock_result()
174 result_mock = self._mock_result()
175
175
176 settings_patch = mock.patch.object(model, settings)
176 settings_patch = mock.patch.object(model, settings)
177 with settings_patch as settings_mock:
177 with settings_patch as settings_mock:
178 settings_mock.get_ui_by_section_and_key.return_value = result_mock
178 settings_mock.get_ui_by_section_and_key.return_value = result_mock
179 settings_mock.get_setting_by_name.return_value = result_mock
179 settings_mock.get_setting_by_name.return_value = result_mock
180 result = model._collect_all_settings(global_=global_)
180 result = model._collect_all_settings(global_=global_)
181
181
182 ui_settings = model.HG_SETTINGS + model.GIT_SETTINGS + model.HOOKS_SETTINGS
182 ui_settings = model.HG_SETTINGS + model.GIT_SETTINGS + model.HOOKS_SETTINGS
183 self._assert_get_settings_calls(
183 self._assert_get_settings_calls(
184 settings_mock, ui_settings, model.GENERAL_SETTINGS)
184 settings_mock, ui_settings, model.GENERAL_SETTINGS)
185 self._assert_collect_all_settings_result(
185 self._assert_collect_all_settings_result(
186 ui_settings, model.GENERAL_SETTINGS, result)
186 ui_settings, model.GENERAL_SETTINGS, result)
187
187
188 @pytest.mark.parametrize('settings, global_', [
188 @pytest.mark.parametrize('settings, global_', [
189 ('global_settings', True),
189 ('global_settings', True),
190 ('repo_settings', False)
190 ('repo_settings', False)
191 ])
191 ])
192 def test_collect_all_settings_without_empty_value(self, settings, global_):
192 def test_collect_all_settings_without_empty_value(self, settings, global_):
193 model = VcsSettingsModel()
193 model = VcsSettingsModel()
194
194
195 settings_patch = mock.patch.object(model, settings)
195 settings_patch = mock.patch.object(model, settings)
196 with settings_patch as settings_mock:
196 with settings_patch as settings_mock:
197 settings_mock.get_ui_by_section_and_key.return_value = None
197 settings_mock.get_ui_by_section_and_key.return_value = None
198 settings_mock.get_setting_by_name.return_value = None
198 settings_mock.get_setting_by_name.return_value = None
199 result = model._collect_all_settings(global_=global_)
199 result = model._collect_all_settings(global_=global_)
200
200
201 assert result == {}
201 assert result == {}
202
202
203 def _mock_result(self):
203 def _mock_result(self):
204 result_mock = mock.Mock()
204 result_mock = mock.Mock()
205 result_mock.ui_value = 'ui_value'
205 result_mock.ui_value = 'ui_value'
206 result_mock.ui_active = True
206 result_mock.ui_active = True
207 result_mock.app_settings_value = 'setting_value'
207 result_mock.app_settings_value = 'setting_value'
208 return result_mock
208 return result_mock
209
209
210 def _assert_get_settings_calls(
210 def _assert_get_settings_calls(
211 self, settings_mock, ui_settings, general_settings):
211 self, settings_mock, ui_settings, general_settings):
212 assert (
212 assert (
213 settings_mock.get_ui_by_section_and_key.call_count ==
213 settings_mock.get_ui_by_section_and_key.call_count ==
214 len(ui_settings))
214 len(ui_settings))
215 assert (
215 assert (
216 settings_mock.get_setting_by_name.call_count ==
216 settings_mock.get_setting_by_name.call_count ==
217 len(general_settings))
217 len(general_settings))
218
218
219 for section, key in ui_settings:
219 for section, key in ui_settings:
220 expected_call = mock.call(section, key)
220 expected_call = mock.call(section, key)
221 assert (
221 assert (
222 expected_call in
222 expected_call in
223 settings_mock.get_ui_by_section_and_key.call_args_list)
223 settings_mock.get_ui_by_section_and_key.call_args_list)
224
224
225 for name in general_settings:
225 for name in general_settings:
226 expected_call = mock.call(name)
226 expected_call = mock.call(name)
227 assert (
227 assert (
228 expected_call in
228 expected_call in
229 settings_mock.get_setting_by_name.call_args_list)
229 settings_mock.get_setting_by_name.call_args_list)
230
230
231 def _assert_collect_all_settings_result(
231 def _assert_collect_all_settings_result(
232 self, ui_settings, general_settings, result):
232 self, ui_settings, general_settings, result):
233 expected_result = {}
233 expected_result = {}
234 for section, key in ui_settings:
234 for section, key in ui_settings:
235 key = '{}_{}'.format(section, key.replace('.', '_'))
235 key = '{}_{}'.format(section, key.replace('.', '_'))
236
236
237 if section in ('extensions', 'hooks'):
237 if section in ('extensions', 'hooks'):
238 value = True
238 value = True
239 elif key in ['vcs_git_lfs_enabled']:
239 elif key in ['vcs_git_lfs_enabled']:
240 value = True
240 value = True
241 else:
241 else:
242 value = 'ui_value'
242 value = 'ui_value'
243 expected_result[key] = value
243 expected_result[key] = value
244
244
245 for name in general_settings:
245 for name in general_settings:
246 key = 'rhodecode_' + name
246 key = 'rhodecode_' + name
247 expected_result[key] = 'setting_value'
247 expected_result[key] = 'setting_value'
248
248
249 assert expected_result == result
249 assert expected_result == result
250
250
251
251
252 class TestCreateOrUpdateRepoHookSettings(object):
252 class TestCreateOrUpdateRepoHookSettings(object):
253 def test_create_when_no_repo_object_found(self, repo_stub):
253 def test_create_when_no_repo_object_found(self, repo_stub):
254 model = VcsSettingsModel(repo=repo_stub.repo_name)
254 model = VcsSettingsModel(repo=repo_stub.repo_name)
255
255
256 self._create_settings(model, HOOKS_FORM_DATA)
256 self._create_settings(model, HOOKS_FORM_DATA)
257
257
258 cleanup = []
258 cleanup = []
259 try:
259 try:
260 for section, key in model.HOOKS_SETTINGS:
260 for section, key in model.HOOKS_SETTINGS:
261 ui = model.repo_settings.get_ui_by_section_and_key(
261 ui = model.repo_settings.get_ui_by_section_and_key(
262 section, key)
262 section, key)
263 assert ui.ui_active is True
263 assert ui.ui_active is True
264 cleanup.append(ui)
264 cleanup.append(ui)
265 finally:
265 finally:
266 for ui in cleanup:
266 for ui in cleanup:
267 Session().delete(ui)
267 Session().delete(ui)
268 Session().commit()
268 Session().commit()
269
269
270 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
270 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
271 model = VcsSettingsModel(repo=repo_stub.repo_name)
271 model = VcsSettingsModel(repo=repo_stub.repo_name)
272
272
273 deleted_key = 'hooks_changegroup_repo_size'
273 deleted_key = 'hooks_changegroup_repo_size'
274 data = HOOKS_FORM_DATA.copy()
274 data = HOOKS_FORM_DATA.copy()
275 data.pop(deleted_key)
275 data.pop(deleted_key)
276
276
277 with pytest.raises(ValueError) as exc_info:
277 with pytest.raises(ValueError) as exc_info:
278 model.create_or_update_repo_hook_settings(data)
278 model.create_or_update_repo_hook_settings(data)
279 Session().commit()
279 Session().commit()
280
280
281 msg = 'The given data does not contain {} key'.format(deleted_key)
281 msg = 'The given data does not contain {} key'.format(deleted_key)
282 assert str(exc_info.value) == msg
282 assert str(exc_info.value) == msg
283
283
284 def test_update_when_repo_object_found(self, repo_stub, settings_util):
284 def test_update_when_repo_object_found(self, repo_stub, settings_util):
285 model = VcsSettingsModel(repo=repo_stub.repo_name)
285 model = VcsSettingsModel(repo=repo_stub.repo_name)
286 for section, key in model.HOOKS_SETTINGS:
286 for section, key in model.HOOKS_SETTINGS:
287 settings_util.create_repo_rhodecode_ui(
287 settings_util.create_repo_rhodecode_ui(
288 repo_stub, section, None, key=key, active=False)
288 repo_stub, section, None, key=key, active=False)
289 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
289 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
290 Session().commit()
290 Session().commit()
291
291
292 for section, key in model.HOOKS_SETTINGS:
292 for section, key in model.HOOKS_SETTINGS:
293 ui = model.repo_settings.get_ui_by_section_and_key(section, key)
293 ui = model.repo_settings.get_ui_by_section_and_key(section, key)
294 assert ui.ui_active is True
294 assert ui.ui_active is True
295
295
296 def _create_settings(self, model, data):
296 def _create_settings(self, model, data):
297 global_patch = mock.patch.object(model, 'global_settings')
297 global_patch = mock.patch.object(model, 'global_settings')
298 global_setting = mock.Mock()
298 global_setting = mock.Mock()
299 global_setting.ui_value = 'Test value'
299 global_setting.ui_value = 'Test value'
300 with global_patch as global_mock:
300 with global_patch as global_mock:
301 global_mock.get_ui_by_section_and_key.return_value = global_setting
301 global_mock.get_ui_by_section_and_key.return_value = global_setting
302 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
302 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
303 Session().commit()
303 Session().commit()
304
304
305
305
306 class TestUpdateGlobalHookSettings(object):
306 class TestUpdateGlobalHookSettings(object):
307 def test_update_raises_exception_when_data_incomplete(self):
307 def test_update_raises_exception_when_data_incomplete(self):
308 model = VcsSettingsModel()
308 model = VcsSettingsModel()
309
309
310 deleted_key = 'hooks_changegroup_repo_size'
310 deleted_key = 'hooks_changegroup_repo_size'
311 data = HOOKS_FORM_DATA.copy()
311 data = HOOKS_FORM_DATA.copy()
312 data.pop(deleted_key)
312 data.pop(deleted_key)
313
313
314 with pytest.raises(ValueError) as exc_info:
314 with pytest.raises(ValueError) as exc_info:
315 model.update_global_hook_settings(data)
315 model.update_global_hook_settings(data)
316 Session().commit()
316 Session().commit()
317
317
318 msg = 'The given data does not contain {} key'.format(deleted_key)
318 msg = 'The given data does not contain {} key'.format(deleted_key)
319 assert str(exc_info.value) == msg
319 assert str(exc_info.value) == msg
320
320
321 def test_update_global_hook_settings(self, settings_util):
321 def test_update_global_hook_settings(self, settings_util):
322 model = VcsSettingsModel()
322 model = VcsSettingsModel()
323 setting_mock = mock.MagicMock()
323 setting_mock = mock.MagicMock()
324 setting_mock.ui_active = False
324 setting_mock.ui_active = False
325 get_settings_patcher = mock.patch.object(
325 get_settings_patcher = mock.patch.object(
326 model.global_settings, 'get_ui_by_section_and_key',
326 model.global_settings, 'get_ui_by_section_and_key',
327 return_value=setting_mock)
327 return_value=setting_mock)
328 session_patcher = mock.patch('rhodecode.model.settings.Session')
328 session_patcher = mock.patch('rhodecode.model.settings.Session')
329 with get_settings_patcher as get_settings_mock, session_patcher:
329 with get_settings_patcher as get_settings_mock, session_patcher:
330 model.update_global_hook_settings(HOOKS_FORM_DATA)
330 model.update_global_hook_settings(HOOKS_FORM_DATA)
331 Session().commit()
331 Session().commit()
332
332
333 assert setting_mock.ui_active is True
333 assert setting_mock.ui_active is True
334 assert get_settings_mock.call_count == 3
334 assert get_settings_mock.call_count == 3
335
335
336
336
337 class TestCreateOrUpdateRepoGeneralSettings(object):
337 class TestCreateOrUpdateRepoGeneralSettings(object):
338 def test_calls_create_or_update_general_settings(self, repo_stub):
338 def test_calls_create_or_update_general_settings(self, repo_stub):
339 model = VcsSettingsModel(repo=repo_stub.repo_name)
339 model = VcsSettingsModel(repo=repo_stub.repo_name)
340 create_patch = mock.patch.object(
340 create_patch = mock.patch.object(
341 model, '_create_or_update_general_settings')
341 model, '_create_or_update_general_settings')
342 with create_patch as create_mock:
342 with create_patch as create_mock:
343 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
343 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
344 Session().commit()
344 Session().commit()
345
345
346 create_mock.assert_called_once_with(
346 create_mock.assert_called_once_with(
347 model.repo_settings, GENERAL_FORM_DATA)
347 model.repo_settings, GENERAL_FORM_DATA)
348
348
349 def test_raises_exception_when_repository_is_not_specified(self):
349 def test_raises_exception_when_repository_is_not_specified(self):
350 model = VcsSettingsModel()
350 model = VcsSettingsModel()
351 with pytest.raises(Exception) as exc_info:
351 with pytest.raises(Exception) as exc_info:
352 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
352 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
353 assert str(exc_info.value) == 'Repository is not specified'
353 assert str(exc_info.value) == 'Repository is not specified'
354
354
355
355
356 class TestCreateOrUpdatGlobalGeneralSettings(object):
356 class TestCreateOrUpdatGlobalGeneralSettings(object):
357 def test_calls_create_or_update_general_settings(self):
357 def test_calls_create_or_update_general_settings(self):
358 model = VcsSettingsModel()
358 model = VcsSettingsModel()
359 create_patch = mock.patch.object(
359 create_patch = mock.patch.object(
360 model, '_create_or_update_general_settings')
360 model, '_create_or_update_general_settings')
361 with create_patch as create_mock:
361 with create_patch as create_mock:
362 model.create_or_update_global_pr_settings(GENERAL_FORM_DATA)
362 model.create_or_update_global_pr_settings(GENERAL_FORM_DATA)
363 create_mock.assert_called_once_with(
363 create_mock.assert_called_once_with(
364 model.global_settings, GENERAL_FORM_DATA)
364 model.global_settings, GENERAL_FORM_DATA)
365
365
366
366
367 class TestCreateOrUpdateGeneralSettings(object):
367 class TestCreateOrUpdateGeneralSettings(object):
368 def test_create_when_no_repo_settings_found(self, repo_stub):
368 def test_create_when_no_repo_settings_found(self, repo_stub):
369 model = VcsSettingsModel(repo=repo_stub.repo_name)
369 model = VcsSettingsModel(repo=repo_stub.repo_name)
370 model._create_or_update_general_settings(
370 model._create_or_update_general_settings(
371 model.repo_settings, GENERAL_FORM_DATA)
371 model.repo_settings, GENERAL_FORM_DATA)
372
372
373 cleanup = []
373 cleanup = []
374 try:
374 try:
375 for name in model.GENERAL_SETTINGS:
375 for name in model.GENERAL_SETTINGS:
376 setting = model.repo_settings.get_setting_by_name(name)
376 setting = model.repo_settings.get_setting_by_name(name)
377 assert setting.app_settings_value is True
377 assert setting.app_settings_value is True
378 cleanup.append(setting)
378 cleanup.append(setting)
379 finally:
379 finally:
380 for setting in cleanup:
380 for setting in cleanup:
381 Session().delete(setting)
381 Session().delete(setting)
382 Session().commit()
382 Session().commit()
383
383
384 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
384 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
385 model = VcsSettingsModel(repo=repo_stub.repo_name)
385 model = VcsSettingsModel(repo=repo_stub.repo_name)
386
386
387 deleted_key = 'rhodecode_pr_merge_enabled'
387 deleted_key = 'rhodecode_pr_merge_enabled'
388 data = GENERAL_FORM_DATA.copy()
388 data = GENERAL_FORM_DATA.copy()
389 data.pop(deleted_key)
389 data.pop(deleted_key)
390
390
391 with pytest.raises(ValueError) as exc_info:
391 with pytest.raises(ValueError) as exc_info:
392 model._create_or_update_general_settings(model.repo_settings, data)
392 model._create_or_update_general_settings(model.repo_settings, data)
393 Session().commit()
393 Session().commit()
394
394
395 msg = 'The given data does not contain {} key'.format(deleted_key)
395 msg = 'The given data does not contain {} key'.format(deleted_key)
396 assert str(exc_info.value) == msg
396 assert str(exc_info.value) == msg
397
397
398 def test_update_when_repo_setting_found(self, repo_stub, settings_util):
398 def test_update_when_repo_setting_found(self, repo_stub, settings_util):
399 model = VcsSettingsModel(repo=repo_stub.repo_name)
399 model = VcsSettingsModel(repo=repo_stub.repo_name)
400 for name in model.GENERAL_SETTINGS:
400 for name in model.GENERAL_SETTINGS:
401 settings_util.create_repo_rhodecode_setting(
401 settings_util.create_repo_rhodecode_setting(
402 repo_stub, name, False, 'bool')
402 repo_stub, name, False, 'bool')
403
403
404 model._create_or_update_general_settings(
404 model._create_or_update_general_settings(
405 model.repo_settings, GENERAL_FORM_DATA)
405 model.repo_settings, GENERAL_FORM_DATA)
406 Session().commit()
406 Session().commit()
407
407
408 for name in model.GENERAL_SETTINGS:
408 for name in model.GENERAL_SETTINGS:
409 setting = model.repo_settings.get_setting_by_name(name)
409 setting = model.repo_settings.get_setting_by_name(name)
410 assert setting.app_settings_value is True
410 assert setting.app_settings_value is True
411
411
412
412
413 class TestCreateRepoSvnSettings(object):
413 class TestCreateRepoSvnSettings(object):
414 def test_calls_create_svn_settings(self, repo_stub):
414 def test_calls_create_svn_settings(self, repo_stub):
415 model = VcsSettingsModel(repo=repo_stub.repo_name)
415 model = VcsSettingsModel(repo=repo_stub.repo_name)
416 with mock.patch.object(model, '_create_svn_settings') as create_mock:
416 with mock.patch.object(model, '_create_svn_settings') as create_mock:
417 model.create_repo_svn_settings(SVN_FORM_DATA)
417 model.create_repo_svn_settings(SVN_FORM_DATA)
418 Session().commit()
418 Session().commit()
419
419
420 create_mock.assert_called_once_with(model.repo_settings, SVN_FORM_DATA)
420 create_mock.assert_called_once_with(model.repo_settings, SVN_FORM_DATA)
421
421
422 def test_raises_exception_when_repository_is_not_specified(self):
422 def test_raises_exception_when_repository_is_not_specified(self):
423 model = VcsSettingsModel()
423 model = VcsSettingsModel()
424 with pytest.raises(Exception) as exc_info:
424 with pytest.raises(Exception) as exc_info:
425 model.create_repo_svn_settings(SVN_FORM_DATA)
425 model.create_repo_svn_settings(SVN_FORM_DATA)
426 Session().commit()
426 Session().commit()
427
427
428 assert str(exc_info.value) == 'Repository is not specified'
428 assert str(exc_info.value) == 'Repository is not specified'
429
429
430
430
431 class TestCreateSvnSettings(object):
431 class TestCreateSvnSettings(object):
432 def test_create(self, repo_stub):
432 def test_create(self, repo_stub):
433 model = VcsSettingsModel(repo=repo_stub.repo_name)
433 model = VcsSettingsModel(repo=repo_stub.repo_name)
434 model._create_svn_settings(model.repo_settings, SVN_FORM_DATA)
434 model._create_svn_settings(model.repo_settings, SVN_FORM_DATA)
435 Session().commit()
435 Session().commit()
436
436
437 branch_ui = model.repo_settings.get_ui_by_section(
437 branch_ui = model.repo_settings.get_ui_by_section(
438 model.SVN_BRANCH_SECTION)
438 model.SVN_BRANCH_SECTION)
439 tag_ui = model.repo_settings.get_ui_by_section(
439 tag_ui = model.repo_settings.get_ui_by_section(
440 model.SVN_TAG_SECTION)
440 model.SVN_TAG_SECTION)
441
441
442 try:
442 try:
443 assert len(branch_ui) == 1
443 assert len(branch_ui) == 1
444 assert len(tag_ui) == 1
444 assert len(tag_ui) == 1
445 finally:
445 finally:
446 Session().delete(branch_ui[0])
446 Session().delete(branch_ui[0])
447 Session().delete(tag_ui[0])
447 Session().delete(tag_ui[0])
448 Session().commit()
448 Session().commit()
449
449
450 def test_create_tag(self, repo_stub):
450 def test_create_tag(self, repo_stub):
451 model = VcsSettingsModel(repo=repo_stub.repo_name)
451 model = VcsSettingsModel(repo=repo_stub.repo_name)
452 data = SVN_FORM_DATA.copy()
452 data = SVN_FORM_DATA.copy()
453 data.pop('new_svn_branch')
453 data.pop('new_svn_branch')
454 model._create_svn_settings(model.repo_settings, data)
454 model._create_svn_settings(model.repo_settings, data)
455 Session().commit()
455 Session().commit()
456
456
457 branch_ui = model.repo_settings.get_ui_by_section(
457 branch_ui = model.repo_settings.get_ui_by_section(
458 model.SVN_BRANCH_SECTION)
458 model.SVN_BRANCH_SECTION)
459 tag_ui = model.repo_settings.get_ui_by_section(
459 tag_ui = model.repo_settings.get_ui_by_section(
460 model.SVN_TAG_SECTION)
460 model.SVN_TAG_SECTION)
461
461
462 try:
462 try:
463 assert len(branch_ui) == 0
463 assert len(branch_ui) == 0
464 assert len(tag_ui) == 1
464 assert len(tag_ui) == 1
465 finally:
465 finally:
466 Session().delete(tag_ui[0])
466 Session().delete(tag_ui[0])
467 Session().commit()
467 Session().commit()
468
468
469 def test_create_nothing_when_no_svn_settings_specified(self, repo_stub):
469 def test_create_nothing_when_no_svn_settings_specified(self, repo_stub):
470 model = VcsSettingsModel(repo=repo_stub.repo_name)
470 model = VcsSettingsModel(repo=repo_stub.repo_name)
471 model._create_svn_settings(model.repo_settings, {})
471 model._create_svn_settings(model.repo_settings, {})
472 Session().commit()
472 Session().commit()
473
473
474 branch_ui = model.repo_settings.get_ui_by_section(
474 branch_ui = model.repo_settings.get_ui_by_section(
475 model.SVN_BRANCH_SECTION)
475 model.SVN_BRANCH_SECTION)
476 tag_ui = model.repo_settings.get_ui_by_section(
476 tag_ui = model.repo_settings.get_ui_by_section(
477 model.SVN_TAG_SECTION)
477 model.SVN_TAG_SECTION)
478
478
479 assert len(branch_ui) == 0
479 assert len(branch_ui) == 0
480 assert len(tag_ui) == 0
480 assert len(tag_ui) == 0
481
481
482 def test_create_nothing_when_empty_settings_specified(self, repo_stub):
482 def test_create_nothing_when_empty_settings_specified(self, repo_stub):
483 model = VcsSettingsModel(repo=repo_stub.repo_name)
483 model = VcsSettingsModel(repo=repo_stub.repo_name)
484 data = {
484 data = {
485 'new_svn_branch': '',
485 'new_svn_branch': '',
486 'new_svn_tag': ''
486 'new_svn_tag': ''
487 }
487 }
488 model._create_svn_settings(model.repo_settings, data)
488 model._create_svn_settings(model.repo_settings, data)
489 Session().commit()
489 Session().commit()
490
490
491 branch_ui = model.repo_settings.get_ui_by_section(
491 branch_ui = model.repo_settings.get_ui_by_section(
492 model.SVN_BRANCH_SECTION)
492 model.SVN_BRANCH_SECTION)
493 tag_ui = model.repo_settings.get_ui_by_section(
493 tag_ui = model.repo_settings.get_ui_by_section(
494 model.SVN_TAG_SECTION)
494 model.SVN_TAG_SECTION)
495
495
496 assert len(branch_ui) == 0
496 assert len(branch_ui) == 0
497 assert len(tag_ui) == 0
497 assert len(tag_ui) == 0
498
498
499
499
500 class TestCreateOrUpdateUi(object):
500 class TestCreateOrUpdateUi(object):
501 def test_create(self, repo_stub):
501 def test_create(self, repo_stub):
502 model = VcsSettingsModel(repo=repo_stub.repo_name)
502 model = VcsSettingsModel(repo=repo_stub.repo_name)
503 model._create_or_update_ui(
503 model._create_or_update_ui(
504 model.repo_settings, 'test-section', 'test-key', active=False,
504 model.repo_settings, 'test-section', 'test-key', active=False,
505 value='False')
505 value='False')
506 Session().commit()
506 Session().commit()
507
507
508 created_ui = model.repo_settings.get_ui_by_section_and_key(
508 created_ui = model.repo_settings.get_ui_by_section_and_key(
509 'test-section', 'test-key')
509 'test-section', 'test-key')
510
510
511 try:
511 try:
512 assert created_ui.ui_active is False
512 assert created_ui.ui_active is False
513 assert str2bool(created_ui.ui_value) is False
513 assert str2bool(created_ui.ui_value) is False
514 finally:
514 finally:
515 Session().delete(created_ui)
515 Session().delete(created_ui)
516 Session().commit()
516 Session().commit()
517
517
518 def test_update(self, repo_stub, settings_util):
518 def test_update(self, repo_stub, settings_util):
519 model = VcsSettingsModel(repo=repo_stub.repo_name)
519 model = VcsSettingsModel(repo=repo_stub.repo_name)
520 # care about only 3 first settings
520 # care about only 3 first settings
521 largefiles, phases, evolve = model.HG_SETTINGS[:3]
521 largefiles, phases, evolve = model.HG_SETTINGS[:3]
522
522
523 section = 'test-section'
523 section = 'test-section'
524 key = 'test-key'
524 key = 'test-key'
525 settings_util.create_repo_rhodecode_ui(
525 settings_util.create_repo_rhodecode_ui(
526 repo_stub, section, 'True', key=key, active=True)
526 repo_stub, section, 'True', key=key, active=True)
527
527
528 model._create_or_update_ui(
528 model._create_or_update_ui(
529 model.repo_settings, section, key, active=False, value='False')
529 model.repo_settings, section, key, active=False, value='False')
530 Session().commit()
530 Session().commit()
531
531
532 created_ui = model.repo_settings.get_ui_by_section_and_key(
532 created_ui = model.repo_settings.get_ui_by_section_and_key(
533 section, key)
533 section, key)
534 assert created_ui.ui_active is False
534 assert created_ui.ui_active is False
535 assert str2bool(created_ui.ui_value) is False
535 assert str2bool(created_ui.ui_value) is False
536
536
537
537
538 class TestCreateOrUpdateRepoHgSettings(object):
538 class TestCreateOrUpdateRepoHgSettings(object):
539 FORM_DATA = {
539 FORM_DATA = {
540 'extensions_largefiles': False,
540 'extensions_largefiles': False,
541 'extensions_evolve': False,
541 'extensions_evolve': False,
542 'phases_publish': False
542 'phases_publish': False
543 }
543 }
544
544
545 def test_creates_repo_hg_settings_when_data_is_correct(self, repo_stub):
545 def test_creates_repo_hg_settings_when_data_is_correct(self, repo_stub):
546 model = VcsSettingsModel(repo=repo_stub.repo_name)
546 model = VcsSettingsModel(repo=repo_stub.repo_name)
547 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
547 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
548 model.create_or_update_repo_hg_settings(self.FORM_DATA)
548 model.create_or_update_repo_hg_settings(self.FORM_DATA)
549 expected_calls = [
549 expected_calls = [
550 mock.call(model.repo_settings, 'extensions', 'largefiles', active=False, value=''),
550 mock.call(model.repo_settings, 'extensions', 'largefiles', active=False, value=''),
551 mock.call(model.repo_settings, 'extensions', 'evolve', active=False, value=''),
551 mock.call(model.repo_settings, 'extensions', 'evolve', active=False, value=''),
552 mock.call(model.repo_settings, 'experimental', 'evolution', active=False, value=''),
552 mock.call(model.repo_settings, 'experimental', 'evolution', active=False, value=''),
553 mock.call(model.repo_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
553 mock.call(model.repo_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
554 mock.call(model.repo_settings, 'extensions', 'topic', active=False, value=''),
554 mock.call(model.repo_settings, 'extensions', 'topic', active=False, value=''),
555 mock.call(model.repo_settings, 'phases', 'publish', value='False'),
555 mock.call(model.repo_settings, 'phases', 'publish', value='False'),
556 ]
556 ]
557 assert expected_calls == create_mock.call_args_list
557 assert expected_calls == create_mock.call_args_list
558
558
559 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
559 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
560 def test_key_is_not_found(self, repo_stub, field_to_remove):
560 def test_key_is_not_found(self, repo_stub, field_to_remove):
561 model = VcsSettingsModel(repo=repo_stub.repo_name)
561 model = VcsSettingsModel(repo=repo_stub.repo_name)
562 data = self.FORM_DATA.copy()
562 data = self.FORM_DATA.copy()
563 data.pop(field_to_remove)
563 data.pop(field_to_remove)
564 with pytest.raises(ValueError) as exc_info:
564 with pytest.raises(ValueError) as exc_info:
565 model.create_or_update_repo_hg_settings(data)
565 model.create_or_update_repo_hg_settings(data)
566 Session().commit()
566 Session().commit()
567
567
568 expected_message = 'The given data does not contain {} key'.format(
568 expected_message = 'The given data does not contain {} key'.format(
569 field_to_remove)
569 field_to_remove)
570 assert str(exc_info.value) == expected_message
570 assert str(exc_info.value) == expected_message
571
571
572 def test_create_raises_exception_when_repository_not_specified(self):
572 def test_create_raises_exception_when_repository_not_specified(self):
573 model = VcsSettingsModel()
573 model = VcsSettingsModel()
574 with pytest.raises(Exception) as exc_info:
574 with pytest.raises(Exception) as exc_info:
575 model.create_or_update_repo_hg_settings(self.FORM_DATA)
575 model.create_or_update_repo_hg_settings(self.FORM_DATA)
576 Session().commit()
576 Session().commit()
577
577
578 assert str(exc_info.value) == 'Repository is not specified'
578 assert str(exc_info.value) == 'Repository is not specified'
579
579
580
580
581 class TestUpdateGlobalSslSetting(object):
581 class TestUpdateGlobalSslSetting(object):
582 def test_updates_global_hg_settings(self):
582 def test_updates_global_hg_settings(self):
583 model = VcsSettingsModel()
583 model = VcsSettingsModel()
584 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
584 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
585 model.update_global_ssl_setting('False')
585 model.update_global_ssl_setting('False')
586 Session().commit()
586 Session().commit()
587
587
588 create_mock.assert_called_once_with(
588 create_mock.assert_called_once_with(
589 model.global_settings, 'web', 'push_ssl', value='False')
589 model.global_settings, 'web', 'push_ssl', value='False')
590
590
591
591
592 class TestCreateOrUpdateGlobalHgSettings(object):
592 class TestCreateOrUpdateGlobalHgSettings(object):
593 FORM_DATA = {
593 FORM_DATA = {
594 'extensions_largefiles': False,
594 'extensions_largefiles': False,
595 'largefiles_usercache': '/example/largefiles-store',
596 'phases_publish': False,
595 'phases_publish': False,
597 'extensions_evolve': False
596 'extensions_evolve': False
598 }
597 }
599
598
600 def test_creates_repo_hg_settings_when_data_is_correct(self):
599 def test_creates_repo_hg_settings_when_data_is_correct(self):
601 model = VcsSettingsModel()
600 model = VcsSettingsModel()
602 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
601 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
603 model.create_or_update_global_hg_settings(self.FORM_DATA)
602 model.create_or_update_global_hg_settings(self.FORM_DATA)
604 Session().commit()
603 Session().commit()
605
604
606 expected_calls = [
605 expected_calls = [
607 mock.call(model.global_settings, 'extensions', 'largefiles', active=False, value=''),
606 mock.call(model.global_settings, 'extensions', 'largefiles', active=False, value=''),
608 mock.call(model.global_settings, 'largefiles', 'usercache', value='/example/largefiles-store'),
609 mock.call(model.global_settings, 'phases', 'publish', value='False'),
607 mock.call(model.global_settings, 'phases', 'publish', value='False'),
610 mock.call(model.global_settings, 'extensions', 'evolve', active=False, value=''),
608 mock.call(model.global_settings, 'extensions', 'evolve', active=False, value=''),
611 mock.call(model.global_settings, 'experimental', 'evolution', active=False, value=''),
609 mock.call(model.global_settings, 'experimental', 'evolution', active=False, value=''),
612 mock.call(model.global_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
610 mock.call(model.global_settings, 'experimental', 'evolution.exchange', active=False, value='no'),
613 mock.call(model.global_settings, 'extensions', 'topic', active=False, value=''),
611 mock.call(model.global_settings, 'extensions', 'topic', active=False, value=''),
614 ]
612 ]
615
613
616 assert expected_calls == create_mock.call_args_list
614 assert expected_calls == create_mock.call_args_list
617
615
618 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
616 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
619 def test_key_is_not_found(self, repo_stub, field_to_remove):
617 def test_key_is_not_found(self, repo_stub, field_to_remove):
620 model = VcsSettingsModel(repo=repo_stub.repo_name)
618 model = VcsSettingsModel(repo=repo_stub.repo_name)
621 data = self.FORM_DATA.copy()
619 data = self.FORM_DATA.copy()
622 data.pop(field_to_remove)
620 data.pop(field_to_remove)
623 with pytest.raises(Exception) as exc_info:
621 with pytest.raises(Exception) as exc_info:
624 model.create_or_update_global_hg_settings(data)
622 model.create_or_update_global_hg_settings(data)
625 Session().commit()
623 Session().commit()
626
624
627 expected_message = 'The given data does not contain {} key'.format(
625 expected_message = 'The given data does not contain {} key'.format(
628 field_to_remove)
626 field_to_remove)
629 assert str(exc_info.value) == expected_message
627 assert str(exc_info.value) == expected_message
630
628
631
629
632 class TestCreateOrUpdateGlobalGitSettings(object):
630 class TestCreateOrUpdateGlobalGitSettings(object):
633 FORM_DATA = {
631 FORM_DATA = {
634 'vcs_git_lfs_enabled': False,
632 'vcs_git_lfs_enabled': False,
635 'vcs_git_lfs_store_location': '/example/lfs-store',
636 }
633 }
637
634
638 def test_creates_repo_hg_settings_when_data_is_correct(self):
635 def test_creates_repo_hg_settings_when_data_is_correct(self):
639 model = VcsSettingsModel()
636 model = VcsSettingsModel()
640 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
637 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
641 model.create_or_update_global_git_settings(self.FORM_DATA)
638 model.create_or_update_global_git_settings(self.FORM_DATA)
642 Session().commit()
639 Session().commit()
643
640
644 expected_calls = [
641 expected_calls = [
645 mock.call(model.global_settings, 'vcs_git_lfs', 'enabled', active=False, value=False),
642 mock.call(model.global_settings, 'vcs_git_lfs', 'enabled', active=False, value=False),
646 mock.call(model.global_settings, 'vcs_git_lfs', 'store_location', value='/example/lfs-store'),
647 ]
643 ]
648 assert expected_calls == create_mock.call_args_list
644 assert expected_calls == create_mock.call_args_list
649
645
650
646
651 class TestDeleteRepoSvnPattern(object):
647 class TestDeleteRepoSvnPattern(object):
652 def test_success_when_repo_is_set(self, backend_svn, settings_util):
648 def test_success_when_repo_is_set(self, backend_svn, settings_util):
653 repo = backend_svn.create_repo()
649 repo = backend_svn.create_repo()
654 repo_name = repo.repo_name
650 repo_name = repo.repo_name
655
651
656 model = VcsSettingsModel(repo=repo_name)
652 model = VcsSettingsModel(repo=repo_name)
657 entry = settings_util.create_repo_rhodecode_ui(
653 entry = settings_util.create_repo_rhodecode_ui(
658 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
654 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
659 Session().commit()
655 Session().commit()
660
656
661 model.delete_repo_svn_pattern(entry.ui_id)
657 model.delete_repo_svn_pattern(entry.ui_id)
662
658
663 def test_fail_when_delete_id_from_other_repo(self, backend_svn):
659 def test_fail_when_delete_id_from_other_repo(self, backend_svn):
664 repo_name = backend_svn.repo_name
660 repo_name = backend_svn.repo_name
665 model = VcsSettingsModel(repo=repo_name)
661 model = VcsSettingsModel(repo=repo_name)
666 delete_ui_patch = mock.patch.object(model.repo_settings, 'delete_ui')
662 delete_ui_patch = mock.patch.object(model.repo_settings, 'delete_ui')
667 with delete_ui_patch as delete_ui_mock:
663 with delete_ui_patch as delete_ui_mock:
668 model.delete_repo_svn_pattern(123)
664 model.delete_repo_svn_pattern(123)
669 Session().commit()
665 Session().commit()
670
666
671 delete_ui_mock.assert_called_once_with(-1)
667 delete_ui_mock.assert_called_once_with(-1)
672
668
673 def test_raises_exception_when_repository_is_not_specified(self):
669 def test_raises_exception_when_repository_is_not_specified(self):
674 model = VcsSettingsModel()
670 model = VcsSettingsModel()
675 with pytest.raises(Exception) as exc_info:
671 with pytest.raises(Exception) as exc_info:
676 model.delete_repo_svn_pattern(123)
672 model.delete_repo_svn_pattern(123)
677 assert str(exc_info.value) == 'Repository is not specified'
673 assert str(exc_info.value) == 'Repository is not specified'
678
674
679
675
680 class TestDeleteGlobalSvnPattern(object):
676 class TestDeleteGlobalSvnPattern(object):
681 def test_delete_global_svn_pattern_calls_delete_ui(self):
677 def test_delete_global_svn_pattern_calls_delete_ui(self):
682 model = VcsSettingsModel()
678 model = VcsSettingsModel()
683 delete_ui_patch = mock.patch.object(model.global_settings, 'delete_ui')
679 delete_ui_patch = mock.patch.object(model.global_settings, 'delete_ui')
684 with delete_ui_patch as delete_ui_mock:
680 with delete_ui_patch as delete_ui_mock:
685 model.delete_global_svn_pattern(123)
681 model.delete_global_svn_pattern(123)
686 delete_ui_mock.assert_called_once_with(123)
682 delete_ui_mock.assert_called_once_with(123)
687
683
688
684
689 class TestFilterUiSettings(object):
685 class TestFilterUiSettings(object):
690 def test_settings_are_filtered(self):
686 def test_settings_are_filtered(self):
691 model = VcsSettingsModel()
687 model = VcsSettingsModel()
692 repo_settings = [
688 repo_settings = [
693 UiSetting('extensions', 'largefiles', '', True),
689 UiSetting('extensions', 'largefiles', '', True),
694 UiSetting('phases', 'publish', 'True', True),
690 UiSetting('phases', 'publish', 'True', True),
695 UiSetting('hooks', 'changegroup.repo_size', 'hook', True),
691 UiSetting('hooks', 'changegroup.repo_size', 'hook', True),
696 UiSetting('hooks', 'changegroup.push_logger', 'hook', True),
692 UiSetting('hooks', 'changegroup.push_logger', 'hook', True),
697 UiSetting('hooks', 'outgoing.pull_logger', 'hook', True),
693 UiSetting('hooks', 'outgoing.pull_logger', 'hook', True),
698 UiSetting(
694 UiSetting(
699 'vcs_svn_branch', '84223c972204fa545ca1b22dac7bef5b68d7442d',
695 'vcs_svn_branch', '84223c972204fa545ca1b22dac7bef5b68d7442d',
700 'test_branch', True),
696 'test_branch', True),
701 UiSetting(
697 UiSetting(
702 'vcs_svn_tag', '84229c972204fa545ca1b22dac7bef5b68d7442d',
698 'vcs_svn_tag', '84229c972204fa545ca1b22dac7bef5b68d7442d',
703 'test_tag', True),
699 'test_tag', True),
704 ]
700 ]
705 non_repo_settings = [
701 non_repo_settings = [
706 UiSetting('largefiles', 'usercache', '/example/largefiles-store', True),
702 UiSetting('largefiles', 'usercache', '/example/largefiles-store', True),
707 UiSetting('test', 'outgoing.pull_logger', 'hook', True),
703 UiSetting('test', 'outgoing.pull_logger', 'hook', True),
708 UiSetting('hooks', 'test2', 'hook', True),
704 UiSetting('hooks', 'test2', 'hook', True),
709 UiSetting(
705 UiSetting(
710 'vcs_svn_repo', '84229c972204fa545ca1b22dac7bef5b68d7442d',
706 'vcs_svn_repo', '84229c972204fa545ca1b22dac7bef5b68d7442d',
711 'test_tag', True),
707 'test_tag', True),
712 ]
708 ]
713 settings = repo_settings + non_repo_settings
709 settings = repo_settings + non_repo_settings
714 filtered_settings = model._filter_ui_settings(settings)
710 filtered_settings = model._filter_ui_settings(settings)
715 assert sorted(filtered_settings) == sorted(repo_settings)
711 assert sorted(filtered_settings) == sorted(repo_settings)
716
712
717
713
718 class TestFilterGeneralSettings(object):
714 class TestFilterGeneralSettings(object):
719 def test_settings_are_filtered(self):
715 def test_settings_are_filtered(self):
720 model = VcsSettingsModel()
716 model = VcsSettingsModel()
721 settings = {
717 settings = {
722 'rhodecode_abcde': 'value1',
718 'rhodecode_abcde': 'value1',
723 'rhodecode_vwxyz': 'value2',
719 'rhodecode_vwxyz': 'value2',
724 }
720 }
725 general_settings = {
721 general_settings = {
726 'rhodecode_{}'.format(key): 'value'
722 'rhodecode_{}'.format(key): 'value'
727 for key in VcsSettingsModel.GENERAL_SETTINGS
723 for key in VcsSettingsModel.GENERAL_SETTINGS
728 }
724 }
729 settings.update(general_settings)
725 settings.update(general_settings)
730
726
731 filtered_settings = model._filter_general_settings(general_settings)
727 filtered_settings = model._filter_general_settings(general_settings)
732 assert sorted(filtered_settings) == sorted(general_settings)
728 assert sorted(filtered_settings) == sorted(general_settings)
733
729
734
730
735 class TestGetRepoUiSettings(object):
731 class TestGetRepoUiSettings(object):
736 def test_global_uis_are_returned_when_no_repo_uis_found(
732 def test_global_uis_are_returned_when_no_repo_uis_found(
737 self, repo_stub):
733 self, repo_stub):
738 model = VcsSettingsModel(repo=repo_stub.repo_name)
734 model = VcsSettingsModel(repo=repo_stub.repo_name)
739 result = model.get_repo_ui_settings()
735 result = model.get_repo_ui_settings()
740 svn_sections = (
736 svn_sections = (
741 VcsSettingsModel.SVN_TAG_SECTION,
737 VcsSettingsModel.SVN_TAG_SECTION,
742 VcsSettingsModel.SVN_BRANCH_SECTION)
738 VcsSettingsModel.SVN_BRANCH_SECTION)
743 expected_result = [
739 expected_result = [
744 s for s in model.global_settings.get_ui()
740 s for s in model.global_settings.get_ui()
745 if s.section not in svn_sections]
741 if s.section not in svn_sections]
746 assert sorted(result) == sorted(expected_result)
742 assert sorted(result) == sorted(expected_result)
747
743
748 def test_repo_uis_are_overriding_global_uis(
744 def test_repo_uis_are_overriding_global_uis(
749 self, repo_stub, settings_util):
745 self, repo_stub, settings_util):
750 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
746 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
751 settings_util.create_repo_rhodecode_ui(
747 settings_util.create_repo_rhodecode_ui(
752 repo_stub, section, 'repo', key=key, active=False)
748 repo_stub, section, 'repo', key=key, active=False)
753 model = VcsSettingsModel(repo=repo_stub.repo_name)
749 model = VcsSettingsModel(repo=repo_stub.repo_name)
754 result = model.get_repo_ui_settings()
750 result = model.get_repo_ui_settings()
755 for setting in result:
751 for setting in result:
756 locator = (setting.section, setting.key)
752 locator = (setting.section, setting.key)
757 if locator in VcsSettingsModel.HOOKS_SETTINGS:
753 if locator in VcsSettingsModel.HOOKS_SETTINGS:
758 assert setting.value == 'repo'
754 assert setting.value == 'repo'
759
755
760 assert setting.active is False
756 assert setting.active is False
761
757
762 def test_global_svn_patterns_are_not_in_list(
758 def test_global_svn_patterns_are_not_in_list(
763 self, repo_stub, settings_util):
759 self, repo_stub, settings_util):
764 svn_sections = (
760 svn_sections = (
765 VcsSettingsModel.SVN_TAG_SECTION,
761 VcsSettingsModel.SVN_TAG_SECTION,
766 VcsSettingsModel.SVN_BRANCH_SECTION)
762 VcsSettingsModel.SVN_BRANCH_SECTION)
767 for section in svn_sections:
763 for section in svn_sections:
768 settings_util.create_rhodecode_ui(
764 settings_util.create_rhodecode_ui(
769 section, 'repo', key='deadbeef' + section, active=False)
765 section, 'repo', key='deadbeef' + section, active=False)
770 Session().commit()
766 Session().commit()
771
767
772 model = VcsSettingsModel(repo=repo_stub.repo_name)
768 model = VcsSettingsModel(repo=repo_stub.repo_name)
773 result = model.get_repo_ui_settings()
769 result = model.get_repo_ui_settings()
774 for setting in result:
770 for setting in result:
775 assert setting.section not in svn_sections
771 assert setting.section not in svn_sections
776
772
777 def test_repo_uis_filtered_by_section_are_returned(
773 def test_repo_uis_filtered_by_section_are_returned(
778 self, repo_stub, settings_util):
774 self, repo_stub, settings_util):
779 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
775 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
780 settings_util.create_repo_rhodecode_ui(
776 settings_util.create_repo_rhodecode_ui(
781 repo_stub, section, 'repo', key=key, active=False)
777 repo_stub, section, 'repo', key=key, active=False)
782 model = VcsSettingsModel(repo=repo_stub.repo_name)
778 model = VcsSettingsModel(repo=repo_stub.repo_name)
783 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
779 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
784 result = model.get_repo_ui_settings(section=section)
780 result = model.get_repo_ui_settings(section=section)
785 for setting in result:
781 for setting in result:
786 assert setting.section == section
782 assert setting.section == section
787
783
788 def test_repo_uis_filtered_by_key_are_returned(
784 def test_repo_uis_filtered_by_key_are_returned(
789 self, repo_stub, settings_util):
785 self, repo_stub, settings_util):
790 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
786 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
791 settings_util.create_repo_rhodecode_ui(
787 settings_util.create_repo_rhodecode_ui(
792 repo_stub, section, 'repo', key=key, active=False)
788 repo_stub, section, 'repo', key=key, active=False)
793 model = VcsSettingsModel(repo=repo_stub.repo_name)
789 model = VcsSettingsModel(repo=repo_stub.repo_name)
794 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
790 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
795 result = model.get_repo_ui_settings(key=key)
791 result = model.get_repo_ui_settings(key=key)
796 for setting in result:
792 for setting in result:
797 assert setting.key == key
793 assert setting.key == key
798
794
799 def test_raises_exception_when_repository_is_not_specified(self):
795 def test_raises_exception_when_repository_is_not_specified(self):
800 model = VcsSettingsModel()
796 model = VcsSettingsModel()
801 with pytest.raises(Exception) as exc_info:
797 with pytest.raises(Exception) as exc_info:
802 model.get_repo_ui_settings()
798 model.get_repo_ui_settings()
803 assert str(exc_info.value) == 'Repository is not specified'
799 assert str(exc_info.value) == 'Repository is not specified'
804
800
805
801
806 class TestGetRepoGeneralSettings(object):
802 class TestGetRepoGeneralSettings(object):
807 def test_global_settings_are_returned_when_no_repo_settings_found(
803 def test_global_settings_are_returned_when_no_repo_settings_found(
808 self, repo_stub):
804 self, repo_stub):
809 model = VcsSettingsModel(repo=repo_stub.repo_name)
805 model = VcsSettingsModel(repo=repo_stub.repo_name)
810 result = model.get_repo_general_settings()
806 result = model.get_repo_general_settings()
811 expected_result = model.global_settings.get_all_settings()
807 expected_result = model.global_settings.get_all_settings()
812 assert sorted(result) == sorted(expected_result)
808 assert sorted(result) == sorted(expected_result)
813
809
814 def test_repo_uis_are_overriding_global_uis(
810 def test_repo_uis_are_overriding_global_uis(
815 self, repo_stub, settings_util):
811 self, repo_stub, settings_util):
816 for key in VcsSettingsModel.GENERAL_SETTINGS:
812 for key in VcsSettingsModel.GENERAL_SETTINGS:
817 settings_util.create_repo_rhodecode_setting(
813 settings_util.create_repo_rhodecode_setting(
818 repo_stub, key, 'abcde', type_='unicode')
814 repo_stub, key, 'abcde', type_='unicode')
819 Session().commit()
815 Session().commit()
820
816
821 model = VcsSettingsModel(repo=repo_stub.repo_name)
817 model = VcsSettingsModel(repo=repo_stub.repo_name)
822 result = model.get_repo_ui_settings()
818 result = model.get_repo_ui_settings()
823 for key in result:
819 for key in result:
824 if key in VcsSettingsModel.GENERAL_SETTINGS:
820 if key in VcsSettingsModel.GENERAL_SETTINGS:
825 assert result[key] == 'abcde'
821 assert result[key] == 'abcde'
826
822
827 def test_raises_exception_when_repository_is_not_specified(self):
823 def test_raises_exception_when_repository_is_not_specified(self):
828 model = VcsSettingsModel()
824 model = VcsSettingsModel()
829 with pytest.raises(Exception) as exc_info:
825 with pytest.raises(Exception) as exc_info:
830 model.get_repo_general_settings()
826 model.get_repo_general_settings()
831 assert str(exc_info.value) == 'Repository is not specified'
827 assert str(exc_info.value) == 'Repository is not specified'
832
828
833
829
834 class TestGetGlobalGeneralSettings(object):
830 class TestGetGlobalGeneralSettings(object):
835 def test_global_settings_are_returned(self, repo_stub):
831 def test_global_settings_are_returned(self, repo_stub):
836 model = VcsSettingsModel()
832 model = VcsSettingsModel()
837 result = model.get_global_general_settings()
833 result = model.get_global_general_settings()
838 expected_result = model.global_settings.get_all_settings()
834 expected_result = model.global_settings.get_all_settings()
839 assert sorted(result) == sorted(expected_result)
835 assert sorted(result) == sorted(expected_result)
840
836
841 def test_repo_uis_are_not_overriding_global_uis(
837 def test_repo_uis_are_not_overriding_global_uis(
842 self, repo_stub, settings_util):
838 self, repo_stub, settings_util):
843 for key in VcsSettingsModel.GENERAL_SETTINGS:
839 for key in VcsSettingsModel.GENERAL_SETTINGS:
844 settings_util.create_repo_rhodecode_setting(
840 settings_util.create_repo_rhodecode_setting(
845 repo_stub, key, 'abcde', type_='unicode')
841 repo_stub, key, 'abcde', type_='unicode')
846 Session().commit()
842 Session().commit()
847
843
848 model = VcsSettingsModel(repo=repo_stub.repo_name)
844 model = VcsSettingsModel(repo=repo_stub.repo_name)
849 result = model.get_global_general_settings()
845 result = model.get_global_general_settings()
850 expected_result = model.global_settings.get_all_settings()
846 expected_result = model.global_settings.get_all_settings()
851 assert sorted(result) == sorted(expected_result)
847 assert sorted(result) == sorted(expected_result)
852
848
853
849
854 class TestGetGlobalUiSettings(object):
850 class TestGetGlobalUiSettings(object):
855 def test_global_uis_are_returned(self, repo_stub):
851 def test_global_uis_are_returned(self, repo_stub):
856 model = VcsSettingsModel()
852 model = VcsSettingsModel()
857 result = model.get_global_ui_settings()
853 result = model.get_global_ui_settings()
858 expected_result = model.global_settings.get_ui()
854 expected_result = model.global_settings.get_ui()
859 assert sorted(result) == sorted(expected_result)
855 assert sorted(result) == sorted(expected_result)
860
856
861 def test_repo_uis_are_not_overriding_global_uis(
857 def test_repo_uis_are_not_overriding_global_uis(
862 self, repo_stub, settings_util):
858 self, repo_stub, settings_util):
863 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
859 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
864 settings_util.create_repo_rhodecode_ui(
860 settings_util.create_repo_rhodecode_ui(
865 repo_stub, section, 'repo', key=key, active=False)
861 repo_stub, section, 'repo', key=key, active=False)
866 Session().commit()
862 Session().commit()
867
863
868 model = VcsSettingsModel(repo=repo_stub.repo_name)
864 model = VcsSettingsModel(repo=repo_stub.repo_name)
869 result = model.get_global_ui_settings()
865 result = model.get_global_ui_settings()
870 expected_result = model.global_settings.get_ui()
866 expected_result = model.global_settings.get_ui()
871 assert sorted(result) == sorted(expected_result)
867 assert sorted(result) == sorted(expected_result)
872
868
873 def test_ui_settings_filtered_by_section(
869 def test_ui_settings_filtered_by_section(
874 self, repo_stub, settings_util):
870 self, repo_stub, settings_util):
875 model = VcsSettingsModel(repo=repo_stub.repo_name)
871 model = VcsSettingsModel(repo=repo_stub.repo_name)
876 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
872 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
877 result = model.get_global_ui_settings(section=section)
873 result = model.get_global_ui_settings(section=section)
878 expected_result = model.global_settings.get_ui(section=section)
874 expected_result = model.global_settings.get_ui(section=section)
879 assert sorted(result) == sorted(expected_result)
875 assert sorted(result) == sorted(expected_result)
880
876
881 def test_ui_settings_filtered_by_key(
877 def test_ui_settings_filtered_by_key(
882 self, repo_stub, settings_util):
878 self, repo_stub, settings_util):
883 model = VcsSettingsModel(repo=repo_stub.repo_name)
879 model = VcsSettingsModel(repo=repo_stub.repo_name)
884 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
880 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
885 result = model.get_global_ui_settings(key=key)
881 result = model.get_global_ui_settings(key=key)
886 expected_result = model.global_settings.get_ui(key=key)
882 expected_result = model.global_settings.get_ui(key=key)
887 assert sorted(result) == sorted(expected_result)
883 assert sorted(result) == sorted(expected_result)
888
884
889
885
890 class TestGetGeneralSettings(object):
886 class TestGetGeneralSettings(object):
891 def test_global_settings_are_returned_when_inherited_is_true(
887 def test_global_settings_are_returned_when_inherited_is_true(
892 self, repo_stub, settings_util):
888 self, repo_stub, settings_util):
893 model = VcsSettingsModel(repo=repo_stub.repo_name)
889 model = VcsSettingsModel(repo=repo_stub.repo_name)
894 model.inherit_global_settings = True
890 model.inherit_global_settings = True
895 for key in VcsSettingsModel.GENERAL_SETTINGS:
891 for key in VcsSettingsModel.GENERAL_SETTINGS:
896 settings_util.create_repo_rhodecode_setting(
892 settings_util.create_repo_rhodecode_setting(
897 repo_stub, key, 'abcde', type_='unicode')
893 repo_stub, key, 'abcde', type_='unicode')
898 Session().commit()
894 Session().commit()
899
895
900 result = model.get_general_settings()
896 result = model.get_general_settings()
901 expected_result = model.get_global_general_settings()
897 expected_result = model.get_global_general_settings()
902 assert sorted(result) == sorted(expected_result)
898 assert sorted(result) == sorted(expected_result)
903
899
904 def test_repo_settings_are_returned_when_inherited_is_false(
900 def test_repo_settings_are_returned_when_inherited_is_false(
905 self, repo_stub, settings_util):
901 self, repo_stub, settings_util):
906 model = VcsSettingsModel(repo=repo_stub.repo_name)
902 model = VcsSettingsModel(repo=repo_stub.repo_name)
907 model.inherit_global_settings = False
903 model.inherit_global_settings = False
908 for key in VcsSettingsModel.GENERAL_SETTINGS:
904 for key in VcsSettingsModel.GENERAL_SETTINGS:
909 settings_util.create_repo_rhodecode_setting(
905 settings_util.create_repo_rhodecode_setting(
910 repo_stub, key, 'abcde', type_='unicode')
906 repo_stub, key, 'abcde', type_='unicode')
911 Session().commit()
907 Session().commit()
912
908
913 result = model.get_general_settings()
909 result = model.get_general_settings()
914 expected_result = model.get_repo_general_settings()
910 expected_result = model.get_repo_general_settings()
915 assert sorted(result) == sorted(expected_result)
911 assert sorted(result) == sorted(expected_result)
916
912
917 def test_global_settings_are_returned_when_no_repository_specified(self):
913 def test_global_settings_are_returned_when_no_repository_specified(self):
918 model = VcsSettingsModel()
914 model = VcsSettingsModel()
919 result = model.get_general_settings()
915 result = model.get_general_settings()
920 expected_result = model.get_global_general_settings()
916 expected_result = model.get_global_general_settings()
921 assert sorted(result) == sorted(expected_result)
917 assert sorted(result) == sorted(expected_result)
922
918
923
919
924 class TestGetUiSettings(object):
920 class TestGetUiSettings(object):
925 def test_global_settings_are_returned_when_inherited_is_true(
921 def test_global_settings_are_returned_when_inherited_is_true(
926 self, repo_stub, settings_util):
922 self, repo_stub, settings_util):
927 model = VcsSettingsModel(repo=repo_stub.repo_name)
923 model = VcsSettingsModel(repo=repo_stub.repo_name)
928 model.inherit_global_settings = True
924 model.inherit_global_settings = True
929 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
925 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
930 settings_util.create_repo_rhodecode_ui(
926 settings_util.create_repo_rhodecode_ui(
931 repo_stub, section, 'repo', key=key, active=True)
927 repo_stub, section, 'repo', key=key, active=True)
932 Session().commit()
928 Session().commit()
933
929
934 result = model.get_ui_settings()
930 result = model.get_ui_settings()
935 expected_result = model.get_global_ui_settings()
931 expected_result = model.get_global_ui_settings()
936 assert sorted(result) == sorted(expected_result)
932 assert sorted(result) == sorted(expected_result)
937
933
938 def test_repo_settings_are_returned_when_inherited_is_false(
934 def test_repo_settings_are_returned_when_inherited_is_false(
939 self, repo_stub, settings_util):
935 self, repo_stub, settings_util):
940 model = VcsSettingsModel(repo=repo_stub.repo_name)
936 model = VcsSettingsModel(repo=repo_stub.repo_name)
941 model.inherit_global_settings = False
937 model.inherit_global_settings = False
942 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
938 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
943 settings_util.create_repo_rhodecode_ui(
939 settings_util.create_repo_rhodecode_ui(
944 repo_stub, section, 'repo', key=key, active=True)
940 repo_stub, section, 'repo', key=key, active=True)
945 Session().commit()
941 Session().commit()
946
942
947 result = model.get_ui_settings()
943 result = model.get_ui_settings()
948 expected_result = model.get_repo_ui_settings()
944 expected_result = model.get_repo_ui_settings()
949 assert sorted(result) == sorted(expected_result)
945 assert sorted(result) == sorted(expected_result)
950
946
951 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
947 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
952 model = VcsSettingsModel(repo=repo_stub.repo_name)
948 model = VcsSettingsModel(repo=repo_stub.repo_name)
953 model.inherit_global_settings = False
949 model.inherit_global_settings = False
954
950
955 args = ('section', 'key')
951 args = ('section', 'key')
956 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
952 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
957 model.get_ui_settings(*args)
953 model.get_ui_settings(*args)
958 Session().commit()
954 Session().commit()
959
955
960 settings_mock.assert_called_once_with(*args)
956 settings_mock.assert_called_once_with(*args)
961
957
962 def test_global_settings_filtered_by_section_and_key(self):
958 def test_global_settings_filtered_by_section_and_key(self):
963 model = VcsSettingsModel()
959 model = VcsSettingsModel()
964 args = ('section', 'key')
960 args = ('section', 'key')
965 with mock.patch.object(model, 'get_global_ui_settings') as (
961 with mock.patch.object(model, 'get_global_ui_settings') as (
966 settings_mock):
962 settings_mock):
967 model.get_ui_settings(*args)
963 model.get_ui_settings(*args)
968 settings_mock.assert_called_once_with(*args)
964 settings_mock.assert_called_once_with(*args)
969
965
970 def test_global_settings_are_returned_when_no_repository_specified(self):
966 def test_global_settings_are_returned_when_no_repository_specified(self):
971 model = VcsSettingsModel()
967 model = VcsSettingsModel()
972 result = model.get_ui_settings()
968 result = model.get_ui_settings()
973 expected_result = model.get_global_ui_settings()
969 expected_result = model.get_global_ui_settings()
974 assert sorted(result) == sorted(expected_result)
970 assert sorted(result) == sorted(expected_result)
975
971
976
972
977 class TestGetSvnPatterns(object):
973 class TestGetSvnPatterns(object):
978 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
974 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
979 model = VcsSettingsModel(repo=repo_stub.repo_name)
975 model = VcsSettingsModel(repo=repo_stub.repo_name)
980 args = ('section', )
976 args = ('section', )
981 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
977 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
982 model.get_svn_patterns(*args)
978 model.get_svn_patterns(*args)
983
979
984 Session().commit()
980 Session().commit()
985 settings_mock.assert_called_once_with(*args)
981 settings_mock.assert_called_once_with(*args)
986
982
987 def test_global_settings_filtered_by_section_and_key(self):
983 def test_global_settings_filtered_by_section_and_key(self):
988 model = VcsSettingsModel()
984 model = VcsSettingsModel()
989 args = ('section', )
985 args = ('section', )
990 with mock.patch.object(model, 'get_global_ui_settings') as (
986 with mock.patch.object(model, 'get_global_ui_settings') as (
991 settings_mock):
987 settings_mock):
992 model.get_svn_patterns(*args)
988 model.get_svn_patterns(*args)
993 settings_mock.assert_called_once_with(*args)
989 settings_mock.assert_called_once_with(*args)
994
990
995
991
996 class TestCreateOrUpdateRepoSettings(object):
992 class TestCreateOrUpdateRepoSettings(object):
997 FORM_DATA = {
993 FORM_DATA = {
998 'inherit_global_settings': False,
994 'inherit_global_settings': False,
999 'hooks_changegroup_repo_size': False,
995 'hooks_changegroup_repo_size': False,
1000 'hooks_changegroup_push_logger': False,
996 'hooks_changegroup_push_logger': False,
1001 'hooks_outgoing_pull_logger': False,
997 'hooks_outgoing_pull_logger': False,
1002 'extensions_largefiles': False,
998 'extensions_largefiles': False,
1003 'extensions_evolve': False,
999 'extensions_evolve': False,
1004 'largefiles_usercache': '/example/largefiles-store',
1005 'vcs_git_lfs_enabled': False,
1000 'vcs_git_lfs_enabled': False,
1006 'vcs_git_lfs_store_location': '/',
1007 'phases_publish': 'False',
1001 'phases_publish': 'False',
1008 'rhodecode_pr_merge_enabled': False,
1002 'rhodecode_pr_merge_enabled': False,
1009 'rhodecode_use_outdated_comments': False,
1003 'rhodecode_use_outdated_comments': False,
1010 'new_svn_branch': '',
1004 'new_svn_branch': '',
1011 'new_svn_tag': ''
1005 'new_svn_tag': ''
1012 }
1006 }
1013
1007
1014 def test_get_raises_exception_when_repository_not_specified(self):
1008 def test_get_raises_exception_when_repository_not_specified(self):
1015 model = VcsSettingsModel()
1009 model = VcsSettingsModel()
1016 with pytest.raises(Exception) as exc_info:
1010 with pytest.raises(Exception) as exc_info:
1017 model.create_or_update_repo_settings(data=self.FORM_DATA)
1011 model.create_or_update_repo_settings(data=self.FORM_DATA)
1018 Session().commit()
1012 Session().commit()
1019
1013
1020 assert str(exc_info.value) == 'Repository is not specified'
1014 assert str(exc_info.value) == 'Repository is not specified'
1021
1015
1022 def test_only_svn_settings_are_updated_when_type_is_svn(self, backend_svn):
1016 def test_only_svn_settings_are_updated_when_type_is_svn(self, backend_svn):
1023 repo = backend_svn.create_repo()
1017 repo = backend_svn.create_repo()
1024 model = VcsSettingsModel(repo=repo)
1018 model = VcsSettingsModel(repo=repo)
1025 with self._patch_model(model) as mocks:
1019 with self._patch_model(model) as mocks:
1026 model.create_or_update_repo_settings(
1020 model.create_or_update_repo_settings(
1027 data=self.FORM_DATA, inherit_global_settings=False)
1021 data=self.FORM_DATA, inherit_global_settings=False)
1028 Session().commit()
1022 Session().commit()
1029
1023
1030 mocks['create_repo_svn_settings'].assert_called_once_with(
1024 mocks['create_repo_svn_settings'].assert_called_once_with(
1031 self.FORM_DATA)
1025 self.FORM_DATA)
1032 non_called_methods = (
1026 non_called_methods = (
1033 'create_or_update_repo_hook_settings',
1027 'create_or_update_repo_hook_settings',
1034 'create_or_update_repo_pr_settings',
1028 'create_or_update_repo_pr_settings',
1035 'create_or_update_repo_hg_settings')
1029 'create_or_update_repo_hg_settings')
1036 for method in non_called_methods:
1030 for method in non_called_methods:
1037 assert mocks[method].call_count == 0
1031 assert mocks[method].call_count == 0
1038
1032
1039 def test_non_svn_settings_are_updated_when_type_is_hg(self, backend_hg):
1033 def test_non_svn_settings_are_updated_when_type_is_hg(self, backend_hg):
1040 repo = backend_hg.create_repo()
1034 repo = backend_hg.create_repo()
1041 model = VcsSettingsModel(repo=repo)
1035 model = VcsSettingsModel(repo=repo)
1042 with self._patch_model(model) as mocks:
1036 with self._patch_model(model) as mocks:
1043 model.create_or_update_repo_settings(
1037 model.create_or_update_repo_settings(
1044 data=self.FORM_DATA, inherit_global_settings=False)
1038 data=self.FORM_DATA, inherit_global_settings=False)
1045 Session().commit()
1039 Session().commit()
1046
1040
1047 assert mocks['create_repo_svn_settings'].call_count == 0
1041 assert mocks['create_repo_svn_settings'].call_count == 0
1048 called_methods = (
1042 called_methods = (
1049 'create_or_update_repo_hook_settings',
1043 'create_or_update_repo_hook_settings',
1050 'create_or_update_repo_pr_settings',
1044 'create_or_update_repo_pr_settings',
1051 'create_or_update_repo_hg_settings')
1045 'create_or_update_repo_hg_settings')
1052 for method in called_methods:
1046 for method in called_methods:
1053 mocks[method].assert_called_once_with(self.FORM_DATA)
1047 mocks[method].assert_called_once_with(self.FORM_DATA)
1054
1048
1055 def test_non_svn_and_hg_settings_are_updated_when_type_is_git(
1049 def test_non_svn_and_hg_settings_are_updated_when_type_is_git(
1056 self, backend_git):
1050 self, backend_git):
1057 repo = backend_git.create_repo()
1051 repo = backend_git.create_repo()
1058 model = VcsSettingsModel(repo=repo)
1052 model = VcsSettingsModel(repo=repo)
1059 with self._patch_model(model) as mocks:
1053 with self._patch_model(model) as mocks:
1060 model.create_or_update_repo_settings(
1054 model.create_or_update_repo_settings(
1061 data=self.FORM_DATA, inherit_global_settings=False)
1055 data=self.FORM_DATA, inherit_global_settings=False)
1062
1056
1063 assert mocks['create_repo_svn_settings'].call_count == 0
1057 assert mocks['create_repo_svn_settings'].call_count == 0
1064 called_methods = (
1058 called_methods = (
1065 'create_or_update_repo_hook_settings',
1059 'create_or_update_repo_hook_settings',
1066 'create_or_update_repo_pr_settings')
1060 'create_or_update_repo_pr_settings')
1067 non_called_methods = (
1061 non_called_methods = (
1068 'create_repo_svn_settings',
1062 'create_repo_svn_settings',
1069 'create_or_update_repo_hg_settings'
1063 'create_or_update_repo_hg_settings'
1070 )
1064 )
1071 for method in called_methods:
1065 for method in called_methods:
1072 mocks[method].assert_called_once_with(self.FORM_DATA)
1066 mocks[method].assert_called_once_with(self.FORM_DATA)
1073 for method in non_called_methods:
1067 for method in non_called_methods:
1074 assert mocks[method].call_count == 0
1068 assert mocks[method].call_count == 0
1075
1069
1076 def test_no_methods_are_called_when_settings_are_inherited(
1070 def test_no_methods_are_called_when_settings_are_inherited(
1077 self, backend):
1071 self, backend):
1078 repo = backend.create_repo()
1072 repo = backend.create_repo()
1079 model = VcsSettingsModel(repo=repo)
1073 model = VcsSettingsModel(repo=repo)
1080 with self._patch_model(model) as mocks:
1074 with self._patch_model(model) as mocks:
1081 model.create_or_update_repo_settings(
1075 model.create_or_update_repo_settings(
1082 data=self.FORM_DATA, inherit_global_settings=True)
1076 data=self.FORM_DATA, inherit_global_settings=True)
1083 for method_name in mocks:
1077 for method_name in mocks:
1084 assert mocks[method_name].call_count == 0
1078 assert mocks[method_name].call_count == 0
1085
1079
1086 def test_cache_is_marked_for_invalidation(self, repo_stub):
1080 def test_cache_is_marked_for_invalidation(self, repo_stub):
1087 model = VcsSettingsModel(repo=repo_stub)
1081 model = VcsSettingsModel(repo=repo_stub)
1088 invalidation_patcher = mock.patch(
1082 invalidation_patcher = mock.patch(
1089 'rhodecode.model.scm.ScmModel.mark_for_invalidation')
1083 'rhodecode.model.scm.ScmModel.mark_for_invalidation')
1090 with invalidation_patcher as invalidation_mock:
1084 with invalidation_patcher as invalidation_mock:
1091 model.create_or_update_repo_settings(
1085 model.create_or_update_repo_settings(
1092 data=self.FORM_DATA, inherit_global_settings=True)
1086 data=self.FORM_DATA, inherit_global_settings=True)
1093 Session().commit()
1087 Session().commit()
1094
1088
1095 invalidation_mock.assert_called_once_with(
1089 invalidation_mock.assert_called_once_with(
1096 repo_stub.repo_name, delete=True)
1090 repo_stub.repo_name, delete=True)
1097
1091
1098 def test_inherit_flag_is_saved(self, repo_stub):
1092 def test_inherit_flag_is_saved(self, repo_stub):
1099 model = VcsSettingsModel(repo=repo_stub)
1093 model = VcsSettingsModel(repo=repo_stub)
1100 model.inherit_global_settings = True
1094 model.inherit_global_settings = True
1101 with self._patch_model(model):
1095 with self._patch_model(model):
1102 model.create_or_update_repo_settings(
1096 model.create_or_update_repo_settings(
1103 data=self.FORM_DATA, inherit_global_settings=False)
1097 data=self.FORM_DATA, inherit_global_settings=False)
1104 Session().commit()
1098 Session().commit()
1105
1099
1106 assert model.inherit_global_settings is False
1100 assert model.inherit_global_settings is False
1107
1101
1108 def _patch_model(self, model):
1102 def _patch_model(self, model):
1109 return mock.patch.multiple(
1103 return mock.patch.multiple(
1110 model,
1104 model,
1111 create_repo_svn_settings=mock.DEFAULT,
1105 create_repo_svn_settings=mock.DEFAULT,
1112 create_or_update_repo_hook_settings=mock.DEFAULT,
1106 create_or_update_repo_hook_settings=mock.DEFAULT,
1113 create_or_update_repo_pr_settings=mock.DEFAULT,
1107 create_or_update_repo_pr_settings=mock.DEFAULT,
1114 create_or_update_repo_hg_settings=mock.DEFAULT)
1108 create_or_update_repo_hg_settings=mock.DEFAULT)
General Comments 0
You need to be logged in to leave comments. Login now