##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r2213:2025acfe merge stable
parent child Browse files
Show More

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

@@ -0,0 +1,50 b''
1 .. _gunicorn-ssl-support:
2
3
4 Gunicorn SSL support
5 --------------------
6
7
8 :term:`Gunicorn` wsgi server allows users to use HTTPS connection directly
9 without a need to use HTTP server like Nginx or Apache. To Configure
10 SSL support directly with :term:`Gunicorn` you need to simply add the key
11 and certificate paths to your configuration file.
12
13 1. Open the :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
14 2. In the ``[server:main]`` section, add two new variables
15 called `certfile` and `keyfile`.
16
17 .. code-block:: ini
18
19 [server:main]
20 host = 127.0.0.1
21 port = 10002
22 use = egg:gunicorn#main
23 workers = 1
24 threads = 1
25 proc_name = RhodeCodeEnterprise
26 worker_class = sync
27 max_requests = 1000
28 timeout = 3600
29 # adding ssl support
30 certfile = /home/ssl/my_server_com.pem
31 keyfile = /home/ssl/my_server_com.key
32
33 4. Save your changes.
34 5. Restart your |RCE| instance, using the following command:
35
36 .. code-block:: bash
37
38 $ rccontrol restart enterprise-1
39
40 After this is enabled you can *only* access your instances via https://
41 protocol. Check out more docs here `Gunicorn SSL Docs`_
42
43 .. note::
44
45 This change only can be applied to |RCE|. VCSServer doesn't support SSL
46 and should be only used with http protocol. Because only |RCE| is available
47 externally all communication will still be over SSL even without VCSServer
48 SSL enabled.
49
50 .. _Gunicorn SSL Docs: http://docs.gunicorn.org/en/stable/settings.html#ssl
@@ -0,0 +1,178 b''
1 |RCE| 4.10.0 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2017-11-02
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - SSH (Beta): added support for authentication via SSH keys. It's possible
14 to use SSH key based authentication instead of HTTP. Users are allowed to
15 store multiple keys and use them to push/pull code via SSH.
16 - Pull requests: store and show a merge strategy. Pull request strategy will
17 be also now shown in the UI.
18 Close/delete branch are shown if that option is selected.
19 - Pull requests: Add option to close a branch before merging for Mercurial.
20 - Processes page. RhodeCode will show a list of all current workers with
21 CPU and Memory usage.
22 It's also possible to restart each worker from the web interface.
23 - Auth tokens: allow specifying a custom expiration date from UI.
24 - Integrations: webhook, allow to set a custom header.
25 - Integrations: webhook, add possibility to specify username and password.
26 - UI: added copy-to-clipboard for commits, file paths, gist/clone urls.
27 - UI: improve support for meta-tags in repository description:
28 Tags are extracted to the beginning of the description during rendering.
29 Show helpers in proper places in groups/repos/forks with all available tags.
30 Add a new deprecated tag.
31 - UI: commits page, hide evolve commits.
32 Now optionally it's possible to show them via a new link on changelog page.
33 - Audit logs: allow showing individual entries for audit log.
34 - Audit logs: expose repo related audit logs in repository view.
35 - User sessions: get ability to count memcached sessions.
36 - Core: added support for REDIS based user sessions and cache backend.
37 - Core: added support for Golang go-import functionality.
38 - SVN: allow specifying alternative template file for mod_dav config.
39 - Markup: make relative links pin to raw files for images/files as links.
40 Allows building relative MD/RST links that go to rendered content
41 - Auth: allow binding the whitelist views to specific auth tokens. This allows
42 to access only specific pages via given auth token. E.g possible to expose
43 raw diff/raw file content only for specific single token.
44 The new format is `viewName@TOKEN`
45 - Channelstream: push events with comments on single commits. Users will get
46 live notification for events on single commits too.
47
48
49 General
50 ^^^^^^^
51
52 - License: add helper to show alternative application method for license via
53 ishell.
54 - http: set REMOTE_USER and REMOTE_HOST http variables in order for more
55 Mercurial extensions compatibility.
56 - User/User groups: show if users or user groups are a part of review rules.
57 - Permissions: new improved visual permissions summary. Show exactly how
58 permissions were inherited, and which rule overwrote the other.
59 - Permissions: added new JSON endpoint to extract permissions as JSON data
60 for 3rd party processing. This allows access for reporting tools without
61 giving any ADMIN API access to fetch permissions.
62 - Pyramid: ported all controllers to Pyramid, with python3 compatible code.
63 - Gunicorn: allow custom logger to be set for a consistent formatting of
64 Gunicorn logs with RhodeCode logs.
65 - Search: per-repo search shouldn't require admin permissions. Read is enough
66 because we access the repo data only.
67 - Git: updated to 2.13.5 release
68 - Mercurial: updated to 4.2.3 release.
69 - Mercurial Evolve: updated to 6.6.0 release.
70 - Dependencies: bumped pysqlite to Mako to 1.0.7
71 - Dependencies: bumped pysqlite to 2.8.3
72 - Dependencies: bumped psycopg2 to 2.7.1
73 - Dependencies: bumped docutils to 0.13.1
74 - Dependencies: bumped simplejson to 3.11.1
75 - Dependencies: bumped alembic to 0.9.2
76 - Dependencies: bumped Beaker to 1.9.0
77 - Dependencies: bumped Markdown to 2.6.8
78 - Dependencies: bumped dogpile.cache to 0.6.4
79 - Dependencies: bumped colander to 1.3.3
80 - Dependencies: bumped appenlight_client to 0.6.21
81 - Dependencies: bumped cprofileV to 1.0.7
82 - Dependencies: bumped ipdb to 0.10.3
83 - Dependencies: bumped supervisor to 3.3.2
84 - Dependencies: bumped subprocess32 to 3.2.7
85 - Dependencies: bumped pathlib2 to 2.3.0.
86 - Dependencies: bumped gunicorn==19.7.1
87 - Dependencies: bumped gevent to 1.2.2 together with greenlet to 0.4.12
88 - Dependencies: bumped venusian to 1.1.0
89 - Dependencies: bumped ptyprocess to 0.5.2
90 - Dependencies: bumped testpath to 0.3.1
91 - Dependencies: bumped Pyramid to 1.9.1
92 - Dependencies: bumped supervisor to 3.3.3
93 - Dependencies: bumped sqlalchemy to version 1.1.11
94
95
96 Security
97 ^^^^^^^^
98
99 - Security: use no-referrer for outside link to stop leaking potential
100 parameters such as auth token stored inside GET flags.
101 - Auth tokens: always check permissions to scope tokens to prevent resource
102 discovery of private repos.
103 - Strip: fix XSS in repo strip view.
104 - Files: prevent XSS in fake errors message on filenodes.
105 - Files: remove right-to-left override character for display in files.
106 This allows faking the name a bit, we in this particular place want to
107 skip the override for enhanced security.
108 - Repo forks: security, check for access to fork_id parameter to prevent
109 resource discovery.
110 - Pull requests: security double check permissions on injected forms of
111 source and target repositories. Fixes resource discovery.
112 - Pull requests: security, prevent from injecting comments to other pull
113 requests for users don't have access to.
114
115
116 Performance
117 ^^^^^^^^^^^
118
119 - Goto-switcher: use special commit: prefix to explicitly search for commits.
120 previous solution could make the go-to switcher slow in case of larger search
121 index present.
122 - Goto-switcher: optimized performance and query capabilities.
123 - Diffs: use whole chunk diff to calculate if it's oversized or not.
124 This fixes an issue if a file is added that has very large number of small
125 lines. In this case the time to detect if the diff should be limited was
126 very long and CPU intensive.
127 - Markup: use cached version of http pattern for urlify_text. This
128 increases performance because we don't have to compile the pattern each time
129 we execute this commonly used function.
130 - Changelog: fix and optimize loading of chunks for file history.
131 - Vcs: reduce sql queries used during pull/push operations.
132 - Auth: use cache_ttl from a plugin to also cache calculated permissions.
133 This gives a 30% speed increase in operations like svn commit.
134
135
136 Fixes
137 ^^^^^
138
139 - Initial-gravatars: fix case of dot being present before @domain.
140 - Vcs: report 404 for shadow repos that are not existing anymore.
141 - RSS/Atom Feeds: generate entries with proper unique ids.
142 - DB: use LONGTEXT for mysql in user_logs. Fixes problem with mysql rejecting
143 insert because of too long json data.
144 - Pull request: add missing audit data for pull_request.close action.
145 - User groups: properly set add/delete members for usage in audit data.
146 - Repo, auth-tokens: UX, set VCS scope if repo scopped token is selected.
147 - Changelog: fix and optimize loading of chunks for file history.
148 - Error reporting: improve handling of exception that are non-standard.
149 Inject traceback information into unhandled exceptions.
150 - Users: add additional information why user with pending reviews
151 shouldn't be deleted.
152 - Auth ldap: improve messages when users failed to authenticate via LDAP.
153 - Sqlalchemy: enabled connection ping.
154 should fix potential issues with Mysql server has gone away issues.
155 - License page: fix usage of url() that could prevent from using convert license.
156 - Permissions: use same way of sorting of user_group permissions like user ones.
157
158
159 Upgrade notes
160 ^^^^^^^^^^^^^
161
162 - Searching for commits in goto-switcher must be now prefixed with
163 commit:<hash>
164 - Because of pyramid porting view names have changed, and we made a backward
165 compatibility mapping for most common ones only.
166 We recommend reviewing your whitelist view access list.
167 There's a new dedicated page with ALL views listed under admin > permissions
168 Please take a look in there to port any non-standard views for whitelist access.
169
170 - SSH support is implemented via combination of internal, and installed hooks.
171 A file called `hgrc_rhodecode` is added to each repository that was used with
172 SSH access. This file is then imported inside main hgrc file, it contains
173 some Mercurial hooks for ACL checks.
174 This breaks full backward compatibility with releases prior to 4.10.0.
175 If you install 4.10+, enable SSH module and use SSH with a Mercurial repo, then
176 rollback used version to 4.9.1. In such case one additional actions is required.
177 Remove following line from `hgrc` file stored inside the repository:
178 `%include hgrc_rhodecode`
@@ -0,0 +1,176 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 os
22 import pytest
23
24 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.lib import helpers as h
26 from rhodecode.model.db import Repository, UserRepoToPerm, User
27 from rhodecode.model.meta import Session
28 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.tests import (
30 assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH, TestController)
31 from rhodecode.tests.fixture import Fixture
32
33 fixture = Fixture()
34
35
36 def route_path(name, params=None, **kwargs):
37 import urllib
38
39 base_url = {
40 'repo_groups': ADMIN_PREFIX + '/repo_groups',
41 'repo_group_new': ADMIN_PREFIX + '/repo_group/new',
42 'repo_group_create': ADMIN_PREFIX + '/repo_group/create',
43
44 }[name].format(**kwargs)
45
46 if params:
47 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
48 return base_url
49
50
51 def _get_permission_for_user(user, repo):
52 perm = UserRepoToPerm.query()\
53 .filter(UserRepoToPerm.repository ==
54 Repository.get_by_repo_name(repo))\
55 .filter(UserRepoToPerm.user == User.get_by_username(user))\
56 .all()
57 return perm
58
59
60 @pytest.mark.usefixtures("app")
61 class TestAdminRepositoryGroups(object):
62 def test_show_repo_groups(self, autologin_user):
63 response = self.app.get(route_path('repo_groups'))
64 response.mustcontain('data: []')
65
66 def test_show_repo_groups_after_creating_group(self, autologin_user):
67 fixture.create_repo_group('test_repo_group')
68 response = self.app.get(route_path('repo_groups'))
69 response.mustcontain('"name_raw": "test_repo_group"')
70 fixture.destroy_repo_group('test_repo_group')
71
72 def test_new(self, autologin_user):
73 self.app.get(route_path('repo_group_new'))
74
75 def test_new_with_parent_group(self, autologin_user, user_util):
76 gr = user_util.create_repo_group()
77
78 self.app.get(route_path('repo_group_new'),
79 params=dict(parent_group=gr.group_name))
80
81 def test_new_by_regular_user_no_permission(self, autologin_regular_user):
82 self.app.get(route_path('repo_group_new'), status=403)
83
84 @pytest.mark.parametrize('repo_group_name', [
85 'git_repo',
86 'git_repo_ąć',
87 'hg_repo',
88 '12345',
89 'hg_repo_ąć',
90 ])
91 def test_create(self, autologin_user, repo_group_name, csrf_token):
92 repo_group_name_unicode = repo_group_name.decode('utf8')
93 description = 'description for newly created repo group'
94
95 response = self.app.post(
96 route_path('repo_group_create'),
97 fixture._get_group_create_params(
98 group_name=repo_group_name,
99 group_description=description,
100 csrf_token=csrf_token))
101
102 # run the check page that triggers the flash message
103 repo_gr_url = h.route_path(
104 'repo_group_home', repo_group_name=repo_group_name)
105
106 assert_session_flash(
107 response,
108 'Created repository group <a href="%s">%s</a>' % (
109 repo_gr_url, repo_group_name_unicode))
110
111 # # test if the repo group was created in the database
112 new_repo_group = RepoGroupModel()._get_repo_group(
113 repo_group_name_unicode)
114 assert new_repo_group is not None
115
116 assert new_repo_group.group_name == repo_group_name_unicode
117 assert new_repo_group.group_description == description
118
119 # test if the repository is visible in the list ?
120 response = self.app.get(repo_gr_url)
121 response.mustcontain(repo_group_name)
122
123 # test if the repository group was created on filesystem
124 is_on_filesystem = os.path.isdir(
125 os.path.join(TESTS_TMP_PATH, repo_group_name))
126 if not is_on_filesystem:
127 self.fail('no repo group %s in filesystem' % repo_group_name)
128
129 RepoGroupModel().delete(repo_group_name_unicode)
130 Session().commit()
131
132 @pytest.mark.parametrize('repo_group_name', [
133 'git_repo',
134 'git_repo_ąć',
135 'hg_repo',
136 '12345',
137 'hg_repo_ąć',
138 ])
139 def test_create_subgroup(self, autologin_user, user_util, repo_group_name, csrf_token):
140 parent_group = user_util.create_repo_group()
141 parent_group_name = parent_group.group_name
142
143 expected_group_name = '{}/{}'.format(
144 parent_group_name, repo_group_name)
145 expected_group_name_unicode = expected_group_name.decode('utf8')
146
147 try:
148 response = self.app.post(
149 route_path('repo_group_create'),
150 fixture._get_group_create_params(
151 group_name=repo_group_name,
152 group_parent_id=parent_group.group_id,
153 group_description='Test desciption',
154 csrf_token=csrf_token))
155
156 assert_session_flash(
157 response,
158 u'Created repository group <a href="%s">%s</a>' % (
159 h.route_path('repo_group_home',
160 repo_group_name=expected_group_name),
161 expected_group_name_unicode))
162 finally:
163 RepoGroupModel().delete(expected_group_name_unicode)
164 Session().commit()
165
166 def test_user_with_creation_permissions_cannot_create_subgroups(
167 self, autologin_regular_user, user_util):
168
169 user_util.grant_user_permission(
170 TEST_USER_REGULAR_LOGIN, 'hg.repogroup.create.true')
171 parent_group = user_util.create_repo_group()
172 parent_group_id = parent_group.group_id
173 self.app.get(
174 route_path('repo_group_new',
175 params=dict(parent_group=parent_group_id), ),
176 status=403)
@@ -0,0 +1,170 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 UserGroup, User
24 from rhodecode.model.meta import Session
25
26 from rhodecode.tests import (
27 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
29
30 fixture = Fixture()
31
32
33 def route_path(name, params=None, **kwargs):
34 import urllib
35 from rhodecode.apps._base import ADMIN_PREFIX
36
37 base_url = {
38 'user_groups': ADMIN_PREFIX + '/user_groups',
39 'user_groups_data': ADMIN_PREFIX + '/user_groups_data',
40 'user_group_members_data': ADMIN_PREFIX + '/user_groups/{user_group_id}/members',
41 'user_groups_new': ADMIN_PREFIX + '/user_groups/new',
42 'user_groups_create': ADMIN_PREFIX + '/user_groups/create',
43 'edit_user_group': ADMIN_PREFIX + '/user_groups/{user_group_id}/edit',
44 }[name].format(**kwargs)
45
46 if params:
47 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
48 return base_url
49
50
51 class TestAdminUserGroupsView(TestController):
52
53 def test_show_users(self):
54 self.log_user()
55 self.app.get(route_path('user_groups'))
56
57 def test_show_user_groups_data(self, xhr_header):
58 self.log_user()
59 response = self.app.get(route_path(
60 'user_groups_data'), extra_environ=xhr_header)
61
62 all_user_groups = UserGroup.query().count()
63 assert response.json['recordsTotal'] == all_user_groups
64
65 def test_show_user_groups_data_filtered(self, xhr_header):
66 self.log_user()
67 response = self.app.get(route_path(
68 'user_groups_data', params={'search[value]': 'empty_search'}),
69 extra_environ=xhr_header)
70
71 all_user_groups = UserGroup.query().count()
72 assert response.json['recordsTotal'] == all_user_groups
73 assert response.json['recordsFiltered'] == 0
74
75 def test_usergroup_escape(self, user_util, xhr_header):
76 self.log_user()
77
78 xss_img = '<img src="/image1" onload="alert(\'Hello, World!\');">'
79 user = user_util.create_user()
80 user.name = xss_img
81 user.lastname = xss_img
82 Session().add(user)
83 Session().commit()
84
85 user_group = user_util.create_user_group()
86
87 user_group.users_group_name = xss_img
88 user_group.user_group_description = '<strong onload="alert();">DESC</strong>'
89
90 response = self.app.get(
91 route_path('user_groups_data'), extra_environ=xhr_header)
92
93 response.mustcontain(
94 '&lt;strong onload=&#34;alert();&#34;&gt;DESC&lt;/strong&gt;')
95 response.mustcontain(
96 '&lt;img src=&#34;/image1&#34; onload=&#34;'
97 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
98
99 def test_edit_user_group_autocomplete_empty_members(self, xhr_header, user_util):
100 self.log_user()
101 ug = user_util.create_user_group()
102 response = self.app.get(
103 route_path('user_group_members_data', user_group_id=ug.users_group_id),
104 extra_environ=xhr_header)
105
106 assert response.json == {'members': []}
107
108 def test_edit_user_group_autocomplete_members(self, xhr_header, user_util):
109 self.log_user()
110 members = [u.user_id for u in User.get_all()]
111 ug = user_util.create_user_group(members=members)
112 response = self.app.get(
113 route_path('user_group_members_data',
114 user_group_id=ug.users_group_id),
115 extra_environ=xhr_header)
116
117 assert len(response.json['members']) == len(members)
118
119 def test_creation_page(self):
120 self.log_user()
121 self.app.get(route_path('user_groups_new'), status=200)
122
123 def test_create(self):
124 from rhodecode.lib import helpers as h
125
126 self.log_user()
127 users_group_name = 'test_user_group'
128 response = self.app.post(route_path('user_groups_create'), {
129 'users_group_name': users_group_name,
130 'user_group_description': 'DESC',
131 'active': True,
132 'csrf_token': self.csrf_token})
133
134 user_group_id = UserGroup.get_by_group_name(
135 users_group_name).users_group_id
136
137 user_group_link = h.link_to(
138 users_group_name,
139 route_path('edit_user_group', user_group_id=user_group_id))
140
141 assert_session_flash(
142 response,
143 'Created user group %s' % user_group_link)
144
145 fixture.destroy_user_group(users_group_name)
146
147 def test_create_with_empty_name(self):
148 self.log_user()
149
150 response = self.app.post(route_path('user_groups_create'), {
151 'users_group_name': '',
152 'user_group_description': 'DESC',
153 'active': True,
154 'csrf_token': self.csrf_token}, status=200)
155
156 response.mustcontain('Please enter a value')
157
158 def test_create_duplicate(self, user_util):
159 self.log_user()
160
161 user_group = user_util.create_user_group()
162 duplicate_name = user_group.users_group_name
163 response = self.app.post(route_path('user_groups_create'), {
164 'users_group_name': duplicate_name,
165 'user_group_description': 'DESC',
166 'active': True,
167 'csrf_token': self.csrf_token}, status=200)
168
169 response.mustcontain(
170 'User group `{}` already exists'.format(user_group.users_group_name))
@@ -0,0 +1,173 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, UserSshKeys
24
25 from rhodecode.tests import TestController, assert_session_flash
26 from rhodecode.tests.fixture import Fixture
27
28 fixture = Fixture()
29
30
31 def route_path(name, params=None, **kwargs):
32 import urllib
33 from rhodecode.apps._base import ADMIN_PREFIX
34
35 base_url = {
36 'edit_user_ssh_keys':
37 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys',
38 'edit_user_ssh_keys_generate_keypair':
39 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/generate',
40 'edit_user_ssh_keys_add':
41 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/new',
42 'edit_user_ssh_keys_delete':
43 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/delete',
44
45 }[name].format(**kwargs)
46
47 if params:
48 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
49 return base_url
50
51
52 class TestAdminUsersSshKeysView(TestController):
53 INVALID_KEY = """\
54 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vevJsuZds1iNU5
55 LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSykfR1D1TdluyIpQLrwgH5kb
56 n8FkVI8zBMCKakxowvN67B0R7b1BT4PPzW2JlOXei/m9W12ZY484VTow6/B+kf2Q8
57 cP8tmCJmKWZma5Em7OTUhvjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6
58 jvdphZTc30I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zP
59 qPFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL
60 your_email@example.com
61 """
62 VALID_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vev' \
63 'JsuZds1iNU5LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSy' \
64 'kfR1D1TdluyIpQLrwgH5kbn8FkVI8zBMCKakxowvN67B0R7b1BT4PP' \
65 'zW2JlOXei/m9W12ZY484VTow6/B+kf2Q8cP8tmCJmKWZma5Em7OTUh' \
66 'vjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6jvdphZTc30' \
67 'I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zPq' \
68 'PFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL ' \
69 'your_email@example.com'
70
71 def test_ssh_keys_default_user(self):
72 self.log_user()
73 user = User.get_default_user()
74 self.app.get(
75 route_path('edit_user_ssh_keys', user_id=user.user_id),
76 status=302)
77
78 def test_add_ssh_key_error(self, user_util):
79 self.log_user()
80 user = user_util.create_user()
81 user_id = user.user_id
82
83 key_data = self.INVALID_KEY
84
85 desc = 'MY SSH KEY'
86 response = self.app.post(
87 route_path('edit_user_ssh_keys_add', user_id=user_id),
88 {'description': desc, 'key_data': key_data,
89 'csrf_token': self.csrf_token})
90 assert_session_flash(response, 'An error occurred during ssh '
91 'key saving: Unable to decode the key')
92
93 def test_ssh_key_duplicate(self, user_util):
94 self.log_user()
95 user = user_util.create_user()
96 user_id = user.user_id
97
98 key_data = self.VALID_KEY
99
100 desc = 'MY SSH KEY'
101 response = self.app.post(
102 route_path('edit_user_ssh_keys_add', user_id=user_id),
103 {'description': desc, 'key_data': key_data,
104 'csrf_token': self.csrf_token})
105 assert_session_flash(response, 'Ssh Key successfully created')
106 response.follow() # flush session flash
107
108 # add the same key AGAIN
109 desc = 'MY SSH KEY'
110 response = self.app.post(
111 route_path('edit_user_ssh_keys_add', user_id=user_id),
112 {'description': desc, 'key_data': key_data,
113 'csrf_token': self.csrf_token})
114 assert_session_flash(response, 'An error occurred during ssh key '
115 'saving: Such key already exists, '
116 'please use a different one')
117
118 def test_add_ssh_key(self, user_util):
119 self.log_user()
120 user = user_util.create_user()
121 user_id = user.user_id
122
123 key_data = self.VALID_KEY
124
125 desc = 'MY SSH KEY'
126 response = self.app.post(
127 route_path('edit_user_ssh_keys_add', user_id=user_id),
128 {'description': desc, 'key_data': key_data,
129 'csrf_token': self.csrf_token})
130 assert_session_flash(response, 'Ssh Key successfully created')
131
132 response = response.follow()
133 response.mustcontain(desc)
134
135 def test_delete_ssh_key(self, user_util):
136 self.log_user()
137 user = user_util.create_user()
138 user_id = user.user_id
139
140 key_data = self.VALID_KEY
141
142 desc = 'MY SSH KEY'
143 response = self.app.post(
144 route_path('edit_user_ssh_keys_add', user_id=user_id),
145 {'description': desc, 'key_data': key_data,
146 'csrf_token': self.csrf_token})
147 assert_session_flash(response, 'Ssh Key successfully created')
148 response = response.follow() # flush the Session flash
149
150 # now delete our key
151 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
152 assert 1 == len(keys)
153
154 response = self.app.post(
155 route_path('edit_user_ssh_keys_delete', user_id=user_id),
156 {'del_ssh_key': keys[0].ssh_key_id,
157 'csrf_token': self.csrf_token})
158
159 assert_session_flash(response, 'Ssh key successfully deleted')
160 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
161 assert 0 == len(keys)
162
163 def test_generate_keypair(self, user_util):
164 self.log_user()
165 user = user_util.create_user()
166 user_id = user.user_id
167
168 response = self.app.get(
169 route_path('edit_user_ssh_keys_generate_keypair', user_id=user_id))
170
171 response.mustcontain('Private key')
172 response.mustcontain('Public key')
173 response.mustcontain('-----BEGIN RSA PRIVATE KEY-----')
@@ -0,0 +1,111 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 import formencode
24 import formencode.htmlfill
25
26 from pyramid.view import view_config
27 from pyramid.httpexceptions import HTTPFound
28 from pyramid.renderers import render
29 from pyramid.response import Response
30
31 from rhodecode.apps._base import BaseAppView
32 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 from rhodecode.lib import helpers as h
35 from rhodecode.model.forms import DefaultsForm
36 from rhodecode.model.meta import Session
37 from rhodecode import BACKENDS
38 from rhodecode.model.settings import SettingsModel
39
40 log = logging.getLogger(__name__)
41
42
43 class AdminDefaultSettingsView(BaseAppView):
44 def load_default_context(self):
45 c = self._get_local_tmpl_context()
46
47 self._register_global_c(c)
48 return c
49
50 @LoginRequired()
51 @HasPermissionAllDecorator('hg.admin')
52 @view_config(
53 route_name='admin_defaults_repositories', request_method='GET',
54 renderer='rhodecode:templates/admin/defaults/defaults.mako')
55 def defaults_repository_show(self):
56 c = self.load_default_context()
57 c.backends = BACKENDS.keys()
58 c.active = 'repositories'
59 defaults = SettingsModel().get_default_repo_settings()
60
61 data = render(
62 'rhodecode:templates/admin/defaults/defaults.mako',
63 self._get_template_context(c), self.request)
64 html = formencode.htmlfill.render(
65 data,
66 defaults=defaults,
67 encoding="UTF-8",
68 force_defaults=False
69 )
70 return Response(html)
71
72 @LoginRequired()
73 @HasPermissionAllDecorator('hg.admin')
74 @CSRFRequired()
75 @view_config(
76 route_name='admin_defaults_repositories_update', request_method='POST',
77 renderer='rhodecode:templates/admin/defaults/defaults.mako')
78 def defaults_repository_update(self):
79 _ = self.request.translate
80 c = self.load_default_context()
81 c.active = 'repositories'
82 form = DefaultsForm()()
83
84 try:
85 form_result = form.to_python(dict(self.request.POST))
86 for k, v in form_result.iteritems():
87 setting = SettingsModel().create_or_update_setting(k, v)
88 Session().add(setting)
89 Session().commit()
90 h.flash(_('Default settings updated successfully'),
91 category='success')
92
93 except formencode.Invalid as errors:
94 data = render(
95 'rhodecode:templates/admin/defaults/defaults.mako',
96 self._get_template_context(c), self.request)
97 html = formencode.htmlfill.render(
98 data,
99 defaults=errors.value,
100 errors=errors.error_dict or {},
101 prefix_error=False,
102 encoding="UTF-8",
103 force_defaults=False
104 )
105 return Response(html)
106 except Exception:
107 log.exception('Exception in update action')
108 h.flash(_('Error occurred during update of default values'),
109 category='error')
110
111 raise HTTPFound(h.route_path('admin_defaults_repositories'))
@@ -0,0 +1,482 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 re
22 import logging
23 import formencode
24 import formencode.htmlfill
25 import datetime
26 from pyramid.interfaces import IRoutesMapper
27
28 from pyramid.view import view_config
29 from pyramid.httpexceptions import HTTPFound
30 from pyramid.renderers import render
31 from pyramid.response import Response
32
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
34 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
35 from rhodecode.events import trigger
36
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
40 from rhodecode.lib.utils2 import aslist, safe_unicode
41 from rhodecode.model.db import (
42 or_, coalesce, User, UserIpMap, UserSshKeys)
43 from rhodecode.model.forms import (
44 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
45 from rhodecode.model.meta import Session
46 from rhodecode.model.permission import PermissionModel
47 from rhodecode.model.settings import SettingsModel
48
49
50 log = logging.getLogger(__name__)
51
52
53 class AdminPermissionsView(BaseAppView, DataGridAppView):
54 def load_default_context(self):
55 c = self._get_local_tmpl_context()
56
57 self._register_global_c(c)
58 PermissionModel().set_global_permission_choices(
59 c, gettext_translator=self.request.translate)
60 return c
61
62 @LoginRequired()
63 @HasPermissionAllDecorator('hg.admin')
64 @view_config(
65 route_name='admin_permissions_application', request_method='GET',
66 renderer='rhodecode:templates/admin/permissions/permissions.mako')
67 def permissions_application(self):
68 c = self.load_default_context()
69 c.active = 'application'
70
71 c.user = User.get_default_user(refresh=True)
72
73 app_settings = SettingsModel().get_all_settings()
74 defaults = {
75 'anonymous': c.user.active,
76 'default_register_message': app_settings.get(
77 'rhodecode_register_message')
78 }
79 defaults.update(c.user.get_default_perms())
80
81 data = render('rhodecode:templates/admin/permissions/permissions.mako',
82 self._get_template_context(c), self.request)
83 html = formencode.htmlfill.render(
84 data,
85 defaults=defaults,
86 encoding="UTF-8",
87 force_defaults=False
88 )
89 return Response(html)
90
91 @LoginRequired()
92 @HasPermissionAllDecorator('hg.admin')
93 @CSRFRequired()
94 @view_config(
95 route_name='admin_permissions_application_update', request_method='POST',
96 renderer='rhodecode:templates/admin/permissions/permissions.mako')
97 def permissions_application_update(self):
98 _ = self.request.translate
99 c = self.load_default_context()
100 c.active = 'application'
101
102 _form = ApplicationPermissionsForm(
103 [x[0] for x in c.register_choices],
104 [x[0] for x in c.password_reset_choices],
105 [x[0] for x in c.extern_activate_choices])()
106
107 try:
108 form_result = _form.to_python(dict(self.request.POST))
109 form_result.update({'perm_user_name': User.DEFAULT_USER})
110 PermissionModel().update_application_permissions(form_result)
111
112 settings = [
113 ('register_message', 'default_register_message'),
114 ]
115 for setting, form_key in settings:
116 sett = SettingsModel().create_or_update_setting(
117 setting, form_result[form_key])
118 Session().add(sett)
119
120 Session().commit()
121 h.flash(_('Application permissions updated successfully'),
122 category='success')
123
124 except formencode.Invalid as errors:
125 defaults = errors.value
126
127 data = render(
128 'rhodecode:templates/admin/permissions/permissions.mako',
129 self._get_template_context(c), self.request)
130 html = formencode.htmlfill.render(
131 data,
132 defaults=defaults,
133 errors=errors.error_dict or {},
134 prefix_error=False,
135 encoding="UTF-8",
136 force_defaults=False
137 )
138 return Response(html)
139
140 except Exception:
141 log.exception("Exception during update of permissions")
142 h.flash(_('Error occurred during update of permissions'),
143 category='error')
144
145 raise HTTPFound(h.route_path('admin_permissions_application'))
146
147 @LoginRequired()
148 @HasPermissionAllDecorator('hg.admin')
149 @view_config(
150 route_name='admin_permissions_object', request_method='GET',
151 renderer='rhodecode:templates/admin/permissions/permissions.mako')
152 def permissions_objects(self):
153 c = self.load_default_context()
154 c.active = 'objects'
155
156 c.user = User.get_default_user(refresh=True)
157 defaults = {}
158 defaults.update(c.user.get_default_perms())
159
160 data = render(
161 'rhodecode:templates/admin/permissions/permissions.mako',
162 self._get_template_context(c), self.request)
163 html = formencode.htmlfill.render(
164 data,
165 defaults=defaults,
166 encoding="UTF-8",
167 force_defaults=False
168 )
169 return Response(html)
170
171 @LoginRequired()
172 @HasPermissionAllDecorator('hg.admin')
173 @CSRFRequired()
174 @view_config(
175 route_name='admin_permissions_object_update', request_method='POST',
176 renderer='rhodecode:templates/admin/permissions/permissions.mako')
177 def permissions_objects_update(self):
178 _ = self.request.translate
179 c = self.load_default_context()
180 c.active = 'objects'
181
182 _form = ObjectPermissionsForm(
183 [x[0] for x in c.repo_perms_choices],
184 [x[0] for x in c.group_perms_choices],
185 [x[0] for x in c.user_group_perms_choices])()
186
187 try:
188 form_result = _form.to_python(dict(self.request.POST))
189 form_result.update({'perm_user_name': User.DEFAULT_USER})
190 PermissionModel().update_object_permissions(form_result)
191
192 Session().commit()
193 h.flash(_('Object permissions updated successfully'),
194 category='success')
195
196 except formencode.Invalid as errors:
197 defaults = errors.value
198
199 data = render(
200 'rhodecode:templates/admin/permissions/permissions.mako',
201 self._get_template_context(c), self.request)
202 html = formencode.htmlfill.render(
203 data,
204 defaults=defaults,
205 errors=errors.error_dict or {},
206 prefix_error=False,
207 encoding="UTF-8",
208 force_defaults=False
209 )
210 return Response(html)
211 except Exception:
212 log.exception("Exception during update of permissions")
213 h.flash(_('Error occurred during update of permissions'),
214 category='error')
215
216 raise HTTPFound(h.route_path('admin_permissions_object'))
217
218 @LoginRequired()
219 @HasPermissionAllDecorator('hg.admin')
220 @view_config(
221 route_name='admin_permissions_global', request_method='GET',
222 renderer='rhodecode:templates/admin/permissions/permissions.mako')
223 def permissions_global(self):
224 c = self.load_default_context()
225 c.active = 'global'
226
227 c.user = User.get_default_user(refresh=True)
228 defaults = {}
229 defaults.update(c.user.get_default_perms())
230
231 data = render(
232 'rhodecode:templates/admin/permissions/permissions.mako',
233 self._get_template_context(c), self.request)
234 html = formencode.htmlfill.render(
235 data,
236 defaults=defaults,
237 encoding="UTF-8",
238 force_defaults=False
239 )
240 return Response(html)
241
242 @LoginRequired()
243 @HasPermissionAllDecorator('hg.admin')
244 @CSRFRequired()
245 @view_config(
246 route_name='admin_permissions_global_update', request_method='POST',
247 renderer='rhodecode:templates/admin/permissions/permissions.mako')
248 def permissions_global_update(self):
249 _ = self.request.translate
250 c = self.load_default_context()
251 c.active = 'global'
252
253 _form = UserPermissionsForm(
254 [x[0] for x in c.repo_create_choices],
255 [x[0] for x in c.repo_create_on_write_choices],
256 [x[0] for x in c.repo_group_create_choices],
257 [x[0] for x in c.user_group_create_choices],
258 [x[0] for x in c.fork_choices],
259 [x[0] for x in c.inherit_default_permission_choices])()
260
261 try:
262 form_result = _form.to_python(dict(self.request.POST))
263 form_result.update({'perm_user_name': User.DEFAULT_USER})
264 PermissionModel().update_user_permissions(form_result)
265
266 Session().commit()
267 h.flash(_('Global permissions updated successfully'),
268 category='success')
269
270 except formencode.Invalid as errors:
271 defaults = errors.value
272
273 data = render(
274 'rhodecode:templates/admin/permissions/permissions.mako',
275 self._get_template_context(c), self.request)
276 html = formencode.htmlfill.render(
277 data,
278 defaults=defaults,
279 errors=errors.error_dict or {},
280 prefix_error=False,
281 encoding="UTF-8",
282 force_defaults=False
283 )
284 return Response(html)
285 except Exception:
286 log.exception("Exception during update of permissions")
287 h.flash(_('Error occurred during update of permissions'),
288 category='error')
289
290 raise HTTPFound(h.route_path('admin_permissions_global'))
291
292 @LoginRequired()
293 @HasPermissionAllDecorator('hg.admin')
294 @view_config(
295 route_name='admin_permissions_ips', request_method='GET',
296 renderer='rhodecode:templates/admin/permissions/permissions.mako')
297 def permissions_ips(self):
298 c = self.load_default_context()
299 c.active = 'ips'
300
301 c.user = User.get_default_user(refresh=True)
302 c.user_ip_map = (
303 UserIpMap.query().filter(UserIpMap.user == c.user).all())
304
305 return self._get_template_context(c)
306
307 @LoginRequired()
308 @HasPermissionAllDecorator('hg.admin')
309 @view_config(
310 route_name='admin_permissions_overview', request_method='GET',
311 renderer='rhodecode:templates/admin/permissions/permissions.mako')
312 def permissions_overview(self):
313 c = self.load_default_context()
314 c.active = 'perms'
315
316 c.user = User.get_default_user(refresh=True)
317 c.perm_user = c.user.AuthUser()
318 return self._get_template_context(c)
319
320 @LoginRequired()
321 @HasPermissionAllDecorator('hg.admin')
322 @view_config(
323 route_name='admin_permissions_auth_token_access', request_method='GET',
324 renderer='rhodecode:templates/admin/permissions/permissions.mako')
325 def auth_token_access(self):
326 from rhodecode import CONFIG
327
328 c = self.load_default_context()
329 c.active = 'auth_token_access'
330
331 c.user = User.get_default_user(refresh=True)
332 c.perm_user = c.user.AuthUser()
333
334 mapper = self.request.registry.queryUtility(IRoutesMapper)
335 c.view_data = []
336
337 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
338 introspector = self.request.registry.introspector
339
340 view_intr = {}
341 for view_data in introspector.get_category('views'):
342 intr = view_data['introspectable']
343
344 if 'route_name' in intr and intr['attr']:
345 view_intr[intr['route_name']] = '{}:{}'.format(
346 str(intr['derived_callable'].func_name), intr['attr']
347 )
348
349 c.whitelist_key = 'api_access_controllers_whitelist'
350 c.whitelist_file = CONFIG.get('__file__')
351 whitelist_views = aslist(
352 CONFIG.get(c.whitelist_key), sep=',')
353
354 for route_info in mapper.get_routes():
355 if not route_info.name.startswith('__'):
356 routepath = route_info.pattern
357
358 def replace(matchobj):
359 if matchobj.group(1):
360 return "{%s}" % matchobj.group(1).split(':')[0]
361 else:
362 return "{%s}" % matchobj.group(2)
363
364 routepath = _argument_prog.sub(replace, routepath)
365
366 if not routepath.startswith('/'):
367 routepath = '/' + routepath
368
369 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
370 active = view_fqn in whitelist_views
371 c.view_data.append((route_info.name, view_fqn, routepath, active))
372
373 c.whitelist_views = whitelist_views
374 return self._get_template_context(c)
375
376 def ssh_enabled(self):
377 return self.request.registry.settings.get(
378 'ssh.generate_authorized_keyfile')
379
380 @LoginRequired()
381 @HasPermissionAllDecorator('hg.admin')
382 @view_config(
383 route_name='admin_permissions_ssh_keys', request_method='GET',
384 renderer='rhodecode:templates/admin/permissions/permissions.mako')
385 def ssh_keys(self):
386 c = self.load_default_context()
387 c.active = 'ssh_keys'
388 c.ssh_enabled = self.ssh_enabled()
389 return self._get_template_context(c)
390
391 @LoginRequired()
392 @HasPermissionAllDecorator('hg.admin')
393 @view_config(
394 route_name='admin_permissions_ssh_keys_data', request_method='GET',
395 renderer='json_ext', xhr=True)
396 def ssh_keys_data(self):
397 _ = self.request.translate
398 column_map = {
399 'fingerprint': 'ssh_key_fingerprint',
400 'username': User.username
401 }
402 draw, start, limit = self._extract_chunk(self.request)
403 search_q, order_by, order_dir = self._extract_ordering(
404 self.request, column_map=column_map)
405
406 ssh_keys_data_total_count = UserSshKeys.query()\
407 .count()
408
409 # json generate
410 base_q = UserSshKeys.query().join(UserSshKeys.user)
411
412 if search_q:
413 like_expression = u'%{}%'.format(safe_unicode(search_q))
414 base_q = base_q.filter(or_(
415 User.username.ilike(like_expression),
416 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
417 ))
418
419 users_data_total_filtered_count = base_q.count()
420
421 sort_col = self._get_order_col(order_by, UserSshKeys)
422 if sort_col:
423 if order_dir == 'asc':
424 # handle null values properly to order by NULL last
425 if order_by in ['created_on']:
426 sort_col = coalesce(sort_col, datetime.date.max)
427 sort_col = sort_col.asc()
428 else:
429 # handle null values properly to order by NULL last
430 if order_by in ['created_on']:
431 sort_col = coalesce(sort_col, datetime.date.min)
432 sort_col = sort_col.desc()
433
434 base_q = base_q.order_by(sort_col)
435 base_q = base_q.offset(start).limit(limit)
436
437 ssh_keys = base_q.all()
438
439 ssh_keys_data = []
440 for ssh_key in ssh_keys:
441 ssh_keys_data.append({
442 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
443 "fingerprint": ssh_key.ssh_key_fingerprint,
444 "description": ssh_key.description,
445 "created_on": h.format_date(ssh_key.created_on),
446 "accessed_on": h.format_date(ssh_key.accessed_on),
447 "action": h.link_to(
448 _('Edit'), h.route_path('edit_user_ssh_keys',
449 user_id=ssh_key.user.user_id))
450 })
451
452 data = ({
453 'draw': draw,
454 'data': ssh_keys_data,
455 'recordsTotal': ssh_keys_data_total_count,
456 'recordsFiltered': users_data_total_filtered_count,
457 })
458
459 return data
460
461 @LoginRequired()
462 @HasPermissionAllDecorator('hg.admin')
463 @CSRFRequired()
464 @view_config(
465 route_name='admin_permissions_ssh_keys_update', request_method='POST',
466 renderer='rhodecode:templates/admin/permissions/permissions.mako')
467 def ssh_keys_update(self):
468 _ = self.request.translate
469 self.load_default_context()
470
471 ssh_enabled = self.ssh_enabled()
472 key_file = self.request.registry.settings.get(
473 'ssh.authorized_keys_file_path')
474 if ssh_enabled:
475 trigger(SshKeyFileChangeEvent(), self.request.registry)
476 h.flash(_('Updated SSH keys file: {}').format(key_file),
477 category='success')
478 else:
479 h.flash(_('SSH key support is disabled in .ini file'),
480 category='warning')
481
482 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -0,0 +1,91 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 import psutil
24 from pyramid.view import view_config
25
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.admin.navigation import navigation_list
28 from rhodecode.lib.auth import (
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30 from rhodecode.lib.utils2 import safe_int
31
32 log = logging.getLogger(__name__)
33
34
35 class AdminProcessManagementView(BaseAppView):
36 def load_default_context(self):
37 c = self._get_local_tmpl_context()
38 self._register_global_c(c)
39 return c
40
41 @LoginRequired()
42 @HasPermissionAllDecorator('hg.admin')
43 @view_config(
44 route_name='admin_settings_process_management', request_method='GET',
45 renderer='rhodecode:templates/admin/settings/settings.mako')
46 def process_management(self):
47 _ = self.request.translate
48 c = self.load_default_context()
49
50 c.active = 'process_management'
51 c.navlist = navigation_list(self.request)
52 c.gunicorn_processes = (
53 p for p in psutil.process_iter() if 'gunicorn' in p.name())
54 return self._get_template_context(c)
55
56 @LoginRequired()
57 @HasPermissionAllDecorator('hg.admin')
58 @CSRFRequired()
59 @view_config(
60 route_name='admin_settings_process_management_signal',
61 request_method='POST', renderer='json_ext')
62 def process_management_signal(self):
63 pids = self.request.json.get('pids', [])
64 result = []
65 def on_terminate(proc):
66 msg = "process `PID:{}` terminated with exit code {}".format(
67 proc.pid, proc.returncode)
68 result.append(msg)
69
70 procs = []
71 for pid in pids:
72 pid = safe_int(pid)
73 if pid:
74 try:
75 proc = psutil.Process(pid)
76 except psutil.NoSuchProcess:
77 continue
78
79 children = proc.children(recursive=True)
80 if children:
81 print('Wont kill Master Process')
82 else:
83 procs.append(proc)
84
85 for p in procs:
86 p.terminate()
87 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
88 for p in alive:
89 p.kill()
90
91 return {'result': result}
@@ -0,0 +1,204 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 import formencode
23 import formencode.htmlfill
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
27 from pyramid.renderers import render
28 from pyramid.response import Response
29
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
31
32 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.auth import (
34 LoginRequired, CSRFRequired, NotAnonymous,
35 HasPermissionAny, HasRepoGroupPermissionAny)
36 from rhodecode.lib import helpers as h, audit_logger
37 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.model.forms import RepoGroupForm
39 from rhodecode.model.repo_group import RepoGroupModel
40 from rhodecode.model.scm import RepoGroupList
41 from rhodecode.model.db import Session, RepoGroup
42
43 log = logging.getLogger(__name__)
44
45
46 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
47
48 def load_default_context(self):
49 c = self._get_local_tmpl_context()
50 self._register_global_c(c)
51 return c
52
53 def _load_form_data(self, c):
54 allow_empty_group = False
55
56 if self._can_create_repo_group():
57 # we're global admin, we're ok and we can create TOP level groups
58 allow_empty_group = True
59
60 # override the choices for this form, we need to filter choices
61 # and display only those we have ADMIN right
62 groups_with_admin_rights = RepoGroupList(
63 RepoGroup.query().all(),
64 perm_set=['group.admin'])
65 c.repo_groups = RepoGroup.groups_choices(
66 groups=groups_with_admin_rights,
67 show_empty_group=allow_empty_group)
68
69 def _can_create_repo_group(self, parent_group_id=None):
70 is_admin = HasPermissionAny('hg.admin')('group create controller')
71 create_repo_group = HasPermissionAny(
72 'hg.repogroup.create.true')('group create controller')
73 if is_admin or (create_repo_group and not parent_group_id):
74 # we're global admin, or we have global repo group create
75 # permission
76 # we're ok and we can create TOP level groups
77 return True
78 elif parent_group_id:
79 # we check the permission if we can write to parent group
80 group = RepoGroup.get(parent_group_id)
81 group_name = group.group_name if group else None
82 if HasRepoGroupPermissionAny('group.admin')(
83 group_name, 'check if user is an admin of group'):
84 # we're an admin of passed in group, we're ok.
85 return True
86 else:
87 return False
88 return False
89
90 @LoginRequired()
91 @NotAnonymous()
92 # perms check inside
93 @view_config(
94 route_name='repo_groups', request_method='GET',
95 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
96 def repo_group_list(self):
97 c = self.load_default_context()
98
99 repo_group_list = RepoGroup.get_all_repo_groups()
100 repo_group_list_acl = RepoGroupList(
101 repo_group_list, perm_set=['group.admin'])
102 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
103 repo_group_list=repo_group_list_acl, admin=True)
104 c.data = json.dumps(repo_group_data)
105 return self._get_template_context(c)
106
107 @LoginRequired()
108 @NotAnonymous()
109 # perm checks inside
110 @view_config(
111 route_name='repo_group_new', request_method='GET',
112 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
113 def repo_group_new(self):
114 c = self.load_default_context()
115
116 # perm check for admin, create_group perm or admin of parent_group
117 parent_group_id = safe_int(self.request.GET.get('parent_group'))
118 if not self._can_create_repo_group(parent_group_id):
119 raise HTTPForbidden()
120
121 self._load_form_data(c)
122
123 defaults = {} # Future proof for default of repo group
124 data = render(
125 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
126 self._get_template_context(c), self.request)
127 html = formencode.htmlfill.render(
128 data,
129 defaults=defaults,
130 encoding="UTF-8",
131 force_defaults=False
132 )
133 return Response(html)
134
135 @LoginRequired()
136 @NotAnonymous()
137 @CSRFRequired()
138 # perm checks inside
139 @view_config(
140 route_name='repo_group_create', request_method='POST',
141 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
142 def repo_group_create(self):
143 c = self.load_default_context()
144 _ = self.request.translate
145
146 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
147 can_create = self._can_create_repo_group(parent_group_id)
148
149 self._load_form_data(c)
150 # permissions for can create group based on parent_id are checked
151 # here in the Form
152 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
153 repo_group_form = RepoGroupForm(available_groups=available_groups,
154 can_create_in_root=can_create)()
155
156 repo_group_name = self.request.POST.get('group_name')
157 try:
158 owner = self._rhodecode_user
159 form_result = repo_group_form.to_python(dict(self.request.POST))
160 repo_group = RepoGroupModel().create(
161 group_name=form_result['group_name_full'],
162 group_description=form_result['group_description'],
163 owner=owner.user_id,
164 copy_permissions=form_result['group_copy_permissions']
165 )
166 Session().flush()
167
168 repo_group_data = repo_group.get_api_data()
169 audit_logger.store_web(
170 'repo_group.create', action_data={'data': repo_group_data},
171 user=self._rhodecode_user)
172
173 Session().commit()
174
175 _new_group_name = form_result['group_name_full']
176
177 repo_group_url = h.link_to(
178 _new_group_name,
179 h.route_path('repo_group_home', repo_group_name=_new_group_name))
180 h.flash(h.literal(_('Created repository group %s')
181 % repo_group_url), category='success')
182
183 except formencode.Invalid as errors:
184 data = render(
185 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
186 self._get_template_context(c), self.request)
187 html = formencode.htmlfill.render(
188 data,
189 defaults=errors.value,
190 errors=errors.error_dict or {},
191 prefix_error=False,
192 encoding="UTF-8",
193 force_defaults=False
194 )
195 return Response(html)
196 except Exception:
197 log.exception("Exception during creation of repository group")
198 h.flash(_('Error occurred during creation of repository group %s')
199 % repo_group_name, category='error')
200 raise HTTPFound(h.route_path('home'))
201
202 raise HTTPFound(
203 h.route_path('repo_group_home',
204 repo_group_name=form_result['group_name_full']))
@@ -0,0 +1,182 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 import formencode
23 import formencode.htmlfill
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
27 from pyramid.renderers import render
28 from pyramid.response import Response
29
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
31
32 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.auth import (
34 LoginRequired, CSRFRequired, NotAnonymous,
35 HasPermissionAny, HasRepoGroupPermissionAny)
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.utils import repo_name_slug
38 from rhodecode.lib.utils2 import safe_int, safe_unicode
39 from rhodecode.model.forms import RepoForm
40 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
42 from rhodecode.model.settings import SettingsModel
43 from rhodecode.model.db import Repository, RepoGroup
44
45 log = logging.getLogger(__name__)
46
47
48 class AdminReposView(BaseAppView, DataGridAppView):
49
50 def load_default_context(self):
51 c = self._get_local_tmpl_context()
52 self._register_global_c(c)
53 return c
54
55 def _load_form_data(self, c):
56 acl_groups = RepoGroupList(RepoGroup.query().all(),
57 perm_set=['group.write', 'group.admin'])
58 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
59 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
60 c.landing_revs_choices, c.landing_revs = \
61 ScmModel().get_repo_landing_revs()
62 c.personal_repo_group = self._rhodecode_user.personal_repo_group
63
64 @LoginRequired()
65 @NotAnonymous()
66 # perms check inside
67 @view_config(
68 route_name='repos', request_method='GET',
69 renderer='rhodecode:templates/admin/repos/repos.mako')
70 def repository_list(self):
71 c = self.load_default_context()
72
73 repo_list = Repository.get_all_repos()
74 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
75 repos_data = RepoModel().get_repos_as_dict(
76 repo_list=c.repo_list, admin=True, super_user_actions=True)
77 # json used to render the grid
78 c.data = json.dumps(repos_data)
79
80 return self._get_template_context(c)
81
82 @LoginRequired()
83 @NotAnonymous()
84 # perms check inside
85 @view_config(
86 route_name='repo_new', request_method='GET',
87 renderer='rhodecode:templates/admin/repos/repo_add.mako')
88 def repository_new(self):
89 c = self.load_default_context()
90
91 new_repo = self.request.GET.get('repo', '')
92 parent_group = safe_int(self.request.GET.get('parent_group'))
93 _gr = RepoGroup.get(parent_group)
94
95 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
96 # you're not super admin nor have global create permissions,
97 # but maybe you have at least write permission to a parent group ?
98
99 gr_name = _gr.group_name if _gr else None
100 # create repositories with write permission on group is set to true
101 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
102 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
103 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
104 if not (group_admin or (group_write and create_on_write)):
105 raise HTTPForbidden()
106
107 self._load_form_data(c)
108 c.new_repo = repo_name_slug(new_repo)
109
110 # apply the defaults from defaults page
111 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
112 # set checkbox to autochecked
113 defaults['repo_copy_permissions'] = True
114
115 parent_group_choice = '-1'
116 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
117 parent_group_choice = self._rhodecode_user.personal_repo_group
118
119 if parent_group and _gr:
120 if parent_group in [x[0] for x in c.repo_groups]:
121 parent_group_choice = safe_unicode(parent_group)
122
123 defaults.update({'repo_group': parent_group_choice})
124
125 data = render('rhodecode:templates/admin/repos/repo_add.mako',
126 self._get_template_context(c), self.request)
127 html = formencode.htmlfill.render(
128 data,
129 defaults=defaults,
130 encoding="UTF-8",
131 force_defaults=False
132 )
133 return Response(html)
134
135 @LoginRequired()
136 @NotAnonymous()
137 @CSRFRequired()
138 # perms check inside
139 @view_config(
140 route_name='repo_create', request_method='POST',
141 renderer='rhodecode:templates/admin/repos/repos.mako')
142 def repository_create(self):
143 c = self.load_default_context()
144
145 form_result = {}
146 task_id = None
147 self._load_form_data(c)
148
149 try:
150 # CanWriteToGroup validators checks permissions of this POST
151 form_result = RepoForm(repo_groups=c.repo_groups_choices,
152 landing_revs=c.landing_revs_choices)()\
153 .to_python(dict(self.request.POST))
154
155 # create is done sometimes async on celery, db transaction
156 # management is handled there.
157 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
158 from celery.result import BaseAsyncResult
159 if isinstance(task, BaseAsyncResult):
160 task_id = task.task_id
161 except formencode.Invalid as errors:
162 data = render('rhodecode:templates/admin/repos/repo_add.mako',
163 self._get_template_context(c), self.request)
164 html = formencode.htmlfill.render(
165 data,
166 defaults=errors.value,
167 errors=errors.error_dict or {},
168 prefix_error=False,
169 encoding="UTF-8",
170 force_defaults=False
171 )
172 return Response(html)
173
174 except Exception as e:
175 msg = self._log_creation_exception(e, form_result.get('repo_name'))
176 h.flash(msg, category='error')
177 raise HTTPFound(h.route_path('home'))
178
179 raise HTTPFound(
180 h.route_path('repo_creating',
181 repo_name=form_result['repo_name_full'],
182 _query=dict(task_id=task_id)))
@@ -0,0 +1,247 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 import formencode
24 import formencode.htmlfill
25
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
28 from pyramid.response import Response
29 from pyramid.renderers import render
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.auth import (
33 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
34 from rhodecode.lib import helpers as h, audit_logger
35 from rhodecode.lib.utils2 import safe_unicode
36
37 from rhodecode.model.forms import UserGroupForm
38 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.scm import UserGroupList
40 from rhodecode.model.db import (
41 or_, count, User, UserGroup, UserGroupMember)
42 from rhodecode.model.meta import Session
43 from rhodecode.model.user_group import UserGroupModel
44
45 log = logging.getLogger(__name__)
46
47
48 class AdminUserGroupsView(BaseAppView, DataGridAppView):
49
50 def load_default_context(self):
51 c = self._get_local_tmpl_context()
52
53 PermissionModel().set_global_permission_choices(
54 c, gettext_translator=self.request.translate)
55
56 self._register_global_c(c)
57 return c
58
59 # permission check in data loading of
60 # `user_groups_list_data` via UserGroupList
61 @LoginRequired()
62 @NotAnonymous()
63 @view_config(
64 route_name='user_groups', request_method='GET',
65 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
66 def user_groups_list(self):
67 c = self.load_default_context()
68 return self._get_template_context(c)
69
70 # permission check inside
71 @LoginRequired()
72 @NotAnonymous()
73 @view_config(
74 route_name='user_groups_data', request_method='GET',
75 renderer='json_ext', xhr=True)
76 def user_groups_list_data(self):
77 column_map = {
78 'active': 'users_group_active',
79 'description': 'user_group_description',
80 'members': 'members_total',
81 'owner': 'user_username',
82 'sync': 'group_data'
83 }
84 draw, start, limit = self._extract_chunk(self.request)
85 search_q, order_by, order_dir = self._extract_ordering(
86 self.request, column_map=column_map)
87
88 _render = self.request.get_partial_renderer(
89 'data_table/_dt_elements.mako')
90
91 def user_group_name(user_group_id, user_group_name):
92 return _render("user_group_name", user_group_id, user_group_name)
93
94 def user_group_actions(user_group_id, user_group_name):
95 return _render("user_group_actions", user_group_id, user_group_name)
96
97 def user_profile(username):
98 return _render('user_profile', username)
99
100 auth_user_group_list = UserGroupList(
101 UserGroup.query().all(), perm_set=['usergroup.admin'])
102
103 allowed_ids = [-1]
104 for user_group in auth_user_group_list:
105 allowed_ids.append(user_group.users_group_id)
106
107 user_groups_data_total_count = UserGroup.query()\
108 .filter(UserGroup.users_group_id.in_(allowed_ids))\
109 .count()
110
111 member_count = count(UserGroupMember.user_id)
112 base_q = Session.query(
113 UserGroup.users_group_name,
114 UserGroup.user_group_description,
115 UserGroup.users_group_active,
116 UserGroup.users_group_id,
117 UserGroup.group_data,
118 User,
119 member_count.label('member_count')
120 ) \
121 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
122 .outerjoin(UserGroupMember) \
123 .join(User, User.user_id == UserGroup.user_id) \
124 .group_by(UserGroup, User)
125
126 if search_q:
127 like_expression = u'%{}%'.format(safe_unicode(search_q))
128 base_q = base_q.filter(or_(
129 UserGroup.users_group_name.ilike(like_expression),
130 ))
131
132 user_groups_data_total_filtered_count = base_q.count()
133
134 if order_by == 'members_total':
135 sort_col = member_count
136 elif order_by == 'user_username':
137 sort_col = User.username
138 else:
139 sort_col = getattr(UserGroup, order_by, None)
140
141 if isinstance(sort_col, count) or sort_col:
142 if order_dir == 'asc':
143 sort_col = sort_col.asc()
144 else:
145 sort_col = sort_col.desc()
146
147 base_q = base_q.order_by(sort_col)
148 base_q = base_q.offset(start).limit(limit)
149
150 # authenticated access to user groups
151 auth_user_group_list = base_q.all()
152
153 user_groups_data = []
154 for user_gr in auth_user_group_list:
155 user_groups_data.append({
156 "users_group_name": user_group_name(
157 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
158 "name_raw": h.escape(user_gr.users_group_name),
159 "description": h.escape(user_gr.user_group_description),
160 "members": user_gr.member_count,
161 # NOTE(marcink): because of advanced query we
162 # need to load it like that
163 "sync": UserGroup._load_group_data(
164 user_gr.group_data).get('extern_type'),
165 "active": h.bool2icon(user_gr.users_group_active),
166 "owner": user_profile(user_gr.User.username),
167 "action": user_group_actions(
168 user_gr.users_group_id, user_gr.users_group_name)
169 })
170
171 data = ({
172 'draw': draw,
173 'data': user_groups_data,
174 'recordsTotal': user_groups_data_total_count,
175 'recordsFiltered': user_groups_data_total_filtered_count,
176 })
177
178 return data
179
180 @LoginRequired()
181 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
182 @view_config(
183 route_name='user_groups_new', request_method='GET',
184 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
185 def user_groups_new(self):
186 c = self.load_default_context()
187 return self._get_template_context(c)
188
189 @LoginRequired()
190 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
191 @CSRFRequired()
192 @view_config(
193 route_name='user_groups_create', request_method='POST',
194 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
195 def user_groups_create(self):
196 _ = self.request.translate
197 c = self.load_default_context()
198 users_group_form = UserGroupForm()()
199
200 user_group_name = self.request.POST.get('users_group_name')
201 try:
202 form_result = users_group_form.to_python(dict(self.request.POST))
203 user_group = UserGroupModel().create(
204 name=form_result['users_group_name'],
205 description=form_result['user_group_description'],
206 owner=self._rhodecode_user.user_id,
207 active=form_result['users_group_active'])
208 Session().flush()
209 creation_data = user_group.get_api_data()
210 user_group_name = form_result['users_group_name']
211
212 audit_logger.store_web(
213 'user_group.create', action_data={'data': creation_data},
214 user=self._rhodecode_user)
215
216 user_group_link = h.link_to(
217 h.escape(user_group_name),
218 h.route_path(
219 'edit_user_group', user_group_id=user_group.users_group_id))
220 h.flash(h.literal(_('Created user group %(user_group_link)s')
221 % {'user_group_link': user_group_link}),
222 category='success')
223 Session().commit()
224 user_group_id = user_group.users_group_id
225 except formencode.Invalid as errors:
226
227 data = render(
228 'rhodecode:templates/admin/user_groups/user_group_add.mako',
229 self._get_template_context(c), self.request)
230 html = formencode.htmlfill.render(
231 data,
232 defaults=errors.value,
233 errors=errors.error_dict or {},
234 prefix_error=False,
235 encoding="UTF-8",
236 force_defaults=False
237 )
238 return Response(html)
239
240 except Exception:
241 log.exception("Exception creating user group")
242 h.flash(_('Error occurred during creation of user group %s') \
243 % user_group_name, category='error')
244 raise HTTPFound(h.route_path('user_groups_new'))
245
246 raise HTTPFound(
247 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -0,0 +1,42 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 from rhodecode.apps._base import ADMIN_PREFIX
21 from rhodecode.lib.utils2 import str2bool
22
23
24 def debug_style_enabled(info, request):
25 return str2bool(request.registry.settings.get('debug_style'))
26
27
28 def includeme(config):
29 config.add_route(
30 name='debug_style_home',
31 pattern=ADMIN_PREFIX + '/debug_style',
32 custom_predicates=(debug_style_enabled,))
33 config.add_route(
34 name='debug_style_template',
35 pattern=ADMIN_PREFIX + '/debug_style/t/{t_path}',
36 custom_predicates=(debug_style_enabled,))
37
38 # Scan module for configuration decorators.
39 config.scan('.views', ignore='.tests')
40
41
42
@@ -0,0 +1,59 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 os
22 import logging
23
24 from pyramid.view import view_config
25 from pyramid.renderers import render_to_response
26 from rhodecode.apps._base import BaseAppView
27
28 log = logging.getLogger(__name__)
29
30
31 class DebugStyleView(BaseAppView):
32 def load_default_context(self):
33 c = self._get_local_tmpl_context()
34 self._register_global_c(c)
35 return c
36
37 @view_config(
38 route_name='debug_style_home', request_method='GET',
39 renderer=None)
40 def index(self):
41 c = self.load_default_context()
42 c.active = 'index'
43
44 return render_to_response(
45 'debug_style/index.html', self._get_template_context(c),
46 request=self.request)
47
48 @view_config(
49 route_name='debug_style_template', request_method='GET',
50 renderer=None)
51 def template(self):
52 t_path = self.request.matchdict['t_path']
53 c = self.load_default_context()
54 c.active = os.path.splitext(t_path)[0]
55 c.came_from = ''
56
57 return render_to_response(
58 'debug_style/' + t_path, self._get_template_context(c),
59 request=self.request) No newline at end of file
@@ -0,0 +1,62 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 from rhodecode.apps._base import ADMIN_PREFIX
21
22
23 def admin_routes(config):
24 config.add_route(
25 name='gists_show', pattern='/gists')
26 config.add_route(
27 name='gists_new', pattern='/gists/new')
28 config.add_route(
29 name='gists_create', pattern='/gists/create')
30
31 config.add_route(
32 name='gist_show', pattern='/gists/{gist_id}')
33
34 config.add_route(
35 name='gist_delete', pattern='/gists/{gist_id}/delete')
36
37 config.add_route(
38 name='gist_edit', pattern='/gists/{gist_id}/edit')
39
40 config.add_route(
41 name='gist_edit_check_revision',
42 pattern='/gists/{gist_id}/edit/check_revision')
43
44 config.add_route(
45 name='gist_update', pattern='/gists/{gist_id}/update')
46
47 config.add_route(
48 name='gist_show_rev',
49 pattern='/gists/{gist_id}/{revision}')
50 config.add_route(
51 name='gist_show_formatted',
52 pattern='/gists/{gist_id}/{revision}/{format}')
53
54 config.add_route(
55 name='gist_show_formatted_path',
56 pattern='/gists/{gist_id}/{revision}/{format}/{f_path:.*}')
57
58
59 def includeme(config):
60 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
61 # Scan module for configuration decorators.
62 config.scan('.views', ignore='.tests')
@@ -0,0 +1,20 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
@@ -0,0 +1,413 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2013-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
24 import formencode
25 import formencode.htmlfill
26 import peppercorn
27
28 from pyramid.httpexceptions import HTTPNotFound, HTTPFound
29 from pyramid.view import view_config
30 from pyramid.renderers import render
31 from pyramid.response import Response
32
33 from rhodecode.apps._base import BaseAppView
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
36 from rhodecode.lib.utils2 import time_to_datetime
37 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError
39 from rhodecode.model.gist import GistModel
40 from rhodecode.model.meta import Session
41 from rhodecode.model.db import Gist, User, or_
42 from rhodecode.model import validation_schema
43 from rhodecode.model.validation_schema.schemas import gist_schema
44
45
46 log = logging.getLogger(__name__)
47
48
49 class GistView(BaseAppView):
50
51 def load_default_context(self):
52 _ = self.request.translate
53 c = self._get_local_tmpl_context()
54 c.user = c.auth_user.get_instance()
55
56 c.lifetime_values = [
57 (-1, _('forever')),
58 (5, _('5 minutes')),
59 (60, _('1 hour')),
60 (60 * 24, _('1 day')),
61 (60 * 24 * 30, _('1 month')),
62 ]
63
64 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
65 c.acl_options = [
66 (Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")),
67 (Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users"))
68 ]
69
70 self._register_global_c(c)
71 return c
72
73 @LoginRequired()
74 @view_config(
75 route_name='gists_show', request_method='GET',
76 renderer='rhodecode:templates/admin/gists/index.mako')
77 def gist_show_all(self):
78 c = self.load_default_context()
79
80 not_default_user = self._rhodecode_user.username != User.DEFAULT_USER
81 c.show_private = self.request.GET.get('private') and not_default_user
82 c.show_public = self.request.GET.get('public') and not_default_user
83 c.show_all = self.request.GET.get('all') and self._rhodecode_user.admin
84
85 gists = _gists = Gist().query()\
86 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
87 .order_by(Gist.created_on.desc())
88
89 c.active = 'public'
90 # MY private
91 if c.show_private and not c.show_public:
92 gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
93 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
94 c.active = 'my_private'
95 # MY public
96 elif c.show_public and not c.show_private:
97 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
98 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
99 c.active = 'my_public'
100 # MY public+private
101 elif c.show_private and c.show_public:
102 gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
103 Gist.gist_type == Gist.GIST_PRIVATE))\
104 .filter(Gist.gist_owner == self._rhodecode_user.user_id)
105 c.active = 'my_all'
106 # Show all by super-admin
107 elif c.show_all:
108 c.active = 'all'
109 gists = _gists
110
111 # default show ALL public gists
112 if not c.show_public and not c.show_private and not c.show_all:
113 gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
114 c.active = 'public'
115
116 _render = self.request.get_partial_renderer(
117 'data_table/_dt_elements.mako')
118
119 data = []
120
121 for gist in gists:
122 data.append({
123 'created_on': _render('gist_created', gist.created_on),
124 'created_on_raw': gist.created_on,
125 'type': _render('gist_type', gist.gist_type),
126 'access_id': _render('gist_access_id', gist.gist_access_id, gist.owner.full_contact),
127 'author': _render('gist_author', gist.owner.full_contact, gist.created_on, gist.gist_expires),
128 'author_raw': h.escape(gist.owner.full_contact),
129 'expires': _render('gist_expires', gist.gist_expires),
130 'description': _render('gist_description', gist.gist_description)
131 })
132 c.data = json.dumps(data)
133
134 return self._get_template_context(c)
135
136 @LoginRequired()
137 @NotAnonymous()
138 @view_config(
139 route_name='gists_new', request_method='GET',
140 renderer='rhodecode:templates/admin/gists/new.mako')
141 def gist_new(self):
142 c = self.load_default_context()
143 return self._get_template_context(c)
144
145 @LoginRequired()
146 @NotAnonymous()
147 @CSRFRequired()
148 @view_config(
149 route_name='gists_create', request_method='POST',
150 renderer='rhodecode:templates/admin/gists/new.mako')
151 def gist_create(self):
152 _ = self.request.translate
153 c = self.load_default_context()
154
155 data = dict(self.request.POST)
156 data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME
157 data['nodes'] = [{
158 'filename': data['filename'],
159 'content': data.get('content'),
160 'mimetype': data.get('mimetype') # None is autodetect
161 }]
162
163 data['gist_type'] = (
164 Gist.GIST_PUBLIC if data.get('public') else Gist.GIST_PRIVATE)
165 data['gist_acl_level'] = (
166 data.get('gist_acl_level') or Gist.ACL_LEVEL_PRIVATE)
167
168 schema = gist_schema.GistSchema().bind(
169 lifetime_options=[x[0] for x in c.lifetime_values])
170
171 try:
172
173 schema_data = schema.deserialize(data)
174 # convert to safer format with just KEYs so we sure no duplicates
175 schema_data['nodes'] = gist_schema.sequence_to_nodes(
176 schema_data['nodes'])
177
178 gist = GistModel().create(
179 gist_id=schema_data['gistid'], # custom access id not real ID
180 description=schema_data['description'],
181 owner=self._rhodecode_user.user_id,
182 gist_mapping=schema_data['nodes'],
183 gist_type=schema_data['gist_type'],
184 lifetime=schema_data['lifetime'],
185 gist_acl_level=schema_data['gist_acl_level']
186 )
187 Session().commit()
188 new_gist_id = gist.gist_access_id
189 except validation_schema.Invalid as errors:
190 defaults = data
191 errors = errors.asdict()
192
193 if 'nodes.0.content' in errors:
194 errors['content'] = errors['nodes.0.content']
195 del errors['nodes.0.content']
196 if 'nodes.0.filename' in errors:
197 errors['filename'] = errors['nodes.0.filename']
198 del errors['nodes.0.filename']
199
200 data = render('rhodecode:templates/admin/gists/new.mako',
201 self._get_template_context(c), self.request)
202 html = formencode.htmlfill.render(
203 data,
204 defaults=defaults,
205 errors=errors,
206 prefix_error=False,
207 encoding="UTF-8",
208 force_defaults=False
209 )
210 return Response(html)
211
212 except Exception:
213 log.exception("Exception while trying to create a gist")
214 h.flash(_('Error occurred during gist creation'), category='error')
215 raise HTTPFound(h.route_url('gists_new'))
216 raise HTTPFound(h.route_url('gist_show', gist_id=new_gist_id))
217
218 @LoginRequired()
219 @NotAnonymous()
220 @CSRFRequired()
221 @view_config(
222 route_name='gist_delete', request_method='POST')
223 def gist_delete(self):
224 _ = self.request.translate
225 gist_id = self.request.matchdict['gist_id']
226
227 c = self.load_default_context()
228 c.gist = Gist.get_or_404(gist_id)
229
230 owner = c.gist.gist_owner == self._rhodecode_user.user_id
231 if not (h.HasPermissionAny('hg.admin')() or owner):
232 log.warning('Deletion of Gist was forbidden '
233 'by unauthorized user: `%s`', self._rhodecode_user)
234 raise HTTPNotFound()
235
236 GistModel().delete(c.gist)
237 Session().commit()
238 h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success')
239
240 raise HTTPFound(h.route_url('gists_show'))
241
242 def _get_gist(self, gist_id):
243
244 gist = Gist.get_or_404(gist_id)
245
246 # Check if this gist is expired
247 if gist.gist_expires != -1:
248 if time.time() > gist.gist_expires:
249 log.error(
250 'Gist expired at %s', time_to_datetime(gist.gist_expires))
251 raise HTTPNotFound()
252
253 # check if this gist requires a login
254 is_default_user = self._rhodecode_user.username == User.DEFAULT_USER
255 if gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user:
256 log.error("Anonymous user %s tried to access protected gist `%s`",
257 self._rhodecode_user, gist_id)
258 raise HTTPNotFound()
259 return gist
260
261 @LoginRequired()
262 @view_config(
263 route_name='gist_show', request_method='GET',
264 renderer='rhodecode:templates/admin/gists/show.mako')
265 @view_config(
266 route_name='gist_show_rev', request_method='GET',
267 renderer='rhodecode:templates/admin/gists/show.mako')
268 @view_config(
269 route_name='gist_show_formatted', request_method='GET',
270 renderer=None)
271 @view_config(
272 route_name='gist_show_formatted_path', request_method='GET',
273 renderer=None)
274 def gist_show(self):
275 gist_id = self.request.matchdict['gist_id']
276
277 # TODO(marcink): expose those via matching dict
278 revision = self.request.matchdict.get('revision', 'tip')
279 f_path = self.request.matchdict.get('f_path', None)
280 return_format = self.request.matchdict.get('format')
281
282 c = self.load_default_context()
283 c.gist = self._get_gist(gist_id)
284 c.render = not self.request.GET.get('no-render', False)
285
286 try:
287 c.file_last_commit, c.files = GistModel().get_gist_files(
288 gist_id, revision=revision)
289 except VCSError:
290 log.exception("Exception in gist show")
291 raise HTTPNotFound()
292
293 if return_format == 'raw':
294 content = '\n\n'.join([f.content for f in c.files
295 if (f_path is None or f.path == f_path)])
296 response = Response(content)
297 response.content_type = 'text/plain'
298 return response
299
300 return self._get_template_context(c)
301
302 @LoginRequired()
303 @NotAnonymous()
304 @view_config(
305 route_name='gist_edit', request_method='GET',
306 renderer='rhodecode:templates/admin/gists/edit.mako')
307 def gist_edit(self):
308 _ = self.request.translate
309 gist_id = self.request.matchdict['gist_id']
310 c = self.load_default_context()
311 c.gist = self._get_gist(gist_id)
312
313 owner = c.gist.gist_owner == self._rhodecode_user.user_id
314 if not (h.HasPermissionAny('hg.admin')() or owner):
315 raise HTTPNotFound()
316
317 try:
318 c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
319 except VCSError:
320 log.exception("Exception in gist edit")
321 raise HTTPNotFound()
322
323 if c.gist.gist_expires == -1:
324 expiry = _('never')
325 else:
326 # this cannot use timeago, since it's used in select2 as a value
327 expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
328
329 c.lifetime_values.append(
330 (0, _('%(expiry)s - current value') % {'expiry': _(expiry)})
331 )
332
333 return self._get_template_context(c)
334
335 @LoginRequired()
336 @NotAnonymous()
337 @CSRFRequired()
338 @view_config(
339 route_name='gist_update', request_method='POST',
340 renderer='rhodecode:templates/admin/gists/edit.mako')
341 def gist_update(self):
342 _ = self.request.translate
343 gist_id = self.request.matchdict['gist_id']
344 c = self.load_default_context()
345 c.gist = self._get_gist(gist_id)
346
347 owner = c.gist.gist_owner == self._rhodecode_user.user_id
348 if not (h.HasPermissionAny('hg.admin')() or owner):
349 raise HTTPNotFound()
350
351 data = peppercorn.parse(self.request.POST.items())
352
353 schema = gist_schema.GistSchema()
354 schema = schema.bind(
355 # '0' is special value to leave lifetime untouched
356 lifetime_options=[x[0] for x in c.lifetime_values] + [0],
357 )
358
359 try:
360 schema_data = schema.deserialize(data)
361 # convert to safer format with just KEYs so we sure no duplicates
362 schema_data['nodes'] = gist_schema.sequence_to_nodes(
363 schema_data['nodes'])
364
365 GistModel().update(
366 gist=c.gist,
367 description=schema_data['description'],
368 owner=c.gist.owner,
369 gist_mapping=schema_data['nodes'],
370 lifetime=schema_data['lifetime'],
371 gist_acl_level=schema_data['gist_acl_level']
372 )
373
374 Session().commit()
375 h.flash(_('Successfully updated gist content'), category='success')
376 except NodeNotChangedError:
377 # raised if nothing was changed in repo itself. We anyway then
378 # store only DB stuff for gist
379 Session().commit()
380 h.flash(_('Successfully updated gist data'), category='success')
381 except validation_schema.Invalid as errors:
382 errors = h.escape(errors.asdict())
383 h.flash(_('Error occurred during update of gist {}: {}').format(
384 gist_id, errors), category='error')
385 except Exception:
386 log.exception("Exception in gist edit")
387 h.flash(_('Error occurred during update of gist %s') % gist_id,
388 category='error')
389
390 raise HTTPFound(h.route_url('gist_show', gist_id=gist_id))
391
392 @LoginRequired()
393 @NotAnonymous()
394 @view_config(
395 route_name='gist_edit_check_revision', request_method='GET',
396 renderer='json_ext')
397 def gist_edit_check_revision(self):
398 _ = self.request.translate
399 gist_id = self.request.matchdict['gist_id']
400 c = self.load_default_context()
401 c.gist = self._get_gist(gist_id)
402
403 last_rev = c.gist.scm_instance().get_commit()
404 success = True
405 revision = self.request.GET.get('revision')
406
407 if revision != last_rev.raw_id:
408 log.error('Last revision %s is different then submitted %s'
409 % (revision, last_rev))
410 # our gist has newer version than we
411 success = False
412
413 return {'success': success}
@@ -0,0 +1,53 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 admin_routes(config):
26
27 config.add_route(
28 name='journal', pattern='/journal')
29 config.add_route(
30 name='journal_rss', pattern='/journal/rss')
31 config.add_route(
32 name='journal_atom', pattern='/journal/atom')
33
34 config.add_route(
35 name='journal_public', pattern='/public_journal')
36 config.add_route(
37 name='journal_public_atom', pattern='/public_journal/atom')
38 config.add_route(
39 name='journal_public_atom_old', pattern='/public_journal_atom')
40
41 config.add_route(
42 name='journal_public_rss', pattern='/public_journal/rss')
43 config.add_route(
44 name='journal_public_rss_old', pattern='/public_journal_rss')
45
46 config.add_route(
47 name='toggle_following', pattern='/toggle_following')
48
49
50 def includeme(config):
51 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
52 # Scan module for configuration decorators.
53 config.scan('.views', ignore='.tests')
@@ -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,384 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 logging
23 import itertools
24
25 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
26
27 from pyramid.view import view_config
28 from pyramid.httpexceptions import HTTPBadRequest
29 from pyramid.response import Response
30 from pyramid.renderers import render
31
32 from rhodecode.apps._base import BaseAppView
33 from rhodecode.model.db import (
34 or_, joinedload, UserLog, UserFollowing, User, UserApiKeys)
35 from rhodecode.model.meta import Session
36 import rhodecode.lib.helpers as h
37 from rhodecode.lib.helpers import Page
38 from rhodecode.lib.user_log_filter import user_log_filter
39 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
40 from rhodecode.lib.utils2 import safe_int, AttributeDict, md5_safe
41 from rhodecode.model.scm import ScmModel
42
43 log = logging.getLogger(__name__)
44
45
46 class JournalView(BaseAppView):
47
48 def load_default_context(self):
49 c = self._get_local_tmpl_context(include_app_defaults=True)
50 self._register_global_c(c)
51 self._load_defaults(c.rhodecode_name)
52
53 # TODO(marcink): what is this, why we need a global register ?
54 c.search_term = self.request.GET.get('filter') or ''
55 return c
56
57 def _get_config(self, rhodecode_name):
58 import rhodecode
59 config = rhodecode.CONFIG
60
61 return {
62 'language': 'en-us',
63 'feed_ttl': '5', # TTL of feed,
64 'feed_items_per_page':
65 safe_int(config.get('rss_items_per_page', 20)),
66 'rhodecode_name': rhodecode_name
67 }
68
69 def _load_defaults(self, rhodecode_name):
70 config = self._get_config(rhodecode_name)
71 # common values for feeds
72 self.language = config["language"]
73 self.ttl = config["feed_ttl"]
74 self.feed_items_per_page = config['feed_items_per_page']
75 self.rhodecode_name = config['rhodecode_name']
76
77 def _get_daily_aggregate(self, journal):
78 groups = []
79 for k, g in itertools.groupby(journal, lambda x: x.action_as_day):
80 user_group = []
81 # groupby username if it's a present value, else
82 # fallback to journal username
83 for _, g2 in itertools.groupby(
84 list(g), lambda x: x.user.username if x.user else x.username):
85 l = list(g2)
86 user_group.append((l[0].user, l))
87
88 groups.append((k, user_group,))
89
90 return groups
91
92 def _get_journal_data(self, following_repos, search_term):
93 repo_ids = [x.follows_repository.repo_id for x in following_repos
94 if x.follows_repository is not None]
95 user_ids = [x.follows_user.user_id for x in following_repos
96 if x.follows_user is not None]
97
98 filtering_criterion = None
99
100 if repo_ids and user_ids:
101 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
102 UserLog.user_id.in_(user_ids))
103 if repo_ids and not user_ids:
104 filtering_criterion = UserLog.repository_id.in_(repo_ids)
105 if not repo_ids and user_ids:
106 filtering_criterion = UserLog.user_id.in_(user_ids)
107 if filtering_criterion is not None:
108 journal = Session().query(UserLog)\
109 .options(joinedload(UserLog.user))\
110 .options(joinedload(UserLog.repository))
111 # filter
112 try:
113 journal = user_log_filter(journal, search_term)
114 except Exception:
115 # we want this to crash for now
116 raise
117 journal = journal.filter(filtering_criterion)\
118 .order_by(UserLog.action_date.desc())
119 else:
120 journal = []
121
122 return journal
123
124 def feed_uid(self, entry_id):
125 return '{}:{}'.format('journal', md5_safe(entry_id))
126
127 def _atom_feed(self, repos, search_term, public=True):
128 _ = self.request.translate
129 journal = self._get_journal_data(repos, search_term)
130 if public:
131 _link = h.route_url('journal_public_atom')
132 _desc = '%s %s %s' % (self.rhodecode_name, _('public journal'),
133 'atom feed')
134 else:
135 _link = h.route_url('journal_atom')
136 _desc = '%s %s %s' % (self.rhodecode_name, _('journal'), 'atom feed')
137
138 feed = Atom1Feed(
139 title=_desc, link=_link, description=_desc,
140 language=self.language, ttl=self.ttl)
141
142 for entry in journal[:self.feed_items_per_page]:
143 user = entry.user
144 if user is None:
145 # fix deleted users
146 user = AttributeDict({'short_contact': entry.username,
147 'email': '',
148 'full_contact': ''})
149 action, action_extra, ico = h.action_parser(entry, feed=True)
150 title = "%s - %s %s" % (user.short_contact, action(),
151 entry.repository.repo_name)
152 desc = action_extra()
153 _url = h.route_url('home')
154 if entry.repository is not None:
155 _url = h.route_url('repo_changelog',
156 repo_name=entry.repository.repo_name)
157
158 feed.add_item(
159 unique_id=self.feed_uid(entry.user_log_id),
160 title=title,
161 pubdate=entry.action_date,
162 link=_url,
163 author_email=user.email,
164 author_name=user.full_contact,
165 description=desc)
166
167 response = Response(feed.writeString('utf-8'))
168 response.content_type = feed.mime_type
169 return response
170
171 def _rss_feed(self, repos, search_term, public=True):
172 _ = self.request.translate
173 journal = self._get_journal_data(repos, search_term)
174 if public:
175 _link = h.route_url('journal_public_atom')
176 _desc = '%s %s %s' % (
177 self.rhodecode_name, _('public journal'), 'rss feed')
178 else:
179 _link = h.route_url('journal_atom')
180 _desc = '%s %s %s' % (
181 self.rhodecode_name, _('journal'), 'rss feed')
182
183 feed = Rss201rev2Feed(
184 title=_desc, link=_link, description=_desc,
185 language=self.language, ttl=self.ttl)
186
187 for entry in journal[:self.feed_items_per_page]:
188 user = entry.user
189 if user is None:
190 # fix deleted users
191 user = AttributeDict({'short_contact': entry.username,
192 'email': '',
193 'full_contact': ''})
194 action, action_extra, ico = h.action_parser(entry, feed=True)
195 title = "%s - %s %s" % (user.short_contact, action(),
196 entry.repository.repo_name)
197 desc = action_extra()
198 _url = h.route_url('home')
199 if entry.repository is not None:
200 _url = h.route_url('repo_changelog',
201 repo_name=entry.repository.repo_name)
202
203 feed.add_item(
204 unique_id=self.feed_uid(entry.user_log_id),
205 title=title,
206 pubdate=entry.action_date,
207 link=_url,
208 author_email=user.email,
209 author_name=user.full_contact,
210 description=desc)
211
212 response = Response(feed.writeString('utf-8'))
213 response.content_type = feed.mime_type
214 return response
215
216 @LoginRequired()
217 @NotAnonymous()
218 @view_config(
219 route_name='journal', request_method='GET',
220 renderer=None)
221 def journal(self):
222 c = self.load_default_context()
223
224 p = safe_int(self.request.GET.get('page', 1), 1)
225 c.user = User.get(self._rhodecode_user.user_id)
226 following = Session().query(UserFollowing)\
227 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
228 .options(joinedload(UserFollowing.follows_repository))\
229 .all()
230
231 journal = self._get_journal_data(following, c.search_term)
232
233 def url_generator(**kw):
234 query_params = {
235 'filter': c.search_term
236 }
237 query_params.update(kw)
238 return self.request.current_route_path(_query=query_params)
239
240 c.journal_pager = Page(
241 journal, page=p, items_per_page=20, url=url_generator)
242 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
243
244 c.journal_data = render(
245 'rhodecode:templates/journal/journal_data.mako',
246 self._get_template_context(c), self.request)
247
248 if self.request.is_xhr:
249 return Response(c.journal_data)
250
251 html = render(
252 'rhodecode:templates/journal/journal.mako',
253 self._get_template_context(c), self.request)
254 return Response(html)
255
256 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
257 @NotAnonymous()
258 @view_config(
259 route_name='journal_atom', request_method='GET',
260 renderer=None)
261 def journal_atom(self):
262 """
263 Produce an atom-1.0 feed via feedgenerator module
264 """
265 c = self.load_default_context()
266 following_repos = Session().query(UserFollowing)\
267 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
268 .options(joinedload(UserFollowing.follows_repository))\
269 .all()
270 return self._atom_feed(following_repos, c.search_term, public=False)
271
272 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
273 @NotAnonymous()
274 @view_config(
275 route_name='journal_rss', request_method='GET',
276 renderer=None)
277 def journal_rss(self):
278 """
279 Produce an rss feed via feedgenerator module
280 """
281 c = self.load_default_context()
282 following_repos = Session().query(UserFollowing)\
283 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
284 .options(joinedload(UserFollowing.follows_repository))\
285 .all()
286 return self._rss_feed(following_repos, c.search_term, public=False)
287
288 @LoginRequired()
289 @NotAnonymous()
290 @CSRFRequired()
291 @view_config(
292 route_name='toggle_following', request_method='POST',
293 renderer='json_ext')
294 def toggle_following(self):
295 user_id = self.request.POST.get('follows_user_id')
296 if user_id:
297 try:
298 ScmModel().toggle_following_user(
299 user_id, self._rhodecode_user.user_id)
300 Session().commit()
301 return 'ok'
302 except Exception:
303 raise HTTPBadRequest()
304
305 repo_id = self.request.POST.get('follows_repo_id')
306 if repo_id:
307 try:
308 ScmModel().toggle_following_repo(
309 repo_id, self._rhodecode_user.user_id)
310 Session().commit()
311 return 'ok'
312 except Exception:
313 raise HTTPBadRequest()
314
315 raise HTTPBadRequest()
316
317 @LoginRequired()
318 @view_config(
319 route_name='journal_public', request_method='GET',
320 renderer=None)
321 def journal_public(self):
322 c = self.load_default_context()
323 # Return a rendered template
324 p = safe_int(self.request.GET.get('page', 1), 1)
325
326 c.following = Session().query(UserFollowing)\
327 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
328 .options(joinedload(UserFollowing.follows_repository))\
329 .all()
330
331 journal = self._get_journal_data(c.following, c.search_term)
332
333 def url_generator(**kw):
334 query_params = {}
335 query_params.update(kw)
336 return self.request.current_route_path(_query=query_params)
337
338 c.journal_pager = Page(
339 journal, page=p, items_per_page=20, url=url_generator)
340 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
341
342 c.journal_data = render(
343 'rhodecode:templates/journal/journal_data.mako',
344 self._get_template_context(c), self.request)
345
346 if self.request.is_xhr:
347 return Response(c.journal_data)
348
349 html = render(
350 'rhodecode:templates/journal/public_journal.mako',
351 self._get_template_context(c), self.request)
352 return Response(html)
353
354 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
355 @view_config(
356 route_name='journal_public_atom', request_method='GET',
357 renderer=None)
358 def journal_public_atom(self):
359 """
360 Produce an atom-1.0 feed via feedgenerator module
361 """
362 c = self.load_default_context()
363 following_repos = Session().query(UserFollowing)\
364 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
365 .options(joinedload(UserFollowing.follows_repository))\
366 .all()
367
368 return self._atom_feed(following_repos, c.search_term)
369
370 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
371 @view_config(
372 route_name='journal_public_rss', request_method='GET',
373 renderer=None)
374 def journal_public_rss(self):
375 """
376 Produce an rss2 feed via feedgenerator module
377 """
378 c = self.load_default_context()
379 following_repos = Session().query(UserFollowing)\
380 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
381 .options(joinedload(UserFollowing.follows_repository))\
382 .all()
383
384 return self._rss_feed(following_repos, c.search_term)
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
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 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 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 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
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,5 +1,5 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.9.1
2 current_version = 4.10.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]
@@ -5,25 +5,20 b' done = false'
5 done = true
5 done = true
6
6
7 [task:rc_tools_pinned]
7 [task:rc_tools_pinned]
8 done = true
9
8
10 [task:fixes_on_stable]
9 [task:fixes_on_stable]
11 done = true
12
10
13 [task:pip2nix_generated]
11 [task:pip2nix_generated]
14 done = true
15
12
16 [task:changelog_updated]
13 [task:changelog_updated]
17 done = true
18
14
19 [task:generate_api_docs]
15 [task:generate_api_docs]
20 done = true
16
17 [task:updated_translation]
21
18
22 [release]
19 [release]
23 state = prepared
20 state = in_progress
24 version = 4.9.1
21 version = 4.10.0
25
26 [task:updated_translation]
27
22
28 [task:generate_js_routes]
23 [task:generate_js_routes]
29
24
@@ -1,5 +1,5 b''
1
1
2 .PHONY: clean docs docs-clean docs-cleanup test test-clean test-only web-build
2 .PHONY: clean docs docs-clean docs-cleanup test test-clean test-only test-only-postgres test-only-mysql web-build
3
3
4 WEBPACK=./node_modules/webpack/bin/webpack.js
4 WEBPACK=./node_modules/webpack/bin/webpack.js
5 GRUNT=grunt
5 GRUNT=grunt
@@ -19,7 +19,25 b' test-clean:'
19 find . -type d -name "__pycache__" -prune -exec rm -rf '{}' ';'
19 find . -type d -name "__pycache__" -prune -exec rm -rf '{}' ';'
20
20
21 test-only:
21 test-only:
22 PYTHONHASHSEED=random py.test -vv -r xw --cov=rhodecode --cov-report=term-missing --cov-report=html rhodecode
22 PYTHONHASHSEED=random \
23 py.test -x -vv -r xw -p no:sugar --cov=rhodecode \
24 --cov-report=term-missing --cov-report=html \
25 rhodecode
26
27 test-only-mysql:
28 PYTHONHASHSEED=random \
29 py.test -x -vv -r xw -p no:sugar --cov=rhodecode \
30 --cov-report=term-missing --cov-report=html \
31 --ini-config-override='{"app:main": {"sqlalchemy.db1.url": "mysql://root:qweqwe@localhost/rhodecode_test"}}' \
32 rhodecode
33
34 test-only-postgres:
35 PYTHONHASHSEED=random \
36 py.test -x -vv -r xw -p no:sugar --cov=rhodecode \
37 --cov-report=term-missing --cov-report=html \
38 --ini-config-override='{"app:main": {"sqlalchemy.db1.url": "postgresql://postgres:qweqwe@localhost/rhodecode_test"}}' \
39 rhodecode
40
23
41
24 docs:
42 docs:
25 (cd docs; nix-build default.nix -o result; make clean html)
43 (cd docs; nix-build default.nix -o result; make clean html)
@@ -73,7 +73,7 b' asyncore_use_poll = true'
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 recommended to be at 1
77 #threads = 1
77 #threads = 1
78 ## process name
78 ## process name
79 #proc_name = rhodecode
79 #proc_name = rhodecode
@@ -111,7 +111,6 b' use = egg:rhodecode-enterprise-ce'
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
115 rhodecode.lib.middleware.request_wrapper
114 rhodecode.lib.middleware.request_wrapper
116
115
117 pyramid.reload_templates = true
116 pyramid.reload_templates = true
@@ -163,12 +162,23 b' startup.import_repos = false'
163 ## the repository.
162 ## the repository.
164 #archive_cache_dir = /tmp/tarballcache
163 #archive_cache_dir = /tmp/tarballcache
165
164
165 ## URL at which the application is running. This is used for bootstraping
166 ## requests in context when no web request is available. Used in ishell, or
167 ## SSH calls. Set this for events to receive proper url for SSH calls.
168 app.base_url = http://rhodecode.local
169
166 ## change this to unique ID for security
170 ## change this to unique ID for security
167 app_instance_uuid = rc-production
171 app_instance_uuid = rc-production
168
172
169 ## cut off limit for large diffs (size in bytes)
173 ## cut off limit for large diffs (size in bytes). If overall diff size on
170 cut_off_limit_diff = 1024000
174 ## commit, or pull request exceeds this limit this diff will be displayed
171 cut_off_limit_file = 256000
175 ## partially. E.g 512000 == 512Kb
176 cut_off_limit_diff = 512000
177
178 ## cut off limit for large files inside diffs (size in bytes). Each individual
179 ## file inside diff which exceeds this limit will be displayed partially.
180 ## E.g 128000 == 128Kb
181 cut_off_limit_file = 128000
172
182
173 ## use cache version of scm repo everywhere
183 ## use cache version of scm repo everywhere
174 vcs_full_cache = true
184 vcs_full_cache = true
@@ -201,22 +211,26 b' rss_include_diff = false'
201 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
211 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
202 gist_alias_url =
212 gist_alias_url =
203
213
204 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
214 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
205 ## used for access.
215 ## used for access.
206 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
216 ## 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.
217 ## came from the the logged in user who own this authentication token.
218 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
219 ## authentication token. Such view would be only accessible when used together
220 ## with this authentication token
208 ##
221 ##
209 ## Syntax is ControllerClass:function_pattern.
222 ## list of all views can be found under `/_admin/permissions/auth_token_access`
210 ## To enable access to raw_files put `FilesController:raw`.
211 ## To enable access to patches add `ChangesetController:changeset_patch`.
212 ## The list should be "," separated and on a single line.
223 ## The list should be "," separated and on a single line.
213 ##
224 ##
214 ## Recommended controllers to enable:
225 ## Most common views to enable:
215 # ChangesetController:changeset_patch,
226 # RepoCommitsView:repo_commit_download
216 # ChangesetController:changeset_raw,
227 # RepoCommitsView:repo_commit_patch
217 # FilesController:raw,
228 # RepoCommitsView:repo_commit_raw
218 # FilesController:archivefile,
229 # RepoCommitsView:repo_commit_raw@TOKEN
219 # GistsController:*,
230 # RepoFilesView:repo_files_diff
231 # RepoFilesView:repo_archivefile
232 # RepoFilesView:repo_file_raw
233 # GistView:*
220 api_access_controllers_whitelist =
234 api_access_controllers_whitelist =
221
235
222 ## default encoding used to convert from and to unicode
236 ## default encoding used to convert from and to unicode
@@ -563,7 +577,7 b' vcs.backends = hg, git, svn'
563
577
564 vcs.connection_timeout = 3600
578 vcs.connection_timeout = 3600
565 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
579 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
566 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
580 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
567 #vcs.svn.compatible_version = pre-1.8-compatible
581 #vcs.svn.compatible_version = pre-1.8-compatible
568
582
569
583
@@ -577,6 +591,8 b' svn.proxy.generate_config = false'
577 svn.proxy.list_parent_path = true
591 svn.proxy.list_parent_path = true
578 ## Set location and file name of generated config file.
592 ## Set location and file name of generated config file.
579 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
593 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
594 ## alternative mod_dav config template. This needs to be a mako template
595 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
580 ## Used as a prefix to the `Location` block in the generated config file.
596 ## Used as a prefix to the `Location` block in the generated config file.
581 ## In most cases it should be set to `/`.
597 ## In most cases it should be set to `/`.
582 svn.proxy.location_root = /
598 svn.proxy.location_root = /
@@ -587,6 +603,43 b' svn.proxy.location_root = /'
587 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
603 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
588 #svn.proxy.reload_timeout = 10
604 #svn.proxy.reload_timeout = 10
589
605
606 ############################################################
607 ### SSH Support Settings ###
608 ############################################################
609
610 ## Defines if a custom authorized_keys file should be created and written on
611 ## any change user ssh keys. Setting this to false also disables posibility
612 ## of adding SSH keys by users from web interface. Super admins can still
613 ## manage SSH Keys.
614 ssh.generate_authorized_keyfile = false
615
616 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
617 # ssh.authorized_keys_ssh_opts =
618
619 ## Path to the authrozied_keys file where the generate entries are placed.
620 ## It is possible to have multiple key files specified in `sshd_config` e.g.
621 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
622 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
623
624 ## Command to execute the SSH wrapper. The binary is available in the
625 ## rhodecode installation directory.
626 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
627 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
628
629 ## Allow shell when executing the ssh-wrapper command
630 ssh.wrapper_cmd_allow_shell = false
631
632 ## Enables logging, and detailed output send back to the client during SSH
633 ## operations. Usefull for debugging, shouldn't be used in production.
634 ssh.enable_debug_logging = true
635
636 ## Paths to binary executable, by default they are the names, but we can
637 ## override them if we want to use a custom one
638 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
639 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
640 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
641
642
590 ## Dummy marker to add new entries after.
643 ## Dummy marker to add new entries after.
591 ## Add any custom entries below. Please don't remove.
644 ## Add any custom entries below. Please don't remove.
592 custom.conf = 1
645 custom.conf = 1
@@ -596,7 +649,7 b' custom.conf = 1'
596 ### LOGGING CONFIGURATION ####
649 ### LOGGING CONFIGURATION ####
597 ################################
650 ################################
598 [loggers]
651 [loggers]
599 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
652 keys = root, sqlalchemy, beaker, rhodecode, ssh_wrapper
600
653
601 [handlers]
654 [handlers]
602 keys = console, console_sql
655 keys = console, console_sql
@@ -611,12 +664,11 b' keys = generic, color_formatter, color_f'
611 level = NOTSET
664 level = NOTSET
612 handlers = console
665 handlers = console
613
666
614 [logger_routes]
667 [logger_sqlalchemy]
615 level = DEBUG
668 level = INFO
616 handlers =
669 handlers = console_sql
617 qualname = routes.middleware
670 qualname = sqlalchemy.engine
618 ## "level = DEBUG" logs the route matched and routing variables.
671 propagate = 0
619 propagate = 1
620
672
621 [logger_beaker]
673 [logger_beaker]
622 level = DEBUG
674 level = DEBUG
@@ -624,23 +676,18 b' handlers ='
624 qualname = beaker.container
676 qualname = beaker.container
625 propagate = 1
677 propagate = 1
626
678
627 [logger_templates]
628 level = INFO
629 handlers =
630 qualname = pylons.templating
631 propagate = 1
632
633 [logger_rhodecode]
679 [logger_rhodecode]
634 level = DEBUG
680 level = DEBUG
635 handlers =
681 handlers =
636 qualname = rhodecode
682 qualname = rhodecode
637 propagate = 1
683 propagate = 1
638
684
639 [logger_sqlalchemy]
685 [logger_ssh_wrapper]
640 level = INFO
686 level = DEBUG
641 handlers = console_sql
687 handlers =
642 qualname = sqlalchemy.engine
688 qualname = ssh_wrapper
643 propagate = 0
689 propagate = 1
690
644
691
645 ##############
692 ##############
646 ## HANDLERS ##
693 ## HANDLERS ##
@@ -15,8 +15,11 b' available post the .ini config.'
15
15
16 import multiprocessing
16 import multiprocessing
17 import sys
17 import sys
18 import time
19 import datetime
18 import threading
20 import threading
19 import traceback
21 import traceback
22 from gunicorn.glogging import Logger
20
23
21
24
22 # GLOBAL
25 # GLOBAL
@@ -87,4 +90,24 b' def pre_request(worker, req):'
87 def post_request(worker, req, environ, resp):
90 def post_request(worker, req, environ, resp):
88 return
91 return
89 worker.log.debug("[<%-10s>] POST WORKER: %s %s resp: %s", worker.pid,
92 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
93 req.method, req.path, resp.status_code)
94
95
96 class RhodeCodeLogger(Logger):
97 """
98 Custom Logger that allows some customization that gunicorn doesn't allow
99 """
100
101 datefmt = r"%Y-%m-%d %H:%M:%S"
102
103 def __init__(self, cfg):
104 Logger.__init__(self, cfg)
105
106 def now(self):
107 """ return date in RhodeCode Log format """
108 now = time.time()
109 msecs = int((now - long(now)) * 1000)
110 return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs)
111
112
113 logger_class = RhodeCodeLogger
@@ -73,7 +73,7 b' use = egg:gunicorn#main'
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 recommended to be at 1
77 #threads = 1
77 #threads = 1
78 ## process name
78 ## process name
79 proc_name = rhodecode
79 proc_name = rhodecode
@@ -137,12 +137,23 b' startup.import_repos = false'
137 ## the repository.
137 ## the repository.
138 #archive_cache_dir = /tmp/tarballcache
138 #archive_cache_dir = /tmp/tarballcache
139
139
140 ## URL at which the application is running. This is used for bootstraping
141 ## requests in context when no web request is available. Used in ishell, or
142 ## SSH calls. Set this for events to receive proper url for SSH calls.
143 app.base_url = http://rhodecode.local
144
140 ## change this to unique ID for security
145 ## change this to unique ID for security
141 app_instance_uuid = rc-production
146 app_instance_uuid = rc-production
142
147
143 ## cut off limit for large diffs (size in bytes)
148 ## cut off limit for large diffs (size in bytes). If overall diff size on
144 cut_off_limit_diff = 1024000
149 ## commit, or pull request exceeds this limit this diff will be displayed
145 cut_off_limit_file = 256000
150 ## partially. E.g 512000 == 512Kb
151 cut_off_limit_diff = 512000
152
153 ## cut off limit for large files inside diffs (size in bytes). Each individual
154 ## file inside diff which exceeds this limit will be displayed partially.
155 ## E.g 128000 == 128Kb
156 cut_off_limit_file = 128000
146
157
147 ## use cache version of scm repo everywhere
158 ## use cache version of scm repo everywhere
148 vcs_full_cache = true
159 vcs_full_cache = true
@@ -175,22 +186,26 b' rss_include_diff = false'
175 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
186 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
176 gist_alias_url =
187 gist_alias_url =
177
188
178 ## List of controllers (using glob pattern syntax) that AUTH TOKENS could be
189 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
179 ## used for access.
190 ## used for access.
180 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
191 ## 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.
192 ## came from the the logged in user who own this authentication token.
193 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
194 ## authentication token. Such view would be only accessible when used together
195 ## with this authentication token
182 ##
196 ##
183 ## Syntax is ControllerClass:function_pattern.
197 ## list of all views can be found under `/_admin/permissions/auth_token_access`
184 ## To enable access to raw_files put `FilesController:raw`.
185 ## To enable access to patches add `ChangesetController:changeset_patch`.
186 ## The list should be "," separated and on a single line.
198 ## The list should be "," separated and on a single line.
187 ##
199 ##
188 ## Recommended controllers to enable:
200 ## Most common views to enable:
189 # ChangesetController:changeset_patch,
201 # RepoCommitsView:repo_commit_download
190 # ChangesetController:changeset_raw,
202 # RepoCommitsView:repo_commit_patch
191 # FilesController:raw,
203 # RepoCommitsView:repo_commit_raw
192 # FilesController:archivefile,
204 # RepoCommitsView:repo_commit_raw@TOKEN
193 # GistsController:*,
205 # RepoFilesView:repo_files_diff
206 # RepoFilesView:repo_archivefile
207 # RepoFilesView:repo_file_raw
208 # GistView:*
194 api_access_controllers_whitelist =
209 api_access_controllers_whitelist =
195
210
196 ## default encoding used to convert from and to unicode
211 ## default encoding used to convert from and to unicode
@@ -532,7 +547,7 b' vcs.backends = hg, git, svn'
532
547
533 vcs.connection_timeout = 3600
548 vcs.connection_timeout = 3600
534 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
549 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
535 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible
550 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
536 #vcs.svn.compatible_version = pre-1.8-compatible
551 #vcs.svn.compatible_version = pre-1.8-compatible
537
552
538
553
@@ -546,6 +561,8 b' svn.proxy.generate_config = false'
546 svn.proxy.list_parent_path = true
561 svn.proxy.list_parent_path = true
547 ## Set location and file name of generated config file.
562 ## Set location and file name of generated config file.
548 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
563 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
564 ## alternative mod_dav config template. This needs to be a mako template
565 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
549 ## Used as a prefix to the `Location` block in the generated config file.
566 ## Used as a prefix to the `Location` block in the generated config file.
550 ## In most cases it should be set to `/`.
567 ## In most cases it should be set to `/`.
551 svn.proxy.location_root = /
568 svn.proxy.location_root = /
@@ -556,6 +573,43 b' svn.proxy.location_root = /'
556 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
573 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
557 #svn.proxy.reload_timeout = 10
574 #svn.proxy.reload_timeout = 10
558
575
576 ############################################################
577 ### SSH Support Settings ###
578 ############################################################
579
580 ## Defines if a custom authorized_keys file should be created and written on
581 ## any change user ssh keys. Setting this to false also disables posibility
582 ## of adding SSH keys by users from web interface. Super admins can still
583 ## manage SSH Keys.
584 ssh.generate_authorized_keyfile = false
585
586 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
587 # ssh.authorized_keys_ssh_opts =
588
589 ## Path to the authrozied_keys file where the generate entries are placed.
590 ## It is possible to have multiple key files specified in `sshd_config` e.g.
591 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
592 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
593
594 ## Command to execute the SSH wrapper. The binary is available in the
595 ## rhodecode installation directory.
596 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
597 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
598
599 ## Allow shell when executing the ssh-wrapper command
600 ssh.wrapper_cmd_allow_shell = false
601
602 ## Enables logging, and detailed output send back to the client during SSH
603 ## operations. Usefull for debugging, shouldn't be used in production.
604 ssh.enable_debug_logging = false
605
606 ## Paths to binary executable, by default they are the names, but we can
607 ## override them if we want to use a custom one
608 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
609 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
610 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
611
612
559 ## Dummy marker to add new entries after.
613 ## Dummy marker to add new entries after.
560 ## Add any custom entries below. Please don't remove.
614 ## Add any custom entries below. Please don't remove.
561 custom.conf = 1
615 custom.conf = 1
@@ -565,7 +619,7 b' custom.conf = 1'
565 ### LOGGING CONFIGURATION ####
619 ### LOGGING CONFIGURATION ####
566 ################################
620 ################################
567 [loggers]
621 [loggers]
568 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
622 keys = root, sqlalchemy, beaker, rhodecode, ssh_wrapper
569
623
570 [handlers]
624 [handlers]
571 keys = console, console_sql
625 keys = console, console_sql
@@ -580,12 +634,11 b' keys = generic, color_formatter, color_f'
580 level = NOTSET
634 level = NOTSET
581 handlers = console
635 handlers = console
582
636
583 [logger_routes]
637 [logger_sqlalchemy]
584 level = DEBUG
638 level = INFO
585 handlers =
639 handlers = console_sql
586 qualname = routes.middleware
640 qualname = sqlalchemy.engine
587 ## "level = DEBUG" logs the route matched and routing variables.
641 propagate = 0
588 propagate = 1
589
642
590 [logger_beaker]
643 [logger_beaker]
591 level = DEBUG
644 level = DEBUG
@@ -593,23 +646,18 b' handlers ='
593 qualname = beaker.container
646 qualname = beaker.container
594 propagate = 1
647 propagate = 1
595
648
596 [logger_templates]
597 level = INFO
598 handlers =
599 qualname = pylons.templating
600 propagate = 1
601
602 [logger_rhodecode]
649 [logger_rhodecode]
603 level = DEBUG
650 level = DEBUG
604 handlers =
651 handlers =
605 qualname = rhodecode
652 qualname = rhodecode
606 propagate = 1
653 propagate = 1
607
654
608 [logger_sqlalchemy]
655 [logger_ssh_wrapper]
609 level = INFO
656 level = DEBUG
610 handlers = console_sql
657 handlers =
611 qualname = sqlalchemy.engine
658 qualname = ssh_wrapper
612 propagate = 0
659 propagate = 1
660
613
661
614 ##############
662 ##############
615 ## HANDLERS ##
663 ## HANDLERS ##
@@ -40,8 +40,17 b' Below config if for an Apache Reverse Pr'
40 ServerName rhodecode.myserver.com
40 ServerName rhodecode.myserver.com
41 ServerAlias rhodecode.myserver.com
41 ServerAlias rhodecode.myserver.com
42
42
43 ## Skip ProxyPass the _static to backend server
44 #ProxyPass /_static !
45
43 ## serve static files by Apache, recommended for performance
46 ## serve static files by Apache, recommended for performance
44 #Alias /_static /home/ubuntu/.rccontrol/community-1/static
47 #Alias /_static/rhodecode /home/ubuntu/.rccontrol/community-1/static
48
49 ## Allow Apache to access the static files in this directory
50 #<Directory /home/ubuntu/.rccontrol/community-1/static/>
51 # AllowOverride none
52 # Require all granted
53 #</Directory>
45
54
46 RequestHeader set X-Forwarded-Proto "https"
55 RequestHeader set X-Forwarded-Proto "https"
47
56
@@ -86,5 +95,9 b' Below config if for an Apache Reverse Pr'
86 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
95 # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
87 #SSLOpenSSLConfCmd DHParameters "/etc/apache2/dhparam.pem"
96 #SSLOpenSSLConfCmd DHParameters "/etc/apache2/dhparam.pem"
88
97
98 ## custom 502 error page. Will be displayed while RhodeCode server
99 ## is turned off
100 ErrorDocument 502 /path/to/.rccontrol/enterprise-1/static/502.html
101
89 </VirtualHost>
102 </VirtualHost>
90
103
@@ -89,6 +89,13 b' Use the following example to configure N'
89
89
90 ## serve static files by Nginx, recommended for performance
90 ## serve static files by Nginx, recommended for performance
91 # location /_static/rhodecode {
91 # location /_static/rhodecode {
92 # gzip on;
93 # gzip_min_length 500;
94 # gzip_proxied any;
95 # gzip_comp_level 4;
96 # gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
97 # gzip_vary on;
98 # gzip_disable "msie6";
92 # alias /path/to/.rccontrol/enterprise-1/static;
99 # alias /path/to/.rccontrol/enterprise-1/static;
93 # }
100 # }
94
101
@@ -127,7 +134,8 b' Use the following example to configure N'
127 proxy_pass http://rc;
134 proxy_pass http://rc;
128 }
135 }
129
136
130 ## custom 502 error page
137 ## custom 502 error page. Will be displayed while RhodeCode server
138 ## is turned off
131 error_page 502 /502.html;
139 error_page 502 /502.html;
132 location = /502.html {
140 location = /502.html {
133 root /path/to/.rccontrol/enterprise-1/static;
141 root /path/to/.rccontrol/enterprise-1/static;
@@ -19,6 +19,7 b' The following are the most common system'
19 config-files-overview
19 config-files-overview
20 vcs-server
20 vcs-server
21 svn-http
21 svn-http
22 gunicorn-ssl-support
22 apache-config
23 apache-config
23 nginx-config
24 nginx-config
24 backup-restore
25 backup-restore
@@ -3,16 +3,19 b''
3 Increase Gunicorn Workers
3 Increase Gunicorn Workers
4 -------------------------
4 -------------------------
5
5
6 .. important::
6
7 |RCE| comes with `Gunicorn`_ packaged in its Nix environment.
8 Gunicorn is a Python WSGI HTTP Server for UNIX.
7
9
8 If you increase the number of :term:`Gunicorn` workers, you also need to
10 To improve |RCE| performance you can increase the number of `Gunicorn`_ workers.
9 increase the threadpool size of the VCS Server. The recommended size is
11 This allows to handle more connections concurently, and provide better
10 6 times the number of Gunicorn workers. To set this, see
12 responsiveness and performance.
11 :ref:`vcs-server-config-file`.
12
13
13 |RCE| comes with `Gunicorn`_ packaged in its Nix environment. To improve
14 By default during installation |RCC| tries to detect how many CPUs are
14 performance you can increase the number of workers. To do this, use the
15 available in the system, and set the number workers based on that information.
15 following steps:
16 However sometimes it's better to manually set the number of workers.
17
18 To do this, use the following steps:
16
19
17 1. Open the :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
20 1. Open the :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
18 2. In the ``[server:main]`` section, increase the number of Gunicorn
21 2. In the ``[server:main]`` section, increase the number of Gunicorn
@@ -20,16 +23,26 b' 2. In the ``[server:main]`` section, inc'
20
23
21 .. code-block:: ini
24 .. code-block:: ini
22
25
23 [server:main]
24 host = 127.0.0.1
25 port = 10002
26 use = egg:gunicorn#main
26 use = egg:gunicorn#main
27 workers = 1
27 ## Sets the number of process workers. You must set `instance_id = *`
28 threads = 1
28 ## when this option is set to more than one worker, recommended
29 proc_name = RhodeCodeEnterprise
29 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
30 ## The `instance_id = *` must be set in the [app:main] section below
31 workers = 4
32 ## process name
33 proc_name = rhodecode
34 ## type of worker class, one of sync, gevent
35 ## recommended for bigger setup is using of of other than sync one
30 worker_class = sync
36 worker_class = sync
37 ## The maximum number of simultaneous clients. Valid only for Gevent
38 #worker_connections = 10
39 ## max number of requests that worker will handle before being gracefully
40 ## restarted, could prevent memory leaks
31 max_requests = 1000
41 max_requests = 1000
32 timeout = 3600
42 max_requests_jitter = 30
43 ## amount of time a worker can spend with handling a request before it
44 ## gets killed and restarted. Set to 6hrs
45 timeout = 21600
33
46
34 3. In the ``[app:main]`` section, set the ``instance_id`` property to ``*``.
47 3. In the ``[app:main]`` section, set the ``instance_id`` property to ``*``.
35
48
@@ -40,72 +53,72 b' 3. In the ``[app:main]`` section, set th'
40 # You must set `instance_id = *`
53 # You must set `instance_id = *`
41 instance_id = *
54 instance_id = *
42
55
43 4. Save your changes.
56 4. Change the VCSServer workers too. Open the
44 5. Restart your |RCE| instance, using the following command:
57 :file:`home/{user}/.rccontrol/{instance-id}/vcsserver.ini` file.
58
59 5. In the ``[server:main]`` section, increase the number of Gunicorn
60 ``workers`` using the following formula :math:`(2 * Cores) + 1`.
61
62 .. code-block:: ini
63
64 ## run with gunicorn --log-config vcsserver.ini --paste vcsserver.ini
65 use = egg:gunicorn#main
66 ## Sets the number of process workers. Recommended
67 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
68 workers = 4
69 ## process name
70 proc_name = rhodecode_vcsserver
71 ## type of worker class, currently `sync` is the only option allowed.
72 worker_class = sync
73 ## The maximum number of simultaneous clients. Valid only for Gevent
74 #worker_connections = 10
75 ## max number of requests that worker will handle before being gracefully
76 ## restarted, could prevent memory leaks
77 max_requests = 1000
78 max_requests_jitter = 30
79 ## amount of time a worker can spend with handling a request before it
80 ## gets killed and restarted. Set to 6hrs
81 timeout = 21600
82
83 6. Save your changes.
84 7. Restart your |RCE| instances, using the following command:
45
85
46 .. code-block:: bash
86 .. code-block:: bash
47
87
48 $ rccontrol restart enterprise-1
88 $ rccontrol restart '*'
89
90
91 Gunicorn Gevent Backend
92 -----------------------
49
93
50 If you scale across different machines, each |RCM| instance
94 Gevent is an asynchronous worker type for Gunicorn. It allows accepting multiple
51 needs to store its data on a shared disk, preferably together with your
95 connections on a single `Gunicorn`_ worker. This means you can handle 100s
52 |repos|. This data directory contains template caches, a whoosh index,
96 of concurrent clones, or API calls using just few workers. A setting called
53 and is used for task locking to ensure safety across multiple instances.
97 `worker_connections` defines on how many connections each worker can
54 To do this, set the following properties in the :file:`rhodecode.ini` file to
98 handle using `Gevent`.
55 set the shared location across all |RCM| instances.
99
100
101 To enable `Gevent` on |RCE| do the following:
102
103
104 1. Open the :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
105 2. In the ``[server:main]`` section, change `worker_class` for Gunicorn.
106
56
107
57 .. code-block:: ini
108 .. code-block:: ini
58
109
59 cache_dir = /file/path # set to shared location
110 ## type of worker class, one of sync, gevent
60 search.location = /file/path # set to shared location
111 ## recommended for bigger setup is using of of other than sync one
112 worker_class = gevent
113 ## The maximum number of simultaneous clients. Valid only for Gevent
114 worker_connections = 30
61
115
62 ####################################
116
63 ### BEAKER CACHE ####
117 .. note::
64 ####################################
118
65 beaker.cache.data_dir = /file/path # set to shared location
119 `Gevent` is currently only supported for Enterprise/Community instances.
66 beaker.cache.lock_dir = /file/path # set to shared location
120 VCSServer doesn't yet support gevent.
67
121
68
122
69
123
70 Gunicorn SSL support
71 --------------------
72
73
74 :term:`Gunicorn` wsgi server allows users to use HTTPS connection directly
75 without a need to use HTTP server like Nginx or Apache. To Configure
76 SSL support directly with :term:`Gunicorn` you need to simply add the key
77 and certificate paths to your configuration file.
78
79 1. Open the :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
80 2. In the ``[server:main]`` section, add two new variables
81 called `certfile` and `keyfile`.
82
83 .. code-block:: ini
84
85 [server:main]
86 host = 127.0.0.1
87 port = 10002
88 use = egg:gunicorn#main
89 workers = 1
90 threads = 1
91 proc_name = RhodeCodeEnterprise
92 worker_class = sync
93 max_requests = 1000
94 timeout = 3600
95 # adding ssl support
96 certfile = /home/ssl/my_server_com.pem
97 keyfile = /home/ssl/my_server_com.key
98
99 4. Save your changes.
100 5. Restart your |RCE| instance, using the following command:
101
102 .. code-block:: bash
103
104 $ rccontrol restart enterprise-1
105
106 After this is enabled you can *only* access your instances via https://
107 protocol. Check out more docs here `Gunicorn SSL Docs`_
108
109
110 .. _Gunicorn: http://gunicorn.org/
124 .. _Gunicorn: http://gunicorn.org/
111 .. _Gunicorn SSL Docs: http://docs.gunicorn.org/en/stable/settings.html#ssl
@@ -25,6 +25,15 b' depending on your available resources.'
25
25
26 .. _move-tmp:
26 .. _move-tmp:
27
27
28
29 In order to make this change permanent it's recommend to set it as /etc/fstab
30 entry.
31
32 .. code-block:: bash
33
34 tmpfs /home/user/.rccontrol/enterprise-1/data tmpfs nodev,nosuid,noexec,nodiratime,size=2G 0 0
35
36
28 Move ``tmp`` to TMPFS
37 Move ``tmp`` to TMPFS
29 ---------------------
38 ---------------------
30
39
@@ -3,21 +3,45 b''
3 Scale Horizontally
3 Scale Horizontally
4 ------------------
4 ------------------
5
5
6 |RCE| is built in a way it support horizontal scaling across multiple machines.
7 There are two main pre-requisites for that:
8
9 - Shared storage that each machine can access.
10 - Shared DB connection across machines.
11
12
6 Horizontal scaling means adding more machines or workers into your pool of
13 Horizontal scaling means adding more machines or workers into your pool of
7 resources. Horizontally scaling |RCE| gives a huge performance increase,
14 resources. Horizontally scaling |RCE| gives a huge performance increase,
8 especially under large traffic scenarios with a high number of requests. This
15 especially under large traffic scenarios with a high number of requests. This
9 is very beneficial when |RCE| is serving many users simultaneously,
16 is very beneficial when |RCE| is serving many users simultaneously,
10 or if continuous integration servers are automatically pulling and pushing code.
17 or if continuous integration servers are automatically pulling and pushing code.
11
18
12 To horizontally scale |RCE| you should use the following steps:
19
20 If you scale across different machines, each |RCM| instance
21 needs to store its data on a shared disk, preferably together with your
22 |repos|. This data directory contains template caches, a full text search index,
23 and is used for task locking to ensure safety across multiple instances.
24 To do this, set the following properties in the :file:`rhodecode.ini` file to
25 set the shared location across all |RCM| instances.
26
27 .. code-block:: ini
28
29 cache_dir = /shared/path/caches # set to shared location
30 search.location = /shared/path/search_index # set to shared location
13
31
14 1. In the :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file,
32 ####################################
15 set ``instance_id = *``. This enables |RCE| to use multiple nodes.
33 ### BEAKER CACHE ####
16 2. Define the number of worker threads using the formula
34 ####################################
17 :math:`(2 * Cores) + 1`. For example 4 CPU cores would lead to
35 beaker.cache.data_dir = /shared/path/data # set to shared location
18 :math:`(2 * 4) + 1 = 9` workers. In some cases it's ok to increase number of
36 beaker.cache.lock_dir = /shared/path/lock # set to shared location
19 workers even beyond this formula. Generally the more workers, the more
37
20 simultaneous connections the system can handle.
38
39 .. note::
40
41 If you use custom caches such as `beaker.cache.auth_plugins.` it's recommended
42 to set it to the memcached/redis or database backend so it can be shared
43 across machines.
44
21
45
22 It is recommended to create another dedicated |RCE| instance to handle
46 It is recommended to create another dedicated |RCE| instance to handle
23 traffic from build farms or continuous integration servers.
47 traffic from build farms or continuous integration servers.
@@ -28,24 +52,7 b' traffic from build farms or continuous i'
28 load balancing rules that will separate regular user traffic from
52 load balancing rules that will separate regular user traffic from
29 automated process traffic like continuous servers or build bots.
53 automated process traffic like continuous servers or build bots.
30
54
31 If you scale across different machines, each |RCE| instance needs to store
32 its data on a shared disk, preferably together with your repositories. This
33 data directory contains template caches, a whoosh index,
34 and is used for task locking to ensure safety across multiple instances. To
35 do this, set the following properties in the
36 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file to set
37 the shared location across all |RCE| instances.
38
39 .. code-block:: ini
40
41 cache_dir = /file/path # set to shared directory location
42 search.location = /file/path # set to shared directory location
43 beaker.cache.data_dir = /file/path # set to shared directory location
44 beaker.cache.lock_dir = /file/path # set to shared directory location
45
46 .. note::
55 .. note::
47
56
48 If Celery is used on each instance then you should run separate Celery
57 If Celery is used on each instance then you should run separate Celery
49 instances, but the message broker should be the same for all of them.
58 instances, but the message broker should be the same for all of them.
50 This excludes one RabbitMQ shared server.
51
@@ -42,7 +42,7 b' archive.'
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 = RepoCommitsView:repo_commit_raw,RepoCommitsView:repo_commit_patch,RepoCommitsView:repo_commit_download
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
@@ -533,6 +533,9 b' get_repo_settings'
533 "hooks_outgoing_pull_logger": true,
533 "hooks_outgoing_pull_logger": true,
534 "phases_publish": "True",
534 "phases_publish": "True",
535 "rhodecode_hg_use_rebase_for_merging": true,
535 "rhodecode_hg_use_rebase_for_merging": true,
536 "rhodecode_hg_close_branch_before_merging": false,
537 "rhodecode_git_use_rebase_for_merging": true,
538 "rhodecode_git_close_branch_before_merging": false,
536 "rhodecode_pr_merge_enabled": true,
539 "rhodecode_pr_merge_enabled": true,
537 "rhodecode_use_outdated_comments": true
540 "rhodecode_use_outdated_comments": true
538 }
541 }
@@ -137,6 +137,30 b' get_method'
137 error : null
137 error : null
138
138
139
139
140 get_repo_store
141 --------------
142
143 .. py:function:: get_repo_store(apiuser)
144
145 Returns the |RCE| repository storage information.
146
147 :param apiuser: This is filled automatically from the |authtoken|.
148 :type apiuser: AuthUser
149
150 Example output:
151
152 .. code-block:: bash
153
154 id : <id_given_in_input>
155 result : {
156 'modules': [<module name>,...]
157 'py_version': <python version>,
158 'platform': <platform type>,
159 'rhodecode_version': <rhodecode version>
160 }
161 error : null
162
163
140 get_server_info
164 get_server_info
141 ---------------
165 ---------------
142
166
@@ -3,11 +3,13 b''
3 LDAP
3 LDAP
4 ----
4 ----
5
5
6 |RCM| supports LDAP (Lightweight Directory Access Protocol) authentication.
6 |RCM| supports LDAP (Lightweight Directory Access Protocol) or
7 AD (active Directory) authentication.
7 All LDAP versions are supported, with the following |RCM| plugins managing each:
8 All LDAP versions are supported, with the following |RCM| plugins managing each:
8
9
9 * For LDAPv3 use ``rhodecode.lib.auth_modules.auth_ldap_group``
10 * For LDAPv3 use ``LDAP (egg:rhodecode-enterprise-ce#ldap)``
10 * For older LDAP versions use ``rhodecode.lib.auth_modules.auth_ldap``
11 * For LDAPv3 with user group sync use ``LDAP + User Groups (egg:rhodecode-enterprise-ee#ldap_group)``
12
11
13
12 .. important::
14 .. important::
13
15
@@ -3,127 +3,136 b''
3 SSH Connection
3 SSH Connection
4 --------------
4 --------------
5
5
6 If you wish to connect to your Git or Mercurial |repos| using SSH, use the
6 If you wish to connect to your |repos| using SSH protocol, use the
7 following instructions.
7 following instructions.
8
8
9 .. note::
9 1. Include |RCE| generated `authorized_keys` file into your sshd_config.
10
10
11 SSH access with full |RCE| permissions will require an Admin |authtoken|.
11 By default a file `authorized_keys_rhodecode` is created containing
12 configuration and all allowed user connection keys are stored inside.
13 On each change of stored keys inside |RCE| this file is updated with
14 proper data.
12
15
13 You need to install the |RC| SSH tool on the server which is running
16 .. code-block:: bash
14 the |RCE| instance.
15
17
16 1. Gather the following information about the instance you wish to connect to:
18 # Edit sshd_config file most likely at /etc/ssh/sshd_config
19 # add or edit the AuthorizedKeysFile, and set to use custom files
20
21 AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
17
22
18 * *Hostname*: Use the ``rccontrol status`` command to view instance details.
23 This way we use a separate file for SSH access and separate one for
19 * *API key*: From the |RCE|, go to
24 SSH access to |RCE| repositories.
20 :menuselection:`username --> My Account --> Auth Tokens`
25
21 * *Configuration file*: Identify the configuration file for that instance,
26
22 the default is :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
27 2. Enable the SSH module on instance.
23 * Identify which |git| and |hg| packages your |RCM| instance is using.
24
28
25 * For |git|, see
29 On the server where |RCE| is running executing:
26 :menuselection:`Admin --> Settings --> System Info`
30
27 * For |hg|, use the ``which hg`` command.
31 .. code-block:: bash
28
32
29 2. Clone the |RC| SSH script,
33 rccontrol enable-module ssh {instance-id}
30 ``hg clone https://code.rhodecode.com/rhodecode-ssh``
31 3. Copy the ``sshwrapper.sample.ini``, and save it as ``sshwrapper.ini``
32 4. Configure the :file:`sshwrapper.ini` file using the following example:
33
34
34 .. code-block:: ini
35 This will add the following configuration into :file:`rhodecode.ini`.
36 This also can be done manually:
35
37
36 [api]
38 .. code-block:: ini
37 host=http://localhost:10005
38 key=24a67076d69c84670132f55166ac79d1faafd660
39
39
40 [shell]
40 ############################################################
41 shell=/bin/bash -l
41 ### SSH Support Settings ###
42 ############################################################
42
43
43 [vcs]
44 ## Defines if a custom authorized_keys file should be created and written on
44 root=/path/to/repos/
45 ## any change user ssh keys. Setting this to false also disables posibility
46 ## of adding SSH keys by users from web interface. Super admins can still
47 ## manage SSH Keys.
48 ssh.generate_authorized_keyfile = true
45
49
46 [rhodecode]
50 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
47 config=/home/user/.rccontrol/enterprise-3/rhodecode.ini
51 # ssh.authorized_keys_ssh_opts =
48
49 [vcs:hg]
50 path=/usr/bin/hg
51
52
52 # should be a base dir for all git binaries, i.e. not ../bin/git
53 ## Path to the authrozied_keys file where the generate entries are placed.
53 [vcs:git]
54 ## It is possible to have multiple key files specified in `sshd_config` e.g.
54 path=/usr/bin
55 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
55
56 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
56 [keys]
57 path=/home/user/.ssh/authorized_keys
58
57
59 5. Add the public key to your |RCE| instance server using the
58 ## Command to execute the SSH wrapper. The binary is available in the
60 :file:`addkey.py` script. This script automatically creates
59 ## rhodecode installation directory.
61 the :file:`authorized_keys` file which was specified in your
60 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
62 :file:`sshwrapper.ini` configuration. Use the following example:
61 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
63
62
64 .. code-block:: bash
63 ## Allow shell when executing the ssh-wrapper command
64 ssh.wrapper_cmd_allow_shell = false
65
65
66 $ ./addkey.py --user username --shell --key /home/username/.ssh/id_rsa.pub
66 ## Enables logging, and detailed output send back to the client during SSH
67
67 ## operations. Usefull for debugging, shouldn't be used in production.
68 .. important::
68 ssh.enable_debug_logging = false
69
69
70 To give SSH access to all users, you will need to maintain
70 ## Paths to binary executable, by default they are the names, but we can
71 each users |authtoken| in the :file:`authorized_keys` file.
71 ## override them if we want to use a custom one
72 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
73 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
74 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
72
75
73 6. Connect to your server using SSH from your local machine.
74
75 .. code-block:: bash
76
76
77 $ ssh user@localhost
77 3. Set base_url for instance to enable proper event handling (Optional):
78 Enter passphrase for key '/home/username/.ssh/id_rsa':
79
78
80 If you need to manually configure the ``authorized_keys`` file,
79 If you wish to have integrations working correctly via SSH please configure
81 add a line for each key using the following example:
80 The Application base_url.
82
81
83 .. code-block:: vim
82 Use the ``rccontrol status`` command to view instance details.
83 Hostname is required for the integration to properly set the instance URL.
84
84
85 command="/home/user/.rhodecode-ssh/sshwrapper.py --user username --shell",
85 When your hostname is known (e.g https://code.rhodecode.com) please set it
86 no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa yourpublickey
86 inside :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
87
87
88 .. tip::
88 add into `[app:main]` section the following configuration:
89
90 .. code-block:: ini
91
92 app.base_url = https://code.rhodecode.com
89
93
90 Best practice would be to create a special SSH user account with each
94
91 users |authtoken| attached.
95 4. Add the public key to your user account for testing.
96 First generate a new key, or use your existing one and have your public key
97 at hand.
92
98
93 |RCE| will manage the user permissions based on the |authtoken| supplied.
99 Go to
94 This would allow you to immediately revoke all SSH access by removing one
100 :menuselection:`My Account --> SSH Keys` and add the public key with proper description.
95 user from your server if you needed to.
96
101
97 See the following command line example of setting this up. These steps
102 This will generate a new entry inside our configured `authorized_keys_rhodecode` file.
98 take place on the server.
99
103
100 .. code-block:: bash
104 Test the connection from your local machine using the following example:
105
106 .. note::
101
107
102 # On the RhodeCode Enterprise server
108 In case of connection problems please set
103 # set up user and clone SSH tool
109 `ssh.enable_debug_logging = true` inside the SSH configuration of
104 $ sudo adduser testuser
110 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
105 $ sudo su - testuser
111 Then add, remove your SSH key and try connecting again.
106 $ hg clone https://code.rhodecode.com/rhodecode-ssh
112 Debug logging will be printed to help find the problems on the server side.
107 $ cd rhodecode-ssh
108
113
109 # Copy and modify the sshwrapper.ini as explained in step 4
114 Test connection using the ssh command from the local machine
110 $ cp sshwrapper.sample.ini sshwrapper.ini
115
116
117 For SVN:
118
119 .. code-block:: bash
111
120
112 $ cd ~
121 SVN_SSH="ssh -i ~/.ssh/id_rsa_test_ssh" svn checkout svn+ssh://rhodecode@rc-server/repo_name
113 $ mkdir .ssh
122
114 $ touch .ssh/authorized_keys
123 For GIT:
115
124
116 # copy your ssh public key, id_rsa.pub, from your local machine
125 .. code-block:: bash
117 # to the server. We’ll use it in the next step
118
126
119 $ python addkey.py --user testuser --shell --key /path/to/id_rsa.pub
127 GIT_SSH_COMMAND='ssh -i ~/.ssh/id_rsa_test_ssh' git clone ssh://rhodecode@rc-server/repo_name
120
128
121 # Note: testssh - user on the rhodecode instance
129 For Mercurial:
122 $ chmod 755 sshwrapper.py
123
130
124 Test the connection from your local machine using the following example:
131 .. code-block:: bash
125
132
126 .. code-block:: bash
133 Add to hgrc:
127
134
128 # Test connection using the ssh command from the local machine
135 [ui]
129 $ ssh testuser@my-server.example.com
136 ssh = ssh -C -i ~/.ssh/id_rsa_test_ssh
137
138 hg clone ssh://rhodecode@rc-server/repo_name
@@ -15,15 +15,13 b' We keep the calls in the form ``{verb}_{'
15 Change and Deprecation
15 Change and Deprecation
16 ======================
16 ======================
17
17
18 API deprecation is documented in the section :ref:`deprecated` together with
18 API deprecation is documented in the section `deprecated` together with
19 other notes about deprecated parts of the application.
19 other notes about deprecated parts of the application.
20
20
21
21
22 Deprecated API calls
22 Deprecated API calls
23 --------------------
23 --------------------
24
24
25 - Make sure to add them into the section :ref:`deprecated`.
26
27 - Use `deprecated` inside of the call docstring to make our users aware of the
25 - Use `deprecated` inside of the call docstring to make our users aware of the
28 deprecation::
26 deprecation::
29
27
@@ -1,66 +1,69 b''
1
1
2 .. _config-celery:
2 .. _config-celery:
3
3
4 Install Celery
4 Configure Celery
5 --------------
5 ----------------
6
6
7 To improve |RCM| performance you should install Celery_ as it makes
7 To improve |RCM| performance you should configure and enabled Celery_ as it makes
8 asynchronous tasks work efficiently. If you
8 asynchronous tasks work efficiently. Most important it allows sending notification
9 install Celery you also need multi-broker support. The recommended message
9 emails, create repository forks, and import repositories in async way.
10 broker is rabbitmq_. |RCM| works in sync
10
11 mode, but running Celery_ will give you a large speed improvement when
11 If you decide to use Celery you also need a working message queue.
12 managing many big repositories.
12 The recommended message broker is rabbitmq_.
13
14
15 In order to have install and configure Celery, follow these steps:
13
16
14 If you want to run |RCM| with Celery you need to run ``celeryd`` using the
17 1. Install RabbitMQ, see the documentation on the Celery website for
15 ``paster`` command and the message broker.
18 `rabbitmq installation`_.
16 The ``paster`` command is already installed during |RCM| installation.
17
19
18 To install and configure Celery, use the following steps:
20 2. Configure Celery in the
21 :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
22 Set the following minimal settings, that are set during rabbitmq_ installation::
19
23
20 1. Install Celery and RabbitMQ, see the documentation on the Celery website for
24 broker.host =
21 `Celery installation`_ and `rabbitmq installation`_.
25 broker.vhost =
22 2. Enable Celery in the
26 broker.user =
23 :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
27 broker.password =
24 3. Run the Celery daemon with the ``paster`` command,
25 using the following example
26 ``.rccontrol/enterprise-1/profile/bin/paster celeryd .rccontrol/enterprise-1/rhodecode.ini``
27
28
28 .. code-block:: ini
29 Full configuration example is below:
30
31 .. code-block:: ini
29
32
30 # Set this section of the ini file to match your Celery installation
33 # Set this section of the ini file to match your Celery installation
31 ####################################
34 ####################################
32 ### CELERY CONFIG ####
35 ### CELERY CONFIG ####
33 ####################################
36 ####################################
34 ## Set to true
37 ## Set to true
35 use_celery = false
38 use_celery = true
36 broker.host = localhost
39 broker.host = localhost
37 broker.vhost = rabbitmqhost
40 broker.vhost = rabbitmqvhost
38 broker.port = 5672
41 broker.port = 5672
39 broker.user = rabbitmq
42 broker.user = rabbitmq
40 broker.password = qweqwe
43 broker.password = secret
41
44
42 celery.imports = rhodecode.lib.celerylib.tasks
45 celery.imports = rhodecode.lib.celerylib.tasks
43
46
44 celery.result.backend = amqp
47 celery.result.backend = amqp
45 celery.result.dburi = amqp://
48 celery.result.dburi = amqp://
46 celery.result.serialier = json
49 celery.result.serialier = json
47
50
48 #celery.send.task.error.emails = true
51 #celery.send.task.error.emails = true
49 #celery.amqp.task.result.expires = 18000
52 #celery.amqp.task.result.expires = 18000
50
53
51 celeryd.concurrency = 2
54 celeryd.concurrency = 2
52 #celeryd.log.file = celeryd.log
55 #celeryd.log.file = celeryd.log
53 celeryd.log.level = debug
56 celeryd.log.level = debug
54 celeryd.max.tasks.per.child = 1
57 celeryd.max.tasks.per.child = 1
55
58
56 ## tasks will never be sent to the queue, but executed locally instead.
59 ## tasks will never be sent to the queue, but executed locally instead.
57 celery.always.eager = false
60 celery.always.eager = false
61
58
62
59 .. code-block:: bash
63 3. Enable celery, and install `celeryd` process script using the `enable-module`::
60
64
61 # Once the above is configured and saved
65 rccontrol enable-module celery {instance-id}
62 # Run celery with the paster command and specify the ini file
66
63 .rccontrol/enterprise-1/profile/bin/paster celeryd .rccontrol/enterprise-1/rhodecode.ini
64
67
65 .. _python: http://www.python.org/
68 .. _python: http://www.python.org/
66 .. _mercurial: http://mercurial.selenic.com/
69 .. _mercurial: http://mercurial.selenic.com/
@@ -68,4 +71,3 b' 3. Run the Celery daemon with the ``past'
68 .. _rabbitmq: http://www.rabbitmq.com/
71 .. _rabbitmq: http://www.rabbitmq.com/
69 .. _rabbitmq installation: http://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html
72 .. _rabbitmq installation: http://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html
70 .. _Celery installation: http://docs.celeryproject.org/en/latest/getting-started/introduction.html#bundles
73 .. _Celery installation: http://docs.celeryproject.org/en/latest/getting-started/introduction.html#bundles
71 .. _virtualenv: http://docs.python-guide.org/en/latest/dev/virtualenvs/
@@ -10,5 +10,5 b' the information in these sections to con'
10
10
11 setup-email
11 setup-email
12 database-string
12 database-string
13 install-celery
13 configure-celery
14 migrate-repos
14 migrate-repos
@@ -9,6 +9,7 b' Release Notes'
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.10.0.rst
12 release-notes-4.9.1.rst
13 release-notes-4.9.1.rst
13 release-notes-4.9.0.rst
14 release-notes-4.9.0.rst
14 release-notes-4.8.0.rst
15 release-notes-4.8.0.rst
@@ -22,7 +22,7 b' section.'
22 gunicorn rhodecode-extensions svn svnversion
22 gunicorn rhodecode-extensions svn svnversion
23 hg rhodecode-gist svnadmin vcsserver
23 hg rhodecode-gist svnadmin vcsserver
24 paster rhodecode-index svndumpfilter
24 paster rhodecode-index svndumpfilter
25 rcserver rhodecode-list-instances svnlook
25 rc-server rhodecode-list-instances svnlook
26 rhodecode-api rhodecode-setup-config svnmucc
26 rhodecode-api rhodecode-setup-config svnmucc
27
27
28 You can then use the tools as described in the :ref:`rc-tools` section using the
28 You can then use the tools as described in the :ref:`rc-tools` section using the
@@ -6,6 +6,7 b''
6 },
6 },
7 "js": {
7 "js": {
8 "src": "rhodecode/public/js/src",
8 "src": "rhodecode/public/js/src",
9 "src_rc": "rhodecode/public/js/rhodecode",
9 "dest": "rhodecode/public/js",
10 "dest": "rhodecode/public/js",
10 "bower": "bower_components",
11 "bower": "bower_components",
11 "node_modules": "node_modules"
12 "node_modules": "node_modules"
@@ -31,13 +32,14 b''
31 },
32 },
32 "dist": {
33 "dist": {
33 "src": [
34 "src": [
34 "<%= dirs.js.src %>/jquery-1.11.1.min.js",
35 "<%= dirs.js.node_modules %>/jquery/dist/jquery.min.js",
36 "<%= dirs.js.node_modules %>/mousetrap/mousetrap.min.js",
37 "<%= dirs.js.node_modules %>/moment/min/moment.min.js",
38 "<%= dirs.js.node_modules %>/clipboard/dist/clipboard.min.js",
39 "<%= dirs.js.node_modules %>/favico.js/favico-0.3.10.min.js",
40 "<%= dirs.js.node_modules %>/appenlight-client/appenlight-client.min.js",
35 "<%= dirs.js.src %>/logging.js",
41 "<%= dirs.js.src %>/logging.js",
36 "<%= dirs.js.src %>/bootstrap.js",
42 "<%= dirs.js.src %>/bootstrap.js",
37 "<%= dirs.js.src %>/mousetrap.js",
38 "<%= dirs.js.src %>/moment.js",
39 "<%= dirs.js.node_modules %>/appenlight-client/appenlight-client.min.js",
40 "<%= dirs.js.node_modules %>/favico.js/favico-0.3.10.min.js",
41 "<%= dirs.js.src %>/i18n_utils.js",
43 "<%= dirs.js.src %>/i18n_utils.js",
42 "<%= dirs.js.src %>/deform.js",
44 "<%= dirs.js.src %>/deform.js",
43 "<%= dirs.js.src %>/plugins/jquery.pjax.js",
45 "<%= dirs.js.src %>/plugins/jquery.pjax.js",
@@ -55,9 +57,10 b''
55 "<%= dirs.js.src %>/codemirror/codemirror_hint.js",
57 "<%= dirs.js.src %>/codemirror/codemirror_hint.js",
56 "<%= dirs.js.src %>/codemirror/codemirror_overlay.js",
58 "<%= dirs.js.src %>/codemirror/codemirror_overlay.js",
57 "<%= dirs.js.src %>/codemirror/codemirror_placeholder.js",
59 "<%= dirs.js.src %>/codemirror/codemirror_placeholder.js",
60 "<%= dirs.js.src %>/codemirror/codemirror_simplemode.js",
58 "<%= dirs.js.dest %>/mode/meta.js",
61 "<%= dirs.js.dest %>/mode/meta.js",
59 "<%= dirs.js.dest %>/mode/meta_ext.js",
62 "<%= dirs.js.dest %>/mode/meta_ext.js",
60 "<%= dirs.js.dest %>/rhodecode/i18n/select2/translations.js",
63 "<%= dirs.js.src_rc %>/i18n/select2/translations.js",
61 "<%= dirs.js.src %>/rhodecode/utils/array.js",
64 "<%= dirs.js.src %>/rhodecode/utils/array.js",
62 "<%= dirs.js.src %>/rhodecode/utils/string.js",
65 "<%= dirs.js.src %>/rhodecode/utils/string.js",
63 "<%= dirs.js.src %>/rhodecode/utils/pyroutes.js",
66 "<%= dirs.js.src %>/rhodecode/utils/pyroutes.js",
@@ -12,9 +12,14 b''
12 "vulcanize": "^1.14.8",
12 "vulcanize": "^1.14.8",
13 "grunt-crisper": "^1.0.1",
13 "grunt-crisper": "^1.0.1",
14 "grunt-vulcanize": "^1.0.0",
14 "grunt-vulcanize": "^1.0.0",
15 "node2nix": "^1.0.0",
15 "jshint": "^2.9.1-rc3",
16 "jshint": "^2.9.1-rc3",
16 "bower": "^1.7.9",
17 "bower": "^1.7.9",
18 "jquery": "1.11.3",
17 "favico.js": "^0.3.10",
19 "favico.js": "^0.3.10",
18 "appenlight-client": "git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.0"
20 "clipboard": "^1.7.1",
21 "moment": "^2.18.1",
22 "mousetrap": "^1.6.1",
23 "appenlight-client": "git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.1"
19 }
24 }
20 }
25 }
@@ -7,7 +7,7 b' buildEnv { name = "bower-env"; ignoreCol'
7 (fetchbower "paper-tooltip" "PolymerElements/paper-tooltip#1.1.3" "PolymerElements/paper-tooltip#^1.1.2" "0vmrm1n8k9sk9nvqy03q177axy22pia6i3j1gxbk72j3pqiqvg6k")
7 (fetchbower "paper-tooltip" "PolymerElements/paper-tooltip#1.1.3" "PolymerElements/paper-tooltip#^1.1.2" "0vmrm1n8k9sk9nvqy03q177axy22pia6i3j1gxbk72j3pqiqvg6k")
8 (fetchbower "paper-toast" "PolymerElements/paper-toast#1.3.0" "PolymerElements/paper-toast#^1.3.0" "0x9rqxsks5455s8pk4aikpp99ijdn6kxr9gvhwh99nbcqdzcxq1m")
8 (fetchbower "paper-toast" "PolymerElements/paper-toast#1.3.0" "PolymerElements/paper-toast#^1.3.0" "0x9rqxsks5455s8pk4aikpp99ijdn6kxr9gvhwh99nbcqdzcxq1m")
9 (fetchbower "paper-toggle-button" "PolymerElements/paper-toggle-button#1.2.0" "PolymerElements/paper-toggle-button#^1.2.0" "0mphcng3ngspbpg4jjn0mb91nvr4xc1phq3qswib15h6sfww1b2w")
9 (fetchbower "paper-toggle-button" "PolymerElements/paper-toggle-button#1.2.0" "PolymerElements/paper-toggle-button#^1.2.0" "0mphcng3ngspbpg4jjn0mb91nvr4xc1phq3qswib15h6sfww1b2w")
10 (fetchbower "iron-ajax" "PolymerElements/iron-ajax#1.4.3" "PolymerElements/iron-ajax#^1.4.3" "1b1z3112ggjdflgrwbpmnbsh3kgcm4hn255wshvrlzds4w069gja")
10 (fetchbower "iron-ajax" "PolymerElements/iron-ajax#1.4.3" "PolymerElements/iron-ajax#^1.4.3" "0m3dx27arwmlcp00b7n516sc5a51f40p9vapr1nvd57l3i3z0pzm")
11 (fetchbower "iron-autogrow-textarea" "PolymerElements/iron-autogrow-textarea#1.0.13" "PolymerElements/iron-autogrow-textarea#^1.0.13" "0zwhpl97vii1s8k0lgain8i9dnw29b0mxc5ixdscx9las13n2lqq")
11 (fetchbower "iron-autogrow-textarea" "PolymerElements/iron-autogrow-textarea#1.0.13" "PolymerElements/iron-autogrow-textarea#^1.0.13" "0zwhpl97vii1s8k0lgain8i9dnw29b0mxc5ixdscx9las13n2lqq")
12 (fetchbower "iron-a11y-keys" "PolymerElements/iron-a11y-keys#1.0.6" "PolymerElements/iron-a11y-keys#^1.0.6" "1xz3mgghfcxixq28sdb654iaxj4nyi1bzcwf77ydkms6fviqs9mv")
12 (fetchbower "iron-a11y-keys" "PolymerElements/iron-a11y-keys#1.0.6" "PolymerElements/iron-a11y-keys#^1.0.6" "1xz3mgghfcxixq28sdb654iaxj4nyi1bzcwf77ydkms6fviqs9mv")
13 (fetchbower "iron-flex-layout" "PolymerElements/iron-flex-layout#1.3.1" "PolymerElements/iron-flex-layout#^1.0.0" "0nswv3ih3bhflgcd2wjfmddqswzgqxb2xbq65jk9w3rkj26hplbl")
13 (fetchbower "iron-flex-layout" "PolymerElements/iron-flex-layout#1.3.1" "PolymerElements/iron-flex-layout#^1.0.0" "0nswv3ih3bhflgcd2wjfmddqswzgqxb2xbq65jk9w3rkj26hplbl")
This diff has been collapsed as it changes many lines, (2397 lines changed) Show them Hide them
@@ -40,13 +40,13 b' let'
40 sha1 = "f6b2f06fc715264837a7ab6c69a1ce1a689c2c29";
40 sha1 = "f6b2f06fc715264837a7ab6c69a1ce1a689c2c29";
41 };
41 };
42 };
42 };
43 "grunt-contrib-less-1.4.0" = {
43 "grunt-contrib-less-1.4.1" = {
44 name = "grunt-contrib-less";
44 name = "grunt-contrib-less";
45 packageName = "grunt-contrib-less";
45 packageName = "grunt-contrib-less";
46 version = "1.4.0";
46 version = "1.4.1";
47 src = fetchurl {
47 src = fetchurl {
48 url = "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-1.4.0.tgz";
48 url = "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-1.4.1.tgz";
49 sha1 = "17ee79cad21c9720ee07b3a991fab5103b513514";
49 sha1 = "3bbdec0b75d12ceaa55d62943625c0b0861cdf6f";
50 };
50 };
51 };
51 };
52 "grunt-contrib-watch-0.6.1" = {
52 "grunt-contrib-watch-0.6.1" = {
@@ -58,22 +58,22 b' let'
58 sha1 = "64fdcba25a635f5b4da1b6ce6f90da0aeb6e3f15";
58 sha1 = "64fdcba25a635f5b4da1b6ce6f90da0aeb6e3f15";
59 };
59 };
60 };
60 };
61 "crisper-2.0.2" = {
61 "crisper-2.1.1" = {
62 name = "crisper";
62 name = "crisper";
63 packageName = "crisper";
63 packageName = "crisper";
64 version = "2.0.2";
64 version = "2.1.1";
65 src = fetchurl {
65 src = fetchurl {
66 url = "https://registry.npmjs.org/crisper/-/crisper-2.0.2.tgz";
66 url = "https://registry.npmjs.org/crisper/-/crisper-2.1.1.tgz";
67 sha1 = "188a7da3d00dcf0c64eff7f253d23dacffba7197";
67 sha1 = "4cc7321c3e90f3c5cbdc3503217f118fd7d5c51c";
68 };
68 };
69 };
69 };
70 "vulcanize-1.14.8" = {
70 "vulcanize-1.16.0" = {
71 name = "vulcanize";
71 name = "vulcanize";
72 packageName = "vulcanize";
72 packageName = "vulcanize";
73 version = "1.14.8";
73 version = "1.16.0";
74 src = fetchurl {
74 src = fetchurl {
75 url = "https://registry.npmjs.org/vulcanize/-/vulcanize-1.14.8.tgz";
75 url = "https://registry.npmjs.org/vulcanize/-/vulcanize-1.16.0.tgz";
76 sha1 = "3cdd6f81d9baf2c5796ddd6d2d289e45975086f7";
76 sha1 = "b0ce3b0044d194ad4908ae4f1a6c6110a6e4d5e6";
77 };
77 };
78 };
78 };
79 "grunt-crisper-1.0.1" = {
79 "grunt-crisper-1.0.1" = {
@@ -94,22 +94,40 b' let'
94 sha1 = "f4d6cfef274f8216c06f6c290e7dbb3b9e9e3b0f";
94 sha1 = "f4d6cfef274f8216c06f6c290e7dbb3b9e9e3b0f";
95 };
95 };
96 };
96 };
97 "jshint-2.9.3" = {
97 "node2nix-1.3.0" = {
98 name = "node2nix";
99 packageName = "node2nix";
100 version = "1.3.0";
101 src = fetchurl {
102 url = "https://registry.npmjs.org/node2nix/-/node2nix-1.3.0.tgz";
103 sha1 = "e830a3bc5880dd22ae47be71a147f776542850cc";
104 };
105 };
106 "jshint-2.9.5" = {
98 name = "jshint";
107 name = "jshint";
99 packageName = "jshint";
108 packageName = "jshint";
100 version = "2.9.3";
109 version = "2.9.5";
101 src = fetchurl {
110 src = fetchurl {
102 url = "https://registry.npmjs.org/jshint/-/jshint-2.9.3.tgz";
111 url = "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz";
103 sha1 = "a2e14ff85c2d6bf8c8080e5aa55129ebc6a2d320";
112 sha1 = "1e7252915ce681b40827ee14248c46d34e9aa62c";
104 };
113 };
105 };
114 };
106 "bower-1.7.9" = {
115 "bower-1.8.2" = {
107 name = "bower";
116 name = "bower";
108 packageName = "bower";
117 packageName = "bower";
109 version = "1.7.9";
118 version = "1.8.2";
110 src = fetchurl {
119 src = fetchurl {
111 url = "https://registry.npmjs.org/bower/-/bower-1.7.9.tgz";
120 url = "https://registry.npmjs.org/bower/-/bower-1.8.2.tgz";
112 sha1 = "b7296c2393e0d75edaa6ca39648132dd255812b0";
121 sha1 = "adf53529c8d4af02ef24fb8d5341c1419d33e2f7";
122 };
123 };
124 "jquery-1.11.3" = {
125 name = "jquery";
126 packageName = "jquery";
127 version = "1.11.3";
128 src = fetchurl {
129 url = "https://registry.npmjs.org/jquery/-/jquery-1.11.3.tgz";
130 sha1 = "dd8b74278b27102d29df63eae28308a8cfa1b583";
113 };
131 };
114 };
132 };
115 "favico.js-0.3.10" = {
133 "favico.js-0.3.10" = {
@@ -121,14 +139,41 b' let'
121 sha1 = "80586e27a117f24a8d51c18a99bdc714d4339301";
139 sha1 = "80586e27a117f24a8d51c18a99bdc714d4339301";
122 };
140 };
123 };
141 };
124 "appenlight-client-git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.0" = {
142 "clipboard-1.7.1" = {
143 name = "clipboard";
144 packageName = "clipboard";
145 version = "1.7.1";
146 src = fetchurl {
147 url = "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz";
148 sha1 = "360d6d6946e99a7a1fef395e42ba92b5e9b5a16b";
149 };
150 };
151 "moment-2.18.1" = {
152 name = "moment";
153 packageName = "moment";
154 version = "2.18.1";
155 src = fetchurl {
156 url = "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz";
157 sha1 = "c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f";
158 };
159 };
160 "mousetrap-1.6.1" = {
161 name = "mousetrap";
162 packageName = "mousetrap";
163 version = "1.6.1";
164 src = fetchurl {
165 url = "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.1.tgz";
166 sha1 = "2a085f5c751294c75e7e81f6ec2545b29cbf42d9";
167 };
168 };
169 "appenlight-client-git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.1" = {
125 name = "appenlight-client";
170 name = "appenlight-client";
126 packageName = "appenlight-client";
171 packageName = "appenlight-client";
127 version = "0.5.0";
172 version = "0.5.1";
128 src = fetchgit {
173 src = fetchgit {
129 url = "https://git@github.com/AppEnlight/appenlight-client-js.git";
174 url = "https://git@github.com/AppEnlight/appenlight-client-js.git";
130 rev = "b1d6853345dc3e96468b34537810b3eb77e0764f";
175 rev = "14712c64c230fbbe94fcbc8094aef5eb3b90b307";
131 sha256 = "2ef00aef7dafdecdc1666d2e83fc190a796849985d04a8f0fad148d64aa4f8db";
176 sha256 = "92111f1104cbf0b31303c366c0fa752cf68af7ddde40d0161edd1b5fd9dd07f7";
132 };
177 };
133 };
178 };
134 "async-0.1.22" = {
179 "async-0.1.22" = {
@@ -383,13 +428,13 b' let'
383 sha1 = "ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b";
428 sha1 = "ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b";
384 };
429 };
385 };
430 };
386 "abbrev-1.0.9" = {
431 "abbrev-1.1.0" = {
387 name = "abbrev";
432 name = "abbrev";
388 packageName = "abbrev";
433 packageName = "abbrev";
389 version = "1.0.9";
434 version = "1.1.0";
390 src = fetchurl {
435 src = fetchurl {
391 url = "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz";
436 url = "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz";
392 sha1 = "91b4792588a7738c25f35dd6f63752a2f8776135";
437 sha1 = "d0554c2256636e2f56e7c2e5ad183f859428d81f";
393 };
438 };
394 };
439 };
395 "argparse-0.1.16" = {
440 "argparse-0.1.16" = {
@@ -509,13 +554,13 b' let'
509 sha1 = "535d045ce6b6363fa40117084629995e9df324c7";
554 sha1 = "535d045ce6b6363fa40117084629995e9df324c7";
510 };
555 };
511 };
556 };
512 "ansi-regex-2.0.0" = {
557 "ansi-regex-2.1.1" = {
513 name = "ansi-regex";
558 name = "ansi-regex";
514 packageName = "ansi-regex";
559 packageName = "ansi-regex";
515 version = "2.0.0";
560 version = "2.1.1";
516 src = fetchurl {
561 src = fetchurl {
517 url = "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz";
562 url = "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz";
518 sha1 = "c5061b6e0ef8a81775e50f5d66151bf6bf371107";
563 sha1 = "c3b33ab5ee360d86e0e628f0468ae7ef27d654df";
519 };
564 };
520 };
565 };
521 "chalk-0.5.1" = {
566 "chalk-0.5.1" = {
@@ -581,40 +626,40 b' let'
581 sha1 = "0d8e946967a3d8143f93e24e298525fc1b2235f9";
626 sha1 = "0d8e946967a3d8143f93e24e298525fc1b2235f9";
582 };
627 };
583 };
628 };
584 "amdefine-1.0.0" = {
629 "amdefine-1.0.1" = {
585 name = "amdefine";
630 name = "amdefine";
586 packageName = "amdefine";
631 packageName = "amdefine";
587 version = "1.0.0";
632 version = "1.0.1";
588 src = fetchurl {
633 src = fetchurl {
589 url = "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz";
634 url = "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz";
590 sha1 = "fd17474700cb5cc9c2b709f0be9d23ce3c198c33";
635 sha1 = "4a5282ac164729e93619bcfd3ad151f817ce91f5";
591 };
636 };
592 };
637 };
593 "async-2.0.1" = {
638 "async-2.5.0" = {
594 name = "async";
639 name = "async";
595 packageName = "async";
640 packageName = "async";
596 version = "2.0.1";
641 version = "2.5.0";
597 src = fetchurl {
642 src = fetchurl {
598 url = "https://registry.npmjs.org/async/-/async-2.0.1.tgz";
643 url = "https://registry.npmjs.org/async/-/async-2.5.0.tgz";
599 sha1 = "b709cc0280a9c36f09f4536be823c838a9049e25";
644 sha1 = "843190fd6b7357a0b9e1c956edddd5ec8462b54d";
600 };
645 };
601 };
646 };
602 "less-2.7.1" = {
647 "less-2.7.2" = {
603 name = "less";
648 name = "less";
604 packageName = "less";
649 packageName = "less";
605 version = "2.7.1";
650 version = "2.7.2";
606 src = fetchurl {
651 src = fetchurl {
607 url = "https://registry.npmjs.org/less/-/less-2.7.1.tgz";
652 url = "https://registry.npmjs.org/less/-/less-2.7.2.tgz";
608 sha1 = "6cbfea22b3b830304e9a5fb371d54fa480c9d7cf";
653 sha1 = "368d6cc73e1fb03981183280918743c5dcf9b3df";
609 };
654 };
610 };
655 };
611 "lodash-4.16.2" = {
656 "lodash-4.17.4" = {
612 name = "lodash";
657 name = "lodash";
613 packageName = "lodash";
658 packageName = "lodash";
614 version = "4.16.2";
659 version = "4.17.4";
615 src = fetchurl {
660 src = fetchurl {
616 url = "https://registry.npmjs.org/lodash/-/lodash-4.16.2.tgz";
661 url = "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz";
617 sha1 = "3e626db827048a699281a8a125226326cfc0e652";
662 sha1 = "78203a4d1c328ae1d86dca6460e369b57f4055ae";
618 };
663 };
619 };
664 };
620 "errno-0.1.4" = {
665 "errno-0.1.4" = {
@@ -626,31 +671,31 b' let'
626 sha1 = "b896e23a9e5e8ba33871fc996abd3635fc9a1c7d";
671 sha1 = "b896e23a9e5e8ba33871fc996abd3635fc9a1c7d";
627 };
672 };
628 };
673 };
629 "graceful-fs-4.1.8" = {
674 "graceful-fs-4.1.11" = {
630 name = "graceful-fs";
675 name = "graceful-fs";
631 packageName = "graceful-fs";
676 packageName = "graceful-fs";
632 version = "4.1.8";
677 version = "4.1.11";
633 src = fetchurl {
678 src = fetchurl {
634 url = "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.8.tgz";
679 url = "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz";
635 sha1 = "da3e11135eb2168bdd374532c4e2649751672890";
680 sha1 = "0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658";
636 };
681 };
637 };
682 };
638 "image-size-0.5.0" = {
683 "image-size-0.5.5" = {
639 name = "image-size";
684 name = "image-size";
640 packageName = "image-size";
685 packageName = "image-size";
641 version = "0.5.0";
686 version = "0.5.5";
642 src = fetchurl {
687 src = fetchurl {
643 url = "https://registry.npmjs.org/image-size/-/image-size-0.5.0.tgz";
688 url = "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz";
644 sha1 = "be7aed1c37b5ac3d9ba1d66a24b4c47ff8397651";
689 sha1 = "09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c";
645 };
690 };
646 };
691 };
647 "mime-1.3.4" = {
692 "mime-1.4.0" = {
648 name = "mime";
693 name = "mime";
649 packageName = "mime";
694 packageName = "mime";
650 version = "1.3.4";
695 version = "1.4.0";
651 src = fetchurl {
696 src = fetchurl {
652 url = "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz";
697 url = "https://registry.npmjs.org/mime/-/mime-1.4.0.tgz";
653 sha1 = "115f9e3b6b3daf2959983cb38f149a2d40eb5d53";
698 sha1 = "69e9e0db51d44f2a3b56e48b7817d7d137f1a343";
654 };
699 };
655 };
700 };
656 "mkdirp-0.5.1" = {
701 "mkdirp-0.5.1" = {
@@ -662,22 +707,31 b' let'
662 sha1 = "30057438eac6cf7f8c4767f38648d6697d75c903";
707 sha1 = "30057438eac6cf7f8c4767f38648d6697d75c903";
663 };
708 };
664 };
709 };
665 "promise-7.1.1" = {
710 "promise-7.3.1" = {
666 name = "promise";
711 name = "promise";
667 packageName = "promise";
712 packageName = "promise";
668 version = "7.1.1";
713 version = "7.3.1";
669 src = fetchurl {
714 src = fetchurl {
670 url = "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz";
715 url = "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz";
671 sha1 = "489654c692616b8aa55b0724fa809bb7db49c5bf";
716 sha1 = "064b72602b18f90f29192b8b1bc418ffd1ebd3bf";
672 };
717 };
673 };
718 };
674 "source-map-0.5.6" = {
719 "source-map-0.5.7" = {
675 name = "source-map";
720 name = "source-map";
676 packageName = "source-map";
721 packageName = "source-map";
677 version = "0.5.6";
722 version = "0.5.7";
678 src = fetchurl {
723 src = fetchurl {
679 url = "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz";
724 url = "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz";
680 sha1 = "75ce38f52bf0733c5a7f0c118d81334a2bb5f412";
725 sha1 = "8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc";
726 };
727 };
728 "request-2.82.0" = {
729 name = "request";
730 packageName = "request";
731 version = "2.82.0";
732 src = fetchurl {
733 url = "https://registry.npmjs.org/request/-/request-2.82.0.tgz";
734 sha1 = "2ba8a92cd7ac45660ea2b10a53ae67cd247516ea";
681 };
735 };
682 };
736 };
683 "prr-0.0.0" = {
737 "prr-0.0.0" = {
@@ -698,13 +752,481 b' let'
698 sha1 = "857fcabfc3397d2625b8228262e86aa7a011b05d";
752 sha1 = "857fcabfc3397d2625b8228262e86aa7a011b05d";
699 };
753 };
700 };
754 };
701 "asap-2.0.5" = {
755 "asap-2.0.6" = {
702 name = "asap";
756 name = "asap";
703 packageName = "asap";
757 packageName = "asap";
704 version = "2.0.5";
758 version = "2.0.6";
759 src = fetchurl {
760 url = "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz";
761 sha1 = "e50347611d7e690943208bbdafebcbc2fb866d46";
762 };
763 };
764 "aws-sign2-0.7.0" = {
765 name = "aws-sign2";
766 packageName = "aws-sign2";
767 version = "0.7.0";
768 src = fetchurl {
769 url = "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz";
770 sha1 = "b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8";
771 };
772 };
773 "aws4-1.6.0" = {
774 name = "aws4";
775 packageName = "aws4";
776 version = "1.6.0";
777 src = fetchurl {
778 url = "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz";
779 sha1 = "83ef5ca860b2b32e4a0deedee8c771b9db57471e";
780 };
781 };
782 "caseless-0.12.0" = {
783 name = "caseless";
784 packageName = "caseless";
785 version = "0.12.0";
786 src = fetchurl {
787 url = "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz";
788 sha1 = "1b681c21ff84033c826543090689420d187151dc";
789 };
790 };
791 "combined-stream-1.0.5" = {
792 name = "combined-stream";
793 packageName = "combined-stream";
794 version = "1.0.5";
795 src = fetchurl {
796 url = "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz";
797 sha1 = "938370a57b4a51dea2c77c15d5c5fdf895164009";
798 };
799 };
800 "extend-3.0.1" = {
801 name = "extend";
802 packageName = "extend";
803 version = "3.0.1";
804 src = fetchurl {
805 url = "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz";
806 sha1 = "a755ea7bc1adfcc5a31ce7e762dbaadc5e636444";
807 };
808 };
809 "forever-agent-0.6.1" = {
810 name = "forever-agent";
811 packageName = "forever-agent";
812 version = "0.6.1";
813 src = fetchurl {
814 url = "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz";
815 sha1 = "fbc71f0c41adeb37f96c577ad1ed42d8fdacca91";
816 };
817 };
818 "form-data-2.3.1" = {
819 name = "form-data";
820 packageName = "form-data";
821 version = "2.3.1";
822 src = fetchurl {
823 url = "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz";
824 sha1 = "6fb94fbd71885306d73d15cc497fe4cc4ecd44bf";
825 };
826 };
827 "har-validator-5.0.3" = {
828 name = "har-validator";
829 packageName = "har-validator";
830 version = "5.0.3";
831 src = fetchurl {
832 url = "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz";
833 sha1 = "ba402c266194f15956ef15e0fcf242993f6a7dfd";
834 };
835 };
836 "hawk-6.0.2" = {
837 name = "hawk";
838 packageName = "hawk";
839 version = "6.0.2";
840 src = fetchurl {
841 url = "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz";
842 sha1 = "af4d914eb065f9b5ce4d9d11c1cb2126eecc3038";
843 };
844 };
845 "http-signature-1.2.0" = {
846 name = "http-signature";
847 packageName = "http-signature";
848 version = "1.2.0";
849 src = fetchurl {
850 url = "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz";
851 sha1 = "9aecd925114772f3d95b65a60abb8f7c18fbace1";
852 };
853 };
854 "is-typedarray-1.0.0" = {
855 name = "is-typedarray";
856 packageName = "is-typedarray";
857 version = "1.0.0";
858 src = fetchurl {
859 url = "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz";
860 sha1 = "e479c80858df0c1b11ddda6940f96011fcda4a9a";
861 };
862 };
863 "isstream-0.1.2" = {
864 name = "isstream";
865 packageName = "isstream";
866 version = "0.1.2";
867 src = fetchurl {
868 url = "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz";
869 sha1 = "47e63f7af55afa6f92e1500e690eb8b8529c099a";
870 };
871 };
872 "json-stringify-safe-5.0.1" = {
873 name = "json-stringify-safe";
874 packageName = "json-stringify-safe";
875 version = "5.0.1";
876 src = fetchurl {
877 url = "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz";
878 sha1 = "1296a2d58fd45f19a0f6ce01d65701e2c735b6eb";
879 };
880 };
881 "mime-types-2.1.17" = {
882 name = "mime-types";
883 packageName = "mime-types";
884 version = "2.1.17";
885 src = fetchurl {
886 url = "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz";
887 sha1 = "09d7a393f03e995a79f8af857b70a9e0ab16557a";
888 };
889 };
890 "oauth-sign-0.8.2" = {
891 name = "oauth-sign";
892 packageName = "oauth-sign";
893 version = "0.8.2";
894 src = fetchurl {
895 url = "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz";
896 sha1 = "46a6ab7f0aead8deae9ec0565780b7d4efeb9d43";
897 };
898 };
899 "performance-now-2.1.0" = {
900 name = "performance-now";
901 packageName = "performance-now";
902 version = "2.1.0";
903 src = fetchurl {
904 url = "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz";
905 sha1 = "6309f4e0e5fa913ec1c69307ae364b4b377c9e7b";
906 };
907 };
908 "qs-6.5.1" = {
909 name = "qs";
910 packageName = "qs";
911 version = "6.5.1";
912 src = fetchurl {
913 url = "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz";
914 sha1 = "349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8";
915 };
916 };
917 "safe-buffer-5.1.1" = {
918 name = "safe-buffer";
919 packageName = "safe-buffer";
920 version = "5.1.1";
921 src = fetchurl {
922 url = "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz";
923 sha1 = "893312af69b2123def71f57889001671eeb2c853";
924 };
925 };
926 "stringstream-0.0.5" = {
927 name = "stringstream";
928 packageName = "stringstream";
929 version = "0.0.5";
930 src = fetchurl {
931 url = "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz";
932 sha1 = "4e484cd4de5a0bbbee18e46307710a8a81621878";
933 };
934 };
935 "tough-cookie-2.3.3" = {
936 name = "tough-cookie";
937 packageName = "tough-cookie";
938 version = "2.3.3";
939 src = fetchurl {
940 url = "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz";
941 sha1 = "0b618a5565b6dea90bf3425d04d55edc475a7561";
942 };
943 };
944 "tunnel-agent-0.6.0" = {
945 name = "tunnel-agent";
946 packageName = "tunnel-agent";
947 version = "0.6.0";
948 src = fetchurl {
949 url = "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz";
950 sha1 = "27a5dea06b36b04a0a9966774b290868f0fc40fd";
951 };
952 };
953 "uuid-3.1.0" = {
954 name = "uuid";
955 packageName = "uuid";
956 version = "3.1.0";
957 src = fetchurl {
958 url = "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz";
959 sha1 = "3dd3d3e790abc24d7b0d3a034ffababe28ebbc04";
960 };
961 };
962 "delayed-stream-1.0.0" = {
963 name = "delayed-stream";
964 packageName = "delayed-stream";
965 version = "1.0.0";
966 src = fetchurl {
967 url = "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz";
968 sha1 = "df3ae199acadfb7d440aaae0b29e2272b24ec619";
969 };
970 };
971 "asynckit-0.4.0" = {
972 name = "asynckit";
973 packageName = "asynckit";
974 version = "0.4.0";
975 src = fetchurl {
976 url = "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz";
977 sha1 = "c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79";
978 };
979 };
980 "ajv-5.2.2" = {
981 name = "ajv";
982 packageName = "ajv";
983 version = "5.2.2";
984 src = fetchurl {
985 url = "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz";
986 sha1 = "47c68d69e86f5d953103b0074a9430dc63da5e39";
987 };
988 };
989 "har-schema-2.0.0" = {
990 name = "har-schema";
991 packageName = "har-schema";
992 version = "2.0.0";
705 src = fetchurl {
993 src = fetchurl {
706 url = "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz";
994 url = "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz";
707 sha1 = "522765b50c3510490e52d7dcfe085ef9ba96958f";
995 sha1 = "a94c2224ebcac04782a0d9035521f24735b7ec92";
996 };
997 };
998 "co-4.6.0" = {
999 name = "co";
1000 packageName = "co";
1001 version = "4.6.0";
1002 src = fetchurl {
1003 url = "https://registry.npmjs.org/co/-/co-4.6.0.tgz";
1004 sha1 = "6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184";
1005 };
1006 };
1007 "fast-deep-equal-1.0.0" = {
1008 name = "fast-deep-equal";
1009 packageName = "fast-deep-equal";
1010 version = "1.0.0";
1011 src = fetchurl {
1012 url = "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz";
1013 sha1 = "96256a3bc975595eb36d82e9929d060d893439ff";
1014 };
1015 };
1016 "json-schema-traverse-0.3.1" = {
1017 name = "json-schema-traverse";
1018 packageName = "json-schema-traverse";
1019 version = "0.3.1";
1020 src = fetchurl {
1021 url = "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz";
1022 sha1 = "349a6d44c53a51de89b40805c5d5e59b417d3340";
1023 };
1024 };
1025 "json-stable-stringify-1.0.1" = {
1026 name = "json-stable-stringify";
1027 packageName = "json-stable-stringify";
1028 version = "1.0.1";
1029 src = fetchurl {
1030 url = "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz";
1031 sha1 = "9a759d39c5f2ff503fd5300646ed445f88c4f9af";
1032 };
1033 };
1034 "jsonify-0.0.0" = {
1035 name = "jsonify";
1036 packageName = "jsonify";
1037 version = "0.0.0";
1038 src = fetchurl {
1039 url = "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz";
1040 sha1 = "2c74b6ee41d93ca51b7b5aaee8f503631d252a73";
1041 };
1042 };
1043 "hoek-4.2.0" = {
1044 name = "hoek";
1045 packageName = "hoek";
1046 version = "4.2.0";
1047 src = fetchurl {
1048 url = "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz";
1049 sha1 = "72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d";
1050 };
1051 };
1052 "boom-4.3.1" = {
1053 name = "boom";
1054 packageName = "boom";
1055 version = "4.3.1";
1056 src = fetchurl {
1057 url = "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz";
1058 sha1 = "4f8a3005cb4a7e3889f749030fd25b96e01d2e31";
1059 };
1060 };
1061 "cryptiles-3.1.2" = {
1062 name = "cryptiles";
1063 packageName = "cryptiles";
1064 version = "3.1.2";
1065 src = fetchurl {
1066 url = "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz";
1067 sha1 = "a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe";
1068 };
1069 };
1070 "sntp-2.0.2" = {
1071 name = "sntp";
1072 packageName = "sntp";
1073 version = "2.0.2";
1074 src = fetchurl {
1075 url = "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz";
1076 sha1 = "5064110f0af85f7cfdb7d6b67a40028ce52b4b2b";
1077 };
1078 };
1079 "boom-5.2.0" = {
1080 name = "boom";
1081 packageName = "boom";
1082 version = "5.2.0";
1083 src = fetchurl {
1084 url = "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz";
1085 sha1 = "5dd9da6ee3a5f302077436290cb717d3f4a54e02";
1086 };
1087 };
1088 "assert-plus-1.0.0" = {
1089 name = "assert-plus";
1090 packageName = "assert-plus";
1091 version = "1.0.0";
1092 src = fetchurl {
1093 url = "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz";
1094 sha1 = "f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525";
1095 };
1096 };
1097 "jsprim-1.4.1" = {
1098 name = "jsprim";
1099 packageName = "jsprim";
1100 version = "1.4.1";
1101 src = fetchurl {
1102 url = "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz";
1103 sha1 = "313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2";
1104 };
1105 };
1106 "sshpk-1.13.1" = {
1107 name = "sshpk";
1108 packageName = "sshpk";
1109 version = "1.13.1";
1110 src = fetchurl {
1111 url = "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz";
1112 sha1 = "512df6da6287144316dc4c18fe1cf1d940739be3";
1113 };
1114 };
1115 "extsprintf-1.3.0" = {
1116 name = "extsprintf";
1117 packageName = "extsprintf";
1118 version = "1.3.0";
1119 src = fetchurl {
1120 url = "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz";
1121 sha1 = "96918440e3041a7a414f8c52e3c574eb3c3e1e05";
1122 };
1123 };
1124 "json-schema-0.2.3" = {
1125 name = "json-schema";
1126 packageName = "json-schema";
1127 version = "0.2.3";
1128 src = fetchurl {
1129 url = "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz";
1130 sha1 = "b480c892e59a2f05954ce727bd3f2a4e882f9e13";
1131 };
1132 };
1133 "verror-1.10.0" = {
1134 name = "verror";
1135 packageName = "verror";
1136 version = "1.10.0";
1137 src = fetchurl {
1138 url = "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz";
1139 sha1 = "3a105ca17053af55d6e270c1f8288682e18da400";
1140 };
1141 };
1142 "core-util-is-1.0.2" = {
1143 name = "core-util-is";
1144 packageName = "core-util-is";
1145 version = "1.0.2";
1146 src = fetchurl {
1147 url = "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz";
1148 sha1 = "b5fd54220aa2bc5ab57aab7140c940754503c1a7";
1149 };
1150 };
1151 "asn1-0.2.3" = {
1152 name = "asn1";
1153 packageName = "asn1";
1154 version = "0.2.3";
1155 src = fetchurl {
1156 url = "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz";
1157 sha1 = "dac8787713c9966849fc8180777ebe9c1ddf3b86";
1158 };
1159 };
1160 "dashdash-1.14.1" = {
1161 name = "dashdash";
1162 packageName = "dashdash";
1163 version = "1.14.1";
1164 src = fetchurl {
1165 url = "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz";
1166 sha1 = "853cfa0f7cbe2fed5de20326b8dd581035f6e2f0";
1167 };
1168 };
1169 "getpass-0.1.7" = {
1170 name = "getpass";
1171 packageName = "getpass";
1172 version = "0.1.7";
1173 src = fetchurl {
1174 url = "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz";
1175 sha1 = "5eff8e3e684d569ae4cb2b1282604e8ba62149fa";
1176 };
1177 };
1178 "jsbn-0.1.1" = {
1179 name = "jsbn";
1180 packageName = "jsbn";
1181 version = "0.1.1";
1182 src = fetchurl {
1183 url = "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz";
1184 sha1 = "a5e654c2e5a2deb5f201d96cefbca80c0ef2f513";
1185 };
1186 };
1187 "tweetnacl-0.14.5" = {
1188 name = "tweetnacl";
1189 packageName = "tweetnacl";
1190 version = "0.14.5";
1191 src = fetchurl {
1192 url = "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz";
1193 sha1 = "5ae68177f192d4456269d108afa93ff8743f4f64";
1194 };
1195 };
1196 "ecc-jsbn-0.1.1" = {
1197 name = "ecc-jsbn";
1198 packageName = "ecc-jsbn";
1199 version = "0.1.1";
1200 src = fetchurl {
1201 url = "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz";
1202 sha1 = "0fc73a9ed5f0d53c38193398523ef7e543777505";
1203 };
1204 };
1205 "bcrypt-pbkdf-1.0.1" = {
1206 name = "bcrypt-pbkdf";
1207 packageName = "bcrypt-pbkdf";
1208 version = "1.0.1";
1209 src = fetchurl {
1210 url = "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz";
1211 sha1 = "63bc5dcb61331b92bc05fd528953c33462a06f8d";
1212 };
1213 };
1214 "mime-db-1.30.0" = {
1215 name = "mime-db";
1216 packageName = "mime-db";
1217 version = "1.30.0";
1218 src = fetchurl {
1219 url = "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz";
1220 sha1 = "74c643da2dd9d6a45399963465b26d5ca7d71f01";
1221 };
1222 };
1223 "punycode-1.4.1" = {
1224 name = "punycode";
1225 packageName = "punycode";
1226 version = "1.4.1";
1227 src = fetchurl {
1228 url = "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz";
1229 sha1 = "c0d5a63b2718800ad8e1eb0fa5269c84dd41845e";
708 };
1230 };
709 };
1231 };
710 "gaze-0.5.2" = {
1232 "gaze-0.5.2" = {
@@ -797,13 +1319,22 b' let'
797 sha1 = "ca7416f20a5e3f9c3b86180f96295fa3d0b52e0d";
1319 sha1 = "ca7416f20a5e3f9c3b86180f96295fa3d0b52e0d";
798 };
1320 };
799 };
1321 };
800 "command-line-args-2.1.6" = {
1322 "command-line-args-3.0.5" = {
801 name = "command-line-args";
1323 name = "command-line-args";
802 packageName = "command-line-args";
1324 packageName = "command-line-args";
803 version = "2.1.6";
1325 version = "3.0.5";
804 src = fetchurl {
1326 src = fetchurl {
805 url = "https://registry.npmjs.org/command-line-args/-/command-line-args-2.1.6.tgz";
1327 url = "https://registry.npmjs.org/command-line-args/-/command-line-args-3.0.5.tgz";
806 sha1 = "f197d6eaff34c9085577484b2864375b294f5697";
1328 sha1 = "5bd4ad45e7983e5c1344918e40280ee2693c5ac0";
1329 };
1330 };
1331 "command-line-usage-3.0.8" = {
1332 name = "command-line-usage";
1333 packageName = "command-line-usage";
1334 version = "3.0.8";
1335 src = fetchurl {
1336 url = "https://registry.npmjs.org/command-line-usage/-/command-line-usage-3.0.8.tgz";
1337 sha1 = "b6a20978c1b383477f5c11a529428b880bfe0f4d";
807 };
1338 };
808 };
1339 };
809 "dom5-1.3.6" = {
1340 "dom5-1.3.6" = {
@@ -815,31 +1346,13 b' let'
815 sha1 = "a7088a9fc5f3b08dc9f6eda4c7abaeb241945e0d";
1346 sha1 = "a7088a9fc5f3b08dc9f6eda4c7abaeb241945e0d";
816 };
1347 };
817 };
1348 };
818 "array-back-1.0.3" = {
1349 "array-back-1.0.4" = {
819 name = "array-back";
1350 name = "array-back";
820 packageName = "array-back";
1351 packageName = "array-back";
821 version = "1.0.3";
1352 version = "1.0.4";
822 src = fetchurl {
823 url = "https://registry.npmjs.org/array-back/-/array-back-1.0.3.tgz";
824 sha1 = "f1128a5cf1b91c80bed4a218f8c5b635c8b10663";
825 };
826 };
827 "command-line-usage-2.0.5" = {
828 name = "command-line-usage";
829 packageName = "command-line-usage";
830 version = "2.0.5";
831 src = fetchurl {
1353 src = fetchurl {
832 url = "https://registry.npmjs.org/command-line-usage/-/command-line-usage-2.0.5.tgz";
1354 url = "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz";
833 sha1 = "f80c35ca5e8624841923ea3be3b9bfbf4f7be27b";
1355 sha1 = "644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b";
834 };
835 };
836 "core-js-2.4.1" = {
837 name = "core-js";
838 packageName = "core-js";
839 version = "2.4.1";
840 src = fetchurl {
841 url = "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz";
842 sha1 = "4de911e667b0eae9124e34254b53aea6fc618d3e";
843 };
1356 };
844 };
1357 };
845 "feature-detect-es6-1.3.1" = {
1358 "feature-detect-es6-1.3.1" = {
@@ -851,139 +1364,22 b' let'
851 sha1 = "f888736af9cb0c91f55663bfa4762eb96ee7047f";
1364 sha1 = "f888736af9cb0c91f55663bfa4762eb96ee7047f";
852 };
1365 };
853 };
1366 };
854 "find-replace-1.0.2" = {
1367 "find-replace-1.0.3" = {
855 name = "find-replace";
1368 name = "find-replace";
856 packageName = "find-replace";
1369 packageName = "find-replace";
857 version = "1.0.2";
1370 version = "1.0.3";
858 src = fetchurl {
859 url = "https://registry.npmjs.org/find-replace/-/find-replace-1.0.2.tgz";
860 sha1 = "a2d6ce740d15f0d92b1b26763e2ce9c0e361fd98";
861 };
862 };
863 "typical-2.6.0" = {
864 name = "typical";
865 packageName = "typical";
866 version = "2.6.0";
867 src = fetchurl {
868 url = "https://registry.npmjs.org/typical/-/typical-2.6.0.tgz";
869 sha1 = "89d51554ab139848a65bcc2c8772f8fb450c40ed";
870 };
871 };
872 "ansi-escape-sequences-2.2.2" = {
873 name = "ansi-escape-sequences";
874 packageName = "ansi-escape-sequences";
875 version = "2.2.2";
876 src = fetchurl {
877 url = "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-2.2.2.tgz";
878 sha1 = "174c78d6f8b7de75f8957ae81c7f72210c701635";
879 };
880 };
881 "column-layout-2.1.4" = {
882 name = "column-layout";
883 packageName = "column-layout";
884 version = "2.1.4";
885 src = fetchurl {
1371 src = fetchurl {
886 url = "https://registry.npmjs.org/column-layout/-/column-layout-2.1.4.tgz";
1372 url = "https://registry.npmjs.org/find-replace/-/find-replace-1.0.3.tgz";
887 sha1 = "ed2857092ccf8338026fe538379d9672d70b3641";
1373 sha1 = "b88e7364d2d9c959559f388c66670d6130441fa0";
888 };
889 };
890 "wordwrapjs-1.2.1" = {
891 name = "wordwrapjs";
892 packageName = "wordwrapjs";
893 version = "1.2.1";
894 src = fetchurl {
895 url = "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-1.2.1.tgz";
896 sha1 = "754a5ea0664cfbff50540dc32d67bda3289fc34b";
897 };
898 };
899 "collect-all-0.2.1" = {
900 name = "collect-all";
901 packageName = "collect-all";
902 version = "0.2.1";
903 src = fetchurl {
904 url = "https://registry.npmjs.org/collect-all/-/collect-all-0.2.1.tgz";
905 sha1 = "7225fb4585c22d4ffac886f0abaf5abc563a1a6a";
906 };
907 };
908 "stream-connect-1.0.2" = {
909 name = "stream-connect";
910 packageName = "stream-connect";
911 version = "1.0.2";
912 src = fetchurl {
913 url = "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz";
914 sha1 = "18bc81f2edb35b8b5d9a8009200a985314428a97";
915 };
1374 };
916 };
1375 };
917 "stream-via-0.1.1" = {
1376 "typical-2.6.1" = {
918 name = "stream-via";
1377 name = "typical";
919 packageName = "stream-via";
1378 packageName = "typical";
920 version = "0.1.1";
1379 version = "2.6.1";
921 src = fetchurl {
922 url = "https://registry.npmjs.org/stream-via/-/stream-via-0.1.1.tgz";
923 sha1 = "0cee5df9c959fb1d3f4eda4819f289d5f9205afc";
924 };
925 };
926 "collect-json-1.0.8" = {
927 name = "collect-json";
928 packageName = "collect-json";
929 version = "1.0.8";
930 src = fetchurl {
931 url = "https://registry.npmjs.org/collect-json/-/collect-json-1.0.8.tgz";
932 sha1 = "aa2fa52b4d1d9444ce690f07a1e3617ab74bb827";
933 };
934 };
935 "deep-extend-0.4.1" = {
936 name = "deep-extend";
937 packageName = "deep-extend";
938 version = "0.4.1";
939 src = fetchurl {
940 url = "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz";
941 sha1 = "efe4113d08085f4e6f9687759810f807469e2253";
942 };
943 };
944 "object-tools-2.0.6" = {
945 name = "object-tools";
946 packageName = "object-tools";
947 version = "2.0.6";
948 src = fetchurl {
1380 src = fetchurl {
949 url = "https://registry.npmjs.org/object-tools/-/object-tools-2.0.6.tgz";
1381 url = "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz";
950 sha1 = "f3fe1c350cda4a6f5d99d9646dc4892a02476ddd";
1382 sha1 = "5c080e5d661cbbe38259d2e70a3c7253e873881d";
951 };
952 };
953 "collect-all-1.0.2" = {
954 name = "collect-all";
955 packageName = "collect-all";
956 version = "1.0.2";
957 src = fetchurl {
958 url = "https://registry.npmjs.org/collect-all/-/collect-all-1.0.2.tgz";
959 sha1 = "39450f1e7aa6086570a006bce93ccf1218a77ea1";
960 };
961 };
962 "stream-via-1.0.3" = {
963 name = "stream-via";
964 packageName = "stream-via";
965 version = "1.0.3";
966 src = fetchurl {
967 url = "https://registry.npmjs.org/stream-via/-/stream-via-1.0.3.tgz";
968 sha1 = "cebd32a5a59d74b3b68e3404942e867184ad4ac9";
969 };
970 };
971 "object-get-2.1.0" = {
972 name = "object-get";
973 packageName = "object-get";
974 version = "2.1.0";
975 src = fetchurl {
976 url = "https://registry.npmjs.org/object-get/-/object-get-2.1.0.tgz";
977 sha1 = "722bbdb60039efa47cad3c6dc2ce51a85c02c5ae";
978 };
979 };
980 "test-value-1.1.0" = {
981 name = "test-value";
982 packageName = "test-value";
983 version = "1.1.0";
984 src = fetchurl {
985 url = "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz";
986 sha1 = "a09136f72ec043d27c893707c2b159bfad7de93f";
987 };
1383 };
988 };
1384 };
989 "test-value-2.1.0" = {
1385 "test-value-2.1.0" = {
@@ -995,6 +1391,60 b' let'
995 sha1 = "11da6ff670f3471a73b625ca4f3fdcf7bb748291";
1391 sha1 = "11da6ff670f3471a73b625ca4f3fdcf7bb748291";
996 };
1392 };
997 };
1393 };
1394 "ansi-escape-sequences-3.0.0" = {
1395 name = "ansi-escape-sequences";
1396 packageName = "ansi-escape-sequences";
1397 version = "3.0.0";
1398 src = fetchurl {
1399 url = "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-3.0.0.tgz";
1400 sha1 = "1c18394b6af9b76ff9a63509fa497669fd2ce53e";
1401 };
1402 };
1403 "table-layout-0.3.0" = {
1404 name = "table-layout";
1405 packageName = "table-layout";
1406 version = "0.3.0";
1407 src = fetchurl {
1408 url = "https://registry.npmjs.org/table-layout/-/table-layout-0.3.0.tgz";
1409 sha1 = "6ee20dc483db371b3e5c87f704ed2f7c799d2c9a";
1410 };
1411 };
1412 "core-js-2.5.1" = {
1413 name = "core-js";
1414 packageName = "core-js";
1415 version = "2.5.1";
1416 src = fetchurl {
1417 url = "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz";
1418 sha1 = "ae6874dc66937789b80754ff5428df66819ca50b";
1419 };
1420 };
1421 "deep-extend-0.4.2" = {
1422 name = "deep-extend";
1423 packageName = "deep-extend";
1424 version = "0.4.2";
1425 src = fetchurl {
1426 url = "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz";
1427 sha1 = "48b699c27e334bf89f10892be432f6e4c7d34a7f";
1428 };
1429 };
1430 "wordwrapjs-2.0.0" = {
1431 name = "wordwrapjs";
1432 packageName = "wordwrapjs";
1433 version = "2.0.0";
1434 src = fetchurl {
1435 url = "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-2.0.0.tgz";
1436 sha1 = "ab55f695e6118da93858fdd70c053d1c5e01ac20";
1437 };
1438 };
1439 "reduce-flatten-1.0.1" = {
1440 name = "reduce-flatten";
1441 packageName = "reduce-flatten";
1442 version = "1.0.1";
1443 src = fetchurl {
1444 url = "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz";
1445 sha1 = "258c78efd153ddf93cb561237f61184f3696e327";
1446 };
1447 };
998 "@types/clone-0.1.30" = {
1448 "@types/clone-0.1.30" = {
999 name = "@types/clone";
1449 name = "@types/clone";
1000 packageName = "@types/clone";
1450 packageName = "@types/clone";
@@ -1004,13 +1454,13 b' let'
1004 sha1 = "e7365648c1b42136a59c7d5040637b3b5c83b614";
1454 sha1 = "e7365648c1b42136a59c7d5040637b3b5c83b614";
1005 };
1455 };
1006 };
1456 };
1007 "@types/node-4.0.30" = {
1457 "@types/node-4.2.20" = {
1008 name = "@types/node";
1458 name = "@types/node";
1009 packageName = "@types/node";
1459 packageName = "@types/node";
1010 version = "4.0.30";
1460 version = "4.2.20";
1011 src = fetchurl {
1461 src = fetchurl {
1012 url = "https://registry.npmjs.org/@types/node/-/node-4.0.30.tgz";
1462 url = "https://registry.npmjs.org/@types/node/-/node-4.2.20.tgz";
1013 sha1 = "553f490ed3030311620f88003e7abfc0edcb301e";
1463 sha1 = "3f7dceff43e07cfff4407fc3495d98a533b32267";
1014 };
1464 };
1015 };
1465 };
1016 "@types/parse5-0.0.31" = {
1466 "@types/parse5-0.0.31" = {
@@ -1040,13 +1490,13 b' let'
1040 sha1 = "9b7f3b0de32be78dc2401b17573ccaf0f6f59d94";
1490 sha1 = "9b7f3b0de32be78dc2401b17573ccaf0f6f59d94";
1041 };
1491 };
1042 };
1492 };
1043 "@types/node-6.0.41" = {
1493 "@types/node-6.0.88" = {
1044 name = "@types/node";
1494 name = "@types/node";
1045 packageName = "@types/node";
1495 packageName = "@types/node";
1046 version = "6.0.41";
1496 version = "6.0.88";
1047 src = fetchurl {
1497 src = fetchurl {
1048 url = "https://registry.npmjs.org/@types/node/-/node-6.0.41.tgz";
1498 url = "https://registry.npmjs.org/@types/node/-/node-6.0.88.tgz";
1049 sha1 = "578cf53aaec65887bcaf16792f8722932e8ff8ea";
1499 sha1 = "f618f11a944f6a18d92b5c472028728a3e3d4b66";
1050 };
1500 };
1051 };
1501 };
1052 "es6-promise-2.3.0" = {
1502 "es6-promise-2.3.0" = {
@@ -1058,13 +1508,13 b' let'
1058 sha1 = "96edb9f2fdb01995822b263dd8aadab6748181bc";
1508 sha1 = "96edb9f2fdb01995822b263dd8aadab6748181bc";
1059 };
1509 };
1060 };
1510 };
1061 "hydrolysis-1.24.1" = {
1511 "hydrolysis-1.25.0" = {
1062 name = "hydrolysis";
1512 name = "hydrolysis";
1063 packageName = "hydrolysis";
1513 packageName = "hydrolysis";
1064 version = "1.24.1";
1514 version = "1.25.0";
1065 src = fetchurl {
1515 src = fetchurl {
1066 url = "https://registry.npmjs.org/hydrolysis/-/hydrolysis-1.24.1.tgz";
1516 url = "https://registry.npmjs.org/hydrolysis/-/hydrolysis-1.25.0.tgz";
1067 sha1 = "0f94f055d1065ac0d81ff40b762d143fef07eff4";
1517 sha1 = "a4fb14a37a1e03b0db52d8aaa57c682272a14d84";
1068 };
1518 };
1069 };
1519 };
1070 "nopt-3.0.6" = {
1520 "nopt-3.0.6" = {
@@ -1085,22 +1535,22 b' let'
1085 sha1 = "06b26113f56beab042545a23bfa88003ccac260f";
1535 sha1 = "06b26113f56beab042545a23bfa88003ccac260f";
1086 };
1536 };
1087 };
1537 };
1088 "update-notifier-0.6.3" = {
1538 "acorn-3.3.0" = {
1089 name = "update-notifier";
1539 name = "acorn";
1090 packageName = "update-notifier";
1540 packageName = "acorn";
1091 version = "0.6.3";
1541 version = "3.3.0";
1092 src = fetchurl {
1542 src = fetchurl {
1093 url = "https://registry.npmjs.org/update-notifier/-/update-notifier-0.6.3.tgz";
1543 url = "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz";
1094 sha1 = "776dec8daa13e962a341e8a1d98354306b67ae08";
1544 sha1 = "45e37fb39e8da3f25baee3ff5369e2bb5f22017a";
1095 };
1545 };
1096 };
1546 };
1097 "babel-polyfill-6.13.0" = {
1547 "babel-polyfill-6.26.0" = {
1098 name = "babel-polyfill";
1548 name = "babel-polyfill";
1099 packageName = "babel-polyfill";
1549 packageName = "babel-polyfill";
1100 version = "6.13.0";
1550 version = "6.26.0";
1101 src = fetchurl {
1551 src = fetchurl {
1102 url = "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.13.0.tgz";
1552 url = "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz";
1103 sha1 = "5978215c25d49a697eb78afc54e63c9d3a73d5ec";
1553 sha1 = "379937abc67d7895970adc621f284cd966cf2153";
1104 };
1554 };
1105 };
1555 };
1106 "doctrine-0.7.2" = {
1556 "doctrine-0.7.2" = {
@@ -1112,22 +1562,31 b' let'
1112 sha1 = "7cb860359ba3be90e040b26b729ce4bfa654c523";
1562 sha1 = "7cb860359ba3be90e040b26b729ce4bfa654c523";
1113 };
1563 };
1114 };
1564 };
1115 "escodegen-1.8.1" = {
1565 "dom5-1.1.0" = {
1566 name = "dom5";
1567 packageName = "dom5";
1568 version = "1.1.0";
1569 src = fetchurl {
1570 url = "https://registry.npmjs.org/dom5/-/dom5-1.1.0.tgz";
1571 sha1 = "3a0c7700c083ab4c4d26938a78b0f0c6dcc37794";
1572 };
1573 };
1574 "escodegen-1.9.0" = {
1116 name = "escodegen";
1575 name = "escodegen";
1117 packageName = "escodegen";
1576 packageName = "escodegen";
1118 version = "1.8.1";
1577 version = "1.9.0";
1119 src = fetchurl {
1578 src = fetchurl {
1120 url = "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz";
1579 url = "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz";
1121 sha1 = "5a5b53af4693110bebb0867aa3430dd3b70a1018";
1580 sha1 = "9811a2f265dc1cd3894420ee3717064b632b8852";
1122 };
1581 };
1123 };
1582 };
1124 "espree-3.3.1" = {
1583 "espree-3.5.1" = {
1125 name = "espree";
1584 name = "espree";
1126 packageName = "espree";
1585 packageName = "espree";
1127 version = "3.3.1";
1586 version = "3.5.1";
1128 src = fetchurl {
1587 src = fetchurl {
1129 url = "https://registry.npmjs.org/espree/-/espree-3.3.1.tgz";
1588 url = "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz";
1130 sha1 = "42107376856738a65ff3b5877f3a58bd52497643";
1589 sha1 = "0c988b8ab46db53100a1954ae4ba995ddd27d87e";
1131 };
1590 };
1132 };
1591 };
1133 "estraverse-3.1.0" = {
1592 "estraverse-3.1.0" = {
@@ -1139,31 +1598,40 b' let'
1139 sha1 = "15e28a446b8b82bc700ccc8b96c78af4da0d6cba";
1598 sha1 = "15e28a446b8b82bc700ccc8b96c78af4da0d6cba";
1140 };
1599 };
1141 };
1600 };
1142 "path-is-absolute-1.0.0" = {
1601 "path-is-absolute-1.0.1" = {
1143 name = "path-is-absolute";
1602 name = "path-is-absolute";
1144 packageName = "path-is-absolute";
1603 packageName = "path-is-absolute";
1145 version = "1.0.0";
1604 version = "1.0.1";
1146 src = fetchurl {
1605 src = fetchurl {
1147 url = "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz";
1606 url = "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz";
1148 sha1 = "263dada66ab3f2fb10bf7f9d24dd8f3e570ef912";
1607 sha1 = "174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f";
1149 };
1608 };
1150 };
1609 };
1151 "babel-runtime-6.11.6" = {
1610 "babel-runtime-6.26.0" = {
1152 name = "babel-runtime";
1611 name = "babel-runtime";
1153 packageName = "babel-runtime";
1612 packageName = "babel-runtime";
1154 version = "6.11.6";
1613 version = "6.26.0";
1155 src = fetchurl {
1614 src = fetchurl {
1156 url = "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.11.6.tgz";
1615 url = "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz";
1157 sha1 = "6db707fef2d49c49bfa3cb64efdb436b518b8222";
1616 sha1 = "965c7058668e82b55d7bfe04ff2337bc8b5647fe";
1158 };
1617 };
1159 };
1618 };
1160 "regenerator-runtime-0.9.5" = {
1619 "regenerator-runtime-0.10.5" = {
1161 name = "regenerator-runtime";
1620 name = "regenerator-runtime";
1162 packageName = "regenerator-runtime";
1621 packageName = "regenerator-runtime";
1163 version = "0.9.5";
1622 version = "0.10.5";
1164 src = fetchurl {
1623 src = fetchurl {
1165 url = "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz";
1624 url = "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz";
1166 sha1 = "403d6d40a4bdff9c330dd9392dcbb2d9a8bba1fc";
1625 sha1 = "336c3efc1220adcedda2c9fab67b5a7955a33658";
1626 };
1627 };
1628 "regenerator-runtime-0.11.0" = {
1629 name = "regenerator-runtime";
1630 packageName = "regenerator-runtime";
1631 version = "0.11.0";
1632 src = fetchurl {
1633 url = "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz";
1634 sha1 = "7e54fe5b5ccd5d6624ea6255c3473be090b802e1";
1167 };
1635 };
1168 };
1636 };
1169 "esutils-1.1.6" = {
1637 "esutils-1.1.6" = {
@@ -1184,13 +1652,13 b' let'
1184 sha1 = "8a18acfca9a8f4177e09abfc6038939b05d1eedf";
1652 sha1 = "8a18acfca9a8f4177e09abfc6038939b05d1eedf";
1185 };
1653 };
1186 };
1654 };
1187 "estraverse-1.9.3" = {
1655 "estraverse-4.2.0" = {
1188 name = "estraverse";
1656 name = "estraverse";
1189 packageName = "estraverse";
1657 packageName = "estraverse";
1190 version = "1.9.3";
1658 version = "4.2.0";
1191 src = fetchurl {
1659 src = fetchurl {
1192 url = "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz";
1660 url = "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz";
1193 sha1 = "af67f2dc922582415950926091a4005d29c9bb44";
1661 sha1 = "0dee3fed31fcd469618ce7342099fc1afa0bdb13";
1194 };
1662 };
1195 };
1663 };
1196 "esutils-2.0.2" = {
1664 "esutils-2.0.2" = {
@@ -1202,13 +1670,13 b' let'
1202 sha1 = "0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b";
1670 sha1 = "0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b";
1203 };
1671 };
1204 };
1672 };
1205 "esprima-2.7.3" = {
1673 "esprima-3.1.3" = {
1206 name = "esprima";
1674 name = "esprima";
1207 packageName = "esprima";
1675 packageName = "esprima";
1208 version = "2.7.3";
1676 version = "3.1.3";
1209 src = fetchurl {
1677 src = fetchurl {
1210 url = "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz";
1678 url = "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz";
1211 sha1 = "96e3b70d5779f6ad49cd032673d1c312767ba581";
1679 sha1 = "fdca51cee6133895e3c88d535ce49dbff62a4633";
1212 };
1680 };
1213 };
1681 };
1214 "optionator-0.8.2" = {
1682 "optionator-0.8.2" = {
@@ -1220,15 +1688,6 b' let'
1220 sha1 = "364c5e409d3f4d6301d6c0b4c05bba50180aeb64";
1688 sha1 = "364c5e409d3f4d6301d6c0b4c05bba50180aeb64";
1221 };
1689 };
1222 };
1690 };
1223 "source-map-0.2.0" = {
1224 name = "source-map";
1225 packageName = "source-map";
1226 version = "0.2.0";
1227 src = fetchurl {
1228 url = "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz";
1229 sha1 = "dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d";
1230 };
1231 };
1232 "prelude-ls-1.1.2" = {
1691 "prelude-ls-1.1.2" = {
1233 name = "prelude-ls";
1692 name = "prelude-ls";
1234 packageName = "prelude-ls";
1693 packageName = "prelude-ls";
@@ -1274,22 +1733,22 b' let'
1274 sha1 = "3b09924edf9f083c0490fdd4c0bc4421e04764ee";
1733 sha1 = "3b09924edf9f083c0490fdd4c0bc4421e04764ee";
1275 };
1734 };
1276 };
1735 };
1277 "fast-levenshtein-2.0.4" = {
1736 "fast-levenshtein-2.0.6" = {
1278 name = "fast-levenshtein";
1737 name = "fast-levenshtein";
1279 packageName = "fast-levenshtein";
1738 packageName = "fast-levenshtein";
1280 version = "2.0.4";
1739 version = "2.0.6";
1281 src = fetchurl {
1740 src = fetchurl {
1282 url = "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.4.tgz";
1741 url = "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz";
1283 sha1 = "e31e729eea62233c60a7bc9dce2bdcc88b4fffe3";
1742 sha1 = "3d8a5c66883a16a30ca8643e851f19baa7797917";
1284 };
1743 };
1285 };
1744 };
1286 "acorn-4.0.3" = {
1745 "acorn-5.1.2" = {
1287 name = "acorn";
1746 name = "acorn";
1288 packageName = "acorn";
1747 packageName = "acorn";
1289 version = "4.0.3";
1748 version = "5.1.2";
1290 src = fetchurl {
1749 src = fetchurl {
1291 url = "https://registry.npmjs.org/acorn/-/acorn-4.0.3.tgz";
1750 url = "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz";
1292 sha1 = "1a3e850b428e73ba6b09d1cc527f5aaad4d03ef1";
1751 sha1 = "911cb53e036807cf0fa778dc5d370fbd864246d7";
1293 };
1752 };
1294 };
1753 };
1295 "acorn-jsx-3.0.1" = {
1754 "acorn-jsx-3.0.1" = {
@@ -1301,220 +1760,166 b' let'
1301 sha1 = "afdf9488fb1ecefc8348f6fb22f464e32a58b36b";
1760 sha1 = "afdf9488fb1ecefc8348f6fb22f464e32a58b36b";
1302 };
1761 };
1303 };
1762 };
1304 "acorn-3.3.0" = {
1763 "object-assign-4.1.1" = {
1305 name = "acorn";
1764 name = "object-assign";
1306 packageName = "acorn";
1765 packageName = "object-assign";
1307 version = "3.3.0";
1766 version = "4.1.1";
1308 src = fetchurl {
1767 src = fetchurl {
1309 url = "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz";
1768 url = "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz";
1310 sha1 = "45e37fb39e8da3f25baee3ff5369e2bb5f22017a";
1769 sha1 = "2109adc7965887cfc05cbbd442cac8bfbb360863";
1311 };
1312 };
1313 "boxen-0.3.1" = {
1314 name = "boxen";
1315 packageName = "boxen";
1316 version = "0.3.1";
1317 src = fetchurl {
1318 url = "https://registry.npmjs.org/boxen/-/boxen-0.3.1.tgz";
1319 sha1 = "a7d898243ae622f7abb6bb604d740a76c6a5461b";
1320 };
1770 };
1321 };
1771 };
1322 "configstore-2.1.0" = {
1772 "crisper-1.2.0" = {
1323 name = "configstore";
1773 name = "crisper";
1324 packageName = "configstore";
1774 packageName = "crisper";
1325 version = "2.1.0";
1775 version = "1.2.0";
1326 src = fetchurl {
1776 src = fetchurl {
1327 url = "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz";
1777 url = "https://registry.npmjs.org/crisper/-/crisper-1.2.0.tgz";
1328 sha1 = "737a3a7036e9886102aa6099e47bb33ab1aba1a1";
1778 sha1 = "9a91f597d71f6110294e076ad44dbb3408568e46";
1329 };
1779 };
1330 };
1780 };
1331 "is-npm-1.0.0" = {
1781 "optparse-1.0.5" = {
1332 name = "is-npm";
1782 name = "optparse";
1333 packageName = "is-npm";
1783 packageName = "optparse";
1334 version = "1.0.0";
1784 version = "1.0.5";
1335 src = fetchurl {
1785 src = fetchurl {
1336 url = "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz";
1786 url = "https://registry.npmjs.org/optparse/-/optparse-1.0.5.tgz";
1337 sha1 = "f2fb63a65e4905b406c86072765a1a4dc793b9f4";
1787 sha1 = "75e75a96506611eb1c65ba89018ff08a981e2c16";
1338 };
1339 };
1340 "latest-version-2.0.0" = {
1341 name = "latest-version";
1342 packageName = "latest-version";
1343 version = "2.0.0";
1344 src = fetchurl {
1345 url = "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz";
1346 sha1 = "56f8d6139620847b8017f8f1f4d78e211324168b";
1347 };
1788 };
1348 };
1789 };
1349 "semver-diff-2.1.0" = {
1790 "semver-5.4.1" = {
1350 name = "semver-diff";
1791 name = "semver";
1351 packageName = "semver-diff";
1792 packageName = "semver";
1352 version = "2.1.0";
1793 version = "5.4.1";
1353 src = fetchurl {
1794 src = fetchurl {
1354 url = "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz";
1795 url = "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz";
1355 sha1 = "4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36";
1796 sha1 = "e059c09d8571f0540823733433505d3a2f00b18e";
1356 };
1797 };
1357 };
1798 };
1358 "filled-array-1.1.0" = {
1799 "npm-registry-client-8.4.0" = {
1359 name = "filled-array";
1800 name = "npm-registry-client";
1360 packageName = "filled-array";
1801 packageName = "npm-registry-client";
1361 version = "1.1.0";
1802 version = "8.4.0";
1362 src = fetchurl {
1803 src = fetchurl {
1363 url = "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz";
1804 url = "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.4.0.tgz";
1364 sha1 = "c3c4f6c663b923459a9aa29912d2d031f1507f84";
1805 sha1 = "d52b901685647fc62a4c03eafecb6ceaa5018d4c";
1365 };
1806 };
1366 };
1807 };
1367 "object-assign-4.1.0" = {
1808 "npmconf-2.1.2" = {
1368 name = "object-assign";
1809 name = "npmconf";
1369 packageName = "object-assign";
1810 packageName = "npmconf";
1370 version = "4.1.0";
1811 version = "2.1.2";
1371 src = fetchurl {
1812 src = fetchurl {
1372 url = "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz";
1813 url = "https://registry.npmjs.org/npmconf/-/npmconf-2.1.2.tgz";
1373 sha1 = "7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0";
1814 sha1 = "66606a4a736f1e77a059aa071a79c94ab781853a";
1374 };
1815 };
1375 };
1816 };
1376 "repeating-2.0.1" = {
1817 "tar-3.1.15" = {
1377 name = "repeating";
1818 name = "tar";
1378 packageName = "repeating";
1819 packageName = "tar";
1379 version = "2.0.1";
1820 version = "3.1.15";
1380 src = fetchurl {
1821 src = fetchurl {
1381 url = "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz";
1822 url = "https://registry.npmjs.org/tar/-/tar-3.1.15.tgz";
1382 sha1 = "5214c53a926d3552707527fbab415dbc08d06dda";
1823 sha1 = "cccdc35b90917d58e4c3837795d5d022d7a1f46f";
1383 };
1824 };
1384 };
1825 };
1385 "string-width-1.0.2" = {
1826 "temp-0.8.3" = {
1386 name = "string-width";
1827 name = "temp";
1387 packageName = "string-width";
1828 packageName = "temp";
1388 version = "1.0.2";
1829 version = "0.8.3";
1389 src = fetchurl {
1830 src = fetchurl {
1390 url = "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz";
1831 url = "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz";
1391 sha1 = "118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3";
1832 sha1 = "e0c6bc4d26b903124410e4fed81103014dfc1f59";
1392 };
1393 };
1394 "widest-line-1.0.0" = {
1395 name = "widest-line";
1396 packageName = "widest-line";
1397 version = "1.0.0";
1398 src = fetchurl {
1399 url = "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz";
1400 sha1 = "0c09c85c2a94683d0d7eaf8ee097d564bf0e105c";
1401 };
1833 };
1402 };
1834 };
1403 "is-finite-1.0.1" = {
1835 "fs.extra-1.3.2" = {
1404 name = "is-finite";
1836 name = "fs.extra";
1405 packageName = "is-finite";
1837 packageName = "fs.extra";
1406 version = "1.0.1";
1838 version = "1.3.2";
1407 src = fetchurl {
1839 src = fetchurl {
1408 url = "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz";
1840 url = "https://registry.npmjs.org/fs.extra/-/fs.extra-1.3.2.tgz";
1409 sha1 = "6438603eaebe2793948ff4a4262ec8db3d62597b";
1841 sha1 = "dd023f93013bee24531f1b33514c37b20fd93349";
1410 };
1411 };
1412 "number-is-nan-1.0.0" = {
1413 name = "number-is-nan";
1414 packageName = "number-is-nan";
1415 version = "1.0.0";
1416 src = fetchurl {
1417 url = "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz";
1418 sha1 = "c020f529c5282adfdd233d91d4b181c3d686dc4b";
1419 };
1842 };
1420 };
1843 };
1421 "code-point-at-1.0.0" = {
1844 "findit-2.0.0" = {
1422 name = "code-point-at";
1845 name = "findit";
1423 packageName = "code-point-at";
1846 packageName = "findit";
1424 version = "1.0.0";
1847 version = "2.0.0";
1425 src = fetchurl {
1848 src = fetchurl {
1426 url = "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz";
1849 url = "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz";
1427 sha1 = "f69b192d3f7d91e382e4b71bddb77878619ab0c6";
1850 sha1 = "6509f0126af4c178551cfa99394e032e13a4d56e";
1428 };
1851 };
1429 };
1852 };
1430 "is-fullwidth-code-point-1.0.0" = {
1853 "base64-js-1.2.1" = {
1431 name = "is-fullwidth-code-point";
1854 name = "base64-js";
1432 packageName = "is-fullwidth-code-point";
1855 packageName = "base64-js";
1433 version = "1.0.0";
1856 version = "1.2.1";
1434 src = fetchurl {
1857 src = fetchurl {
1435 url = "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz";
1858 url = "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz";
1436 sha1 = "ef9e31386f031a7f0d643af82fde50c457ef00cb";
1859 sha1 = "a91947da1f4a516ea38e5b4ec0ec3773675e0886";
1437 };
1860 };
1438 };
1861 };
1439 "dot-prop-3.0.0" = {
1862 "slasp-0.0.4" = {
1440 name = "dot-prop";
1863 name = "slasp";
1441 packageName = "dot-prop";
1864 packageName = "slasp";
1442 version = "3.0.0";
1865 version = "0.0.4";
1443 src = fetchurl {
1866 src = fetchurl {
1444 url = "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz";
1867 url = "https://registry.npmjs.org/slasp/-/slasp-0.0.4.tgz";
1445 sha1 = "1b708af094a49c9a0e7dbcad790aba539dac1177";
1868 sha1 = "9adc26ee729a0f95095851a5489f87a5258d57a9";
1446 };
1447 };
1448 "os-tmpdir-1.0.1" = {
1449 name = "os-tmpdir";
1450 packageName = "os-tmpdir";
1451 version = "1.0.1";
1452 src = fetchurl {
1453 url = "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz";
1454 sha1 = "e9b423a1edaf479882562e92ed71d7743a071b6e";
1455 };
1869 };
1456 };
1870 };
1457 "osenv-0.1.3" = {
1871 "nijs-0.0.23" = {
1458 name = "osenv";
1872 name = "nijs";
1459 packageName = "osenv";
1873 packageName = "nijs";
1460 version = "0.1.3";
1874 version = "0.0.23";
1461 src = fetchurl {
1875 src = fetchurl {
1462 url = "https://registry.npmjs.org/osenv/-/osenv-0.1.3.tgz";
1876 url = "https://registry.npmjs.org/nijs/-/nijs-0.0.23.tgz";
1463 sha1 = "83cf05c6d6458fc4d5ac6362ea325d92f2754217";
1877 sha1 = "dbf8f4a0acafbe3b8d9b71c24cbd1d851de6c31a";
1464 };
1878 };
1465 };
1879 };
1466 "uuid-2.0.3" = {
1880 "concat-stream-1.6.0" = {
1467 name = "uuid";
1881 name = "concat-stream";
1468 packageName = "uuid";
1882 packageName = "concat-stream";
1469 version = "2.0.3";
1883 version = "1.6.0";
1470 src = fetchurl {
1884 src = fetchurl {
1471 url = "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz";
1885 url = "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz";
1472 sha1 = "67e2e863797215530dff318e5bf9dcebfd47b21a";
1886 sha1 = "0aac662fd52be78964d5532f694784e70110acf7";
1473 };
1474 };
1475 "write-file-atomic-1.2.0" = {
1476 name = "write-file-atomic";
1477 packageName = "write-file-atomic";
1478 version = "1.2.0";
1479 src = fetchurl {
1480 url = "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.2.0.tgz";
1481 sha1 = "14c66d4e4cb3ca0565c28cf3b7a6f3e4d5938fab";
1482 };
1887 };
1483 };
1888 };
1484 "xdg-basedir-2.0.0" = {
1889 "normalize-package-data-2.4.0" = {
1485 name = "xdg-basedir";
1890 name = "normalize-package-data";
1486 packageName = "xdg-basedir";
1891 packageName = "normalize-package-data";
1487 version = "2.0.0";
1892 version = "2.4.0";
1488 src = fetchurl {
1893 src = fetchurl {
1489 url = "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz";
1894 url = "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz";
1490 sha1 = "edbc903cc385fc04523d966a335504b5504d1bd2";
1895 sha1 = "12f95a307d58352075a04907b84ac8be98ac012f";
1491 };
1896 };
1492 };
1897 };
1493 "is-obj-1.0.1" = {
1898 "npm-package-arg-5.1.2" = {
1494 name = "is-obj";
1899 name = "npm-package-arg";
1495 packageName = "is-obj";
1900 packageName = "npm-package-arg";
1496 version = "1.0.1";
1901 version = "5.1.2";
1497 src = fetchurl {
1902 src = fetchurl {
1498 url = "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz";
1903 url = "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-5.1.2.tgz";
1499 sha1 = "3e4729ac1f5fde025cd7d83a896dab9f4f67db0f";
1904 sha1 = "fb18d17bb61e60900d6312619919bd753755ab37";
1500 };
1905 };
1501 };
1906 };
1502 "os-homedir-1.0.1" = {
1907 "once-1.4.0" = {
1503 name = "os-homedir";
1908 name = "once";
1504 packageName = "os-homedir";
1909 packageName = "once";
1505 version = "1.0.1";
1910 version = "1.4.0";
1506 src = fetchurl {
1911 src = fetchurl {
1507 url = "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz";
1912 url = "https://registry.npmjs.org/once/-/once-1.4.0.tgz";
1508 sha1 = "0d62bdf44b916fd3bbdcf2cab191948fb094f007";
1913 sha1 = "583b1aa775961d4b113ac17d9c50baef9dd76bd1";
1509 };
1914 };
1510 };
1915 };
1511 "imurmurhash-0.1.4" = {
1916 "retry-0.10.1" = {
1512 name = "imurmurhash";
1917 name = "retry";
1513 packageName = "imurmurhash";
1918 packageName = "retry";
1514 version = "0.1.4";
1919 version = "0.10.1";
1515 src = fetchurl {
1920 src = fetchurl {
1516 url = "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz";
1921 url = "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz";
1517 sha1 = "9218b9b2b928a238b13dc4fb6b6d576f231453ea";
1922 sha1 = "e76388d217992c252750241d3d3956fed98d8ff4";
1518 };
1923 };
1519 };
1924 };
1520 "slide-1.1.6" = {
1925 "slide-1.1.6" = {
@@ -1526,238 +1931,40 b' let'
1526 sha1 = "56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707";
1931 sha1 = "56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707";
1527 };
1932 };
1528 };
1933 };
1529 "package-json-2.4.0" = {
1934 "ssri-4.1.6" = {
1530 name = "package-json";
1935 name = "ssri";
1531 packageName = "package-json";
1936 packageName = "ssri";
1532 version = "2.4.0";
1937 version = "4.1.6";
1533 src = fetchurl {
1534 url = "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz";
1535 sha1 = "0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb";
1536 };
1537 };
1538 "got-5.6.0" = {
1539 name = "got";
1540 packageName = "got";
1541 version = "5.6.0";
1542 src = fetchurl {
1543 url = "https://registry.npmjs.org/got/-/got-5.6.0.tgz";
1544 sha1 = "bb1d7ee163b78082bbc8eb836f3f395004ea6fbf";
1545 };
1546 };
1547 "registry-auth-token-3.0.1" = {
1548 name = "registry-auth-token";
1549 packageName = "registry-auth-token";
1550 version = "3.0.1";
1551 src = fetchurl {
1552 url = "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.0.1.tgz";
1553 sha1 = "c3ee5ec585bce29f88bf41629a3944c71ed53e25";
1554 };
1555 };
1556 "registry-url-3.1.0" = {
1557 name = "registry-url";
1558 packageName = "registry-url";
1559 version = "3.1.0";
1560 src = fetchurl {
1561 url = "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz";
1562 sha1 = "3d4ef870f73dde1d77f0cf9a381432444e174942";
1563 };
1564 };
1565 "semver-5.3.0" = {
1566 name = "semver";
1567 packageName = "semver";
1568 version = "5.3.0";
1569 src = fetchurl {
1570 url = "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz";
1571 sha1 = "9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f";
1572 };
1573 };
1574 "create-error-class-3.0.2" = {
1575 name = "create-error-class";
1576 packageName = "create-error-class";
1577 version = "3.0.2";
1578 src = fetchurl {
1938 src = fetchurl {
1579 url = "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz";
1939 url = "https://registry.npmjs.org/ssri/-/ssri-4.1.6.tgz";
1580 sha1 = "06be7abef947a3f14a30fd610671d401bca8b7b6";
1940 sha1 = "0cb49b6ac84457e7bdd466cb730c3cb623e9a25b";
1581 };
1582 };
1583 "duplexer2-0.1.4" = {
1584 name = "duplexer2";
1585 packageName = "duplexer2";
1586 version = "0.1.4";
1587 src = fetchurl {
1588 url = "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz";
1589 sha1 = "8b12dab878c0d69e3e7891051662a32fc6bddcc1";
1590 };
1591 };
1592 "is-plain-obj-1.1.0" = {
1593 name = "is-plain-obj";
1594 packageName = "is-plain-obj";
1595 version = "1.1.0";
1596 src = fetchurl {
1597 url = "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz";
1598 sha1 = "71a50c8429dfca773c92a390a4a03b39fcd51d3e";
1599 };
1600 };
1601 "is-redirect-1.0.0" = {
1602 name = "is-redirect";
1603 packageName = "is-redirect";
1604 version = "1.0.0";
1605 src = fetchurl {
1606 url = "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz";
1607 sha1 = "1d03dded53bd8db0f30c26e4f95d36fc7c87dc24";
1608 };
1609 };
1610 "is-retry-allowed-1.1.0" = {
1611 name = "is-retry-allowed";
1612 packageName = "is-retry-allowed";
1613 version = "1.1.0";
1614 src = fetchurl {
1615 url = "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz";
1616 sha1 = "11a060568b67339444033d0125a61a20d564fb34";
1617 };
1618 };
1619 "is-stream-1.1.0" = {
1620 name = "is-stream";
1621 packageName = "is-stream";
1622 version = "1.1.0";
1623 src = fetchurl {
1624 url = "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz";
1625 sha1 = "12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44";
1626 };
1627 };
1628 "lowercase-keys-1.0.0" = {
1629 name = "lowercase-keys";
1630 packageName = "lowercase-keys";
1631 version = "1.0.0";
1632 src = fetchurl {
1633 url = "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz";
1634 sha1 = "4e3366b39e7f5457e35f1324bdf6f88d0bfc7306";
1635 };
1941 };
1636 };
1942 };
1637 "node-status-codes-1.0.0" = {
1943 "npmlog-4.1.2" = {
1638 name = "node-status-codes";
1944 name = "npmlog";
1639 packageName = "node-status-codes";
1945 packageName = "npmlog";
1640 version = "1.0.0";
1946 version = "4.1.2";
1641 src = fetchurl {
1642 url = "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz";
1643 sha1 = "5ae5541d024645d32a58fcddc9ceecea7ae3ac2f";
1644 };
1645 };
1646 "parse-json-2.2.0" = {
1647 name = "parse-json";
1648 packageName = "parse-json";
1649 version = "2.2.0";
1650 src = fetchurl {
1651 url = "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz";
1652 sha1 = "f480f40434ef80741f8469099f8dea18f55a4dc9";
1653 };
1654 };
1655 "pinkie-promise-2.0.1" = {
1656 name = "pinkie-promise";
1657 packageName = "pinkie-promise";
1658 version = "2.0.1";
1659 src = fetchurl {
1947 src = fetchurl {
1660 url = "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz";
1948 url = "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz";
1661 sha1 = "2135d6dfa7a358c069ac9b178776288228450ffa";
1949 sha1 = "08a7f2a8bf734604779a9efa4ad5cc717abb954b";
1662 };
1663 };
1664 "read-all-stream-3.1.0" = {
1665 name = "read-all-stream";
1666 packageName = "read-all-stream";
1667 version = "3.1.0";
1668 src = fetchurl {
1669 url = "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz";
1670 sha1 = "35c3e177f2078ef789ee4bfafa4373074eaef4fa";
1671 };
1672 };
1673 "readable-stream-2.1.5" = {
1674 name = "readable-stream";
1675 packageName = "readable-stream";
1676 version = "2.1.5";
1677 src = fetchurl {
1678 url = "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz";
1679 sha1 = "66fa8b720e1438b364681f2ad1a63c618448c9d0";
1680 };
1681 };
1682 "timed-out-2.0.0" = {
1683 name = "timed-out";
1684 packageName = "timed-out";
1685 version = "2.0.0";
1686 src = fetchurl {
1687 url = "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz";
1688 sha1 = "f38b0ae81d3747d628001f41dafc652ace671c0a";
1689 };
1950 };
1690 };
1951 };
1691 "unzip-response-1.0.1" = {
1952 "typedarray-0.0.6" = {
1692 name = "unzip-response";
1953 name = "typedarray";
1693 packageName = "unzip-response";
1954 packageName = "typedarray";
1694 version = "1.0.1";
1955 version = "0.0.6";
1695 src = fetchurl {
1956 src = fetchurl {
1696 url = "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.1.tgz";
1957 url = "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz";
1697 sha1 = "4a73959f2989470fa503791cefb54e1dbbc68412";
1958 sha1 = "867ac74e3864187b1d3d47d996a78ec5c8830777";
1698 };
1699 };
1700 "url-parse-lax-1.0.0" = {
1701 name = "url-parse-lax";
1702 packageName = "url-parse-lax";
1703 version = "1.0.0";
1704 src = fetchurl {
1705 url = "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz";
1706 sha1 = "7af8f303645e9bd79a272e7a14ac68bc0609da73";
1707 };
1708 };
1709 "capture-stack-trace-1.0.0" = {
1710 name = "capture-stack-trace";
1711 packageName = "capture-stack-trace";
1712 version = "1.0.0";
1713 src = fetchurl {
1714 url = "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz";
1715 sha1 = "4a6fa07399c26bba47f0b2496b4d0fb408c5550d";
1716 };
1959 };
1717 };
1960 };
1718 "error-ex-1.3.0" = {
1961 "readable-stream-2.3.3" = {
1719 name = "error-ex";
1962 name = "readable-stream";
1720 packageName = "error-ex";
1963 packageName = "readable-stream";
1721 version = "1.3.0";
1964 version = "2.3.3";
1722 src = fetchurl {
1723 url = "https://registry.npmjs.org/error-ex/-/error-ex-1.3.0.tgz";
1724 sha1 = "e67b43f3e82c96ea3a584ffee0b9fc3325d802d9";
1725 };
1726 };
1727 "is-arrayish-0.2.1" = {
1728 name = "is-arrayish";
1729 packageName = "is-arrayish";
1730 version = "0.2.1";
1731 src = fetchurl {
1965 src = fetchurl {
1732 url = "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz";
1966 url = "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz";
1733 sha1 = "77c99840527aa8ecb1a8ba697b80645a7a926a9d";
1967 sha1 = "368f2512d79f9d46fdfc71349ae7878bbc1eb95c";
1734 };
1735 };
1736 "pinkie-2.0.4" = {
1737 name = "pinkie";
1738 packageName = "pinkie";
1739 version = "2.0.4";
1740 src = fetchurl {
1741 url = "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz";
1742 sha1 = "72556b80cfa0d48a974e80e77248e80ed4f7f870";
1743 };
1744 };
1745 "buffer-shims-1.0.0" = {
1746 name = "buffer-shims";
1747 packageName = "buffer-shims";
1748 version = "1.0.0";
1749 src = fetchurl {
1750 url = "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz";
1751 sha1 = "9978ce317388c649ad8793028c3477ef044a8b51";
1752 };
1753 };
1754 "core-util-is-1.0.2" = {
1755 name = "core-util-is";
1756 packageName = "core-util-is";
1757 version = "1.0.2";
1758 src = fetchurl {
1759 url = "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz";
1760 sha1 = "b5fd54220aa2bc5ab57aab7140c940754503c1a7";
1761 };
1968 };
1762 };
1969 };
1763 "isarray-1.0.0" = {
1970 "isarray-1.0.0" = {
@@ -1778,13 +1985,13 b' let'
1778 sha1 = "150e20b756590ad3f91093f25a4f2ad8bff30ba3";
1985 sha1 = "150e20b756590ad3f91093f25a4f2ad8bff30ba3";
1779 };
1986 };
1780 };
1987 };
1781 "string_decoder-0.10.31" = {
1988 "string_decoder-1.0.3" = {
1782 name = "string_decoder";
1989 name = "string_decoder";
1783 packageName = "string_decoder";
1990 packageName = "string_decoder";
1784 version = "0.10.31";
1991 version = "1.0.3";
1785 src = fetchurl {
1992 src = fetchurl {
1786 url = "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz";
1993 url = "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz";
1787 sha1 = "62e203bc41766c6c28c9fc84301dab1c5310fa94";
1994 sha1 = "0fc67d7c141825de94282dd536bec6b9bce860ab";
1788 };
1995 };
1789 };
1996 };
1790 "util-deprecate-1.0.2" = {
1997 "util-deprecate-1.0.2" = {
@@ -1796,22 +2003,247 b' let'
1796 sha1 = "450d4dc9fa70de732762fbd2d4a28981419a0ccf";
2003 sha1 = "450d4dc9fa70de732762fbd2d4a28981419a0ccf";
1797 };
2004 };
1798 };
2005 };
1799 "prepend-http-1.0.4" = {
2006 "hosted-git-info-2.5.0" = {
1800 name = "prepend-http";
2007 name = "hosted-git-info";
1801 packageName = "prepend-http";
2008 packageName = "hosted-git-info";
2009 version = "2.5.0";
2010 src = fetchurl {
2011 url = "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz";
2012 sha1 = "6d60e34b3abbc8313062c3b798ef8d901a07af3c";
2013 };
2014 };
2015 "is-builtin-module-1.0.0" = {
2016 name = "is-builtin-module";
2017 packageName = "is-builtin-module";
2018 version = "1.0.0";
2019 src = fetchurl {
2020 url = "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz";
2021 sha1 = "540572d34f7ac3119f8f76c30cbc1b1e037affbe";
2022 };
2023 };
2024 "validate-npm-package-license-3.0.1" = {
2025 name = "validate-npm-package-license";
2026 packageName = "validate-npm-package-license";
2027 version = "3.0.1";
2028 src = fetchurl {
2029 url = "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz";
2030 sha1 = "2804babe712ad3379459acfbe24746ab2c303fbc";
2031 };
2032 };
2033 "builtin-modules-1.1.1" = {
2034 name = "builtin-modules";
2035 packageName = "builtin-modules";
2036 version = "1.1.1";
2037 src = fetchurl {
2038 url = "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz";
2039 sha1 = "270f076c5a72c02f5b65a47df94c5fe3a278892f";
2040 };
2041 };
2042 "spdx-correct-1.0.2" = {
2043 name = "spdx-correct";
2044 packageName = "spdx-correct";
2045 version = "1.0.2";
2046 src = fetchurl {
2047 url = "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz";
2048 sha1 = "4b3073d933ff51f3912f03ac5519498a4150db40";
2049 };
2050 };
2051 "spdx-expression-parse-1.0.4" = {
2052 name = "spdx-expression-parse";
2053 packageName = "spdx-expression-parse";
1802 version = "1.0.4";
2054 version = "1.0.4";
1803 src = fetchurl {
2055 src = fetchurl {
1804 url = "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz";
2056 url = "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz";
1805 sha1 = "d4f4562b0ce3696e41ac52d0e002e57a635dc6dc";
2057 sha1 = "9bdf2f20e1f40ed447fbe273266191fced51626c";
2058 };
2059 };
2060 "spdx-license-ids-1.2.2" = {
2061 name = "spdx-license-ids";
2062 packageName = "spdx-license-ids";
2063 version = "1.2.2";
2064 src = fetchurl {
2065 url = "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz";
2066 sha1 = "c9df7a3424594ade6bd11900d596696dc06bac57";
2067 };
2068 };
2069 "osenv-0.1.4" = {
2070 name = "osenv";
2071 packageName = "osenv";
2072 version = "0.1.4";
2073 src = fetchurl {
2074 url = "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz";
2075 sha1 = "42fe6d5953df06c8064be6f176c3d05aaaa34644";
2076 };
2077 };
2078 "validate-npm-package-name-3.0.0" = {
2079 name = "validate-npm-package-name";
2080 packageName = "validate-npm-package-name";
2081 version = "3.0.0";
2082 src = fetchurl {
2083 url = "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz";
2084 sha1 = "5fa912d81eb7d0c74afc140de7317f0ca7df437e";
2085 };
2086 };
2087 "os-homedir-1.0.2" = {
2088 name = "os-homedir";
2089 packageName = "os-homedir";
2090 version = "1.0.2";
2091 src = fetchurl {
2092 url = "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz";
2093 sha1 = "ffbc4988336e0e833de0c168c7ef152121aa7fb3";
2094 };
2095 };
2096 "os-tmpdir-1.0.2" = {
2097 name = "os-tmpdir";
2098 packageName = "os-tmpdir";
2099 version = "1.0.2";
2100 src = fetchurl {
2101 url = "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz";
2102 sha1 = "bbe67406c79aa85c5cfec766fe5734555dfa1274";
2103 };
2104 };
2105 "builtins-1.0.3" = {
2106 name = "builtins";
2107 packageName = "builtins";
2108 version = "1.0.3";
2109 src = fetchurl {
2110 url = "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz";
2111 sha1 = "cb94faeb61c8696451db36534e1422f94f0aee88";
2112 };
2113 };
2114 "wrappy-1.0.2" = {
2115 name = "wrappy";
2116 packageName = "wrappy";
2117 version = "1.0.2";
2118 src = fetchurl {
2119 url = "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz";
2120 sha1 = "b5243d8f3ec1aa35f1364605bc0d1036e30ab69f";
2121 };
2122 };
2123 "are-we-there-yet-1.1.4" = {
2124 name = "are-we-there-yet";
2125 packageName = "are-we-there-yet";
2126 version = "1.1.4";
2127 src = fetchurl {
2128 url = "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz";
2129 sha1 = "bb5dca382bb94f05e15194373d16fd3ba1ca110d";
2130 };
2131 };
2132 "console-control-strings-1.1.0" = {
2133 name = "console-control-strings";
2134 packageName = "console-control-strings";
2135 version = "1.1.0";
2136 src = fetchurl {
2137 url = "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz";
2138 sha1 = "3d7cf4464db6446ea644bf4b39507f9851008e8e";
2139 };
2140 };
2141 "gauge-2.7.4" = {
2142 name = "gauge";
2143 packageName = "gauge";
2144 version = "2.7.4";
2145 src = fetchurl {
2146 url = "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz";
2147 sha1 = "2c03405c7538c39d7eb37b317022e325fb018bf7";
1806 };
2148 };
1807 };
2149 };
1808 "rc-1.1.6" = {
2150 "set-blocking-2.0.0" = {
1809 name = "rc";
2151 name = "set-blocking";
1810 packageName = "rc";
2152 packageName = "set-blocking";
1811 version = "1.1.6";
2153 version = "2.0.0";
2154 src = fetchurl {
2155 url = "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz";
2156 sha1 = "045f9782d011ae9a6803ddd382b24392b3d890f7";
2157 };
2158 };
2159 "delegates-1.0.0" = {
2160 name = "delegates";
2161 packageName = "delegates";
2162 version = "1.0.0";
2163 src = fetchurl {
2164 url = "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz";
2165 sha1 = "84c6e159b81904fdca59a0ef44cd870d31250f9a";
2166 };
2167 };
2168 "aproba-1.2.0" = {
2169 name = "aproba";
2170 packageName = "aproba";
2171 version = "1.2.0";
2172 src = fetchurl {
2173 url = "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz";
2174 sha1 = "6802e6264efd18c790a1b0d517f0f2627bf2c94a";
2175 };
2176 };
2177 "has-unicode-2.0.1" = {
2178 name = "has-unicode";
2179 packageName = "has-unicode";
2180 version = "2.0.1";
2181 src = fetchurl {
2182 url = "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz";
2183 sha1 = "e0e6fe6a28cf51138855e086d1691e771de2a8b9";
2184 };
2185 };
2186 "signal-exit-3.0.2" = {
2187 name = "signal-exit";
2188 packageName = "signal-exit";
2189 version = "3.0.2";
1812 src = fetchurl {
2190 src = fetchurl {
1813 url = "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz";
2191 url = "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz";
1814 sha1 = "43651b76b6ae53b5c802f1151fa3fc3b059969c9";
2192 sha1 = "b5fdc08f1287ea1178628e415e25132b73646c6d";
2193 };
2194 };
2195 "string-width-1.0.2" = {
2196 name = "string-width";
2197 packageName = "string-width";
2198 version = "1.0.2";
2199 src = fetchurl {
2200 url = "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz";
2201 sha1 = "118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3";
2202 };
2203 };
2204 "wide-align-1.1.2" = {
2205 name = "wide-align";
2206 packageName = "wide-align";
2207 version = "1.1.2";
2208 src = fetchurl {
2209 url = "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz";
2210 sha1 = "571e0f1b0604636ebc0dfc21b0339bbe31341710";
2211 };
2212 };
2213 "code-point-at-1.1.0" = {
2214 name = "code-point-at";
2215 packageName = "code-point-at";
2216 version = "1.1.0";
2217 src = fetchurl {
2218 url = "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz";
2219 sha1 = "0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77";
2220 };
2221 };
2222 "is-fullwidth-code-point-1.0.0" = {
2223 name = "is-fullwidth-code-point";
2224 packageName = "is-fullwidth-code-point";
2225 version = "1.0.0";
2226 src = fetchurl {
2227 url = "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz";
2228 sha1 = "ef9e31386f031a7f0d643af82fde50c457ef00cb";
2229 };
2230 };
2231 "number-is-nan-1.0.1" = {
2232 name = "number-is-nan";
2233 packageName = "number-is-nan";
2234 version = "1.0.1";
2235 src = fetchurl {
2236 url = "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz";
2237 sha1 = "097b602b53422a522c1afb8790318336941a011d";
2238 };
2239 };
2240 "config-chain-1.1.11" = {
2241 name = "config-chain";
2242 packageName = "config-chain";
2243 version = "1.1.11";
2244 src = fetchurl {
2245 url = "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz";
2246 sha1 = "aba09747dfbe4c3e70e766a6e41586e1859fc6f2";
1815 };
2247 };
1816 };
2248 };
1817 "ini-1.3.4" = {
2249 "ini-1.3.4" = {
@@ -1823,40 +2255,130 b' let'
1823 sha1 = "0537cb79daf59b59a1a517dff706c86ec039162e";
2255 sha1 = "0537cb79daf59b59a1a517dff706c86ec039162e";
1824 };
2256 };
1825 };
2257 };
1826 "minimist-1.2.0" = {
2258 "once-1.3.3" = {
1827 name = "minimist";
2259 name = "once";
1828 packageName = "minimist";
2260 packageName = "once";
1829 version = "1.2.0";
2261 version = "1.3.3";
2262 src = fetchurl {
2263 url = "https://registry.npmjs.org/once/-/once-1.3.3.tgz";
2264 sha1 = "b2e261557ce4c314ec8304f3fa82663e4297ca20";
2265 };
2266 };
2267 "semver-4.3.6" = {
2268 name = "semver";
2269 packageName = "semver";
2270 version = "4.3.6";
2271 src = fetchurl {
2272 url = "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz";
2273 sha1 = "300bc6e0e86374f7ba61068b5b1ecd57fc6532da";
2274 };
2275 };
2276 "uid-number-0.0.5" = {
2277 name = "uid-number";
2278 packageName = "uid-number";
2279 version = "0.0.5";
1830 src = fetchurl {
2280 src = fetchurl {
1831 url = "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz";
2281 url = "https://registry.npmjs.org/uid-number/-/uid-number-0.0.5.tgz";
1832 sha1 = "a35008b20f41383eec1fb914f4cd5df79a264284";
2282 sha1 = "5a3db23ef5dbd55b81fce0ec9a2ac6fccdebb81e";
2283 };
2284 };
2285 "proto-list-1.2.4" = {
2286 name = "proto-list";
2287 packageName = "proto-list";
2288 version = "1.2.4";
2289 src = fetchurl {
2290 url = "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz";
2291 sha1 = "212d5bfe1318306a420f6402b8e26ff39647a849";
2292 };
2293 };
2294 "minipass-2.2.1" = {
2295 name = "minipass";
2296 packageName = "minipass";
2297 version = "2.2.1";
2298 src = fetchurl {
2299 url = "https://registry.npmjs.org/minipass/-/minipass-2.2.1.tgz";
2300 sha1 = "5ada97538b1027b4cf7213432428578cb564011f";
2301 };
2302 };
2303 "minizlib-1.0.3" = {
2304 name = "minizlib";
2305 packageName = "minizlib";
2306 version = "1.0.3";
2307 src = fetchurl {
2308 url = "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz";
2309 sha1 = "d5c1abf77be154619952e253336eccab9b2a32f5";
1833 };
2310 };
1834 };
2311 };
1835 "strip-json-comments-1.0.4" = {
2312 "yallist-3.0.2" = {
1836 name = "strip-json-comments";
2313 name = "yallist";
1837 packageName = "strip-json-comments";
2314 packageName = "yallist";
1838 version = "1.0.4";
2315 version = "3.0.2";
1839 src = fetchurl {
2316 src = fetchurl {
1840 url = "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz";
2317 url = "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz";
1841 sha1 = "1e15fbcac97d3ee99bf2d73b4c656b082bbafb91";
2318 sha1 = "8452b4bb7e83c7c188d8041c1a837c773d6d8bb9";
2319 };
2320 };
2321 "fs-extra-0.6.4" = {
2322 name = "fs-extra";
2323 packageName = "fs-extra";
2324 version = "0.6.4";
2325 src = fetchurl {
2326 url = "https://registry.npmjs.org/fs-extra/-/fs-extra-0.6.4.tgz";
2327 sha1 = "f46f0c75b7841f8d200b3348cd4d691d5a099d15";
2328 };
2329 };
2330 "mkdirp-0.3.5" = {
2331 name = "mkdirp";
2332 packageName = "mkdirp";
2333 version = "0.3.5";
2334 src = fetchurl {
2335 url = "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz";
2336 sha1 = "de3e5f8961c88c787ee1368df849ac4413eca8d7";
1842 };
2337 };
1843 };
2338 };
1844 "crisper-1.2.0" = {
2339 "walk-2.3.9" = {
1845 name = "crisper";
2340 name = "walk";
1846 packageName = "crisper";
2341 packageName = "walk";
1847 version = "1.2.0";
2342 version = "2.3.9";
1848 src = fetchurl {
2343 src = fetchurl {
1849 url = "https://registry.npmjs.org/crisper/-/crisper-1.2.0.tgz";
2344 url = "https://registry.npmjs.org/walk/-/walk-2.3.9.tgz";
1850 sha1 = "9a91f597d71f6110294e076ad44dbb3408568e46";
2345 sha1 = "31b4db6678f2ae01c39ea9fb8725a9031e558a7b";
2346 };
2347 };
2348 "ncp-0.4.2" = {
2349 name = "ncp";
2350 packageName = "ncp";
2351 version = "0.4.2";
2352 src = fetchurl {
2353 url = "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz";
2354 sha1 = "abcc6cbd3ec2ed2a729ff6e7c1fa8f01784a8574";
1851 };
2355 };
1852 };
2356 };
1853 "cli-1.0.0" = {
2357 "jsonfile-1.0.1" = {
2358 name = "jsonfile";
2359 packageName = "jsonfile";
2360 version = "1.0.1";
2361 src = fetchurl {
2362 url = "https://registry.npmjs.org/jsonfile/-/jsonfile-1.0.1.tgz";
2363 sha1 = "ea5efe40b83690b98667614a7392fc60e842c0dd";
2364 };
2365 };
2366 "foreachasync-3.0.0" = {
2367 name = "foreachasync";
2368 packageName = "foreachasync";
2369 version = "3.0.0";
2370 src = fetchurl {
2371 url = "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz";
2372 sha1 = "5502987dc8714be3392097f32e0071c9dee07cf6";
2373 };
2374 };
2375 "cli-1.0.1" = {
1854 name = "cli";
2376 name = "cli";
1855 packageName = "cli";
2377 packageName = "cli";
1856 version = "1.0.0";
2378 version = "1.0.1";
1857 src = fetchurl {
2379 src = fetchurl {
1858 url = "https://registry.npmjs.org/cli/-/cli-1.0.0.tgz";
2380 url = "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz";
1859 sha1 = "ee07dfc1390e3f2e6a9957cf88e1d4bfa777719d";
2381 sha1 = "22817534f24bfa4950c34d532d48ecbc621b8c14";
1860 };
2382 };
1861 };
2383 };
1862 "console-browserify-1.1.0" = {
2384 "console-browserify-1.1.0" = {
@@ -1877,13 +2399,13 b' let'
1877 sha1 = "996c28b191516a8be86501a7d79757e5c70c1068";
2399 sha1 = "996c28b191516a8be86501a7d79757e5c70c1068";
1878 };
2400 };
1879 };
2401 };
1880 "minimatch-3.0.3" = {
2402 "minimatch-3.0.4" = {
1881 name = "minimatch";
2403 name = "minimatch";
1882 packageName = "minimatch";
2404 packageName = "minimatch";
1883 version = "3.0.3";
2405 version = "3.0.4";
1884 src = fetchurl {
2406 src = fetchurl {
1885 url = "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz";
2407 url = "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz";
1886 sha1 = "2a4e4090b96b2db06a9d7df01055a62a77c9b774";
2408 sha1 = "5166e286457f03306064be5497e8dbb0c3d32083";
1887 };
2409 };
1888 };
2410 };
1889 "shelljs-0.3.0" = {
2411 "shelljs-0.3.0" = {
@@ -1895,6 +2417,15 b' let'
1895 sha1 = "3596e6307a781544f591f37da618360f31db57b1";
2417 sha1 = "3596e6307a781544f591f37da618360f31db57b1";
1896 };
2418 };
1897 };
2419 };
2420 "strip-json-comments-1.0.4" = {
2421 name = "strip-json-comments";
2422 packageName = "strip-json-comments";
2423 version = "1.0.4";
2424 src = fetchurl {
2425 url = "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz";
2426 sha1 = "1e15fbcac97d3ee99bf2d73b4c656b082bbafb91";
2427 };
2428 };
1898 "lodash-3.7.0" = {
2429 "lodash-3.7.0" = {
1899 name = "lodash";
2430 name = "lodash";
1900 packageName = "lodash";
2431 packageName = "lodash";
@@ -1904,13 +2435,13 b' let'
1904 sha1 = "3678bd8ab995057c07ade836ed2ef087da811d45";
2435 sha1 = "3678bd8ab995057c07ade836ed2ef087da811d45";
1905 };
2436 };
1906 };
2437 };
1907 "glob-7.1.0" = {
2438 "glob-7.1.2" = {
1908 name = "glob";
2439 name = "glob";
1909 packageName = "glob";
2440 packageName = "glob";
1910 version = "7.1.0";
2441 version = "7.1.2";
1911 src = fetchurl {
2442 src = fetchurl {
1912 url = "https://registry.npmjs.org/glob/-/glob-7.1.0.tgz";
2443 url = "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz";
1913 sha1 = "36add856d746d0d99e4cc2797bba1ae2c67272fd";
2444 sha1 = "c19c9df9a028702d678612384a6552404c636d15";
1914 };
2445 };
1915 };
2446 };
1916 "fs.realpath-1.0.0" = {
2447 "fs.realpath-1.0.0" = {
@@ -1922,49 +2453,31 b' let'
1922 sha1 = "1504ad2523158caa40db4a2787cb01411994ea4f";
2453 sha1 = "1504ad2523158caa40db4a2787cb01411994ea4f";
1923 };
2454 };
1924 };
2455 };
1925 "inflight-1.0.5" = {
2456 "inflight-1.0.6" = {
1926 name = "inflight";
2457 name = "inflight";
1927 packageName = "inflight";
2458 packageName = "inflight";
1928 version = "1.0.5";
2459 version = "1.0.6";
1929 src = fetchurl {
2460 src = fetchurl {
1930 url = "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz";
2461 url = "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz";
1931 sha1 = "db3204cd5a9de2e6cd890b85c6e2f66bcf4f620a";
2462 sha1 = "49bd6331d7d02d0c09bc910a1075ba8165b56df9";
1932 };
1933 };
1934 "once-1.4.0" = {
1935 name = "once";
1936 packageName = "once";
1937 version = "1.4.0";
1938 src = fetchurl {
1939 url = "https://registry.npmjs.org/once/-/once-1.4.0.tgz";
1940 sha1 = "583b1aa775961d4b113ac17d9c50baef9dd76bd1";
1941 };
2463 };
1942 };
2464 };
1943 "wrappy-1.0.2" = {
2465 "brace-expansion-1.1.8" = {
1944 name = "wrappy";
1945 packageName = "wrappy";
1946 version = "1.0.2";
1947 src = fetchurl {
1948 url = "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz";
1949 sha1 = "b5243d8f3ec1aa35f1364605bc0d1036e30ab69f";
1950 };
1951 };
1952 "brace-expansion-1.1.6" = {
1953 name = "brace-expansion";
2466 name = "brace-expansion";
1954 packageName = "brace-expansion";
2467 packageName = "brace-expansion";
1955 version = "1.1.6";
2468 version = "1.1.8";
1956 src = fetchurl {
2469 src = fetchurl {
1957 url = "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz";
2470 url = "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz";
1958 sha1 = "7197d7eaa9b87e648390ea61fc66c84427420df9";
2471 sha1 = "c07b211c7c952ec1f8efd51a77ef0d1d3990a292";
1959 };
2472 };
1960 };
2473 };
1961 "balanced-match-0.4.2" = {
2474 "balanced-match-1.0.0" = {
1962 name = "balanced-match";
2475 name = "balanced-match";
1963 packageName = "balanced-match";
2476 packageName = "balanced-match";
1964 version = "0.4.2";
2477 version = "1.0.0";
1965 src = fetchurl {
2478 src = fetchurl {
1966 url = "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz";
2479 url = "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz";
1967 sha1 = "cb3f3e3c732dc0f01ee70b403f302e61d7709838";
2480 sha1 = "89b4d199ab2bee49de164ea02b89ce462d71b767";
1968 };
2481 };
1969 };
2482 };
1970 "concat-map-0.0.1" = {
2483 "concat-map-0.0.1" = {
@@ -2057,6 +2570,51 b' let'
2057 sha1 = "6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0";
2570 sha1 = "6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0";
2058 };
2571 };
2059 };
2572 };
2573 "string_decoder-0.10.31" = {
2574 name = "string_decoder";
2575 packageName = "string_decoder";
2576 version = "0.10.31";
2577 src = fetchurl {
2578 url = "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz";
2579 sha1 = "62e203bc41766c6c28c9fc84301dab1c5310fa94";
2580 };
2581 };
2582 "good-listener-1.2.2" = {
2583 name = "good-listener";
2584 packageName = "good-listener";
2585 version = "1.2.2";
2586 src = fetchurl {
2587 url = "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz";
2588 sha1 = "d53b30cdf9313dffb7dc9a0d477096aa6d145c50";
2589 };
2590 };
2591 "select-1.1.2" = {
2592 name = "select";
2593 packageName = "select";
2594 version = "1.1.2";
2595 src = fetchurl {
2596 url = "https://registry.npmjs.org/select/-/select-1.1.2.tgz";
2597 sha1 = "0e7350acdec80b1108528786ec1d4418d11b396d";
2598 };
2599 };
2600 "tiny-emitter-2.0.2" = {
2601 name = "tiny-emitter";
2602 packageName = "tiny-emitter";
2603 version = "2.0.2";
2604 src = fetchurl {
2605 url = "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz";
2606 sha1 = "82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c";
2607 };
2608 };
2609 "delegate-3.1.3" = {
2610 name = "delegate";
2611 packageName = "delegate";
2612 version = "3.1.3";
2613 src = fetchurl {
2614 url = "https://registry.npmjs.org/delegate/-/delegate-3.1.3.tgz";
2615 sha1 = "9a8251a777d7025faa55737bc3b071742127a9fd";
2616 };
2617 };
2060 };
2618 };
2061 args = {
2619 args = {
2062 name = "rhodecode-enterprise";
2620 name = "rhodecode-enterprise";
@@ -2077,10 +2635,10 b' let'
2077 ];
2635 ];
2078 })
2636 })
2079 sources."grunt-contrib-jshint-0.12.0"
2637 sources."grunt-contrib-jshint-0.12.0"
2080 (sources."grunt-contrib-less-1.4.0" // {
2638 (sources."grunt-contrib-less-1.4.1" // {
2081 dependencies = [
2639 dependencies = [
2082 sources."async-2.0.1"
2640 sources."async-2.5.0"
2083 sources."lodash-4.16.2"
2641 sources."lodash-4.17.4"
2084 ];
2642 ];
2085 })
2643 })
2086 (sources."grunt-contrib-watch-0.6.1" // {
2644 (sources."grunt-contrib-watch-0.6.1" // {
@@ -2089,8 +2647,8 b' let'
2089 sources."async-0.2.10"
2647 sources."async-0.2.10"
2090 ];
2648 ];
2091 })
2649 })
2092 sources."crisper-2.0.2"
2650 sources."crisper-2.1.1"
2093 (sources."vulcanize-1.14.8" // {
2651 (sources."vulcanize-1.16.0" // {
2094 dependencies = [
2652 dependencies = [
2095 sources."nopt-3.0.6"
2653 sources."nopt-3.0.6"
2096 ];
2654 ];
@@ -2102,15 +2660,20 b' let'
2102 sources."nopt-3.0.6"
2660 sources."nopt-3.0.6"
2103 ];
2661 ];
2104 })
2662 })
2105 (sources."jshint-2.9.3" // {
2663 sources."node2nix-1.3.0"
2664 (sources."jshint-2.9.5" // {
2106 dependencies = [
2665 dependencies = [
2107 sources."minimatch-3.0.3"
2666 sources."minimatch-3.0.4"
2108 sources."lodash-3.7.0"
2667 sources."lodash-3.7.0"
2109 ];
2668 ];
2110 })
2669 })
2111 sources."bower-1.7.9"
2670 sources."bower-1.8.2"
2671 sources."jquery-1.11.3"
2112 sources."favico.js-0.3.10"
2672 sources."favico.js-0.3.10"
2113 sources."appenlight-client-git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.0"
2673 sources."clipboard-1.7.1"
2674 sources."moment-2.18.1"
2675 sources."mousetrap-1.6.1"
2676 sources."appenlight-client-git+https://git@github.com/AppEnlight/appenlight-client-js.git#0.5.1"
2114 sources."async-0.1.22"
2677 sources."async-0.1.22"
2115 sources."coffee-script-1.3.3"
2678 sources."coffee-script-1.3.3"
2116 sources."colors-0.6.2"
2679 sources."colors-0.6.2"
@@ -2150,7 +2713,7 b' let'
2150 sources."lru-cache-2.7.3"
2713 sources."lru-cache-2.7.3"
2151 sources."sigmund-1.0.1"
2714 sources."sigmund-1.0.1"
2152 sources."graceful-fs-1.2.3"
2715 sources."graceful-fs-1.2.3"
2153 sources."abbrev-1.0.9"
2716 sources."abbrev-1.1.0"
2154 (sources."argparse-0.1.16" // {
2717 (sources."argparse-0.1.16" // {
2155 dependencies = [
2718 dependencies = [
2156 sources."underscore.string-2.4.0"
2719 sources."underscore.string-2.4.0"
@@ -2171,31 +2734,90 b' let'
2171 sources."has-ansi-2.0.0"
2734 sources."has-ansi-2.0.0"
2172 sources."strip-ansi-3.0.1"
2735 sources."strip-ansi-3.0.1"
2173 sources."supports-color-2.0.0"
2736 sources."supports-color-2.0.0"
2174 sources."ansi-regex-2.0.0"
2737 sources."ansi-regex-2.1.1"
2175 sources."source-map-0.3.0"
2738 sources."source-map-0.3.0"
2176 sources."amdefine-1.0.0"
2739 sources."amdefine-1.0.1"
2177 (sources."less-2.7.1" // {
2740 (sources."less-2.7.2" // {
2178 dependencies = [
2741 dependencies = [
2179 sources."graceful-fs-4.1.8"
2742 sources."graceful-fs-4.1.11"
2180 sources."source-map-0.5.6"
2743 sources."source-map-0.5.7"
2181 ];
2744 ];
2182 })
2745 })
2183 sources."errno-0.1.4"
2746 sources."errno-0.1.4"
2184 sources."image-size-0.5.0"
2747 sources."image-size-0.5.5"
2185 sources."mime-1.3.4"
2748 sources."mime-1.4.0"
2186 sources."mkdirp-0.5.1"
2749 sources."mkdirp-0.5.1"
2187 sources."promise-7.1.1"
2750 sources."promise-7.3.1"
2751 sources."request-2.82.0"
2188 sources."prr-0.0.0"
2752 sources."prr-0.0.0"
2189 sources."minimist-0.0.8"
2753 sources."minimist-0.0.8"
2190 sources."asap-2.0.5"
2754 sources."asap-2.0.6"
2755 sources."aws-sign2-0.7.0"
2756 sources."aws4-1.6.0"
2757 sources."caseless-0.12.0"
2758 sources."combined-stream-1.0.5"
2759 sources."extend-3.0.1"
2760 sources."forever-agent-0.6.1"
2761 sources."form-data-2.3.1"
2762 sources."har-validator-5.0.3"
2763 sources."hawk-6.0.2"
2764 sources."http-signature-1.2.0"
2765 sources."is-typedarray-1.0.0"
2766 sources."isstream-0.1.2"
2767 sources."json-stringify-safe-5.0.1"
2768 sources."mime-types-2.1.17"
2769 sources."oauth-sign-0.8.2"
2770 sources."performance-now-2.1.0"
2771 sources."qs-6.5.1"
2772 sources."safe-buffer-5.1.1"
2773 sources."stringstream-0.0.5"
2774 sources."tough-cookie-2.3.3"
2775 sources."tunnel-agent-0.6.0"
2776 sources."uuid-3.1.0"
2777 sources."delayed-stream-1.0.0"
2778 sources."asynckit-0.4.0"
2779 sources."ajv-5.2.2"
2780 sources."har-schema-2.0.0"
2781 sources."co-4.6.0"
2782 sources."fast-deep-equal-1.0.0"
2783 sources."json-schema-traverse-0.3.1"
2784 sources."json-stable-stringify-1.0.1"
2785 sources."jsonify-0.0.0"
2786 sources."hoek-4.2.0"
2787 sources."boom-4.3.1"
2788 (sources."cryptiles-3.1.2" // {
2789 dependencies = [
2790 sources."boom-5.2.0"
2791 ];
2792 })
2793 sources."sntp-2.0.2"
2794 sources."assert-plus-1.0.0"
2795 sources."jsprim-1.4.1"
2796 sources."sshpk-1.13.1"
2797 sources."extsprintf-1.3.0"
2798 sources."json-schema-0.2.3"
2799 sources."verror-1.10.0"
2800 sources."core-util-is-1.0.2"
2801 sources."asn1-0.2.3"
2802 sources."dashdash-1.14.1"
2803 sources."getpass-0.1.7"
2804 sources."jsbn-0.1.1"
2805 sources."tweetnacl-0.14.5"
2806 sources."ecc-jsbn-0.1.1"
2807 sources."bcrypt-pbkdf-1.0.1"
2808 sources."mime-db-1.30.0"
2809 sources."punycode-1.4.1"
2191 sources."gaze-0.5.2"
2810 sources."gaze-0.5.2"
2192 sources."tiny-lr-fork-0.0.5"
2811 (sources."tiny-lr-fork-0.0.5" // {
2812 dependencies = [
2813 sources."qs-0.5.6"
2814 ];
2815 })
2193 (sources."globule-0.1.0" // {
2816 (sources."globule-0.1.0" // {
2194 dependencies = [
2817 dependencies = [
2195 sources."lodash-1.0.2"
2818 sources."lodash-1.0.2"
2196 ];
2819 ];
2197 })
2820 })
2198 sources."qs-0.5.6"
2199 sources."faye-websocket-0.4.4"
2821 sources."faye-websocket-0.4.4"
2200 (sources."noptify-0.0.3" // {
2822 (sources."noptify-0.0.3" // {
2201 dependencies = [
2823 dependencies = [
@@ -2203,62 +2825,60 b' let'
2203 ];
2825 ];
2204 })
2826 })
2205 sources."debug-0.7.4"
2827 sources."debug-0.7.4"
2206 sources."command-line-args-2.1.6"
2828 sources."command-line-args-3.0.5"
2829 sources."command-line-usage-3.0.8"
2207 sources."dom5-1.3.6"
2830 sources."dom5-1.3.6"
2208 sources."array-back-1.0.3"
2831 sources."array-back-1.0.4"
2209 sources."command-line-usage-2.0.5"
2210 sources."core-js-2.4.1"
2211 sources."feature-detect-es6-1.3.1"
2832 sources."feature-detect-es6-1.3.1"
2212 (sources."find-replace-1.0.2" // {
2833 sources."find-replace-1.0.3"
2213 dependencies = [
2834 sources."typical-2.6.1"
2214 sources."test-value-2.1.0"
2835 sources."test-value-2.1.0"
2215 ];
2836 sources."ansi-escape-sequences-3.0.0"
2216 })
2837 sources."table-layout-0.3.0"
2217 sources."typical-2.6.0"
2838 sources."core-js-2.5.1"
2218 sources."ansi-escape-sequences-2.2.2"
2839 sources."deep-extend-0.4.2"
2219 sources."column-layout-2.1.4"
2840 sources."wordwrapjs-2.0.0"
2220 sources."wordwrapjs-1.2.1"
2841 sources."reduce-flatten-1.0.1"
2221 sources."collect-all-0.2.1"
2222 sources."stream-connect-1.0.2"
2223 sources."stream-via-0.1.1"
2224 (sources."collect-json-1.0.8" // {
2225 dependencies = [
2226 sources."collect-all-1.0.2"
2227 sources."stream-via-1.0.3"
2228 ];
2229 })
2230 sources."deep-extend-0.4.1"
2231 sources."object-tools-2.0.6"
2232 sources."object-get-2.1.0"
2233 sources."test-value-1.1.0"
2234 sources."@types/clone-0.1.30"
2842 sources."@types/clone-0.1.30"
2235 sources."@types/node-4.0.30"
2843 sources."@types/node-4.2.20"
2236 (sources."@types/parse5-0.0.31" // {
2844 (sources."@types/parse5-0.0.31" // {
2237 dependencies = [
2845 dependencies = [
2238 sources."@types/node-6.0.41"
2846 sources."@types/node-6.0.88"
2239 ];
2847 ];
2240 })
2848 })
2241 sources."clone-1.0.2"
2849 sources."clone-1.0.2"
2242 sources."parse5-1.5.1"
2850 sources."parse5-1.5.1"
2243 sources."es6-promise-2.3.0"
2851 sources."es6-promise-2.3.0"
2244 sources."hydrolysis-1.24.1"
2852 (sources."hydrolysis-1.25.0" // {
2245 sources."path-posix-1.0.0"
2246 sources."update-notifier-0.6.3"
2247 sources."babel-polyfill-6.13.0"
2248 sources."doctrine-0.7.2"
2249 (sources."escodegen-1.8.1" // {
2250 dependencies = [
2853 dependencies = [
2251 sources."estraverse-1.9.3"
2854 sources."dom5-1.1.0"
2252 sources."esutils-2.0.2"
2253 sources."esprima-2.7.3"
2254 sources."source-map-0.2.0"
2255 ];
2855 ];
2256 })
2856 })
2257 sources."espree-3.3.1"
2857 sources."path-posix-1.0.0"
2858 sources."acorn-3.3.0"
2859 sources."babel-polyfill-6.26.0"
2860 sources."doctrine-0.7.2"
2861 (sources."escodegen-1.9.0" // {
2862 dependencies = [
2863 sources."estraverse-4.2.0"
2864 sources."esutils-2.0.2"
2865 sources."esprima-3.1.3"
2866 sources."source-map-0.5.7"
2867 ];
2868 })
2869 (sources."espree-3.5.1" // {
2870 dependencies = [
2871 sources."acorn-5.1.2"
2872 ];
2873 })
2258 sources."estraverse-3.1.0"
2874 sources."estraverse-3.1.0"
2259 sources."path-is-absolute-1.0.0"
2875 sources."path-is-absolute-1.0.1"
2260 sources."babel-runtime-6.11.6"
2876 (sources."babel-runtime-6.26.0" // {
2261 sources."regenerator-runtime-0.9.5"
2877 dependencies = [
2878 sources."regenerator-runtime-0.11.0"
2879 ];
2880 })
2881 sources."regenerator-runtime-0.10.5"
2262 sources."esutils-1.1.6"
2882 sources."esutils-1.1.6"
2263 sources."isarray-0.0.1"
2883 sources."isarray-0.0.1"
2264 sources."optionator-0.8.2"
2884 sources."optionator-0.8.2"
@@ -2267,105 +2887,112 b' let'
2267 sources."wordwrap-1.0.0"
2887 sources."wordwrap-1.0.0"
2268 sources."type-check-0.3.2"
2888 sources."type-check-0.3.2"
2269 sources."levn-0.3.0"
2889 sources."levn-0.3.0"
2270 sources."fast-levenshtein-2.0.4"
2890 sources."fast-levenshtein-2.0.6"
2271 sources."acorn-4.0.3"
2891 sources."acorn-jsx-3.0.1"
2272 (sources."acorn-jsx-3.0.1" // {
2892 sources."object-assign-4.1.1"
2893 sources."optparse-1.0.5"
2894 sources."semver-5.4.1"
2895 (sources."npm-registry-client-8.4.0" // {
2273 dependencies = [
2896 dependencies = [
2274 sources."acorn-3.3.0"
2897 sources."graceful-fs-4.1.11"
2275 ];
2898 ];
2276 })
2899 })
2277 sources."boxen-0.3.1"
2900 (sources."npmconf-2.1.2" // {
2278 (sources."configstore-2.1.0" // {
2279 dependencies = [
2901 dependencies = [
2280 sources."graceful-fs-4.1.8"
2902 sources."nopt-3.0.6"
2903 sources."once-1.3.3"
2904 sources."semver-4.3.6"
2281 ];
2905 ];
2282 })
2906 })
2283 sources."is-npm-1.0.0"
2907 sources."tar-3.1.15"
2284 sources."latest-version-2.0.0"
2908 sources."temp-0.8.3"
2285 sources."semver-diff-2.1.0"
2909 (sources."fs.extra-1.3.2" // {
2286 sources."filled-array-1.1.0"
2287 sources."object-assign-4.1.0"
2288 sources."repeating-2.0.1"
2289 sources."string-width-1.0.2"
2290 sources."widest-line-1.0.0"
2291 sources."is-finite-1.0.1"
2292 sources."number-is-nan-1.0.0"
2293 sources."code-point-at-1.0.0"
2294 sources."is-fullwidth-code-point-1.0.0"
2295 sources."dot-prop-3.0.0"
2296 sources."os-tmpdir-1.0.1"
2297 sources."osenv-0.1.3"
2298 sources."uuid-2.0.3"
2299 (sources."write-file-atomic-1.2.0" // {
2300 dependencies = [
2910 dependencies = [
2301 sources."graceful-fs-4.1.8"
2911 sources."mkdirp-0.3.5"
2302 ];
2912 ];
2303 })
2913 })
2304 sources."xdg-basedir-2.0.0"
2914 sources."findit-2.0.0"
2305 sources."is-obj-1.0.1"
2915 sources."base64-js-1.2.1"
2306 sources."os-homedir-1.0.1"
2916 sources."slasp-0.0.4"
2307 sources."imurmurhash-0.1.4"
2917 sources."nijs-0.0.23"
2918 sources."concat-stream-1.6.0"
2919 sources."normalize-package-data-2.4.0"
2920 sources."npm-package-arg-5.1.2"
2921 sources."once-1.4.0"
2922 sources."retry-0.10.1"
2308 sources."slide-1.1.6"
2923 sources."slide-1.1.6"
2309 sources."package-json-2.4.0"
2924 sources."ssri-4.1.6"
2310 sources."got-5.6.0"
2925 sources."npmlog-4.1.2"
2311 sources."registry-auth-token-3.0.1"
2926 sources."typedarray-0.0.6"
2312 sources."registry-url-3.1.0"
2927 (sources."readable-stream-2.3.3" // {
2313 sources."semver-5.3.0"
2314 sources."create-error-class-3.0.2"
2315 sources."duplexer2-0.1.4"
2316 sources."is-plain-obj-1.1.0"
2317 sources."is-redirect-1.0.0"
2318 sources."is-retry-allowed-1.1.0"
2319 sources."is-stream-1.1.0"
2320 sources."lowercase-keys-1.0.0"
2321 sources."node-status-codes-1.0.0"
2322 sources."parse-json-2.2.0"
2323 sources."pinkie-promise-2.0.1"
2324 sources."read-all-stream-3.1.0"
2325 (sources."readable-stream-2.1.5" // {
2326 dependencies = [
2928 dependencies = [
2327 sources."isarray-1.0.0"
2929 sources."isarray-1.0.0"
2328 ];
2930 ];
2329 })
2931 })
2330 sources."timed-out-2.0.0"
2331 sources."unzip-response-1.0.1"
2332 sources."url-parse-lax-1.0.0"
2333 sources."capture-stack-trace-1.0.0"
2334 sources."error-ex-1.3.0"
2335 sources."is-arrayish-0.2.1"
2336 sources."pinkie-2.0.4"
2337 sources."buffer-shims-1.0.0"
2338 sources."core-util-is-1.0.2"
2339 sources."process-nextick-args-1.0.7"
2932 sources."process-nextick-args-1.0.7"
2340 sources."string_decoder-0.10.31"
2933 sources."string_decoder-1.0.3"
2341 sources."util-deprecate-1.0.2"
2934 sources."util-deprecate-1.0.2"
2342 sources."prepend-http-1.0.4"
2935 sources."hosted-git-info-2.5.0"
2343 (sources."rc-1.1.6" // {
2936 sources."is-builtin-module-1.0.0"
2937 sources."validate-npm-package-license-3.0.1"
2938 sources."builtin-modules-1.1.1"
2939 sources."spdx-correct-1.0.2"
2940 sources."spdx-expression-parse-1.0.4"
2941 sources."spdx-license-ids-1.2.2"
2942 sources."osenv-0.1.4"
2943 sources."validate-npm-package-name-3.0.0"
2944 sources."os-homedir-1.0.2"
2945 sources."os-tmpdir-1.0.2"
2946 sources."builtins-1.0.3"
2947 sources."wrappy-1.0.2"
2948 sources."are-we-there-yet-1.1.4"
2949 sources."console-control-strings-1.1.0"
2950 sources."gauge-2.7.4"
2951 sources."set-blocking-2.0.0"
2952 sources."delegates-1.0.0"
2953 sources."aproba-1.2.0"
2954 sources."has-unicode-2.0.1"
2955 sources."signal-exit-3.0.2"
2956 sources."string-width-1.0.2"
2957 sources."wide-align-1.1.2"
2958 sources."code-point-at-1.1.0"
2959 sources."is-fullwidth-code-point-1.0.0"
2960 sources."number-is-nan-1.0.1"
2961 sources."config-chain-1.1.11"
2962 sources."ini-1.3.4"
2963 sources."uid-number-0.0.5"
2964 sources."proto-list-1.2.4"
2965 sources."minipass-2.2.1"
2966 sources."minizlib-1.0.3"
2967 sources."yallist-3.0.2"
2968 (sources."fs-extra-0.6.4" // {
2344 dependencies = [
2969 dependencies = [
2345 sources."minimist-1.2.0"
2970 sources."mkdirp-0.3.5"
2346 ];
2971 ];
2347 })
2972 })
2348 sources."ini-1.3.4"
2973 sources."walk-2.3.9"
2349 sources."strip-json-comments-1.0.4"
2974 sources."ncp-0.4.2"
2350 (sources."cli-1.0.0" // {
2975 sources."jsonfile-1.0.1"
2976 sources."foreachasync-3.0.0"
2977 (sources."cli-1.0.1" // {
2351 dependencies = [
2978 dependencies = [
2352 sources."glob-7.1.0"
2979 sources."glob-7.1.2"
2353 sources."minimatch-3.0.3"
2980 sources."minimatch-3.0.4"
2354 ];
2981 ];
2355 })
2982 })
2356 sources."console-browserify-1.1.0"
2983 sources."console-browserify-1.1.0"
2357 (sources."htmlparser2-3.8.3" // {
2984 (sources."htmlparser2-3.8.3" // {
2358 dependencies = [
2985 dependencies = [
2359 sources."readable-stream-1.1.14"
2986 sources."readable-stream-1.1.14"
2987 sources."string_decoder-0.10.31"
2360 ];
2988 ];
2361 })
2989 })
2362 sources."shelljs-0.3.0"
2990 sources."shelljs-0.3.0"
2991 sources."strip-json-comments-1.0.4"
2363 sources."fs.realpath-1.0.0"
2992 sources."fs.realpath-1.0.0"
2364 sources."inflight-1.0.5"
2993 sources."inflight-1.0.6"
2365 sources."once-1.4.0"
2994 sources."brace-expansion-1.1.8"
2366 sources."wrappy-1.0.2"
2995 sources."balanced-match-1.0.0"
2367 sources."brace-expansion-1.1.6"
2368 sources."balanced-match-0.4.2"
2369 sources."concat-map-0.0.1"
2996 sources."concat-map-0.0.1"
2370 sources."date-now-0.1.4"
2997 sources."date-now-0.1.4"
2371 sources."domhandler-2.3.0"
2998 sources."domhandler-2.3.0"
@@ -2378,6 +3005,10 b' let'
2378 sources."entities-1.1.1"
3005 sources."entities-1.1.1"
2379 ];
3006 ];
2380 })
3007 })
3008 sources."good-listener-1.2.2"
3009 sources."select-1.1.2"
3010 sources."tiny-emitter-2.0.2"
3011 sources."delegate-3.1.3"
2381 ];
3012 ];
2382 meta = {
3013 meta = {
2383 };
3014 };
@@ -16,26 +16,26 b''
16 };
16 };
17 };
17 };
18 Beaker = super.buildPythonPackage {
18 Beaker = super.buildPythonPackage {
19 name = "Beaker-1.7.0";
19 name = "Beaker-1.9.0";
20 buildInputs = with self; [];
20 buildInputs = with self; [];
21 doCheck = false;
21 doCheck = false;
22 propagatedBuildInputs = with self; [];
22 propagatedBuildInputs = with self; [funcsigs];
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/93/b2/12de6937b06e9615dbb3cb3a1c9af17f133f435bdef59f4ad42032b6eb49/Beaker-1.9.0.tar.gz";
25 md5 = "386be3f7fe427358881eee4622b428b3";
25 md5 = "38b3fcdfa24faf97c6cf66991eb54e9c";
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.7";
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/df/50/d8c1ada7d537c64b0f76453fa31dedb6af6e27b82fcf0331e5f71a4cf98b/CProfileV-1.0.7.tar.gz";
38 md5 = "08c7c242b6e64237bc53c5d13537e03d";
38 md5 = "db4c7640438aa3d8887e194c81c7a019";
39 };
39 };
40 meta = {
40 meta = {
41 license = [ pkgs.lib.licenses.mit ];
41 license = [ pkgs.lib.licenses.mit ];
@@ -81,26 +81,26 b''
81 };
81 };
82 };
82 };
83 Mako = super.buildPythonPackage {
83 Mako = super.buildPythonPackage {
84 name = "Mako-1.0.6";
84 name = "Mako-1.0.7";
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/eb/f3/67579bb486517c0d49547f9697e36582cd19dafb5df9e687ed8e22de57fa/Mako-1.0.7.tar.gz";
90 md5 = "a28e22a339080316b2acc352b9ee631c";
90 md5 = "5836cc997b1b773ef389bf6629c30e65";
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.8";
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/1d/25/3f6d2cb31ec42ca5bd3bfbea99b63892b735d76e26f20dd2dcc34ffe4f0d/Markdown-2.6.8.tar.gz";
103 md5 = "632710a7474bbb74a82084392251061f";
103 md5 = "d9ef057a5bd185f6f536400a31fc5d45";
104 };
104 };
105 meta = {
105 meta = {
106 license = [ pkgs.lib.licenses.bsdOriginal ];
106 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -211,13 +211,13 b''
211 };
211 };
212 };
212 };
213 SQLAlchemy = super.buildPythonPackage {
213 SQLAlchemy = super.buildPythonPackage {
214 name = "SQLAlchemy-0.9.9";
214 name = "SQLAlchemy-1.1.11";
215 buildInputs = with self; [];
215 buildInputs = with self; [];
216 doCheck = false;
216 doCheck = false;
217 propagatedBuildInputs = with self; [];
217 propagatedBuildInputs = with self; [];
218 src = fetchurl {
218 src = fetchurl {
219 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
219 url = "https://pypi.python.org/packages/59/f1/28f2205c3175e6bf32300c0f30f9d91dbc9eb910debbff3ffecb88d18528/SQLAlchemy-1.1.11.tar.gz";
220 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
220 md5 = "3de387eddb4012083a4562928c511e43";
221 };
221 };
222 meta = {
222 meta = {
223 license = [ pkgs.lib.licenses.mit ];
223 license = [ pkgs.lib.licenses.mit ];
@@ -302,26 +302,26 b''
302 };
302 };
303 };
303 };
304 WebOb = super.buildPythonPackage {
304 WebOb = super.buildPythonPackage {
305 name = "WebOb-1.3.1";
305 name = "WebOb-1.7.3";
306 buildInputs = with self; [];
306 buildInputs = with self; [];
307 doCheck = false;
307 doCheck = false;
308 propagatedBuildInputs = with self; [];
308 propagatedBuildInputs = with self; [];
309 src = fetchurl {
309 src = fetchurl {
310 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
310 url = "https://pypi.python.org/packages/46/87/2f96d8d43b2078fae6e1d33fa86b95c228cebed060f4e3c7576cc44ea83b/WebOb-1.7.3.tar.gz";
311 md5 = "20918251c5726956ba8fef22d1556177";
311 md5 = "350028baffc508e3d23c078118e35316";
312 };
312 };
313 meta = {
313 meta = {
314 license = [ pkgs.lib.licenses.mit ];
314 license = [ pkgs.lib.licenses.mit ];
315 };
315 };
316 };
316 };
317 WebTest = super.buildPythonPackage {
317 WebTest = super.buildPythonPackage {
318 name = "WebTest-1.4.3";
318 name = "WebTest-2.0.27";
319 buildInputs = with self; [];
319 buildInputs = with self; [];
320 doCheck = false;
320 doCheck = false;
321 propagatedBuildInputs = with self; [WebOb];
321 propagatedBuildInputs = with self; [six WebOb waitress beautifulsoup4];
322 src = fetchurl {
322 src = fetchurl {
323 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
323 url = "https://pypi.python.org/packages/80/fa/ca3a759985c72e3a124cbca3e1f8a2e931a07ffd31fd45d8f7bf21cb95cf/WebTest-2.0.27.tar.gz";
324 md5 = "631ce728bed92c681a4020a36adbc353";
324 md5 = "54e6515ac71c51b6fc90179483c749ad";
325 };
325 };
326 meta = {
326 meta = {
327 license = [ pkgs.lib.licenses.mit ];
327 license = [ pkgs.lib.licenses.mit ];
@@ -341,13 +341,13 b''
341 };
341 };
342 };
342 };
343 alembic = super.buildPythonPackage {
343 alembic = super.buildPythonPackage {
344 name = "alembic-0.8.4";
344 name = "alembic-0.9.2";
345 buildInputs = with self; [];
345 buildInputs = with self; [];
346 doCheck = false;
346 doCheck = false;
347 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
347 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor python-dateutil];
348 src = fetchurl {
348 src = fetchurl {
349 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
349 url = "https://pypi.python.org/packages/78/48/b5b26e7218b415f40b60b92c53853d242e5456c0f19f6c66101d98ff5f2a/alembic-0.9.2.tar.gz";
350 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
350 md5 = "40daf8bae50969beea40efaaf0839ff4";
351 };
351 };
352 meta = {
352 meta = {
353 license = [ pkgs.lib.licenses.mit ];
353 license = [ pkgs.lib.licenses.mit ];
@@ -380,16 +380,16 b''
380 };
380 };
381 };
381 };
382 appenlight-client = super.buildPythonPackage {
382 appenlight-client = super.buildPythonPackage {
383 name = "appenlight-client-0.6.14";
383 name = "appenlight-client-0.6.21";
384 buildInputs = with self; [];
384 buildInputs = with self; [];
385 doCheck = false;
385 doCheck = false;
386 propagatedBuildInputs = with self; [WebOb requests];
386 propagatedBuildInputs = with self; [WebOb requests six];
387 src = fetchurl {
387 src = fetchurl {
388 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
388 url = "https://pypi.python.org/packages/c9/23/91b66cfa0b963662c10b2a06ccaadf3f3a4848a7a2aa16255cb43d5160ec/appenlight_client-0.6.21.tar.gz";
389 md5 = "578c69b09f4356d898fff1199b98a95c";
389 md5 = "273999ac854fdaefa8d0fb61965a4ed9";
390 };
390 };
391 meta = {
391 meta = {
392 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "DFSG approved"; } ];
392 license = [ pkgs.lib.licenses.bsdOriginal ];
393 };
393 };
394 };
394 };
395 authomatic = super.buildPythonPackage {
395 authomatic = super.buildPythonPackage {
@@ -405,19 +405,6 b''
405 license = [ pkgs.lib.licenses.mit ];
405 license = [ pkgs.lib.licenses.mit ];
406 };
406 };
407 };
407 };
408 backport-ipaddress = super.buildPythonPackage {
409 name = "backport-ipaddress-0.1";
410 buildInputs = with self; [];
411 doCheck = false;
412 propagatedBuildInputs = with self; [];
413 src = fetchurl {
414 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
415 md5 = "9c1f45f4361f71b124d7293a60006c05";
416 };
417 meta = {
418 license = [ pkgs.lib.licenses.psfl ];
419 };
420 };
421 backports.shutil-get-terminal-size = super.buildPythonPackage {
408 backports.shutil-get-terminal-size = super.buildPythonPackage {
422 name = "backports.shutil-get-terminal-size-1.0.0";
409 name = "backports.shutil-get-terminal-size-1.0.0";
423 buildInputs = with self; [];
410 buildInputs = with self; [];
@@ -431,6 +418,19 b''
431 license = [ pkgs.lib.licenses.mit ];
418 license = [ pkgs.lib.licenses.mit ];
432 };
419 };
433 };
420 };
421 beautifulsoup4 = super.buildPythonPackage {
422 name = "beautifulsoup4-4.6.0";
423 buildInputs = with self; [];
424 doCheck = false;
425 propagatedBuildInputs = with self; [];
426 src = fetchurl {
427 url = "https://pypi.python.org/packages/fa/8d/1d14391fdaed5abada4e0f63543fef49b8331a34ca60c88bd521bcf7f782/beautifulsoup4-4.6.0.tar.gz";
428 md5 = "c17714d0f91a23b708a592cb3c697728";
429 };
430 meta = {
431 license = [ pkgs.lib.licenses.mit ];
432 };
433 };
434 bleach = super.buildPythonPackage {
434 bleach = super.buildPythonPackage {
435 name = "bleach-1.5.0";
435 name = "bleach-1.5.0";
436 buildInputs = with self; [];
436 buildInputs = with self; [];
@@ -510,13 +510,13 b''
510 };
510 };
511 };
511 };
512 colander = super.buildPythonPackage {
512 colander = super.buildPythonPackage {
513 name = "colander-1.2";
513 name = "colander-1.3.3";
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/54/a9/9862a561e015b2c7b56404c0b13828a8bdc51e05ab3703bd792cec064487/colander-1.3.3.tar.gz";
519 md5 = "83db21b07936a0726e588dae1914b9ed";
519 md5 = "f5d783768c51d73695f49bbe95778ab4";
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)"; } ];
@@ -614,26 +614,26 b''
614 };
614 };
615 };
615 };
616 docutils = super.buildPythonPackage {
616 docutils = super.buildPythonPackage {
617 name = "docutils-0.12";
617 name = "docutils-0.13.1";
618 buildInputs = with self; [];
618 buildInputs = with self; [];
619 doCheck = false;
619 doCheck = false;
620 propagatedBuildInputs = with self; [];
620 propagatedBuildInputs = with self; [];
621 src = fetchurl {
621 src = fetchurl {
622 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
622 url = "https://pypi.python.org/packages/05/25/7b5484aca5d46915493f1fd4ecb63c38c333bd32aa9ad6e19da8d08895ae/docutils-0.13.1.tar.gz";
623 md5 = "4622263b62c5c771c03502afa3157768";
623 md5 = "ea4a893c633c788be9b8078b6b305d53";
624 };
624 };
625 meta = {
625 meta = {
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 ];
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 ];
627 };
627 };
628 };
628 };
629 dogpile.cache = super.buildPythonPackage {
629 dogpile.cache = super.buildPythonPackage {
630 name = "dogpile.cache-0.6.1";
630 name = "dogpile.cache-0.6.4";
631 buildInputs = with self; [];
631 buildInputs = with self; [];
632 doCheck = false;
632 doCheck = false;
633 propagatedBuildInputs = with self; [];
633 propagatedBuildInputs = with self; [];
634 src = fetchurl {
634 src = fetchurl {
635 url = "https://pypi.python.org/packages/f6/a0/6f2142c58c6588d17c734265b103ae1cd0741e1681dd9483a63f22033375/dogpile.cache-0.6.1.tar.gz";
635 url = "https://pypi.python.org/packages/b6/3d/35c05ca01c070bb70d9d422f2c4858ecb021b05b21af438fec5ccd7b945c/dogpile.cache-0.6.4.tar.gz";
636 md5 = "35d7fb30f22bbd0685763d894dd079a9";
636 md5 = "66e0a6cae6c08cb1ea25f89d0eadfeb0";
637 };
637 };
638 meta = {
638 meta = {
639 license = [ pkgs.lib.licenses.bsdOriginal ];
639 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -653,13 +653,13 b''
653 };
653 };
654 };
654 };
655 ecdsa = super.buildPythonPackage {
655 ecdsa = super.buildPythonPackage {
656 name = "ecdsa-0.11";
656 name = "ecdsa-0.13";
657 buildInputs = with self; [];
657 buildInputs = with self; [];
658 doCheck = false;
658 doCheck = false;
659 propagatedBuildInputs = with self; [];
659 propagatedBuildInputs = with self; [];
660 src = fetchurl {
660 src = fetchurl {
661 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
661 url = "https://pypi.python.org/packages/f9/e5/99ebb176e47f150ac115ffeda5fedb6a3dbb3c00c74a59fd84ddf12f5857/ecdsa-0.13.tar.gz";
662 md5 = "8ef586fe4dbb156697d756900cb41d7c";
662 md5 = "1f60eda9cb5c46722856db41a3ae6670";
663 };
663 };
664 meta = {
664 meta = {
665 license = [ pkgs.lib.licenses.mit ];
665 license = [ pkgs.lib.licenses.mit ];
@@ -717,6 +717,19 b''
717 license = [ pkgs.lib.licenses.bsdOriginal ];
717 license = [ pkgs.lib.licenses.bsdOriginal ];
718 };
718 };
719 };
719 };
720 funcsigs = super.buildPythonPackage {
721 name = "funcsigs-1.0.2";
722 buildInputs = with self; [];
723 doCheck = false;
724 propagatedBuildInputs = with self; [];
725 src = fetchurl {
726 url = "https://pypi.python.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
727 md5 = "7e583285b1fb8a76305d6d68f4ccc14e";
728 };
729 meta = {
730 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
731 };
732 };
720 functools32 = super.buildPythonPackage {
733 functools32 = super.buildPythonPackage {
721 name = "functools32-3.2.3.post2";
734 name = "functools32-3.2.3.post2";
722 buildInputs = with self; [];
735 buildInputs = with self; [];
@@ -757,13 +770,13 b''
757 };
770 };
758 };
771 };
759 gevent = super.buildPythonPackage {
772 gevent = super.buildPythonPackage {
760 name = "gevent-1.1.2";
773 name = "gevent-1.2.2";
761 buildInputs = with self; [];
774 buildInputs = with self; [];
762 doCheck = false;
775 doCheck = false;
763 propagatedBuildInputs = with self; [greenlet];
776 propagatedBuildInputs = with self; [greenlet];
764 src = fetchurl {
777 src = fetchurl {
765 url = "https://pypi.python.org/packages/43/8f/cb3224a0e6ab663547f45c10d0651cfd52633fde4283bf68d627084df8cc/gevent-1.1.2.tar.gz";
778 url = "https://pypi.python.org/packages/1b/92/b111f76e54d2be11375b47b213b56687214f258fd9dae703546d30b837be/gevent-1.2.2.tar.gz";
766 md5 = "bb32a2f852a4997138014d5007215c6e";
779 md5 = "7f0baf355384fe5ff2ecf66853422554";
767 };
780 };
768 meta = {
781 meta = {
769 license = [ pkgs.lib.licenses.mit ];
782 license = [ pkgs.lib.licenses.mit ];
@@ -796,39 +809,39 b''
796 };
809 };
797 };
810 };
798 graphviz = super.buildPythonPackage {
811 graphviz = super.buildPythonPackage {
799 name = "graphviz-0.7.1";
812 name = "graphviz-0.8";
800 buildInputs = with self; [];
813 buildInputs = with self; [];
801 doCheck = false;
814 doCheck = false;
802 propagatedBuildInputs = with self; [];
815 propagatedBuildInputs = with self; [];
803 src = fetchurl {
816 src = fetchurl {
804 url = "https://pypi.python.org/packages/7d/2d/f5cfa56467ca5a65eb44e1103d89d2f65dbc4f04cf7a1f3d38e973c3d1a8/graphviz-0.7.1.zip";
817 url = "https://pypi.python.org/packages/da/84/0e997520323d6b01124eb01c68d5c101814d0aab53083cd62bd75a90f70b/graphviz-0.8.zip";
805 md5 = "d5926e89975121d56dec777a79bfc9d1";
818 md5 = "9486a885360a5ee54a81eb2950470c71";
806 };
819 };
807 meta = {
820 meta = {
808 license = [ pkgs.lib.licenses.mit ];
821 license = [ pkgs.lib.licenses.mit ];
809 };
822 };
810 };
823 };
811 greenlet = super.buildPythonPackage {
824 greenlet = super.buildPythonPackage {
812 name = "greenlet-0.4.10";
825 name = "greenlet-0.4.12";
813 buildInputs = with self; [];
826 buildInputs = with self; [];
814 doCheck = false;
827 doCheck = false;
815 propagatedBuildInputs = with self; [];
828 propagatedBuildInputs = with self; [];
816 src = fetchurl {
829 src = fetchurl {
817 url = "https://pypi.python.org/packages/67/62/ca2a95648666eaa2ffeb6a9b3964f21d419ae27f82f2e66b53da5b943fc4/greenlet-0.4.10.zip";
830 url = "https://pypi.python.org/packages/be/76/82af375d98724054b7e273b5d9369346937324f9bcc20980b45b068ef0b0/greenlet-0.4.12.tar.gz";
818 md5 = "bed0c4b3b896702131f4d5c72f87c41d";
831 md5 = "e8637647d58a26c4a1f51ca393e53c00";
819 };
832 };
820 meta = {
833 meta = {
821 license = [ pkgs.lib.licenses.mit ];
834 license = [ pkgs.lib.licenses.mit ];
822 };
835 };
823 };
836 };
824 gunicorn = super.buildPythonPackage {
837 gunicorn = super.buildPythonPackage {
825 name = "gunicorn-19.6.0";
838 name = "gunicorn-19.7.1";
826 buildInputs = with self; [];
839 buildInputs = with self; [];
827 doCheck = false;
840 doCheck = false;
828 propagatedBuildInputs = with self; [];
841 propagatedBuildInputs = with self; [];
829 src = fetchurl {
842 src = fetchurl {
830 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
843 url = "https://pypi.python.org/packages/30/3a/10bb213cede0cc4d13ac2263316c872a64bf4c819000c8ccd801f1d5f822/gunicorn-19.7.1.tar.gz";
831 md5 = "338e5e8a83ea0f0625f768dba4597530";
844 md5 = "174d3c3cd670a5be0404d84c484e590c";
832 };
845 };
833 meta = {
846 meta = {
834 license = [ pkgs.lib.licenses.mit ];
847 license = [ pkgs.lib.licenses.mit ];
@@ -847,6 +860,19 b''
847 license = [ pkgs.lib.licenses.mit ];
860 license = [ pkgs.lib.licenses.mit ];
848 };
861 };
849 };
862 };
863 hupper = super.buildPythonPackage {
864 name = "hupper-1.0";
865 buildInputs = with self; [];
866 doCheck = false;
867 propagatedBuildInputs = with self; [];
868 src = fetchurl {
869 url = "https://pypi.python.org/packages/2e/07/df892c564dc09bb3cf6f6deb976c26adf9117db75ba218cb4353dbc9d826/hupper-1.0.tar.gz";
870 md5 = "26e77da7d5ac5858f59af050d1a6eb5a";
871 };
872 meta = {
873 license = [ pkgs.lib.licenses.mit ];
874 };
875 };
850 infrae.cache = super.buildPythonPackage {
876 infrae.cache = super.buildPythonPackage {
851 name = "infrae.cache-1.0.1";
877 name = "infrae.cache-1.0.1";
852 buildInputs = with self; [];
878 buildInputs = with self; [];
@@ -873,14 +899,27 b''
873 license = [ pkgs.lib.licenses.bsdOriginal ];
899 license = [ pkgs.lib.licenses.bsdOriginal ];
874 };
900 };
875 };
901 };
876 ipdb = super.buildPythonPackage {
902 ipaddress = super.buildPythonPackage {
877 name = "ipdb-0.10.1";
903 name = "ipaddress-1.0.18";
878 buildInputs = with self; [];
904 buildInputs = with self; [];
879 doCheck = false;
905 doCheck = false;
880 propagatedBuildInputs = with self; [ipython setuptools];
906 propagatedBuildInputs = with self; [];
881 src = fetchurl {
907 src = fetchurl {
882 url = "https://pypi.python.org/packages/eb/0a/0a37dc19572580336ad3813792c0d18c8d7117c2d66fc63c501f13a7a8f8/ipdb-0.10.1.tar.gz";
908 url = "https://pypi.python.org/packages/4e/13/774faf38b445d0b3a844b65747175b2e0500164b7c28d78e34987a5bfe06/ipaddress-1.0.18.tar.gz";
883 md5 = "4aeab65f633ddc98ebdb5eebf08dc713";
909 md5 = "310c2dfd64eb6f0df44aa8c59f2334a7";
910 };
911 meta = {
912 license = [ pkgs.lib.licenses.psfl ];
913 };
914 };
915 ipdb = super.buildPythonPackage {
916 name = "ipdb-0.10.3";
917 buildInputs = with self; [];
918 doCheck = false;
919 propagatedBuildInputs = with self; [setuptools ipython];
920 src = fetchurl {
921 url = "https://pypi.python.org/packages/ad/cc/0e7298e1fbf2efd52667c9354a12aa69fb6f796ce230cca03525051718ef/ipdb-0.10.3.tar.gz";
922 md5 = "def1f6ac075d54bdee07e6501263d4fa";
884 };
923 };
885 meta = {
924 meta = {
886 license = [ pkgs.lib.licenses.bsdOriginal ];
925 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -1121,39 +1160,26 b''
1121 };
1160 };
1122 };
1161 };
1123 pandocfilters = super.buildPythonPackage {
1162 pandocfilters = super.buildPythonPackage {
1124 name = "pandocfilters-1.4.1";
1163 name = "pandocfilters-1.4.2";
1125 buildInputs = with self; [];
1164 buildInputs = with self; [];
1126 doCheck = false;
1165 doCheck = false;
1127 propagatedBuildInputs = with self; [];
1166 propagatedBuildInputs = with self; [];
1128 src = fetchurl {
1167 src = fetchurl {
1129 url = "https://pypi.python.org/packages/e3/1f/21d1b7e8ca571e80b796c758d361fdf5554335ff138158654684bc5401d8/pandocfilters-1.4.1.tar.gz";
1168 url = "https://pypi.python.org/packages/4c/ea/236e2584af67bb6df960832731a6e5325fd4441de001767da328c33368ce/pandocfilters-1.4.2.tar.gz";
1130 md5 = "7680d9f9ec07397dd17f380ee3818b9d";
1169 md5 = "dc391791ef54c7de1572d7b46b63361f";
1131 };
1170 };
1132 meta = {
1171 meta = {
1133 license = [ pkgs.lib.licenses.bsdOriginal ];
1172 license = [ pkgs.lib.licenses.bsdOriginal ];
1134 };
1173 };
1135 };
1174 };
1136 paramiko = super.buildPythonPackage {
1175 pathlib2 = super.buildPythonPackage {
1137 name = "paramiko-1.15.1";
1176 name = "pathlib2-2.3.0";
1138 buildInputs = with self; [];
1177 buildInputs = with self; [];
1139 doCheck = false;
1178 doCheck = false;
1140 propagatedBuildInputs = with self; [pycrypto ecdsa];
1179 propagatedBuildInputs = with self; [six scandir];
1141 src = fetchurl {
1180 src = fetchurl {
1142 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
1181 url = "https://pypi.python.org/packages/a1/14/df0deb867c2733f7d857523c10942b3d6612a1b222502fdffa9439943dfb/pathlib2-2.3.0.tar.gz";
1143 md5 = "48c274c3f9b1282932567b21f6acf3b5";
1182 md5 = "89c90409d11fd5947966b6a30a47d18c";
1144 };
1145 meta = {
1146 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1147 };
1148 };
1149 pathlib2 = super.buildPythonPackage {
1150 name = "pathlib2-2.1.0";
1151 buildInputs = with self; [];
1152 doCheck = false;
1153 propagatedBuildInputs = with self; [six];
1154 src = fetchurl {
1155 url = "https://pypi.python.org/packages/c9/27/8448b10d8440c08efeff0794adf7d0ed27adb98372c70c7b38f3947d4749/pathlib2-2.1.0.tar.gz";
1156 md5 = "38e4f58b4d69dfcb9edb49a54a8b28d2";
1157 };
1183 };
1158 meta = {
1184 meta = {
1159 license = [ pkgs.lib.licenses.mit ];
1185 license = [ pkgs.lib.licenses.mit ];
@@ -1198,14 +1224,40 b''
1198 license = [ pkgs.lib.licenses.mit ];
1224 license = [ pkgs.lib.licenses.mit ];
1199 };
1225 };
1200 };
1226 };
1227 plaster = super.buildPythonPackage {
1228 name = "plaster-0.5";
1229 buildInputs = with self; [];
1230 doCheck = false;
1231 propagatedBuildInputs = with self; [setuptools];
1232 src = fetchurl {
1233 url = "https://pypi.python.org/packages/99/b3/d7ca1fe31d2b56dba68a238721fda6820770f9c2a3de17a582d4b5b2edcc/plaster-0.5.tar.gz";
1234 md5 = "c59345a67a860cfcaa1bd6a81451399d";
1235 };
1236 meta = {
1237 license = [ pkgs.lib.licenses.mit ];
1238 };
1239 };
1240 plaster-pastedeploy = super.buildPythonPackage {
1241 name = "plaster-pastedeploy-0.4.1";
1242 buildInputs = with self; [];
1243 doCheck = false;
1244 propagatedBuildInputs = with self; [PasteDeploy plaster];
1245 src = fetchurl {
1246 url = "https://pypi.python.org/packages/9d/6e/f8be01ed41c94e6c54ac97cf2eb142a702aae0c8cce31c846f785e525b40/plaster_pastedeploy-0.4.1.tar.gz";
1247 md5 = "f48d5344b922e56c4978eebf1cd2e0d3";
1248 };
1249 meta = {
1250 license = [ pkgs.lib.licenses.mit ];
1251 };
1252 };
1201 prompt-toolkit = super.buildPythonPackage {
1253 prompt-toolkit = super.buildPythonPackage {
1202 name = "prompt-toolkit-1.0.14";
1254 name = "prompt-toolkit-1.0.15";
1203 buildInputs = with self; [];
1255 buildInputs = with self; [];
1204 doCheck = false;
1256 doCheck = false;
1205 propagatedBuildInputs = with self; [six wcwidth];
1257 propagatedBuildInputs = with self; [six wcwidth];
1206 src = fetchurl {
1258 src = fetchurl {
1207 url = "https://pypi.python.org/packages/55/56/8c39509b614bda53e638b7500f12577d663ac1b868aef53426fc6a26c3f5/prompt_toolkit-1.0.14.tar.gz";
1259 url = "https://pypi.python.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz";
1208 md5 = "f24061ae133ed32c6b764e92bd48c496";
1260 md5 = "8fe70295006dbc8afedd43e5eba99032";
1209 };
1261 };
1210 meta = {
1262 meta = {
1211 license = [ pkgs.lib.licenses.bsdOriginal ];
1263 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -1225,39 +1277,39 b''
1225 };
1277 };
1226 };
1278 };
1227 psycopg2 = super.buildPythonPackage {
1279 psycopg2 = super.buildPythonPackage {
1228 name = "psycopg2-2.6.1";
1280 name = "psycopg2-2.7.1";
1229 buildInputs = with self; [];
1281 buildInputs = with self; [];
1230 doCheck = false;
1282 doCheck = false;
1231 propagatedBuildInputs = with self; [];
1283 propagatedBuildInputs = with self; [];
1232 src = fetchurl {
1284 src = fetchurl {
1233 url = "https://pypi.python.org/packages/86/fd/cc8315be63a41fe000cce20482a917e874cdc1151e62cb0141f5e55f711e/psycopg2-2.6.1.tar.gz";
1285 url = "https://pypi.python.org/packages/f8/e9/5793369ce8a41bf5467623ded8d59a434dfef9c136351aca4e70c2657ba0/psycopg2-2.7.1.tar.gz";
1234 md5 = "842b44f8c95517ed5b792081a2370da1";
1286 md5 = "67848ac33af88336046802f6ef7081f3";
1235 };
1287 };
1236 meta = {
1288 meta = {
1237 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1289 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1238 };
1290 };
1239 };
1291 };
1240 ptyprocess = super.buildPythonPackage {
1292 ptyprocess = super.buildPythonPackage {
1241 name = "ptyprocess-0.5.1";
1293 name = "ptyprocess-0.5.2";
1242 buildInputs = with self; [];
1294 buildInputs = with self; [];
1243 doCheck = false;
1295 doCheck = false;
1244 propagatedBuildInputs = with self; [];
1296 propagatedBuildInputs = with self; [];
1245 src = fetchurl {
1297 src = fetchurl {
1246 url = "https://pypi.python.org/packages/db/d7/b465161910f3d1cef593c5e002bff67e0384898f597f1a7fdc8db4c02bf6/ptyprocess-0.5.1.tar.gz";
1298 url = "https://pypi.python.org/packages/51/83/5d07dc35534640b06f9d9f1a1d2bc2513fb9cc7595a1b0e28ae5477056ce/ptyprocess-0.5.2.tar.gz";
1247 md5 = "94e537122914cc9ec9c1eadcd36e73a1";
1299 md5 = "d3b8febae1b8c53b054bd818d0bb8665";
1248 };
1300 };
1249 meta = {
1301 meta = {
1250 license = [ ];
1302 license = [ ];
1251 };
1303 };
1252 };
1304 };
1253 py = super.buildPythonPackage {
1305 py = super.buildPythonPackage {
1254 name = "py-1.4.31";
1306 name = "py-1.4.34";
1255 buildInputs = with self; [];
1307 buildInputs = with self; [];
1256 doCheck = false;
1308 doCheck = false;
1257 propagatedBuildInputs = with self; [];
1309 propagatedBuildInputs = with self; [];
1258 src = fetchurl {
1310 src = fetchurl {
1259 url = "https://pypi.python.org/packages/f4/9a/8dfda23f36600dd701c6722316ba8a3ab4b990261f83e7d3ffc6dfedf7ef/py-1.4.31.tar.gz";
1311 url = "https://pypi.python.org/packages/68/35/58572278f1c097b403879c1e9369069633d1cbad5239b9057944bb764782/py-1.4.34.tar.gz";
1260 md5 = "5d2c63c56dc3f2115ec35c066ecd582b";
1312 md5 = "d9c3d8f734b0819ff48e355d77bf1730";
1261 };
1313 };
1262 meta = {
1314 meta = {
1263 license = [ pkgs.lib.licenses.mit ];
1315 license = [ pkgs.lib.licenses.mit ];
@@ -1355,13 +1407,13 b''
1355 };
1407 };
1356 };
1408 };
1357 pyramid = super.buildPythonPackage {
1409 pyramid = super.buildPythonPackage {
1358 name = "pyramid-1.7.4";
1410 name = "pyramid-1.9.1";
1359 buildInputs = with self; [];
1411 buildInputs = with self; [];
1360 doCheck = false;
1412 doCheck = false;
1361 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
1413 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy plaster plaster-pastedeploy hupper];
1362 src = fetchurl {
1414 src = fetchurl {
1363 url = "https://pypi.python.org/packages/33/91/55f5c661f8923902cd1f68d75f2b937c45e7682857356cf18f0be5493899/pyramid-1.7.4.tar.gz";
1415 url = "https://pypi.python.org/packages/9a/57/73447be9e7d0512d601e3f0a1fb9d7d1efb941911f49efdfe036d2826507/pyramid-1.9.1.tar.gz";
1364 md5 = "6ef1dfdcff9136d04490410757c4c446";
1416 md5 = "0163e19c58c2d12976a3b6fdb57e052d";
1365 };
1417 };
1366 meta = {
1418 meta = {
1367 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1419 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
@@ -1381,13 +1433,13 b''
1381 };
1433 };
1382 };
1434 };
1383 pyramid-debugtoolbar = super.buildPythonPackage {
1435 pyramid-debugtoolbar = super.buildPythonPackage {
1384 name = "pyramid-debugtoolbar-3.0.5";
1436 name = "pyramid-debugtoolbar-4.2.1";
1385 buildInputs = with self; [];
1437 buildInputs = with self; [];
1386 doCheck = false;
1438 doCheck = false;
1387 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
1439 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments ipaddress];
1388 src = fetchurl {
1440 src = fetchurl {
1389 url = "https://pypi.python.org/packages/64/0e/df00bfb55605900e7a2f7e4a18dd83575a6651688e297d5a0aa4c208fd7d/pyramid_debugtoolbar-3.0.5.tar.gz";
1441 url = "https://pypi.python.org/packages/db/26/94620b7752936e2cd74838263ff366db9b454f7394bfb62d1eb2f84b29c1/pyramid_debugtoolbar-4.2.1.tar.gz";
1390 md5 = "aebab8c3bfdc6f89e4d3adc1d126538e";
1442 md5 = "3dfaced2fab1644ff5284017be9d92b9";
1391 };
1443 };
1392 meta = {
1444 meta = {
1393 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1445 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
@@ -1420,26 +1472,26 b''
1420 };
1472 };
1421 };
1473 };
1422 pysqlite = super.buildPythonPackage {
1474 pysqlite = super.buildPythonPackage {
1423 name = "pysqlite-2.6.3";
1475 name = "pysqlite-2.8.3";
1424 buildInputs = with self; [];
1476 buildInputs = with self; [];
1425 doCheck = false;
1477 doCheck = false;
1426 propagatedBuildInputs = with self; [];
1478 propagatedBuildInputs = with self; [];
1427 src = fetchurl {
1479 src = fetchurl {
1428 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
1480 url = "https://pypi.python.org/packages/42/02/981b6703e3c83c5b25a829c6e77aad059f9481b0bbacb47e6e8ca12bd731/pysqlite-2.8.3.tar.gz";
1429 md5 = "7ff1cedee74646b50117acff87aa1cfa";
1481 md5 = "033f17b8644577715aee55e8832ac9fc";
1430 };
1482 };
1431 meta = {
1483 meta = {
1432 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1484 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1433 };
1485 };
1434 };
1486 };
1435 pytest = super.buildPythonPackage {
1487 pytest = super.buildPythonPackage {
1436 name = "pytest-3.0.5";
1488 name = "pytest-3.1.2";
1437 buildInputs = with self; [];
1489 buildInputs = with self; [];
1438 doCheck = false;
1490 doCheck = false;
1439 propagatedBuildInputs = with self; [py];
1491 propagatedBuildInputs = with self; [py setuptools];
1440 src = fetchurl {
1492 src = fetchurl {
1441 url = "https://pypi.python.org/packages/a8/87/b7ca49efe52d2b4169f2bfc49aa5e384173c4619ea8e635f123a0dac5b75/pytest-3.0.5.tar.gz";
1493 url = "https://pypi.python.org/packages/72/2b/2d3155e01f45a5a04427857352ee88220ee39550b2bc078f9db3190aea46/pytest-3.1.2.tar.gz";
1442 md5 = "cefd527b59332688bf5db4a10aa8a7cb";
1494 md5 = "c4d179f89043cc925e1c169d03128e02";
1443 };
1495 };
1444 meta = {
1496 meta = {
1445 license = [ pkgs.lib.licenses.mit ];
1497 license = [ pkgs.lib.licenses.mit ];
@@ -1459,52 +1511,52 b''
1459 };
1511 };
1460 };
1512 };
1461 pytest-cov = super.buildPythonPackage {
1513 pytest-cov = super.buildPythonPackage {
1462 name = "pytest-cov-2.4.0";
1514 name = "pytest-cov-2.5.1";
1463 buildInputs = with self; [];
1515 buildInputs = with self; [];
1464 doCheck = false;
1516 doCheck = false;
1465 propagatedBuildInputs = with self; [pytest coverage];
1517 propagatedBuildInputs = with self; [pytest coverage];
1466 src = fetchurl {
1518 src = fetchurl {
1467 url = "https://pypi.python.org/packages/00/c0/2bfd1fcdb9d407b8ac8185b1cb5ff458105c6b207a9a7f0e13032de9828f/pytest-cov-2.4.0.tar.gz";
1519 url = "https://pypi.python.org/packages/24/b4/7290d65b2f3633db51393bdf8ae66309b37620bc3ec116c5e357e3e37238/pytest-cov-2.5.1.tar.gz";
1468 md5 = "2fda09677d232acc99ec1b3c5831e33f";
1520 md5 = "5acf38d4909e19819eb5c1754fbfc0ac";
1469 };
1521 };
1470 meta = {
1522 meta = {
1471 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1523 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1472 };
1524 };
1473 };
1525 };
1474 pytest-profiling = super.buildPythonPackage {
1526 pytest-profiling = super.buildPythonPackage {
1475 name = "pytest-profiling-1.2.2";
1527 name = "pytest-profiling-1.2.6";
1476 buildInputs = with self; [];
1528 buildInputs = with self; [];
1477 doCheck = false;
1529 doCheck = false;
1478 propagatedBuildInputs = with self; [six pytest gprof2dot];
1530 propagatedBuildInputs = with self; [six pytest gprof2dot];
1479 src = fetchurl {
1531 src = fetchurl {
1480 url = "https://pypi.python.org/packages/73/e8/804681323bac0bc45c520ec34185ba8469008942266d0074699b204835c1/pytest-profiling-1.2.2.tar.gz";
1532 url = "https://pypi.python.org/packages/f9/0d/df67fb9ce16c2cef201693da956321b1bccfbf9a4ead39748b9f9d1d74cb/pytest-profiling-1.2.6.tar.gz";
1481 md5 = "0a16d7dda2d23b91e9730fa4558cf728";
1533 md5 = "50eb4c66c3762a2f1a49669bedc0b894";
1482 };
1534 };
1483 meta = {
1535 meta = {
1484 license = [ pkgs.lib.licenses.mit ];
1536 license = [ pkgs.lib.licenses.mit ];
1485 };
1537 };
1486 };
1538 };
1487 pytest-runner = super.buildPythonPackage {
1539 pytest-runner = super.buildPythonPackage {
1488 name = "pytest-runner-2.9";
1540 name = "pytest-runner-2.11.1";
1489 buildInputs = with self; [];
1541 buildInputs = with self; [];
1490 doCheck = false;
1542 doCheck = false;
1491 propagatedBuildInputs = with self; [];
1543 propagatedBuildInputs = with self; [];
1492 src = fetchurl {
1544 src = fetchurl {
1493 url = "https://pypi.python.org/packages/11/d4/c335ddf94463e451109e3494e909765c3e5205787b772e3b25ee8601b86a/pytest-runner-2.9.tar.gz";
1545 url = "https://pypi.python.org/packages/9e/4d/08889e5e27a9f5d6096b9ad257f4dea1faabb03c5ded8f665ead448f5d8a/pytest-runner-2.11.1.tar.gz";
1494 md5 = "2212a2e34404b0960b2fdc2c469247b2";
1546 md5 = "bdb73eb18eca2727944a2dcf963c5a81";
1495 };
1547 };
1496 meta = {
1548 meta = {
1497 license = [ pkgs.lib.licenses.mit ];
1549 license = [ pkgs.lib.licenses.mit ];
1498 };
1550 };
1499 };
1551 };
1500 pytest-sugar = super.buildPythonPackage {
1552 pytest-sugar = super.buildPythonPackage {
1501 name = "pytest-sugar-0.7.1";
1553 name = "pytest-sugar-0.8.0";
1502 buildInputs = with self; [];
1554 buildInputs = with self; [];
1503 doCheck = false;
1555 doCheck = false;
1504 propagatedBuildInputs = with self; [pytest termcolor];
1556 propagatedBuildInputs = with self; [pytest termcolor];
1505 src = fetchurl {
1557 src = fetchurl {
1506 url = "https://pypi.python.org/packages/03/97/05d988b4fa870e7373e8ee4582408543b9ca2bd35c3c67b569369c6f9c49/pytest-sugar-0.7.1.tar.gz";
1558 url = "https://pypi.python.org/packages/a5/b0/b2773dee078f17773a5bf2dfad49b0be57b6354bbd84bbefe4313e509d87/pytest-sugar-0.8.0.tar.gz";
1507 md5 = "7400f7c11f3d572b2c2a3b60352d35fe";
1559 md5 = "8cafbdad648068e0e44b8fc5f9faae42";
1508 };
1560 };
1509 meta = {
1561 meta = {
1510 license = [ pkgs.lib.licenses.bsdOriginal ];
1562 license = [ pkgs.lib.licenses.bsdOriginal ];
@@ -1550,26 +1602,26 b''
1550 };
1602 };
1551 };
1603 };
1552 python-ldap = super.buildPythonPackage {
1604 python-ldap = super.buildPythonPackage {
1553 name = "python-ldap-2.4.19";
1605 name = "python-ldap-2.4.40";
1554 buildInputs = with self; [];
1606 buildInputs = with self; [];
1555 doCheck = false;
1607 doCheck = false;
1556 propagatedBuildInputs = with self; [setuptools];
1608 propagatedBuildInputs = with self; [setuptools];
1557 src = fetchurl {
1609 src = fetchurl {
1558 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
1610 url = "https://pypi.python.org/packages/4a/d8/7d70a7469058a3987d224061a81d778951ac2b48220bdcc511e4b1b37176/python-ldap-2.4.40.tar.gz";
1559 md5 = "b941bf31d09739492aa19ef679e94ae3";
1611 md5 = "aea0233f7d39b0c7549fcd310deeb0e5";
1560 };
1612 };
1561 meta = {
1613 meta = {
1562 license = [ pkgs.lib.licenses.psfl ];
1614 license = [ pkgs.lib.licenses.psfl ];
1563 };
1615 };
1564 };
1616 };
1565 python-memcached = super.buildPythonPackage {
1617 python-memcached = super.buildPythonPackage {
1566 name = "python-memcached-1.57";
1618 name = "python-memcached-1.58";
1567 buildInputs = with self; [];
1619 buildInputs = with self; [];
1568 doCheck = false;
1620 doCheck = false;
1569 propagatedBuildInputs = with self; [six];
1621 propagatedBuildInputs = with self; [six];
1570 src = fetchurl {
1622 src = fetchurl {
1571 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
1623 url = "https://pypi.python.org/packages/f7/62/14b2448cfb04427366f24104c9da97cf8ea380d7258a3233f066a951a8d8/python-memcached-1.58.tar.gz";
1572 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
1624 md5 = "23b258105013d14d899828d334e6b044";
1573 };
1625 };
1574 meta = {
1626 meta = {
1575 license = [ pkgs.lib.licenses.psfl ];
1627 license = [ pkgs.lib.licenses.psfl ];
@@ -1627,6 +1679,19 b''
1627 license = [ { fullName = "MIT/X11"; } ];
1679 license = [ { fullName = "MIT/X11"; } ];
1628 };
1680 };
1629 };
1681 };
1682 redis = super.buildPythonPackage {
1683 name = "redis-2.10.6";
1684 buildInputs = with self; [];
1685 doCheck = false;
1686 propagatedBuildInputs = with self; [];
1687 src = fetchurl {
1688 url = "https://pypi.python.org/packages/09/8d/6d34b75326bf96d4139a2ddd8e74b80840f800a0a79f9294399e212cb9a7/redis-2.10.6.tar.gz";
1689 md5 = "048348d8cfe0b5d0bba2f4d835005c3b";
1690 };
1691 meta = {
1692 license = [ pkgs.lib.licenses.mit ];
1693 };
1694 };
1630 repoze.lru = super.buildPythonPackage {
1695 repoze.lru = super.buildPythonPackage {
1631 name = "repoze.lru-0.6";
1696 name = "repoze.lru-0.6";
1632 buildInputs = with self; [];
1697 buildInputs = with self; [];
@@ -1654,28 +1719,41 b''
1654 };
1719 };
1655 };
1720 };
1656 rhodecode-enterprise-ce = super.buildPythonPackage {
1721 rhodecode-enterprise-ce = super.buildPythonPackage {
1657 name = "rhodecode-enterprise-ce-4.9.1";
1722 name = "rhodecode-enterprise-ce-4.10.0";
1658 buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj];
1723 buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj];
1659 doCheck = true;
1724 doCheck = true;
1660 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];
1725 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 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 redis repoze.lru requests simplejson sshpubkeys subprocess32 waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
1661 src = ./.;
1726 src = ./.;
1662 meta = {
1727 meta = {
1663 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
1728 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
1664 };
1729 };
1665 };
1730 };
1666 rhodecode-tools = super.buildPythonPackage {
1731 rhodecode-tools = super.buildPythonPackage {
1667 name = "rhodecode-tools-0.12.0";
1732 name = "rhodecode-tools-0.13.0";
1668 buildInputs = with self; [];
1733 buildInputs = with self; [];
1669 doCheck = false;
1734 doCheck = false;
1670 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests elasticsearch elasticsearch-dsl urllib3 Whoosh];
1735 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests elasticsearch elasticsearch-dsl urllib3 Whoosh];
1671 src = fetchurl {
1736 src = fetchurl {
1672 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.12.0.tar.gz?md5=9ca040356fa7e38d3f64529a4cffdca4";
1737 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.13.0.tar.gz?md5=f937b0cb34d0779103895a5ec5689ee4";
1673 md5 = "9ca040356fa7e38d3f64529a4cffdca4";
1738 md5 = "f937b0cb34d0779103895a5ec5689ee4";
1674 };
1739 };
1675 meta = {
1740 meta = {
1676 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1741 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1677 };
1742 };
1678 };
1743 };
1744 scandir = super.buildPythonPackage {
1745 name = "scandir-1.5";
1746 buildInputs = with self; [];
1747 doCheck = false;
1748 propagatedBuildInputs = with self; [];
1749 src = fetchurl {
1750 url = "https://pypi.python.org/packages/bd/f4/3143e0289faf0883228017dbc6387a66d0b468df646645e29e1eb89ea10e/scandir-1.5.tar.gz";
1751 md5 = "a2713043de681bba6b084be42e7a8a44";
1752 };
1753 meta = {
1754 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
1755 };
1756 };
1679 setproctitle = super.buildPythonPackage {
1757 setproctitle = super.buildPythonPackage {
1680 name = "setproctitle-1.1.8";
1758 name = "setproctitle-1.1.8";
1681 buildInputs = with self; [];
1759 buildInputs = with self; [];
@@ -1729,13 +1807,13 b''
1729 };
1807 };
1730 };
1808 };
1731 simplejson = super.buildPythonPackage {
1809 simplejson = super.buildPythonPackage {
1732 name = "simplejson-3.7.2";
1810 name = "simplejson-3.11.1";
1733 buildInputs = with self; [];
1811 buildInputs = with self; [];
1734 doCheck = false;
1812 doCheck = false;
1735 propagatedBuildInputs = with self; [];
1813 propagatedBuildInputs = with self; [];
1736 src = fetchurl {
1814 src = fetchurl {
1737 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1815 url = "https://pypi.python.org/packages/08/48/c97b668d6da7d7bebe7ea1817a6f76394b0ec959cb04214ca833c34359df/simplejson-3.11.1.tar.gz";
1738 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1816 md5 = "6e2f1bd5fb0a926facf5d89d217a7183";
1739 };
1817 };
1740 meta = {
1818 meta = {
1741 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
1819 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
@@ -1754,27 +1832,40 b''
1754 license = [ pkgs.lib.licenses.mit ];
1832 license = [ pkgs.lib.licenses.mit ];
1755 };
1833 };
1756 };
1834 };
1835 sshpubkeys = super.buildPythonPackage {
1836 name = "sshpubkeys-2.2.0";
1837 buildInputs = with self; [];
1838 doCheck = false;
1839 propagatedBuildInputs = with self; [pycrypto ecdsa];
1840 src = fetchurl {
1841 url = "https://pypi.python.org/packages/27/da/337fabeb3dca6b62039a93ceaa636f25065e0ae92b575b1235342076cf0a/sshpubkeys-2.2.0.tar.gz";
1842 md5 = "458e45f6b92b1afa84f0ffe1f1c90935";
1843 };
1844 meta = {
1845 license = [ pkgs.lib.licenses.bsdOriginal ];
1846 };
1847 };
1757 subprocess32 = super.buildPythonPackage {
1848 subprocess32 = super.buildPythonPackage {
1758 name = "subprocess32-3.2.6";
1849 name = "subprocess32-3.2.7";
1759 buildInputs = with self; [];
1850 buildInputs = with self; [];
1760 doCheck = false;
1851 doCheck = false;
1761 propagatedBuildInputs = with self; [];
1852 propagatedBuildInputs = with self; [];
1762 src = fetchurl {
1853 src = fetchurl {
1763 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1854 url = "https://pypi.python.org/packages/b8/2f/49e53b0d0e94611a2dc624a1ad24d41b6d94d0f1b0a078443407ea2214c2/subprocess32-3.2.7.tar.gz";
1764 md5 = "754c5ab9f533e764f931136974b618f1";
1855 md5 = "824c801e479d3e916879aae3e9c15e16";
1765 };
1856 };
1766 meta = {
1857 meta = {
1767 license = [ pkgs.lib.licenses.psfl ];
1858 license = [ pkgs.lib.licenses.psfl ];
1768 };
1859 };
1769 };
1860 };
1770 supervisor = super.buildPythonPackage {
1861 supervisor = super.buildPythonPackage {
1771 name = "supervisor-3.3.1";
1862 name = "supervisor-3.3.3";
1772 buildInputs = with self; [];
1863 buildInputs = with self; [];
1773 doCheck = false;
1864 doCheck = false;
1774 propagatedBuildInputs = with self; [meld3];
1865 propagatedBuildInputs = with self; [meld3];
1775 src = fetchurl {
1866 src = fetchurl {
1776 url = "https://pypi.python.org/packages/80/37/964c0d53cbd328796b1aeb7abea4c0f7b0e8c7197ea9b0b9967b7d004def/supervisor-3.3.1.tar.gz";
1867 url = "https://pypi.python.org/packages/31/7e/788fc6566211e77c395ea272058eb71299c65cc5e55b6214d479c6c2ec9a/supervisor-3.3.3.tar.gz";
1777 md5 = "202f760f9bf4930ec06557bac73e5cf2";
1868 md5 = "0fe86dfec4e5c5d98324d24c4cf944bd";
1778 };
1869 };
1779 meta = {
1870 meta = {
1780 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1871 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
@@ -1794,13 +1885,13 b''
1794 };
1885 };
1795 };
1886 };
1796 testpath = super.buildPythonPackage {
1887 testpath = super.buildPythonPackage {
1797 name = "testpath-0.1";
1888 name = "testpath-0.3.1";
1798 buildInputs = with self; [];
1889 buildInputs = with self; [];
1799 doCheck = false;
1890 doCheck = false;
1800 propagatedBuildInputs = with self; [];
1891 propagatedBuildInputs = with self; [];
1801 src = fetchurl {
1892 src = fetchurl {
1802 url = "https://pypi.python.org/packages/f9/c4/c0b22f35138bc26a6058c39cb61db1e8977e5e9550b12cd2cb02ef56fc51/testpath-0.1.tar.gz";
1893 url = "https://pypi.python.org/packages/f4/8b/b71e9ee10e5f751e9d959bc750ab122ba04187f5aa52aabdc4e63b0e31a7/testpath-0.3.1.tar.gz";
1803 md5 = "401918bcd0b0e5b71a9b909835117bc6";
1894 md5 = "2cd5ed5522fda781bb497c9d80ae2fc9";
1804 };
1895 };
1805 meta = {
1896 meta = {
1806 license = [ pkgs.lib.licenses.mit ];
1897 license = [ pkgs.lib.licenses.mit ];
@@ -1859,13 +1950,13 b''
1859 };
1950 };
1860 };
1951 };
1861 uWSGI = super.buildPythonPackage {
1952 uWSGI = super.buildPythonPackage {
1862 name = "uWSGI-2.0.11.2";
1953 name = "uWSGI-2.0.15";
1863 buildInputs = with self; [];
1954 buildInputs = with self; [];
1864 doCheck = false;
1955 doCheck = false;
1865 propagatedBuildInputs = with self; [];
1956 propagatedBuildInputs = with self; [];
1866 src = fetchurl {
1957 src = fetchurl {
1867 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1958 url = "https://pypi.python.org/packages/bb/0a/45e5aa80dc135889594bb371c082d20fb7ee7303b174874c996888cc8511/uwsgi-2.0.15.tar.gz";
1868 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1959 md5 = "fc50bd9e83b7602fa474b032167010a7";
1869 };
1960 };
1870 meta = {
1961 meta = {
1871 license = [ pkgs.lib.licenses.gpl2 ];
1962 license = [ pkgs.lib.licenses.gpl2 ];
@@ -1885,26 +1976,26 b''
1885 };
1976 };
1886 };
1977 };
1887 venusian = super.buildPythonPackage {
1978 venusian = super.buildPythonPackage {
1888 name = "venusian-1.0";
1979 name = "venusian-1.1.0";
1889 buildInputs = with self; [];
1980 buildInputs = with self; [];
1890 doCheck = false;
1981 doCheck = false;
1891 propagatedBuildInputs = with self; [];
1982 propagatedBuildInputs = with self; [];
1892 src = fetchurl {
1983 src = fetchurl {
1893 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1984 url = "https://pypi.python.org/packages/38/24/b4b470ab9e0a2e2e9b9030c7735828c8934b4c6b45befd1bb713ec2aeb2d/venusian-1.1.0.tar.gz";
1894 md5 = "dccf2eafb7113759d60c86faf5538756";
1985 md5 = "56bc5e6756e4bda37bcdb94f74a72b8f";
1895 };
1986 };
1896 meta = {
1987 meta = {
1897 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1988 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1898 };
1989 };
1899 };
1990 };
1900 waitress = super.buildPythonPackage {
1991 waitress = super.buildPythonPackage {
1901 name = "waitress-1.0.1";
1992 name = "waitress-1.0.2";
1902 buildInputs = with self; [];
1993 buildInputs = with self; [];
1903 doCheck = false;
1994 doCheck = false;
1904 propagatedBuildInputs = with self; [];
1995 propagatedBuildInputs = with self; [];
1905 src = fetchurl {
1996 src = fetchurl {
1906 url = "https://pypi.python.org/packages/78/7d/84d11b96c3f60164dec3bef4a859a03aeae0231aa93f57fbe0d05fa4ff36/waitress-1.0.1.tar.gz";
1997 url = "https://pypi.python.org/packages/cd/f4/400d00863afa1e03618e31fd7e2092479a71b8c9718b00eb1eeb603746c6/waitress-1.0.2.tar.gz";
1907 md5 = "dda92358a7569669086155923a46e57c";
1998 md5 = "b968f39e95d609f6194c6e50425d4bb7";
1908 };
1999 };
1909 meta = {
2000 meta = {
1910 license = [ pkgs.lib.licenses.zpt21 ];
2001 license = [ pkgs.lib.licenses.zpt21 ];
@@ -4,7 +4,11 b' pylons_config = rhodecode/tests/rhodecod'
4 vcsserver_protocol = http
4 vcsserver_protocol = http
5 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
5 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
6 norecursedirs = tests/scripts
6 norecursedirs = tests/scripts
7 addopts = -k "not _BaseTest"
7
8 addopts =
9 -k "not _BaseTest"
10 --pdbcls=IPython.terminal.debugger:TerminalPdb
11
8 markers =
12 markers =
9 vcs_operations: Mark tests depending on a running RhodeCode instance.
13 vcs_operations: Mark tests depending on a running RhodeCode instance.
10 xfail_backends: Mark tests as xfail for given backends.
14 xfail_backends: Mark tests as xfail for given backends.
@@ -145,9 +145,9 b' let'
145 --repos=$PWD/repos \
145 --repos=$PWD/repos \
146 enterprise.ini > /dev/null
146 enterprise.ini > /dev/null
147
147
148 echo "Starting rcserver"
148 echo "Starting rc-server"
149 vcsserver --config ${vcsserverCfg} >vcsserver.log 2>&1 &
149 vcsserver --config ${vcsserverCfg} >vcsserver.log 2>&1 &
150 rcserver enterprise.ini >rcserver.log 2>&1 &
150 rc-server enterprise.ini >rc-server.log 2>&1 &
151
151
152 while ! curl -f -s http://localhost:5000 > /dev/null
152 while ! curl -f -s http://localhost:5000 > /dev/null
153 do
153 do
@@ -159,7 +159,7 b' let'
159 echo "Starting the test run"
159 echo "Starting the test run"
160 py.test -c example.ini -vs --maxfail=5 tests
160 py.test -c example.ini -vs --maxfail=5 tests
161
161
162 echo "Kill rcserver"
162 echo "Kill rc-server"
163 kill %2
163 kill %2
164 kill %1
164 kill %1
165 '';
165 '';
@@ -170,13 +170,13 b' let'
170 mkdir -p $out
170 mkdir -p $out
171 cp enterprise.ini $out
171 cp enterprise.ini $out
172 cp ${vcsserverCfg} $out/vcsserver.ini
172 cp ${vcsserverCfg} $out/vcsserver.ini
173 cp rcserver.log $out
173 cp rc-server.log $out
174 cp vcsserver.log $out
174 cp vcsserver.log $out
175
175
176 mkdir -p $out/nix-support
176 mkdir -p $out/nix-support
177 echo "report config $out enterprise.ini" >> $out/nix-support/hydra-build-products
177 echo "report config $out enterprise.ini" >> $out/nix-support/hydra-build-products
178 echo "report config $out vcsserver.ini" >> $out/nix-support/hydra-build-products
178 echo "report config $out vcsserver.ini" >> $out/nix-support/hydra-build-products
179 echo "report rcserver $out rcserver.log" >> $out/nix-support/hydra-build-products
179 echo "report rc-server $out rc-server.log" >> $out/nix-support/hydra-build-products
180 echo "report vcsserver $out vcsserver.log" >> $out/nix-support/hydra-build-products
180 echo "report vcsserver $out vcsserver.log" >> $out/nix-support/hydra-build-products
181 '';
181 '';
182 };
182 };
@@ -6,21 +6,20 b' 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 Beaker==1.9.0
10 Beaker==1.7.0
11 celery==2.2.10
10 celery==2.2.10
12 Chameleon==2.24
11 Chameleon==2.24
13 channelstream==0.5.2
12 channelstream==0.5.2
14 click==5.1
13 click==5.1
15 colander==1.2
14 colander==1.3.3
16 configobj==5.0.6
15 configobj==5.0.6
17 cssselect==1.0.1
16 cssselect==1.0.1
18 decorator==4.0.11
17 decorator==4.0.11
19 deform==2.0.4
18 deform==2.0.4
20 docutils==0.12
19 docutils==0.13.1
21 dogpile.cache==0.6.1
20 dogpile.cache==0.6.4
22 dogpile.core==0.4.1
21 dogpile.core==0.4.1
23 ecdsa==0.11
22 ecdsa==0.13
24 FormEncode==1.2.4
23 FormEncode==1.2.4
25 future==0.14.3
24 future==0.14.3
26 futures==3.0.2
25 futures==3.0.2
@@ -31,8 +30,8 b' itsdangerous==0.24'
31 Jinja2==2.7.3
30 Jinja2==2.7.3
32 kombu==1.5.1
31 kombu==1.5.1
33 lxml==3.7.3
32 lxml==3.7.3
34 Mako==1.0.6
33 Mako==1.0.7
35 Markdown==2.6.7
34 Markdown==2.6.8
36 MarkupSafe==0.23
35 MarkupSafe==0.23
37 meld3==1.0.2
36 meld3==1.0.2
38 msgpack-python==0.4.8
37 msgpack-python==0.4.8
@@ -40,13 +39,13 b' MySQL-python==1.2.5'
40 nose==1.3.6
39 nose==1.3.6
41 objgraph==3.1.0
40 objgraph==3.1.0
42 packaging==15.2
41 packaging==15.2
43 paramiko==1.15.1
44 Paste==2.0.3
42 Paste==2.0.3
45 PasteDeploy==1.5.2
43 PasteDeploy==1.5.2
46 PasteScript==1.7.5
44 PasteScript==1.7.5
47 pathlib2==2.1.0
45 pathlib2==2.3.0
46 peppercorn==0.5
48 psutil==4.3.1
47 psutil==4.3.1
49 psycopg2==2.6.1
48 psycopg2==2.7.1
50 py-bcrypt==0.4
49 py-bcrypt==0.4
51 pycrypto==2.6.1
50 pycrypto==2.6.1
52 pycurl==7.19.5
51 pycurl==7.19.5
@@ -55,38 +54,40 b' pygments-markdown-lexer==0.1.0.dev39'
55 Pygments==2.2.0
54 Pygments==2.2.0
56 pyparsing==1.5.7
55 pyparsing==1.5.7
57 pyramid-beaker==0.8
56 pyramid-beaker==0.8
58 pyramid-debugtoolbar==3.0.5
57 pyramid-debugtoolbar==4.2.1
59 pyramid-jinja2==2.5
58 pyramid-jinja2==2.5
60 pyramid-mako==1.0.2
59 pyramid-mako==1.0.2
61 pyramid==1.7.4
60 pyramid==1.9.1
62 pysqlite==2.6.3
61 pysqlite==2.8.3
63 python-dateutil==2.1
62 python-dateutil==2.1
64 python-ldap==2.4.19
63 python-ldap==2.4.40
65 python-memcached==1.57
64 python-memcached==1.58
66 python-pam==1.8.2
65 python-pam==1.8.2
67 pytz==2015.4
66 pytz==2015.4
68 pyzmq==14.6.0
67 pyzmq==14.6.0
69 recaptcha-client==1.0.6
68 recaptcha-client==1.0.6
69 redis==2.10.6
70 repoze.lru==0.6
70 repoze.lru==0.6
71 requests==2.9.1
71 requests==2.9.1
72 Routes==1.13
72 Routes==1.13
73 setproctitle==1.1.8
73 setproctitle==1.1.8
74 simplejson==3.7.2
74 simplejson==3.11.1
75 six==1.9.0
75 six==1.9.0
76 Sphinx==1.2.2
76 Sphinx==1.2.2
77 SQLAlchemy==0.9.9
77 SQLAlchemy==1.1.11
78 subprocess32==3.2.6
78 sshpubkeys==2.2.0
79 supervisor==3.3.1
79 subprocess32==3.2.7
80 supervisor==3.3.3
80 Tempita==0.5.2
81 Tempita==0.5.2
81 translationstring==1.3
82 translationstring==1.3
82 trollius==1.0.4
83 trollius==1.0.4
83 urllib3==1.16
84 urllib3==1.16
84 URLObject==2.4.0
85 URLObject==2.4.0
85 venusian==1.0
86 venusian==1.1.0
86 WebError==0.10.3
87 WebError==0.10.3
87 WebHelpers2==2.0
88 WebHelpers2==2.0
88 WebHelpers==1.3
89 WebHelpers==1.3
89 WebOb==1.3.1
90 WebOb==1.7.3
90 Whoosh==2.7.4
91 Whoosh==2.7.4
91 wsgiref==0.1.2
92 wsgiref==0.1.2
92 zope.cachedescriptors==4.0.0
93 zope.cachedescriptors==4.0.0
@@ -104,33 +105,34 b' https://code.rhodecode.com/upstream/py-g'
104 # entrypoints backport, pypi version doesn't support egg installs
105 # 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 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 nbconvert==5.1.1
108 bleach==1.5.0
107 nbformat==4.3.0
109 nbformat==4.3.0
108 jupyter_client==5.0.0
110 jupyter_client==5.0.0
109
111
110 ## cli tools
112 ## cli tools
111 alembic==0.8.4
113 alembic==0.9.2
112 invoke==0.13.0
114 invoke==0.13.0
113 bumpversion==0.5.3
115 bumpversion==0.5.3
114 transifex-client==0.10
116 transifex-client==0.10
115
117
116 ## http servers
118 ## http servers
117 gevent==1.1.2
119 gevent==1.2.2
118 greenlet==0.4.10
120 greenlet==0.4.12
119 gunicorn==19.6.0
121 gunicorn==19.7.1
120 waitress==1.0.1
122 waitress==1.0.2
121 uWSGI==2.0.11.2
123 uWSGI==2.0.15
122
124
123 ## debug
125 ## debug
124 ipdb==0.10.1
126 ipdb==0.10.3
125 ipython==5.1.0
127 ipython==5.1.0
126 CProfileV==1.0.6
128 CProfileV==1.0.7
127 bottle==0.12.8
129 bottle==0.12.8
128
130
129 ## rhodecode-tools, special case
131 ## rhodecode-tools, special case
130 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.12.0.tar.gz?md5=9ca040356fa7e38d3f64529a4cffdca4#egg=rhodecode-tools==0.12.0
132 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.13.0.tar.gz?md5=f937b0cb34d0779103895a5ec5689ee4#egg=rhodecode-tools==0.13.0
131
133
132 ## appenlight
134 ## appenlight
133 appenlight-client==0.6.14
135 appenlight-client==0.6.21
134
136
135 ## test related requirements
137 ## test related requirements
136 -r requirements_test.txt
138 -r requirements_test.txt
@@ -1,15 +1,15 b''
1 # test related requirements
1 # test related requirements
2 pytest==3.0.5
2 pytest==3.1.2
3 py==1.4.31
3 py==1.4.34
4 pytest-cov==2.4.0
4 pytest-cov==2.5.1
5 pytest-sugar==0.7.1
5 pytest-sugar==0.8.0
6 pytest-runner==2.9.0
6 pytest-runner==2.11.1
7 pytest-catchlog==1.2.2
7 pytest-catchlog==1.2.2
8 pytest-profiling==1.2.2
8 pytest-profiling==1.2.6
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==2.0.27
14 cov-core==1.15.0
14 cov-core==1.15.0
15 coverage==3.7.1
15 coverage==3.7.1
@@ -1,1 +1,1 b''
1 4.9.1 No newline at end of file
1 4.10.0 No newline at end of file
@@ -51,7 +51,7 b' PYRAMID_SETTINGS = {}'
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__ = 78 # defines current db version for migrations
54 __dbversion__ = 81 # 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'
@@ -281,8 +281,8 b' def request_view(request):'
281 })
281 })
282
282
283 # register some common functions for usage
283 # register some common functions for usage
284 attach_context_attributes(TemplateArgs(), request, request.rpc_user.user_id,
284 attach_context_attributes(
285 attach_to_request=True)
285 TemplateArgs(), request, request.rpc_user.user_id)
286
286
287 try:
287 try:
288 ret_value = func(**call_params)
288 ret_value = func(**call_params)
@@ -45,7 +45,7 b' def testuser_api(request, pylonsapp):'
45 # create TOKEN for user, if he doesn't have one
45 # create TOKEN for user, if he doesn't have one
46 if not cls.test_user.api_key:
46 if not cls.test_user.api_key:
47 AuthTokenModel().create(
47 AuthTokenModel().create(
48 user=cls.test_user, description='TEST_USER_TOKEN')
48 user=cls.test_user, description=u'TEST_USER_TOKEN')
49
49
50 Session().commit()
50 Session().commit()
51 cls.TEST_USER_LOGIN = cls.test_user.username
51 cls.TEST_USER_LOGIN = cls.test_user.username
@@ -28,7 +28,7 b' from rhodecode.api.tests.utils import ('
28
28
29 @pytest.mark.usefixtures("testuser_api", "app")
29 @pytest.mark.usefixtures("testuser_api", "app")
30 class TestApiGetGist(object):
30 class TestApiGetGist(object):
31 def test_api_get_gist(self, gist_util, http_host_stub):
31 def test_api_get_gist(self, gist_util, http_host_only_stub):
32 gist = gist_util.create_gist()
32 gist = gist_util.create_gist()
33 gist_id = gist.gist_access_id
33 gist_id = gist.gist_access_id
34 gist_created_on = gist.created_on
34 gist_created_on = gist.created_on
@@ -45,14 +45,14 b' class TestApiGetGist(object):'
45 'expires': -1.0,
45 'expires': -1.0,
46 'gist_id': int(gist_id),
46 'gist_id': int(gist_id),
47 'type': 'public',
47 'type': 'public',
48 'url': 'http://%s/_admin/gists/%s' % (http_host_stub, gist_id,),
48 'url': 'http://%s/_admin/gists/%s' % (http_host_only_stub, gist_id,),
49 'acl_level': Gist.ACL_LEVEL_PUBLIC,
49 'acl_level': Gist.ACL_LEVEL_PUBLIC,
50 'content': None,
50 'content': None,
51 }
51 }
52
52
53 assert_ok(id_, expected, given=response.body)
53 assert_ok(id_, expected, given=response.body)
54
54
55 def test_api_get_gist_with_content(self, gist_util, http_host_stub):
55 def test_api_get_gist_with_content(self, gist_util, http_host_only_stub):
56 mapping = {
56 mapping = {
57 u'filename1.txt': {'content': u'hello world'},
57 u'filename1.txt': {'content': u'hello world'},
58 u'filename1ą.txt': {'content': u'hello worldę'}
58 u'filename1ą.txt': {'content': u'hello worldę'}
@@ -73,7 +73,7 b' class TestApiGetGist(object):'
73 'expires': -1.0,
73 'expires': -1.0,
74 'gist_id': int(gist_id),
74 'gist_id': int(gist_id),
75 'type': 'public',
75 'type': 'public',
76 'url': 'http://%s/_admin/gists/%s' % (http_host_stub, gist_id,),
76 'url': 'http://%s/_admin/gists/%s' % (http_host_only_stub, gist_id,),
77 'acl_level': Gist.ACL_LEVEL_PUBLIC,
77 'acl_level': Gist.ACL_LEVEL_PUBLIC,
78 'content': {
78 'content': {
79 u'filename1.txt': u'hello world',
79 u'filename1.txt': u'hello world',
@@ -21,10 +21,10 b''
21
21
22 import pytest
22 import pytest
23 import urlobject
23 import urlobject
24 from pylons import url
25
24
26 from rhodecode.api.tests.utils import (
25 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok)
26 build_data, api_call, assert_error, assert_ok)
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.utils2 import safe_unicode
28 from rhodecode.lib.utils2 import safe_unicode
29
29
30 pytestmark = pytest.mark.backends("git", "hg")
30 pytestmark = pytest.mark.backends("git", "hg")
@@ -46,10 +46,10 b' class TestGetPullRequest(object):'
46 assert response.status == '200 OK'
46 assert response.status == '200 OK'
47
47
48 url_obj = urlobject.URLObject(
48 url_obj = urlobject.URLObject(
49 url(
49 h.route_url(
50 'pullrequest_show',
50 'pullrequest_show',
51 repo_name=pull_request.target_repo.repo_name,
51 repo_name=pull_request.target_repo.repo_name,
52 pull_request_id=pull_request.pull_request_id, qualified=True))
52 pull_request_id=pull_request.pull_request_id))
53
53
54 pr_url = safe_unicode(
54 pr_url = safe_unicode(
55 url_obj.with_netloc(http_host_only_stub))
55 url_obj.with_netloc(http_host_only_stub))
@@ -116,7 +116,7 b' class TestGetRepos(object):'
116 response = api_call(self.app, params)
116 response = api_call(self.app, params)
117
117
118 user = User.get_by_username(self.TEST_USER_LOGIN)
118 user = User.get_by_username(self.TEST_USER_LOGIN)
119 allowed_repos = user.AuthUser.permissions['repositories']
119 allowed_repos = user.AuthUser().permissions['repositories']
120
120
121 result = []
121 result = []
122 for repo in RepoModel().get_all():
122 for repo in RepoModel().get_all():
@@ -274,7 +274,8 b' def merge_pull_request('
274
274
275 pull_request = get_pull_request_or_error(pullrequestid)
275 pull_request = get_pull_request_or_error(pullrequestid)
276
276
277 check = MergeCheck.validate(pull_request, user=apiuser)
277 check = MergeCheck.validate(
278 pull_request, user=apiuser, translator=request.translate)
278 merge_possible = not check.failed
279 merge_possible = not check.failed
279
280
280 if not merge_possible:
281 if not merge_possible:
@@ -33,6 +33,7 b' from rhodecode.lib import user_sessions'
33 from rhodecode.lib.utils2 import safe_int
33 from rhodecode.lib.utils2 import safe_int
34 from rhodecode.model.db import UserIpMap
34 from rhodecode.model.db import UserIpMap
35 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.settings import VcsSettingsModel
36
37
37 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
38
39
@@ -75,6 +76,35 b' def get_server_info(request, apiuser):'
75
76
76
77
77 @jsonrpc_method()
78 @jsonrpc_method()
79 def get_repo_store(request, apiuser):
80 """
81 Returns the |RCE| repository storage information.
82
83 :param apiuser: This is filled automatically from the |authtoken|.
84 :type apiuser: AuthUser
85
86 Example output:
87
88 .. code-block:: bash
89
90 id : <id_given_in_input>
91 result : {
92 'modules': [<module name>,...]
93 'py_version': <python version>,
94 'platform': <platform type>,
95 'rhodecode_version': <rhodecode version>
96 }
97 error : null
98 """
99
100 if not has_superadmin_permission(apiuser):
101 raise JSONRPCForbidden()
102
103 path = VcsSettingsModel().get_repos_location()
104 return {"path": path}
105
106
107 @jsonrpc_method()
78 def get_ip(request, apiuser, userid=Optional(OAttr('apiuser'))):
108 def get_ip(request, apiuser, userid=Optional(OAttr('apiuser'))):
79 """
109 """
80 Displays the IP Address as seen from the |RCE| server.
110 Displays the IP Address as seen from the |RCE| server.
@@ -464,6 +464,7 b' def add_user_to_user_group(request, apiu'
464 raise JSONRPCError('user group `%s` does not exist' % (
464 raise JSONRPCError('user group `%s` does not exist' % (
465 usergroupid,))
465 usergroupid,))
466
466
467 old_values = user_group.get_api_data()
467 try:
468 try:
468 ugm = UserGroupModel().add_user_to_group(user_group, user)
469 ugm = UserGroupModel().add_user_to_group(user_group, user)
469 success = True if ugm is not True else False
470 success = True if ugm is not True else False
@@ -474,7 +475,8 b' def add_user_to_user_group(request, apiu'
474 if success:
475 if success:
475 user_data = user.get_api_data()
476 user_data = user.get_api_data()
476 audit_logger.store_api(
477 audit_logger.store_api(
477 'user_group.edit.member.add', action_data={'user': user_data},
478 'user_group.edit.member.add',
479 action_data={'user': user_data, 'old_data': old_values},
478 user=apiuser)
480 user=apiuser)
479
481
480 Session().commit()
482 Session().commit()
@@ -534,6 +536,7 b' def remove_user_from_user_group(request,'
534 raise JSONRPCError(
536 raise JSONRPCError(
535 'user group `%s` does not exist' % (usergroupid,))
537 'user group `%s` does not exist' % (usergroupid,))
536
538
539 old_values = user_group.get_api_data()
537 try:
540 try:
538 success = UserGroupModel().remove_user_from_group(user_group, user)
541 success = UserGroupModel().remove_user_from_group(user_group, user)
539 msg = 'removed member `%s` from user group `%s`' % (
542 msg = 'removed member `%s` from user group `%s`' % (
@@ -543,7 +546,8 b' def remove_user_from_user_group(request,'
543 if success:
546 if success:
544 user_data = user.get_api_data()
547 user_data = user.get_api_data()
545 audit_logger.store_api(
548 audit_logger.store_api(
546 'user_group.edit.member.delete', action_data={'user': user_data},
549 'user_group.edit.member.delete',
550 action_data={'user': user_data, 'old_data': old_values},
547 user=apiuser)
551 user=apiuser)
548
552
549 Session().commit()
553 Session().commit()
@@ -20,16 +20,17 b''
20
20
21 import time
21 import time
22 import logging
22 import logging
23 from pylons import tmpl_context as c
23 import operator
24
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
25
26
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib.utils import PartialRenderer
28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
30 from rhodecode.lib.ext_json import json
31 from rhodecode.model import repo
30 from rhodecode.model import repo
32 from rhodecode.model import repo_group
31 from rhodecode.model import repo_group
32 from rhodecode.model import user_group
33 from rhodecode.model import user
33 from rhodecode.model.db import User
34 from rhodecode.model.db import User
34 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
35
36
@@ -39,6 +40,19 b' log = logging.getLogger(__name__)'
39 ADMIN_PREFIX = '/_admin'
40 ADMIN_PREFIX = '/_admin'
40 STATIC_FILE_PREFIX = '/_static'
41 STATIC_FILE_PREFIX = '/_static'
41
42
43 URL_NAME_REQUIREMENTS = {
44 # group name can have a slash in them, but they must not end with a slash
45 'group_name': r'.*?[^/]',
46 'repo_group_name': r'.*?[^/]',
47 # repo names can have a slash in them, but they must not end with a slash
48 'repo_name': r'.*?[^/]',
49 # file path eats up everything at the end
50 'f_path': r'.*',
51 # reference types
52 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
53 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
54 }
55
42
56
43 def add_route_with_slash(config,name, pattern, **kw):
57 def add_route_with_slash(config,name, pattern, **kw):
44 config.add_route(name, pattern, **kw)
58 config.add_route(name, pattern, **kw)
@@ -46,6 +60,17 b' def add_route_with_slash(config,name, pa'
46 config.add_route(name + '_slash', pattern + '/', **kw)
60 config.add_route(name + '_slash', pattern + '/', **kw)
47
61
48
62
63 def add_route_requirements(route_path, requirements=URL_NAME_REQUIREMENTS):
64 """
65 Adds regex requirements to pyramid routes using a mapping dict
66 e.g::
67 add_route_requirements('{repo_name}/settings')
68 """
69 for key, regex in requirements.items():
70 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
71 return route_path
72
73
49 def get_format_ref_id(repo):
74 def get_format_ref_id(repo):
50 """Returns a `repo` specific reference formatter function"""
75 """Returns a `repo` specific reference formatter function"""
51 if h.is_svn(repo):
76 if h.is_svn(repo):
@@ -105,23 +130,50 b' class BaseAppView(object):'
105 raise HTTPFound(
130 raise HTTPFound(
106 self.request.route_path('my_account_password'))
131 self.request.route_path('my_account_password'))
107
132
133 def _log_creation_exception(self, e, repo_name):
134 _ = self.request.translate
135 reason = None
136 if len(e.args) == 2:
137 reason = e.args[1]
138
139 if reason == 'INVALID_CERTIFICATE':
140 log.exception(
141 'Exception creating a repository: invalid certificate')
142 msg = (_('Error creating repository %s: invalid certificate')
143 % repo_name)
144 else:
145 log.exception("Exception creating a repository")
146 msg = (_('Error creating repository %s')
147 % repo_name)
148 return msg
149
108 def _get_local_tmpl_context(self, include_app_defaults=False):
150 def _get_local_tmpl_context(self, include_app_defaults=False):
109 c = TemplateArgs()
151 c = TemplateArgs()
110 c.auth_user = self.request.user
152 c.auth_user = self.request.user
153 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
154 c.rhodecode_user = self.request.user
155
111 if include_app_defaults:
156 if include_app_defaults:
112 # NOTE(marcink): after full pyramid migration include_app_defaults
157 # NOTE(marcink): after full pyramid migration include_app_defaults
113 # should be turned on by default
158 # should be turned on by default
114 from rhodecode.lib.base import attach_context_attributes
159 from rhodecode.lib.base import attach_context_attributes
115 attach_context_attributes(c, self.request, self.request.user.user_id)
160 attach_context_attributes(c, self.request, self.request.user.user_id)
161
116 return c
162 return c
117
163
118 def _register_global_c(self, tmpl_args):
164 def _register_global_c(self, tmpl_args):
119 """
165 """
120 Registers attributes to pylons global `c`
166 Registers attributes to pylons global `c`
121 """
167 """
168
122 # TODO(marcink): remove once pyramid migration is finished
169 # TODO(marcink): remove once pyramid migration is finished
123 for k, v in tmpl_args.items():
170 from pylons import tmpl_context as c
124 setattr(c, k, v)
171 try:
172 for k, v in tmpl_args.items():
173 setattr(c, k, v)
174 except TypeError:
175 log.exception('Failed to register pylons C')
176 pass
125
177
126 def _get_template_context(self, tmpl_args):
178 def _get_template_context(self, tmpl_args):
127 self._register_global_c(tmpl_args)
179 self._register_global_c(tmpl_args)
@@ -129,6 +181,10 b' class BaseAppView(object):'
129 local_tmpl_args = {
181 local_tmpl_args = {
130 'defaults': {},
182 'defaults': {},
131 'errors': {},
183 'errors': {},
184 # register a fake 'c' to be used in templates instead of global
185 # pylons c, after migration to pyramid we should rename it to 'c'
186 # make sure we replace usage of _c in templates too
187 '_c': tmpl_args
132 }
188 }
133 local_tmpl_args.update(tmpl_args)
189 local_tmpl_args.update(tmpl_args)
134 return local_tmpl_args
190 return local_tmpl_args
@@ -160,6 +216,7 b' class RepoAppView(BaseAppView):'
160 self.db_repo_name, error.message)
216 self.db_repo_name, error.message)
161
217
162 def _get_local_tmpl_context(self, include_app_defaults=False):
218 def _get_local_tmpl_context(self, include_app_defaults=False):
219 _ = self.request.translate
163 c = super(RepoAppView, self)._get_local_tmpl_context(
220 c = super(RepoAppView, self)._get_local_tmpl_context(
164 include_app_defaults=include_app_defaults)
221 include_app_defaults=include_app_defaults)
165
222
@@ -174,9 +231,70 b' class RepoAppView(BaseAppView):'
174 except RepositoryRequirementError as e:
231 except RepositoryRequirementError as e:
175 c.repository_requirements_missing = True
232 c.repository_requirements_missing = True
176 self._handle_missing_requirements(e)
233 self._handle_missing_requirements(e)
234 self.rhodecode_vcs_repo = None
235
236 if (not c.repository_requirements_missing
237 and self.rhodecode_vcs_repo is None):
238 # unable to fetch this repo as vcs instance, report back to user
239 h.flash(_(
240 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
241 "Please check if it exist, or is not damaged.") %
242 {'repo_name': c.repo_name},
243 category='error', ignore_duplicate=True)
244 raise HTTPFound(h.route_path('home'))
177
245
178 return c
246 return c
179
247
248 def _get_f_path(self, matchdict, default=None):
249 f_path = matchdict.get('f_path')
250 if f_path:
251 # fix for multiple initial slashes that causes errors for GIT
252 return f_path.lstrip('/')
253
254 return default
255
256
257 class RepoGroupAppView(BaseAppView):
258 def __init__(self, context, request):
259 super(RepoGroupAppView, self).__init__(context, request)
260 self.db_repo_group = request.db_repo_group
261 self.db_repo_group_name = self.db_repo_group.group_name
262
263 def _revoke_perms_on_yourself(self, form_result):
264 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
265 form_result['perm_updates'])
266 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
267 form_result['perm_additions'])
268 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
269 form_result['perm_deletions'])
270 admin_perm = 'group.admin'
271 if _updates and _updates[0][1] != admin_perm or \
272 _additions and _additions[0][1] != admin_perm or \
273 _deletions and _deletions[0][1] != admin_perm:
274 return True
275 return False
276
277
278 class UserGroupAppView(BaseAppView):
279 def __init__(self, context, request):
280 super(UserGroupAppView, self).__init__(context, request)
281 self.db_user_group = request.db_user_group
282 self.db_user_group_name = self.db_user_group.users_group_name
283
284
285 class UserAppView(BaseAppView):
286 def __init__(self, context, request):
287 super(UserAppView, self).__init__(context, request)
288 self.db_user = request.db_user
289 self.db_user_id = self.db_user.user_id
290
291 _ = self.request.translate
292 if not request.db_user_supports_default:
293 if self.db_user.username == User.DEFAULT_USER:
294 h.flash(_("Editing user `{}` is disabled.".format(
295 User.DEFAULT_USER)), category='warning')
296 raise HTTPFound(h.route_path('users'))
297
180
298
181 class DataGridAppView(object):
299 class DataGridAppView(object):
182 """
300 """
@@ -203,6 +321,15 b' class DataGridAppView(object):'
203 draw = safe_int(request.GET.get('draw'))
321 draw = safe_int(request.GET.get('draw'))
204 return draw, start, length
322 return draw, start, length
205
323
324 def _get_order_col(self, order_by, model):
325 if isinstance(order_by, basestring):
326 try:
327 return operator.attrgetter(order_by)(model)
328 except AttributeError:
329 return None
330 else:
331 return order_by
332
206
333
207 class BaseReferencesView(RepoAppView):
334 class BaseReferencesView(RepoAppView):
208 """
335 """
@@ -211,35 +338,48 b' class BaseReferencesView(RepoAppView):'
211 def load_default_context(self):
338 def load_default_context(self):
212 c = self._get_local_tmpl_context()
339 c = self._get_local_tmpl_context()
213
340
214 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
215 c.repo_info = self.db_repo
216
217 self._register_global_c(c)
341 self._register_global_c(c)
218 return c
342 return c
219
343
220 def load_refs_context(self, ref_items, partials_template):
344 def load_refs_context(self, ref_items, partials_template):
221 _render = PartialRenderer(partials_template)
345 _render = self.request.get_partial_renderer(partials_template)
222 _data = []
223 pre_load = ["author", "date", "message"]
346 pre_load = ["author", "date", "message"]
224
347
225 is_svn = h.is_svn(self.rhodecode_vcs_repo)
348 is_svn = h.is_svn(self.rhodecode_vcs_repo)
349 is_hg = h.is_hg(self.rhodecode_vcs_repo)
350
226 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
351 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
227
352
353 closed_refs = {}
354 if is_hg:
355 closed_refs = self.rhodecode_vcs_repo.branches_closed
356
357 data = []
228 for ref_name, commit_id in ref_items:
358 for ref_name, commit_id in ref_items:
229 commit = self.rhodecode_vcs_repo.get_commit(
359 commit = self.rhodecode_vcs_repo.get_commit(
230 commit_id=commit_id, pre_load=pre_load)
360 commit_id=commit_id, pre_load=pre_load)
361 closed = ref_name in closed_refs
231
362
232 # TODO: johbo: Unify generation of reference links
363 # TODO: johbo: Unify generation of reference links
233 use_commit_id = '/' in ref_name or is_svn
364 use_commit_id = '/' in ref_name or is_svn
234 files_url = h.url(
365
235 'files_home',
366 if use_commit_id:
236 repo_name=c.repo_name,
367 files_url = h.route_path(
237 f_path=ref_name if is_svn else '',
368 'repo_files',
238 revision=commit_id if use_commit_id else ref_name,
369 repo_name=self.db_repo_name,
239 at=ref_name)
370 f_path=ref_name if is_svn else '',
371 commit_id=commit_id)
240
372
241 _data.append({
373 else:
242 "name": _render('name', ref_name, files_url),
374 files_url = h.route_path(
375 'repo_files',
376 repo_name=self.db_repo_name,
377 f_path=ref_name if is_svn else '',
378 commit_id=ref_name,
379 _query=dict(at=ref_name))
380
381 data.append({
382 "name": _render('name', ref_name, files_url, closed),
243 "name_raw": ref_name,
383 "name_raw": ref_name,
244 "date": _render('date', commit.date),
384 "date": _render('date', commit.date),
245 "date_raw": datetime_to_time(commit.date),
385 "date_raw": datetime_to_time(commit.date),
@@ -250,8 +390,8 b' class BaseReferencesView(RepoAppView):'
250 "compare": _render(
390 "compare": _render(
251 'compare', format_ref_id(ref_name, commit.raw_id)),
391 'compare', format_ref_id(ref_name, commit.raw_id)),
252 })
392 })
253 c.has_references = bool(_data)
393
254 c.data = json.dumps(_data)
394 return data
255
395
256
396
257 class RepoRoutePredicate(object):
397 class RepoRoutePredicate(object):
@@ -273,14 +413,22 b' class RepoRoutePredicate(object):'
273 repo_model = repo.RepoModel()
413 repo_model = repo.RepoModel()
274 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
414 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
275
415
416 def redirect_if_creating(db_repo):
417 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
418 raise HTTPFound(
419 request.route_path('repo_creating',
420 repo_name=db_repo.repo_name))
421
276 if by_name_match:
422 if by_name_match:
277 # register this as request object we can re-use later
423 # register this as request object we can re-use later
278 request.db_repo = by_name_match
424 request.db_repo = by_name_match
425 redirect_if_creating(by_name_match)
279 return True
426 return True
280
427
281 by_id_match = repo_model.get_repo_by_id(repo_name)
428 by_id_match = repo_model.get_repo_by_id(repo_name)
282 if by_id_match:
429 if by_id_match:
283 request.db_repo = by_id_match
430 request.db_repo = by_id_match
431 redirect_if_creating(by_id_match)
284 return True
432 return True
285
433
286 return False
434 return False
@@ -348,6 +496,79 b' class RepoGroupRoutePredicate(object):'
348 return False
496 return False
349
497
350
498
499 class UserGroupRoutePredicate(object):
500 def __init__(self, val, config):
501 self.val = val
502
503 def text(self):
504 return 'user_group_route = %s' % self.val
505
506 phash = text
507
508 def __call__(self, info, request):
509 if hasattr(request, 'vcs_call'):
510 # skip vcs calls
511 return
512
513 user_group_id = info['match']['user_group_id']
514 user_group_model = user_group.UserGroup()
515 by_id_match = user_group_model.get(
516 user_group_id, cache=True)
517
518 if by_id_match:
519 # register this as request object we can re-use later
520 request.db_user_group = by_id_match
521 return True
522
523 return False
524
525
526 class UserRoutePredicateBase(object):
527 supports_default = None
528
529 def __init__(self, val, config):
530 self.val = val
531
532 def text(self):
533 raise NotImplementedError()
534
535 def __call__(self, info, request):
536 if hasattr(request, 'vcs_call'):
537 # skip vcs calls
538 return
539
540 user_id = info['match']['user_id']
541 user_model = user.User()
542 by_id_match = user_model.get(
543 user_id, cache=True)
544
545 if by_id_match:
546 # register this as request object we can re-use later
547 request.db_user = by_id_match
548 request.db_user_supports_default = self.supports_default
549 return True
550
551 return False
552
553
554 class UserRoutePredicate(UserRoutePredicateBase):
555 supports_default = False
556
557 def text(self):
558 return 'user_route = %s' % self.val
559
560 phash = text
561
562
563 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
564 supports_default = True
565
566 def text(self):
567 return 'user_with_default_route = %s' % self.val
568
569 phash = text
570
571
351 def includeme(config):
572 def includeme(config):
352 config.add_route_predicate(
573 config.add_route_predicate(
353 'repo_route', RepoRoutePredicate)
574 'repo_route', RepoRoutePredicate)
@@ -355,3 +576,9 b' def includeme(config):'
355 'repo_accepted_types', RepoTypeRoutePredicate)
576 'repo_accepted_types', RepoTypeRoutePredicate)
356 config.add_route_predicate(
577 config.add_route_predicate(
357 'repo_group_route', RepoGroupRoutePredicate)
578 'repo_group_route', RepoGroupRoutePredicate)
579 config.add_route_predicate(
580 'user_group_route', UserGroupRoutePredicate)
581 config.add_route_predicate(
582 'user_route_with_default', UserRouteWithDefaultPredicate)
583 config.add_route_predicate(
584 'user_route', UserRoutePredicate) No newline at end of file
@@ -34,14 +34,18 b' def admin_routes(config):'
34 pattern='/audit_logs')
34 pattern='/audit_logs')
35
35
36 config.add_route(
36 config.add_route(
37 name='admin_audit_log_entry',
38 pattern='/audit_logs/{audit_log_id}')
39
40 config.add_route(
37 name='pull_requests_global_0', # backward compat
41 name='pull_requests_global_0', # backward compat
38 pattern='/pull_requests/{pull_request_id:[0-9]+}')
42 pattern='/pull_requests/{pull_request_id:\d+}')
39 config.add_route(
43 config.add_route(
40 name='pull_requests_global_1', # backward compat
44 name='pull_requests_global_1', # backward compat
41 pattern='/pull-requests/{pull_request_id:[0-9]+}')
45 pattern='/pull-requests/{pull_request_id:\d+}')
42 config.add_route(
46 config.add_route(
43 name='pull_requests_global',
47 name='pull_requests_global',
44 pattern='/pull-request/{pull_request_id:[0-9]+}')
48 pattern='/pull-request/{pull_request_id:\d+}')
45
49
46 config.add_route(
50 config.add_route(
47 name='admin_settings_open_source',
51 name='admin_settings_open_source',
@@ -64,11 +68,66 b' def admin_routes(config):'
64 name='admin_settings_sessions_cleanup',
68 name='admin_settings_sessions_cleanup',
65 pattern='/settings/sessions/cleanup')
69 pattern='/settings/sessions/cleanup')
66
70
71 config.add_route(
72 name='admin_settings_process_management',
73 pattern='/settings/process_management')
74 config.add_route(
75 name='admin_settings_process_management_signal',
76 pattern='/settings/process_management/signal')
77
78 # default settings
79 config.add_route(
80 name='admin_defaults_repositories',
81 pattern='/defaults/repositories')
82 config.add_route(
83 name='admin_defaults_repositories_update',
84 pattern='/defaults/repositories/update')
85
67 # global permissions
86 # global permissions
87
88 config.add_route(
89 name='admin_permissions_application',
90 pattern='/permissions/application')
91 config.add_route(
92 name='admin_permissions_application_update',
93 pattern='/permissions/application/update')
94
95 config.add_route(
96 name='admin_permissions_global',
97 pattern='/permissions/global')
98 config.add_route(
99 name='admin_permissions_global_update',
100 pattern='/permissions/global/update')
101
102 config.add_route(
103 name='admin_permissions_object',
104 pattern='/permissions/object')
105 config.add_route(
106 name='admin_permissions_object_update',
107 pattern='/permissions/object/update')
108
68 config.add_route(
109 config.add_route(
69 name='admin_permissions_ips',
110 name='admin_permissions_ips',
70 pattern='/permissions/ips')
111 pattern='/permissions/ips')
71
112
113 config.add_route(
114 name='admin_permissions_overview',
115 pattern='/permissions/overview')
116
117 config.add_route(
118 name='admin_permissions_auth_token_access',
119 pattern='/permissions/auth_token_access')
120
121 config.add_route(
122 name='admin_permissions_ssh_keys',
123 pattern='/permissions/ssh_keys')
124 config.add_route(
125 name='admin_permissions_ssh_keys_data',
126 pattern='/permissions/ssh_keys/data')
127 config.add_route(
128 name='admin_permissions_ssh_keys_update',
129 pattern='/permissions/ssh_keys/update')
130
72 # users admin
131 # users admin
73 config.add_route(
132 config.add_route(
74 name='users',
133 name='users',
@@ -78,52 +137,176 b' def admin_routes(config):'
78 name='users_data',
137 name='users_data',
79 pattern='/users_data')
138 pattern='/users_data')
80
139
140 config.add_route(
141 name='users_create',
142 pattern='/users/create')
143
144 config.add_route(
145 name='users_new',
146 pattern='/users/new')
147
148 # user management
149 config.add_route(
150 name='user_edit',
151 pattern='/users/{user_id:\d+}/edit',
152 user_route=True)
153 config.add_route(
154 name='user_edit_advanced',
155 pattern='/users/{user_id:\d+}/edit/advanced',
156 user_route=True)
157 config.add_route(
158 name='user_edit_global_perms',
159 pattern='/users/{user_id:\d+}/edit/global_permissions',
160 user_route=True)
161 config.add_route(
162 name='user_edit_global_perms_update',
163 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
164 user_route=True)
165 config.add_route(
166 name='user_update',
167 pattern='/users/{user_id:\d+}/update',
168 user_route=True)
169 config.add_route(
170 name='user_delete',
171 pattern='/users/{user_id:\d+}/delete',
172 user_route=True)
173 config.add_route(
174 name='user_force_password_reset',
175 pattern='/users/{user_id:\d+}/password_reset',
176 user_route=True)
177 config.add_route(
178 name='user_create_personal_repo_group',
179 pattern='/users/{user_id:\d+}/create_repo_group',
180 user_route=True)
181
81 # user auth tokens
182 # user auth tokens
82 config.add_route(
183 config.add_route(
83 name='edit_user_auth_tokens',
184 name='edit_user_auth_tokens',
84 pattern='/users/{user_id:\d+}/edit/auth_tokens')
185 pattern='/users/{user_id:\d+}/edit/auth_tokens',
186 user_route=True)
85 config.add_route(
187 config.add_route(
86 name='edit_user_auth_tokens_add',
188 name='edit_user_auth_tokens_add',
87 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
189 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
190 user_route=True)
88 config.add_route(
191 config.add_route(
89 name='edit_user_auth_tokens_delete',
192 name='edit_user_auth_tokens_delete',
90 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
193 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
194 user_route=True)
195
196 # user ssh keys
197 config.add_route(
198 name='edit_user_ssh_keys',
199 pattern='/users/{user_id:\d+}/edit/ssh_keys',
200 user_route=True)
201 config.add_route(
202 name='edit_user_ssh_keys_generate_keypair',
203 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
204 user_route=True)
205 config.add_route(
206 name='edit_user_ssh_keys_add',
207 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
208 user_route=True)
209 config.add_route(
210 name='edit_user_ssh_keys_delete',
211 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
212 user_route=True)
91
213
92 # user emails
214 # user emails
93 config.add_route(
215 config.add_route(
94 name='edit_user_emails',
216 name='edit_user_emails',
95 pattern='/users/{user_id:\d+}/edit/emails')
217 pattern='/users/{user_id:\d+}/edit/emails',
218 user_route=True)
96 config.add_route(
219 config.add_route(
97 name='edit_user_emails_add',
220 name='edit_user_emails_add',
98 pattern='/users/{user_id:\d+}/edit/emails/new')
221 pattern='/users/{user_id:\d+}/edit/emails/new',
222 user_route=True)
99 config.add_route(
223 config.add_route(
100 name='edit_user_emails_delete',
224 name='edit_user_emails_delete',
101 pattern='/users/{user_id:\d+}/edit/emails/delete')
225 pattern='/users/{user_id:\d+}/edit/emails/delete',
226 user_route=True)
102
227
103 # user IPs
228 # user IPs
104 config.add_route(
229 config.add_route(
105 name='edit_user_ips',
230 name='edit_user_ips',
106 pattern='/users/{user_id:\d+}/edit/ips')
231 pattern='/users/{user_id:\d+}/edit/ips',
232 user_route=True)
107 config.add_route(
233 config.add_route(
108 name='edit_user_ips_add',
234 name='edit_user_ips_add',
109 pattern='/users/{user_id:\d+}/edit/ips/new')
235 pattern='/users/{user_id:\d+}/edit/ips/new',
236 user_route_with_default=True) # enabled for default user too
110 config.add_route(
237 config.add_route(
111 name='edit_user_ips_delete',
238 name='edit_user_ips_delete',
112 pattern='/users/{user_id:\d+}/edit/ips/delete')
239 pattern='/users/{user_id:\d+}/edit/ips/delete',
240 user_route_with_default=True) # enabled for default user too
113
241
114 # user groups management
242 # user perms
243 config.add_route(
244 name='edit_user_perms_summary',
245 pattern='/users/{user_id:\d+}/edit/permissions_summary',
246 user_route=True)
247 config.add_route(
248 name='edit_user_perms_summary_json',
249 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
250 user_route=True)
251
252 # user user groups management
115 config.add_route(
253 config.add_route(
116 name='edit_user_groups_management',
254 name='edit_user_groups_management',
117 pattern='/users/{user_id:\d+}/edit/groups_management')
255 pattern='/users/{user_id:\d+}/edit/groups_management',
256 user_route=True)
118
257
119 config.add_route(
258 config.add_route(
120 name='edit_user_groups_management_updates',
259 name='edit_user_groups_management_updates',
121 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
260 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
261 user_route=True)
122
262
123 # user audit logs
263 # user audit logs
124 config.add_route(
264 config.add_route(
125 name='edit_user_audit_logs',
265 name='edit_user_audit_logs',
126 pattern='/users/{user_id:\d+}/edit/audit')
266 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
267
268 # user-groups admin
269 config.add_route(
270 name='user_groups',
271 pattern='/user_groups')
272
273 config.add_route(
274 name='user_groups_data',
275 pattern='/user_groups_data')
276
277 config.add_route(
278 name='user_groups_new',
279 pattern='/user_groups/new')
280
281 config.add_route(
282 name='user_groups_create',
283 pattern='/user_groups/create')
284
285 # repos admin
286 config.add_route(
287 name='repos',
288 pattern='/repos')
289
290 config.add_route(
291 name='repo_new',
292 pattern='/repos/new')
293
294 config.add_route(
295 name='repo_create',
296 pattern='/repos/create')
297
298 # repo groups admin
299 config.add_route(
300 name='repo_groups',
301 pattern='/repo_groups')
302
303 config.add_route(
304 name='repo_group_new',
305 pattern='/repo_group/new')
306
307 config.add_route(
308 name='repo_group_create',
309 pattern='/repo_group/create')
127
310
128
311
129 def includeme(config):
312 def includeme(config):
@@ -139,4 +322,4 b' def includeme(config):'
139 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
322 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
140
323
141 # Scan module for configuration decorators.
324 # Scan module for configuration decorators.
142 config.scan()
325 config.scan('.views', ignore='.tests')
@@ -22,7 +22,6 b''
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pylons import url
26 from zope.interface import implementer
25 from zope.interface import implementer
27
26
28 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
27 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
@@ -64,6 +63,7 b' class NavEntry(object):'
64 pyramid_request = get_current_request()
63 pyramid_request = get_current_request()
65 return pyramid_request.route_path(self.view_name)
64 return pyramid_request.route_path(self.view_name)
66 else:
65 else:
66 from pylons import url
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):
@@ -94,6 +94,8 b' class NavigationRegistry(object):'
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('process_management', _('Processes'),
98 'admin_settings_process_management', pyramid=True),
97 NavEntry('sessions', _('User Sessions'),
99 NavEntry('sessions', _('User Sessions'),
98 'admin_settings_sessions', pyramid=True),
100 'admin_settings_sessions', pyramid=True),
99 NavEntry('open_source', _('Open Source Licenses'),
101 NavEntry('open_source', _('Open Source Licenses'),
@@ -20,15 +20,31 b''
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.tests import assert_session_flash, url
23 from rhodecode.tests import assert_session_flash
24 from rhodecode.model.settings import SettingsModel
24 from rhodecode.model.settings import SettingsModel
25
25
26
26
27 def route_path(name, params=None, **kwargs):
28 import urllib
29 from rhodecode.apps._base import ADMIN_PREFIX
30
31 base_url = {
32 'admin_defaults_repositories':
33 ADMIN_PREFIX + '/defaults/repositories',
34 'admin_defaults_repositories_update':
35 ADMIN_PREFIX + '/defaults/repositories/update',
36 }[name].format(**kwargs)
37
38 if params:
39 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
40 return base_url
41
42
27 @pytest.mark.usefixtures("app")
43 @pytest.mark.usefixtures("app")
28 class TestDefaultsController:
44 class TestDefaultsView(object):
29
45
30 def test_index(self, autologin_user):
46 def test_index(self, autologin_user):
31 response = self.app.get(url('admin_defaults_repositories'))
47 response = self.app.get(route_path('admin_defaults_repositories'))
32 response.mustcontain('default_repo_private')
48 response.mustcontain('default_repo_private')
33 response.mustcontain('default_repo_enable_statistics')
49 response.mustcontain('default_repo_enable_statistics')
34 response.mustcontain('default_repo_enable_downloads')
50 response.mustcontain('default_repo_enable_downloads')
@@ -44,7 +60,7 b' class TestDefaultsController:'
44 'csrf_token': csrf_token,
60 'csrf_token': csrf_token,
45 }
61 }
46 response = self.app.post(
62 response = self.app.post(
47 url('admin_defaults_repositories'), params=params)
63 route_path('admin_defaults_repositories_update'), params=params)
48 assert_session_flash(response, 'Default settings updated successfully')
64 assert_session_flash(response, 'Default settings updated successfully')
49
65
50 defs = SettingsModel().get_default_repo_settings()
66 defs = SettingsModel().get_default_repo_settings()
@@ -61,8 +77,9 b' class TestDefaultsController:'
61 'csrf_token': csrf_token,
77 'csrf_token': csrf_token,
62 }
78 }
63 response = self.app.post(
79 response = self.app.post(
64 url('admin_defaults_repositories'), params=params)
80 route_path('admin_defaults_repositories_update'), params=params)
65 assert_session_flash(response, 'Default settings updated successfully')
81 assert_session_flash(response, 'Default settings updated successfully')
82
66 defs = SettingsModel().get_default_repo_settings()
83 defs = SettingsModel().get_default_repo_settings()
67 del params['csrf_token']
84 del params['csrf_token']
68 assert params == defs
85 assert params == defs
@@ -18,11 +18,14 b''
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 pytest
22 import pytest
22 from rhodecode.model.db import User, UserIpMap
23 from rhodecode.model.db import User, UserIpMap
24 from rhodecode.model.meta import Session
23 from rhodecode.model.permission import PermissionModel
25 from rhodecode.model.permission import PermissionModel
26 from rhodecode.model.ssh_key import SshKeyModel
24 from rhodecode.tests import (
27 from rhodecode.tests import (
25 TestController, url, clear_all_caches, assert_session_flash)
28 TestController, clear_all_caches, assert_session_flash)
26
29
27
30
28 def route_path(name, params=None, **kwargs):
31 def route_path(name, params=None, **kwargs):
@@ -36,6 +39,34 b' def route_path(name, params=None, **kwar'
36 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
39 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
37 'edit_user_ips_delete':
40 'edit_user_ips_delete':
38 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
41 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
42
43 'admin_permissions_application':
44 ADMIN_PREFIX + '/permissions/application',
45 'admin_permissions_application_update':
46 ADMIN_PREFIX + '/permissions/application/update',
47
48 'admin_permissions_global':
49 ADMIN_PREFIX + '/permissions/global',
50 'admin_permissions_global_update':
51 ADMIN_PREFIX + '/permissions/global/update',
52
53 'admin_permissions_object':
54 ADMIN_PREFIX + '/permissions/object',
55 'admin_permissions_object_update':
56 ADMIN_PREFIX + '/permissions/object/update',
57
58 'admin_permissions_ips':
59 ADMIN_PREFIX + '/permissions/ips',
60 'admin_permissions_overview':
61 ADMIN_PREFIX + '/permissions/overview',
62
63 'admin_permissions_ssh_keys':
64 ADMIN_PREFIX + '/permissions/ssh_keys',
65 'admin_permissions_ssh_keys_data':
66 ADMIN_PREFIX + '/permissions/ssh_keys/data',
67 'admin_permissions_ssh_keys_update':
68 ADMIN_PREFIX + '/permissions/ssh_keys/update'
69
39 }[name].format(**kwargs)
70 }[name].format(**kwargs)
40
71
41 if params:
72 if params:
@@ -55,7 +86,7 b' class TestAdminPermissionsController(Tes'
55
86
56 def test_index_application(self):
87 def test_index_application(self):
57 self.log_user()
88 self.log_user()
58 self.app.get(url('admin_permissions_application'))
89 self.app.get(route_path('admin_permissions_application'))
59
90
60 @pytest.mark.parametrize(
91 @pytest.mark.parametrize(
61 'anonymous, default_register, default_register_message, default_password_reset,'
92 'anonymous, default_register, default_register_message, default_password_reset,'
@@ -87,7 +118,7 b' class TestAdminPermissionsController(Tes'
87 'default_password_reset': default_password_reset,
118 'default_password_reset': default_password_reset,
88 'default_extern_activate': default_extern_activate,
119 'default_extern_activate': default_extern_activate,
89 }
120 }
90 response = self.app.post(url('admin_permissions_application'),
121 response = self.app.post(route_path('admin_permissions_application_update'),
91 params=params)
122 params=params)
92 if expect_form_error:
123 if expect_form_error:
93 assert response.status_int == 200
124 assert response.status_int == 200
@@ -101,7 +132,7 b' class TestAdminPermissionsController(Tes'
101
132
102 def test_index_object(self):
133 def test_index_object(self):
103 self.log_user()
134 self.log_user()
104 self.app.get(url('admin_permissions_object'))
135 self.app.get(route_path('admin_permissions_object'))
105
136
106 @pytest.mark.parametrize(
137 @pytest.mark.parametrize(
107 'repo, repo_group, user_group, expect_error, expect_form_error', [
138 'repo, repo_group, user_group, expect_error, expect_form_error', [
@@ -127,7 +158,7 b' class TestAdminPermissionsController(Tes'
127 'default_user_group_perm': user_group,
158 'default_user_group_perm': user_group,
128 'overwrite_default_user_group': False,
159 'overwrite_default_user_group': False,
129 }
160 }
130 response = self.app.post(url('admin_permissions_object'),
161 response = self.app.post(route_path('admin_permissions_object_update'),
131 params=params)
162 params=params)
132 if expect_form_error:
163 if expect_form_error:
133 assert response.status_int == 200
164 assert response.status_int == 200
@@ -141,7 +172,7 b' class TestAdminPermissionsController(Tes'
141
172
142 def test_index_global(self):
173 def test_index_global(self):
143 self.log_user()
174 self.log_user()
144 self.app.get(url('admin_permissions_global'))
175 self.app.get(route_path('admin_permissions_global'))
145
176
146 @pytest.mark.parametrize(
177 @pytest.mark.parametrize(
147 'repo_create, repo_create_write, user_group_create, repo_group_create,'
178 'repo_create, repo_create_write, user_group_create, repo_group_create,'
@@ -175,7 +206,7 b' class TestAdminPermissionsController(Tes'
175 'default_fork_create': fork_create,
206 'default_fork_create': fork_create,
176 'default_inherit_default_permissions': inherit_default_permissions
207 'default_inherit_default_permissions': inherit_default_permissions
177 }
208 }
178 response = self.app.post(url('admin_permissions_global'),
209 response = self.app.post(route_path('admin_permissions_global_update'),
179 params=params)
210 params=params)
180 if expect_form_error:
211 if expect_form_error:
181 assert response.status_int == 200
212 assert response.status_int == 200
@@ -189,7 +220,7 b' class TestAdminPermissionsController(Tes'
189
220
190 def test_index_ips(self):
221 def test_index_ips(self):
191 self.log_user()
222 self.log_user()
192 response = self.app.get(url('admin_permissions_ips'))
223 response = self.app.get(route_path('admin_permissions_ips'))
193 # TODO: Test response...
224 # TODO: Test response...
194 response.mustcontain('All IP addresses are allowed')
225 response.mustcontain('All IP addresses are allowed')
195
226
@@ -203,7 +234,7 b' class TestAdminPermissionsController(Tes'
203 route_path('edit_user_ips_add', user_id=default_user_id),
234 route_path('edit_user_ips_add', user_id=default_user_id),
204 params={'new_ip': '127.0.0.0/24', 'csrf_token': self.csrf_token})
235 params={'new_ip': '127.0.0.0/24', 'csrf_token': self.csrf_token})
205
236
206 response = self.app.get(url('admin_permissions_ips'))
237 response = self.app.get(route_path('admin_permissions_ips'))
207 response.mustcontain('127.0.0.0/24')
238 response.mustcontain('127.0.0.0/24')
208 response.mustcontain('127.0.0.0 - 127.0.0.255')
239 response.mustcontain('127.0.0.0 - 127.0.0.255')
209
240
@@ -219,11 +250,51 b' class TestAdminPermissionsController(Tes'
219 assert_session_flash(response, 'Removed ip address from user whitelist')
250 assert_session_flash(response, 'Removed ip address from user whitelist')
220
251
221 clear_all_caches()
252 clear_all_caches()
222 response = self.app.get(url('admin_permissions_ips'))
253 response = self.app.get(route_path('admin_permissions_ips'))
223 response.mustcontain('All IP addresses are allowed')
254 response.mustcontain('All IP addresses are allowed')
224 response.mustcontain(no=['127.0.0.0/24'])
255 response.mustcontain(no=['127.0.0.0/24'])
225 response.mustcontain(no=['127.0.0.0 - 127.0.0.255'])
256 response.mustcontain(no=['127.0.0.0 - 127.0.0.255'])
226
257
227 def test_index_overview(self):
258 def test_index_overview(self):
228 self.log_user()
259 self.log_user()
229 self.app.get(url('admin_permissions_overview'))
260 self.app.get(route_path('admin_permissions_overview'))
261
262 def test_ssh_keys(self):
263 self.log_user()
264 self.app.get(route_path('admin_permissions_ssh_keys'), status=200)
265
266 def test_ssh_keys_data(self, user_util, xhr_header):
267 self.log_user()
268 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
269 extra_environ=xhr_header)
270 assert response.json == {u'data': [], u'draw': None,
271 u'recordsFiltered': 0, u'recordsTotal': 0}
272
273 dummy_user = user_util.create_user()
274 SshKeyModel().create(dummy_user, 'ab:cd:ef', 'KEYKEY', 'test_key')
275 Session().commit()
276 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
277 extra_environ=xhr_header)
278 assert response.json['data'][0]['fingerprint'] == 'ab:cd:ef'
279
280 def test_ssh_keys_update(self):
281 self.log_user()
282 response = self.app.post(
283 route_path('admin_permissions_ssh_keys_update'),
284 dict(csrf_token=self.csrf_token), status=302)
285
286 assert_session_flash(
287 response, 'Updated SSH keys file')
288
289 def test_ssh_keys_update_disabled(self):
290 self.log_user()
291
292 from rhodecode.apps.admin.views.permissions import AdminPermissionsView
293 with mock.patch.object(AdminPermissionsView, 'ssh_enabled',
294 return_value=False):
295 response = self.app.post(
296 route_path('admin_permissions_ssh_keys_update'),
297 dict(csrf_token=self.csrf_token), status=302)
298
299 assert_session_flash(
300 response, 'SSH key support is disabled in .ini file') No newline at end of file
This diff has been collapsed as it changes many lines, (730 lines changed) Show them Hide them
@@ -23,55 +23,83 b' import urllib'
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.lib import auth
27 from rhodecode.lib import auth
27 from rhodecode.lib.utils2 import safe_str, str2bool
28 from rhodecode.lib.utils2 import safe_str
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
29 from rhodecode.model.db import (
30 from rhodecode.model.db import (
30 Repository, RepoGroup, UserRepoToPerm, User, Permission)
31 Repository, RepoGroup, UserRepoToPerm, User, Permission)
31 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
35 from rhodecode.model.user import UserModel
35 from rhodecode.model.user import UserModel
36 from rhodecode.tests import (
36 from rhodecode.tests import (
37 login_user_session, url, assert_session_flash, TEST_USER_ADMIN_LOGIN,
37 login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, HG_REPO, GIT_REPO,
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
39 logout_user_session)
40 from rhodecode.tests.fixture import Fixture, error_function
39 from rhodecode.tests.fixture import Fixture, error_function
41 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
40 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
42
41
43 fixture = Fixture()
42 fixture = Fixture()
44
43
45
44
45 def route_path(name, params=None, **kwargs):
46 import urllib
47
48 base_url = {
49 'repos': ADMIN_PREFIX + '/repos',
50 'repo_new': ADMIN_PREFIX + '/repos/new',
51 'repo_create': ADMIN_PREFIX + '/repos/create',
52
53 'repo_creating_check': '/{repo_name}/repo_creating_check',
54 }[name].format(**kwargs)
55
56 if params:
57 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
58 return base_url
59
60
61 def _get_permission_for_user(user, repo):
62 perm = UserRepoToPerm.query()\
63 .filter(UserRepoToPerm.repository ==
64 Repository.get_by_repo_name(repo))\
65 .filter(UserRepoToPerm.user == User.get_by_username(user))\
66 .all()
67 return perm
68
69
46 @pytest.mark.usefixtures("app")
70 @pytest.mark.usefixtures("app")
47 class TestAdminRepos(object):
71 class TestAdminRepos(object):
48
72
49 def test_index(self):
73 def test_repo_list(self, autologin_user, user_util):
50 self.app.get(url('repos'))
74 repo = user_util.create_repo()
75 response = self.app.get(
76 route_path('repos'), status=200)
51
77
52 def test_create_page_restricted(self, autologin_user, backend):
78 response.mustcontain(repo.repo_name)
79
80 def test_create_page_restricted_to_single_backend(self, autologin_user, backend):
53 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
81 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
54 response = self.app.get(url('new_repo'), status=200)
82 response = self.app.get(route_path('repo_new'), status=200)
55 assert_response = AssertResponse(response)
83 assert_response = AssertResponse(response)
56 element = assert_response.get_element('#repo_type')
84 element = assert_response.get_element('#repo_type')
57 assert element.text_content() == '\ngit\n'
85 assert element.text_content() == '\ngit\n'
58
86
59 def test_create_page_non_restricted(self, autologin_user, backend):
87 def test_create_page_non_restricted_backends(self, autologin_user, backend):
60 response = self.app.get(url('new_repo'), status=200)
88 response = self.app.get(route_path('repo_new'), status=200)
61 assert_response = AssertResponse(response)
89 assert_response = AssertResponse(response)
62 assert_response.element_contains('#repo_type', 'git')
90 assert_response.element_contains('#repo_type', 'git')
63 assert_response.element_contains('#repo_type', 'svn')
91 assert_response.element_contains('#repo_type', 'svn')
64 assert_response.element_contains('#repo_type', 'hg')
92 assert_response.element_contains('#repo_type', 'hg')
65
93
66 @pytest.mark.parametrize("suffix",
94 @pytest.mark.parametrize(
67 [u'', u'xxa'], ids=['', 'non-ascii'])
95 "suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
68 def test_create(self, autologin_user, backend, suffix, csrf_token):
96 def test_create(self, autologin_user, backend, suffix, csrf_token):
69 repo_name_unicode = backend.new_repo_name(suffix=suffix)
97 repo_name_unicode = backend.new_repo_name(suffix=suffix)
70 repo_name = repo_name_unicode.encode('utf8')
98 repo_name = repo_name_unicode.encode('utf8')
71 description_unicode = u'description for newly created repo' + suffix
99 description_unicode = u'description for newly created repo' + suffix
72 description = description_unicode.encode('utf8')
100 description = description_unicode.encode('utf8')
73 response = self.app.post(
101 response = self.app.post(
74 url('repos'),
102 route_path('repo_create'),
75 fixture._get_repo_create_params(
103 fixture._get_repo_create_params(
76 repo_private=False,
104 repo_private=False,
77 repo_name=repo_name,
105 repo_name=repo_name,
@@ -83,12 +111,12 b' class TestAdminRepos(object):'
83 self.assert_repository_is_created_correctly(
111 self.assert_repository_is_created_correctly(
84 repo_name, description, backend)
112 repo_name, description, backend)
85
113
86 def test_create_numeric(self, autologin_user, backend, csrf_token):
114 def test_create_numeric_name(self, autologin_user, backend, csrf_token):
87 numeric_repo = '1234'
115 numeric_repo = '1234'
88 repo_name = numeric_repo
116 repo_name = numeric_repo
89 description = 'description for newly created repo' + numeric_repo
117 description = 'description for newly created repo' + numeric_repo
90 self.app.post(
118 self.app.post(
91 url('repos'),
119 route_path('repo_create'),
92 fixture._get_repo_create_params(
120 fixture._get_repo_create_params(
93 repo_private=False,
121 repo_private=False,
94 repo_name=repo_name,
122 repo_name=repo_name,
@@ -114,7 +142,7 b' class TestAdminRepos(object):'
114 [group_name, repo_name])
142 [group_name, repo_name])
115 description = u'description for newly created repo'
143 description = u'description for newly created repo'
116 self.app.post(
144 self.app.post(
117 url('repos'),
145 route_path('repo_create'),
118 fixture._get_repo_create_params(
146 fixture._get_repo_create_params(
119 repo_private=False,
147 repo_private=False,
120 repo_name=safe_str(repo_name),
148 repo_name=safe_str(repo_name),
@@ -137,7 +165,7 b' class TestAdminRepos(object):'
137 RepoGroupModel().delete(group_name)
165 RepoGroupModel().delete(group_name)
138 Session().commit()
166 Session().commit()
139
167
140 def test_create_in_group_numeric(
168 def test_create_in_group_numeric_name(
141 self, autologin_user, backend, csrf_token):
169 self, autologin_user, backend, csrf_token):
142 # create GROUP
170 # create GROUP
143 group_name = 'sometest_%s' % backend.alias
171 group_name = 'sometest_%s' % backend.alias
@@ -150,7 +178,7 b' class TestAdminRepos(object):'
150 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
178 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
151 description = 'description for newly created repo'
179 description = 'description for newly created repo'
152 self.app.post(
180 self.app.post(
153 url('repos'),
181 route_path('repo_create'),
154 fixture._get_repo_create_params(
182 fixture._get_repo_create_params(
155 repo_private=False,
183 repo_private=False,
156 repo_name=repo_name,
184 repo_name=repo_name,
@@ -209,7 +237,7 b' class TestAdminRepos(object):'
209 repo_name = 'ingroup'
237 repo_name = 'ingroup'
210 description = 'description for newly created repo'
238 description = 'description for newly created repo'
211 response = self.app.post(
239 response = self.app.post(
212 url('repos'),
240 route_path('repo_create'),
213 fixture._get_repo_create_params(
241 fixture._get_repo_create_params(
214 repo_private=False,
242 repo_private=False,
215 repo_name=repo_name,
243 repo_name=repo_name,
@@ -226,7 +254,7 b' class TestAdminRepos(object):'
226 [group_name_allowed, repo_name])
254 [group_name_allowed, repo_name])
227 description = 'description for newly created repo'
255 description = 'description for newly created repo'
228 response = self.app.post(
256 response = self.app.post(
229 url('repos'),
257 route_path('repo_create'),
230 fixture._get_repo_create_params(
258 fixture._get_repo_create_params(
231 repo_private=False,
259 repo_private=False,
232 repo_name=repo_name,
260 repo_name=repo_name,
@@ -270,7 +298,7 b' class TestAdminRepos(object):'
270 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
298 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
271 description = 'description for newly created repo'
299 description = 'description for newly created repo'
272 self.app.post(
300 self.app.post(
273 url('repos'),
301 route_path('repo_create'),
274 fixture._get_repo_create_params(
302 fixture._get_repo_create_params(
275 repo_private=False,
303 repo_private=False,
276 repo_name=repo_name,
304 repo_name=repo_name,
@@ -314,7 +342,7 b' class TestAdminRepos(object):'
314
342
315 repo_name = backend.new_repo_name()
343 repo_name = backend.new_repo_name()
316 response = self.app.post(
344 response = self.app.post(
317 url('repos'),
345 route_path('repo_create'),
318 fixture._get_repo_create_params(
346 fixture._get_repo_create_params(
319 repo_private=False,
347 repo_private=False,
320 repo_name=repo_name,
348 repo_name=repo_name,
@@ -342,7 +370,7 b' class TestAdminRepos(object):'
342 repo_name = backend.new_repo_name()
370 repo_name = backend.new_repo_name()
343 description = 'description for newly created repo'
371 description = 'description for newly created repo'
344 response = self.app.post(
372 response = self.app.post(
345 url('repos'),
373 route_path('repo_create'),
346 fixture._get_repo_create_params(
374 fixture._get_repo_create_params(
347 repo_private=False,
375 repo_private=False,
348 repo_name=repo_name,
376 repo_name=repo_name,
@@ -358,7 +386,7 b' class TestAdminRepos(object):'
358 repo_name = backend.new_repo_name()
386 repo_name = backend.new_repo_name()
359 description = 'description for newly created repo'
387 description = 'description for newly created repo'
360 response = self.app.post(
388 response = self.app.post(
361 url('repos'),
389 route_path('repo_create'),
362 fixture._get_repo_create_params(
390 fixture._get_repo_create_params(
363 repo_private=False,
391 repo_private=False,
364 repo_name=repo_name,
392 repo_name=repo_name,
@@ -373,7 +401,7 b' class TestAdminRepos(object):'
373 repo_name = backend.new_repo_name() + ".git"
401 repo_name = backend.new_repo_name() + ".git"
374 description = 'description for newly created repo'
402 description = 'description for newly created repo'
375 response = self.app.post(
403 response = self.app.post(
376 url('repos'),
404 route_path('repo_create'),
377 fixture._get_repo_create_params(
405 fixture._get_repo_create_params(
378 repo_private=False,
406 repo_private=False,
379 repo_name=repo_name,
407 repo_name=repo_name,
@@ -382,11 +410,8 b' class TestAdminRepos(object):'
382 csrf_token=csrf_token))
410 csrf_token=csrf_token))
383 response.mustcontain('Repository name cannot end with .git')
411 response.mustcontain('Repository name cannot end with .git')
384
412
385 def test_show(self, autologin_user, backend):
386 self.app.get(url('repo', repo_name=backend.repo_name))
387
388 def test_default_user_cannot_access_private_repo_in_a_group(
413 def test_default_user_cannot_access_private_repo_in_a_group(
389 self, autologin_user, user_util, backend, csrf_token):
414 self, autologin_user, user_util, backend):
390
415
391 group = user_util.create_repo_group()
416 group = user_util.create_repo_group()
392
417
@@ -422,7 +447,7 b' class TestAdminRepos(object):'
422 repo_name = backend.new_repo_name()
447 repo_name = backend.new_repo_name()
423 description = 'description for newly created repo'
448 description = 'description for newly created repo'
424 response = self.app.post(
449 response = self.app.post(
425 url('repos'),
450 route_path('repo_create'),
426 fixture._get_repo_create_params(
451 fixture._get_repo_create_params(
427 repo_private=False,
452 repo_private=False,
428 repo_name=repo_name,
453 repo_name=repo_name,
@@ -441,7 +466,7 b' class TestAdminRepos(object):'
441 description = 'description for newly created repo'
466 description = 'description for newly created repo'
442
467
443 response = self.app.post(
468 response = self.app.post(
444 url('repos'),
469 route_path('repo_create'),
445 fixture._get_repo_create_params(
470 fixture._get_repo_create_params(
446 repo_private=False,
471 repo_private=False,
447 repo_name=repo_name,
472 repo_name=repo_name,
@@ -461,7 +486,8 b' class TestAdminRepos(object):'
461 repo_name_utf8 = safe_str(repo_name)
486 repo_name_utf8 = safe_str(repo_name)
462
487
463 # run the check page that triggers the flash message
488 # run the check page that triggers the flash message
464 response = self.app.get(url('repo_check_home', repo_name=repo_name))
489 response = self.app.get(
490 route_path('repo_creating_check', repo_name=safe_str(repo_name)))
465 assert response.json == {u'result': True}
491 assert response.json == {u'result': True}
466
492
467 flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
493 flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
@@ -475,643 +501,9 b' class TestAdminRepos(object):'
475 assert new_repo.description == description
501 assert new_repo.description == description
476
502
477 # test if the repository is visible in the list ?
503 # test if the repository is visible in the list ?
478 response = self.app.get(h.route_path('repo_summary', repo_name=repo_name))
504 response = self.app.get(
505 h.route_path('repo_summary', repo_name=safe_str(repo_name)))
479 response.mustcontain(repo_name)
506 response.mustcontain(repo_name)
480 response.mustcontain(backend.alias)
507 response.mustcontain(backend.alias)
481
508
482 assert repo_on_filesystem(repo_name)
509 assert repo_on_filesystem(repo_name)
483
484
485 @pytest.mark.usefixtures("app")
486 class TestVcsSettings(object):
487 FORM_DATA = {
488 'inherit_global_settings': False,
489 'hooks_changegroup_repo_size': False,
490 'hooks_changegroup_push_logger': False,
491 'hooks_outgoing_pull_logger': False,
492 'extensions_largefiles': False,
493 'extensions_evolve': False,
494 'phases_publish': 'False',
495 'rhodecode_pr_merge_enabled': False,
496 'rhodecode_use_outdated_comments': False,
497 'new_svn_branch': '',
498 'new_svn_tag': ''
499 }
500
501 @pytest.mark.skip_backends('svn')
502 def test_global_settings_initial_values(self, autologin_user, backend):
503 repo_name = backend.repo_name
504 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
505
506 expected_settings = (
507 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled',
508 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
509 'hooks_outgoing_pull_logger'
510 )
511 for setting in expected_settings:
512 self.assert_repo_value_equals_global_value(response, setting)
513
514 def test_show_settings_requires_repo_admin_permission(
515 self, backend, user_util, settings_util):
516 repo = backend.create_repo()
517 repo_name = repo.repo_name
518 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
519 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
520 login_user_session(
521 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
522 self.app.get(url('repo_vcs_settings', repo_name=repo_name), status=200)
523
524 def test_inherit_global_settings_flag_is_true_by_default(
525 self, autologin_user, backend):
526 repo_name = backend.repo_name
527 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
528
529 assert_response = AssertResponse(response)
530 element = assert_response.get_element('#inherit_global_settings')
531 assert element.checked
532
533 @pytest.mark.parametrize('checked_value', [True, False])
534 def test_inherit_global_settings_value(
535 self, autologin_user, backend, checked_value, settings_util):
536 repo = backend.create_repo()
537 repo_name = repo.repo_name
538 settings_util.create_repo_rhodecode_setting(
539 repo, 'inherit_vcs_settings', checked_value, 'bool')
540 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
541
542 assert_response = AssertResponse(response)
543 element = assert_response.get_element('#inherit_global_settings')
544 assert element.checked == checked_value
545
546 @pytest.mark.skip_backends('svn')
547 def test_hooks_settings_are_created(
548 self, autologin_user, backend, csrf_token):
549 repo_name = backend.repo_name
550 data = self.FORM_DATA.copy()
551 data['csrf_token'] = csrf_token
552 self.app.post(
553 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
554 settings = SettingsModel(repo=repo_name)
555 try:
556 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
557 ui = settings.get_ui_by_section_and_key(section, key)
558 assert ui.ui_active is False
559 finally:
560 self._cleanup_repo_settings(settings)
561
562 def test_hooks_settings_are_not_created_for_svn(
563 self, autologin_user, backend_svn, csrf_token):
564 repo_name = backend_svn.repo_name
565 data = self.FORM_DATA.copy()
566 data['csrf_token'] = csrf_token
567 self.app.post(
568 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
569 settings = SettingsModel(repo=repo_name)
570 try:
571 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
572 ui = settings.get_ui_by_section_and_key(section, key)
573 assert ui is None
574 finally:
575 self._cleanup_repo_settings(settings)
576
577 @pytest.mark.skip_backends('svn')
578 def test_hooks_settings_are_updated(
579 self, autologin_user, backend, csrf_token):
580 repo_name = backend.repo_name
581 settings = SettingsModel(repo=repo_name)
582 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
583 settings.create_ui_section_value(section, '', key=key, active=True)
584
585 data = self.FORM_DATA.copy()
586 data['csrf_token'] = csrf_token
587 self.app.post(
588 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
589 try:
590 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
591 ui = settings.get_ui_by_section_and_key(section, key)
592 assert ui.ui_active is False
593 finally:
594 self._cleanup_repo_settings(settings)
595
596 def test_hooks_settings_are_not_updated_for_svn(
597 self, autologin_user, backend_svn, csrf_token):
598 repo_name = backend_svn.repo_name
599 settings = SettingsModel(repo=repo_name)
600 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
601 settings.create_ui_section_value(section, '', key=key, active=True)
602
603 data = self.FORM_DATA.copy()
604 data['csrf_token'] = csrf_token
605 self.app.post(
606 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
607 try:
608 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
609 ui = settings.get_ui_by_section_and_key(section, key)
610 assert ui.ui_active is True
611 finally:
612 self._cleanup_repo_settings(settings)
613
614 @pytest.mark.skip_backends('svn')
615 def test_pr_settings_are_created(
616 self, autologin_user, backend, csrf_token):
617 repo_name = backend.repo_name
618 data = self.FORM_DATA.copy()
619 data['csrf_token'] = csrf_token
620 self.app.post(
621 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
622 settings = SettingsModel(repo=repo_name)
623 try:
624 for name in VcsSettingsModel.GENERAL_SETTINGS:
625 setting = settings.get_setting_by_name(name)
626 assert setting.app_settings_value is False
627 finally:
628 self._cleanup_repo_settings(settings)
629
630 def test_pr_settings_are_not_created_for_svn(
631 self, autologin_user, backend_svn, csrf_token):
632 repo_name = backend_svn.repo_name
633 data = self.FORM_DATA.copy()
634 data['csrf_token'] = csrf_token
635 self.app.post(
636 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
637 settings = SettingsModel(repo=repo_name)
638 try:
639 for name in VcsSettingsModel.GENERAL_SETTINGS:
640 setting = settings.get_setting_by_name(name)
641 assert setting is None
642 finally:
643 self._cleanup_repo_settings(settings)
644
645 def test_pr_settings_creation_requires_repo_admin_permission(
646 self, backend, user_util, settings_util, csrf_token):
647 repo = backend.create_repo()
648 repo_name = repo.repo_name
649
650 logout_user_session(self.app, csrf_token)
651 session = login_user_session(
652 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
653 new_csrf_token = auth.get_csrf_token(session)
654
655 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
656 repo = Repository.get_by_repo_name(repo_name)
657 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
658 data = self.FORM_DATA.copy()
659 data['csrf_token'] = new_csrf_token
660 settings = SettingsModel(repo=repo_name)
661
662 try:
663 self.app.post(
664 url('repo_vcs_settings', repo_name=repo_name), data,
665 status=302)
666 finally:
667 self._cleanup_repo_settings(settings)
668
669 @pytest.mark.skip_backends('svn')
670 def test_pr_settings_are_updated(
671 self, autologin_user, backend, csrf_token):
672 repo_name = backend.repo_name
673 settings = SettingsModel(repo=repo_name)
674 for name in VcsSettingsModel.GENERAL_SETTINGS:
675 settings.create_or_update_setting(name, True, 'bool')
676
677 data = self.FORM_DATA.copy()
678 data['csrf_token'] = csrf_token
679 self.app.post(
680 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
681 try:
682 for name in VcsSettingsModel.GENERAL_SETTINGS:
683 setting = settings.get_setting_by_name(name)
684 assert setting.app_settings_value is False
685 finally:
686 self._cleanup_repo_settings(settings)
687
688 def test_pr_settings_are_not_updated_for_svn(
689 self, autologin_user, backend_svn, csrf_token):
690 repo_name = backend_svn.repo_name
691 settings = SettingsModel(repo=repo_name)
692 for name in VcsSettingsModel.GENERAL_SETTINGS:
693 settings.create_or_update_setting(name, True, 'bool')
694
695 data = self.FORM_DATA.copy()
696 data['csrf_token'] = csrf_token
697 self.app.post(
698 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
699 try:
700 for name in VcsSettingsModel.GENERAL_SETTINGS:
701 setting = settings.get_setting_by_name(name)
702 assert setting.app_settings_value is True
703 finally:
704 self._cleanup_repo_settings(settings)
705
706 def test_svn_settings_are_created(
707 self, autologin_user, backend_svn, csrf_token, settings_util):
708 repo_name = backend_svn.repo_name
709 data = self.FORM_DATA.copy()
710 data['new_svn_tag'] = 'svn-tag'
711 data['new_svn_branch'] = 'svn-branch'
712 data['csrf_token'] = csrf_token
713
714 # Create few global settings to make sure that uniqueness validators
715 # are not triggered
716 settings_util.create_rhodecode_ui(
717 VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
718 settings_util.create_rhodecode_ui(
719 VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag')
720
721 self.app.post(
722 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
723 settings = SettingsModel(repo=repo_name)
724 try:
725 svn_branches = settings.get_ui_by_section(
726 VcsSettingsModel.SVN_BRANCH_SECTION)
727 svn_branch_names = [b.ui_value for b in svn_branches]
728 svn_tags = settings.get_ui_by_section(
729 VcsSettingsModel.SVN_TAG_SECTION)
730 svn_tag_names = [b.ui_value for b in svn_tags]
731 assert 'svn-branch' in svn_branch_names
732 assert 'svn-tag' in svn_tag_names
733 finally:
734 self._cleanup_repo_settings(settings)
735
736 def test_svn_settings_are_unique(
737 self, autologin_user, backend_svn, csrf_token, settings_util):
738 repo = backend_svn.repo
739 repo_name = repo.repo_name
740 data = self.FORM_DATA.copy()
741 data['new_svn_tag'] = 'test_tag'
742 data['new_svn_branch'] = 'test_branch'
743 data['csrf_token'] = csrf_token
744 settings_util.create_repo_rhodecode_ui(
745 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch')
746 settings_util.create_repo_rhodecode_ui(
747 repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag')
748
749 response = self.app.post(
750 url('repo_vcs_settings', repo_name=repo_name), data, status=200)
751 response.mustcontain('Pattern already exists')
752
753 def test_svn_settings_with_empty_values_are_not_created(
754 self, autologin_user, backend_svn, csrf_token):
755 repo_name = backend_svn.repo_name
756 data = self.FORM_DATA.copy()
757 data['csrf_token'] = csrf_token
758 self.app.post(
759 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
760 settings = SettingsModel(repo=repo_name)
761 try:
762 svn_branches = settings.get_ui_by_section(
763 VcsSettingsModel.SVN_BRANCH_SECTION)
764 svn_tags = settings.get_ui_by_section(
765 VcsSettingsModel.SVN_TAG_SECTION)
766 assert len(svn_branches) == 0
767 assert len(svn_tags) == 0
768 finally:
769 self._cleanup_repo_settings(settings)
770
771 def test_svn_settings_are_shown_for_svn_repository(
772 self, autologin_user, backend_svn, csrf_token):
773 repo_name = backend_svn.repo_name
774 response = self.app.get(
775 url('repo_vcs_settings', repo_name=repo_name), status=200)
776 response.mustcontain('Subversion Settings')
777
778 @pytest.mark.skip_backends('svn')
779 def test_svn_settings_are_not_created_for_not_svn_repository(
780 self, autologin_user, backend, csrf_token):
781 repo_name = backend.repo_name
782 data = self.FORM_DATA.copy()
783 data['csrf_token'] = csrf_token
784 self.app.post(
785 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
786 settings = SettingsModel(repo=repo_name)
787 try:
788 svn_branches = settings.get_ui_by_section(
789 VcsSettingsModel.SVN_BRANCH_SECTION)
790 svn_tags = settings.get_ui_by_section(
791 VcsSettingsModel.SVN_TAG_SECTION)
792 assert len(svn_branches) == 0
793 assert len(svn_tags) == 0
794 finally:
795 self._cleanup_repo_settings(settings)
796
797 @pytest.mark.skip_backends('svn')
798 def test_svn_settings_are_shown_only_for_svn_repository(
799 self, autologin_user, backend, csrf_token):
800 repo_name = backend.repo_name
801 response = self.app.get(
802 url('repo_vcs_settings', repo_name=repo_name), status=200)
803 response.mustcontain(no='Subversion Settings')
804
805 def test_hg_settings_are_created(
806 self, autologin_user, backend_hg, csrf_token):
807 repo_name = backend_hg.repo_name
808 data = self.FORM_DATA.copy()
809 data['new_svn_tag'] = 'svn-tag'
810 data['new_svn_branch'] = 'svn-branch'
811 data['csrf_token'] = csrf_token
812 self.app.post(
813 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
814 settings = SettingsModel(repo=repo_name)
815 try:
816 largefiles_ui = settings.get_ui_by_section_and_key(
817 'extensions', 'largefiles')
818 assert largefiles_ui.ui_active is False
819 phases_ui = settings.get_ui_by_section_and_key(
820 'phases', 'publish')
821 assert str2bool(phases_ui.ui_value) is False
822 finally:
823 self._cleanup_repo_settings(settings)
824
825 def test_hg_settings_are_updated(
826 self, autologin_user, backend_hg, csrf_token):
827 repo_name = backend_hg.repo_name
828 settings = SettingsModel(repo=repo_name)
829 settings.create_ui_section_value(
830 'extensions', '', key='largefiles', active=True)
831 settings.create_ui_section_value(
832 'phases', '1', key='publish', active=True)
833
834 data = self.FORM_DATA.copy()
835 data['csrf_token'] = csrf_token
836 self.app.post(
837 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
838 try:
839 largefiles_ui = settings.get_ui_by_section_and_key(
840 'extensions', 'largefiles')
841 assert largefiles_ui.ui_active is False
842 phases_ui = settings.get_ui_by_section_and_key(
843 'phases', 'publish')
844 assert str2bool(phases_ui.ui_value) is False
845 finally:
846 self._cleanup_repo_settings(settings)
847
848 def test_hg_settings_are_shown_for_hg_repository(
849 self, autologin_user, backend_hg, csrf_token):
850 repo_name = backend_hg.repo_name
851 response = self.app.get(
852 url('repo_vcs_settings', repo_name=repo_name), status=200)
853 response.mustcontain('Mercurial Settings')
854
855 @pytest.mark.skip_backends('hg')
856 def test_hg_settings_are_created_only_for_hg_repository(
857 self, autologin_user, backend, csrf_token):
858 repo_name = backend.repo_name
859 data = self.FORM_DATA.copy()
860 data['csrf_token'] = csrf_token
861 self.app.post(
862 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
863 settings = SettingsModel(repo=repo_name)
864 try:
865 largefiles_ui = settings.get_ui_by_section_and_key(
866 'extensions', 'largefiles')
867 assert largefiles_ui is None
868 phases_ui = settings.get_ui_by_section_and_key(
869 'phases', 'publish')
870 assert phases_ui is None
871 finally:
872 self._cleanup_repo_settings(settings)
873
874 @pytest.mark.skip_backends('hg')
875 def test_hg_settings_are_shown_only_for_hg_repository(
876 self, autologin_user, backend, csrf_token):
877 repo_name = backend.repo_name
878 response = self.app.get(
879 url('repo_vcs_settings', repo_name=repo_name), status=200)
880 response.mustcontain(no='Mercurial Settings')
881
882 @pytest.mark.skip_backends('hg')
883 def test_hg_settings_are_updated_only_for_hg_repository(
884 self, autologin_user, backend, csrf_token):
885 repo_name = backend.repo_name
886 settings = SettingsModel(repo=repo_name)
887 settings.create_ui_section_value(
888 'extensions', '', key='largefiles', active=True)
889 settings.create_ui_section_value(
890 'phases', '1', key='publish', active=True)
891
892 data = self.FORM_DATA.copy()
893 data['csrf_token'] = csrf_token
894 self.app.post(
895 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
896 try:
897 largefiles_ui = settings.get_ui_by_section_and_key(
898 'extensions', 'largefiles')
899 assert largefiles_ui.ui_active is True
900 phases_ui = settings.get_ui_by_section_and_key(
901 'phases', 'publish')
902 assert phases_ui.ui_value == '1'
903 finally:
904 self._cleanup_repo_settings(settings)
905
906 def test_per_repo_svn_settings_are_displayed(
907 self, autologin_user, backend_svn, settings_util):
908 repo = backend_svn.create_repo()
909 repo_name = repo.repo_name
910 branches = [
911 settings_util.create_repo_rhodecode_ui(
912 repo, VcsSettingsModel.SVN_BRANCH_SECTION,
913 'branch_{}'.format(i))
914 for i in range(10)]
915 tags = [
916 settings_util.create_repo_rhodecode_ui(
917 repo, VcsSettingsModel.SVN_TAG_SECTION, 'tag_{}'.format(i))
918 for i in range(10)]
919
920 response = self.app.get(
921 url('repo_vcs_settings', repo_name=repo_name), status=200)
922 assert_response = AssertResponse(response)
923 for branch in branches:
924 css_selector = '[name=branch_value_{}]'.format(branch.ui_id)
925 element = assert_response.get_element(css_selector)
926 assert element.value == branch.ui_value
927 for tag in tags:
928 css_selector = '[name=tag_ui_value_new_{}]'.format(tag.ui_id)
929 element = assert_response.get_element(css_selector)
930 assert element.value == tag.ui_value
931
932 def test_per_repo_hg_and_pr_settings_are_not_displayed_for_svn(
933 self, autologin_user, backend_svn, settings_util):
934 repo = backend_svn.create_repo()
935 repo_name = repo.repo_name
936 response = self.app.get(
937 url('repo_vcs_settings', repo_name=repo_name), status=200)
938 response.mustcontain(no='<label>Hooks:</label>')
939 response.mustcontain(no='<label>Pull Request Settings:</label>')
940
941 def test_inherit_global_settings_value_is_saved(
942 self, autologin_user, backend, csrf_token):
943 repo_name = backend.repo_name
944 data = self.FORM_DATA.copy()
945 data['csrf_token'] = csrf_token
946 data['inherit_global_settings'] = True
947 self.app.post(
948 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
949
950 settings = SettingsModel(repo=repo_name)
951 vcs_settings = VcsSettingsModel(repo=repo_name)
952 try:
953 assert vcs_settings.inherit_global_settings is True
954 finally:
955 self._cleanup_repo_settings(settings)
956
957 def test_repo_cache_is_invalidated_when_settings_are_updated(
958 self, autologin_user, backend, csrf_token):
959 repo_name = backend.repo_name
960 data = self.FORM_DATA.copy()
961 data['csrf_token'] = csrf_token
962 data['inherit_global_settings'] = True
963 settings = SettingsModel(repo=repo_name)
964
965 invalidation_patcher = mock.patch(
966 'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
967 with invalidation_patcher as invalidation_mock:
968 self.app.post(
969 url('repo_vcs_settings', repo_name=repo_name), data,
970 status=302)
971 try:
972 invalidation_mock.assert_called_once_with(repo_name, delete=True)
973 finally:
974 self._cleanup_repo_settings(settings)
975
976 def test_other_settings_not_saved_inherit_global_settings_is_true(
977 self, autologin_user, backend, csrf_token):
978 repo_name = backend.repo_name
979 data = self.FORM_DATA.copy()
980 data['csrf_token'] = csrf_token
981 data['inherit_global_settings'] = True
982 self.app.post(
983 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
984
985 settings = SettingsModel(repo=repo_name)
986 ui_settings = (
987 VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
988
989 vcs_settings = []
990 try:
991 for section, key in ui_settings:
992 ui = settings.get_ui_by_section_and_key(section, key)
993 if ui:
994 vcs_settings.append(ui)
995 vcs_settings.extend(settings.get_ui_by_section(
996 VcsSettingsModel.SVN_BRANCH_SECTION))
997 vcs_settings.extend(settings.get_ui_by_section(
998 VcsSettingsModel.SVN_TAG_SECTION))
999 for name in VcsSettingsModel.GENERAL_SETTINGS:
1000 setting = settings.get_setting_by_name(name)
1001 if setting:
1002 vcs_settings.append(setting)
1003 assert vcs_settings == []
1004 finally:
1005 self._cleanup_repo_settings(settings)
1006
1007 def test_delete_svn_branch_and_tag_patterns(
1008 self, autologin_user, backend_svn, settings_util, csrf_token):
1009 repo = backend_svn.create_repo()
1010 repo_name = repo.repo_name
1011 branch = settings_util.create_repo_rhodecode_ui(
1012 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
1013 cleanup=False)
1014 tag = settings_util.create_repo_rhodecode_ui(
1015 repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag', cleanup=False)
1016 data = {
1017 '_method': 'delete',
1018 'csrf_token': csrf_token
1019 }
1020 for id_ in (branch.ui_id, tag.ui_id):
1021 data['delete_svn_pattern'] = id_,
1022 self.app.post(
1023 url('repo_vcs_settings', repo_name=repo_name), data,
1024 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
1025 settings = VcsSettingsModel(repo=repo_name)
1026 assert settings.get_repo_svn_branch_patterns() == []
1027
1028 def test_delete_svn_branch_requires_repo_admin_permission(
1029 self, backend_svn, user_util, settings_util, csrf_token):
1030 repo = backend_svn.create_repo()
1031 repo_name = repo.repo_name
1032
1033 logout_user_session(self.app, csrf_token)
1034 session = login_user_session(
1035 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
1036 csrf_token = auth.get_csrf_token(session)
1037
1038 repo = Repository.get_by_repo_name(repo_name)
1039 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
1040 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
1041 branch = settings_util.create_repo_rhodecode_ui(
1042 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
1043 cleanup=False)
1044 data = {
1045 '_method': 'delete',
1046 'csrf_token': csrf_token,
1047 'delete_svn_pattern': branch.ui_id
1048 }
1049 self.app.post(
1050 url('repo_vcs_settings', repo_name=repo_name), data,
1051 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
1052
1053 def test_delete_svn_branch_raises_400_when_not_found(
1054 self, autologin_user, backend_svn, settings_util, csrf_token):
1055 repo_name = backend_svn.repo_name
1056 data = {
1057 '_method': 'delete',
1058 'delete_svn_pattern': 123,
1059 'csrf_token': csrf_token
1060 }
1061 self.app.post(
1062 url('repo_vcs_settings', repo_name=repo_name), data,
1063 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
1064
1065 def test_delete_svn_branch_raises_400_when_no_id_specified(
1066 self, autologin_user, backend_svn, settings_util, csrf_token):
1067 repo_name = backend_svn.repo_name
1068 data = {
1069 '_method': 'delete',
1070 'csrf_token': csrf_token
1071 }
1072 self.app.post(
1073 url('repo_vcs_settings', repo_name=repo_name), data,
1074 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
1075
1076 def _cleanup_repo_settings(self, settings_model):
1077 cleanup = []
1078 ui_settings = (
1079 VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
1080
1081 for section, key in ui_settings:
1082 ui = settings_model.get_ui_by_section_and_key(section, key)
1083 if ui:
1084 cleanup.append(ui)
1085
1086 cleanup.extend(settings_model.get_ui_by_section(
1087 VcsSettingsModel.INHERIT_SETTINGS))
1088 cleanup.extend(settings_model.get_ui_by_section(
1089 VcsSettingsModel.SVN_BRANCH_SECTION))
1090 cleanup.extend(settings_model.get_ui_by_section(
1091 VcsSettingsModel.SVN_TAG_SECTION))
1092
1093 for name in VcsSettingsModel.GENERAL_SETTINGS:
1094 setting = settings_model.get_setting_by_name(name)
1095 if setting:
1096 cleanup.append(setting)
1097
1098 for object_ in cleanup:
1099 Session().delete(object_)
1100 Session().commit()
1101
1102 def assert_repo_value_equals_global_value(self, response, setting):
1103 assert_response = AssertResponse(response)
1104 global_css_selector = '[name={}_inherited]'.format(setting)
1105 repo_css_selector = '[name={}]'.format(setting)
1106 repo_element = assert_response.get_element(repo_css_selector)
1107 global_element = assert_response.get_element(global_css_selector)
1108 assert repo_element.value == global_element.value
1109
1110
1111 def _get_permission_for_user(user, repo):
1112 perm = UserRepoToPerm.query()\
1113 .filter(UserRepoToPerm.repository ==
1114 Repository.get_by_repo_name(repo))\
1115 .filter(UserRepoToPerm.user == User.get_by_username(user))\
1116 .all()
1117 return perm
This diff has been collapsed as it changes many lines, (518 lines changed) Show them Hide them
@@ -19,8 +19,12 b''
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 from sqlalchemy.orm.exc import NoResultFound
22
23
23 from rhodecode.model.db import User, UserApiKeys, UserEmailMap
24 from rhodecode.lib import auth
25 from rhodecode.lib import helpers as h
26 from rhodecode.model import validators
27 from rhodecode.model.db import User, UserApiKeys, UserEmailMap, Repository
24 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
25 from rhodecode.model.user import UserModel
29 from rhodecode.model.user import UserModel
26
30
@@ -40,6 +44,27 b' def route_path(name, params=None, **kwar'
40 ADMIN_PREFIX + '/users',
44 ADMIN_PREFIX + '/users',
41 'users_data':
45 'users_data':
42 ADMIN_PREFIX + '/users_data',
46 ADMIN_PREFIX + '/users_data',
47 'users_create':
48 ADMIN_PREFIX + '/users/create',
49 'users_new':
50 ADMIN_PREFIX + '/users/new',
51 'user_edit':
52 ADMIN_PREFIX + '/users/{user_id}/edit',
53 'user_edit_advanced':
54 ADMIN_PREFIX + '/users/{user_id}/edit/advanced',
55 'user_edit_global_perms':
56 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions',
57 'user_edit_global_perms_update':
58 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions/update',
59 'user_update':
60 ADMIN_PREFIX + '/users/{user_id}/update',
61 'user_delete':
62 ADMIN_PREFIX + '/users/{user_id}/delete',
63 'user_force_password_reset':
64 ADMIN_PREFIX + '/users/{user_id}/password_reset',
65 'user_create_personal_repo_group':
66 ADMIN_PREFIX + '/users/{user_id}/create_repo_group',
67
43 'edit_user_auth_tokens':
68 'edit_user_auth_tokens':
44 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
69 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
45 'edit_user_auth_tokens_add':
70 'edit_user_auth_tokens_add':
@@ -60,6 +85,15 b' def route_path(name, params=None, **kwar'
60 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
85 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
61 'edit_user_ips_delete':
86 'edit_user_ips_delete':
62 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
87 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
88
89 'edit_user_perms_summary':
90 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary',
91 'edit_user_perms_summary_json':
92 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary/json',
93
94 'edit_user_audit_logs':
95 ADMIN_PREFIX + '/users/{user_id}/edit/audit',
96
63 }[name].format(**kwargs)
97 }[name].format(**kwargs)
64
98
65 if params:
99 if params:
@@ -135,7 +169,7 b' class TestAdminUsersView(TestController)'
135 self.log_user()
169 self.log_user()
136 user = user_util.create_user()
170 user = user_util.create_user()
137 user_id = user.user_id
171 user_id = user.user_id
138 keys = user.extra_auth_tokens
172 keys = user.auth_tokens
139 assert 2 == len(keys)
173 assert 2 == len(keys)
140
174
141 response = self.app.post(
175 response = self.app.post(
@@ -220,7 +254,8 b' class TestAdminUsersView(TestController)'
220 def test_emails(self):
254 def test_emails(self):
221 self.log_user()
255 self.log_user()
222 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
256 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
223 response = self.app.get(route_path('edit_user_emails', user_id=user.user_id))
257 response = self.app.get(
258 route_path('edit_user_emails', user_id=user.user_id))
224 response.mustcontain('No additional emails specified')
259 response.mustcontain('No additional emails specified')
225
260
226 def test_emails_add(self, user_util):
261 def test_emails_add(self, user_util):
@@ -233,7 +268,8 b' class TestAdminUsersView(TestController)'
233 params={'new_email': 'example@rhodecode.com',
268 params={'new_email': 'example@rhodecode.com',
234 'csrf_token': self.csrf_token})
269 'csrf_token': self.csrf_token})
235
270
236 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
271 response = self.app.get(
272 route_path('edit_user_emails', user_id=user_id))
237 response.mustcontain('example@rhodecode.com')
273 response.mustcontain('example@rhodecode.com')
238
274
239 def test_emails_add_existing_email(self, user_util, user_regular):
275 def test_emails_add_existing_email(self, user_util, user_regular):
@@ -250,7 +286,8 b' class TestAdminUsersView(TestController)'
250 assert_session_flash(
286 assert_session_flash(
251 response, 'This e-mail address is already taken')
287 response, 'This e-mail address is already taken')
252
288
253 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
289 response = self.app.get(
290 route_path('edit_user_emails', user_id=user_id))
254 response.mustcontain(no=[existing_email])
291 response.mustcontain(no=[existing_email])
255
292
256 def test_emails_delete(self, user_util):
293 def test_emails_delete(self, user_util):
@@ -263,7 +300,8 b' class TestAdminUsersView(TestController)'
263 params={'new_email': 'example@rhodecode.com',
300 params={'new_email': 'example@rhodecode.com',
264 'csrf_token': self.csrf_token})
301 'csrf_token': self.csrf_token})
265
302
266 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
303 response = self.app.get(
304 route_path('edit_user_emails', user_id=user_id))
267 response.mustcontain('example@rhodecode.com')
305 response.mustcontain('example@rhodecode.com')
268
306
269 user_email = UserEmailMap.query()\
307 user_email = UserEmailMap.query()\
@@ -277,5 +315,469 b' class TestAdminUsersView(TestController)'
277 params={'del_email_id': del_email_id,
315 params={'del_email_id': del_email_id,
278 'csrf_token': self.csrf_token})
316 'csrf_token': self.csrf_token})
279
317
280 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
318 response = self.app.get(
281 response.mustcontain(no=['example@rhodecode.com']) No newline at end of file
319 route_path('edit_user_emails', user_id=user_id))
320 response.mustcontain(no=['example@rhodecode.com'])
321
322
323 def test_create(self, request, xhr_header):
324 self.log_user()
325 username = 'newtestuser'
326 password = 'test12'
327 password_confirmation = password
328 name = 'name'
329 lastname = 'lastname'
330 email = 'mail@mail.com'
331
332 self.app.get(route_path('users_new'))
333
334 response = self.app.post(route_path('users_create'), params={
335 'username': username,
336 'password': password,
337 'password_confirmation': password_confirmation,
338 'firstname': name,
339 'active': True,
340 'lastname': lastname,
341 'extern_name': 'rhodecode',
342 'extern_type': 'rhodecode',
343 'email': email,
344 'csrf_token': self.csrf_token,
345 })
346 user_link = h.link_to(
347 username,
348 route_path(
349 'user_edit', user_id=User.get_by_username(username).user_id))
350 assert_session_flash(response, 'Created user %s' % (user_link,))
351
352 @request.addfinalizer
353 def cleanup():
354 fixture.destroy_user(username)
355 Session().commit()
356
357 new_user = User.query().filter(User.username == username).one()
358
359 assert new_user.username == username
360 assert auth.check_password(password, new_user.password)
361 assert new_user.name == name
362 assert new_user.lastname == lastname
363 assert new_user.email == email
364
365 response = self.app.get(route_path('users_data'),
366 extra_environ=xhr_header)
367 response.mustcontain(username)
368
369 def test_create_err(self):
370 self.log_user()
371 username = 'new_user'
372 password = ''
373 name = 'name'
374 lastname = 'lastname'
375 email = 'errmail.com'
376
377 self.app.get(route_path('users_new'))
378
379 response = self.app.post(route_path('users_create'), params={
380 'username': username,
381 'password': password,
382 'name': name,
383 'active': False,
384 'lastname': lastname,
385 'email': email,
386 'csrf_token': self.csrf_token,
387 })
388
389 msg = validators.ValidUsername(
390 False, {})._messages['system_invalid_username']
391 msg = h.html_escape(msg % {'username': 'new_user'})
392 response.mustcontain('<span class="error-message">%s</span>' % msg)
393 response.mustcontain(
394 '<span class="error-message">Please enter a value</span>')
395 response.mustcontain(
396 '<span class="error-message">An email address must contain a'
397 ' single @</span>')
398
399 def get_user():
400 Session().query(User).filter(User.username == username).one()
401
402 with pytest.raises(NoResultFound):
403 get_user()
404
405 def test_new(self):
406 self.log_user()
407 self.app.get(route_path('users_new'))
408
409 @pytest.mark.parametrize("name, attrs", [
410 ('firstname', {'firstname': 'new_username'}),
411 ('lastname', {'lastname': 'new_username'}),
412 ('admin', {'admin': True}),
413 ('admin', {'admin': False}),
414 ('extern_type', {'extern_type': 'ldap'}),
415 ('extern_type', {'extern_type': None}),
416 ('extern_name', {'extern_name': 'test'}),
417 ('extern_name', {'extern_name': None}),
418 ('active', {'active': False}),
419 ('active', {'active': True}),
420 ('email', {'email': 'some@email.com'}),
421 ('language', {'language': 'de'}),
422 ('language', {'language': 'en'}),
423 # ('new_password', {'new_password': 'foobar123',
424 # 'password_confirmation': 'foobar123'})
425 ])
426 def test_update(self, name, attrs, user_util):
427 self.log_user()
428 usr = user_util.create_user(
429 password='qweqwe',
430 email='testme@rhodecode.org',
431 extern_type='rhodecode',
432 extern_name='xxx',
433 )
434 user_id = usr.user_id
435 Session().commit()
436
437 params = usr.get_api_data()
438 cur_lang = params['language'] or 'en'
439 params.update({
440 'password_confirmation': '',
441 'new_password': '',
442 'language': cur_lang,
443 'csrf_token': self.csrf_token,
444 })
445 params.update({'new_password': ''})
446 params.update(attrs)
447 if name == 'email':
448 params['emails'] = [attrs['email']]
449 elif name == 'extern_type':
450 # cannot update this via form, expected value is original one
451 params['extern_type'] = "rhodecode"
452 elif name == 'extern_name':
453 # cannot update this via form, expected value is original one
454 params['extern_name'] = 'xxx'
455 # special case since this user is not
456 # logged in yet his data is not filled
457 # so we use creation data
458
459 response = self.app.post(
460 route_path('user_update', user_id=usr.user_id), params)
461 assert response.status_int == 302
462 assert_session_flash(response, 'User updated successfully')
463
464 updated_user = User.get(user_id)
465 updated_params = updated_user.get_api_data()
466 updated_params.update({'password_confirmation': ''})
467 updated_params.update({'new_password': ''})
468
469 del params['csrf_token']
470 assert params == updated_params
471
472 def test_update_and_migrate_password(
473 self, autologin_user, real_crypto_backend, user_util):
474
475 user = user_util.create_user()
476 temp_user = user.username
477 user.password = auth._RhodeCodeCryptoSha256().hash_create(
478 b'test123')
479 Session().add(user)
480 Session().commit()
481
482 params = user.get_api_data()
483
484 params.update({
485 'password_confirmation': 'qweqwe123',
486 'new_password': 'qweqwe123',
487 'language': 'en',
488 'csrf_token': autologin_user.csrf_token,
489 })
490
491 response = self.app.post(
492 route_path('user_update', user_id=user.user_id), params)
493 assert response.status_int == 302
494 assert_session_flash(response, 'User updated successfully')
495
496 # new password should be bcrypted, after log-in and transfer
497 user = User.get_by_username(temp_user)
498 assert user.password.startswith('$')
499
500 updated_user = User.get_by_username(temp_user)
501 updated_params = updated_user.get_api_data()
502 updated_params.update({'password_confirmation': 'qweqwe123'})
503 updated_params.update({'new_password': 'qweqwe123'})
504
505 del params['csrf_token']
506 assert params == updated_params
507
508 def test_delete(self):
509 self.log_user()
510 username = 'newtestuserdeleteme'
511
512 fixture.create_user(name=username)
513
514 new_user = Session().query(User)\
515 .filter(User.username == username).one()
516 response = self.app.post(
517 route_path('user_delete', user_id=new_user.user_id),
518 params={'csrf_token': self.csrf_token})
519
520 assert_session_flash(response, 'Successfully deleted user')
521
522 def test_delete_owner_of_repository(self, request, user_util):
523 self.log_user()
524 obj_name = 'test_repo'
525 usr = user_util.create_user()
526 username = usr.username
527 fixture.create_repo(obj_name, cur_user=usr.username)
528
529 new_user = Session().query(User)\
530 .filter(User.username == username).one()
531 response = self.app.post(
532 route_path('user_delete', user_id=new_user.user_id),
533 params={'csrf_token': self.csrf_token})
534
535 msg = 'user "%s" still owns 1 repositories and cannot be removed. ' \
536 'Switch owners or remove those repositories:%s' % (username,
537 obj_name)
538 assert_session_flash(response, msg)
539 fixture.destroy_repo(obj_name)
540
541 def test_delete_owner_of_repository_detaching(self, request, user_util):
542 self.log_user()
543 obj_name = 'test_repo'
544 usr = user_util.create_user(auto_cleanup=False)
545 username = usr.username
546 fixture.create_repo(obj_name, cur_user=usr.username)
547
548 new_user = Session().query(User)\
549 .filter(User.username == username).one()
550 response = self.app.post(
551 route_path('user_delete', user_id=new_user.user_id),
552 params={'user_repos': 'detach', 'csrf_token': self.csrf_token})
553
554 msg = 'Detached 1 repositories'
555 assert_session_flash(response, msg)
556 fixture.destroy_repo(obj_name)
557
558 def test_delete_owner_of_repository_deleting(self, request, user_util):
559 self.log_user()
560 obj_name = 'test_repo'
561 usr = user_util.create_user(auto_cleanup=False)
562 username = usr.username
563 fixture.create_repo(obj_name, cur_user=usr.username)
564
565 new_user = Session().query(User)\
566 .filter(User.username == username).one()
567 response = self.app.post(
568 route_path('user_delete', user_id=new_user.user_id),
569 params={'user_repos': 'delete', 'csrf_token': self.csrf_token})
570
571 msg = 'Deleted 1 repositories'
572 assert_session_flash(response, msg)
573
574 def test_delete_owner_of_repository_group(self, request, user_util):
575 self.log_user()
576 obj_name = 'test_group'
577 usr = user_util.create_user()
578 username = usr.username
579 fixture.create_repo_group(obj_name, cur_user=usr.username)
580
581 new_user = Session().query(User)\
582 .filter(User.username == username).one()
583 response = self.app.post(
584 route_path('user_delete', user_id=new_user.user_id),
585 params={'csrf_token': self.csrf_token})
586
587 msg = 'user "%s" still owns 1 repository groups and cannot be removed. ' \
588 'Switch owners or remove those repository groups:%s' % (username,
589 obj_name)
590 assert_session_flash(response, msg)
591 fixture.destroy_repo_group(obj_name)
592
593 def test_delete_owner_of_repository_group_detaching(self, request, user_util):
594 self.log_user()
595 obj_name = 'test_group'
596 usr = user_util.create_user(auto_cleanup=False)
597 username = usr.username
598 fixture.create_repo_group(obj_name, cur_user=usr.username)
599
600 new_user = Session().query(User)\
601 .filter(User.username == username).one()
602 response = self.app.post(
603 route_path('user_delete', user_id=new_user.user_id),
604 params={'user_repo_groups': 'delete', 'csrf_token': self.csrf_token})
605
606 msg = 'Deleted 1 repository groups'
607 assert_session_flash(response, msg)
608
609 def test_delete_owner_of_repository_group_deleting(self, request, user_util):
610 self.log_user()
611 obj_name = 'test_group'
612 usr = user_util.create_user(auto_cleanup=False)
613 username = usr.username
614 fixture.create_repo_group(obj_name, cur_user=usr.username)
615
616 new_user = Session().query(User)\
617 .filter(User.username == username).one()
618 response = self.app.post(
619 route_path('user_delete', user_id=new_user.user_id),
620 params={'user_repo_groups': 'detach', 'csrf_token': self.csrf_token})
621
622 msg = 'Detached 1 repository groups'
623 assert_session_flash(response, msg)
624 fixture.destroy_repo_group(obj_name)
625
626 def test_delete_owner_of_user_group(self, request, user_util):
627 self.log_user()
628 obj_name = 'test_user_group'
629 usr = user_util.create_user()
630 username = usr.username
631 fixture.create_user_group(obj_name, cur_user=usr.username)
632
633 new_user = Session().query(User)\
634 .filter(User.username == username).one()
635 response = self.app.post(
636 route_path('user_delete', user_id=new_user.user_id),
637 params={'csrf_token': self.csrf_token})
638
639 msg = 'user "%s" still owns 1 user groups and cannot be removed. ' \
640 'Switch owners or remove those user groups:%s' % (username,
641 obj_name)
642 assert_session_flash(response, msg)
643 fixture.destroy_user_group(obj_name)
644
645 def test_delete_owner_of_user_group_detaching(self, request, user_util):
646 self.log_user()
647 obj_name = 'test_user_group'
648 usr = user_util.create_user(auto_cleanup=False)
649 username = usr.username
650 fixture.create_user_group(obj_name, cur_user=usr.username)
651
652 new_user = Session().query(User)\
653 .filter(User.username == username).one()
654 try:
655 response = self.app.post(
656 route_path('user_delete', user_id=new_user.user_id),
657 params={'user_user_groups': 'detach',
658 'csrf_token': self.csrf_token})
659
660 msg = 'Detached 1 user groups'
661 assert_session_flash(response, msg)
662 finally:
663 fixture.destroy_user_group(obj_name)
664
665 def test_delete_owner_of_user_group_deleting(self, request, user_util):
666 self.log_user()
667 obj_name = 'test_user_group'
668 usr = user_util.create_user(auto_cleanup=False)
669 username = usr.username
670 fixture.create_user_group(obj_name, cur_user=usr.username)
671
672 new_user = Session().query(User)\
673 .filter(User.username == username).one()
674 response = self.app.post(
675 route_path('user_delete', user_id=new_user.user_id),
676 params={'user_user_groups': 'delete', 'csrf_token': self.csrf_token})
677
678 msg = 'Deleted 1 user groups'
679 assert_session_flash(response, msg)
680
681 def test_edit(self, user_util):
682 self.log_user()
683 user = user_util.create_user()
684 self.app.get(route_path('user_edit', user_id=user.user_id))
685
686 def test_edit_default_user_redirect(self):
687 self.log_user()
688 user = User.get_default_user()
689 self.app.get(route_path('user_edit', user_id=user.user_id), status=302)
690
691 @pytest.mark.parametrize(
692 'repo_create, repo_create_write, user_group_create, repo_group_create,'
693 'fork_create, inherit_default_permissions, expect_error,'
694 'expect_form_error', [
695 ('hg.create.none', 'hg.create.write_on_repogroup.false',
696 'hg.usergroup.create.false', 'hg.repogroup.create.false',
697 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
698 ('hg.create.repository', 'hg.create.write_on_repogroup.false',
699 'hg.usergroup.create.false', 'hg.repogroup.create.false',
700 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
701 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
702 'hg.usergroup.create.true', 'hg.repogroup.create.true',
703 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
704 False),
705 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
706 'hg.usergroup.create.true', 'hg.repogroup.create.true',
707 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
708 True),
709 ('', '', '', '', '', '', True, False),
710 ])
711 def test_global_perms_on_user(
712 self, repo_create, repo_create_write, user_group_create,
713 repo_group_create, fork_create, expect_error, expect_form_error,
714 inherit_default_permissions, user_util):
715 self.log_user()
716 user = user_util.create_user()
717 uid = user.user_id
718
719 # ENABLE REPO CREATE ON A GROUP
720 perm_params = {
721 'inherit_default_permissions': False,
722 'default_repo_create': repo_create,
723 'default_repo_create_on_write': repo_create_write,
724 'default_user_group_create': user_group_create,
725 'default_repo_group_create': repo_group_create,
726 'default_fork_create': fork_create,
727 'default_inherit_default_permissions': inherit_default_permissions,
728 'csrf_token': self.csrf_token,
729 }
730 response = self.app.post(
731 route_path('user_edit_global_perms_update', user_id=uid),
732 params=perm_params)
733
734 if expect_form_error:
735 assert response.status_int == 200
736 response.mustcontain('Value must be one of')
737 else:
738 if expect_error:
739 msg = 'An error occurred during permissions saving'
740 else:
741 msg = 'User global permissions updated successfully'
742 ug = User.get(uid)
743 del perm_params['inherit_default_permissions']
744 del perm_params['csrf_token']
745 assert perm_params == ug.get_default_perms()
746 assert_session_flash(response, msg)
747
748 def test_global_permissions_initial_values(self, user_util):
749 self.log_user()
750 user = user_util.create_user()
751 uid = user.user_id
752 response = self.app.get(
753 route_path('user_edit_global_perms', user_id=uid))
754 default_user = User.get_default_user()
755 default_permissions = default_user.get_default_perms()
756 assert_response = response.assert_response()
757 expected_permissions = (
758 'default_repo_create', 'default_repo_create_on_write',
759 'default_fork_create', 'default_repo_group_create',
760 'default_user_group_create', 'default_inherit_default_permissions')
761 for permission in expected_permissions:
762 css_selector = '[name={}][checked=checked]'.format(permission)
763 element = assert_response.get_element(css_selector)
764 assert element.value == default_permissions[permission]
765
766 def test_perms_summary_page(self):
767 user = self.log_user()
768 response = self.app.get(
769 route_path('edit_user_perms_summary', user_id=user['user_id']))
770 for repo in Repository.query().all():
771 response.mustcontain(repo.repo_name)
772
773 def test_perms_summary_page_json(self):
774 user = self.log_user()
775 response = self.app.get(
776 route_path('edit_user_perms_summary_json', user_id=user['user_id']))
777 for repo in Repository.query().all():
778 response.mustcontain(repo.repo_name)
779
780 def test_audit_log_page(self):
781 user = self.log_user()
782 self.app.get(
783 route_path('edit_user_audit_logs', user_id=user['user_id']))
@@ -20,11 +20,11 b''
20
20
21 import logging
21 import logging
22
22
23 from pyramid.httpexceptions import HTTPNotFound
23 from pyramid.view import view_config
24 from pyramid.view import view_config
24 from sqlalchemy.orm import joinedload
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.model.db import UserLog
27 from rhodecode.model.db import joinedload, UserLog
28 from rhodecode.lib.user_log_filter import user_log_filter
28 from rhodecode.lib.user_log_filter import user_log_filter
29 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
29 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
30 from rhodecode.lib.utils2 import safe_int
30 from rhodecode.lib.utils2 import safe_int
@@ -71,3 +71,21 b' class AdminAuditLogsView(BaseAppView):'
71 c.audit_logs = Page(users_log, page=p, items_per_page=10,
71 c.audit_logs = Page(users_log, page=p, items_per_page=10,
72 url=url_generator)
72 url=url_generator)
73 return self._get_template_context(c)
73 return self._get_template_context(c)
74
75 @LoginRequired()
76 @HasPermissionAllDecorator('hg.admin')
77 @view_config(
78 route_name='admin_audit_log_entry', request_method='GET',
79 renderer='rhodecode:templates/admin/admin_audit_log_entry.mako')
80 def admin_audit_log_entry(self):
81 c = self.load_default_context()
82 audit_log_id = self.request.matchdict['audit_log_id']
83
84 c.audit_log_entry = UserLog.query()\
85 .options(joinedload(UserLog.user))\
86 .options(joinedload(UserLog.repository))\
87 .filter(UserLog.user_log_id == audit_log_id).scalar()
88 if not c.audit_log_entry:
89 raise HTTPNotFound()
90
91 return self._get_template_context(c)
@@ -20,7 +20,6 b''
20
20
21 import logging
21 import logging
22
22
23
24 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
24 from pyramid.view import view_config
26
25
@@ -54,8 +53,10 b' class AdminMainView(BaseAppView):'
54 :param pull_request_id: id of pull requests in the system
53 :param pull_request_id: id of pull requests in the system
55 """
54 """
56
55
57 pull_request_id = self.request.matchdict.get('pull_request_id')
56 pull_request = PullRequest.get_or_404(
58 pull_request = PullRequest.get_or_404(pull_request_id, pyramid_exc=True)
57 self.request.matchdict['pull_request_id'])
58 pull_request_id = pull_request.pull_request_id
59
59 repo_name = pull_request.target_repo.repo_name
60 repo_name = pull_request.target_repo.repo_name
60
61
61 raise HTTPFound(
62 raise HTTPFound(
@@ -21,7 +21,6 b''
21 import collections
21 import collections
22 import logging
22 import logging
23
23
24
25 from pyramid.view import view_config
24 from pyramid.view import view_config
26
25
27 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
@@ -48,7 +47,7 b' class OpenSourceLicensesAdminSettingsVie'
48 c = self.load_default_context()
47 c = self.load_default_context()
49 c.active = 'open_source'
48 c.active = 'open_source'
50 c.navlist = navigation_list(self.request)
49 c.navlist = navigation_list(self.request)
51 c.opensource_licenses = collections.OrderedDict(
50 items = sorted(read_opensource_licenses().items(), key=lambda t: t[0])
52 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
51 c.opensource_licenses = collections.OrderedDict(items)
53
52
54 return self._get_template_context(c)
53 return self._get_template_context(c)
This diff has been collapsed as it changes many lines, (839 lines changed) Show them Hide them
@@ -21,50 +21,51 b''
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24
25
25 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.view import view_config
27 from pyramid.view import view_config
27 from sqlalchemy.sql.functions import coalesce
28 from pyramid.renderers import render
29 from pyramid.response import Response
28
30
29 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.events import trigger
30
35
31 from rhodecode.lib import audit_logger
36 from rhodecode.lib import audit_logger
37 from rhodecode.lib.exceptions import (
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
39 UserOwnsUserGroupsException, DefaultUserException)
32 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.auth import (
41 from rhodecode.lib.auth import (
34 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
42 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
35 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
36 from rhodecode.lib.utils import PartialRenderer
44 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
37 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.model.auth_token import AuthTokenModel
45 from rhodecode.model.auth_token import AuthTokenModel
46 from rhodecode.model.forms import (
47 UserForm, UserIndividualPermissionsForm, UserPermissionsForm)
48 from rhodecode.model.permission import PermissionModel
49 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.ssh_key import SshKeyModel
39 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
40 from rhodecode.model.user_group import UserGroupModel
52 from rhodecode.model.user_group import UserGroupModel
41 from rhodecode.model.db import User, or_, UserIpMap, UserEmailMap, UserApiKeys
53 from rhodecode.model.db import (
54 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
55 UserApiKeys, UserSshKeys, RepoGroup)
42 from rhodecode.model.meta import Session
56 from rhodecode.model.meta import Session
43
57
44 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
45
59
46
60
47 class AdminUsersView(BaseAppView, DataGridAppView):
61 class AdminUsersView(BaseAppView, DataGridAppView):
48 ALLOW_SCOPED_TOKENS = False
49 """
50 This view has alternative version inside EE, if modified please take a look
51 in there as well.
52 """
53
62
54 def load_default_context(self):
63 def load_default_context(self):
55 c = self._get_local_tmpl_context()
64 c = self._get_local_tmpl_context()
56 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
57 self._register_global_c(c)
65 self._register_global_c(c)
58 return c
66 return c
59
67
60 def _redirect_for_default_user(self, username):
68 @LoginRequired()
61 _ = self.request.translate
62 if username == User.DEFAULT_USER:
63 h.flash(_("You can't edit this user"), category='warning')
64 # TODO(marcink): redirect to 'users' admin panel once this
65 # is a pyramid view
66 raise HTTPFound('/')
67
68 @HasPermissionAllDecorator('hg.admin')
69 @HasPermissionAllDecorator('hg.admin')
69 @view_config(
70 @view_config(
70 route_name='users', request_method='GET',
71 route_name='users', request_method='GET',
@@ -73,16 +74,23 b' class AdminUsersView(BaseAppView, DataGr'
73 c = self.load_default_context()
74 c = self.load_default_context()
74 return self._get_template_context(c)
75 return self._get_template_context(c)
75
76
77 @LoginRequired()
76 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
77 @view_config(
79 @view_config(
78 # renderer defined below
80 # renderer defined below
79 route_name='users_data', request_method='GET',
81 route_name='users_data', request_method='GET',
80 renderer='json_ext', xhr=True)
82 renderer='json_ext', xhr=True)
81 def users_list_data(self):
83 def users_list_data(self):
84 column_map = {
85 'first_name': 'name',
86 'last_name': 'lastname',
87 }
82 draw, start, limit = self._extract_chunk(self.request)
88 draw, start, limit = self._extract_chunk(self.request)
83 search_q, order_by, order_dir = self._extract_ordering(self.request)
89 search_q, order_by, order_dir = self._extract_ordering(
90 self.request, column_map=column_map)
84
91
85 _render = PartialRenderer('data_table/_dt_elements.mako')
92 _render = self.request.get_partial_renderer(
93 'data_table/_dt_elements.mako')
86
94
87 def user_actions(user_id, username):
95 def user_actions(user_id, username):
88 return _render("user_actions", user_id, username)
96 return _render("user_actions", user_id, username)
@@ -126,7 +134,7 b' class AdminUsersView(BaseAppView, DataGr'
126 users_data = []
134 users_data = []
127 for user in users_list:
135 for user in users_list:
128 users_data.append({
136 users_data.append({
129 "username": h.gravatar_with_user(user.username),
137 "username": h.gravatar_with_user(self.request, user.username),
130 "email": user.email,
138 "email": user.email,
131 "first_name": user.first_name,
139 "first_name": user.first_name,
132 "last_name": user.last_name,
140 "last_name": user.last_name,
@@ -149,6 +157,529 b' class AdminUsersView(BaseAppView, DataGr'
149
157
150 return data
158 return data
151
159
160 def _set_personal_repo_group_template_vars(self, c_obj):
161 DummyUser = AttributeDict({
162 'username': '${username}',
163 'user_id': '${user_id}',
164 })
165 c_obj.default_create_repo_group = RepoGroupModel() \
166 .get_default_create_personal_repo_group()
167 c_obj.personal_repo_group_name = RepoGroupModel() \
168 .get_personal_group_name(DummyUser)
169
170 @LoginRequired()
171 @HasPermissionAllDecorator('hg.admin')
172 @view_config(
173 route_name='users_new', request_method='GET',
174 renderer='rhodecode:templates/admin/users/user_add.mako')
175 def users_new(self):
176 _ = self.request.translate
177 c = self.load_default_context()
178 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
179 self._set_personal_repo_group_template_vars(c)
180 return self._get_template_context(c)
181
182 @LoginRequired()
183 @HasPermissionAllDecorator('hg.admin')
184 @CSRFRequired()
185 @view_config(
186 route_name='users_create', request_method='POST',
187 renderer='rhodecode:templates/admin/users/user_add.mako')
188 def users_create(self):
189 _ = self.request.translate
190 c = self.load_default_context()
191 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
192 user_model = UserModel()
193 user_form = UserForm()()
194 try:
195 form_result = user_form.to_python(dict(self.request.POST))
196 user = user_model.create(form_result)
197 Session().flush()
198 creation_data = user.get_api_data()
199 username = form_result['username']
200
201 audit_logger.store_web(
202 'user.create', action_data={'data': creation_data},
203 user=c.rhodecode_user)
204
205 user_link = h.link_to(
206 h.escape(username),
207 h.route_path('user_edit', user_id=user.user_id))
208 h.flash(h.literal(_('Created user %(user_link)s')
209 % {'user_link': user_link}), category='success')
210 Session().commit()
211 except formencode.Invalid as errors:
212 self._set_personal_repo_group_template_vars(c)
213 data = render(
214 'rhodecode:templates/admin/users/user_add.mako',
215 self._get_template_context(c), self.request)
216 html = formencode.htmlfill.render(
217 data,
218 defaults=errors.value,
219 errors=errors.error_dict or {},
220 prefix_error=False,
221 encoding="UTF-8",
222 force_defaults=False
223 )
224 return Response(html)
225 except UserCreationError as e:
226 h.flash(e, 'error')
227 except Exception:
228 log.exception("Exception creation of user")
229 h.flash(_('Error occurred during creation of user %s')
230 % self.request.POST.get('username'), category='error')
231 raise HTTPFound(h.route_path('users'))
232
233
234 class UsersView(UserAppView):
235 ALLOW_SCOPED_TOKENS = False
236 """
237 This view has alternative version inside EE, if modified please take a look
238 in there as well.
239 """
240
241 def load_default_context(self):
242 c = self._get_local_tmpl_context()
243 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
244 c.allowed_languages = [
245 ('en', 'English (en)'),
246 ('de', 'German (de)'),
247 ('fr', 'French (fr)'),
248 ('it', 'Italian (it)'),
249 ('ja', 'Japanese (ja)'),
250 ('pl', 'Polish (pl)'),
251 ('pt', 'Portuguese (pt)'),
252 ('ru', 'Russian (ru)'),
253 ('zh', 'Chinese (zh)'),
254 ]
255 req = self.request
256
257 c.available_permissions = req.registry.settings['available_permissions']
258 PermissionModel().set_global_permission_choices(
259 c, gettext_translator=req.translate)
260
261 self._register_global_c(c)
262 return c
263
264 @LoginRequired()
265 @HasPermissionAllDecorator('hg.admin')
266 @CSRFRequired()
267 @view_config(
268 route_name='user_update', request_method='POST',
269 renderer='rhodecode:templates/admin/users/user_edit.mako')
270 def user_update(self):
271 _ = self.request.translate
272 c = self.load_default_context()
273
274 user_id = self.db_user_id
275 c.user = self.db_user
276
277 c.active = 'profile'
278 c.extern_type = c.user.extern_type
279 c.extern_name = c.user.extern_name
280 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
281 available_languages = [x[0] for x in c.allowed_languages]
282 _form = UserForm(edit=True, available_languages=available_languages,
283 old_data={'user_id': user_id,
284 'email': c.user.email})()
285 form_result = {}
286 old_values = c.user.get_api_data()
287 try:
288 form_result = _form.to_python(dict(self.request.POST))
289 skip_attrs = ['extern_type', 'extern_name']
290 # TODO: plugin should define if username can be updated
291 if c.extern_type != "rhodecode":
292 # forbid updating username for external accounts
293 skip_attrs.append('username')
294
295 UserModel().update_user(
296 user_id, skip_attrs=skip_attrs, **form_result)
297
298 audit_logger.store_web(
299 'user.edit', action_data={'old_data': old_values},
300 user=c.rhodecode_user)
301
302 Session().commit()
303 h.flash(_('User updated successfully'), category='success')
304 except formencode.Invalid as errors:
305 data = render(
306 'rhodecode:templates/admin/users/user_edit.mako',
307 self._get_template_context(c), self.request)
308 html = formencode.htmlfill.render(
309 data,
310 defaults=errors.value,
311 errors=errors.error_dict or {},
312 prefix_error=False,
313 encoding="UTF-8",
314 force_defaults=False
315 )
316 return Response(html)
317 except UserCreationError as e:
318 h.flash(e, 'error')
319 except Exception:
320 log.exception("Exception updating user")
321 h.flash(_('Error occurred during update of user %s')
322 % form_result.get('username'), category='error')
323 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
324
325 @LoginRequired()
326 @HasPermissionAllDecorator('hg.admin')
327 @CSRFRequired()
328 @view_config(
329 route_name='user_delete', request_method='POST',
330 renderer='rhodecode:templates/admin/users/user_edit.mako')
331 def user_delete(self):
332 _ = self.request.translate
333 c = self.load_default_context()
334 c.user = self.db_user
335
336 _repos = c.user.repositories
337 _repo_groups = c.user.repository_groups
338 _user_groups = c.user.user_groups
339
340 handle_repos = None
341 handle_repo_groups = None
342 handle_user_groups = None
343 # dummy call for flash of handle
344 set_handle_flash_repos = lambda: None
345 set_handle_flash_repo_groups = lambda: None
346 set_handle_flash_user_groups = lambda: None
347
348 if _repos and self.request.POST.get('user_repos'):
349 do = self.request.POST['user_repos']
350 if do == 'detach':
351 handle_repos = 'detach'
352 set_handle_flash_repos = lambda: h.flash(
353 _('Detached %s repositories') % len(_repos),
354 category='success')
355 elif do == 'delete':
356 handle_repos = 'delete'
357 set_handle_flash_repos = lambda: h.flash(
358 _('Deleted %s repositories') % len(_repos),
359 category='success')
360
361 if _repo_groups and self.request.POST.get('user_repo_groups'):
362 do = self.request.POST['user_repo_groups']
363 if do == 'detach':
364 handle_repo_groups = 'detach'
365 set_handle_flash_repo_groups = lambda: h.flash(
366 _('Detached %s repository groups') % len(_repo_groups),
367 category='success')
368 elif do == 'delete':
369 handle_repo_groups = 'delete'
370 set_handle_flash_repo_groups = lambda: h.flash(
371 _('Deleted %s repository groups') % len(_repo_groups),
372 category='success')
373
374 if _user_groups and self.request.POST.get('user_user_groups'):
375 do = self.request.POST['user_user_groups']
376 if do == 'detach':
377 handle_user_groups = 'detach'
378 set_handle_flash_user_groups = lambda: h.flash(
379 _('Detached %s user groups') % len(_user_groups),
380 category='success')
381 elif do == 'delete':
382 handle_user_groups = 'delete'
383 set_handle_flash_user_groups = lambda: h.flash(
384 _('Deleted %s user groups') % len(_user_groups),
385 category='success')
386
387 old_values = c.user.get_api_data()
388 try:
389 UserModel().delete(c.user, handle_repos=handle_repos,
390 handle_repo_groups=handle_repo_groups,
391 handle_user_groups=handle_user_groups)
392
393 audit_logger.store_web(
394 'user.delete', action_data={'old_data': old_values},
395 user=c.rhodecode_user)
396
397 Session().commit()
398 set_handle_flash_repos()
399 set_handle_flash_repo_groups()
400 set_handle_flash_user_groups()
401 h.flash(_('Successfully deleted user'), category='success')
402 except (UserOwnsReposException, UserOwnsRepoGroupsException,
403 UserOwnsUserGroupsException, DefaultUserException) as e:
404 h.flash(e, category='warning')
405 except Exception:
406 log.exception("Exception during deletion of user")
407 h.flash(_('An error occurred during deletion of user'),
408 category='error')
409 raise HTTPFound(h.route_path('users'))
410
411 @LoginRequired()
412 @HasPermissionAllDecorator('hg.admin')
413 @view_config(
414 route_name='user_edit', request_method='GET',
415 renderer='rhodecode:templates/admin/users/user_edit.mako')
416 def user_edit(self):
417 _ = self.request.translate
418 c = self.load_default_context()
419 c.user = self.db_user
420
421 c.active = 'profile'
422 c.extern_type = c.user.extern_type
423 c.extern_name = c.user.extern_name
424 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
425
426 defaults = c.user.get_dict()
427 defaults.update({'language': c.user.user_data.get('language')})
428
429 data = render(
430 'rhodecode:templates/admin/users/user_edit.mako',
431 self._get_template_context(c), self.request)
432 html = formencode.htmlfill.render(
433 data,
434 defaults=defaults,
435 encoding="UTF-8",
436 force_defaults=False
437 )
438 return Response(html)
439
440 @LoginRequired()
441 @HasPermissionAllDecorator('hg.admin')
442 @view_config(
443 route_name='user_edit_advanced', request_method='GET',
444 renderer='rhodecode:templates/admin/users/user_edit.mako')
445 def user_edit_advanced(self):
446 _ = self.request.translate
447 c = self.load_default_context()
448
449 user_id = self.db_user_id
450 c.user = self.db_user
451
452 c.active = 'advanced'
453 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
454 c.personal_repo_group_name = RepoGroupModel()\
455 .get_personal_group_name(c.user)
456
457 c.user_to_review_rules = sorted(
458 (x.user for x in c.user.user_review_rules),
459 key=lambda u: u.username.lower())
460
461 c.first_admin = User.get_first_super_admin()
462 defaults = c.user.get_dict()
463
464 # Interim workaround if the user participated on any pull requests as a
465 # reviewer.
466 has_review = len(c.user.reviewer_pull_requests)
467 c.can_delete_user = not has_review
468 c.can_delete_user_message = ''
469 inactive_link = h.link_to(
470 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
471 if has_review == 1:
472 c.can_delete_user_message = h.literal(_(
473 'The user participates as reviewer in {} pull request and '
474 'cannot be deleted. \nYou can set the user to '
475 '"{}" instead of deleting it.').format(
476 has_review, inactive_link))
477 elif has_review:
478 c.can_delete_user_message = h.literal(_(
479 'The user participates as reviewer in {} pull requests and '
480 'cannot be deleted. \nYou can set the user to '
481 '"{}" instead of deleting it.').format(
482 has_review, inactive_link))
483
484 data = render(
485 'rhodecode:templates/admin/users/user_edit.mako',
486 self._get_template_context(c), self.request)
487 html = formencode.htmlfill.render(
488 data,
489 defaults=defaults,
490 encoding="UTF-8",
491 force_defaults=False
492 )
493 return Response(html)
494
495 @LoginRequired()
496 @HasPermissionAllDecorator('hg.admin')
497 @view_config(
498 route_name='user_edit_global_perms', request_method='GET',
499 renderer='rhodecode:templates/admin/users/user_edit.mako')
500 def user_edit_global_perms(self):
501 _ = self.request.translate
502 c = self.load_default_context()
503 c.user = self.db_user
504
505 c.active = 'global_perms'
506
507 c.default_user = User.get_default_user()
508 defaults = c.user.get_dict()
509 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
510 defaults.update(c.default_user.get_default_perms())
511 defaults.update(c.user.get_default_perms())
512
513 data = render(
514 'rhodecode:templates/admin/users/user_edit.mako',
515 self._get_template_context(c), self.request)
516 html = formencode.htmlfill.render(
517 data,
518 defaults=defaults,
519 encoding="UTF-8",
520 force_defaults=False
521 )
522 return Response(html)
523
524 @LoginRequired()
525 @HasPermissionAllDecorator('hg.admin')
526 @CSRFRequired()
527 @view_config(
528 route_name='user_edit_global_perms_update', request_method='POST',
529 renderer='rhodecode:templates/admin/users/user_edit.mako')
530 def user_edit_global_perms_update(self):
531 _ = self.request.translate
532 c = self.load_default_context()
533
534 user_id = self.db_user_id
535 c.user = self.db_user
536
537 c.active = 'global_perms'
538 try:
539 # first stage that verifies the checkbox
540 _form = UserIndividualPermissionsForm()
541 form_result = _form.to_python(dict(self.request.POST))
542 inherit_perms = form_result['inherit_default_permissions']
543 c.user.inherit_default_permissions = inherit_perms
544 Session().add(c.user)
545
546 if not inherit_perms:
547 # only update the individual ones if we un check the flag
548 _form = UserPermissionsForm(
549 [x[0] for x in c.repo_create_choices],
550 [x[0] for x in c.repo_create_on_write_choices],
551 [x[0] for x in c.repo_group_create_choices],
552 [x[0] for x in c.user_group_create_choices],
553 [x[0] for x in c.fork_choices],
554 [x[0] for x in c.inherit_default_permission_choices])()
555
556 form_result = _form.to_python(dict(self.request.POST))
557 form_result.update({'perm_user_id': c.user.user_id})
558
559 PermissionModel().update_user_permissions(form_result)
560
561 # TODO(marcink): implement global permissions
562 # audit_log.store_web('user.edit.permissions')
563
564 Session().commit()
565 h.flash(_('User global permissions updated successfully'),
566 category='success')
567
568 except formencode.Invalid as errors:
569 data = render(
570 'rhodecode:templates/admin/users/user_edit.mako',
571 self._get_template_context(c), self.request)
572 html = formencode.htmlfill.render(
573 data,
574 defaults=errors.value,
575 errors=errors.error_dict or {},
576 prefix_error=False,
577 encoding="UTF-8",
578 force_defaults=False
579 )
580 return Response(html)
581 except Exception:
582 log.exception("Exception during permissions saving")
583 h.flash(_('An error occurred during permissions saving'),
584 category='error')
585 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
586
587 @LoginRequired()
588 @HasPermissionAllDecorator('hg.admin')
589 @CSRFRequired()
590 @view_config(
591 route_name='user_force_password_reset', request_method='POST',
592 renderer='rhodecode:templates/admin/users/user_edit.mako')
593 def user_force_password_reset(self):
594 """
595 toggle reset password flag for this user
596 """
597 _ = self.request.translate
598 c = self.load_default_context()
599
600 user_id = self.db_user_id
601 c.user = self.db_user
602
603 try:
604 old_value = c.user.user_data.get('force_password_change')
605 c.user.update_userdata(force_password_change=not old_value)
606
607 if old_value:
608 msg = _('Force password change disabled for user')
609 audit_logger.store_web(
610 'user.edit.password_reset.disabled',
611 user=c.rhodecode_user)
612 else:
613 msg = _('Force password change enabled for user')
614 audit_logger.store_web(
615 'user.edit.password_reset.enabled',
616 user=c.rhodecode_user)
617
618 Session().commit()
619 h.flash(msg, category='success')
620 except Exception:
621 log.exception("Exception during password reset for user")
622 h.flash(_('An error occurred during password reset for user'),
623 category='error')
624
625 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
626
627 @LoginRequired()
628 @HasPermissionAllDecorator('hg.admin')
629 @CSRFRequired()
630 @view_config(
631 route_name='user_create_personal_repo_group', request_method='POST',
632 renderer='rhodecode:templates/admin/users/user_edit.mako')
633 def user_create_personal_repo_group(self):
634 """
635 Create personal repository group for this user
636 """
637 from rhodecode.model.repo_group import RepoGroupModel
638
639 _ = self.request.translate
640 c = self.load_default_context()
641
642 user_id = self.db_user_id
643 c.user = self.db_user
644
645 personal_repo_group = RepoGroup.get_user_personal_repo_group(
646 c.user.user_id)
647 if personal_repo_group:
648 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
649
650 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
651 c.user)
652 named_personal_group = RepoGroup.get_by_group_name(
653 personal_repo_group_name)
654 try:
655
656 if named_personal_group and named_personal_group.user_id == c.user.user_id:
657 # migrate the same named group, and mark it as personal
658 named_personal_group.personal = True
659 Session().add(named_personal_group)
660 Session().commit()
661 msg = _('Linked repository group `%s` as personal' % (
662 personal_repo_group_name,))
663 h.flash(msg, category='success')
664 elif not named_personal_group:
665 RepoGroupModel().create_personal_repo_group(c.user)
666
667 msg = _('Created repository group `%s`' % (
668 personal_repo_group_name,))
669 h.flash(msg, category='success')
670 else:
671 msg = _('Repository group `%s` is already taken' % (
672 personal_repo_group_name,))
673 h.flash(msg, category='warning')
674 except Exception:
675 log.exception("Exception during repository group creation")
676 msg = _(
677 'An error occurred during repository group creation for user')
678 h.flash(msg, category='error')
679 Session().rollback()
680
681 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
682
152 @LoginRequired()
683 @LoginRequired()
153 @HasPermissionAllDecorator('hg.admin')
684 @HasPermissionAllDecorator('hg.admin')
154 @view_config(
685 @view_config(
@@ -157,27 +688,18 b' class AdminUsersView(BaseAppView, DataGr'
157 def auth_tokens(self):
688 def auth_tokens(self):
158 _ = self.request.translate
689 _ = self.request.translate
159 c = self.load_default_context()
690 c = self.load_default_context()
160
691 c.user = self.db_user
161 user_id = self.request.matchdict.get('user_id')
162 c.user = User.get_or_404(user_id, pyramid_exc=True)
163 self._redirect_for_default_user(c.user.username)
164
692
165 c.active = 'auth_tokens'
693 c.active = 'auth_tokens'
166
694
167 c.lifetime_values = [
695 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
168 (str(-1), _('forever')),
169 (str(5), _('5 minutes')),
170 (str(60), _('1 hour')),
171 (str(60 * 24), _('1 day')),
172 (str(60 * 24 * 30), _('1 month')),
173 ]
174 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
175 c.role_values = [
696 c.role_values = [
176 (x, AuthTokenModel.cls._get_role_name(x))
697 (x, AuthTokenModel.cls._get_role_name(x))
177 for x in AuthTokenModel.cls.ROLES]
698 for x in AuthTokenModel.cls.ROLES]
178 c.role_options = [(c.role_values, _("Role"))]
699 c.role_options = [(c.role_values, _("Role"))]
179 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
700 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
180 c.user.user_id, show_expired=True)
701 c.user.user_id, show_expired=True)
702 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
181 return self._get_template_context(c)
703 return self._get_template_context(c)
182
704
183 def maybe_attach_token_scope(self, token):
705 def maybe_attach_token_scope(self, token):
@@ -193,10 +715,8 b' class AdminUsersView(BaseAppView, DataGr'
193 _ = self.request.translate
715 _ = self.request.translate
194 c = self.load_default_context()
716 c = self.load_default_context()
195
717
196 user_id = self.request.matchdict.get('user_id')
718 user_id = self.db_user_id
197 c.user = User.get_or_404(user_id, pyramid_exc=True)
719 c.user = self.db_user
198
199 self._redirect_for_default_user(c.user.username)
200
720
201 user_data = c.user.get_api_data()
721 user_data = c.user.get_api_data()
202 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
722 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
@@ -226,15 +746,15 b' class AdminUsersView(BaseAppView, DataGr'
226 _ = self.request.translate
746 _ = self.request.translate
227 c = self.load_default_context()
747 c = self.load_default_context()
228
748
229 user_id = self.request.matchdict.get('user_id')
749 user_id = self.db_user_id
230 c.user = User.get_or_404(user_id, pyramid_exc=True)
750 c.user = self.db_user
231 self._redirect_for_default_user(c.user.username)
751
232 user_data = c.user.get_api_data()
752 user_data = c.user.get_api_data()
233
753
234 del_auth_token = self.request.POST.get('del_auth_token')
754 del_auth_token = self.request.POST.get('del_auth_token')
235
755
236 if del_auth_token:
756 if del_auth_token:
237 token = UserApiKeys.get_or_404(del_auth_token, pyramid_exc=True)
757 token = UserApiKeys.get_or_404(del_auth_token)
238 token_data = token.get_api_data()
758 token_data = token.get_api_data()
239
759
240 AuthTokenModel().delete(del_auth_token, c.user.user_id)
760 AuthTokenModel().delete(del_auth_token, c.user.user_id)
@@ -250,15 +770,127 b' class AdminUsersView(BaseAppView, DataGr'
250 @LoginRequired()
770 @LoginRequired()
251 @HasPermissionAllDecorator('hg.admin')
771 @HasPermissionAllDecorator('hg.admin')
252 @view_config(
772 @view_config(
773 route_name='edit_user_ssh_keys', request_method='GET',
774 renderer='rhodecode:templates/admin/users/user_edit.mako')
775 def ssh_keys(self):
776 _ = self.request.translate
777 c = self.load_default_context()
778 c.user = self.db_user
779
780 c.active = 'ssh_keys'
781 c.default_key = self.request.GET.get('default_key')
782 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
783 return self._get_template_context(c)
784
785 @LoginRequired()
786 @HasPermissionAllDecorator('hg.admin')
787 @view_config(
788 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
789 renderer='rhodecode:templates/admin/users/user_edit.mako')
790 def ssh_keys_generate_keypair(self):
791 _ = self.request.translate
792 c = self.load_default_context()
793
794 c.user = self.db_user
795
796 c.active = 'ssh_keys_generate'
797 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
798 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
799
800 return self._get_template_context(c)
801
802 @LoginRequired()
803 @HasPermissionAllDecorator('hg.admin')
804 @CSRFRequired()
805 @view_config(
806 route_name='edit_user_ssh_keys_add', request_method='POST')
807 def ssh_keys_add(self):
808 _ = self.request.translate
809 c = self.load_default_context()
810
811 user_id = self.db_user_id
812 c.user = self.db_user
813
814 user_data = c.user.get_api_data()
815 key_data = self.request.POST.get('key_data')
816 description = self.request.POST.get('description')
817
818 try:
819 if not key_data:
820 raise ValueError('Please add a valid public key')
821
822 key = SshKeyModel().parse_key(key_data.strip())
823 fingerprint = key.hash_md5()
824
825 ssh_key = SshKeyModel().create(
826 c.user.user_id, fingerprint, key_data, description)
827 ssh_key_data = ssh_key.get_api_data()
828
829 audit_logger.store_web(
830 'user.edit.ssh_key.add', action_data={
831 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
832 user=self._rhodecode_user, )
833 Session().commit()
834
835 # Trigger an event on change of keys.
836 trigger(SshKeyFileChangeEvent(), self.request.registry)
837
838 h.flash(_("Ssh Key successfully created"), category='success')
839
840 except IntegrityError:
841 log.exception("Exception during ssh key saving")
842 h.flash(_('An error occurred during ssh key saving: {}').format(
843 'Such key already exists, please use a different one'),
844 category='error')
845 except Exception as e:
846 log.exception("Exception during ssh key saving")
847 h.flash(_('An error occurred during ssh key saving: {}').format(e),
848 category='error')
849
850 return HTTPFound(
851 h.route_path('edit_user_ssh_keys', user_id=user_id))
852
853 @LoginRequired()
854 @HasPermissionAllDecorator('hg.admin')
855 @CSRFRequired()
856 @view_config(
857 route_name='edit_user_ssh_keys_delete', request_method='POST')
858 def ssh_keys_delete(self):
859 _ = self.request.translate
860 c = self.load_default_context()
861
862 user_id = self.db_user_id
863 c.user = self.db_user
864
865 user_data = c.user.get_api_data()
866
867 del_ssh_key = self.request.POST.get('del_ssh_key')
868
869 if del_ssh_key:
870 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
871 ssh_key_data = ssh_key.get_api_data()
872
873 SshKeyModel().delete(del_ssh_key, c.user.user_id)
874 audit_logger.store_web(
875 'user.edit.ssh_key.delete', action_data={
876 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
877 user=self._rhodecode_user,)
878 Session().commit()
879 # Trigger an event on change of keys.
880 trigger(SshKeyFileChangeEvent(), self.request.registry)
881 h.flash(_("Ssh key successfully deleted"), category='success')
882
883 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
884
885 @LoginRequired()
886 @HasPermissionAllDecorator('hg.admin')
887 @view_config(
253 route_name='edit_user_emails', request_method='GET',
888 route_name='edit_user_emails', request_method='GET',
254 renderer='rhodecode:templates/admin/users/user_edit.mako')
889 renderer='rhodecode:templates/admin/users/user_edit.mako')
255 def emails(self):
890 def emails(self):
256 _ = self.request.translate
891 _ = self.request.translate
257 c = self.load_default_context()
892 c = self.load_default_context()
258
893 c.user = self.db_user
259 user_id = self.request.matchdict.get('user_id')
260 c.user = User.get_or_404(user_id, pyramid_exc=True)
261 self._redirect_for_default_user(c.user.username)
262
894
263 c.active = 'emails'
895 c.active = 'emails'
264 c.user_email_map = UserEmailMap.query() \
896 c.user_email_map = UserEmailMap.query() \
@@ -275,22 +907,26 b' class AdminUsersView(BaseAppView, DataGr'
275 _ = self.request.translate
907 _ = self.request.translate
276 c = self.load_default_context()
908 c = self.load_default_context()
277
909
278 user_id = self.request.matchdict.get('user_id')
910 user_id = self.db_user_id
279 c.user = User.get_or_404(user_id, pyramid_exc=True)
911 c.user = self.db_user
280 self._redirect_for_default_user(c.user.username)
281
912
282 email = self.request.POST.get('new_email')
913 email = self.request.POST.get('new_email')
283 user_data = c.user.get_api_data()
914 user_data = c.user.get_api_data()
284 try:
915 try:
285 UserModel().add_extra_email(c.user.user_id, email)
916 UserModel().add_extra_email(c.user.user_id, email)
286 audit_logger.store_web(
917 audit_logger.store_web(
287 'user.edit.email.add', action_data={'email': email, 'user': user_data},
918 'user.edit.email.add',
919 action_data={'email': email, 'user': user_data},
288 user=self._rhodecode_user)
920 user=self._rhodecode_user)
289 Session().commit()
921 Session().commit()
290 h.flash(_("Added new email address `%s` for user account") % email,
922 h.flash(_("Added new email address `%s` for user account") % email,
291 category='success')
923 category='success')
292 except formencode.Invalid as error:
924 except formencode.Invalid as error:
293 h.flash(h.escape(error.error_dict['email']), category='error')
925 h.flash(h.escape(error.error_dict['email']), category='error')
926 except IntegrityError:
927 log.warning("Email %s already exists", email)
928 h.flash(_('Email `{}` is already registered for another user.').format(email),
929 category='error')
294 except Exception:
930 except Exception:
295 log.exception("Exception during email saving")
931 log.exception("Exception during email saving")
296 h.flash(_('An error occurred during email saving'),
932 h.flash(_('An error occurred during email saving'),
@@ -306,9 +942,8 b' class AdminUsersView(BaseAppView, DataGr'
306 _ = self.request.translate
942 _ = self.request.translate
307 c = self.load_default_context()
943 c = self.load_default_context()
308
944
309 user_id = self.request.matchdict.get('user_id')
945 user_id = self.db_user_id
310 c.user = User.get_or_404(user_id, pyramid_exc=True)
946 c.user = self.db_user
311 self._redirect_for_default_user(c.user.username)
312
947
313 email_id = self.request.POST.get('del_email_id')
948 email_id = self.request.POST.get('del_email_id')
314 user_model = UserModel()
949 user_model = UserModel()
@@ -317,7 +952,8 b' class AdminUsersView(BaseAppView, DataGr'
317 user_data = c.user.get_api_data()
952 user_data = c.user.get_api_data()
318 user_model.delete_extra_email(c.user.user_id, email_id)
953 user_model.delete_extra_email(c.user.user_id, email_id)
319 audit_logger.store_web(
954 audit_logger.store_web(
320 'user.edit.email.delete', action_data={'email': email, 'user': user_data},
955 'user.edit.email.delete',
956 action_data={'email': email, 'user': user_data},
321 user=self._rhodecode_user)
957 user=self._rhodecode_user)
322 Session().commit()
958 Session().commit()
323 h.flash(_("Removed email address from user account"),
959 h.flash(_("Removed email address from user account"),
@@ -332,10 +968,7 b' class AdminUsersView(BaseAppView, DataGr'
332 def ips(self):
968 def ips(self):
333 _ = self.request.translate
969 _ = self.request.translate
334 c = self.load_default_context()
970 c = self.load_default_context()
335
971 c.user = self.db_user
336 user_id = self.request.matchdict.get('user_id')
337 c.user = User.get_or_404(user_id, pyramid_exc=True)
338 self._redirect_for_default_user(c.user.username)
339
972
340 c.active = 'ips'
973 c.active = 'ips'
341 c.user_ip_map = UserIpMap.query() \
974 c.user_ip_map = UserIpMap.query() \
@@ -352,14 +985,14 b' class AdminUsersView(BaseAppView, DataGr'
352 @CSRFRequired()
985 @CSRFRequired()
353 @view_config(
986 @view_config(
354 route_name='edit_user_ips_add', request_method='POST')
987 route_name='edit_user_ips_add', request_method='POST')
988 # NOTE(marcink): this view is allowed for default users, as we can
989 # edit their IP white list
355 def ips_add(self):
990 def ips_add(self):
356 _ = self.request.translate
991 _ = self.request.translate
357 c = self.load_default_context()
992 c = self.load_default_context()
358
993
359 user_id = self.request.matchdict.get('user_id')
994 user_id = self.db_user_id
360 c.user = User.get_or_404(user_id, pyramid_exc=True)
995 c.user = self.db_user
361 # NOTE(marcink): this view is allowed for default users, as we can
362 # edit their IP white list
363
996
364 user_model = UserModel()
997 user_model = UserModel()
365 desc = self.request.POST.get('description')
998 desc = self.request.POST.get('description')
@@ -377,7 +1010,8 b' class AdminUsersView(BaseAppView, DataGr'
377 try:
1010 try:
378 user_model.add_extra_ip(c.user.user_id, ip, desc)
1011 user_model.add_extra_ip(c.user.user_id, ip, desc)
379 audit_logger.store_web(
1012 audit_logger.store_web(
380 'user.edit.ip.add', action_data={'ip': ip, 'user': user_data},
1013 'user.edit.ip.add',
1014 action_data={'ip': ip, 'user': user_data},
381 user=self._rhodecode_user)
1015 user=self._rhodecode_user)
382 Session().commit()
1016 Session().commit()
383 added.append(ip)
1017 added.append(ip)
@@ -402,14 +1036,14 b' class AdminUsersView(BaseAppView, DataGr'
402 @CSRFRequired()
1036 @CSRFRequired()
403 @view_config(
1037 @view_config(
404 route_name='edit_user_ips_delete', request_method='POST')
1038 route_name='edit_user_ips_delete', request_method='POST')
1039 # NOTE(marcink): this view is allowed for default users, as we can
1040 # edit their IP white list
405 def ips_delete(self):
1041 def ips_delete(self):
406 _ = self.request.translate
1042 _ = self.request.translate
407 c = self.load_default_context()
1043 c = self.load_default_context()
408
1044
409 user_id = self.request.matchdict.get('user_id')
1045 user_id = self.db_user_id
410 c.user = User.get_or_404(user_id, pyramid_exc=True)
1046 c.user = self.db_user
411 # NOTE(marcink): this view is allowed for default users, as we can
412 # edit their IP white list
413
1047
414 ip_id = self.request.POST.get('del_ip_id')
1048 ip_id = self.request.POST.get('del_ip_id')
415 user_model = UserModel()
1049 user_model = UserModel()
@@ -434,11 +1068,9 b' class AdminUsersView(BaseAppView, DataGr'
434 renderer='rhodecode:templates/admin/users/user_edit.mako')
1068 renderer='rhodecode:templates/admin/users/user_edit.mako')
435 def groups_management(self):
1069 def groups_management(self):
436 c = self.load_default_context()
1070 c = self.load_default_context()
1071 c.user = self.db_user
1072 c.data = c.user.group_member
437
1073
438 user_id = self.request.matchdict.get('user_id')
439 c.user = User.get_or_404(user_id, pyramid_exc=True)
440 c.data = c.user.group_member
441 self._redirect_for_default_user(c.user.username)
442 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1074 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
443 for group in c.user.group_member]
1075 for group in c.user.group_member]
444 c.groups = json.dumps(groups)
1076 c.groups = json.dumps(groups)
@@ -455,17 +1087,35 b' class AdminUsersView(BaseAppView, DataGr'
455 _ = self.request.translate
1087 _ = self.request.translate
456 c = self.load_default_context()
1088 c = self.load_default_context()
457
1089
458 user_id = self.request.matchdict.get('user_id')
1090 user_id = self.db_user_id
459 c.user = User.get_or_404(user_id, pyramid_exc=True)
1091 c.user = self.db_user
460 self._redirect_for_default_user(c.user.username)
1092
1093 user_groups = set(self.request.POST.getall('users_group_id'))
1094 user_groups_objects = []
1095
1096 for ugid in user_groups:
1097 user_groups_objects.append(
1098 UserGroupModel().get_group(safe_int(ugid)))
1099 user_group_model = UserGroupModel()
1100 added_to_groups, removed_from_groups = \
1101 user_group_model.change_groups(c.user, user_groups_objects)
461
1102
462 users_groups = set(self.request.POST.getall('users_group_id'))
1103 user_data = c.user.get_api_data()
463 users_groups_model = []
1104 for user_group_id in added_to_groups:
1105 user_group = UserGroup.get(user_group_id)
1106 old_values = user_group.get_api_data()
1107 audit_logger.store_web(
1108 'user_group.edit.member.add',
1109 action_data={'user': user_data, 'old_data': old_values},
1110 user=self._rhodecode_user)
464
1111
465 for ugid in users_groups:
1112 for user_group_id in removed_from_groups:
466 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
1113 user_group = UserGroup.get(user_group_id)
467 user_group_model = UserGroupModel()
1114 old_values = user_group.get_api_data()
468 user_group_model.change_groups(c.user, users_groups_model)
1115 audit_logger.store_web(
1116 'user_group.edit.member.delete',
1117 action_data={'user': user_data, 'old_data': old_values},
1118 user=self._rhodecode_user)
469
1119
470 Session().commit()
1120 Session().commit()
471 c.active = 'user_groups_management'
1121 c.active = 'user_groups_management'
@@ -482,10 +1132,8 b' class AdminUsersView(BaseAppView, DataGr'
482 def user_audit_logs(self):
1132 def user_audit_logs(self):
483 _ = self.request.translate
1133 _ = self.request.translate
484 c = self.load_default_context()
1134 c = self.load_default_context()
1135 c.user = self.db_user
485
1136
486 user_id = self.request.matchdict.get('user_id')
487 c.user = User.get_or_404(user_id, pyramid_exc=True)
488 self._redirect_for_default_user(c.user.username)
489 c.active = 'audit'
1137 c.active = 'audit'
490
1138
491 p = safe_int(self.request.GET.get('page', 1), 1)
1139 p = safe_int(self.request.GET.get('page', 1), 1)
@@ -503,3 +1151,28 b' class AdminUsersView(BaseAppView, DataGr'
503 c.filter_term = filter_term
1151 c.filter_term = filter_term
504 return self._get_template_context(c)
1152 return self._get_template_context(c)
505
1153
1154 @LoginRequired()
1155 @HasPermissionAllDecorator('hg.admin')
1156 @view_config(
1157 route_name='edit_user_perms_summary', request_method='GET',
1158 renderer='rhodecode:templates/admin/users/user_edit.mako')
1159 def user_perms_summary(self):
1160 _ = self.request.translate
1161 c = self.load_default_context()
1162 c.user = self.db_user
1163
1164 c.active = 'perms_summary'
1165 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1166
1167 return self._get_template_context(c)
1168
1169 @LoginRequired()
1170 @HasPermissionAllDecorator('hg.admin')
1171 @view_config(
1172 route_name='edit_user_perms_summary_json', request_method='GET',
1173 renderer='json_ext')
1174 def user_perms_summary_json(self):
1175 self.load_default_context()
1176 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1177
1178 return perm_user.permissions
@@ -87,4 +87,4 b' def includeme(config):'
87 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
87 pattern=settings.get('channelstream.proxy_path') or '/_channelstream')
88
88
89 # Scan module for configuration decorators.
89 # Scan module for configuration decorators.
90 config.scan()
90 config.scan('.views', ignore='.tests')
@@ -18,20 +18,11 b''
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 """
22 Channel Stream controller for rhodecode
23
24 :created_on: Oct 10, 2015
25 :author: marcinl
26 :copyright: (c) 2013-2015 RhodeCode GmbH.
27 :license: Commercial License, see LICENSE for more details.
28 """
29
30 import logging
21 import logging
31 import uuid
22 import uuid
32
23
33 from pyramid.view import view_config
24 from pyramid.view import view_config
34 from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPBadGateway
25 from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPBadGateway
35
26
36 from rhodecode.lib.channelstream import (
27 from rhodecode.lib.channelstream import (
37 channelstream_request,
28 channelstream_request,
@@ -27,7 +27,31 b' from rhodecode.model.gist import GistMod'
27 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
28 from rhodecode.tests import (
28 from rhodecode.tests import (
29 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
29 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
30 TestController, assert_session_flash, url)
30 TestController, assert_session_flash)
31
32
33 def route_path(name, params=None, **kwargs):
34 import urllib
35 from rhodecode.apps._base import ADMIN_PREFIX
36
37 base_url = {
38 'gists_show': ADMIN_PREFIX + '/gists',
39 'gists_new': ADMIN_PREFIX + '/gists/new',
40 'gists_create': ADMIN_PREFIX + '/gists/create',
41 'gist_show': ADMIN_PREFIX + '/gists/{gist_id}',
42 'gist_delete': ADMIN_PREFIX + '/gists/{gist_id}/delete',
43 'gist_edit': ADMIN_PREFIX + '/gists/{gist_id}/edit',
44 'gist_edit_check_revision': ADMIN_PREFIX + '/gists/{gist_id}/edit/check_revision',
45 'gist_update': ADMIN_PREFIX + '/gists/{gist_id}/update',
46 'gist_show_rev': ADMIN_PREFIX + '/gists/{gist_id}/{revision}',
47 'gist_show_formatted': ADMIN_PREFIX + '/gists/{gist_id}/{revision}/{format}',
48 'gist_show_formatted_path': ADMIN_PREFIX + '/gists/{gist_id}/{revision}/{format}/{f_path}',
49
50 }[name].format(**kwargs)
51
52 if params:
53 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
54 return base_url
31
55
32
56
33 class GistUtility(object):
57 class GistUtility(object):
@@ -70,7 +94,7 b' class TestGistsController(TestController'
70
94
71 def test_index_empty(self, create_gist):
95 def test_index_empty(self, create_gist):
72 self.log_user()
96 self.log_user()
73 response = self.app.get(url('gists'))
97 response = self.app.get(route_path('gists_show'))
74 response.mustcontain('data: [],')
98 response.mustcontain('data: [],')
75
99
76 def test_index(self, create_gist):
100 def test_index(self, create_gist):
@@ -79,7 +103,7 b' class TestGistsController(TestController'
79 g2 = create_gist('gist2', lifetime=1400)
103 g2 = create_gist('gist2', lifetime=1400)
80 g3 = create_gist('gist3', description='gist3-desc')
104 g3 = create_gist('gist3', description='gist3-desc')
81 g4 = create_gist('gist4', gist_type='private').gist_access_id
105 g4 = create_gist('gist4', gist_type='private').gist_access_id
82 response = self.app.get(url('gists'))
106 response = self.app.get(route_path('gists_show'))
83
107
84 response.mustcontain('gist: %s' % g1.gist_access_id)
108 response.mustcontain('gist: %s' % g1.gist_access_id)
85 response.mustcontain('gist: %s' % g2.gist_access_id)
109 response.mustcontain('gist: %s' % g2.gist_access_id)
@@ -95,7 +119,7 b' class TestGistsController(TestController'
95 def test_index_private_gists(self, create_gist):
119 def test_index_private_gists(self, create_gist):
96 self.log_user()
120 self.log_user()
97 gist = create_gist('gist5', gist_type='private')
121 gist = create_gist('gist5', gist_type='private')
98 response = self.app.get(url('gists', private=1))
122 response = self.app.get(route_path('gists_show', params=dict(private=1)))
99
123
100 # and privates
124 # and privates
101 response.mustcontain('gist: %s' % gist.gist_access_id)
125 response.mustcontain('gist: %s' % gist.gist_access_id)
@@ -107,7 +131,7 b' class TestGistsController(TestController'
107 create_gist('gist3', description='gist3-desc')
131 create_gist('gist3', description='gist3-desc')
108 create_gist('gist4', gist_type='private')
132 create_gist('gist4', gist_type='private')
109
133
110 response = self.app.get(url('gists', all=1))
134 response = self.app.get(route_path('gists_show', params=dict(all=1)))
111
135
112 assert len(GistModel.get_all()) == 4
136 assert len(GistModel.get_all()) == 4
113 # and privates
137 # and privates
@@ -120,7 +144,7 b' class TestGistsController(TestController'
120 create_gist('gist3', gist_type='private')
144 create_gist('gist3', gist_type='private')
121 create_gist('gist4', gist_type='private')
145 create_gist('gist4', gist_type='private')
122
146
123 response = self.app.get(url('gists', all=1))
147 response = self.app.get(route_path('gists_show', params=dict(all=1)))
124
148
125 assert len(GistModel.get_all()) == 3
149 assert len(GistModel.get_all()) == 3
126 # since we don't have access to private in this view, we
150 # since we don't have access to private in this view, we
@@ -131,7 +155,7 b' class TestGistsController(TestController'
131 def test_create(self):
155 def test_create(self):
132 self.log_user()
156 self.log_user()
133 response = self.app.post(
157 response = self.app.post(
134 url('gists'),
158 route_path('gists_create'),
135 params={'lifetime': -1,
159 params={'lifetime': -1,
136 'content': 'gist test',
160 'content': 'gist test',
137 'filename': 'foo',
161 'filename': 'foo',
@@ -146,7 +170,7 b' class TestGistsController(TestController'
146 def test_create_with_path_with_dirs(self):
170 def test_create_with_path_with_dirs(self):
147 self.log_user()
171 self.log_user()
148 response = self.app.post(
172 response = self.app.post(
149 url('gists'),
173 route_path('gists_create'),
150 params={'lifetime': -1,
174 params={'lifetime': -1,
151 'content': 'gist test',
175 'content': 'gist test',
152 'filename': '/home/foo',
176 'filename': '/home/foo',
@@ -163,12 +187,13 b' class TestGistsController(TestController'
163 Session().add(gist)
187 Session().add(gist)
164 Session().commit()
188 Session().commit()
165
189
166 self.app.get(url('gist', gist_id=gist.gist_access_id), status=404)
190 self.app.get(route_path('gist_show', gist_id=gist.gist_access_id),
191 status=404)
167
192
168 def test_create_private(self):
193 def test_create_private(self):
169 self.log_user()
194 self.log_user()
170 response = self.app.post(
195 response = self.app.post(
171 url('gists'),
196 route_path('gists_create'),
172 params={'lifetime': -1,
197 params={'lifetime': -1,
173 'content': 'private gist test',
198 'content': 'private gist test',
174 'filename': 'private-foo',
199 'filename': 'private-foo',
@@ -187,7 +212,7 b' class TestGistsController(TestController'
187 def test_create_private_acl_private(self):
212 def test_create_private_acl_private(self):
188 self.log_user()
213 self.log_user()
189 response = self.app.post(
214 response = self.app.post(
190 url('gists'),
215 route_path('gists_create'),
191 params={'lifetime': -1,
216 params={'lifetime': -1,
192 'content': 'private gist test',
217 'content': 'private gist test',
193 'filename': 'private-foo',
218 'filename': 'private-foo',
@@ -206,7 +231,7 b' class TestGistsController(TestController'
206 def test_create_with_description(self):
231 def test_create_with_description(self):
207 self.log_user()
232 self.log_user()
208 response = self.app.post(
233 response = self.app.post(
209 url('gists'),
234 route_path('gists_create'),
210 params={'lifetime': -1,
235 params={'lifetime': -1,
211 'content': 'gist test',
236 'content': 'gist test',
212 'filename': 'foo-desc',
237 'filename': 'foo-desc',
@@ -231,7 +256,8 b' class TestGistsController(TestController'
231 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
256 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
232 'csrf_token': self.csrf_token
257 'csrf_token': self.csrf_token
233 }
258 }
234 response = self.app.post(url('gists'), params=params, status=302)
259 response = self.app.post(
260 route_path('gists_create'), params=params, status=302)
235 self.logout_user()
261 self.logout_user()
236 response = response.follow()
262 response = response.follow()
237 response.mustcontain('added file: foo-desc')
263 response.mustcontain('added file: foo-desc')
@@ -240,35 +266,36 b' class TestGistsController(TestController'
240
266
241 def test_new(self):
267 def test_new(self):
242 self.log_user()
268 self.log_user()
243 self.app.get(url('new_gist'))
269 self.app.get(route_path('gists_new'))
244
270
245 def test_delete(self, create_gist):
271 def test_delete(self, create_gist):
246 self.log_user()
272 self.log_user()
247 gist = create_gist('delete-me')
273 gist = create_gist('delete-me')
248 response = self.app.post(
274 response = self.app.post(
249 url('gist', gist_id=gist.gist_id),
275 route_path('gist_delete', gist_id=gist.gist_id),
250 params={'_method': 'delete', 'csrf_token': self.csrf_token})
276 params={'csrf_token': self.csrf_token})
251 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
277 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
252
278
253 def test_delete_normal_user_his_gist(self, create_gist):
279 def test_delete_normal_user_his_gist(self, create_gist):
254 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
280 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
255 gist = create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
281 gist = create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
282
256 response = self.app.post(
283 response = self.app.post(
257 url('gist', gist_id=gist.gist_id),
284 route_path('gist_delete', gist_id=gist.gist_id),
258 params={'_method': 'delete', 'csrf_token': self.csrf_token})
285 params={'csrf_token': self.csrf_token})
259 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
286 assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
260
287
261 def test_delete_normal_user_not_his_own_gist(self, create_gist):
288 def test_delete_normal_user_not_his_own_gist(self, create_gist):
262 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
289 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
263 gist = create_gist('delete-me')
290 gist = create_gist('delete-me-2')
291
264 self.app.post(
292 self.app.post(
265 url('gist', gist_id=gist.gist_id),
293 route_path('gist_delete', gist_id=gist.gist_id),
266 params={'_method': 'delete', 'csrf_token': self.csrf_token},
294 params={'csrf_token': self.csrf_token}, status=404)
267 status=403)
268
295
269 def test_show(self, create_gist):
296 def test_show(self, create_gist):
270 gist = create_gist('gist-show-me')
297 gist = create_gist('gist-show-me')
271 response = self.app.get(url('gist', gist_id=gist.gist_access_id))
298 response = self.app.get(route_path('gist_show', gist_id=gist.gist_access_id))
272
299
273 response.mustcontain('added file: gist-show-me<')
300 response.mustcontain('added file: gist-show-me<')
274
301
@@ -283,16 +310,19 b' class TestGistsController(TestController'
283 with mock.patch(
310 with mock.patch(
284 'rhodecode.lib.vcs.settings.ALIASES', ['git']):
311 'rhodecode.lib.vcs.settings.ALIASES', ['git']):
285 gist = create_gist('gist-show-me-again')
312 gist = create_gist('gist-show-me-again')
286 self.app.get(url('gist', gist_id=gist.gist_access_id), status=200)
313 self.app.get(
314 route_path('gist_show', gist_id=gist.gist_access_id), status=200)
287
315
288 def test_show_acl_private(self, create_gist):
316 def test_show_acl_private(self, create_gist):
289 gist = create_gist('gist-show-me-only-when-im-logged-in',
317 gist = create_gist('gist-show-me-only-when-im-logged-in',
290 acl_level=Gist.ACL_LEVEL_PRIVATE)
318 acl_level=Gist.ACL_LEVEL_PRIVATE)
291 self.app.get(url('gist', gist_id=gist.gist_access_id), status=404)
319 self.app.get(
320 route_path('gist_show', gist_id=gist.gist_access_id), status=404)
292
321
293 # now we log-in we should see thi gist
322 # now we log-in we should see thi gist
294 self.log_user()
323 self.log_user()
295 response = self.app.get(url('gist', gist_id=gist.gist_access_id))
324 response = self.app.get(
325 route_path('gist_show', gist_id=gist.gist_access_id))
296 response.mustcontain('added file: gist-show-me-only-when-im-logged-in')
326 response.mustcontain('added file: gist-show-me-only-when-im-logged-in')
297
327
298 assert_response = response.assert_response()
328 assert_response = response.assert_response()
@@ -303,36 +333,42 b' class TestGistsController(TestController'
303
333
304 def test_show_as_raw(self, create_gist):
334 def test_show_as_raw(self, create_gist):
305 gist = create_gist('gist-show-me', content='GIST CONTENT')
335 gist = create_gist('gist-show-me', content='GIST CONTENT')
306 response = self.app.get(url('formatted_gist',
336 response = self.app.get(
307 gist_id=gist.gist_access_id, format='raw'))
337 route_path('gist_show_formatted',
338 gist_id=gist.gist_access_id, revision='tip',
339 format='raw'))
308 assert response.body == 'GIST CONTENT'
340 assert response.body == 'GIST CONTENT'
309
341
310 def test_show_as_raw_individual_file(self, create_gist):
342 def test_show_as_raw_individual_file(self, create_gist):
311 gist = create_gist('gist-show-me-raw', content='GIST BODY')
343 gist = create_gist('gist-show-me-raw', content='GIST BODY')
312 response = self.app.get(url('formatted_gist_file',
344 response = self.app.get(
313 gist_id=gist.gist_access_id, format='raw',
345 route_path('gist_show_formatted_path',
314 revision='tip', f_path='gist-show-me-raw'))
346 gist_id=gist.gist_access_id, format='raw',
347 revision='tip', f_path='gist-show-me-raw'))
315 assert response.body == 'GIST BODY'
348 assert response.body == 'GIST BODY'
316
349
317 def test_edit_page(self, create_gist):
350 def test_edit_page(self, create_gist):
318 self.log_user()
351 self.log_user()
319 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
352 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
320 response = self.app.get(url('edit_gist', gist_id=gist.gist_access_id))
353 response = self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id))
321 response.mustcontain('GIST EDIT BODY')
354 response.mustcontain('GIST EDIT BODY')
322
355
323 def test_edit_page_non_logged_user(self, create_gist):
356 def test_edit_page_non_logged_user(self, create_gist):
324 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
357 gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
325 self.app.get(url('edit_gist', gist_id=gist.gist_access_id), status=302)
358 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
359 status=302)
326
360
327 def test_edit_normal_user_his_gist(self, create_gist):
361 def test_edit_normal_user_his_gist(self, create_gist):
328 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
362 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
329 gist = create_gist('gist-for-edit', owner=TEST_USER_REGULAR_LOGIN)
363 gist = create_gist('gist-for-edit', owner=TEST_USER_REGULAR_LOGIN)
330 self.app.get(url('edit_gist', gist_id=gist.gist_access_id, status=200))
364 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id,
365 status=200))
331
366
332 def test_edit_normal_user_not_his_own_gist(self, create_gist):
367 def test_edit_normal_user_not_his_own_gist(self, create_gist):
333 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
368 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
334 gist = create_gist('delete-me')
369 gist = create_gist('delete-me')
335 self.app.get(url('edit_gist', gist_id=gist.gist_access_id), status=403)
370 self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
371 status=404)
336
372
337 def test_user_first_name_is_escaped(self, user_util, create_gist):
373 def test_user_first_name_is_escaped(self, user_util, create_gist):
338 xss_atack_string = '"><script>alert(\'First Name\')</script>'
374 xss_atack_string = '"><script>alert(\'First Name\')</script>'
@@ -341,7 +377,7 b' class TestGistsController(TestController'
341 user = user_util.create_user(
377 user = user_util.create_user(
342 firstname=xss_atack_string, password=password)
378 firstname=xss_atack_string, password=password)
343 create_gist('gist', gist_type='public', owner=user.username)
379 create_gist('gist', gist_type='public', owner=user.username)
344 response = self.app.get(url('gists'))
380 response = self.app.get(route_path('gists_show'))
345 response.mustcontain(xss_escaped_string)
381 response.mustcontain(xss_escaped_string)
346
382
347 def test_user_last_name_is_escaped(self, user_util, create_gist):
383 def test_user_last_name_is_escaped(self, user_util, create_gist):
@@ -351,5 +387,5 b' class TestGistsController(TestController'
351 user = user_util.create_user(
387 user = user_util.create_user(
352 lastname=xss_atack_string, password=password)
388 lastname=xss_atack_string, password=password)
353 create_gist('gist', gist_type='public', owner=user.username)
389 create_gist('gist', gist_type='public', owner=user.username)
354 response = self.app.get(url('gists'))
390 response = self.app.get(route_path('gists_show'))
355 response.mustcontain(xss_escaped_string)
391 response.mustcontain(xss_escaped_string)
@@ -46,4 +46,4 b' def includeme(config):'
46 routing_links.connect_redirection_links(config)
46 routing_links.connect_redirection_links(config)
47
47
48 # Scan module for configuration decorators.
48 # Scan module for configuration decorators.
49 config.scan()
49 config.scan('.views', ignore='.tests')
@@ -54,14 +54,16 b' class TestHomeController(TestController)'
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
55
55
56 def test_index_contains_statics_with_ver(self):
56 def test_index_contains_statics_with_ver(self):
57 from pylons import tmpl_context as c
57 from rhodecode.lib.base import calculate_version_hash
58
58
59 self.log_user()
59 self.log_user()
60 response = self.app.get(route_path('home'))
60 response = self.app.get(route_path('home'))
61
61
62 rhodecode_version_hash = c.rhodecode_version_hash
62 rhodecode_version_hash = calculate_version_hash(
63 {'beaker.session.secret':'test-rc-uytcxaz'})
63 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
64 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
64 response.mustcontain('rhodecode-components.js?ver={0}'.format(rhodecode_version_hash))
65 response.mustcontain('rhodecode-components.js?ver={0}'.format(
66 rhodecode_version_hash))
65
67
66 def test_index_contains_backend_specific_details(self, backend):
68 def test_index_contains_backend_specific_details(self, backend):
67 self.log_user()
69 self.log_user()
@@ -132,3 +134,9 b' class TestHomeController(TestController)'
132 response.mustcontain(version_string)
134 response.mustcontain(version_string)
133 if state is False:
135 if state is False:
134 response.mustcontain(no=[version_string])
136 response.mustcontain(no=[version_string])
137
138 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
139 response = self.app.get(route_path('home'))
140 assert_response = response.assert_response()
141 element = assert_response.get_element('.logout #csrf_token')
142 assert element.value == csrf_token
@@ -25,15 +25,16 b' from pyramid.view import view_config'
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous, \
28 from rhodecode.lib.auth import (
29 HasRepoGroupPermissionAnyDecorator
29 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
30 from rhodecode.lib.index import searcher_from_config
30 from rhodecode.lib.index import searcher_from_config
31 from rhodecode.lib.utils2 import safe_unicode, str2bool
31 from rhodecode.lib.utils2 import safe_unicode, str2bool
32 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.ext_json import json
33 from rhodecode.model.db import func, Repository, RepoGroup
33 from rhodecode.model.db import (
34 func, or_, in_filter_generator, Repository, RepoGroup)
34 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo_group import RepoGroupModel
36 from rhodecode.model.repo_group import RepoGroupModel
36 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
37 from rhodecode.model.scm import RepoGroupList, RepoList
37 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user_group import UserGroupModel
39 from rhodecode.model.user_group import UserGroupModel
39
40
@@ -101,9 +102,17 b' class HomeView(BaseAppView):'
101 return {'suggestions': _user_groups}
102 return {'suggestions': _user_groups}
102
103
103 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
104 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
105 allowed_ids = self._rhodecode_user.repo_acl_ids(
106 ['repository.read', 'repository.write', 'repository.admin'],
107 cache=False, name_filter=name_contains) or [-1]
108
104 query = Repository.query()\
109 query = Repository.query()\
105 .order_by(func.length(Repository.repo_name))\
110 .order_by(func.length(Repository.repo_name))\
106 .order_by(Repository.repo_name)
111 .order_by(Repository.repo_name)\
112 .filter(or_(
113 # generate multiple IN to fix limitation problems
114 *in_filter_generator(Repository.repo_id, allowed_ids)
115 ))
107
116
108 if repo_type:
117 if repo_type:
109 query = query.filter(Repository.repo_type == repo_type)
118 query = query.filter(Repository.repo_type == repo_type)
@@ -114,23 +123,31 b' class HomeView(BaseAppView):'
114 Repository.repo_name.ilike(ilike_expression))
123 Repository.repo_name.ilike(ilike_expression))
115 query = query.limit(limit)
124 query = query.limit(limit)
116
125
117 all_repos = query.all()
126 acl_repo_iter = query
118 # permission checks are inside this function
127
119 repo_iter = ScmModel().get_repos(all_repos)
120 return [
128 return [
121 {
129 {
122 'id': obj['name'],
130 'id': obj.repo_name,
123 'text': obj['name'],
131 'text': obj.repo_name,
124 'type': 'repo',
132 'type': 'repo',
125 'obj': obj['dbrepo'],
133 'obj': {'repo_type': obj.repo_type, 'private': obj.private,
126 'url': h.route_path('repo_summary', repo_name=obj['name'])
134 'repo_id': obj.repo_id},
135 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
127 }
136 }
128 for obj in repo_iter]
137 for obj in acl_repo_iter]
129
138
130 def _get_repo_group_list(self, name_contains=None, limit=20):
139 def _get_repo_group_list(self, name_contains=None, limit=20):
140 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
141 ['group.read', 'group.write', 'group.admin'],
142 cache=False, name_filter=name_contains) or [-1]
143
131 query = RepoGroup.query()\
144 query = RepoGroup.query()\
132 .order_by(func.length(RepoGroup.group_name))\
145 .order_by(func.length(RepoGroup.group_name))\
133 .order_by(RepoGroup.group_name)
146 .order_by(RepoGroup.group_name) \
147 .filter(or_(
148 # generate multiple IN to fix limitation problems
149 *in_filter_generator(RepoGroup.group_id, allowed_ids)
150 ))
134
151
135 if name_contains:
152 if name_contains:
136 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
153 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
@@ -138,23 +155,24 b' class HomeView(BaseAppView):'
138 RepoGroup.group_name.ilike(ilike_expression))
155 RepoGroup.group_name.ilike(ilike_expression))
139 query = query.limit(limit)
156 query = query.limit(limit)
140
157
141 all_groups = query.all()
158 acl_repo_iter = query
142 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
159
143 return [
160 return [
144 {
161 {
145 'id': obj.group_name,
162 'id': obj.group_name,
146 'text': obj.group_name,
163 'text': obj.group_name,
147 'type': 'group',
164 'type': 'group',
148 'obj': {},
165 'obj': {},
149 'url': h.route_path('repo_group_home', repo_group_name=obj.group_name)
166 'url': h.route_path(
167 'repo_group_home', repo_group_name=obj.group_name)
150 }
168 }
151 for obj in repo_groups_iter]
169 for obj in acl_repo_iter]
152
170
153 def _get_hash_commit_list(self, auth_user, hash_starts_with=None):
171 def _get_hash_commit_list(self, auth_user, query=None):
154 if not hash_starts_with or len(hash_starts_with) < 3:
172 if not query or len(query) < 3:
155 return []
173 return []
156
174
157 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
175 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
158
176
159 if len(commit_hashes) != 1:
177 if len(commit_hashes) != 1:
160 return []
178 return []
@@ -172,9 +190,9 b' class HomeView(BaseAppView):'
172 'text': entry['commit_id'],
190 'text': entry['commit_id'],
173 'type': 'commit',
191 'type': 'commit',
174 'obj': {'repo': entry['repository']},
192 'obj': {'repo': entry['repository']},
175 'url': h.url('changeset_home',
193 'url': h.route_path(
176 repo_name=entry['repository'],
194 'repo_commit',
177 revision=entry['commit_id'])
195 repo_name=entry['repository'], commit_id=entry['commit_id'])
178 }
196 }
179 for entry in result['results']]
197 for entry in result['results']]
180
198
@@ -19,24 +19,62 b''
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 datetime
21 import datetime
22 from rhodecode.tests import TestController, url
22
23 import pytest
24
25 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.tests import TestController
23 from rhodecode.model.db import UserFollowing, Repository
27 from rhodecode.model.db import UserFollowing, Repository
24
28
25
29
26 class TestJournalController(TestController):
30 def route_path(name, params=None, **kwargs):
31 import urllib
27
32
28 def test_index(self):
33 base_url = {
34 'journal': ADMIN_PREFIX + '/journal',
35 'journal_rss': ADMIN_PREFIX + '/journal/rss',
36 'journal_atom': ADMIN_PREFIX + '/journal/atom',
37 'journal_public': ADMIN_PREFIX + '/public_journal',
38 'journal_public_atom': ADMIN_PREFIX + '/public_journal/atom',
39 'journal_public_atom_old': ADMIN_PREFIX + '/public_journal_atom',
40 'journal_public_rss': ADMIN_PREFIX + '/public_journal/rss',
41 'journal_public_rss_old': ADMIN_PREFIX + '/public_journal_rss',
42 'toggle_following': ADMIN_PREFIX + '/toggle_following',
43 }[name].format(**kwargs)
44
45 if params:
46 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
47 return base_url
48
49
50 class TestJournalViews(TestController):
51
52 def test_journal(self):
29 self.log_user()
53 self.log_user()
30 response = self.app.get(url(controller='journal', action='index'))
54 response = self.app.get(route_path('journal'))
31 response.mustcontain(
55 # response.mustcontain(
32 """<div class="journal_day">%s</div>""" % datetime.date.today())
56 # """<div class="journal_day">%s</div>""" % datetime.date.today())
57
58 @pytest.mark.parametrize("feed_type, content_type", [
59 ('rss', "application/rss+xml"),
60 ('atom', "application/atom+xml")
61 ])
62 def test_journal_feed(self, feed_type, content_type):
63 self.log_user()
64 response = self.app.get(
65 route_path(
66 'journal_{}'.format(feed_type)),
67 status=200)
68
69 assert response.content_type == content_type
33
70
34 def test_toggle_following_repository(self, backend):
71 def test_toggle_following_repository(self, backend):
35 user = self.log_user()
72 user = self.log_user()
36 repo = Repository.get_by_repo_name(backend.repo_name)
73 repo = Repository.get_by_repo_name(backend.repo_name)
37 repo_id = repo.repo_id
74 repo_id = repo.repo_id
38 self.app.post(url('toggle_following'), {'follows_repo_id': repo_id,
75 self.app.post(
39 'csrf_token': self.csrf_token})
76 route_path('toggle_following'), {'follows_repo_id': repo_id,
77 'csrf_token': self.csrf_token})
40
78
41 followings = UserFollowing.query()\
79 followings = UserFollowing.query()\
42 .filter(UserFollowing.user_id == user['user_id'])\
80 .filter(UserFollowing.user_id == user['user_id'])\
@@ -44,8 +82,9 b' class TestJournalController(TestControll'
44
82
45 assert len(followings) == 0
83 assert len(followings) == 0
46
84
47 self.app.post(url('toggle_following'), {'follows_repo_id': repo_id,
85 self.app.post(
48 'csrf_token': self.csrf_token})
86 route_path('toggle_following'), {'follows_repo_id': repo_id,
87 'csrf_token': self.csrf_token})
49
88
50 followings = UserFollowing.query()\
89 followings = UserFollowing.query()\
51 .filter(UserFollowing.user_id == user['user_id'])\
90 .filter(UserFollowing.user_id == user['user_id'])\
@@ -53,12 +92,15 b' class TestJournalController(TestControll'
53
92
54 assert len(followings) == 1
93 assert len(followings) == 1
55
94
56 def test_public_journal_atom(self):
95 @pytest.mark.parametrize("feed_type, content_type", [
96 ('rss', "application/rss+xml"),
97 ('atom', "application/atom+xml")
98 ])
99 def test_public_journal_feed(self, feed_type, content_type):
57 self.log_user()
100 self.log_user()
58 response = self.app.get(url(controller='journal',
101 response = self.app.get(
59 action='public_journal_atom'),)
102 route_path(
103 'journal_public_{}'.format(feed_type)),
104 status=200)
60
105
61 def test_public_journal_rss(self):
106 assert response.content_type == content_type
62 self.log_user()
63 response = self.app.get(url(controller='journal',
64 action='public_journal_rss'),)
@@ -41,4 +41,4 b' def includeme(config):'
41 pattern=ADMIN_PREFIX + '/password_reset_confirmation')
41 pattern=ADMIN_PREFIX + '/password_reset_confirmation')
42
42
43 # Scan module for configuration decorators.
43 # Scan module for configuration decorators.
44 config.scan()
44 config.scan('.views', ignore='.tests')
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_login.py to rhodecode/apps/login/tests/test_login.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_login.py to rhodecode/apps/login/tests/test_login.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_password_reset.py to rhodecode/apps/login/tests/test_password_reset.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_password_reset.py to rhodecode/apps/login/tests/test_password_reset.py
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 renamed from rhodecode/tests/functional/test_admin_notifications.py to rhodecode/apps/my_account/tests/test_my_account_notifications.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_admin_notifications.py to rhodecode/apps/my_account/tests/test_my_account_notifications.py
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 renamed from rhodecode/apps/my_account/views.py to rhodecode/apps/my_account/views/my_account.py
NO CONTENT: file renamed from rhodecode/apps/my_account/views.py to rhodecode/apps/my_account/views/my_account.py
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 renamed from rhodecode/tests/functional/test_changelog.py to rhodecode/apps/repository/tests/test_repo_changelog.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_changelog.py to rhodecode/apps/repository/tests/test_repo_changelog.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_commit_comments.py to rhodecode/apps/repository/tests/test_repo_commit_comments.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_commit_comments.py to rhodecode/apps/repository/tests/test_repo_commit_comments.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_changeset.py to rhodecode/apps/repository/tests/test_repo_commits.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_changeset.py to rhodecode/apps/repository/tests/test_repo_commits.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_compare.py to rhodecode/apps/repository/tests/test_repo_compare.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_compare.py to rhodecode/apps/repository/tests/test_repo_compare.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_compare_local.py to rhodecode/apps/repository/tests/test_repo_compare_local.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_compare_local.py to rhodecode/apps/repository/tests/test_repo_compare_local.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_compare_on_single_file.py to rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_compare_on_single_file.py to rhodecode/apps/repository/tests/test_repo_compare_on_single_file.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_feed.py to rhodecode/apps/repository/tests/test_repo_feed.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_feed.py to rhodecode/apps/repository/tests/test_repo_feed.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_files.py to rhodecode/apps/repository/tests/test_repo_files.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_files.py to rhodecode/apps/repository/tests/test_repo_files.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_admin_repos_issuetracker.py to rhodecode/apps/repository/tests/test_repo_issue_tracker.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_admin_repos_issuetracker.py to rhodecode/apps/repository/tests/test_repo_issue_tracker.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/tests/functional/test_pullrequests.py to rhodecode/apps/repository/tests/test_repo_pullrequests.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_pullrequests.py to rhodecode/apps/repository/tests/test_repo_pullrequests.py
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 renamed from rhodecode/tests/functional/test_admin_user_groups.py to rhodecode/apps/user_group/tests/test_user_groups.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_admin_user_groups.py to rhodecode/apps/user_group/tests/test_user_groups.py
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 renamed from rhodecode/tests/functional/test_integrations.py to rhodecode/integrations/tests/test_integrations.py
NO CONTENT: file renamed from rhodecode/tests/functional/test_integrations.py to rhodecode/integrations/tests/test_integrations.py
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 chmod 100644 => 100755, binary diff hidden
NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
1 NO CONTENT: modified file chmod 100644 => 100755
NO CONTENT: modified file chmod 100644 => 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
1 NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
1 NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
NO CONTENT: modified file chmod 100644 => 100755, binary diff hidden
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 renamed from rhodecode/public/js/excanvas.min.js to rhodecode/public/js/src/excanvas.min.js
NO CONTENT: file renamed from rhodecode/public/js/excanvas.min.js to rhodecode/public/js/src/excanvas.min.js
1 NO CONTENT: file renamed from rhodecode/public/js/jquery.commits-graph.js to rhodecode/public/js/src/plugins/jquery.commits-graph.js
NO CONTENT: file renamed from rhodecode/public/js/jquery.commits-graph.js to rhodecode/public/js/src/plugins/jquery.commits-graph.js
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 renamed from rhodecode/templates/admin/notifications/show_notification.mako to rhodecode/templates/admin/notifications/notifications_show.mako
NO CONTENT: file renamed from rhodecode/templates/admin/notifications/show_notification.mako to rhodecode/templates/admin/notifications/notifications_show.mako
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from rhodecode/templates/admin/notifications/notifications.mako to rhodecode/templates/admin/notifications/notifications_show_all.mako
NO CONTENT: file renamed from rhodecode/templates/admin/notifications/notifications.mako to rhodecode/templates/admin/notifications/notifications_show_all.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: file renamed from rhodecode/templates/admin/repo_groups/repo_group_edit_perms.mako to rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.mako
NO CONTENT: file renamed from rhodecode/templates/admin/repo_groups/repo_group_edit_perms.mako to rhodecode/templates/admin/repo_groups/repo_group_edit_permissions.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: 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 renamed from rhodecode/templates/summary/base.mako to rhodecode/templates/summary/summary_base.mako
NO CONTENT: file renamed from rhodecode/templates/summary/base.mako to rhodecode/templates/summary/summary_base.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: file renamed from rhodecode/tests/other/test_libs.py to rhodecode/tests/lib/test_libs.py
NO CONTENT: file renamed from rhodecode/tests/other/test_libs.py to rhodecode/tests/lib/test_libs.py
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
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
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
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
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