##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r1610:1f5086cf merge stable
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,14 b''
1 .. _git-lfs-loc:
2
3 Change the |git| LFS storage Location
4 -------------------------------------
5
6 |RCE| manages |git| LFS files from the following default location
7 :file:`/home/{user}/repos/.cache/lfs_store`. If you wish to change this, use
8 the following steps:
9
10 1. Open :menuselection:`Admin --> Settings --> VCS` as super-admin.
11
12 In section called `Git Settings` you can change where the LFS
13 objects should be stored.
14
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,162 b''
1 |RCE| 4.7.0 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2017-04-08
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - Git: added support for Git LFS v2 protocol. RhodeCode now supports both
14 Mercurial Largefiles, and Git LFS for storing large binaries.
15 - Largefiles: detect Git LFS or Mercurial Largefiles objects in UI.
16 Those are now available for downloading together with showing their size.
17 - Files: Jupyter notebooks will be now rendered inside the file view. Including
18 MatJax support, and relative images.
19 - Files: render images inside the file view.
20 Instead of displaying binary message, render images icons and gifs
21 inside the file view page.
22 - Files: relative ULR support inside rendered files. It's now possible to
23 write Markup files and relative links will be handled from the RhodeCode
24 instance itself. Adds basic wiki functionality.
25 - Files: allow to show inline pdf in browser using embedded files from source code.
26 - Annotation: added shortcut links to browse the annotation view with previous
27 commits. Allows browsing history for each line from annotation view.
28 - Pull Requests: add explicit close action instead of close with status from
29 status selector. This allows closing of approved or rejected
30 pull requests, without performing a merge action.
31 - Authentication: LDAP now has an option to sync LDA groups using two
32 distinct ways. Either using rfc2307 or rfc2307bis. Increases compatibility
33 with different OpenLDAP and AD servers.
34 - Slack: updated slack integration to use the attachments for nicer formatting.
35 Added number of commits inside the message, changed UI for all Slack events.
36 - Authentication (EE edition only): added repository scope for VCS type auth
37 tokens. Each token can be now bound to particular repository for added security.
38 - User administration: added audit page to allow showing single user actions.
39 - API: implemented `get_user_audit_logs` method to fetch audit logs via API endpoint.
40 - User administration: It's now possible to edit user group membership from
41 user view.
42 - User groups administration: added managing and showing the group
43 synchronization in UI. It's now possible to enable manual group syncing on
44 already existing user groups from external sources such as LDAP/AD.
45 - Repositories: added new strip view allowing removing commits from repositories
46 via web interface for repository administrators.
47 - System Info: added info about workers and worker type.
48 Added more details about CPU. Expose workers of VCSServer in system info data.
49 Detect database migration errors.
50
51
52 General
53 ^^^^^^^
54
55 - Core: ported many views into pure pyramid code with python3.6 compatibility.
56 - Core: removed deprecated Pyro4 backend from Enterprise code.
57 - Maintenance: implemented maintenance view for Mercurial and GIT repositories.
58 For HG it will run `hg verify`, and for GIT a `git gc` command.
59 - Notifications: different approach with fixed/standard container. Floating
60 notifications no longer hide the menu when browsed on top of the page.
61 Also added option to remove single elements from stacked notifications.
62 - VCS server: exception-handling: better handling of remote exception and logging.
63 - VCS server: propagate hooks tracebacks to VCS server for easier debugging.
64 - Core: prevent `httplib3` logs to spam internal RhodeCode logs.
65 It often confuses people looking at those entries, misleading during debug.
66 - Mercurial: allow editing Largefiles store location from web interface.
67 - Git: allow editing GIT LFS store location from web interface.
68 - API: add get_method API call. This allows showing the method and it's parameter
69 from the CLI without reading the documentation.
70 In addition use it's mechanics to propose users other methods with close names
71 if the calling method is not found.
72 - UI: add timezone info into tooltips.
73 - Dependencies: bumped pyramid to 1.7.4
74 - Dependencies: bumped Mercurial version to 4.1.2
75
76
77 Security
78 ^^^^^^^^
79
80 - Hooks: added changes to propagate commit metadata on pre-push.
81 This allows easier implementation of checking hooks such as branch protection.
82 - Hooks: added new pretx hook to allow mercurial checks such as protected
83 branches, or force push.
84 - Auth: give owner of user group proper admin permissions to the user group.
85 This makes the behaviour consistent with repositories and repository groups.
86 And allows delegation of administration of those to other users.
87 - Password reset: strengthen security on password reset logic.
88 Generate token that has special password reset role.
89 Set 10 minute expiration for the token.
90 Add some logic to prevent brute forcing attacks.
91 Use more implicit messages to prevent user email discovery attacks.
92 - Core: added checks for password change for authenticated users in pure
93 Pyramid views. 2 views were still available and not forcing users to change
94 their passwords.
95 - Auth tokens: removed builtin auth-token for users.
96 Builtin token were non-removable, and always generated for new users. This
97 wasn't best practice for security as some users are strictly not allowed to
98 use tokens. From now on new users needs a new token generation in case they
99 want to use token based authentication.
100 - Auth tokens: don't generate builtin token for new users.
101 Also don't change them when password reset is made.
102 - Api: added last-activity into returned data of get_user api.
103
104
105 Performance
106 ^^^^^^^^^^^
107
108 - Mercurial: enabled new `Zstandard` compression algorithm available with
109 Mercurial 4.1.X. This allows faster, more CPU efficient clones when used
110 with new Mercurial clients.
111
112 - Users Admin: moved user admin to pyramid, and made it load users in chunks.
113 Fixed loading data to be lazy fetched, drastically improves speed of user
114 administration page in case of large amount of users.
115
116
117 Fixes
118 ^^^^^
119
120 - Search: goto commit search will now use a safe search option and never
121 throw any exceptions even if search is misconfigured
122 e.g. Elastic Search cluster is down.
123 - Events: fix a case for events called from API that couldn't fetch
124 registered user object.
125 - Comments: unlock submit if we use slash commands to set status.
126 - UI: fixed an issue with date of last change was not displayed correctly.
127 - Emails: added comment types (TODO/NOTE) into emails.
128 - Events: fix wrongly returned author data.
129 - Error middleware: read the instance title from cached object.
130 Reading from settings inside error handler can cause error hiding when
131 error_handler was caused by database errors.
132 - Pull requests: show version age component should use local dates instead of UTC.
133 - Pull requests: lock button when updating reviewers to forbid multi-submit
134 problems. Additionally fixed some small UI issues found in that view.
135 - Pull requests: forbid browsing versions on closed pull request.
136 - Pull requests: allow super-admins to delete pull requests instead of only owners.
137 - Diffs: support mercurial copy operation in diffs details.
138 - SVN: escape special chars to allow interactions with non-standard svn paths.
139 Path with special characters such as '#' will no longer trigger 404 errors.
140 - Data grids: fix some styling and processing text display.
141 - API: use consistent way to extract users, repos, repo groups and user groups
142 by id or name. Makes usage of Number vs String to differentiate if we pick
143 object ID or it's name this will allow editing of objects by either id or
144 it's name, including numeric string names.
145 - API: validate commit_id when using commit_comment API
146 - API: cleanup sessions enforce older_then must be a valid INT.
147
148
149 Upgrade notes
150 ^^^^^^^^^^^^^
151
152 - Auth-tokens: a builtin token will be migrated for all users into a custom
153 external token. We advise to inform users that the current builtin tokens
154 will now show as external ones. Builtin tokens were removed to allow expiring
155 ,or removing them. It's now possible to create users without any tokens.
156
157 From now on new users needs a new token generation in case they want to use
158 token based authentication.
159
160 - Hooks: we added via migration a pre transaction hook for Mercurial. If you're
161 using a custom code inside pre-push function of rcextensions make sure it
162 will not block your pushes.
@@ -0,0 +1,93 b''
1 .. _git-lfs-files:
2
3 |git| LFS Extension
4 ===================
5
6
7 Git Large File Storage (or LFS) is a new, open-source extension to Git that
8 aims to improve handling of large files. It does this by replacing large files
9 in your repository—such as graphics and videos—with simple text pointers.
10 |RC| Server includes an embedded LFS object store server, allowing storage of
11 large files without the need for an external object store.
12 Git LFS is disabled by default, globally, and for each individual repository.
13
14 .. note::
15
16 |RC| implements V2 API of Git LFS. Please make sure your git client is
17 using the latest version (2.0.X recommended) to leverage full feature set
18 of the V2 API.
19
20
21
22 Enabling Git LFS
23 ++++++++++++++++
24
25 Git LFS is disabled by default within |RC| Server.
26
27 To enable Git LFS Globally:
28
29 - Go to :menuselection:`Admin --> Settings --> VCS`
30
31 - Scroll down into `Git settings`
32
33 - Tick `Enable lfs extension`
34
35 - Save your settings.
36
37 Those settings apply globally to each repository that inherits from the defaults
38 You can leave `lfs extension` disabled globally, and only enable it per
39 repository that would use the lfs.
40
41
42 .. note::
43
44 You might want to adjust the global storage location at that point, however
45 we recommend leaving the default one created.
46
47
48 Installing and using the Git LFS command line client
49 ++++++++++++++++++++++++++++++++++++++++++++++++++++
50
51 Git LFS aims to integrate with the standard Git workflow as seamlessly
52 as possible. To push your first Git LFS files to an existing repository
53 Download and install the git-lfs command line client
54 Install the Git LFS filters::
55
56 git lfs install
57
58 This adds the following lines to the .gitconfig file located in your home directory::
59
60 [filter "lfs"]
61 clean = git-lfs clean %f
62 smudge = git-lfs smudge %f
63 required = true
64
65 The above change applies globally, so it is not necessary to run this for
66 each repository you work with. Choose the file types you would like LFS to
67 handle by executing the git lfs track command. The git lfs track command
68 creates or updates the .gitattributes file in your repository.
69 Change to your cloned repository, then execute git add to ensure updates
70 to the .gitattributes are later committed::
71
72 git lfs track "*.jpg"
73 git add .gitattributes
74
75 Add, commit, and push your changes as you normally would::
76
77 git add image.jpg
78 git commit -m "Added an image"
79 git push
80
81 When pushed, the Git tree updates to include a pointer to the file actual
82 file content. This pointer will include the SHA256 hash of the object and its
83 size in bytes. For example::
84
85 oid sha256:4fa32d6f9b1461c4a53618a47324ee43e36ce7ceaea2ad440cc811a7e6881be1
86 size 2580390
87
88
89 The object itself will be uploaded to a separate location via the Git LFS Batch API.
90 The transfer is validated and authorized by |RC| server itself.
91
92 If give repository has Git LFS disabled, a proper message will be sent back to
93 the client and upload of LFS objects will be forbidden.
@@ -0,0 +1,12 b''
1 diff -rup celery-2.2.10-orig/setup.py celery-2.2.10/setup.py
2 --- celery-2.2.10-orig/setup.py 2017-02-25 15:30:34.000000000 +0100
3 +++ celery-2.2.10/setup.py 2017-02-25 15:30:34.000000000 +0100
4 @@ -48,7 +48,7 @@ try:
5 except ImportError:
6 install_requires.append("importlib")
7 install_requires.extend([
8 - "python-dateutil>=1.5.0,<2.0.0",
9 + "python-dateutil>=1.5.0,<2.2.0",
10 "anyjson>=0.3.1",
11 "kombu>=1.1.2,<2.0.0",
12 "pyparsing>=1.5.0,<2.0.0",
@@ -0,0 +1,58 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 import pytest
23
24 from rhodecode.api.tests.utils import build_data, api_call, assert_ok
25
26
27 @pytest.mark.usefixtures("testuser_api", "app")
28 class TestGetMethod(object):
29 def test_get_methods_no_matches(self):
30 id_, params = build_data(self.apikey, 'get_method', pattern='hello')
31 response = api_call(self.app, params)
32
33 expected = []
34 assert_ok(id_, expected, given=response.body)
35
36 def test_get_methods(self):
37 id_, params = build_data(self.apikey, 'get_method', pattern='*comment*')
38 response = api_call(self.app, params)
39
40 expected = ['changeset_comment', 'comment_pull_request',
41 'comment_commit']
42 assert_ok(id_, expected, given=response.body)
43
44 def test_get_methods_on_single_match(self):
45 id_, params = build_data(self.apikey, 'get_method', pattern='*comment_commit*')
46 response = api_call(self.app, params)
47
48 expected = ['comment_commit',
49 {'apiuser': '<RequiredType>',
50 'comment_type': "<Optional:u'note'>",
51 'commit_id': '<RequiredType>',
52 'message': '<RequiredType>',
53 'repoid': '<RequiredType>',
54 'request': '<RequiredType>',
55 'resolves_comment_id': '<Optional:None>',
56 'status': '<Optional:None>',
57 'userid': '<Optional:<OptionalAttr:apiuser>>'}]
58 assert_ok(id_, expected, given=response.body)
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,163 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import time
22 import logging
23 from pylons import tmpl_context as c
24 from pyramid.httpexceptions import HTTPFound
25
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int
28 from rhodecode.model import repo
29 from rhodecode.model.db import User
30 from rhodecode.model.scm import ScmModel
31
32 log = logging.getLogger(__name__)
33
34
35 ADMIN_PREFIX = '/_admin'
36 STATIC_FILE_PREFIX = '/_static'
37
38
39 class TemplateArgs(StrictAttributeDict):
40 pass
41
42
43 class BaseAppView(object):
44
45 def __init__(self, context, request):
46 self.request = request
47 self.context = context
48 self.session = request.session
49 self._rhodecode_user = request.user # auth user
50 self._rhodecode_db_user = self._rhodecode_user.get_instance()
51 self._maybe_needs_password_change(
52 request.matched_route.name, self._rhodecode_db_user)
53
54 def _maybe_needs_password_change(self, view_name, user_obj):
55 log.debug('Checking if user %s needs password change on view %s',
56 user_obj, view_name)
57 skip_user_views = [
58 'logout', 'login',
59 'my_account_password', 'my_account_password_update'
60 ]
61
62 if not user_obj:
63 return
64
65 if user_obj.username == User.DEFAULT_USER:
66 return
67
68 now = time.time()
69 should_change = user_obj.user_data.get('force_password_change')
70 change_after = safe_int(should_change) or 0
71 if should_change and now > change_after:
72 log.debug('User %s requires password change', user_obj)
73 h.flash('You are required to change your password', 'warning',
74 ignore_duplicate=True)
75
76 if view_name not in skip_user_views:
77 raise HTTPFound(
78 self.request.route_path('my_account_password'))
79
80 def _get_local_tmpl_context(self):
81 c = TemplateArgs()
82 c.auth_user = self.request.user
83 return c
84
85 def _register_global_c(self, tmpl_args):
86 """
87 Registers attributes to pylons global `c`
88 """
89 # TODO(marcink): remove once pyramid migration is finished
90 for k, v in tmpl_args.items():
91 setattr(c, k, v)
92
93 def _get_template_context(self, tmpl_args):
94 self._register_global_c(tmpl_args)
95
96 local_tmpl_args = {
97 'defaults': {},
98 'errors': {},
99 }
100 local_tmpl_args.update(tmpl_args)
101 return local_tmpl_args
102
103 def load_default_context(self):
104 """
105 example:
106
107 def load_default_context(self):
108 c = self._get_local_tmpl_context()
109 c.custom_var = 'foobar'
110 self._register_global_c(c)
111 return c
112 """
113 raise NotImplementedError('Needs implementation in view class')
114
115
116 class RepoAppView(BaseAppView):
117
118 def __init__(self, context, request):
119 super(RepoAppView, self).__init__(context, request)
120 self.db_repo = request.db_repo
121 self.db_repo_name = self.db_repo.repo_name
122 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
123
124 def _get_local_tmpl_context(self):
125 c = super(RepoAppView, self)._get_local_tmpl_context()
126 # register common vars for this type of view
127 c.rhodecode_db_repo = self.db_repo
128 c.repo_name = self.db_repo_name
129 c.repository_pull_requests = self.db_repo_pull_requests
130 return c
131
132
133 class RepoRoutePredicate(object):
134 def __init__(self, val, config):
135 self.val = val
136
137 def text(self):
138 return 'repo_route = %s' % self.val
139
140 phash = text
141
142 def __call__(self, info, request):
143 repo_name = info['match']['repo_name']
144 repo_model = repo.RepoModel()
145 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
146 # if we match quickly from database, short circuit the operation,
147 # and validate repo based on the type.
148 if by_name_match:
149 # register this as request object we can re-use later
150 request.db_repo = by_name_match
151 return True
152
153 by_id_match = repo_model.get_repo_by_id(repo_name)
154 if by_id_match:
155 request.db_repo = by_id_match
156 return True
157
158 return False
159
160
161 def includeme(config):
162 config.add_route_predicate(
163 'repo_route', RepoRoutePredicate)
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,142 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.model.db import User, UserApiKeys
24
25 from rhodecode.tests import (
26 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
27 from rhodecode.tests.fixture import Fixture
28
29 fixture = Fixture()
30
31
32 def route_path(name, params=None, **kwargs):
33 import urllib
34 from rhodecode.apps._base import ADMIN_PREFIX
35
36 base_url = {
37 'users':
38 ADMIN_PREFIX + '/users',
39 'users_data':
40 ADMIN_PREFIX + '/users_data',
41 'edit_user_auth_tokens':
42 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
43 'edit_user_auth_tokens_add':
44 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/new',
45 'edit_user_auth_tokens_delete':
46 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/delete',
47 }[name].format(**kwargs)
48
49 if params:
50 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
51 return base_url
52
53
54 class TestAdminUsersView(TestController):
55
56 def test_show_users(self):
57 self.log_user()
58 self.app.get(route_path('users'))
59
60 def test_show_users_data(self, xhr_header):
61 self.log_user()
62 response = self.app.get(route_path(
63 'users_data'), extra_environ=xhr_header)
64
65 all_users = User.query().filter(
66 User.username != User.DEFAULT_USER).count()
67 assert response.json['recordsTotal'] == all_users
68
69 def test_show_users_data_filtered(self, xhr_header):
70 self.log_user()
71 response = self.app.get(route_path(
72 'users_data', params={'search[value]': 'empty_search'}),
73 extra_environ=xhr_header)
74
75 all_users = User.query().filter(
76 User.username != User.DEFAULT_USER).count()
77 assert response.json['recordsTotal'] == all_users
78 assert response.json['recordsFiltered'] == 0
79
80 def test_auth_tokens_default_user(self):
81 self.log_user()
82 user = User.get_default_user()
83 response = self.app.get(
84 route_path('edit_user_auth_tokens', user_id=user.user_id),
85 status=302)
86
87 def test_auth_tokens(self):
88 self.log_user()
89
90 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
91 response = self.app.get(
92 route_path('edit_user_auth_tokens', user_id=user.user_id))
93 for token in user.auth_tokens:
94 response.mustcontain(token)
95 response.mustcontain('never')
96
97 @pytest.mark.parametrize("desc, lifetime", [
98 ('forever', -1),
99 ('5mins', 60*5),
100 ('30days', 60*60*24*30),
101 ])
102 def test_add_auth_token(self, desc, lifetime, user_util):
103 self.log_user()
104 user = user_util.create_user()
105 user_id = user.user_id
106
107 response = self.app.post(
108 route_path('edit_user_auth_tokens_add', user_id=user_id),
109 {'description': desc, 'lifetime': lifetime,
110 'csrf_token': self.csrf_token})
111 assert_session_flash(response, 'Auth token successfully created')
112
113 response = response.follow()
114 user = User.get(user_id)
115 for auth_token in user.auth_tokens:
116 response.mustcontain(auth_token)
117
118 def test_delete_auth_token(self, user_util):
119 self.log_user()
120 user = user_util.create_user()
121 user_id = user.user_id
122 keys = user.extra_auth_tokens
123 assert 2 == len(keys)
124
125 response = self.app.post(
126 route_path('edit_user_auth_tokens_add', user_id=user_id),
127 {'description': 'desc', 'lifetime': -1,
128 'csrf_token': self.csrf_token})
129 assert_session_flash(response, 'Auth token successfully created')
130 response.follow()
131
132 # now delete our key
133 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
134 assert 3 == len(keys)
135
136 response = self.app.post(
137 route_path('edit_user_auth_tokens_delete', user_id=user_id),
138 {'del_auth_token': keys[0].api_key, 'csrf_token': self.csrf_token})
139
140 assert_session_flash(response, 'Auth token successfully deleted')
141 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
142 assert 2 == len(keys)
@@ -0,0 +1,317 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
25
26 from rhodecode.lib.helpers import Page
27 from rhodecode_tools.lib.ext_json import json
28
29 from rhodecode.apps._base import BaseAppView
30 from rhodecode.lib.auth import (
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib.utils import PartialRenderer
34 from rhodecode.lib.utils2 import safe_int, safe_unicode
35 from rhodecode.model.auth_token import AuthTokenModel
36 from rhodecode.model.user import UserModel
37 from rhodecode.model.user_group import UserGroupModel
38 from rhodecode.model.db import User, or_
39 from rhodecode.model.meta import Session
40
41 log = logging.getLogger(__name__)
42
43
44 class AdminUsersView(BaseAppView):
45 ALLOW_SCOPED_TOKENS = False
46 """
47 This view has alternative version inside EE, if modified please take a look
48 in there as well.
49 """
50
51 def load_default_context(self):
52 c = self._get_local_tmpl_context()
53 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
54 self._register_global_c(c)
55 return c
56
57 def _redirect_for_default_user(self, username):
58 _ = self.request.translate
59 if username == User.DEFAULT_USER:
60 h.flash(_("You can't edit this user"), category='warning')
61 # TODO(marcink): redirect to 'users' admin panel once this
62 # is a pyramid view
63 raise HTTPFound('/')
64
65 def _extract_ordering(self, request):
66 column_index = safe_int(request.GET.get('order[0][column]'))
67 order_dir = request.GET.get(
68 'order[0][dir]', 'desc')
69 order_by = request.GET.get(
70 'columns[%s][data][sort]' % column_index, 'name_raw')
71
72 # translate datatable to DB columns
73 order_by = {
74 'first_name': 'name',
75 'last_name': 'lastname',
76 }.get(order_by) or order_by
77
78 search_q = request.GET.get('search[value]')
79 return search_q, order_by, order_dir
80
81 def _extract_chunk(self, request):
82 start = safe_int(request.GET.get('start'), 0)
83 length = safe_int(request.GET.get('length'), 25)
84 draw = safe_int(request.GET.get('draw'))
85 return draw, start, length
86
87 @HasPermissionAllDecorator('hg.admin')
88 @view_config(
89 route_name='users', request_method='GET',
90 renderer='rhodecode:templates/admin/users/users.mako')
91 def users_list(self):
92 c = self.load_default_context()
93 return self._get_template_context(c)
94
95 @HasPermissionAllDecorator('hg.admin')
96 @view_config(
97 # renderer defined below
98 route_name='users_data', request_method='GET', renderer='json',
99 xhr=True)
100 def users_list_data(self):
101 draw, start, limit = self._extract_chunk(self.request)
102 search_q, order_by, order_dir = self._extract_ordering(self.request)
103
104 _render = PartialRenderer('data_table/_dt_elements.mako')
105
106 def user_actions(user_id, username):
107 return _render("user_actions", user_id, username)
108
109 users_data_total_count = User.query()\
110 .filter(User.username != User.DEFAULT_USER) \
111 .count()
112
113 # json generate
114 base_q = User.query().filter(User.username != User.DEFAULT_USER)
115
116 if search_q:
117 like_expression = u'%{}%'.format(safe_unicode(search_q))
118 base_q = base_q.filter(or_(
119 User.username.ilike(like_expression),
120 User._email.ilike(like_expression),
121 User.name.ilike(like_expression),
122 User.lastname.ilike(like_expression),
123 ))
124
125 users_data_total_filtered_count = base_q.count()
126
127 sort_col = getattr(User, order_by, None)
128 if sort_col and order_dir == 'asc':
129 base_q = base_q.order_by(sort_col.asc().nullslast())
130 elif sort_col:
131 base_q = base_q.order_by(sort_col.desc().nullslast())
132
133 base_q = base_q.offset(start).limit(limit)
134 users_list = base_q.all()
135
136 users_data = []
137 for user in users_list:
138 users_data.append({
139 "username": h.gravatar_with_user(user.username),
140 "email": user.email,
141 "first_name": h.escape(user.name),
142 "last_name": h.escape(user.lastname),
143 "last_login": h.format_date(user.last_login),
144 "last_activity": h.format_date(user.last_activity),
145 "active": h.bool2icon(user.active),
146 "active_raw": user.active,
147 "admin": h.bool2icon(user.admin),
148 "extern_type": user.extern_type,
149 "extern_name": user.extern_name,
150 "action": user_actions(user.user_id, user.username),
151 })
152
153 data = ({
154 'draw': draw,
155 'data': users_data,
156 'recordsTotal': users_data_total_count,
157 'recordsFiltered': users_data_total_filtered_count,
158 })
159
160 return data
161
162 @LoginRequired()
163 @HasPermissionAllDecorator('hg.admin')
164 @view_config(
165 route_name='edit_user_auth_tokens', request_method='GET',
166 renderer='rhodecode:templates/admin/users/user_edit.mako')
167 def auth_tokens(self):
168 _ = self.request.translate
169 c = self.load_default_context()
170
171 user_id = self.request.matchdict.get('user_id')
172 c.user = User.get_or_404(user_id, pyramid_exc=True)
173 self._redirect_for_default_user(c.user.username)
174
175 c.active = 'auth_tokens'
176
177 c.lifetime_values = [
178 (str(-1), _('forever')),
179 (str(5), _('5 minutes')),
180 (str(60), _('1 hour')),
181 (str(60 * 24), _('1 day')),
182 (str(60 * 24 * 30), _('1 month')),
183 ]
184 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
185 c.role_values = [
186 (x, AuthTokenModel.cls._get_role_name(x))
187 for x in AuthTokenModel.cls.ROLES]
188 c.role_options = [(c.role_values, _("Role"))]
189 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
190 c.user.user_id, show_expired=True)
191 return self._get_template_context(c)
192
193 def maybe_attach_token_scope(self, token):
194 # implemented in EE edition
195 pass
196
197 @LoginRequired()
198 @HasPermissionAllDecorator('hg.admin')
199 @CSRFRequired()
200 @view_config(
201 route_name='edit_user_auth_tokens_add', request_method='POST')
202 def auth_tokens_add(self):
203 _ = self.request.translate
204 c = self.load_default_context()
205
206 user_id = self.request.matchdict.get('user_id')
207 c.user = User.get_or_404(user_id, pyramid_exc=True)
208 self._redirect_for_default_user(c.user.username)
209
210 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
211 description = self.request.POST.get('description')
212 role = self.request.POST.get('role')
213
214 token = AuthTokenModel().create(
215 c.user.user_id, description, lifetime, role)
216 self.maybe_attach_token_scope(token)
217 Session().commit()
218
219 h.flash(_("Auth token successfully created"), category='success')
220 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
221
222 @LoginRequired()
223 @HasPermissionAllDecorator('hg.admin')
224 @CSRFRequired()
225 @view_config(
226 route_name='edit_user_auth_tokens_delete', request_method='POST')
227 def auth_tokens_delete(self):
228 _ = self.request.translate
229 c = self.load_default_context()
230
231 user_id = self.request.matchdict.get('user_id')
232 c.user = User.get_or_404(user_id, pyramid_exc=True)
233 self._redirect_for_default_user(c.user.username)
234
235 del_auth_token = self.request.POST.get('del_auth_token')
236
237 if del_auth_token:
238 AuthTokenModel().delete(del_auth_token, c.user.user_id)
239 Session().commit()
240 h.flash(_("Auth token successfully deleted"), category='success')
241
242 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
243
244 @LoginRequired()
245 @HasPermissionAllDecorator('hg.admin')
246 @view_config(
247 route_name='edit_user_groups_management', request_method='GET',
248 renderer='rhodecode:templates/admin/users/user_edit.mako')
249 def groups_management(self):
250 c = self.load_default_context()
251
252 user_id = self.request.matchdict.get('user_id')
253 c.user = User.get_or_404(user_id, pyramid_exc=True)
254 c.data = c.user.group_member
255 self._redirect_for_default_user(c.user.username)
256 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member]
257 c.groups = json.dumps(groups)
258 c.active = 'groups'
259
260 return self._get_template_context(c)
261
262 @LoginRequired()
263 @HasPermissionAllDecorator('hg.admin')
264 @view_config(
265 route_name='edit_user_groups_management_updates', request_method='POST')
266 def groups_management_updates(self):
267 _ = self.request.translate
268 c = self.load_default_context()
269
270 user_id = self.request.matchdict.get('user_id')
271 c.user = User.get_or_404(user_id, pyramid_exc=True)
272 self._redirect_for_default_user(c.user.username)
273
274 users_groups = set(self.request.POST.getall('users_group_id'))
275 users_groups_model = []
276
277 for ugid in users_groups:
278 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
279 user_group_model = UserGroupModel()
280 user_group_model.change_groups(c.user, users_groups_model)
281
282 Session().commit()
283 c.active = 'user_groups_management'
284 h.flash(_("Groups successfully changed"), category='success')
285
286 return HTTPFound(h.route_path(
287 'edit_user_groups_management', user_id=user_id))
288
289 @LoginRequired()
290 @HasPermissionAllDecorator('hg.admin')
291 @view_config(
292 route_name='edit_user_audit_logs', request_method='GET',
293 renderer='rhodecode:templates/admin/users/user_edit.mako')
294 def user_audit_logs(self):
295 _ = self.request.translate
296 c = self.load_default_context()
297
298 user_id = self.request.matchdict.get('user_id')
299 c.user = User.get_or_404(user_id, pyramid_exc=True)
300 self._redirect_for_default_user(c.user.username)
301 c.active = 'audit'
302
303 p = safe_int(self.request.GET.get('page', 1), 1)
304
305 filter_term = self.request.GET.get('filter')
306 c.user_log = UserModel().get_user_log(c.user, filter_term)
307
308 def url_generator(**kw):
309 if filter_term:
310 kw['filter'] = filter_term
311 return self.request.current_route_path(_query=kw)
312
313 c.user_log = Page(c.user_log, page=p, items_per_page=10,
314 url=url_generator)
315 c.filter_term = filter_term
316 return self._get_template_context(c)
317
@@ -0,0 +1,52 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 from rhodecode.apps._base import ADMIN_PREFIX
23
24
25 def includeme(config):
26
27 config.add_route(
28 name='my_account_profile',
29 pattern=ADMIN_PREFIX + '/my_account/profile')
30
31 config.add_route(
32 name='my_account_password',
33 pattern=ADMIN_PREFIX + '/my_account/password')
34
35 config.add_route(
36 name='my_account_password_update',
37 pattern=ADMIN_PREFIX + '/my_account/password')
38
39 config.add_route(
40 name='my_account_auth_tokens',
41 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
42 config.add_route(
43 name='my_account_auth_tokens_add',
44 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new',
45 )
46 config.add_route(
47 name='my_account_auth_tokens_delete',
48 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete',
49 )
50
51 # Scan module for configuration decorators.
52 config.scan()
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -0,0 +1,111 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.model.db import User
25 from rhodecode.tests import (
26 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
27 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.utils import AssertResponse
30
31 fixture = Fixture()
32
33
34 def route_path(name, **kwargs):
35 return {
36 'my_account_auth_tokens':
37 ADMIN_PREFIX + '/my_account/auth_tokens',
38 'my_account_auth_tokens_add':
39 ADMIN_PREFIX + '/my_account/auth_tokens/new',
40 'my_account_auth_tokens_delete':
41 ADMIN_PREFIX + '/my_account/auth_tokens/delete',
42 }[name].format(**kwargs)
43
44
45 class TestMyAccountAuthTokens(TestController):
46
47 def test_my_account_auth_tokens(self):
48 usr = self.log_user('test_regular2', 'test12')
49 user = User.get(usr['user_id'])
50 response = self.app.get(route_path('my_account_auth_tokens'))
51 for token in user.auth_tokens:
52 response.mustcontain(token)
53 response.mustcontain('never')
54
55 def test_my_account_add_auth_tokens_wrong_csrf(self, user_util):
56 user = user_util.create_user(password='qweqwe')
57 self.log_user(user.username, 'qweqwe')
58
59 self.app.post(
60 route_path('my_account_auth_tokens_add'),
61 {'description': 'desc', 'lifetime': -1}, status=403)
62
63 @pytest.mark.parametrize("desc, lifetime", [
64 ('forever', -1),
65 ('5mins', 60*5),
66 ('30days', 60*60*24*30),
67 ])
68 def test_my_account_add_auth_tokens(self, desc, lifetime, user_util):
69 user = user_util.create_user(password='qweqwe')
70 user_id = user.user_id
71 self.log_user(user.username, 'qweqwe')
72
73 response = self.app.post(
74 route_path('my_account_auth_tokens_add'),
75 {'description': desc, 'lifetime': lifetime,
76 'csrf_token': self.csrf_token})
77 assert_session_flash(response, 'Auth token successfully created')
78
79 response = response.follow()
80 user = User.get(user_id)
81 for auth_token in user.auth_tokens:
82 response.mustcontain(auth_token)
83
84 def test_my_account_delete_auth_token(self, user_util):
85 user = user_util.create_user(password='qweqwe')
86 user_id = user.user_id
87 self.log_user(user.username, 'qweqwe')
88
89 user = User.get(user_id)
90 keys = user.extra_auth_tokens
91 assert 2 == len(keys)
92
93 response = self.app.post(
94 route_path('my_account_auth_tokens_add'),
95 {'description': 'desc', 'lifetime': -1,
96 'csrf_token': self.csrf_token})
97 assert_session_flash(response, 'Auth token successfully created')
98 response.follow()
99
100 user = User.get(user_id)
101 keys = user.extra_auth_tokens
102 assert 3 == len(keys)
103
104 response = self.app.post(
105 route_path('my_account_auth_tokens_delete'),
106 {'del_auth_token': keys[0].api_key, 'csrf_token': self.csrf_token})
107 assert_session_flash(response, 'Auth token successfully deleted')
108
109 user = User.get(user_id)
110 keys = user.extra_auth_tokens
111 assert 2 == len(keys)
@@ -0,0 +1,137 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22 import mock
23
24 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.lib import helpers as h
26 from rhodecode.lib.auth import check_password
27 from rhodecode.model.meta import Session
28 from rhodecode.model.user import UserModel
29 from rhodecode.tests import assert_session_flash
30 from rhodecode.tests.fixture import Fixture, TestController, error_function
31
32 fixture = Fixture()
33
34
35 def route_path(name, **kwargs):
36 return {
37 'home': '/',
38 'my_account_password':
39 ADMIN_PREFIX + '/my_account/password',
40 }[name].format(**kwargs)
41
42
43 test_user_1 = 'testme'
44 test_user_1_password = '0jd83nHNS/d23n'
45
46
47 class TestMyAccountPassword(TestController):
48 def test_valid_change_password(self, user_util):
49 new_password = 'my_new_valid_password'
50 user = user_util.create_user(password=test_user_1_password)
51 self.log_user(user.username, test_user_1_password)
52
53 form_data = [
54 ('current_password', test_user_1_password),
55 ('__start__', 'new_password:mapping'),
56 ('new_password', new_password),
57 ('new_password-confirm', new_password),
58 ('__end__', 'new_password:mapping'),
59 ('csrf_token', self.csrf_token),
60 ]
61 response = self.app.post(route_path('my_account_password'), form_data).follow()
62 assert 'Successfully updated password' in response
63
64 # check_password depends on user being in session
65 Session().add(user)
66 try:
67 assert check_password(new_password, user.password)
68 finally:
69 Session().expunge(user)
70
71 @pytest.mark.parametrize('current_pw, new_pw, confirm_pw', [
72 ('', 'abcdef123', 'abcdef123'),
73 ('wrong_pw', 'abcdef123', 'abcdef123'),
74 (test_user_1_password, test_user_1_password, test_user_1_password),
75 (test_user_1_password, '', ''),
76 (test_user_1_password, 'abcdef123', ''),
77 (test_user_1_password, '', 'abcdef123'),
78 (test_user_1_password, 'not_the', 'same_pw'),
79 (test_user_1_password, 'short', 'short'),
80 ])
81 def test_invalid_change_password(self, current_pw, new_pw, confirm_pw,
82 user_util):
83 user = user_util.create_user(password=test_user_1_password)
84 self.log_user(user.username, test_user_1_password)
85
86 form_data = [
87 ('current_password', current_pw),
88 ('__start__', 'new_password:mapping'),
89 ('new_password', new_pw),
90 ('new_password-confirm', confirm_pw),
91 ('__end__', 'new_password:mapping'),
92 ('csrf_token', self.csrf_token),
93 ]
94 response = self.app.post(route_path('my_account_password'), form_data)
95
96 assert_response = response.assert_response()
97 assert assert_response.get_elements('.error-block')
98
99 @mock.patch.object(UserModel, 'update_user', error_function)
100 def test_invalid_change_password_exception(self, user_util):
101 user = user_util.create_user(password=test_user_1_password)
102 self.log_user(user.username, test_user_1_password)
103
104 form_data = [
105 ('current_password', test_user_1_password),
106 ('__start__', 'new_password:mapping'),
107 ('new_password', '123456'),
108 ('new_password-confirm', '123456'),
109 ('__end__', 'new_password:mapping'),
110 ('csrf_token', self.csrf_token),
111 ]
112 response = self.app.post(route_path('my_account_password'), form_data)
113 assert_session_flash(
114 response, 'Error occurred during update of user password')
115
116 def test_password_is_updated_in_session_on_password_change(self, user_util):
117 old_password = 'abcdef123'
118 new_password = 'abcdef124'
119
120 user = user_util.create_user(password=old_password)
121 session = self.log_user(user.username, old_password)
122 old_password_hash = session['password']
123
124 form_data = [
125 ('current_password', old_password),
126 ('__start__', 'new_password:mapping'),
127 ('new_password', new_password),
128 ('new_password-confirm', new_password),
129 ('__end__', 'new_password:mapping'),
130 ('csrf_token', self.csrf_token),
131 ]
132 self.app.post(route_path('my_account_password'), form_data)
133
134 response = self.app.get(route_path('home'))
135 new_password_hash = response.session['rhodecode_user']['password']
136
137 assert old_password_hash != new_password_hash No newline at end of file
@@ -0,0 +1,55 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.tests import (
25 TestController, TEST_USER_ADMIN_LOGIN,
26 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
27 from rhodecode.tests.fixture import Fixture
28
29 fixture = Fixture()
30
31
32 def route_path(name, **kwargs):
33 return {
34 'my_account':
35 ADMIN_PREFIX + '/my_account/profile',
36 }[name].format(**kwargs)
37
38
39 class TestMyAccountProfile(TestController):
40
41 def test_my_account(self):
42 self.log_user()
43 response = self.app.get(route_path('my_account'))
44
45 response.mustcontain(TEST_USER_ADMIN_LOGIN)
46 response.mustcontain('href="/_admin/my_account/edit"')
47 response.mustcontain('Photo')
48
49 def test_my_account_regular_user(self):
50 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
51 response = self.app.get(route_path('my_account'))
52
53 response.mustcontain(TEST_USER_REGULAR_LOGIN)
54 response.mustcontain('href="/_admin/my_account/edit"')
55 response.mustcontain('Photo')
@@ -0,0 +1,194 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
25
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode import forms
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.utils2 import safe_int, md5
31 from rhodecode.model.auth_token import AuthTokenModel
32 from rhodecode.model.meta import Session
33 from rhodecode.model.user import UserModel
34 from rhodecode.model.validation_schema.schemas import user_schema
35
36 log = logging.getLogger(__name__)
37
38
39 class MyAccountView(BaseAppView):
40 ALLOW_SCOPED_TOKENS = False
41 """
42 This view has alternative version inside EE, if modified please take a look
43 in there as well.
44 """
45
46 def load_default_context(self):
47 c = self._get_local_tmpl_context()
48 c.user = c.auth_user.get_instance()
49 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
50 self._register_global_c(c)
51 return c
52
53 @LoginRequired()
54 @NotAnonymous()
55 @view_config(
56 route_name='my_account_profile', request_method='GET',
57 renderer='rhodecode:templates/admin/my_account/my_account.mako')
58 def my_account_profile(self):
59 c = self.load_default_context()
60 c.active = 'profile'
61 return self._get_template_context(c)
62
63 @LoginRequired()
64 @NotAnonymous()
65 @view_config(
66 route_name='my_account_password', request_method='GET',
67 renderer='rhodecode:templates/admin/my_account/my_account.mako')
68 def my_account_password(self):
69 c = self.load_default_context()
70 c.active = 'password'
71 c.extern_type = c.user.extern_type
72
73 schema = user_schema.ChangePasswordSchema().bind(
74 username=c.user.username)
75
76 form = forms.Form(
77 schema, buttons=(forms.buttons.save, forms.buttons.reset))
78
79 c.form = form
80 return self._get_template_context(c)
81
82 @LoginRequired()
83 @NotAnonymous()
84 @CSRFRequired()
85 @view_config(
86 route_name='my_account_password', request_method='POST',
87 renderer='rhodecode:templates/admin/my_account/my_account.mako')
88 def my_account_password_update(self):
89 _ = self.request.translate
90 c = self.load_default_context()
91 c.active = 'password'
92 c.extern_type = c.user.extern_type
93
94 schema = user_schema.ChangePasswordSchema().bind(
95 username=c.user.username)
96
97 form = forms.Form(
98 schema, buttons=(forms.buttons.save, forms.buttons.reset))
99
100 if c.extern_type != 'rhodecode':
101 raise HTTPFound(self.request.route_path('my_account_password'))
102
103 controls = self.request.POST.items()
104 try:
105 valid_data = form.validate(controls)
106 UserModel().update_user(c.user.user_id, **valid_data)
107 c.user.update_userdata(force_password_change=False)
108 Session().commit()
109 except forms.ValidationFailure as e:
110 c.form = e
111 return self._get_template_context(c)
112
113 except Exception:
114 log.exception("Exception updating password")
115 h.flash(_('Error occurred during update of user password'),
116 category='error')
117 else:
118 instance = c.auth_user.get_instance()
119 self.session.setdefault('rhodecode_user', {}).update(
120 {'password': md5(instance.password)})
121 self.session.save()
122 h.flash(_("Successfully updated password"), category='success')
123
124 raise HTTPFound(self.request.route_path('my_account_password'))
125
126 @LoginRequired()
127 @NotAnonymous()
128 @view_config(
129 route_name='my_account_auth_tokens', request_method='GET',
130 renderer='rhodecode:templates/admin/my_account/my_account.mako')
131 def my_account_auth_tokens(self):
132 _ = self.request.translate
133
134 c = self.load_default_context()
135 c.active = 'auth_tokens'
136
137 c.lifetime_values = [
138 (str(-1), _('forever')),
139 (str(5), _('5 minutes')),
140 (str(60), _('1 hour')),
141 (str(60 * 24), _('1 day')),
142 (str(60 * 24 * 30), _('1 month')),
143 ]
144 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
145 c.role_values = [
146 (x, AuthTokenModel.cls._get_role_name(x))
147 for x in AuthTokenModel.cls.ROLES]
148 c.role_options = [(c.role_values, _("Role"))]
149 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
150 c.user.user_id, show_expired=True)
151 return self._get_template_context(c)
152
153 def maybe_attach_token_scope(self, token):
154 # implemented in EE edition
155 pass
156
157 @LoginRequired()
158 @NotAnonymous()
159 @CSRFRequired()
160 @view_config(
161 route_name='my_account_auth_tokens_add', request_method='POST')
162 def my_account_auth_tokens_add(self):
163 _ = self.request.translate
164 c = self.load_default_context()
165
166 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
167 description = self.request.POST.get('description')
168 role = self.request.POST.get('role')
169
170 token = AuthTokenModel().create(
171 c.user.user_id, description, lifetime, role)
172 self.maybe_attach_token_scope(token)
173 Session().commit()
174
175 h.flash(_("Auth token successfully created"), category='success')
176 return HTTPFound(h.route_path('my_account_auth_tokens'))
177
178 @LoginRequired()
179 @NotAnonymous()
180 @CSRFRequired()
181 @view_config(
182 route_name='my_account_auth_tokens_delete', request_method='POST')
183 def my_account_auth_tokens_delete(self):
184 _ = self.request.translate
185 c = self.load_default_context()
186
187 del_auth_token = self.request.POST.get('del_auth_token')
188
189 if del_auth_token:
190 AuthTokenModel().delete(del_auth_token, c.user.user_id)
191 Session().commit()
192 h.flash(_("Auth token successfully deleted"), category='success')
193
194 return HTTPFound(h.route_path('my_account_auth_tokens'))
@@ -0,0 +1,46 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 def includeme(config):
23
24 config.add_route(
25 name='repo_maintenance',
26 pattern='/{repo_name:.*?[^/]}/maintenance', repo_route=True)
27
28 config.add_route(
29 name='repo_maintenance_execute',
30 pattern='/{repo_name:.*?[^/]}/maintenance/execute', repo_route=True)
31
32
33 # Strip
34 config.add_route(
35 name='strip',
36 pattern='/{repo_name:.*?[^/]}/strip', repo_route=True)
37
38 config.add_route(
39 name='strip_check',
40 pattern='/{repo_name:.*?[^/]}/strip_check', repo_route=True)
41
42 config.add_route(
43 name='strip_execute',
44 pattern='/{repo_name:.*?[^/]}/strip_execute', repo_route=True)
45 # Scan module for configuration decorators.
46 config.scan()
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -0,0 +1,70 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24
25 from rhodecode.apps._base import RepoAppView
26 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
27 NotAnonymous)
28 from rhodecode.lib import repo_maintenance
29
30 log = logging.getLogger(__name__)
31
32
33 class RepoMaintenanceView(RepoAppView):
34 def load_default_context(self):
35 c = self._get_local_tmpl_context()
36
37 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
38 c.repo_info = self.db_repo
39
40 self._register_global_c(c)
41 return c
42
43 @LoginRequired()
44 @NotAnonymous()
45 @HasRepoPermissionAnyDecorator('repository.admin')
46 @view_config(
47 route_name='repo_maintenance', request_method='GET',
48 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
49 def repo_maintenance(self):
50 c = self.load_default_context()
51 c.active = 'maintenance'
52 maintenance = repo_maintenance.RepoMaintenance()
53 c.executable_tasks = maintenance.get_tasks_for_repo(self.db_repo)
54 return self._get_template_context(c)
55
56 @LoginRequired()
57 @NotAnonymous()
58 @HasRepoPermissionAnyDecorator('repository.admin')
59 @view_config(
60 route_name='repo_maintenance_execute', request_method='GET',
61 renderer='json', xhr=True)
62 def repo_maintenance_execute(self):
63 c = self.load_default_context()
64 c.active = 'maintenance'
65 _ = self.request.translate
66
67 maintenance = repo_maintenance.RepoMaintenance()
68 executed_types = maintenance.execute(self.db_repo)
69
70 return executed_types
@@ -0,0 +1,110 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22 from pyramid.view import view_config
23
24 from rhodecode.apps._base import RepoAppView
25 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
26 NotAnonymous)
27
28
29 log = logging.getLogger(__name__)
30
31
32 class StripView(RepoAppView):
33 def load_default_context(self):
34 c = self._get_local_tmpl_context()
35
36 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
37 c.repo_info = self.db_repo
38
39 self._register_global_c(c)
40 return c
41
42 @LoginRequired()
43 @NotAnonymous()
44 @HasRepoPermissionAnyDecorator('repository.admin')
45 @view_config(
46 route_name='strip', request_method='GET',
47 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
48 def strip(self):
49 c = self.load_default_context()
50 c.active = 'strip'
51 c.strip_limit = 10
52
53 return self._get_template_context(c)
54
55 @LoginRequired()
56 @NotAnonymous()
57 @HasRepoPermissionAnyDecorator('repository.admin')
58 @view_config(
59 route_name='strip_check', request_method='POST',
60 renderer='json', xhr=True
61 )
62 def strip_check(self):
63 from rhodecode.lib.vcs.backends.base import EmptyCommit
64 data = {}
65 rp = self.request.POST
66 for i in range(1, 11):
67 chset = 'changeset_id-%d'%(i,)
68 check = rp.get(chset)
69 if check:
70 data[i] = self.db_repo.get_changeset(rp[chset])
71 if isinstance(data[i], EmptyCommit):
72 data[i] = {'rev': None, 'commit': rp[chset]}
73 else:
74 data[i] = {'rev': data[i].raw_id, 'branch': data[i].branch, 'author': data[i].author,
75 'comment': data[i].message}
76 else:
77 break
78 return data
79
80 @LoginRequired()
81 @NotAnonymous()
82 @HasRepoPermissionAnyDecorator('repository.admin')
83 @view_config(
84 route_name='strip_execute', request_method='POST',
85 renderer='json', xhr=True
86 )
87 def strip_execute(self):
88
89 from rhodecode.model.scm import ScmModel
90 from rhodecode.lib.ext_json import json
91
92 c = self.load_default_context()
93 user = self._rhodecode_user
94 rp = self.request.POST
95 data = {}
96 for idx in rp:
97 commit = json.loads(rp[idx])
98 #If someone put two times the same branch
99 if commit['branch'] in data.keys():
100 continue
101 try:
102 ScmModel().strip(repo=c.repo_info,
103 commit_id=commit['rev'], branch=commit['branch'])
104 log.info('Stripped commit %s from repo `%s` by %s' % (commit['rev'], c.repo_info.repo_name, user))
105 data[commit['rev']] = True
106 except Exception, e:
107 data[commit['rev']] = False
108 log.debug('Stripped commit %s from repo `%s` failed by %s, exeption %s' % (commit['rev'],
109 c.repo_info.repo_name, user, e.message))
110 return data
@@ -0,0 +1,28 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 def includeme(config):
23 config.add_route(
24 name='user_profile',
25 pattern='/_profiles/{username}')
26
27 # Scan module for configuration decorators.
28 config.scan()
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,6 +1,6 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.6.1
2 current_version = 4.7.0
3 message = release: Bump version {current_version} to {new_version}
3 message = release: Bump version {current_version} to {new_version}
4
4
5 [bumpversion:file:rhodecode/VERSION]
5 [bumpversion:file:rhodecode/VERSION]
6
6
@@ -1,18 +1,20 b''
1 [run]
1 [run]
2
2
3 branch = True
3 branch = True
4
4
5 include =
5 include =
6 rhodecode/lib/*
6 rhodecode/lib/*
7 rhodecode/model/*
7 rhodecode/model/*
8 rhodecode/controllers/*
8 rhodecode/controllers/*
9
9
10 omit =
10 omit =
11 rhodecode/lib/vcs/remote/*
11 rhodecode/lib/vcs/remote/*
12 rhodecode/lib/dbmigrate/*
12 rhodecode/lib/dbmigrate/*
13 rhodecode/lib/paster_commands/*
14 rhodecode/tests/*
13
15
14 [report]
16 [report]
15
17
16 exclude_lines =
18 exclude_lines =
17 raise NotImplementedError
19 raise NotImplementedError
18
20
@@ -1,33 +1,28 b''
1 [DEFAULT]
1 [DEFAULT]
2 done = false
2 done = false
3
3
4 [task:bump_version]
4 [task:bump_version]
5 done = true
5 done = true
6
6
7 [task:rc_tools_pinned]
8 done = true
9
10 [task:fixes_on_stable]
7 [task:fixes_on_stable]
11 done = true
12
8
13 [task:pip2nix_generated]
9 [task:pip2nix_generated]
14 done = true
15
10
16 [task:changelog_updated]
11 [task:changelog_updated]
17 done = true
18
12
19 [task:generate_api_docs]
13 [task:generate_api_docs]
20 done = true
14
15 [task:updated_translation]
21
16
22 [release]
17 [release]
23 state = prepared
18 state = in_progress
24 version = 4.6.1
19 version = 4.7.0
25
20
26 [task:updated_translation]
21 [task:rc_tools_pinned]
27
22
28 [task:generate_js_routes]
23 [task:generate_js_routes]
29
24
30 [task:updated_trial_license]
25 [task:updated_trial_license]
31
26
32 [task:generate_oss_licenses]
27 [task:generate_oss_licenses]
33
28
@@ -1,53 +1,53 b''
1 # top level files
1 # top level files
2 include test.ini
2
3 include MANIFEST.in
3 include MANIFEST.in
4 include README.rst
4 include README.rst
5 include CHANGES.rst
5 include CHANGES.rst
6 include LICENSE.txt
6 include LICENSE.txt
7
7
8 include rhodecode/VERSION
8 include rhodecode/VERSION
9
9
10 # docs
10 # docs
11 recursive-include docs *
11 recursive-include docs *
12
12
13 # all config files
13 # all config files
14 recursive-include configs *
14 recursive-include configs *
15
15
16 # translations
16 # translations
17 recursive-include rhodecode/i18n *
17 recursive-include rhodecode/i18n *
18
18
19 # hook templates
19 # hook templates
20 recursive-include rhodecode/config/hook_templates *
20 recursive-include rhodecode/config/hook_templates *
21
21
22 # non-python core stuff
22 # non-python core stuff
23 recursive-include rhodecode *.cfg
23 recursive-include rhodecode *.cfg
24 recursive-include rhodecode *.json
24 recursive-include rhodecode *.json
25 recursive-include rhodecode *.ini_tmpl
25 recursive-include rhodecode *.ini_tmpl
26 recursive-include rhodecode *.sh
26 recursive-include rhodecode *.sh
27 recursive-include rhodecode *.mako
27 recursive-include rhodecode *.mako
28
28
29 # 502 page
29 # 502 page
30 include rhodecode/public/502.html
30 include rhodecode/public/502.html
31
31
32
32
33 # images, css
33 # images, css
34 include rhodecode/public/css/*.css
34 include rhodecode/public/css/*.css
35 include rhodecode/public/images/*.*
35 include rhodecode/public/images/*.*
36
36
37 # sound files
37 # sound files
38 include rhodecode/public/sounds/*.mp3
38 include rhodecode/public/sounds/*.mp3
39 include rhodecode/public/sounds/*.wav
39 include rhodecode/public/sounds/*.wav
40
40
41 # fonts
41 # fonts
42 recursive-include rhodecode/public/fonts/ProximaNova *
42 recursive-include rhodecode/public/fonts/ProximaNova *
43 recursive-include rhodecode/public/fonts/RCIcons *
43 recursive-include rhodecode/public/fonts/RCIcons *
44
44
45 # js
45 # js
46 recursive-include rhodecode/public/js *
46 recursive-include rhodecode/public/js *
47
47
48 # templates
48 # templates
49 recursive-include rhodecode/templates *
49 recursive-include rhodecode/templates *
50
50
51 # skip any tests files
51 # skip any tests files
52 recursive-exclude rhodecode/tests *
52 recursive-exclude rhodecode/tests *
53
53
@@ -1,681 +1,678 b''
1
1
2
2
3 ################################################################################
3 ################################################################################
4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10
10
11 ################################################################################
11 ################################################################################
12 ## EMAIL CONFIGURATION ##
12 ## EMAIL CONFIGURATION ##
13 ## Uncomment and replace with the email address which should receive ##
13 ## Uncomment and replace with the email address which should receive ##
14 ## any error reports after an application crash ##
14 ## any error reports after an application crash ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 ################################################################################
16 ################################################################################
17
17
18 ## prefix all emails subjects with given prefix, helps filtering out emails
18 ## prefix all emails subjects with given prefix, helps filtering out emails
19 #email_prefix = [RhodeCode]
19 #email_prefix = [RhodeCode]
20
20
21 ## email FROM address all mails will be sent
21 ## email FROM address all mails will be sent
22 #app_email_from = rhodecode-noreply@localhost
22 #app_email_from = rhodecode-noreply@localhost
23
23
24 ## Uncomment and replace with the address which should receive any error report
24 ## Uncomment and replace with the address which should receive any error report
25 ## note: using appenlight for error handling doesn't need this to be uncommented
25 ## note: using appenlight for error handling doesn't need this to be uncommented
26 #email_to = admin@localhost
26 #email_to = admin@localhost
27
27
28 ## in case of Application errors, sent an error email form
28 ## in case of Application errors, sent an error email form
29 #error_email_from = rhodecode_error@localhost
29 #error_email_from = rhodecode_error@localhost
30
30
31 ## additional error message to be send in case of server crash
31 ## additional error message to be send in case of server crash
32 #error_message =
32 #error_message =
33
33
34
34
35 #smtp_server = mail.server.com
35 #smtp_server = mail.server.com
36 #smtp_username =
36 #smtp_username =
37 #smtp_password =
37 #smtp_password =
38 #smtp_port =
38 #smtp_port =
39 #smtp_use_tls = false
39 #smtp_use_tls = false
40 #smtp_use_ssl = true
40 #smtp_use_ssl = true
41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
42 #smtp_auth =
42 #smtp_auth =
43
43
44 [server:main]
44 [server:main]
45 ## COMMON ##
45 ## COMMON ##
46 host = 127.0.0.1
46 host = 127.0.0.1
47 port = 5000
47 port = 5000
48
48
49 ##################################
49 ##################################
50 ## WAITRESS WSGI SERVER ##
50 ## WAITRESS WSGI SERVER ##
51 ## Recommended for Development ##
51 ## Recommended for Development ##
52 ##################################
52 ##################################
53
53
54 use = egg:waitress#main
54 use = egg:waitress#main
55 ## number of worker threads
55 ## number of worker threads
56 threads = 5
56 threads = 5
57 ## MAX BODY SIZE 100GB
57 ## MAX BODY SIZE 100GB
58 max_request_body_size = 107374182400
58 max_request_body_size = 107374182400
59 ## Use poll instead of select, fixes file descriptors limits problems.
59 ## Use poll instead of select, fixes file descriptors limits problems.
60 ## May not work on old windows systems.
60 ## May not work on old windows systems.
61 asyncore_use_poll = true
61 asyncore_use_poll = true
62
62
63
63
64 ##########################
64 ##########################
65 ## GUNICORN WSGI SERVER ##
65 ## GUNICORN WSGI SERVER ##
66 ##########################
66 ##########################
67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
68
68
69 #use = egg:gunicorn#main
69 #use = egg:gunicorn#main
70 ## Sets the number of process workers. You must set `instance_id = *`
70 ## Sets the number of process workers. You must set `instance_id = *`
71 ## when this option is set to more than one worker, recommended
71 ## when this option is set to more than one worker, recommended
72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
73 ## The `instance_id = *` must be set in the [app:main] section below
73 ## The `instance_id = *` must be set in the [app:main] section below
74 #workers = 2
74 #workers = 2
75 ## number of threads for each of the worker, must be set to 1 for gevent
75 ## number of threads for each of the worker, must be set to 1 for gevent
76 ## generally recommened to be at 1
76 ## generally recommened to be at 1
77 #threads = 1
77 #threads = 1
78 ## process name
78 ## process name
79 #proc_name = rhodecode
79 #proc_name = rhodecode
80 ## type of worker class, one of sync, gevent
80 ## type of worker class, one of sync, gevent
81 ## recommended for bigger setup is using of of other than sync one
81 ## recommended for bigger setup is using of of other than sync one
82 #worker_class = sync
82 #worker_class = sync
83 ## The maximum number of simultaneous clients. Valid only for Gevent
83 ## The maximum number of simultaneous clients. Valid only for Gevent
84 #worker_connections = 10
84 #worker_connections = 10
85 ## max number of requests that worker will handle before being gracefully
85 ## max number of requests that worker will handle before being gracefully
86 ## restarted, could prevent memory leaks
86 ## restarted, could prevent memory leaks
87 #max_requests = 1000
87 #max_requests = 1000
88 #max_requests_jitter = 30
88 #max_requests_jitter = 30
89 ## amount of time a worker can spend with handling a request before it
89 ## amount of time a worker can spend with handling a request before it
90 ## gets killed and restarted. Set to 6hrs
90 ## gets killed and restarted. Set to 6hrs
91 #timeout = 21600
91 #timeout = 21600
92
92
93
93
94 ## prefix middleware for RhodeCode.
94 ## prefix middleware for RhodeCode.
95 ## recommended when using proxy setup.
95 ## recommended when using proxy setup.
96 ## allows to set RhodeCode under a prefix in server.
96 ## allows to set RhodeCode under a prefix in server.
97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
98 ## And set your prefix like: `prefix = /custom_prefix`
98 ## And set your prefix like: `prefix = /custom_prefix`
99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
100 ## to make your cookies only work on prefix url
100 ## to make your cookies only work on prefix url
101 [filter:proxy-prefix]
101 [filter:proxy-prefix]
102 use = egg:PasteDeploy#prefix
102 use = egg:PasteDeploy#prefix
103 prefix = /
103 prefix = /
104
104
105 [app:main]
105 [app:main]
106 use = egg:rhodecode-enterprise-ce
106 use = egg:rhodecode-enterprise-ce
107
107
108 ## enable proxy prefix middleware, defined above
108 ## enable proxy prefix middleware, defined above
109 #filter-with = proxy-prefix
109 #filter-with = proxy-prefix
110
110
111 # During development the we want to have the debug toolbar enabled
111 # During development the we want to have the debug toolbar enabled
112 pyramid.includes =
112 pyramid.includes =
113 pyramid_debugtoolbar
113 pyramid_debugtoolbar
114 rhodecode.utils.debugtoolbar
114 rhodecode.utils.debugtoolbar
115 rhodecode.lib.middleware.request_wrapper
115 rhodecode.lib.middleware.request_wrapper
116
116
117 pyramid.reload_templates = true
117 pyramid.reload_templates = true
118
118
119 debugtoolbar.hosts = 0.0.0.0/0
119 debugtoolbar.hosts = 0.0.0.0/0
120 debugtoolbar.exclude_prefixes =
120 debugtoolbar.exclude_prefixes =
121 /css
121 /css
122 /fonts
122 /fonts
123 /images
123 /images
124 /js
124 /js
125
125
126 ## RHODECODE PLUGINS ##
126 ## RHODECODE PLUGINS ##
127 rhodecode.includes =
127 rhodecode.includes =
128 rhodecode.api
128 rhodecode.api
129
129
130
130
131 # api prefix url
131 # api prefix url
132 rhodecode.api.url = /_admin/api
132 rhodecode.api.url = /_admin/api
133
133
134
134
135 ## END RHODECODE PLUGINS ##
135 ## END RHODECODE PLUGINS ##
136
136
137 ## encryption key used to encrypt social plugin tokens,
137 ## encryption key used to encrypt social plugin tokens,
138 ## remote_urls with credentials etc, if not set it defaults to
138 ## remote_urls with credentials etc, if not set it defaults to
139 ## `beaker.session.secret`
139 ## `beaker.session.secret`
140 #rhodecode.encrypted_values.secret =
140 #rhodecode.encrypted_values.secret =
141
141
142 ## decryption strict mode (enabled by default). It controls if decryption raises
142 ## decryption strict mode (enabled by default). It controls if decryption raises
143 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
143 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
144 #rhodecode.encrypted_values.strict = false
144 #rhodecode.encrypted_values.strict = false
145
145
146 ## return gzipped responses from Rhodecode (static files/application)
146 ## return gzipped responses from Rhodecode (static files/application)
147 gzip_responses = false
147 gzip_responses = false
148
148
149 ## autogenerate javascript routes file on startup
149 ## autogenerate javascript routes file on startup
150 generate_js_files = false
150 generate_js_files = false
151
151
152 ## Optional Languages
152 ## Optional Languages
153 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
153 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
154 lang = en
154 lang = en
155
155
156 ## perform a full repository scan on each server start, this should be
156 ## perform a full repository scan on each server start, this should be
157 ## set to false after first startup, to allow faster server restarts.
157 ## set to false after first startup, to allow faster server restarts.
158 startup.import_repos = false
158 startup.import_repos = false
159
159
160 ## Uncomment and set this path to use archive download cache.
160 ## Uncomment and set this path to use archive download cache.
161 ## Once enabled, generated archives will be cached at this location
161 ## Once enabled, generated archives will be cached at this location
162 ## and served from the cache during subsequent requests for the same archive of
162 ## and served from the cache during subsequent requests for the same archive of
163 ## the repository.
163 ## the repository.
164 #archive_cache_dir = /tmp/tarballcache
164 #archive_cache_dir = /tmp/tarballcache
165
165
166 ## change this to unique ID for security
166 ## change this to unique ID for security
167 app_instance_uuid = rc-production
167 app_instance_uuid = rc-production
168
168
169 ## cut off limit for large diffs (size in bytes)
169 ## cut off limit for large diffs (size in bytes)
170 cut_off_limit_diff = 1024000
170 cut_off_limit_diff = 1024000
171 cut_off_limit_file = 256000
171 cut_off_limit_file = 256000
172
172
173 ## use cache version of scm repo everywhere
173 ## use cache version of scm repo everywhere
174 vcs_full_cache = true
174 vcs_full_cache = true
175
175
176 ## force https in RhodeCode, fixes https redirects, assumes it's always https
176 ## force https in RhodeCode, fixes https redirects, assumes it's always https
177 ## Normally this is controlled by proper http flags sent from http server
177 ## Normally this is controlled by proper http flags sent from http server
178 force_https = false
178 force_https = false
179
179
180 ## use Strict-Transport-Security headers
180 ## use Strict-Transport-Security headers
181 use_htsts = false
181 use_htsts = false
182
182
183 ## number of commits stats will parse on each iteration
183 ## number of commits stats will parse on each iteration
184 commit_parse_limit = 25
184 commit_parse_limit = 25
185
185
186 ## git rev filter option, --all is the default filter, if you need to
186 ## git rev filter option, --all is the default filter, if you need to
187 ## hide all refs in changelog switch this to --branches --tags
187 ## hide all refs in changelog switch this to --branches --tags
188 git_rev_filter = --branches --tags
188 git_rev_filter = --branches --tags
189
189
190 # Set to true if your repos are exposed using the dumb protocol
190 # Set to true if your repos are exposed using the dumb protocol
191 git_update_server_info = false
191 git_update_server_info = false
192
192
193 ## RSS/ATOM feed options
193 ## RSS/ATOM feed options
194 rss_cut_off_limit = 256000
194 rss_cut_off_limit = 256000
195 rss_items_per_page = 10
195 rss_items_per_page = 10
196 rss_include_diff = false
196 rss_include_diff = false
197
197
198 ## gist URL alias, used to create nicer urls for gist. This should be an
198 ## gist URL alias, used to create nicer urls for gist. This should be an
199 ## url that does rewrites to _admin/gists/{gistid}.
199 ## url that does rewrites to _admin/gists/{gistid}.
200 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
200 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
201 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
201 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
202 gist_alias_url =
202 gist_alias_url =
203
203
204 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
204 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
205 ## used for access.
205 ## used for access.
206 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
206 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
207 ## came from the the logged in user who own this authentication token.
207 ## came from the the logged in user who own this authentication token.
208 ##
208 ##
209 ## Syntax is ControllerClass:function_pattern.
209 ## Syntax is ControllerClass:function_pattern.
210 ## To enable access to raw_files put `FilesController:raw`.
210 ## To enable access to raw_files put `FilesController:raw`.
211 ## To enable access to patches add `ChangesetController:changeset_patch`.
211 ## To enable access to patches add `ChangesetController:changeset_patch`.
212 ## The list should be "," separated and on a single line.
212 ## The list should be "," separated and on a single line.
213 ##
213 ##
214 ## Recommended controllers to enable:
214 ## Recommended controllers to enable:
215 # ChangesetController:changeset_patch,
215 # ChangesetController:changeset_patch,
216 # ChangesetController:changeset_raw,
216 # ChangesetController:changeset_raw,
217 # FilesController:raw,
217 # FilesController:raw,
218 # FilesController:archivefile,
218 # FilesController:archivefile,
219 # GistsController:*,
219 # GistsController:*,
220 api_access_controllers_whitelist =
220 api_access_controllers_whitelist =
221
221
222 ## default encoding used to convert from and to unicode
222 ## default encoding used to convert from and to unicode
223 ## can be also a comma separated list of encoding in case of mixed encodings
223 ## can be also a comma separated list of encoding in case of mixed encodings
224 default_encoding = UTF-8
224 default_encoding = UTF-8
225
225
226 ## instance-id prefix
226 ## instance-id prefix
227 ## a prefix key for this instance used for cache invalidation when running
227 ## a prefix key for this instance used for cache invalidation when running
228 ## multiple instances of rhodecode, make sure it's globally unique for
228 ## multiple instances of rhodecode, make sure it's globally unique for
229 ## all running rhodecode instances. Leave empty if you don't use it
229 ## all running rhodecode instances. Leave empty if you don't use it
230 instance_id =
230 instance_id =
231
231
232 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
232 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
233 ## of an authentication plugin also if it is disabled by it's settings.
233 ## of an authentication plugin also if it is disabled by it's settings.
234 ## This could be useful if you are unable to log in to the system due to broken
234 ## This could be useful if you are unable to log in to the system due to broken
235 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
235 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
236 ## module to log in again and fix the settings.
236 ## module to log in again and fix the settings.
237 ##
237 ##
238 ## Available builtin plugin IDs (hash is part of the ID):
238 ## Available builtin plugin IDs (hash is part of the ID):
239 ## egg:rhodecode-enterprise-ce#rhodecode
239 ## egg:rhodecode-enterprise-ce#rhodecode
240 ## egg:rhodecode-enterprise-ce#pam
240 ## egg:rhodecode-enterprise-ce#pam
241 ## egg:rhodecode-enterprise-ce#ldap
241 ## egg:rhodecode-enterprise-ce#ldap
242 ## egg:rhodecode-enterprise-ce#jasig_cas
242 ## egg:rhodecode-enterprise-ce#jasig_cas
243 ## egg:rhodecode-enterprise-ce#headers
243 ## egg:rhodecode-enterprise-ce#headers
244 ## egg:rhodecode-enterprise-ce#crowd
244 ## egg:rhodecode-enterprise-ce#crowd
245 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
245 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
246
246
247 ## alternative return HTTP header for failed authentication. Default HTTP
247 ## alternative return HTTP header for failed authentication. Default HTTP
248 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
248 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
249 ## handling that causing a series of failed authentication calls.
249 ## handling that causing a series of failed authentication calls.
250 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
250 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
251 ## This will be served instead of default 401 on bad authnetication
251 ## This will be served instead of default 401 on bad authnetication
252 auth_ret_code =
252 auth_ret_code =
253
253
254 ## use special detection method when serving auth_ret_code, instead of serving
254 ## use special detection method when serving auth_ret_code, instead of serving
255 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
255 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
256 ## and then serve auth_ret_code to clients
256 ## and then serve auth_ret_code to clients
257 auth_ret_code_detection = false
257 auth_ret_code_detection = false
258
258
259 ## locking return code. When repository is locked return this HTTP code. 2XX
259 ## locking return code. When repository is locked return this HTTP code. 2XX
260 ## codes don't break the transactions while 4XX codes do
260 ## codes don't break the transactions while 4XX codes do
261 lock_ret_code = 423
261 lock_ret_code = 423
262
262
263 ## allows to change the repository location in settings page
263 ## allows to change the repository location in settings page
264 allow_repo_location_change = true
264 allow_repo_location_change = true
265
265
266 ## allows to setup custom hooks in settings page
266 ## allows to setup custom hooks in settings page
267 allow_custom_hooks_settings = true
267 allow_custom_hooks_settings = true
268
268
269 ## generated license token, goto license page in RhodeCode settings to obtain
269 ## generated license token, goto license page in RhodeCode settings to obtain
270 ## new token
270 ## new token
271 license_token =
271 license_token =
272
272
273 ## supervisor connection uri, for managing supervisor and logs.
273 ## supervisor connection uri, for managing supervisor and logs.
274 supervisor.uri =
274 supervisor.uri =
275 ## supervisord group name/id we only want this RC instance to handle
275 ## supervisord group name/id we only want this RC instance to handle
276 supervisor.group_id = dev
276 supervisor.group_id = dev
277
277
278 ## Display extended labs settings
278 ## Display extended labs settings
279 labs_settings_active = true
279 labs_settings_active = true
280
280
281 ####################################
281 ####################################
282 ### CELERY CONFIG ####
282 ### CELERY CONFIG ####
283 ####################################
283 ####################################
284 use_celery = false
284 use_celery = false
285 broker.host = localhost
285 broker.host = localhost
286 broker.vhost = rabbitmqhost
286 broker.vhost = rabbitmqhost
287 broker.port = 5672
287 broker.port = 5672
288 broker.user = rabbitmq
288 broker.user = rabbitmq
289 broker.password = qweqwe
289 broker.password = qweqwe
290
290
291 celery.imports = rhodecode.lib.celerylib.tasks
291 celery.imports = rhodecode.lib.celerylib.tasks
292
292
293 celery.result.backend = amqp
293 celery.result.backend = amqp
294 celery.result.dburi = amqp://
294 celery.result.dburi = amqp://
295 celery.result.serialier = json
295 celery.result.serialier = json
296
296
297 #celery.send.task.error.emails = true
297 #celery.send.task.error.emails = true
298 #celery.amqp.task.result.expires = 18000
298 #celery.amqp.task.result.expires = 18000
299
299
300 celeryd.concurrency = 2
300 celeryd.concurrency = 2
301 #celeryd.log.file = celeryd.log
301 #celeryd.log.file = celeryd.log
302 celeryd.log.level = debug
302 celeryd.log.level = debug
303 celeryd.max.tasks.per.child = 1
303 celeryd.max.tasks.per.child = 1
304
304
305 ## tasks will never be sent to the queue, but executed locally instead.
305 ## tasks will never be sent to the queue, but executed locally instead.
306 celery.always.eager = false
306 celery.always.eager = false
307
307
308 ####################################
308 ####################################
309 ### BEAKER CACHE ####
309 ### BEAKER CACHE ####
310 ####################################
310 ####################################
311 # default cache dir for templates. Putting this into a ramdisk
311 # default cache dir for templates. Putting this into a ramdisk
312 ## can boost performance, eg. %(here)s/data_ramdisk
312 ## can boost performance, eg. %(here)s/data_ramdisk
313 cache_dir = %(here)s/data
313 cache_dir = %(here)s/data
314
314
315 ## locking and default file storage for Beaker. Putting this into a ramdisk
315 ## locking and default file storage for Beaker. Putting this into a ramdisk
316 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
316 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
317 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
317 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
318 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
318 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
319
319
320 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
320 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
321
321
322 beaker.cache.super_short_term.type = memory
322 beaker.cache.super_short_term.type = memory
323 beaker.cache.super_short_term.expire = 10
323 beaker.cache.super_short_term.expire = 10
324 beaker.cache.super_short_term.key_length = 256
324 beaker.cache.super_short_term.key_length = 256
325
325
326 beaker.cache.short_term.type = memory
326 beaker.cache.short_term.type = memory
327 beaker.cache.short_term.expire = 60
327 beaker.cache.short_term.expire = 60
328 beaker.cache.short_term.key_length = 256
328 beaker.cache.short_term.key_length = 256
329
329
330 beaker.cache.long_term.type = memory
330 beaker.cache.long_term.type = memory
331 beaker.cache.long_term.expire = 36000
331 beaker.cache.long_term.expire = 36000
332 beaker.cache.long_term.key_length = 256
332 beaker.cache.long_term.key_length = 256
333
333
334 beaker.cache.sql_cache_short.type = memory
334 beaker.cache.sql_cache_short.type = memory
335 beaker.cache.sql_cache_short.expire = 10
335 beaker.cache.sql_cache_short.expire = 10
336 beaker.cache.sql_cache_short.key_length = 256
336 beaker.cache.sql_cache_short.key_length = 256
337
337
338 ## default is memory cache, configure only if required
338 ## default is memory cache, configure only if required
339 ## using multi-node or multi-worker setup
339 ## using multi-node or multi-worker setup
340 #beaker.cache.auth_plugins.type = ext:database
340 #beaker.cache.auth_plugins.type = ext:database
341 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
341 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
342 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
342 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
343 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
343 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
344 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
344 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
345 #beaker.cache.auth_plugins.sa.pool_size = 10
345 #beaker.cache.auth_plugins.sa.pool_size = 10
346 #beaker.cache.auth_plugins.sa.max_overflow = 0
346 #beaker.cache.auth_plugins.sa.max_overflow = 0
347
347
348 beaker.cache.repo_cache_long.type = memorylru_base
348 beaker.cache.repo_cache_long.type = memorylru_base
349 beaker.cache.repo_cache_long.max_items = 4096
349 beaker.cache.repo_cache_long.max_items = 4096
350 beaker.cache.repo_cache_long.expire = 2592000
350 beaker.cache.repo_cache_long.expire = 2592000
351
351
352 ## default is memorylru_base cache, configure only if required
352 ## default is memorylru_base cache, configure only if required
353 ## using multi-node or multi-worker setup
353 ## using multi-node or multi-worker setup
354 #beaker.cache.repo_cache_long.type = ext:memcached
354 #beaker.cache.repo_cache_long.type = ext:memcached
355 #beaker.cache.repo_cache_long.url = localhost:11211
355 #beaker.cache.repo_cache_long.url = localhost:11211
356 #beaker.cache.repo_cache_long.expire = 1209600
356 #beaker.cache.repo_cache_long.expire = 1209600
357 #beaker.cache.repo_cache_long.key_length = 256
357 #beaker.cache.repo_cache_long.key_length = 256
358
358
359 ####################################
359 ####################################
360 ### BEAKER SESSION ####
360 ### BEAKER SESSION ####
361 ####################################
361 ####################################
362
362
363 ## .session.type is type of storage options for the session, current allowed
363 ## .session.type is type of storage options for the session, current allowed
364 ## types are file, ext:memcached, ext:database, and memory (default).
364 ## types are file, ext:memcached, ext:database, and memory (default).
365 beaker.session.type = file
365 beaker.session.type = file
366 beaker.session.data_dir = %(here)s/data/sessions/data
366 beaker.session.data_dir = %(here)s/data/sessions/data
367
367
368 ## db based session, fast, and allows easy management over logged in users
368 ## db based session, fast, and allows easy management over logged in users
369 #beaker.session.type = ext:database
369 #beaker.session.type = ext:database
370 #beaker.session.table_name = db_session
370 #beaker.session.table_name = db_session
371 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
371 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
372 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
372 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
373 #beaker.session.sa.pool_recycle = 3600
373 #beaker.session.sa.pool_recycle = 3600
374 #beaker.session.sa.echo = false
374 #beaker.session.sa.echo = false
375
375
376 beaker.session.key = rhodecode
376 beaker.session.key = rhodecode
377 beaker.session.secret = develop-rc-uytcxaz
377 beaker.session.secret = develop-rc-uytcxaz
378 beaker.session.lock_dir = %(here)s/data/sessions/lock
378 beaker.session.lock_dir = %(here)s/data/sessions/lock
379
379
380 ## Secure encrypted cookie. Requires AES and AES python libraries
380 ## Secure encrypted cookie. Requires AES and AES python libraries
381 ## you must disable beaker.session.secret to use this
381 ## you must disable beaker.session.secret to use this
382 #beaker.session.encrypt_key = key_for_encryption
382 #beaker.session.encrypt_key = key_for_encryption
383 #beaker.session.validate_key = validation_key
383 #beaker.session.validate_key = validation_key
384
384
385 ## sets session as invalid(also logging out user) if it haven not been
385 ## sets session as invalid(also logging out user) if it haven not been
386 ## accessed for given amount of time in seconds
386 ## accessed for given amount of time in seconds
387 beaker.session.timeout = 2592000
387 beaker.session.timeout = 2592000
388 beaker.session.httponly = true
388 beaker.session.httponly = true
389 ## Path to use for the cookie. Set to prefix if you use prefix middleware
389 ## Path to use for the cookie. Set to prefix if you use prefix middleware
390 #beaker.session.cookie_path = /custom_prefix
390 #beaker.session.cookie_path = /custom_prefix
391
391
392 ## uncomment for https secure cookie
392 ## uncomment for https secure cookie
393 beaker.session.secure = false
393 beaker.session.secure = false
394
394
395 ## auto save the session to not to use .save()
395 ## auto save the session to not to use .save()
396 beaker.session.auto = false
396 beaker.session.auto = false
397
397
398 ## default cookie expiration time in seconds, set to `true` to set expire
398 ## default cookie expiration time in seconds, set to `true` to set expire
399 ## at browser close
399 ## at browser close
400 #beaker.session.cookie_expires = 3600
400 #beaker.session.cookie_expires = 3600
401
401
402 ###################################
402 ###################################
403 ## SEARCH INDEXING CONFIGURATION ##
403 ## SEARCH INDEXING CONFIGURATION ##
404 ###################################
404 ###################################
405 ## Full text search indexer is available in rhodecode-tools under
405 ## Full text search indexer is available in rhodecode-tools under
406 ## `rhodecode-tools index` command
406 ## `rhodecode-tools index` command
407
407
408 ## WHOOSH Backend, doesn't require additional services to run
408 ## WHOOSH Backend, doesn't require additional services to run
409 ## it works good with few dozen repos
409 ## it works good with few dozen repos
410 search.module = rhodecode.lib.index.whoosh
410 search.module = rhodecode.lib.index.whoosh
411 search.location = %(here)s/data/index
411 search.location = %(here)s/data/index
412
412
413 ########################################
413 ########################################
414 ### CHANNELSTREAM CONFIG ####
414 ### CHANNELSTREAM CONFIG ####
415 ########################################
415 ########################################
416 ## channelstream enables persistent connections and live notification
416 ## channelstream enables persistent connections and live notification
417 ## in the system. It's also used by the chat system
417 ## in the system. It's also used by the chat system
418 channelstream.enabled = false
418 channelstream.enabled = false
419
419
420 ## server address for channelstream server on the backend
420 ## server address for channelstream server on the backend
421 channelstream.server = 127.0.0.1:9800
421 channelstream.server = 127.0.0.1:9800
422
422
423 ## location of the channelstream server from outside world
423 ## location of the channelstream server from outside world
424 ## use ws:// for http or wss:// for https. This address needs to be handled
424 ## use ws:// for http or wss:// for https. This address needs to be handled
425 ## by external HTTP server such as Nginx or Apache
425 ## by external HTTP server such as Nginx or Apache
426 ## see nginx/apache configuration examples in our docs
426 ## see nginx/apache configuration examples in our docs
427 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
427 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
428 channelstream.secret = secret
428 channelstream.secret = secret
429 channelstream.history.location = %(here)s/channelstream_history
429 channelstream.history.location = %(here)s/channelstream_history
430
430
431 ## Internal application path that Javascript uses to connect into.
431 ## Internal application path that Javascript uses to connect into.
432 ## If you use proxy-prefix the prefix should be added before /_channelstream
432 ## If you use proxy-prefix the prefix should be added before /_channelstream
433 channelstream.proxy_path = /_channelstream
433 channelstream.proxy_path = /_channelstream
434
434
435
435
436 ###################################
436 ###################################
437 ## APPENLIGHT CONFIG ##
437 ## APPENLIGHT CONFIG ##
438 ###################################
438 ###################################
439
439
440 ## Appenlight is tailored to work with RhodeCode, see
440 ## Appenlight is tailored to work with RhodeCode, see
441 ## http://appenlight.com for details how to obtain an account
441 ## http://appenlight.com for details how to obtain an account
442
442
443 ## appenlight integration enabled
443 ## appenlight integration enabled
444 appenlight = false
444 appenlight = false
445
445
446 appenlight.server_url = https://api.appenlight.com
446 appenlight.server_url = https://api.appenlight.com
447 appenlight.api_key = YOUR_API_KEY
447 appenlight.api_key = YOUR_API_KEY
448 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
448 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
449
449
450 # used for JS client
450 # used for JS client
451 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
451 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
452
452
453 ## TWEAK AMOUNT OF INFO SENT HERE
453 ## TWEAK AMOUNT OF INFO SENT HERE
454
454
455 ## enables 404 error logging (default False)
455 ## enables 404 error logging (default False)
456 appenlight.report_404 = false
456 appenlight.report_404 = false
457
457
458 ## time in seconds after request is considered being slow (default 1)
458 ## time in seconds after request is considered being slow (default 1)
459 appenlight.slow_request_time = 1
459 appenlight.slow_request_time = 1
460
460
461 ## record slow requests in application
461 ## record slow requests in application
462 ## (needs to be enabled for slow datastore recording and time tracking)
462 ## (needs to be enabled for slow datastore recording and time tracking)
463 appenlight.slow_requests = true
463 appenlight.slow_requests = true
464
464
465 ## enable hooking to application loggers
465 ## enable hooking to application loggers
466 appenlight.logging = true
466 appenlight.logging = true
467
467
468 ## minimum log level for log capture
468 ## minimum log level for log capture
469 appenlight.logging.level = WARNING
469 appenlight.logging.level = WARNING
470
470
471 ## send logs only from erroneous/slow requests
471 ## send logs only from erroneous/slow requests
472 ## (saves API quota for intensive logging)
472 ## (saves API quota for intensive logging)
473 appenlight.logging_on_error = false
473 appenlight.logging_on_error = false
474
474
475 ## list of additonal keywords that should be grabbed from environ object
475 ## list of additonal keywords that should be grabbed from environ object
476 ## can be string with comma separated list of words in lowercase
476 ## can be string with comma separated list of words in lowercase
477 ## (by default client will always send following info:
477 ## (by default client will always send following info:
478 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
478 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
479 ## start with HTTP* this list be extended with additional keywords here
479 ## start with HTTP* this list be extended with additional keywords here
480 appenlight.environ_keys_whitelist =
480 appenlight.environ_keys_whitelist =
481
481
482 ## list of keywords that should be blanked from request object
482 ## list of keywords that should be blanked from request object
483 ## can be string with comma separated list of words in lowercase
483 ## can be string with comma separated list of words in lowercase
484 ## (by default client will always blank keys that contain following words
484 ## (by default client will always blank keys that contain following words
485 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
485 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
486 ## this list be extended with additional keywords set here
486 ## this list be extended with additional keywords set here
487 appenlight.request_keys_blacklist =
487 appenlight.request_keys_blacklist =
488
488
489 ## list of namespaces that should be ignores when gathering log entries
489 ## list of namespaces that should be ignores when gathering log entries
490 ## can be string with comma separated list of namespaces
490 ## can be string with comma separated list of namespaces
491 ## (by default the client ignores own entries: appenlight_client.client)
491 ## (by default the client ignores own entries: appenlight_client.client)
492 appenlight.log_namespace_blacklist =
492 appenlight.log_namespace_blacklist =
493
493
494
494
495 ################################################################################
495 ################################################################################
496 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
496 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
497 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
497 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
498 ## execute malicious code after an exception is raised. ##
498 ## execute malicious code after an exception is raised. ##
499 ################################################################################
499 ################################################################################
500 #set debug = false
500 #set debug = false
501
501
502
502
503 ##############
503 ##############
504 ## STYLING ##
504 ## STYLING ##
505 ##############
505 ##############
506 debug_style = true
506 debug_style = true
507
507
508 ###########################################
508 ###########################################
509 ### MAIN RHODECODE DATABASE CONFIG ###
509 ### MAIN RHODECODE DATABASE CONFIG ###
510 ###########################################
510 ###########################################
511 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
511 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
512 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
512 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
513 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
513 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
514 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
514 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
515
515
516 # see sqlalchemy docs for other advanced settings
516 # see sqlalchemy docs for other advanced settings
517
517
518 ## print the sql statements to output
518 ## print the sql statements to output
519 sqlalchemy.db1.echo = false
519 sqlalchemy.db1.echo = false
520 ## recycle the connections after this amount of seconds
520 ## recycle the connections after this amount of seconds
521 sqlalchemy.db1.pool_recycle = 3600
521 sqlalchemy.db1.pool_recycle = 3600
522 sqlalchemy.db1.convert_unicode = true
522 sqlalchemy.db1.convert_unicode = true
523
523
524 ## the number of connections to keep open inside the connection pool.
524 ## the number of connections to keep open inside the connection pool.
525 ## 0 indicates no limit
525 ## 0 indicates no limit
526 #sqlalchemy.db1.pool_size = 5
526 #sqlalchemy.db1.pool_size = 5
527
527
528 ## the number of connections to allow in connection pool "overflow", that is
528 ## the number of connections to allow in connection pool "overflow", that is
529 ## connections that can be opened above and beyond the pool_size setting,
529 ## connections that can be opened above and beyond the pool_size setting,
530 ## which defaults to five.
530 ## which defaults to five.
531 #sqlalchemy.db1.max_overflow = 10
531 #sqlalchemy.db1.max_overflow = 10
532
532
533
533
534 ##################
534 ##################
535 ### VCS CONFIG ###
535 ### VCS CONFIG ###
536 ##################
536 ##################
537 vcs.server.enable = true
537 vcs.server.enable = true
538 vcs.server = localhost:9900
538 vcs.server = localhost:9900
539
539
540 ## Web server connectivity protocol, responsible for web based VCS operatations
540 ## Web server connectivity protocol, responsible for web based VCS operatations
541 ## Available protocols are:
541 ## Available protocols are:
542 ## `pyro4` - use pyro4 server
543 ## `http` - use http-rpc backend (default)
542 ## `http` - use http-rpc backend (default)
544 vcs.server.protocol = http
543 vcs.server.protocol = http
545
544
546 ## Push/Pull operations protocol, available options are:
545 ## Push/Pull operations protocol, available options are:
547 ## `pyro4` - use pyro4 server
548 ## `http` - use http-rpc backend (default)
546 ## `http` - use http-rpc backend (default)
549 ##
547 ##
550 vcs.scm_app_implementation = http
548 vcs.scm_app_implementation = http
551
549
552 ## Push/Pull operations hooks protocol, available options are:
550 ## Push/Pull operations hooks protocol, available options are:
553 ## `pyro4` - use pyro4 server
554 ## `http` - use http-rpc backend (default)
551 ## `http` - use http-rpc backend (default)
555 vcs.hooks.protocol = http
552 vcs.hooks.protocol = http
556
553
557 vcs.server.log_level = debug
554 vcs.server.log_level = debug
558 ## Start VCSServer with this instance as a subprocess, usefull for development
555 ## Start VCSServer with this instance as a subprocess, usefull for development
559 vcs.start_server = true
556 vcs.start_server = true
560
557
561 ## List of enabled VCS backends, available options are:
558 ## List of enabled VCS backends, available options are:
562 ## `hg` - mercurial
559 ## `hg` - mercurial
563 ## `git` - git
560 ## `git` - git
564 ## `svn` - subversion
561 ## `svn` - subversion
565 vcs.backends = hg, git, svn
562 vcs.backends = hg, git, svn
566
563
567 vcs.connection_timeout = 3600
564 vcs.connection_timeout = 3600
568 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
565 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
569 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
566 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
570 #vcs.svn.compatible_version = pre-1.8-compatible
567 #vcs.svn.compatible_version = pre-1.8-compatible
571
568
572
569
573 ############################################################
570 ############################################################
574 ### Subversion proxy support (mod_dav_svn) ###
571 ### Subversion proxy support (mod_dav_svn) ###
575 ### Maps RhodeCode repo groups into SVN paths for Apache ###
572 ### Maps RhodeCode repo groups into SVN paths for Apache ###
576 ############################################################
573 ############################################################
577 ## Enable or disable the config file generation.
574 ## Enable or disable the config file generation.
578 svn.proxy.generate_config = false
575 svn.proxy.generate_config = false
579 ## Generate config file with `SVNListParentPath` set to `On`.
576 ## Generate config file with `SVNListParentPath` set to `On`.
580 svn.proxy.list_parent_path = true
577 svn.proxy.list_parent_path = true
581 ## Set location and file name of generated config file.
578 ## Set location and file name of generated config file.
582 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
579 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
583 ## Used as a prefix to the `Location` block in the generated config file.
580 ## Used as a prefix to the `Location` block in the generated config file.
584 ## In most cases it should be set to `/`.
581 ## In most cases it should be set to `/`.
585 svn.proxy.location_root = /
582 svn.proxy.location_root = /
586 ## Command to reload the mod dav svn configuration on change.
583 ## Command to reload the mod dav svn configuration on change.
587 ## Example: `/etc/init.d/apache2 reload`
584 ## Example: `/etc/init.d/apache2 reload`
588 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
585 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
589 ## If the timeout expires before the reload command finishes, the command will
586 ## If the timeout expires before the reload command finishes, the command will
590 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
587 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
591 #svn.proxy.reload_timeout = 10
588 #svn.proxy.reload_timeout = 10
592
589
593 ## Dummy marker to add new entries after.
590 ## Dummy marker to add new entries after.
594 ## Add any custom entries below. Please don't remove.
591 ## Add any custom entries below. Please don't remove.
595 custom.conf = 1
592 custom.conf = 1
596
593
597
594
598 ################################
595 ################################
599 ### LOGGING CONFIGURATION ####
596 ### LOGGING CONFIGURATION ####
600 ################################
597 ################################
601 [loggers]
598 [loggers]
602 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
599 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
603
600
604 [handlers]
601 [handlers]
605 keys = console, console_sql
602 keys = console, console_sql
606
603
607 [formatters]
604 [formatters]
608 keys = generic, color_formatter, color_formatter_sql
605 keys = generic, color_formatter, color_formatter_sql
609
606
610 #############
607 #############
611 ## LOGGERS ##
608 ## LOGGERS ##
612 #############
609 #############
613 [logger_root]
610 [logger_root]
614 level = NOTSET
611 level = NOTSET
615 handlers = console
612 handlers = console
616
613
617 [logger_routes]
614 [logger_routes]
618 level = DEBUG
615 level = DEBUG
619 handlers =
616 handlers =
620 qualname = routes.middleware
617 qualname = routes.middleware
621 ## "level = DEBUG" logs the route matched and routing variables.
618 ## "level = DEBUG" logs the route matched and routing variables.
622 propagate = 1
619 propagate = 1
623
620
624 [logger_beaker]
621 [logger_beaker]
625 level = DEBUG
622 level = DEBUG
626 handlers =
623 handlers =
627 qualname = beaker.container
624 qualname = beaker.container
628 propagate = 1
625 propagate = 1
629
626
630 [logger_templates]
627 [logger_templates]
631 level = INFO
628 level = INFO
632 handlers =
629 handlers =
633 qualname = pylons.templating
630 qualname = pylons.templating
634 propagate = 1
631 propagate = 1
635
632
636 [logger_rhodecode]
633 [logger_rhodecode]
637 level = DEBUG
634 level = DEBUG
638 handlers =
635 handlers =
639 qualname = rhodecode
636 qualname = rhodecode
640 propagate = 1
637 propagate = 1
641
638
642 [logger_sqlalchemy]
639 [logger_sqlalchemy]
643 level = INFO
640 level = INFO
644 handlers = console_sql
641 handlers = console_sql
645 qualname = sqlalchemy.engine
642 qualname = sqlalchemy.engine
646 propagate = 0
643 propagate = 0
647
644
648 ##############
645 ##############
649 ## HANDLERS ##
646 ## HANDLERS ##
650 ##############
647 ##############
651
648
652 [handler_console]
649 [handler_console]
653 class = StreamHandler
650 class = StreamHandler
654 args = (sys.stderr, )
651 args = (sys.stderr, )
655 level = DEBUG
652 level = DEBUG
656 formatter = color_formatter
653 formatter = color_formatter
657
654
658 [handler_console_sql]
655 [handler_console_sql]
659 class = StreamHandler
656 class = StreamHandler
660 args = (sys.stderr, )
657 args = (sys.stderr, )
661 level = DEBUG
658 level = DEBUG
662 formatter = color_formatter_sql
659 formatter = color_formatter_sql
663
660
664 ################
661 ################
665 ## FORMATTERS ##
662 ## FORMATTERS ##
666 ################
663 ################
667
664
668 [formatter_generic]
665 [formatter_generic]
669 class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter
666 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
670 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
667 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
671 datefmt = %Y-%m-%d %H:%M:%S
668 datefmt = %Y-%m-%d %H:%M:%S
672
669
673 [formatter_color_formatter]
670 [formatter_color_formatter]
674 class = rhodecode.lib.logging_formatter.ColorFormatter
671 class = rhodecode.lib.logging_formatter.ColorFormatter
675 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
672 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
676 datefmt = %Y-%m-%d %H:%M:%S
673 datefmt = %Y-%m-%d %H:%M:%S
677
674
678 [formatter_color_formatter_sql]
675 [formatter_color_formatter_sql]
679 class = rhodecode.lib.logging_formatter.ColorFormatterSql
676 class = rhodecode.lib.logging_formatter.ColorFormatterSql
680 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
677 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
681 datefmt = %Y-%m-%d %H:%M:%S
678 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,90 +1,90 b''
1 """
1 """
2 gunicorn config extension and hooks. Sets additional configuration that is
2 gunicorn config extension and hooks. Sets additional configuration that is
3 available post the .ini config.
3 available post the .ini config.
4
4
5 - workers = ${cpu_number}
5 - workers = ${cpu_number}
6 - threads = 1
6 - threads = 1
7 - proc_name = ${gunicorn_proc_name}
7 - proc_name = ${gunicorn_proc_name}
8 - worker_class = sync
8 - worker_class = sync
9 - worker_connections = 10
9 - worker_connections = 10
10 - max_requests = 1000
10 - max_requests = 1000
11 - max_requests_jitter = 30
11 - max_requests_jitter = 30
12 - timeout = 21600
12 - timeout = 21600
13
13
14 """
14 """
15
15
16 import multiprocessing
16 import multiprocessing
17 import sys
17 import sys
18 import threading
18 import threading
19 import traceback
19 import traceback
20
20
21
21
22 # GLOBAL
22 # GLOBAL
23 errorlog = '-'
23 errorlog = '-'
24 accesslog = '-'
24 accesslog = '-'
25 loglevel = 'debug'
25 loglevel = 'debug'
26
26
27 # SECURITY
27 # SECURITY
28 limit_request_line = 4094
28 limit_request_line = 4094
29 limit_request_fields = 100
29 limit_request_fields = 100
30 limit_request_field_size = 8190
30 limit_request_field_size = 8190
31
31
32 # SERVER MECHANICS
32 # SERVER MECHANICS
33 # None == system temp dir
33 # None == system temp dir
34 worker_tmp_dir = None
34 worker_tmp_dir = None
35 tmp_upload_dir = None
35 tmp_upload_dir = None
36
36
37 # Custom log format
37 # Custom log format
38 access_log_format = (
38 access_log_format = (
39 '%(t)s GNCRN %(p)-8s %(h)-15s rqt:%(L)s %(s)s %(b)s "%(m)s:%(U)s %(q)s" usr:%(u)s "%(f)s" "%(a)s"')
39 '%(t)s GNCRN %(p)-8s %(h)-15s rqt:%(L)s %(s)s %(b)-6s "%(m)s:%(U)s %(q)s" usr:%(u)s "%(f)s" "%(a)s"')
40
40
41 # self adjust workers based on CPU count
41 # self adjust workers based on CPU count
42 # workers = multiprocessing.cpu_count() * 2 + 1
42 # workers = multiprocessing.cpu_count() * 2 + 1
43
43
44
44
45 def post_fork(server, worker):
45 def post_fork(server, worker):
46 server.log.info("[<%-10s>] WORKER spawned", worker.pid)
46 server.log.info("[<%-10s>] WORKER spawned", worker.pid)
47
47
48
48
49 def pre_fork(server, worker):
49 def pre_fork(server, worker):
50 pass
50 pass
51
51
52
52
53 def pre_exec(server):
53 def pre_exec(server):
54 server.log.info("Forked child, re-executing.")
54 server.log.info("Forked child, re-executing.")
55
55
56
56
57 def when_ready(server):
57 def when_ready(server):
58 server.log.info("Server is ready. Spawning workers")
58 server.log.info("Server is ready. Spawning workers")
59
59
60
60
61 def worker_int(worker):
61 def worker_int(worker):
62 worker.log.info("[<%-10s>] worker received INT or QUIT signal", worker.pid)
62 worker.log.info("[<%-10s>] worker received INT or QUIT signal", worker.pid)
63
63
64 # get traceback info, on worker crash
64 # get traceback info, on worker crash
65 id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
65 id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
66 code = []
66 code = []
67 for thread_id, stack in sys._current_frames().items():
67 for thread_id, stack in sys._current_frames().items():
68 code.append(
68 code.append(
69 "\n# Thread: %s(%d)" % (id2name.get(thread_id, ""), thread_id))
69 "\n# Thread: %s(%d)" % (id2name.get(thread_id, ""), thread_id))
70 for fname, lineno, name, line in traceback.extract_stack(stack):
70 for fname, lineno, name, line in traceback.extract_stack(stack):
71 code.append('File: "%s", line %d, in %s' % (fname, lineno, name))
71 code.append('File: "%s", line %d, in %s' % (fname, lineno, name))
72 if line:
72 if line:
73 code.append(" %s" % (line.strip()))
73 code.append(" %s" % (line.strip()))
74 worker.log.debug("\n".join(code))
74 worker.log.debug("\n".join(code))
75
75
76
76
77 def worker_abort(worker):
77 def worker_abort(worker):
78 worker.log.info("[<%-10s>] worker received SIGABRT signal", worker.pid)
78 worker.log.info("[<%-10s>] worker received SIGABRT signal", worker.pid)
79
79
80
80
81 def pre_request(worker, req):
81 def pre_request(worker, req):
82 return
82 return
83 worker.log.debug("[<%-10s>] PRE WORKER: %s %s",
83 worker.log.debug("[<%-10s>] PRE WORKER: %s %s",
84 worker.pid, req.method, req.path)
84 worker.pid, req.method, req.path)
85
85
86
86
87 def post_request(worker, req, environ, resp):
87 def post_request(worker, req, environ, resp):
88 return
88 return
89 worker.log.debug("[<%-10s>] POST WORKER: %s %s resp: %s", worker.pid,
89 worker.log.debug("[<%-10s>] POST WORKER: %s %s resp: %s", worker.pid,
90 req.method, req.path, resp.status_code) No newline at end of file
90 req.method, req.path, resp.status_code)
@@ -1,650 +1,647 b''
1
1
2
2
3 ################################################################################
3 ################################################################################
4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10
10
11 ################################################################################
11 ################################################################################
12 ## EMAIL CONFIGURATION ##
12 ## EMAIL CONFIGURATION ##
13 ## Uncomment and replace with the email address which should receive ##
13 ## Uncomment and replace with the email address which should receive ##
14 ## any error reports after an application crash ##
14 ## any error reports after an application crash ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 ################################################################################
16 ################################################################################
17
17
18 ## prefix all emails subjects with given prefix, helps filtering out emails
18 ## prefix all emails subjects with given prefix, helps filtering out emails
19 #email_prefix = [RhodeCode]
19 #email_prefix = [RhodeCode]
20
20
21 ## email FROM address all mails will be sent
21 ## email FROM address all mails will be sent
22 #app_email_from = rhodecode-noreply@localhost
22 #app_email_from = rhodecode-noreply@localhost
23
23
24 ## Uncomment and replace with the address which should receive any error report
24 ## Uncomment and replace with the address which should receive any error report
25 ## note: using appenlight for error handling doesn't need this to be uncommented
25 ## note: using appenlight for error handling doesn't need this to be uncommented
26 #email_to = admin@localhost
26 #email_to = admin@localhost
27
27
28 ## in case of Application errors, sent an error email form
28 ## in case of Application errors, sent an error email form
29 #error_email_from = rhodecode_error@localhost
29 #error_email_from = rhodecode_error@localhost
30
30
31 ## additional error message to be send in case of server crash
31 ## additional error message to be send in case of server crash
32 #error_message =
32 #error_message =
33
33
34
34
35 #smtp_server = mail.server.com
35 #smtp_server = mail.server.com
36 #smtp_username =
36 #smtp_username =
37 #smtp_password =
37 #smtp_password =
38 #smtp_port =
38 #smtp_port =
39 #smtp_use_tls = false
39 #smtp_use_tls = false
40 #smtp_use_ssl = true
40 #smtp_use_ssl = true
41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
42 #smtp_auth =
42 #smtp_auth =
43
43
44 [server:main]
44 [server:main]
45 ## COMMON ##
45 ## COMMON ##
46 host = 127.0.0.1
46 host = 127.0.0.1
47 port = 5000
47 port = 5000
48
48
49 ##################################
49 ##################################
50 ## WAITRESS WSGI SERVER ##
50 ## WAITRESS WSGI SERVER ##
51 ## Recommended for Development ##
51 ## Recommended for Development ##
52 ##################################
52 ##################################
53
53
54 #use = egg:waitress#main
54 #use = egg:waitress#main
55 ## number of worker threads
55 ## number of worker threads
56 #threads = 5
56 #threads = 5
57 ## MAX BODY SIZE 100GB
57 ## MAX BODY SIZE 100GB
58 #max_request_body_size = 107374182400
58 #max_request_body_size = 107374182400
59 ## Use poll instead of select, fixes file descriptors limits problems.
59 ## Use poll instead of select, fixes file descriptors limits problems.
60 ## May not work on old windows systems.
60 ## May not work on old windows systems.
61 #asyncore_use_poll = true
61 #asyncore_use_poll = true
62
62
63
63
64 ##########################
64 ##########################
65 ## GUNICORN WSGI SERVER ##
65 ## GUNICORN WSGI SERVER ##
66 ##########################
66 ##########################
67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
68
68
69 use = egg:gunicorn#main
69 use = egg:gunicorn#main
70 ## Sets the number of process workers. You must set `instance_id = *`
70 ## Sets the number of process workers. You must set `instance_id = *`
71 ## when this option is set to more than one worker, recommended
71 ## when this option is set to more than one worker, recommended
72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
73 ## The `instance_id = *` must be set in the [app:main] section below
73 ## The `instance_id = *` must be set in the [app:main] section below
74 workers = 2
74 workers = 2
75 ## number of threads for each of the worker, must be set to 1 for gevent
75 ## number of threads for each of the worker, must be set to 1 for gevent
76 ## generally recommened to be at 1
76 ## generally recommened to be at 1
77 #threads = 1
77 #threads = 1
78 ## process name
78 ## process name
79 proc_name = rhodecode
79 proc_name = rhodecode
80 ## type of worker class, one of sync, gevent
80 ## type of worker class, one of sync, gevent
81 ## recommended for bigger setup is using of of other than sync one
81 ## recommended for bigger setup is using of of other than sync one
82 worker_class = sync
82 worker_class = sync
83 ## The maximum number of simultaneous clients. Valid only for Gevent
83 ## The maximum number of simultaneous clients. Valid only for Gevent
84 #worker_connections = 10
84 #worker_connections = 10
85 ## max number of requests that worker will handle before being gracefully
85 ## max number of requests that worker will handle before being gracefully
86 ## restarted, could prevent memory leaks
86 ## restarted, could prevent memory leaks
87 max_requests = 1000
87 max_requests = 1000
88 max_requests_jitter = 30
88 max_requests_jitter = 30
89 ## amount of time a worker can spend with handling a request before it
89 ## amount of time a worker can spend with handling a request before it
90 ## gets killed and restarted. Set to 6hrs
90 ## gets killed and restarted. Set to 6hrs
91 timeout = 21600
91 timeout = 21600
92
92
93
93
94 ## prefix middleware for RhodeCode.
94 ## prefix middleware for RhodeCode.
95 ## recommended when using proxy setup.
95 ## recommended when using proxy setup.
96 ## allows to set RhodeCode under a prefix in server.
96 ## allows to set RhodeCode under a prefix in server.
97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
98 ## And set your prefix like: `prefix = /custom_prefix`
98 ## And set your prefix like: `prefix = /custom_prefix`
99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
100 ## to make your cookies only work on prefix url
100 ## to make your cookies only work on prefix url
101 [filter:proxy-prefix]
101 [filter:proxy-prefix]
102 use = egg:PasteDeploy#prefix
102 use = egg:PasteDeploy#prefix
103 prefix = /
103 prefix = /
104
104
105 [app:main]
105 [app:main]
106 use = egg:rhodecode-enterprise-ce
106 use = egg:rhodecode-enterprise-ce
107
107
108 ## enable proxy prefix middleware, defined above
108 ## enable proxy prefix middleware, defined above
109 #filter-with = proxy-prefix
109 #filter-with = proxy-prefix
110
110
111 ## encryption key used to encrypt social plugin tokens,
111 ## encryption key used to encrypt social plugin tokens,
112 ## remote_urls with credentials etc, if not set it defaults to
112 ## remote_urls with credentials etc, if not set it defaults to
113 ## `beaker.session.secret`
113 ## `beaker.session.secret`
114 #rhodecode.encrypted_values.secret =
114 #rhodecode.encrypted_values.secret =
115
115
116 ## decryption strict mode (enabled by default). It controls if decryption raises
116 ## decryption strict mode (enabled by default). It controls if decryption raises
117 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
117 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
118 #rhodecode.encrypted_values.strict = false
118 #rhodecode.encrypted_values.strict = false
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 ## autogenerate javascript routes file on startup
123 ## autogenerate javascript routes file on startup
124 generate_js_files = false
124 generate_js_files = false
125
125
126 ## Optional Languages
126 ## Optional Languages
127 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
127 ## 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 on each server start, this should be
130 ## perform a full repository scan on each server start, this should be
131 ## set to false after first startup, to allow faster server restarts.
131 ## set to false after first startup, to allow faster server restarts.
132 startup.import_repos = false
132 startup.import_repos = false
133
133
134 ## Uncomment and set this path to use archive download cache.
134 ## Uncomment and set this path to use archive download cache.
135 ## Once enabled, generated archives will be cached at this location
135 ## Once enabled, generated archives will be cached at this location
136 ## and served from the cache during subsequent requests for the same archive of
136 ## and served from the cache during subsequent requests for the same archive of
137 ## the repository.
137 ## the repository.
138 #archive_cache_dir = /tmp/tarballcache
138 #archive_cache_dir = /tmp/tarballcache
139
139
140 ## change this to unique ID for security
140 ## change this to unique ID for security
141 app_instance_uuid = rc-production
141 app_instance_uuid = rc-production
142
142
143 ## cut off limit for large diffs (size in bytes)
143 ## cut off limit for large diffs (size in bytes)
144 cut_off_limit_diff = 1024000
144 cut_off_limit_diff = 1024000
145 cut_off_limit_file = 256000
145 cut_off_limit_file = 256000
146
146
147 ## use cache version of scm repo everywhere
147 ## use cache version of scm repo everywhere
148 vcs_full_cache = true
148 vcs_full_cache = true
149
149
150 ## force https in RhodeCode, fixes https redirects, assumes it's always https
150 ## force https in RhodeCode, fixes https redirects, assumes it's always https
151 ## Normally this is controlled by proper http flags sent from http server
151 ## Normally this is controlled by proper http flags sent from http server
152 force_https = false
152 force_https = false
153
153
154 ## use Strict-Transport-Security headers
154 ## use Strict-Transport-Security headers
155 use_htsts = false
155 use_htsts = false
156
156
157 ## number of commits stats will parse on each iteration
157 ## number of commits stats will parse on each iteration
158 commit_parse_limit = 25
158 commit_parse_limit = 25
159
159
160 ## git rev filter option, --all is the default filter, if you need to
160 ## git rev filter option, --all is the default filter, if you need to
161 ## hide all refs in changelog switch this to --branches --tags
161 ## hide all refs in changelog switch this to --branches --tags
162 git_rev_filter = --branches --tags
162 git_rev_filter = --branches --tags
163
163
164 # Set to true if your repos are exposed using the dumb protocol
164 # Set to true if your repos are exposed using the dumb protocol
165 git_update_server_info = false
165 git_update_server_info = false
166
166
167 ## RSS/ATOM feed options
167 ## RSS/ATOM feed options
168 rss_cut_off_limit = 256000
168 rss_cut_off_limit = 256000
169 rss_items_per_page = 10
169 rss_items_per_page = 10
170 rss_include_diff = false
170 rss_include_diff = false
171
171
172 ## gist URL alias, used to create nicer urls for gist. This should be an
172 ## gist URL alias, used to create nicer urls for gist. This should be an
173 ## url that does rewrites to _admin/gists/{gistid}.
173 ## url that does rewrites to _admin/gists/{gistid}.
174 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
174 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
175 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
175 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
176 gist_alias_url =
176 gist_alias_url =
177
177
178 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
178 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
179 ## used for access.
179 ## used for access.
180 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
180 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
181 ## came from the the logged in user who own this authentication token.
181 ## came from the the logged in user who own this authentication token.
182 ##
182 ##
183 ## Syntax is ControllerClass:function_pattern.
183 ## Syntax is ControllerClass:function_pattern.
184 ## To enable access to raw_files put `FilesController:raw`.
184 ## To enable access to raw_files put `FilesController:raw`.
185 ## To enable access to patches add `ChangesetController:changeset_patch`.
185 ## To enable access to patches add `ChangesetController:changeset_patch`.
186 ## The list should be "," separated and on a single line.
186 ## The list should be "," separated and on a single line.
187 ##
187 ##
188 ## Recommended controllers to enable:
188 ## Recommended controllers to enable:
189 # ChangesetController:changeset_patch,
189 # ChangesetController:changeset_patch,
190 # ChangesetController:changeset_raw,
190 # ChangesetController:changeset_raw,
191 # FilesController:raw,
191 # FilesController:raw,
192 # FilesController:archivefile,
192 # FilesController:archivefile,
193 # GistsController:*,
193 # GistsController:*,
194 api_access_controllers_whitelist =
194 api_access_controllers_whitelist =
195
195
196 ## default encoding used to convert from and to unicode
196 ## default encoding used to convert from and to unicode
197 ## can be also a comma separated list of encoding in case of mixed encodings
197 ## can be also a comma separated list of encoding in case of mixed encodings
198 default_encoding = UTF-8
198 default_encoding = UTF-8
199
199
200 ## instance-id prefix
200 ## instance-id prefix
201 ## a prefix key for this instance used for cache invalidation when running
201 ## a prefix key for this instance used for cache invalidation when running
202 ## multiple instances of rhodecode, make sure it's globally unique for
202 ## multiple instances of rhodecode, make sure it's globally unique for
203 ## all running rhodecode instances. Leave empty if you don't use it
203 ## all running rhodecode instances. Leave empty if you don't use it
204 instance_id =
204 instance_id =
205
205
206 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
206 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
207 ## of an authentication plugin also if it is disabled by it's settings.
207 ## of an authentication plugin also if it is disabled by it's settings.
208 ## This could be useful if you are unable to log in to the system due to broken
208 ## This could be useful if you are unable to log in to the system due to broken
209 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
209 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
210 ## module to log in again and fix the settings.
210 ## module to log in again and fix the settings.
211 ##
211 ##
212 ## Available builtin plugin IDs (hash is part of the ID):
212 ## Available builtin plugin IDs (hash is part of the ID):
213 ## egg:rhodecode-enterprise-ce#rhodecode
213 ## egg:rhodecode-enterprise-ce#rhodecode
214 ## egg:rhodecode-enterprise-ce#pam
214 ## egg:rhodecode-enterprise-ce#pam
215 ## egg:rhodecode-enterprise-ce#ldap
215 ## egg:rhodecode-enterprise-ce#ldap
216 ## egg:rhodecode-enterprise-ce#jasig_cas
216 ## egg:rhodecode-enterprise-ce#jasig_cas
217 ## egg:rhodecode-enterprise-ce#headers
217 ## egg:rhodecode-enterprise-ce#headers
218 ## egg:rhodecode-enterprise-ce#crowd
218 ## egg:rhodecode-enterprise-ce#crowd
219 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
219 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
220
220
221 ## alternative return HTTP header for failed authentication. Default HTTP
221 ## alternative return HTTP header for failed authentication. Default HTTP
222 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
222 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
223 ## handling that causing a series of failed authentication calls.
223 ## handling that causing a series of failed authentication calls.
224 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
224 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
225 ## This will be served instead of default 401 on bad authnetication
225 ## This will be served instead of default 401 on bad authnetication
226 auth_ret_code =
226 auth_ret_code =
227
227
228 ## use special detection method when serving auth_ret_code, instead of serving
228 ## use special detection method when serving auth_ret_code, instead of serving
229 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
229 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
230 ## and then serve auth_ret_code to clients
230 ## and then serve auth_ret_code to clients
231 auth_ret_code_detection = false
231 auth_ret_code_detection = false
232
232
233 ## locking return code. When repository is locked return this HTTP code. 2XX
233 ## locking return code. When repository is locked return this HTTP code. 2XX
234 ## codes don't break the transactions while 4XX codes do
234 ## codes don't break the transactions while 4XX codes do
235 lock_ret_code = 423
235 lock_ret_code = 423
236
236
237 ## allows to change the repository location in settings page
237 ## allows to change the repository location in settings page
238 allow_repo_location_change = true
238 allow_repo_location_change = true
239
239
240 ## allows to setup custom hooks in settings page
240 ## allows to setup custom hooks in settings page
241 allow_custom_hooks_settings = true
241 allow_custom_hooks_settings = true
242
242
243 ## generated license token, goto license page in RhodeCode settings to obtain
243 ## generated license token, goto license page in RhodeCode settings to obtain
244 ## new token
244 ## new token
245 license_token =
245 license_token =
246
246
247 ## supervisor connection uri, for managing supervisor and logs.
247 ## supervisor connection uri, for managing supervisor and logs.
248 supervisor.uri =
248 supervisor.uri =
249 ## supervisord group name/id we only want this RC instance to handle
249 ## supervisord group name/id we only want this RC instance to handle
250 supervisor.group_id = prod
250 supervisor.group_id = prod
251
251
252 ## Display extended labs settings
252 ## Display extended labs settings
253 labs_settings_active = true
253 labs_settings_active = true
254
254
255 ####################################
255 ####################################
256 ### CELERY CONFIG ####
256 ### CELERY CONFIG ####
257 ####################################
257 ####################################
258 use_celery = false
258 use_celery = false
259 broker.host = localhost
259 broker.host = localhost
260 broker.vhost = rabbitmqhost
260 broker.vhost = rabbitmqhost
261 broker.port = 5672
261 broker.port = 5672
262 broker.user = rabbitmq
262 broker.user = rabbitmq
263 broker.password = qweqwe
263 broker.password = qweqwe
264
264
265 celery.imports = rhodecode.lib.celerylib.tasks
265 celery.imports = rhodecode.lib.celerylib.tasks
266
266
267 celery.result.backend = amqp
267 celery.result.backend = amqp
268 celery.result.dburi = amqp://
268 celery.result.dburi = amqp://
269 celery.result.serialier = json
269 celery.result.serialier = json
270
270
271 #celery.send.task.error.emails = true
271 #celery.send.task.error.emails = true
272 #celery.amqp.task.result.expires = 18000
272 #celery.amqp.task.result.expires = 18000
273
273
274 celeryd.concurrency = 2
274 celeryd.concurrency = 2
275 #celeryd.log.file = celeryd.log
275 #celeryd.log.file = celeryd.log
276 celeryd.log.level = debug
276 celeryd.log.level = debug
277 celeryd.max.tasks.per.child = 1
277 celeryd.max.tasks.per.child = 1
278
278
279 ## tasks will never be sent to the queue, but executed locally instead.
279 ## tasks will never be sent to the queue, but executed locally instead.
280 celery.always.eager = false
280 celery.always.eager = false
281
281
282 ####################################
282 ####################################
283 ### BEAKER CACHE ####
283 ### BEAKER CACHE ####
284 ####################################
284 ####################################
285 # default cache dir for templates. Putting this into a ramdisk
285 # default cache dir for templates. Putting this into a ramdisk
286 ## can boost performance, eg. %(here)s/data_ramdisk
286 ## can boost performance, eg. %(here)s/data_ramdisk
287 cache_dir = %(here)s/data
287 cache_dir = %(here)s/data
288
288
289 ## locking and default file storage for Beaker. Putting this into a ramdisk
289 ## locking and default file storage for Beaker. Putting this into a ramdisk
290 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
290 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
291 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
291 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
292 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
292 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
293
293
294 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
294 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
295
295
296 beaker.cache.super_short_term.type = memory
296 beaker.cache.super_short_term.type = memory
297 beaker.cache.super_short_term.expire = 10
297 beaker.cache.super_short_term.expire = 10
298 beaker.cache.super_short_term.key_length = 256
298 beaker.cache.super_short_term.key_length = 256
299
299
300 beaker.cache.short_term.type = memory
300 beaker.cache.short_term.type = memory
301 beaker.cache.short_term.expire = 60
301 beaker.cache.short_term.expire = 60
302 beaker.cache.short_term.key_length = 256
302 beaker.cache.short_term.key_length = 256
303
303
304 beaker.cache.long_term.type = memory
304 beaker.cache.long_term.type = memory
305 beaker.cache.long_term.expire = 36000
305 beaker.cache.long_term.expire = 36000
306 beaker.cache.long_term.key_length = 256
306 beaker.cache.long_term.key_length = 256
307
307
308 beaker.cache.sql_cache_short.type = memory
308 beaker.cache.sql_cache_short.type = memory
309 beaker.cache.sql_cache_short.expire = 10
309 beaker.cache.sql_cache_short.expire = 10
310 beaker.cache.sql_cache_short.key_length = 256
310 beaker.cache.sql_cache_short.key_length = 256
311
311
312 ## default is memory cache, configure only if required
312 ## default is memory cache, configure only if required
313 ## using multi-node or multi-worker setup
313 ## using multi-node or multi-worker setup
314 #beaker.cache.auth_plugins.type = ext:database
314 #beaker.cache.auth_plugins.type = ext:database
315 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
315 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
316 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
316 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
317 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
317 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
318 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
318 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
319 #beaker.cache.auth_plugins.sa.pool_size = 10
319 #beaker.cache.auth_plugins.sa.pool_size = 10
320 #beaker.cache.auth_plugins.sa.max_overflow = 0
320 #beaker.cache.auth_plugins.sa.max_overflow = 0
321
321
322 beaker.cache.repo_cache_long.type = memorylru_base
322 beaker.cache.repo_cache_long.type = memorylru_base
323 beaker.cache.repo_cache_long.max_items = 4096
323 beaker.cache.repo_cache_long.max_items = 4096
324 beaker.cache.repo_cache_long.expire = 2592000
324 beaker.cache.repo_cache_long.expire = 2592000
325
325
326 ## default is memorylru_base cache, configure only if required
326 ## default is memorylru_base cache, configure only if required
327 ## using multi-node or multi-worker setup
327 ## using multi-node or multi-worker setup
328 #beaker.cache.repo_cache_long.type = ext:memcached
328 #beaker.cache.repo_cache_long.type = ext:memcached
329 #beaker.cache.repo_cache_long.url = localhost:11211
329 #beaker.cache.repo_cache_long.url = localhost:11211
330 #beaker.cache.repo_cache_long.expire = 1209600
330 #beaker.cache.repo_cache_long.expire = 1209600
331 #beaker.cache.repo_cache_long.key_length = 256
331 #beaker.cache.repo_cache_long.key_length = 256
332
332
333 ####################################
333 ####################################
334 ### BEAKER SESSION ####
334 ### BEAKER SESSION ####
335 ####################################
335 ####################################
336
336
337 ## .session.type is type of storage options for the session, current allowed
337 ## .session.type is type of storage options for the session, current allowed
338 ## types are file, ext:memcached, ext:database, and memory (default).
338 ## types are file, ext:memcached, ext:database, and memory (default).
339 beaker.session.type = file
339 beaker.session.type = file
340 beaker.session.data_dir = %(here)s/data/sessions/data
340 beaker.session.data_dir = %(here)s/data/sessions/data
341
341
342 ## db based session, fast, and allows easy management over logged in users
342 ## db based session, fast, and allows easy management over logged in users
343 #beaker.session.type = ext:database
343 #beaker.session.type = ext:database
344 #beaker.session.table_name = db_session
344 #beaker.session.table_name = db_session
345 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
345 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
346 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
346 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
347 #beaker.session.sa.pool_recycle = 3600
347 #beaker.session.sa.pool_recycle = 3600
348 #beaker.session.sa.echo = false
348 #beaker.session.sa.echo = false
349
349
350 beaker.session.key = rhodecode
350 beaker.session.key = rhodecode
351 beaker.session.secret = production-rc-uytcxaz
351 beaker.session.secret = production-rc-uytcxaz
352 beaker.session.lock_dir = %(here)s/data/sessions/lock
352 beaker.session.lock_dir = %(here)s/data/sessions/lock
353
353
354 ## Secure encrypted cookie. Requires AES and AES python libraries
354 ## Secure encrypted cookie. Requires AES and AES python libraries
355 ## you must disable beaker.session.secret to use this
355 ## you must disable beaker.session.secret to use this
356 #beaker.session.encrypt_key = key_for_encryption
356 #beaker.session.encrypt_key = key_for_encryption
357 #beaker.session.validate_key = validation_key
357 #beaker.session.validate_key = validation_key
358
358
359 ## sets session as invalid(also logging out user) if it haven not been
359 ## sets session as invalid(also logging out user) if it haven not been
360 ## accessed for given amount of time in seconds
360 ## accessed for given amount of time in seconds
361 beaker.session.timeout = 2592000
361 beaker.session.timeout = 2592000
362 beaker.session.httponly = true
362 beaker.session.httponly = true
363 ## Path to use for the cookie. Set to prefix if you use prefix middleware
363 ## Path to use for the cookie. Set to prefix if you use prefix middleware
364 #beaker.session.cookie_path = /custom_prefix
364 #beaker.session.cookie_path = /custom_prefix
365
365
366 ## uncomment for https secure cookie
366 ## uncomment for https secure cookie
367 beaker.session.secure = false
367 beaker.session.secure = false
368
368
369 ## auto save the session to not to use .save()
369 ## auto save the session to not to use .save()
370 beaker.session.auto = false
370 beaker.session.auto = false
371
371
372 ## default cookie expiration time in seconds, set to `true` to set expire
372 ## default cookie expiration time in seconds, set to `true` to set expire
373 ## at browser close
373 ## at browser close
374 #beaker.session.cookie_expires = 3600
374 #beaker.session.cookie_expires = 3600
375
375
376 ###################################
376 ###################################
377 ## SEARCH INDEXING CONFIGURATION ##
377 ## SEARCH INDEXING CONFIGURATION ##
378 ###################################
378 ###################################
379 ## Full text search indexer is available in rhodecode-tools under
379 ## Full text search indexer is available in rhodecode-tools under
380 ## `rhodecode-tools index` command
380 ## `rhodecode-tools index` command
381
381
382 ## WHOOSH Backend, doesn't require additional services to run
382 ## WHOOSH Backend, doesn't require additional services to run
383 ## it works good with few dozen repos
383 ## it works good with few dozen repos
384 search.module = rhodecode.lib.index.whoosh
384 search.module = rhodecode.lib.index.whoosh
385 search.location = %(here)s/data/index
385 search.location = %(here)s/data/index
386
386
387 ########################################
387 ########################################
388 ### CHANNELSTREAM CONFIG ####
388 ### CHANNELSTREAM CONFIG ####
389 ########################################
389 ########################################
390 ## channelstream enables persistent connections and live notification
390 ## channelstream enables persistent connections and live notification
391 ## in the system. It's also used by the chat system
391 ## in the system. It's also used by the chat system
392 channelstream.enabled = false
392 channelstream.enabled = false
393
393
394 ## server address for channelstream server on the backend
394 ## server address for channelstream server on the backend
395 channelstream.server = 127.0.0.1:9800
395 channelstream.server = 127.0.0.1:9800
396
396
397 ## location of the channelstream server from outside world
397 ## location of the channelstream server from outside world
398 ## use ws:// for http or wss:// for https. This address needs to be handled
398 ## use ws:// for http or wss:// for https. This address needs to be handled
399 ## by external HTTP server such as Nginx or Apache
399 ## by external HTTP server such as Nginx or Apache
400 ## see nginx/apache configuration examples in our docs
400 ## see nginx/apache configuration examples in our docs
401 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
401 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
402 channelstream.secret = secret
402 channelstream.secret = secret
403 channelstream.history.location = %(here)s/channelstream_history
403 channelstream.history.location = %(here)s/channelstream_history
404
404
405 ## Internal application path that Javascript uses to connect into.
405 ## Internal application path that Javascript uses to connect into.
406 ## If you use proxy-prefix the prefix should be added before /_channelstream
406 ## If you use proxy-prefix the prefix should be added before /_channelstream
407 channelstream.proxy_path = /_channelstream
407 channelstream.proxy_path = /_channelstream
408
408
409
409
410 ###################################
410 ###################################
411 ## APPENLIGHT CONFIG ##
411 ## APPENLIGHT CONFIG ##
412 ###################################
412 ###################################
413
413
414 ## Appenlight is tailored to work with RhodeCode, see
414 ## Appenlight is tailored to work with RhodeCode, see
415 ## http://appenlight.com for details how to obtain an account
415 ## http://appenlight.com for details how to obtain an account
416
416
417 ## appenlight integration enabled
417 ## appenlight integration enabled
418 appenlight = false
418 appenlight = false
419
419
420 appenlight.server_url = https://api.appenlight.com
420 appenlight.server_url = https://api.appenlight.com
421 appenlight.api_key = YOUR_API_KEY
421 appenlight.api_key = YOUR_API_KEY
422 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
422 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
423
423
424 # used for JS client
424 # used for JS client
425 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
425 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
426
426
427 ## TWEAK AMOUNT OF INFO SENT HERE
427 ## TWEAK AMOUNT OF INFO SENT HERE
428
428
429 ## enables 404 error logging (default False)
429 ## enables 404 error logging (default False)
430 appenlight.report_404 = false
430 appenlight.report_404 = false
431
431
432 ## time in seconds after request is considered being slow (default 1)
432 ## time in seconds after request is considered being slow (default 1)
433 appenlight.slow_request_time = 1
433 appenlight.slow_request_time = 1
434
434
435 ## record slow requests in application
435 ## record slow requests in application
436 ## (needs to be enabled for slow datastore recording and time tracking)
436 ## (needs to be enabled for slow datastore recording and time tracking)
437 appenlight.slow_requests = true
437 appenlight.slow_requests = true
438
438
439 ## enable hooking to application loggers
439 ## enable hooking to application loggers
440 appenlight.logging = true
440 appenlight.logging = true
441
441
442 ## minimum log level for log capture
442 ## minimum log level for log capture
443 appenlight.logging.level = WARNING
443 appenlight.logging.level = WARNING
444
444
445 ## send logs only from erroneous/slow requests
445 ## send logs only from erroneous/slow requests
446 ## (saves API quota for intensive logging)
446 ## (saves API quota for intensive logging)
447 appenlight.logging_on_error = false
447 appenlight.logging_on_error = false
448
448
449 ## list of additonal keywords that should be grabbed from environ object
449 ## list of additonal keywords that should be grabbed from environ object
450 ## can be string with comma separated list of words in lowercase
450 ## can be string with comma separated list of words in lowercase
451 ## (by default client will always send following info:
451 ## (by default client will always send following info:
452 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
452 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
453 ## start with HTTP* this list be extended with additional keywords here
453 ## start with HTTP* this list be extended with additional keywords here
454 appenlight.environ_keys_whitelist =
454 appenlight.environ_keys_whitelist =
455
455
456 ## list of keywords that should be blanked from request object
456 ## list of keywords that should be blanked from request object
457 ## can be string with comma separated list of words in lowercase
457 ## can be string with comma separated list of words in lowercase
458 ## (by default client will always blank keys that contain following words
458 ## (by default client will always blank keys that contain following words
459 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
459 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
460 ## this list be extended with additional keywords set here
460 ## this list be extended with additional keywords set here
461 appenlight.request_keys_blacklist =
461 appenlight.request_keys_blacklist =
462
462
463 ## list of namespaces that should be ignores when gathering log entries
463 ## list of namespaces that should be ignores when gathering log entries
464 ## can be string with comma separated list of namespaces
464 ## can be string with comma separated list of namespaces
465 ## (by default the client ignores own entries: appenlight_client.client)
465 ## (by default the client ignores own entries: appenlight_client.client)
466 appenlight.log_namespace_blacklist =
466 appenlight.log_namespace_blacklist =
467
467
468
468
469 ################################################################################
469 ################################################################################
470 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
470 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
471 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
471 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
472 ## execute malicious code after an exception is raised. ##
472 ## execute malicious code after an exception is raised. ##
473 ################################################################################
473 ################################################################################
474 set debug = false
474 set debug = false
475
475
476
476
477 ###########################################
477 ###########################################
478 ### MAIN RHODECODE DATABASE CONFIG ###
478 ### MAIN RHODECODE DATABASE CONFIG ###
479 ###########################################
479 ###########################################
480 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
480 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
481 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
481 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
482 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
482 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
483 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
483 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
484
484
485 # see sqlalchemy docs for other advanced settings
485 # see sqlalchemy docs for other advanced settings
486
486
487 ## print the sql statements to output
487 ## print the sql statements to output
488 sqlalchemy.db1.echo = false
488 sqlalchemy.db1.echo = false
489 ## recycle the connections after this amount of seconds
489 ## recycle the connections after this amount of seconds
490 sqlalchemy.db1.pool_recycle = 3600
490 sqlalchemy.db1.pool_recycle = 3600
491 sqlalchemy.db1.convert_unicode = true
491 sqlalchemy.db1.convert_unicode = true
492
492
493 ## the number of connections to keep open inside the connection pool.
493 ## the number of connections to keep open inside the connection pool.
494 ## 0 indicates no limit
494 ## 0 indicates no limit
495 #sqlalchemy.db1.pool_size = 5
495 #sqlalchemy.db1.pool_size = 5
496
496
497 ## the number of connections to allow in connection pool "overflow", that is
497 ## the number of connections to allow in connection pool "overflow", that is
498 ## connections that can be opened above and beyond the pool_size setting,
498 ## connections that can be opened above and beyond the pool_size setting,
499 ## which defaults to five.
499 ## which defaults to five.
500 #sqlalchemy.db1.max_overflow = 10
500 #sqlalchemy.db1.max_overflow = 10
501
501
502
502
503 ##################
503 ##################
504 ### VCS CONFIG ###
504 ### VCS CONFIG ###
505 ##################
505 ##################
506 vcs.server.enable = true
506 vcs.server.enable = true
507 vcs.server = localhost:9900
507 vcs.server = localhost:9900
508
508
509 ## Web server connectivity protocol, responsible for web based VCS operatations
509 ## Web server connectivity protocol, responsible for web based VCS operatations
510 ## Available protocols are:
510 ## Available protocols are:
511 ## `pyro4` - use pyro4 server
512 ## `http` - use http-rpc backend (default)
511 ## `http` - use http-rpc backend (default)
513 vcs.server.protocol = http
512 vcs.server.protocol = http
514
513
515 ## Push/Pull operations protocol, available options are:
514 ## Push/Pull operations protocol, available options are:
516 ## `pyro4` - use pyro4 server
517 ## `http` - use http-rpc backend (default)
515 ## `http` - use http-rpc backend (default)
518 ##
516 ##
519 vcs.scm_app_implementation = http
517 vcs.scm_app_implementation = http
520
518
521 ## Push/Pull operations hooks protocol, available options are:
519 ## Push/Pull operations hooks protocol, available options are:
522 ## `pyro4` - use pyro4 server
523 ## `http` - use http-rpc backend (default)
520 ## `http` - use http-rpc backend (default)
524 vcs.hooks.protocol = http
521 vcs.hooks.protocol = http
525
522
526 vcs.server.log_level = info
523 vcs.server.log_level = info
527 ## Start VCSServer with this instance as a subprocess, usefull for development
524 ## Start VCSServer with this instance as a subprocess, usefull for development
528 vcs.start_server = false
525 vcs.start_server = false
529
526
530 ## List of enabled VCS backends, available options are:
527 ## List of enabled VCS backends, available options are:
531 ## `hg` - mercurial
528 ## `hg` - mercurial
532 ## `git` - git
529 ## `git` - git
533 ## `svn` - subversion
530 ## `svn` - subversion
534 vcs.backends = hg, git, svn
531 vcs.backends = hg, git, svn
535
532
536 vcs.connection_timeout = 3600
533 vcs.connection_timeout = 3600
537 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
534 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
538 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
535 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
539 #vcs.svn.compatible_version = pre-1.8-compatible
536 #vcs.svn.compatible_version = pre-1.8-compatible
540
537
541
538
542 ############################################################
539 ############################################################
543 ### Subversion proxy support (mod_dav_svn) ###
540 ### Subversion proxy support (mod_dav_svn) ###
544 ### Maps RhodeCode repo groups into SVN paths for Apache ###
541 ### Maps RhodeCode repo groups into SVN paths for Apache ###
545 ############################################################
542 ############################################################
546 ## Enable or disable the config file generation.
543 ## Enable or disable the config file generation.
547 svn.proxy.generate_config = false
544 svn.proxy.generate_config = false
548 ## Generate config file with `SVNListParentPath` set to `On`.
545 ## Generate config file with `SVNListParentPath` set to `On`.
549 svn.proxy.list_parent_path = true
546 svn.proxy.list_parent_path = true
550 ## Set location and file name of generated config file.
547 ## Set location and file name of generated config file.
551 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
548 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
552 ## Used as a prefix to the `Location` block in the generated config file.
549 ## Used as a prefix to the `Location` block in the generated config file.
553 ## In most cases it should be set to `/`.
550 ## In most cases it should be set to `/`.
554 svn.proxy.location_root = /
551 svn.proxy.location_root = /
555 ## Command to reload the mod dav svn configuration on change.
552 ## Command to reload the mod dav svn configuration on change.
556 ## Example: `/etc/init.d/apache2 reload`
553 ## Example: `/etc/init.d/apache2 reload`
557 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
554 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
558 ## If the timeout expires before the reload command finishes, the command will
555 ## If the timeout expires before the reload command finishes, the command will
559 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
556 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
560 #svn.proxy.reload_timeout = 10
557 #svn.proxy.reload_timeout = 10
561
558
562 ## Dummy marker to add new entries after.
559 ## Dummy marker to add new entries after.
563 ## Add any custom entries below. Please don't remove.
560 ## Add any custom entries below. Please don't remove.
564 custom.conf = 1
561 custom.conf = 1
565
562
566
563
567 ################################
564 ################################
568 ### LOGGING CONFIGURATION ####
565 ### LOGGING CONFIGURATION ####
569 ################################
566 ################################
570 [loggers]
567 [loggers]
571 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
568 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
572
569
573 [handlers]
570 [handlers]
574 keys = console, console_sql
571 keys = console, console_sql
575
572
576 [formatters]
573 [formatters]
577 keys = generic, color_formatter, color_formatter_sql
574 keys = generic, color_formatter, color_formatter_sql
578
575
579 #############
576 #############
580 ## LOGGERS ##
577 ## LOGGERS ##
581 #############
578 #############
582 [logger_root]
579 [logger_root]
583 level = NOTSET
580 level = NOTSET
584 handlers = console
581 handlers = console
585
582
586 [logger_routes]
583 [logger_routes]
587 level = DEBUG
584 level = DEBUG
588 handlers =
585 handlers =
589 qualname = routes.middleware
586 qualname = routes.middleware
590 ## "level = DEBUG" logs the route matched and routing variables.
587 ## "level = DEBUG" logs the route matched and routing variables.
591 propagate = 1
588 propagate = 1
592
589
593 [logger_beaker]
590 [logger_beaker]
594 level = DEBUG
591 level = DEBUG
595 handlers =
592 handlers =
596 qualname = beaker.container
593 qualname = beaker.container
597 propagate = 1
594 propagate = 1
598
595
599 [logger_templates]
596 [logger_templates]
600 level = INFO
597 level = INFO
601 handlers =
598 handlers =
602 qualname = pylons.templating
599 qualname = pylons.templating
603 propagate = 1
600 propagate = 1
604
601
605 [logger_rhodecode]
602 [logger_rhodecode]
606 level = DEBUG
603 level = DEBUG
607 handlers =
604 handlers =
608 qualname = rhodecode
605 qualname = rhodecode
609 propagate = 1
606 propagate = 1
610
607
611 [logger_sqlalchemy]
608 [logger_sqlalchemy]
612 level = INFO
609 level = INFO
613 handlers = console_sql
610 handlers = console_sql
614 qualname = sqlalchemy.engine
611 qualname = sqlalchemy.engine
615 propagate = 0
612 propagate = 0
616
613
617 ##############
614 ##############
618 ## HANDLERS ##
615 ## HANDLERS ##
619 ##############
616 ##############
620
617
621 [handler_console]
618 [handler_console]
622 class = StreamHandler
619 class = StreamHandler
623 args = (sys.stderr, )
620 args = (sys.stderr, )
624 level = INFO
621 level = INFO
625 formatter = generic
622 formatter = generic
626
623
627 [handler_console_sql]
624 [handler_console_sql]
628 class = StreamHandler
625 class = StreamHandler
629 args = (sys.stderr, )
626 args = (sys.stderr, )
630 level = WARN
627 level = WARN
631 formatter = generic
628 formatter = generic
632
629
633 ################
630 ################
634 ## FORMATTERS ##
631 ## FORMATTERS ##
635 ################
632 ################
636
633
637 [formatter_generic]
634 [formatter_generic]
638 class = rhodecode.lib.logging_formatter.Pyro4AwareFormatter
635 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
639 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
636 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
640 datefmt = %Y-%m-%d %H:%M:%S
637 datefmt = %Y-%m-%d %H:%M:%S
641
638
642 [formatter_color_formatter]
639 [formatter_color_formatter]
643 class = rhodecode.lib.logging_formatter.ColorFormatter
640 class = rhodecode.lib.logging_formatter.ColorFormatter
644 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
641 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
645 datefmt = %Y-%m-%d %H:%M:%S
642 datefmt = %Y-%m-%d %H:%M:%S
646
643
647 [formatter_color_formatter_sql]
644 [formatter_color_formatter_sql]
648 class = rhodecode.lib.logging_formatter.ColorFormatterSql
645 class = rhodecode.lib.logging_formatter.ColorFormatterSql
649 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
646 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
650 datefmt = %Y-%m-%d %H:%M:%S
647 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,240 +1,241 b''
1 # Nix environment for the community edition
1 # Nix environment for the community edition
2 #
2 #
3 # This shall be as lean as possible, just producing the Enterprise
3 # This shall be as lean as possible, just producing the Enterprise
4 # derivation. For advanced tweaks to pimp up the development environment we use
4 # derivation. For advanced tweaks to pimp up the development environment we use
5 # "shell.nix" so that it does not have to clutter this file.
5 # "shell.nix" so that it does not have to clutter this file.
6
6
7 args@
7 args@
8 { pythonPackages ? "python27Packages"
8 { pythonPackages ? "python27Packages"
9 , pythonExternalOverrides ? self: super: {}
9 , pythonExternalOverrides ? self: super: {}
10 , doCheck ? true
10 , doCheck ? true
11 , ...
11 , ...
12 }:
12 }:
13
13
14 let
14 let
15
15
16 # Use nixpkgs from args or import them. We use this indirect approach
16 # Use nixpkgs from args or import them. We use this indirect approach
17 # through args to be able to use the name `pkgs` for our customized packages.
17 # through args to be able to use the name `pkgs` for our customized packages.
18 # Otherwise we will end up with an infinite recursion.
18 # Otherwise we will end up with an infinite recursion.
19 nixpkgs = args.pkgs or (import <nixpkgs> { });
19 nixpkgs = args.pkgs or (import <nixpkgs> { });
20
20
21 # johbo: Interim bridge which allows us to build with the upcoming
21 # johbo: Interim bridge which allows us to build with the upcoming
22 # nixos.16.09 branch (unstable at the moment of writing this note) and the
22 # nixos.16.09 branch (unstable at the moment of writing this note) and the
23 # current stable nixos-16.03.
23 # current stable nixos-16.03.
24 backwardsCompatibleFetchgit = { ... }@args:
24 backwardsCompatibleFetchgit = { ... }@args:
25 let
25 let
26 origSources = nixpkgs.fetchgit args;
26 origSources = nixpkgs.fetchgit args;
27 in
27 in
28 nixpkgs.lib.overrideDerivation origSources (oldAttrs: {
28 nixpkgs.lib.overrideDerivation origSources (oldAttrs: {
29 NIX_PREFETCH_GIT_CHECKOUT_HOOK = ''
29 NIX_PREFETCH_GIT_CHECKOUT_HOOK = ''
30 find $out -name '.git*' -print0 | xargs -0 rm -rf
30 find $out -name '.git*' -print0 | xargs -0 rm -rf
31 '';
31 '';
32 });
32 });
33
33
34 # Create a customized version of nixpkgs which should be used throughout the
34 # Create a customized version of nixpkgs which should be used throughout the
35 # rest of this file.
35 # rest of this file.
36 pkgs = nixpkgs.overridePackages (self: super: {
36 pkgs = nixpkgs.overridePackages (self: super: {
37 fetchgit = backwardsCompatibleFetchgit;
37 fetchgit = backwardsCompatibleFetchgit;
38 });
38 });
39
39
40 # Evaluates to the last segment of a file system path.
40 # Evaluates to the last segment of a file system path.
41 basename = path: with pkgs.lib; last (splitString "/" path);
41 basename = path: with pkgs.lib; last (splitString "/" path);
42
42
43 # source code filter used as arugment to builtins.filterSource.
43 # source code filter used as arugment to builtins.filterSource.
44 src-filter = path: type: with pkgs.lib;
44 src-filter = path: type: with pkgs.lib;
45 let
45 let
46 ext = last (splitString "." path);
46 ext = last (splitString "." path);
47 in
47 in
48 !builtins.elem (basename path) [
48 !builtins.elem (basename path) [
49 ".git" ".hg" "__pycache__" ".eggs"
49 ".git" ".hg" "__pycache__" ".eggs"
50 "bower_components" "node_modules"
50 "bower_components" "node_modules"
51 "build" "data" "result" "tmp"] &&
51 "build" "data" "result" "tmp"] &&
52 !builtins.elem ext ["egg-info" "pyc"] &&
52 !builtins.elem ext ["egg-info" "pyc"] &&
53 # TODO: johbo: This check is wrong, since "path" contains an absolute path,
53 # TODO: johbo: This check is wrong, since "path" contains an absolute path,
54 # it would still be good to restore it since we want to ignore "result-*".
54 # it would still be good to restore it since we want to ignore "result-*".
55 !hasPrefix "result" path;
55 !hasPrefix "result" path;
56
56
57 basePythonPackages = with builtins; if isAttrs pythonPackages
57 basePythonPackages = with builtins; if isAttrs pythonPackages
58 then pythonPackages
58 then pythonPackages
59 else getAttr pythonPackages pkgs;
59 else getAttr pythonPackages pkgs;
60
60
61 buildBowerComponents =
61 buildBowerComponents =
62 pkgs.buildBowerComponents or
62 pkgs.buildBowerComponents or
63 (import ./pkgs/backport-16.03-build-bower-components.nix { inherit pkgs; });
63 (import ./pkgs/backport-16.03-build-bower-components.nix { inherit pkgs; });
64
64
65 sources = pkgs.config.rc.sources or {};
65 sources = pkgs.config.rc.sources or {};
66 version = builtins.readFile ./rhodecode/VERSION;
66 version = builtins.readFile ./rhodecode/VERSION;
67 rhodecode-enterprise-ce-src = builtins.filterSource src-filter ./.;
67 rhodecode-enterprise-ce-src = builtins.filterSource src-filter ./.;
68
68
69 nodeEnv = import ./pkgs/node-default.nix {
69 nodeEnv = import ./pkgs/node-default.nix {
70 inherit pkgs;
70 inherit pkgs;
71 };
71 };
72 nodeDependencies = nodeEnv.shell.nodeDependencies;
72 nodeDependencies = nodeEnv.shell.nodeDependencies;
73
73
74 bowerComponents = buildBowerComponents {
74 bowerComponents = buildBowerComponents {
75 name = "enterprise-ce-${version}";
75 name = "enterprise-ce-${version}";
76 generated = ./pkgs/bower-packages.nix;
76 generated = ./pkgs/bower-packages.nix;
77 src = rhodecode-enterprise-ce-src;
77 src = rhodecode-enterprise-ce-src;
78 };
78 };
79
79
80 pythonGeneratedPackages = self: basePythonPackages.override (a: {
80 pythonGeneratedPackages = self: basePythonPackages.override (a: {
81 inherit self;
81 inherit self;
82 })
82 })
83 // (scopedImport {
83 // (scopedImport {
84 self = self;
84 self = self;
85 super = basePythonPackages;
85 super = basePythonPackages;
86 inherit pkgs;
86 inherit pkgs;
87 inherit (pkgs) fetchurl fetchgit;
87 inherit (pkgs) fetchurl fetchgit;
88 } ./pkgs/python-packages.nix);
88 } ./pkgs/python-packages.nix);
89
89
90 pythonOverrides = import ./pkgs/python-packages-overrides.nix {
90 pythonOverrides = import ./pkgs/python-packages-overrides.nix {
91 inherit
91 inherit
92 basePythonPackages
92 basePythonPackages
93 pkgs;
93 pkgs;
94 };
94 };
95
95
96 pythonLocalOverrides = self: super: {
96 pythonLocalOverrides = self: super: {
97 rhodecode-enterprise-ce =
97 rhodecode-enterprise-ce =
98 let
98 let
99 linkNodeAndBowerPackages = ''
99 linkNodeAndBowerPackages = ''
100 echo "Export RhodeCode CE path"
100 echo "Export RhodeCode CE path"
101 export RHODECODE_CE_PATH=${rhodecode-enterprise-ce-src}
101 export RHODECODE_CE_PATH=${rhodecode-enterprise-ce-src}
102 echo "Link node packages"
102 echo "Link node packages"
103 rm -fr node_modules
103 rm -fr node_modules
104 mkdir node_modules
104 mkdir node_modules
105 # johbo: Linking individual packages allows us to run "npm install"
105 # johbo: Linking individual packages allows us to run "npm install"
106 # inside of a shell to try things out. Re-entering the shell will
106 # inside of a shell to try things out. Re-entering the shell will
107 # restore a clean environment.
107 # restore a clean environment.
108 ln -s ${nodeDependencies}/lib/node_modules/* node_modules/
108 ln -s ${nodeDependencies}/lib/node_modules/* node_modules/
109
109
110 echo "DONE: Link node packages"
110 echo "DONE: Link node packages"
111
111
112 echo "Link bower packages"
112 echo "Link bower packages"
113 rm -fr bower_components
113 rm -fr bower_components
114 mkdir bower_components
114 mkdir bower_components
115
115
116 ln -s ${bowerComponents}/bower_components/* bower_components/
116 ln -s ${bowerComponents}/bower_components/* bower_components/
117 echo "DONE: Link bower packages"
117 echo "DONE: Link bower packages"
118 '';
118 '';
119 in super.rhodecode-enterprise-ce.override (attrs: {
119 in super.rhodecode-enterprise-ce.override (attrs: {
120
120
121 inherit
121 inherit
122 doCheck
122 doCheck
123 version;
123 version;
124 name = "rhodecode-enterprise-ce-${version}";
124 name = "rhodecode-enterprise-ce-${version}";
125 releaseName = "RhodeCodeEnterpriseCE-${version}";
125 releaseName = "RhodeCodeEnterpriseCE-${version}";
126 src = rhodecode-enterprise-ce-src;
126 src = rhodecode-enterprise-ce-src;
127 dontStrip = true; # prevent strip, we don't need it.
127
128
128 buildInputs =
129 buildInputs =
129 attrs.buildInputs ++
130 attrs.buildInputs ++
130 (with self; [
131 (with self; [
131 pkgs.nodePackages.bower
132 pkgs.nodePackages.bower
132 pkgs.nodePackages.grunt-cli
133 pkgs.nodePackages.grunt-cli
133 pkgs.subversion
134 pkgs.subversion
134 pytest-catchlog
135 pytest-catchlog
135 rhodecode-testdata
136 rhodecode-testdata
136 ]);
137 ]);
137
138
138 #TODO: either move this into overrides, OR use the new machanics from
139 #TODO: either move this into overrides, OR use the new machanics from
139 # pip2nix and requiremtn.txt file
140 # pip2nix and requiremtn.txt file
140 propagatedBuildInputs = attrs.propagatedBuildInputs ++ (with self; [
141 propagatedBuildInputs = attrs.propagatedBuildInputs ++ (with self; [
141 rhodecode-tools
142 rhodecode-tools
142 ]);
143 ]);
143
144
144 # TODO: johbo: Make a nicer way to expose the parts. Maybe
145 # TODO: johbo: Make a nicer way to expose the parts. Maybe
145 # pkgs/default.nix?
146 # pkgs/default.nix?
146 passthru = {
147 passthru = {
147 inherit
148 inherit
148 bowerComponents
149 bowerComponents
149 linkNodeAndBowerPackages
150 linkNodeAndBowerPackages
150 myPythonPackagesUnfix
151 myPythonPackagesUnfix
151 pythonLocalOverrides;
152 pythonLocalOverrides;
152 pythonPackages = self;
153 pythonPackages = self;
153 };
154 };
154
155
155 LC_ALL = "en_US.UTF-8";
156 LC_ALL = "en_US.UTF-8";
156 LOCALE_ARCHIVE =
157 LOCALE_ARCHIVE =
157 if pkgs.stdenv ? glibc
158 if pkgs.stdenv ? glibc
158 then "${pkgs.glibcLocales}/lib/locale/locale-archive"
159 then "${pkgs.glibcLocales}/lib/locale/locale-archive"
159 else "";
160 else "";
160
161
161 preCheck = ''
162 preCheck = ''
162 export PATH="$out/bin:$PATH"
163 export PATH="$out/bin:$PATH"
163 '';
164 '';
164
165
165 postCheck = ''
166 postCheck = ''
166 rm -rf $out/lib/${self.python.libPrefix}/site-packages/pytest_pylons
167 rm -rf $out/lib/${self.python.libPrefix}/site-packages/pytest_pylons
167 rm -rf $out/lib/${self.python.libPrefix}/site-packages/rhodecode/tests
168 rm -rf $out/lib/${self.python.libPrefix}/site-packages/rhodecode/tests
168 '';
169 '';
169
170
170 preBuild = linkNodeAndBowerPackages + ''
171 preBuild = linkNodeAndBowerPackages + ''
171 grunt
172 grunt
172 rm -fr node_modules
173 rm -fr node_modules
173 '';
174 '';
174
175
175 postInstall = ''
176 postInstall = ''
176 # python based programs need to be wrapped
177 # python based programs need to be wrapped
177 ln -s ${self.supervisor}/bin/supervisor* $out/bin/
178 ln -s ${self.supervisor}/bin/supervisor* $out/bin/
178 ln -s ${self.gunicorn}/bin/gunicorn $out/bin/
179 ln -s ${self.gunicorn}/bin/gunicorn $out/bin/
179 ln -s ${self.PasteScript}/bin/paster $out/bin/
180 ln -s ${self.PasteScript}/bin/paster $out/bin/
180 ln -s ${self.channelstream}/bin/channelstream $out/bin/
181 ln -s ${self.channelstream}/bin/channelstream $out/bin/
181 ln -s ${self.pyramid}/bin/* $out/bin/ #*/
182 ln -s ${self.pyramid}/bin/* $out/bin/ #*/
182
183
183 # rhodecode-tools
184 # rhodecode-tools
184 # TODO: johbo: re-think this. Do the tools import anything from enterprise?
185 # TODO: johbo: re-think this. Do the tools import anything from enterprise?
185 ln -s ${self.rhodecode-tools}/bin/rhodecode-* $out/bin/
186 ln -s ${self.rhodecode-tools}/bin/rhodecode-* $out/bin/
186
187
187 # note that condition should be restricted when adding further tools
188 # note that condition should be restricted when adding further tools
188 for file in $out/bin/*; do #*/
189 for file in $out/bin/*; do #*/
189 wrapProgram $file \
190 wrapProgram $file \
190 --prefix PYTHONPATH : $PYTHONPATH \
191 --prefix PYTHONPATH : $PYTHONPATH \
191 --prefix PATH : $PATH \
192 --prefix PATH : $PATH \
192 --set PYTHONHASHSEED random
193 --set PYTHONHASHSEED random
193 done
194 done
194
195
195 mkdir $out/etc
196 mkdir $out/etc
196 cp configs/production.ini $out/etc
197 cp configs/production.ini $out/etc
197
198
198 echo "Writing meta information for rccontrol to nix-support/rccontrol"
199 echo "Writing meta information for rccontrol to nix-support/rccontrol"
199 mkdir -p $out/nix-support/rccontrol
200 mkdir -p $out/nix-support/rccontrol
200 cp -v rhodecode/VERSION $out/nix-support/rccontrol/version
201 cp -v rhodecode/VERSION $out/nix-support/rccontrol/version
201 echo "DONE: Meta information for rccontrol written"
202 echo "DONE: Meta information for rccontrol written"
202
203
203 # TODO: johbo: Make part of ac-tests
204 # TODO: johbo: Make part of ac-tests
204 if [ ! -f rhodecode/public/js/scripts.js ]; then
205 if [ ! -f rhodecode/public/js/scripts.js ]; then
205 echo "Missing scripts.js"
206 echo "Missing scripts.js"
206 exit 1
207 exit 1
207 fi
208 fi
208 if [ ! -f rhodecode/public/css/style.css ]; then
209 if [ ! -f rhodecode/public/css/style.css ]; then
209 echo "Missing style.css"
210 echo "Missing style.css"
210 exit 1
211 exit 1
211 fi
212 fi
212 '';
213 '';
213
214
214 });
215 });
215
216
216 rhodecode-testdata = import "${rhodecode-testdata-src}/default.nix" {
217 rhodecode-testdata = import "${rhodecode-testdata-src}/default.nix" {
217 inherit
218 inherit
218 doCheck
219 doCheck
219 pkgs
220 pkgs
220 pythonPackages;
221 pythonPackages;
221 };
222 };
222
223
223 };
224 };
224
225
225 rhodecode-testdata-src = sources.rhodecode-testdata or (
226 rhodecode-testdata-src = sources.rhodecode-testdata or (
226 pkgs.fetchhg {
227 pkgs.fetchhg {
227 url = "https://code.rhodecode.com/upstream/rc_testdata";
228 url = "https://code.rhodecode.com/upstream/rc_testdata";
228 rev = "v0.9.0";
229 rev = "v0.10.0";
229 sha256 = "0k0ccb7cncd6mmzwckfbr6l7fsymcympwcm948qc3i0f0m6bbg1y";
230 sha256 = "0zn9swwvx4vgw4qn8q3ri26vvzgrxn15x6xnjrysi1bwmz01qjl0";
230 });
231 });
231
232
232 # Apply all overrides and fix the final package set
233 # Apply all overrides and fix the final package set
233 myPythonPackagesUnfix = with pkgs.lib;
234 myPythonPackagesUnfix = with pkgs.lib;
234 (extends pythonExternalOverrides
235 (extends pythonExternalOverrides
235 (extends pythonLocalOverrides
236 (extends pythonLocalOverrides
236 (extends pythonOverrides
237 (extends pythonOverrides
237 pythonGeneratedPackages)));
238 pythonGeneratedPackages)));
238 myPythonPackages = (pkgs.lib.fix myPythonPackagesUnfix);
239 myPythonPackages = (pkgs.lib.fix myPythonPackagesUnfix);
239
240
240 in myPythonPackages.rhodecode-enterprise-ce
241 in myPythonPackages.rhodecode-enterprise-ce
@@ -1,23 +1,22 b''
1
1
2 ======================================
2 ======================================
3 VCS client and VCSServer integration
3 VCS client and VCSServer integration
4 ======================================
4 ======================================
5
5
6 Enterprise uses the VCSServer as a backend to provide version control
6 Enterprise uses the VCSServer as a backend to provide version control
7 functionalities. This section describes the components in Enterprise which talk
7 functionalities. This section describes the components in Enterprise which talk
8 to the VCSServer.
8 to the VCSServer.
9
9
10 The client library is implemented in :mod:`rhodecode.lib.vcs`. For HTTP based
10 The client library is implemented in :mod:`rhodecode.lib.vcs`. For HTTP based
11 access of the command line clients special middlewares and utilities are
11 access of the command line clients special middlewares and utilities are
12 implemented in :mod:`rhodecode.lib.middleware`.
12 implemented in :mod:`rhodecode.lib.middleware`.
13
13
14
14
15
15
16
16
17 .. toctree::
17 .. toctree::
18 :maxdepth: 2
18 :maxdepth: 2
19
19
20 http-transition
21 middleware
20 middleware
22 vcsserver
21 vcsserver
23 subversion
22 subversion
@@ -1,87 +1,87 b''
1 .. _apache-conf-eg:
1 .. _apache-conf-eg:
2
2
3 Apache Configuration Example
3 Apache Configuration Example
4 ----------------------------
4 ----------------------------
5
5
6 Use the following example to configure Apache as a your web server.
6 Use the following example to configure Apache as a your web server.
7 Below config if for an Apache Reverse Proxy configuration.
7 Below config if for an Apache Reverse Proxy configuration.
8
8
9 .. note::
9 .. note::
10
10
11 Apache requires the following modules to be enabled. Below is an example
11 Apache requires the following modules to be enabled. Below is an example
12 how to enable them on Ubuntu Server
12 how to enable them on Ubuntu Server
13
13
14
14
15 .. code-block:: bash
15 .. code-block:: bash
16
16
17 $ sudo a2enmod proxy
17 $ sudo a2enmod proxy
18 $ sudo a2enmod proxy_http
18 $ sudo a2enmod proxy_http
19 $ sudo a2enmod proxy_balancer
19 $ sudo a2enmod proxy_balancer
20 $ sudo a2enmod headers
20 $ sudo a2enmod headers
21 $ sudo a2enmod ssl
21 $ sudo a2enmod ssl
22 $ sudo a2enmod rewrite
22 $ sudo a2enmod rewrite
23
23
24 # requires Apache 2.4+, required to handle websockets/channelstream
24 # requires Apache 2.4+, required to handle websockets/channelstream
25 $ sudo a2enmod proxy_wstunnel
25 $ sudo a2enmod proxy_wstunnel
26
26
27
27
28 .. code-block:: apache
28 .. code-block:: apache
29
29
30 ## HTTP to HTTPS rewrite
30 ## HTTP to HTTPS rewrite
31 <VirtualHost *:80>
31 <VirtualHost *:80>
32 ServerName rhodecode.myserver.com
32 ServerName rhodecode.myserver.com
33 DocumentRoot /var/www/html
33 DocumentRoot /var/www/html
34 Redirect permanent / https://rhodecode.myserver.com/
34 Redirect permanent / https://rhodecode.myserver.com/
35 </VirtualHost>
35 </VirtualHost>
36
36
37 ## MAIN SSL enabled server
37 ## MAIN SSL enabled server
38 <VirtualHost *:443>
38 <VirtualHost *:443>
39
39
40 ServerName rhodecode.myserver.com
40 ServerName rhodecode.myserver.com
41 ServerAlias rhodecode.myserver.com
41 ServerAlias rhodecode.myserver.com
42
42
43 ## serve static files by Apache, recommended for performance
43 ## serve static files by Apache, recommended for performance
44 #Alias /_static /home/ubuntu/.rccontrol/community-1/static
44 #Alias /_static /home/ubuntu/.rccontrol/community-1/static
45
45
46 RequestHeader set X-Forwarded-Proto "https"
46 RequestHeader set X-Forwarded-Proto "https"
47
47
48 ## channelstream websocket handling
48 ## channelstream websocket handling
49 ProxyPass /_channelstream ws://localhost:9800
49 ProxyPass /_channelstream ws://localhost:9800
50 ProxyPassReverse /_channelstream ws://localhost:9800
50 ProxyPassReverse /_channelstream ws://localhost:9800
51
51
52 <Proxy *>
52 <Proxy *>
53 Order allow,deny
53 Order allow,deny
54 Allow from all
54 Allow from all
55 </Proxy>
55 </Proxy>
56
56
57 # Directive to properly generate url (clone url) for RhodeCode
57 # Directive to properly generate url (clone url) for RhodeCode
58 ProxyPreserveHost On
58 ProxyPreserveHost On
59
59
60 # Url to running RhodeCode instance. This is shown as `- URL:` when
60 # Url to running RhodeCode instance. This is shown as `- URL:` when
61 # running rccontrol status.
61 # running rccontrol status.
62 ProxyPass / http://127.0.0.1:10002/
62 ProxyPass / http://127.0.0.1:10002/ timeout=7200 Keepalive=On
63 ProxyPassReverse / http://127.0.0.1:10002/
63 ProxyPassReverse / http://127.0.0.1:10002/
64
64
65 # strict http prevents from https -> http downgrade
65 # strict http prevents from https -> http downgrade
66 Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
66 Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
67
67
68 # Set x-frame options
68 # Set x-frame options
69 Header always append X-Frame-Options SAMEORIGIN
69 Header always append X-Frame-Options SAMEORIGIN
70
70
71 # To enable https use line below
71 # To enable https use line below
72 # SetEnvIf X-Url-Scheme https HTTPS=1
72 # SetEnvIf X-Url-Scheme https HTTPS=1
73
73
74 # SSL setup
74 # SSL setup
75 SSLEngine On
75 SSLEngine On
76 SSLCertificateFile /etc/apache2/ssl/rhodecode.myserver.pem
76 SSLCertificateFile /etc/apache2/ssl/rhodecode.myserver.pem
77 SSLCertificateKeyFile /etc/apache2/ssl/rhodecode.myserver.key
77 SSLCertificateKeyFile /etc/apache2/ssl/rhodecode.myserver.key
78
78
79 SSLProtocol all -SSLv2 -SSLv3
79 SSLProtocol all -SSLv2 -SSLv3
80 SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
80 SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
81 SSLHonorCipherOrder on
81 SSLHonorCipherOrder on
82
82
83 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
83 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
84 #SSLOpenSSLConfCmd DHParameters "/etc/apache2/dhparam.pem"
84 #SSLOpenSSLConfCmd DHParameters "/etc/apache2/dhparam.pem"
85
85
86 </VirtualHost>
86 </VirtualHost>
87
87
@@ -1,130 +1,124 b''
1 .. _debug-mode:
1 .. _debug-mode:
2
2
3 Enabling Debug Mode
3 Enabling Debug Mode
4 -------------------
4 -------------------
5
5
6 To enable debug mode on a |RCE| instance you need to set the debug property
6 To enable debug mode on a |RCE| instance you need to set the debug property
7 in the :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file. To
7 in the :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file. To
8 do this, use the following steps
8 do this, use the following steps
9
9
10 1. Open the file and set the ``debug`` line to ``true``
10 1. Open the file and set the ``debug`` line to ``true``
11 2. Restart you instance using the ``rccontrol restart`` command,
11 2. Restart you instance using the ``rccontrol restart`` command,
12 see the following example:
12 see the following example:
13
13
14 You can also set the log level, the follow are the valid options;
14 You can also set the log level, the follow are the valid options;
15 ``debug``, ``info``, ``warning``, or ``fatal``.
15 ``debug``, ``info``, ``warning``, or ``fatal``.
16
16
17 .. code-block:: ini
17 .. code-block:: ini
18
18
19 [DEFAULT]
19 [DEFAULT]
20 debug = true
20 debug = true
21 pdebug = false
21 pdebug = false
22
22
23 .. code-block:: bash
23 .. code-block:: bash
24
24
25 # Restart your instance
25 # Restart your instance
26 $ rccontrol restart enterprise-1
26 $ rccontrol restart enterprise-1
27 Instance "enterprise-1" successfully stopped.
27 Instance "enterprise-1" successfully stopped.
28 Instance "enterprise-1" successfully started.
28 Instance "enterprise-1" successfully started.
29
29
30 Debug and Logging Configuration
30 Debug and Logging Configuration
31 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
31 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32
32
33 Further debugging and logging settings can also be set in the
33 Further debugging and logging settings can also be set in the
34 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
34 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
35
35
36 In the logging section, the various packages that run with |RCE| can have
36 In the logging section, the various packages that run with |RCE| can have
37 different debug levels set. If you want to increase the logging level change
37 different debug levels set. If you want to increase the logging level change
38 ``level = DEBUG`` line to one of the valid options.
38 ``level = DEBUG`` line to one of the valid options.
39
39
40 You also need to change the log level for handlers. See the example
40 You also need to change the log level for handlers. See the example
41 ``##handler`` section below. The ``handler`` level takes the same options as
41 ``##handler`` section below. The ``handler`` level takes the same options as
42 the ``debug`` level.
42 the ``debug`` level.
43
43
44 .. code-block:: ini
44 .. code-block:: ini
45
45
46 ################################
46 ################################
47 ### LOGGING CONFIGURATION ####
47 ### LOGGING CONFIGURATION ####
48 ################################
48 ################################
49 [loggers]
49 [loggers]
50 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates
50 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
51
51
52 [handlers]
52 [handlers]
53 keys = console, console_sql, file, file_rotating
53 keys = console, console_sql, file, file_rotating
54
54
55 [formatters]
55 [formatters]
56 keys = generic, color_formatter, color_formatter_sql
56 keys = generic, color_formatter, color_formatter_sql
57
57
58 #############
58 #############
59 ## LOGGERS ##
59 ## LOGGERS ##
60 #############
60 #############
61 [logger_root]
61 [logger_root]
62 level = NOTSET
62 level = NOTSET
63 handlers = console
63 handlers = console
64
64
65 [logger_routes]
65 [logger_routes]
66 level = DEBUG
66 level = DEBUG
67 handlers =
67 handlers =
68 qualname = routes.middleware
68 qualname = routes.middleware
69 ## "level = DEBUG" logs the route matched and routing variables.
69 ## "level = DEBUG" logs the route matched and routing variables.
70 propagate = 1
70 propagate = 1
71
71
72 [logger_beaker]
72 [logger_beaker]
73 level = DEBUG
73 level = DEBUG
74 handlers =
74 handlers =
75 qualname = beaker.container
75 qualname = beaker.container
76 propagate = 1
76 propagate = 1
77
77
78 [logger_pyro4]
79 level = DEBUG
80 handlers =
81 qualname = Pyro4
82 propagate = 1
83
84 [logger_templates]
78 [logger_templates]
85 level = INFO
79 level = INFO
86 handlers =
80 handlers =
87 qualname = pylons.templating
81 qualname = pylons.templating
88 propagate = 1
82 propagate = 1
89
83
90 [logger_rhodecode]
84 [logger_rhodecode]
91 level = DEBUG
85 level = DEBUG
92 handlers =
86 handlers =
93 qualname = rhodecode
87 qualname = rhodecode
94 propagate = 1
88 propagate = 1
95
89
96 [logger_sqlalchemy]
90 [logger_sqlalchemy]
97 level = INFO
91 level = INFO
98 handlers = console_sql
92 handlers = console_sql
99 qualname = sqlalchemy.engine
93 qualname = sqlalchemy.engine
100 propagate = 0
94 propagate = 0
101
95
102 ##############
96 ##############
103 ## HANDLERS ##
97 ## HANDLERS ##
104 ##############
98 ##############
105
99
106 [handler_console]
100 [handler_console]
107 class = StreamHandler
101 class = StreamHandler
108 args = (sys.stderr,)
102 args = (sys.stderr,)
109 level = INFO
103 level = INFO
110 formatter = generic
104 formatter = generic
111
105
112 [handler_console_sql]
106 [handler_console_sql]
113 class = StreamHandler
107 class = StreamHandler
114 args = (sys.stderr,)
108 args = (sys.stderr,)
115 level = WARN
109 level = WARN
116 formatter = generic
110 formatter = generic
117
111
118 [handler_file]
112 [handler_file]
119 class = FileHandler
113 class = FileHandler
120 args = ('rhodecode.log', 'a',)
114 args = ('rhodecode.log', 'a',)
121 level = INFO
115 level = INFO
122 formatter = generic
116 formatter = generic
123
117
124 [handler_file_rotating]
118 [handler_file_rotating]
125 class = logging.handlers.TimedRotatingFileHandler
119 class = logging.handlers.TimedRotatingFileHandler
126 # 'D', 5 - rotate every 5days
120 # 'D', 5 - rotate every 5days
127 # you can set 'h', 'midnight'
121 # you can set 'h', 'midnight'
128 args = ('rhodecode.log', 'D', 5, 10,)
122 args = ('rhodecode.log', 'D', 5, 10,)
129 level = INFO
123 level = INFO
130 formatter = generic
124 formatter = generic
@@ -1,138 +1,150 b''
1 .. _svn-http:
1 .. _svn-http:
2
2
3 |svn| With Write Over HTTP
3 |svn| With Write Over HTTP
4 ^^^^^^^^^^^^^^^^^^^^^^^^^^
4 ^^^^^^^^^^^^^^^^^^^^^^^^^^
5
5
6 To use |svn| with read/write support over the |svn| HTTP protocol, you have to
6 To use |svn| with read/write support over the |svn| HTTP protocol, you have to
7 configure the HTTP |svn| backend.
7 configure the HTTP |svn| backend.
8
8
9 Prerequisites
9 Prerequisites
10 =============
10 =============
11
11
12 - Enable HTTP support inside the admin VCS settings on your |RCE| instance
12 - Enable HTTP support inside the admin VCS settings on your |RCE| instance
13 - You need to install the following tools on the machine that is running an
13 - You need to install the following tools on the machine that is running an
14 instance of |RCE|:
14 instance of |RCE|:
15 ``Apache HTTP Server`` and ``mod_dav_svn``.
15 ``Apache HTTP Server`` and ``mod_dav_svn``.
16
16
17
17
18 Using Ubuntu 14.04 Distribution as an example execute the following:
18 Using Ubuntu 14.04 Distribution as an example execute the following:
19
19
20 .. code-block:: bash
20 .. code-block:: bash
21
21
22 $ sudo apt-get install apache2 libapache2-mod-svn
22 $ sudo apt-get install apache2 libapache2-mod-svn
23
23
24 Once installed you need to enable ``dav_svn``:
24 Once installed you need to enable ``dav_svn``:
25
25
26 .. code-block:: bash
26 .. code-block:: bash
27
27
28 $ sudo a2enmod dav_svn
28 $ sudo a2enmod dav_svn
29 $ sudo a2enmod headers
29 $ sudo a2enmod headers
30 $ sudo a2enmod authn_anon
30 $ sudo a2enmod authn_anon
31
31
32
32
33 Configuring Apache Setup
33 Configuring Apache Setup
34 ========================
34 ========================
35
35
36 .. tip::
36 .. tip::
37
37
38 It is recommended to run Apache on a port other than 80, due to possible
38 It is recommended to run Apache on a port other than 80, due to possible
39 conflicts with other HTTP servers like nginx. To do this, set the
39 conflicts with other HTTP servers like nginx. To do this, set the
40 ``Listen`` parameter in the ``/etc/apache2/ports.conf`` file, for example
40 ``Listen`` parameter in the ``/etc/apache2/ports.conf`` file, for example
41 ``Listen 8090``.
41 ``Listen 8090``.
42
42
43
43
44 .. warning::
44 .. warning::
45
45
46 Make sure your Apache instance which runs the mod_dav_svn module is
46 Make sure your Apache instance which runs the mod_dav_svn module is
47 only accessible by |RCE|. Otherwise everyone is able to browse
47 only accessible by |RCE|. Otherwise everyone is able to browse
48 the repositories or run subversion operations (checkout/commit/etc.).
48 the repositories or run subversion operations (checkout/commit/etc.).
49
49
50 It is also recommended to run apache as the same user as |RCE|, otherwise
50 It is also recommended to run apache as the same user as |RCE|, otherwise
51 permission issues could occur. To do this edit the ``/etc/apache2/envvars``
51 permission issues could occur. To do this edit the ``/etc/apache2/envvars``
52
52
53 .. code-block:: apache
53 .. code-block:: apache
54
54
55 export APACHE_RUN_USER=rhodecode
55 export APACHE_RUN_USER=rhodecode
56 export APACHE_RUN_GROUP=rhodecode
56 export APACHE_RUN_GROUP=rhodecode
57
57
58 1. To configure Apache, create and edit a virtual hosts file, for example
58 1. To configure Apache, create and edit a virtual hosts file, for example
59 :file:`/etc/apache2/sites-enabled/default.conf`. Below is an example
59 :file:`/etc/apache2/sites-enabled/default.conf`. Below is an example
60 how to use one with auto-generated config ```mod_dav_svn.conf```
60 how to use one with auto-generated config ```mod_dav_svn.conf```
61 from configured |RCE| instance.
61 from configured |RCE| instance.
62
62
63 .. code-block:: apache
63 .. code-block:: apache
64
64
65 <VirtualHost *:8090>
65 <VirtualHost *:8090>
66 ServerAdmin rhodecode-admin@localhost
66 ServerAdmin rhodecode-admin@localhost
67 DocumentRoot /var/www/html
67 DocumentRoot /var/www/html
68 ErrorLog ${'${APACHE_LOG_DIR}'}/error.log
68 ErrorLog ${'${APACHE_LOG_DIR}'}/error.log
69 CustomLog ${'${APACHE_LOG_DIR}'}/access.log combined
69 CustomLog ${'${APACHE_LOG_DIR}'}/access.log combined
70 Include /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
70 Include /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
71 </VirtualHost>
71 </VirtualHost>
72
72
73
73
74 2. Go to the :menuselection:`Admin --> Settings --> VCS` page, and
74 2. Go to the :menuselection:`Admin --> Settings --> VCS` page, and
75 enable :guilabel:`Proxy Subversion HTTP requests`, and specify the
75 enable :guilabel:`Proxy Subversion HTTP requests`, and specify the
76 :guilabel:`Subversion HTTP Server URL`.
76 :guilabel:`Subversion HTTP Server URL`.
77
77
78 3. Open the |RCE| configuration file,
78 3. Open the |RCE| configuration file,
79 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
79 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
80
80
81 4. Add the following configuration option in the ``[app:main]``
81 4. Add the following configuration option in the ``[app:main]``
82 section if you don't have it yet.
82 section if you don't have it yet.
83
83
84 This enables mapping of the created |RCE| repo groups into special
84 This enables mapping of the created |RCE| repo groups into special
85 |svn| paths. Each time a new repository group is created, the system will
85 |svn| paths. Each time a new repository group is created, the system will
86 update the template file and create new mapping. Apache web server needs to
86 update the template file and create new mapping. Apache web server needs to
87 be reloaded to pick up the changes on this file.
87 be reloaded to pick up the changes on this file.
88 To do this, simply configure `svn.proxy.reload_cmd` inside the .ini file.
88 To do this, simply configure `svn.proxy.reload_cmd` inside the .ini file.
89 Example configuration:
89 Example configuration:
90
90
91
91
92 .. code-block:: ini
92 .. code-block:: ini
93
93
94 ############################################################
94 ############################################################
95 ### Subversion proxy support (mod_dav_svn) ###
95 ### Subversion proxy support (mod_dav_svn) ###
96 ### Maps RhodeCode repo groups into SVN paths for Apache ###
96 ### Maps RhodeCode repo groups into SVN paths for Apache ###
97 ############################################################
97 ############################################################
98 ## Enable or disable the config file generation.
98 ## Enable or disable the config file generation.
99 svn.proxy.generate_config = true
99 svn.proxy.generate_config = true
100 ## Generate config file with `SVNListParentPath` set to `On`.
100 ## Generate config file with `SVNListParentPath` set to `On`.
101 svn.proxy.list_parent_path = true
101 svn.proxy.list_parent_path = true
102 ## Set location and file name of generated config file.
102 ## Set location and file name of generated config file.
103 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
103 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
104 ## Used as a prefix to the <Location> block in the generated config file.
104 ## Used as a prefix to the <Location> block in the generated config file.
105 ## In most cases it should be set to `/`.
105 ## In most cases it should be set to `/`.
106 svn.proxy.location_root = /
106 svn.proxy.location_root = /
107 ## Command to reload the mod dav svn configuration on change.
107 ## Command to reload the mod dav svn configuration on change.
108 ## Example: `/etc/init.d/apache2 reload`
108 ## Example: `/etc/init.d/apache2 reload`
109 svn.proxy.reload_cmd = /etc/init.d/apache2 reload
109 svn.proxy.reload_cmd = /etc/init.d/apache2 reload
110 ## If the timeout expires before the reload command finishes, the command will
110 ## If the timeout expires before the reload command finishes, the command will
111 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
111 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
112 #svn.proxy.reload_timeout = 10
112 #svn.proxy.reload_timeout = 10
113
113
114
114
115 This would create a special template file called ```mod_dav_svn.conf```. We
115 This would create a special template file called ```mod_dav_svn.conf```. We
116 used that file path in the apache config above inside the Include statement.
116 used that file path in the apache config above inside the Include statement.
117 It's also possible to generate the config from the
117 It's also possible to manually generate the config from the
118 :menuselection:`Admin --> Settings --> VCS` page.
118 :menuselection:`Admin --> Settings --> VCS` page by clicking a
119 `Generate Apache Config` button.
120
121 5. Now only things left is to enable svn support, and generate the initial
122 configuration.
123
124 - Select `Proxy subversion HTTP requests` checkbox
125 - Enter http://localhost:8090 into `Subversion HTTP Server URL`
126 - Click the `Generate Apache Config` button.
127
128 This config will be automatically re-generated once an user-groups is added
129 to properly map the additional paths generated.
130
119
131
120
132
121 Using |svn|
133 Using |svn|
122 ===========
134 ===========
123
135
124 Once |svn| has been enabled on your instance, you can use it with the
136 Once |svn| has been enabled on your instance, you can use it with the
125 following examples. For more |svn| information, see the `Subversion Red Book`_
137 following examples. For more |svn| information, see the `Subversion Red Book`_
126
138
127 .. code-block:: bash
139 .. code-block:: bash
128
140
129 # To clone a repository
141 # To clone a repository
130 svn checkout http://my-svn-server.example.com/my-svn-repo
142 svn checkout http://my-svn-server.example.com/my-svn-repo
131
143
132 # svn commit
144 # svn commit
133 svn commit
145 svn commit
134
146
135
147
136 .. _Subversion Red Book: http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.ref.svn
148 .. _Subversion Red Book: http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.ref.svn
137
149
138 .. _Ask Ubuntu: http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue No newline at end of file
150 .. _Ask Ubuntu: http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue
@@ -1,32 +1,14 b''
1 .. _hg-lrg-loc:
1 .. _hg-lrg-loc:
2
2
3 Change the |hg| Large Files Location
3 Change the |hg| Large Files Location
4 ------------------------------------
4 ------------------------------------
5
5
6 |RCE| manages |hg| larges files from the following default location
6 |RCE| manages |hg| larges files from the following default location
7 :file:`/home/{user}/repos/.cache/largefiles`. If you wish to change this, use
7 :file:`/home/{user}/repos/.cache/largefiles`. If you wish to change this, use
8 the following steps:
8 the following steps:
9
9
10 1. Open ishell from the terminal and use it to log into the |RCE| database by
10 1. Open :menuselection:`Admin --> Settings --> VCS` as super-admin.
11 specifying the instance :file:`rhodecode.ini` file.
12
13 .. code-block:: bash
14
15 # Open iShell from the terminal and set ini file
16 $ rccontrol ishell enterprise-1
17
18 2. Run the following commands, and ensure that |RCE| has write access to the
19 new directory:
20
11
21 .. code-block:: bash
12 In section called `Mercurial Settings` you can change where the largefiles
13 objects should be stored.
22
14
23 # Once logged into the database, use SQL to redirect
24 # the large files location
25 In [1]: from rhodecode.model.settings import SettingsModel
26 In [2]: SettingsModel().get_ui_by_key('usercache')
27 Out[2]: <RhodeCodeUi[largefiles]usercache=>/mnt/hgfs/shared/workspace/xxxx/.cache/largefiles]>
28
29 In [3]: largefiles_cache = SettingsModel().get_ui_by_key('usercache')
30 In [4]: largefiles_cache.ui_value = '/new/path’
31 In [5]: Session().add(largefiles_cache);Session().commit()
32
@@ -1,21 +1,22 b''
1 .. _rhodecode-tuning-ref:
1 .. _rhodecode-tuning-ref:
2
2
3 Tuning |RCE|
3 Tuning |RCE|
4 ============
4 ============
5
5
6 To customize your |RCE| |version| installation for maximum performance you
6 To customize your |RCE| |version| installation for maximum performance you
7 may find some of the following methods useful.
7 may find some of the following methods useful.
8
8
9 .. toctree::
9 .. toctree::
10
10
11 tuning-gunicorn
11 tuning-gunicorn
12 tuning-vcs-memory-cache
12 tuning-vcs-memory-cache
13 tuning-user-sessions-performance
13 tuning-user-sessions-performance
14 tuning-increase-db-performance
14 tuning-increase-db-performance
15 tuning-scale-horizontally
15 tuning-scale-horizontally
16 tuning-increase-cache-size
16 tuning-increase-cache-size
17 tuning-mount-cache-memory
17 tuning-mount-cache-memory
18 tuning-change-encoding
18 tuning-change-encoding
19 tuning-change-large-file-dir
19 tuning-change-large-file-dir
20 tuning-change-lfs-dir
20 tuning-hg-auth-loop
21 tuning-hg-auth-loop
21
22
@@ -1,304 +1,298 b''
1 .. _vcs-server:
1 .. _vcs-server:
2
2
3 VCS Server Management
3 VCS Server Management
4 ---------------------
4 ---------------------
5
5
6 The VCS Server handles |RCM| backend functionality. You need to configure
6 The VCS Server handles |RCM| backend functionality. You need to configure
7 a VCS Server to run with a |RCM| instance. If you do not, you will be missing
7 a VCS Server to run with a |RCM| instance. If you do not, you will be missing
8 the connection between |RCM| and its |repos|. This will cause error messages
8 the connection between |RCM| and its |repos|. This will cause error messages
9 on the web interface. You can run your setup in the following configurations,
9 on the web interface. You can run your setup in the following configurations,
10 currently the best performance is one VCS Server per |RCM| instance:
10 currently the best performance is one VCS Server per |RCM| instance:
11
11
12 * One VCS Server per |RCM| instance.
12 * One VCS Server per |RCM| instance.
13 * One VCS Server handling multiple instances.
13 * One VCS Server handling multiple instances.
14
14
15 .. important::
15 .. important::
16
16
17 If your server locale settings are not correctly configured,
17 If your server locale settings are not correctly configured,
18 |RCE| and the VCS Server can run into issues. See this `Ask Ubuntu`_ post
18 |RCE| and the VCS Server can run into issues. See this `Ask Ubuntu`_ post
19 which explains the problem and gives a solution.
19 which explains the problem and gives a solution.
20
20
21 For more information, see the following sections:
21 For more information, see the following sections:
22
22
23 * :ref:`install-vcs`
23 * :ref:`install-vcs`
24 * :ref:`config-vcs`
24 * :ref:`config-vcs`
25 * :ref:`vcs-server-options`
25 * :ref:`vcs-server-options`
26 * :ref:`vcs-server-versions`
26 * :ref:`vcs-server-versions`
27 * :ref:`vcs-server-maintain`
27 * :ref:`vcs-server-maintain`
28 * :ref:`vcs-server-config-file`
28 * :ref:`vcs-server-config-file`
29 * :ref:`svn-http`
29 * :ref:`svn-http`
30
30
31 .. _install-vcs:
31 .. _install-vcs:
32
32
33 VCS Server Installation
33 VCS Server Installation
34 ^^^^^^^^^^^^^^^^^^^^^^^
34 ^^^^^^^^^^^^^^^^^^^^^^^
35
35
36 To install a VCS Server, see
36 To install a VCS Server, see
37 :ref:`Installing a VCS server <control:install-vcsserver>`.
37 :ref:`Installing a VCS server <control:install-vcsserver>`.
38
38
39 .. _config-vcs:
39 .. _config-vcs:
40
40
41 Hooking |RCE| to its VCS Server
41 Hooking |RCE| to its VCS Server
42 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
43
43
44 To configure a |RCE| instance to use a VCS server, see
44 To configure a |RCE| instance to use a VCS server, see
45 :ref:`Configuring the VCS Server connection <control:manually-vcsserver-ini>`.
45 :ref:`Configuring the VCS Server connection <control:manually-vcsserver-ini>`.
46
46
47 .. _vcs-server-options:
47 .. _vcs-server-options:
48
48
49 |RCE| VCS Server Options
49 |RCE| VCS Server Options
50 ^^^^^^^^^^^^^^^^^^^^^^^^
50 ^^^^^^^^^^^^^^^^^^^^^^^^
51
51
52 The following list shows the available options on the |RCM| side of the
52 The following list shows the available options on the |RCM| side of the
53 connection to the VCS Server. The settings are configured per
53 connection to the VCS Server. The settings are configured per
54 instance in the
54 instance in the
55 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
55 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
56
56
57 .. rst-class:: dl-horizontal
57 .. rst-class:: dl-horizontal
58
58
59 \vcs.backends <available-vcs-systems>
59 \vcs.backends <available-vcs-systems>
60 Set a comma-separated list of the |repo| options available from the
60 Set a comma-separated list of the |repo| options available from the
61 web interface. The default is ``hg, git, svn``,
61 web interface. The default is ``hg, git, svn``,
62 which is all |repo| types available.
62 which is all |repo| types available.
63
63
64 \vcs.connection_timeout <seconds>
64 \vcs.connection_timeout <seconds>
65 Set the length of time in seconds that the VCS Server waits for
65 Set the length of time in seconds that the VCS Server waits for
66 requests to process. After the timeout expires,
66 requests to process. After the timeout expires,
67 the request is closed. The default is ``3600``. Set to a higher
67 the request is closed. The default is ``3600``. Set to a higher
68 number if you experience network latency, or timeout issues with very
68 number if you experience network latency, or timeout issues with very
69 large push/pull requests.
69 large push/pull requests.
70
70
71 \vcs.server.enable <boolean>
71 \vcs.server.enable <boolean>
72 Enable or disable the VCS Server. The available options are ``true`` or
72 Enable or disable the VCS Server. The available options are ``true`` or
73 ``false``. The default is ``true``.
73 ``false``. The default is ``true``.
74
74
75 \vcs.server <host:port>
75 \vcs.server <host:port>
76 Set the host, either hostname or IP Address, and port of the VCS server
76 Set the host, either hostname or IP Address, and port of the VCS server
77 you wish to run with your |RCM| instance.
77 you wish to run with your |RCM| instance.
78
78
79 .. code-block:: ini
79 .. code-block:: ini
80
80
81 ##################
81 ##################
82 ### VCS CONFIG ###
82 ### VCS CONFIG ###
83 ##################
83 ##################
84 # set this line to match your VCS Server
84 # set this line to match your VCS Server
85 vcs.server = 127.0.0.1:10004
85 vcs.server = 127.0.0.1:10004
86 # Set to False to disable the VCS Server
86 # Set to False to disable the VCS Server
87 vcs.server.enable = True
87 vcs.server.enable = True
88 vcs.backends = hg, git, svn
88 vcs.backends = hg, git, svn
89 vcs.connection_timeout = 3600
89 vcs.connection_timeout = 3600
90
90
91
91
92 .. _vcs-server-versions:
92 .. _vcs-server-versions:
93
93
94 VCS Server Versions
94 VCS Server Versions
95 ^^^^^^^^^^^^^^^^^^^
95 ^^^^^^^^^^^^^^^^^^^
96
96
97 An updated version of the VCS Server is released with each |RCE| version. Use
97 An updated version of the VCS Server is released with each |RCE| version. Use
98 the VCS Server number that matches with the |RCE| version to pair the
98 the VCS Server number that matches with the |RCE| version to pair the
99 appropriate ones together. For |RCE| versions pre 3.3.0,
99 appropriate ones together. For |RCE| versions pre 3.3.0,
100 VCS Server 1.X.Y works with |RCE| 3.X.Y, for example:
100 VCS Server 1.X.Y works with |RCE| 3.X.Y, for example:
101
101
102 * VCS Server 1.0.0 works with |RCE| 3.0.0
102 * VCS Server 1.0.0 works with |RCE| 3.0.0
103 * VCS Server 1.2.2 works with |RCE| 3.2.2
103 * VCS Server 1.2.2 works with |RCE| 3.2.2
104
104
105 For |RCE| versions post 3.3.0, the VCS Server and |RCE| version numbers
105 For |RCE| versions post 3.3.0, the VCS Server and |RCE| version numbers
106 match, for example:
106 match, for example:
107
107
108 * VCS Server |release| works with |RCE| |release|
108 * VCS Server |release| works with |RCE| |release|
109
109
110 .. _vcs-server-maintain:
110 .. _vcs-server-maintain:
111
111
112 VCS Server Memory Optimization
112 VCS Server Memory Optimization
113 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
113 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
114
114
115 To configure the VCS server to manage the cache efficiently, you need to
115 To configure the VCS server to manage the cache efficiently, you need to
116 configure the following options in the
116 configure the following options in the
117 :file:`/home/{user}/.rccontrol/{vcsserver-id}/vcsserver.ini` file. Once
117 :file:`/home/{user}/.rccontrol/{vcsserver-id}/vcsserver.ini` file. Once
118 configured, restart the VCS Server.
118 configured, restart the VCS Server.
119
119
120 .. rst-class:: dl-horizontal
120 .. rst-class:: dl-horizontal
121
121
122 \beaker.cache.repo_object.type = memorylru
122 \beaker.cache.repo_object.type = memorylru
123 Configures the cache to discard the least recently used items.
123 Configures the cache to discard the least recently used items.
124 This setting takes the following valid options:
124 This setting takes the following valid options:
125
125
126 * ``memorylru``: The default setting, which removes the least recently
126 * ``memorylru``: The default setting, which removes the least recently
127 used items from the cache.
127 used items from the cache.
128 * ``memory``: Runs the VCS Server without clearing the cache.
128 * ``memory``: Runs the VCS Server without clearing the cache.
129 * ``nocache``: Runs the VCS Server without a cache. This will
129 * ``nocache``: Runs the VCS Server without a cache. This will
130 dramatically reduce the VCS Server performance.
130 dramatically reduce the VCS Server performance.
131
131
132 \beaker.cache.repo_object.max_items = 100
132 \beaker.cache.repo_object.max_items = 100
133 Sets the maximum number of items stored in the cache, before the cache
133 Sets the maximum number of items stored in the cache, before the cache
134 starts to be cleared.
134 starts to be cleared.
135
135
136 As a general rule of thumb, running this value at 120 resulted in a
136 As a general rule of thumb, running this value at 120 resulted in a
137 5GB cache. Running it at 240 resulted in a 9GB cache. Your results
137 5GB cache. Running it at 240 resulted in a 9GB cache. Your results
138 will differ based on usage patterns and |repo| sizes.
138 will differ based on usage patterns and |repo| sizes.
139
139
140 Tweaking this value to run at a fairly constant memory load on your
140 Tweaking this value to run at a fairly constant memory load on your
141 server will help performance.
141 server will help performance.
142
142
143 To clear the cache completely, you can restart the VCS Server.
143 To clear the cache completely, you can restart the VCS Server.
144
144
145 .. important::
145 .. important::
146
146
147 While the VCS Server handles a restart gracefully on the web interface,
147 While the VCS Server handles a restart gracefully on the web interface,
148 it will drop connections during push/pull requests. So it is recommended
148 it will drop connections during push/pull requests. So it is recommended
149 you only perform this when there is very little traffic on the instance.
149 you only perform this when there is very little traffic on the instance.
150
150
151 Use the following example to restart your VCS Server,
151 Use the following example to restart your VCS Server,
152 for full details see the :ref:`RhodeCode Control CLI <control:rcc-cli>`.
152 for full details see the :ref:`RhodeCode Control CLI <control:rcc-cli>`.
153
153
154 .. code-block:: bash
154 .. code-block:: bash
155
155
156 $ rccontrol status
156 $ rccontrol status
157
157
158 .. code-block:: vim
158 .. code-block:: vim
159
159
160 - NAME: vcsserver-1
160 - NAME: vcsserver-1
161 - STATUS: RUNNING
161 - STATUS: RUNNING
162 - TYPE: VCSServer
162 - TYPE: VCSServer
163 - VERSION: 1.0.0
163 - VERSION: 1.0.0
164 - URL: http://127.0.0.1:10001
164 - URL: http://127.0.0.1:10001
165
165
166 $ rccontrol restart vcsserver-1
166 $ rccontrol restart vcsserver-1
167 Instance "vcsserver-1" successfully stopped.
167 Instance "vcsserver-1" successfully stopped.
168 Instance "vcsserver-1" successfully started.
168 Instance "vcsserver-1" successfully started.
169
169
170 .. _vcs-server-config-file:
170 .. _vcs-server-config-file:
171
171
172 VCS Server Configuration
172 VCS Server Configuration
173 ^^^^^^^^^^^^^^^^^^^^^^^^
173 ^^^^^^^^^^^^^^^^^^^^^^^^
174
174
175 You can configure settings for multiple VCS Servers on your
175 You can configure settings for multiple VCS Servers on your
176 system using their individual configuration files. Use the following
176 system using their individual configuration files. Use the following
177 properties inside the configuration file to set up your system. The default
177 properties inside the configuration file to set up your system. The default
178 location is :file:`home/{user}/.rccontrol/{vcsserver-id}/vcsserver.ini`.
178 location is :file:`home/{user}/.rccontrol/{vcsserver-id}/vcsserver.ini`.
179 For a more detailed explanation of the logger levers, see :ref:`debug-mode`.
179 For a more detailed explanation of the logger levers, see :ref:`debug-mode`.
180
180
181 .. rst-class:: dl-horizontal
181 .. rst-class:: dl-horizontal
182
182
183 \host <ip-address>
183 \host <ip-address>
184 Set the host on which the VCS Server will run.
184 Set the host on which the VCS Server will run.
185
185
186 \port <int>
186 \port <int>
187 Set the port number on which the VCS Server will be available.
187 Set the port number on which the VCS Server will be available.
188
188
189 \locale <locale_utf>
189 \locale <locale_utf>
190 Set the locale the VCS Server expects.
190 Set the locale the VCS Server expects.
191
191
192 \threadpool_size <int>
192 \threadpool_size <int>
193 Set the size of the threadpool used to communicate
193 Set the size of the threadpool used to communicate
194 with the WSGI workers. This should be at least 6 times the number of
194 with the WSGI workers. This should be at least 6 times the number of
195 WSGI worker processes.
195 WSGI worker processes.
196
196
197 \timeout <seconds>
197 \timeout <seconds>
198 Set the timeout for RPC communication in seconds.
198 Set the timeout for RPC communication in seconds.
199
199
200 .. note::
200 .. note::
201
201
202 After making changes, you need to restart your VCS Server to pick them up.
202 After making changes, you need to restart your VCS Server to pick them up.
203
203
204 .. code-block:: ini
204 .. code-block:: ini
205
205
206 ################################################################################
206 ################################################################################
207 # RhodeCode VCSServer - configuration #
207 # RhodeCode VCSServer - configuration #
208 # #
208 # #
209 ################################################################################
209 ################################################################################
210
210
211 [DEFAULT]
211 [DEFAULT]
212 host = 127.0.0.1
212 host = 127.0.0.1
213 port = 9900
213 port = 9900
214 locale = en_US.UTF-8
214 locale = en_US.UTF-8
215 # number of worker threads, this should be set based on a formula threadpool=N*6
215 # number of worker threads, this should be set based on a formula threadpool=N*6
216 # where N is number of RhodeCode Enterprise workers, eg. running 2 instances
216 # where N is number of RhodeCode Enterprise workers, eg. running 2 instances
217 # 8 gunicorn workers each would be 2 * 8 * 6 = 96, threadpool_size = 96
217 # 8 gunicorn workers each would be 2 * 8 * 6 = 96, threadpool_size = 96
218 threadpool_size = 16
218 threadpool_size = 16
219 timeout = 0
219 timeout = 0
220
220
221 # cache regions, please don't change
221 # cache regions, please don't change
222 beaker.cache.regions = repo_object
222 beaker.cache.regions = repo_object
223 beaker.cache.repo_object.type = memorylru
223 beaker.cache.repo_object.type = memorylru
224 beaker.cache.repo_object.max_items = 1000
224 beaker.cache.repo_object.max_items = 1000
225
225
226 # cache auto-expires after N seconds
226 # cache auto-expires after N seconds
227 beaker.cache.repo_object.expire = 10
227 beaker.cache.repo_object.expire = 10
228 beaker.cache.repo_object.enabled = true
228 beaker.cache.repo_object.enabled = true
229
229
230
230
231 ################################
231 ################################
232 ### LOGGING CONFIGURATION ####
232 ### LOGGING CONFIGURATION ####
233 ################################
233 ################################
234 [loggers]
234 [loggers]
235 keys = root, vcsserver, pyro4, beaker
235 keys = root, vcsserver, beaker
236
236
237 [handlers]
237 [handlers]
238 keys = console
238 keys = console
239
239
240 [formatters]
240 [formatters]
241 keys = generic
241 keys = generic
242
242
243 #############
243 #############
244 ## LOGGERS ##
244 ## LOGGERS ##
245 #############
245 #############
246 [logger_root]
246 [logger_root]
247 level = NOTSET
247 level = NOTSET
248 handlers = console
248 handlers = console
249
249
250 [logger_vcsserver]
250 [logger_vcsserver]
251 level = DEBUG
251 level = DEBUG
252 handlers =
252 handlers =
253 qualname = vcsserver
253 qualname = vcsserver
254 propagate = 1
254 propagate = 1
255
255
256 [logger_beaker]
256 [logger_beaker]
257 level = DEBUG
257 level = DEBUG
258 handlers =
258 handlers =
259 qualname = beaker
259 qualname = beaker
260 propagate = 1
260 propagate = 1
261
261
262 [logger_pyro4]
263 level = DEBUG
264 handlers =
265 qualname = Pyro4
266 propagate = 1
267
268
262
269 ##############
263 ##############
270 ## HANDLERS ##
264 ## HANDLERS ##
271 ##############
265 ##############
272
266
273 [handler_console]
267 [handler_console]
274 class = StreamHandler
268 class = StreamHandler
275 args = (sys.stderr,)
269 args = (sys.stderr,)
276 level = DEBUG
270 level = DEBUG
277 formatter = generic
271 formatter = generic
278
272
279 [handler_file]
273 [handler_file]
280 class = FileHandler
274 class = FileHandler
281 args = ('vcsserver.log', 'a',)
275 args = ('vcsserver.log', 'a',)
282 level = DEBUG
276 level = DEBUG
283 formatter = generic
277 formatter = generic
284
278
285 [handler_file_rotating]
279 [handler_file_rotating]
286 class = logging.handlers.TimedRotatingFileHandler
280 class = logging.handlers.TimedRotatingFileHandler
287 # 'D', 5 - rotate every 5days
281 # 'D', 5 - rotate every 5days
288 # you can set 'h', 'midnight'
282 # you can set 'h', 'midnight'
289 args = ('vcsserver.log', 'D', 5, 10,)
283 args = ('vcsserver.log', 'D', 5, 10,)
290 level = DEBUG
284 level = DEBUG
291 formatter = generic
285 formatter = generic
292
286
293 ################
287 ################
294 ## FORMATTERS ##
288 ## FORMATTERS ##
295 ################
289 ################
296
290
297 [formatter_generic]
291 [formatter_generic]
298 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
292 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
299 datefmt = %Y-%m-%d %H:%M:%S
293 datefmt = %Y-%m-%d %H:%M:%S
300
294
301
295
302 .. _Subversion Red Book: http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.ref.svn
296 .. _Subversion Red Book: http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.ref.svn
303
297
304 .. _Ask Ubuntu: http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue
298 .. _Ask Ubuntu: http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue
@@ -1,207 +1,207 b''
1 .. _api:
1 .. _api:
2
2
3 API Documentation
3 API Documentation
4 =================
4 =================
5
5
6 The |RCE| API uses a single scheme for calling all API methods. The API is
6 The |RCE| API uses a single scheme for calling all API methods. The API is
7 implemented with JSON protocol in both directions. To send API requests to
7 implemented with JSON protocol in both directions. To send API requests to
8 your instance of |RCE|, use the following URL format
8 your instance of |RCE|, use the following URL format
9 ``<your_server>/_admin``
9 ``<your_server>/_admin``
10
10
11 .. note::
11 .. note::
12
12
13 To use the API, you should configure the :file:`~/.rhoderc` file with
13 To use the API, you should configure the :file:`~/.rhoderc` file with
14 access details per instance. For more information, see
14 access details per instance. For more information, see
15 :ref:`config-rhoderc`.
15 :ref:`config-rhoderc`.
16
16
17
17
18 API ACCESS FOR WEB VIEWS
18 API ACCESS FOR WEB VIEWS
19 ------------------------
19 ------------------------
20
20
21 API access can also be turned on for each web view in |RCE| that is
21 API access can also be turned on for each web view in |RCE| that is
22 decorated with a `@LoginRequired` decorator. To enable API access, change
22 decorated with a `@LoginRequired` decorator. To enable API access, change
23 the standard login decorator to `@LoginRequired(api_access=True)`.
23 the standard login decorator to `@LoginRequired(api_access=True)`.
24
24
25 From |RCM| version 1.7.0 you can configure a white list
25 From |RCM| version 1.7.0 you can configure a white list
26 of views that have API access enabled by default. To enable these,
26 of views that have API access enabled by default. To enable these,
27 edit the |RCM| configuration ``.ini`` file. The default location is:
27 edit the |RCM| configuration ``.ini`` file. The default location is:
28
28
29 * |RCM| Pre-2.2.7 :file:`root/rhodecode/data/production.ini`
29 * |RCM| Pre-2.2.7 :file:`root/rhodecode/data/production.ini`
30 * |RCM| 3.0 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
30 * |RCM| 3.0 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
31
31
32 To configure the white list, edit this section of the file. In this
32 To configure the white list, edit this section of the file. In this
33 configuration example, API access is granted to the patch/diff raw file and
33 configuration example, API access is granted to the patch/diff raw file and
34 archive.
34 archive.
35
35
36 .. code-block:: ini
36 .. code-block:: ini
37
37
38 ## List of controllers (using glob syntax) that AUTH TOKENS could be used for access.
38 ## List of controllers (using glob syntax) that AUTH TOKENS could be used for access.
39 ## Adding ?auth_token = <token> to the url authenticates this request as if it
39 ## Adding ?auth_token = <token> to the url authenticates this request as if it
40 ## came from the the logged in user who own this authentication token.
40 ## came from the the logged in user who own this authentication token.
41 ##
41 ##
42 ## Syntax is <ControllerClass>:<function_pattern>.
42 ## Syntax is <ControllerClass>:<function_pattern>.
43 ## The list should be "," separated and on a single line.
43 ## The list should be "," separated and on a single line.
44 ##
44 ##
45 api_access_controllers_whitelist = ChangesetController:changeset_patch,ChangesetController:changeset_raw,ilesController:raw,FilesController:archivefile,
45 api_access_controllers_whitelist = ChangesetController:changeset_patch,ChangesetController:changeset_raw,ilesController:raw,FilesController:archivefile,
46
46
47 After this change, a |RCE| view can be accessed without login by adding a
47 After this change, a |RCE| view can be accessed without login by adding a
48 GET parameter ``?auth_token=<auth_token>`` to a url. For example to
48 GET parameter ``?auth_token=<auth_token>`` to a url. For example to
49 access the raw diff.
49 access the raw diff.
50
50
51 .. code-block:: html
51 .. code-block:: html
52
52
53 http://<server>/<repo>/changeset-diff/<sha>?auth_token=<auth_token>
53 http://<server>/<repo>/changeset-diff/<sha>?auth_token=<auth_token>
54
54
55 By default this is only enabled on RSS/ATOM feed views. Exposing raw diffs is a
55 By default this is only enabled on RSS/ATOM feed views. Exposing raw diffs is a
56 good way to integrate with 3rd party services like code review, or build farms
56 good way to integrate with 3rd party services like code review, or build farms
57 that could download archives.
57 that could download archives.
58
58
59 API ACCESS
59 API ACCESS
60 ----------
60 ----------
61
61
62 All clients are required to send JSON-RPC spec JSON data.
62 All clients are required to send JSON-RPC spec JSON data.
63
63
64 .. code-block:: bash
64 .. code-block:: bash
65
65
66 {
66 {
67 "id:"<id>",
67 "id:"<id>",
68 "auth_token":"<auth_token>",
68 "auth_token":"<auth_token>",
69 "method":"<method_name>",
69 "method":"<method_name>",
70 "args":{"<arg_key>":"<arg_val>"}
70 "args":{"<arg_key>":"<arg_val>"}
71 }
71 }
72
72
73 Example call for auto pulling from remote repositories using curl:
73 Example call for auto pulling from remote repositories using curl:
74
74
75 .. code-block:: bash
75 .. code-block:: bash
76
76
77 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,
77 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,
78 "auth_token":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull", "args":{"repo":"CPython"}}'
78 "auth_token":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull", "args":{"repoid":"CPython"}}'
79
79
80 Provide those parameters:
80 Provide those parameters:
81 - **id** A value of any type, which is used to match the response with the
81 - **id** A value of any type, which is used to match the response with the
82 request that it is replying to.
82 request that it is replying to.
83 - **auth_token** for access and permission validation.
83 - **auth_token** for access and permission validation.
84 - **method** is name of method to call
84 - **method** is name of method to call
85 - **args** is an ``key:value`` list of arguments to pass to method
85 - **args** is an ``key:value`` list of arguments to pass to method
86
86
87 .. note::
87 .. note::
88
88
89 To get your |authtoken|, from the |RCE| interface,
89 To get your |authtoken|, from the |RCE| interface,
90 go to:
90 go to:
91 :menuselection:`username --> My account --> Auth tokens`
91 :menuselection:`username --> My account --> Auth tokens`
92
92
93 For security reasons you should always create a dedicated |authtoken| for
93 For security reasons you should always create a dedicated |authtoken| for
94 API use only.
94 API use only.
95
95
96
96
97 The |RCE| API will always return a JSON-RPC response:
97 The |RCE| API will always return a JSON-RPC response:
98
98
99 .. code-block:: bash
99 .. code-block:: bash
100
100
101 {
101 {
102 "id": <id>, # matching id sent by request
102 "id": <id>, # matching id sent by request
103 "result": "<result>"|null, # JSON formatted result, null if any errors
103 "result": "<result>"|null, # JSON formatted result, null if any errors
104 "error": "null"|<error_message> # JSON formatted error (if any)
104 "error": "null"|<error_message> # JSON formatted error (if any)
105 }
105 }
106
106
107 All responses from API will be with `HTTP/1.0 200 OK` status code.
107 All responses from API will be with `HTTP/1.0 200 OK` status code.
108 If there is an error when calling the API, the *error* key will contain a
108 If there is an error when calling the API, the *error* key will contain a
109 failure description and the *result* will be `null`.
109 failure description and the *result* will be `null`.
110
110
111 API CLIENT
111 API CLIENT
112 ----------
112 ----------
113
113
114 To install the |RCE| API, see :ref:`install-tools`. To configure the API per
114 To install the |RCE| API, see :ref:`install-tools`. To configure the API per
115 instance, see the :ref:`rc-tools` section as you need to configure a
115 instance, see the :ref:`rc-tools` section as you need to configure a
116 :file:`~/.rhoderc` file with your |authtokens|.
116 :file:`~/.rhoderc` file with your |authtokens|.
117
117
118 Once you have set up your instance API access, use the following examples to
118 Once you have set up your instance API access, use the following examples to
119 get started.
119 get started.
120
120
121 .. code-block:: bash
121 .. code-block:: bash
122
122
123 # Getting the 'rhodecode' repository
123 # Getting the 'rhodecode' repository
124 # from a RhodeCode Enterprise instance
124 # from a RhodeCode Enterprise instance
125 rhodecode-api --instance-name=enterprise-1 get_repo repoid:rhodecode
125 rhodecode-api --instance-name=enterprise-1 get_repo repoid:rhodecode
126
126
127 Calling method get_repo => http://127.0.0.1:5000
127 Calling method get_repo => http://127.0.0.1:5000
128 Server response
128 Server response
129 {
129 {
130 <json data>
130 <json data>
131 }
131 }
132
132
133 # Creating a new mercurial repository called 'brand-new'
133 # Creating a new mercurial repository called 'brand-new'
134 # with a description 'Repo-description'
134 # with a description 'Repo-description'
135 rhodecode-api --instance-name=enterprise-1 create_repo repo_name:brand-new repo_type:hg description:Repo-description
135 rhodecode-api --instance-name=enterprise-1 create_repo repo_name:brand-new repo_type:hg description:Repo-description
136 {
136 {
137 "error": null,
137 "error": null,
138 "id": 1110,
138 "id": 1110,
139 "result": {
139 "result": {
140 "msg": "Created new repository `brand-new`",
140 "msg": "Created new repository `brand-new`",
141 "success": true,
141 "success": true,
142 "task": null
142 "task": null
143 }
143 }
144 }
144 }
145
145
146 A broken example, what not to do.
146 A broken example, what not to do.
147
147
148 .. code-block:: bash
148 .. code-block:: bash
149
149
150 # A call missing the required arguments
150 # A call missing the required arguments
151 # and not specifying the instance
151 # and not specifying the instance
152 rhodecode-api get_repo
152 rhodecode-api get_repo
153
153
154 Calling method get_repo => http://127.0.0.1:5000
154 Calling method get_repo => http://127.0.0.1:5000
155 Server response
155 Server response
156 "Missing non optional `repoid` arg in JSON DATA"
156 "Missing non optional `repoid` arg in JSON DATA"
157
157
158 You can specify pure JSON using the ``--format`` parameter.
158 You can specify pure JSON using the ``--format`` parameter.
159
159
160 .. code-block:: bash
160 .. code-block:: bash
161
161
162 rhodecode-api --format=json get_repo repoid:rhodecode
162 rhodecode-api --format=json get_repo repoid:rhodecode
163
163
164 In such case only output that this function shows is pure JSON, we can use that
164 In such case only output that this function shows is pure JSON, we can use that
165 and pipe output to some json formatter.
165 and pipe output to some json formatter.
166
166
167 If output is in pure JSON format, you can pipe output to a JSON formatter.
167 If output is in pure JSON format, you can pipe output to a JSON formatter.
168
168
169 .. code-block:: bash
169 .. code-block:: bash
170
170
171 rhodecode-api --instance-name=enterprise-1 --format=json get_repo repoid:rhodecode | python -m json.tool
171 rhodecode-api --instance-name=enterprise-1 --format=json get_repo repoid:rhodecode | python -m json.tool
172
172
173 API METHODS
173 API METHODS
174 -----------
174 -----------
175
175
176 Each method by default required following arguments.
176 Each method by default required following arguments.
177
177
178 .. code-block:: bash
178 .. code-block:: bash
179
179
180 id : "<id_for_response>"
180 id : "<id_for_response>"
181 auth_token : "<auth_token>"
181 auth_token : "<auth_token>"
182 method : "<method name>"
182 method : "<method name>"
183 args : {}
183 args : {}
184
184
185 Use each **param** from docs and put it in args, Optional parameters
185 Use each **param** from docs and put it in args, Optional parameters
186 are not required in args.
186 are not required in args.
187
187
188 .. code-block:: bash
188 .. code-block:: bash
189
189
190 args: {"repoid": "rhodecode"}
190 args: {"repoid": "rhodecode"}
191
191
192 .. Note: From this point on things are generated by the script in
192 .. Note: From this point on things are generated by the script in
193 `scripts/fabfile.py`. To change things below, update the docstrings in the
193 `scripts/fabfile.py`. To change things below, update the docstrings in the
194 ApiController.
194 ApiController.
195
195
196 .. --- API DEFS MARKER ---
196 .. --- API DEFS MARKER ---
197 .. toctree::
197 .. toctree::
198
198
199 methods/license-methods
199 methods/license-methods
200 methods/deprecated-methods
200 methods/deprecated-methods
201 methods/gist-methods
201 methods/gist-methods
202 methods/pull-request-methods
202 methods/pull-request-methods
203 methods/repo-methods
203 methods/repo-methods
204 methods/repo-group-methods
204 methods/repo-group-methods
205 methods/server-methods
205 methods/server-methods
206 methods/user-methods
206 methods/user-methods
207 methods/user-group-methods
207 methods/user-group-methods
@@ -1,376 +1,371 b''
1 .. _pull-request-methods-ref:
1 .. _pull-request-methods-ref:
2
2
3 pull_request methods
3 pull_request methods
4 ====================
4 ====================
5
5
6 close_pull_request
6 close_pull_request
7 ------------------
7 ------------------
8
8
9 .. py:function:: close_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
9 .. py:function:: close_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
10
10
11 Close the pull request specified by `pullrequestid`.
11 Close the pull request specified by `pullrequestid`.
12
12
13 :param apiuser: This is filled automatically from the |authtoken|.
13 :param apiuser: This is filled automatically from the |authtoken|.
14 :type apiuser: AuthUser
14 :type apiuser: AuthUser
15 :param repoid: Repository name or repository ID to which the pull
15 :param repoid: Repository name or repository ID to which the pull
16 request belongs.
16 request belongs.
17 :type repoid: str or int
17 :type repoid: str or int
18 :param pullrequestid: ID of the pull request to be closed.
18 :param pullrequestid: ID of the pull request to be closed.
19 :type pullrequestid: int
19 :type pullrequestid: int
20 :param userid: Close the pull request as this user.
20 :param userid: Close the pull request as this user.
21 :type userid: Optional(str or int)
21 :type userid: Optional(str or int)
22
22
23 Example output:
23 Example output:
24
24
25 .. code-block:: bash
25 .. code-block:: bash
26
26
27 "id": <id_given_in_input>,
27 "id": <id_given_in_input>,
28 "result":
28 "result": {
29 {
30 "pull_request_id": "<int>",
29 "pull_request_id": "<int>",
31 "closed": "<bool>"
30 "closed": "<bool>"
32 },
31 },
33 "error": null
32 "error": null
34
33
35
34
36 comment_pull_request
35 comment_pull_request
37 --------------------
36 --------------------
38
37
39 .. py:function:: comment_pull_request(apiuser, repoid, pullrequestid, message=<Optional:None>, commit_id=<Optional:None>, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
38 .. py:function:: comment_pull_request(apiuser, repoid, pullrequestid, message=<Optional:None>, commit_id=<Optional:None>, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
40
39
41 Comment on the pull request specified with the `pullrequestid`,
40 Comment on the pull request specified with the `pullrequestid`,
42 in the |repo| specified by the `repoid`, and optionally change the
41 in the |repo| specified by the `repoid`, and optionally change the
43 review status.
42 review status.
44
43
45 :param apiuser: This is filled automatically from the |authtoken|.
44 :param apiuser: This is filled automatically from the |authtoken|.
46 :type apiuser: AuthUser
45 :type apiuser: AuthUser
47 :param repoid: The repository name or repository ID.
46 :param repoid: The repository name or repository ID.
48 :type repoid: str or int
47 :type repoid: str or int
49 :param pullrequestid: The pull request ID.
48 :param pullrequestid: The pull request ID.
50 :type pullrequestid: int
49 :type pullrequestid: int
51 :param commit_id: Specify the commit_id for which to set a comment. If
50 :param commit_id: Specify the commit_id for which to set a comment. If
52 given commit_id is different than latest in the PR status
51 given commit_id is different than latest in the PR status
53 change won't be performed.
52 change won't be performed.
54 :type commit_id: str
53 :type commit_id: str
55 :param message: The text content of the comment.
54 :param message: The text content of the comment.
56 :type message: str
55 :type message: str
57 :param status: (**Optional**) Set the approval status of the pull
56 :param status: (**Optional**) Set the approval status of the pull
58 request. One of: 'not_reviewed', 'approved', 'rejected',
57 request. One of: 'not_reviewed', 'approved', 'rejected',
59 'under_review'
58 'under_review'
60 :type status: str
59 :type status: str
61 :param comment_type: Comment type, one of: 'note', 'todo'
60 :param comment_type: Comment type, one of: 'note', 'todo'
62 :type comment_type: Optional(str), default: 'note'
61 :type comment_type: Optional(str), default: 'note'
63 :param userid: Comment on the pull request as this user
62 :param userid: Comment on the pull request as this user
64 :type userid: Optional(str or int)
63 :type userid: Optional(str or int)
65
64
66 Example output:
65 Example output:
67
66
68 .. code-block:: bash
67 .. code-block:: bash
69
68
70 id : <id_given_in_input>
69 id : <id_given_in_input>
71 result :
70 result : {
72 {
73 "pull_request_id": "<Integer>",
71 "pull_request_id": "<Integer>",
74 "comment_id": "<Integer>",
72 "comment_id": "<Integer>",
75 "status": {"given": <given_status>,
73 "status": {"given": <given_status>,
76 "was_changed": <bool status_was_actually_changed> },
74 "was_changed": <bool status_was_actually_changed> },
77 }
75 },
78 error : null
76 error : null
79
77
80
78
81 create_pull_request
79 create_pull_request
82 -------------------
80 -------------------
83
81
84 .. py:function:: create_pull_request(apiuser, source_repo, target_repo, source_ref, target_ref, title, description=<Optional:''>, reviewers=<Optional:None>)
82 .. py:function:: create_pull_request(apiuser, source_repo, target_repo, source_ref, target_ref, title, description=<Optional:''>, reviewers=<Optional:None>)
85
83
86 Creates a new pull request.
84 Creates a new pull request.
87
85
88 Accepts refs in the following formats:
86 Accepts refs in the following formats:
89
87
90 * branch:<branch_name>:<sha>
88 * branch:<branch_name>:<sha>
91 * branch:<branch_name>
89 * branch:<branch_name>
92 * bookmark:<bookmark_name>:<sha> (Mercurial only)
90 * bookmark:<bookmark_name>:<sha> (Mercurial only)
93 * bookmark:<bookmark_name> (Mercurial only)
91 * bookmark:<bookmark_name> (Mercurial only)
94
92
95 :param apiuser: This is filled automatically from the |authtoken|.
93 :param apiuser: This is filled automatically from the |authtoken|.
96 :type apiuser: AuthUser
94 :type apiuser: AuthUser
97 :param source_repo: Set the source repository name.
95 :param source_repo: Set the source repository name.
98 :type source_repo: str
96 :type source_repo: str
99 :param target_repo: Set the target repository name.
97 :param target_repo: Set the target repository name.
100 :type target_repo: str
98 :type target_repo: str
101 :param source_ref: Set the source ref name.
99 :param source_ref: Set the source ref name.
102 :type source_ref: str
100 :type source_ref: str
103 :param target_ref: Set the target ref name.
101 :param target_ref: Set the target ref name.
104 :type target_ref: str
102 :type target_ref: str
105 :param title: Set the pull request title.
103 :param title: Set the pull request title.
106 :type title: str
104 :type title: str
107 :param description: Set the pull request description.
105 :param description: Set the pull request description.
108 :type description: Optional(str)
106 :type description: Optional(str)
109 :param reviewers: Set the new pull request reviewers list.
107 :param reviewers: Set the new pull request reviewers list.
110 :type reviewers: Optional(list)
108 :type reviewers: Optional(list)
111 Accepts username strings or objects of the format:
109 Accepts username strings or objects of the format:
112 {
110
113 'username': 'nick', 'reasons': ['original author']
111 {'username': 'nick', 'reasons': ['original author']}
114 }
115
112
116
113
117 get_pull_request
114 get_pull_request
118 ----------------
115 ----------------
119
116
120 .. py:function:: get_pull_request(apiuser, repoid, pullrequestid)
117 .. py:function:: get_pull_request(apiuser, repoid, pullrequestid)
121
118
122 Get a pull request based on the given ID.
119 Get a pull request based on the given ID.
123
120
124 :param apiuser: This is filled automatically from the |authtoken|.
121 :param apiuser: This is filled automatically from the |authtoken|.
125 :type apiuser: AuthUser
122 :type apiuser: AuthUser
126 :param repoid: Repository name or repository ID from where the pull
123 :param repoid: Repository name or repository ID from where the pull
127 request was opened.
124 request was opened.
128 :type repoid: str or int
125 :type repoid: str or int
129 :param pullrequestid: ID of the requested pull request.
126 :param pullrequestid: ID of the requested pull request.
130 :type pullrequestid: int
127 :type pullrequestid: int
131
128
132 Example output:
129 Example output:
133
130
134 .. code-block:: bash
131 .. code-block:: bash
135
132
136 "id": <id_given_in_input>,
133 "id": <id_given_in_input>,
137 "result":
134 "result":
138 {
135 {
139 "pull_request_id": "<pull_request_id>",
136 "pull_request_id": "<pull_request_id>",
140 "url": "<url>",
137 "url": "<url>",
141 "title": "<title>",
138 "title": "<title>",
142 "description": "<description>",
139 "description": "<description>",
143 "status" : "<status>",
140 "status" : "<status>",
144 "created_on": "<date_time_created>",
141 "created_on": "<date_time_created>",
145 "updated_on": "<date_time_updated>",
142 "updated_on": "<date_time_updated>",
146 "commit_ids": [
143 "commit_ids": [
147 ...
144 ...
148 "<commit_id>",
145 "<commit_id>",
149 "<commit_id>",
146 "<commit_id>",
150 ...
147 ...
151 ],
148 ],
152 "review_status": "<review_status>",
149 "review_status": "<review_status>",
153 "mergeable": {
150 "mergeable": {
154 "status": "<bool>",
151 "status": "<bool>",
155 "message": "<message>",
152 "message": "<message>",
156 },
153 },
157 "source": {
154 "source": {
158 "clone_url": "<clone_url>",
155 "clone_url": "<clone_url>",
159 "repository": "<repository_name>",
156 "repository": "<repository_name>",
160 "reference":
157 "reference":
161 {
158 {
162 "name": "<name>",
159 "name": "<name>",
163 "type": "<type>",
160 "type": "<type>",
164 "commit_id": "<commit_id>",
161 "commit_id": "<commit_id>",
165 }
162 }
166 },
163 },
167 "target": {
164 "target": {
168 "clone_url": "<clone_url>",
165 "clone_url": "<clone_url>",
169 "repository": "<repository_name>",
166 "repository": "<repository_name>",
170 "reference":
167 "reference":
171 {
168 {
172 "name": "<name>",
169 "name": "<name>",
173 "type": "<type>",
170 "type": "<type>",
174 "commit_id": "<commit_id>",
171 "commit_id": "<commit_id>",
175 }
172 }
176 },
173 },
177 "merge": {
174 "merge": {
178 "clone_url": "<clone_url>",
175 "clone_url": "<clone_url>",
179 "reference":
176 "reference":
180 {
177 {
181 "name": "<name>",
178 "name": "<name>",
182 "type": "<type>",
179 "type": "<type>",
183 "commit_id": "<commit_id>",
180 "commit_id": "<commit_id>",
184 }
181 }
185 },
182 },
186 "author": <user_obj>,
183 "author": <user_obj>,
187 "reviewers": [
184 "reviewers": [
188 ...
185 ...
189 {
186 {
190 "user": "<user_obj>",
187 "user": "<user_obj>",
191 "review_status": "<review_status>",
188 "review_status": "<review_status>",
192 }
189 }
193 ...
190 ...
194 ]
191 ]
195 },
192 },
196 "error": null
193 "error": null
197
194
198
195
199 get_pull_requests
196 get_pull_requests
200 -----------------
197 -----------------
201
198
202 .. py:function:: get_pull_requests(apiuser, repoid, status=<Optional:'new'>)
199 .. py:function:: get_pull_requests(apiuser, repoid, status=<Optional:'new'>)
203
200
204 Get all pull requests from the repository specified in `repoid`.
201 Get all pull requests from the repository specified in `repoid`.
205
202
206 :param apiuser: This is filled automatically from the |authtoken|.
203 :param apiuser: This is filled automatically from the |authtoken|.
207 :type apiuser: AuthUser
204 :type apiuser: AuthUser
208 :param repoid: Repository name or repository ID.
205 :param repoid: Repository name or repository ID.
209 :type repoid: str or int
206 :type repoid: str or int
210 :param status: Only return pull requests with the specified status.
207 :param status: Only return pull requests with the specified status.
211 Valid options are.
208 Valid options are.
212 * ``new`` (default)
209 * ``new`` (default)
213 * ``open``
210 * ``open``
214 * ``closed``
211 * ``closed``
215 :type status: str
212 :type status: str
216
213
217 Example output:
214 Example output:
218
215
219 .. code-block:: bash
216 .. code-block:: bash
220
217
221 "id": <id_given_in_input>,
218 "id": <id_given_in_input>,
222 "result":
219 "result":
223 [
220 [
224 ...
221 ...
225 {
222 {
226 "pull_request_id": "<pull_request_id>",
223 "pull_request_id": "<pull_request_id>",
227 "url": "<url>",
224 "url": "<url>",
228 "title" : "<title>",
225 "title" : "<title>",
229 "description": "<description>",
226 "description": "<description>",
230 "status": "<status>",
227 "status": "<status>",
231 "created_on": "<date_time_created>",
228 "created_on": "<date_time_created>",
232 "updated_on": "<date_time_updated>",
229 "updated_on": "<date_time_updated>",
233 "commit_ids": [
230 "commit_ids": [
234 ...
231 ...
235 "<commit_id>",
232 "<commit_id>",
236 "<commit_id>",
233 "<commit_id>",
237 ...
234 ...
238 ],
235 ],
239 "review_status": "<review_status>",
236 "review_status": "<review_status>",
240 "mergeable": {
237 "mergeable": {
241 "status": "<bool>",
238 "status": "<bool>",
242 "message: "<message>",
239 "message: "<message>",
243 },
240 },
244 "source": {
241 "source": {
245 "clone_url": "<clone_url>",
242 "clone_url": "<clone_url>",
246 "reference":
243 "reference":
247 {
244 {
248 "name": "<name>",
245 "name": "<name>",
249 "type": "<type>",
246 "type": "<type>",
250 "commit_id": "<commit_id>",
247 "commit_id": "<commit_id>",
251 }
248 }
252 },
249 },
253 "target": {
250 "target": {
254 "clone_url": "<clone_url>",
251 "clone_url": "<clone_url>",
255 "reference":
252 "reference":
256 {
253 {
257 "name": "<name>",
254 "name": "<name>",
258 "type": "<type>",
255 "type": "<type>",
259 "commit_id": "<commit_id>",
256 "commit_id": "<commit_id>",
260 }
257 }
261 },
258 },
262 "merge": {
259 "merge": {
263 "clone_url": "<clone_url>",
260 "clone_url": "<clone_url>",
264 "reference":
261 "reference":
265 {
262 {
266 "name": "<name>",
263 "name": "<name>",
267 "type": "<type>",
264 "type": "<type>",
268 "commit_id": "<commit_id>",
265 "commit_id": "<commit_id>",
269 }
266 }
270 },
267 },
271 "author": <user_obj>,
268 "author": <user_obj>,
272 "reviewers": [
269 "reviewers": [
273 ...
270 ...
274 {
271 {
275 "user": "<user_obj>",
272 "user": "<user_obj>",
276 "review_status": "<review_status>",
273 "review_status": "<review_status>",
277 }
274 }
278 ...
275 ...
279 ]
276 ]
280 }
277 }
281 ...
278 ...
282 ],
279 ],
283 "error": null
280 "error": null
284
281
285
282
286 merge_pull_request
283 merge_pull_request
287 ------------------
284 ------------------
288
285
289 .. py:function:: merge_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
286 .. py:function:: merge_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
290
287
291 Merge the pull request specified by `pullrequestid` into its target
288 Merge the pull request specified by `pullrequestid` into its target
292 repository.
289 repository.
293
290
294 :param apiuser: This is filled automatically from the |authtoken|.
291 :param apiuser: This is filled automatically from the |authtoken|.
295 :type apiuser: AuthUser
292 :type apiuser: AuthUser
296 :param repoid: The Repository name or repository ID of the
293 :param repoid: The Repository name or repository ID of the
297 target repository to which the |pr| is to be merged.
294 target repository to which the |pr| is to be merged.
298 :type repoid: str or int
295 :type repoid: str or int
299 :param pullrequestid: ID of the pull request which shall be merged.
296 :param pullrequestid: ID of the pull request which shall be merged.
300 :type pullrequestid: int
297 :type pullrequestid: int
301 :param userid: Merge the pull request as this user.
298 :param userid: Merge the pull request as this user.
302 :type userid: Optional(str or int)
299 :type userid: Optional(str or int)
303
300
304 Example output:
301 Example output:
305
302
306 .. code-block:: bash
303 .. code-block:: bash
307
304
308 "id": <id_given_in_input>,
305 "id": <id_given_in_input>,
309 "result":
306 "result": {
310 {
311 "executed": "<bool>",
307 "executed": "<bool>",
312 "failure_reason": "<int>",
308 "failure_reason": "<int>",
313 "merge_commit_id": "<merge_commit_id>",
309 "merge_commit_id": "<merge_commit_id>",
314 "possible": "<bool>",
310 "possible": "<bool>",
315 "merge_ref": {
311 "merge_ref": {
316 "commit_id": "<commit_id>",
312 "commit_id": "<commit_id>",
317 "type": "<type>",
313 "type": "<type>",
318 "name": "<name>"
314 "name": "<name>"
319 }
315 }
320 },
316 },
321 "error": null
317 "error": null
322
318
323
319
324 update_pull_request
320 update_pull_request
325 -------------------
321 -------------------
326
322
327 .. py:function:: update_pull_request(apiuser, repoid, pullrequestid, title=<Optional:''>, description=<Optional:''>, reviewers=<Optional:None>, update_commits=<Optional:None>, close_pull_request=<Optional:None>)
323 .. py:function:: update_pull_request(apiuser, repoid, pullrequestid, title=<Optional:''>, description=<Optional:''>, reviewers=<Optional:None>, update_commits=<Optional:None>, close_pull_request=<Optional:None>)
328
324
329 Updates a pull request.
325 Updates a pull request.
330
326
331 :param apiuser: This is filled automatically from the |authtoken|.
327 :param apiuser: This is filled automatically from the |authtoken|.
332 :type apiuser: AuthUser
328 :type apiuser: AuthUser
333 :param repoid: The repository name or repository ID.
329 :param repoid: The repository name or repository ID.
334 :type repoid: str or int
330 :type repoid: str or int
335 :param pullrequestid: The pull request ID.
331 :param pullrequestid: The pull request ID.
336 :type pullrequestid: int
332 :type pullrequestid: int
337 :param title: Set the pull request title.
333 :param title: Set the pull request title.
338 :type title: str
334 :type title: str
339 :param description: Update pull request description.
335 :param description: Update pull request description.
340 :type description: Optional(str)
336 :type description: Optional(str)
341 :param reviewers: Update pull request reviewers list with new value.
337 :param reviewers: Update pull request reviewers list with new value.
342 :type reviewers: Optional(list)
338 :type reviewers: Optional(list)
343 :param update_commits: Trigger update of commits for this pull request
339 :param update_commits: Trigger update of commits for this pull request
344 :type: update_commits: Optional(bool)
340 :type: update_commits: Optional(bool)
345 :param close_pull_request: Close this pull request with rejected state
341 :param close_pull_request: Close this pull request with rejected state
346 :type: close_pull_request: Optional(bool)
342 :type: close_pull_request: Optional(bool)
347
343
348 Example output:
344 Example output:
349
345
350 .. code-block:: bash
346 .. code-block:: bash
351
347
352 id : <id_given_in_input>
348 id : <id_given_in_input>
353 result :
349 result : {
354 {
355 "msg": "Updated pull request `63`",
350 "msg": "Updated pull request `63`",
356 "pull_request": <pull_request_object>,
351 "pull_request": <pull_request_object>,
357 "updated_reviewers": {
352 "updated_reviewers": {
358 "added": [
353 "added": [
359 "username"
354 "username"
360 ],
355 ],
361 "removed": []
356 "removed": []
362 },
357 },
363 "updated_commits": {
358 "updated_commits": {
364 "added": [
359 "added": [
365 "<sha1_hash>"
360 "<sha1_hash>"
366 ],
361 ],
367 "common": [
362 "common": [
368 "<sha1_hash>",
363 "<sha1_hash>",
369 "<sha1_hash>",
364 "<sha1_hash>",
370 ],
365 ],
371 "removed": []
366 "removed": []
372 }
367 }
373 }
368 }
374 error : null
369 error : null
375
370
376
371
@@ -1,158 +1,210 b''
1 .. _server-methods-ref:
1 .. _server-methods-ref:
2
2
3 server methods
3 server methods
4 ==============
4 ==============
5
5
6 cleanup_sessions
6 cleanup_sessions
7 ----------------
7 ----------------
8
8
9 .. py:function:: cleanup_sessions(apiuser, older_then=<Optional:60>)
9 .. py:function:: cleanup_sessions(apiuser, older_then=<Optional:60>)
10
10
11 Triggers a session cleanup action.
11 Triggers a session cleanup action.
12
12
13 If the ``older_then`` option is set, only sessions that hasn't been
13 If the ``older_then`` option is set, only sessions that hasn't been
14 accessed in the given number of days will be removed.
14 accessed in the given number of days will be removed.
15
15
16 This command can only be run using an |authtoken| with admin rights to
16 This command can only be run using an |authtoken| with admin rights to
17 the specified repository.
17 the specified repository.
18
18
19 This command takes the following options:
19 This command takes the following options:
20
20
21 :param apiuser: This is filled automatically from the |authtoken|.
21 :param apiuser: This is filled automatically from the |authtoken|.
22 :type apiuser: AuthUser
22 :type apiuser: AuthUser
23 :param older_then: Deletes session that hasn't been accessed
23 :param older_then: Deletes session that hasn't been accessed
24 in given number of days.
24 in given number of days.
25 :type older_then: Optional(int)
25 :type older_then: Optional(int)
26
26
27 Example output:
27 Example output:
28
28
29 .. code-block:: bash
29 .. code-block:: bash
30
30
31 id : <id_given_in_input>
31 id : <id_given_in_input>
32 result: {
32 result: {
33 "backend": "<type of backend>",
33 "backend": "<type of backend>",
34 "sessions_removed": <number_of_removed_sessions>
34 "sessions_removed": <number_of_removed_sessions>
35 }
35 }
36 error : null
36 error : null
37
37
38 Example error output:
38 Example error output:
39
39
40 .. code-block:: bash
40 .. code-block:: bash
41
41
42 id : <id_given_in_input>
42 id : <id_given_in_input>
43 result : null
43 result : null
44 error : {
44 error : {
45 'Error occurred during session cleanup'
45 'Error occurred during session cleanup'
46 }
46 }
47
47
48
48
49 get_ip
49 get_ip
50 ------
50 ------
51
51
52 .. py:function:: get_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
52 .. py:function:: get_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
53
53
54 Displays the IP Address as seen from the |RCE| server.
54 Displays the IP Address as seen from the |RCE| server.
55
55
56 * This command displays the IP Address, as well as all the defined IP
56 * This command displays the IP Address, as well as all the defined IP
57 addresses for the specified user. If the ``userid`` is not set, the
57 addresses for the specified user. If the ``userid`` is not set, the
58 data returned is for the user calling the method.
58 data returned is for the user calling the method.
59
59
60 This command can only be run using an |authtoken| with admin rights to
60 This command can only be run using an |authtoken| with admin rights to
61 the specified repository.
61 the specified repository.
62
62
63 This command takes the following options:
63 This command takes the following options:
64
64
65 :param apiuser: This is filled automatically from |authtoken|.
65 :param apiuser: This is filled automatically from |authtoken|.
66 :type apiuser: AuthUser
66 :type apiuser: AuthUser
67 :param userid: Sets the userid for which associated IP Address data
67 :param userid: Sets the userid for which associated IP Address data
68 is returned.
68 is returned.
69 :type userid: Optional(str or int)
69 :type userid: Optional(str or int)
70
70
71 Example output:
71 Example output:
72
72
73 .. code-block:: bash
73 .. code-block:: bash
74
74
75 id : <id_given_in_input>
75 id : <id_given_in_input>
76 result : {
76 result : {
77 "server_ip_addr": "<ip_from_clien>",
77 "server_ip_addr": "<ip_from_clien>",
78 "user_ips": [
78 "user_ips": [
79 {
79 {
80 "ip_addr": "<ip_with_mask>",
80 "ip_addr": "<ip_with_mask>",
81 "ip_range": ["<start_ip>", "<end_ip>"],
81 "ip_range": ["<start_ip>", "<end_ip>"],
82 },
82 },
83 ...
83 ...
84 ]
84 ]
85 }
85 }
86
86
87
87
88 get_method
89 ----------
90
91 .. py:function:: get_method(apiuser, pattern=<Optional:'*'>)
92
93 Returns list of all available API methods. By default match pattern
94 os "*" but any other pattern can be specified. eg *comment* will return
95 all methods with comment inside them. If just single method is matched
96 returned data will also include method specification
97
98 This command can only be run using an |authtoken| with admin rights to
99 the specified repository.
100
101 This command takes the following options:
102
103 :param apiuser: This is filled automatically from the |authtoken|.
104 :type apiuser: AuthUser
105 :param pattern: pattern to match method names against
106 :type older_then: Optional("*")
107
108 Example output:
109
110 .. code-block:: bash
111
112 id : <id_given_in_input>
113 "result": [
114 "changeset_comment",
115 "comment_pull_request",
116 "comment_commit"
117 ]
118 error : null
119
120 .. code-block:: bash
121
122 id : <id_given_in_input>
123 "result": [
124 "comment_commit",
125 {
126 "apiuser": "<RequiredType>",
127 "comment_type": "<Optional:u'note'>",
128 "commit_id": "<RequiredType>",
129 "message": "<RequiredType>",
130 "repoid": "<RequiredType>",
131 "request": "<RequiredType>",
132 "resolves_comment_id": "<Optional:None>",
133 "status": "<Optional:None>",
134 "userid": "<Optional:<OptionalAttr:apiuser>>"
135 }
136 ]
137 error : null
138
139
88 get_server_info
140 get_server_info
89 ---------------
141 ---------------
90
142
91 .. py:function:: get_server_info(apiuser)
143 .. py:function:: get_server_info(apiuser)
92
144
93 Returns the |RCE| server information.
145 Returns the |RCE| server information.
94
146
95 This includes the running version of |RCE| and all installed
147 This includes the running version of |RCE| and all installed
96 packages. This command takes the following options:
148 packages. This command takes the following options:
97
149
98 :param apiuser: This is filled automatically from the |authtoken|.
150 :param apiuser: This is filled automatically from the |authtoken|.
99 :type apiuser: AuthUser
151 :type apiuser: AuthUser
100
152
101 Example output:
153 Example output:
102
154
103 .. code-block:: bash
155 .. code-block:: bash
104
156
105 id : <id_given_in_input>
157 id : <id_given_in_input>
106 result : {
158 result : {
107 'modules': [<module name>,...]
159 'modules': [<module name>,...]
108 'py_version': <python version>,
160 'py_version': <python version>,
109 'platform': <platform type>,
161 'platform': <platform type>,
110 'rhodecode_version': <rhodecode version>
162 'rhodecode_version': <rhodecode version>
111 }
163 }
112 error : null
164 error : null
113
165
114
166
115 rescan_repos
167 rescan_repos
116 ------------
168 ------------
117
169
118 .. py:function:: rescan_repos(apiuser, remove_obsolete=<Optional:False>)
170 .. py:function:: rescan_repos(apiuser, remove_obsolete=<Optional:False>)
119
171
120 Triggers a rescan of the specified repositories.
172 Triggers a rescan of the specified repositories.
121
173
122 * If the ``remove_obsolete`` option is set, it also deletes repositories
174 * If the ``remove_obsolete`` option is set, it also deletes repositories
123 that are found in the database but not on the file system, so called
175 that are found in the database but not on the file system, so called
124 "clean zombies".
176 "clean zombies".
125
177
126 This command can only be run using an |authtoken| with admin rights to
178 This command can only be run using an |authtoken| with admin rights to
127 the specified repository.
179 the specified repository.
128
180
129 This command takes the following options:
181 This command takes the following options:
130
182
131 :param apiuser: This is filled automatically from the |authtoken|.
183 :param apiuser: This is filled automatically from the |authtoken|.
132 :type apiuser: AuthUser
184 :type apiuser: AuthUser
133 :param remove_obsolete: Deletes repositories from the database that
185 :param remove_obsolete: Deletes repositories from the database that
134 are not found on the filesystem.
186 are not found on the filesystem.
135 :type remove_obsolete: Optional(``True`` | ``False``)
187 :type remove_obsolete: Optional(``True`` | ``False``)
136
188
137 Example output:
189 Example output:
138
190
139 .. code-block:: bash
191 .. code-block:: bash
140
192
141 id : <id_given_in_input>
193 id : <id_given_in_input>
142 result : {
194 result : {
143 'added': [<added repository name>,...]
195 'added': [<added repository name>,...]
144 'removed': [<removed repository name>,...]
196 'removed': [<removed repository name>,...]
145 }
197 }
146 error : null
198 error : null
147
199
148 Example error output:
200 Example error output:
149
201
150 .. code-block:: bash
202 .. code-block:: bash
151
203
152 id : <id_given_in_input>
204 id : <id_given_in_input>
153 result : null
205 result : null
154 error : {
206 error : {
155 'Error occurred during rescan repositories action'
207 'Error occurred during rescan repositories action'
156 }
208 }
157
209
158
210
@@ -1,297 +1,325 b''
1 .. _user-methods-ref:
1 .. _user-methods-ref:
2
2
3 user methods
3 user methods
4 ============
4 ============
5
5
6 create_user
6 create_user
7 -----------
7 -----------
8
8
9 .. py:function:: create_user(apiuser, username, email, password=<Optional:''>, firstname=<Optional:''>, lastname=<Optional:''>, active=<Optional:True>, admin=<Optional:False>, extern_name=<Optional:'rhodecode'>, extern_type=<Optional:'rhodecode'>, force_password_change=<Optional:False>, create_personal_repo_group=<Optional:None>)
9 .. py:function:: create_user(apiuser, username, email, password=<Optional:''>, firstname=<Optional:''>, lastname=<Optional:''>, active=<Optional:True>, admin=<Optional:False>, extern_name=<Optional:'rhodecode'>, extern_type=<Optional:'rhodecode'>, force_password_change=<Optional:False>, create_personal_repo_group=<Optional:None>)
10
10
11 Creates a new user and returns the new user object.
11 Creates a new user and returns the new user object.
12
12
13 This command can only be run using an |authtoken| with admin rights to
13 This command can only be run using an |authtoken| with admin rights to
14 the specified repository.
14 the specified repository.
15
15
16 This command takes the following options:
16 This command takes the following options:
17
17
18 :param apiuser: This is filled automatically from the |authtoken|.
18 :param apiuser: This is filled automatically from the |authtoken|.
19 :type apiuser: AuthUser
19 :type apiuser: AuthUser
20 :param username: Set the new username.
20 :param username: Set the new username.
21 :type username: str or int
21 :type username: str or int
22 :param email: Set the user email address.
22 :param email: Set the user email address.
23 :type email: str
23 :type email: str
24 :param password: Set the new user password.
24 :param password: Set the new user password.
25 :type password: Optional(str)
25 :type password: Optional(str)
26 :param firstname: Set the new user firstname.
26 :param firstname: Set the new user firstname.
27 :type firstname: Optional(str)
27 :type firstname: Optional(str)
28 :param lastname: Set the new user surname.
28 :param lastname: Set the new user surname.
29 :type lastname: Optional(str)
29 :type lastname: Optional(str)
30 :param active: Set the user as active.
30 :param active: Set the user as active.
31 :type active: Optional(``True`` | ``False``)
31 :type active: Optional(``True`` | ``False``)
32 :param admin: Give the new user admin rights.
32 :param admin: Give the new user admin rights.
33 :type admin: Optional(``True`` | ``False``)
33 :type admin: Optional(``True`` | ``False``)
34 :param extern_name: Set the authentication plugin name.
34 :param extern_name: Set the authentication plugin name.
35 Using LDAP this is filled with LDAP UID.
35 Using LDAP this is filled with LDAP UID.
36 :type extern_name: Optional(str)
36 :type extern_name: Optional(str)
37 :param extern_type: Set the new user authentication plugin.
37 :param extern_type: Set the new user authentication plugin.
38 :type extern_type: Optional(str)
38 :type extern_type: Optional(str)
39 :param force_password_change: Force the new user to change password
39 :param force_password_change: Force the new user to change password
40 on next login.
40 on next login.
41 :type force_password_change: Optional(``True`` | ``False``)
41 :type force_password_change: Optional(``True`` | ``False``)
42 :param create_personal_repo_group: Create personal repo group for this user
42 :param create_personal_repo_group: Create personal repo group for this user
43 :type create_personal_repo_group: Optional(``True`` | ``False``)
43 :type create_personal_repo_group: Optional(``True`` | ``False``)
44
44 Example output:
45 Example output:
45
46
46 .. code-block:: bash
47 .. code-block:: bash
47
48
48 id : <id_given_in_input>
49 id : <id_given_in_input>
49 result: {
50 result: {
50 "msg" : "created new user `<username>`",
51 "msg" : "created new user `<username>`",
51 "user": <user_obj>
52 "user": <user_obj>
52 }
53 }
53 error: null
54 error: null
54
55
55 Example error output:
56 Example error output:
56
57
57 .. code-block:: bash
58 .. code-block:: bash
58
59
59 id : <id_given_in_input>
60 id : <id_given_in_input>
60 result : null
61 result : null
61 error : {
62 error : {
62 "user `<username>` already exist"
63 "user `<username>` already exist"
63 or
64 or
64 "email `<email>` already exist"
65 "email `<email>` already exist"
65 or
66 or
66 "failed to create user `<username>`"
67 "failed to create user `<username>`"
67 }
68 }
68
69
69
70
70 delete_user
71 delete_user
71 -----------
72 -----------
72
73
73 .. py:function:: delete_user(apiuser, userid)
74 .. py:function:: delete_user(apiuser, userid)
74
75
75 Deletes the specified user from the |RCE| user database.
76 Deletes the specified user from the |RCE| user database.
76
77
77 This command can only be run using an |authtoken| with admin rights to
78 This command can only be run using an |authtoken| with admin rights to
78 the specified repository.
79 the specified repository.
79
80
80 .. important::
81 .. important::
81
82
82 Ensure all open pull requests and open code review
83 Ensure all open pull requests and open code review
83 requests to this user are close.
84 requests to this user are close.
84
85
85 Also ensure all repositories, or repository groups owned by this
86 Also ensure all repositories, or repository groups owned by this
86 user are reassigned before deletion.
87 user are reassigned before deletion.
87
88
88 This command takes the following options:
89 This command takes the following options:
89
90
90 :param apiuser: This is filled automatically from the |authtoken|.
91 :param apiuser: This is filled automatically from the |authtoken|.
91 :type apiuser: AuthUser
92 :type apiuser: AuthUser
92 :param userid: Set the user to delete.
93 :param userid: Set the user to delete.
93 :type userid: str or int
94 :type userid: str or int
94
95
95 Example output:
96 Example output:
96
97
97 .. code-block:: bash
98 .. code-block:: bash
98
99
99 id : <id_given_in_input>
100 id : <id_given_in_input>
100 result: {
101 result: {
101 "msg" : "deleted user ID:<userid> <username>",
102 "msg" : "deleted user ID:<userid> <username>",
102 "user": null
103 "user": null
103 }
104 }
104 error: null
105 error: null
105
106
106 Example error output:
107 Example error output:
107
108
108 .. code-block:: bash
109 .. code-block:: bash
109
110
110 id : <id_given_in_input>
111 id : <id_given_in_input>
111 result : null
112 result : null
112 error : {
113 error : {
113 "failed to delete user ID:<userid> <username>"
114 "failed to delete user ID:<userid> <username>"
114 }
115 }
115
116
116
117
117 get_user
118 get_user
118 --------
119 --------
119
120
120 .. py:function:: get_user(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
121 .. py:function:: get_user(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
121
122
122 Returns the information associated with a username or userid.
123 Returns the information associated with a username or userid.
123
124
124 * If the ``userid`` is not set, this command returns the information
125 * If the ``userid`` is not set, this command returns the information
125 for the ``userid`` calling the method.
126 for the ``userid`` calling the method.
126
127
127 .. note::
128 .. note::
128
129
129 Normal users may only run this command against their ``userid``. For
130 Normal users may only run this command against their ``userid``. For
130 full privileges you must run this command using an |authtoken| with
131 full privileges you must run this command using an |authtoken| with
131 admin rights.
132 admin rights.
132
133
133 :param apiuser: This is filled automatically from the |authtoken|.
134 :param apiuser: This is filled automatically from the |authtoken|.
134 :type apiuser: AuthUser
135 :type apiuser: AuthUser
135 :param userid: Sets the userid for which data will be returned.
136 :param userid: Sets the userid for which data will be returned.
136 :type userid: Optional(str or int)
137 :type userid: Optional(str or int)
137
138
138 Example output:
139 Example output:
139
140
140 .. code-block:: bash
141 .. code-block:: bash
141
142
142 {
143 {
143 "error": null,
144 "error": null,
144 "id": <id>,
145 "id": <id>,
145 "result": {
146 "result": {
146 "active": true,
147 "active": true,
147 "admin": false,
148 "admin": false,
148 "api_key": "api-key",
149 "api_keys": [ list of keys ],
149 "api_keys": [ list of keys ],
150 "auth_tokens": [ list of tokens with details ],
150 "email": "user@example.com",
151 "email": "user@example.com",
151 "emails": [
152 "emails": [
152 "user@example.com"
153 "user@example.com"
153 ],
154 ],
154 "extern_name": "rhodecode",
155 "extern_name": "rhodecode",
155 "extern_type": "rhodecode",
156 "extern_type": "rhodecode",
156 "firstname": "username",
157 "firstname": "username",
157 "ip_addresses": [],
158 "ip_addresses": [],
158 "language": null,
159 "language": null,
159 "last_login": "Timestamp",
160 "last_login": "Timestamp",
161 "last_activity": "Timestamp",
160 "lastname": "surnae",
162 "lastname": "surnae",
161 "permissions": {
163 "permissions": {
162 "global": [
164 "global": [
163 "hg.inherit_default_perms.true",
165 "hg.inherit_default_perms.true",
164 "usergroup.read",
166 "usergroup.read",
165 "hg.repogroup.create.false",
167 "hg.repogroup.create.false",
166 "hg.create.none",
168 "hg.create.none",
167 "hg.password_reset.enabled",
169 "hg.password_reset.enabled",
168 "hg.extern_activate.manual",
170 "hg.extern_activate.manual",
169 "hg.create.write_on_repogroup.false",
171 "hg.create.write_on_repogroup.false",
170 "hg.usergroup.create.false",
172 "hg.usergroup.create.false",
171 "group.none",
173 "group.none",
172 "repository.none",
174 "repository.none",
173 "hg.register.none",
175 "hg.register.none",
174 "hg.fork.repository"
176 "hg.fork.repository"
175 ],
177 ],
176 "repositories": { "username/example": "repository.write"},
178 "repositories": { "username/example": "repository.write"},
177 "repositories_groups": { "user-group/repo": "group.none" },
179 "repositories_groups": { "user-group/repo": "group.none" },
178 "user_groups": { "user_group_name": "usergroup.read" }
180 "user_groups": { "user_group_name": "usergroup.read" }
179 },
181 },
180 "user_id": 32,
182 "user_id": 32,
181 "username": "username"
183 "username": "username"
182 }
184 }
183 }
185 }
184
186
185
187
188 get_user_audit_logs
189 -------------------
190
191 .. py:function:: get_user_audit_logs(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
192
193 Fetches all action logs made by the specified user.
194
195 This command takes the following options:
196
197 :param apiuser: This is filled automatically from the |authtoken|.
198 :type apiuser: AuthUser
199 :param userid: Sets the userid whose list of locked |repos| will be
200 displayed.
201 :type userid: Optional(str or int)
202
203 Example output:
204
205 .. code-block:: bash
206
207 id : <id_given_in_input>
208 result : {
209 [action, action,...]
210 }
211 error : null
212
213
186 get_user_locks
214 get_user_locks
187 --------------
215 --------------
188
216
189 .. py:function:: get_user_locks(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
217 .. py:function:: get_user_locks(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
190
218
191 Displays all repositories locked by the specified user.
219 Displays all repositories locked by the specified user.
192
220
193 * If this command is run by a non-admin user, it returns
221 * If this command is run by a non-admin user, it returns
194 a list of |repos| locked by that user.
222 a list of |repos| locked by that user.
195
223
196 This command takes the following options:
224 This command takes the following options:
197
225
198 :param apiuser: This is filled automatically from the |authtoken|.
226 :param apiuser: This is filled automatically from the |authtoken|.
199 :type apiuser: AuthUser
227 :type apiuser: AuthUser
200 :param userid: Sets the userid whose list of locked |repos| will be
228 :param userid: Sets the userid whose list of locked |repos| will be
201 displayed.
229 displayed.
202 :type userid: Optional(str or int)
230 :type userid: Optional(str or int)
203
231
204 Example output:
232 Example output:
205
233
206 .. code-block:: bash
234 .. code-block:: bash
207
235
208 id : <id_given_in_input>
236 id : <id_given_in_input>
209 result : {
237 result : {
210 [repo_object, repo_object,...]
238 [repo_object, repo_object,...]
211 }
239 }
212 error : null
240 error : null
213
241
214
242
215 get_users
243 get_users
216 ---------
244 ---------
217
245
218 .. py:function:: get_users(apiuser)
246 .. py:function:: get_users(apiuser)
219
247
220 Lists all users in the |RCE| user database.
248 Lists all users in the |RCE| user database.
221
249
222 This command can only be run using an |authtoken| with admin rights to
250 This command can only be run using an |authtoken| with admin rights to
223 the specified repository.
251 the specified repository.
224
252
225 This command takes the following options:
253 This command takes the following options:
226
254
227 :param apiuser: This is filled automatically from the |authtoken|.
255 :param apiuser: This is filled automatically from the |authtoken|.
228 :type apiuser: AuthUser
256 :type apiuser: AuthUser
229
257
230 Example output:
258 Example output:
231
259
232 .. code-block:: bash
260 .. code-block:: bash
233
261
234 id : <id_given_in_input>
262 id : <id_given_in_input>
235 result: [<user_object>, ...]
263 result: [<user_object>, ...]
236 error: null
264 error: null
237
265
238
266
239 update_user
267 update_user
240 -----------
268 -----------
241
269
242 .. py:function:: update_user(apiuser, userid, username=<Optional:None>, email=<Optional:None>, password=<Optional:None>, firstname=<Optional:None>, lastname=<Optional:None>, active=<Optional:None>, admin=<Optional:None>, extern_type=<Optional:None>, extern_name=<Optional:None>)
270 .. py:function:: update_user(apiuser, userid, username=<Optional:None>, email=<Optional:None>, password=<Optional:None>, firstname=<Optional:None>, lastname=<Optional:None>, active=<Optional:None>, admin=<Optional:None>, extern_type=<Optional:None>, extern_name=<Optional:None>)
243
271
244 Updates the details for the specified user, if that user exists.
272 Updates the details for the specified user, if that user exists.
245
273
246 This command can only be run using an |authtoken| with admin rights to
274 This command can only be run using an |authtoken| with admin rights to
247 the specified repository.
275 the specified repository.
248
276
249 This command takes the following options:
277 This command takes the following options:
250
278
251 :param apiuser: This is filled automatically from |authtoken|.
279 :param apiuser: This is filled automatically from |authtoken|.
252 :type apiuser: AuthUser
280 :type apiuser: AuthUser
253 :param userid: Set the ``userid`` to update.
281 :param userid: Set the ``userid`` to update.
254 :type userid: str or int
282 :type userid: str or int
255 :param username: Set the new username.
283 :param username: Set the new username.
256 :type username: str or int
284 :type username: str or int
257 :param email: Set the new email.
285 :param email: Set the new email.
258 :type email: str
286 :type email: str
259 :param password: Set the new password.
287 :param password: Set the new password.
260 :type password: Optional(str)
288 :type password: Optional(str)
261 :param firstname: Set the new first name.
289 :param firstname: Set the new first name.
262 :type firstname: Optional(str)
290 :type firstname: Optional(str)
263 :param lastname: Set the new surname.
291 :param lastname: Set the new surname.
264 :type lastname: Optional(str)
292 :type lastname: Optional(str)
265 :param active: Set the new user as active.
293 :param active: Set the new user as active.
266 :type active: Optional(``True`` | ``False``)
294 :type active: Optional(``True`` | ``False``)
267 :param admin: Give the user admin rights.
295 :param admin: Give the user admin rights.
268 :type admin: Optional(``True`` | ``False``)
296 :type admin: Optional(``True`` | ``False``)
269 :param extern_name: Set the authentication plugin user name.
297 :param extern_name: Set the authentication plugin user name.
270 Using LDAP this is filled with LDAP UID.
298 Using LDAP this is filled with LDAP UID.
271 :type extern_name: Optional(str)
299 :type extern_name: Optional(str)
272 :param extern_type: Set the authentication plugin type.
300 :param extern_type: Set the authentication plugin type.
273 :type extern_type: Optional(str)
301 :type extern_type: Optional(str)
274
302
275
303
276 Example output:
304 Example output:
277
305
278 .. code-block:: bash
306 .. code-block:: bash
279
307
280 id : <id_given_in_input>
308 id : <id_given_in_input>
281 result: {
309 result: {
282 "msg" : "updated user ID:<userid> <username>",
310 "msg" : "updated user ID:<userid> <username>",
283 "user": <user_object>,
311 "user": <user_object>,
284 }
312 }
285 error: null
313 error: null
286
314
287 Example error output:
315 Example error output:
288
316
289 .. code-block:: bash
317 .. code-block:: bash
290
318
291 id : <id_given_in_input>
319 id : <id_given_in_input>
292 result : null
320 result : null
293 error : {
321 error : {
294 "failed to update user `<username>`"
322 "failed to update user `<username>`"
295 }
323 }
296
324
297
325
@@ -1,22 +1,29 b''
1 .. _ldap-act-dir-ref:
1 .. _ldap-act-dir-ref:
2
2
3 Active Directory
3 Active Directory
4 ----------------
4 ----------------
5
5
6 |RCM| can use Microsoft Active Directory for user authentication. This is
6 |RCM| can use Microsoft Active Directory for user authentication. This is
7 done through an LDAP or LDAPS connection to Active Directory. Use the
7 done through an LDAP or LDAPS connection to Active Directory. Use the
8 following example LDAP configuration setting to set your Active Directory
8 following example LDAP configuration setting to set your Active Directory
9 authentication.
9 authentication.
10
10
11 .. code-block:: ini
11 .. code-block:: ini
12
12
13 # Set the Base DN
13 # Set the Base DN
14 Base DN = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
14 Base DN = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
15 # Set the Active Directory SAM-Account-Name
15 # Set the Active Directory SAM-Account-Name
16 Login Attribute = sAMAccountName
16 Login Attribute = sAMAccountName
17 # Set the Active Directory user name
17 # Set the Active Directory user name
18 First Name Attribute = usernameame
18 First Name Attribute = usernameame
19 # Set the Active Directory user surname
19 # Set the Active Directory user surname
20 Last Name Attribute = user_surname
20 Last Name Attribute = user_surname
21 # Set the Active Directory user email
21 # Set the Active Directory user email
22 E-mail Attribute = userEmail No newline at end of file
22 E-mail Attribute = userEmail
23
24
25 Below is example setup that can be used with Active Directory and ldap groups.
26
27 .. image:: ../images/ldap-groups-example.png
28 :alt: LDAP/AD setup example
29 :scale: 50 % No newline at end of file
@@ -1,107 +1,113 b''
1 .. _ldap-gloss-ref:
1 .. _ldap-gloss-ref:
2
2
3 |LDAP| Glossary
3 |LDAP| Glossary
4 ---------------
4 ---------------
5
5
6 This topic aims to give you a concise overview of the different settings and
6 This topic aims to give you a concise overview of the different settings and
7 requirements that enabling |LDAP| on |RCE| requires.
7 requirements that enabling |LDAP| on |RCE| requires.
8
8
9 Required settings
9 Required settings
10 ^^^^^^^^^^^^^^^^^
10 ^^^^^^^^^^^^^^^^^
11
11
12 The following LDAP attributes are required when enabling |LDAP| on |RCE|.
12 The following LDAP attributes are required when enabling |LDAP| on |RCE|.
13
13
14 * **Hostname** or **IP Address**: Use a comma separated list for failover
14 * **Hostname** or **IP Address**: Use a comma separated list for failover
15 support.
15 support.
16 * **First Name**
16 * **First Name**
17 * **Surname**
17 * **Surname**
18 * **Email**
18 * **Email**
19 * **Port**: Port `389` for unencrypted LDAP or port `636` for SSL-encrypted
19 * **Port**: Port `389` for unencrypted LDAP or port `636` for SSL-encrypted
20 LDAP (LDAPS).
20 LDAP (LDAPS).
21 * **Base DN (Distinguished Name)**: The Distinguished Name (DN)
21 * **Base DN (Distinguished Name)**: The Distinguished Name (DN)
22 is how searches for users will be performed, and these searches can be
22 is how searches for users will be performed, and these searches can be
23 controlled by using an LDAP Filter or LDAP Search Scope. A DN is a sequence of
23 controlled by using an LDAP Filter or LDAP Search Scope. A DN is a sequence of
24 relative distinguished names (RDN) connected by commas. For example,
24 relative distinguished names (RDN) connected by commas. For example,
25
25
26 .. code-block:: vim
26 .. code-block:: vim
27
27
28 DN: cn='Monty Python',ou='people',dc='example',dc='com'
28 DN: cn='Monty Python',ou='people',dc='example',dc='com'
29
29
30 * **Connection security level**: The following are the valid types:
30 * **Connection security level**: The following are the valid types:
31
31
32 * *No encryption*: This connection type uses a plain non-encrypted connection.
32 * *No encryption*: This connection type uses a plain non-encrypted connection.
33 * *LDAPS connection*: This connection type uses end-to-end SSL. To enable
33 * *LDAPS connection*: This connection type uses end-to-end SSL. To enable
34 an LDAPS connection you must set the following requirements:
34 an LDAPS connection you must set the following requirements:
35
35
36 * You must specify port `636`
36 * You must specify port `636`
37 * Certificate checks are required.
37 * Certificate checks are required.
38 * To enable ``START_TLS`` on LDAP connection, set the path to the SSL
38 * To enable ``START_TLS`` on LDAP connection, set the path to the SSL
39 certificate in the default LDAP configuration file. The default
39 certificate in the default LDAP configuration file. The default
40 `ldap.conf` file is located in `/etc/openldap/ldap.conf`.
40 `ldap.conf` file is located in `/etc/openldap/ldap.conf`.
41
41
42 .. code-block:: vim
42 .. code-block:: vim
43
43
44 TLS_CACERT /etc/ssl/certs/ca.crt
44 TLS_CACERT /etc/ssl/certs/ca.crt
45
45
46 * The LDAP username or account used to connect to |RCE|. This will be added
46 * The LDAP username or account used to connect to |RCE|. This will be added
47 to the LDAP filter for locating the user object.
47 to the LDAP filter for locating the user object.
48 * For example, if an LDAP filter is specified as `LDAPFILTER`,
48 * For example, if an LDAP filter is specified as `LDAPFILTER`,
49 the login attribute is specified as `uid`, and the user connects as
49 the login attribute is specified as `uid`, and the user connects as
50 `jsmith`, then the LDAP Filter will be like the following example.
50 `jsmith`, then the LDAP Filter will be like the following example.
51
51
52 .. code-block:: vim
52 .. code-block:: vim
53
53
54 (&(LDAPFILTER)(uid=jsmith))
54 (&(LDAPFILTER)(uid=jsmith))
55
55
56 * The LDAP search scope must be set. This limits how far LDAP will search for
56 * The LDAP search scope must be set. This limits how far LDAP will search for
57 a matching object.
57 a matching object.
58
58
59 * ``BASE`` Only allows searching of the Base DN.
59 * ``BASE`` Only allows searching of the Base DN.
60 * ``ONELEVEL`` Searches all entries under the Base DN,
60 * ``ONELEVEL`` Searches all entries under the Base DN,
61 but not the Base DN itself.
61 but not the Base DN itself.
62 * ``SUBTREE`` Searches all entries below the Base DN, but not Base DN itself.
62 * ``SUBTREE`` Searches all entries below the Base DN, but not Base DN itself.
63
63
64 .. note::
64 .. note::
65
65
66 When using ``SUBTREE`` LDAP filtering it is useful to limit object location.
66 When using ``SUBTREE`` LDAP filtering it is useful to limit object location.
67
67
68 Optional settings
68 Optional settings
69 ^^^^^^^^^^^^^^^^^
69 ^^^^^^^^^^^^^^^^^
70
70
71 The following are optional when enabling LDAP on |RCM|
71 The following are optional when enabling LDAP on |RCM|
72
72
73 * An LDAP account is only required if the LDAP server does not allow
73 * An LDAP account is only required if the LDAP server does not allow
74 anonymous browsing of records.
74 anonymous browsing of records.
75 * An LDAP password is only required if the LDAP server does not allow
75 * An LDAP password is only required if the LDAP server does not allow
76 anonymous browsing of records
76 anonymous browsing of records
77 * Using an LDAP filter is optional. An LDAP filter defined by `RFC 2254`_. This
77 * Using an LDAP filter is optional. An LDAP filter defined by `RFC 2254`_. This
78 is more useful that the LDAP Search Scope if set to `SUBTREE`. The filter
78 is more useful that the LDAP Search Scope if set to `SUBTREE`. The filter
79 is useful for limiting which LDAP objects are identified as representing
79 is useful for limiting which LDAP objects are identified as representing
80 Users for authentication. The filter is augmented by Login Attribute
80 Users for authentication. The filter is augmented by Login Attribute
81 below. This can commonly be left blank.
81 below. This can commonly be left blank.
82 * Certificate Checks are only required if you need to use LDAPS.
82 * Certificate Checks are only required if you need to use LDAPS.
83 You can use the following levels of LDAP service with RhodeCode Enterprise:
83 You can use the following levels of LDAP service with RhodeCode Enterprise:
84
84
85 * **NEVER** : A serve certificate will never be requested or checked.
85 * **NEVER** : A serve certificate will never be requested or checked.
86 * **ALLOW** : A server certificate is requested. Failure to provide a
86 * **ALLOW** : A server certificate is requested. Failure to provide a
87 certificate or providing a bad certificate will not terminate the session.
87 certificate or providing a bad certificate will not terminate the session.
88 * **TRY** : A server certificate is requested. Failure to provide a
88 * **TRY** : A server certificate is requested. Failure to provide a
89 certificate does not halt the session; providing a bad certificate
89 certificate does not halt the session; providing a bad certificate
90 halts the session.
90 halts the session.
91 * **DEMAND** : A server certificate is requested and must be provided
91 * **DEMAND** : A server certificate is requested and must be provided
92 and authenticated for the session to proceed.
92 and authenticated for the session to proceed.
93 * **HARD** : The same as DEMAND.
93 * **HARD** : The same as DEMAND.
94
94
95 .. note::
95 .. note::
96
96
97 Only **DEMAND** or **HARD** offer full SSL security while the other
97 Only **DEMAND** or **HARD** offer full SSL security while the other
98 options are vulnerable to man-in-the-middle attacks.
98 options are vulnerable to man-in-the-middle attacks.
99
99
100 |RCE| uses ``OPENLDAP`` libraries. This allows **DEMAND** or
100 |RCE| uses ``OPENLDAP`` libraries. This allows **DEMAND** or
101 **HARD** LDAPS connections to use self-signed certificates or
101 **HARD** LDAPS connections to use self-signed certificates or
102 certificates that do not have traceable certificates of authority.
102 certificates that do not have traceable certificates of authority.
103 To enable this functionality install the SSL certificates in the
103 To enable this functionality install the SSL certificates in the
104 following directory: `/etc/openldap/cacerts`
104 following directory: `/etc/openldap/cacerts`
105
105
106
106
107 Below is example setup that can be used with Active Directory and ldap groups.
108
109 .. image:: ../images/ldap-groups-example.png
110 :alt: LDAP/AD setup example
111 :scale: 50 %
112
107 .. _RFC 2254: http://www.rfc-base.org/rfc-2254.html No newline at end of file
113 .. _RFC 2254: http://www.rfc-base.org/rfc-2254.html
@@ -1,195 +1,219 b''
1 .. _dev-setup:
1 .. _dev-setup:
2
2
3 ===================
3 ===================
4 Development setup
4 Development setup
5 ===================
5 ===================
6
6
7
7
8 RhodeCode Enterprise runs inside a Nix managed environment. This ensures build
8 RhodeCode Enterprise runs inside a Nix managed environment. This ensures build
9 environment dependencies are correctly declared and installed during setup.
9 environment dependencies are correctly declared and installed during setup.
10 It also enables atomic upgrades, rollbacks, and multiple instances of RhodeCode
10 It also enables atomic upgrades, rollbacks, and multiple instances of RhodeCode
11 Enterprise running with isolation.
11 Enterprise running with isolation.
12
12
13 To set up RhodeCode Enterprise inside the Nix environment, use the following steps:
13 To set up RhodeCode Enterprise inside the Nix environment, use the following steps:
14
14
15
15
16
16
17 Setup Nix Package Manager
17 Setup Nix Package Manager
18 -------------------------
18 -------------------------
19
19
20 To install the Nix Package Manager, please run::
20 To install the Nix Package Manager, please run::
21
21
22 $ curl https://nixos.org/nix/install | sh
22 $ curl https://nixos.org/nix/install | sh
23
23
24 or go to https://nixos.org/nix/ and follow the installation instructions.
24 or go to https://nixos.org/nix/ and follow the installation instructions.
25 Once this is correctly set up on your system, you should be able to use the
25 Once this is correctly set up on your system, you should be able to use the
26 following commands:
26 following commands:
27
27
28 * `nix-env`
28 * `nix-env`
29
29
30 * `nix-shell`
30 * `nix-shell`
31
31
32
32
33 .. tip::
33 .. tip::
34
34
35 Update your channels frequently by running ``nix-channel --update``.
35 Update your channels frequently by running ``nix-channel --update``.
36
36
37
37
38 Switch nix to the latest STABLE channel
38 Switch nix to the latest STABLE channel
39 ---------------------------------------
39 ---------------------------------------
40
40
41 run::
41 run::
42
42
43 nix-channel --add https://nixos.org/channels/nixos-16.03 nixpkgs
43 nix-channel --add https://nixos.org/channels/nixos-16.03 nixpkgs
44
44
45 Followed by::
45 Followed by::
46
46
47 nix-channel --update
47 nix-channel --update
48
48
49
49
50 Install required binaries
50 Install required binaries
51 -------------------------
51 -------------------------
52
52
53 We need some handy tools first.
53 We need some handy tools first.
54
54
55 run::
55 run::
56
56
57 nix-env -i nix-prefetch-hg
57 nix-env -i nix-prefetch-hg
58 nix-env -i nix-prefetch-git
58 nix-env -i nix-prefetch-git
59
59
60
60
61 Clone the required repositories
61 Clone the required repositories
62 -------------------------------
62 -------------------------------
63
63
64 After Nix is set up, clone the RhodeCode Enterprise Community Edition and
64 After Nix is set up, clone the RhodeCode Enterprise Community Edition and
65 RhodeCode VCSServer repositories into the same directory.
65 RhodeCode VCSServer repositories into the same directory.
66 RhodeCode currently is using Mercurial Version Control System, please make sure
66 RhodeCode currently is using Mercurial Version Control System, please make sure
67 you have it installed before continuing.
67 you have it installed before continuing.
68
68
69 To obtain the required sources, use the following commands:
69 To obtain the required sources, use the following commands:
70
70
71 mkdir rhodecode-develop && cd rhodecode-develop
71 mkdir rhodecode-develop && cd rhodecode-develop
72 hg clone https://code.rhodecode.com/rhodecode-enterprise-ce
72 hg clone https://code.rhodecode.com/rhodecode-enterprise-ce
73 hg clone https://code.rhodecode.com/rhodecode-vcsserver
73 hg clone https://code.rhodecode.com/rhodecode-vcsserver
74
74
75 .. note::
75 .. note::
76
76
77 If you cannot clone the repository, please contact us via support@rhodecode.com
77 If you cannot clone the repository, please contact us via support@rhodecode.com
78
78
79
79
80 Install some required libraries
80 Install some required libraries
81 -------------------------------
81 -------------------------------
82
82
83 There are some required drivers that we need to install to test RhodeCode
83 There are some required drivers that we need to install to test RhodeCode
84 under different types of databases. For example in Ubuntu we need to install
84 under different types of databases. For example in Ubuntu we need to install
85 the following.
85 the following.
86
86
87 required libraries::
87 required libraries::
88
88
89 sudo apt-get install libapr1-dev libaprutil1-dev
89 sudo apt-get install libapr1-dev libaprutil1-dev
90 sudo apt-get install libsvn-dev
90 sudo apt-get install libsvn-dev
91 sudo apt-get install mysql-server libmysqlclient-dev
91 sudo apt-get install mysql-server libmysqlclient-dev
92 sudo apt-get install postgresql postgresql-contrib libpq-dev
92 sudo apt-get install postgresql postgresql-contrib libpq-dev
93 sudo apt-get install libcurl4-openssl-dev
93 sudo apt-get install libcurl4-openssl-dev
94
94
95
95
96 Enter the Development Shell
96 Enter the Development Shell
97 ---------------------------
97 ---------------------------
98
98
99 The final step is to start the development shells. To do this, run the
99 The final step is to start the development shells. To do this, run the
100 following command from inside the cloned repository::
100 following command from inside the cloned repository::
101
101
102 #first, the vcsserver
102 #first, the vcsserver
103 cd ~/rhodecode-vcsserver
103 cd ~/rhodecode-vcsserver
104 nix-shell
104 nix-shell
105
105
106 # then enterprise sources
106 # then enterprise sources
107 cd ~/rhodecode-enterprise-ce
107 cd ~/rhodecode-enterprise-ce
108 nix-shell
108 nix-shell
109
109
110 .. note::
110 .. note::
111
111
112 On the first run, this will take a while to download and optionally compile
112 On the first run, this will take a while to download and optionally compile
113 a few things. The following runs will be faster. The development shell works
113 a few things. The following runs will be faster. The development shell works
114 fine on both MacOS and Linux platforms.
114 fine on both MacOS and Linux platforms.
115
115
116
116
117 Create config.nix for development
118 ---------------------------------
119
120 In order to run proper tests and setup linking across projects, a config.nix
121 file needs to be setup::
122
123 # create config
124 mkdir -p ~/.nixpkgs
125 touch ~/.nixpkgs/config.nix
126
127 # put the below content into the ~/.nixpkgs/config.nix file
128 # adjusts, the path to where you cloned your repositories.
129
130 {
131 rc = {
132 sources = {
133 rhodecode-vcsserver = "/home/dev/rhodecode-vcsserver";
134 rhodecode-enterprise-ce = "/home/dev/rhodecode-enterprise-ce";
135 rhodecode-enterprise-ee = "/home/dev/rhodecode-enterprise-ee";
136 };
137 };
138 }
139
140
117
141
118 Creating a Development Configuration
142 Creating a Development Configuration
119 ------------------------------------
143 ------------------------------------
120
144
121 To create a development environment for RhodeCode Enterprise,
145 To create a development environment for RhodeCode Enterprise,
122 use the following steps:
146 use the following steps:
123
147
124 1. Create a copy of vcsserver config:
148 1. Create a copy of vcsserver config:
125 `cp ~/rhodecode-vcsserver/configs/development.ini ~/rhodecode-vcsserver/configs/dev.ini`
149 `cp ~/rhodecode-vcsserver/configs/development.ini ~/rhodecode-vcsserver/configs/dev.ini`
126 2. Create a copy of rhodocode config:
150 2. Create a copy of rhodocode config:
127 `cp ~/rhodecode-enterprise-ce/configs/development.ini ~/rhodecode-enterprise-ce/configs/dev.ini`
151 `cp ~/rhodecode-enterprise-ce/configs/development.ini ~/rhodecode-enterprise-ce/configs/dev.ini`
128 3. Adjust the configuration settings to your needs if needed.
152 3. Adjust the configuration settings to your needs if needed.
129
153
130 .. note::
154 .. note::
131
155
132 It is recommended to use the name `dev.ini` since it's included in .hgignore file.
156 It is recommended to use the name `dev.ini` since it's included in .hgignore file.
133
157
134
158
135 Setup the Development Database
159 Setup the Development Database
136 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
160 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
137
161
138 To create a development database, use the following example. This is a one
162 To create a development database, use the following example. This is a one
139 time operation executed from the nix-shell of rhodecode-enterprise-ce sources ::
163 time operation executed from the nix-shell of rhodecode-enterprise-ce sources ::
140
164
141 paster setup-rhodecode dev.ini \
165 paster setup-rhodecode dev.ini \
142 --user=admin --password=secret \
166 --user=admin --password=secret \
143 --email=admin@example.com \
167 --email=admin@example.com \
144 --repos=~/my_dev_repos
168 --repos=~/my_dev_repos
145
169
146
170
147 Compile CSS and JavaScript
171 Compile CSS and JavaScript
148 ^^^^^^^^^^^^^^^^^^^^^^^^^^
172 ^^^^^^^^^^^^^^^^^^^^^^^^^^
149
173
150 To use the application's frontend and prepare it for production deployment,
174 To use the application's frontend and prepare it for production deployment,
151 you will need to compile the CSS and JavaScript with Grunt.
175 you will need to compile the CSS and JavaScript with Grunt.
152 This is easily done from within the nix-shell using the following command::
176 This is easily done from within the nix-shell using the following command::
153
177
154 grunt
178 grunt
155
179
156 When developing new features you will need to recompile following any
180 When developing new features you will need to recompile following any
157 changes made to the CSS or JavaScript files when developing the code::
181 changes made to the CSS or JavaScript files when developing the code::
158
182
159 grunt watch
183 grunt watch
160
184
161 This prepares the development (with comments/whitespace) versions of files.
185 This prepares the development (with comments/whitespace) versions of files.
162
186
163 Start the Development Servers
187 Start the Development Servers
164 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
188 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
165
189
166 From the rhodecode-vcsserver directory, start the development server in another
190 From the rhodecode-vcsserver directory, start the development server in another
167 nix-shell, using the following command::
191 nix-shell, using the following command::
168
192
169 pserve configs/dev.ini
193 pserve configs/dev.ini
170
194
171 In the adjacent nix-shell which you created for your development server, you may
195 In the adjacent nix-shell which you created for your development server, you may
172 now start CE with the following command::
196 now start CE with the following command::
173
197
174
198
175 pserve --reload configs/dev.ini
199 pserve --reload configs/dev.ini
176
200
177 .. note::
201 .. note::
178
202
179 `--reload` flag will automatically reload the server when source file changes.
203 `--reload` flag will automatically reload the server when source file changes.
180
204
181
205
182 Run the Environment Tests
206 Run the Environment Tests
183 ^^^^^^^^^^^^^^^^^^^^^^^^^
207 ^^^^^^^^^^^^^^^^^^^^^^^^^
184
208
185 Please make sure that the tests are passing to verify that your environment is
209 Please make sure that the tests are passing to verify that your environment is
186 set up correctly. RhodeCode uses py.test to run tests.
210 set up correctly. RhodeCode uses py.test to run tests.
187 While your instance is running, start a new nix-shell and simply run
211 While your instance is running, start a new nix-shell and simply run
188 ``make test`` to run the basic test suite.
212 ``make test`` to run the basic test suite.
189
213
190
214
191 Need Help?
215 Need Help?
192 ^^^^^^^^^^
216 ^^^^^^^^^^
193
217
194 Join us on Slack via https://rhodecode.com/join or post questions in our
218 Join us on Slack via https://rhodecode.com/join or post questions in our
195 Community Portal at https://community.rhodecode.com
219 Community Portal at https://community.rhodecode.com
@@ -1,180 +1,180 b''
1 |RCE| 4.5.0 |RNS|
1 |RCE| 4.5.0 |RNS|
2 -----------------
2 -----------------
3
3
4 Release Date
4 Release Date
5 ^^^^^^^^^^^^
5 ^^^^^^^^^^^^
6
6
7 - 2016-12-02
7 - 2016-12-02
8
8
9
9
10 New Features
10 New Features
11 ^^^^^^^^^^^^
11 ^^^^^^^^^^^^
12
12
13 - Diffs: re-implemented diff engine. Added: Syntax highlighting inside diffs,
13 - Diffs: re-implemented diff engine. Added: Syntax highlighting inside diffs,
14 new side-by-side view with commenting and live chat. Enabled soft-wrapping of
14 new side-by-side view with commenting and live chat. Enabled soft-wrapping of
15 long lines and much improved rendering speed for large diffs.
15 long lines and much improved rendering speed for large diffs.
16 - File source view: new file display engine. File view now
16 - File source view: new file display engine. File view now
17 soft wraps long lines. Double click inside file view show occurrences of
17 soft wraps long lines. Double click inside file view show occurrences of
18 clicked item. Added pygments-markdown-lexer for highlighting markdown syntax.
18 clicked item. Added pygments-markdown-lexer for highlighting markdown syntax.
19 - Files annotation: Added new grouped annotations. Color all related commits
19 - Files annotation: Added new grouped annotations. Color all related commits
20 by double clicking singe commit in annotation view.
20 by double clicking singe commit in annotation view.
21 - Pull request reviewers (EE only): added new default reviewers functionality.
21 - Pull request reviewers (EE only): added new default reviewers functionality.
22 Allows picking users or user groups defined as reviewers for new pull request.
22 Allows picking users or user groups defined as reviewers for new pull request.
23 Picking reviewers can be based on branch name, changed file name patterns or
23 Picking reviewers can be based on branch name, changed file name patterns or
24 original author of changed source code. eg *.css -> design team.
24 original author of changed source code. eg \*.css -> design team.
25 Master branch -> repo owner, fixes #1131.
25 Master branch -> repo owner, fixes #1131.
26 - Pull request reviewers: store and show reasons why given person is a reviewer.
26 - Pull request reviewers: store and show reasons why given person is a reviewer.
27 Manually adding reviewers after creating a PR will now be also indicated
27 Manually adding reviewers after creating a PR will now be also indicated
28 together with who added a given person to review.
28 together with who added a given person to review.
29 - Integrations: Webhooks integration now allows to use variables inside the
29 - Integrations: Webhooks integration now allows to use variables inside the
30 call URL. Currently supported variables are ${repo_name}, ${repo_type},
30 call URL. Currently supported variables are ${repo_name}, ${repo_type},
31 ${repo_id}, ${repo_url}, ${branch}, ${commit_id}, ${pull_request_id},
31 ${repo_id}, ${repo_url}, ${branch}, ${commit_id}, ${pull_request_id},
32 ${pull_request_url}. Commits are now grouped by branches as well.
32 ${pull_request_url}. Commits are now grouped by branches as well.
33 Allows much easier integration with CI systems.
33 Allows much easier integration with CI systems.
34 - Integrations (EE only): allow wildcard * project key in Jira integration
34 - Integrations (EE only): allow wildcard * project key in Jira integration
35 settings to allow referencing multiple projects per commit, fixes #4267.
35 settings to allow referencing multiple projects per commit, fixes #4267.
36 - Live notifications: RhodeCode sends live notification to online
36 - Live notifications: RhodeCode sends live notification to online
37 users on certain events and pages. Currently this works on: invite to chat,
37 users on certain events and pages. Currently this works on: invite to chat,
38 update pull request, commit/inline comment. Part of live code review system.
38 update pull request, commit/inline comment. Part of live code review system.
39 Allows users to update the reviewed code while doing the review and never
39 Allows users to update the reviewed code while doing the review and never
40 miss any updates or comment replies as they happen. Requires channelstream
40 miss any updates or comment replies as they happen. Requires channelstream
41 to be enabled.
41 to be enabled.
42 - Repository groups: added default personal repository groups. Personal groups
42 - Repository groups: added default personal repository groups. Personal groups
43 are isolated playground for users allowing them to create projects or forks.
43 are isolated playground for users allowing them to create projects or forks.
44 Adds new setting to automatically create personal repo groups for newly
44 Adds new setting to automatically create personal repo groups for newly
45 created users. New groups are created from specified pattern, for example
45 created users. New groups are created from specified pattern, for example
46 /u/{username}. Implements #4003.
46 /u/{username}. Implements #4003.
47 - Security: It's now possible to disable password reset functionality.
47 - Security: It's now possible to disable password reset functionality.
48 This is useful for cases when users only use LDAP or similar types of
48 This is useful for cases when users only use LDAP or similar types of
49 authentication. Implements #3944
49 authentication. Implements #3944
50 - Pull requests: exposed shadow repositories to end users. Users are now given
50 - Pull requests: exposed shadow repositories to end users. Users are now given
51 access to the shadow repository which represents state after merge performed.
51 access to the shadow repository which represents state after merge performed.
52 In this way users or especially CI servers can much easier perform code
52 In this way users or especially CI servers can much easier perform code
53 analysis of the final merged code.
53 analysis of the final merged code.
54 - Pull requests: My account > pull request page now uses datagrid.
54 - Pull requests: My account > pull request page now uses datagrid.
55 It's faster, filterable and sortable. Fixes #4297.
55 It's faster, filterable and sortable. Fixes #4297.
56 - Pull requests: delete pull request action was moved from my account
56 - Pull requests: delete pull request action was moved from my account
57 into pull request view itself. This is where everyone was looking for it.
57 into pull request view itself. This is where everyone was looking for it.
58 - Pull requests: improve showing failed merges with proper status in pull
58 - Pull requests: improve showing failed merges with proper status in pull
59 request page.
59 request page.
60 - User groups: overhaul of edit user group page. Added new selector for
60 - User groups: overhaul of edit user group page. Added new selector for
61 adding new user group members.
61 adding new user group members.
62 - Licensing (EE only): exposed unlock link to deactivate users that are over
62 - Licensing (EE only): exposed unlock link to deactivate users that are over
63 license limit, to unlock full functionality. This might happen when migrating
63 license limit, to unlock full functionality. This might happen when migrating
64 from CE into EE, and your license supports less active users then allowed.
64 from CE into EE, and your license supports less active users then allowed.
65 - Global settings: add a new header/footer template to allow flash filtering.
65 - Global settings: add a new header/footer template to allow flash filtering.
66 In case a license warning appears and admin wants to hide it for some time.
66 In case a license warning appears and admin wants to hide it for some time.
67 The new template can be used to do this.
67 The new template can be used to do this.
68 - System info: added create snapshot button to easily generate system state
68 - System info: added create snapshot button to easily generate system state
69 report. Comes in handy for support and reporting. System state holds
69 report. Comes in handy for support and reporting. System state holds
70 information such as free disk/memory, CPU load and some of RhodeCode settings.
70 information such as free disk/memory, CPU load and some of RhodeCode settings.
71 - System info: fetch and show vcs settings from vcsserver. Fixes #4276.
71 - System info: fetch and show vcs settings from vcsserver. Fixes #4276.
72 - System info: use real memory usage based on new psutil api available.
72 - System info: use real memory usage based on new psutil api available.
73 - System info: added info about temporary storage.
73 - System info: added info about temporary storage.
74 - System info: expose inode limits and usage. Fixes #4282.
74 - System info: expose inode limits and usage. Fixes #4282.
75 - Ui: added new icon for merge commit.
75 - Ui: added new icon for merge commit.
76
76
77
77
78
78
79 General
79 General
80 ^^^^^^^
80 ^^^^^^^
81
81
82 - Notifications: move all notifications into polymer for consistency.
82 - Notifications: move all notifications into polymer for consistency.
83 Fixes #4201.
83 Fixes #4201.
84 - Live chat (EE): Improved UI for live-chat. Use Codemirror editor as
84 - Live chat (EE): Improved UI for live-chat. Use Codemirror editor as
85 input for text box.
85 input for text box.
86 - Api: WARNING DEPRECATION, refactor repository group schemas. Fixes #4133.
86 - Api: WARNING DEPRECATION, refactor repository group schemas. Fixes #4133.
87 When using create_repo, create_repo_group, update_repo, update_repo_group
87 When using create_repo, create_repo_group, update_repo, update_repo_group
88 the *_name parameter now takes full path including sub repository groups.
88 the \*_name parameter now takes full path including sub repository groups.
89 This is the only way to add resource under another repository group.
89 This is the only way to add resource under another repository group.
90 Furthermore giving non-existing path will no longer create the missing
90 Furthermore giving non-existing path will no longer create the missing
91 structure. This change makes the api more consistent, it better validates
91 structure. This change makes the api more consistent, it better validates
92 the errors in the data sent to given api call.
92 the errors in the data sent to given api call.
93 - Pull requests: disable subrepo handling on pull requests. It means users can
93 - Pull requests: disable subrepo handling on pull requests. It means users can
94 now use more types of repositories with subrepos to create pull requests.
94 now use more types of repositories with subrepos to create pull requests.
95 Since handling is disabled, repositories behind authentication, or outside
95 Since handling is disabled, repositories behind authentication, or outside
96 of network can be used.
96 of network can be used.
97 - VCSServer: fetch backend info from vcsserver including git/hg/svn versions
97 - VCSServer: fetch backend info from vcsserver including git/hg/svn versions
98 and connection information.
98 and connection information.
99 - Svn support: it's no longer required to put in repo root path to
99 - Svn support: it's no longer required to put in repo root path to
100 generate mod-dav-svn config. Fixes #4203.
100 generate mod-dav-svn config. Fixes #4203.
101 - Svn support: Add reload command option (svn.proxy.reload_cmd) to ini files.
101 - Svn support: Add reload command option (svn.proxy.reload_cmd) to ini files.
102 Apache can now be automatically reloaded when the mod_dav_svn config changes.
102 Apache can now be automatically reloaded when the mod_dav_svn config changes.
103 - Svn support: Add a view to trigger the (re)generation of Apache mod_dav_svn
103 - Svn support: Add a view to trigger the (re)generation of Apache mod_dav_svn
104 configuration file. Users are able to generate such file manually by clicking
104 configuration file. Users are able to generate such file manually by clicking
105 that button.
105 that button.
106 - Dependency: updated subversion library to 1.9.
106 - Dependency: updated subversion library to 1.9.
107 - Dependency: updated ipython to 5.1.0.
107 - Dependency: updated ipython to 5.1.0.
108 - Dependency: updated psutil to 4.3.1.
108 - Dependency: updated psutil to 4.3.1.
109
109
110
110
111 Security
111 Security
112 ^^^^^^^^
112 ^^^^^^^^
113
113
114 - Hipchat: escape user entered data to avoid xss/formatting problems.
114 - Hipchat: escape user entered data to avoid xss/formatting problems.
115 - VCSServer: obfuscate credentials added into remote url during remote
115 - VCSServer: obfuscate credentials added into remote url during remote
116 repository creation. Prevents leaking of those credentials inside
116 repository creation. Prevents leaking of those credentials inside
117 RhodeCode logs.
117 RhodeCode logs.
118
118
119
119
120 Performance
120 Performance
121 ^^^^^^^^^^^
121 ^^^^^^^^^^^
122
122
123 - Diffs: new diff engine is much smarter when it comes to showing huge diffs.
123 - Diffs: new diff engine is much smarter when it comes to showing huge diffs.
124 The rendering speed should be much improved in such cases, however showing
124 The rendering speed should be much improved in such cases, however showing
125 full diff is still supported.
125 full diff is still supported.
126 - VCS backends: when using a repo object from database, re-use this information
126 - VCS backends: when using a repo object from database, re-use this information
127 instead of trying to detect a backend. Reduces the traffic to vcsserver.
127 instead of trying to detect a backend. Reduces the traffic to vcsserver.
128 - Pull requests: Add a column to hold the last merge revision. This will skip
128 - Pull requests: Add a column to hold the last merge revision. This will skip
129 heavy recalculation of merge state if nothing changed inside a pull request.
129 heavy recalculation of merge state if nothing changed inside a pull request.
130 - File source view: don't load the file if it is over the size limit since it
130 - File source view: don't load the file if it is over the size limit since it
131 won't be displayed anyway. This increases speed of loading the page when a
131 won't be displayed anyway. This increases speed of loading the page when a
132 file is above cut-off limit defined.
132 file is above cut-off limit defined.
133
133
134
134
135 Fixes
135 Fixes
136 ^^^^^
136 ^^^^^
137
137
138 - Users admin: fixed search filter in user admin page.
138 - Users admin: fixed search filter in user admin page.
139 - Autocomplete: improve the lookup of users with non-ascii characters. In case
139 - Autocomplete: improve the lookup of users with non-ascii characters. In case
140 of unicode email the previous method could generate wrong data, and
140 of unicode email the previous method could generate wrong data, and
141 make search not show up such users.
141 make search not show up such users.
142 - Svn: added request header downgrade for COPY command to work on
142 - Svn: added request header downgrade for COPY command to work on
143 https setup. Fixes #4307.
143 https setup. Fixes #4307.
144 - Svn: add handling of renamed files inside our generated changes metadata.
144 - Svn: add handling of renamed files inside our generated changes metadata.
145 Fixes #4258.
145 Fixes #4258.
146 - Pull requests: fixed problem with creating pull requests on empty repositories.
146 - Pull requests: fixed problem with creating pull requests on empty repositories.
147 - Events: use branch from previous commit for repo push event commits data so
147 - Events: use branch from previous commit for repo push event commits data so
148 that per-branch grouping works. Fixes #4233.
148 that per-branch grouping works. Fixes #4233.
149 - Login: make sure recaptcha data is always validated. Fixes #4279.
149 - Login: make sure recaptcha data is always validated. Fixes #4279.
150 - Vcs: Use commit date as modification time when creating archives.
150 - Vcs: Use commit date as modification time when creating archives.
151 Fixes problem with unstable hashes for archives. Fixes #4247.
151 Fixes problem with unstable hashes for archives. Fixes #4247.
152 - Issue trackers: fixed bug where saving empty issue tracker via form was
152 - Issue trackers: fixed bug where saving empty issue tracker via form was
153 causing exception. Fixes #4278.
153 causing exception. Fixes #4278.
154 - Styling: fixed gravatar size for pull request reviewers.
154 - Styling: fixed gravatar size for pull request reviewers.
155 - Ldap: fixed email extraction typo. An empty email from LDAP server will now
155 - Ldap: fixed email extraction typo. An empty email from LDAP server will now
156 not overwrite the stored one.
156 not overwrite the stored one.
157 - Integrations: use consistent formatting of users data in Slack integration.
157 - Integrations: use consistent formatting of users data in Slack integration.
158 - Meta-tags: meta tags are not taken into account when truncating descriptions
158 - Meta-tags: meta tags are not taken into account when truncating descriptions
159 that are too long. Fixes #4305.
159 that are too long. Fixes #4305.
160
160
161
161
162 Upgrade notes
162 Upgrade notes
163 ^^^^^^^^^^^^^
163 ^^^^^^^^^^^^^
164
164
165 - Api: please adjust your scripts that uses any of create_repo,
165 - Api: please adjust your scripts that uses any of create_repo,
166 create_repo_group, update_repo, update_repo_group. There's an important change
166 create_repo_group, update_repo, update_repo_group. There's an important change
167 in how the repo_name/group_name parameters work. Please check the API docs
167 in how the repo_name/group_name parameters work. Please check the API docs
168 for latest information.
168 for latest information.
169
169
170 - Installation: starting from 4.5.0 installer now changes the default mode to http.
170 - Installation: starting from 4.5.0 installer now changes the default mode to http.
171 If you were using the `self_managed_supervisor=True` flag inside
171 If you were using the `self_managed_supervisor=True` flag inside
172 `.rccontrol.ini` to manually switch to that mode. This is no longer required
172 `.rccontrol.ini` to manually switch to that mode. This is no longer required
173 and we recommend removing that flag. Migration should already change that
173 and we recommend removing that flag. Migration should already change that
174 however in case of any troubles with VCSServer after upgrade
174 however in case of any troubles with VCSServer after upgrade
175 please make sure `vcs.protocol=` is set to `http` and not `pyro4` inside
175 please make sure `vcs.protocol=` is set to `http` and not `pyro4` inside
176 rhodecode.ini
176 rhodecode.ini
177
177
178 - New setting about password recovery was introduced. Please make sure to
178 - New setting about password recovery was introduced. Please make sure to
179 adjust what ever default you want to have inside your instance. The default
179 adjust what ever default you want to have inside your instance. The default
180 is that password recovery is enabled.
180 is that password recovery is enabled.
@@ -1,94 +1,95 b''
1 .. _rhodecode-release-notes-ref:
1 .. _rhodecode-release-notes-ref:
2
2
3 Release Notes
3 Release Notes
4 =============
4 =============
5
5
6 |RCE| 4.x Versions
6 |RCE| 4.x Versions
7 ------------------
7 ------------------
8
8
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.7.0.rst
12 release-notes-4.6.1.rst
13 release-notes-4.6.1.rst
13 release-notes-4.6.0.rst
14 release-notes-4.6.0.rst
14 release-notes-4.5.2.rst
15 release-notes-4.5.2.rst
15 release-notes-4.5.1.rst
16 release-notes-4.5.1.rst
16 release-notes-4.5.0.rst
17 release-notes-4.5.0.rst
17 release-notes-4.4.2.rst
18 release-notes-4.4.2.rst
18 release-notes-4.4.1.rst
19 release-notes-4.4.1.rst
19 release-notes-4.4.0.rst
20 release-notes-4.4.0.rst
20 release-notes-4.3.1.rst
21 release-notes-4.3.1.rst
21 release-notes-4.3.0.rst
22 release-notes-4.3.0.rst
22 release-notes-4.2.1.rst
23 release-notes-4.2.1.rst
23 release-notes-4.2.0.rst
24 release-notes-4.2.0.rst
24 release-notes-4.1.2.rst
25 release-notes-4.1.2.rst
25 release-notes-4.1.1.rst
26 release-notes-4.1.1.rst
26 release-notes-4.1.0.rst
27 release-notes-4.1.0.rst
27 release-notes-4.0.1.rst
28 release-notes-4.0.1.rst
28 release-notes-4.0.0.rst
29 release-notes-4.0.0.rst
29
30
30 |RCE| 3.x Versions
31 |RCE| 3.x Versions
31 ------------------
32 ------------------
32
33
33 .. toctree::
34 .. toctree::
34 :maxdepth: 1
35 :maxdepth: 1
35
36
36 release-notes-3.8.4.rst
37 release-notes-3.8.4.rst
37 release-notes-3.8.3.rst
38 release-notes-3.8.3.rst
38 release-notes-3.8.2.rst
39 release-notes-3.8.2.rst
39 release-notes-3.8.1.rst
40 release-notes-3.8.1.rst
40 release-notes-3.8.0.rst
41 release-notes-3.8.0.rst
41 release-notes-3.7.1.rst
42 release-notes-3.7.1.rst
42 release-notes-3.7.0.rst
43 release-notes-3.7.0.rst
43 release-notes-3.6.1.rst
44 release-notes-3.6.1.rst
44 release-notes-3.6.0.rst
45 release-notes-3.6.0.rst
45 release-notes-3.5.2.rst
46 release-notes-3.5.2.rst
46 release-notes-3.5.1.rst
47 release-notes-3.5.1.rst
47 release-notes-3.5.0.rst
48 release-notes-3.5.0.rst
48 release-notes-3.4.1.rst
49 release-notes-3.4.1.rst
49 release-notes-3.4.0.rst
50 release-notes-3.4.0.rst
50 release-notes-3.3.4.rst
51 release-notes-3.3.4.rst
51 release-notes-3.3.3.rst
52 release-notes-3.3.3.rst
52 release-notes-3.3.2.rst
53 release-notes-3.3.2.rst
53 release-notes-3.3.1.rst
54 release-notes-3.3.1.rst
54 release-notes-3.3.0.rst
55 release-notes-3.3.0.rst
55 release-notes-3.2.3.rst
56 release-notes-3.2.3.rst
56 release-notes-3.2.2.rst
57 release-notes-3.2.2.rst
57 release-notes-3.2.1.rst
58 release-notes-3.2.1.rst
58 release-notes-3.2.0.rst
59 release-notes-3.2.0.rst
59 release-notes-3.1.1.rst
60 release-notes-3.1.1.rst
60 release-notes-3.1.0.rst
61 release-notes-3.1.0.rst
61 release-notes-3.0.2.rst
62 release-notes-3.0.2.rst
62 release-notes-3.0.1.rst
63 release-notes-3.0.1.rst
63 release-notes-3.0.0.rst
64 release-notes-3.0.0.rst
64
65
65 |RCE| 2.x Versions
66 |RCE| 2.x Versions
66 ------------------
67 ------------------
67
68
68 .. toctree::
69 .. toctree::
69 :maxdepth: 1
70 :maxdepth: 1
70
71
71 release-notes-2.2.8.rst
72 release-notes-2.2.8.rst
72 release-notes-2.2.7.rst
73 release-notes-2.2.7.rst
73 release-notes-2.2.6.rst
74 release-notes-2.2.6.rst
74 release-notes-2.2.5.rst
75 release-notes-2.2.5.rst
75 release-notes-2.2.4.rst
76 release-notes-2.2.4.rst
76 release-notes-2.2.3.rst
77 release-notes-2.2.3.rst
77 release-notes-2.2.2.rst
78 release-notes-2.2.2.rst
78 release-notes-2.2.1.rst
79 release-notes-2.2.1.rst
79 release-notes-2.2.0.rst
80 release-notes-2.2.0.rst
80 release-notes-2.1.0.rst
81 release-notes-2.1.0.rst
81 release-notes-2.0.2.rst
82 release-notes-2.0.2.rst
82 release-notes-2.0.1.rst
83 release-notes-2.0.1.rst
83 release-notes-2.0.0.rst
84 release-notes-2.0.0.rst
84
85
85 |RCE| 1.x Versions
86 |RCE| 1.x Versions
86 ------------------
87 ------------------
87
88
88 .. toctree::
89 .. toctree::
89 :maxdepth: 1
90 :maxdepth: 1
90
91
91 release-notes-1.7.2.rst
92 release-notes-1.7.2.rst
92 release-notes-1.7.1.rst
93 release-notes-1.7.1.rst
93 release-notes-1.7.0.rst
94 release-notes-1.7.0.rst
94 release-notes-1.6.0.rst
95 release-notes-1.6.0.rst
@@ -1,39 +1,69 b''
1 .. _hg-big-files:
1 .. _hg-big-files:
2
2
3 |hg| Large Files Extension
3 |hg| Large Files Extension
4 ==========================
4 ==========================
5
5
6 Large files, such as image or zip files can cause a lot of bandwidth overhead
6 Large files, such as image or zip files can cause a lot of bandwidth overhead
7 during clone, push, and pull operations. To remove this inefficiency, |hg|
7 during clone, push, and pull operations. To remove this inefficiency, |hg|
8 has a large files extension which tracks their revisions by checksums. This
8 has a large files extension which tracks their revisions by checksums. This
9 means that the large files are only downloaded when they are needed as part
9 means that the large files are only downloaded when they are needed as part
10 of the current revision. This saves both disk space and bandwidth.
10 of the current revision. This saves both disk space and bandwidth.
11
11
12
13 Enabling HG Largefiles
14 ++++++++++++++++++++++
15
16 Mercurial Largefiles extension is disabled by default within |RC| Server.
17
18 To enable Mercurial Largefiles Globally:
19
20 - Go to :menuselection:`Admin --> Settings --> VCS`
21
22 - Scroll down into `Mercurial settings`
23
24 - Tick `Enable largefiles extension`
25
26 - Save your settings.
27
28 Those settings apply globally to each repository that inherits from the defaults
29 You can leave `largefiles extension` disabled globally, and only enable it per
30 repository that would use the largefiles.
31
32
33 .. note::
34
35 You might want to adjust the global storage location at that point, however
36 we recommend leaving the default one created.
37
38
39 Installing and using the |hg| Largefiles
40 ++++++++++++++++++++++++++++++++++++++++
41
12 To find out more, see the |hg| `Large Files Extensions Documentation`_.
42 To find out more, see the |hg| `Large Files Extensions Documentation`_.
13
43
14 To configure the large files extension, you need to set up your
44 To configure the large files extension, you need to set up your
15 :file:`~/.hgrc` file.
45 :file:`~/.hgrc` file.
16
46
17 1. Open your :file:`~/.hgrc` file.
47 1. Open your :file:`~/.hgrc` file.
18 2. Add ``largefiles =`` to the ``[extensions]`` section.
48 2. Add ``largefiles =`` to the ``[extensions]`` section.
19 3. Configure the ``[largefiles]`` section with the patterns and file size you
49 3. Configure the ``[largefiles]`` section with the patterns and file size you
20 wish |hg| to handle as large. The ``minsize`` option is specified in
50 wish |hg| to handle as large. The ``minsize`` option is specified in
21 megabytes.
51 megabytes.
22 4. Save your changes.
52 4. Save your changes.
23
53
24 .. code-block:: ini
54 .. code-block:: ini
25
55
26 [extensions]
56 [extensions]
27 hgext.churn =
57 hgext.churn =
28 largefiles =
58 largefiles =
29 rebase =
59 rebase =
30 record =
60 record =
31 histedit =
61 histedit =
32
62
33 [largefiles]
63 [largefiles]
34 patterns = re:.*\.(png|bmp|jpg|zip|tar|tar.gz|rar)$
64 patterns = re:.*\.(png|bmp|jpg|zip|tar|tar.gz|rar)$
35 minsize = 10
65 minsize = 10
36
66
37 For a complete :file:`~/.hgrc` file example, see :ref:`config-hgrc`.
67 For a complete :file:`~/.hgrc` file example, see :ref:`config-hgrc`.
38
68
39 .. _Large Files Extensions Documentation: http://mercurial.selenic.com/wiki/LargefilesExtension
69 .. _Large Files Extensions Documentation: http://mercurial.selenic.com/wiki/LargefilesExtension
@@ -1,26 +1,27 b''
1 .. _rc-tutorials:
1 .. _rc-tutorials:
2
2
3 |RCE| Tutorials
3 |RCE| Tutorials
4 ===============
4 ===============
5
5
6 The following tutorials are aimed at helping you to deploy |RCE| in various
6 The following tutorials are aimed at helping you to deploy |RCE| in various
7 different ways. If you would like to see a certain scenario documented,
7 different ways. If you would like to see a certain scenario documented,
8 then please send a request to support@rhodecode.com
8 then please send a request to support@rhodecode.com
9
9
10 .. toctree::
10 .. toctree::
11 :maxdepth: 1
11 :maxdepth: 1
12
12
13 deploy-from-host
13 deploy-from-host
14 hg-large-ext
14 hg-large-ext
15 git-lfs-ext
15 multi-instance-setup
16 multi-instance-setup
16 scaling-best-practices
17 scaling-best-practices
17 squash-commits
18 squash-commits
18 squash-commits-git
19 squash-commits-git
19 rebase-commits
20 rebase-commits
20 rebase-commits-git
21 rebase-commits-git
21 api-examples
22 api-examples
22 merging
23 merging
23 branching-vs-bookmarking
24 branching-vs-bookmarking
24 windows-to-linux
25 windows-to-linux
25 dvcs-best-practices
26 dvcs-best-practices
26 appenlight-setup
27 appenlight-setup
@@ -1,261 +1,276 b''
1 # Overrides for the generated python-packages.nix
1 # Overrides for the generated python-packages.nix
2 #
2 #
3 # This function is intended to be used as an extension to the generated file
3 # This function is intended to be used as an extension to the generated file
4 # python-packages.nix. The main objective is to add needed dependencies of C
4 # python-packages.nix. The main objective is to add needed dependencies of C
5 # libraries and tweak the build instructions where needed.
5 # libraries and tweak the build instructions where needed.
6
6
7 { pkgs, basePythonPackages }:
7 { pkgs, basePythonPackages }:
8
8
9 let
9 let
10 sed = "sed -i";
10 sed = "sed -i";
11 localLicenses = {
11 localLicenses = {
12 repoze = {
12 repoze = {
13 fullName = "Repoze License";
13 fullName = "Repoze License";
14 url = http://www.repoze.org/LICENSE.txt;
14 url = http://www.repoze.org/LICENSE.txt;
15 };
15 };
16 };
16 };
17
17
18 in
18 in
19
19
20 self: super: {
20 self: super: {
21
21
22 appenlight-client = super.appenlight-client.override (attrs: {
22 appenlight-client = super.appenlight-client.override (attrs: {
23 meta = {
23 meta = {
24 license = [ pkgs.lib.licenses.bsdOriginal ];
24 license = [ pkgs.lib.licenses.bsdOriginal ];
25 };
25 };
26 });
26 });
27
27
28 future = super.future.override (attrs: {
28 future = super.future.override (attrs: {
29 meta = {
29 meta = {
30 license = [ pkgs.lib.licenses.mit ];
30 license = [ pkgs.lib.licenses.mit ];
31 };
31 };
32 });
32 });
33
33
34 gnureadline = super.gnureadline.override (attrs: {
34 gnureadline = super.gnureadline.override (attrs: {
35 buildInputs = attrs.buildInputs ++ [
35 buildInputs = attrs.buildInputs ++ [
36 pkgs.ncurses
36 pkgs.ncurses
37 ];
37 ];
38 patchPhase = ''
38 patchPhase = ''
39 substituteInPlace setup.py --replace "/bin/bash" "${pkgs.bash}/bin/bash"
39 substituteInPlace setup.py --replace "/bin/bash" "${pkgs.bash}/bin/bash"
40 '';
40 '';
41 });
41 });
42
42
43 gunicorn = super.gunicorn.override (attrs: {
43 gunicorn = super.gunicorn.override (attrs: {
44 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
44 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
45 # johbo: futures is needed as long as we are on Python 2, otherwise
45 # johbo: futures is needed as long as we are on Python 2, otherwise
46 # gunicorn explodes if used with multiple threads per worker.
46 # gunicorn explodes if used with multiple threads per worker.
47 self.futures
47 self.futures
48 ];
48 ];
49 });
49 });
50
50
51 nbconvert = super.nbconvert.override (attrs: {
52 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
53 # marcink: plug in jupyter-client for notebook rendering
54 self.jupyter-client
55 ];
56 });
57
51 ipython = super.ipython.override (attrs: {
58 ipython = super.ipython.override (attrs: {
52 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
59 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
53 self.gnureadline
60 self.gnureadline
54 ];
61 ];
55 });
62 });
56
63
64 celery = super.celery.override (attrs: {
65 # The current version of kombu needs some patching to work with the
66 # other libs. Should be removed once we update celery and kombu.
67 patches = [
68 ./patch-celery-dateutil.diff
69 ];
70 });
71
57 kombu = super.kombu.override (attrs: {
72 kombu = super.kombu.override (attrs: {
58 # The current version of kombu needs some patching to work with the
73 # The current version of kombu needs some patching to work with the
59 # other libs. Should be removed once we update celery and kombu.
74 # other libs. Should be removed once we update celery and kombu.
60 patches = [
75 patches = [
61 ./patch-kombu-py-2-7-11.diff
76 ./patch-kombu-py-2-7-11.diff
62 ./patch-kombu-msgpack.diff
77 ./patch-kombu-msgpack.diff
63 ];
78 ];
64 });
79 });
65
80
66 lxml = super.lxml.override (attrs: {
81 lxml = super.lxml.override (attrs: {
67 # johbo: On 16.09 we need this to compile on darwin, otherwise compilation
82 # johbo: On 16.09 we need this to compile on darwin, otherwise compilation
68 # fails on Darwin.
83 # fails on Darwin.
69 hardeningDisable = if pkgs.stdenv.isDarwin then [ "format" ] else null;
84 hardeningDisable = if pkgs.stdenv.isDarwin then [ "format" ] else null;
70 buildInputs = with self; [
85 buildInputs = with self; [
71 pkgs.libxml2
86 pkgs.libxml2
72 pkgs.libxslt
87 pkgs.libxslt
73 ];
88 ];
74 });
89 });
75
90
76 MySQL-python = super.MySQL-python.override (attrs: {
91 MySQL-python = super.MySQL-python.override (attrs: {
77 buildInputs = attrs.buildInputs ++ [
92 buildInputs = attrs.buildInputs ++ [
78 pkgs.openssl
93 pkgs.openssl
79 ];
94 ];
80 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
95 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
81 pkgs.mysql.lib
96 pkgs.mysql.lib
82 pkgs.zlib
97 pkgs.zlib
83 ];
98 ];
84 });
99 });
85
100
86 psutil = super.psutil.override (attrs: {
101 psutil = super.psutil.override (attrs: {
87 buildInputs = attrs.buildInputs ++
102 buildInputs = attrs.buildInputs ++
88 pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.darwin.IOKit;
103 pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.darwin.IOKit;
89 });
104 });
90
105
91 psycopg2 = super.psycopg2.override (attrs: {
106 psycopg2 = super.psycopg2.override (attrs: {
92 buildInputs = attrs.buildInputs ++
107 buildInputs = attrs.buildInputs ++
93 pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.openssl;
108 pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.openssl;
94 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
109 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
95 pkgs.postgresql
110 pkgs.postgresql
96 ];
111 ];
97 meta = {
112 meta = {
98 license = pkgs.lib.licenses.lgpl3Plus;
113 license = pkgs.lib.licenses.lgpl3Plus;
99 };
114 };
100 });
115 });
101
116
102 py-gfm = super.py-gfm.override {
117 py-gfm = super.py-gfm.override {
103 name = "py-gfm-0.1.3.rhodecode-upstream1";
118 name = "py-gfm-0.1.3.rhodecode-upstream1";
104 };
119 };
105
120
106 pycurl = super.pycurl.override (attrs: {
121 pycurl = super.pycurl.override (attrs: {
107 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
122 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
108 pkgs.curl
123 pkgs.curl
109 pkgs.openssl
124 pkgs.openssl
110 ];
125 ];
111 preConfigure = ''
126 preConfigure = ''
112 substituteInPlace setup.py --replace '--static-libs' '--libs'
127 substituteInPlace setup.py --replace '--static-libs' '--libs'
113 export PYCURL_SSL_LIBRARY=openssl
128 export PYCURL_SSL_LIBRARY=openssl
114 '';
129 '';
115 meta = {
130 meta = {
116 # TODO: It is LGPL and MIT
131 # TODO: It is LGPL and MIT
117 license = pkgs.lib.licenses.mit;
132 license = pkgs.lib.licenses.mit;
118 };
133 };
119 });
134 });
120
135
121 Pylons = super.Pylons.override (attrs: {
136 Pylons = super.Pylons.override (attrs: {
122 name = "Pylons-1.0.2.rhodecode-patch1";
137 name = "Pylons-1.0.2.rhodecode-patch1";
123 });
138 });
124
139
125 pyramid = super.pyramid.override (attrs: {
140 pyramid = super.pyramid.override (attrs: {
126 postFixup = ''
141 postFixup = ''
127 wrapPythonPrograms
142 wrapPythonPrograms
128 # TODO: johbo: "wrapPython" adds this magic line which
143 # TODO: johbo: "wrapPython" adds this magic line which
129 # confuses pserve.
144 # confuses pserve.
130 ${sed} '/import sys; sys.argv/d' $out/bin/.pserve-wrapped
145 ${sed} '/import sys; sys.argv/d' $out/bin/.pserve-wrapped
131 '';
146 '';
132 meta = {
147 meta = {
133 license = localLicenses.repoze;
148 license = localLicenses.repoze;
134 };
149 };
135 });
150 });
136
151
137 pyramid-debugtoolbar = super.pyramid-debugtoolbar.override (attrs: {
152 pyramid-debugtoolbar = super.pyramid-debugtoolbar.override (attrs: {
138 meta = {
153 meta = {
139 license = [ pkgs.lib.licenses.bsdOriginal localLicenses.repoze ];
154 license = [ pkgs.lib.licenses.bsdOriginal localLicenses.repoze ];
140 };
155 };
141 });
156 });
142
157
143 pysqlite = super.pysqlite.override (attrs: {
158 pysqlite = super.pysqlite.override (attrs: {
144 propagatedBuildInputs = [
159 propagatedBuildInputs = [
145 pkgs.sqlite
160 pkgs.sqlite
146 ];
161 ];
147 meta = {
162 meta = {
148 license = [ pkgs.lib.licenses.zlib pkgs.lib.licenses.libpng ];
163 license = [ pkgs.lib.licenses.zlib pkgs.lib.licenses.libpng ];
149 };
164 };
150 });
165 });
151
166
152 pytest-runner = super.pytest-runner.override (attrs: {
167 pytest-runner = super.pytest-runner.override (attrs: {
153 propagatedBuildInputs = [
168 propagatedBuildInputs = [
154 self.setuptools-scm
169 self.setuptools-scm
155 ];
170 ];
156 });
171 });
157
172
158 python-ldap = super.python-ldap.override (attrs: {
173 python-ldap = super.python-ldap.override (attrs: {
159 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
174 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
160 pkgs.cyrus_sasl
175 pkgs.cyrus_sasl
161 pkgs.openldap
176 pkgs.openldap
162 pkgs.openssl
177 pkgs.openssl
163 ];
178 ];
164 # TODO: johbo: Remove the "or" once we drop 16.03 support.
179 # TODO: johbo: Remove the "or" once we drop 16.03 support.
165 NIX_CFLAGS_COMPILE = "-I${pkgs.cyrus_sasl.dev or pkgs.cyrus_sasl}/include/sasl";
180 NIX_CFLAGS_COMPILE = "-I${pkgs.cyrus_sasl.dev or pkgs.cyrus_sasl}/include/sasl";
166 });
181 });
167
182
168 python-pam = super.python-pam.override (attrs:
183 python-pam = super.python-pam.override (attrs:
169 let
184 let
170 includeLibPam = pkgs.stdenv.isLinux;
185 includeLibPam = pkgs.stdenv.isLinux;
171 in {
186 in {
172 # TODO: johbo: Move the option up into the default.nix, we should
187 # TODO: johbo: Move the option up into the default.nix, we should
173 # include python-pam only on supported platforms.
188 # include python-pam only on supported platforms.
174 propagatedBuildInputs = attrs.propagatedBuildInputs ++
189 propagatedBuildInputs = attrs.propagatedBuildInputs ++
175 pkgs.lib.optional includeLibPam [
190 pkgs.lib.optional includeLibPam [
176 pkgs.pam
191 pkgs.pam
177 ];
192 ];
178 # TODO: johbo: Check if this can be avoided, or transform into
193 # TODO: johbo: Check if this can be avoided, or transform into
179 # a real patch
194 # a real patch
180 patchPhase = pkgs.lib.optionals includeLibPam ''
195 patchPhase = pkgs.lib.optionals includeLibPam ''
181 substituteInPlace pam.py \
196 substituteInPlace pam.py \
182 --replace 'find_library("pam")' '"${pkgs.pam}/lib/libpam.so.0"'
197 --replace 'find_library("pam")' '"${pkgs.pam}/lib/libpam.so.0"'
183 '';
198 '';
184 });
199 });
185
200
186 URLObject = super.URLObject.override (attrs: {
201 URLObject = super.URLObject.override (attrs: {
187 meta = {
202 meta = {
188 license = {
203 license = {
189 spdxId = "Unlicense";
204 spdxId = "Unlicense";
190 fullName = "The Unlicense";
205 fullName = "The Unlicense";
191 url = http://unlicense.org/;
206 url = http://unlicense.org/;
192 };
207 };
193 };
208 };
194 });
209 });
195
210
196 amqplib = super.amqplib.override (attrs: {
211 amqplib = super.amqplib.override (attrs: {
197 meta = {
212 meta = {
198 license = pkgs.lib.licenses.lgpl3;
213 license = pkgs.lib.licenses.lgpl3;
199 };
214 };
200 });
215 });
201
216
202 docutils = super.docutils.override (attrs: {
217 docutils = super.docutils.override (attrs: {
203 meta = {
218 meta = {
204 license = pkgs.lib.licenses.bsd2;
219 license = pkgs.lib.licenses.bsd2;
205 };
220 };
206 });
221 });
207
222
208 colander = super.colander.override (attrs: {
223 colander = super.colander.override (attrs: {
209 meta = {
224 meta = {
210 license = localLicenses.repoze;
225 license = localLicenses.repoze;
211 };
226 };
212 });
227 });
213
228
214 pyramid-beaker = super.pyramid-beaker.override (attrs: {
229 pyramid-beaker = super.pyramid-beaker.override (attrs: {
215 meta = {
230 meta = {
216 license = localLicenses.repoze;
231 license = localLicenses.repoze;
217 };
232 };
218 });
233 });
219
234
220 pyramid-mako = super.pyramid-mako.override (attrs: {
235 pyramid-mako = super.pyramid-mako.override (attrs: {
221 meta = {
236 meta = {
222 license = localLicenses.repoze;
237 license = localLicenses.repoze;
223 };
238 };
224 });
239 });
225
240
226 repoze.lru = super.repoze.lru.override (attrs: {
241 repoze.lru = super.repoze.lru.override (attrs: {
227 meta = {
242 meta = {
228 license = localLicenses.repoze;
243 license = localLicenses.repoze;
229 };
244 };
230 });
245 });
231
246
232 recaptcha-client = super.recaptcha-client.override (attrs: {
247 recaptcha-client = super.recaptcha-client.override (attrs: {
233 meta = {
248 meta = {
234 # TODO: It is MIT/X11
249 # TODO: It is MIT/X11
235 license = pkgs.lib.licenses.mit;
250 license = pkgs.lib.licenses.mit;
236 };
251 };
237 });
252 });
238
253
239 python-editor = super.python-editor.override (attrs: {
254 python-editor = super.python-editor.override (attrs: {
240 meta = {
255 meta = {
241 license = pkgs.lib.licenses.asl20;
256 license = pkgs.lib.licenses.asl20;
242 };
257 };
243 });
258 });
244
259
245 translationstring = super.translationstring.override (attrs: {
260 translationstring = super.translationstring.override (attrs: {
246 meta = {
261 meta = {
247 license = localLicenses.repoze;
262 license = localLicenses.repoze;
248 };
263 };
249 });
264 });
250
265
251 venusian = super.venusian.override (attrs: {
266 venusian = super.venusian.override (attrs: {
252 meta = {
267 meta = {
253 license = localLicenses.repoze;
268 license = localLicenses.repoze;
254 };
269 };
255 });
270 });
256
271
257 # Avoid that setuptools is replaced, this leads to trouble
272 # Avoid that setuptools is replaced, this leads to trouble
258 # with buildPythonPackage.
273 # with buildPythonPackage.
259 setuptools = basePythonPackages.setuptools;
274 setuptools = basePythonPackages.setuptools;
260
275
261 }
276 }
@@ -1,1852 +1,1995 b''
1 # Generated by pip2nix 0.4.0
1 # Generated by pip2nix 0.4.0
2 # See https://github.com/johbo/pip2nix
2 # See https://github.com/johbo/pip2nix
3
3
4 {
4 {
5 Babel = super.buildPythonPackage {
5 Babel = super.buildPythonPackage {
6 name = "Babel-1.3";
6 name = "Babel-1.3";
7 buildInputs = with self; [];
7 buildInputs = with self; [];
8 doCheck = false;
8 doCheck = false;
9 propagatedBuildInputs = with self; [pytz];
9 propagatedBuildInputs = with self; [pytz];
10 src = fetchurl {
10 src = fetchurl {
11 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
11 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
12 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
12 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
13 };
13 };
14 meta = {
14 meta = {
15 license = [ pkgs.lib.licenses.bsdOriginal ];
15 license = [ pkgs.lib.licenses.bsdOriginal ];
16 };
16 };
17 };
17 };
18 Beaker = super.buildPythonPackage {
18 Beaker = super.buildPythonPackage {
19 name = "Beaker-1.7.0";
19 name = "Beaker-1.7.0";
20 buildInputs = with self; [];
20 buildInputs = with self; [];
21 doCheck = false;
21 doCheck = false;
22 propagatedBuildInputs = with self; [];
22 propagatedBuildInputs = with self; [];
23 src = fetchurl {
23 src = fetchurl {
24 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
24 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
25 md5 = "386be3f7fe427358881eee4622b428b3";
25 md5 = "386be3f7fe427358881eee4622b428b3";
26 };
26 };
27 meta = {
27 meta = {
28 license = [ pkgs.lib.licenses.bsdOriginal ];
28 license = [ pkgs.lib.licenses.bsdOriginal ];
29 };
29 };
30 };
30 };
31 CProfileV = super.buildPythonPackage {
31 CProfileV = super.buildPythonPackage {
32 name = "CProfileV-1.0.6";
32 name = "CProfileV-1.0.6";
33 buildInputs = with self; [];
33 buildInputs = with self; [];
34 doCheck = false;
34 doCheck = false;
35 propagatedBuildInputs = with self; [bottle];
35 propagatedBuildInputs = with self; [bottle];
36 src = fetchurl {
36 src = fetchurl {
37 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
37 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
38 md5 = "08c7c242b6e64237bc53c5d13537e03d";
38 md5 = "08c7c242b6e64237bc53c5d13537e03d";
39 };
39 };
40 meta = {
40 meta = {
41 license = [ pkgs.lib.licenses.mit ];
41 license = [ pkgs.lib.licenses.mit ];
42 };
42 };
43 };
43 };
44 Chameleon = super.buildPythonPackage {
44 Chameleon = super.buildPythonPackage {
45 name = "Chameleon-2.24";
45 name = "Chameleon-2.24";
46 buildInputs = with self; [];
46 buildInputs = with self; [];
47 doCheck = false;
47 doCheck = false;
48 propagatedBuildInputs = with self; [];
48 propagatedBuildInputs = with self; [];
49 src = fetchurl {
49 src = fetchurl {
50 url = "https://pypi.python.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
50 url = "https://pypi.python.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
51 md5 = "1b01f1f6533a8a11d0d2f2366dec5342";
51 md5 = "1b01f1f6533a8a11d0d2f2366dec5342";
52 };
52 };
53 meta = {
53 meta = {
54 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
54 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
55 };
55 };
56 };
56 };
57 FormEncode = super.buildPythonPackage {
57 FormEncode = super.buildPythonPackage {
58 name = "FormEncode-1.2.4";
58 name = "FormEncode-1.2.4";
59 buildInputs = with self; [];
59 buildInputs = with self; [];
60 doCheck = false;
60 doCheck = false;
61 propagatedBuildInputs = with self; [];
61 propagatedBuildInputs = with self; [];
62 src = fetchurl {
62 src = fetchurl {
63 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
63 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
64 md5 = "6bc17fb9aed8aea198975e888e2077f4";
64 md5 = "6bc17fb9aed8aea198975e888e2077f4";
65 };
65 };
66 meta = {
66 meta = {
67 license = [ pkgs.lib.licenses.psfl ];
67 license = [ pkgs.lib.licenses.psfl ];
68 };
68 };
69 };
69 };
70 Jinja2 = super.buildPythonPackage {
70 Jinja2 = super.buildPythonPackage {
71 name = "Jinja2-2.7.3";
71 name = "Jinja2-2.7.3";
72 buildInputs = with self; [];
72 buildInputs = with self; [];
73 doCheck = false;
73 doCheck = false;
74 propagatedBuildInputs = with self; [MarkupSafe];
74 propagatedBuildInputs = with self; [MarkupSafe];
75 src = fetchurl {
75 src = fetchurl {
76 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
76 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
77 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
77 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
78 };
78 };
79 meta = {
79 meta = {
80 license = [ pkgs.lib.licenses.bsdOriginal ];
80 license = [ pkgs.lib.licenses.bsdOriginal ];
81 };
81 };
82 };
82 };
83 Mako = super.buildPythonPackage {
83 Mako = super.buildPythonPackage {
84 name = "Mako-1.0.6";
84 name = "Mako-1.0.6";
85 buildInputs = with self; [];
85 buildInputs = with self; [];
86 doCheck = false;
86 doCheck = false;
87 propagatedBuildInputs = with self; [MarkupSafe];
87 propagatedBuildInputs = with self; [MarkupSafe];
88 src = fetchurl {
88 src = fetchurl {
89 url = "https://pypi.python.org/packages/56/4b/cb75836863a6382199aefb3d3809937e21fa4cb0db15a4f4ba0ecc2e7e8e/Mako-1.0.6.tar.gz";
89 url = "https://pypi.python.org/packages/56/4b/cb75836863a6382199aefb3d3809937e21fa4cb0db15a4f4ba0ecc2e7e8e/Mako-1.0.6.tar.gz";
90 md5 = "a28e22a339080316b2acc352b9ee631c";
90 md5 = "a28e22a339080316b2acc352b9ee631c";
91 };
91 };
92 meta = {
92 meta = {
93 license = [ pkgs.lib.licenses.mit ];
93 license = [ pkgs.lib.licenses.mit ];
94 };
94 };
95 };
95 };
96 Markdown = super.buildPythonPackage {
96 Markdown = super.buildPythonPackage {
97 name = "Markdown-2.6.7";
97 name = "Markdown-2.6.7";
98 buildInputs = with self; [];
98 buildInputs = with self; [];
99 doCheck = false;
99 doCheck = false;
100 propagatedBuildInputs = with self; [];
100 propagatedBuildInputs = with self; [];
101 src = fetchurl {
101 src = fetchurl {
102 url = "https://pypi.python.org/packages/48/a4/fc6b002789c2239ac620ca963694c95b8f74e4747769cdf6021276939e74/Markdown-2.6.7.zip";
102 url = "https://pypi.python.org/packages/48/a4/fc6b002789c2239ac620ca963694c95b8f74e4747769cdf6021276939e74/Markdown-2.6.7.zip";
103 md5 = "632710a7474bbb74a82084392251061f";
103 md5 = "632710a7474bbb74a82084392251061f";
104 };
104 };
105 meta = {
105 meta = {
106 license = [ pkgs.lib.licenses.bsdOriginal ];
106 license = [ pkgs.lib.licenses.bsdOriginal ];
107 };
107 };
108 };
108 };
109 MarkupSafe = super.buildPythonPackage {
109 MarkupSafe = super.buildPythonPackage {
110 name = "MarkupSafe-0.23";
110 name = "MarkupSafe-0.23";
111 buildInputs = with self; [];
111 buildInputs = with self; [];
112 doCheck = false;
112 doCheck = false;
113 propagatedBuildInputs = with self; [];
113 propagatedBuildInputs = with self; [];
114 src = fetchurl {
114 src = fetchurl {
115 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
115 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
116 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
116 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
117 };
117 };
118 meta = {
118 meta = {
119 license = [ pkgs.lib.licenses.bsdOriginal ];
119 license = [ pkgs.lib.licenses.bsdOriginal ];
120 };
120 };
121 };
121 };
122 MySQL-python = super.buildPythonPackage {
122 MySQL-python = super.buildPythonPackage {
123 name = "MySQL-python-1.2.5";
123 name = "MySQL-python-1.2.5";
124 buildInputs = with self; [];
124 buildInputs = with self; [];
125 doCheck = false;
125 doCheck = false;
126 propagatedBuildInputs = with self; [];
126 propagatedBuildInputs = with self; [];
127 src = fetchurl {
127 src = fetchurl {
128 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
128 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
129 md5 = "654f75b302db6ed8dc5a898c625e030c";
129 md5 = "654f75b302db6ed8dc5a898c625e030c";
130 };
130 };
131 meta = {
131 meta = {
132 license = [ pkgs.lib.licenses.gpl1 ];
132 license = [ pkgs.lib.licenses.gpl1 ];
133 };
133 };
134 };
134 };
135 Paste = super.buildPythonPackage {
135 Paste = super.buildPythonPackage {
136 name = "Paste-2.0.3";
136 name = "Paste-2.0.3";
137 buildInputs = with self; [];
137 buildInputs = with self; [];
138 doCheck = false;
138 doCheck = false;
139 propagatedBuildInputs = with self; [six];
139 propagatedBuildInputs = with self; [six];
140 src = fetchurl {
140 src = fetchurl {
141 url = "https://pypi.python.org/packages/30/c3/5c2f7c7a02e4f58d4454353fa1c32c94f79fa4e36d07a67c0ac295ea369e/Paste-2.0.3.tar.gz";
141 url = "https://pypi.python.org/packages/30/c3/5c2f7c7a02e4f58d4454353fa1c32c94f79fa4e36d07a67c0ac295ea369e/Paste-2.0.3.tar.gz";
142 md5 = "1231e14eae62fa7ed76e9130b04bc61e";
142 md5 = "1231e14eae62fa7ed76e9130b04bc61e";
143 };
143 };
144 meta = {
144 meta = {
145 license = [ pkgs.lib.licenses.mit ];
145 license = [ pkgs.lib.licenses.mit ];
146 };
146 };
147 };
147 };
148 PasteDeploy = super.buildPythonPackage {
148 PasteDeploy = super.buildPythonPackage {
149 name = "PasteDeploy-1.5.2";
149 name = "PasteDeploy-1.5.2";
150 buildInputs = with self; [];
150 buildInputs = with self; [];
151 doCheck = false;
151 doCheck = false;
152 propagatedBuildInputs = with self; [];
152 propagatedBuildInputs = with self; [];
153 src = fetchurl {
153 src = fetchurl {
154 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
154 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
155 md5 = "352b7205c78c8de4987578d19431af3b";
155 md5 = "352b7205c78c8de4987578d19431af3b";
156 };
156 };
157 meta = {
157 meta = {
158 license = [ pkgs.lib.licenses.mit ];
158 license = [ pkgs.lib.licenses.mit ];
159 };
159 };
160 };
160 };
161 PasteScript = super.buildPythonPackage {
161 PasteScript = super.buildPythonPackage {
162 name = "PasteScript-1.7.5";
162 name = "PasteScript-1.7.5";
163 buildInputs = with self; [];
163 buildInputs = with self; [];
164 doCheck = false;
164 doCheck = false;
165 propagatedBuildInputs = with self; [Paste PasteDeploy];
165 propagatedBuildInputs = with self; [Paste PasteDeploy];
166 src = fetchurl {
166 src = fetchurl {
167 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
167 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
168 md5 = "4c72d78dcb6bb993f30536842c16af4d";
168 md5 = "4c72d78dcb6bb993f30536842c16af4d";
169 };
169 };
170 meta = {
170 meta = {
171 license = [ pkgs.lib.licenses.mit ];
171 license = [ pkgs.lib.licenses.mit ];
172 };
172 };
173 };
173 };
174 Pygments = super.buildPythonPackage {
174 Pygments = super.buildPythonPackage {
175 name = "Pygments-2.2.0";
175 name = "Pygments-2.2.0";
176 buildInputs = with self; [];
176 buildInputs = with self; [];
177 doCheck = false;
177 doCheck = false;
178 propagatedBuildInputs = with self; [];
178 propagatedBuildInputs = with self; [];
179 src = fetchurl {
179 src = fetchurl {
180 url = "https://pypi.python.org/packages/71/2a/2e4e77803a8bd6408a2903340ac498cb0a2181811af7c9ec92cb70b0308a/Pygments-2.2.0.tar.gz";
180 url = "https://pypi.python.org/packages/71/2a/2e4e77803a8bd6408a2903340ac498cb0a2181811af7c9ec92cb70b0308a/Pygments-2.2.0.tar.gz";
181 md5 = "13037baca42f16917cbd5ad2fab50844";
181 md5 = "13037baca42f16917cbd5ad2fab50844";
182 };
182 };
183 meta = {
183 meta = {
184 license = [ pkgs.lib.licenses.bsdOriginal ];
184 license = [ pkgs.lib.licenses.bsdOriginal ];
185 };
185 };
186 };
186 };
187 Pylons = super.buildPythonPackage {
187 Pylons = super.buildPythonPackage {
188 name = "Pylons-1.0.2.dev20170205";
188 name = "Pylons-1.0.2.dev20170205";
189 buildInputs = with self; [];
189 buildInputs = with self; [];
190 doCheck = false;
190 doCheck = false;
191 propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb];
191 propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb];
192 src = fetchurl {
192 src = fetchurl {
193 url = "https://code.rhodecode.com/upstream/pylons/archive/707354ee4261b9c10450404fc9852ccea4fd667d.tar.gz?md5=f26633726fa2cd3a340316ee6a5d218f";
193 url = "https://code.rhodecode.com/upstream/pylons/archive/707354ee4261b9c10450404fc9852ccea4fd667d.tar.gz?md5=f26633726fa2cd3a340316ee6a5d218f";
194 md5 = "f26633726fa2cd3a340316ee6a5d218f";
194 md5 = "f26633726fa2cd3a340316ee6a5d218f";
195 };
195 };
196 meta = {
196 meta = {
197 license = [ pkgs.lib.licenses.bsdOriginal ];
197 license = [ pkgs.lib.licenses.bsdOriginal ];
198 };
198 };
199 };
199 };
200 Pyro4 = super.buildPythonPackage {
201 name = "Pyro4-4.41";
202 buildInputs = with self; [];
203 doCheck = false;
204 propagatedBuildInputs = with self; [serpent];
205 src = fetchurl {
206 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
207 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
208 };
209 meta = {
210 license = [ pkgs.lib.licenses.mit ];
211 };
212 };
213 Routes = super.buildPythonPackage {
200 Routes = super.buildPythonPackage {
214 name = "Routes-1.13";
201 name = "Routes-1.13";
215 buildInputs = with self; [];
202 buildInputs = with self; [];
216 doCheck = false;
203 doCheck = false;
217 propagatedBuildInputs = with self; [repoze.lru];
204 propagatedBuildInputs = with self; [repoze.lru];
218 src = fetchurl {
205 src = fetchurl {
219 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
206 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
220 md5 = "d527b0ab7dd9172b1275a41f97448783";
207 md5 = "d527b0ab7dd9172b1275a41f97448783";
221 };
208 };
222 meta = {
209 meta = {
223 license = [ pkgs.lib.licenses.bsdOriginal ];
210 license = [ pkgs.lib.licenses.bsdOriginal ];
224 };
211 };
225 };
212 };
226 SQLAlchemy = super.buildPythonPackage {
213 SQLAlchemy = super.buildPythonPackage {
227 name = "SQLAlchemy-0.9.9";
214 name = "SQLAlchemy-0.9.9";
228 buildInputs = with self; [];
215 buildInputs = with self; [];
229 doCheck = false;
216 doCheck = false;
230 propagatedBuildInputs = with self; [];
217 propagatedBuildInputs = with self; [];
231 src = fetchurl {
218 src = fetchurl {
232 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
219 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
233 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
220 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
234 };
221 };
235 meta = {
222 meta = {
236 license = [ pkgs.lib.licenses.mit ];
223 license = [ pkgs.lib.licenses.mit ];
237 };
224 };
238 };
225 };
239 Sphinx = super.buildPythonPackage {
226 Sphinx = super.buildPythonPackage {
240 name = "Sphinx-1.2.2";
227 name = "Sphinx-1.2.2";
241 buildInputs = with self; [];
228 buildInputs = with self; [];
242 doCheck = false;
229 doCheck = false;
243 propagatedBuildInputs = with self; [Pygments docutils Jinja2];
230 propagatedBuildInputs = with self; [Pygments docutils Jinja2];
244 src = fetchurl {
231 src = fetchurl {
245 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
232 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
246 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
233 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
247 };
234 };
248 meta = {
235 meta = {
249 license = [ pkgs.lib.licenses.bsdOriginal ];
236 license = [ pkgs.lib.licenses.bsdOriginal ];
250 };
237 };
251 };
238 };
252 Tempita = super.buildPythonPackage {
239 Tempita = super.buildPythonPackage {
253 name = "Tempita-0.5.2";
240 name = "Tempita-0.5.2";
254 buildInputs = with self; [];
241 buildInputs = with self; [];
255 doCheck = false;
242 doCheck = false;
256 propagatedBuildInputs = with self; [];
243 propagatedBuildInputs = with self; [];
257 src = fetchurl {
244 src = fetchurl {
258 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
245 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
259 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
246 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
260 };
247 };
261 meta = {
248 meta = {
262 license = [ pkgs.lib.licenses.mit ];
249 license = [ pkgs.lib.licenses.mit ];
263 };
250 };
264 };
251 };
265 URLObject = super.buildPythonPackage {
252 URLObject = super.buildPythonPackage {
266 name = "URLObject-2.4.0";
253 name = "URLObject-2.4.0";
267 buildInputs = with self; [];
254 buildInputs = with self; [];
268 doCheck = false;
255 doCheck = false;
269 propagatedBuildInputs = with self; [];
256 propagatedBuildInputs = with self; [];
270 src = fetchurl {
257 src = fetchurl {
271 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
258 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
272 md5 = "2ed819738a9f0a3051f31dc9924e3065";
259 md5 = "2ed819738a9f0a3051f31dc9924e3065";
273 };
260 };
274 meta = {
261 meta = {
275 license = [ ];
262 license = [ ];
276 };
263 };
277 };
264 };
278 WebError = super.buildPythonPackage {
265 WebError = super.buildPythonPackage {
279 name = "WebError-0.10.3";
266 name = "WebError-0.10.3";
280 buildInputs = with self; [];
267 buildInputs = with self; [];
281 doCheck = false;
268 doCheck = false;
282 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
269 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
283 src = fetchurl {
270 src = fetchurl {
284 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
271 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
285 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
272 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
286 };
273 };
287 meta = {
274 meta = {
288 license = [ pkgs.lib.licenses.mit ];
275 license = [ pkgs.lib.licenses.mit ];
289 };
276 };
290 };
277 };
291 WebHelpers = super.buildPythonPackage {
278 WebHelpers = super.buildPythonPackage {
292 name = "WebHelpers-1.3";
279 name = "WebHelpers-1.3";
293 buildInputs = with self; [];
280 buildInputs = with self; [];
294 doCheck = false;
281 doCheck = false;
295 propagatedBuildInputs = with self; [MarkupSafe];
282 propagatedBuildInputs = with self; [MarkupSafe];
296 src = fetchurl {
283 src = fetchurl {
297 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
284 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
298 md5 = "32749ffadfc40fea51075a7def32588b";
285 md5 = "32749ffadfc40fea51075a7def32588b";
299 };
286 };
300 meta = {
287 meta = {
301 license = [ pkgs.lib.licenses.bsdOriginal ];
288 license = [ pkgs.lib.licenses.bsdOriginal ];
302 };
289 };
303 };
290 };
304 WebHelpers2 = super.buildPythonPackage {
291 WebHelpers2 = super.buildPythonPackage {
305 name = "WebHelpers2-2.0";
292 name = "WebHelpers2-2.0";
306 buildInputs = with self; [];
293 buildInputs = with self; [];
307 doCheck = false;
294 doCheck = false;
308 propagatedBuildInputs = with self; [MarkupSafe six];
295 propagatedBuildInputs = with self; [MarkupSafe six];
309 src = fetchurl {
296 src = fetchurl {
310 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
297 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
311 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
298 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
312 };
299 };
313 meta = {
300 meta = {
314 license = [ pkgs.lib.licenses.mit ];
301 license = [ pkgs.lib.licenses.mit ];
315 };
302 };
316 };
303 };
317 WebOb = super.buildPythonPackage {
304 WebOb = super.buildPythonPackage {
318 name = "WebOb-1.3.1";
305 name = "WebOb-1.3.1";
319 buildInputs = with self; [];
306 buildInputs = with self; [];
320 doCheck = false;
307 doCheck = false;
321 propagatedBuildInputs = with self; [];
308 propagatedBuildInputs = with self; [];
322 src = fetchurl {
309 src = fetchurl {
323 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
310 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
324 md5 = "20918251c5726956ba8fef22d1556177";
311 md5 = "20918251c5726956ba8fef22d1556177";
325 };
312 };
326 meta = {
313 meta = {
327 license = [ pkgs.lib.licenses.mit ];
314 license = [ pkgs.lib.licenses.mit ];
328 };
315 };
329 };
316 };
330 WebTest = super.buildPythonPackage {
317 WebTest = super.buildPythonPackage {
331 name = "WebTest-1.4.3";
318 name = "WebTest-1.4.3";
332 buildInputs = with self; [];
319 buildInputs = with self; [];
333 doCheck = false;
320 doCheck = false;
334 propagatedBuildInputs = with self; [WebOb];
321 propagatedBuildInputs = with self; [WebOb];
335 src = fetchurl {
322 src = fetchurl {
336 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
323 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
337 md5 = "631ce728bed92c681a4020a36adbc353";
324 md5 = "631ce728bed92c681a4020a36adbc353";
338 };
325 };
339 meta = {
326 meta = {
340 license = [ pkgs.lib.licenses.mit ];
327 license = [ pkgs.lib.licenses.mit ];
341 };
328 };
342 };
329 };
343 Whoosh = super.buildPythonPackage {
330 Whoosh = super.buildPythonPackage {
344 name = "Whoosh-2.7.4";
331 name = "Whoosh-2.7.4";
345 buildInputs = with self; [];
332 buildInputs = with self; [];
346 doCheck = false;
333 doCheck = false;
347 propagatedBuildInputs = with self; [];
334 propagatedBuildInputs = with self; [];
348 src = fetchurl {
335 src = fetchurl {
349 url = "https://pypi.python.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
336 url = "https://pypi.python.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
350 md5 = "c2710105f20b3e29936bd2357383c325";
337 md5 = "c2710105f20b3e29936bd2357383c325";
351 };
338 };
352 meta = {
339 meta = {
353 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
340 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
354 };
341 };
355 };
342 };
356 alembic = super.buildPythonPackage {
343 alembic = super.buildPythonPackage {
357 name = "alembic-0.8.4";
344 name = "alembic-0.8.4";
358 buildInputs = with self; [];
345 buildInputs = with self; [];
359 doCheck = false;
346 doCheck = false;
360 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
347 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
361 src = fetchurl {
348 src = fetchurl {
362 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
349 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
363 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
350 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
364 };
351 };
365 meta = {
352 meta = {
366 license = [ pkgs.lib.licenses.mit ];
353 license = [ pkgs.lib.licenses.mit ];
367 };
354 };
368 };
355 };
369 amqplib = super.buildPythonPackage {
356 amqplib = super.buildPythonPackage {
370 name = "amqplib-1.0.2";
357 name = "amqplib-1.0.2";
371 buildInputs = with self; [];
358 buildInputs = with self; [];
372 doCheck = false;
359 doCheck = false;
373 propagatedBuildInputs = with self; [];
360 propagatedBuildInputs = with self; [];
374 src = fetchurl {
361 src = fetchurl {
375 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
362 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
376 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
363 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
377 };
364 };
378 meta = {
365 meta = {
379 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
366 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
380 };
367 };
381 };
368 };
382 anyjson = super.buildPythonPackage {
369 anyjson = super.buildPythonPackage {
383 name = "anyjson-0.3.3";
370 name = "anyjson-0.3.3";
384 buildInputs = with self; [];
371 buildInputs = with self; [];
385 doCheck = false;
372 doCheck = false;
386 propagatedBuildInputs = with self; [];
373 propagatedBuildInputs = with self; [];
387 src = fetchurl {
374 src = fetchurl {
388 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
375 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
389 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
376 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
390 };
377 };
391 meta = {
378 meta = {
392 license = [ pkgs.lib.licenses.bsdOriginal ];
379 license = [ pkgs.lib.licenses.bsdOriginal ];
393 };
380 };
394 };
381 };
395 appenlight-client = super.buildPythonPackage {
382 appenlight-client = super.buildPythonPackage {
396 name = "appenlight-client-0.6.14";
383 name = "appenlight-client-0.6.14";
397 buildInputs = with self; [];
384 buildInputs = with self; [];
398 doCheck = false;
385 doCheck = false;
399 propagatedBuildInputs = with self; [WebOb requests];
386 propagatedBuildInputs = with self; [WebOb requests];
400 src = fetchurl {
387 src = fetchurl {
401 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
388 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
402 md5 = "578c69b09f4356d898fff1199b98a95c";
389 md5 = "578c69b09f4356d898fff1199b98a95c";
403 };
390 };
404 meta = {
391 meta = {
405 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "DFSG approved"; } ];
392 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "DFSG approved"; } ];
406 };
393 };
407 };
394 };
408 authomatic = super.buildPythonPackage {
395 authomatic = super.buildPythonPackage {
409 name = "authomatic-0.1.0.post1";
396 name = "authomatic-0.1.0.post1";
410 buildInputs = with self; [];
397 buildInputs = with self; [];
411 doCheck = false;
398 doCheck = false;
412 propagatedBuildInputs = with self; [];
399 propagatedBuildInputs = with self; [];
413 src = fetchurl {
400 src = fetchurl {
414 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
401 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
415 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
402 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
416 };
403 };
417 meta = {
404 meta = {
418 license = [ pkgs.lib.licenses.mit ];
405 license = [ pkgs.lib.licenses.mit ];
419 };
406 };
420 };
407 };
421 backport-ipaddress = super.buildPythonPackage {
408 backport-ipaddress = super.buildPythonPackage {
422 name = "backport-ipaddress-0.1";
409 name = "backport-ipaddress-0.1";
423 buildInputs = with self; [];
410 buildInputs = with self; [];
424 doCheck = false;
411 doCheck = false;
425 propagatedBuildInputs = with self; [];
412 propagatedBuildInputs = with self; [];
426 src = fetchurl {
413 src = fetchurl {
427 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
414 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
428 md5 = "9c1f45f4361f71b124d7293a60006c05";
415 md5 = "9c1f45f4361f71b124d7293a60006c05";
429 };
416 };
430 meta = {
417 meta = {
431 license = [ pkgs.lib.licenses.psfl ];
418 license = [ pkgs.lib.licenses.psfl ];
432 };
419 };
433 };
420 };
434 backports.shutil-get-terminal-size = super.buildPythonPackage {
421 backports.shutil-get-terminal-size = super.buildPythonPackage {
435 name = "backports.shutil-get-terminal-size-1.0.0";
422 name = "backports.shutil-get-terminal-size-1.0.0";
436 buildInputs = with self; [];
423 buildInputs = with self; [];
437 doCheck = false;
424 doCheck = false;
438 propagatedBuildInputs = with self; [];
425 propagatedBuildInputs = with self; [];
439 src = fetchurl {
426 src = fetchurl {
440 url = "https://pypi.python.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
427 url = "https://pypi.python.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
441 md5 = "03267762480bd86b50580dc19dff3c66";
428 md5 = "03267762480bd86b50580dc19dff3c66";
442 };
429 };
443 meta = {
430 meta = {
444 license = [ pkgs.lib.licenses.mit ];
431 license = [ pkgs.lib.licenses.mit ];
445 };
432 };
446 };
433 };
434 bleach = super.buildPythonPackage {
435 name = "bleach-1.5.0";
436 buildInputs = with self; [];
437 doCheck = false;
438 propagatedBuildInputs = with self; [six html5lib];
439 src = fetchurl {
440 url = "https://pypi.python.org/packages/99/00/25a8fce4de102bf6e3cc76bc4ea60685b2fee33bde1b34830c70cacc26a7/bleach-1.5.0.tar.gz";
441 md5 = "b663300efdf421b3b727b19d7be9c7e7";
442 };
443 meta = {
444 license = [ pkgs.lib.licenses.asl20 ];
445 };
446 };
447 bottle = super.buildPythonPackage {
447 bottle = super.buildPythonPackage {
448 name = "bottle-0.12.8";
448 name = "bottle-0.12.8";
449 buildInputs = with self; [];
449 buildInputs = with self; [];
450 doCheck = false;
450 doCheck = false;
451 propagatedBuildInputs = with self; [];
451 propagatedBuildInputs = with self; [];
452 src = fetchurl {
452 src = fetchurl {
453 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
453 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
454 md5 = "13132c0a8f607bf860810a6ee9064c5b";
454 md5 = "13132c0a8f607bf860810a6ee9064c5b";
455 };
455 };
456 meta = {
456 meta = {
457 license = [ pkgs.lib.licenses.mit ];
457 license = [ pkgs.lib.licenses.mit ];
458 };
458 };
459 };
459 };
460 bumpversion = super.buildPythonPackage {
460 bumpversion = super.buildPythonPackage {
461 name = "bumpversion-0.5.3";
461 name = "bumpversion-0.5.3";
462 buildInputs = with self; [];
462 buildInputs = with self; [];
463 doCheck = false;
463 doCheck = false;
464 propagatedBuildInputs = with self; [];
464 propagatedBuildInputs = with self; [];
465 src = fetchurl {
465 src = fetchurl {
466 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
466 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
467 md5 = "c66a3492eafcf5ad4b024be9fca29820";
467 md5 = "c66a3492eafcf5ad4b024be9fca29820";
468 };
468 };
469 meta = {
469 meta = {
470 license = [ pkgs.lib.licenses.mit ];
470 license = [ pkgs.lib.licenses.mit ];
471 };
471 };
472 };
472 };
473 celery = super.buildPythonPackage {
473 celery = super.buildPythonPackage {
474 name = "celery-2.2.10";
474 name = "celery-2.2.10";
475 buildInputs = with self; [];
475 buildInputs = with self; [];
476 doCheck = false;
476 doCheck = false;
477 propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing];
477 propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing];
478 src = fetchurl {
478 src = fetchurl {
479 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
479 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
480 md5 = "898bc87e54f278055b561316ba73e222";
480 md5 = "898bc87e54f278055b561316ba73e222";
481 };
481 };
482 meta = {
482 meta = {
483 license = [ pkgs.lib.licenses.bsdOriginal ];
483 license = [ pkgs.lib.licenses.bsdOriginal ];
484 };
484 };
485 };
485 };
486 channelstream = super.buildPythonPackage {
486 channelstream = super.buildPythonPackage {
487 name = "channelstream-0.5.2";
487 name = "channelstream-0.5.2";
488 buildInputs = with self; [];
488 buildInputs = with self; [];
489 doCheck = false;
489 doCheck = false;
490 propagatedBuildInputs = with self; [gevent ws4py pyramid pyramid-jinja2 itsdangerous requests six];
490 propagatedBuildInputs = with self; [gevent ws4py pyramid pyramid-jinja2 itsdangerous requests six];
491 src = fetchurl {
491 src = fetchurl {
492 url = "https://pypi.python.org/packages/2b/31/29a8e085cf5bf97fa88e7b947adabfc581a18a3463adf77fb6dada34a65f/channelstream-0.5.2.tar.gz";
492 url = "https://pypi.python.org/packages/2b/31/29a8e085cf5bf97fa88e7b947adabfc581a18a3463adf77fb6dada34a65f/channelstream-0.5.2.tar.gz";
493 md5 = "1c5eb2a8a405be6f1073da94da6d81d3";
493 md5 = "1c5eb2a8a405be6f1073da94da6d81d3";
494 };
494 };
495 meta = {
495 meta = {
496 license = [ pkgs.lib.licenses.bsdOriginal ];
496 license = [ pkgs.lib.licenses.bsdOriginal ];
497 };
497 };
498 };
498 };
499 click = super.buildPythonPackage {
499 click = super.buildPythonPackage {
500 name = "click-5.1";
500 name = "click-5.1";
501 buildInputs = with self; [];
501 buildInputs = with self; [];
502 doCheck = false;
502 doCheck = false;
503 propagatedBuildInputs = with self; [];
503 propagatedBuildInputs = with self; [];
504 src = fetchurl {
504 src = fetchurl {
505 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
505 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
506 md5 = "9c5323008cccfe232a8b161fc8196d41";
506 md5 = "9c5323008cccfe232a8b161fc8196d41";
507 };
507 };
508 meta = {
508 meta = {
509 license = [ pkgs.lib.licenses.bsdOriginal ];
509 license = [ pkgs.lib.licenses.bsdOriginal ];
510 };
510 };
511 };
511 };
512 colander = super.buildPythonPackage {
512 colander = super.buildPythonPackage {
513 name = "colander-1.2";
513 name = "colander-1.2";
514 buildInputs = with self; [];
514 buildInputs = with self; [];
515 doCheck = false;
515 doCheck = false;
516 propagatedBuildInputs = with self; [translationstring iso8601];
516 propagatedBuildInputs = with self; [translationstring iso8601];
517 src = fetchurl {
517 src = fetchurl {
518 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
518 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
519 md5 = "83db21b07936a0726e588dae1914b9ed";
519 md5 = "83db21b07936a0726e588dae1914b9ed";
520 };
520 };
521 meta = {
521 meta = {
522 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
522 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
523 };
523 };
524 };
524 };
525 configobj = super.buildPythonPackage {
525 configobj = super.buildPythonPackage {
526 name = "configobj-5.0.6";
526 name = "configobj-5.0.6";
527 buildInputs = with self; [];
527 buildInputs = with self; [];
528 doCheck = false;
528 doCheck = false;
529 propagatedBuildInputs = with self; [six];
529 propagatedBuildInputs = with self; [six];
530 src = fetchurl {
530 src = fetchurl {
531 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
531 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
532 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
532 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
533 };
533 };
534 meta = {
534 meta = {
535 license = [ pkgs.lib.licenses.bsdOriginal ];
535 license = [ pkgs.lib.licenses.bsdOriginal ];
536 };
536 };
537 };
537 };
538 configparser = super.buildPythonPackage {
539 name = "configparser-3.5.0";
540 buildInputs = with self; [];
541 doCheck = false;
542 propagatedBuildInputs = with self; [];
543 src = fetchurl {
544 url = "https://pypi.python.org/packages/7c/69/c2ce7e91c89dc073eb1aa74c0621c3eefbffe8216b3f9af9d3885265c01c/configparser-3.5.0.tar.gz";
545 md5 = "cfdd915a5b7a6c09917a64a573140538";
546 };
547 meta = {
548 license = [ pkgs.lib.licenses.mit ];
549 };
550 };
538 cov-core = super.buildPythonPackage {
551 cov-core = super.buildPythonPackage {
539 name = "cov-core-1.15.0";
552 name = "cov-core-1.15.0";
540 buildInputs = with self; [];
553 buildInputs = with self; [];
541 doCheck = false;
554 doCheck = false;
542 propagatedBuildInputs = with self; [coverage];
555 propagatedBuildInputs = with self; [coverage];
543 src = fetchurl {
556 src = fetchurl {
544 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
557 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
545 md5 = "f519d4cb4c4e52856afb14af52919fe6";
558 md5 = "f519d4cb4c4e52856afb14af52919fe6";
546 };
559 };
547 meta = {
560 meta = {
548 license = [ pkgs.lib.licenses.mit ];
561 license = [ pkgs.lib.licenses.mit ];
549 };
562 };
550 };
563 };
551 coverage = super.buildPythonPackage {
564 coverage = super.buildPythonPackage {
552 name = "coverage-3.7.1";
565 name = "coverage-3.7.1";
553 buildInputs = with self; [];
566 buildInputs = with self; [];
554 doCheck = false;
567 doCheck = false;
555 propagatedBuildInputs = with self; [];
568 propagatedBuildInputs = with self; [];
556 src = fetchurl {
569 src = fetchurl {
557 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
570 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
558 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
571 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
559 };
572 };
560 meta = {
573 meta = {
561 license = [ pkgs.lib.licenses.bsdOriginal ];
574 license = [ pkgs.lib.licenses.bsdOriginal ];
562 };
575 };
563 };
576 };
564 cssselect = super.buildPythonPackage {
577 cssselect = super.buildPythonPackage {
565 name = "cssselect-0.9.1";
578 name = "cssselect-1.0.1";
566 buildInputs = with self; [];
579 buildInputs = with self; [];
567 doCheck = false;
580 doCheck = false;
568 propagatedBuildInputs = with self; [];
581 propagatedBuildInputs = with self; [];
569 src = fetchurl {
582 src = fetchurl {
570 url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz";
583 url = "https://pypi.python.org/packages/77/ff/9c865275cd19290feba56344eba570e719efb7ca5b34d67ed12b22ebbb0d/cssselect-1.0.1.tar.gz";
571 md5 = "c74f45966277dc7a0f768b9b0f3522ac";
584 md5 = "3fa03bf82a9f0b1223c0f1eb1369e139";
572 };
585 };
573 meta = {
586 meta = {
574 license = [ pkgs.lib.licenses.bsdOriginal ];
587 license = [ pkgs.lib.licenses.bsdOriginal ];
575 };
588 };
576 };
589 };
577 decorator = super.buildPythonPackage {
590 decorator = super.buildPythonPackage {
578 name = "decorator-3.4.2";
591 name = "decorator-4.0.11";
579 buildInputs = with self; [];
592 buildInputs = with self; [];
580 doCheck = false;
593 doCheck = false;
581 propagatedBuildInputs = with self; [];
594 propagatedBuildInputs = with self; [];
582 src = fetchurl {
595 src = fetchurl {
583 url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz";
596 url = "https://pypi.python.org/packages/cc/ac/5a16f1fc0506ff72fcc8fd4e858e3a1c231f224ab79bb7c4c9b2094cc570/decorator-4.0.11.tar.gz";
584 md5 = "9e0536870d2b83ae27d58dbf22582f4d";
597 md5 = "73644c8f0bd4983d1b6a34b49adec0ae";
585 };
598 };
586 meta = {
599 meta = {
587 license = [ pkgs.lib.licenses.bsdOriginal ];
600 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
588 };
601 };
589 };
602 };
590 deform = super.buildPythonPackage {
603 deform = super.buildPythonPackage {
591 name = "deform-2.0a2";
604 name = "deform-2.0a2";
592 buildInputs = with self; [];
605 buildInputs = with self; [];
593 doCheck = false;
606 doCheck = false;
594 propagatedBuildInputs = with self; [Chameleon colander peppercorn translationstring zope.deprecation];
607 propagatedBuildInputs = with self; [Chameleon colander peppercorn translationstring zope.deprecation];
595 src = fetchurl {
608 src = fetchurl {
596 url = "https://pypi.python.org/packages/8d/b3/aab57e81da974a806dc9c5fa024a6404720f890a6dcf2e80885e3cb4609a/deform-2.0a2.tar.gz";
609 url = "https://pypi.python.org/packages/8d/b3/aab57e81da974a806dc9c5fa024a6404720f890a6dcf2e80885e3cb4609a/deform-2.0a2.tar.gz";
597 md5 = "7a90d41f7fbc18002ce74f39bd90a5e4";
610 md5 = "7a90d41f7fbc18002ce74f39bd90a5e4";
598 };
611 };
599 meta = {
612 meta = {
600 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
613 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
601 };
614 };
602 };
615 };
603 docutils = super.buildPythonPackage {
616 docutils = super.buildPythonPackage {
604 name = "docutils-0.12";
617 name = "docutils-0.12";
605 buildInputs = with self; [];
618 buildInputs = with self; [];
606 doCheck = false;
619 doCheck = false;
607 propagatedBuildInputs = with self; [];
620 propagatedBuildInputs = with self; [];
608 src = fetchurl {
621 src = fetchurl {
609 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
622 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
610 md5 = "4622263b62c5c771c03502afa3157768";
623 md5 = "4622263b62c5c771c03502afa3157768";
611 };
624 };
612 meta = {
625 meta = {
613 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
626 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
614 };
627 };
615 };
628 };
616 dogpile.cache = super.buildPythonPackage {
629 dogpile.cache = super.buildPythonPackage {
617 name = "dogpile.cache-0.6.1";
630 name = "dogpile.cache-0.6.1";
618 buildInputs = with self; [];
631 buildInputs = with self; [];
619 doCheck = false;
632 doCheck = false;
620 propagatedBuildInputs = with self; [];
633 propagatedBuildInputs = with self; [];
621 src = fetchurl {
634 src = fetchurl {
622 url = "https://pypi.python.org/packages/f6/a0/6f2142c58c6588d17c734265b103ae1cd0741e1681dd9483a63f22033375/dogpile.cache-0.6.1.tar.gz";
635 url = "https://pypi.python.org/packages/f6/a0/6f2142c58c6588d17c734265b103ae1cd0741e1681dd9483a63f22033375/dogpile.cache-0.6.1.tar.gz";
623 md5 = "35d7fb30f22bbd0685763d894dd079a9";
636 md5 = "35d7fb30f22bbd0685763d894dd079a9";
624 };
637 };
625 meta = {
638 meta = {
626 license = [ pkgs.lib.licenses.bsdOriginal ];
639 license = [ pkgs.lib.licenses.bsdOriginal ];
627 };
640 };
628 };
641 };
629 dogpile.core = super.buildPythonPackage {
642 dogpile.core = super.buildPythonPackage {
630 name = "dogpile.core-0.4.1";
643 name = "dogpile.core-0.4.1";
631 buildInputs = with self; [];
644 buildInputs = with self; [];
632 doCheck = false;
645 doCheck = false;
633 propagatedBuildInputs = with self; [];
646 propagatedBuildInputs = with self; [];
634 src = fetchurl {
647 src = fetchurl {
635 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
648 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
636 md5 = "01cb19f52bba3e95c9b560f39341f045";
649 md5 = "01cb19f52bba3e95c9b560f39341f045";
637 };
650 };
638 meta = {
651 meta = {
639 license = [ pkgs.lib.licenses.bsdOriginal ];
652 license = [ pkgs.lib.licenses.bsdOriginal ];
640 };
653 };
641 };
654 };
642 ecdsa = super.buildPythonPackage {
655 ecdsa = super.buildPythonPackage {
643 name = "ecdsa-0.11";
656 name = "ecdsa-0.11";
644 buildInputs = with self; [];
657 buildInputs = with self; [];
645 doCheck = false;
658 doCheck = false;
646 propagatedBuildInputs = with self; [];
659 propagatedBuildInputs = with self; [];
647 src = fetchurl {
660 src = fetchurl {
648 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
661 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
649 md5 = "8ef586fe4dbb156697d756900cb41d7c";
662 md5 = "8ef586fe4dbb156697d756900cb41d7c";
650 };
663 };
651 meta = {
664 meta = {
652 license = [ pkgs.lib.licenses.mit ];
665 license = [ pkgs.lib.licenses.mit ];
653 };
666 };
654 };
667 };
655 elasticsearch = super.buildPythonPackage {
668 elasticsearch = super.buildPythonPackage {
656 name = "elasticsearch-2.3.0";
669 name = "elasticsearch-2.3.0";
657 buildInputs = with self; [];
670 buildInputs = with self; [];
658 doCheck = false;
671 doCheck = false;
659 propagatedBuildInputs = with self; [urllib3];
672 propagatedBuildInputs = with self; [urllib3];
660 src = fetchurl {
673 src = fetchurl {
661 url = "https://pypi.python.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
674 url = "https://pypi.python.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
662 md5 = "2550f3b51629cf1ef9636608af92c340";
675 md5 = "2550f3b51629cf1ef9636608af92c340";
663 };
676 };
664 meta = {
677 meta = {
665 license = [ pkgs.lib.licenses.asl20 ];
678 license = [ pkgs.lib.licenses.asl20 ];
666 };
679 };
667 };
680 };
668 elasticsearch-dsl = super.buildPythonPackage {
681 elasticsearch-dsl = super.buildPythonPackage {
669 name = "elasticsearch-dsl-2.2.0";
682 name = "elasticsearch-dsl-2.2.0";
670 buildInputs = with self; [];
683 buildInputs = with self; [];
671 doCheck = false;
684 doCheck = false;
672 propagatedBuildInputs = with self; [six python-dateutil elasticsearch];
685 propagatedBuildInputs = with self; [six python-dateutil elasticsearch];
673 src = fetchurl {
686 src = fetchurl {
674 url = "https://pypi.python.org/packages/66/2f/52a086968788e58461641570f45c3207a52d46ebbe9b77dc22b6a8ffda66/elasticsearch-dsl-2.2.0.tar.gz";
687 url = "https://pypi.python.org/packages/66/2f/52a086968788e58461641570f45c3207a52d46ebbe9b77dc22b6a8ffda66/elasticsearch-dsl-2.2.0.tar.gz";
675 md5 = "fa6bd3c87ea3caa8f0f051bc37c53221";
688 md5 = "fa6bd3c87ea3caa8f0f051bc37c53221";
676 };
689 };
677 meta = {
690 meta = {
678 license = [ pkgs.lib.licenses.asl20 ];
691 license = [ pkgs.lib.licenses.asl20 ];
679 };
692 };
680 };
693 };
694 entrypoints = super.buildPythonPackage {
695 name = "entrypoints-0.2.2";
696 buildInputs = with self; [];
697 doCheck = false;
698 propagatedBuildInputs = with self; [configparser];
699 src = fetchurl {
700 url = "https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313";
701 md5 = "7db37771aea9ac9fefe093e5d6987313";
702 };
703 meta = {
704 license = [ pkgs.lib.licenses.mit ];
705 };
706 };
681 enum34 = super.buildPythonPackage {
707 enum34 = super.buildPythonPackage {
682 name = "enum34-1.1.6";
708 name = "enum34-1.1.6";
683 buildInputs = with self; [];
709 buildInputs = with self; [];
684 doCheck = false;
710 doCheck = false;
685 propagatedBuildInputs = with self; [];
711 propagatedBuildInputs = with self; [];
686 src = fetchurl {
712 src = fetchurl {
687 url = "https://pypi.python.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
713 url = "https://pypi.python.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
688 md5 = "5f13a0841a61f7fc295c514490d120d0";
714 md5 = "5f13a0841a61f7fc295c514490d120d0";
689 };
715 };
690 meta = {
716 meta = {
691 license = [ pkgs.lib.licenses.bsdOriginal ];
717 license = [ pkgs.lib.licenses.bsdOriginal ];
692 };
718 };
693 };
719 };
720 functools32 = super.buildPythonPackage {
721 name = "functools32-3.2.3.post2";
722 buildInputs = with self; [];
723 doCheck = false;
724 propagatedBuildInputs = with self; [];
725 src = fetchurl {
726 url = "https://pypi.python.org/packages/5e/1a/0aa2c8195a204a9f51284018562dea77e25511f02fe924fac202fc012172/functools32-3.2.3-2.zip";
727 md5 = "d55232eb132ec779e6893c902a0bc5ad";
728 };
729 meta = {
730 license = [ pkgs.lib.licenses.psfl ];
731 };
732 };
694 future = super.buildPythonPackage {
733 future = super.buildPythonPackage {
695 name = "future-0.14.3";
734 name = "future-0.14.3";
696 buildInputs = with self; [];
735 buildInputs = with self; [];
697 doCheck = false;
736 doCheck = false;
698 propagatedBuildInputs = with self; [];
737 propagatedBuildInputs = with self; [];
699 src = fetchurl {
738 src = fetchurl {
700 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
739 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
701 md5 = "e94079b0bd1fc054929e8769fc0f6083";
740 md5 = "e94079b0bd1fc054929e8769fc0f6083";
702 };
741 };
703 meta = {
742 meta = {
704 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
743 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
705 };
744 };
706 };
745 };
707 futures = super.buildPythonPackage {
746 futures = super.buildPythonPackage {
708 name = "futures-3.0.2";
747 name = "futures-3.0.2";
709 buildInputs = with self; [];
748 buildInputs = with self; [];
710 doCheck = false;
749 doCheck = false;
711 propagatedBuildInputs = with self; [];
750 propagatedBuildInputs = with self; [];
712 src = fetchurl {
751 src = fetchurl {
713 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
752 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
714 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
753 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
715 };
754 };
716 meta = {
755 meta = {
717 license = [ pkgs.lib.licenses.bsdOriginal ];
756 license = [ pkgs.lib.licenses.bsdOriginal ];
718 };
757 };
719 };
758 };
720 gevent = super.buildPythonPackage {
759 gevent = super.buildPythonPackage {
721 name = "gevent-1.1.2";
760 name = "gevent-1.1.2";
722 buildInputs = with self; [];
761 buildInputs = with self; [];
723 doCheck = false;
762 doCheck = false;
724 propagatedBuildInputs = with self; [greenlet];
763 propagatedBuildInputs = with self; [greenlet];
725 src = fetchurl {
764 src = fetchurl {
726 url = "https://pypi.python.org/packages/43/8f/cb3224a0e6ab663547f45c10d0651cfd52633fde4283bf68d627084df8cc/gevent-1.1.2.tar.gz";
765 url = "https://pypi.python.org/packages/43/8f/cb3224a0e6ab663547f45c10d0651cfd52633fde4283bf68d627084df8cc/gevent-1.1.2.tar.gz";
727 md5 = "bb32a2f852a4997138014d5007215c6e";
766 md5 = "bb32a2f852a4997138014d5007215c6e";
728 };
767 };
729 meta = {
768 meta = {
730 license = [ pkgs.lib.licenses.mit ];
769 license = [ pkgs.lib.licenses.mit ];
731 };
770 };
732 };
771 };
733 gnureadline = super.buildPythonPackage {
772 gnureadline = super.buildPythonPackage {
734 name = "gnureadline-6.3.3";
773 name = "gnureadline-6.3.3";
735 buildInputs = with self; [];
774 buildInputs = with self; [];
736 doCheck = false;
775 doCheck = false;
737 propagatedBuildInputs = with self; [];
776 propagatedBuildInputs = with self; [];
738 src = fetchurl {
777 src = fetchurl {
739 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
778 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
740 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
779 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
741 };
780 };
742 meta = {
781 meta = {
743 license = [ pkgs.lib.licenses.gpl1 ];
782 license = [ pkgs.lib.licenses.gpl1 ];
744 };
783 };
745 };
784 };
746 gprof2dot = super.buildPythonPackage {
785 gprof2dot = super.buildPythonPackage {
747 name = "gprof2dot-2016.10.13";
786 name = "gprof2dot-2016.10.13";
748 buildInputs = with self; [];
787 buildInputs = with self; [];
749 doCheck = false;
788 doCheck = false;
750 propagatedBuildInputs = with self; [];
789 propagatedBuildInputs = with self; [];
751 src = fetchurl {
790 src = fetchurl {
752 url = "https://pypi.python.org/packages/a0/e0/73c71baed306f0402a00a94ffc7b2be94ad1296dfcb8b46912655b93154c/gprof2dot-2016.10.13.tar.gz";
791 url = "https://pypi.python.org/packages/a0/e0/73c71baed306f0402a00a94ffc7b2be94ad1296dfcb8b46912655b93154c/gprof2dot-2016.10.13.tar.gz";
753 md5 = "0125401f15fd2afe1df686a76c64a4fd";
792 md5 = "0125401f15fd2afe1df686a76c64a4fd";
754 };
793 };
755 meta = {
794 meta = {
756 license = [ { fullName = "LGPL"; } ];
795 license = [ { fullName = "LGPL"; } ];
757 };
796 };
758 };
797 };
759 greenlet = super.buildPythonPackage {
798 greenlet = super.buildPythonPackage {
760 name = "greenlet-0.4.10";
799 name = "greenlet-0.4.10";
761 buildInputs = with self; [];
800 buildInputs = with self; [];
762 doCheck = false;
801 doCheck = false;
763 propagatedBuildInputs = with self; [];
802 propagatedBuildInputs = with self; [];
764 src = fetchurl {
803 src = fetchurl {
765 url = "https://pypi.python.org/packages/67/62/ca2a95648666eaa2ffeb6a9b3964f21d419ae27f82f2e66b53da5b943fc4/greenlet-0.4.10.zip";
804 url = "https://pypi.python.org/packages/67/62/ca2a95648666eaa2ffeb6a9b3964f21d419ae27f82f2e66b53da5b943fc4/greenlet-0.4.10.zip";
766 md5 = "bed0c4b3b896702131f4d5c72f87c41d";
805 md5 = "bed0c4b3b896702131f4d5c72f87c41d";
767 };
806 };
768 meta = {
807 meta = {
769 license = [ pkgs.lib.licenses.mit ];
808 license = [ pkgs.lib.licenses.mit ];
770 };
809 };
771 };
810 };
772 gunicorn = super.buildPythonPackage {
811 gunicorn = super.buildPythonPackage {
773 name = "gunicorn-19.6.0";
812 name = "gunicorn-19.6.0";
774 buildInputs = with self; [];
813 buildInputs = with self; [];
775 doCheck = false;
814 doCheck = false;
776 propagatedBuildInputs = with self; [];
815 propagatedBuildInputs = with self; [];
777 src = fetchurl {
816 src = fetchurl {
778 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
817 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
779 md5 = "338e5e8a83ea0f0625f768dba4597530";
818 md5 = "338e5e8a83ea0f0625f768dba4597530";
780 };
819 };
781 meta = {
820 meta = {
782 license = [ pkgs.lib.licenses.mit ];
821 license = [ pkgs.lib.licenses.mit ];
783 };
822 };
784 };
823 };
824 html5lib = super.buildPythonPackage {
825 name = "html5lib-0.9999999";
826 buildInputs = with self; [];
827 doCheck = false;
828 propagatedBuildInputs = with self; [six];
829 src = fetchurl {
830 url = "https://pypi.python.org/packages/ae/ae/bcb60402c60932b32dfaf19bb53870b29eda2cd17551ba5639219fb5ebf9/html5lib-0.9999999.tar.gz";
831 md5 = "ef43cb05e9e799f25d65d1135838a96f";
832 };
833 meta = {
834 license = [ pkgs.lib.licenses.mit ];
835 };
836 };
785 infrae.cache = super.buildPythonPackage {
837 infrae.cache = super.buildPythonPackage {
786 name = "infrae.cache-1.0.1";
838 name = "infrae.cache-1.0.1";
787 buildInputs = with self; [];
839 buildInputs = with self; [];
788 doCheck = false;
840 doCheck = false;
789 propagatedBuildInputs = with self; [Beaker repoze.lru];
841 propagatedBuildInputs = with self; [Beaker repoze.lru];
790 src = fetchurl {
842 src = fetchurl {
791 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
843 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
792 md5 = "b09076a766747e6ed2a755cc62088e32";
844 md5 = "b09076a766747e6ed2a755cc62088e32";
793 };
845 };
794 meta = {
846 meta = {
795 license = [ pkgs.lib.licenses.zpt21 ];
847 license = [ pkgs.lib.licenses.zpt21 ];
796 };
848 };
797 };
849 };
798 invoke = super.buildPythonPackage {
850 invoke = super.buildPythonPackage {
799 name = "invoke-0.13.0";
851 name = "invoke-0.13.0";
800 buildInputs = with self; [];
852 buildInputs = with self; [];
801 doCheck = false;
853 doCheck = false;
802 propagatedBuildInputs = with self; [];
854 propagatedBuildInputs = with self; [];
803 src = fetchurl {
855 src = fetchurl {
804 url = "https://pypi.python.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
856 url = "https://pypi.python.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
805 md5 = "c0d1ed4bfb34eaab551662d8cfee6540";
857 md5 = "c0d1ed4bfb34eaab551662d8cfee6540";
806 };
858 };
807 meta = {
859 meta = {
808 license = [ pkgs.lib.licenses.bsdOriginal ];
860 license = [ pkgs.lib.licenses.bsdOriginal ];
809 };
861 };
810 };
862 };
811 ipdb = super.buildPythonPackage {
863 ipdb = super.buildPythonPackage {
812 name = "ipdb-0.10.1";
864 name = "ipdb-0.10.1";
813 buildInputs = with self; [];
865 buildInputs = with self; [];
814 doCheck = false;
866 doCheck = false;
815 propagatedBuildInputs = with self; [ipython setuptools];
867 propagatedBuildInputs = with self; [ipython setuptools];
816 src = fetchurl {
868 src = fetchurl {
817 url = "https://pypi.python.org/packages/eb/0a/0a37dc19572580336ad3813792c0d18c8d7117c2d66fc63c501f13a7a8f8/ipdb-0.10.1.tar.gz";
869 url = "https://pypi.python.org/packages/eb/0a/0a37dc19572580336ad3813792c0d18c8d7117c2d66fc63c501f13a7a8f8/ipdb-0.10.1.tar.gz";
818 md5 = "4aeab65f633ddc98ebdb5eebf08dc713";
870 md5 = "4aeab65f633ddc98ebdb5eebf08dc713";
819 };
871 };
820 meta = {
872 meta = {
821 license = [ pkgs.lib.licenses.bsdOriginal ];
873 license = [ pkgs.lib.licenses.bsdOriginal ];
822 };
874 };
823 };
875 };
824 ipython = super.buildPythonPackage {
876 ipython = super.buildPythonPackage {
825 name = "ipython-5.1.0";
877 name = "ipython-5.1.0";
826 buildInputs = with self; [];
878 buildInputs = with self; [];
827 doCheck = false;
879 doCheck = false;
828 propagatedBuildInputs = with self; [setuptools decorator pickleshare simplegeneric traitlets prompt-toolkit Pygments pexpect backports.shutil-get-terminal-size pathlib2 pexpect];
880 propagatedBuildInputs = with self; [setuptools decorator pickleshare simplegeneric traitlets prompt-toolkit Pygments pexpect backports.shutil-get-terminal-size pathlib2 pexpect];
829 src = fetchurl {
881 src = fetchurl {
830 url = "https://pypi.python.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
882 url = "https://pypi.python.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
831 md5 = "47c8122420f65b58784cb4b9b4af35e3";
883 md5 = "47c8122420f65b58784cb4b9b4af35e3";
832 };
884 };
833 meta = {
885 meta = {
834 license = [ pkgs.lib.licenses.bsdOriginal ];
886 license = [ pkgs.lib.licenses.bsdOriginal ];
835 };
887 };
836 };
888 };
837 ipython-genutils = super.buildPythonPackage {
889 ipython-genutils = super.buildPythonPackage {
838 name = "ipython-genutils-0.1.0";
890 name = "ipython-genutils-0.1.0";
839 buildInputs = with self; [];
891 buildInputs = with self; [];
840 doCheck = false;
892 doCheck = false;
841 propagatedBuildInputs = with self; [];
893 propagatedBuildInputs = with self; [];
842 src = fetchurl {
894 src = fetchurl {
843 url = "https://pypi.python.org/packages/71/b7/a64c71578521606edbbce15151358598f3dfb72a3431763edc2baf19e71f/ipython_genutils-0.1.0.tar.gz";
895 url = "https://pypi.python.org/packages/71/b7/a64c71578521606edbbce15151358598f3dfb72a3431763edc2baf19e71f/ipython_genutils-0.1.0.tar.gz";
844 md5 = "9a8afbe0978adbcbfcb3b35b2d015a56";
896 md5 = "9a8afbe0978adbcbfcb3b35b2d015a56";
845 };
897 };
846 meta = {
898 meta = {
847 license = [ pkgs.lib.licenses.bsdOriginal ];
899 license = [ pkgs.lib.licenses.bsdOriginal ];
848 };
900 };
849 };
901 };
850 iso8601 = super.buildPythonPackage {
902 iso8601 = super.buildPythonPackage {
851 name = "iso8601-0.1.11";
903 name = "iso8601-0.1.11";
852 buildInputs = with self; [];
904 buildInputs = with self; [];
853 doCheck = false;
905 doCheck = false;
854 propagatedBuildInputs = with self; [];
906 propagatedBuildInputs = with self; [];
855 src = fetchurl {
907 src = fetchurl {
856 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
908 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
857 md5 = "b06d11cd14a64096f907086044f0fe38";
909 md5 = "b06d11cd14a64096f907086044f0fe38";
858 };
910 };
859 meta = {
911 meta = {
860 license = [ pkgs.lib.licenses.mit ];
912 license = [ pkgs.lib.licenses.mit ];
861 };
913 };
862 };
914 };
863 itsdangerous = super.buildPythonPackage {
915 itsdangerous = super.buildPythonPackage {
864 name = "itsdangerous-0.24";
916 name = "itsdangerous-0.24";
865 buildInputs = with self; [];
917 buildInputs = with self; [];
866 doCheck = false;
918 doCheck = false;
867 propagatedBuildInputs = with self; [];
919 propagatedBuildInputs = with self; [];
868 src = fetchurl {
920 src = fetchurl {
869 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
921 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
870 md5 = "a3d55aa79369aef5345c036a8a26307f";
922 md5 = "a3d55aa79369aef5345c036a8a26307f";
871 };
923 };
872 meta = {
924 meta = {
873 license = [ pkgs.lib.licenses.bsdOriginal ];
925 license = [ pkgs.lib.licenses.bsdOriginal ];
874 };
926 };
875 };
927 };
928 jsonschema = super.buildPythonPackage {
929 name = "jsonschema-2.6.0";
930 buildInputs = with self; [];
931 doCheck = false;
932 propagatedBuildInputs = with self; [functools32];
933 src = fetchurl {
934 url = "https://pypi.python.org/packages/58/b9/171dbb07e18c6346090a37f03c7e74410a1a56123f847efed59af260a298/jsonschema-2.6.0.tar.gz";
935 md5 = "50c6b69a373a8b55ff1e0ec6e78f13f4";
936 };
937 meta = {
938 license = [ pkgs.lib.licenses.mit ];
939 };
940 };
941 jupyter-client = super.buildPythonPackage {
942 name = "jupyter-client-5.0.0";
943 buildInputs = with self; [];
944 doCheck = false;
945 propagatedBuildInputs = with self; [traitlets jupyter-core pyzmq python-dateutil];
946 src = fetchurl {
947 url = "https://pypi.python.org/packages/e5/6f/65412ed462202b90134b7e761b0b7e7f949e07a549c1755475333727b3d0/jupyter_client-5.0.0.tar.gz";
948 md5 = "1acd331b5c9fb4d79dae9939e79f2426";
949 };
950 meta = {
951 license = [ pkgs.lib.licenses.bsdOriginal ];
952 };
953 };
954 jupyter-core = super.buildPythonPackage {
955 name = "jupyter-core-4.3.0";
956 buildInputs = with self; [];
957 doCheck = false;
958 propagatedBuildInputs = with self; [traitlets];
959 src = fetchurl {
960 url = "https://pypi.python.org/packages/2f/39/5138f975100ce14d150938df48a83cd852a3fd8e24b1244f4113848e69e2/jupyter_core-4.3.0.tar.gz";
961 md5 = "18819511a809afdeed9a995a9c27bcfb";
962 };
963 meta = {
964 license = [ pkgs.lib.licenses.bsdOriginal ];
965 };
966 };
876 kombu = super.buildPythonPackage {
967 kombu = super.buildPythonPackage {
877 name = "kombu-1.5.1";
968 name = "kombu-1.5.1";
878 buildInputs = with self; [];
969 buildInputs = with self; [];
879 doCheck = false;
970 doCheck = false;
880 propagatedBuildInputs = with self; [anyjson amqplib];
971 propagatedBuildInputs = with self; [anyjson amqplib];
881 src = fetchurl {
972 src = fetchurl {
882 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
973 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
883 md5 = "50662f3c7e9395b3d0721fb75d100b63";
974 md5 = "50662f3c7e9395b3d0721fb75d100b63";
884 };
975 };
885 meta = {
976 meta = {
886 license = [ pkgs.lib.licenses.bsdOriginal ];
977 license = [ pkgs.lib.licenses.bsdOriginal ];
887 };
978 };
888 };
979 };
889 lxml = super.buildPythonPackage {
980 lxml = super.buildPythonPackage {
890 name = "lxml-3.4.4";
981 name = "lxml-3.7.3";
891 buildInputs = with self; [];
982 buildInputs = with self; [];
892 doCheck = false;
983 doCheck = false;
893 propagatedBuildInputs = with self; [];
984 propagatedBuildInputs = with self; [];
894 src = fetchurl {
985 src = fetchurl {
895 url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz";
986 url = "https://pypi.python.org/packages/39/e8/a8e0b1fa65dd021d48fe21464f71783655f39a41f218293c1c590d54eb82/lxml-3.7.3.tar.gz";
896 md5 = "a9a65972afc173ec7a39c585f4eea69c";
987 md5 = "075692ce442e69bbd604d44e21c02753";
897 };
988 };
898 meta = {
989 meta = {
899 license = [ pkgs.lib.licenses.bsdOriginal ];
990 license = [ pkgs.lib.licenses.bsdOriginal ];
900 };
991 };
901 };
992 };
902 meld3 = super.buildPythonPackage {
993 meld3 = super.buildPythonPackage {
903 name = "meld3-1.0.2";
994 name = "meld3-1.0.2";
904 buildInputs = with self; [];
995 buildInputs = with self; [];
905 doCheck = false;
996 doCheck = false;
906 propagatedBuildInputs = with self; [];
997 propagatedBuildInputs = with self; [];
907 src = fetchurl {
998 src = fetchurl {
908 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
999 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
909 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
1000 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
910 };
1001 };
911 meta = {
1002 meta = {
912 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1003 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
913 };
1004 };
914 };
1005 };
1006 mistune = super.buildPythonPackage {
1007 name = "mistune-0.7.3";
1008 buildInputs = with self; [];
1009 doCheck = false;
1010 propagatedBuildInputs = with self; [];
1011 src = fetchurl {
1012 url = "https://pypi.python.org/packages/88/1e/be99791262b3a794332fda598a07c2749a433b9378586361ba9d8e824607/mistune-0.7.3.tar.gz";
1013 md5 = "4eba50bd121b83716fa4be6a4049004b";
1014 };
1015 meta = {
1016 license = [ pkgs.lib.licenses.bsdOriginal ];
1017 };
1018 };
915 mock = super.buildPythonPackage {
1019 mock = super.buildPythonPackage {
916 name = "mock-1.0.1";
1020 name = "mock-1.0.1";
917 buildInputs = with self; [];
1021 buildInputs = with self; [];
918 doCheck = false;
1022 doCheck = false;
919 propagatedBuildInputs = with self; [];
1023 propagatedBuildInputs = with self; [];
920 src = fetchurl {
1024 src = fetchurl {
921 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
1025 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
922 md5 = "869f08d003c289a97c1a6610faf5e913";
1026 md5 = "869f08d003c289a97c1a6610faf5e913";
923 };
1027 };
924 meta = {
1028 meta = {
925 license = [ pkgs.lib.licenses.bsdOriginal ];
1029 license = [ pkgs.lib.licenses.bsdOriginal ];
926 };
1030 };
927 };
1031 };
928 msgpack-python = super.buildPythonPackage {
1032 msgpack-python = super.buildPythonPackage {
929 name = "msgpack-python-0.4.8";
1033 name = "msgpack-python-0.4.8";
930 buildInputs = with self; [];
1034 buildInputs = with self; [];
931 doCheck = false;
1035 doCheck = false;
932 propagatedBuildInputs = with self; [];
1036 propagatedBuildInputs = with self; [];
933 src = fetchurl {
1037 src = fetchurl {
934 url = "https://pypi.python.org/packages/21/27/8a1d82041c7a2a51fcc73675875a5f9ea06c2663e02fcfeb708be1d081a0/msgpack-python-0.4.8.tar.gz";
1038 url = "https://pypi.python.org/packages/21/27/8a1d82041c7a2a51fcc73675875a5f9ea06c2663e02fcfeb708be1d081a0/msgpack-python-0.4.8.tar.gz";
935 md5 = "dcd854fb41ee7584ebbf35e049e6be98";
1039 md5 = "dcd854fb41ee7584ebbf35e049e6be98";
936 };
1040 };
937 meta = {
1041 meta = {
938 license = [ pkgs.lib.licenses.asl20 ];
1042 license = [ pkgs.lib.licenses.asl20 ];
939 };
1043 };
940 };
1044 };
1045 nbconvert = super.buildPythonPackage {
1046 name = "nbconvert-5.1.1";
1047 buildInputs = with self; [];
1048 doCheck = false;
1049 propagatedBuildInputs = with self; [mistune Jinja2 Pygments traitlets jupyter-core nbformat entrypoints bleach pandocfilters testpath];
1050 src = fetchurl {
1051 url = "https://pypi.python.org/packages/95/58/df1c91f1658ee5df19097f915a1e71c91fc824a708d82d2b2e35f8b80e9a/nbconvert-5.1.1.tar.gz";
1052 md5 = "d0263fb03a44db2f94eea09a608ed813";
1053 };
1054 meta = {
1055 license = [ pkgs.lib.licenses.bsdOriginal ];
1056 };
1057 };
1058 nbformat = super.buildPythonPackage {
1059 name = "nbformat-4.3.0";
1060 buildInputs = with self; [];
1061 doCheck = false;
1062 propagatedBuildInputs = with self; [ipython-genutils traitlets jsonschema jupyter-core];
1063 src = fetchurl {
1064 url = "https://pypi.python.org/packages/f9/c5/89df4abf906f766727f976e170caa85b4f1c1d1feb1f45d716016e68e19f/nbformat-4.3.0.tar.gz";
1065 md5 = "9a00d20425914cd5ba5f97769d9963ca";
1066 };
1067 meta = {
1068 license = [ pkgs.lib.licenses.bsdOriginal ];
1069 };
1070 };
941 nose = super.buildPythonPackage {
1071 nose = super.buildPythonPackage {
942 name = "nose-1.3.6";
1072 name = "nose-1.3.6";
943 buildInputs = with self; [];
1073 buildInputs = with self; [];
944 doCheck = false;
1074 doCheck = false;
945 propagatedBuildInputs = with self; [];
1075 propagatedBuildInputs = with self; [];
946 src = fetchurl {
1076 src = fetchurl {
947 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
1077 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
948 md5 = "0ca546d81ca8309080fc80cb389e7a16";
1078 md5 = "0ca546d81ca8309080fc80cb389e7a16";
949 };
1079 };
950 meta = {
1080 meta = {
951 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "GNU LGPL"; } ];
1081 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "GNU LGPL"; } ];
952 };
1082 };
953 };
1083 };
954 objgraph = super.buildPythonPackage {
1084 objgraph = super.buildPythonPackage {
955 name = "objgraph-2.0.0";
1085 name = "objgraph-2.0.0";
956 buildInputs = with self; [];
1086 buildInputs = with self; [];
957 doCheck = false;
1087 doCheck = false;
958 propagatedBuildInputs = with self; [];
1088 propagatedBuildInputs = with self; [];
959 src = fetchurl {
1089 src = fetchurl {
960 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
1090 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
961 md5 = "25b0d5e5adc74aa63ead15699614159c";
1091 md5 = "25b0d5e5adc74aa63ead15699614159c";
962 };
1092 };
963 meta = {
1093 meta = {
964 license = [ pkgs.lib.licenses.mit ];
1094 license = [ pkgs.lib.licenses.mit ];
965 };
1095 };
966 };
1096 };
967 packaging = super.buildPythonPackage {
1097 packaging = super.buildPythonPackage {
968 name = "packaging-15.2";
1098 name = "packaging-15.2";
969 buildInputs = with self; [];
1099 buildInputs = with self; [];
970 doCheck = false;
1100 doCheck = false;
971 propagatedBuildInputs = with self; [];
1101 propagatedBuildInputs = with self; [];
972 src = fetchurl {
1102 src = fetchurl {
973 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
1103 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
974 md5 = "c16093476f6ced42128bf610e5db3784";
1104 md5 = "c16093476f6ced42128bf610e5db3784";
975 };
1105 };
976 meta = {
1106 meta = {
977 license = [ pkgs.lib.licenses.asl20 ];
1107 license = [ pkgs.lib.licenses.asl20 ];
978 };
1108 };
979 };
1109 };
1110 pandocfilters = super.buildPythonPackage {
1111 name = "pandocfilters-1.4.1";
1112 buildInputs = with self; [];
1113 doCheck = false;
1114 propagatedBuildInputs = with self; [];
1115 src = fetchurl {
1116 url = "https://pypi.python.org/packages/e3/1f/21d1b7e8ca571e80b796c758d361fdf5554335ff138158654684bc5401d8/pandocfilters-1.4.1.tar.gz";
1117 md5 = "7680d9f9ec07397dd17f380ee3818b9d";
1118 };
1119 meta = {
1120 license = [ pkgs.lib.licenses.bsdOriginal ];
1121 };
1122 };
980 paramiko = super.buildPythonPackage {
1123 paramiko = super.buildPythonPackage {
981 name = "paramiko-1.15.1";
1124 name = "paramiko-1.15.1";
982 buildInputs = with self; [];
1125 buildInputs = with self; [];
983 doCheck = false;
1126 doCheck = false;
984 propagatedBuildInputs = with self; [pycrypto ecdsa];
1127 propagatedBuildInputs = with self; [pycrypto ecdsa];
985 src = fetchurl {
1128 src = fetchurl {
986 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
1129 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
987 md5 = "48c274c3f9b1282932567b21f6acf3b5";
1130 md5 = "48c274c3f9b1282932567b21f6acf3b5";
988 };
1131 };
989 meta = {
1132 meta = {
990 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1133 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
991 };
1134 };
992 };
1135 };
993 pathlib2 = super.buildPythonPackage {
1136 pathlib2 = super.buildPythonPackage {
994 name = "pathlib2-2.1.0";
1137 name = "pathlib2-2.1.0";
995 buildInputs = with self; [];
1138 buildInputs = with self; [];
996 doCheck = false;
1139 doCheck = false;
997 propagatedBuildInputs = with self; [six];
1140 propagatedBuildInputs = with self; [six];
998 src = fetchurl {
1141 src = fetchurl {
999 url = "https://pypi.python.org/packages/c9/27/8448b10d8440c08efeff0794adf7d0ed27adb98372c70c7b38f3947d4749/pathlib2-2.1.0.tar.gz";
1142 url = "https://pypi.python.org/packages/c9/27/8448b10d8440c08efeff0794adf7d0ed27adb98372c70c7b38f3947d4749/pathlib2-2.1.0.tar.gz";
1000 md5 = "38e4f58b4d69dfcb9edb49a54a8b28d2";
1143 md5 = "38e4f58b4d69dfcb9edb49a54a8b28d2";
1001 };
1144 };
1002 meta = {
1145 meta = {
1003 license = [ pkgs.lib.licenses.mit ];
1146 license = [ pkgs.lib.licenses.mit ];
1004 };
1147 };
1005 };
1148 };
1006 peppercorn = super.buildPythonPackage {
1149 peppercorn = super.buildPythonPackage {
1007 name = "peppercorn-0.5";
1150 name = "peppercorn-0.5";
1008 buildInputs = with self; [];
1151 buildInputs = with self; [];
1009 doCheck = false;
1152 doCheck = false;
1010 propagatedBuildInputs = with self; [];
1153 propagatedBuildInputs = with self; [];
1011 src = fetchurl {
1154 src = fetchurl {
1012 url = "https://pypi.python.org/packages/45/ec/a62ec317d1324a01567c5221b420742f094f05ee48097e5157d32be3755c/peppercorn-0.5.tar.gz";
1155 url = "https://pypi.python.org/packages/45/ec/a62ec317d1324a01567c5221b420742f094f05ee48097e5157d32be3755c/peppercorn-0.5.tar.gz";
1013 md5 = "f08efbca5790019ab45d76b7244abd40";
1156 md5 = "f08efbca5790019ab45d76b7244abd40";
1014 };
1157 };
1015 meta = {
1158 meta = {
1016 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1159 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1017 };
1160 };
1018 };
1161 };
1019 pexpect = super.buildPythonPackage {
1162 pexpect = super.buildPythonPackage {
1020 name = "pexpect-4.2.1";
1163 name = "pexpect-4.2.1";
1021 buildInputs = with self; [];
1164 buildInputs = with self; [];
1022 doCheck = false;
1165 doCheck = false;
1023 propagatedBuildInputs = with self; [ptyprocess];
1166 propagatedBuildInputs = with self; [ptyprocess];
1024 src = fetchurl {
1167 src = fetchurl {
1025 url = "https://pypi.python.org/packages/e8/13/d0b0599099d6cd23663043a2a0bb7c61e58c6ba359b2656e6fb000ef5b98/pexpect-4.2.1.tar.gz";
1168 url = "https://pypi.python.org/packages/e8/13/d0b0599099d6cd23663043a2a0bb7c61e58c6ba359b2656e6fb000ef5b98/pexpect-4.2.1.tar.gz";
1026 md5 = "3694410001a99dff83f0b500a1ca1c95";
1169 md5 = "3694410001a99dff83f0b500a1ca1c95";
1027 };
1170 };
1028 meta = {
1171 meta = {
1029 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1172 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1030 };
1173 };
1031 };
1174 };
1032 pickleshare = super.buildPythonPackage {
1175 pickleshare = super.buildPythonPackage {
1033 name = "pickleshare-0.7.4";
1176 name = "pickleshare-0.7.4";
1034 buildInputs = with self; [];
1177 buildInputs = with self; [];
1035 doCheck = false;
1178 doCheck = false;
1036 propagatedBuildInputs = with self; [pathlib2];
1179 propagatedBuildInputs = with self; [pathlib2];
1037 src = fetchurl {
1180 src = fetchurl {
1038 url = "https://pypi.python.org/packages/69/fe/dd137d84daa0fd13a709e448138e310d9ea93070620c9db5454e234af525/pickleshare-0.7.4.tar.gz";
1181 url = "https://pypi.python.org/packages/69/fe/dd137d84daa0fd13a709e448138e310d9ea93070620c9db5454e234af525/pickleshare-0.7.4.tar.gz";
1039 md5 = "6a9e5dd8dfc023031f6b7b3f824cab12";
1182 md5 = "6a9e5dd8dfc023031f6b7b3f824cab12";
1040 };
1183 };
1041 meta = {
1184 meta = {
1042 license = [ pkgs.lib.licenses.mit ];
1185 license = [ pkgs.lib.licenses.mit ];
1043 };
1186 };
1044 };
1187 };
1045 prompt-toolkit = super.buildPythonPackage {
1188 prompt-toolkit = super.buildPythonPackage {
1046 name = "prompt-toolkit-1.0.9";
1189 name = "prompt-toolkit-1.0.13";
1047 buildInputs = with self; [];
1190 buildInputs = with self; [];
1048 doCheck = false;
1191 doCheck = false;
1049 propagatedBuildInputs = with self; [six wcwidth];
1192 propagatedBuildInputs = with self; [six wcwidth];
1050 src = fetchurl {
1193 src = fetchurl {
1051 url = "https://pypi.python.org/packages/83/14/5ac258da6c530eca02852ee25c7a9ff3ca78287bb4c198d0d0055845d856/prompt_toolkit-1.0.9.tar.gz";
1194 url = "https://pypi.python.org/packages/23/be/4876b52d5cc159cbd4b0ff6e7aa419a26470849a43a8f647857a4a24467b/prompt_toolkit-1.0.13.tar.gz";
1052 md5 = "a39f91a54308fb7446b1a421c11f227c";
1195 md5 = "427b496d2c147bd3819bc3a7f6e0d493";
1053 };
1196 };
1054 meta = {
1197 meta = {
1055 license = [ pkgs.lib.licenses.bsdOriginal ];
1198 license = [ pkgs.lib.licenses.bsdOriginal ];
1056 };
1199 };
1057 };
1200 };
1058 psutil = super.buildPythonPackage {
1201 psutil = super.buildPythonPackage {
1059 name = "psutil-4.3.1";
1202 name = "psutil-4.3.1";
1060 buildInputs = with self; [];
1203 buildInputs = with self; [];
1061 doCheck = false;
1204 doCheck = false;
1062 propagatedBuildInputs = with self; [];
1205 propagatedBuildInputs = with self; [];
1063 src = fetchurl {
1206 src = fetchurl {
1064 url = "https://pypi.python.org/packages/78/cc/f267a1371f229bf16db6a4e604428c3b032b823b83155bd33cef45e49a53/psutil-4.3.1.tar.gz";
1207 url = "https://pypi.python.org/packages/78/cc/f267a1371f229bf16db6a4e604428c3b032b823b83155bd33cef45e49a53/psutil-4.3.1.tar.gz";
1065 md5 = "199a366dba829c88bddaf5b41d19ddc0";
1208 md5 = "199a366dba829c88bddaf5b41d19ddc0";
1066 };
1209 };
1067 meta = {
1210 meta = {
1068 license = [ pkgs.lib.licenses.bsdOriginal ];
1211 license = [ pkgs.lib.licenses.bsdOriginal ];
1069 };
1212 };
1070 };
1213 };
1071 psycopg2 = super.buildPythonPackage {
1214 psycopg2 = super.buildPythonPackage {
1072 name = "psycopg2-2.6.1";
1215 name = "psycopg2-2.6.1";
1073 buildInputs = with self; [];
1216 buildInputs = with self; [];
1074 doCheck = false;
1217 doCheck = false;
1075 propagatedBuildInputs = with self; [];
1218 propagatedBuildInputs = with self; [];
1076 src = fetchurl {
1219 src = fetchurl {
1077 url = "https://pypi.python.org/packages/86/fd/cc8315be63a41fe000cce20482a917e874cdc1151e62cb0141f5e55f711e/psycopg2-2.6.1.tar.gz";
1220 url = "https://pypi.python.org/packages/86/fd/cc8315be63a41fe000cce20482a917e874cdc1151e62cb0141f5e55f711e/psycopg2-2.6.1.tar.gz";
1078 md5 = "842b44f8c95517ed5b792081a2370da1";
1221 md5 = "842b44f8c95517ed5b792081a2370da1";
1079 };
1222 };
1080 meta = {
1223 meta = {
1081 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1224 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1082 };
1225 };
1083 };
1226 };
1084 ptyprocess = super.buildPythonPackage {
1227 ptyprocess = super.buildPythonPackage {
1085 name = "ptyprocess-0.5.1";
1228 name = "ptyprocess-0.5.1";
1086 buildInputs = with self; [];
1229 buildInputs = with self; [];
1087 doCheck = false;
1230 doCheck = false;
1088 propagatedBuildInputs = with self; [];
1231 propagatedBuildInputs = with self; [];
1089 src = fetchurl {
1232 src = fetchurl {
1090 url = "https://pypi.python.org/packages/db/d7/b465161910f3d1cef593c5e002bff67e0384898f597f1a7fdc8db4c02bf6/ptyprocess-0.5.1.tar.gz";
1233 url = "https://pypi.python.org/packages/db/d7/b465161910f3d1cef593c5e002bff67e0384898f597f1a7fdc8db4c02bf6/ptyprocess-0.5.1.tar.gz";
1091 md5 = "94e537122914cc9ec9c1eadcd36e73a1";
1234 md5 = "94e537122914cc9ec9c1eadcd36e73a1";
1092 };
1235 };
1093 meta = {
1236 meta = {
1094 license = [ ];
1237 license = [ ];
1095 };
1238 };
1096 };
1239 };
1097 py = super.buildPythonPackage {
1240 py = super.buildPythonPackage {
1098 name = "py-1.4.31";
1241 name = "py-1.4.31";
1099 buildInputs = with self; [];
1242 buildInputs = with self; [];
1100 doCheck = false;
1243 doCheck = false;
1101 propagatedBuildInputs = with self; [];
1244 propagatedBuildInputs = with self; [];
1102 src = fetchurl {
1245 src = fetchurl {
1103 url = "https://pypi.python.org/packages/f4/9a/8dfda23f36600dd701c6722316ba8a3ab4b990261f83e7d3ffc6dfedf7ef/py-1.4.31.tar.gz";
1246 url = "https://pypi.python.org/packages/f4/9a/8dfda23f36600dd701c6722316ba8a3ab4b990261f83e7d3ffc6dfedf7ef/py-1.4.31.tar.gz";
1104 md5 = "5d2c63c56dc3f2115ec35c066ecd582b";
1247 md5 = "5d2c63c56dc3f2115ec35c066ecd582b";
1105 };
1248 };
1106 meta = {
1249 meta = {
1107 license = [ pkgs.lib.licenses.mit ];
1250 license = [ pkgs.lib.licenses.mit ];
1108 };
1251 };
1109 };
1252 };
1110 py-bcrypt = super.buildPythonPackage {
1253 py-bcrypt = super.buildPythonPackage {
1111 name = "py-bcrypt-0.4";
1254 name = "py-bcrypt-0.4";
1112 buildInputs = with self; [];
1255 buildInputs = with self; [];
1113 doCheck = false;
1256 doCheck = false;
1114 propagatedBuildInputs = with self; [];
1257 propagatedBuildInputs = with self; [];
1115 src = fetchurl {
1258 src = fetchurl {
1116 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1259 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1117 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
1260 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
1118 };
1261 };
1119 meta = {
1262 meta = {
1120 license = [ pkgs.lib.licenses.bsdOriginal ];
1263 license = [ pkgs.lib.licenses.bsdOriginal ];
1121 };
1264 };
1122 };
1265 };
1123 py-gfm = super.buildPythonPackage {
1266 py-gfm = super.buildPythonPackage {
1124 name = "py-gfm-0.1.3";
1267 name = "py-gfm-0.1.3";
1125 buildInputs = with self; [];
1268 buildInputs = with self; [];
1126 doCheck = false;
1269 doCheck = false;
1127 propagatedBuildInputs = with self; [setuptools Markdown];
1270 propagatedBuildInputs = with self; [setuptools Markdown];
1128 src = fetchurl {
1271 src = fetchurl {
1129 url = "https://code.rhodecode.com/upstream/py-gfm/archive/0d66a19bc16e3d49de273c0f797d4e4781e8c0f2.tar.gz?md5=0d0d5385bfb629eea636a80b9c2bfd16";
1272 url = "https://code.rhodecode.com/upstream/py-gfm/archive/0d66a19bc16e3d49de273c0f797d4e4781e8c0f2.tar.gz?md5=0d0d5385bfb629eea636a80b9c2bfd16";
1130 md5 = "0d0d5385bfb629eea636a80b9c2bfd16";
1273 md5 = "0d0d5385bfb629eea636a80b9c2bfd16";
1131 };
1274 };
1132 meta = {
1275 meta = {
1133 license = [ pkgs.lib.licenses.bsdOriginal ];
1276 license = [ pkgs.lib.licenses.bsdOriginal ];
1134 };
1277 };
1135 };
1278 };
1136 pycrypto = super.buildPythonPackage {
1279 pycrypto = super.buildPythonPackage {
1137 name = "pycrypto-2.6.1";
1280 name = "pycrypto-2.6.1";
1138 buildInputs = with self; [];
1281 buildInputs = with self; [];
1139 doCheck = false;
1282 doCheck = false;
1140 propagatedBuildInputs = with self; [];
1283 propagatedBuildInputs = with self; [];
1141 src = fetchurl {
1284 src = fetchurl {
1142 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1285 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1143 md5 = "55a61a054aa66812daf5161a0d5d7eda";
1286 md5 = "55a61a054aa66812daf5161a0d5d7eda";
1144 };
1287 };
1145 meta = {
1288 meta = {
1146 license = [ pkgs.lib.licenses.publicDomain ];
1289 license = [ pkgs.lib.licenses.publicDomain ];
1147 };
1290 };
1148 };
1291 };
1149 pycurl = super.buildPythonPackage {
1292 pycurl = super.buildPythonPackage {
1150 name = "pycurl-7.19.5";
1293 name = "pycurl-7.19.5";
1151 buildInputs = with self; [];
1294 buildInputs = with self; [];
1152 doCheck = false;
1295 doCheck = false;
1153 propagatedBuildInputs = with self; [];
1296 propagatedBuildInputs = with self; [];
1154 src = fetchurl {
1297 src = fetchurl {
1155 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
1298 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
1156 md5 = "47b4eac84118e2606658122104e62072";
1299 md5 = "47b4eac84118e2606658122104e62072";
1157 };
1300 };
1158 meta = {
1301 meta = {
1159 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1302 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1160 };
1303 };
1161 };
1304 };
1162 pyflakes = super.buildPythonPackage {
1305 pyflakes = super.buildPythonPackage {
1163 name = "pyflakes-0.8.1";
1306 name = "pyflakes-0.8.1";
1164 buildInputs = with self; [];
1307 buildInputs = with self; [];
1165 doCheck = false;
1308 doCheck = false;
1166 propagatedBuildInputs = with self; [];
1309 propagatedBuildInputs = with self; [];
1167 src = fetchurl {
1310 src = fetchurl {
1168 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1311 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1169 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
1312 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
1170 };
1313 };
1171 meta = {
1314 meta = {
1172 license = [ pkgs.lib.licenses.mit ];
1315 license = [ pkgs.lib.licenses.mit ];
1173 };
1316 };
1174 };
1317 };
1175 pygments-markdown-lexer = super.buildPythonPackage {
1318 pygments-markdown-lexer = super.buildPythonPackage {
1176 name = "pygments-markdown-lexer-0.1.0.dev39";
1319 name = "pygments-markdown-lexer-0.1.0.dev39";
1177 buildInputs = with self; [];
1320 buildInputs = with self; [];
1178 doCheck = false;
1321 doCheck = false;
1179 propagatedBuildInputs = with self; [Pygments];
1322 propagatedBuildInputs = with self; [Pygments];
1180 src = fetchurl {
1323 src = fetchurl {
1181 url = "https://pypi.python.org/packages/c3/12/674cdee66635d638cedb2c5d9c85ce507b7b2f91bdba29e482f1b1160ff6/pygments-markdown-lexer-0.1.0.dev39.zip";
1324 url = "https://pypi.python.org/packages/c3/12/674cdee66635d638cedb2c5d9c85ce507b7b2f91bdba29e482f1b1160ff6/pygments-markdown-lexer-0.1.0.dev39.zip";
1182 md5 = "6360fe0f6d1f896e35b7a0142ce6459c";
1325 md5 = "6360fe0f6d1f896e35b7a0142ce6459c";
1183 };
1326 };
1184 meta = {
1327 meta = {
1185 license = [ pkgs.lib.licenses.asl20 ];
1328 license = [ pkgs.lib.licenses.asl20 ];
1186 };
1329 };
1187 };
1330 };
1188 pyparsing = super.buildPythonPackage {
1331 pyparsing = super.buildPythonPackage {
1189 name = "pyparsing-1.5.7";
1332 name = "pyparsing-1.5.7";
1190 buildInputs = with self; [];
1333 buildInputs = with self; [];
1191 doCheck = false;
1334 doCheck = false;
1192 propagatedBuildInputs = with self; [];
1335 propagatedBuildInputs = with self; [];
1193 src = fetchurl {
1336 src = fetchurl {
1194 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
1337 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
1195 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
1338 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
1196 };
1339 };
1197 meta = {
1340 meta = {
1198 license = [ pkgs.lib.licenses.mit ];
1341 license = [ pkgs.lib.licenses.mit ];
1199 };
1342 };
1200 };
1343 };
1201 pyramid = super.buildPythonPackage {
1344 pyramid = super.buildPythonPackage {
1202 name = "pyramid-1.6.1";
1345 name = "pyramid-1.7.4";
1203 buildInputs = with self; [];
1346 buildInputs = with self; [];
1204 doCheck = false;
1347 doCheck = false;
1205 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
1348 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
1206 src = fetchurl {
1349 src = fetchurl {
1207 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
1350 url = "https://pypi.python.org/packages/33/91/55f5c661f8923902cd1f68d75f2b937c45e7682857356cf18f0be5493899/pyramid-1.7.4.tar.gz";
1208 md5 = "b18688ff3cc33efdbb098a35b45dd122";
1351 md5 = "6ef1dfdcff9136d04490410757c4c446";
1209 };
1352 };
1210 meta = {
1353 meta = {
1211 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1354 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1212 };
1355 };
1213 };
1356 };
1214 pyramid-beaker = super.buildPythonPackage {
1357 pyramid-beaker = super.buildPythonPackage {
1215 name = "pyramid-beaker-0.8";
1358 name = "pyramid-beaker-0.8";
1216 buildInputs = with self; [];
1359 buildInputs = with self; [];
1217 doCheck = false;
1360 doCheck = false;
1218 propagatedBuildInputs = with self; [pyramid Beaker];
1361 propagatedBuildInputs = with self; [pyramid Beaker];
1219 src = fetchurl {
1362 src = fetchurl {
1220 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1363 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1221 md5 = "22f14be31b06549f80890e2c63a93834";
1364 md5 = "22f14be31b06549f80890e2c63a93834";
1222 };
1365 };
1223 meta = {
1366 meta = {
1224 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1367 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1225 };
1368 };
1226 };
1369 };
1227 pyramid-debugtoolbar = super.buildPythonPackage {
1370 pyramid-debugtoolbar = super.buildPythonPackage {
1228 name = "pyramid-debugtoolbar-2.4.2";
1371 name = "pyramid-debugtoolbar-3.0.5";
1229 buildInputs = with self; [];
1372 buildInputs = with self; [];
1230 doCheck = false;
1373 doCheck = false;
1231 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
1374 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
1232 src = fetchurl {
1375 src = fetchurl {
1233 url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz";
1376 url = "https://pypi.python.org/packages/64/0e/df00bfb55605900e7a2f7e4a18dd83575a6651688e297d5a0aa4c208fd7d/pyramid_debugtoolbar-3.0.5.tar.gz";
1234 md5 = "073ea67086cc4bd5decc3a000853642d";
1377 md5 = "aebab8c3bfdc6f89e4d3adc1d126538e";
1235 };
1378 };
1236 meta = {
1379 meta = {
1237 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1380 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1238 };
1381 };
1239 };
1382 };
1240 pyramid-jinja2 = super.buildPythonPackage {
1383 pyramid-jinja2 = super.buildPythonPackage {
1241 name = "pyramid-jinja2-2.5";
1384 name = "pyramid-jinja2-2.5";
1242 buildInputs = with self; [];
1385 buildInputs = with self; [];
1243 doCheck = false;
1386 doCheck = false;
1244 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
1387 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
1245 src = fetchurl {
1388 src = fetchurl {
1246 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
1389 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
1247 md5 = "07cb6547204ac5e6f0b22a954ccee928";
1390 md5 = "07cb6547204ac5e6f0b22a954ccee928";
1248 };
1391 };
1249 meta = {
1392 meta = {
1250 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1393 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1251 };
1394 };
1252 };
1395 };
1253 pyramid-mako = super.buildPythonPackage {
1396 pyramid-mako = super.buildPythonPackage {
1254 name = "pyramid-mako-1.0.2";
1397 name = "pyramid-mako-1.0.2";
1255 buildInputs = with self; [];
1398 buildInputs = with self; [];
1256 doCheck = false;
1399 doCheck = false;
1257 propagatedBuildInputs = with self; [pyramid Mako];
1400 propagatedBuildInputs = with self; [pyramid Mako];
1258 src = fetchurl {
1401 src = fetchurl {
1259 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1402 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1260 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
1403 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
1261 };
1404 };
1262 meta = {
1405 meta = {
1263 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1406 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1264 };
1407 };
1265 };
1408 };
1266 pysqlite = super.buildPythonPackage {
1409 pysqlite = super.buildPythonPackage {
1267 name = "pysqlite-2.6.3";
1410 name = "pysqlite-2.6.3";
1268 buildInputs = with self; [];
1411 buildInputs = with self; [];
1269 doCheck = false;
1412 doCheck = false;
1270 propagatedBuildInputs = with self; [];
1413 propagatedBuildInputs = with self; [];
1271 src = fetchurl {
1414 src = fetchurl {
1272 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
1415 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
1273 md5 = "7ff1cedee74646b50117acff87aa1cfa";
1416 md5 = "7ff1cedee74646b50117acff87aa1cfa";
1274 };
1417 };
1275 meta = {
1418 meta = {
1276 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1419 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1277 };
1420 };
1278 };
1421 };
1279 pytest = super.buildPythonPackage {
1422 pytest = super.buildPythonPackage {
1280 name = "pytest-3.0.5";
1423 name = "pytest-3.0.5";
1281 buildInputs = with self; [];
1424 buildInputs = with self; [];
1282 doCheck = false;
1425 doCheck = false;
1283 propagatedBuildInputs = with self; [py];
1426 propagatedBuildInputs = with self; [py];
1284 src = fetchurl {
1427 src = fetchurl {
1285 url = "https://pypi.python.org/packages/a8/87/b7ca49efe52d2b4169f2bfc49aa5e384173c4619ea8e635f123a0dac5b75/pytest-3.0.5.tar.gz";
1428 url = "https://pypi.python.org/packages/a8/87/b7ca49efe52d2b4169f2bfc49aa5e384173c4619ea8e635f123a0dac5b75/pytest-3.0.5.tar.gz";
1286 md5 = "cefd527b59332688bf5db4a10aa8a7cb";
1429 md5 = "cefd527b59332688bf5db4a10aa8a7cb";
1287 };
1430 };
1288 meta = {
1431 meta = {
1289 license = [ pkgs.lib.licenses.mit ];
1432 license = [ pkgs.lib.licenses.mit ];
1290 };
1433 };
1291 };
1434 };
1292 pytest-catchlog = super.buildPythonPackage {
1435 pytest-catchlog = super.buildPythonPackage {
1293 name = "pytest-catchlog-1.2.2";
1436 name = "pytest-catchlog-1.2.2";
1294 buildInputs = with self; [];
1437 buildInputs = with self; [];
1295 doCheck = false;
1438 doCheck = false;
1296 propagatedBuildInputs = with self; [py pytest];
1439 propagatedBuildInputs = with self; [py pytest];
1297 src = fetchurl {
1440 src = fetchurl {
1298 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
1441 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
1299 md5 = "09d890c54c7456c818102b7ff8c182c8";
1442 md5 = "09d890c54c7456c818102b7ff8c182c8";
1300 };
1443 };
1301 meta = {
1444 meta = {
1302 license = [ pkgs.lib.licenses.mit ];
1445 license = [ pkgs.lib.licenses.mit ];
1303 };
1446 };
1304 };
1447 };
1305 pytest-cov = super.buildPythonPackage {
1448 pytest-cov = super.buildPythonPackage {
1306 name = "pytest-cov-2.4.0";
1449 name = "pytest-cov-2.4.0";
1307 buildInputs = with self; [];
1450 buildInputs = with self; [];
1308 doCheck = false;
1451 doCheck = false;
1309 propagatedBuildInputs = with self; [pytest coverage];
1452 propagatedBuildInputs = with self; [pytest coverage];
1310 src = fetchurl {
1453 src = fetchurl {
1311 url = "https://pypi.python.org/packages/00/c0/2bfd1fcdb9d407b8ac8185b1cb5ff458105c6b207a9a7f0e13032de9828f/pytest-cov-2.4.0.tar.gz";
1454 url = "https://pypi.python.org/packages/00/c0/2bfd1fcdb9d407b8ac8185b1cb5ff458105c6b207a9a7f0e13032de9828f/pytest-cov-2.4.0.tar.gz";
1312 md5 = "2fda09677d232acc99ec1b3c5831e33f";
1455 md5 = "2fda09677d232acc99ec1b3c5831e33f";
1313 };
1456 };
1314 meta = {
1457 meta = {
1315 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1458 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1316 };
1459 };
1317 };
1460 };
1318 pytest-profiling = super.buildPythonPackage {
1461 pytest-profiling = super.buildPythonPackage {
1319 name = "pytest-profiling-1.2.2";
1462 name = "pytest-profiling-1.2.2";
1320 buildInputs = with self; [];
1463 buildInputs = with self; [];
1321 doCheck = false;
1464 doCheck = false;
1322 propagatedBuildInputs = with self; [six pytest gprof2dot];
1465 propagatedBuildInputs = with self; [six pytest gprof2dot];
1323 src = fetchurl {
1466 src = fetchurl {
1324 url = "https://pypi.python.org/packages/73/e8/804681323bac0bc45c520ec34185ba8469008942266d0074699b204835c1/pytest-profiling-1.2.2.tar.gz";
1467 url = "https://pypi.python.org/packages/73/e8/804681323bac0bc45c520ec34185ba8469008942266d0074699b204835c1/pytest-profiling-1.2.2.tar.gz";
1325 md5 = "0a16d7dda2d23b91e9730fa4558cf728";
1468 md5 = "0a16d7dda2d23b91e9730fa4558cf728";
1326 };
1469 };
1327 meta = {
1470 meta = {
1328 license = [ pkgs.lib.licenses.mit ];
1471 license = [ pkgs.lib.licenses.mit ];
1329 };
1472 };
1330 };
1473 };
1331 pytest-runner = super.buildPythonPackage {
1474 pytest-runner = super.buildPythonPackage {
1332 name = "pytest-runner-2.9";
1475 name = "pytest-runner-2.9";
1333 buildInputs = with self; [];
1476 buildInputs = with self; [];
1334 doCheck = false;
1477 doCheck = false;
1335 propagatedBuildInputs = with self; [];
1478 propagatedBuildInputs = with self; [];
1336 src = fetchurl {
1479 src = fetchurl {
1337 url = "https://pypi.python.org/packages/11/d4/c335ddf94463e451109e3494e909765c3e5205787b772e3b25ee8601b86a/pytest-runner-2.9.tar.gz";
1480 url = "https://pypi.python.org/packages/11/d4/c335ddf94463e451109e3494e909765c3e5205787b772e3b25ee8601b86a/pytest-runner-2.9.tar.gz";
1338 md5 = "2212a2e34404b0960b2fdc2c469247b2";
1481 md5 = "2212a2e34404b0960b2fdc2c469247b2";
1339 };
1482 };
1340 meta = {
1483 meta = {
1341 license = [ pkgs.lib.licenses.mit ];
1484 license = [ pkgs.lib.licenses.mit ];
1342 };
1485 };
1343 };
1486 };
1344 pytest-sugar = super.buildPythonPackage {
1487 pytest-sugar = super.buildPythonPackage {
1345 name = "pytest-sugar-0.7.1";
1488 name = "pytest-sugar-0.7.1";
1346 buildInputs = with self; [];
1489 buildInputs = with self; [];
1347 doCheck = false;
1490 doCheck = false;
1348 propagatedBuildInputs = with self; [pytest termcolor];
1491 propagatedBuildInputs = with self; [pytest termcolor];
1349 src = fetchurl {
1492 src = fetchurl {
1350 url = "https://pypi.python.org/packages/03/97/05d988b4fa870e7373e8ee4582408543b9ca2bd35c3c67b569369c6f9c49/pytest-sugar-0.7.1.tar.gz";
1493 url = "https://pypi.python.org/packages/03/97/05d988b4fa870e7373e8ee4582408543b9ca2bd35c3c67b569369c6f9c49/pytest-sugar-0.7.1.tar.gz";
1351 md5 = "7400f7c11f3d572b2c2a3b60352d35fe";
1494 md5 = "7400f7c11f3d572b2c2a3b60352d35fe";
1352 };
1495 };
1353 meta = {
1496 meta = {
1354 license = [ pkgs.lib.licenses.bsdOriginal ];
1497 license = [ pkgs.lib.licenses.bsdOriginal ];
1355 };
1498 };
1356 };
1499 };
1357 pytest-timeout = super.buildPythonPackage {
1500 pytest-timeout = super.buildPythonPackage {
1358 name = "pytest-timeout-1.2.0";
1501 name = "pytest-timeout-1.2.0";
1359 buildInputs = with self; [];
1502 buildInputs = with self; [];
1360 doCheck = false;
1503 doCheck = false;
1361 propagatedBuildInputs = with self; [pytest];
1504 propagatedBuildInputs = with self; [pytest];
1362 src = fetchurl {
1505 src = fetchurl {
1363 url = "https://pypi.python.org/packages/cc/b7/b2a61365ea6b6d2e8881360ae7ed8dad0327ad2df89f2f0be4a02304deb2/pytest-timeout-1.2.0.tar.gz";
1506 url = "https://pypi.python.org/packages/cc/b7/b2a61365ea6b6d2e8881360ae7ed8dad0327ad2df89f2f0be4a02304deb2/pytest-timeout-1.2.0.tar.gz";
1364 md5 = "83607d91aa163562c7ee835da57d061d";
1507 md5 = "83607d91aa163562c7ee835da57d061d";
1365 };
1508 };
1366 meta = {
1509 meta = {
1367 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1510 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1368 };
1511 };
1369 };
1512 };
1370 python-dateutil = super.buildPythonPackage {
1513 python-dateutil = super.buildPythonPackage {
1371 name = "python-dateutil-1.5";
1514 name = "python-dateutil-2.1";
1372 buildInputs = with self; [];
1515 buildInputs = with self; [];
1373 doCheck = false;
1516 doCheck = false;
1374 propagatedBuildInputs = with self; [];
1517 propagatedBuildInputs = with self; [six];
1375 src = fetchurl {
1518 src = fetchurl {
1376 url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz";
1519 url = "https://pypi.python.org/packages/65/52/9c18dac21f174ad31b65e22d24297864a954e6fe65876eba3f5773d2da43/python-dateutil-2.1.tar.gz";
1377 md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5";
1520 md5 = "1534bb15cf311f07afaa3aacba1c028b";
1378 };
1521 };
1379 meta = {
1522 meta = {
1380 license = [ pkgs.lib.licenses.psfl ];
1523 license = [ { fullName = "Simplified BSD"; } ];
1381 };
1524 };
1382 };
1525 };
1383 python-editor = super.buildPythonPackage {
1526 python-editor = super.buildPythonPackage {
1384 name = "python-editor-1.0.3";
1527 name = "python-editor-1.0.3";
1385 buildInputs = with self; [];
1528 buildInputs = with self; [];
1386 doCheck = false;
1529 doCheck = false;
1387 propagatedBuildInputs = with self; [];
1530 propagatedBuildInputs = with self; [];
1388 src = fetchurl {
1531 src = fetchurl {
1389 url = "https://pypi.python.org/packages/65/1e/adf6e000ea5dc909aa420352d6ba37f16434c8a3c2fa030445411a1ed545/python-editor-1.0.3.tar.gz";
1532 url = "https://pypi.python.org/packages/65/1e/adf6e000ea5dc909aa420352d6ba37f16434c8a3c2fa030445411a1ed545/python-editor-1.0.3.tar.gz";
1390 md5 = "0aca5f2ef176ce68e98a5b7e31372835";
1533 md5 = "0aca5f2ef176ce68e98a5b7e31372835";
1391 };
1534 };
1392 meta = {
1535 meta = {
1393 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1536 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1394 };
1537 };
1395 };
1538 };
1396 python-ldap = super.buildPythonPackage {
1539 python-ldap = super.buildPythonPackage {
1397 name = "python-ldap-2.4.19";
1540 name = "python-ldap-2.4.19";
1398 buildInputs = with self; [];
1541 buildInputs = with self; [];
1399 doCheck = false;
1542 doCheck = false;
1400 propagatedBuildInputs = with self; [setuptools];
1543 propagatedBuildInputs = with self; [setuptools];
1401 src = fetchurl {
1544 src = fetchurl {
1402 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
1545 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
1403 md5 = "b941bf31d09739492aa19ef679e94ae3";
1546 md5 = "b941bf31d09739492aa19ef679e94ae3";
1404 };
1547 };
1405 meta = {
1548 meta = {
1406 license = [ pkgs.lib.licenses.psfl ];
1549 license = [ pkgs.lib.licenses.psfl ];
1407 };
1550 };
1408 };
1551 };
1409 python-memcached = super.buildPythonPackage {
1552 python-memcached = super.buildPythonPackage {
1410 name = "python-memcached-1.57";
1553 name = "python-memcached-1.57";
1411 buildInputs = with self; [];
1554 buildInputs = with self; [];
1412 doCheck = false;
1555 doCheck = false;
1413 propagatedBuildInputs = with self; [six];
1556 propagatedBuildInputs = with self; [six];
1414 src = fetchurl {
1557 src = fetchurl {
1415 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
1558 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
1416 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
1559 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
1417 };
1560 };
1418 meta = {
1561 meta = {
1419 license = [ pkgs.lib.licenses.psfl ];
1562 license = [ pkgs.lib.licenses.psfl ];
1420 };
1563 };
1421 };
1564 };
1422 python-pam = super.buildPythonPackage {
1565 python-pam = super.buildPythonPackage {
1423 name = "python-pam-1.8.2";
1566 name = "python-pam-1.8.2";
1424 buildInputs = with self; [];
1567 buildInputs = with self; [];
1425 doCheck = false;
1568 doCheck = false;
1426 propagatedBuildInputs = with self; [];
1569 propagatedBuildInputs = with self; [];
1427 src = fetchurl {
1570 src = fetchurl {
1428 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
1571 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
1429 md5 = "db71b6b999246fb05d78ecfbe166629d";
1572 md5 = "db71b6b999246fb05d78ecfbe166629d";
1430 };
1573 };
1431 meta = {
1574 meta = {
1432 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1575 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1433 };
1576 };
1434 };
1577 };
1435 pytz = super.buildPythonPackage {
1578 pytz = super.buildPythonPackage {
1436 name = "pytz-2015.4";
1579 name = "pytz-2015.4";
1437 buildInputs = with self; [];
1580 buildInputs = with self; [];
1438 doCheck = false;
1581 doCheck = false;
1439 propagatedBuildInputs = with self; [];
1582 propagatedBuildInputs = with self; [];
1440 src = fetchurl {
1583 src = fetchurl {
1441 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
1584 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
1442 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1585 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1443 };
1586 };
1444 meta = {
1587 meta = {
1445 license = [ pkgs.lib.licenses.mit ];
1588 license = [ pkgs.lib.licenses.mit ];
1446 };
1589 };
1447 };
1590 };
1448 pyzmq = super.buildPythonPackage {
1591 pyzmq = super.buildPythonPackage {
1449 name = "pyzmq-14.6.0";
1592 name = "pyzmq-14.6.0";
1450 buildInputs = with self; [];
1593 buildInputs = with self; [];
1451 doCheck = false;
1594 doCheck = false;
1452 propagatedBuildInputs = with self; [];
1595 propagatedBuildInputs = with self; [];
1453 src = fetchurl {
1596 src = fetchurl {
1454 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1597 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1455 md5 = "395b5de95a931afa5b14c9349a5b8024";
1598 md5 = "395b5de95a931afa5b14c9349a5b8024";
1456 };
1599 };
1457 meta = {
1600 meta = {
1458 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1601 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1459 };
1602 };
1460 };
1603 };
1461 recaptcha-client = super.buildPythonPackage {
1604 recaptcha-client = super.buildPythonPackage {
1462 name = "recaptcha-client-1.0.6";
1605 name = "recaptcha-client-1.0.6";
1463 buildInputs = with self; [];
1606 buildInputs = with self; [];
1464 doCheck = false;
1607 doCheck = false;
1465 propagatedBuildInputs = with self; [];
1608 propagatedBuildInputs = with self; [];
1466 src = fetchurl {
1609 src = fetchurl {
1467 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1610 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1468 md5 = "74228180f7e1fb76c4d7089160b0d919";
1611 md5 = "74228180f7e1fb76c4d7089160b0d919";
1469 };
1612 };
1470 meta = {
1613 meta = {
1471 license = [ { fullName = "MIT/X11"; } ];
1614 license = [ { fullName = "MIT/X11"; } ];
1472 };
1615 };
1473 };
1616 };
1474 repoze.lru = super.buildPythonPackage {
1617 repoze.lru = super.buildPythonPackage {
1475 name = "repoze.lru-0.6";
1618 name = "repoze.lru-0.6";
1476 buildInputs = with self; [];
1619 buildInputs = with self; [];
1477 doCheck = false;
1620 doCheck = false;
1478 propagatedBuildInputs = with self; [];
1621 propagatedBuildInputs = with self; [];
1479 src = fetchurl {
1622 src = fetchurl {
1480 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1623 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1481 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1624 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1482 };
1625 };
1483 meta = {
1626 meta = {
1484 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1627 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1485 };
1628 };
1486 };
1629 };
1487 requests = super.buildPythonPackage {
1630 requests = super.buildPythonPackage {
1488 name = "requests-2.9.1";
1631 name = "requests-2.9.1";
1489 buildInputs = with self; [];
1632 buildInputs = with self; [];
1490 doCheck = false;
1633 doCheck = false;
1491 propagatedBuildInputs = with self; [];
1634 propagatedBuildInputs = with self; [];
1492 src = fetchurl {
1635 src = fetchurl {
1493 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1636 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1494 md5 = "0b7f480d19012ec52bab78292efd976d";
1637 md5 = "0b7f480d19012ec52bab78292efd976d";
1495 };
1638 };
1496 meta = {
1639 meta = {
1497 license = [ pkgs.lib.licenses.asl20 ];
1640 license = [ pkgs.lib.licenses.asl20 ];
1498 };
1641 };
1499 };
1642 };
1500 rhodecode-enterprise-ce = super.buildPythonPackage {
1643 rhodecode-enterprise-ce = super.buildPythonPackage {
1501 name = "rhodecode-enterprise-ce-4.6.1";
1644 name = "rhodecode-enterprise-ce-4.7.0";
1502 buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage cssselect lxml configobj];
1645 buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj];
1503 doCheck = true;
1646 doCheck = true;
1504 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments pygments-markdown-lexer Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery channelstream colander decorator deform docutils gevent gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson subprocess32 waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
1647 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments pygments-markdown-lexer Pylons Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress cssselect celery channelstream colander decorator deform docutils gevent gunicorn infrae.cache ipython iso8601 kombu lxml msgpack-python nbconvert packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson subprocess32 waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
1505 src = ./.;
1648 src = ./.;
1506 meta = {
1649 meta = {
1507 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
1650 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
1508 };
1651 };
1509 };
1652 };
1510 rhodecode-tools = super.buildPythonPackage {
1653 rhodecode-tools = super.buildPythonPackage {
1511 name = "rhodecode-tools-0.11.0";
1654 name = "rhodecode-tools-0.11.0";
1512 buildInputs = with self; [];
1655 buildInputs = with self; [];
1513 doCheck = false;
1656 doCheck = false;
1514 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests elasticsearch elasticsearch-dsl urllib3 Whoosh];
1657 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests elasticsearch elasticsearch-dsl urllib3 Whoosh];
1515 src = fetchurl {
1658 src = fetchurl {
1516 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.11.0.tar.gz?md5=e5fd0a8363af08a0ced71b50ca9cce15";
1659 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.11.0.tar.gz?md5=e5fd0a8363af08a0ced71b50ca9cce15";
1517 md5 = "e5fd0a8363af08a0ced71b50ca9cce15";
1660 md5 = "e5fd0a8363af08a0ced71b50ca9cce15";
1518 };
1661 };
1519 meta = {
1662 meta = {
1520 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1663 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1521 };
1664 };
1522 };
1665 };
1523 serpent = super.buildPythonPackage {
1524 name = "serpent-1.15";
1525 buildInputs = with self; [];
1526 doCheck = false;
1527 propagatedBuildInputs = with self; [];
1528 src = fetchurl {
1529 url = "https://pypi.python.org/packages/7b/38/b2b27673a882ff2ea5871bb3e3e6b496ebbaafd1612e51990ffb158b9254/serpent-1.15.tar.gz";
1530 md5 = "e27b1aad5c218e16442f52abb7c7053a";
1531 };
1532 meta = {
1533 license = [ pkgs.lib.licenses.mit ];
1534 };
1535 };
1536 setproctitle = super.buildPythonPackage {
1666 setproctitle = super.buildPythonPackage {
1537 name = "setproctitle-1.1.8";
1667 name = "setproctitle-1.1.8";
1538 buildInputs = with self; [];
1668 buildInputs = with self; [];
1539 doCheck = false;
1669 doCheck = false;
1540 propagatedBuildInputs = with self; [];
1670 propagatedBuildInputs = with self; [];
1541 src = fetchurl {
1671 src = fetchurl {
1542 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1672 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1543 md5 = "728f4c8c6031bbe56083a48594027edd";
1673 md5 = "728f4c8c6031bbe56083a48594027edd";
1544 };
1674 };
1545 meta = {
1675 meta = {
1546 license = [ pkgs.lib.licenses.bsdOriginal ];
1676 license = [ pkgs.lib.licenses.bsdOriginal ];
1547 };
1677 };
1548 };
1678 };
1549 setuptools = super.buildPythonPackage {
1679 setuptools = super.buildPythonPackage {
1550 name = "setuptools-30.1.0";
1680 name = "setuptools-30.1.0";
1551 buildInputs = with self; [];
1681 buildInputs = with self; [];
1552 doCheck = false;
1682 doCheck = false;
1553 propagatedBuildInputs = with self; [];
1683 propagatedBuildInputs = with self; [];
1554 src = fetchurl {
1684 src = fetchurl {
1555 url = "https://pypi.python.org/packages/1e/43/002c8616db9a3e7be23c2556e39b90a32bb40ba0dc652de1999d5334d372/setuptools-30.1.0.tar.gz";
1685 url = "https://pypi.python.org/packages/1e/43/002c8616db9a3e7be23c2556e39b90a32bb40ba0dc652de1999d5334d372/setuptools-30.1.0.tar.gz";
1556 md5 = "cac497f42e5096ac8df29e38d3f81c3e";
1686 md5 = "cac497f42e5096ac8df29e38d3f81c3e";
1557 };
1687 };
1558 meta = {
1688 meta = {
1559 license = [ pkgs.lib.licenses.mit ];
1689 license = [ pkgs.lib.licenses.mit ];
1560 };
1690 };
1561 };
1691 };
1562 setuptools-scm = super.buildPythonPackage {
1692 setuptools-scm = super.buildPythonPackage {
1563 name = "setuptools-scm-1.15.0";
1693 name = "setuptools-scm-1.15.0";
1564 buildInputs = with self; [];
1694 buildInputs = with self; [];
1565 doCheck = false;
1695 doCheck = false;
1566 propagatedBuildInputs = with self; [];
1696 propagatedBuildInputs = with self; [];
1567 src = fetchurl {
1697 src = fetchurl {
1568 url = "https://pypi.python.org/packages/80/b7/31b6ae5fcb188e37f7e31abe75f9be90490a5456a72860fa6e643f8a3cbc/setuptools_scm-1.15.0.tar.gz";
1698 url = "https://pypi.python.org/packages/80/b7/31b6ae5fcb188e37f7e31abe75f9be90490a5456a72860fa6e643f8a3cbc/setuptools_scm-1.15.0.tar.gz";
1569 md5 = "b6916c78ed6253d6602444fad4279c5b";
1699 md5 = "b6916c78ed6253d6602444fad4279c5b";
1570 };
1700 };
1571 meta = {
1701 meta = {
1572 license = [ pkgs.lib.licenses.mit ];
1702 license = [ pkgs.lib.licenses.mit ];
1573 };
1703 };
1574 };
1704 };
1575 simplegeneric = super.buildPythonPackage {
1705 simplegeneric = super.buildPythonPackage {
1576 name = "simplegeneric-0.8.1";
1706 name = "simplegeneric-0.8.1";
1577 buildInputs = with self; [];
1707 buildInputs = with self; [];
1578 doCheck = false;
1708 doCheck = false;
1579 propagatedBuildInputs = with self; [];
1709 propagatedBuildInputs = with self; [];
1580 src = fetchurl {
1710 src = fetchurl {
1581 url = "https://pypi.python.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
1711 url = "https://pypi.python.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
1582 md5 = "f9c1fab00fd981be588fc32759f474e3";
1712 md5 = "f9c1fab00fd981be588fc32759f474e3";
1583 };
1713 };
1584 meta = {
1714 meta = {
1585 license = [ pkgs.lib.licenses.zpt21 ];
1715 license = [ pkgs.lib.licenses.zpt21 ];
1586 };
1716 };
1587 };
1717 };
1588 simplejson = super.buildPythonPackage {
1718 simplejson = super.buildPythonPackage {
1589 name = "simplejson-3.7.2";
1719 name = "simplejson-3.7.2";
1590 buildInputs = with self; [];
1720 buildInputs = with self; [];
1591 doCheck = false;
1721 doCheck = false;
1592 propagatedBuildInputs = with self; [];
1722 propagatedBuildInputs = with self; [];
1593 src = fetchurl {
1723 src = fetchurl {
1594 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1724 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1595 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1725 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1596 };
1726 };
1597 meta = {
1727 meta = {
1598 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
1728 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
1599 };
1729 };
1600 };
1730 };
1601 six = super.buildPythonPackage {
1731 six = super.buildPythonPackage {
1602 name = "six-1.9.0";
1732 name = "six-1.9.0";
1603 buildInputs = with self; [];
1733 buildInputs = with self; [];
1604 doCheck = false;
1734 doCheck = false;
1605 propagatedBuildInputs = with self; [];
1735 propagatedBuildInputs = with self; [];
1606 src = fetchurl {
1736 src = fetchurl {
1607 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1737 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1608 md5 = "476881ef4012262dfc8adc645ee786c4";
1738 md5 = "476881ef4012262dfc8adc645ee786c4";
1609 };
1739 };
1610 meta = {
1740 meta = {
1611 license = [ pkgs.lib.licenses.mit ];
1741 license = [ pkgs.lib.licenses.mit ];
1612 };
1742 };
1613 };
1743 };
1614 subprocess32 = super.buildPythonPackage {
1744 subprocess32 = super.buildPythonPackage {
1615 name = "subprocess32-3.2.6";
1745 name = "subprocess32-3.2.6";
1616 buildInputs = with self; [];
1746 buildInputs = with self; [];
1617 doCheck = false;
1747 doCheck = false;
1618 propagatedBuildInputs = with self; [];
1748 propagatedBuildInputs = with self; [];
1619 src = fetchurl {
1749 src = fetchurl {
1620 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1750 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1621 md5 = "754c5ab9f533e764f931136974b618f1";
1751 md5 = "754c5ab9f533e764f931136974b618f1";
1622 };
1752 };
1623 meta = {
1753 meta = {
1624 license = [ pkgs.lib.licenses.psfl ];
1754 license = [ pkgs.lib.licenses.psfl ];
1625 };
1755 };
1626 };
1756 };
1627 supervisor = super.buildPythonPackage {
1757 supervisor = super.buildPythonPackage {
1628 name = "supervisor-3.3.1";
1758 name = "supervisor-3.3.1";
1629 buildInputs = with self; [];
1759 buildInputs = with self; [];
1630 doCheck = false;
1760 doCheck = false;
1631 propagatedBuildInputs = with self; [meld3];
1761 propagatedBuildInputs = with self; [meld3];
1632 src = fetchurl {
1762 src = fetchurl {
1633 url = "https://pypi.python.org/packages/80/37/964c0d53cbd328796b1aeb7abea4c0f7b0e8c7197ea9b0b9967b7d004def/supervisor-3.3.1.tar.gz";
1763 url = "https://pypi.python.org/packages/80/37/964c0d53cbd328796b1aeb7abea4c0f7b0e8c7197ea9b0b9967b7d004def/supervisor-3.3.1.tar.gz";
1634 md5 = "202f760f9bf4930ec06557bac73e5cf2";
1764 md5 = "202f760f9bf4930ec06557bac73e5cf2";
1635 };
1765 };
1636 meta = {
1766 meta = {
1637 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1767 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1638 };
1768 };
1639 };
1769 };
1640 termcolor = super.buildPythonPackage {
1770 termcolor = super.buildPythonPackage {
1641 name = "termcolor-1.1.0";
1771 name = "termcolor-1.1.0";
1642 buildInputs = with self; [];
1772 buildInputs = with self; [];
1643 doCheck = false;
1773 doCheck = false;
1644 propagatedBuildInputs = with self; [];
1774 propagatedBuildInputs = with self; [];
1645 src = fetchurl {
1775 src = fetchurl {
1646 url = "https://pypi.python.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
1776 url = "https://pypi.python.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
1647 md5 = "043e89644f8909d462fbbfa511c768df";
1777 md5 = "043e89644f8909d462fbbfa511c768df";
1648 };
1778 };
1649 meta = {
1779 meta = {
1650 license = [ pkgs.lib.licenses.mit ];
1780 license = [ pkgs.lib.licenses.mit ];
1651 };
1781 };
1652 };
1782 };
1783 testpath = super.buildPythonPackage {
1784 name = "testpath-0.1";
1785 buildInputs = with self; [];
1786 doCheck = false;
1787 propagatedBuildInputs = with self; [];
1788 src = fetchurl {
1789 url = "https://pypi.python.org/packages/f9/c4/c0b22f35138bc26a6058c39cb61db1e8977e5e9550b12cd2cb02ef56fc51/testpath-0.1.tar.gz";
1790 md5 = "401918bcd0b0e5b71a9b909835117bc6";
1791 };
1792 meta = {
1793 license = [ pkgs.lib.licenses.mit ];
1794 };
1795 };
1653 traitlets = super.buildPythonPackage {
1796 traitlets = super.buildPythonPackage {
1654 name = "traitlets-4.3.1";
1797 name = "traitlets-4.3.2";
1655 buildInputs = with self; [];
1798 buildInputs = with self; [];
1656 doCheck = false;
1799 doCheck = false;
1657 propagatedBuildInputs = with self; [ipython-genutils six decorator enum34];
1800 propagatedBuildInputs = with self; [ipython-genutils six decorator enum34];
1658 src = fetchurl {
1801 src = fetchurl {
1659 url = "https://pypi.python.org/packages/b1/d6/5b5aa6d5c474691909b91493da1e8972e309c9f01ecfe4aeafd272eb3234/traitlets-4.3.1.tar.gz";
1802 url = "https://pypi.python.org/packages/a5/98/7f5ef2fe9e9e071813aaf9cb91d1a732e0a68b6c44a32b38cb8e14c3f069/traitlets-4.3.2.tar.gz";
1660 md5 = "dd0b1b6e5d31ce446d55a4b5e5083c98";
1803 md5 = "3068663f2f38fd939a9eb3a500ccc154";
1661 };
1804 };
1662 meta = {
1805 meta = {
1663 license = [ pkgs.lib.licenses.bsdOriginal ];
1806 license = [ pkgs.lib.licenses.bsdOriginal ];
1664 };
1807 };
1665 };
1808 };
1666 transifex-client = super.buildPythonPackage {
1809 transifex-client = super.buildPythonPackage {
1667 name = "transifex-client-0.10";
1810 name = "transifex-client-0.10";
1668 buildInputs = with self; [];
1811 buildInputs = with self; [];
1669 doCheck = false;
1812 doCheck = false;
1670 propagatedBuildInputs = with self; [];
1813 propagatedBuildInputs = with self; [];
1671 src = fetchurl {
1814 src = fetchurl {
1672 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1815 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1673 md5 = "5549538d84b8eede6b254cd81ae024fa";
1816 md5 = "5549538d84b8eede6b254cd81ae024fa";
1674 };
1817 };
1675 meta = {
1818 meta = {
1676 license = [ pkgs.lib.licenses.gpl2 ];
1819 license = [ pkgs.lib.licenses.gpl2 ];
1677 };
1820 };
1678 };
1821 };
1679 translationstring = super.buildPythonPackage {
1822 translationstring = super.buildPythonPackage {
1680 name = "translationstring-1.3";
1823 name = "translationstring-1.3";
1681 buildInputs = with self; [];
1824 buildInputs = with self; [];
1682 doCheck = false;
1825 doCheck = false;
1683 propagatedBuildInputs = with self; [];
1826 propagatedBuildInputs = with self; [];
1684 src = fetchurl {
1827 src = fetchurl {
1685 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1828 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1686 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1829 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1687 };
1830 };
1688 meta = {
1831 meta = {
1689 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
1832 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
1690 };
1833 };
1691 };
1834 };
1692 trollius = super.buildPythonPackage {
1835 trollius = super.buildPythonPackage {
1693 name = "trollius-1.0.4";
1836 name = "trollius-1.0.4";
1694 buildInputs = with self; [];
1837 buildInputs = with self; [];
1695 doCheck = false;
1838 doCheck = false;
1696 propagatedBuildInputs = with self; [futures];
1839 propagatedBuildInputs = with self; [futures];
1697 src = fetchurl {
1840 src = fetchurl {
1698 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1841 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1699 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1842 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1700 };
1843 };
1701 meta = {
1844 meta = {
1702 license = [ pkgs.lib.licenses.asl20 ];
1845 license = [ pkgs.lib.licenses.asl20 ];
1703 };
1846 };
1704 };
1847 };
1705 uWSGI = super.buildPythonPackage {
1848 uWSGI = super.buildPythonPackage {
1706 name = "uWSGI-2.0.11.2";
1849 name = "uWSGI-2.0.11.2";
1707 buildInputs = with self; [];
1850 buildInputs = with self; [];
1708 doCheck = false;
1851 doCheck = false;
1709 propagatedBuildInputs = with self; [];
1852 propagatedBuildInputs = with self; [];
1710 src = fetchurl {
1853 src = fetchurl {
1711 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1854 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1712 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1855 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1713 };
1856 };
1714 meta = {
1857 meta = {
1715 license = [ pkgs.lib.licenses.gpl2 ];
1858 license = [ pkgs.lib.licenses.gpl2 ];
1716 };
1859 };
1717 };
1860 };
1718 urllib3 = super.buildPythonPackage {
1861 urllib3 = super.buildPythonPackage {
1719 name = "urllib3-1.16";
1862 name = "urllib3-1.16";
1720 buildInputs = with self; [];
1863 buildInputs = with self; [];
1721 doCheck = false;
1864 doCheck = false;
1722 propagatedBuildInputs = with self; [];
1865 propagatedBuildInputs = with self; [];
1723 src = fetchurl {
1866 src = fetchurl {
1724 url = "https://pypi.python.org/packages/3b/f0/e763169124e3f5db0926bc3dbfcd580a105f9ca44cf5d8e6c7a803c9f6b5/urllib3-1.16.tar.gz";
1867 url = "https://pypi.python.org/packages/3b/f0/e763169124e3f5db0926bc3dbfcd580a105f9ca44cf5d8e6c7a803c9f6b5/urllib3-1.16.tar.gz";
1725 md5 = "fcaab1c5385c57deeb7053d3d7d81d59";
1868 md5 = "fcaab1c5385c57deeb7053d3d7d81d59";
1726 };
1869 };
1727 meta = {
1870 meta = {
1728 license = [ pkgs.lib.licenses.mit ];
1871 license = [ pkgs.lib.licenses.mit ];
1729 };
1872 };
1730 };
1873 };
1731 venusian = super.buildPythonPackage {
1874 venusian = super.buildPythonPackage {
1732 name = "venusian-1.0";
1875 name = "venusian-1.0";
1733 buildInputs = with self; [];
1876 buildInputs = with self; [];
1734 doCheck = false;
1877 doCheck = false;
1735 propagatedBuildInputs = with self; [];
1878 propagatedBuildInputs = with self; [];
1736 src = fetchurl {
1879 src = fetchurl {
1737 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1880 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1738 md5 = "dccf2eafb7113759d60c86faf5538756";
1881 md5 = "dccf2eafb7113759d60c86faf5538756";
1739 };
1882 };
1740 meta = {
1883 meta = {
1741 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1884 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1742 };
1885 };
1743 };
1886 };
1744 waitress = super.buildPythonPackage {
1887 waitress = super.buildPythonPackage {
1745 name = "waitress-1.0.1";
1888 name = "waitress-1.0.1";
1746 buildInputs = with self; [];
1889 buildInputs = with self; [];
1747 doCheck = false;
1890 doCheck = false;
1748 propagatedBuildInputs = with self; [];
1891 propagatedBuildInputs = with self; [];
1749 src = fetchurl {
1892 src = fetchurl {
1750 url = "https://pypi.python.org/packages/78/7d/84d11b96c3f60164dec3bef4a859a03aeae0231aa93f57fbe0d05fa4ff36/waitress-1.0.1.tar.gz";
1893 url = "https://pypi.python.org/packages/78/7d/84d11b96c3f60164dec3bef4a859a03aeae0231aa93f57fbe0d05fa4ff36/waitress-1.0.1.tar.gz";
1751 md5 = "dda92358a7569669086155923a46e57c";
1894 md5 = "dda92358a7569669086155923a46e57c";
1752 };
1895 };
1753 meta = {
1896 meta = {
1754 license = [ pkgs.lib.licenses.zpt21 ];
1897 license = [ pkgs.lib.licenses.zpt21 ];
1755 };
1898 };
1756 };
1899 };
1757 wcwidth = super.buildPythonPackage {
1900 wcwidth = super.buildPythonPackage {
1758 name = "wcwidth-0.1.7";
1901 name = "wcwidth-0.1.7";
1759 buildInputs = with self; [];
1902 buildInputs = with self; [];
1760 doCheck = false;
1903 doCheck = false;
1761 propagatedBuildInputs = with self; [];
1904 propagatedBuildInputs = with self; [];
1762 src = fetchurl {
1905 src = fetchurl {
1763 url = "https://pypi.python.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
1906 url = "https://pypi.python.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
1764 md5 = "b3b6a0a08f0c8a34d1de8cf44150a4ad";
1907 md5 = "b3b6a0a08f0c8a34d1de8cf44150a4ad";
1765 };
1908 };
1766 meta = {
1909 meta = {
1767 license = [ pkgs.lib.licenses.mit ];
1910 license = [ pkgs.lib.licenses.mit ];
1768 };
1911 };
1769 };
1912 };
1770 ws4py = super.buildPythonPackage {
1913 ws4py = super.buildPythonPackage {
1771 name = "ws4py-0.3.5";
1914 name = "ws4py-0.3.5";
1772 buildInputs = with self; [];
1915 buildInputs = with self; [];
1773 doCheck = false;
1916 doCheck = false;
1774 propagatedBuildInputs = with self; [];
1917 propagatedBuildInputs = with self; [];
1775 src = fetchurl {
1918 src = fetchurl {
1776 url = "https://pypi.python.org/packages/b6/4f/34af703be86939629479e74d6e650e39f3bd73b3b09212c34e5125764cbc/ws4py-0.3.5.zip";
1919 url = "https://pypi.python.org/packages/b6/4f/34af703be86939629479e74d6e650e39f3bd73b3b09212c34e5125764cbc/ws4py-0.3.5.zip";
1777 md5 = "a261b75c20b980e55ce7451a3576a867";
1920 md5 = "a261b75c20b980e55ce7451a3576a867";
1778 };
1921 };
1779 meta = {
1922 meta = {
1780 license = [ pkgs.lib.licenses.bsdOriginal ];
1923 license = [ pkgs.lib.licenses.bsdOriginal ];
1781 };
1924 };
1782 };
1925 };
1783 wsgiref = super.buildPythonPackage {
1926 wsgiref = super.buildPythonPackage {
1784 name = "wsgiref-0.1.2";
1927 name = "wsgiref-0.1.2";
1785 buildInputs = with self; [];
1928 buildInputs = with self; [];
1786 doCheck = false;
1929 doCheck = false;
1787 propagatedBuildInputs = with self; [];
1930 propagatedBuildInputs = with self; [];
1788 src = fetchurl {
1931 src = fetchurl {
1789 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1932 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1790 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1933 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1791 };
1934 };
1792 meta = {
1935 meta = {
1793 license = [ { fullName = "PSF or ZPL"; } ];
1936 license = [ { fullName = "PSF or ZPL"; } ];
1794 };
1937 };
1795 };
1938 };
1796 zope.cachedescriptors = super.buildPythonPackage {
1939 zope.cachedescriptors = super.buildPythonPackage {
1797 name = "zope.cachedescriptors-4.0.0";
1940 name = "zope.cachedescriptors-4.0.0";
1798 buildInputs = with self; [];
1941 buildInputs = with self; [];
1799 doCheck = false;
1942 doCheck = false;
1800 propagatedBuildInputs = with self; [setuptools];
1943 propagatedBuildInputs = with self; [setuptools];
1801 src = fetchurl {
1944 src = fetchurl {
1802 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1945 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1803 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1946 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1804 };
1947 };
1805 meta = {
1948 meta = {
1806 license = [ pkgs.lib.licenses.zpt21 ];
1949 license = [ pkgs.lib.licenses.zpt21 ];
1807 };
1950 };
1808 };
1951 };
1809 zope.deprecation = super.buildPythonPackage {
1952 zope.deprecation = super.buildPythonPackage {
1810 name = "zope.deprecation-4.1.2";
1953 name = "zope.deprecation-4.1.2";
1811 buildInputs = with self; [];
1954 buildInputs = with self; [];
1812 doCheck = false;
1955 doCheck = false;
1813 propagatedBuildInputs = with self; [setuptools];
1956 propagatedBuildInputs = with self; [setuptools];
1814 src = fetchurl {
1957 src = fetchurl {
1815 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1958 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1816 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1959 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1817 };
1960 };
1818 meta = {
1961 meta = {
1819 license = [ pkgs.lib.licenses.zpt21 ];
1962 license = [ pkgs.lib.licenses.zpt21 ];
1820 };
1963 };
1821 };
1964 };
1822 zope.event = super.buildPythonPackage {
1965 zope.event = super.buildPythonPackage {
1823 name = "zope.event-4.0.3";
1966 name = "zope.event-4.0.3";
1824 buildInputs = with self; [];
1967 buildInputs = with self; [];
1825 doCheck = false;
1968 doCheck = false;
1826 propagatedBuildInputs = with self; [setuptools];
1969 propagatedBuildInputs = with self; [setuptools];
1827 src = fetchurl {
1970 src = fetchurl {
1828 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1971 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1829 md5 = "9a3780916332b18b8b85f522bcc3e249";
1972 md5 = "9a3780916332b18b8b85f522bcc3e249";
1830 };
1973 };
1831 meta = {
1974 meta = {
1832 license = [ pkgs.lib.licenses.zpt21 ];
1975 license = [ pkgs.lib.licenses.zpt21 ];
1833 };
1976 };
1834 };
1977 };
1835 zope.interface = super.buildPythonPackage {
1978 zope.interface = super.buildPythonPackage {
1836 name = "zope.interface-4.1.3";
1979 name = "zope.interface-4.1.3";
1837 buildInputs = with self; [];
1980 buildInputs = with self; [];
1838 doCheck = false;
1981 doCheck = false;
1839 propagatedBuildInputs = with self; [setuptools];
1982 propagatedBuildInputs = with self; [setuptools];
1840 src = fetchurl {
1983 src = fetchurl {
1841 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1984 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1842 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1985 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1843 };
1986 };
1844 meta = {
1987 meta = {
1845 license = [ pkgs.lib.licenses.zpt21 ];
1988 license = [ pkgs.lib.licenses.zpt21 ];
1846 };
1989 };
1847 };
1990 };
1848
1991
1849 ### Test requirements
1992 ### Test requirements
1850
1993
1851
1994
1852 }
1995 }
@@ -1,12 +1,11 b''
1 [pytest]
1 [pytest]
2 testpaths = ./rhodecode
2 testpaths = ./rhodecode
3 pylons_config = rhodecode/tests/rhodecode.ini
3 pylons_config = rhodecode/tests/rhodecode.ini
4 vcsserver_protocol = http
4 vcsserver_protocol = http
5 vcsserver_config_pyro4 = rhodecode/tests/vcsserver_pyro4.ini
6 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
5 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
7 norecursedirs = tests/scripts
6 norecursedirs = tests/scripts
8 addopts = -k "not _BaseTest"
7 addopts = -k "not _BaseTest"
9 markers =
8 markers =
10 vcs_operations: Mark tests depending on a running RhodeCode instance.
9 vcs_operations: Mark tests depending on a running RhodeCode instance.
11 xfail_backends: Mark tests as xfail for given backends.
10 xfail_backends: Mark tests as xfail for given backends.
12 skip_backends: Mark tests as skipped for given backends.
11 skip_backends: Mark tests as skipped for given backends.
@@ -1,131 +1,136 b''
1 ## core
1 ## core
2 setuptools==30.1.0
2 setuptools==30.1.0
3 setuptools-scm==1.15.0
3 setuptools-scm==1.15.0
4
4
5 amqplib==1.0.2
5 amqplib==1.0.2
6 anyjson==0.3.3
6 anyjson==0.3.3
7 authomatic==0.1.0.post1
7 authomatic==0.1.0.post1
8 Babel==1.3
8 Babel==1.3
9 backport-ipaddress==0.1
9 backport-ipaddress==0.1
10 Beaker==1.7.0
10 Beaker==1.7.0
11 celery==2.2.10
11 celery==2.2.10
12 Chameleon==2.24
12 Chameleon==2.24
13 channelstream==0.5.2
13 channelstream==0.5.2
14 click==5.1
14 click==5.1
15 colander==1.2
15 colander==1.2
16 configobj==5.0.6
16 configobj==5.0.6
17 decorator==3.4.2
17 cssselect==1.0.1
18 decorator==4.0.11
18 deform==2.0a2
19 deform==2.0a2
19 docutils==0.12
20 docutils==0.12
20 dogpile.cache==0.6.1
21 dogpile.cache==0.6.1
21 dogpile.core==0.4.1
22 dogpile.core==0.4.1
22 ecdsa==0.11
23 ecdsa==0.11
23 FormEncode==1.2.4
24 FormEncode==1.2.4
24 future==0.14.3
25 future==0.14.3
25 futures==3.0.2
26 futures==3.0.2
26 gnureadline==6.3.3
27 gnureadline==6.3.3
27 infrae.cache==1.0.1
28 infrae.cache==1.0.1
28 iso8601==0.1.11
29 iso8601==0.1.11
29 itsdangerous==0.24
30 itsdangerous==0.24
30 Jinja2==2.7.3
31 Jinja2==2.7.3
31 kombu==1.5.1
32 kombu==1.5.1
33 lxml==3.7.3
32 Mako==1.0.6
34 Mako==1.0.6
33 Markdown==2.6.7
35 Markdown==2.6.7
34 MarkupSafe==0.23
36 MarkupSafe==0.23
35 meld3==1.0.2
37 meld3==1.0.2
36 msgpack-python==0.4.8
38 msgpack-python==0.4.8
37 MySQL-python==1.2.5
39 MySQL-python==1.2.5
38 nose==1.3.6
40 nose==1.3.6
39 objgraph==2.0.0
41 objgraph==2.0.0
40 packaging==15.2
42 packaging==15.2
41 paramiko==1.15.1
43 paramiko==1.15.1
42 Paste==2.0.3
44 Paste==2.0.3
43 PasteDeploy==1.5.2
45 PasteDeploy==1.5.2
44 PasteScript==1.7.5
46 PasteScript==1.7.5
47 pathlib2==2.1.0
45 psutil==4.3.1
48 psutil==4.3.1
46 psycopg2==2.6.1
49 psycopg2==2.6.1
47 py-bcrypt==0.4
50 py-bcrypt==0.4
48 pycrypto==2.6.1
51 pycrypto==2.6.1
49 pycurl==7.19.5
52 pycurl==7.19.5
50 pyflakes==0.8.1
53 pyflakes==0.8.1
51 pygments-markdown-lexer==0.1.0.dev39
54 pygments-markdown-lexer==0.1.0.dev39
52 Pygments==2.2.0
55 Pygments==2.2.0
53 pyparsing==1.5.7
56 pyparsing==1.5.7
54 pyramid-beaker==0.8
57 pyramid-beaker==0.8
55 pyramid-debugtoolbar==2.4.2
58 pyramid-debugtoolbar==3.0.5
56 pyramid-jinja2==2.5
59 pyramid-jinja2==2.5
57 pyramid-mako==1.0.2
60 pyramid-mako==1.0.2
58 pyramid==1.6.1
61 pyramid==1.7.4
59 pysqlite==2.6.3
62 pysqlite==2.6.3
60 python-dateutil==1.5
63 python-dateutil==2.1
61 python-ldap==2.4.19
64 python-ldap==2.4.19
62 python-memcached==1.57
65 python-memcached==1.57
63 python-pam==1.8.2
66 python-pam==1.8.2
64 pytz==2015.4
67 pytz==2015.4
65 pyzmq==14.6.0
68 pyzmq==14.6.0
66 recaptcha-client==1.0.6
69 recaptcha-client==1.0.6
67 repoze.lru==0.6
70 repoze.lru==0.6
68 requests==2.9.1
71 requests==2.9.1
69 Routes==1.13
72 Routes==1.13
70 setproctitle==1.1.8
73 setproctitle==1.1.8
71 simplejson==3.7.2
74 simplejson==3.7.2
72 six==1.9.0
75 six==1.9.0
73 Sphinx==1.2.2
76 Sphinx==1.2.2
74 SQLAlchemy==0.9.9
77 SQLAlchemy==0.9.9
75 subprocess32==3.2.6
78 subprocess32==3.2.6
76 supervisor==3.3.1
79 supervisor==3.3.1
77 Tempita==0.5.2
80 Tempita==0.5.2
78 translationstring==1.3
81 translationstring==1.3
79 trollius==1.0.4
82 trollius==1.0.4
80 urllib3==1.16
83 urllib3==1.16
81 URLObject==2.4.0
84 URLObject==2.4.0
82 venusian==1.0
85 venusian==1.0
83 WebError==0.10.3
86 WebError==0.10.3
84 WebHelpers2==2.0
87 WebHelpers2==2.0
85 WebHelpers==1.3
88 WebHelpers==1.3
86 WebOb==1.3.1
89 WebOb==1.3.1
87 Whoosh==2.7.4
90 Whoosh==2.7.4
88 wsgiref==0.1.2
91 wsgiref==0.1.2
89 zope.cachedescriptors==4.0.0
92 zope.cachedescriptors==4.0.0
90 zope.deprecation==4.1.2
93 zope.deprecation==4.1.2
91 zope.event==4.0.3
94 zope.event==4.0.3
92 zope.interface==4.1.3
95 zope.interface==4.1.3
93
96
94 ## customized/patched libs
97 ## customized/patched libs
95 # our patched version of Pylons==1.0.2
98 # our patched version of Pylons==1.0.2
96 https://code.rhodecode.com/upstream/pylons/archive/707354ee4261b9c10450404fc9852ccea4fd667d.tar.gz?md5=f26633726fa2cd3a340316ee6a5d218f#egg=Pylons==1.0.2.rhodecode-patch-1
99 https://code.rhodecode.com/upstream/pylons/archive/707354ee4261b9c10450404fc9852ccea4fd667d.tar.gz?md5=f26633726fa2cd3a340316ee6a5d218f#egg=Pylons==1.0.2.rhodecode-patch-1
97 # not released py-gfm==0.1.3
100 # not released py-gfm==0.1.3
98 https://code.rhodecode.com/upstream/py-gfm/archive/0d66a19bc16e3d49de273c0f797d4e4781e8c0f2.tar.gz?md5=0d0d5385bfb629eea636a80b9c2bfd16#egg=py-gfm==0.1.3.rhodecode-upstream1
101 https://code.rhodecode.com/upstream/py-gfm/archive/0d66a19bc16e3d49de273c0f797d4e4781e8c0f2.tar.gz?md5=0d0d5385bfb629eea636a80b9c2bfd16#egg=py-gfm==0.1.3.rhodecode-upstream1
99
102
103 # IPYTHON RENDERING
104 # entrypoints backport, pypi version doesn't support egg installs
105 https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313#egg=entrypoints==0.2.2.rhodecode-upstream1
106 nbconvert==5.1.1
107 nbformat==4.3.0
108 jupyter_client==5.0.0
100
109
101 ## cli tools
110 ## cli tools
102 alembic==0.8.4
111 alembic==0.8.4
103 invoke==0.13.0
112 invoke==0.13.0
104 bumpversion==0.5.3
113 bumpversion==0.5.3
105 transifex-client==0.10
114 transifex-client==0.10
106
115
107 ## http servers
116 ## http servers
108 gevent==1.1.2
117 gevent==1.1.2
109 greenlet==0.4.10
118 greenlet==0.4.10
110 gunicorn==19.6.0
119 gunicorn==19.6.0
111 waitress==1.0.1
120 waitress==1.0.1
112 uWSGI==2.0.11.2
121 uWSGI==2.0.11.2
113
122
114 ## debug
123 ## debug
115 ipdb==0.10.1
124 ipdb==0.10.1
116 ipython==5.1.0
125 ipython==5.1.0
117 CProfileV==1.0.6
126 CProfileV==1.0.6
118 bottle==0.12.8
127 bottle==0.12.8
119
128
120 ## rhodecode-tools, special case
129 ## rhodecode-tools, special case
121 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.11.0.tar.gz?md5=e5fd0a8363af08a0ced71b50ca9cce15#egg=rhodecode-tools==0.11.0
130 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.11.0.tar.gz?md5=e5fd0a8363af08a0ced71b50ca9cce15#egg=rhodecode-tools==0.11.0
122
131
123 ## appenlight
132 ## appenlight
124 appenlight-client==0.6.14
133 appenlight-client==0.6.14
125
134
126 # Pyro/Deprecated TODO(Marcink): remove in 4.7 release.
127 Pyro4==4.41
128 serpent==1.15
129
130 ## test related requirements
135 ## test related requirements
131 -r requirements_test.txt
136 -r requirements_test.txt
@@ -1,17 +1,15 b''
1 # test related requirements
1 # test related requirements
2 pytest==3.0.5
2 pytest==3.0.5
3 py==1.4.31
3 py==1.4.31
4 pytest-cov==2.4.0
4 pytest-cov==2.4.0
5 pytest-sugar==0.7.1
5 pytest-sugar==0.7.1
6 pytest-runner==2.9.0
6 pytest-runner==2.9.0
7 pytest-catchlog==1.2.2
7 pytest-catchlog==1.2.2
8 pytest-profiling==1.2.2
8 pytest-profiling==1.2.2
9 gprof2dot==2016.10.13
9 gprof2dot==2016.10.13
10 pytest-timeout==1.2.0
10 pytest-timeout==1.2.0
11
11
12 mock==1.0.1
12 mock==1.0.1
13 WebTest==1.4.3
13 WebTest==1.4.3
14 cov-core==1.15.0
14 cov-core==1.15.0
15 coverage==3.7.1
15 coverage==3.7.1
16 cssselect==0.9.1
17 lxml==3.4.4
@@ -1,1 +1,1 b''
1 4.6.1 No newline at end of file
1 4.7.0 No newline at end of file
@@ -1,63 +1,63 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22
22
23 RhodeCode, a web based repository management software
23 RhodeCode, a web based repository management software
24 versioning implementation: http://www.python.org/dev/peps/pep-0386/
24 versioning implementation: http://www.python.org/dev/peps/pep-0386/
25 """
25 """
26
26
27 import os
27 import os
28 import sys
28 import sys
29 import platform
29 import platform
30
30
31 VERSION = tuple(open(os.path.join(
31 VERSION = tuple(open(os.path.join(
32 os.path.dirname(__file__), 'VERSION')).read().split('.'))
32 os.path.dirname(__file__), 'VERSION')).read().split('.'))
33
33
34 BACKENDS = {
34 BACKENDS = {
35 'hg': 'Mercurial repository',
35 'hg': 'Mercurial repository',
36 'git': 'Git repository',
36 'git': 'Git repository',
37 'svn': 'Subversion repository',
37 'svn': 'Subversion repository',
38 }
38 }
39
39
40 CELERY_ENABLED = False
40 CELERY_ENABLED = False
41 CELERY_EAGER = False
41 CELERY_EAGER = False
42
42
43 # link to config for pylons
43 # link to config for pylons
44 CONFIG = {}
44 CONFIG = {}
45
45
46 # Populated with the settings dictionary from application init in
46 # Populated with the settings dictionary from application init in
47 # rhodecode.conf.environment.load_pyramid_environment
47 # rhodecode.conf.environment.load_pyramid_environment
48 PYRAMID_SETTINGS = {}
48 PYRAMID_SETTINGS = {}
49
49
50 # Linked module for extensions
50 # Linked module for extensions
51 EXTENSIONS = {}
51 EXTENSIONS = {}
52
52
53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
54 __dbversion__ = 64 # defines current db version for migrations
54 __dbversion__ = 71 # defines current db version for migrations
55 __platform__ = platform.system()
55 __platform__ = platform.system()
56 __license__ = 'AGPLv3, and Commercial License'
56 __license__ = 'AGPLv3, and Commercial License'
57 __author__ = 'RhodeCode GmbH'
57 __author__ = 'RhodeCode GmbH'
58 __url__ = 'https://code.rhodecode.com'
58 __url__ = 'https://code.rhodecode.com'
59
59
60 is_windows = __platform__ in ['Windows']
60 is_windows = __platform__ in ['Windows']
61 is_unix = not is_windows
61 is_unix = not is_windows
62 is_test = False
62 is_test = False
63 disable_error_handler = False
63 disable_error_handler = False
@@ -1,510 +1,536 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import inspect
21 import inspect
22 import itertools
22 import itertools
23 import logging
23 import logging
24 import types
24 import types
25 import fnmatch
25
26
26 import decorator
27 import decorator
27 import venusian
28 import venusian
28 from collections import OrderedDict
29 from collections import OrderedDict
29
30
30 from pyramid.exceptions import ConfigurationError
31 from pyramid.exceptions import ConfigurationError
31 from pyramid.renderers import render
32 from pyramid.renderers import render
32 from pyramid.response import Response
33 from pyramid.response import Response
33 from pyramid.httpexceptions import HTTPNotFound
34 from pyramid.httpexceptions import HTTPNotFound
34
35
35 from rhodecode.api.exc import (
36 from rhodecode.api.exc import (
36 JSONRPCBaseError, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
37 JSONRPCBaseError, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
37 from rhodecode.lib.auth import AuthUser
38 from rhodecode.lib.auth import AuthUser
38 from rhodecode.lib.base import get_ip_addr
39 from rhodecode.lib.base import get_ip_addr
39 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.utils2 import safe_str
41 from rhodecode.lib.utils2 import safe_str
41 from rhodecode.lib.plugins.utils import get_plugin_settings
42 from rhodecode.lib.plugins.utils import get_plugin_settings
42 from rhodecode.model.db import User, UserApiKeys
43 from rhodecode.model.db import User, UserApiKeys
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
46 DEFAULT_RENDERER = 'jsonrpc_renderer'
47 DEFAULT_RENDERER = 'jsonrpc_renderer'
47 DEFAULT_URL = '/_admin/apiv2'
48 DEFAULT_URL = '/_admin/apiv2'
48
49
49
50
51 def find_methods(jsonrpc_methods, pattern):
52 matches = OrderedDict()
53 if not isinstance(pattern, (list, tuple)):
54 pattern = [pattern]
55
56 for single_pattern in pattern:
57 for method_name, method in jsonrpc_methods.items():
58 if fnmatch.fnmatch(method_name, single_pattern):
59 matches[method_name] = method
60 return matches
61
62
50 class ExtJsonRenderer(object):
63 class ExtJsonRenderer(object):
51 """
64 """
52 Custom renderer that mkaes use of our ext_json lib
65 Custom renderer that mkaes use of our ext_json lib
53
66
54 """
67 """
55
68
56 def __init__(self, serializer=json.dumps, **kw):
69 def __init__(self, serializer=json.dumps, **kw):
57 """ Any keyword arguments will be passed to the ``serializer``
70 """ Any keyword arguments will be passed to the ``serializer``
58 function."""
71 function."""
59 self.serializer = serializer
72 self.serializer = serializer
60 self.kw = kw
73 self.kw = kw
61
74
62 def __call__(self, info):
75 def __call__(self, info):
63 """ Returns a plain JSON-encoded string with content-type
76 """ Returns a plain JSON-encoded string with content-type
64 ``application/json``. The content-type may be overridden by
77 ``application/json``. The content-type may be overridden by
65 setting ``request.response.content_type``."""
78 setting ``request.response.content_type``."""
66
79
67 def _render(value, system):
80 def _render(value, system):
68 request = system.get('request')
81 request = system.get('request')
69 if request is not None:
82 if request is not None:
70 response = request.response
83 response = request.response
71 ct = response.content_type
84 ct = response.content_type
72 if ct == response.default_content_type:
85 if ct == response.default_content_type:
73 response.content_type = 'application/json'
86 response.content_type = 'application/json'
74
87
75 return self.serializer(value, **self.kw)
88 return self.serializer(value, **self.kw)
76
89
77 return _render
90 return _render
78
91
79
92
80 def jsonrpc_response(request, result):
93 def jsonrpc_response(request, result):
81 rpc_id = getattr(request, 'rpc_id', None)
94 rpc_id = getattr(request, 'rpc_id', None)
82 response = request.response
95 response = request.response
83
96
84 # store content_type before render is called
97 # store content_type before render is called
85 ct = response.content_type
98 ct = response.content_type
86
99
87 ret_value = ''
100 ret_value = ''
88 if rpc_id:
101 if rpc_id:
89 ret_value = {
102 ret_value = {
90 'id': rpc_id,
103 'id': rpc_id,
91 'result': result,
104 'result': result,
92 'error': None,
105 'error': None,
93 }
106 }
94
107
95 # fetch deprecation warnings, and store it inside results
108 # fetch deprecation warnings, and store it inside results
96 deprecation = getattr(request, 'rpc_deprecation', None)
109 deprecation = getattr(request, 'rpc_deprecation', None)
97 if deprecation:
110 if deprecation:
98 ret_value['DEPRECATION_WARNING'] = deprecation
111 ret_value['DEPRECATION_WARNING'] = deprecation
99
112
100 raw_body = render(DEFAULT_RENDERER, ret_value, request=request)
113 raw_body = render(DEFAULT_RENDERER, ret_value, request=request)
101 response.body = safe_str(raw_body, response.charset)
114 response.body = safe_str(raw_body, response.charset)
102
115
103 if ct == response.default_content_type:
116 if ct == response.default_content_type:
104 response.content_type = 'application/json'
117 response.content_type = 'application/json'
105
118
106 return response
119 return response
107
120
108
121
109 def jsonrpc_error(request, message, retid=None, code=None):
122 def jsonrpc_error(request, message, retid=None, code=None):
110 """
123 """
111 Generate a Response object with a JSON-RPC error body
124 Generate a Response object with a JSON-RPC error body
112
125
113 :param code:
126 :param code:
114 :param retid:
127 :param retid:
115 :param message:
128 :param message:
116 """
129 """
117 err_dict = {'id': retid, 'result': None, 'error': message}
130 err_dict = {'id': retid, 'result': None, 'error': message}
118 body = render(DEFAULT_RENDERER, err_dict, request=request).encode('utf-8')
131 body = render(DEFAULT_RENDERER, err_dict, request=request).encode('utf-8')
119 return Response(
132 return Response(
120 body=body,
133 body=body,
121 status=code,
134 status=code,
122 content_type='application/json'
135 content_type='application/json'
123 )
136 )
124
137
125
138
126 def exception_view(exc, request):
139 def exception_view(exc, request):
127 rpc_id = getattr(request, 'rpc_id', None)
140 rpc_id = getattr(request, 'rpc_id', None)
128
141
129 fault_message = 'undefined error'
142 fault_message = 'undefined error'
130 if isinstance(exc, JSONRPCError):
143 if isinstance(exc, JSONRPCError):
131 fault_message = exc.message
144 fault_message = exc.message
132 log.debug('json-rpc error rpc_id:%s "%s"', rpc_id, fault_message)
145 log.debug('json-rpc error rpc_id:%s "%s"', rpc_id, fault_message)
133 elif isinstance(exc, JSONRPCValidationError):
146 elif isinstance(exc, JSONRPCValidationError):
134 colander_exc = exc.colander_exception
147 colander_exc = exc.colander_exception
135 # TODO(marcink): think maybe of nicer way to serialize errors ?
148 # TODO(marcink): think maybe of nicer way to serialize errors ?
136 fault_message = colander_exc.asdict()
149 fault_message = colander_exc.asdict()
137 log.debug('json-rpc error rpc_id:%s "%s"', rpc_id, fault_message)
150 log.debug('json-rpc error rpc_id:%s "%s"', rpc_id, fault_message)
138 elif isinstance(exc, JSONRPCForbidden):
151 elif isinstance(exc, JSONRPCForbidden):
139 fault_message = 'Access was denied to this resource.'
152 fault_message = 'Access was denied to this resource.'
140 log.warning('json-rpc forbidden call rpc_id:%s "%s"', rpc_id, fault_message)
153 log.warning('json-rpc forbidden call rpc_id:%s "%s"', rpc_id, fault_message)
141 elif isinstance(exc, HTTPNotFound):
154 elif isinstance(exc, HTTPNotFound):
142 method = request.rpc_method
155 method = request.rpc_method
143 log.debug('json-rpc method `%s` not found in list of '
156 log.debug('json-rpc method `%s` not found in list of '
144 'api calls: %s, rpc_id:%s',
157 'api calls: %s, rpc_id:%s',
145 method, request.registry.jsonrpc_methods.keys(), rpc_id)
158 method, request.registry.jsonrpc_methods.keys(), rpc_id)
146 fault_message = "No such method: {}".format(method)
159
160 similar = 'none'
161 try:
162 similar_paterns = ['*{}*'.format(x) for x in method.split('_')]
163 similar_found = find_methods(
164 request.registry.jsonrpc_methods, similar_paterns)
165 similar = ', '.join(similar_found.keys()) or similar
166 except Exception:
167 # make the whole above block safe
168 pass
169
170 fault_message = "No such method: {}. Similar methods: {}".format(
171 method, similar)
147
172
148 return jsonrpc_error(request, fault_message, rpc_id)
173 return jsonrpc_error(request, fault_message, rpc_id)
149
174
150
175
151 def request_view(request):
176 def request_view(request):
152 """
177 """
153 Main request handling method. It handles all logic to call a specific
178 Main request handling method. It handles all logic to call a specific
154 exposed method
179 exposed method
155 """
180 """
156
181
157 # check if we can find this session using api_key, get_by_auth_token
182 # check if we can find this session using api_key, get_by_auth_token
158 # search not expired tokens only
183 # search not expired tokens only
159
184
160 try:
185 try:
161 api_user = User.get_by_auth_token(request.rpc_api_key)
186 api_user = User.get_by_auth_token(request.rpc_api_key)
162
187
163 if api_user is None:
188 if api_user is None:
164 return jsonrpc_error(
189 return jsonrpc_error(
165 request, retid=request.rpc_id, message='Invalid API KEY')
190 request, retid=request.rpc_id, message='Invalid API KEY')
166
191
167 if not api_user.active:
192 if not api_user.active:
168 return jsonrpc_error(
193 return jsonrpc_error(
169 request, retid=request.rpc_id,
194 request, retid=request.rpc_id,
170 message='Request from this user not allowed')
195 message='Request from this user not allowed')
171
196
172 # check if we are allowed to use this IP
197 # check if we are allowed to use this IP
173 auth_u = AuthUser(
198 auth_u = AuthUser(
174 api_user.user_id, request.rpc_api_key, ip_addr=request.rpc_ip_addr)
199 api_user.user_id, request.rpc_api_key, ip_addr=request.rpc_ip_addr)
175 if not auth_u.ip_allowed:
200 if not auth_u.ip_allowed:
176 return jsonrpc_error(
201 return jsonrpc_error(
177 request, retid=request.rpc_id,
202 request, retid=request.rpc_id,
178 message='Request from IP:%s not allowed' % (
203 message='Request from IP:%s not allowed' % (
179 request.rpc_ip_addr,))
204 request.rpc_ip_addr,))
180 else:
205 else:
181 log.info('Access for IP:%s allowed' % (request.rpc_ip_addr,))
206 log.info('Access for IP:%s allowed' % (request.rpc_ip_addr,))
182
207
183 # register our auth-user
208 # register our auth-user
184 request.rpc_user = auth_u
209 request.rpc_user = auth_u
185
210
186 # now check if token is valid for API
211 # now check if token is valid for API
187 role = UserApiKeys.ROLE_API
212 auth_token = request.rpc_api_key
188 extra_auth_tokens = [
213 token_match = api_user.authenticate_by_token(
189 x.api_key for x in User.extra_valid_auth_tokens(api_user, role=role)]
214 auth_token, roles=[UserApiKeys.ROLE_API])
190 active_tokens = [api_user.api_key] + extra_auth_tokens
215 invalid_token = not token_match
191
216
192 log.debug('Checking if API key has proper role')
217 log.debug('Checking if API KEY is valid with proper role')
193 if request.rpc_api_key not in active_tokens:
218 if invalid_token:
194 return jsonrpc_error(
219 return jsonrpc_error(
195 request, retid=request.rpc_id,
220 request, retid=request.rpc_id,
196 message='API KEY has bad role for an API call')
221 message='API KEY invalid or, has bad role for an API call')
197
222
198 except Exception as e:
223 except Exception:
199 log.exception('Error on API AUTH')
224 log.exception('Error on API AUTH')
200 return jsonrpc_error(
225 return jsonrpc_error(
201 request, retid=request.rpc_id, message='Invalid API KEY')
226 request, retid=request.rpc_id, message='Invalid API KEY')
202
227
203 method = request.rpc_method
228 method = request.rpc_method
204 func = request.registry.jsonrpc_methods[method]
229 func = request.registry.jsonrpc_methods[method]
205
230
206 # now that we have a method, add request._req_params to
231 # now that we have a method, add request._req_params to
207 # self.kargs and dispatch control to WGIController
232 # self.kargs and dispatch control to WGIController
208 argspec = inspect.getargspec(func)
233 argspec = inspect.getargspec(func)
209 arglist = argspec[0]
234 arglist = argspec[0]
210 defaults = map(type, argspec[3] or [])
235 defaults = map(type, argspec[3] or [])
211 default_empty = types.NotImplementedType
236 default_empty = types.NotImplementedType
212
237
213 # kw arguments required by this method
238 # kw arguments required by this method
214 func_kwargs = dict(itertools.izip_longest(
239 func_kwargs = dict(itertools.izip_longest(
215 reversed(arglist), reversed(defaults), fillvalue=default_empty))
240 reversed(arglist), reversed(defaults), fillvalue=default_empty))
216
241
217 # This attribute will need to be first param of a method that uses
242 # This attribute will need to be first param of a method that uses
218 # api_key, which is translated to instance of user at that name
243 # api_key, which is translated to instance of user at that name
219 user_var = 'apiuser'
244 user_var = 'apiuser'
220 request_var = 'request'
245 request_var = 'request'
221
246
222 for arg in [user_var, request_var]:
247 for arg in [user_var, request_var]:
223 if arg not in arglist:
248 if arg not in arglist:
224 return jsonrpc_error(
249 return jsonrpc_error(
225 request,
250 request,
226 retid=request.rpc_id,
251 retid=request.rpc_id,
227 message='This method [%s] does not support '
252 message='This method [%s] does not support '
228 'required parameter `%s`' % (func.__name__, arg))
253 'required parameter `%s`' % (func.__name__, arg))
229
254
230 # get our arglist and check if we provided them as args
255 # get our arglist and check if we provided them as args
231 for arg, default in func_kwargs.items():
256 for arg, default in func_kwargs.items():
232 if arg in [user_var, request_var]:
257 if arg in [user_var, request_var]:
233 # user_var and request_var are pre-hardcoded parameters and we
258 # user_var and request_var are pre-hardcoded parameters and we
234 # don't need to do any translation
259 # don't need to do any translation
235 continue
260 continue
236
261
237 # skip the required param check if it's default value is
262 # skip the required param check if it's default value is
238 # NotImplementedType (default_empty)
263 # NotImplementedType (default_empty)
239 if default == default_empty and arg not in request.rpc_params:
264 if default == default_empty and arg not in request.rpc_params:
240 return jsonrpc_error(
265 return jsonrpc_error(
241 request,
266 request,
242 retid=request.rpc_id,
267 retid=request.rpc_id,
243 message=('Missing non optional `%s` arg in JSON DATA' % arg)
268 message=('Missing non optional `%s` arg in JSON DATA' % arg)
244 )
269 )
245
270
246 # sanitize extra passed arguments
271 # sanitize extra passed arguments
247 for k in request.rpc_params.keys()[:]:
272 for k in request.rpc_params.keys()[:]:
248 if k not in func_kwargs:
273 if k not in func_kwargs:
249 del request.rpc_params[k]
274 del request.rpc_params[k]
250
275
251 call_params = request.rpc_params
276 call_params = request.rpc_params
252 call_params.update({
277 call_params.update({
253 'request': request,
278 'request': request,
254 'apiuser': auth_u
279 'apiuser': auth_u
255 })
280 })
256 try:
281 try:
257 ret_value = func(**call_params)
282 ret_value = func(**call_params)
258 return jsonrpc_response(request, ret_value)
283 return jsonrpc_response(request, ret_value)
259 except JSONRPCBaseError:
284 except JSONRPCBaseError:
260 raise
285 raise
261 except Exception:
286 except Exception:
262 log.exception('Unhandled exception occurred on api call: %s', func)
287 log.exception('Unhandled exception occurred on api call: %s', func)
263 return jsonrpc_error(request, retid=request.rpc_id,
288 return jsonrpc_error(request, retid=request.rpc_id,
264 message='Internal server error')
289 message='Internal server error')
265
290
266
291
267 def setup_request(request):
292 def setup_request(request):
268 """
293 """
269 Parse a JSON-RPC request body. It's used inside the predicates method
294 Parse a JSON-RPC request body. It's used inside the predicates method
270 to validate and bootstrap requests for usage in rpc calls.
295 to validate and bootstrap requests for usage in rpc calls.
271
296
272 We need to raise JSONRPCError here if we want to return some errors back to
297 We need to raise JSONRPCError here if we want to return some errors back to
273 user.
298 user.
274 """
299 """
275
300
276 log.debug('Executing setup request: %r', request)
301 log.debug('Executing setup request: %r', request)
277 request.rpc_ip_addr = get_ip_addr(request.environ)
302 request.rpc_ip_addr = get_ip_addr(request.environ)
278 # TODO(marcink): deprecate GET at some point
303 # TODO(marcink): deprecate GET at some point
279 if request.method not in ['POST', 'GET']:
304 if request.method not in ['POST', 'GET']:
280 log.debug('unsupported request method "%s"', request.method)
305 log.debug('unsupported request method "%s"', request.method)
281 raise JSONRPCError(
306 raise JSONRPCError(
282 'unsupported request method "%s". Please use POST' % request.method)
307 'unsupported request method "%s". Please use POST' % request.method)
283
308
284 if 'CONTENT_LENGTH' not in request.environ:
309 if 'CONTENT_LENGTH' not in request.environ:
285 log.debug("No Content-Length")
310 log.debug("No Content-Length")
286 raise JSONRPCError("Empty body, No Content-Length in request")
311 raise JSONRPCError("Empty body, No Content-Length in request")
287
312
288 else:
313 else:
289 length = request.environ['CONTENT_LENGTH']
314 length = request.environ['CONTENT_LENGTH']
290 log.debug('Content-Length: %s', length)
315 log.debug('Content-Length: %s', length)
291
316
292 if length == 0:
317 if length == 0:
293 log.debug("Content-Length is 0")
318 log.debug("Content-Length is 0")
294 raise JSONRPCError("Content-Length is 0")
319 raise JSONRPCError("Content-Length is 0")
295
320
296 raw_body = request.body
321 raw_body = request.body
297 try:
322 try:
298 json_body = json.loads(raw_body)
323 json_body = json.loads(raw_body)
299 except ValueError as e:
324 except ValueError as e:
300 # catch JSON errors Here
325 # catch JSON errors Here
301 raise JSONRPCError("JSON parse error ERR:%s RAW:%r" % (e, raw_body))
326 raise JSONRPCError("JSON parse error ERR:%s RAW:%r" % (e, raw_body))
302
327
303 request.rpc_id = json_body.get('id')
328 request.rpc_id = json_body.get('id')
304 request.rpc_method = json_body.get('method')
329 request.rpc_method = json_body.get('method')
305
330
306 # check required base parameters
331 # check required base parameters
307 try:
332 try:
308 api_key = json_body.get('api_key')
333 api_key = json_body.get('api_key')
309 if not api_key:
334 if not api_key:
310 api_key = json_body.get('auth_token')
335 api_key = json_body.get('auth_token')
311
336
312 if not api_key:
337 if not api_key:
313 raise KeyError('api_key or auth_token')
338 raise KeyError('api_key or auth_token')
314
339
315 # TODO(marcink): support passing in token in request header
340 # TODO(marcink): support passing in token in request header
316
341
317 request.rpc_api_key = api_key
342 request.rpc_api_key = api_key
318 request.rpc_id = json_body['id']
343 request.rpc_id = json_body['id']
319 request.rpc_method = json_body['method']
344 request.rpc_method = json_body['method']
320 request.rpc_params = json_body['args'] \
345 request.rpc_params = json_body['args'] \
321 if isinstance(json_body['args'], dict) else {}
346 if isinstance(json_body['args'], dict) else {}
322
347
323 log.debug(
348 log.debug(
324 'method: %s, params: %s' % (request.rpc_method, request.rpc_params))
349 'method: %s, params: %s' % (request.rpc_method, request.rpc_params))
325 except KeyError as e:
350 except KeyError as e:
326 raise JSONRPCError('Incorrect JSON data. Missing %s' % e)
351 raise JSONRPCError('Incorrect JSON data. Missing %s' % e)
327
352
328 log.debug('setup complete, now handling method:%s rpcid:%s',
353 log.debug('setup complete, now handling method:%s rpcid:%s',
329 request.rpc_method, request.rpc_id, )
354 request.rpc_method, request.rpc_id, )
330
355
331
356
332 class RoutePredicate(object):
357 class RoutePredicate(object):
333 def __init__(self, val, config):
358 def __init__(self, val, config):
334 self.val = val
359 self.val = val
335
360
336 def text(self):
361 def text(self):
337 return 'jsonrpc route = %s' % self.val
362 return 'jsonrpc route = %s' % self.val
338
363
339 phash = text
364 phash = text
340
365
341 def __call__(self, info, request):
366 def __call__(self, info, request):
342 if self.val:
367 if self.val:
343 # potentially setup and bootstrap our call
368 # potentially setup and bootstrap our call
344 setup_request(request)
369 setup_request(request)
345
370
346 # Always return True so that even if it isn't a valid RPC it
371 # Always return True so that even if it isn't a valid RPC it
347 # will fall through to the underlaying handlers like notfound_view
372 # will fall through to the underlaying handlers like notfound_view
348 return True
373 return True
349
374
350
375
351 class NotFoundPredicate(object):
376 class NotFoundPredicate(object):
352 def __init__(self, val, config):
377 def __init__(self, val, config):
353 self.val = val
378 self.val = val
379 self.methods = config.registry.jsonrpc_methods
354
380
355 def text(self):
381 def text(self):
356 return 'jsonrpc method not found = %s' % self.val
382 return 'jsonrpc method not found = {}.'.format(self.val)
357
383
358 phash = text
384 phash = text
359
385
360 def __call__(self, info, request):
386 def __call__(self, info, request):
361 return hasattr(request, 'rpc_method')
387 return hasattr(request, 'rpc_method')
362
388
363
389
364 class MethodPredicate(object):
390 class MethodPredicate(object):
365 def __init__(self, val, config):
391 def __init__(self, val, config):
366 self.method = val
392 self.method = val
367
393
368 def text(self):
394 def text(self):
369 return 'jsonrpc method = %s' % self.method
395 return 'jsonrpc method = %s' % self.method
370
396
371 phash = text
397 phash = text
372
398
373 def __call__(self, context, request):
399 def __call__(self, context, request):
374 # we need to explicitly return False here, so pyramid doesn't try to
400 # we need to explicitly return False here, so pyramid doesn't try to
375 # execute our view directly. We need our main handler to execute things
401 # execute our view directly. We need our main handler to execute things
376 return getattr(request, 'rpc_method') == self.method
402 return getattr(request, 'rpc_method') == self.method
377
403
378
404
379 def add_jsonrpc_method(config, view, **kwargs):
405 def add_jsonrpc_method(config, view, **kwargs):
380 # pop the method name
406 # pop the method name
381 method = kwargs.pop('method', None)
407 method = kwargs.pop('method', None)
382
408
383 if method is None:
409 if method is None:
384 raise ConfigurationError(
410 raise ConfigurationError(
385 'Cannot register a JSON-RPC method without specifying the '
411 'Cannot register a JSON-RPC method without specifying the '
386 '"method"')
412 '"method"')
387
413
388 # we define custom predicate, to enable to detect conflicting methods,
414 # we define custom predicate, to enable to detect conflicting methods,
389 # those predicates are kind of "translation" from the decorator variables
415 # those predicates are kind of "translation" from the decorator variables
390 # to internal predicates names
416 # to internal predicates names
391
417
392 kwargs['jsonrpc_method'] = method
418 kwargs['jsonrpc_method'] = method
393
419
394 # register our view into global view store for validation
420 # register our view into global view store for validation
395 config.registry.jsonrpc_methods[method] = view
421 config.registry.jsonrpc_methods[method] = view
396
422
397 # we're using our main request_view handler, here, so each method
423 # we're using our main request_view handler, here, so each method
398 # has a unified handler for itself
424 # has a unified handler for itself
399 config.add_view(request_view, route_name='apiv2', **kwargs)
425 config.add_view(request_view, route_name='apiv2', **kwargs)
400
426
401
427
402 class jsonrpc_method(object):
428 class jsonrpc_method(object):
403 """
429 """
404 decorator that works similar to @add_view_config decorator,
430 decorator that works similar to @add_view_config decorator,
405 but tailored for our JSON RPC
431 but tailored for our JSON RPC
406 """
432 """
407
433
408 venusian = venusian # for testing injection
434 venusian = venusian # for testing injection
409
435
410 def __init__(self, method=None, **kwargs):
436 def __init__(self, method=None, **kwargs):
411 self.method = method
437 self.method = method
412 self.kwargs = kwargs
438 self.kwargs = kwargs
413
439
414 def __call__(self, wrapped):
440 def __call__(self, wrapped):
415 kwargs = self.kwargs.copy()
441 kwargs = self.kwargs.copy()
416 kwargs['method'] = self.method or wrapped.__name__
442 kwargs['method'] = self.method or wrapped.__name__
417 depth = kwargs.pop('_depth', 0)
443 depth = kwargs.pop('_depth', 0)
418
444
419 def callback(context, name, ob):
445 def callback(context, name, ob):
420 config = context.config.with_package(info.module)
446 config = context.config.with_package(info.module)
421 config.add_jsonrpc_method(view=ob, **kwargs)
447 config.add_jsonrpc_method(view=ob, **kwargs)
422
448
423 info = venusian.attach(wrapped, callback, category='pyramid',
449 info = venusian.attach(wrapped, callback, category='pyramid',
424 depth=depth + 1)
450 depth=depth + 1)
425 if info.scope == 'class':
451 if info.scope == 'class':
426 # ensure that attr is set if decorating a class method
452 # ensure that attr is set if decorating a class method
427 kwargs.setdefault('attr', wrapped.__name__)
453 kwargs.setdefault('attr', wrapped.__name__)
428
454
429 kwargs['_info'] = info.codeinfo # fbo action_method
455 kwargs['_info'] = info.codeinfo # fbo action_method
430 return wrapped
456 return wrapped
431
457
432
458
433 class jsonrpc_deprecated_method(object):
459 class jsonrpc_deprecated_method(object):
434 """
460 """
435 Marks method as deprecated, adds log.warning, and inject special key to
461 Marks method as deprecated, adds log.warning, and inject special key to
436 the request variable to mark method as deprecated.
462 the request variable to mark method as deprecated.
437 Also injects special docstring that extract_docs will catch to mark
463 Also injects special docstring that extract_docs will catch to mark
438 method as deprecated.
464 method as deprecated.
439
465
440 :param use_method: specify which method should be used instead of
466 :param use_method: specify which method should be used instead of
441 the decorated one
467 the decorated one
442
468
443 Use like::
469 Use like::
444
470
445 @jsonrpc_method()
471 @jsonrpc_method()
446 @jsonrpc_deprecated_method(use_method='new_func', deprecated_at_version='3.0.0')
472 @jsonrpc_deprecated_method(use_method='new_func', deprecated_at_version='3.0.0')
447 def old_func(request, apiuser, arg1, arg2):
473 def old_func(request, apiuser, arg1, arg2):
448 ...
474 ...
449 """
475 """
450
476
451 def __init__(self, use_method, deprecated_at_version):
477 def __init__(self, use_method, deprecated_at_version):
452 self.use_method = use_method
478 self.use_method = use_method
453 self.deprecated_at_version = deprecated_at_version
479 self.deprecated_at_version = deprecated_at_version
454 self.deprecated_msg = ''
480 self.deprecated_msg = ''
455
481
456 def __call__(self, func):
482 def __call__(self, func):
457 self.deprecated_msg = 'Please use method `{method}` instead.'.format(
483 self.deprecated_msg = 'Please use method `{method}` instead.'.format(
458 method=self.use_method)
484 method=self.use_method)
459
485
460 docstring = """\n
486 docstring = """\n
461 .. deprecated:: {version}
487 .. deprecated:: {version}
462
488
463 {deprecation_message}
489 {deprecation_message}
464
490
465 {original_docstring}
491 {original_docstring}
466 """
492 """
467 func.__doc__ = docstring.format(
493 func.__doc__ = docstring.format(
468 version=self.deprecated_at_version,
494 version=self.deprecated_at_version,
469 deprecation_message=self.deprecated_msg,
495 deprecation_message=self.deprecated_msg,
470 original_docstring=func.__doc__)
496 original_docstring=func.__doc__)
471 return decorator.decorator(self.__wrapper, func)
497 return decorator.decorator(self.__wrapper, func)
472
498
473 def __wrapper(self, func, *fargs, **fkwargs):
499 def __wrapper(self, func, *fargs, **fkwargs):
474 log.warning('DEPRECATED API CALL on function %s, please '
500 log.warning('DEPRECATED API CALL on function %s, please '
475 'use `%s` instead', func, self.use_method)
501 'use `%s` instead', func, self.use_method)
476 # alter function docstring to mark as deprecated, this is picked up
502 # alter function docstring to mark as deprecated, this is picked up
477 # via fabric file that generates API DOC.
503 # via fabric file that generates API DOC.
478 result = func(*fargs, **fkwargs)
504 result = func(*fargs, **fkwargs)
479
505
480 request = fargs[0]
506 request = fargs[0]
481 request.rpc_deprecation = 'DEPRECATED METHOD ' + self.deprecated_msg
507 request.rpc_deprecation = 'DEPRECATED METHOD ' + self.deprecated_msg
482 return result
508 return result
483
509
484
510
485 def includeme(config):
511 def includeme(config):
486 plugin_module = 'rhodecode.api'
512 plugin_module = 'rhodecode.api'
487 plugin_settings = get_plugin_settings(
513 plugin_settings = get_plugin_settings(
488 plugin_module, config.registry.settings)
514 plugin_module, config.registry.settings)
489
515
490 if not hasattr(config.registry, 'jsonrpc_methods'):
516 if not hasattr(config.registry, 'jsonrpc_methods'):
491 config.registry.jsonrpc_methods = OrderedDict()
517 config.registry.jsonrpc_methods = OrderedDict()
492
518
493 # match filter by given method only
519 # match filter by given method only
494 config.add_view_predicate('jsonrpc_method', MethodPredicate)
520 config.add_view_predicate('jsonrpc_method', MethodPredicate)
495
521
496 config.add_renderer(DEFAULT_RENDERER, ExtJsonRenderer(
522 config.add_renderer(DEFAULT_RENDERER, ExtJsonRenderer(
497 serializer=json.dumps, indent=4))
523 serializer=json.dumps, indent=4))
498 config.add_directive('add_jsonrpc_method', add_jsonrpc_method)
524 config.add_directive('add_jsonrpc_method', add_jsonrpc_method)
499
525
500 config.add_route_predicate(
526 config.add_route_predicate(
501 'jsonrpc_call', RoutePredicate)
527 'jsonrpc_call', RoutePredicate)
502
528
503 config.add_route(
529 config.add_route(
504 'apiv2', plugin_settings.get('url', DEFAULT_URL), jsonrpc_call=True)
530 'apiv2', plugin_settings.get('url', DEFAULT_URL), jsonrpc_call=True)
505
531
506 config.scan(plugin_module, ignore='rhodecode.api.tests')
532 config.scan(plugin_module, ignore='rhodecode.api.tests')
507 # register some exception handling view
533 # register some exception handling view
508 config.add_view(exception_view, context=JSONRPCBaseError)
534 config.add_view(exception_view, context=JSONRPCBaseError)
509 config.add_view_predicate('jsonrpc_method_not_found', NotFoundPredicate)
535 config.add_view_predicate('jsonrpc_method_not_found', NotFoundPredicate)
510 config.add_notfound_view(exception_view, jsonrpc_method_not_found=True)
536 config.add_notfound_view(exception_view, jsonrpc_method_not_found=True)
@@ -1,42 +1,52 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.model.meta import Session
23 from rhodecode.model.meta import Session
24 from rhodecode.model.user import UserModel
24 from rhodecode.model.user import UserModel
25 from rhodecode.model.auth_token import AuthTokenModel
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26
27
27
28
28 @pytest.fixture(scope="class")
29 @pytest.fixture(scope="class")
29 def testuser_api(request, pylonsapp):
30 def testuser_api(request, pylonsapp):
30 cls = request.cls
31 cls = request.cls
32
33 # ADMIN USER
31 cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
34 cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
32 cls.apikey = cls.usr.api_key
35 cls.apikey = cls.usr.api_key
36
37 # REGULAR USER
33 cls.test_user = UserModel().create_or_update(
38 cls.test_user = UserModel().create_or_update(
34 username='test-api',
39 username='test-api',
35 password='test',
40 password='test',
36 email='test@api.rhodecode.org',
41 email='test@api.rhodecode.org',
37 firstname='first',
42 firstname='first',
38 lastname='last'
43 lastname='last'
39 )
44 )
45 # create TOKEN for user, if he doesn't have one
46 if not cls.test_user.api_key:
47 AuthTokenModel().create(
48 user=cls.test_user, description='TEST_USER_TOKEN')
49
40 Session().commit()
50 Session().commit()
41 cls.TEST_USER_LOGIN = cls.test_user.username
51 cls.TEST_USER_LOGIN = cls.test_user.username
42 cls.apikey_regular = cls.test_user.api_key
52 cls.apikey_regular = cls.test_user.api_key
@@ -1,125 +1,131 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.api.utils import Optional, OAttr
23 from rhodecode.api.utils import Optional, OAttr
24 from rhodecode.api.tests.utils import (
24 from rhodecode.api.tests.utils import (
25 build_data, api_call, assert_error, assert_ok)
25 build_data, api_call, assert_error, assert_ok)
26
26
27
27
28 @pytest.mark.usefixtures("testuser_api", "app")
28 @pytest.mark.usefixtures("testuser_api", "app")
29 class TestApi(object):
29 class TestApi(object):
30 maxDiff = None
30 maxDiff = None
31
31
32 def test_Optional_object(self):
32 def test_Optional_object(self):
33
33
34 option1 = Optional(None)
34 option1 = Optional(None)
35 assert '<Optional:%s>' % (None,) == repr(option1)
35 assert '<Optional:%s>' % (None,) == repr(option1)
36 assert option1() is None
36 assert option1() is None
37
37
38 assert 1 == Optional.extract(Optional(1))
38 assert 1 == Optional.extract(Optional(1))
39 assert 'example' == Optional.extract('example')
39 assert 'example' == Optional.extract('example')
40
40
41 def test_Optional_OAttr(self):
41 def test_Optional_OAttr(self):
42 option1 = Optional(OAttr('apiuser'))
42 option1 = Optional(OAttr('apiuser'))
43 assert 'apiuser' == Optional.extract(option1)
43 assert 'apiuser' == Optional.extract(option1)
44
44
45 def test_OAttr_object(self):
45 def test_OAttr_object(self):
46 oattr1 = OAttr('apiuser')
46 oattr1 = OAttr('apiuser')
47 assert '<OptionalAttr:apiuser>' == repr(oattr1)
47 assert '<OptionalAttr:apiuser>' == repr(oattr1)
48 assert oattr1() == oattr1
48 assert oattr1() == oattr1
49
49
50 def test_api_wrong_key(self):
50 def test_api_wrong_key(self):
51 id_, params = build_data('trololo', 'get_user')
51 id_, params = build_data('trololo', 'get_user')
52 response = api_call(self.app, params)
52 response = api_call(self.app, params)
53
53
54 expected = 'Invalid API KEY'
54 expected = 'Invalid API KEY'
55 assert_error(id_, expected, given=response.body)
55 assert_error(id_, expected, given=response.body)
56
56
57 def test_api_missing_non_optional_param(self):
57 def test_api_missing_non_optional_param(self):
58 id_, params = build_data(self.apikey, 'get_repo')
58 id_, params = build_data(self.apikey, 'get_repo')
59 response = api_call(self.app, params)
59 response = api_call(self.app, params)
60
60
61 expected = 'Missing non optional `repoid` arg in JSON DATA'
61 expected = 'Missing non optional `repoid` arg in JSON DATA'
62 assert_error(id_, expected, given=response.body)
62 assert_error(id_, expected, given=response.body)
63
63
64 def test_api_missing_non_optional_param_args_null(self):
64 def test_api_missing_non_optional_param_args_null(self):
65 id_, params = build_data(self.apikey, 'get_repo')
65 id_, params = build_data(self.apikey, 'get_repo')
66 params = params.replace('"args": {}', '"args": null')
66 params = params.replace('"args": {}', '"args": null')
67 response = api_call(self.app, params)
67 response = api_call(self.app, params)
68
68
69 expected = 'Missing non optional `repoid` arg in JSON DATA'
69 expected = 'Missing non optional `repoid` arg in JSON DATA'
70 assert_error(id_, expected, given=response.body)
70 assert_error(id_, expected, given=response.body)
71
71
72 def test_api_missing_non_optional_param_args_bad(self):
72 def test_api_missing_non_optional_param_args_bad(self):
73 id_, params = build_data(self.apikey, 'get_repo')
73 id_, params = build_data(self.apikey, 'get_repo')
74 params = params.replace('"args": {}', '"args": 1')
74 params = params.replace('"args": {}', '"args": 1')
75 response = api_call(self.app, params)
75 response = api_call(self.app, params)
76
76
77 expected = 'Missing non optional `repoid` arg in JSON DATA'
77 expected = 'Missing non optional `repoid` arg in JSON DATA'
78 assert_error(id_, expected, given=response.body)
78 assert_error(id_, expected, given=response.body)
79
79
80 def test_api_non_existing_method(self, request):
80 def test_api_non_existing_method(self, request):
81 id_, params = build_data(self.apikey, 'not_existing', args='xx')
81 id_, params = build_data(self.apikey, 'not_existing', args='xx')
82 response = api_call(self.app, params)
82 response = api_call(self.app, params)
83 expected = 'No such method: not_existing'
83 expected = 'No such method: not_existing. Similar methods: none'
84 assert_error(id_, expected, given=response.body)
85
86 def test_api_non_existing_method_have_similar(self, request):
87 id_, params = build_data(self.apikey, 'comment', args='xx')
88 response = api_call(self.app, params)
89 expected = 'No such method: comment. Similar methods: changeset_comment, comment_pull_request, comment_commit'
84 assert_error(id_, expected, given=response.body)
90 assert_error(id_, expected, given=response.body)
85
91
86 def test_api_disabled_user(self, request):
92 def test_api_disabled_user(self, request):
87
93
88 def set_active(active):
94 def set_active(active):
89 from rhodecode.model.db import Session, User
95 from rhodecode.model.db import Session, User
90 user = User.get_by_auth_token(self.apikey)
96 user = User.get_by_auth_token(self.apikey)
91 user.active = active
97 user.active = active
92 Session().add(user)
98 Session().add(user)
93 Session().commit()
99 Session().commit()
94
100
95 request.addfinalizer(lambda: set_active(True))
101 request.addfinalizer(lambda: set_active(True))
96
102
97 set_active(False)
103 set_active(False)
98 id_, params = build_data(self.apikey, 'test', args='xx')
104 id_, params = build_data(self.apikey, 'test', args='xx')
99 response = api_call(self.app, params)
105 response = api_call(self.app, params)
100 expected = 'Request from this user not allowed'
106 expected = 'Request from this user not allowed'
101 assert_error(id_, expected, given=response.body)
107 assert_error(id_, expected, given=response.body)
102
108
103 def test_api_args_is_null(self):
109 def test_api_args_is_null(self):
104 __, params = build_data(self.apikey, 'get_users', )
110 __, params = build_data(self.apikey, 'get_users', )
105 params = params.replace('"args": {}', '"args": null')
111 params = params.replace('"args": {}', '"args": null')
106 response = api_call(self.app, params)
112 response = api_call(self.app, params)
107 assert response.status == '200 OK'
113 assert response.status == '200 OK'
108
114
109 def test_api_args_is_bad(self):
115 def test_api_args_is_bad(self):
110 __, params = build_data(self.apikey, 'get_users', )
116 __, params = build_data(self.apikey, 'get_users', )
111 params = params.replace('"args": {}', '"args": 1')
117 params = params.replace('"args": {}', '"args": 1')
112 response = api_call(self.app, params)
118 response = api_call(self.app, params)
113 assert response.status == '200 OK'
119 assert response.status == '200 OK'
114
120
115 def test_api_args_different_args(self):
121 def test_api_args_different_args(self):
116 import string
122 import string
117 expected = {
123 expected = {
118 'ascii_letters': string.ascii_letters,
124 'ascii_letters': string.ascii_letters,
119 'ws': string.whitespace,
125 'ws': string.whitespace,
120 'printables': string.printable
126 'printables': string.printable
121 }
127 }
122 id_, params = build_data(self.apikey, 'test', args=expected)
128 id_, params = build_data(self.apikey, 'test', args=expected)
123 response = api_call(self.app, params)
129 response = api_call(self.app, params)
124 assert response.status == '200 OK'
130 assert response.status == '200 OK'
125 assert_ok(id_, expected, response.body)
131 assert_ok(id_, expected, response.body)
@@ -1,111 +1,112 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.model.db import UserLog
23 from rhodecode.model.db import UserLog
24 from rhodecode.model.pull_request import PullRequestModel
24 from rhodecode.model.pull_request import PullRequestModel
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.api.tests.utils import (
26 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok)
27 build_data, api_call, assert_error, assert_ok)
28
28
29
29
30 @pytest.mark.usefixtures("testuser_api", "app")
30 @pytest.mark.usefixtures("testuser_api", "app")
31 class TestClosePullRequest(object):
31 class TestClosePullRequest(object):
32
32 @pytest.mark.backends("git", "hg")
33 @pytest.mark.backends("git", "hg")
33 def test_api_close_pull_request(self, pr_util):
34 def test_api_close_pull_request(self, pr_util):
34 pull_request = pr_util.create_pull_request()
35 pull_request = pr_util.create_pull_request()
35 pull_request_id = pull_request.pull_request_id
36 pull_request_id = pull_request.pull_request_id
36 author = pull_request.user_id
37 author = pull_request.user_id
37 repo = pull_request.target_repo.repo_id
38 repo = pull_request.target_repo.repo_id
38 id_, params = build_data(
39 id_, params = build_data(
39 self.apikey, 'close_pull_request',
40 self.apikey, 'close_pull_request',
40 repoid=pull_request.target_repo.repo_name,
41 repoid=pull_request.target_repo.repo_name,
41 pullrequestid=pull_request.pull_request_id)
42 pullrequestid=pull_request.pull_request_id)
42 response = api_call(self.app, params)
43 response = api_call(self.app, params)
43 expected = {
44 expected = {
44 'pull_request_id': pull_request_id,
45 'pull_request_id': pull_request_id,
45 'closed': True,
46 'closed': True,
46 }
47 }
47 assert_ok(id_, expected, response.body)
48 assert_ok(id_, expected, response.body)
48 action = 'user_closed_pull_request:%d' % pull_request_id
49 action = 'user_closed_pull_request:%d' % pull_request_id
49 journal = UserLog.query()\
50 journal = UserLog.query()\
50 .filter(UserLog.user_id == author)\
51 .filter(UserLog.user_id == author)\
51 .filter(UserLog.repository_id == repo)\
52 .filter(UserLog.repository_id == repo)\
52 .filter(UserLog.action == action)\
53 .filter(UserLog.action == action)\
53 .all()
54 .all()
54 assert len(journal) == 1
55 assert len(journal) == 1
55
56
56 @pytest.mark.backends("git", "hg")
57 @pytest.mark.backends("git", "hg")
57 def test_api_close_pull_request_already_closed_error(self, pr_util):
58 def test_api_close_pull_request_already_closed_error(self, pr_util):
58 pull_request = pr_util.create_pull_request()
59 pull_request = pr_util.create_pull_request()
59 pull_request_id = pull_request.pull_request_id
60 pull_request_id = pull_request.pull_request_id
60 pull_request_repo = pull_request.target_repo.repo_name
61 pull_request_repo = pull_request.target_repo.repo_name
61 PullRequestModel().close_pull_request(
62 PullRequestModel().close_pull_request(
62 pull_request, pull_request.author)
63 pull_request, pull_request.author)
63 id_, params = build_data(
64 id_, params = build_data(
64 self.apikey, 'close_pull_request',
65 self.apikey, 'close_pull_request',
65 repoid=pull_request_repo, pullrequestid=pull_request_id)
66 repoid=pull_request_repo, pullrequestid=pull_request_id)
66 response = api_call(self.app, params)
67 response = api_call(self.app, params)
67
68
68 expected = 'pull request `%s` is already closed' % pull_request_id
69 expected = 'pull request `%s` is already closed' % pull_request_id
69 assert_error(id_, expected, given=response.body)
70 assert_error(id_, expected, given=response.body)
70
71
71 @pytest.mark.backends("git", "hg")
72 @pytest.mark.backends("git", "hg")
72 def test_api_close_pull_request_repo_error(self):
73 def test_api_close_pull_request_repo_error(self):
73 id_, params = build_data(
74 id_, params = build_data(
74 self.apikey, 'close_pull_request',
75 self.apikey, 'close_pull_request',
75 repoid=666, pullrequestid=1)
76 repoid=666, pullrequestid=1)
76 response = api_call(self.app, params)
77 response = api_call(self.app, params)
77
78
78 expected = 'repository `666` does not exist'
79 expected = 'repository `666` does not exist'
79 assert_error(id_, expected, given=response.body)
80 assert_error(id_, expected, given=response.body)
80
81
81 @pytest.mark.backends("git", "hg")
82 @pytest.mark.backends("git", "hg")
82 def test_api_close_pull_request_non_admin_with_userid_error(self,
83 def test_api_close_pull_request_non_admin_with_userid_error(self,
83 pr_util):
84 pr_util):
84 pull_request = pr_util.create_pull_request()
85 pull_request = pr_util.create_pull_request()
85 id_, params = build_data(
86 id_, params = build_data(
86 self.apikey_regular, 'close_pull_request',
87 self.apikey_regular, 'close_pull_request',
87 repoid=pull_request.target_repo.repo_name,
88 repoid=pull_request.target_repo.repo_name,
88 pullrequestid=pull_request.pull_request_id,
89 pullrequestid=pull_request.pull_request_id,
89 userid=TEST_USER_ADMIN_LOGIN)
90 userid=TEST_USER_ADMIN_LOGIN)
90 response = api_call(self.app, params)
91 response = api_call(self.app, params)
91
92
92 expected = 'userid is not the same as your user'
93 expected = 'userid is not the same as your user'
93 assert_error(id_, expected, given=response.body)
94 assert_error(id_, expected, given=response.body)
94
95
95 @pytest.mark.backends("git", "hg")
96 @pytest.mark.backends("git", "hg")
96 def test_api_close_pull_request_no_perms_to_close(
97 def test_api_close_pull_request_no_perms_to_close(
97 self, user_util, pr_util):
98 self, user_util, pr_util):
98 user = user_util.create_user()
99 user = user_util.create_user()
99 pull_request = pr_util.create_pull_request()
100 pull_request = pr_util.create_pull_request()
100
101
101 id_, params = build_data(
102 id_, params = build_data(
102 user.api_key, 'close_pull_request',
103 user.api_key, 'close_pull_request',
103 repoid=pull_request.target_repo.repo_name,
104 repoid=pull_request.target_repo.repo_name,
104 pullrequestid=pull_request.pull_request_id,)
105 pullrequestid=pull_request.pull_request_id,)
105 response = api_call(self.app, params)
106 response = api_call(self.app, params)
106
107
107 expected = ('pull request `%s` close failed, '
108 expected = ('pull request `%s` close failed, '
108 'no permission to close.') % pull_request.pull_request_id
109 'no permission to close.') % pull_request.pull_request_id
109
110
110 response_json = response.json['error']
111 response_json = response.json['error']
111 assert response_json == expected
112 assert response_json == expected
@@ -1,73 +1,72 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.repo import RepoModel
24 from rhodecode.model.repo import RepoModel
25 from rhodecode.api.tests.utils import (
25 from rhodecode.api.tests.utils import (
26 build_data, api_call, assert_error, assert_ok, crash)
26 build_data, api_call, assert_error, assert_ok, crash)
27
27
28
28
29 @pytest.mark.usefixtures("testuser_api", "app")
29 @pytest.mark.usefixtures("testuser_api", "app")
30 class TestApiDeleteRepo(object):
30 class TestApiDeleteRepo(object):
31 def test_api_delete_repo(self, backend):
31 def test_api_delete_repo(self, backend):
32 repo = backend.create_repo()
32 repo = backend.create_repo()
33
33
34 id_, params = build_data(
34 id_, params = build_data(
35 self.apikey, 'delete_repo', repoid=repo.repo_name, )
35 self.apikey, 'delete_repo', repoid=repo.repo_name, )
36 response = api_call(self.app, params)
36 response = api_call(self.app, params)
37
37
38 expected = {
38 expected = {
39 'msg': 'Deleted repository `%s`' % (repo.repo_name,),
39 'msg': 'Deleted repository `%s`' % (repo.repo_name,),
40 'success': True
40 'success': True
41 }
41 }
42 assert_ok(id_, expected, given=response.body)
42 assert_ok(id_, expected, given=response.body)
43
43
44 def test_api_delete_repo_by_non_admin(self, backend, user_regular):
44 def test_api_delete_repo_by_non_admin(self, backend, user_regular):
45 repo = backend.create_repo(cur_user=user_regular.username)
45 repo = backend.create_repo(cur_user=user_regular.username)
46 id_, params = build_data(
46 id_, params = build_data(
47 user_regular.api_key, 'delete_repo', repoid=repo.repo_name, )
47 user_regular.api_key, 'delete_repo', repoid=repo.repo_name, )
48 response = api_call(self.app, params)
48 response = api_call(self.app, params)
49
49
50 expected = {
50 expected = {
51 'msg': 'Deleted repository `%s`' % (repo.repo_name,),
51 'msg': 'Deleted repository `%s`' % (repo.repo_name,),
52 'success': True
52 'success': True
53 }
53 }
54 assert_ok(id_, expected, given=response.body)
54 assert_ok(id_, expected, given=response.body)
55
55
56 def test_api_delete_repo_by_non_admin_no_permission(
56 def test_api_delete_repo_by_non_admin_no_permission(self, backend):
57 self, backend, user_regular):
58 repo = backend.create_repo()
57 repo = backend.create_repo()
59 id_, params = build_data(
58 id_, params = build_data(
60 user_regular.api_key, 'delete_repo', repoid=repo.repo_name, )
59 self.apikey_regular, 'delete_repo', repoid=repo.repo_name, )
61 response = api_call(self.app, params)
60 response = api_call(self.app, params)
62 expected = 'repository `%s` does not exist' % (repo.repo_name)
61 expected = 'repository `%s` does not exist' % (repo.repo_name)
63 assert_error(id_, expected, given=response.body)
62 assert_error(id_, expected, given=response.body)
64
63
65 def test_api_delete_repo_exception_occurred(self, backend):
64 def test_api_delete_repo_exception_occurred(self, backend):
66 repo = backend.create_repo()
65 repo = backend.create_repo()
67 id_, params = build_data(
66 id_, params = build_data(
68 self.apikey, 'delete_repo', repoid=repo.repo_name, )
67 self.apikey, 'delete_repo', repoid=repo.repo_name, )
69 with mock.patch.object(RepoModel, 'delete', crash):
68 with mock.patch.object(RepoModel, 'delete', crash):
70 response = api_call(self.app, params)
69 response = api_call(self.app, params)
71 expected = 'failed to delete repository `%s`' % (
70 expected = 'failed to delete repository `%s`' % (
72 repo.repo_name,)
71 repo.repo_name,)
73 assert_error(id_, expected, given=response.body)
72 assert_error(id_, expected, given=response.body)
@@ -1,108 +1,110 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.user import UserModel
24 from rhodecode.model.user import UserModel
25 from rhodecode.model.user_group import UserGroupModel
25 from rhodecode.model.user_group import UserGroupModel
26 from rhodecode.tests import TEST_USER_REGULAR_LOGIN
26 from rhodecode.tests import TEST_USER_ADMIN_EMAIL
27 from rhodecode.api.tests.utils import (
27 from rhodecode.api.tests.utils import (
28 build_data, api_call, assert_error, assert_ok, crash, jsonify)
28 build_data, api_call, assert_error, assert_ok, crash, jsonify)
29
29
30
30
31 @pytest.mark.usefixtures("testuser_api", "app")
31 @pytest.mark.usefixtures("testuser_api", "app")
32 class TestUpdateUserGroup(object):
32 class TestUpdateUserGroup(object):
33 @pytest.mark.parametrize("changing_attr, updates", [
33 @pytest.mark.parametrize("changing_attr, updates", [
34 ('group_name', {'group_name': 'new_group_name'}),
34 ('group_name', {'group_name': 'new_group_name'}),
35 ('group_name', {'group_name': 'test_group_for_update'}),
35 ('group_name', {'group_name': 'test_group_for_update'}),
36 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
36 # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
37 ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}),
37 ('active', {'active': False}),
38 ('active', {'active': False}),
38 ('active', {'active': True})
39 ('active', {'active': True})
39 ])
40 ])
40 def test_api_update_user_group(self, changing_attr, updates, user_util):
41 def test_api_update_user_group(self, changing_attr, updates, user_util):
41 user_group = user_util.create_user_group()
42 user_group = user_util.create_user_group()
42 group_name = user_group.users_group_name
43 group_name = user_group.users_group_name
43 expected_api_data = user_group.get_api_data()
44 expected_api_data = user_group.get_api_data()
44 expected_api_data.update(updates)
45 expected_api_data.update(updates)
45
46
46 id_, params = build_data(
47 id_, params = build_data(
47 self.apikey, 'update_user_group', usergroupid=group_name,
48 self.apikey, 'update_user_group', usergroupid=group_name,
48 **updates)
49 **updates)
49 response = api_call(self.app, params)
50 response = api_call(self.app, params)
50
51
51 expected = {
52 expected = {
52 'msg': 'updated user group ID:%s %s' % (
53 'msg': 'updated user group ID:%s %s' % (
53 user_group.users_group_id, user_group.users_group_name),
54 user_group.users_group_id, user_group.users_group_name),
54 'user_group': jsonify(expected_api_data)
55 'user_group': jsonify(expected_api_data)
55 }
56 }
56 assert_ok(id_, expected, given=response.body)
57 assert_ok(id_, expected, given=response.body)
57
58
58 @pytest.mark.parametrize("changing_attr, updates", [
59 @pytest.mark.parametrize("changing_attr, updates", [
59 # TODO: mikhail: decide if we need to test against the commented params
60 # TODO: mikhail: decide if we need to test against the commented params
60 # ('group_name', {'group_name': 'new_group_name'}),
61 # ('group_name', {'group_name': 'new_group_name'}),
61 # ('group_name', {'group_name': 'test_group_for_update'}),
62 # ('group_name', {'group_name': 'test_group_for_update'}),
62 ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
63 # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
64 ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}),
63 ('active', {'active': False}),
65 ('active', {'active': False}),
64 ('active', {'active': True})
66 ('active', {'active': True})
65 ])
67 ])
66 def test_api_update_user_group_regular_user(
68 def test_api_update_user_group_regular_user(
67 self, changing_attr, updates, user_util):
69 self, changing_attr, updates, user_util):
68 user_group = user_util.create_user_group()
70 user_group = user_util.create_user_group()
69 group_name = user_group.users_group_name
71 group_name = user_group.users_group_name
70 expected_api_data = user_group.get_api_data()
72 expected_api_data = user_group.get_api_data()
71 expected_api_data.update(updates)
73 expected_api_data.update(updates)
72
74
73
75
74 # grant permission to this user
76 # grant permission to this user
75 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
77 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
76
78
77 user_util.grant_user_permission_to_user_group(
79 user_util.grant_user_permission_to_user_group(
78 user_group, user, 'usergroup.admin')
80 user_group, user, 'usergroup.admin')
79 id_, params = build_data(
81 id_, params = build_data(
80 self.apikey_regular, 'update_user_group',
82 self.apikey_regular, 'update_user_group',
81 usergroupid=group_name, **updates)
83 usergroupid=group_name, **updates)
82 response = api_call(self.app, params)
84 response = api_call(self.app, params)
83 expected = {
85 expected = {
84 'msg': 'updated user group ID:%s %s' % (
86 'msg': 'updated user group ID:%s %s' % (
85 user_group.users_group_id, user_group.users_group_name),
87 user_group.users_group_id, user_group.users_group_name),
86 'user_group': jsonify(expected_api_data)
88 'user_group': jsonify(expected_api_data)
87 }
89 }
88 assert_ok(id_, expected, given=response.body)
90 assert_ok(id_, expected, given=response.body)
89
91
90 def test_api_update_user_group_regular_user_no_permission(self, user_util):
92 def test_api_update_user_group_regular_user_no_permission(self, user_util):
91 user_group = user_util.create_user_group()
93 user_group = user_util.create_user_group()
92 group_name = user_group.users_group_name
94 group_name = user_group.users_group_name
93 id_, params = build_data(
95 id_, params = build_data(
94 self.apikey_regular, 'update_user_group', usergroupid=group_name)
96 self.apikey_regular, 'update_user_group', usergroupid=group_name)
95 response = api_call(self.app, params)
97 response = api_call(self.app, params)
96
98
97 expected = 'user group `%s` does not exist' % (group_name)
99 expected = 'user group `%s` does not exist' % (group_name)
98 assert_error(id_, expected, given=response.body)
100 assert_error(id_, expected, given=response.body)
99
101
100 @mock.patch.object(UserGroupModel, 'update', crash)
102 @mock.patch.object(UserGroupModel, 'update', crash)
101 def test_api_update_user_group_exception_occurred(self, user_util):
103 def test_api_update_user_group_exception_occurred(self, user_util):
102 user_group = user_util.create_user_group()
104 user_group = user_util.create_user_group()
103 group_name = user_group.users_group_name
105 group_name = user_group.users_group_name
104 id_, params = build_data(
106 id_, params = build_data(
105 self.apikey, 'update_user_group', usergroupid=group_name)
107 self.apikey, 'update_user_group', usergroupid=group_name)
106 response = api_call(self.app, params)
108 response = api_call(self.app, params)
107 expected = 'failed to update user group `%s`' % (group_name,)
109 expected = 'failed to update user group `%s`' % (group_name,)
108 assert_error(id_, expected, given=response.body)
110 assert_error(id_, expected, given=response.body)
@@ -1,268 +1,294 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import pytest
22 import pytest
23 from mock import Mock, patch
23 from mock import Mock, patch
24
24
25 from rhodecode.api import utils
25 from rhodecode.api import utils
26 from rhodecode.api import JSONRPCError
26 from rhodecode.api import JSONRPCError
27 from rhodecode.lib.vcs.exceptions import RepositoryError
27 from rhodecode.lib.vcs.exceptions import RepositoryError
28
28
29
29
30 class TestGetCommitOrError(object):
30 class TestGetCommitOrError(object):
31 def setup(self):
31 def setup(self):
32 self.commit_hash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
32 self.commit_hash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
33
33
34 @pytest.mark.parametrize("ref", ['ref', '12345', 'a:b:c:d', 'branch:name'])
34 @pytest.mark.parametrize("ref", ['ref', '12345', 'a:b:c:d', 'branch:name'])
35 def test_ref_cannot_be_parsed(self, ref):
35 def test_ref_cannot_be_parsed(self, ref):
36 repo = Mock()
36 repo = Mock()
37 with pytest.raises(JSONRPCError) as excinfo:
37 with pytest.raises(JSONRPCError) as excinfo:
38 utils.get_commit_or_error(ref, repo)
38 utils.get_commit_or_error(ref, repo)
39 expected_message = (
39 expected_message = (
40 'Ref `{ref}` given in a wrong format. Please check the API'
40 'Ref `{ref}` given in a wrong format. Please check the API'
41 ' documentation for more details'.format(ref=ref)
41 ' documentation for more details'.format(ref=ref)
42 )
42 )
43 assert excinfo.value.message == expected_message
43 assert excinfo.value.message == expected_message
44
44
45 def test_success_with_hash_specified(self):
45 def test_success_with_hash_specified(self):
46 repo = Mock()
46 repo = Mock()
47 ref_type = 'branch'
47 ref_type = 'branch'
48 ref = '{}:master:{}'.format(ref_type, self.commit_hash)
48 ref = '{}:master:{}'.format(ref_type, self.commit_hash)
49
49
50 with patch('rhodecode.api.utils.get_commit_from_ref_name') as get_commit:
50 with patch('rhodecode.api.utils.get_commit_from_ref_name') as get_commit:
51 result = utils.get_commit_or_error(ref, repo)
51 result = utils.get_commit_or_error(ref, repo)
52 get_commit.assert_called_once_with(
52 get_commit.assert_called_once_with(
53 repo, self.commit_hash)
53 repo, self.commit_hash)
54 assert result == get_commit()
54 assert result == get_commit()
55
55
56 def test_raises_an_error_when_commit_not_found(self):
56 def test_raises_an_error_when_commit_not_found(self):
57 repo = Mock()
57 repo = Mock()
58 ref = 'branch:master:{}'.format(self.commit_hash)
58 ref = 'branch:master:{}'.format(self.commit_hash)
59
59
60 with patch('rhodecode.api.utils.get_commit_from_ref_name') as get_commit:
60 with patch('rhodecode.api.utils.get_commit_from_ref_name') as get_commit:
61 get_commit.side_effect = RepositoryError('Commit not found')
61 get_commit.side_effect = RepositoryError('Commit not found')
62 with pytest.raises(JSONRPCError) as excinfo:
62 with pytest.raises(JSONRPCError) as excinfo:
63 utils.get_commit_or_error(ref, repo)
63 utils.get_commit_or_error(ref, repo)
64 expected_message = 'Ref `{}` does not exist'.format(ref)
64 expected_message = 'Ref `{}` does not exist'.format(ref)
65 assert excinfo.value.message == expected_message
65 assert excinfo.value.message == expected_message
66
66
67
67
68 class TestResolveRefOrError(object):
68 class TestResolveRefOrError(object):
69 def setup(self):
69 def setup(self):
70 self.commit_hash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
70 self.commit_hash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
71
71
72 def test_success_with_no_hash_specified(self):
72 def test_success_with_no_hash_specified(self):
73 repo = Mock()
73 repo = Mock()
74 ref_type = 'branch'
74 ref_type = 'branch'
75 ref_name = 'master'
75 ref_name = 'master'
76 ref = '{}:{}'.format(ref_type, ref_name)
76 ref = '{}:{}'.format(ref_type, ref_name)
77
77
78 with patch('rhodecode.api.utils._get_ref_hash') \
78 with patch('rhodecode.api.utils._get_ref_hash') \
79 as _get_ref_hash:
79 as _get_ref_hash:
80 _get_ref_hash.return_value = self.commit_hash
80 _get_ref_hash.return_value = self.commit_hash
81 result = utils.resolve_ref_or_error(ref, repo)
81 result = utils.resolve_ref_or_error(ref, repo)
82 _get_ref_hash.assert_called_once_with(repo, ref_type, ref_name)
82 _get_ref_hash.assert_called_once_with(repo, ref_type, ref_name)
83 assert result == '{}:{}'.format(ref, self.commit_hash)
83 assert result == '{}:{}'.format(ref, self.commit_hash)
84
84
85 def test_non_supported_refs(self):
85 def test_non_supported_refs(self):
86 repo = Mock()
86 repo = Mock()
87 ref = 'ancestor:ref'
87 ref = 'ancestor:ref'
88 with pytest.raises(JSONRPCError) as excinfo:
88 with pytest.raises(JSONRPCError) as excinfo:
89 utils.resolve_ref_or_error(ref, repo)
89 utils.resolve_ref_or_error(ref, repo)
90 expected_message = 'The specified ancestor `ref` does not exist'
90 expected_message = 'The specified ancestor `ref` does not exist'
91 assert excinfo.value.message == expected_message
91 assert excinfo.value.message == expected_message
92
92
93 def test_branch_is_not_found(self):
93 def test_branch_is_not_found(self):
94 repo = Mock()
94 repo = Mock()
95 ref = 'branch:non-existing-one'
95 ref = 'branch:non-existing-one'
96 with patch('rhodecode.api.utils._get_ref_hash')\
96 with patch('rhodecode.api.utils._get_ref_hash')\
97 as _get_ref_hash:
97 as _get_ref_hash:
98 _get_ref_hash.side_effect = KeyError()
98 _get_ref_hash.side_effect = KeyError()
99 with pytest.raises(JSONRPCError) as excinfo:
99 with pytest.raises(JSONRPCError) as excinfo:
100 utils.resolve_ref_or_error(ref, repo)
100 utils.resolve_ref_or_error(ref, repo)
101 expected_message = (
101 expected_message = (
102 'The specified branch `non-existing-one` does not exist')
102 'The specified branch `non-existing-one` does not exist')
103 assert excinfo.value.message == expected_message
103 assert excinfo.value.message == expected_message
104
104
105 def test_bookmark_is_not_found(self):
105 def test_bookmark_is_not_found(self):
106 repo = Mock()
106 repo = Mock()
107 ref = 'bookmark:non-existing-one'
107 ref = 'bookmark:non-existing-one'
108 with patch('rhodecode.api.utils._get_ref_hash')\
108 with patch('rhodecode.api.utils._get_ref_hash')\
109 as _get_ref_hash:
109 as _get_ref_hash:
110 _get_ref_hash.side_effect = KeyError()
110 _get_ref_hash.side_effect = KeyError()
111 with pytest.raises(JSONRPCError) as excinfo:
111 with pytest.raises(JSONRPCError) as excinfo:
112 utils.resolve_ref_or_error(ref, repo)
112 utils.resolve_ref_or_error(ref, repo)
113 expected_message = (
113 expected_message = (
114 'The specified bookmark `non-existing-one` does not exist')
114 'The specified bookmark `non-existing-one` does not exist')
115 assert excinfo.value.message == expected_message
115 assert excinfo.value.message == expected_message
116
116
117 @pytest.mark.parametrize("ref", ['ref', '12345', 'a:b:c:d'])
117 @pytest.mark.parametrize("ref", ['ref', '12345', 'a:b:c:d'])
118 def test_ref_cannot_be_parsed(self, ref):
118 def test_ref_cannot_be_parsed(self, ref):
119 repo = Mock()
119 repo = Mock()
120 with pytest.raises(JSONRPCError) as excinfo:
120 with pytest.raises(JSONRPCError) as excinfo:
121 utils.resolve_ref_or_error(ref, repo)
121 utils.resolve_ref_or_error(ref, repo)
122 expected_message = (
122 expected_message = (
123 'Ref `{ref}` given in a wrong format. Please check the API'
123 'Ref `{ref}` given in a wrong format. Please check the API'
124 ' documentation for more details'.format(ref=ref)
124 ' documentation for more details'.format(ref=ref)
125 )
125 )
126 assert excinfo.value.message == expected_message
126 assert excinfo.value.message == expected_message
127
127
128
128
129 class TestGetRefHash(object):
129 class TestGetRefHash(object):
130 def setup(self):
130 def setup(self):
131 self.commit_hash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
131 self.commit_hash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
132 self.bookmark_name = 'test-bookmark'
132 self.bookmark_name = 'test-bookmark'
133
133
134 @pytest.mark.parametrize("alias, branch_name", [
134 @pytest.mark.parametrize("alias, branch_name", [
135 ("git", "master"),
135 ("git", "master"),
136 ("hg", "default")
136 ("hg", "default")
137 ])
137 ])
138 def test_returns_hash_by_branch_name(self, alias, branch_name):
138 def test_returns_hash_by_branch_name(self, alias, branch_name):
139 with patch('rhodecode.model.db.Repository') as repo:
139 with patch('rhodecode.model.db.Repository') as repo:
140 repo.scm_instance().alias = alias
140 repo.scm_instance().alias = alias
141 repo.scm_instance().branches = {branch_name: self.commit_hash}
141 repo.scm_instance().branches = {branch_name: self.commit_hash}
142 result_hash = utils._get_ref_hash(repo, 'branch', branch_name)
142 result_hash = utils._get_ref_hash(repo, 'branch', branch_name)
143 assert result_hash == self.commit_hash
143 assert result_hash == self.commit_hash
144
144
145 @pytest.mark.parametrize("alias, branch_name", [
145 @pytest.mark.parametrize("alias, branch_name", [
146 ("git", "master"),
146 ("git", "master"),
147 ("hg", "default")
147 ("hg", "default")
148 ])
148 ])
149 def test_raises_error_when_branch_is_not_found(self, alias, branch_name):
149 def test_raises_error_when_branch_is_not_found(self, alias, branch_name):
150 with patch('rhodecode.model.db.Repository') as repo:
150 with patch('rhodecode.model.db.Repository') as repo:
151 repo.scm_instance().alias = alias
151 repo.scm_instance().alias = alias
152 repo.scm_instance().branches = {}
152 repo.scm_instance().branches = {}
153 with pytest.raises(KeyError):
153 with pytest.raises(KeyError):
154 utils._get_ref_hash(repo, 'branch', branch_name)
154 utils._get_ref_hash(repo, 'branch', branch_name)
155
155
156 def test_returns_hash_when_bookmark_is_specified_for_hg(self):
156 def test_returns_hash_when_bookmark_is_specified_for_hg(self):
157 with patch('rhodecode.model.db.Repository') as repo:
157 with patch('rhodecode.model.db.Repository') as repo:
158 repo.scm_instance().alias = 'hg'
158 repo.scm_instance().alias = 'hg'
159 repo.scm_instance().bookmarks = {
159 repo.scm_instance().bookmarks = {
160 self.bookmark_name: self.commit_hash}
160 self.bookmark_name: self.commit_hash}
161 result_hash = utils._get_ref_hash(
161 result_hash = utils._get_ref_hash(
162 repo, 'bookmark', self.bookmark_name)
162 repo, 'bookmark', self.bookmark_name)
163 assert result_hash == self.commit_hash
163 assert result_hash == self.commit_hash
164
164
165 def test_raises_error_when_bookmark_is_not_found_in_hg_repo(self):
165 def test_raises_error_when_bookmark_is_not_found_in_hg_repo(self):
166 with patch('rhodecode.model.db.Repository') as repo:
166 with patch('rhodecode.model.db.Repository') as repo:
167 repo.scm_instance().alias = 'hg'
167 repo.scm_instance().alias = 'hg'
168 repo.scm_instance().bookmarks = {}
168 repo.scm_instance().bookmarks = {}
169 with pytest.raises(KeyError):
169 with pytest.raises(KeyError):
170 utils._get_ref_hash(repo, 'bookmark', self.bookmark_name)
170 utils._get_ref_hash(repo, 'bookmark', self.bookmark_name)
171
171
172 def test_raises_error_when_bookmark_is_specified_for_git(self):
172 def test_raises_error_when_bookmark_is_specified_for_git(self):
173 with patch('rhodecode.model.db.Repository') as repo:
173 with patch('rhodecode.model.db.Repository') as repo:
174 repo.scm_instance().alias = 'git'
174 repo.scm_instance().alias = 'git'
175 repo.scm_instance().bookmarks = {
175 repo.scm_instance().bookmarks = {
176 self.bookmark_name: self.commit_hash}
176 self.bookmark_name: self.commit_hash}
177 with pytest.raises(ValueError):
177 with pytest.raises(ValueError):
178 utils._get_ref_hash(repo, 'bookmark', self.bookmark_name)
178 utils._get_ref_hash(repo, 'bookmark', self.bookmark_name)
179
179
180
180
181 class TestUserByNameOrError(object):
181 class TestUserByNameOrError(object):
182 def test_user_found_by_id(self):
182 def test_user_found_by_id(self):
183 fake_user = Mock(id=123)
183 fake_user = Mock(id=123)
184
185 patcher = patch('rhodecode.model.user.UserModel.get_user')
186 with patcher as get_user:
187 get_user.return_value = fake_user
188
189 patcher = patch('rhodecode.model.user.UserModel.get_by_username')
190 with patcher as get_by_username:
191 result = utils.get_user_or_error(123)
192 assert result == fake_user
193
194 def test_user_not_found_by_id_as_str(self):
195 fake_user = Mock(id=123)
196
184 patcher = patch('rhodecode.model.user.UserModel.get_user')
197 patcher = patch('rhodecode.model.user.UserModel.get_user')
185 with patcher as get_user:
198 with patcher as get_user:
186 get_user.return_value = fake_user
199 get_user.return_value = fake_user
187 result = utils.get_user_or_error('123')
200 patcher = patch('rhodecode.model.user.UserModel.get_by_username')
188 assert result == fake_user
201 with patcher as get_by_username:
202 get_by_username.return_value = None
203
204 with pytest.raises(JSONRPCError):
205 utils.get_user_or_error('123')
189
206
190 def test_user_found_by_name(self):
207 def test_user_found_by_name(self):
191 fake_user = Mock(id=123)
208 fake_user = Mock(id=123)
192 patcher = patch('rhodecode.model.user.UserModel.get_by_username')
209
193 with patcher as get_by_username:
210 patcher = patch('rhodecode.model.user.UserModel.get_user')
194 get_by_username.return_value = fake_user
211 with patcher as get_user:
195 result = utils.get_user_or_error('test')
212 get_user.return_value = None
196 assert result == fake_user
213
214 patcher = patch('rhodecode.model.user.UserModel.get_by_username')
215 with patcher as get_by_username:
216 get_by_username.return_value = fake_user
217
218 result = utils.get_user_or_error('test')
219 assert result == fake_user
197
220
198 def test_user_not_found_by_id(self):
221 def test_user_not_found_by_id(self):
199 patcher = patch('rhodecode.model.user.UserModel.get_user')
222 patcher = patch('rhodecode.model.user.UserModel.get_user')
200 with patcher as get_user:
223 with patcher as get_user:
201 get_user.return_value = None
224 get_user.return_value = None
202 with pytest.raises(JSONRPCError) as excinfo:
225 patcher = patch('rhodecode.model.user.UserModel.get_by_username')
203 utils.get_user_or_error('123')
226 with patcher as get_by_username:
227 get_by_username.return_value = None
204
228
205 expected_message = 'user `123` does not exist'
229 with pytest.raises(JSONRPCError) as excinfo:
206 assert excinfo.value.message == expected_message
230 utils.get_user_or_error(123)
231
232 expected_message = 'user `123` does not exist'
233 assert excinfo.value.message == expected_message
207
234
208 def test_user_not_found_by_name(self):
235 def test_user_not_found_by_name(self):
209 patcher = patch('rhodecode.model.user.UserModel.get_by_username')
236 patcher = patch('rhodecode.model.user.UserModel.get_by_username')
210 with patcher as get_by_username:
237 with patcher as get_by_username:
211 get_by_username.return_value = None
238 get_by_username.return_value = None
212 with pytest.raises(JSONRPCError) as excinfo:
239 with pytest.raises(JSONRPCError) as excinfo:
213 utils.get_user_or_error('test')
240 utils.get_user_or_error('test')
214
241
215 expected_message = 'user `test` does not exist'
242 expected_message = 'user `test` does not exist'
216 assert excinfo.value.message == expected_message
243 assert excinfo.value.message == expected_message
217
244
218
245
219 class TestGetCommitDict:
246 class TestGetCommitDict(object):
220
221 @pytest.mark.parametrize('filename, expected', [
247 @pytest.mark.parametrize('filename, expected', [
222 (b'sp\xc3\xa4cial', u'sp\xe4cial'),
248 (b'sp\xc3\xa4cial', u'sp\xe4cial'),
223 (b'sp\xa4cial', u'sp\ufffdcial'),
249 (b'sp\xa4cial', u'sp\ufffdcial'),
224 ])
250 ])
225 def test_decodes_filenames_to_unicode(self, filename, expected):
251 def test_decodes_filenames_to_unicode(self, filename, expected):
226 result = utils._get_commit_dict(filename=filename, op='A')
252 result = utils._get_commit_dict(filename=filename, op='A')
227 assert result['filename'] == expected
253 assert result['filename'] == expected
228
254
229
255
230 class TestRepoAccess(object):
256 class TestRepoAccess(object):
231 def setup_method(self, method):
257 def setup_method(self, method):
232
258
233 self.admin_perm_patch = patch(
259 self.admin_perm_patch = patch(
234 'rhodecode.api.utils.HasPermissionAnyApi')
260 'rhodecode.api.utils.HasPermissionAnyApi')
235 self.repo_perm_patch = patch(
261 self.repo_perm_patch = patch(
236 'rhodecode.api.utils.HasRepoPermissionAnyApi')
262 'rhodecode.api.utils.HasRepoPermissionAnyApi')
237
263
238 def test_has_superadmin_permission_checks_for_admin(self):
264 def test_has_superadmin_permission_checks_for_admin(self):
239 admin_mock = Mock()
265 admin_mock = Mock()
240 with self.admin_perm_patch as amock:
266 with self.admin_perm_patch as amock:
241 amock.return_value = admin_mock
267 amock.return_value = admin_mock
242 assert utils.has_superadmin_permission('fake_user')
268 assert utils.has_superadmin_permission('fake_user')
243 amock.assert_called_once_with('hg.admin')
269 amock.assert_called_once_with('hg.admin')
244
270
245 admin_mock.assert_called_once_with(user='fake_user')
271 admin_mock.assert_called_once_with(user='fake_user')
246
272
247 def test_has_repo_permissions_checks_for_repo_access(self):
273 def test_has_repo_permissions_checks_for_repo_access(self):
248 repo_mock = Mock()
274 repo_mock = Mock()
249 fake_repo = Mock()
275 fake_repo = Mock()
250 with self.repo_perm_patch as rmock:
276 with self.repo_perm_patch as rmock:
251 rmock.return_value = repo_mock
277 rmock.return_value = repo_mock
252 assert utils.validate_repo_permissions(
278 assert utils.validate_repo_permissions(
253 'fake_user', 'fake_repo_id', fake_repo,
279 'fake_user', 'fake_repo_id', fake_repo,
254 ['perm1', 'perm2'])
280 ['perm1', 'perm2'])
255 rmock.assert_called_once_with(*['perm1', 'perm2'])
281 rmock.assert_called_once_with(*['perm1', 'perm2'])
256
282
257 repo_mock.assert_called_once_with(
283 repo_mock.assert_called_once_with(
258 user='fake_user', repo_name=fake_repo.repo_name)
284 user='fake_user', repo_name=fake_repo.repo_name)
259
285
260 def test_has_repo_permissions_raises_not_found(self):
286 def test_has_repo_permissions_raises_not_found(self):
261 repo_mock = Mock(return_value=False)
287 repo_mock = Mock(return_value=False)
262 fake_repo = Mock()
288 fake_repo = Mock()
263 with self.repo_perm_patch as rmock:
289 with self.repo_perm_patch as rmock:
264 rmock.return_value = repo_mock
290 rmock.return_value = repo_mock
265 with pytest.raises(JSONRPCError) as excinfo:
291 with pytest.raises(JSONRPCError) as excinfo:
266 utils.validate_repo_permissions(
292 utils.validate_repo_permissions(
267 'fake_user', 'fake_repo_id', fake_repo, 'perms')
293 'fake_user', 'fake_repo_id', fake_repo, 'perms')
268 assert 'fake_repo_id' in excinfo
294 assert 'fake_repo_id' in excinfo
@@ -1,412 +1,442 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2017 RhodeCode GmbH
3 # Copyright (C) 2014-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 JSON RPC utils
22 JSON RPC utils
23 """
23 """
24
24
25 import collections
25 import collections
26 import logging
26 import logging
27
27
28 from rhodecode.api.exc import JSONRPCError
28 from rhodecode.api.exc import JSONRPCError
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 HasPermissionAnyApi, HasRepoPermissionAnyApi, HasRepoGroupPermissionAnyApi)
30 HasPermissionAnyApi, HasRepoPermissionAnyApi, HasRepoGroupPermissionAnyApi)
31 from rhodecode.lib.utils import safe_unicode
31 from rhodecode.lib.utils import safe_unicode
32 from rhodecode.lib.vcs.exceptions import RepositoryError
32 from rhodecode.lib.vcs.exceptions import RepositoryError
33 from rhodecode.controllers.utils import get_commit_from_ref_name
33 from rhodecode.controllers.utils import get_commit_from_ref_name
34 from rhodecode.lib.utils2 import str2bool
34 from rhodecode.lib.utils2 import str2bool
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class OAttr(object):
39 class OAttr(object):
40 """
40 """
41 Special Option that defines other attribute, and can default to them
41 Special Option that defines other attribute, and can default to them
42
42
43 Example::
43 Example::
44
44
45 def test(apiuser, userid=Optional(OAttr('apiuser')):
45 def test(apiuser, userid=Optional(OAttr('apiuser')):
46 user = Optional.extract(userid, evaluate_locals=local())
46 user = Optional.extract(userid, evaluate_locals=local())
47 #if we pass in userid, we get it, else it will default to apiuser
47 #if we pass in userid, we get it, else it will default to apiuser
48 #attribute
48 #attribute
49 """
49 """
50
50
51 def __init__(self, attr_name):
51 def __init__(self, attr_name):
52 self.attr_name = attr_name
52 self.attr_name = attr_name
53
53
54 def __repr__(self):
54 def __repr__(self):
55 return '<OptionalAttr:%s>' % self.attr_name
55 return '<OptionalAttr:%s>' % self.attr_name
56
56
57 def __call__(self):
57 def __call__(self):
58 return self
58 return self
59
59
60
60
61 class Optional(object):
61 class Optional(object):
62 """
62 """
63 Defines an optional parameter::
63 Defines an optional parameter::
64
64
65 param = param.getval() if isinstance(param, Optional) else param
65 param = param.getval() if isinstance(param, Optional) else param
66 param = param() if isinstance(param, Optional) else param
66 param = param() if isinstance(param, Optional) else param
67
67
68 is equivalent of::
68 is equivalent of::
69
69
70 param = Optional.extract(param)
70 param = Optional.extract(param)
71
71
72 """
72 """
73
73
74 def __init__(self, type_):
74 def __init__(self, type_):
75 self.type_ = type_
75 self.type_ = type_
76
76
77 def __repr__(self):
77 def __repr__(self):
78 return '<Optional:%s>' % self.type_.__repr__()
78 return '<Optional:%s>' % self.type_.__repr__()
79
79
80 def __call__(self):
80 def __call__(self):
81 return self.getval()
81 return self.getval()
82
82
83 def getval(self, evaluate_locals=None):
83 def getval(self, evaluate_locals=None):
84 """
84 """
85 returns value from this Optional instance
85 returns value from this Optional instance
86 """
86 """
87 if isinstance(self.type_, OAttr):
87 if isinstance(self.type_, OAttr):
88 param_name = self.type_.attr_name
88 param_name = self.type_.attr_name
89 if evaluate_locals:
89 if evaluate_locals:
90 return evaluate_locals[param_name]
90 return evaluate_locals[param_name]
91 # use params name
91 # use params name
92 return param_name
92 return param_name
93 return self.type_
93 return self.type_
94
94
95 @classmethod
95 @classmethod
96 def extract(cls, val, evaluate_locals=None, binary=None):
96 def extract(cls, val, evaluate_locals=None, binary=None):
97 """
97 """
98 Extracts value from Optional() instance
98 Extracts value from Optional() instance
99
99
100 :param val:
100 :param val:
101 :return: original value if it's not Optional instance else
101 :return: original value if it's not Optional instance else
102 value of instance
102 value of instance
103 """
103 """
104 if isinstance(val, cls):
104 if isinstance(val, cls):
105 val = val.getval(evaluate_locals)
105 val = val.getval(evaluate_locals)
106
106
107 if binary:
107 if binary:
108 val = str2bool(val)
108 val = str2bool(val)
109
109
110 return val
110 return val
111
111
112
112
113 def parse_args(cli_args, key_prefix=''):
113 def parse_args(cli_args, key_prefix=''):
114 from rhodecode.lib.utils2 import (escape_split)
114 from rhodecode.lib.utils2 import (escape_split)
115 kwargs = collections.defaultdict(dict)
115 kwargs = collections.defaultdict(dict)
116 for el in escape_split(cli_args, ','):
116 for el in escape_split(cli_args, ','):
117 kv = escape_split(el, '=', 1)
117 kv = escape_split(el, '=', 1)
118 if len(kv) == 2:
118 if len(kv) == 2:
119 k, v = kv
119 k, v = kv
120 kwargs[key_prefix + k] = v
120 kwargs[key_prefix + k] = v
121 return kwargs
121 return kwargs
122
122
123
123
124 def get_origin(obj):
124 def get_origin(obj):
125 """
125 """
126 Get origin of permission from object.
126 Get origin of permission from object.
127
127
128 :param obj:
128 :param obj:
129 """
129 """
130 origin = 'permission'
130 origin = 'permission'
131
131
132 if getattr(obj, 'owner_row', '') and getattr(obj, 'admin_row', ''):
132 if getattr(obj, 'owner_row', '') and getattr(obj, 'admin_row', ''):
133 # admin and owner case, maybe we should use dual string ?
133 # admin and owner case, maybe we should use dual string ?
134 origin = 'owner'
134 origin = 'owner'
135 elif getattr(obj, 'owner_row', ''):
135 elif getattr(obj, 'owner_row', ''):
136 origin = 'owner'
136 origin = 'owner'
137 elif getattr(obj, 'admin_row', ''):
137 elif getattr(obj, 'admin_row', ''):
138 origin = 'super-admin'
138 origin = 'super-admin'
139 return origin
139 return origin
140
140
141
141
142 def store_update(updates, attr, name):
142 def store_update(updates, attr, name):
143 """
143 """
144 Stores param in updates dict if it's not instance of Optional
144 Stores param in updates dict if it's not instance of Optional
145 allows easy updates of passed in params
145 allows easy updates of passed in params
146 """
146 """
147 if not isinstance(attr, Optional):
147 if not isinstance(attr, Optional):
148 updates[name] = attr
148 updates[name] = attr
149
149
150
150
151 def has_superadmin_permission(apiuser):
151 def has_superadmin_permission(apiuser):
152 """
152 """
153 Return True if apiuser is admin or return False
153 Return True if apiuser is admin or return False
154
154
155 :param apiuser:
155 :param apiuser:
156 """
156 """
157 if HasPermissionAnyApi('hg.admin')(user=apiuser):
157 if HasPermissionAnyApi('hg.admin')(user=apiuser):
158 return True
158 return True
159 return False
159 return False
160
160
161
161
162 def validate_repo_permissions(apiuser, repoid, repo, perms):
162 def validate_repo_permissions(apiuser, repoid, repo, perms):
163 """
163 """
164 Raise JsonRPCError if apiuser is not authorized or return True
164 Raise JsonRPCError if apiuser is not authorized or return True
165
165
166 :param apiuser:
166 :param apiuser:
167 :param repoid:
167 :param repoid:
168 :param repo:
168 :param repo:
169 :param perms:
169 :param perms:
170 """
170 """
171 if not HasRepoPermissionAnyApi(*perms)(
171 if not HasRepoPermissionAnyApi(*perms)(
172 user=apiuser, repo_name=repo.repo_name):
172 user=apiuser, repo_name=repo.repo_name):
173 raise JSONRPCError(
173 raise JSONRPCError(
174 'repository `%s` does not exist' % repoid)
174 'repository `%s` does not exist' % repoid)
175
175
176 return True
176 return True
177
177
178
178
179 def validate_repo_group_permissions(apiuser, repogroupid, repo_group, perms):
179 def validate_repo_group_permissions(apiuser, repogroupid, repo_group, perms):
180 """
180 """
181 Raise JsonRPCError if apiuser is not authorized or return True
181 Raise JsonRPCError if apiuser is not authorized or return True
182
182
183 :param apiuser:
183 :param apiuser:
184 :param repogroupid: just the id of repository group
184 :param repogroupid: just the id of repository group
185 :param repo_group: instance of repo_group
185 :param repo_group: instance of repo_group
186 :param perms:
186 :param perms:
187 """
187 """
188 if not HasRepoGroupPermissionAnyApi(*perms)(
188 if not HasRepoGroupPermissionAnyApi(*perms)(
189 user=apiuser, group_name=repo_group.group_name):
189 user=apiuser, group_name=repo_group.group_name):
190 raise JSONRPCError(
190 raise JSONRPCError(
191 'repository group `%s` does not exist' % repogroupid)
191 'repository group `%s` does not exist' % repogroupid)
192
192
193 return True
193 return True
194
194
195
195
196 def validate_set_owner_permissions(apiuser, owner):
196 def validate_set_owner_permissions(apiuser, owner):
197 if isinstance(owner, Optional):
197 if isinstance(owner, Optional):
198 owner = get_user_or_error(apiuser.user_id)
198 owner = get_user_or_error(apiuser.user_id)
199 else:
199 else:
200 if has_superadmin_permission(apiuser):
200 if has_superadmin_permission(apiuser):
201 owner = get_user_or_error(owner)
201 owner = get_user_or_error(owner)
202 else:
202 else:
203 # forbid setting owner for non-admins
203 # forbid setting owner for non-admins
204 raise JSONRPCError(
204 raise JSONRPCError(
205 'Only RhodeCode super-admin can specify `owner` param')
205 'Only RhodeCode super-admin can specify `owner` param')
206 return owner
206 return owner
207
207
208
208
209 def get_user_or_error(userid):
209 def get_user_or_error(userid):
210 """
210 """
211 Get user by id or name or return JsonRPCError if not found
211 Get user by id or name or return JsonRPCError if not found
212
212
213 :param userid:
213 :param userid:
214 """
214 """
215 from rhodecode.model.user import UserModel
215 from rhodecode.model.user import UserModel
216 user_model = UserModel()
216
217
217 user_model = UserModel()
218 if isinstance(userid, (int, long)):
218 try:
219 try:
219 user = user_model.get_user(int(userid))
220 user = user_model.get_user(userid)
220 except ValueError:
221 except ValueError:
222 user = None
223 else:
221 user = user_model.get_by_username(userid)
224 user = user_model.get_by_username(userid)
222
225
223 if user is None:
226 if user is None:
224 raise JSONRPCError("user `%s` does not exist" % (userid,))
227 raise JSONRPCError(
228 'user `%s` does not exist' % (userid,))
225 return user
229 return user
226
230
227
231
228 def get_repo_or_error(repoid):
232 def get_repo_or_error(repoid):
229 """
233 """
230 Get repo by id or name or return JsonRPCError if not found
234 Get repo by id or name or return JsonRPCError if not found
231
235
232 :param repoid:
236 :param repoid:
233 """
237 """
234 from rhodecode.model.repo import RepoModel
238 from rhodecode.model.repo import RepoModel
239 repo_model = RepoModel()
235
240
236 repo = RepoModel().get_repo(repoid)
241 if isinstance(repoid, (int, long)):
242 try:
243 repo = repo_model.get_repo(repoid)
244 except ValueError:
245 repo = None
246 else:
247 repo = repo_model.get_by_repo_name(repoid)
248
237 if repo is None:
249 if repo is None:
238 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
250 raise JSONRPCError(
251 'repository `%s` does not exist' % (repoid,))
239 return repo
252 return repo
240
253
241
254
242 def get_repo_group_or_error(repogroupid):
255 def get_repo_group_or_error(repogroupid):
243 """
256 """
244 Get repo group by id or name or return JsonRPCError if not found
257 Get repo group by id or name or return JsonRPCError if not found
245
258
246 :param repogroupid:
259 :param repogroupid:
247 """
260 """
248 from rhodecode.model.repo_group import RepoGroupModel
261 from rhodecode.model.repo_group import RepoGroupModel
262 repo_group_model = RepoGroupModel()
249
263
250 repo_group = RepoGroupModel()._get_repo_group(repogroupid)
264 if isinstance(repogroupid, (int, long)):
265 try:
266 repo_group = repo_group_model._get_repo_group(repogroupid)
267 except ValueError:
268 repo_group = None
269 else:
270 repo_group = repo_group_model.get_by_group_name(repogroupid)
271
251 if repo_group is None:
272 if repo_group is None:
252 raise JSONRPCError(
273 raise JSONRPCError(
253 'repository group `%s` does not exist' % (repogroupid,))
274 'repository group `%s` does not exist' % (repogroupid,))
254 return repo_group
275 return repo_group
255
276
256
277
257 def get_user_group_or_error(usergroupid):
278 def get_user_group_or_error(usergroupid):
258 """
279 """
259 Get user group by id or name or return JsonRPCError if not found
280 Get user group by id or name or return JsonRPCError if not found
260
281
261 :param usergroupid:
282 :param usergroupid:
262 """
283 """
263 from rhodecode.model.user_group import UserGroupModel
284 from rhodecode.model.user_group import UserGroupModel
285 user_group_model = UserGroupModel()
264
286
265 user_group = UserGroupModel().get_group(usergroupid)
287 if isinstance(usergroupid, (int, long)):
288 try:
289 user_group = user_group_model.get_group(usergroupid)
290 except ValueError:
291 user_group = None
292 else:
293 user_group = user_group_model.get_by_name(usergroupid)
294
266 if user_group is None:
295 if user_group is None:
267 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
296 raise JSONRPCError(
297 'user group `%s` does not exist' % (usergroupid,))
268 return user_group
298 return user_group
269
299
270
300
271 def get_perm_or_error(permid, prefix=None):
301 def get_perm_or_error(permid, prefix=None):
272 """
302 """
273 Get permission by id or name or return JsonRPCError if not found
303 Get permission by id or name or return JsonRPCError if not found
274
304
275 :param permid:
305 :param permid:
276 """
306 """
277 from rhodecode.model.permission import PermissionModel
307 from rhodecode.model.permission import PermissionModel
278
308
279 perm = PermissionModel.cls.get_by_key(permid)
309 perm = PermissionModel.cls.get_by_key(permid)
280 if perm is None:
310 if perm is None:
281 raise JSONRPCError('permission `%s` does not exist' % (permid,))
311 raise JSONRPCError('permission `%s` does not exist' % (permid,))
282 if prefix:
312 if prefix:
283 if not perm.permission_name.startswith(prefix):
313 if not perm.permission_name.startswith(prefix):
284 raise JSONRPCError('permission `%s` is invalid, '
314 raise JSONRPCError('permission `%s` is invalid, '
285 'should start with %s' % (permid, prefix))
315 'should start with %s' % (permid, prefix))
286 return perm
316 return perm
287
317
288
318
289 def get_gist_or_error(gistid):
319 def get_gist_or_error(gistid):
290 """
320 """
291 Get gist by id or gist_access_id or return JsonRPCError if not found
321 Get gist by id or gist_access_id or return JsonRPCError if not found
292
322
293 :param gistid:
323 :param gistid:
294 """
324 """
295 from rhodecode.model.gist import GistModel
325 from rhodecode.model.gist import GistModel
296
326
297 gist = GistModel.cls.get_by_access_id(gistid)
327 gist = GistModel.cls.get_by_access_id(gistid)
298 if gist is None:
328 if gist is None:
299 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
329 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
300 return gist
330 return gist
301
331
302
332
303 def get_pull_request_or_error(pullrequestid):
333 def get_pull_request_or_error(pullrequestid):
304 """
334 """
305 Get pull request by id or return JsonRPCError if not found
335 Get pull request by id or return JsonRPCError if not found
306
336
307 :param pullrequestid:
337 :param pullrequestid:
308 """
338 """
309 from rhodecode.model.pull_request import PullRequestModel
339 from rhodecode.model.pull_request import PullRequestModel
310
340
311 try:
341 try:
312 pull_request = PullRequestModel().get(int(pullrequestid))
342 pull_request = PullRequestModel().get(int(pullrequestid))
313 except ValueError:
343 except ValueError:
314 raise JSONRPCError('pullrequestid must be an integer')
344 raise JSONRPCError('pullrequestid must be an integer')
315 if not pull_request:
345 if not pull_request:
316 raise JSONRPCError('pull request `%s` does not exist' % (
346 raise JSONRPCError('pull request `%s` does not exist' % (
317 pullrequestid,))
347 pullrequestid,))
318 return pull_request
348 return pull_request
319
349
320
350
321 def build_commit_data(commit, detail_level):
351 def build_commit_data(commit, detail_level):
322 parsed_diff = []
352 parsed_diff = []
323 if detail_level == 'extended':
353 if detail_level == 'extended':
324 for f in commit.added:
354 for f in commit.added:
325 parsed_diff.append(_get_commit_dict(filename=f.path, op='A'))
355 parsed_diff.append(_get_commit_dict(filename=f.path, op='A'))
326 for f in commit.changed:
356 for f in commit.changed:
327 parsed_diff.append(_get_commit_dict(filename=f.path, op='M'))
357 parsed_diff.append(_get_commit_dict(filename=f.path, op='M'))
328 for f in commit.removed:
358 for f in commit.removed:
329 parsed_diff.append(_get_commit_dict(filename=f.path, op='D'))
359 parsed_diff.append(_get_commit_dict(filename=f.path, op='D'))
330
360
331 elif detail_level == 'full':
361 elif detail_level == 'full':
332 from rhodecode.lib.diffs import DiffProcessor
362 from rhodecode.lib.diffs import DiffProcessor
333 diff_processor = DiffProcessor(commit.diff())
363 diff_processor = DiffProcessor(commit.diff())
334 for dp in diff_processor.prepare():
364 for dp in diff_processor.prepare():
335 del dp['stats']['ops']
365 del dp['stats']['ops']
336 _stats = dp['stats']
366 _stats = dp['stats']
337 parsed_diff.append(_get_commit_dict(
367 parsed_diff.append(_get_commit_dict(
338 filename=dp['filename'], op=dp['operation'],
368 filename=dp['filename'], op=dp['operation'],
339 new_revision=dp['new_revision'],
369 new_revision=dp['new_revision'],
340 old_revision=dp['old_revision'],
370 old_revision=dp['old_revision'],
341 raw_diff=dp['raw_diff'], stats=_stats))
371 raw_diff=dp['raw_diff'], stats=_stats))
342
372
343 return parsed_diff
373 return parsed_diff
344
374
345
375
346 def get_commit_or_error(ref, repo):
376 def get_commit_or_error(ref, repo):
347 try:
377 try:
348 ref_type, _, ref_hash = ref.split(':')
378 ref_type, _, ref_hash = ref.split(':')
349 except ValueError:
379 except ValueError:
350 raise JSONRPCError(
380 raise JSONRPCError(
351 'Ref `{ref}` given in a wrong format. Please check the API'
381 'Ref `{ref}` given in a wrong format. Please check the API'
352 ' documentation for more details'.format(ref=ref))
382 ' documentation for more details'.format(ref=ref))
353 try:
383 try:
354 # TODO: dan: refactor this to use repo.scm_instance().get_commit()
384 # TODO: dan: refactor this to use repo.scm_instance().get_commit()
355 # once get_commit supports ref_types
385 # once get_commit supports ref_types
356 return get_commit_from_ref_name(repo, ref_hash)
386 return get_commit_from_ref_name(repo, ref_hash)
357 except RepositoryError:
387 except RepositoryError:
358 raise JSONRPCError('Ref `{ref}` does not exist'.format(ref=ref))
388 raise JSONRPCError('Ref `{ref}` does not exist'.format(ref=ref))
359
389
360
390
361 def resolve_ref_or_error(ref, repo):
391 def resolve_ref_or_error(ref, repo):
362 def _parse_ref(type_, name, hash_=None):
392 def _parse_ref(type_, name, hash_=None):
363 return type_, name, hash_
393 return type_, name, hash_
364
394
365 try:
395 try:
366 ref_type, ref_name, ref_hash = _parse_ref(*ref.split(':'))
396 ref_type, ref_name, ref_hash = _parse_ref(*ref.split(':'))
367 except TypeError:
397 except TypeError:
368 raise JSONRPCError(
398 raise JSONRPCError(
369 'Ref `{ref}` given in a wrong format. Please check the API'
399 'Ref `{ref}` given in a wrong format. Please check the API'
370 ' documentation for more details'.format(ref=ref))
400 ' documentation for more details'.format(ref=ref))
371
401
372 try:
402 try:
373 ref_hash = ref_hash or _get_ref_hash(repo, ref_type, ref_name)
403 ref_hash = ref_hash or _get_ref_hash(repo, ref_type, ref_name)
374 except (KeyError, ValueError):
404 except (KeyError, ValueError):
375 raise JSONRPCError(
405 raise JSONRPCError(
376 'The specified {type} `{name}` does not exist'.format(
406 'The specified {type} `{name}` does not exist'.format(
377 type=ref_type, name=ref_name))
407 type=ref_type, name=ref_name))
378
408
379 return ':'.join([ref_type, ref_name, ref_hash])
409 return ':'.join([ref_type, ref_name, ref_hash])
380
410
381
411
382 def _get_commit_dict(
412 def _get_commit_dict(
383 filename, op, new_revision=None, old_revision=None,
413 filename, op, new_revision=None, old_revision=None,
384 raw_diff=None, stats=None):
414 raw_diff=None, stats=None):
385 if stats is None:
415 if stats is None:
386 stats = {
416 stats = {
387 "added": None,
417 "added": None,
388 "binary": None,
418 "binary": None,
389 "deleted": None
419 "deleted": None
390 }
420 }
391 return {
421 return {
392 "filename": safe_unicode(filename),
422 "filename": safe_unicode(filename),
393 "op": op,
423 "op": op,
394
424
395 # extra details
425 # extra details
396 "new_revision": new_revision,
426 "new_revision": new_revision,
397 "old_revision": old_revision,
427 "old_revision": old_revision,
398
428
399 "raw_diff": raw_diff,
429 "raw_diff": raw_diff,
400 "stats": stats
430 "stats": stats
401 }
431 }
402
432
403
433
404 # TODO: mikhail: Think about moving this function to some library
434 # TODO: mikhail: Think about moving this function to some library
405 def _get_ref_hash(repo, type_, name):
435 def _get_ref_hash(repo, type_, name):
406 vcs_repo = repo.scm_instance()
436 vcs_repo = repo.scm_instance()
407 if type_ == 'branch' and vcs_repo.alias in ('hg', 'git'):
437 if type_ == 'branch' and vcs_repo.alias in ('hg', 'git'):
408 return vcs_repo.branches[name]
438 return vcs_repo.branches[name]
409 elif type_ == 'bookmark' and vcs_repo.alias == 'hg':
439 elif type_ == 'bookmark' and vcs_repo.alias == 'hg':
410 return vcs_repo.bookmarks[name]
440 return vcs_repo.bookmarks[name]
411 else:
441 else:
412 raise ValueError()
442 raise ValueError()
@@ -1,731 +1,725 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23
23
24 from rhodecode.api import jsonrpc_method, JSONRPCError
24 from rhodecode.api import jsonrpc_method, JSONRPCError
25 from rhodecode.api.utils import (
25 from rhodecode.api.utils import (
26 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
26 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
27 get_pull_request_or_error, get_commit_or_error, get_user_or_error,
27 get_pull_request_or_error, get_commit_or_error, get_user_or_error,
28 validate_repo_permissions, resolve_ref_or_error)
28 validate_repo_permissions, resolve_ref_or_error)
29 from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
29 from rhodecode.lib.auth import (HasRepoPermissionAnyApi)
30 from rhodecode.lib.base import vcs_operation_context
30 from rhodecode.lib.base import vcs_operation_context
31 from rhodecode.lib.utils2 import str2bool
31 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.model.changeset_status import ChangesetStatusModel
32 from rhodecode.model.changeset_status import ChangesetStatusModel
33 from rhodecode.model.comment import CommentsModel
33 from rhodecode.model.comment import CommentsModel
34 from rhodecode.model.db import Session, ChangesetStatus, ChangesetComment
34 from rhodecode.model.db import Session, ChangesetStatus, ChangesetComment
35 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
35 from rhodecode.model.pull_request import PullRequestModel, MergeCheck
36 from rhodecode.model.settings import SettingsModel
36 from rhodecode.model.settings import SettingsModel
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 @jsonrpc_method()
41 @jsonrpc_method()
42 def get_pull_request(request, apiuser, repoid, pullrequestid):
42 def get_pull_request(request, apiuser, repoid, pullrequestid):
43 """
43 """
44 Get a pull request based on the given ID.
44 Get a pull request based on the given ID.
45
45
46 :param apiuser: This is filled automatically from the |authtoken|.
46 :param apiuser: This is filled automatically from the |authtoken|.
47 :type apiuser: AuthUser
47 :type apiuser: AuthUser
48 :param repoid: Repository name or repository ID from where the pull
48 :param repoid: Repository name or repository ID from where the pull
49 request was opened.
49 request was opened.
50 :type repoid: str or int
50 :type repoid: str or int
51 :param pullrequestid: ID of the requested pull request.
51 :param pullrequestid: ID of the requested pull request.
52 :type pullrequestid: int
52 :type pullrequestid: int
53
53
54 Example output:
54 Example output:
55
55
56 .. code-block:: bash
56 .. code-block:: bash
57
57
58 "id": <id_given_in_input>,
58 "id": <id_given_in_input>,
59 "result":
59 "result":
60 {
60 {
61 "pull_request_id": "<pull_request_id>",
61 "pull_request_id": "<pull_request_id>",
62 "url": "<url>",
62 "url": "<url>",
63 "title": "<title>",
63 "title": "<title>",
64 "description": "<description>",
64 "description": "<description>",
65 "status" : "<status>",
65 "status" : "<status>",
66 "created_on": "<date_time_created>",
66 "created_on": "<date_time_created>",
67 "updated_on": "<date_time_updated>",
67 "updated_on": "<date_time_updated>",
68 "commit_ids": [
68 "commit_ids": [
69 ...
69 ...
70 "<commit_id>",
70 "<commit_id>",
71 "<commit_id>",
71 "<commit_id>",
72 ...
72 ...
73 ],
73 ],
74 "review_status": "<review_status>",
74 "review_status": "<review_status>",
75 "mergeable": {
75 "mergeable": {
76 "status": "<bool>",
76 "status": "<bool>",
77 "message": "<message>",
77 "message": "<message>",
78 },
78 },
79 "source": {
79 "source": {
80 "clone_url": "<clone_url>",
80 "clone_url": "<clone_url>",
81 "repository": "<repository_name>",
81 "repository": "<repository_name>",
82 "reference":
82 "reference":
83 {
83 {
84 "name": "<name>",
84 "name": "<name>",
85 "type": "<type>",
85 "type": "<type>",
86 "commit_id": "<commit_id>",
86 "commit_id": "<commit_id>",
87 }
87 }
88 },
88 },
89 "target": {
89 "target": {
90 "clone_url": "<clone_url>",
90 "clone_url": "<clone_url>",
91 "repository": "<repository_name>",
91 "repository": "<repository_name>",
92 "reference":
92 "reference":
93 {
93 {
94 "name": "<name>",
94 "name": "<name>",
95 "type": "<type>",
95 "type": "<type>",
96 "commit_id": "<commit_id>",
96 "commit_id": "<commit_id>",
97 }
97 }
98 },
98 },
99 "merge": {
99 "merge": {
100 "clone_url": "<clone_url>",
100 "clone_url": "<clone_url>",
101 "reference":
101 "reference":
102 {
102 {
103 "name": "<name>",
103 "name": "<name>",
104 "type": "<type>",
104 "type": "<type>",
105 "commit_id": "<commit_id>",
105 "commit_id": "<commit_id>",
106 }
106 }
107 },
107 },
108 "author": <user_obj>,
108 "author": <user_obj>,
109 "reviewers": [
109 "reviewers": [
110 ...
110 ...
111 {
111 {
112 "user": "<user_obj>",
112 "user": "<user_obj>",
113 "review_status": "<review_status>",
113 "review_status": "<review_status>",
114 }
114 }
115 ...
115 ...
116 ]
116 ]
117 },
117 },
118 "error": null
118 "error": null
119 """
119 """
120 get_repo_or_error(repoid)
120 get_repo_or_error(repoid)
121 pull_request = get_pull_request_or_error(pullrequestid)
121 pull_request = get_pull_request_or_error(pullrequestid)
122 if not PullRequestModel().check_user_read(
122 if not PullRequestModel().check_user_read(
123 pull_request, apiuser, api=True):
123 pull_request, apiuser, api=True):
124 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
124 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
125 data = pull_request.get_api_data()
125 data = pull_request.get_api_data()
126 return data
126 return data
127
127
128
128
129 @jsonrpc_method()
129 @jsonrpc_method()
130 def get_pull_requests(request, apiuser, repoid, status=Optional('new')):
130 def get_pull_requests(request, apiuser, repoid, status=Optional('new')):
131 """
131 """
132 Get all pull requests from the repository specified in `repoid`.
132 Get all pull requests from the repository specified in `repoid`.
133
133
134 :param apiuser: This is filled automatically from the |authtoken|.
134 :param apiuser: This is filled automatically from the |authtoken|.
135 :type apiuser: AuthUser
135 :type apiuser: AuthUser
136 :param repoid: Repository name or repository ID.
136 :param repoid: Repository name or repository ID.
137 :type repoid: str or int
137 :type repoid: str or int
138 :param status: Only return pull requests with the specified status.
138 :param status: Only return pull requests with the specified status.
139 Valid options are.
139 Valid options are.
140 * ``new`` (default)
140 * ``new`` (default)
141 * ``open``
141 * ``open``
142 * ``closed``
142 * ``closed``
143 :type status: str
143 :type status: str
144
144
145 Example output:
145 Example output:
146
146
147 .. code-block:: bash
147 .. code-block:: bash
148
148
149 "id": <id_given_in_input>,
149 "id": <id_given_in_input>,
150 "result":
150 "result":
151 [
151 [
152 ...
152 ...
153 {
153 {
154 "pull_request_id": "<pull_request_id>",
154 "pull_request_id": "<pull_request_id>",
155 "url": "<url>",
155 "url": "<url>",
156 "title" : "<title>",
156 "title" : "<title>",
157 "description": "<description>",
157 "description": "<description>",
158 "status": "<status>",
158 "status": "<status>",
159 "created_on": "<date_time_created>",
159 "created_on": "<date_time_created>",
160 "updated_on": "<date_time_updated>",
160 "updated_on": "<date_time_updated>",
161 "commit_ids": [
161 "commit_ids": [
162 ...
162 ...
163 "<commit_id>",
163 "<commit_id>",
164 "<commit_id>",
164 "<commit_id>",
165 ...
165 ...
166 ],
166 ],
167 "review_status": "<review_status>",
167 "review_status": "<review_status>",
168 "mergeable": {
168 "mergeable": {
169 "status": "<bool>",
169 "status": "<bool>",
170 "message: "<message>",
170 "message: "<message>",
171 },
171 },
172 "source": {
172 "source": {
173 "clone_url": "<clone_url>",
173 "clone_url": "<clone_url>",
174 "reference":
174 "reference":
175 {
175 {
176 "name": "<name>",
176 "name": "<name>",
177 "type": "<type>",
177 "type": "<type>",
178 "commit_id": "<commit_id>",
178 "commit_id": "<commit_id>",
179 }
179 }
180 },
180 },
181 "target": {
181 "target": {
182 "clone_url": "<clone_url>",
182 "clone_url": "<clone_url>",
183 "reference":
183 "reference":
184 {
184 {
185 "name": "<name>",
185 "name": "<name>",
186 "type": "<type>",
186 "type": "<type>",
187 "commit_id": "<commit_id>",
187 "commit_id": "<commit_id>",
188 }
188 }
189 },
189 },
190 "merge": {
190 "merge": {
191 "clone_url": "<clone_url>",
191 "clone_url": "<clone_url>",
192 "reference":
192 "reference":
193 {
193 {
194 "name": "<name>",
194 "name": "<name>",
195 "type": "<type>",
195 "type": "<type>",
196 "commit_id": "<commit_id>",
196 "commit_id": "<commit_id>",
197 }
197 }
198 },
198 },
199 "author": <user_obj>,
199 "author": <user_obj>,
200 "reviewers": [
200 "reviewers": [
201 ...
201 ...
202 {
202 {
203 "user": "<user_obj>",
203 "user": "<user_obj>",
204 "review_status": "<review_status>",
204 "review_status": "<review_status>",
205 }
205 }
206 ...
206 ...
207 ]
207 ]
208 }
208 }
209 ...
209 ...
210 ],
210 ],
211 "error": null
211 "error": null
212
212
213 """
213 """
214 repo = get_repo_or_error(repoid)
214 repo = get_repo_or_error(repoid)
215 if not has_superadmin_permission(apiuser):
215 if not has_superadmin_permission(apiuser):
216 _perms = (
216 _perms = (
217 'repository.admin', 'repository.write', 'repository.read',)
217 'repository.admin', 'repository.write', 'repository.read',)
218 validate_repo_permissions(apiuser, repoid, repo, _perms)
218 validate_repo_permissions(apiuser, repoid, repo, _perms)
219
219
220 status = Optional.extract(status)
220 status = Optional.extract(status)
221 pull_requests = PullRequestModel().get_all(repo, statuses=[status])
221 pull_requests = PullRequestModel().get_all(repo, statuses=[status])
222 data = [pr.get_api_data() for pr in pull_requests]
222 data = [pr.get_api_data() for pr in pull_requests]
223 return data
223 return data
224
224
225
225
226 @jsonrpc_method()
226 @jsonrpc_method()
227 def merge_pull_request(request, apiuser, repoid, pullrequestid,
227 def merge_pull_request(request, apiuser, repoid, pullrequestid,
228 userid=Optional(OAttr('apiuser'))):
228 userid=Optional(OAttr('apiuser'))):
229 """
229 """
230 Merge the pull request specified by `pullrequestid` into its target
230 Merge the pull request specified by `pullrequestid` into its target
231 repository.
231 repository.
232
232
233 :param apiuser: This is filled automatically from the |authtoken|.
233 :param apiuser: This is filled automatically from the |authtoken|.
234 :type apiuser: AuthUser
234 :type apiuser: AuthUser
235 :param repoid: The Repository name or repository ID of the
235 :param repoid: The Repository name or repository ID of the
236 target repository to which the |pr| is to be merged.
236 target repository to which the |pr| is to be merged.
237 :type repoid: str or int
237 :type repoid: str or int
238 :param pullrequestid: ID of the pull request which shall be merged.
238 :param pullrequestid: ID of the pull request which shall be merged.
239 :type pullrequestid: int
239 :type pullrequestid: int
240 :param userid: Merge the pull request as this user.
240 :param userid: Merge the pull request as this user.
241 :type userid: Optional(str or int)
241 :type userid: Optional(str or int)
242
242
243 Example output:
243 Example output:
244
244
245 .. code-block:: bash
245 .. code-block:: bash
246
246
247 "id": <id_given_in_input>,
247 "id": <id_given_in_input>,
248 "result":
248 "result": {
249 {
250 "executed": "<bool>",
249 "executed": "<bool>",
251 "failure_reason": "<int>",
250 "failure_reason": "<int>",
252 "merge_commit_id": "<merge_commit_id>",
251 "merge_commit_id": "<merge_commit_id>",
253 "possible": "<bool>",
252 "possible": "<bool>",
254 "merge_ref": {
253 "merge_ref": {
255 "commit_id": "<commit_id>",
254 "commit_id": "<commit_id>",
256 "type": "<type>",
255 "type": "<type>",
257 "name": "<name>"
256 "name": "<name>"
258 }
257 }
259 },
258 },
260 "error": null
259 "error": null
261
262 """
260 """
263 repo = get_repo_or_error(repoid)
261 repo = get_repo_or_error(repoid)
264 if not isinstance(userid, Optional):
262 if not isinstance(userid, Optional):
265 if (has_superadmin_permission(apiuser) or
263 if (has_superadmin_permission(apiuser) or
266 HasRepoPermissionAnyApi('repository.admin')(
264 HasRepoPermissionAnyApi('repository.admin')(
267 user=apiuser, repo_name=repo.repo_name)):
265 user=apiuser, repo_name=repo.repo_name)):
268 apiuser = get_user_or_error(userid)
266 apiuser = get_user_or_error(userid)
269 else:
267 else:
270 raise JSONRPCError('userid is not the same as your user')
268 raise JSONRPCError('userid is not the same as your user')
271
269
272 pull_request = get_pull_request_or_error(pullrequestid)
270 pull_request = get_pull_request_or_error(pullrequestid)
273
271
274 check = MergeCheck.validate(pull_request, user=apiuser)
272 check = MergeCheck.validate(pull_request, user=apiuser)
275 merge_possible = not check.failed
273 merge_possible = not check.failed
276
274
277 if not merge_possible:
275 if not merge_possible:
278 reasons = ','.join([msg for _e, msg in check.errors])
276 reasons = ','.join([msg for _e, msg in check.errors])
279 raise JSONRPCError(
277 raise JSONRPCError(
280 'merge not possible for following reasons: {}'.format(reasons))
278 'merge not possible for following reasons: {}'.format(reasons))
281
279
282 target_repo = pull_request.target_repo
280 target_repo = pull_request.target_repo
283 extras = vcs_operation_context(
281 extras = vcs_operation_context(
284 request.environ, repo_name=target_repo.repo_name,
282 request.environ, repo_name=target_repo.repo_name,
285 username=apiuser.username, action='push',
283 username=apiuser.username, action='push',
286 scm=target_repo.repo_type)
284 scm=target_repo.repo_type)
287 merge_response = PullRequestModel().merge(
285 merge_response = PullRequestModel().merge(
288 pull_request, apiuser, extras=extras)
286 pull_request, apiuser, extras=extras)
289 if merge_response.executed:
287 if merge_response.executed:
290 PullRequestModel().close_pull_request(
288 PullRequestModel().close_pull_request(
291 pull_request.pull_request_id, apiuser)
289 pull_request.pull_request_id, apiuser)
292
290
293 Session().commit()
291 Session().commit()
294
292
295 # In previous versions the merge response directly contained the merge
293 # In previous versions the merge response directly contained the merge
296 # commit id. It is now contained in the merge reference object. To be
294 # commit id. It is now contained in the merge reference object. To be
297 # backwards compatible we have to extract it again.
295 # backwards compatible we have to extract it again.
298 merge_response = merge_response._asdict()
296 merge_response = merge_response._asdict()
299 merge_response['merge_commit_id'] = merge_response['merge_ref'].commit_id
297 merge_response['merge_commit_id'] = merge_response['merge_ref'].commit_id
300
298
301 return merge_response
299 return merge_response
302
300
303
301
304 @jsonrpc_method()
302 @jsonrpc_method()
305 def close_pull_request(request, apiuser, repoid, pullrequestid,
303 def close_pull_request(request, apiuser, repoid, pullrequestid,
306 userid=Optional(OAttr('apiuser'))):
304 userid=Optional(OAttr('apiuser'))):
307 """
305 """
308 Close the pull request specified by `pullrequestid`.
306 Close the pull request specified by `pullrequestid`.
309
307
310 :param apiuser: This is filled automatically from the |authtoken|.
308 :param apiuser: This is filled automatically from the |authtoken|.
311 :type apiuser: AuthUser
309 :type apiuser: AuthUser
312 :param repoid: Repository name or repository ID to which the pull
310 :param repoid: Repository name or repository ID to which the pull
313 request belongs.
311 request belongs.
314 :type repoid: str or int
312 :type repoid: str or int
315 :param pullrequestid: ID of the pull request to be closed.
313 :param pullrequestid: ID of the pull request to be closed.
316 :type pullrequestid: int
314 :type pullrequestid: int
317 :param userid: Close the pull request as this user.
315 :param userid: Close the pull request as this user.
318 :type userid: Optional(str or int)
316 :type userid: Optional(str or int)
319
317
320 Example output:
318 Example output:
321
319
322 .. code-block:: bash
320 .. code-block:: bash
323
321
324 "id": <id_given_in_input>,
322 "id": <id_given_in_input>,
325 "result":
323 "result": {
326 {
327 "pull_request_id": "<int>",
324 "pull_request_id": "<int>",
328 "closed": "<bool>"
325 "closed": "<bool>"
329 },
326 },
330 "error": null
327 "error": null
331
328
332 """
329 """
333 repo = get_repo_or_error(repoid)
330 repo = get_repo_or_error(repoid)
334 if not isinstance(userid, Optional):
331 if not isinstance(userid, Optional):
335 if (has_superadmin_permission(apiuser) or
332 if (has_superadmin_permission(apiuser) or
336 HasRepoPermissionAnyApi('repository.admin')(
333 HasRepoPermissionAnyApi('repository.admin')(
337 user=apiuser, repo_name=repo.repo_name)):
334 user=apiuser, repo_name=repo.repo_name)):
338 apiuser = get_user_or_error(userid)
335 apiuser = get_user_or_error(userid)
339 else:
336 else:
340 raise JSONRPCError('userid is not the same as your user')
337 raise JSONRPCError('userid is not the same as your user')
341
338
342 pull_request = get_pull_request_or_error(pullrequestid)
339 pull_request = get_pull_request_or_error(pullrequestid)
343 if not PullRequestModel().check_user_update(
340 if not PullRequestModel().check_user_update(
344 pull_request, apiuser, api=True):
341 pull_request, apiuser, api=True):
345 raise JSONRPCError(
342 raise JSONRPCError(
346 'pull request `%s` close failed, no permission to close.' % (
343 'pull request `%s` close failed, no permission to close.' % (
347 pullrequestid,))
344 pullrequestid,))
348 if pull_request.is_closed():
345 if pull_request.is_closed():
349 raise JSONRPCError(
346 raise JSONRPCError(
350 'pull request `%s` is already closed' % (pullrequestid,))
347 'pull request `%s` is already closed' % (pullrequestid,))
351
348
352 PullRequestModel().close_pull_request(
349 PullRequestModel().close_pull_request(
353 pull_request.pull_request_id, apiuser)
350 pull_request.pull_request_id, apiuser)
354 Session().commit()
351 Session().commit()
355 data = {
352 data = {
356 'pull_request_id': pull_request.pull_request_id,
353 'pull_request_id': pull_request.pull_request_id,
357 'closed': True,
354 'closed': True,
358 }
355 }
359 return data
356 return data
360
357
361
358
362 @jsonrpc_method()
359 @jsonrpc_method()
363 def comment_pull_request(
360 def comment_pull_request(
364 request, apiuser, repoid, pullrequestid, message=Optional(None),
361 request, apiuser, repoid, pullrequestid, message=Optional(None),
365 commit_id=Optional(None), status=Optional(None),
362 commit_id=Optional(None), status=Optional(None),
366 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
363 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
367 resolves_comment_id=Optional(None),
364 resolves_comment_id=Optional(None),
368 userid=Optional(OAttr('apiuser'))):
365 userid=Optional(OAttr('apiuser'))):
369 """
366 """
370 Comment on the pull request specified with the `pullrequestid`,
367 Comment on the pull request specified with the `pullrequestid`,
371 in the |repo| specified by the `repoid`, and optionally change the
368 in the |repo| specified by the `repoid`, and optionally change the
372 review status.
369 review status.
373
370
374 :param apiuser: This is filled automatically from the |authtoken|.
371 :param apiuser: This is filled automatically from the |authtoken|.
375 :type apiuser: AuthUser
372 :type apiuser: AuthUser
376 :param repoid: The repository name or repository ID.
373 :param repoid: The repository name or repository ID.
377 :type repoid: str or int
374 :type repoid: str or int
378 :param pullrequestid: The pull request ID.
375 :param pullrequestid: The pull request ID.
379 :type pullrequestid: int
376 :type pullrequestid: int
380 :param commit_id: Specify the commit_id for which to set a comment. If
377 :param commit_id: Specify the commit_id for which to set a comment. If
381 given commit_id is different than latest in the PR status
378 given commit_id is different than latest in the PR status
382 change won't be performed.
379 change won't be performed.
383 :type commit_id: str
380 :type commit_id: str
384 :param message: The text content of the comment.
381 :param message: The text content of the comment.
385 :type message: str
382 :type message: str
386 :param status: (**Optional**) Set the approval status of the pull
383 :param status: (**Optional**) Set the approval status of the pull
387 request. One of: 'not_reviewed', 'approved', 'rejected',
384 request. One of: 'not_reviewed', 'approved', 'rejected',
388 'under_review'
385 'under_review'
389 :type status: str
386 :type status: str
390 :param comment_type: Comment type, one of: 'note', 'todo'
387 :param comment_type: Comment type, one of: 'note', 'todo'
391 :type comment_type: Optional(str), default: 'note'
388 :type comment_type: Optional(str), default: 'note'
392 :param userid: Comment on the pull request as this user
389 :param userid: Comment on the pull request as this user
393 :type userid: Optional(str or int)
390 :type userid: Optional(str or int)
394
391
395 Example output:
392 Example output:
396
393
397 .. code-block:: bash
394 .. code-block:: bash
398
395
399 id : <id_given_in_input>
396 id : <id_given_in_input>
400 result :
397 result : {
401 {
402 "pull_request_id": "<Integer>",
398 "pull_request_id": "<Integer>",
403 "comment_id": "<Integer>",
399 "comment_id": "<Integer>",
404 "status": {"given": <given_status>,
400 "status": {"given": <given_status>,
405 "was_changed": <bool status_was_actually_changed> },
401 "was_changed": <bool status_was_actually_changed> },
406 }
402 },
407 error : null
403 error : null
408 """
404 """
409 repo = get_repo_or_error(repoid)
405 repo = get_repo_or_error(repoid)
410 if not isinstance(userid, Optional):
406 if not isinstance(userid, Optional):
411 if (has_superadmin_permission(apiuser) or
407 if (has_superadmin_permission(apiuser) or
412 HasRepoPermissionAnyApi('repository.admin')(
408 HasRepoPermissionAnyApi('repository.admin')(
413 user=apiuser, repo_name=repo.repo_name)):
409 user=apiuser, repo_name=repo.repo_name)):
414 apiuser = get_user_or_error(userid)
410 apiuser = get_user_or_error(userid)
415 else:
411 else:
416 raise JSONRPCError('userid is not the same as your user')
412 raise JSONRPCError('userid is not the same as your user')
417
413
418 pull_request = get_pull_request_or_error(pullrequestid)
414 pull_request = get_pull_request_or_error(pullrequestid)
419 if not PullRequestModel().check_user_read(
415 if not PullRequestModel().check_user_read(
420 pull_request, apiuser, api=True):
416 pull_request, apiuser, api=True):
421 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
417 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
422 message = Optional.extract(message)
418 message = Optional.extract(message)
423 status = Optional.extract(status)
419 status = Optional.extract(status)
424 commit_id = Optional.extract(commit_id)
420 commit_id = Optional.extract(commit_id)
425 comment_type = Optional.extract(comment_type)
421 comment_type = Optional.extract(comment_type)
426 resolves_comment_id = Optional.extract(resolves_comment_id)
422 resolves_comment_id = Optional.extract(resolves_comment_id)
427
423
428 if not message and not status:
424 if not message and not status:
429 raise JSONRPCError(
425 raise JSONRPCError(
430 'Both message and status parameters are missing. '
426 'Both message and status parameters are missing. '
431 'At least one is required.')
427 'At least one is required.')
432
428
433 if (status not in (st[0] for st in ChangesetStatus.STATUSES) and
429 if (status not in (st[0] for st in ChangesetStatus.STATUSES) and
434 status is not None):
430 status is not None):
435 raise JSONRPCError('Unknown comment status: `%s`' % status)
431 raise JSONRPCError('Unknown comment status: `%s`' % status)
436
432
437 if commit_id and commit_id not in pull_request.revisions:
433 if commit_id and commit_id not in pull_request.revisions:
438 raise JSONRPCError(
434 raise JSONRPCError(
439 'Invalid commit_id `%s` for this pull request.' % commit_id)
435 'Invalid commit_id `%s` for this pull request.' % commit_id)
440
436
441 allowed_to_change_status = PullRequestModel().check_user_change_status(
437 allowed_to_change_status = PullRequestModel().check_user_change_status(
442 pull_request, apiuser)
438 pull_request, apiuser)
443
439
444 # if commit_id is passed re-validated if user is allowed to change status
440 # if commit_id is passed re-validated if user is allowed to change status
445 # based on latest commit_id from the PR
441 # based on latest commit_id from the PR
446 if commit_id:
442 if commit_id:
447 commit_idx = pull_request.revisions.index(commit_id)
443 commit_idx = pull_request.revisions.index(commit_id)
448 if commit_idx != 0:
444 if commit_idx != 0:
449 allowed_to_change_status = False
445 allowed_to_change_status = False
450
446
451 if resolves_comment_id:
447 if resolves_comment_id:
452 comment = ChangesetComment.get(resolves_comment_id)
448 comment = ChangesetComment.get(resolves_comment_id)
453 if not comment:
449 if not comment:
454 raise JSONRPCError(
450 raise JSONRPCError(
455 'Invalid resolves_comment_id `%s` for this pull request.'
451 'Invalid resolves_comment_id `%s` for this pull request.'
456 % resolves_comment_id)
452 % resolves_comment_id)
457 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
453 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
458 raise JSONRPCError(
454 raise JSONRPCError(
459 'Comment `%s` is wrong type for setting status to resolved.'
455 'Comment `%s` is wrong type for setting status to resolved.'
460 % resolves_comment_id)
456 % resolves_comment_id)
461
457
462 text = message
458 text = message
463 status_label = ChangesetStatus.get_status_lbl(status)
459 status_label = ChangesetStatus.get_status_lbl(status)
464 if status and allowed_to_change_status:
460 if status and allowed_to_change_status:
465 st_message = ('Status change %(transition_icon)s %(status)s'
461 st_message = ('Status change %(transition_icon)s %(status)s'
466 % {'transition_icon': '>', 'status': status_label})
462 % {'transition_icon': '>', 'status': status_label})
467 text = message or st_message
463 text = message or st_message
468
464
469 rc_config = SettingsModel().get_all_settings()
465 rc_config = SettingsModel().get_all_settings()
470 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
466 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
471
467
472 status_change = status and allowed_to_change_status
468 status_change = status and allowed_to_change_status
473 comment = CommentsModel().create(
469 comment = CommentsModel().create(
474 text=text,
470 text=text,
475 repo=pull_request.target_repo.repo_id,
471 repo=pull_request.target_repo.repo_id,
476 user=apiuser.user_id,
472 user=apiuser.user_id,
477 pull_request=pull_request.pull_request_id,
473 pull_request=pull_request.pull_request_id,
478 f_path=None,
474 f_path=None,
479 line_no=None,
475 line_no=None,
480 status_change=(status_label if status_change else None),
476 status_change=(status_label if status_change else None),
481 status_change_type=(status if status_change else None),
477 status_change_type=(status if status_change else None),
482 closing_pr=False,
478 closing_pr=False,
483 renderer=renderer,
479 renderer=renderer,
484 comment_type=comment_type,
480 comment_type=comment_type,
485 resolves_comment_id=resolves_comment_id
481 resolves_comment_id=resolves_comment_id
486 )
482 )
487
483
488 if allowed_to_change_status and status:
484 if allowed_to_change_status and status:
489 ChangesetStatusModel().set_status(
485 ChangesetStatusModel().set_status(
490 pull_request.target_repo.repo_id,
486 pull_request.target_repo.repo_id,
491 status,
487 status,
492 apiuser.user_id,
488 apiuser.user_id,
493 comment,
489 comment,
494 pull_request=pull_request.pull_request_id
490 pull_request=pull_request.pull_request_id
495 )
491 )
496 Session().flush()
492 Session().flush()
497
493
498 Session().commit()
494 Session().commit()
499 data = {
495 data = {
500 'pull_request_id': pull_request.pull_request_id,
496 'pull_request_id': pull_request.pull_request_id,
501 'comment_id': comment.comment_id if comment else None,
497 'comment_id': comment.comment_id if comment else None,
502 'status': {'given': status, 'was_changed': status_change},
498 'status': {'given': status, 'was_changed': status_change},
503 }
499 }
504 return data
500 return data
505
501
506
502
507 @jsonrpc_method()
503 @jsonrpc_method()
508 def create_pull_request(
504 def create_pull_request(
509 request, apiuser, source_repo, target_repo, source_ref, target_ref,
505 request, apiuser, source_repo, target_repo, source_ref, target_ref,
510 title, description=Optional(''), reviewers=Optional(None)):
506 title, description=Optional(''), reviewers=Optional(None)):
511 """
507 """
512 Creates a new pull request.
508 Creates a new pull request.
513
509
514 Accepts refs in the following formats:
510 Accepts refs in the following formats:
515
511
516 * branch:<branch_name>:<sha>
512 * branch:<branch_name>:<sha>
517 * branch:<branch_name>
513 * branch:<branch_name>
518 * bookmark:<bookmark_name>:<sha> (Mercurial only)
514 * bookmark:<bookmark_name>:<sha> (Mercurial only)
519 * bookmark:<bookmark_name> (Mercurial only)
515 * bookmark:<bookmark_name> (Mercurial only)
520
516
521 :param apiuser: This is filled automatically from the |authtoken|.
517 :param apiuser: This is filled automatically from the |authtoken|.
522 :type apiuser: AuthUser
518 :type apiuser: AuthUser
523 :param source_repo: Set the source repository name.
519 :param source_repo: Set the source repository name.
524 :type source_repo: str
520 :type source_repo: str
525 :param target_repo: Set the target repository name.
521 :param target_repo: Set the target repository name.
526 :type target_repo: str
522 :type target_repo: str
527 :param source_ref: Set the source ref name.
523 :param source_ref: Set the source ref name.
528 :type source_ref: str
524 :type source_ref: str
529 :param target_ref: Set the target ref name.
525 :param target_ref: Set the target ref name.
530 :type target_ref: str
526 :type target_ref: str
531 :param title: Set the pull request title.
527 :param title: Set the pull request title.
532 :type title: str
528 :type title: str
533 :param description: Set the pull request description.
529 :param description: Set the pull request description.
534 :type description: Optional(str)
530 :type description: Optional(str)
535 :param reviewers: Set the new pull request reviewers list.
531 :param reviewers: Set the new pull request reviewers list.
536 :type reviewers: Optional(list)
532 :type reviewers: Optional(list)
537 Accepts username strings or objects of the format:
533 Accepts username strings or objects of the format:
538 {
534
539 'username': 'nick', 'reasons': ['original author']
535 {'username': 'nick', 'reasons': ['original author']}
540 }
541 """
536 """
542
537
543 source = get_repo_or_error(source_repo)
538 source = get_repo_or_error(source_repo)
544 target = get_repo_or_error(target_repo)
539 target = get_repo_or_error(target_repo)
545 if not has_superadmin_permission(apiuser):
540 if not has_superadmin_permission(apiuser):
546 _perms = ('repository.admin', 'repository.write', 'repository.read',)
541 _perms = ('repository.admin', 'repository.write', 'repository.read',)
547 validate_repo_permissions(apiuser, source_repo, source, _perms)
542 validate_repo_permissions(apiuser, source_repo, source, _perms)
548
543
549 full_source_ref = resolve_ref_or_error(source_ref, source)
544 full_source_ref = resolve_ref_or_error(source_ref, source)
550 full_target_ref = resolve_ref_or_error(target_ref, target)
545 full_target_ref = resolve_ref_or_error(target_ref, target)
551 source_commit = get_commit_or_error(full_source_ref, source)
546 source_commit = get_commit_or_error(full_source_ref, source)
552 target_commit = get_commit_or_error(full_target_ref, target)
547 target_commit = get_commit_or_error(full_target_ref, target)
553 source_scm = source.scm_instance()
548 source_scm = source.scm_instance()
554 target_scm = target.scm_instance()
549 target_scm = target.scm_instance()
555
550
556 commit_ranges = target_scm.compare(
551 commit_ranges = target_scm.compare(
557 target_commit.raw_id, source_commit.raw_id, source_scm,
552 target_commit.raw_id, source_commit.raw_id, source_scm,
558 merge=True, pre_load=[])
553 merge=True, pre_load=[])
559
554
560 ancestor = target_scm.get_common_ancestor(
555 ancestor = target_scm.get_common_ancestor(
561 target_commit.raw_id, source_commit.raw_id, source_scm)
556 target_commit.raw_id, source_commit.raw_id, source_scm)
562
557
563 if not commit_ranges:
558 if not commit_ranges:
564 raise JSONRPCError('no commits found')
559 raise JSONRPCError('no commits found')
565
560
566 if not ancestor:
561 if not ancestor:
567 raise JSONRPCError('no common ancestor found')
562 raise JSONRPCError('no common ancestor found')
568
563
569 reviewer_objects = Optional.extract(reviewers) or []
564 reviewer_objects = Optional.extract(reviewers) or []
570 if not isinstance(reviewer_objects, list):
565 if not isinstance(reviewer_objects, list):
571 raise JSONRPCError('reviewers should be specified as a list')
566 raise JSONRPCError('reviewers should be specified as a list')
572
567
573 reviewers_reasons = []
568 reviewers_reasons = []
574 for reviewer_object in reviewer_objects:
569 for reviewer_object in reviewer_objects:
575 reviewer_reasons = []
570 reviewer_reasons = []
576 if isinstance(reviewer_object, (basestring, int)):
571 if isinstance(reviewer_object, (basestring, int)):
577 reviewer_username = reviewer_object
572 reviewer_username = reviewer_object
578 else:
573 else:
579 reviewer_username = reviewer_object['username']
574 reviewer_username = reviewer_object['username']
580 reviewer_reasons = reviewer_object.get('reasons', [])
575 reviewer_reasons = reviewer_object.get('reasons', [])
581
576
582 user = get_user_or_error(reviewer_username)
577 user = get_user_or_error(reviewer_username)
583 reviewers_reasons.append((user.user_id, reviewer_reasons))
578 reviewers_reasons.append((user.user_id, reviewer_reasons))
584
579
585 pull_request_model = PullRequestModel()
580 pull_request_model = PullRequestModel()
586 pull_request = pull_request_model.create(
581 pull_request = pull_request_model.create(
587 created_by=apiuser.user_id,
582 created_by=apiuser.user_id,
588 source_repo=source_repo,
583 source_repo=source_repo,
589 source_ref=full_source_ref,
584 source_ref=full_source_ref,
590 target_repo=target_repo,
585 target_repo=target_repo,
591 target_ref=full_target_ref,
586 target_ref=full_target_ref,
592 revisions=reversed(
587 revisions=reversed(
593 [commit.raw_id for commit in reversed(commit_ranges)]),
588 [commit.raw_id for commit in reversed(commit_ranges)]),
594 reviewers=reviewers_reasons,
589 reviewers=reviewers_reasons,
595 title=title,
590 title=title,
596 description=Optional.extract(description)
591 description=Optional.extract(description)
597 )
592 )
598
593
599 Session().commit()
594 Session().commit()
600 data = {
595 data = {
601 'msg': 'Created new pull request `{}`'.format(title),
596 'msg': 'Created new pull request `{}`'.format(title),
602 'pull_request_id': pull_request.pull_request_id,
597 'pull_request_id': pull_request.pull_request_id,
603 }
598 }
604 return data
599 return data
605
600
606
601
607 @jsonrpc_method()
602 @jsonrpc_method()
608 def update_pull_request(
603 def update_pull_request(
609 request, apiuser, repoid, pullrequestid, title=Optional(''),
604 request, apiuser, repoid, pullrequestid, title=Optional(''),
610 description=Optional(''), reviewers=Optional(None),
605 description=Optional(''), reviewers=Optional(None),
611 update_commits=Optional(None), close_pull_request=Optional(None)):
606 update_commits=Optional(None), close_pull_request=Optional(None)):
612 """
607 """
613 Updates a pull request.
608 Updates a pull request.
614
609
615 :param apiuser: This is filled automatically from the |authtoken|.
610 :param apiuser: This is filled automatically from the |authtoken|.
616 :type apiuser: AuthUser
611 :type apiuser: AuthUser
617 :param repoid: The repository name or repository ID.
612 :param repoid: The repository name or repository ID.
618 :type repoid: str or int
613 :type repoid: str or int
619 :param pullrequestid: The pull request ID.
614 :param pullrequestid: The pull request ID.
620 :type pullrequestid: int
615 :type pullrequestid: int
621 :param title: Set the pull request title.
616 :param title: Set the pull request title.
622 :type title: str
617 :type title: str
623 :param description: Update pull request description.
618 :param description: Update pull request description.
624 :type description: Optional(str)
619 :type description: Optional(str)
625 :param reviewers: Update pull request reviewers list with new value.
620 :param reviewers: Update pull request reviewers list with new value.
626 :type reviewers: Optional(list)
621 :type reviewers: Optional(list)
627 :param update_commits: Trigger update of commits for this pull request
622 :param update_commits: Trigger update of commits for this pull request
628 :type: update_commits: Optional(bool)
623 :type: update_commits: Optional(bool)
629 :param close_pull_request: Close this pull request with rejected state
624 :param close_pull_request: Close this pull request with rejected state
630 :type: close_pull_request: Optional(bool)
625 :type: close_pull_request: Optional(bool)
631
626
632 Example output:
627 Example output:
633
628
634 .. code-block:: bash
629 .. code-block:: bash
635
630
636 id : <id_given_in_input>
631 id : <id_given_in_input>
637 result :
632 result : {
638 {
639 "msg": "Updated pull request `63`",
633 "msg": "Updated pull request `63`",
640 "pull_request": <pull_request_object>,
634 "pull_request": <pull_request_object>,
641 "updated_reviewers": {
635 "updated_reviewers": {
642 "added": [
636 "added": [
643 "username"
637 "username"
644 ],
638 ],
645 "removed": []
639 "removed": []
646 },
640 },
647 "updated_commits": {
641 "updated_commits": {
648 "added": [
642 "added": [
649 "<sha1_hash>"
643 "<sha1_hash>"
650 ],
644 ],
651 "common": [
645 "common": [
652 "<sha1_hash>",
646 "<sha1_hash>",
653 "<sha1_hash>",
647 "<sha1_hash>",
654 ],
648 ],
655 "removed": []
649 "removed": []
656 }
650 }
657 }
651 }
658 error : null
652 error : null
659 """
653 """
660
654
661 repo = get_repo_or_error(repoid)
655 repo = get_repo_or_error(repoid)
662 pull_request = get_pull_request_or_error(pullrequestid)
656 pull_request = get_pull_request_or_error(pullrequestid)
663 if not PullRequestModel().check_user_update(
657 if not PullRequestModel().check_user_update(
664 pull_request, apiuser, api=True):
658 pull_request, apiuser, api=True):
665 raise JSONRPCError(
659 raise JSONRPCError(
666 'pull request `%s` update failed, no permission to update.' % (
660 'pull request `%s` update failed, no permission to update.' % (
667 pullrequestid,))
661 pullrequestid,))
668 if pull_request.is_closed():
662 if pull_request.is_closed():
669 raise JSONRPCError(
663 raise JSONRPCError(
670 'pull request `%s` update failed, pull request is closed' % (
664 'pull request `%s` update failed, pull request is closed' % (
671 pullrequestid,))
665 pullrequestid,))
672
666
673 reviewer_objects = Optional.extract(reviewers) or []
667 reviewer_objects = Optional.extract(reviewers) or []
674 if not isinstance(reviewer_objects, list):
668 if not isinstance(reviewer_objects, list):
675 raise JSONRPCError('reviewers should be specified as a list')
669 raise JSONRPCError('reviewers should be specified as a list')
676
670
677 reviewers_reasons = []
671 reviewers_reasons = []
678 reviewer_ids = set()
672 reviewer_ids = set()
679 for reviewer_object in reviewer_objects:
673 for reviewer_object in reviewer_objects:
680 reviewer_reasons = []
674 reviewer_reasons = []
681 if isinstance(reviewer_object, (int, basestring)):
675 if isinstance(reviewer_object, (int, basestring)):
682 reviewer_username = reviewer_object
676 reviewer_username = reviewer_object
683 else:
677 else:
684 reviewer_username = reviewer_object['username']
678 reviewer_username = reviewer_object['username']
685 reviewer_reasons = reviewer_object.get('reasons', [])
679 reviewer_reasons = reviewer_object.get('reasons', [])
686
680
687 user = get_user_or_error(reviewer_username)
681 user = get_user_or_error(reviewer_username)
688 reviewer_ids.add(user.user_id)
682 reviewer_ids.add(user.user_id)
689 reviewers_reasons.append((user.user_id, reviewer_reasons))
683 reviewers_reasons.append((user.user_id, reviewer_reasons))
690
684
691 title = Optional.extract(title)
685 title = Optional.extract(title)
692 description = Optional.extract(description)
686 description = Optional.extract(description)
693 if title or description:
687 if title or description:
694 PullRequestModel().edit(
688 PullRequestModel().edit(
695 pull_request, title or pull_request.title,
689 pull_request, title or pull_request.title,
696 description or pull_request.description)
690 description or pull_request.description)
697 Session().commit()
691 Session().commit()
698
692
699 commit_changes = {"added": [], "common": [], "removed": []}
693 commit_changes = {"added": [], "common": [], "removed": []}
700 if str2bool(Optional.extract(update_commits)):
694 if str2bool(Optional.extract(update_commits)):
701 if PullRequestModel().has_valid_update_type(pull_request):
695 if PullRequestModel().has_valid_update_type(pull_request):
702 update_response = PullRequestModel().update_commits(
696 update_response = PullRequestModel().update_commits(
703 pull_request)
697 pull_request)
704 commit_changes = update_response.changes or commit_changes
698 commit_changes = update_response.changes or commit_changes
705 Session().commit()
699 Session().commit()
706
700
707 reviewers_changes = {"added": [], "removed": []}
701 reviewers_changes = {"added": [], "removed": []}
708 if reviewer_ids:
702 if reviewer_ids:
709 added_reviewers, removed_reviewers = \
703 added_reviewers, removed_reviewers = \
710 PullRequestModel().update_reviewers(pull_request, reviewers_reasons)
704 PullRequestModel().update_reviewers(pull_request, reviewers_reasons)
711
705
712 reviewers_changes['added'] = sorted(
706 reviewers_changes['added'] = sorted(
713 [get_user_or_error(n).username for n in added_reviewers])
707 [get_user_or_error(n).username for n in added_reviewers])
714 reviewers_changes['removed'] = sorted(
708 reviewers_changes['removed'] = sorted(
715 [get_user_or_error(n).username for n in removed_reviewers])
709 [get_user_or_error(n).username for n in removed_reviewers])
716 Session().commit()
710 Session().commit()
717
711
718 if str2bool(Optional.extract(close_pull_request)):
712 if str2bool(Optional.extract(close_pull_request)):
719 PullRequestModel().close_pull_request_with_comment(
713 PullRequestModel().close_pull_request_with_comment(
720 pull_request, apiuser, repo)
714 pull_request, apiuser, repo)
721 Session().commit()
715 Session().commit()
722
716
723 data = {
717 data = {
724 'msg': 'Updated pull request `{}`'.format(
718 'msg': 'Updated pull request `{}`'.format(
725 pull_request.pull_request_id),
719 pull_request.pull_request_id),
726 'pull_request': pull_request.get_api_data(),
720 'pull_request': pull_request.get_api_data(),
727 'updated_commits': commit_changes,
721 'updated_commits': commit_changes,
728 'updated_reviewers': reviewers_changes
722 'updated_reviewers': reviewers_changes
729 }
723 }
730
724
731 return data
725 return data
@@ -1,245 +1,321 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21 import inspect
22 import logging
22 import logging
23 import itertools
23
24
24 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
25 from rhodecode.api import (
26 jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods)
25
27
26 from rhodecode.api.utils import (
28 from rhodecode.api.utils import (
27 Optional, OAttr, has_superadmin_permission, get_user_or_error)
29 Optional, OAttr, has_superadmin_permission, get_user_or_error)
28 from rhodecode.lib.utils import repo2db_mapper
30 from rhodecode.lib.utils import repo2db_mapper
29 from rhodecode.lib import system_info
31 from rhodecode.lib import system_info
30 from rhodecode.lib import user_sessions
32 from rhodecode.lib import user_sessions
31 from rhodecode.lib.utils2 import safe_int
33 from rhodecode.lib.utils2 import safe_int
32 from rhodecode.model.db import UserIpMap
34 from rhodecode.model.db import UserIpMap
33 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
34
36
35 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
36
38
37
39
38 @jsonrpc_method()
40 @jsonrpc_method()
39 def get_server_info(request, apiuser):
41 def get_server_info(request, apiuser):
40 """
42 """
41 Returns the |RCE| server information.
43 Returns the |RCE| server information.
42
44
43 This includes the running version of |RCE| and all installed
45 This includes the running version of |RCE| and all installed
44 packages. This command takes the following options:
46 packages. This command takes the following options:
45
47
46 :param apiuser: This is filled automatically from the |authtoken|.
48 :param apiuser: This is filled automatically from the |authtoken|.
47 :type apiuser: AuthUser
49 :type apiuser: AuthUser
48
50
49 Example output:
51 Example output:
50
52
51 .. code-block:: bash
53 .. code-block:: bash
52
54
53 id : <id_given_in_input>
55 id : <id_given_in_input>
54 result : {
56 result : {
55 'modules': [<module name>,...]
57 'modules': [<module name>,...]
56 'py_version': <python version>,
58 'py_version': <python version>,
57 'platform': <platform type>,
59 'platform': <platform type>,
58 'rhodecode_version': <rhodecode version>
60 'rhodecode_version': <rhodecode version>
59 }
61 }
60 error : null
62 error : null
61 """
63 """
62
64
63 if not has_superadmin_permission(apiuser):
65 if not has_superadmin_permission(apiuser):
64 raise JSONRPCForbidden()
66 raise JSONRPCForbidden()
65
67
66 server_info = ScmModel().get_server_info(request.environ)
68 server_info = ScmModel().get_server_info(request.environ)
67 # rhodecode-index requires those
69 # rhodecode-index requires those
68
70
69 server_info['index_storage'] = server_info['search']['value']['location']
71 server_info['index_storage'] = server_info['search']['value']['location']
70 server_info['storage'] = server_info['storage']['value']['path']
72 server_info['storage'] = server_info['storage']['value']['path']
71
73
72 return server_info
74 return server_info
73
75
74
76
75 @jsonrpc_method()
77 @jsonrpc_method()
76 def get_ip(request, apiuser, userid=Optional(OAttr('apiuser'))):
78 def get_ip(request, apiuser, userid=Optional(OAttr('apiuser'))):
77 """
79 """
78 Displays the IP Address as seen from the |RCE| server.
80 Displays the IP Address as seen from the |RCE| server.
79
81
80 * This command displays the IP Address, as well as all the defined IP
82 * This command displays the IP Address, as well as all the defined IP
81 addresses for the specified user. If the ``userid`` is not set, the
83 addresses for the specified user. If the ``userid`` is not set, the
82 data returned is for the user calling the method.
84 data returned is for the user calling the method.
83
85
84 This command can only be run using an |authtoken| with admin rights to
86 This command can only be run using an |authtoken| with admin rights to
85 the specified repository.
87 the specified repository.
86
88
87 This command takes the following options:
89 This command takes the following options:
88
90
89 :param apiuser: This is filled automatically from |authtoken|.
91 :param apiuser: This is filled automatically from |authtoken|.
90 :type apiuser: AuthUser
92 :type apiuser: AuthUser
91 :param userid: Sets the userid for which associated IP Address data
93 :param userid: Sets the userid for which associated IP Address data
92 is returned.
94 is returned.
93 :type userid: Optional(str or int)
95 :type userid: Optional(str or int)
94
96
95 Example output:
97 Example output:
96
98
97 .. code-block:: bash
99 .. code-block:: bash
98
100
99 id : <id_given_in_input>
101 id : <id_given_in_input>
100 result : {
102 result : {
101 "server_ip_addr": "<ip_from_clien>",
103 "server_ip_addr": "<ip_from_clien>",
102 "user_ips": [
104 "user_ips": [
103 {
105 {
104 "ip_addr": "<ip_with_mask>",
106 "ip_addr": "<ip_with_mask>",
105 "ip_range": ["<start_ip>", "<end_ip>"],
107 "ip_range": ["<start_ip>", "<end_ip>"],
106 },
108 },
107 ...
109 ...
108 ]
110 ]
109 }
111 }
110
112
111 """
113 """
112 if not has_superadmin_permission(apiuser):
114 if not has_superadmin_permission(apiuser):
113 raise JSONRPCForbidden()
115 raise JSONRPCForbidden()
114
116
115 userid = Optional.extract(userid, evaluate_locals=locals())
117 userid = Optional.extract(userid, evaluate_locals=locals())
116 userid = getattr(userid, 'user_id', userid)
118 userid = getattr(userid, 'user_id', userid)
117
119
118 user = get_user_or_error(userid)
120 user = get_user_or_error(userid)
119 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
121 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
120 return {
122 return {
121 'server_ip_addr': request.rpc_ip_addr,
123 'server_ip_addr': request.rpc_ip_addr,
122 'user_ips': ips
124 'user_ips': ips
123 }
125 }
124
126
125
127
126 @jsonrpc_method()
128 @jsonrpc_method()
127 def rescan_repos(request, apiuser, remove_obsolete=Optional(False)):
129 def rescan_repos(request, apiuser, remove_obsolete=Optional(False)):
128 """
130 """
129 Triggers a rescan of the specified repositories.
131 Triggers a rescan of the specified repositories.
130
132
131 * If the ``remove_obsolete`` option is set, it also deletes repositories
133 * If the ``remove_obsolete`` option is set, it also deletes repositories
132 that are found in the database but not on the file system, so called
134 that are found in the database but not on the file system, so called
133 "clean zombies".
135 "clean zombies".
134
136
135 This command can only be run using an |authtoken| with admin rights to
137 This command can only be run using an |authtoken| with admin rights to
136 the specified repository.
138 the specified repository.
137
139
138 This command takes the following options:
140 This command takes the following options:
139
141
140 :param apiuser: This is filled automatically from the |authtoken|.
142 :param apiuser: This is filled automatically from the |authtoken|.
141 :type apiuser: AuthUser
143 :type apiuser: AuthUser
142 :param remove_obsolete: Deletes repositories from the database that
144 :param remove_obsolete: Deletes repositories from the database that
143 are not found on the filesystem.
145 are not found on the filesystem.
144 :type remove_obsolete: Optional(``True`` | ``False``)
146 :type remove_obsolete: Optional(``True`` | ``False``)
145
147
146 Example output:
148 Example output:
147
149
148 .. code-block:: bash
150 .. code-block:: bash
149
151
150 id : <id_given_in_input>
152 id : <id_given_in_input>
151 result : {
153 result : {
152 'added': [<added repository name>,...]
154 'added': [<added repository name>,...]
153 'removed': [<removed repository name>,...]
155 'removed': [<removed repository name>,...]
154 }
156 }
155 error : null
157 error : null
156
158
157 Example error output:
159 Example error output:
158
160
159 .. code-block:: bash
161 .. code-block:: bash
160
162
161 id : <id_given_in_input>
163 id : <id_given_in_input>
162 result : null
164 result : null
163 error : {
165 error : {
164 'Error occurred during rescan repositories action'
166 'Error occurred during rescan repositories action'
165 }
167 }
166
168
167 """
169 """
168 if not has_superadmin_permission(apiuser):
170 if not has_superadmin_permission(apiuser):
169 raise JSONRPCForbidden()
171 raise JSONRPCForbidden()
170
172
171 try:
173 try:
172 rm_obsolete = Optional.extract(remove_obsolete)
174 rm_obsolete = Optional.extract(remove_obsolete)
173 added, removed = repo2db_mapper(ScmModel().repo_scan(),
175 added, removed = repo2db_mapper(ScmModel().repo_scan(),
174 remove_obsolete=rm_obsolete)
176 remove_obsolete=rm_obsolete)
175 return {'added': added, 'removed': removed}
177 return {'added': added, 'removed': removed}
176 except Exception:
178 except Exception:
177 log.exception('Failed to run repo rescann')
179 log.exception('Failed to run repo rescann')
178 raise JSONRPCError(
180 raise JSONRPCError(
179 'Error occurred during rescan repositories action'
181 'Error occurred during rescan repositories action'
180 )
182 )
181
183
182
184
183 @jsonrpc_method()
185 @jsonrpc_method()
184 def cleanup_sessions(request, apiuser, older_then=Optional(60)):
186 def cleanup_sessions(request, apiuser, older_then=Optional(60)):
185 """
187 """
186 Triggers a session cleanup action.
188 Triggers a session cleanup action.
187
189
188 If the ``older_then`` option is set, only sessions that hasn't been
190 If the ``older_then`` option is set, only sessions that hasn't been
189 accessed in the given number of days will be removed.
191 accessed in the given number of days will be removed.
190
192
191 This command can only be run using an |authtoken| with admin rights to
193 This command can only be run using an |authtoken| with admin rights to
192 the specified repository.
194 the specified repository.
193
195
194 This command takes the following options:
196 This command takes the following options:
195
197
196 :param apiuser: This is filled automatically from the |authtoken|.
198 :param apiuser: This is filled automatically from the |authtoken|.
197 :type apiuser: AuthUser
199 :type apiuser: AuthUser
198 :param older_then: Deletes session that hasn't been accessed
200 :param older_then: Deletes session that hasn't been accessed
199 in given number of days.
201 in given number of days.
200 :type older_then: Optional(int)
202 :type older_then: Optional(int)
201
203
202 Example output:
204 Example output:
203
205
204 .. code-block:: bash
206 .. code-block:: bash
205
207
206 id : <id_given_in_input>
208 id : <id_given_in_input>
207 result: {
209 result: {
208 "backend": "<type of backend>",
210 "backend": "<type of backend>",
209 "sessions_removed": <number_of_removed_sessions>
211 "sessions_removed": <number_of_removed_sessions>
210 }
212 }
211 error : null
213 error : null
212
214
213 Example error output:
215 Example error output:
214
216
215 .. code-block:: bash
217 .. code-block:: bash
216
218
217 id : <id_given_in_input>
219 id : <id_given_in_input>
218 result : null
220 result : null
219 error : {
221 error : {
220 'Error occurred during session cleanup'
222 'Error occurred during session cleanup'
221 }
223 }
222
224
223 """
225 """
224 if not has_superadmin_permission(apiuser):
226 if not has_superadmin_permission(apiuser):
225 raise JSONRPCForbidden()
227 raise JSONRPCForbidden()
226
228
227 older_then = safe_int(Optional.extract(older_then)) or 60
229 older_then = safe_int(Optional.extract(older_then)) or 60
228 older_than_seconds = 60 * 60 * 24 * older_then
230 older_than_seconds = 60 * 60 * 24 * older_then
229
231
230 config = system_info.rhodecode_config().get_value()['value']['config']
232 config = system_info.rhodecode_config().get_value()['value']['config']
231 session_model = user_sessions.get_session_handler(
233 session_model = user_sessions.get_session_handler(
232 config.get('beaker.session.type', 'memory'))(config)
234 config.get('beaker.session.type', 'memory'))(config)
233
235
234 backend = session_model.SESSION_TYPE
236 backend = session_model.SESSION_TYPE
235 try:
237 try:
236 cleaned = session_model.clean_sessions(
238 cleaned = session_model.clean_sessions(
237 older_than_seconds=older_than_seconds)
239 older_than_seconds=older_than_seconds)
238 return {'sessions_removed': cleaned, 'backend': backend}
240 return {'sessions_removed': cleaned, 'backend': backend}
239 except user_sessions.CleanupCommand as msg:
241 except user_sessions.CleanupCommand as msg:
240 return {'cleanup_command': msg.message, 'backend': backend}
242 return {'cleanup_command': msg.message, 'backend': backend}
241 except Exception as e:
243 except Exception as e:
242 log.exception('Failed session cleanup')
244 log.exception('Failed session cleanup')
243 raise JSONRPCError(
245 raise JSONRPCError(
244 'Error occurred during session cleanup'
246 'Error occurred during session cleanup'
245 )
247 )
248
249
250 @jsonrpc_method()
251 def get_method(request, apiuser, pattern=Optional('*')):
252 """
253 Returns list of all available API methods. By default match pattern
254 os "*" but any other pattern can be specified. eg *comment* will return
255 all methods with comment inside them. If just single method is matched
256 returned data will also include method specification
257
258 This command can only be run using an |authtoken| with admin rights to
259 the specified repository.
260
261 This command takes the following options:
262
263 :param apiuser: This is filled automatically from the |authtoken|.
264 :type apiuser: AuthUser
265 :param pattern: pattern to match method names against
266 :type older_then: Optional("*")
267
268 Example output:
269
270 .. code-block:: bash
271
272 id : <id_given_in_input>
273 "result": [
274 "changeset_comment",
275 "comment_pull_request",
276 "comment_commit"
277 ]
278 error : null
279
280 .. code-block:: bash
281
282 id : <id_given_in_input>
283 "result": [
284 "comment_commit",
285 {
286 "apiuser": "<RequiredType>",
287 "comment_type": "<Optional:u'note'>",
288 "commit_id": "<RequiredType>",
289 "message": "<RequiredType>",
290 "repoid": "<RequiredType>",
291 "request": "<RequiredType>",
292 "resolves_comment_id": "<Optional:None>",
293 "status": "<Optional:None>",
294 "userid": "<Optional:<OptionalAttr:apiuser>>"
295 }
296 ]
297 error : null
298 """
299 if not has_superadmin_permission(apiuser):
300 raise JSONRPCForbidden()
301
302 pattern = Optional.extract(pattern)
303
304 matches = find_methods(request.registry.jsonrpc_methods, pattern)
305
306 args_desc = []
307 if len(matches) == 1:
308 func = matches[matches.keys()[0]]
309
310 argspec = inspect.getargspec(func)
311 arglist = argspec[0]
312 defaults = map(repr, argspec[3] or [])
313
314 default_empty = '<RequiredType>'
315
316 # kw arguments required by this method
317 func_kwargs = dict(itertools.izip_longest(
318 reversed(arglist), reversed(defaults), fillvalue=default_empty))
319 args_desc.append(func_kwargs)
320
321 return matches.keys() + args_desc
@@ -1,472 +1,515 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
24 from rhodecode.api.utils import (
24 from rhodecode.api.utils import (
25 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
25 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
26 from rhodecode.lib.auth import AuthUser, PasswordGenerator
26 from rhodecode.lib.auth import AuthUser, PasswordGenerator
27 from rhodecode.lib.exceptions import DefaultUserException
27 from rhodecode.lib.exceptions import DefaultUserException
28 from rhodecode.lib.utils2 import safe_int, str2bool
28 from rhodecode.lib.utils2 import safe_int, str2bool
29 from rhodecode.model.db import Session, User, Repository
29 from rhodecode.model.db import Session, User, Repository
30 from rhodecode.model.user import UserModel
30 from rhodecode.model.user import UserModel
31
31
32
33 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
34
33
35
34
36 @jsonrpc_method()
35 @jsonrpc_method()
37 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
36 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
38 """
37 """
39 Returns the information associated with a username or userid.
38 Returns the information associated with a username or userid.
40
39
41 * If the ``userid`` is not set, this command returns the information
40 * If the ``userid`` is not set, this command returns the information
42 for the ``userid`` calling the method.
41 for the ``userid`` calling the method.
43
42
44 .. note::
43 .. note::
45
44
46 Normal users may only run this command against their ``userid``. For
45 Normal users may only run this command against their ``userid``. For
47 full privileges you must run this command using an |authtoken| with
46 full privileges you must run this command using an |authtoken| with
48 admin rights.
47 admin rights.
49
48
50 :param apiuser: This is filled automatically from the |authtoken|.
49 :param apiuser: This is filled automatically from the |authtoken|.
51 :type apiuser: AuthUser
50 :type apiuser: AuthUser
52 :param userid: Sets the userid for which data will be returned.
51 :param userid: Sets the userid for which data will be returned.
53 :type userid: Optional(str or int)
52 :type userid: Optional(str or int)
54
53
55 Example output:
54 Example output:
56
55
57 .. code-block:: bash
56 .. code-block:: bash
58
57
59 {
58 {
60 "error": null,
59 "error": null,
61 "id": <id>,
60 "id": <id>,
62 "result": {
61 "result": {
63 "active": true,
62 "active": true,
64 "admin": false,
63 "admin": false,
65 "api_key": "api-key",
66 "api_keys": [ list of keys ],
64 "api_keys": [ list of keys ],
65 "auth_tokens": [ list of tokens with details ],
67 "email": "user@example.com",
66 "email": "user@example.com",
68 "emails": [
67 "emails": [
69 "user@example.com"
68 "user@example.com"
70 ],
69 ],
71 "extern_name": "rhodecode",
70 "extern_name": "rhodecode",
72 "extern_type": "rhodecode",
71 "extern_type": "rhodecode",
73 "firstname": "username",
72 "firstname": "username",
74 "ip_addresses": [],
73 "ip_addresses": [],
75 "language": null,
74 "language": null,
76 "last_login": "Timestamp",
75 "last_login": "Timestamp",
76 "last_activity": "Timestamp",
77 "lastname": "surnae",
77 "lastname": "surnae",
78 "permissions": {
78 "permissions": {
79 "global": [
79 "global": [
80 "hg.inherit_default_perms.true",
80 "hg.inherit_default_perms.true",
81 "usergroup.read",
81 "usergroup.read",
82 "hg.repogroup.create.false",
82 "hg.repogroup.create.false",
83 "hg.create.none",
83 "hg.create.none",
84 "hg.password_reset.enabled",
84 "hg.password_reset.enabled",
85 "hg.extern_activate.manual",
85 "hg.extern_activate.manual",
86 "hg.create.write_on_repogroup.false",
86 "hg.create.write_on_repogroup.false",
87 "hg.usergroup.create.false",
87 "hg.usergroup.create.false",
88 "group.none",
88 "group.none",
89 "repository.none",
89 "repository.none",
90 "hg.register.none",
90 "hg.register.none",
91 "hg.fork.repository"
91 "hg.fork.repository"
92 ],
92 ],
93 "repositories": { "username/example": "repository.write"},
93 "repositories": { "username/example": "repository.write"},
94 "repositories_groups": { "user-group/repo": "group.none" },
94 "repositories_groups": { "user-group/repo": "group.none" },
95 "user_groups": { "user_group_name": "usergroup.read" }
95 "user_groups": { "user_group_name": "usergroup.read" }
96 },
96 },
97 "user_id": 32,
97 "user_id": 32,
98 "username": "username"
98 "username": "username"
99 }
99 }
100 }
100 }
101 """
101 """
102
102
103 if not has_superadmin_permission(apiuser):
103 if not has_superadmin_permission(apiuser):
104 # make sure normal user does not pass someone else userid,
104 # make sure normal user does not pass someone else userid,
105 # he is not allowed to do that
105 # he is not allowed to do that
106 if not isinstance(userid, Optional) and userid != apiuser.user_id:
106 if not isinstance(userid, Optional) and userid != apiuser.user_id:
107 raise JSONRPCError('userid is not the same as your user')
107 raise JSONRPCError('userid is not the same as your user')
108
108
109 userid = Optional.extract(userid, evaluate_locals=locals())
109 userid = Optional.extract(userid, evaluate_locals=locals())
110 userid = getattr(userid, 'user_id', userid)
110 userid = getattr(userid, 'user_id', userid)
111
111
112 user = get_user_or_error(userid)
112 user = get_user_or_error(userid)
113 data = user.get_api_data(include_secrets=True)
113 data = user.get_api_data(include_secrets=True)
114 data['permissions'] = AuthUser(user_id=user.user_id).permissions
114 data['permissions'] = AuthUser(user_id=user.user_id).permissions
115 return data
115 return data
116
116
117
117
118 @jsonrpc_method()
118 @jsonrpc_method()
119 def get_users(request, apiuser):
119 def get_users(request, apiuser):
120 """
120 """
121 Lists all users in the |RCE| user database.
121 Lists all users in the |RCE| user database.
122
122
123 This command can only be run using an |authtoken| with admin rights to
123 This command can only be run using an |authtoken| with admin rights to
124 the specified repository.
124 the specified repository.
125
125
126 This command takes the following options:
126 This command takes the following options:
127
127
128 :param apiuser: This is filled automatically from the |authtoken|.
128 :param apiuser: This is filled automatically from the |authtoken|.
129 :type apiuser: AuthUser
129 :type apiuser: AuthUser
130
130
131 Example output:
131 Example output:
132
132
133 .. code-block:: bash
133 .. code-block:: bash
134
134
135 id : <id_given_in_input>
135 id : <id_given_in_input>
136 result: [<user_object>, ...]
136 result: [<user_object>, ...]
137 error: null
137 error: null
138 """
138 """
139
139
140 if not has_superadmin_permission(apiuser):
140 if not has_superadmin_permission(apiuser):
141 raise JSONRPCForbidden()
141 raise JSONRPCForbidden()
142
142
143 result = []
143 result = []
144 users_list = User.query().order_by(User.username) \
144 users_list = User.query().order_by(User.username) \
145 .filter(User.username != User.DEFAULT_USER) \
145 .filter(User.username != User.DEFAULT_USER) \
146 .all()
146 .all()
147 for user in users_list:
147 for user in users_list:
148 result.append(user.get_api_data(include_secrets=True))
148 result.append(user.get_api_data(include_secrets=True))
149 return result
149 return result
150
150
151
151
152 @jsonrpc_method()
152 @jsonrpc_method()
153 def create_user(request, apiuser, username, email, password=Optional(''),
153 def create_user(request, apiuser, username, email, password=Optional(''),
154 firstname=Optional(''), lastname=Optional(''),
154 firstname=Optional(''), lastname=Optional(''),
155 active=Optional(True), admin=Optional(False),
155 active=Optional(True), admin=Optional(False),
156 extern_name=Optional('rhodecode'),
156 extern_name=Optional('rhodecode'),
157 extern_type=Optional('rhodecode'),
157 extern_type=Optional('rhodecode'),
158 force_password_change=Optional(False),
158 force_password_change=Optional(False),
159 create_personal_repo_group=Optional(None)):
159 create_personal_repo_group=Optional(None)):
160 """
160 """
161 Creates a new user and returns the new user object.
161 Creates a new user and returns the new user object.
162
162
163 This command can only be run using an |authtoken| with admin rights to
163 This command can only be run using an |authtoken| with admin rights to
164 the specified repository.
164 the specified repository.
165
165
166 This command takes the following options:
166 This command takes the following options:
167
167
168 :param apiuser: This is filled automatically from the |authtoken|.
168 :param apiuser: This is filled automatically from the |authtoken|.
169 :type apiuser: AuthUser
169 :type apiuser: AuthUser
170 :param username: Set the new username.
170 :param username: Set the new username.
171 :type username: str or int
171 :type username: str or int
172 :param email: Set the user email address.
172 :param email: Set the user email address.
173 :type email: str
173 :type email: str
174 :param password: Set the new user password.
174 :param password: Set the new user password.
175 :type password: Optional(str)
175 :type password: Optional(str)
176 :param firstname: Set the new user firstname.
176 :param firstname: Set the new user firstname.
177 :type firstname: Optional(str)
177 :type firstname: Optional(str)
178 :param lastname: Set the new user surname.
178 :param lastname: Set the new user surname.
179 :type lastname: Optional(str)
179 :type lastname: Optional(str)
180 :param active: Set the user as active.
180 :param active: Set the user as active.
181 :type active: Optional(``True`` | ``False``)
181 :type active: Optional(``True`` | ``False``)
182 :param admin: Give the new user admin rights.
182 :param admin: Give the new user admin rights.
183 :type admin: Optional(``True`` | ``False``)
183 :type admin: Optional(``True`` | ``False``)
184 :param extern_name: Set the authentication plugin name.
184 :param extern_name: Set the authentication plugin name.
185 Using LDAP this is filled with LDAP UID.
185 Using LDAP this is filled with LDAP UID.
186 :type extern_name: Optional(str)
186 :type extern_name: Optional(str)
187 :param extern_type: Set the new user authentication plugin.
187 :param extern_type: Set the new user authentication plugin.
188 :type extern_type: Optional(str)
188 :type extern_type: Optional(str)
189 :param force_password_change: Force the new user to change password
189 :param force_password_change: Force the new user to change password
190 on next login.
190 on next login.
191 :type force_password_change: Optional(``True`` | ``False``)
191 :type force_password_change: Optional(``True`` | ``False``)
192 :param create_personal_repo_group: Create personal repo group for this user
192 :param create_personal_repo_group: Create personal repo group for this user
193 :type create_personal_repo_group: Optional(``True`` | ``False``)
193 :type create_personal_repo_group: Optional(``True`` | ``False``)
194
194 Example output:
195 Example output:
195
196
196 .. code-block:: bash
197 .. code-block:: bash
197
198
198 id : <id_given_in_input>
199 id : <id_given_in_input>
199 result: {
200 result: {
200 "msg" : "created new user `<username>`",
201 "msg" : "created new user `<username>`",
201 "user": <user_obj>
202 "user": <user_obj>
202 }
203 }
203 error: null
204 error: null
204
205
205 Example error output:
206 Example error output:
206
207
207 .. code-block:: bash
208 .. code-block:: bash
208
209
209 id : <id_given_in_input>
210 id : <id_given_in_input>
210 result : null
211 result : null
211 error : {
212 error : {
212 "user `<username>` already exist"
213 "user `<username>` already exist"
213 or
214 or
214 "email `<email>` already exist"
215 "email `<email>` already exist"
215 or
216 or
216 "failed to create user `<username>`"
217 "failed to create user `<username>`"
217 }
218 }
218
219
219 """
220 """
220 if not has_superadmin_permission(apiuser):
221 if not has_superadmin_permission(apiuser):
221 raise JSONRPCForbidden()
222 raise JSONRPCForbidden()
222
223
223 if UserModel().get_by_username(username):
224 if UserModel().get_by_username(username):
224 raise JSONRPCError("user `%s` already exist" % (username,))
225 raise JSONRPCError("user `%s` already exist" % (username,))
225
226
226 if UserModel().get_by_email(email, case_insensitive=True):
227 if UserModel().get_by_email(email, case_insensitive=True):
227 raise JSONRPCError("email `%s` already exist" % (email,))
228 raise JSONRPCError("email `%s` already exist" % (email,))
228
229
229 # generate random password if we actually given the
230 # generate random password if we actually given the
230 # extern_name and it's not rhodecode
231 # extern_name and it's not rhodecode
231 if (not isinstance(extern_name, Optional) and
232 if (not isinstance(extern_name, Optional) and
232 Optional.extract(extern_name) != 'rhodecode'):
233 Optional.extract(extern_name) != 'rhodecode'):
233 # generate temporary password if user is external
234 # generate temporary password if user is external
234 password = PasswordGenerator().gen_password(length=16)
235 password = PasswordGenerator().gen_password(length=16)
235 create_repo_group = Optional.extract(create_personal_repo_group)
236 create_repo_group = Optional.extract(create_personal_repo_group)
236 if isinstance(create_repo_group, basestring):
237 if isinstance(create_repo_group, basestring):
237 create_repo_group = str2bool(create_repo_group)
238 create_repo_group = str2bool(create_repo_group)
238
239
239 try:
240 try:
240 user = UserModel().create_or_update(
241 user = UserModel().create_or_update(
241 username=Optional.extract(username),
242 username=Optional.extract(username),
242 password=Optional.extract(password),
243 password=Optional.extract(password),
243 email=Optional.extract(email),
244 email=Optional.extract(email),
244 firstname=Optional.extract(firstname),
245 firstname=Optional.extract(firstname),
245 lastname=Optional.extract(lastname),
246 lastname=Optional.extract(lastname),
246 active=Optional.extract(active),
247 active=Optional.extract(active),
247 admin=Optional.extract(admin),
248 admin=Optional.extract(admin),
248 extern_type=Optional.extract(extern_type),
249 extern_type=Optional.extract(extern_type),
249 extern_name=Optional.extract(extern_name),
250 extern_name=Optional.extract(extern_name),
250 force_password_change=Optional.extract(force_password_change),
251 force_password_change=Optional.extract(force_password_change),
251 create_repo_group=create_repo_group
252 create_repo_group=create_repo_group
252 )
253 )
253 Session().commit()
254 Session().commit()
254 return {
255 return {
255 'msg': 'created new user `%s`' % username,
256 'msg': 'created new user `%s`' % username,
256 'user': user.get_api_data(include_secrets=True)
257 'user': user.get_api_data(include_secrets=True)
257 }
258 }
258 except Exception:
259 except Exception:
259 log.exception('Error occurred during creation of user')
260 log.exception('Error occurred during creation of user')
260 raise JSONRPCError('failed to create user `%s`' % (username,))
261 raise JSONRPCError('failed to create user `%s`' % (username,))
261
262
262
263
263 @jsonrpc_method()
264 @jsonrpc_method()
264 def update_user(request, apiuser, userid, username=Optional(None),
265 def update_user(request, apiuser, userid, username=Optional(None),
265 email=Optional(None), password=Optional(None),
266 email=Optional(None), password=Optional(None),
266 firstname=Optional(None), lastname=Optional(None),
267 firstname=Optional(None), lastname=Optional(None),
267 active=Optional(None), admin=Optional(None),
268 active=Optional(None), admin=Optional(None),
268 extern_type=Optional(None), extern_name=Optional(None), ):
269 extern_type=Optional(None), extern_name=Optional(None), ):
269 """
270 """
270 Updates the details for the specified user, if that user exists.
271 Updates the details for the specified user, if that user exists.
271
272
272 This command can only be run using an |authtoken| with admin rights to
273 This command can only be run using an |authtoken| with admin rights to
273 the specified repository.
274 the specified repository.
274
275
275 This command takes the following options:
276 This command takes the following options:
276
277
277 :param apiuser: This is filled automatically from |authtoken|.
278 :param apiuser: This is filled automatically from |authtoken|.
278 :type apiuser: AuthUser
279 :type apiuser: AuthUser
279 :param userid: Set the ``userid`` to update.
280 :param userid: Set the ``userid`` to update.
280 :type userid: str or int
281 :type userid: str or int
281 :param username: Set the new username.
282 :param username: Set the new username.
282 :type username: str or int
283 :type username: str or int
283 :param email: Set the new email.
284 :param email: Set the new email.
284 :type email: str
285 :type email: str
285 :param password: Set the new password.
286 :param password: Set the new password.
286 :type password: Optional(str)
287 :type password: Optional(str)
287 :param firstname: Set the new first name.
288 :param firstname: Set the new first name.
288 :type firstname: Optional(str)
289 :type firstname: Optional(str)
289 :param lastname: Set the new surname.
290 :param lastname: Set the new surname.
290 :type lastname: Optional(str)
291 :type lastname: Optional(str)
291 :param active: Set the new user as active.
292 :param active: Set the new user as active.
292 :type active: Optional(``True`` | ``False``)
293 :type active: Optional(``True`` | ``False``)
293 :param admin: Give the user admin rights.
294 :param admin: Give the user admin rights.
294 :type admin: Optional(``True`` | ``False``)
295 :type admin: Optional(``True`` | ``False``)
295 :param extern_name: Set the authentication plugin user name.
296 :param extern_name: Set the authentication plugin user name.
296 Using LDAP this is filled with LDAP UID.
297 Using LDAP this is filled with LDAP UID.
297 :type extern_name: Optional(str)
298 :type extern_name: Optional(str)
298 :param extern_type: Set the authentication plugin type.
299 :param extern_type: Set the authentication plugin type.
299 :type extern_type: Optional(str)
300 :type extern_type: Optional(str)
300
301
301
302
302 Example output:
303 Example output:
303
304
304 .. code-block:: bash
305 .. code-block:: bash
305
306
306 id : <id_given_in_input>
307 id : <id_given_in_input>
307 result: {
308 result: {
308 "msg" : "updated user ID:<userid> <username>",
309 "msg" : "updated user ID:<userid> <username>",
309 "user": <user_object>,
310 "user": <user_object>,
310 }
311 }
311 error: null
312 error: null
312
313
313 Example error output:
314 Example error output:
314
315
315 .. code-block:: bash
316 .. code-block:: bash
316
317
317 id : <id_given_in_input>
318 id : <id_given_in_input>
318 result : null
319 result : null
319 error : {
320 error : {
320 "failed to update user `<username>`"
321 "failed to update user `<username>`"
321 }
322 }
322
323
323 """
324 """
324 if not has_superadmin_permission(apiuser):
325 if not has_superadmin_permission(apiuser):
325 raise JSONRPCForbidden()
326 raise JSONRPCForbidden()
326
327
327 user = get_user_or_error(userid)
328 user = get_user_or_error(userid)
328
329
329 # only non optional arguments will be stored in updates
330 # only non optional arguments will be stored in updates
330 updates = {}
331 updates = {}
331
332
332 try:
333 try:
333
334
334 store_update(updates, username, 'username')
335 store_update(updates, username, 'username')
335 store_update(updates, password, 'password')
336 store_update(updates, password, 'password')
336 store_update(updates, email, 'email')
337 store_update(updates, email, 'email')
337 store_update(updates, firstname, 'name')
338 store_update(updates, firstname, 'name')
338 store_update(updates, lastname, 'lastname')
339 store_update(updates, lastname, 'lastname')
339 store_update(updates, active, 'active')
340 store_update(updates, active, 'active')
340 store_update(updates, admin, 'admin')
341 store_update(updates, admin, 'admin')
341 store_update(updates, extern_name, 'extern_name')
342 store_update(updates, extern_name, 'extern_name')
342 store_update(updates, extern_type, 'extern_type')
343 store_update(updates, extern_type, 'extern_type')
343
344
344 user = UserModel().update_user(user, **updates)
345 user = UserModel().update_user(user, **updates)
345 Session().commit()
346 Session().commit()
346 return {
347 return {
347 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
348 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
348 'user': user.get_api_data(include_secrets=True)
349 'user': user.get_api_data(include_secrets=True)
349 }
350 }
350 except DefaultUserException:
351 except DefaultUserException:
351 log.exception("Default user edit exception")
352 log.exception("Default user edit exception")
352 raise JSONRPCError('editing default user is forbidden')
353 raise JSONRPCError('editing default user is forbidden')
353 except Exception:
354 except Exception:
354 log.exception("Error occurred during update of user")
355 log.exception("Error occurred during update of user")
355 raise JSONRPCError('failed to update user `%s`' % (userid,))
356 raise JSONRPCError('failed to update user `%s`' % (userid,))
356
357
357
358
358 @jsonrpc_method()
359 @jsonrpc_method()
359 def delete_user(request, apiuser, userid):
360 def delete_user(request, apiuser, userid):
360 """
361 """
361 Deletes the specified user from the |RCE| user database.
362 Deletes the specified user from the |RCE| user database.
362
363
363 This command can only be run using an |authtoken| with admin rights to
364 This command can only be run using an |authtoken| with admin rights to
364 the specified repository.
365 the specified repository.
365
366
366 .. important::
367 .. important::
367
368
368 Ensure all open pull requests and open code review
369 Ensure all open pull requests and open code review
369 requests to this user are close.
370 requests to this user are close.
370
371
371 Also ensure all repositories, or repository groups owned by this
372 Also ensure all repositories, or repository groups owned by this
372 user are reassigned before deletion.
373 user are reassigned before deletion.
373
374
374 This command takes the following options:
375 This command takes the following options:
375
376
376 :param apiuser: This is filled automatically from the |authtoken|.
377 :param apiuser: This is filled automatically from the |authtoken|.
377 :type apiuser: AuthUser
378 :type apiuser: AuthUser
378 :param userid: Set the user to delete.
379 :param userid: Set the user to delete.
379 :type userid: str or int
380 :type userid: str or int
380
381
381 Example output:
382 Example output:
382
383
383 .. code-block:: bash
384 .. code-block:: bash
384
385
385 id : <id_given_in_input>
386 id : <id_given_in_input>
386 result: {
387 result: {
387 "msg" : "deleted user ID:<userid> <username>",
388 "msg" : "deleted user ID:<userid> <username>",
388 "user": null
389 "user": null
389 }
390 }
390 error: null
391 error: null
391
392
392 Example error output:
393 Example error output:
393
394
394 .. code-block:: bash
395 .. code-block:: bash
395
396
396 id : <id_given_in_input>
397 id : <id_given_in_input>
397 result : null
398 result : null
398 error : {
399 error : {
399 "failed to delete user ID:<userid> <username>"
400 "failed to delete user ID:<userid> <username>"
400 }
401 }
401
402
402 """
403 """
403 if not has_superadmin_permission(apiuser):
404 if not has_superadmin_permission(apiuser):
404 raise JSONRPCForbidden()
405 raise JSONRPCForbidden()
405
406
406 user = get_user_or_error(userid)
407 user = get_user_or_error(userid)
407
408
408 try:
409 try:
409 UserModel().delete(userid)
410 UserModel().delete(userid)
410 Session().commit()
411 Session().commit()
411 return {
412 return {
412 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
413 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
413 'user': None
414 'user': None
414 }
415 }
415 except Exception:
416 except Exception:
416 log.exception("Error occurred during deleting of user")
417 log.exception("Error occurred during deleting of user")
417 raise JSONRPCError(
418 raise JSONRPCError(
418 'failed to delete user ID:%s %s' % (user.user_id, user.username))
419 'failed to delete user ID:%s %s' % (user.user_id, user.username))
419
420
420
421
421 @jsonrpc_method()
422 @jsonrpc_method()
422 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
423 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
423 """
424 """
424 Displays all repositories locked by the specified user.
425 Displays all repositories locked by the specified user.
425
426
426 * If this command is run by a non-admin user, it returns
427 * If this command is run by a non-admin user, it returns
427 a list of |repos| locked by that user.
428 a list of |repos| locked by that user.
428
429
429 This command takes the following options:
430 This command takes the following options:
430
431
431 :param apiuser: This is filled automatically from the |authtoken|.
432 :param apiuser: This is filled automatically from the |authtoken|.
432 :type apiuser: AuthUser
433 :type apiuser: AuthUser
433 :param userid: Sets the userid whose list of locked |repos| will be
434 :param userid: Sets the userid whose list of locked |repos| will be
434 displayed.
435 displayed.
435 :type userid: Optional(str or int)
436 :type userid: Optional(str or int)
436
437
437 Example output:
438 Example output:
438
439
439 .. code-block:: bash
440 .. code-block:: bash
440
441
441 id : <id_given_in_input>
442 id : <id_given_in_input>
442 result : {
443 result : {
443 [repo_object, repo_object,...]
444 [repo_object, repo_object,...]
444 }
445 }
445 error : null
446 error : null
446 """
447 """
447
448
448 include_secrets = False
449 include_secrets = False
449 if not has_superadmin_permission(apiuser):
450 if not has_superadmin_permission(apiuser):
450 # make sure normal user does not pass someone else userid,
451 # make sure normal user does not pass someone else userid,
451 # he is not allowed to do that
452 # he is not allowed to do that
452 if not isinstance(userid, Optional) and userid != apiuser.user_id:
453 if not isinstance(userid, Optional) and userid != apiuser.user_id:
453 raise JSONRPCError('userid is not the same as your user')
454 raise JSONRPCError('userid is not the same as your user')
454 else:
455 else:
455 include_secrets = True
456 include_secrets = True
456
457
457 userid = Optional.extract(userid, evaluate_locals=locals())
458 userid = Optional.extract(userid, evaluate_locals=locals())
458 userid = getattr(userid, 'user_id', userid)
459 userid = getattr(userid, 'user_id', userid)
459 user = get_user_or_error(userid)
460 user = get_user_or_error(userid)
460
461
461 ret = []
462 ret = []
462
463
463 # show all locks
464 # show all locks
464 for r in Repository.getAll():
465 for r in Repository.getAll():
465 _user_id, _time, _reason = r.locked
466 _user_id, _time, _reason = r.locked
466 if _user_id and _time:
467 if _user_id and _time:
467 _api_data = r.get_api_data(include_secrets=include_secrets)
468 _api_data = r.get_api_data(include_secrets=include_secrets)
468 # if we use user filter just show the locks for this user
469 # if we use user filter just show the locks for this user
469 if safe_int(_user_id) == user.user_id:
470 if safe_int(_user_id) == user.user_id:
470 ret.append(_api_data)
471 ret.append(_api_data)
471
472
472 return ret
473 return ret
474
475
476 @jsonrpc_method()
477 def get_user_audit_logs(request, apiuser, userid=Optional(OAttr('apiuser'))):
478 """
479 Fetches all action logs made by the specified user.
480
481 This command takes the following options:
482
483 :param apiuser: This is filled automatically from the |authtoken|.
484 :type apiuser: AuthUser
485 :param userid: Sets the userid whose list of locked |repos| will be
486 displayed.
487 :type userid: Optional(str or int)
488
489 Example output:
490
491 .. code-block:: bash
492
493 id : <id_given_in_input>
494 result : {
495 [action, action,...]
496 }
497 error : null
498 """
499
500 if not has_superadmin_permission(apiuser):
501 # make sure normal user does not pass someone else userid,
502 # he is not allowed to do that
503 if not isinstance(userid, Optional) and userid != apiuser.user_id:
504 raise JSONRPCError('userid is not the same as your user')
505
506 userid = Optional.extract(userid, evaluate_locals=locals())
507 userid = getattr(userid, 'user_id', userid)
508 user = get_user_or_error(userid)
509
510 ret = []
511
512 # show all user actions
513 for entry in UserModel().get_user_log(user, filter_term=None):
514 ret.append(entry)
515 return ret
@@ -1,57 +1,99 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.admin.navigation import NavigationRegistry
22 from rhodecode.apps.admin.navigation import NavigationRegistry
23 from rhodecode.config.routing import ADMIN_PREFIX
23 from rhodecode.config.routing import ADMIN_PREFIX
24 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25
25
26
26
27 def admin_routes(config):
28 """
29 Admin prefixed routes
30 """
31
32 config.add_route(
33 name='admin_settings_open_source',
34 pattern='/settings/open_source')
35 config.add_route(
36 name='admin_settings_vcs_svn_generate_cfg',
37 pattern='/settings/vcs/svn_generate_cfg')
38
39 config.add_route(
40 name='admin_settings_system',
41 pattern='/settings/system')
42 config.add_route(
43 name='admin_settings_system_update',
44 pattern='/settings/system/updates')
45
46 config.add_route(
47 name='admin_settings_sessions',
48 pattern='/settings/sessions')
49 config.add_route(
50 name='admin_settings_sessions_cleanup',
51 pattern='/settings/sessions/cleanup')
52
53 # users admin
54 config.add_route(
55 name='users',
56 pattern='/users')
57
58 config.add_route(
59 name='users_data',
60 pattern='/users_data')
61
62 # user auth tokens
63 config.add_route(
64 name='edit_user_auth_tokens',
65 pattern='/users/{user_id:\d+}/edit/auth_tokens')
66 config.add_route(
67 name='edit_user_auth_tokens_add',
68 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
69 config.add_route(
70 name='edit_user_auth_tokens_delete',
71 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
72
73 # user groups management
74 config.add_route(
75 name='edit_user_groups_management',
76 pattern='/users/{user_id:\d+}/edit/groups_management')
77
78 config.add_route(
79 name='edit_user_groups_management_updates',
80 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
81
82 # user audit logs
83 config.add_route(
84 name='edit_user_audit_logs',
85 pattern='/users/{user_id:\d+}/edit/audit')
86
87
27 def includeme(config):
88 def includeme(config):
28 settings = config.get_settings()
89 settings = config.get_settings()
29
90
30 # Create admin navigation registry and add it to the pyramid registry.
91 # Create admin navigation registry and add it to the pyramid registry.
31 labs_active = str2bool(settings.get('labs_settings_active', False))
92 labs_active = str2bool(settings.get('labs_settings_active', False))
32 navigation_registry = NavigationRegistry(labs_active=labs_active)
93 navigation_registry = NavigationRegistry(labs_active=labs_active)
33 config.registry.registerUtility(navigation_registry)
94 config.registry.registerUtility(navigation_registry)
34
95
35 config.add_route(
96 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
36 name='admin_settings_open_source',
37 pattern=ADMIN_PREFIX + '/settings/open_source')
38 config.add_route(
39 name='admin_settings_vcs_svn_generate_cfg',
40 pattern=ADMIN_PREFIX + '/settings/vcs/svn_generate_cfg')
41
42 config.add_route(
43 name='admin_settings_system',
44 pattern=ADMIN_PREFIX + '/settings/system')
45 config.add_route(
46 name='admin_settings_system_update',
47 pattern=ADMIN_PREFIX + '/settings/system/updates')
48
49 config.add_route(
50 name='admin_settings_sessions',
51 pattern=ADMIN_PREFIX + '/settings/sessions')
52 config.add_route(
53 name='admin_settings_sessions_cleanup',
54 pattern=ADMIN_PREFIX + '/settings/sessions/cleanup')
55
97
56 # Scan module for configuration decorators.
98 # Scan module for configuration decorators.
57 config.scan()
99 config.scan()
1 NO CONTENT: file renamed from rhodecode/admin/interfaces.py to rhodecode/apps/admin/interfaces.py
NO CONTENT: file renamed from rhodecode/admin/interfaces.py to rhodecode/apps/admin/interfaces.py
@@ -1,140 +1,140 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pylons import url
25 from pylons import url
26 from zope.interface import implementer
26 from zope.interface import implementer
27
27
28 from rhodecode.admin.interfaces import IAdminNavigationRegistry
28 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
29 from rhodecode.lib.utils import get_registry
29 from rhodecode.lib.utils import get_registry
30 from rhodecode.translation import _
30 from rhodecode.translation import _
31
31
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
36
36
37
37
38 class NavEntry(object):
38 class NavEntry(object):
39 """
39 """
40 Represents an entry in the admin navigation.
40 Represents an entry in the admin navigation.
41
41
42 :param key: Unique identifier used to store reference in an OrderedDict.
42 :param key: Unique identifier used to store reference in an OrderedDict.
43 :param name: Display name, usually a translation string.
43 :param name: Display name, usually a translation string.
44 :param view_name: Name of the view, used generate the URL.
44 :param view_name: Name of the view, used generate the URL.
45 :param pyramid: Indicator to use pyramid for URL generation. This should
45 :param pyramid: Indicator to use pyramid for URL generation. This should
46 be removed as soon as we are fully migrated to pyramid.
46 be removed as soon as we are fully migrated to pyramid.
47 """
47 """
48
48
49 def __init__(self, key, name, view_name, pyramid=False):
49 def __init__(self, key, name, view_name, pyramid=False):
50 self.key = key
50 self.key = key
51 self.name = name
51 self.name = name
52 self.view_name = view_name
52 self.view_name = view_name
53 self.pyramid = pyramid
53 self.pyramid = pyramid
54
54
55 def generate_url(self, request):
55 def generate_url(self, request):
56 if self.pyramid:
56 if self.pyramid:
57 if hasattr(request, 'route_path'):
57 if hasattr(request, 'route_path'):
58 return request.route_path(self.view_name)
58 return request.route_path(self.view_name)
59 else:
59 else:
60 # TODO: johbo: Remove this after migrating to pyramid.
60 # TODO: johbo: Remove this after migrating to pyramid.
61 # We need the pyramid request here to generate URLs to pyramid
61 # We need the pyramid request here to generate URLs to pyramid
62 # views from within pylons views.
62 # views from within pylons views.
63 from pyramid.threadlocal import get_current_request
63 from pyramid.threadlocal import get_current_request
64 pyramid_request = get_current_request()
64 pyramid_request = get_current_request()
65 return pyramid_request.route_path(self.view_name)
65 return pyramid_request.route_path(self.view_name)
66 else:
66 else:
67 return url(self.view_name)
67 return url(self.view_name)
68
68
69 def get_localized_name(self, request):
69 def get_localized_name(self, request):
70 if hasattr(request, 'translate'):
70 if hasattr(request, 'translate'):
71 return request.translate(self.name)
71 return request.translate(self.name)
72 else:
72 else:
73 # TODO(marcink): Remove this after migrating to pyramid
73 # TODO(marcink): Remove this after migrating to pyramid
74 from pyramid.threadlocal import get_current_request
74 from pyramid.threadlocal import get_current_request
75 pyramid_request = get_current_request()
75 pyramid_request = get_current_request()
76 return pyramid_request.translate(self.name)
76 return pyramid_request.translate(self.name)
77
77
78
78
79 @implementer(IAdminNavigationRegistry)
79 @implementer(IAdminNavigationRegistry)
80 class NavigationRegistry(object):
80 class NavigationRegistry(object):
81
81
82 _base_entries = [
82 _base_entries = [
83 NavEntry('global', _('Global'), 'admin_settings_global'),
83 NavEntry('global', _('Global'), 'admin_settings_global'),
84 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
84 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
85 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
85 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
86 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
86 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
87 NavEntry('issuetracker', _('Issue Tracker'),
87 NavEntry('issuetracker', _('Issue Tracker'),
88 'admin_settings_issuetracker'),
88 'admin_settings_issuetracker'),
89 NavEntry('email', _('Email'), 'admin_settings_email'),
89 NavEntry('email', _('Email'), 'admin_settings_email'),
90 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
90 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
91 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
91 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
92
92
93 NavEntry('integrations', _('Integrations'),
93 NavEntry('integrations', _('Integrations'),
94 'global_integrations_home', pyramid=True),
94 'global_integrations_home', pyramid=True),
95 NavEntry('system', _('System Info'),
95 NavEntry('system', _('System Info'),
96 'admin_settings_system', pyramid=True),
96 'admin_settings_system', pyramid=True),
97 NavEntry('sessions', _('User Sessions'),
97 NavEntry('sessions', _('User Sessions'),
98 'admin_settings_sessions', pyramid=True),
98 'admin_settings_sessions', pyramid=True),
99 NavEntry('open_source', _('Open Source Licenses'),
99 NavEntry('open_source', _('Open Source Licenses'),
100 'admin_settings_open_source', pyramid=True),
100 'admin_settings_open_source', pyramid=True),
101
101
102 # TODO: marcink: we disable supervisor now until the supervisor stats
102 # TODO: marcink: we disable supervisor now until the supervisor stats
103 # page is fixed in the nix configuration
103 # page is fixed in the nix configuration
104 # NavEntry('supervisor', _('Supervisor'), 'admin_settings_supervisor'),
104 # NavEntry('supervisor', _('Supervisor'), 'admin_settings_supervisor'),
105 ]
105 ]
106
106
107 _labs_entry = NavEntry('labs', _('Labs'), 'admin_settings_labs')
107 _labs_entry = NavEntry('labs', _('Labs'), 'admin_settings_labs')
108
108
109 def __init__(self, labs_active=False):
109 def __init__(self, labs_active=False):
110 self._registered_entries = collections.OrderedDict([
110 self._registered_entries = collections.OrderedDict([
111 (item.key, item) for item in self.__class__._base_entries
111 (item.key, item) for item in self.__class__._base_entries
112 ])
112 ])
113
113
114 if labs_active:
114 if labs_active:
115 self.add_entry(self._labs_entry)
115 self.add_entry(self._labs_entry)
116
116
117 def add_entry(self, entry):
117 def add_entry(self, entry):
118 self._registered_entries[entry.key] = entry
118 self._registered_entries[entry.key] = entry
119
119
120 def get_navlist(self, request):
120 def get_navlist(self, request):
121 navlist = [NavListEntry(i.key, i.get_localized_name(request),
121 navlist = [NavListEntry(i.key, i.get_localized_name(request),
122 i.generate_url(request))
122 i.generate_url(request))
123 for i in self._registered_entries.values()]
123 for i in self._registered_entries.values()]
124 return navlist
124 return navlist
125
125
126
126
127 def navigation_registry(request):
127 def navigation_registry(request):
128 """
128 """
129 Helper that returns the admin navigation registry.
129 Helper that returns the admin navigation registry.
130 """
130 """
131 pyramid_registry = get_registry(request)
131 pyramid_registry = get_registry(request)
132 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
132 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
133 return nav_registry
133 return nav_registry
134
134
135
135
136 def navigation_list(request):
136 def navigation_list(request):
137 """
137 """
138 Helper that returns the admin navigation as list of NavListEntry objects.
138 Helper that returns the admin navigation as list of NavListEntry objects.
139 """
139 """
140 return navigation_registry(request).get_navlist(request)
140 return navigation_registry(request).get_navlist(request)
1 NO CONTENT: file renamed from rhodecode/admin/views/__init__.py to rhodecode/apps/admin/views/__init__.py
NO CONTENT: file renamed from rhodecode/admin/views/__init__.py to rhodecode/apps/admin/views/__init__.py
@@ -1,48 +1,48 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import collections
21 import collections
22 import logging
22 import logging
23
23
24 from pylons import tmpl_context as c
24 from pylons import tmpl_context as c
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.admin.views.base import AdminSettingsView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.admin.navigation import navigation_list
28 from rhodecode.apps.admin.navigation import navigation_list
29 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
29 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 from rhodecode.lib.utils import read_opensource_licenses
30 from rhodecode.lib.utils import read_opensource_licenses
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 class OpenSourceLicensesAdminSettingsView(AdminSettingsView):
35 class OpenSourceLicensesAdminSettingsView(BaseAppView):
36
36
37 @LoginRequired()
37 @LoginRequired()
38 @HasPermissionAllDecorator('hg.admin')
38 @HasPermissionAllDecorator('hg.admin')
39 @view_config(
39 @view_config(
40 route_name='admin_settings_open_source', request_method='GET',
40 route_name='admin_settings_open_source', request_method='GET',
41 renderer='rhodecode:templates/admin/settings/settings.mako')
41 renderer='rhodecode:templates/admin/settings/settings.mako')
42 def open_source_licenses(self):
42 def open_source_licenses(self):
43 c.active = 'open_source'
43 c.active = 'open_source'
44 c.navlist = navigation_list(self.request)
44 c.navlist = navigation_list(self.request)
45 c.opensource_licenses = collections.OrderedDict(
45 c.opensource_licenses = collections.OrderedDict(
46 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
46 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
47
47
48 return {}
48 return {}
@@ -1,100 +1,96 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pylons import tmpl_context as c
23 from pylons import tmpl_context as c
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
26
26
27 from rhodecode.translation import _
27 from rhodecode.apps._base import BaseAppView
28
28 from rhodecode.apps.admin.navigation import navigation_list
29 from rhodecode.admin.views.base import AdminSettingsView
30 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib.utils2 import safe_int
33 from rhodecode.lib import system_info
32 from rhodecode.lib import system_info
34 from rhodecode.lib import user_sessions
33 from rhodecode.lib import user_sessions
35
34
36
35
37 from rhodecode.admin.navigation import navigation_list
38
39
40 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
41
37
42
38
43 class AdminSessionSettingsView(AdminSettingsView):
39 class AdminSessionSettingsView(BaseAppView):
44
40
45 @LoginRequired()
41 @LoginRequired()
46 @HasPermissionAllDecorator('hg.admin')
42 @HasPermissionAllDecorator('hg.admin')
47 @view_config(
43 @view_config(
48 route_name='admin_settings_sessions', request_method='GET',
44 route_name='admin_settings_sessions', request_method='GET',
49 renderer='rhodecode:templates/admin/settings/settings.mako')
45 renderer='rhodecode:templates/admin/settings/settings.mako')
50 def settings_sessions(self):
46 def settings_sessions(self):
51 c.active = 'sessions'
47 c.active = 'sessions'
52 c.navlist = navigation_list(self.request)
48 c.navlist = navigation_list(self.request)
53
49
54 c.cleanup_older_days = 60
50 c.cleanup_older_days = 60
55 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
51 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
56
52
57 config = system_info.rhodecode_config().get_value()['value']['config']
53 config = system_info.rhodecode_config().get_value()['value']['config']
58 c.session_model = user_sessions.get_session_handler(
54 c.session_model = user_sessions.get_session_handler(
59 config.get('beaker.session.type', 'memory'))(config)
55 config.get('beaker.session.type', 'memory'))(config)
60
56
61 c.session_conf = c.session_model.config
57 c.session_conf = c.session_model.config
62 c.session_count = c.session_model.get_count()
58 c.session_count = c.session_model.get_count()
63 c.session_expired_count = c.session_model.get_expired_count(
59 c.session_expired_count = c.session_model.get_expired_count(
64 older_than_seconds)
60 older_than_seconds)
65
61
66 return {}
62 return {}
67
63
68 @LoginRequired()
64 @LoginRequired()
69 @CSRFRequired()
65 @CSRFRequired()
70 @HasPermissionAllDecorator('hg.admin')
66 @HasPermissionAllDecorator('hg.admin')
71 @view_config(
67 @view_config(
72 route_name='admin_settings_sessions_cleanup', request_method='POST')
68 route_name='admin_settings_sessions_cleanup', request_method='POST')
73 def settings_sessions_cleanup(self):
69 def settings_sessions_cleanup(self):
74 _ = self.request.translate
70 _ = self.request.translate
75 expire_days = safe_int(self.request.params.get('expire_days'))
71 expire_days = safe_int(self.request.params.get('expire_days'))
76
72
77 if expire_days is None:
73 if expire_days is None:
78 expire_days = 60
74 expire_days = 60
79
75
80 older_than_seconds = 60 * 60 * 24 * expire_days
76 older_than_seconds = 60 * 60 * 24 * expire_days
81
77
82 config = system_info.rhodecode_config().get_value()['value']['config']
78 config = system_info.rhodecode_config().get_value()['value']['config']
83 session_model = user_sessions.get_session_handler(
79 session_model = user_sessions.get_session_handler(
84 config.get('beaker.session.type', 'memory'))(config)
80 config.get('beaker.session.type', 'memory'))(config)
85
81
86 try:
82 try:
87 session_model.clean_sessions(
83 session_model.clean_sessions(
88 older_than_seconds=older_than_seconds)
84 older_than_seconds=older_than_seconds)
89 self.request.session.flash(
85 self.request.session.flash(
90 _('Cleaned up old sessions'), queue='success')
86 _('Cleaned up old sessions'), queue='success')
91 except user_sessions.CleanupCommand as msg:
87 except user_sessions.CleanupCommand as msg:
92 self.request.session.flash(msg.message, queue='warning')
88 self.request.session.flash(msg.message, queue='warning')
93 except Exception as e:
89 except Exception as e:
94 log.exception('Failed session cleanup')
90 log.exception('Failed session cleanup')
95 self.request.session.flash(
91 self.request.session.flash(
96 _('Failed to cleanup up old sessions'), queue='error')
92 _('Failed to cleanup up old sessions'), queue='error')
97
93
98 redirect_to = self.request.resource_path(
94 redirect_to = self.request.resource_path(
99 self.context, route_name='admin_settings_sessions')
95 self.context, route_name='admin_settings_sessions')
100 return HTTPFound(redirect_to)
96 return HTTPFound(redirect_to)
@@ -1,60 +1,59 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24
24
25 from rhodecode.svn_support.utils import generate_mod_dav_svn_config
25 from rhodecode.apps._base import BaseAppView
26
26 from rhodecode.apps.svn_support.utils import generate_mod_dav_svn_config
27 from rhodecode.admin.views.base import AdminSettingsView
28 from rhodecode.lib.auth import (
27 from rhodecode.lib.auth import (
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30
29
31 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
32
31
33
32
34 class SvnConfigAdminSettingsView(AdminSettingsView):
33 class SvnConfigAdminSettingsView(BaseAppView):
35
34
36 @LoginRequired()
35 @LoginRequired()
37 @CSRFRequired()
36 @CSRFRequired()
38 @HasPermissionAllDecorator('hg.admin')
37 @HasPermissionAllDecorator('hg.admin')
39 @view_config(
38 @view_config(
40 route_name='admin_settings_vcs_svn_generate_cfg',
39 route_name='admin_settings_vcs_svn_generate_cfg',
41 request_method='POST', renderer='json')
40 request_method='POST', renderer='json')
42 def vcs_svn_generate_config(self):
41 def vcs_svn_generate_config(self):
43 _ = self.request.translate
42 _ = self.request.translate
44 try:
43 try:
45 generate_mod_dav_svn_config(self.request.registry)
44 generate_mod_dav_svn_config(self.request.registry)
46 msg = {
45 msg = {
47 'message': _('Apache configuration for Subversion generated.'),
46 'message': _('Apache configuration for Subversion generated.'),
48 'level': 'success',
47 'level': 'success',
49 }
48 }
50 except Exception:
49 except Exception:
51 log.exception(
50 log.exception(
52 'Exception while generating the Apache '
51 'Exception while generating the Apache '
53 'configuration for Subversion.')
52 'configuration for Subversion.')
54 msg = {
53 msg = {
55 'message': _('Failed to generate the Apache configuration for Subversion.'),
54 'message': _('Failed to generate the Apache configuration for Subversion.'),
56 'level': 'error',
55 'level': 'error',
57 }
56 }
58
57
59 data = {'message': msg}
58 data = {'message': msg}
60 return data
59 return data
@@ -1,202 +1,203 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import urllib2
22 import urllib2
23 import packaging.version
23 import packaging.version
24
24
25 from pylons import tmpl_context as c
25 from pylons import tmpl_context as c
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27
27
28 import rhodecode
28 import rhodecode
29 from rhodecode.apps._base import BaseAppView
30 from rhodecode.apps.admin.navigation import navigation_list
29 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
32 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
31 from rhodecode.lib.utils2 import str2bool
33 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.lib import system_info
34 from rhodecode.lib import system_info
33 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
34
35 from rhodecode.admin.views.base import AdminSettingsView
36 from rhodecode.admin.navigation import navigation_list
37 from rhodecode.model.settings import SettingsModel
36 from rhodecode.model.settings import SettingsModel
38
37
39 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
40
39
41
40
42 class AdminSystemInfoSettingsView(AdminSettingsView):
41 class AdminSystemInfoSettingsView(BaseAppView):
43
42
44 @staticmethod
43 @staticmethod
45 def get_update_data(update_url):
44 def get_update_data(update_url):
46 """Return the JSON update data."""
45 """Return the JSON update data."""
47 ver = rhodecode.__version__
46 ver = rhodecode.__version__
48 log.debug('Checking for upgrade on `%s` server', update_url)
47 log.debug('Checking for upgrade on `%s` server', update_url)
49 opener = urllib2.build_opener()
48 opener = urllib2.build_opener()
50 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
49 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
51 response = opener.open(update_url)
50 response = opener.open(update_url)
52 response_data = response.read()
51 response_data = response.read()
53 data = json.loads(response_data)
52 data = json.loads(response_data)
54
53
55 return data
54 return data
56
55
57 def get_update_url(self):
56 def get_update_url(self):
58 settings = SettingsModel().get_all_settings()
57 settings = SettingsModel().get_all_settings()
59 return settings.get('rhodecode_update_url')
58 return settings.get('rhodecode_update_url')
60
59
61 @LoginRequired()
60 @LoginRequired()
62 @HasPermissionAllDecorator('hg.admin')
61 @HasPermissionAllDecorator('hg.admin')
63 @view_config(
62 @view_config(
64 route_name='admin_settings_system', request_method='GET',
63 route_name='admin_settings_system', request_method='GET',
65 renderer='rhodecode:templates/admin/settings/settings.mako')
64 renderer='rhodecode:templates/admin/settings/settings.mako')
66 def settings_system_info(self):
65 def settings_system_info(self):
67 _ = self.request.translate
66 _ = self.request.translate
68
67
69 c.active = 'system'
68 c.active = 'system'
70 c.navlist = navigation_list(self.request)
69 c.navlist = navigation_list(self.request)
71
70
72 # TODO(marcink), figure out how to allow only selected users to do this
71 # TODO(marcink), figure out how to allow only selected users to do this
73 c.allowed_to_snapshot = self._rhodecode_user.admin
72 c.allowed_to_snapshot = self._rhodecode_user.admin
74
73
75 snapshot = str2bool(self.request.params.get('snapshot'))
74 snapshot = str2bool(self.request.params.get('snapshot'))
76
75
77 c.rhodecode_update_url = self.get_update_url()
76 c.rhodecode_update_url = self.get_update_url()
78 server_info = system_info.get_system_info(self.request.environ)
77 server_info = system_info.get_system_info(self.request.environ)
79
78
80 for key, val in server_info.items():
79 for key, val in server_info.items():
81 setattr(c, key, val)
80 setattr(c, key, val)
82
81
83 def val(name, subkey='human_value'):
82 def val(name, subkey='human_value'):
84 return server_info[name][subkey]
83 return server_info[name][subkey]
85
84
86 def state(name):
85 def state(name):
87 return server_info[name]['state']
86 return server_info[name]['state']
88
87
89 def val2(name):
88 def val2(name):
90 val = server_info[name]['human_value']
89 val = server_info[name]['human_value']
91 state = server_info[name]['state']
90 state = server_info[name]['state']
92 return val, state
91 return val, state
93
92
94 update_info_msg = _('Note: please make sure this server can '
93 update_info_msg = _('Note: please make sure this server can '
95 'access `${url}` for the update link to work',
94 'access `${url}` for the update link to work',
96 mapping=dict(url=c.rhodecode_update_url))
95 mapping=dict(url=c.rhodecode_update_url))
97 c.data_items = [
96 c.data_items = [
98 # update info
97 # update info
99 (_('Update info'), h.literal(
98 (_('Update info'), h.literal(
100 '<span class="link" id="check_for_update" >%s.</span>' % (
99 '<span class="link" id="check_for_update" >%s.</span>' % (
101 _('Check for updates')) +
100 _('Check for updates')) +
102 '<br/> <span >%s.</span>' % (update_info_msg)
101 '<br/> <span >%s.</span>' % (update_info_msg)
103 ), ''),
102 ), ''),
104
103
105 # RhodeCode specific
104 # RhodeCode specific
106 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
105 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
107 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
106 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
108 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
107 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
109 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
108 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
109 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
110 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
110 ('', '', ''), # spacer
111 ('', '', ''), # spacer
111
112
112 # Database
113 # Database
113 (_('Database'), val('database')['url'], state('database')),
114 (_('Database'), val('database')['url'], state('database')),
114 (_('Database version'), val('database')['version'], state('database')),
115 (_('Database version'), val('database')['version'], state('database')),
115 ('', '', ''), # spacer
116 ('', '', ''), # spacer
116
117
117 # Platform/Python
118 # Platform/Python
118 (_('Platform'), val('platform')['name'], state('platform')),
119 (_('Platform'), val('platform')['name'], state('platform')),
119 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
120 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
120 (_('Python version'), val('python')['version'], state('python')),
121 (_('Python version'), val('python')['version'], state('python')),
121 (_('Python path'), val('python')['executable'], state('python')),
122 (_('Python path'), val('python')['executable'], state('python')),
122 ('', '', ''), # spacer
123 ('', '', ''), # spacer
123
124
124 # Systems stats
125 # Systems stats
125 (_('CPU'), val('cpu'), state('cpu')),
126 (_('CPU'), val('cpu')['text'], state('cpu')),
126 (_('Load'), val('load')['text'], state('load')),
127 (_('Load'), val('load')['text'], state('load')),
127 (_('Memory'), val('memory')['text'], state('memory')),
128 (_('Memory'), val('memory')['text'], state('memory')),
128 (_('Uptime'), val('uptime')['text'], state('uptime')),
129 (_('Uptime'), val('uptime')['text'], state('uptime')),
129 ('', '', ''), # spacer
130 ('', '', ''), # spacer
130
131
131 # Repo storage
132 # Repo storage
132 (_('Storage location'), val('storage')['path'], state('storage')),
133 (_('Storage location'), val('storage')['path'], state('storage')),
133 (_('Storage info'), val('storage')['text'], state('storage')),
134 (_('Storage info'), val('storage')['text'], state('storage')),
134 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
135 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
135
136
136 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
137 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
137 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
138 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
138
139
139 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
140 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
140 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
141 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
141
142
142 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
143 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
143 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
144 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
144
145
145 (_('Search info'), val('search')['text'], state('search')),
146 (_('Search info'), val('search')['text'], state('search')),
146 (_('Search location'), val('search')['location'], state('search')),
147 (_('Search location'), val('search')['location'], state('search')),
147 ('', '', ''), # spacer
148 ('', '', ''), # spacer
148
149
149 # VCS specific
150 # VCS specific
150 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
151 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
151 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
152 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
152 (_('GIT'), val('git'), state('git')),
153 (_('GIT'), val('git'), state('git')),
153 (_('HG'), val('hg'), state('hg')),
154 (_('HG'), val('hg'), state('hg')),
154 (_('SVN'), val('svn'), state('svn')),
155 (_('SVN'), val('svn'), state('svn')),
155
156
156 ]
157 ]
157
158
158 if snapshot:
159 if snapshot:
159 if c.allowed_to_snapshot:
160 if c.allowed_to_snapshot:
160 c.data_items.pop(0) # remove server info
161 c.data_items.pop(0) # remove server info
161 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
162 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
162 else:
163 else:
163 self.request.session.flash(
164 self.request.session.flash(
164 'You are not allowed to do this', queue='warning')
165 'You are not allowed to do this', queue='warning')
165 return {}
166 return {}
166
167
167 @LoginRequired()
168 @LoginRequired()
168 @HasPermissionAllDecorator('hg.admin')
169 @HasPermissionAllDecorator('hg.admin')
169 @view_config(
170 @view_config(
170 route_name='admin_settings_system_update', request_method='GET',
171 route_name='admin_settings_system_update', request_method='GET',
171 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
172 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
172 def settings_system_info_check_update(self):
173 def settings_system_info_check_update(self):
173 _ = self.request.translate
174 _ = self.request.translate
174
175
175 update_url = self.get_update_url()
176 update_url = self.get_update_url()
176
177
177 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
178 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
178 try:
179 try:
179 data = self.get_update_data(update_url)
180 data = self.get_update_data(update_url)
180 except urllib2.URLError as e:
181 except urllib2.URLError as e:
181 log.exception("Exception contacting upgrade server")
182 log.exception("Exception contacting upgrade server")
182 self.request.override_renderer = 'string'
183 self.request.override_renderer = 'string'
183 return _err('Failed to contact upgrade server: %r' % e)
184 return _err('Failed to contact upgrade server: %r' % e)
184 except ValueError as e:
185 except ValueError as e:
185 log.exception("Bad data sent from update server")
186 log.exception("Bad data sent from update server")
186 self.request.override_renderer = 'string'
187 self.request.override_renderer = 'string'
187 return _err('Bad data sent from update server')
188 return _err('Bad data sent from update server')
188
189
189 latest = data['versions'][0]
190 latest = data['versions'][0]
190
191
191 c.update_url = update_url
192 c.update_url = update_url
192 c.latest_data = latest
193 c.latest_data = latest
193 c.latest_ver = latest['version']
194 c.latest_ver = latest['version']
194 c.cur_ver = rhodecode.__version__
195 c.cur_ver = rhodecode.__version__
195 c.should_upgrade = False
196 c.should_upgrade = False
196
197
197 if (packaging.version.Version(c.latest_ver) >
198 if (packaging.version.Version(c.latest_ver) >
198 packaging.version.Version(c.cur_ver)):
199 packaging.version.Version(c.cur_ver)):
199 c.should_upgrade = True
200 c.should_upgrade = True
200 c.important_notices = latest['general']
201 c.important_notices = latest['general']
201
202
202 return {}
203 return {}
@@ -1,88 +1,90 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22
22
23 from pyramid.settings import asbool
23 from pyramid.settings import asbool
24
24
25 from rhodecode.config.routing import ADMIN_PREFIX
25 from rhodecode.config.routing import ADMIN_PREFIX
26 from rhodecode.lib.ext_json import json
26 from rhodecode.lib.ext_json import json
27
27
28
28
29 def url_gen(request):
29 def url_gen(request):
30 registry = request.registry
30 registry = request.registry
31 longpoll_url = registry.settings.get('channelstream.longpoll_url', '')
31 longpoll_url = registry.settings.get('channelstream.longpoll_url', '')
32 ws_url = registry.settings.get('channelstream.ws_url', '')
32 ws_url = registry.settings.get('channelstream.ws_url', '')
33 proxy_url = request.route_url('channelstream_proxy')
33 proxy_url = request.route_url('channelstream_proxy')
34 urls = {
34 urls = {
35 'connect': request.route_path('channelstream_connect'),
35 'connect': request.route_path('channelstream_connect'),
36 'subscribe': request.route_path('channelstream_subscribe'),
36 'subscribe': request.route_path('channelstream_subscribe'),
37 'longpoll': longpoll_url or proxy_url,
37 'longpoll': longpoll_url or proxy_url,
38 'ws': ws_url or proxy_url.replace('http', 'ws')
38 'ws': ws_url or proxy_url.replace('http', 'ws')
39 }
39 }
40 return json.dumps(urls)
40 return json.dumps(urls)
41
41
42
42
43 PLUGIN_DEFINITION = {
43 PLUGIN_DEFINITION = {
44 'name': 'channelstream',
44 'name': 'channelstream',
45 'config': {
45 'config': {
46 'javascript': [],
46 'javascript': [],
47 'css': [],
47 'css': [],
48 'template_hooks': {
48 'template_hooks': {
49 'plugin_init_template': 'rhodecode:templates/channelstream/plugin_init.mako'
49 'plugin_init_template': 'rhodecode:templates/channelstream/plugin_init.mako'
50 },
50 },
51 'url_gen': url_gen,
51 'url_gen': url_gen,
52 'static': None,
52 'static': None,
53 'enabled': False,
53 'enabled': False,
54 'server': '',
54 'server': '',
55 'secret': ''
55 'secret': ''
56 }
56 }
57 }
57 }
58
58
59
59
60 def includeme(config):
60 def includeme(config):
61 settings = config.registry.settings
61 settings = config.registry.settings
62 PLUGIN_DEFINITION['config']['enabled'] = asbool(
62 PLUGIN_DEFINITION['config']['enabled'] = asbool(
63 settings.get('channelstream.enabled'))
63 settings.get('channelstream.enabled'))
64 PLUGIN_DEFINITION['config']['server'] = settings.get(
64 PLUGIN_DEFINITION['config']['server'] = settings.get(
65 'channelstream.server', '')
65 'channelstream.server', '')
66 PLUGIN_DEFINITION['config']['secret'] = settings.get(
66 PLUGIN_DEFINITION['config']['secret'] = settings.get(
67 'channelstream.secret', '')
67 'channelstream.secret', '')
68 PLUGIN_DEFINITION['config']['history.location'] = settings.get(
68 PLUGIN_DEFINITION['config']['history.location'] = settings.get(
69 'channelstream.history.location', '')
69 'channelstream.history.location', '')
70 config.register_rhodecode_plugin(
70 config.register_rhodecode_plugin(
71 PLUGIN_DEFINITION['name'],
71 PLUGIN_DEFINITION['name'],
72 PLUGIN_DEFINITION['config']
72 PLUGIN_DEFINITION['config']
73 )
73 )
74 # create plugin history location
74 # create plugin history location
75 history_dir = PLUGIN_DEFINITION['config']['history.location']
75 history_dir = PLUGIN_DEFINITION['config']['history.location']
76 if history_dir and not os.path.exists(history_dir):
76 if history_dir and not os.path.exists(history_dir):
77 os.makedirs(history_dir, 0750)
77 os.makedirs(history_dir, 0750)
78
78
79 config.add_route(
79 config.add_route(
80 name='channelstream_connect',
80 name='channelstream_connect',
81 pattern=ADMIN_PREFIX + '/channelstream/connect')
81 pattern=ADMIN_PREFIX + '/channelstream/connect')
82 config.add_route(
82 config.add_route(
83 name='channelstream_subscribe',
83 name='channelstream_subscribe',
84 pattern=ADMIN_PREFIX + '/channelstream/subscribe')
84 pattern=ADMIN_PREFIX + '/channelstream/subscribe')
85 config.add_route(
85 config.add_route(
86 name='channelstream_proxy',
86 name='channelstream_proxy',
87 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
87 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
88 config.scan('rhodecode.channelstream')
88
89 # Scan module for configuration decorators.
90 config.scan()
1 NO CONTENT: file renamed from rhodecode/channelstream/views.py to rhodecode/apps/channelstream/views.py
NO CONTENT: file renamed from rhodecode/channelstream/views.py to rhodecode/apps/channelstream/views.py
1 NO CONTENT: file renamed from rhodecode/login/__init__.py to rhodecode/apps/login/__init__.py
NO CONTENT: file renamed from rhodecode/login/__init__.py to rhodecode/apps/login/__init__.py
1 NO CONTENT: file renamed from rhodecode/login/tests/__init__.py to rhodecode/apps/login/tests/__init__.py
NO CONTENT: file renamed from rhodecode/login/tests/__init__.py to rhodecode/apps/login/tests/__init__.py
@@ -1,128 +1,133 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import mock
22 import mock
23 import pytest
23 import pytest
24
24
25 from rhodecode.apps.login.views import LoginView, CaptchaData
25 from rhodecode.config.routing import ADMIN_PREFIX
26 from rhodecode.config.routing import ADMIN_PREFIX
26 from rhodecode.login.views import LoginView, CaptchaData
27 from rhodecode.lib.utils2 import AttributeDict
27 from rhodecode.model.settings import SettingsModel
28 from rhodecode.model.settings import SettingsModel
28 from rhodecode.tests.utils import AssertResponse
29 from rhodecode.tests.utils import AssertResponse
29
30
30
31
31 class RhodeCodeSetting(object):
32 class RhodeCodeSetting(object):
32 def __init__(self, name, value):
33 def __init__(self, name, value):
33 self.name = name
34 self.name = name
34 self.value = value
35 self.value = value
35
36
36 def __enter__(self):
37 def __enter__(self):
37 from rhodecode.model.settings import SettingsModel
38 from rhodecode.model.settings import SettingsModel
38 model = SettingsModel()
39 model = SettingsModel()
39 self.old_setting = model.get_setting_by_name(self.name)
40 self.old_setting = model.get_setting_by_name(self.name)
40 model.create_or_update_setting(name=self.name, val=self.value)
41 model.create_or_update_setting(name=self.name, val=self.value)
41 return self
42 return self
42
43
43 def __exit__(self, type, value, traceback):
44 def __exit__(self, exc_type, exc_val, exc_tb):
44 model = SettingsModel()
45 model = SettingsModel()
45 if self.old_setting:
46 if self.old_setting:
46 model.create_or_update_setting(
47 model.create_or_update_setting(
47 name=self.name, val=self.old_setting.app_settings_value)
48 name=self.name, val=self.old_setting.app_settings_value)
48 else:
49 else:
49 model.create_or_update_setting(name=self.name)
50 model.create_or_update_setting(name=self.name)
50
51
51
52
52 class TestRegisterCaptcha(object):
53 class TestRegisterCaptcha(object):
53
54
54 @pytest.mark.parametrize('private_key, public_key, expected', [
55 @pytest.mark.parametrize('private_key, public_key, expected', [
55 ('', '', CaptchaData(False, '', '')),
56 ('', '', CaptchaData(False, '', '')),
56 ('', 'pubkey', CaptchaData(False, '', 'pubkey')),
57 ('', 'pubkey', CaptchaData(False, '', 'pubkey')),
57 ('privkey', '', CaptchaData(True, 'privkey', '')),
58 ('privkey', '', CaptchaData(True, 'privkey', '')),
58 ('privkey', 'pubkey', CaptchaData(True, 'privkey', 'pubkey')),
59 ('privkey', 'pubkey', CaptchaData(True, 'privkey', 'pubkey')),
59 ])
60 ])
60 def test_get_captcha_data(self, private_key, public_key, expected, db):
61 def test_get_captcha_data(self, private_key, public_key, expected, db,
61 login_view = LoginView(mock.Mock(), mock.Mock())
62 request_stub, user_util):
63 request_stub.user = user_util.create_user().AuthUser
64 request_stub.matched_route = AttributeDict({'name': 'login'})
65 login_view = LoginView(mock.Mock(), request_stub)
66
62 with RhodeCodeSetting('captcha_private_key', private_key):
67 with RhodeCodeSetting('captcha_private_key', private_key):
63 with RhodeCodeSetting('captcha_public_key', public_key):
68 with RhodeCodeSetting('captcha_public_key', public_key):
64 captcha = login_view._get_captcha_data()
69 captcha = login_view._get_captcha_data()
65 assert captcha == expected
70 assert captcha == expected
66
71
67 @pytest.mark.parametrize('active', [False, True])
72 @pytest.mark.parametrize('active', [False, True])
68 @mock.patch.object(LoginView, '_get_captcha_data')
73 @mock.patch.object(LoginView, '_get_captcha_data')
69 def test_private_key_does_not_leak_to_html(
74 def test_private_key_does_not_leak_to_html(
70 self, m_get_captcha_data, active, app):
75 self, m_get_captcha_data, active, app):
71 captcha = CaptchaData(
76 captcha = CaptchaData(
72 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
77 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
73 m_get_captcha_data.return_value = captcha
78 m_get_captcha_data.return_value = captcha
74
79
75 response = app.get(ADMIN_PREFIX + '/register')
80 response = app.get(ADMIN_PREFIX + '/register')
76 assert 'PRIVATE_KEY' not in response
81 assert 'PRIVATE_KEY' not in response
77
82
78 @pytest.mark.parametrize('active', [False, True])
83 @pytest.mark.parametrize('active', [False, True])
79 @mock.patch.object(LoginView, '_get_captcha_data')
84 @mock.patch.object(LoginView, '_get_captcha_data')
80 def test_register_view_renders_captcha(
85 def test_register_view_renders_captcha(
81 self, m_get_captcha_data, active, app):
86 self, m_get_captcha_data, active, app):
82 captcha = CaptchaData(
87 captcha = CaptchaData(
83 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
88 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
84 m_get_captcha_data.return_value = captcha
89 m_get_captcha_data.return_value = captcha
85
90
86 response = app.get(ADMIN_PREFIX + '/register')
91 response = app.get(ADMIN_PREFIX + '/register')
87
92
88 assertr = AssertResponse(response)
93 assertr = AssertResponse(response)
89 if active:
94 if active:
90 assertr.one_element_exists('#recaptcha_field')
95 assertr.one_element_exists('#recaptcha_field')
91 else:
96 else:
92 assertr.no_element_exists('#recaptcha_field')
97 assertr.no_element_exists('#recaptcha_field')
93
98
94 @pytest.mark.parametrize('valid', [False, True])
99 @pytest.mark.parametrize('valid', [False, True])
95 @mock.patch('rhodecode.login.views.submit')
100 @mock.patch('rhodecode.apps.login.views.submit')
96 @mock.patch.object(LoginView, '_get_captcha_data')
101 @mock.patch.object(LoginView, '_get_captcha_data')
97 def test_register_with_active_captcha(
102 def test_register_with_active_captcha(
98 self, m_get_captcha_data, m_submit, valid, app, csrf_token):
103 self, m_get_captcha_data, m_submit, valid, app, csrf_token):
99 captcha = CaptchaData(
104 captcha = CaptchaData(
100 active=True, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
105 active=True, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
101 m_get_captcha_data.return_value = captcha
106 m_get_captcha_data.return_value = captcha
102 m_response = mock.Mock()
107 m_response = mock.Mock()
103 m_response.is_valid = valid
108 m_response.is_valid = valid
104 m_submit.return_value = m_response
109 m_submit.return_value = m_response
105
110
106 params = {
111 params = {
107 'csrf_token': csrf_token,
112 'csrf_token': csrf_token,
108 'email': 'pytest@example.com',
113 'email': 'pytest@example.com',
109 'firstname': 'pytest-firstname',
114 'firstname': 'pytest-firstname',
110 'lastname': 'pytest-lastname',
115 'lastname': 'pytest-lastname',
111 'password': 'secret',
116 'password': 'secret',
112 'password_confirmation': 'secret',
117 'password_confirmation': 'secret',
113 'username': 'pytest',
118 'username': 'pytest',
114 }
119 }
115 response = app.post(ADMIN_PREFIX + '/register', params=params)
120 response = app.post(ADMIN_PREFIX + '/register', params=params)
116
121
117 if valid:
122 if valid:
118 # If we provided a valid captcha input we expect a successful
123 # If we provided a valid captcha input we expect a successful
119 # registration and redirect to the login page.
124 # registration and redirect to the login page.
120 assert response.status_int == 302
125 assert response.status_int == 302
121 assert 'location' in response.headers
126 assert 'location' in response.headers
122 assert ADMIN_PREFIX + '/login' in response.headers['location']
127 assert ADMIN_PREFIX + '/login' in response.headers['location']
123 else:
128 else:
124 # If captche input is invalid we expect to stay on the registration
129 # If captche input is invalid we expect to stay on the registration
125 # page with an error message displayed.
130 # page with an error message displayed.
126 assertr = AssertResponse(response)
131 assertr = AssertResponse(response)
127 assert response.status_int == 200
132 assert response.status_int == 200
128 assertr.one_element_exists('#recaptcha_field ~ span.error-message')
133 assertr.one_element_exists('#recaptcha_field ~ span.error-message')
@@ -1,358 +1,396 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import collections
22 import collections
22 import datetime
23 import datetime
23 import formencode
24 import formencode
24 import logging
25 import logging
25 import urlparse
26 import urlparse
26
27
27 from pylons import url
28 from pylons import url
28 from pyramid.httpexceptions import HTTPFound
29 from pyramid.httpexceptions import HTTPFound
29 from pyramid.view import view_config
30 from pyramid.view import view_config
30 from recaptcha.client.captcha import submit
31 from recaptcha.client.captcha import submit
31
32
33 from rhodecode.apps._base import BaseAppView
32 from rhodecode.authentication.base import authenticate, HTTP_TYPE
34 from rhodecode.authentication.base import authenticate, HTTP_TYPE
33 from rhodecode.events import UserRegistered
35 from rhodecode.events import UserRegistered
34 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
35 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
36 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
38 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
37 from rhodecode.lib.base import get_ip_addr
39 from rhodecode.lib.base import get_ip_addr
38 from rhodecode.lib.exceptions import UserCreationError
40 from rhodecode.lib.exceptions import UserCreationError
39 from rhodecode.lib.utils2 import safe_str
41 from rhodecode.lib.utils2 import safe_str
40 from rhodecode.model.db import User
42 from rhodecode.model.db import User, UserApiKeys
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
43 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
42 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
45 from rhodecode.model.auth_token import AuthTokenModel
43 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
45 from rhodecode.translation import _
48 from rhodecode.translation import _
46
49
47
50
48 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
49
52
50 CaptchaData = collections.namedtuple(
53 CaptchaData = collections.namedtuple(
51 'CaptchaData', 'active, private_key, public_key')
54 'CaptchaData', 'active, private_key, public_key')
52
55
53
56
54 def _store_user_in_session(session, username, remember=False):
57 def _store_user_in_session(session, username, remember=False):
55 user = User.get_by_username(username, case_insensitive=True)
58 user = User.get_by_username(username, case_insensitive=True)
56 auth_user = AuthUser(user.user_id)
59 auth_user = AuthUser(user.user_id)
57 auth_user.set_authenticated()
60 auth_user.set_authenticated()
58 cs = auth_user.get_cookie_store()
61 cs = auth_user.get_cookie_store()
59 session['rhodecode_user'] = cs
62 session['rhodecode_user'] = cs
60 user.update_lastlogin()
63 user.update_lastlogin()
61 Session().commit()
64 Session().commit()
62
65
63 # If they want to be remembered, update the cookie
66 # If they want to be remembered, update the cookie
64 if remember:
67 if remember:
65 _year = (datetime.datetime.now() +
68 _year = (datetime.datetime.now() +
66 datetime.timedelta(seconds=60 * 60 * 24 * 365))
69 datetime.timedelta(seconds=60 * 60 * 24 * 365))
67 session._set_cookie_expires(_year)
70 session._set_cookie_expires(_year)
68
71
69 session.save()
72 session.save()
70
73
71 safe_cs = cs.copy()
74 safe_cs = cs.copy()
72 safe_cs['password'] = '****'
75 safe_cs['password'] = '****'
73 log.info('user %s is now authenticated and stored in '
76 log.info('user %s is now authenticated and stored in '
74 'session, session attrs %s', username, safe_cs)
77 'session, session attrs %s', username, safe_cs)
75
78
76 # dumps session attrs back to cookie
79 # dumps session attrs back to cookie
77 session._update_cookie_out()
80 session._update_cookie_out()
78 # we set new cookie
81 # we set new cookie
79 headers = None
82 headers = None
80 if session.request['set_cookie']:
83 if session.request['set_cookie']:
81 # send set-cookie headers back to response to update cookie
84 # send set-cookie headers back to response to update cookie
82 headers = [('Set-Cookie', session.request['cookie_out'])]
85 headers = [('Set-Cookie', session.request['cookie_out'])]
83 return headers
86 return headers
84
87
85
88
86 def get_came_from(request):
89 def get_came_from(request):
87 came_from = safe_str(request.GET.get('came_from', ''))
90 came_from = safe_str(request.GET.get('came_from', ''))
88 parsed = urlparse.urlparse(came_from)
91 parsed = urlparse.urlparse(came_from)
89 allowed_schemes = ['http', 'https']
92 allowed_schemes = ['http', 'https']
90 if parsed.scheme and parsed.scheme not in allowed_schemes:
93 if parsed.scheme and parsed.scheme not in allowed_schemes:
91 log.error('Suspicious URL scheme detected %s for url %s' %
94 log.error('Suspicious URL scheme detected %s for url %s' %
92 (parsed.scheme, parsed))
95 (parsed.scheme, parsed))
93 came_from = url('home')
96 came_from = url('home')
94 elif parsed.netloc and request.host != parsed.netloc:
97 elif parsed.netloc and request.host != parsed.netloc:
95 log.error('Suspicious NETLOC detected %s for url %s server url '
98 log.error('Suspicious NETLOC detected %s for url %s server url '
96 'is: %s' % (parsed.netloc, parsed, request.host))
99 'is: %s' % (parsed.netloc, parsed, request.host))
97 came_from = url('home')
100 came_from = url('home')
98 elif any(bad_str in parsed.path for bad_str in ('\r', '\n')):
101 elif any(bad_str in parsed.path for bad_str in ('\r', '\n')):
99 log.error('Header injection detected `%s` for url %s server url ' %
102 log.error('Header injection detected `%s` for url %s server url ' %
100 (parsed.path, parsed))
103 (parsed.path, parsed))
101 came_from = url('home')
104 came_from = url('home')
102
105
103 return came_from or url('home')
106 return came_from or url('home')
104
107
105
108
106 class LoginView(object):
109 class LoginView(BaseAppView):
107
110
108 def __init__(self, context, request):
111 def load_default_context(self):
109 self.request = request
112 c = self._get_local_tmpl_context()
110 self.context = context
113 c.came_from = get_came_from(self.request)
111 self.session = request.session
114 self._register_global_c(c)
112 self._rhodecode_user = request.user
115 return c
113
114 def _get_template_context(self):
115 return {
116 'came_from': get_came_from(self.request),
117 'defaults': {},
118 'errors': {},
119 }
120
116
121 def _get_captcha_data(self):
117 def _get_captcha_data(self):
122 settings = SettingsModel().get_all_settings()
118 settings = SettingsModel().get_all_settings()
123 private_key = settings.get('rhodecode_captcha_private_key')
119 private_key = settings.get('rhodecode_captcha_private_key')
124 public_key = settings.get('rhodecode_captcha_public_key')
120 public_key = settings.get('rhodecode_captcha_public_key')
125 active = bool(private_key)
121 active = bool(private_key)
126 return CaptchaData(
122 return CaptchaData(
127 active=active, private_key=private_key, public_key=public_key)
123 active=active, private_key=private_key, public_key=public_key)
128
124
129 @view_config(
125 @view_config(
130 route_name='login', request_method='GET',
126 route_name='login', request_method='GET',
131 renderer='rhodecode:templates/login.mako')
127 renderer='rhodecode:templates/login.mako')
132 def login(self):
128 def login(self):
133 came_from = get_came_from(self.request)
129 c = self.load_default_context()
134 user = self.request.user
130 auth_user = self._rhodecode_user
135
131
136 # redirect if already logged in
132 # redirect if already logged in
137 if user.is_authenticated and not user.is_default and user.ip_allowed:
133 if (auth_user.is_authenticated and
138 raise HTTPFound(came_from)
134 not auth_user.is_default and auth_user.ip_allowed):
135 raise HTTPFound(c.came_from)
139
136
140 # check if we use headers plugin, and try to login using it.
137 # check if we use headers plugin, and try to login using it.
141 try:
138 try:
142 log.debug('Running PRE-AUTH for headers based authentication')
139 log.debug('Running PRE-AUTH for headers based authentication')
143 auth_info = authenticate(
140 auth_info = authenticate(
144 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
141 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
145 if auth_info:
142 if auth_info:
146 headers = _store_user_in_session(
143 headers = _store_user_in_session(
147 self.session, auth_info.get('username'))
144 self.session, auth_info.get('username'))
148 raise HTTPFound(came_from, headers=headers)
145 raise HTTPFound(c.came_from, headers=headers)
149 except UserCreationError as e:
146 except UserCreationError as e:
150 log.error(e)
147 log.error(e)
151 self.session.flash(e, queue='error')
148 self.session.flash(e, queue='error')
152
149
153 return self._get_template_context()
150 return self._get_template_context(c)
154
151
155 @view_config(
152 @view_config(
156 route_name='login', request_method='POST',
153 route_name='login', request_method='POST',
157 renderer='rhodecode:templates/login.mako')
154 renderer='rhodecode:templates/login.mako')
158 def login_post(self):
155 def login_post(self):
159 came_from = get_came_from(self.request)
156 c = self.load_default_context()
160
157
161 login_form = LoginForm()()
158 login_form = LoginForm()()
162
159
163 try:
160 try:
164 self.session.invalidate()
161 self.session.invalidate()
165 form_result = login_form.to_python(self.request.params)
162 form_result = login_form.to_python(self.request.params)
166 # form checks for username/password, now we're authenticated
163 # form checks for username/password, now we're authenticated
167 headers = _store_user_in_session(
164 headers = _store_user_in_session(
168 self.session,
165 self.session,
169 username=form_result['username'],
166 username=form_result['username'],
170 remember=form_result['remember'])
167 remember=form_result['remember'])
171 log.debug('Redirecting to "%s" after login.', came_from)
168 log.debug('Redirecting to "%s" after login.', c.came_from)
172 raise HTTPFound(came_from, headers=headers)
169 raise HTTPFound(c.came_from, headers=headers)
173 except formencode.Invalid as errors:
170 except formencode.Invalid as errors:
174 defaults = errors.value
171 defaults = errors.value
175 # remove password from filling in form again
172 # remove password from filling in form again
176 defaults.pop('password', None)
173 defaults.pop('password', None)
177 render_ctx = self._get_template_context()
174 render_ctx = self._get_template_context(c)
178 render_ctx.update({
175 render_ctx.update({
179 'errors': errors.error_dict,
176 'errors': errors.error_dict,
180 'defaults': defaults,
177 'defaults': defaults,
181 })
178 })
182 return render_ctx
179 return render_ctx
183
180
184 except UserCreationError as e:
181 except UserCreationError as e:
185 # headers auth or other auth functions that create users on
182 # headers auth or other auth functions that create users on
186 # the fly can throw this exception signaling that there's issue
183 # the fly can throw this exception signaling that there's issue
187 # with user creation, explanation should be provided in
184 # with user creation, explanation should be provided in
188 # Exception itself
185 # Exception itself
189 self.session.flash(e, queue='error')
186 self.session.flash(e, queue='error')
190 return self._get_template_context()
187 return self._get_template_context(c)
191
188
192 @CSRFRequired()
189 @CSRFRequired()
193 @view_config(route_name='logout', request_method='POST')
190 @view_config(route_name='logout', request_method='POST')
194 def logout(self):
191 def logout(self):
195 user = self.request.user
192 auth_user = self._rhodecode_user
196 log.info('Deleting session for user: `%s`', user)
193 log.info('Deleting session for user: `%s`', auth_user)
197 self.session.delete()
194 self.session.delete()
198 return HTTPFound(url('home'))
195 return HTTPFound(url('home'))
199
196
200 @HasPermissionAnyDecorator(
197 @HasPermissionAnyDecorator(
201 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
198 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
202 @view_config(
199 @view_config(
203 route_name='register', request_method='GET',
200 route_name='register', request_method='GET',
204 renderer='rhodecode:templates/register.mako',)
201 renderer='rhodecode:templates/register.mako',)
205 def register(self, defaults=None, errors=None):
202 def register(self, defaults=None, errors=None):
203 c = self.load_default_context()
206 defaults = defaults or {}
204 defaults = defaults or {}
207 errors = errors or {}
205 errors = errors or {}
208
206
209 settings = SettingsModel().get_all_settings()
207 settings = SettingsModel().get_all_settings()
210 register_message = settings.get('rhodecode_register_message') or ''
208 register_message = settings.get('rhodecode_register_message') or ''
211 captcha = self._get_captcha_data()
209 captcha = self._get_captcha_data()
212 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
210 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
213 .AuthUser.permissions['global']
211 .AuthUser.permissions['global']
214
212
215 render_ctx = self._get_template_context()
213 render_ctx = self._get_template_context(c)
216 render_ctx.update({
214 render_ctx.update({
217 'defaults': defaults,
215 'defaults': defaults,
218 'errors': errors,
216 'errors': errors,
219 'auto_active': auto_active,
217 'auto_active': auto_active,
220 'captcha_active': captcha.active,
218 'captcha_active': captcha.active,
221 'captcha_public_key': captcha.public_key,
219 'captcha_public_key': captcha.public_key,
222 'register_message': register_message,
220 'register_message': register_message,
223 })
221 })
224 return render_ctx
222 return render_ctx
225
223
226 @HasPermissionAnyDecorator(
224 @HasPermissionAnyDecorator(
227 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
225 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
228 @view_config(
226 @view_config(
229 route_name='register', request_method='POST',
227 route_name='register', request_method='POST',
230 renderer='rhodecode:templates/register.mako')
228 renderer='rhodecode:templates/register.mako')
231 def register_post(self):
229 def register_post(self):
232 captcha = self._get_captcha_data()
230 captcha = self._get_captcha_data()
233 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
231 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
234 .AuthUser.permissions['global']
232 .AuthUser.permissions['global']
235
233
236 register_form = RegisterForm()()
234 register_form = RegisterForm()()
237 try:
235 try:
238 form_result = register_form.to_python(self.request.params)
236 form_result = register_form.to_python(self.request.params)
239 form_result['active'] = auto_active
237 form_result['active'] = auto_active
240
238
241 if captcha.active:
239 if captcha.active:
242 response = submit(
240 response = submit(
243 self.request.params.get('recaptcha_challenge_field'),
241 self.request.params.get('recaptcha_challenge_field'),
244 self.request.params.get('recaptcha_response_field'),
242 self.request.params.get('recaptcha_response_field'),
245 private_key=captcha.private_key,
243 private_key=captcha.private_key,
246 remoteip=get_ip_addr(self.request.environ))
244 remoteip=get_ip_addr(self.request.environ))
247 if not response.is_valid:
245 if not response.is_valid:
248 _value = form_result
246 _value = form_result
249 _msg = _('Bad captcha')
247 _msg = _('Bad captcha')
250 error_dict = {'recaptcha_field': _msg}
248 error_dict = {'recaptcha_field': _msg}
251 raise formencode.Invalid(_msg, _value, None,
249 raise formencode.Invalid(_msg, _value, None,
252 error_dict=error_dict)
250 error_dict=error_dict)
253
251
254 new_user = UserModel().create_registration(form_result)
252 new_user = UserModel().create_registration(form_result)
255 event = UserRegistered(user=new_user, session=self.session)
253 event = UserRegistered(user=new_user, session=self.session)
256 self.request.registry.notify(event)
254 self.request.registry.notify(event)
257 self.session.flash(
255 self.session.flash(
258 _('You have successfully registered with RhodeCode'),
256 _('You have successfully registered with RhodeCode'),
259 queue='success')
257 queue='success')
260 Session().commit()
258 Session().commit()
261
259
262 redirect_ro = self.request.route_path('login')
260 redirect_ro = self.request.route_path('login')
263 raise HTTPFound(redirect_ro)
261 raise HTTPFound(redirect_ro)
264
262
265 except formencode.Invalid as errors:
263 except formencode.Invalid as errors:
266 errors.value.pop('password', None)
264 errors.value.pop('password', None)
267 errors.value.pop('password_confirmation', None)
265 errors.value.pop('password_confirmation', None)
268 return self.register(
266 return self.register(
269 defaults=errors.value, errors=errors.error_dict)
267 defaults=errors.value, errors=errors.error_dict)
270
268
271 except UserCreationError as e:
269 except UserCreationError as e:
272 # container auth or other auth functions that create users on
270 # container auth or other auth functions that create users on
273 # the fly can throw this exception signaling that there's issue
271 # the fly can throw this exception signaling that there's issue
274 # with user creation, explanation should be provided in
272 # with user creation, explanation should be provided in
275 # Exception itself
273 # Exception itself
276 self.session.flash(e, queue='error')
274 self.session.flash(e, queue='error')
277 return self.register()
275 return self.register()
278
276
279 @view_config(
277 @view_config(
280 route_name='reset_password', request_method=('GET', 'POST'),
278 route_name='reset_password', request_method=('GET', 'POST'),
281 renderer='rhodecode:templates/password_reset.mako')
279 renderer='rhodecode:templates/password_reset.mako')
282 def password_reset(self):
280 def password_reset(self):
283 captcha = self._get_captcha_data()
281 captcha = self._get_captcha_data()
284
282
285 render_ctx = {
283 render_ctx = {
286 'captcha_active': captcha.active,
284 'captcha_active': captcha.active,
287 'captcha_public_key': captcha.public_key,
285 'captcha_public_key': captcha.public_key,
288 'defaults': {},
286 'defaults': {},
289 'errors': {},
287 'errors': {},
290 }
288 }
291
289
290 # always send implicit message to prevent from discovery of
291 # matching emails
292 msg = _('If such email exists, a password reset link was sent to it.')
293
292 if self.request.POST:
294 if self.request.POST:
295 if h.HasPermissionAny('hg.password_reset.disabled')():
296 _email = self.request.POST.get('email', '')
297 log.error('Failed attempt to reset password for `%s`.', _email)
298 self.session.flash(_('Password reset has been disabled.'),
299 queue='error')
300 return HTTPFound(self.request.route_path('reset_password'))
301
293 password_reset_form = PasswordResetForm()()
302 password_reset_form = PasswordResetForm()()
294 try:
303 try:
295 form_result = password_reset_form.to_python(
304 form_result = password_reset_form.to_python(
296 self.request.params)
305 self.request.params)
297 if h.HasPermissionAny('hg.password_reset.disabled')():
306 user_email = form_result['email']
298 log.error('Failed attempt to reset password for %s.', form_result['email'] )
307
299 self.session.flash(
300 _('Password reset has been disabled.'),
301 queue='error')
302 return HTTPFound(self.request.route_path('reset_password'))
303 if captcha.active:
308 if captcha.active:
304 response = submit(
309 response = submit(
305 self.request.params.get('recaptcha_challenge_field'),
310 self.request.params.get('recaptcha_challenge_field'),
306 self.request.params.get('recaptcha_response_field'),
311 self.request.params.get('recaptcha_response_field'),
307 private_key=captcha.private_key,
312 private_key=captcha.private_key,
308 remoteip=get_ip_addr(self.request.environ))
313 remoteip=get_ip_addr(self.request.environ))
309 if not response.is_valid:
314 if not response.is_valid:
310 _value = form_result
315 _value = form_result
311 _msg = _('Bad captcha')
316 _msg = _('Bad captcha')
312 error_dict = {'recaptcha_field': _msg}
317 error_dict = {'recaptcha_field': _msg}
313 raise formencode.Invalid(_msg, _value, None,
318 raise formencode.Invalid(
314 error_dict=error_dict)
319 _msg, _value, None, error_dict=error_dict)
315
320
316 # Generate reset URL and send mail.
321 # Generate reset URL and send mail.
317 user_email = form_result['email']
318 user = User.get_by_email(user_email)
322 user = User.get_by_email(user_email)
323
324 # generate password reset token that expires in 10minutes
325 desc = 'Generated token for password reset from {}'.format(
326 datetime.datetime.now().isoformat())
327 reset_token = AuthTokenModel().create(
328 user, lifetime=10,
329 description=desc,
330 role=UserApiKeys.ROLE_PASSWORD_RESET)
331 Session().commit()
332
333 log.debug('Successfully created password recovery token')
319 password_reset_url = self.request.route_url(
334 password_reset_url = self.request.route_url(
320 'reset_password_confirmation',
335 'reset_password_confirmation',
321 _query={'key': user.api_key})
336 _query={'key': reset_token.api_key})
322 UserModel().reset_password_link(
337 UserModel().reset_password_link(
323 form_result, password_reset_url)
338 form_result, password_reset_url)
324
325 # Display success message and redirect.
339 # Display success message and redirect.
326 self.session.flash(
340 self.session.flash(msg, queue='success')
327 _('Your password reset link was sent'),
341 return HTTPFound(self.request.route_path('reset_password'))
328 queue='success')
329 return HTTPFound(self.request.route_path('login'))
330
342
331 except formencode.Invalid as errors:
343 except formencode.Invalid as errors:
332 render_ctx.update({
344 render_ctx.update({
333 'defaults': errors.value,
345 'defaults': errors.value,
334 'errors': errors.error_dict,
346 'errors': errors.error_dict,
335 })
347 })
348 if not self.request.params.get('email'):
349 # case of empty email, we want to report that
350 return render_ctx
351
352 if 'recaptcha_field' in errors.error_dict:
353 # case of failed captcha
354 return render_ctx
355
356 log.debug('faking response on invalid password reset')
357 # make this take 2s, to prevent brute forcing.
358 time.sleep(2)
359 self.session.flash(msg, queue='success')
360 return HTTPFound(self.request.route_path('reset_password'))
336
361
337 return render_ctx
362 return render_ctx
338
363
339 @view_config(route_name='reset_password_confirmation',
364 @view_config(route_name='reset_password_confirmation',
340 request_method='GET')
365 request_method='GET')
341 def password_reset_confirmation(self):
366 def password_reset_confirmation(self):
367
342 if self.request.GET and self.request.GET.get('key'):
368 if self.request.GET and self.request.GET.get('key'):
369 # make this take 2s, to prevent brute forcing.
370 time.sleep(2)
371
372 token = AuthTokenModel().get_auth_token(
373 self.request.GET.get('key'))
374
375 # verify token is the correct role
376 if token is None or token.role != UserApiKeys.ROLE_PASSWORD_RESET:
377 log.debug('Got token with role:%s expected is %s',
378 getattr(token, 'role', 'EMPTY_TOKEN'),
379 UserApiKeys.ROLE_PASSWORD_RESET)
380 self.session.flash(
381 _('Given reset token is invalid'), queue='error')
382 return HTTPFound(self.request.route_path('reset_password'))
383
343 try:
384 try:
344 user = User.get_by_auth_token(self.request.GET.get('key'))
385 owner = token.user
345 password_reset_url = self.request.route_url(
386 data = {'email': owner.email, 'token': token.api_key}
346 'reset_password_confirmation',
387 UserModel().reset_password(data)
347 _query={'key': user.api_key})
348 data = {'email': user.email}
349 UserModel().reset_password(data, password_reset_url)
350 self.session.flash(
388 self.session.flash(
351 _('Your password reset was successful, '
389 _('Your password reset was successful, '
352 'a new password has been sent to your email'),
390 'a new password has been sent to your email'),
353 queue='success')
391 queue='success')
354 except Exception as e:
392 except Exception as e:
355 log.error(e)
393 log.error(e)
356 return HTTPFound(self.request.route_path('reset_password'))
394 return HTTPFound(self.request.route_path('reset_password'))
357
395
358 return HTTPFound(self.request.route_path('login'))
396 return HTTPFound(self.request.route_path('login'))
1 NO CONTENT: file renamed from rhodecode/svn_support/__init__.py to rhodecode/apps/svn_support/__init__.py
NO CONTENT: file renamed from rhodecode/svn_support/__init__.py to rhodecode/apps/svn_support/__init__.py
1 NO CONTENT: file renamed from rhodecode/svn_support/config_keys.py to rhodecode/apps/svn_support/config_keys.py
NO CONTENT: file renamed from rhodecode/svn_support/config_keys.py to rhodecode/apps/svn_support/config_keys.py
1 NO CONTENT: file renamed from rhodecode/svn_support/events.py to rhodecode/apps/svn_support/events.py
NO CONTENT: file renamed from rhodecode/svn_support/events.py to rhodecode/apps/svn_support/events.py
1 NO CONTENT: file renamed from rhodecode/svn_support/subscribers.py to rhodecode/apps/svn_support/subscribers.py
NO CONTENT: file renamed from rhodecode/svn_support/subscribers.py to rhodecode/apps/svn_support/subscribers.py
1 NO CONTENT: file renamed from rhodecode/svn_support/templates/mod-dav-svn.conf.mako to rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
NO CONTENT: file renamed from rhodecode/svn_support/templates/mod-dav-svn.conf.mako to rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
@@ -1,107 +1,107 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import mock
22 import mock
23 import pytest
23 import pytest
24 import re
24 import re
25
25
26 from pyramid import testing
26 from pyramid import testing
27
27
28 from rhodecode.svn_support import utils
28 from rhodecode.apps.svn_support import utils
29
29
30
30
31 class TestModDavSvnConfig(object):
31 class TestModDavSvnConfig(object):
32
32
33 @classmethod
33 @classmethod
34 def setup_class(cls):
34 def setup_class(cls):
35 # Make mako renderer available in tests.
35 # Make mako renderer available in tests.
36 config = testing.setUp()
36 config = testing.setUp()
37 config.include('pyramid_mako')
37 config.include('pyramid_mako')
38
38
39 cls.location_root = u'/location/root/çµäö'
39 cls.location_root = u'/location/root/çµäö'
40 cls.parent_path_root = u'/parent/path/çµäö'
40 cls.parent_path_root = u'/parent/path/çµäö'
41 cls.realm = u'Dummy Realm (äöüçµ)'
41 cls.realm = u'Dummy Realm (äöüçµ)'
42
42
43 @classmethod
43 @classmethod
44 def get_repo_group_mocks(cls, count=1):
44 def get_repo_group_mocks(cls, count=1):
45 repo_groups = []
45 repo_groups = []
46 for num in range(0, count):
46 for num in range(0, count):
47 full_path = u'/path/to/RepöGröúp-°µ {}'.format(num)
47 full_path = u'/path/to/RepöGröúp-°µ {}'.format(num)
48 repo_group_mock = mock.MagicMock()
48 repo_group_mock = mock.MagicMock()
49 repo_group_mock.full_path = full_path
49 repo_group_mock.full_path = full_path
50 repo_group_mock.full_path_splitted = full_path.split('/')
50 repo_group_mock.full_path_splitted = full_path.split('/')
51 repo_groups.append(repo_group_mock)
51 repo_groups.append(repo_group_mock)
52 return repo_groups
52 return repo_groups
53
53
54 def assert_root_location_directive(self, config):
54 def assert_root_location_directive(self, config):
55 pattern = u'<Location "{location}">'.format(
55 pattern = u'<Location "{location}">'.format(
56 location=self.location_root)
56 location=self.location_root)
57 assert len(re.findall(pattern, config)) == 1
57 assert len(re.findall(pattern, config)) == 1
58
58
59 def assert_group_location_directive(self, config, group_path):
59 def assert_group_location_directive(self, config, group_path):
60 pattern = u'<Location "{location}{group_path}">'.format(
60 pattern = u'<Location "{location}{group_path}">'.format(
61 location=self.location_root, group_path=group_path)
61 location=self.location_root, group_path=group_path)
62 assert len(re.findall(pattern, config)) == 1
62 assert len(re.findall(pattern, config)) == 1
63
63
64 def test_render_mod_dav_svn_config(self):
64 def test_render_mod_dav_svn_config(self):
65 repo_groups = self.get_repo_group_mocks(count=10)
65 repo_groups = self.get_repo_group_mocks(count=10)
66 generated_config = utils._render_mod_dav_svn_config(
66 generated_config = utils._render_mod_dav_svn_config(
67 parent_path_root=self.parent_path_root,
67 parent_path_root=self.parent_path_root,
68 list_parent_path=True,
68 list_parent_path=True,
69 location_root=self.location_root,
69 location_root=self.location_root,
70 repo_groups=repo_groups,
70 repo_groups=repo_groups,
71 realm=self.realm,
71 realm=self.realm,
72 use_ssl=True
72 use_ssl=True
73 )
73 )
74 # Assert that one location directive exists for each repository group.
74 # Assert that one location directive exists for each repository group.
75 for group in repo_groups:
75 for group in repo_groups:
76 self.assert_group_location_directive(
76 self.assert_group_location_directive(
77 generated_config, group.full_path)
77 generated_config, group.full_path)
78
78
79 # Assert that the root location directive exists.
79 # Assert that the root location directive exists.
80 self.assert_root_location_directive(generated_config)
80 self.assert_root_location_directive(generated_config)
81
81
82 @pytest.mark.parametrize('list_parent_path', [True, False])
82 @pytest.mark.parametrize('list_parent_path', [True, False])
83 @pytest.mark.parametrize('use_ssl', [True, False])
83 @pytest.mark.parametrize('use_ssl', [True, False])
84 def test_list_parent_path(self, list_parent_path, use_ssl):
84 def test_list_parent_path(self, list_parent_path, use_ssl):
85 generated_config = utils._render_mod_dav_svn_config(
85 generated_config = utils._render_mod_dav_svn_config(
86 parent_path_root=self.parent_path_root,
86 parent_path_root=self.parent_path_root,
87 list_parent_path=list_parent_path,
87 list_parent_path=list_parent_path,
88 location_root=self.location_root,
88 location_root=self.location_root,
89 repo_groups=self.get_repo_group_mocks(count=10),
89 repo_groups=self.get_repo_group_mocks(count=10),
90 realm=self.realm,
90 realm=self.realm,
91 use_ssl=use_ssl
91 use_ssl=use_ssl
92 )
92 )
93
93
94 # Assert that correct configuration directive is present.
94 # Assert that correct configuration directive is present.
95 if list_parent_path:
95 if list_parent_path:
96 assert not re.search('SVNListParentPath\s+Off', generated_config)
96 assert not re.search('SVNListParentPath\s+Off', generated_config)
97 assert re.search('SVNListParentPath\s+On', generated_config)
97 assert re.search('SVNListParentPath\s+On', generated_config)
98 else:
98 else:
99 assert re.search('SVNListParentPath\s+Off', generated_config)
99 assert re.search('SVNListParentPath\s+Off', generated_config)
100 assert not re.search('SVNListParentPath\s+On', generated_config)
100 assert not re.search('SVNListParentPath\s+On', generated_config)
101
101
102 if use_ssl:
102 if use_ssl:
103 assert 'RequestHeader edit Destination ^https: http: early' \
103 assert 'RequestHeader edit Destination ^https: http: early' \
104 in generated_config
104 in generated_config
105 else:
105 else:
106 assert '#RequestHeader edit Destination ^https: http: early' \
106 assert '#RequestHeader edit Destination ^https: http: early' \
107 in generated_config
107 in generated_config
@@ -1,93 +1,93 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import codecs
21 import codecs
22 import logging
22 import logging
23 import os
23 import os
24 from pyramid.renderers import render
24 from pyramid.renderers import render
25
25
26 from rhodecode.events import trigger
26 from rhodecode.events import trigger
27 from rhodecode.lib.utils import get_rhodecode_realm, get_rhodecode_base_path
27 from rhodecode.lib.utils import get_rhodecode_realm, get_rhodecode_base_path
28 from rhodecode.lib.utils2 import str2bool
28 from rhodecode.lib.utils2 import str2bool
29 from rhodecode.model.db import RepoGroup
29 from rhodecode.model.db import RepoGroup
30
30
31 from . import config_keys
31 from . import config_keys
32 from .events import ModDavSvnConfigChange
32 from .events import ModDavSvnConfigChange
33
33
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37
37
38 def generate_mod_dav_svn_config(registry):
38 def generate_mod_dav_svn_config(registry):
39 """
39 """
40 Generate the configuration file for use with subversion's mod_dav_svn
40 Generate the configuration file for use with subversion's mod_dav_svn
41 module. The configuration has to contain a <Location> block for each
41 module. The configuration has to contain a <Location> block for each
42 available repository group because the mod_dav_svn module does not support
42 available repository group because the mod_dav_svn module does not support
43 repositories organized in sub folders.
43 repositories organized in sub folders.
44 """
44 """
45 settings = registry.settings
45 settings = registry.settings
46 use_ssl = str2bool(registry.settings['force_https'])
46 use_ssl = str2bool(registry.settings['force_https'])
47
47
48 config = _render_mod_dav_svn_config(
48 config = _render_mod_dav_svn_config(
49 use_ssl=use_ssl,
49 use_ssl=use_ssl,
50 parent_path_root=get_rhodecode_base_path(),
50 parent_path_root=get_rhodecode_base_path(),
51 list_parent_path=settings[config_keys.list_parent_path],
51 list_parent_path=settings[config_keys.list_parent_path],
52 location_root=settings[config_keys.location_root],
52 location_root=settings[config_keys.location_root],
53 repo_groups=RepoGroup.get_all_repo_groups(),
53 repo_groups=RepoGroup.get_all_repo_groups(),
54 realm=get_rhodecode_realm())
54 realm=get_rhodecode_realm())
55 _write_mod_dav_svn_config(config, settings[config_keys.config_file_path])
55 _write_mod_dav_svn_config(config, settings[config_keys.config_file_path])
56
56
57 # Trigger an event on mod dav svn configuration change.
57 # Trigger an event on mod dav svn configuration change.
58 trigger(ModDavSvnConfigChange(), registry)
58 trigger(ModDavSvnConfigChange(), registry)
59
59
60
60
61 def _render_mod_dav_svn_config(
61 def _render_mod_dav_svn_config(
62 parent_path_root, list_parent_path, location_root, repo_groups, realm,
62 parent_path_root, list_parent_path, location_root, repo_groups, realm,
63 use_ssl):
63 use_ssl):
64 """
64 """
65 Render mod_dav_svn configuration to string.
65 Render mod_dav_svn configuration to string.
66 """
66 """
67 repo_group_paths = []
67 repo_group_paths = []
68 for repo_group in repo_groups:
68 for repo_group in repo_groups:
69 group_path = repo_group.full_path_splitted
69 group_path = repo_group.full_path_splitted
70 location = os.path.join(location_root, *group_path)
70 location = os.path.join(location_root, *group_path)
71 parent_path = os.path.join(parent_path_root, *group_path)
71 parent_path = os.path.join(parent_path_root, *group_path)
72 repo_group_paths.append((location, parent_path))
72 repo_group_paths.append((location, parent_path))
73
73
74 context = {
74 context = {
75 'location_root': location_root,
75 'location_root': location_root,
76 'parent_path_root': parent_path_root,
76 'parent_path_root': parent_path_root,
77 'repo_group_paths': repo_group_paths,
77 'repo_group_paths': repo_group_paths,
78 'svn_list_parent_path': list_parent_path,
78 'svn_list_parent_path': list_parent_path,
79 'rhodecode_realm': realm,
79 'rhodecode_realm': realm,
80 'use_https': use_ssl
80 'use_https': use_ssl
81 }
81 }
82
82
83 # Render the configuration template to string.
83 # Render the configuration template to string.
84 template = 'rhodecode:svn_support/templates/mod-dav-svn.conf.mako'
84 template = 'rhodecode:apps/svn_support/templates/mod-dav-svn.conf.mako'
85 return render(template, context)
85 return render(template, context)
86
86
87
87
88 def _write_mod_dav_svn_config(config, filepath):
88 def _write_mod_dav_svn_config(config, filepath):
89 """
89 """
90 Write mod_dav_svn config to file.
90 Write mod_dav_svn config to file.
91 """
91 """
92 with codecs.open(filepath, 'w', encoding='utf-8') as f:
92 with codecs.open(filepath, 'w', encoding='utf-8') as f:
93 f.write(config)
93 f.write(config)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file copied from rhodecode/templates/admin/user_groups/user_groups.mako to rhodecode/templates/admin/users/user_edit_groups.mako
NO CONTENT: file copied from rhodecode/templates/admin/user_groups/user_groups.mako to rhodecode/templates/admin/users/user_edit_groups.mako
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now