##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r306:08b6bdad merge stable
parent child Browse files
Show More

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

@@ -0,0 +1,137 b''
1 .. _checklist-tickets:
2
3 =================
4 Ticket Checklists
5 =================
6
7
8 Ticket Description
9 ==================
10
11 In general these things really matter in the description:
12
13 - Reasoning / Rationale. Explain "WHY" it makes sense and is important.
14
15 - How to reproduce. Easy to follow steps, that’s important.
16
17 - Observation: The problem (short)
18
19 - Expectation: How it should be (short)
20
21 - Specs: It is fine to draft them as good as it works.
22
23 If anything is unclear, please ask for a review or help on this via the
24 Community Portal or Slack channel.
25
26
27 Checklists for Tickets
28 ======================
29
30 BUG
31 ---
32
33 Definition: An existing function that does not work as expected for the user.
34
35 - Problem description
36 - Steps needed to recreate (gherkin)
37 - Link to the screen in question and/or description of how to find it via
38 navigation
39 - Explanation of what the expected outcome is
40 - Any hints into the source of the problem
41 - Information about platform/browser/db/etc. where applicable
42 - Examples of other similar cases which have different behaviour
43
44 DESIGN
45 ------
46
47 Definition: Styling and user interface issues, including cosmetic improvements
48 or appearance and behaviour of frontend functionality.
49
50 - Screenshot/animation of existing page/behaviour
51 - Sketches or wireframes if available
52 - Link to the screen in question and/or description of how to find it via
53 navigation
54 - Problem description
55 - Explanation of what the expected outcome is
56 - Since this may be examined by a designer; it should be written in a way that a
57 non-developer can understand
58
59 EPIC
60 ----
61
62 Definition: A collection of tickets which together complete a larger overall
63 project.
64
65 - Benefit explanation
66 - Clear objective - when is this complete?
67 - Explanations of exceptions/corner cases
68 - Documentation subtask
69 - Comprehensive wireframes and/or design subtasks
70 - Links to subtasks
71
72 FEATURE
73 -------
74
75 Definition: A new function in the software which previously did not exist.
76
77 - Benefit explanation
78 - Clear objective
79 - Explanations of exceptions/corner cases
80 - Documentation subtask
81 - Comprehensive wireframes and/or design subtasks
82
83 SUPPORT
84 -------
85
86 Definition: An issue related to a customer report.
87
88 - Link to support ticket, if available
89 - Problem description
90 - Steps needed to recreate (gherkin)
91 - Link to the screen in question and/or description of how to find it via
92 navigation
93 - Explanation of what the expected outcome is
94 - Any hints into the source of the problem
95 - Information about platform/browser/db/etc. where applicable
96 - Examples of other similar cases which have different behaviour
97
98 TASK
99 ----
100
101 Definition: An improvement or step towards implementing a feature or fixing
102 a bug. Includes refactoring and other tech debt.
103
104 - Clear objective
105 - Benefit explanation
106 - Links to parent/related tickets
107
108
109 All details below.
110
111
112 External links:
113
114 - Avoid linking to external images; they disappear over time. Please attach any
115 relevant images to the ticket itself.
116
117 - External links in general: They also disappear over time, consider copying the
118 relevant bit of information into a comment or write a paragraph to sum up the
119 general idea.
120
121
122 Hints
123 =====
124
125 Change Description
126 ------------------
127
128 It can be tricky to figure out how to change the description of a ticket. There
129 is a very small pencil which has to be clicked once you see the edit form of a
130 ticket.
131
132
133 .. figure:: images/redmine-description.png
134 :alt: Example of pencil to change the ticket description
135
136 Shows an example of the pencil which lets you change the description.
137
@@ -0,0 +1,153 b''
1
2 ==================================================
3 Code style and structure guide for frontend work
4 ==================================================
5
6 About: Outline of frontend development practices.
7
8
9
10
11 Templates
12 =========
13
14 - Indent with 4 spaces in general.
15 - Embedded Python code follows the same conventions as in the backend.
16
17 A common problem is missed spaces around operators.
18
19
20
21
22 Grunt
23 =====
24
25 We use Grunt to compile our JavaScript and LESS files. This is done automatically
26 when you start an instance. If you are changing these files, however, it is
27 recommended to amend `--reload` to the `runserver` command, or use `grunt watch`
28 - the Gruntfile is located in the base directory. For more info on Grunt, see
29 http://gruntjs.com/
30
31
32
33
34 LESS CSS
35 ========
36
37
38 Style
39 -----
40
41 - Use 4 spaces instead of tabs.
42 - Avoid ``!important``; it is very often an indicator for a problem.
43
44
45
46
47 Structure
48 ---------
49
50 It is important that we maintain consistency in the LESS files so that things
51 scale properly. CSS is organized using LESS and then compiled into a CSS file
52 to be used in production. Find the class you need to change and change it
53 there. Do not add overriding styles at the end of the file. The LESS file will
54 be minified; use plenty of spacing and comments for readability.
55
56 These will be kept in auxillary LESS files to be imported (in this order) at the top:
57
58 - `fonts.less` (font-face declarations)
59 - `mixins` (place all LESS mixins here)
60 - `helpers` (basic classes for hiding mobile elements, centering, etc)
61 - `variables` (theme-specific colors, spacing, and fonts which might change later)
62
63
64 Sections of the primary LESS file are as follows. Add comments describing
65 layout and modules.
66
67 .. code-block:: css
68
69 //--- BASE ------------------//
70 Very basic, sitewide styles.
71
72 //--- LAYOUT ------------------//
73 Essential layout, ex. containers and wrappers.
74 Do not put type styles in here.
75
76 //--- MODULES ------------------//
77 Reusable sections, such as sidebars and menus.
78
79 //--- THEME ------------------//
80 Theme styles, typography, etc.
81
82
83
84 Formatting rules
85 ~~~~~~~~~~~~~~~~
86
87 - Each rule should be indented on a separate line (this is helpful for diff
88 checking).
89
90 - Use a space after each colon and a semicolon after each last rule.
91
92 - Put a blank line between each class.
93
94 - Nested classes should be listed after the parent class' rules, separated with a
95 blank line, and indented.
96
97 - Using the below as a guide, place each rule in order of its effect on content,
98 layout, sizing, and last listing minor style changes such as font color and
99 backgrounds. Not every possible rule is listed here; when adding new ones,
100 judge where it should go in the list based on that hierarchy.
101
102 .. code-block:: scss
103
104 .class {
105 content
106 list-style-type
107 position
108 float
109 top
110 right
111 bottom
112 left
113 height
114 max-height
115 min-height
116 width
117 max-width
118 min-width
119 margin
120 padding
121 indent
122 vertical-align
123 text-align
124 border
125 border-radius
126 font-size
127 line-height
128 font
129 font-style
130 font-variant
131 font-weight
132 color
133 text-shadow
134 background
135 background-color
136 box-shadow
137 background-url
138 background-position
139 background-repeat
140 background-cover
141 transitions
142 cursor
143 pointer-events
144
145 .nested-class {
146 position
147 background-color
148
149 &:hover {
150 color
151 }
152 }
153 }
@@ -0,0 +1,111 b''
1
2 =======================
3 Contributing Overview
4 =======================
5
6
7 RhodeCode Community Edition is an open source code management platform. We
8 encourage contributions to our project from the community. This is a basic
9 overview of the procedures for adding your contribution to RhodeCode.
10
11
12
13 Check the Issue Tracker
14 =======================
15
16 Make an account at https://issues.rhodecode.com/account/register and browse the
17 current tickets for bugs to fix and tasks to do. Have a bug or feature that you
18 can't find in the tracker? Create a new issue for it. When you select a ticket,
19 make sure to assign it to yourself and mark it "in progress" to avoid duplicated
20 work.
21
22
23
24 Sign Up at code.rhodecode.com
25 =============================
26
27 Make an account at https://code.rhodecode.com/ using an email or your existing
28 GitHub, Bitbucket, Google, or Twitter account. Fork the repo you'd like to
29 contribute to; we suggest adding your username to the fork name. Clone your fork
30 to your computer. We use Mercurial for source control management; see
31 https://www.mercurial-scm.org/guide to get started quickly.
32
33
34
35 Set Up A Local Instance
36 =======================
37
38 You will need to set up an instance of RhodeCode CE using VCSServer so that you
39 can see your work locally as you make changes. We recommend using Linux for this
40 but it can also be built on OSX.
41
42 See :doc:`dev-setup` for instructions.
43
44
45
46 Code!
47 =====
48
49 You can now make, see, and test your changes locally. We are always improving to
50 keep our code clean and the cost of maintaining it low. This applies in the same
51 way for contributions. We run automated checks on our pull requests, and expect
52 understandable code. We also aim to provide test coverage for as much of our
53 codebase as possible; any new features should be augmented with tests.
54
55 Keep in mind that when we accept your contribution, we also take responsibility
56 for it; we must understand it to take on that responsibility.
57
58 See :doc:`standards` for more detailed information.
59
60
61
62 Commit And Push Your Changes
63 ============================
64
65 We highly recommend making a new bookmark for each feature, bug, or set of
66 commits you make so that you can point to it when creating your pull request.
67 Please also reference the ticket number in your commit messages. Don't forget to
68 push the bookmark!
69
70
71
72 Submit a Pull Request
73 =====================
74
75 Go to your fork, and choose "Create Pull Request" from the Options menu. Use
76 your bookmark as the source, and choose someone to review it. Don't worry about
77 chosing the right person; we'll assign the best contributor for the job. You'll
78 get feedback and an assigned status.
79
80 Be prepared to make updates to your pull request after some feedback.
81 Collaboration is part of the process and improvements can often be made.
82
83
84
85 Sign the Contributor License Agreement
86 ======================================
87
88 If your contribution is approved, you will need to virtually sign the license
89 agreement in order for it to be merged into the project's codebase. You can read
90 it on our website here: https://rhodecode.com/static/pdf/RhodeCode-CLA.pdf
91
92 To sign, go to code.rhodecode.com
93 and clone the CLA repository. Add your name and make a pull request to add it to
94 the contributor agreement; this serves as your virtual signature. Once your
95 signature is merged, add a link to the relevant commit to your contribution
96 pull request.
97
98
99
100 That's it! We'll take it from there. Thanks for your contribution!
101 ------------------------------------------------------------------
102
103 .. note:: If you have any questions or comments, feel free to contact us through
104 either the community portal(community.rhodecode.com), IRC
105 (irc.freenode.net), or Slack (rhodecode.com/join).
106
107
108
109
110
111
@@ -0,0 +1,177 b''
1
2 ======================
3 Contribution Standards
4 ======================
5
6 Standards help to improve the quality of our product and its development. Herein
7 we define our standards for processes and development to maintain consistency
8 and function well as a community. It is a work in progress; modifications to this
9 document should be discussed and agreed upon by the community.
10
11
12 --------------------------------------------------------------------------------
13
14 Code
15 ====
16
17 This provides an outline for standards we use in our codebase to keep our code
18 easy to read and easy to maintain. Much of our code guidelines are based on the
19 book `Clean Code <http://www.pearsonhighered.com/educator/product/Clean-Code-A-Handbook-of-Agile-Software-Craftsmanship/9780132350884.page>`_
20 by Robert Martin.
21
22 We maintain a Tech Glossary to provide consistency in terms and symbolic names
23 used for items and concepts within the application. This is found in the CE
24 project in /docs-internal/glossary.rst
25
26
27 Refactoring
28 -----------
29 Make it better than you found it!
30
31 Our codebase can always use improvement and often benefits from refactoring.
32 New code should be refactored as it is being written, and old code should be
33 treated with the same care as if it was new. Before doing any refactoring,
34 ensure that there is test coverage on the affected code; this will help
35 minimize issues.
36
37
38 Python
39 ------
40 For Python, we use `PEP8 <https://www.python.org/dev/peps/pep-0008/>`_.
41 We adjust lines of code to under 80 characters and use 4 spaces for indentation.
42
43
44 JavaScript
45 ----------
46 This currently remains undefined. Suggestions welcome!
47
48
49 HTML
50 ----
51 Unfortunately, we currently have no strict HTML standards, but there are a few
52 guidelines we do follow. Templates must work in all modern browsers. HTML should
53 be clean and easy to read, and additionally should be free of inline CSS or
54 JavaScript. It is recommended to use data attributes for JS actions where
55 possible in order to separate it from styling and prevent unintentional changes.
56
57
58 LESS/CSS
59 --------
60 We use LESS for our CSS; see :doc:`frontend` for structure and formatting
61 guidelines.
62
63
64 Linters
65 -------
66 Currently, we have a linter for pull requests which checks code against PEP8.
67 We intend to add more in the future as we clarify standards.
68
69
70 --------------------------------------------------------------------------------
71
72 Naming Conventions
73 ==================
74
75 These still need to be defined for naming everything from Python variables to
76 HTML classes to files and folders.
77
78
79 --------------------------------------------------------------------------------
80
81 Testing
82 =======
83
84 Testing is a very important aspect of our process, especially as we are our own
85 quality control team. While it is of course unrealistic to hit every potential
86 combination, our goal is to cover every line of Python code with a test.
87
88 The following is a brief introduction to our test suite. Our tests are primarily
89 written using `py.test <http://pytest.org/>`_
90
91
92 Acceptance Tests
93 ----------------
94 Also known as "ac tests", these test from the user and business perspective to
95 check if the requirements of a feature are met. Scenarios are created at a
96 feature's inception and help to define its value.
97
98 py.test is used for ac tests; they are located in a repo separate from the
99 other tests which follow. Each feature has a .feature file which contains a
100 brief description and the scenarios to be tested.
101
102
103 Functional Tests
104 ----------------
105 These test specific functionality in the application which checks through the
106 entire stack. Typically these are user actions or permissions which go through
107 the web browser. They are located in rhodecode/tests.
108
109
110 Unit Tests
111 ----------
112 These test isolated, individual modules to ensure that they function correctly.
113 They are located in rhodecode/tests.
114
115
116 Integration Tests
117 -----------------
118 These are used for testing performance of larger systems than the unit tests.
119 They are located in rhodecode/tests.
120
121
122 JavaScript Testing
123 ------------------
124 Currently, we have not defined how to test our JavaScript code.
125
126
127 --------------------------------------------------------------------------------
128
129 Pull Requests
130 =============
131
132 Pull requests should generally contain only one thing: a single feature, one bug
133 fix, etc.. The commit history should be concise and clean, and the pull request
134 should contain the ticket number (also a good idea for the commits themselves)
135 to provide context for the reviewer.
136
137 See also: :doc:`checklist-pull-request`
138
139
140 Reviewers
141 ---------
142 Each pull request must be approved by at least one member of the RhodeCode
143 team. Assignments may be based on expertise or familiarity with a particular
144 area of code, or simply availability. Switching up or adding extra community
145 members for different pull requests helps to share knowledge as well as provide
146 other perspectives.
147
148
149 Responsibility
150 --------------
151 The community is responsible for maintaining features and this must be taken
152 into consideration. External contributions must be held to the same standards
153 as internal contributions.
154
155
156 Feature Switch
157 --------------
158 Experimental and work-in-progress features can be hidden behind one of two
159 switches:
160
161 * A setting can be added to the Labs page in the Admin section which may allow
162 customers to access and toggle additional features.
163 * For work-in-progress or other features where customer access is not desired,
164 use a custom setting in the .ini file as a trigger.
165
166
167 --------------------------------------------------------------------------------
168
169 Tickets
170 =======
171
172 Redmine tickets are a crucial part of our development process. Any code added or
173 changed in our codebase should have a corresponding ticket to document it. With
174 this in mind, it is important that tickets be as clear and concise as possible,
175 including what the expected outcome is.
176
177 See also: :doc:`checklist-tickets`
@@ -0,0 +1,70 b''
1 |RCE| 4.2.0 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2016-06-30
8
9
10 General
11 ^^^^^^^
12
13 - Autocomplete: add GET flag support to show/hide active users on autocomplete,
14 also display this information in autocomplete data. ref #3374
15 - Gravatar: add flag to show current gravatar + user as disabled user (non-active)
16 - Repos, repo groups, user groups: allow to use disabled users in owner field.
17 This fixes #3374.
18 - Repos, repo groups, user groups: visually show what user is an owner of a
19 resource, and if potentially he is disabled in the system.
20 - Pull requests: reorder navigation on repo pull requests, fixes #2995
21 - Dependencies: bump dulwich to 0.13.0
22
23 New Features
24 ^^^^^^^^^^^^
25
26 - My account: made pull requests aggregate view for users look like not
27 created in 1995. Now uses a consistent look with repo one.
28 - emails: expose profile link on registation email that super-admins receive.
29 Implements #3999.
30 - Social auth: move the buttons to the top of nav so they are easier to reach.
31
32
33 Security
34 ^^^^^^^^
35
36 - Encryption: allow to pass in alternative key for encryption values. Now
37 users can use `rhodecode.encrypted_values.secret` that is alternative key,
38 de-coupled from `beaker.session` key.
39 - Encryption: Implement a slightly improved AesCipher encryption.
40 This addresses issues from #4036.
41 - Encryption: encrypted values now by default uses HMAC signatures to detect
42 if data or secret key is incorrect. The strict checks can be disabled using
43 `rhodecode.encrypted_values.strict = false` .ini setting
44
45
46 Performance
47 ^^^^^^^^^^^
48
49 - Sql: use smarter JOINs when fetching repository information
50 - Helpers: optimize slight calls for link_to_user to save some intense queries.
51 - App settings: use computed caches for repository settings, this in some cases
52 brings almost 4x performance increase for large repos with a lot of issue
53 tracker patterns.
54
55
56 Fixes
57 ^^^^^
58
59 - Fixed events on user pre/post create actions
60 - Authentication: fixed problem with saving forms with errors on auth plugins
61 - Svn: Avoid chunked transfer for Subversion that caused checkout issues in some cases.
62 - Users: fix generate new user password helper.
63 - Celery: fixed problem with workers running action in sync mode in some cases.
64 - Setup-db: fix redundant question on writable dir. The question needs to be
65 asked only when the dir is actually not writable.
66 - Elasticsearch: fixed issues when searching single repo using elastic search
67 - Social auth: fix issues with non-active users using social authentication
68 causing a 500 error.
69 - Fixed problem with largefiles extensions on per-repo settings using local
70 .hgrc files present inside the repo directory.
@@ -0,0 +1,40 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 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.admin.navigation import NavigationRegistry
23 from rhodecode.config.routing import ADMIN_PREFIX
24 from rhodecode.lib.utils2 import str2bool
25
26
27 def includeme(config):
28 settings = config.get_settings()
29
30 # Create admin navigation registry and add it to the pyramid registry.
31 labs_active = str2bool(settings.get('labs_settings_active', False))
32 navigation_registry = NavigationRegistry(labs_active=labs_active)
33 config.registry.registerUtility(navigation_registry)
34
35 config.add_route(
36 name='admin_settings_open_source',
37 pattern=ADMIN_PREFIX + '/settings/open_source')
38
39 # Scan module for configuration decorators.
40 config.scan()
@@ -0,0 +1,29 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 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 from zope.interface import Interface
22
23
24 class IAdminNavigationRegistry(Interface):
25 """
26 Interface for the admin navigation registry. Currently this is only
27 used to register and retrieve it via pyramids registry.
28 """
29 pass
@@ -0,0 +1,124 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 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 collections
24
25 from pylons import url
26 from zope.interface import implementer
27
28 from rhodecode.admin.interfaces import IAdminNavigationRegistry
29 from rhodecode.lib.utils import get_registry
30 from rhodecode.translation import _
31
32
33 log = logging.getLogger(__name__)
34
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
36
37
38 class NavEntry(object):
39 """
40 Represents an entry in the admin navigation.
41
42 :param key: Unique identifier used to store reference in an OrderedDict.
43 :param name: Display name, usually a translation string.
44 :param view_name: Name of the view, used generate the URL.
45 :param pyramid: Indicator to use pyramid for URL generation. This should
46 be removed as soon as we are fully migrated to pyramid.
47 """
48
49 def __init__(self, key, name, view_name, pyramid=False):
50 self.key = key
51 self.name = name
52 self.view_name = view_name
53 self.pyramid = pyramid
54
55 def generate_url(self, request):
56 if self.pyramid:
57 if hasattr(request, 'route_path'):
58 return request.route_path(self.view_name)
59 else:
60 # TODO: johbo: Remove this after migrating to pyramid.
61 # We need the pyramid request here to generate URLs to pyramid
62 # views from within pylons views.
63 from pyramid.threadlocal import get_current_request
64 pyramid_request = get_current_request()
65 return pyramid_request.route_path(self.view_name)
66 else:
67 return url(self.view_name)
68
69
70 @implementer(IAdminNavigationRegistry)
71 class NavigationRegistry(object):
72
73 _base_entries = [
74 NavEntry('global', _('Global'), 'admin_settings_global'),
75 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
76 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
77 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
78 NavEntry('issuetracker', _('Issue Tracker'),
79 'admin_settings_issuetracker'),
80 NavEntry('email', _('Email'), 'admin_settings_email'),
81 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
82 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
83 NavEntry('system', _('System Info'), 'admin_settings_system'),
84 NavEntry('open_source', _('Open Source Licenses'),
85 'admin_settings_open_source', pyramid=True),
86 # TODO: marcink: we disable supervisor now until the supervisor stats
87 # page is fixed in the nix configuration
88 # NavEntry('supervisor', _('Supervisor'), 'admin_settings_supervisor'),
89 ]
90
91 _labs_entry = NavEntry('labs', _('Labs'),
92 'admin_settings_labs')
93
94 def __init__(self, labs_active=False):
95 self._registered_entries = collections.OrderedDict([
96 (item.key, item) for item in self.__class__._base_entries
97 ])
98
99 if labs_active:
100 self.add_entry(self._labs_entry)
101
102 def add_entry(self, entry):
103 self._registered_entries[entry.key] = entry
104
105 def get_navlist(self, request):
106 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
107 for i in self._registered_entries.values()]
108 return navlist
109
110
111 def navigation_registry(request):
112 """
113 Helper that returns the admin navigation registry.
114 """
115 pyramid_registry = get_registry(request)
116 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
117 return nav_registry
118
119
120 def navigation_list(request):
121 """
122 Helper that returns the admin navigation as list of NavListEntry objects.
123 """
124 return navigation_registry(request).get_navlist(request)
@@ -0,0 +1,55 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 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 collections
22 import logging
23
24 from pylons import tmpl_context as c
25 from pyramid.view import view_config
26
27 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
28 from rhodecode.lib.utils import read_opensource_licenses
29
30 from .navigation import navigation_list
31
32
33 log = logging.getLogger(__name__)
34
35
36 class AdminSettingsView(object):
37
38 def __init__(self, context, request):
39 self.request = request
40 self.context = context
41 self.session = request.session
42 self._rhodecode_user = request.user
43
44 @LoginRequired()
45 @HasPermissionAllDecorator('hg.admin')
46 @view_config(
47 route_name='admin_settings_open_source', request_method='GET',
48 renderer='rhodecode:templates/admin/settings/settings.html')
49 def open_source_licenses(self):
50 c.active = 'open_source'
51 c.navlist = navigation_list(self.request)
52 c.opensource_licenses = collections.OrderedDict(
53 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
54
55 return {}
1 NO CONTENT: new file 100644
@@ -0,0 +1,92 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 import pytest
23
24
25 class EnabledAuthPlugin():
26 """
27 Context manager that updates the 'auth_plugins' setting in DB to enable
28 a plugin. Previous setting is restored on exit. The rhodecode auth plugin
29 is also enabled because it is needed to login the test users.
30 """
31
32 def __init__(self, plugin):
33 self.new_value = set([
34 'egg:rhodecode-enterprise-ce#rhodecode',
35 plugin.get_id()
36 ])
37
38 def __enter__(self):
39 from rhodecode.model.settings import SettingsModel
40 self._old_value = SettingsModel().get_auth_plugins()
41 SettingsModel().create_or_update_setting(
42 'auth_plugins', ','.join(self.new_value))
43
44 def __exit__(self, type, value, traceback):
45 from rhodecode.model.settings import SettingsModel
46 SettingsModel().create_or_update_setting(
47 'auth_plugins', ','.join(self._old_value))
48
49
50 class DisabledAuthPlugin():
51 """
52 Context manager that updates the 'auth_plugins' setting in DB to disable
53 a plugin. Previous setting is restored on exit.
54 """
55
56 def __init__(self, plugin):
57 self.plugin_id = plugin.get_id()
58
59 def __enter__(self):
60 from rhodecode.model.settings import SettingsModel
61 self._old_value = SettingsModel().get_auth_plugins()
62 new_value = [id_ for id_ in self._old_value if id_ != self.plugin_id]
63 SettingsModel().create_or_update_setting(
64 'auth_plugins', ','.join(new_value))
65
66 def __exit__(self, type, value, traceback):
67 from rhodecode.model.settings import SettingsModel
68 SettingsModel().create_or_update_setting(
69 'auth_plugins', ','.join(self._old_value))
70
71
72 @pytest.fixture(params=[
73 ('rhodecode.authentication.plugins.auth_crowd', 'egg:rhodecode-enterprise-ce#crowd'),
74 ('rhodecode.authentication.plugins.auth_headers', 'egg:rhodecode-enterprise-ce#headers'),
75 ('rhodecode.authentication.plugins.auth_jasig_cas', 'egg:rhodecode-enterprise-ce#jasig_cas'),
76 ('rhodecode.authentication.plugins.auth_ldap', 'egg:rhodecode-enterprise-ce#ldap'),
77 ('rhodecode.authentication.plugins.auth_pam', 'egg:rhodecode-enterprise-ce#pam'),
78 ('rhodecode.authentication.plugins.auth_rhodecode', 'egg:rhodecode-enterprise-ce#rhodecode'),
79 ('rhodecode.authentication.plugins.auth_token', 'egg:rhodecode-enterprise-ce#token'),
80 ])
81 def auth_plugin(request):
82 """
83 Fixture that provides instance for each authentication plugin. These
84 instances are NOT the instances which are registered to the authentication
85 registry.
86 """
87 from importlib import import_module
88
89 # Create plugin instance.
90 module, plugin_id = request.param
91 plugin_module = import_module(module)
92 return plugin_module.plugin_factory(plugin_id)
1 NO CONTENT: new file 100644
@@ -0,0 +1,77 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 import pytest
23
24 from rhodecode.authentication.tests.conftest import (
25 EnabledAuthPlugin, DisabledAuthPlugin)
26 from rhodecode.config.routing import ADMIN_PREFIX
27
28
29 @pytest.mark.usefixtures('autologin_user', 'app')
30 class TestAuthenticationSettings:
31
32 def test_auth_settings_global_view_get(self, app):
33 url = '{prefix}/auth/'.format(prefix=ADMIN_PREFIX)
34 response = app.get(url)
35 assert response.status_code == 200
36
37 def test_plugin_settings_view_get(self, app, auth_plugin):
38 url = '{prefix}/auth/{name}'.format(
39 prefix=ADMIN_PREFIX,
40 name=auth_plugin.name)
41 with EnabledAuthPlugin(auth_plugin):
42 response = app.get(url)
43 assert response.status_code == 200
44
45 def test_plugin_settings_view_post(self, app, auth_plugin, csrf_token):
46 url = '{prefix}/auth/{name}'.format(
47 prefix=ADMIN_PREFIX,
48 name=auth_plugin.name)
49 params = {
50 'enabled': True,
51 'cache_ttl': 0,
52 'csrf_token': csrf_token,
53 }
54 with EnabledAuthPlugin(auth_plugin):
55 response = app.post(url, params=params)
56 assert response.status_code in [200, 302]
57
58 def test_plugin_settings_view_get_404(self, app, auth_plugin):
59 url = '{prefix}/auth/{name}'.format(
60 prefix=ADMIN_PREFIX,
61 name=auth_plugin.name)
62 with DisabledAuthPlugin(auth_plugin):
63 response = app.get(url, status=404)
64 assert response.status_code == 404
65
66 def test_plugin_settings_view_post_404(self, app, auth_plugin, csrf_token):
67 url = '{prefix}/auth/{name}'.format(
68 prefix=ADMIN_PREFIX,
69 name=auth_plugin.name)
70 params = {
71 'enabled': True,
72 'cache_ttl': 0,
73 'csrf_token': csrf_token,
74 }
75 with DisabledAuthPlugin(auth_plugin):
76 response = app.post(url, params=params, status=404)
77 assert response.status_code == 404
@@ -0,0 +1,28 b''
1 # Copyright (C) 2016 RhodeCode GmbH
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
19 """
20 Checks around the API of the class RhodeCodeAuthPluginBase.
21 """
22
23 from rhodecode.authentication.base import RhodeCodeAuthPluginBase
24
25
26 def test_str_returns_plugin_id():
27 plugin = RhodeCodeAuthPluginBase(plugin_id='stub_plugin_id')
28 assert str(plugin) == 'stub_plugin_id'
@@ -0,0 +1,75 b''
1 <%
2 elems = [
3 ## general
4 (_('RhodeCode Enterprise version'), c.rhodecode_version, ''),
5 (_('Upgrade info endpoint'), c.rhodecode_update_url, ''),
6 (_('Configuration INI file'), c.rhodecode_config_ini, ''),
7 ## systems stats
8 (_('RhodeCode Enterprise Server IP'), c.server_ip, ''),
9 (_('RhodeCode Enterprise Server ID'), c.server_id, ''),
10 (_('Platform'), c.platform, ''),
11 (_('Uptime'), c.uptime_age, ''),
12 (_('Storage location'), c.storage, ''),
13 (_('Storage disk space'), "%s/%s, %s%% used%s" % (h.format_byte_size_binary(c.disk['used']), h.format_byte_size_binary(c.disk['total']),(c.disk['percent']), ' %s' % c.disk['error'] if 'error' in c.disk else ''), ''),
14
15 (_('Search index storage'), c.index_storage, ''),
16 (_('Search index size'), "%s %s" % (h.format_byte_size_binary(c.disk_index['used']), ' %s' % c.disk_index['error'] if 'error' in c.disk_index else ''), ''),
17
18 (_('Gist storage'), c.gist_storage, ''),
19 (_('Gist storage size'), "%s (%s items)%s" % (h.format_byte_size_binary(c.disk_gist['used']),c.disk_gist['items'], ' %s' % c.disk_gist['error'] if 'error' in c.disk_gist else ''), ''),
20
21 (_('Archive cache'), c.archive_storage, ''),
22 (_('Archive cache size'), "%s%s" % (h.format_byte_size_binary(c.disk_archive['used']), ' %s' % c.disk_archive['error'] if 'error' in c.disk_archive else ''), ''),
23
24 (_('System memory'), c.system_memory, ''),
25 (_('CPU'), '%s %%' %(c.cpu), ''),
26 (_('Load'), '1min: %s, 5min: %s, 15min: %s' %(c.load['1_min'],c.load['5_min'],c.load['15_min']), ''),
27
28 ## rhodecode stuff
29 (_('Python version'), c.py_version, ''),
30 (_('Python path'), c.py_path, ''),
31 (_('GIT version'), c.git_version, ''),
32 (_('HG version'), c.hg_version, ''),
33 (_('SVN version'), c.svn_version, ''),
34 (_('Database'), "%s @ version: %s" % (c.db_type, c.db_migrate_version), ''),
35 (_('Database version'), c.db_version, ''),
36
37 ]
38 %>
39
40 <pre>
41 SYSTEM INFO
42 -----------
43
44 % for dt, dd, tt in elems:
45 ${dt}: ${dd}
46 % endfor
47
48 PYTHON PACKAGES
49 ---------------
50
51 % for key, value in c.py_modules:
52 ${key}: ${value}
53 % endfor
54
55 SYSTEM SETTINGS
56 ---------------
57
58 % for key, value in sorted(c.rhodecode_ini_safe.items()):
59 % if isinstance(value, dict):
60
61 % for key2, value2 in value.items():
62 [${key}]${key2}: ${value2}
63 % endfor
64
65 % else:
66 ${key}: ${value}
67 % endif
68 % endfor
69
70 </pre>
71
72
73
74
75
@@ -1,5 +1,5 b''
1 1 [bumpversion]
2 current_version = 4.1.2
2 current_version = 4.2.0
3 3 message = release: Bump version {current_version} to {new_version}
4 4
5 5 [bumpversion:file:rhodecode/VERSION]
@@ -1,32 +1,26 b''
1 1 [DEFAULT]
2 2 done = false
3 3
4 [task:fixes_on_stable]
5
6 [task:changelog_updated]
7
4 8 [task:bump_version]
5 9 done = true
6 10
7 [task:rc_tools_pinned]
8 done = true
11 [task:generate_api_docs]
12
13 [task:updated_translation]
9 14
10 [task:fixes_on_stable]
11 done = true
15 [release]
16 state = in_progress
17 version = 4.2.0
18
19 [task:rc_tools_pinned]
12 20
13 21 [task:pip2nix_generated]
14 done = true
15
16 [task:changelog_updated]
17 done = true
18
19 [task:generate_api_docs]
20 done = true
21 22
22 23 [task:generate_js_routes]
23 done = true
24
25 [release]
26 state = prepared
27 version = 4.1.2
28
29 [task:updated_translation]
30 24
31 25 [task:updated_trial_license]
32 26
@@ -41,6 +41,19 b' The latest sources can be obtained from '
41 41 https://code.rhodecode.com
42 42
43 43
44 Contributions
45 -------------
46
47 RhodeCode is open-source; contributions are welcome!
48
49 Please see the contribution documentation inside of the docs folder, which is
50 also available at
51 https://docs.rhodecode.com/RhodeCode-Enterprise/contributing/contributing.html
52
53 For additional information about collaboration tools, our issue tracker,
54 licensing, and contribution credit, visit https://rhodecode.com/open-source
55
56
44 57 RhodeCode Features
45 58 ------------------
46 59
@@ -1,7 +1,7 b''
1 1 README - Quickstart
2 2 ===================
3 3
4 This folder contains functional tests and the automation of specification
4 This folder contains the functional tests and automation of specification
5 5 examples. Details about testing can be found in
6 6 `/docs-internal/testing/index.rst`.
7 7
@@ -21,7 +21,7 b' Use the following example call for the d'
21 21 --api-key=9999999999999999999999999999999999999999 \
22 22 your-enterprise-config.ini
23 23
24 This way the username, password and auth token of the admin user will match the
24 This way the username, password, and auth token of the admin user will match the
25 25 defaults from the test run.
26 26
27 27
@@ -34,7 +34,7 b' 1. Make sure your Rhodecode Enterprise i'
34 34 2. Enter `nix-shell` from the acceptance_tests folder::
35 35
36 36 cd acceptance_tests
37 nix-shell -I ~/dev
37 nix-shell
38 38
39 39 Make sure that `rcpkgs` and `rcnixpkgs` are available on the nix path.
40 40
@@ -8,7 +8,6 b''
8 8
9 9 [DEFAULT]
10 10 debug = true
11 pdebug = false
12 11 ################################################################################
13 12 ## Uncomment and replace with the email address which should receive ##
14 13 ## any error reports after an application crash ##
@@ -115,11 +114,23 b' rhodecode.api.url = /_admin/api'
115 114
116 115 ## END RHODECODE PLUGINS ##
117 116
117 ## encryption key used to encrypt social plugin tokens,
118 ## remote_urls with credentials etc, if not set it defaults to
119 ## `beaker.session.secret`
120 #rhodecode.encrypted_values.secret =
121
122 ## decryption strict mode (enabled by default). It controls if decryption raises
123 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
124 #rhodecode.encrypted_values.strict = false
125
118 126 full_stack = true
119 127
120 128 ## Serve static files via RhodeCode, disable to serve them via HTTP server
121 129 static_files = true
122 130
131 # autogenerate javascript routes file on startup
132 generate_js_files = false
133
123 134 ## Optional Languages
124 135 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
125 136 lang = en
@@ -380,7 +391,6 b' beaker.session.auto = false'
380 391 search.module = rhodecode.lib.index.whoosh
381 392 search.location = %(here)s/data/index
382 393
383
384 394 ###################################
385 395 ## APPENLIGHT CONFIG ##
386 396 ###################################
@@ -514,7 +524,7 b' vcs.connection_timeout = 3600'
514 524 ### LOGGING CONFIGURATION ####
515 525 ################################
516 526 [loggers]
517 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
527 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates
518 528
519 529 [handlers]
520 530 keys = console, console_sql
@@ -566,12 +576,6 b' handlers = console_sql'
566 576 qualname = sqlalchemy.engine
567 577 propagate = 0
568 578
569 [logger_whoosh_indexer]
570 level = DEBUG
571 handlers =
572 qualname = whoosh_indexer
573 propagate = 1
574
575 579 ##############
576 580 ## HANDLERS ##
577 581 ##############
@@ -8,7 +8,6 b''
8 8
9 9 [DEFAULT]
10 10 debug = true
11 pdebug = false
12 11 ################################################################################
13 12 ## Uncomment and replace with the email address which should receive ##
14 13 ## any error reports after an application crash ##
@@ -89,11 +88,23 b' use = egg:rhodecode-enterprise-ce'
89 88 ## enable proxy prefix middleware, defined below
90 89 #filter-with = proxy-prefix
91 90
91 ## encryption key used to encrypt social plugin tokens,
92 ## remote_urls with credentials etc, if not set it defaults to
93 ## `beaker.session.secret`
94 #rhodecode.encrypted_values.secret =
95
96 ## decryption strict mode (enabled by default). It controls if decryption raises
97 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
98 #rhodecode.encrypted_values.strict = false
99
92 100 full_stack = true
93 101
94 102 ## Serve static files via RhodeCode, disable to serve them via HTTP server
95 103 static_files = true
96 104
105 # autogenerate javascript routes file on startup
106 generate_js_files = false
107
97 108 ## Optional Languages
98 109 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
99 110 lang = en
@@ -354,7 +365,6 b' beaker.session.auto = false'
354 365 search.module = rhodecode.lib.index.whoosh
355 366 search.location = %(here)s/data/index
356 367
357
358 368 ###################################
359 369 ## APPENLIGHT CONFIG ##
360 370 ###################################
@@ -483,7 +493,7 b' vcs.connection_timeout = 3600'
483 493 ### LOGGING CONFIGURATION ####
484 494 ################################
485 495 [loggers]
486 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
496 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates
487 497
488 498 [handlers]
489 499 keys = console, console_sql
@@ -535,12 +545,6 b' handlers = console_sql'
535 545 qualname = sqlalchemy.engine
536 546 propagate = 0
537 547
538 [logger_whoosh_indexer]
539 level = DEBUG
540 handlers =
541 qualname = whoosh_indexer
542 propagate = 1
543
544 548 ##############
545 549 ## HANDLERS ##
546 550 ##############
@@ -46,6 +46,7 b' let'
46 46 !elem ext ["egg-info" "pyc"] &&
47 47 !startsWith "result" path;
48 48
49 sources = pkgs.config.rc.sources or {};
49 50 rhodecode-enterprise-ce-src = builtins.filterSource src-filter ./.;
50 51
51 52 # Load the generated node packages
@@ -98,9 +99,11 b' let'
98 99 '';
99 100 in super.rhodecode-enterprise-ce.override (attrs: {
100 101
101 inherit doCheck;
102 inherit
103 doCheck
104 version;
102 105 name = "rhodecode-enterprise-ce-${version}";
103 version = version;
106 releaseName = "RhodeCodeEnterpriseCE-${version}";
104 107 src = rhodecode-enterprise-ce-src;
105 108
106 109 buildInputs =
@@ -109,7 +112,7 b' let'
109 112 pkgs.nodePackages.grunt-cli
110 113 pkgs.subversion
111 114 pytest-catchlog
112 rc_testdata
115 rhodecode-testdata
113 116 ]);
114 117
115 118 propagatedBuildInputs = attrs.propagatedBuildInputs ++ (with self; [
@@ -197,17 +200,22 b' let'
197 200
198 201 });
199 202
200 rc_testdata = self.buildPythonPackage rec {
201 name = "rc_testdata-0.7.0";
202 src = pkgs.fetchhg {
203 url = "https://code.rhodecode.com/upstream/rc_testdata";
204 rev = "v0.7.0";
205 sha256 = "0w3z0zn8lagr707v67lgys23sl6pbi4xg7pfvdbw58h3q384h6rx";
206 };
203 rhodecode-testdata = import "${rhodecode-testdata-src}/default.nix" {
204 inherit
205 doCheck
206 pkgs
207 pythonPackages;
207 208 };
208 209
209 210 };
210 211
212 rhodecode-testdata-src = sources.rhodecode-testdata or (
213 pkgs.fetchhg {
214 url = "https://code.rhodecode.com/upstream/rc_testdata";
215 rev = "v0.8.0";
216 sha256 = "0hy1ba134rq2f9si85yx7j4qhc9ky0hjzdk553s3q026i7km809m";
217 });
218
211 219 # Apply all overrides and fix the final package set
212 220 myPythonPackagesUnfix =
213 221 (extends pythonExternalOverrides
@@ -47,7 +47,7 b' the ``debug`` level.'
47 47 ### LOGGING CONFIGURATION ####
48 48 ################################
49 49 [loggers]
50 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
50 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates
51 51
52 52 [handlers]
53 53 keys = console, console_sql, file, file_rotating
@@ -99,12 +99,6 b' the ``debug`` level.'
99 99 qualname = sqlalchemy.engine
100 100 propagate = 0
101 101
102 [logger_whoosh_indexer]
103 level = DEBUG
104 handlers =
105 qualname = whoosh_indexer
106 propagate = 1
107
108 102 ##############
109 103 ## HANDLERS ##
110 104 ##############
@@ -5,13 +5,14 b' Contributing to RhodeCode'
5 5
6 6
7 7
8 Welcome to contribution guides and development docs of RhodeCode.
8 Welcome to the contribution guides and development docs of RhodeCode.
9 9
10 10
11 11
12 12 .. toctree::
13 13 :maxdepth: 1
14 14
15 overview
15 16 testing/index
16 17 dev-setup
17 18 db-schema
@@ -2,7 +2,7 b''
2 2 DB Schema and Migration
3 3 =======================
4 4
5 To create or alter tables in the database it's necessary to change a couple of
5 To create or alter tables in the database, it's necessary to change a couple of
6 6 files, apart from configuring the settings pointing to the latest database
7 7 schema.
8 8
@@ -11,11 +11,11 b' Database Model and ORM'
11 11 ----------------------
12 12
13 13 On ``rhodecode.model.db`` you will find the database definition of all tables and
14 fields. Any fresh install database will be correctly created by the definitions
15 here. So, any change to this files will affect the tests without having to change
14 fields. Any freshly installed database will be correctly created by the definitions
15 here. So, any change to this file will affect the tests without having to change
16 16 any other file.
17 17
18 A second layer are the businness classes that are inside ``rhodecode.model``.
18 A second layer are the business classes inside ``rhodecode.model``.
19 19
20 20
21 21 Database Migration
@@ -30,7 +30,7 b' Three files play a role when creating da'
30 30
31 31 Schema is a snapshot of the database version BEFORE the migration. So, it's
32 32 the initial state before any changes were added. The name convention is
33 the latest release version where the snapshot were created, and not the
33 the latest release version where the snapshot was created, and not the
34 34 target version of this code.
35 35
36 36 Version is the method that will define how to UPGRADE/DOWNGRADE the database.
@@ -45,8 +45,8 b' For examples on how to create those file'
45 45 Migration Command
46 46 ^^^^^^^^^^^^^^^^^
47 47
48 After you changed the database ORM and migration files, you can run::
48 After you've changed the database ORM and migration files, you can run::
49 49
50 50 paster upgrade-db <ini-file>
51 51
52 And the database will be upgraded up to the version defined in the ``__init__`` file. No newline at end of file
52 The database will be upgraded up to the version defined in the ``__init__`` file. No newline at end of file
@@ -13,7 +13,7 b' purposes. This section contains an overv'
13 13 =============
14 14
15 15 Enables the section "Style" in the application. This section provides an
16 overview of all components which are found in the frontend style of the
16 overview of all components which are found in the frontend of the
17 17 application.
18 18
19 19
@@ -29,9 +29,9 b' to ease development.'
29 29 `[logging]`
30 30 ===========
31 31
32 Use this to configure loggig to your current needs. The documentation of
33 Python's `logging` module explains all details. The following snippets are
34 useful for day to day development work.
32 Use this to configure logging to your current needs. The documentation of
33 Python's `logging` module explains all of the details. The following snippets
34 are useful for day to day development work.
35 35
36 36
37 37 Mute SQL output
@@ -1,3 +1,4 b''
1 .. _dev-setup:
1 2
2 3 ===================
3 4 Development setup
@@ -7,21 +8,21 b''
7 8 RhodeCode Enterprise runs inside a Nix managed environment. This ensures build
8 9 environment dependencies are correctly declared and installed during setup.
9 10 It also enables atomic upgrades, rollbacks, and multiple instances of RhodeCode
10 Enterprise for efficient cluster management.
11 Enterprise running with isolation.
11 12
12 To set up RhodeCode Enterprise inside the Nix environment use the following steps:
13 To set up RhodeCode Enterprise inside the Nix environment, use the following steps:
13 14
14 15
15 16
16 17 Setup Nix Package Manager
17 18 -------------------------
18 19
19 To install the Nix Package Manager please run::
20 To install the Nix Package Manager, please run::
20 21
21 22 $ curl https://nixos.org/nix/install | sh
22 23
23 or go to https://nixos.org/nix/ and follow their installation instructions.
24 Once this is correctly set up on your system you should be able to use the
24 or go to https://nixos.org/nix/ and follow the installation instructions.
25 Once this is correctly set up on your system, you should be able to use the
25 26 following commands:
26 27
27 28 * `nix-env`
@@ -34,8 +35,8 b' following commands:'
34 35 Update your channels frequently by running ``nix-channel --upgrade``.
35 36
36 37
37 Switch nix to latest STABLE channel
38 -----------------------------------
38 Switch nix to the latest STABLE channel
39 ---------------------------------------
39 40
40 41 run::
41 42
@@ -49,7 +50,7 b' Followed by::'
49 50 Clone the required repositories
50 51 -------------------------------
51 52
52 After Nix is set up, clone the RhodeCode Enterprise Community Edition, and
53 After Nix is set up, clone the RhodeCode Enterprise Community Edition and
53 54 RhodeCode VCSServer repositories into the same directory.
54 55 To do this, use the following example::
55 56
@@ -59,23 +60,25 b' To do this, use the following example::'
59 60
60 61 .. note::
61 62
62 If you cannot clone the repository, please request read permissions.
63 If you cannot clone the repository, please request read permissions
64 via support@rhodecode.com
63 65
64 66
65 67
66 68 Enter the Development Shell
67 69 ---------------------------
68 70
69 The final step is to start into the development shell. To do this run the
71 The final step is to start the development shell. To do this, run the
70 72 following command from inside the cloned repository::
71 73
72 74 cd ~/rhodecode-enterprise-ce
73 nix-shell --arg dev true
75 nix-shell
74 76
75 77 .. note::
76 78
77 79 On the first run, this will take a while to download and optionally compile
78 a few things. The next runs of it will be faster.
80 a few things. The following runs will be faster. The development shell works
81 fine on MacOS and Linux platforms.
79 82
80 83
81 84
@@ -90,13 +93,13 b' 2. Adjust the configuration settings to '
90 93
91 94 .. note::
92 95
93 It is recommended to call it `dev.ini`.
96 It is recommended to use the name `dev.ini`.
94 97
95 98
96 99 Setup the Development Database
97 100 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
98 101
99 To create a development database use the following example. This is a one
102 To create a development database, use the following example. This is a one
100 103 time operation::
101 104
102 105 paster setup-rhodecode dev.ini \
@@ -109,7 +112,7 b' Start the Development Server'
109 112 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
110 113
111 114 When starting the development server, you should start the vcsserver as a
112 separate process. To do this use one of the following examples:
115 separate process. To do this, use one of the following examples:
113 116
114 117 1. Set the `start.vcs_server` flag in the ``dev.ini`` file to true. For example:
115 118
@@ -136,6 +139,6 b' 3. Start the development server in a dif'
136 139 Run the Environment Tests
137 140 ^^^^^^^^^^^^^^^^^^^^^^^^^
138 141
139 Please make sure that the test are passing to verify that your environment is
140 set up correctly. More details about the tests are described in:
141 :file:`/docs/dev/testing`.
142 Please make sure that the tests are passing to verify that your environment is
143 set up correctly. RhodeCode uses py.test to run tests.
144 Please simply run ``make test`` to run the basic test suite.
@@ -17,12 +17,12 b''
17 17 Overview
18 18 ========
19 19
20 We have a quite big test suite inside of :file:`rhodecode/tests` which is a mix
20 We have a quite large test suite inside of :file:`rhodecode/tests` which is a mix
21 21 of unit tests and functional or integration tests. More details are in
22 22 :ref:`test-unit-and-functional`.
23 23
24 24
25 Apart from that we start to apply "Specification by Example" and maintain a
26 collection of such specifications together with an implementation so that it can
27 be validated in an automatic way. The files can be found in
25 Apart from that, we are starting to apply "Specification by Example" and maintain
26 a collection of such specifications together with an implementation so that it
27 can be validated in an automatic way. The files can be found in
28 28 :file:`acceptance_tests`. More details are in :ref:`test-spec-by-example`.
@@ -66,10 +66,10 b' Locators'
66 66 --------
67 67
68 68 The specific information how to locate an element inside of the DOM tree of a
69 page is kept in a separate class. This class serves mainly as a data container,
69 page is kept in a separate class. This class serves mainly as a data container;
70 70 it shall not contain any logic.
71 71
72 72 The reason for keeping the locators separate is that we expect a frequent need
73 for change whenever we work on our templates. In such a case it is more
74 efficient to have all locators together and update them there instead of having
75 to find all locators inside of the logic of a page object.
73 for change whenever we work on our templates. In such a case, it is more
74 efficient to have all of thelocators together and update them there instead of
75 having to find every locator inside of the logic of a page object.
@@ -27,7 +27,7 b' py.test integration'
27 27 The integration with the test runner is based on the following three parts:
28 28
29 29 - `pytest_pylons` is a py.test plugin which does the integration with the
30 Pylons web framework. It sets up the Pylons environment based on a given ini
30 Pylons web framework. It sets up the Pylons environment based on the given ini
31 31 file.
32 32
33 33 Tests which depend on the Pylons environment to be set up must request the
@@ -9,6 +9,7 b' Release Notes'
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.2.0.rst
12 13 release-notes-4.1.2.rst
13 14 release-notes-4.1.1.rst
14 15 release-notes-4.1.0.rst
@@ -34,9 +34,6 b' let'
34 34 # figure it out without calling into nix-store.
35 35 enterprise = import ./default.nix {
36 36 doCheck = false;
37 with_vcsserver = false;
38 with_pyramid = false;
39 cythonize = false;
40 37 };
41 38
42 39 # For a given derivation, return the list of all dependencies
@@ -8,10 +8,28 b''
8 8
9 9 let
10 10 sed = "sed -i";
11 localLicenses = {
12 repoze = {
13 fullName = "Repoze License";
14 url = http://www.repoze.org/LICENSE.txt;
15 };
16 };
11 17 in
12 18
13 19 self: super: {
14 20
21 appenlight-client = super.appenlight-client.override (attrs: {
22 meta = {
23 license = [ pkgs.lib.licenses.bsdOriginal ];
24 };
25 });
26
27 future = super.future.override (attrs: {
28 meta = {
29 license = [ pkgs.lib.licenses.mit ];
30 };
31 });
32
15 33 gnureadline = super.gnureadline.override (attrs: {
16 34 buildInputs = attrs.buildInputs ++ [
17 35 pkgs.ncurses
@@ -72,6 +90,9 b' self: super: {'
72 90 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
73 91 pkgs.postgresql
74 92 ];
93 meta = {
94 license = pkgs.lib.licenses.lgpl3Plus;
95 };
75 96 });
76 97
77 98 pycurl = super.pycurl.override (attrs: {
@@ -83,6 +104,10 b' self: super: {'
83 104 substituteInPlace setup.py --replace '--static-libs' '--libs'
84 105 export PYCURL_SSL_LIBRARY=openssl
85 106 '';
107 meta = {
108 # TODO: It is LGPL and MIT
109 license = pkgs.lib.licenses.mit;
110 };
86 111 });
87 112
88 113 Pylons = super.Pylons.override (attrs: {
@@ -101,6 +126,15 b' self: super: {'
101 126 # confuses pserve.
102 127 ${sed} '/import sys; sys.argv/d' $out/bin/.pserve-wrapped
103 128 '';
129 meta = {
130 license = localLicenses.repoze;
131 };
132 });
133
134 pyramid-debugtoolbar = super.pyramid-debugtoolbar.override (attrs: {
135 meta = {
136 license = [ pkgs.lib.licenses.bsdOriginal localLicenses.repoze ];
137 };
104 138 });
105 139
106 140 Pyro4 = super.Pyro4.override (attrs: {
@@ -117,6 +151,9 b' self: super: {'
117 151 propagatedBuildInputs = [
118 152 pkgs.sqlite
119 153 ];
154 meta = {
155 license = [ pkgs.lib.licenses.zlib pkgs.lib.licenses.libpng ];
156 };
120 157 });
121 158
122 159 pytest-runner = super.pytest-runner.override (attrs: {
@@ -158,6 +195,77 b' self: super: {'
158 195 ];
159 196 });
160 197
198 URLObject = super.URLObject.override (attrs: {
199 meta = {
200 license = {
201 spdxId = "Unlicense";
202 fullName = "The Unlicense";
203 url = http://unlicense.org/;
204 };
205 };
206 });
207
208 amqplib = super.amqplib.override (attrs: {
209 meta = {
210 license = pkgs.lib.licenses.lgpl3;
211 };
212 });
213
214 docutils = super.docutils.override (attrs: {
215 meta = {
216 license = pkgs.lib.licenses.bsd2;
217 };
218 });
219
220 colander = super.colander.override (attrs: {
221 meta = {
222 license = localLicenses.repoze;
223 };
224 });
225
226 pyramid-beaker = super.pyramid-beaker.override (attrs: {
227 meta = {
228 license = localLicenses.repoze;
229 };
230 });
231
232 pyramid-mako = super.pyramid-mako.override (attrs: {
233 meta = {
234 license = localLicenses.repoze;
235 };
236 });
237
238 repoze.lru = super.repoze.lru.override (attrs: {
239 meta = {
240 license = localLicenses.repoze;
241 };
242 });
243
244 recaptcha-client = super.recaptcha-client.override (attrs: {
245 meta = {
246 # TODO: It is MIT/X11
247 license = pkgs.lib.licenses.mit;
248 };
249 });
250
251 python-editor = super.python-editor.override (attrs: {
252 meta = {
253 license = pkgs.lib.licenses.asl20;
254 };
255 });
256
257 translationstring = super.translationstring.override (attrs: {
258 meta = {
259 license = localLicenses.repoze;
260 };
261 });
262
263 venusian = super.venusian.override (attrs: {
264 meta = {
265 license = localLicenses.repoze;
266 };
267 });
268
161 269 # Avoid that setuptools is replaced, this leads to trouble
162 270 # with buildPythonPackage.
163 271 setuptools = basePythonPackages.setuptools;
@@ -8,6 +8,9 b''
8 8 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
9 9 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
10 10 };
11 meta = {
12 license = [ pkgs.lib.licenses.bsdOriginal ];
13 };
11 14 };
12 15 Beaker = super.buildPythonPackage {
13 16 name = "Beaker-1.7.0";
@@ -18,6 +21,9 b''
18 21 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
19 22 md5 = "386be3f7fe427358881eee4622b428b3";
20 23 };
24 meta = {
25 license = [ pkgs.lib.licenses.bsdOriginal ];
26 };
21 27 };
22 28 CProfileV = super.buildPythonPackage {
23 29 name = "CProfileV-1.0.6";
@@ -28,6 +34,9 b''
28 34 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
29 35 md5 = "08c7c242b6e64237bc53c5d13537e03d";
30 36 };
37 meta = {
38 license = [ pkgs.lib.licenses.mit ];
39 };
31 40 };
32 41 Fabric = super.buildPythonPackage {
33 42 name = "Fabric-1.10.0";
@@ -38,6 +47,9 b''
38 47 url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz";
39 48 md5 = "2cb96473387f0e7aa035210892352f4a";
40 49 };
50 meta = {
51 license = [ pkgs.lib.licenses.bsdOriginal ];
52 };
41 53 };
42 54 FormEncode = super.buildPythonPackage {
43 55 name = "FormEncode-1.2.4";
@@ -48,6 +60,9 b''
48 60 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
49 61 md5 = "6bc17fb9aed8aea198975e888e2077f4";
50 62 };
63 meta = {
64 license = [ pkgs.lib.licenses.psfl ];
65 };
51 66 };
52 67 Jinja2 = super.buildPythonPackage {
53 68 name = "Jinja2-2.7.3";
@@ -58,6 +73,9 b''
58 73 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
59 74 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
60 75 };
76 meta = {
77 license = [ pkgs.lib.licenses.bsdOriginal ];
78 };
61 79 };
62 80 Mako = super.buildPythonPackage {
63 81 name = "Mako-1.0.1";
@@ -68,6 +86,9 b''
68 86 url = "https://pypi.python.org/packages/8e/a4/aa56533ecaa5f22ca92428f74e074d0c9337282933c722391902c8f9e0f8/Mako-1.0.1.tar.gz";
69 87 md5 = "9f0aafd177b039ef67b90ea350497a54";
70 88 };
89 meta = {
90 license = [ pkgs.lib.licenses.mit ];
91 };
71 92 };
72 93 Markdown = super.buildPythonPackage {
73 94 name = "Markdown-2.6.2";
@@ -78,6 +99,9 b''
78 99 url = "https://pypi.python.org/packages/62/8b/83658b5f6c220d5fcde9f9852d46ea54765d734cfbc5a9f4c05bfc36db4d/Markdown-2.6.2.tar.gz";
79 100 md5 = "256d19afcc564dc4ce4c229bb762f7ae";
80 101 };
102 meta = {
103 license = [ pkgs.lib.licenses.bsdOriginal ];
104 };
81 105 };
82 106 MarkupSafe = super.buildPythonPackage {
83 107 name = "MarkupSafe-0.23";
@@ -88,6 +112,9 b''
88 112 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
89 113 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
90 114 };
115 meta = {
116 license = [ pkgs.lib.licenses.bsdOriginal ];
117 };
91 118 };
92 119 MySQL-python = super.buildPythonPackage {
93 120 name = "MySQL-python-1.2.5";
@@ -98,6 +125,9 b''
98 125 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
99 126 md5 = "654f75b302db6ed8dc5a898c625e030c";
100 127 };
128 meta = {
129 license = [ pkgs.lib.licenses.gpl1 ];
130 };
101 131 };
102 132 Paste = super.buildPythonPackage {
103 133 name = "Paste-2.0.2";
@@ -108,6 +138,9 b''
108 138 url = "https://pypi.python.org/packages/d5/8d/0f8ac40687b97ff3e07ebd1369be20bdb3f93864d2dc3c2ff542edb4ce50/Paste-2.0.2.tar.gz";
109 139 md5 = "4bfc8a7eaf858f6309d2ac0f40fc951c";
110 140 };
141 meta = {
142 license = [ pkgs.lib.licenses.mit ];
143 };
111 144 };
112 145 PasteDeploy = super.buildPythonPackage {
113 146 name = "PasteDeploy-1.5.2";
@@ -118,6 +151,9 b''
118 151 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
119 152 md5 = "352b7205c78c8de4987578d19431af3b";
120 153 };
154 meta = {
155 license = [ pkgs.lib.licenses.mit ];
156 };
121 157 };
122 158 PasteScript = super.buildPythonPackage {
123 159 name = "PasteScript-1.7.5";
@@ -128,6 +164,9 b''
128 164 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
129 165 md5 = "4c72d78dcb6bb993f30536842c16af4d";
130 166 };
167 meta = {
168 license = [ pkgs.lib.licenses.mit ];
169 };
131 170 };
132 171 Pygments = super.buildPythonPackage {
133 172 name = "Pygments-2.0.2";
@@ -138,6 +177,9 b''
138 177 url = "https://pypi.python.org/packages/f4/c6/bdbc5a8a112256b2b6136af304dbae93d8b1ef8738ff2d12a51018800e46/Pygments-2.0.2.tar.gz";
139 178 md5 = "238587a1370d62405edabd0794b3ec4a";
140 179 };
180 meta = {
181 license = [ pkgs.lib.licenses.bsdOriginal ];
182 };
141 183 };
142 184 Pylons = super.buildPythonPackage {
143 185 name = "Pylons-1.0.1";
@@ -148,6 +190,9 b''
148 190 url = "https://pypi.python.org/packages/a2/69/b835a6bad00acbfeed3f33c6e44fa3f936efc998c795bfb15c61a79ecf62/Pylons-1.0.1.tar.gz";
149 191 md5 = "6cb880d75fa81213192142b07a6e4915";
150 192 };
193 meta = {
194 license = [ pkgs.lib.licenses.bsdOriginal ];
195 };
151 196 };
152 197 Pyro4 = super.buildPythonPackage {
153 198 name = "Pyro4-4.41";
@@ -158,6 +203,9 b''
158 203 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
159 204 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
160 205 };
206 meta = {
207 license = [ pkgs.lib.licenses.mit ];
208 };
161 209 };
162 210 Routes = super.buildPythonPackage {
163 211 name = "Routes-1.13";
@@ -168,6 +216,9 b''
168 216 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
169 217 md5 = "d527b0ab7dd9172b1275a41f97448783";
170 218 };
219 meta = {
220 license = [ pkgs.lib.licenses.bsdOriginal ];
221 };
171 222 };
172 223 SQLAlchemy = super.buildPythonPackage {
173 224 name = "SQLAlchemy-0.9.9";
@@ -178,6 +229,9 b''
178 229 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
179 230 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
180 231 };
232 meta = {
233 license = [ pkgs.lib.licenses.mit ];
234 };
181 235 };
182 236 Sphinx = super.buildPythonPackage {
183 237 name = "Sphinx-1.2.2";
@@ -188,6 +242,9 b''
188 242 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
189 243 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
190 244 };
245 meta = {
246 license = [ pkgs.lib.licenses.bsdOriginal ];
247 };
191 248 };
192 249 Tempita = super.buildPythonPackage {
193 250 name = "Tempita-0.5.2";
@@ -198,6 +255,9 b''
198 255 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
199 256 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
200 257 };
258 meta = {
259 license = [ pkgs.lib.licenses.mit ];
260 };
201 261 };
202 262 URLObject = super.buildPythonPackage {
203 263 name = "URLObject-2.4.0";
@@ -208,6 +268,9 b''
208 268 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
209 269 md5 = "2ed819738a9f0a3051f31dc9924e3065";
210 270 };
271 meta = {
272 license = [ ];
273 };
211 274 };
212 275 WebError = super.buildPythonPackage {
213 276 name = "WebError-0.10.3";
@@ -218,6 +281,9 b''
218 281 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
219 282 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
220 283 };
284 meta = {
285 license = [ pkgs.lib.licenses.mit ];
286 };
221 287 };
222 288 WebHelpers = super.buildPythonPackage {
223 289 name = "WebHelpers-1.3";
@@ -228,6 +294,9 b''
228 294 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
229 295 md5 = "32749ffadfc40fea51075a7def32588b";
230 296 };
297 meta = {
298 license = [ pkgs.lib.licenses.bsdOriginal ];
299 };
231 300 };
232 301 WebHelpers2 = super.buildPythonPackage {
233 302 name = "WebHelpers2-2.0";
@@ -238,6 +307,9 b''
238 307 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
239 308 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
240 309 };
310 meta = {
311 license = [ pkgs.lib.licenses.mit ];
312 };
241 313 };
242 314 WebOb = super.buildPythonPackage {
243 315 name = "WebOb-1.3.1";
@@ -248,6 +320,9 b''
248 320 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
249 321 md5 = "20918251c5726956ba8fef22d1556177";
250 322 };
323 meta = {
324 license = [ pkgs.lib.licenses.mit ];
325 };
251 326 };
252 327 WebTest = super.buildPythonPackage {
253 328 name = "WebTest-1.4.3";
@@ -258,6 +333,9 b''
258 333 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
259 334 md5 = "631ce728bed92c681a4020a36adbc353";
260 335 };
336 meta = {
337 license = [ pkgs.lib.licenses.mit ];
338 };
261 339 };
262 340 Whoosh = super.buildPythonPackage {
263 341 name = "Whoosh-2.7.0";
@@ -268,6 +346,9 b''
268 346 url = "https://pypi.python.org/packages/1c/dc/2f0231ff3875ded36df8c1ab851451e51a237dc0e5a86d3d96036158da94/Whoosh-2.7.0.zip";
269 347 md5 = "7abfd970f16fadc7311960f3fa0bc7a9";
270 348 };
349 meta = {
350 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
351 };
271 352 };
272 353 alembic = super.buildPythonPackage {
273 354 name = "alembic-0.8.4";
@@ -278,6 +359,9 b''
278 359 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
279 360 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
280 361 };
362 meta = {
363 license = [ pkgs.lib.licenses.mit ];
364 };
281 365 };
282 366 amqplib = super.buildPythonPackage {
283 367 name = "amqplib-1.0.2";
@@ -288,6 +372,9 b''
288 372 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
289 373 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
290 374 };
375 meta = {
376 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
377 };
291 378 };
292 379 anyjson = super.buildPythonPackage {
293 380 name = "anyjson-0.3.3";
@@ -298,6 +385,9 b''
298 385 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
299 386 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
300 387 };
388 meta = {
389 license = [ pkgs.lib.licenses.bsdOriginal ];
390 };
301 391 };
302 392 appenlight-client = super.buildPythonPackage {
303 393 name = "appenlight-client-0.6.14";
@@ -308,6 +398,9 b''
308 398 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
309 399 md5 = "578c69b09f4356d898fff1199b98a95c";
310 400 };
401 meta = {
402 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "DFSG approved"; } ];
403 };
311 404 };
312 405 authomatic = super.buildPythonPackage {
313 406 name = "authomatic-0.1.0.post1";
@@ -318,6 +411,9 b''
318 411 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
319 412 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
320 413 };
414 meta = {
415 license = [ pkgs.lib.licenses.mit ];
416 };
321 417 };
322 418 backport-ipaddress = super.buildPythonPackage {
323 419 name = "backport-ipaddress-0.1";
@@ -328,6 +424,9 b''
328 424 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
329 425 md5 = "9c1f45f4361f71b124d7293a60006c05";
330 426 };
427 meta = {
428 license = [ pkgs.lib.licenses.psfl ];
429 };
331 430 };
332 431 bottle = super.buildPythonPackage {
333 432 name = "bottle-0.12.8";
@@ -338,6 +437,9 b''
338 437 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
339 438 md5 = "13132c0a8f607bf860810a6ee9064c5b";
340 439 };
440 meta = {
441 license = [ pkgs.lib.licenses.mit ];
442 };
341 443 };
342 444 bumpversion = super.buildPythonPackage {
343 445 name = "bumpversion-0.5.3";
@@ -348,6 +450,9 b''
348 450 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
349 451 md5 = "c66a3492eafcf5ad4b024be9fca29820";
350 452 };
453 meta = {
454 license = [ pkgs.lib.licenses.mit ];
455 };
351 456 };
352 457 celery = super.buildPythonPackage {
353 458 name = "celery-2.2.10";
@@ -358,6 +463,9 b''
358 463 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
359 464 md5 = "898bc87e54f278055b561316ba73e222";
360 465 };
466 meta = {
467 license = [ pkgs.lib.licenses.bsdOriginal ];
468 };
361 469 };
362 470 click = super.buildPythonPackage {
363 471 name = "click-5.1";
@@ -368,6 +476,9 b''
368 476 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
369 477 md5 = "9c5323008cccfe232a8b161fc8196d41";
370 478 };
479 meta = {
480 license = [ pkgs.lib.licenses.bsdOriginal ];
481 };
371 482 };
372 483 colander = super.buildPythonPackage {
373 484 name = "colander-1.2";
@@ -378,6 +489,9 b''
378 489 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
379 490 md5 = "83db21b07936a0726e588dae1914b9ed";
380 491 };
492 meta = {
493 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
494 };
381 495 };
382 496 configobj = super.buildPythonPackage {
383 497 name = "configobj-5.0.6";
@@ -388,6 +502,9 b''
388 502 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
389 503 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
390 504 };
505 meta = {
506 license = [ pkgs.lib.licenses.bsdOriginal ];
507 };
391 508 };
392 509 cov-core = super.buildPythonPackage {
393 510 name = "cov-core-1.15.0";
@@ -398,6 +515,9 b''
398 515 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
399 516 md5 = "f519d4cb4c4e52856afb14af52919fe6";
400 517 };
518 meta = {
519 license = [ pkgs.lib.licenses.mit ];
520 };
401 521 };
402 522 coverage = super.buildPythonPackage {
403 523 name = "coverage-3.7.1";
@@ -408,6 +528,9 b''
408 528 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
409 529 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
410 530 };
531 meta = {
532 license = [ pkgs.lib.licenses.bsdOriginal ];
533 };
411 534 };
412 535 cssselect = super.buildPythonPackage {
413 536 name = "cssselect-0.9.1";
@@ -418,6 +541,9 b''
418 541 url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz";
419 542 md5 = "c74f45966277dc7a0f768b9b0f3522ac";
420 543 };
544 meta = {
545 license = [ pkgs.lib.licenses.bsdOriginal ];
546 };
421 547 };
422 548 decorator = super.buildPythonPackage {
423 549 name = "decorator-3.4.2";
@@ -428,6 +554,9 b''
428 554 url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz";
429 555 md5 = "9e0536870d2b83ae27d58dbf22582f4d";
430 556 };
557 meta = {
558 license = [ pkgs.lib.licenses.bsdOriginal ];
559 };
431 560 };
432 561 docutils = super.buildPythonPackage {
433 562 name = "docutils-0.12";
@@ -438,6 +567,9 b''
438 567 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
439 568 md5 = "4622263b62c5c771c03502afa3157768";
440 569 };
570 meta = {
571 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 ];
572 };
441 573 };
442 574 dogpile.cache = super.buildPythonPackage {
443 575 name = "dogpile.cache-0.5.7";
@@ -448,6 +580,9 b''
448 580 url = "https://pypi.python.org/packages/07/74/2a83bedf758156d9c95d112691bbad870d3b77ccbcfb781b4ef836ea7d96/dogpile.cache-0.5.7.tar.gz";
449 581 md5 = "3e58ce41af574aab41d78e9c4190f194";
450 582 };
583 meta = {
584 license = [ pkgs.lib.licenses.bsdOriginal ];
585 };
451 586 };
452 587 dogpile.core = super.buildPythonPackage {
453 588 name = "dogpile.core-0.4.1";
@@ -458,6 +593,9 b''
458 593 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
459 594 md5 = "01cb19f52bba3e95c9b560f39341f045";
460 595 };
596 meta = {
597 license = [ pkgs.lib.licenses.bsdOriginal ];
598 };
461 599 };
462 600 dulwich = super.buildPythonPackage {
463 601 name = "dulwich-0.12.0";
@@ -468,6 +606,9 b''
468 606 url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz";
469 607 md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa";
470 608 };
609 meta = {
610 license = [ pkgs.lib.licenses.gpl2Plus ];
611 };
471 612 };
472 613 ecdsa = super.buildPythonPackage {
473 614 name = "ecdsa-0.11";
@@ -478,6 +619,9 b''
478 619 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
479 620 md5 = "8ef586fe4dbb156697d756900cb41d7c";
480 621 };
622 meta = {
623 license = [ pkgs.lib.licenses.mit ];
624 };
481 625 };
482 626 elasticsearch = super.buildPythonPackage {
483 627 name = "elasticsearch-2.3.0";
@@ -488,6 +632,9 b''
488 632 url = "https://pypi.python.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
489 633 md5 = "2550f3b51629cf1ef9636608af92c340";
490 634 };
635 meta = {
636 license = [ pkgs.lib.licenses.asl20 ];
637 };
491 638 };
492 639 elasticsearch-dsl = super.buildPythonPackage {
493 640 name = "elasticsearch-dsl-2.0.0";
@@ -498,6 +645,9 b''
498 645 url = "https://pypi.python.org/packages/4e/5d/e788ae8dbe2ff4d13426db0a027533386a5c276c77a2654dc0e2007ce04a/elasticsearch-dsl-2.0.0.tar.gz";
499 646 md5 = "4cdfec81bb35383dd3b7d02d7dc5ee68";
500 647 };
648 meta = {
649 license = [ pkgs.lib.licenses.asl20 ];
650 };
501 651 };
502 652 flake8 = super.buildPythonPackage {
503 653 name = "flake8-2.4.1";
@@ -508,6 +658,9 b''
508 658 url = "https://pypi.python.org/packages/8f/b5/9a73c66c7dba273bac8758398f060c008a25f3e84531063b42503b5d0a95/flake8-2.4.1.tar.gz";
509 659 md5 = "ed45d3db81a3b7c88bd63c6e37ca1d65";
510 660 };
661 meta = {
662 license = [ pkgs.lib.licenses.mit ];
663 };
511 664 };
512 665 future = super.buildPythonPackage {
513 666 name = "future-0.14.3";
@@ -518,6 +671,9 b''
518 671 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
519 672 md5 = "e94079b0bd1fc054929e8769fc0f6083";
520 673 };
674 meta = {
675 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
676 };
521 677 };
522 678 futures = super.buildPythonPackage {
523 679 name = "futures-3.0.2";
@@ -528,6 +684,9 b''
528 684 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
529 685 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
530 686 };
687 meta = {
688 license = [ pkgs.lib.licenses.bsdOriginal ];
689 };
531 690 };
532 691 gnureadline = super.buildPythonPackage {
533 692 name = "gnureadline-6.3.3";
@@ -538,9 +697,12 b''
538 697 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
539 698 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
540 699 };
700 meta = {
701 license = [ pkgs.lib.licenses.gpl1 ];
702 };
541 703 };
542 704 gprof2dot = super.buildPythonPackage {
543 name = "gprof2dot-2015.12.01";
705 name = "gprof2dot-2015.12.1";
544 706 buildInputs = with self; [];
545 707 doCheck = false;
546 708 propagatedBuildInputs = with self; [];
@@ -548,6 +710,9 b''
548 710 url = "https://pypi.python.org/packages/b9/34/7bf93c1952d40fa5c95ad963f4d8344b61ef58558632402eca18e6c14127/gprof2dot-2015.12.1.tar.gz";
549 711 md5 = "e23bf4e2f94db032750c193384b4165b";
550 712 };
713 meta = {
714 license = [ { fullName = "LGPL"; } ];
715 };
551 716 };
552 717 greenlet = super.buildPythonPackage {
553 718 name = "greenlet-0.4.9";
@@ -558,6 +723,9 b''
558 723 url = "https://pypi.python.org/packages/4e/3d/9d421539b74e33608b245092870156b2e171fb49f2b51390aa4641eecb4a/greenlet-0.4.9.zip";
559 724 md5 = "c6659cdb2a5e591723e629d2eef22e82";
560 725 };
726 meta = {
727 license = [ pkgs.lib.licenses.mit ];
728 };
561 729 };
562 730 gunicorn = super.buildPythonPackage {
563 731 name = "gunicorn-19.6.0";
@@ -568,6 +736,9 b''
568 736 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
569 737 md5 = "338e5e8a83ea0f0625f768dba4597530";
570 738 };
739 meta = {
740 license = [ pkgs.lib.licenses.mit ];
741 };
571 742 };
572 743 infrae.cache = super.buildPythonPackage {
573 744 name = "infrae.cache-1.0.1";
@@ -578,15 +749,21 b''
578 749 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
579 750 md5 = "b09076a766747e6ed2a755cc62088e32";
580 751 };
752 meta = {
753 license = [ pkgs.lib.licenses.zpt21 ];
754 };
581 755 };
582 756 invoke = super.buildPythonPackage {
583 name = "invoke-0.11.1";
757 name = "invoke-0.13.0";
584 758 buildInputs = with self; [];
585 759 doCheck = false;
586 760 propagatedBuildInputs = with self; [];
587 761 src = fetchurl {
588 url = "https://pypi.python.org/packages/d3/bb/36a5558ea19882073def7b0edeef4a0e6282056fed96506dd10b1d532bd4/invoke-0.11.1.tar.gz";
589 md5 = "3d4ecbe26779ceef1046ecf702c9c4a8";
762 url = "https://pypi.python.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
763 md5 = "c0d1ed4bfb34eaab551662d8cfee6540";
764 };
765 meta = {
766 license = [ pkgs.lib.licenses.bsdOriginal ];
590 767 };
591 768 };
592 769 ipdb = super.buildPythonPackage {
@@ -598,6 +775,9 b''
598 775 url = "https://pypi.python.org/packages/f0/25/d7dd430ced6cd8dc242a933c8682b5dbf32eb4011d82f87e34209e5ec845/ipdb-0.8.zip";
599 776 md5 = "96dca0712efa01aa5eaf6b22071dd3ed";
600 777 };
778 meta = {
779 license = [ pkgs.lib.licenses.gpl1 ];
780 };
601 781 };
602 782 ipython = super.buildPythonPackage {
603 783 name = "ipython-3.1.0";
@@ -608,6 +788,9 b''
608 788 url = "https://pypi.python.org/packages/06/91/120c0835254c120af89f066afaabf81289bc2726c1fc3ca0555df6882f58/ipython-3.1.0.tar.gz";
609 789 md5 = "a749d90c16068687b0ec45a27e72ef8f";
610 790 };
791 meta = {
792 license = [ pkgs.lib.licenses.bsdOriginal ];
793 };
611 794 };
612 795 iso8601 = super.buildPythonPackage {
613 796 name = "iso8601-0.1.11";
@@ -618,6 +801,9 b''
618 801 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
619 802 md5 = "b06d11cd14a64096f907086044f0fe38";
620 803 };
804 meta = {
805 license = [ pkgs.lib.licenses.mit ];
806 };
621 807 };
622 808 itsdangerous = super.buildPythonPackage {
623 809 name = "itsdangerous-0.24";
@@ -628,6 +814,9 b''
628 814 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
629 815 md5 = "a3d55aa79369aef5345c036a8a26307f";
630 816 };
817 meta = {
818 license = [ pkgs.lib.licenses.bsdOriginal ];
819 };
631 820 };
632 821 kombu = super.buildPythonPackage {
633 822 name = "kombu-1.5.1";
@@ -638,6 +827,9 b''
638 827 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
639 828 md5 = "50662f3c7e9395b3d0721fb75d100b63";
640 829 };
830 meta = {
831 license = [ pkgs.lib.licenses.bsdOriginal ];
832 };
641 833 };
642 834 lxml = super.buildPythonPackage {
643 835 name = "lxml-3.4.4";
@@ -648,6 +840,9 b''
648 840 url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz";
649 841 md5 = "a9a65972afc173ec7a39c585f4eea69c";
650 842 };
843 meta = {
844 license = [ pkgs.lib.licenses.bsdOriginal ];
845 };
651 846 };
652 847 mccabe = super.buildPythonPackage {
653 848 name = "mccabe-0.3";
@@ -658,6 +853,9 b''
658 853 url = "https://pypi.python.org/packages/c9/2e/75231479e11a906b64ac43bad9d0bb534d00080b18bdca8db9da46e1faf7/mccabe-0.3.tar.gz";
659 854 md5 = "81640948ff226f8c12b3277059489157";
660 855 };
856 meta = {
857 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
858 };
661 859 };
662 860 meld3 = super.buildPythonPackage {
663 861 name = "meld3-1.0.2";
@@ -668,6 +866,9 b''
668 866 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
669 867 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
670 868 };
869 meta = {
870 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
871 };
671 872 };
672 873 mock = super.buildPythonPackage {
673 874 name = "mock-1.0.1";
@@ -678,6 +879,9 b''
678 879 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
679 880 md5 = "869f08d003c289a97c1a6610faf5e913";
680 881 };
882 meta = {
883 license = [ pkgs.lib.licenses.bsdOriginal ];
884 };
681 885 };
682 886 msgpack-python = super.buildPythonPackage {
683 887 name = "msgpack-python-0.4.6";
@@ -688,6 +892,9 b''
688 892 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
689 893 md5 = "8b317669314cf1bc881716cccdaccb30";
690 894 };
895 meta = {
896 license = [ pkgs.lib.licenses.asl20 ];
897 };
691 898 };
692 899 nose = super.buildPythonPackage {
693 900 name = "nose-1.3.6";
@@ -698,6 +905,9 b''
698 905 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
699 906 md5 = "0ca546d81ca8309080fc80cb389e7a16";
700 907 };
908 meta = {
909 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "GNU LGPL"; } ];
910 };
701 911 };
702 912 objgraph = super.buildPythonPackage {
703 913 name = "objgraph-2.0.0";
@@ -708,6 +918,9 b''
708 918 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
709 919 md5 = "25b0d5e5adc74aa63ead15699614159c";
710 920 };
921 meta = {
922 license = [ pkgs.lib.licenses.mit ];
923 };
711 924 };
712 925 packaging = super.buildPythonPackage {
713 926 name = "packaging-15.2";
@@ -718,6 +931,9 b''
718 931 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
719 932 md5 = "c16093476f6ced42128bf610e5db3784";
720 933 };
934 meta = {
935 license = [ pkgs.lib.licenses.asl20 ];
936 };
721 937 };
722 938 paramiko = super.buildPythonPackage {
723 939 name = "paramiko-1.15.1";
@@ -728,6 +944,9 b''
728 944 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
729 945 md5 = "48c274c3f9b1282932567b21f6acf3b5";
730 946 };
947 meta = {
948 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
949 };
731 950 };
732 951 pep8 = super.buildPythonPackage {
733 952 name = "pep8-1.5.7";
@@ -738,6 +957,9 b''
738 957 url = "https://pypi.python.org/packages/8b/de/259f5e735897ada1683489dd514b2a1c91aaa74e5e6b68f80acf128a6368/pep8-1.5.7.tar.gz";
739 958 md5 = "f6adbdd69365ecca20513c709f9b7c93";
740 959 };
960 meta = {
961 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
962 };
741 963 };
742 964 psutil = super.buildPythonPackage {
743 965 name = "psutil-2.2.1";
@@ -748,6 +970,9 b''
748 970 url = "https://pypi.python.org/packages/df/47/ee54ef14dd40f8ce831a7581001a5096494dc99fe71586260ca6b531fe86/psutil-2.2.1.tar.gz";
749 971 md5 = "1a2b58cd9e3a53528bb6148f0c4d5244";
750 972 };
973 meta = {
974 license = [ pkgs.lib.licenses.bsdOriginal ];
975 };
751 976 };
752 977 psycopg2 = super.buildPythonPackage {
753 978 name = "psycopg2-2.6";
@@ -758,6 +983,9 b''
758 983 url = "https://pypi.python.org/packages/dd/c7/9016ff8ff69da269b1848276eebfb264af5badf6b38caad805426771f04d/psycopg2-2.6.tar.gz";
759 984 md5 = "fbbb039a8765d561a1c04969bbae7c74";
760 985 };
986 meta = {
987 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
988 };
761 989 };
762 990 py = super.buildPythonPackage {
763 991 name = "py-1.4.29";
@@ -768,6 +996,9 b''
768 996 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
769 997 md5 = "c28e0accba523a29b35a48bb703fb96c";
770 998 };
999 meta = {
1000 license = [ pkgs.lib.licenses.mit ];
1001 };
771 1002 };
772 1003 py-bcrypt = super.buildPythonPackage {
773 1004 name = "py-bcrypt-0.4";
@@ -778,6 +1009,9 b''
778 1009 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
779 1010 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
780 1011 };
1012 meta = {
1013 license = [ pkgs.lib.licenses.bsdOriginal ];
1014 };
781 1015 };
782 1016 pycrypto = super.buildPythonPackage {
783 1017 name = "pycrypto-2.6.1";
@@ -788,6 +1022,9 b''
788 1022 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
789 1023 md5 = "55a61a054aa66812daf5161a0d5d7eda";
790 1024 };
1025 meta = {
1026 license = [ pkgs.lib.licenses.publicDomain ];
1027 };
791 1028 };
792 1029 pycurl = super.buildPythonPackage {
793 1030 name = "pycurl-7.19.5";
@@ -798,6 +1035,9 b''
798 1035 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
799 1036 md5 = "47b4eac84118e2606658122104e62072";
800 1037 };
1038 meta = {
1039 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1040 };
801 1041 };
802 1042 pyflakes = super.buildPythonPackage {
803 1043 name = "pyflakes-0.8.1";
@@ -808,6 +1048,9 b''
808 1048 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
809 1049 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
810 1050 };
1051 meta = {
1052 license = [ pkgs.lib.licenses.mit ];
1053 };
811 1054 };
812 1055 pyparsing = super.buildPythonPackage {
813 1056 name = "pyparsing-1.5.7";
@@ -818,6 +1061,9 b''
818 1061 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
819 1062 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
820 1063 };
1064 meta = {
1065 license = [ pkgs.lib.licenses.mit ];
1066 };
821 1067 };
822 1068 pyramid = super.buildPythonPackage {
823 1069 name = "pyramid-1.6.1";
@@ -828,6 +1074,9 b''
828 1074 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
829 1075 md5 = "b18688ff3cc33efdbb098a35b45dd122";
830 1076 };
1077 meta = {
1078 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1079 };
831 1080 };
832 1081 pyramid-beaker = super.buildPythonPackage {
833 1082 name = "pyramid-beaker-0.8";
@@ -838,6 +1087,9 b''
838 1087 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
839 1088 md5 = "22f14be31b06549f80890e2c63a93834";
840 1089 };
1090 meta = {
1091 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1092 };
841 1093 };
842 1094 pyramid-debugtoolbar = super.buildPythonPackage {
843 1095 name = "pyramid-debugtoolbar-2.4.2";
@@ -848,6 +1100,9 b''
848 1100 url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz";
849 1101 md5 = "073ea67086cc4bd5decc3a000853642d";
850 1102 };
1103 meta = {
1104 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1105 };
851 1106 };
852 1107 pyramid-jinja2 = super.buildPythonPackage {
853 1108 name = "pyramid-jinja2-2.5";
@@ -858,6 +1113,9 b''
858 1113 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
859 1114 md5 = "07cb6547204ac5e6f0b22a954ccee928";
860 1115 };
1116 meta = {
1117 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1118 };
861 1119 };
862 1120 pyramid-mako = super.buildPythonPackage {
863 1121 name = "pyramid-mako-1.0.2";
@@ -868,6 +1126,9 b''
868 1126 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
869 1127 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
870 1128 };
1129 meta = {
1130 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1131 };
871 1132 };
872 1133 pysqlite = super.buildPythonPackage {
873 1134 name = "pysqlite-2.6.3";
@@ -878,6 +1139,9 b''
878 1139 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
879 1140 md5 = "7ff1cedee74646b50117acff87aa1cfa";
880 1141 };
1142 meta = {
1143 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1144 };
881 1145 };
882 1146 pytest = super.buildPythonPackage {
883 1147 name = "pytest-2.8.5";
@@ -888,6 +1152,9 b''
888 1152 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
889 1153 md5 = "8493b06f700862f1294298d6c1b715a9";
890 1154 };
1155 meta = {
1156 license = [ pkgs.lib.licenses.mit ];
1157 };
891 1158 };
892 1159 pytest-catchlog = super.buildPythonPackage {
893 1160 name = "pytest-catchlog-1.2.2";
@@ -898,6 +1165,9 b''
898 1165 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
899 1166 md5 = "09d890c54c7456c818102b7ff8c182c8";
900 1167 };
1168 meta = {
1169 license = [ pkgs.lib.licenses.mit ];
1170 };
901 1171 };
902 1172 pytest-cov = super.buildPythonPackage {
903 1173 name = "pytest-cov-1.8.1";
@@ -908,6 +1178,9 b''
908 1178 url = "https://pypi.python.org/packages/11/4b/b04646e97f1721878eb21e9f779102d84dd044d324382263b1770a3e4838/pytest-cov-1.8.1.tar.gz";
909 1179 md5 = "76c778afa2494088270348be42d759fc";
910 1180 };
1181 meta = {
1182 license = [ pkgs.lib.licenses.mit ];
1183 };
911 1184 };
912 1185 pytest-profiling = super.buildPythonPackage {
913 1186 name = "pytest-profiling-1.0.1";
@@ -918,6 +1191,9 b''
918 1191 url = "https://pypi.python.org/packages/d8/67/8ffab73406e22870e07fa4dc8dce1d7689b26dba8efd00161c9b6fc01ec0/pytest-profiling-1.0.1.tar.gz";
919 1192 md5 = "354404eb5b3fd4dc5eb7fffbb3d9b68b";
920 1193 };
1194 meta = {
1195 license = [ pkgs.lib.licenses.mit ];
1196 };
921 1197 };
922 1198 pytest-runner = super.buildPythonPackage {
923 1199 name = "pytest-runner-2.7.1";
@@ -928,6 +1204,9 b''
928 1204 url = "https://pypi.python.org/packages/99/6b/c4ff4418d3424d4475b7af60724fd4a5cdd91ed8e489dc9443281f0052bc/pytest-runner-2.7.1.tar.gz";
929 1205 md5 = "e56f0bc8d79a6bd91772b44ef4215c7e";
930 1206 };
1207 meta = {
1208 license = [ pkgs.lib.licenses.mit ];
1209 };
931 1210 };
932 1211 pytest-timeout = super.buildPythonPackage {
933 1212 name = "pytest-timeout-0.4";
@@ -938,6 +1217,9 b''
938 1217 url = "https://pypi.python.org/packages/24/48/5f6bd4b8026a26e1dd427243d560a29a0f1b24a5c7cffca4bf049a7bb65b/pytest-timeout-0.4.tar.gz";
939 1218 md5 = "03b28aff69cbbfb959ed35ade5fde262";
940 1219 };
1220 meta = {
1221 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1222 };
941 1223 };
942 1224 python-dateutil = super.buildPythonPackage {
943 1225 name = "python-dateutil-1.5";
@@ -948,6 +1230,9 b''
948 1230 url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz";
949 1231 md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5";
950 1232 };
1233 meta = {
1234 license = [ pkgs.lib.licenses.psfl ];
1235 };
951 1236 };
952 1237 python-editor = super.buildPythonPackage {
953 1238 name = "python-editor-1.0.1";
@@ -958,6 +1243,9 b''
958 1243 url = "https://pypi.python.org/packages/2b/c0/df7b87d5cf016f82eab3b05cd35f53287c1178ad8c42bfb6fa61b89b22f6/python-editor-1.0.1.tar.gz";
959 1244 md5 = "e1fa63535b40e022fa4fd646fd8b511a";
960 1245 };
1246 meta = {
1247 license = [ pkgs.lib.licenses.asl20 ];
1248 };
961 1249 };
962 1250 python-ldap = super.buildPythonPackage {
963 1251 name = "python-ldap-2.4.19";
@@ -968,6 +1256,9 b''
968 1256 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
969 1257 md5 = "b941bf31d09739492aa19ef679e94ae3";
970 1258 };
1259 meta = {
1260 license = [ pkgs.lib.licenses.psfl ];
1261 };
971 1262 };
972 1263 python-memcached = super.buildPythonPackage {
973 1264 name = "python-memcached-1.57";
@@ -978,6 +1269,9 b''
978 1269 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
979 1270 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
980 1271 };
1272 meta = {
1273 license = [ pkgs.lib.licenses.psfl ];
1274 };
981 1275 };
982 1276 python-pam = super.buildPythonPackage {
983 1277 name = "python-pam-1.8.2";
@@ -988,6 +1282,9 b''
988 1282 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
989 1283 md5 = "db71b6b999246fb05d78ecfbe166629d";
990 1284 };
1285 meta = {
1286 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1287 };
991 1288 };
992 1289 pytz = super.buildPythonPackage {
993 1290 name = "pytz-2015.4";
@@ -998,6 +1295,9 b''
998 1295 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
999 1296 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1000 1297 };
1298 meta = {
1299 license = [ pkgs.lib.licenses.mit ];
1300 };
1001 1301 };
1002 1302 pyzmq = super.buildPythonPackage {
1003 1303 name = "pyzmq-14.6.0";
@@ -1008,6 +1308,9 b''
1008 1308 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1009 1309 md5 = "395b5de95a931afa5b14c9349a5b8024";
1010 1310 };
1311 meta = {
1312 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1313 };
1011 1314 };
1012 1315 recaptcha-client = super.buildPythonPackage {
1013 1316 name = "recaptcha-client-1.0.6";
@@ -1018,6 +1321,9 b''
1018 1321 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1019 1322 md5 = "74228180f7e1fb76c4d7089160b0d919";
1020 1323 };
1324 meta = {
1325 license = [ { fullName = "MIT/X11"; } ];
1326 };
1021 1327 };
1022 1328 repoze.lru = super.buildPythonPackage {
1023 1329 name = "repoze.lru-0.6";
@@ -1028,6 +1334,9 b''
1028 1334 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1029 1335 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1030 1336 };
1337 meta = {
1338 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1339 };
1031 1340 };
1032 1341 requests = super.buildPythonPackage {
1033 1342 name = "requests-2.9.1";
@@ -1038,13 +1347,19 b''
1038 1347 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1039 1348 md5 = "0b7f480d19012ec52bab78292efd976d";
1040 1349 };
1350 meta = {
1351 license = [ pkgs.lib.licenses.asl20 ];
1352 };
1041 1353 };
1042 1354 rhodecode-enterprise-ce = super.buildPythonPackage {
1043 name = "rhodecode-enterprise-ce-4.1.2";
1355 name = "rhodecode-enterprise-ce-4.2.0";
1044 1356 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1045 1357 doCheck = true;
1046 1358 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery colander decorator docutils gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 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 waitress zope.cachedescriptors psutil py-bcrypt];
1047 1359 src = ./.;
1360 meta = {
1361 license = [ { fullName = "AGPLv3, and Commercial License"; } ];
1362 };
1048 1363 };
1049 1364 rhodecode-tools = super.buildPythonPackage {
1050 1365 name = "rhodecode-tools-0.8.3";
@@ -1055,6 +1370,9 b''
1055 1370 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.8.3.zip";
1056 1371 md5 = "9acdfd71b8ddf4056057065f37ab9ccb";
1057 1372 };
1373 meta = {
1374 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1375 };
1058 1376 };
1059 1377 serpent = super.buildPythonPackage {
1060 1378 name = "serpent-1.12";
@@ -1065,6 +1383,9 b''
1065 1383 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
1066 1384 md5 = "05869ac7b062828b34f8f927f0457b65";
1067 1385 };
1386 meta = {
1387 license = [ pkgs.lib.licenses.mit ];
1388 };
1068 1389 };
1069 1390 setproctitle = super.buildPythonPackage {
1070 1391 name = "setproctitle-1.1.8";
@@ -1075,6 +1396,9 b''
1075 1396 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1076 1397 md5 = "728f4c8c6031bbe56083a48594027edd";
1077 1398 };
1399 meta = {
1400 license = [ pkgs.lib.licenses.bsdOriginal ];
1401 };
1078 1402 };
1079 1403 setuptools = super.buildPythonPackage {
1080 1404 name = "setuptools-20.8.1";
@@ -1085,6 +1409,9 b''
1085 1409 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
1086 1410 md5 = "fe58a5cac0df20bb83942b252a4b0543";
1087 1411 };
1412 meta = {
1413 license = [ pkgs.lib.licenses.mit ];
1414 };
1088 1415 };
1089 1416 setuptools-scm = super.buildPythonPackage {
1090 1417 name = "setuptools-scm-1.11.0";
@@ -1095,6 +1422,9 b''
1095 1422 url = "https://pypi.python.org/packages/cd/5f/e3a038292358058d83d764a47d09114aa5a8003ed4529518f9e580f1a94f/setuptools_scm-1.11.0.tar.gz";
1096 1423 md5 = "4c5c896ba52e134bbc3507bac6400087";
1097 1424 };
1425 meta = {
1426 license = [ pkgs.lib.licenses.mit ];
1427 };
1098 1428 };
1099 1429 simplejson = super.buildPythonPackage {
1100 1430 name = "simplejson-3.7.2";
@@ -1105,6 +1435,9 b''
1105 1435 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1106 1436 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1107 1437 };
1438 meta = {
1439 license = [ pkgs.lib.licenses.mit pkgs.lib.licenses.afl21 ];
1440 };
1108 1441 };
1109 1442 six = super.buildPythonPackage {
1110 1443 name = "six-1.9.0";
@@ -1115,6 +1448,9 b''
1115 1448 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1116 1449 md5 = "476881ef4012262dfc8adc645ee786c4";
1117 1450 };
1451 meta = {
1452 license = [ pkgs.lib.licenses.mit ];
1453 };
1118 1454 };
1119 1455 subprocess32 = super.buildPythonPackage {
1120 1456 name = "subprocess32-3.2.6";
@@ -1125,6 +1461,9 b''
1125 1461 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1126 1462 md5 = "754c5ab9f533e764f931136974b618f1";
1127 1463 };
1464 meta = {
1465 license = [ pkgs.lib.licenses.psfl ];
1466 };
1128 1467 };
1129 1468 supervisor = super.buildPythonPackage {
1130 1469 name = "supervisor-3.1.3";
@@ -1135,6 +1474,9 b''
1135 1474 url = "https://pypi.python.org/packages/a6/41/65ad5bd66230b173eb4d0b8810230f3a9c59ef52ae066e540b6b99895db7/supervisor-3.1.3.tar.gz";
1136 1475 md5 = "aad263c4fbc070de63dd354864d5e552";
1137 1476 };
1477 meta = {
1478 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1479 };
1138 1480 };
1139 1481 transifex-client = super.buildPythonPackage {
1140 1482 name = "transifex-client-0.10";
@@ -1145,6 +1487,9 b''
1145 1487 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1146 1488 md5 = "5549538d84b8eede6b254cd81ae024fa";
1147 1489 };
1490 meta = {
1491 license = [ pkgs.lib.licenses.gpl2 ];
1492 };
1148 1493 };
1149 1494 translationstring = super.buildPythonPackage {
1150 1495 name = "translationstring-1.3";
@@ -1155,6 +1500,9 b''
1155 1500 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1156 1501 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1157 1502 };
1503 meta = {
1504 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
1505 };
1158 1506 };
1159 1507 trollius = super.buildPythonPackage {
1160 1508 name = "trollius-1.0.4";
@@ -1165,6 +1513,9 b''
1165 1513 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1166 1514 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1167 1515 };
1516 meta = {
1517 license = [ pkgs.lib.licenses.asl20 ];
1518 };
1168 1519 };
1169 1520 uWSGI = super.buildPythonPackage {
1170 1521 name = "uWSGI-2.0.11.2";
@@ -1175,6 +1526,9 b''
1175 1526 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1176 1527 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1177 1528 };
1529 meta = {
1530 license = [ pkgs.lib.licenses.gpl2 ];
1531 };
1178 1532 };
1179 1533 urllib3 = super.buildPythonPackage {
1180 1534 name = "urllib3-1.16";
@@ -1185,6 +1539,9 b''
1185 1539 url = "https://pypi.python.org/packages/3b/f0/e763169124e3f5db0926bc3dbfcd580a105f9ca44cf5d8e6c7a803c9f6b5/urllib3-1.16.tar.gz";
1186 1540 md5 = "fcaab1c5385c57deeb7053d3d7d81d59";
1187 1541 };
1542 meta = {
1543 license = [ pkgs.lib.licenses.mit ];
1544 };
1188 1545 };
1189 1546 venusian = super.buildPythonPackage {
1190 1547 name = "venusian-1.0";
@@ -1195,6 +1552,9 b''
1195 1552 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1196 1553 md5 = "dccf2eafb7113759d60c86faf5538756";
1197 1554 };
1555 meta = {
1556 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1557 };
1198 1558 };
1199 1559 waitress = super.buildPythonPackage {
1200 1560 name = "waitress-0.8.9";
@@ -1205,6 +1565,9 b''
1205 1565 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
1206 1566 md5 = "da3f2e62b3676be5dd630703a68e2a04";
1207 1567 };
1568 meta = {
1569 license = [ pkgs.lib.licenses.zpt21 ];
1570 };
1208 1571 };
1209 1572 wsgiref = super.buildPythonPackage {
1210 1573 name = "wsgiref-0.1.2";
@@ -1215,6 +1578,9 b''
1215 1578 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1216 1579 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1217 1580 };
1581 meta = {
1582 license = [ { fullName = "PSF or ZPL"; } ];
1583 };
1218 1584 };
1219 1585 zope.cachedescriptors = super.buildPythonPackage {
1220 1586 name = "zope.cachedescriptors-4.0.0";
@@ -1225,6 +1591,9 b''
1225 1591 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1226 1592 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1227 1593 };
1594 meta = {
1595 license = [ pkgs.lib.licenses.zpt21 ];
1596 };
1228 1597 };
1229 1598 zope.deprecation = super.buildPythonPackage {
1230 1599 name = "zope.deprecation-4.1.2";
@@ -1235,6 +1604,9 b''
1235 1604 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1236 1605 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1237 1606 };
1607 meta = {
1608 license = [ pkgs.lib.licenses.zpt21 ];
1609 };
1238 1610 };
1239 1611 zope.event = super.buildPythonPackage {
1240 1612 name = "zope.event-4.0.3";
@@ -1245,6 +1617,9 b''
1245 1617 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1246 1618 md5 = "9a3780916332b18b8b85f522bcc3e249";
1247 1619 };
1620 meta = {
1621 license = [ pkgs.lib.licenses.zpt21 ];
1622 };
1248 1623 };
1249 1624 zope.interface = super.buildPythonPackage {
1250 1625 name = "zope.interface-4.1.3";
@@ -1255,6 +1630,9 b''
1255 1630 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1256 1631 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1257 1632 };
1633 meta = {
1634 license = [ pkgs.lib.licenses.zpt21 ];
1635 };
1258 1636 };
1259 1637
1260 1638 ### Test requirements
@@ -25,6 +25,7 b''
25 25 # vcsserver resides.
26 26
27 27 { pkgs ? import <nixpkgs> {}
28 , doCheck ? true
28 29 }:
29 30
30 31 let
@@ -69,7 +69,6 b' flake8==2.4.1'
69 69 future==0.14.3
70 70 futures==3.0.2
71 71 gprof2dot==2015.12.1
72 greenlet==0.4.9
73 72 gunicorn==19.6.0
74 73
75 74 # TODO: Needs subvertpy and blows up without Subversion headers,
@@ -78,7 +77,7 b' gunicorn==19.6.0'
78 77
79 78 gnureadline==6.3.3
80 79 infrae.cache==1.0.1
81 invoke==0.11.1
80 invoke==0.13.0
82 81 ipdb==0.8
83 82 ipython==3.1.0
84 83 iso8601==0.1.11
@@ -1,1 +1,1 b''
1 4.1.2 No newline at end of file
1 4.2.0 No newline at end of file
@@ -22,6 +22,7 b''
22 22 Authentication modules
23 23 """
24 24
25 import colander
25 26 import logging
26 27 import time
27 28 import traceback
@@ -97,16 +98,18 b' class RhodeCodeAuthPluginBase(object):'
97 98 # Mapping of python to DB settings model types. Plugins may override or
98 99 # extend this mapping.
99 100 _settings_type_map = {
100 str: 'str',
101 int: 'int',
102 unicode: 'unicode',
103 bool: 'bool',
104 list: 'list',
101 colander.String: 'unicode',
102 colander.Integer: 'int',
103 colander.Boolean: 'bool',
104 colander.List: 'list',
105 105 }
106 106
107 107 def __init__(self, plugin_id):
108 108 self._plugin_id = plugin_id
109 109
110 def __str__(self):
111 return self.get_id()
112
110 113 def _get_setting_full_name(self, name):
111 114 """
112 115 Return the full setting name used for storing values in the database.
@@ -116,16 +119,19 b' class RhodeCodeAuthPluginBase(object):'
116 119 # PluginSetting or to use the plugin id here.
117 120 return 'auth_{}_{}'.format(self.name, name)
118 121
119 def _get_setting_type(self, name, value):
122 def _get_setting_type(self, name):
123 """
124 Return the type of a setting. This type is defined by the SettingsModel
125 and determines how the setting is stored in DB. Optionally the suffix
126 `.encrypted` is appended to instruct SettingsModel to store it
127 encrypted.
120 128 """
121 Get the type as used by the SettingsModel accordingly to type of passed
122 value. Optionally the suffix `.encrypted` is appended to instruct
123 SettingsModel to store it encrypted.
124 """
125 type_ = self._settings_type_map.get(type(value), 'unicode')
129 schema_node = self.get_settings_schema().get(name)
130 db_type = self._settings_type_map.get(
131 type(schema_node.typ), 'unicode')
126 132 if name in self._settings_encrypted:
127 type_ = '{}.encrypted'.format(type_)
128 return type_
133 db_type = '{}.encrypted'.format(db_type)
134 return db_type
129 135
130 136 def is_enabled(self):
131 137 """
@@ -161,20 +167,20 b' class RhodeCodeAuthPluginBase(object):'
161 167 """
162 168 return AuthnPluginSettingsSchemaBase()
163 169
164 def get_setting_by_name(self, name):
170 def get_setting_by_name(self, name, default=None):
165 171 """
166 172 Returns a plugin setting by name.
167 173 """
168 174 full_name = self._get_setting_full_name(name)
169 175 db_setting = SettingsModel().get_setting_by_name(full_name)
170 return db_setting.app_settings_value if db_setting else None
176 return db_setting.app_settings_value if db_setting else default
171 177
172 178 def create_or_update_setting(self, name, value):
173 179 """
174 180 Create or update a setting for this plugin in the persistent storage.
175 181 """
176 182 full_name = self._get_setting_full_name(name)
177 type_ = self._get_setting_type(name, value)
183 type_ = self._get_setting_type(name)
178 184 db_setting = SettingsModel().create_or_update_setting(
179 185 full_name, value, type_)
180 186 return db_setting.app_settings_value
@@ -56,10 +56,12 b' class AuthnPluginViewBase(object):'
56 56 errors = errors or {}
57 57 schema = self.plugin.get_settings_schema()
58 58
59 # Get default values for the form.
59 # Compute default values for the form. Priority is:
60 # 1. Passed to this method 2. DB value 3. Schema default
60 61 for node in schema:
61 db_value = self.plugin.get_setting_by_name(node.name)
62 defaults.setdefault(node.name, db_value)
62 if node.name not in defaults:
63 defaults[node.name] = self.plugin.get_setting_by_name(
64 node.name, node.default)
63 65
64 66 template_context = {
65 67 'defaults': defaults,
@@ -78,15 +80,17 b' class AuthnPluginViewBase(object):'
78 80 View that validates and stores the plugin settings.
79 81 """
80 82 schema = self.plugin.get_settings_schema()
83 data = self.request.params
84
81 85 try:
82 valid_data = schema.deserialize(self.request.params)
86 valid_data = schema.deserialize(data)
83 87 except colander.Invalid, e:
84 88 # Display error message and display form again.
85 89 self.request.session.flash(
86 90 _('Errors exist when saving plugin settings. '
87 91 'Please check the form inputs.'),
88 92 queue='error')
89 defaults = schema.flatten(self.request.params)
93 defaults = {key: data[key] for key in data if key in schema}
90 94 return self.settings_get(errors=e.asdict(), defaults=defaults)
91 95
92 96 # Store validated data.
@@ -82,7 +82,7 b' def load_environment(global_conf, app_co'
82 82
83 83 config['routes.map'] = make_map(config)
84 84
85 if asbool(config['debug']):
85 if asbool(config.get('generate_js_files', 'false')):
86 86 jsroutes = config['routes.map'].jsroutes()
87 87 jsroutes_file_content = generate_jsroutes_content(jsroutes)
88 88 jsroutes_file_path = os.path.join(
@@ -29,7 +29,8 b' def generate_jsroutes_content(jsroutes):'
29 29 * DO NOT CHANGE THIS FILE MANUALLY *
30 30 * *
31 31 * *
32 * This file is automatically generated when the app starts up. *
32 * This file is automatically generated when the app starts up with *
33 * generate_js_files = true *
33 34 * *
34 35 * To add a route here pass jsroute=True to the route definition in the app *
35 36 * *
@@ -1,217 +1,256 b''
1 1 {
2 "cyrus-sasl-2.1.26": {
3 "cyrus": "http://cyrusimap.web.cmu.edu/mediawiki/index.php/Downloads#Licensing"
2 "nodejs-4.3.1": {
3 "MIT License": "http://spdx.org/licenses/MIT"
4 4 },
5 "openldap-2.4.41": {
6 "OLDAP-2.8": "http://spdx.org/licenses/OLDAP-2.8"
5 "postgresql-9.5.1": {
6 "PostgreSQL License": "http://spdx.org/licenses/PostgreSQL"
7 7 },
8 "openssl-1.0.1p": {
9 "OpenSSL": "http://spdx.org/licenses/OpenSSL"
10 },
11 "python-2.7.10": {
12 "Python-2.0": "http://spdx.org/licenses/Python-2.0"
8 "python-2.7.11": {
9 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
13 10 },
14 11 "python2.7-Babel-1.3": {
15 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
12 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
16 13 },
17 14 "python2.7-Beaker-1.7.0": {
18 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
15 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
19 16 },
20 17 "python2.7-FormEncode-1.2.4": {
21 "Python-2.0": "http://spdx.org/licenses/Python-2.0"
18 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
22 19 },
23 20 "python2.7-Mako-1.0.1": {
24 "MIT": "http://spdx.org/licenses/MIT"
21 "MIT License": "http://spdx.org/licenses/MIT"
25 22 },
26 23 "python2.7-Markdown-2.6.2": {
27 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
24 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
28 25 },
29 26 "python2.7-MarkupSafe-0.23": {
30 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
27 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
31 28 },
32 29 "python2.7-Paste-2.0.2": {
33 "MIT": "http://spdx.org/licenses/MIT"
30 "MIT License": "http://spdx.org/licenses/MIT"
34 31 },
35 32 "python2.7-PasteDeploy-1.5.2": {
36 "MIT": "http://spdx.org/licenses/MIT"
33 "MIT License": "http://spdx.org/licenses/MIT"
37 34 },
38 35 "python2.7-PasteScript-1.7.5": {
39 "MIT": "http://spdx.org/licenses/MIT"
36 "MIT License": "http://spdx.org/licenses/MIT"
40 37 },
41 38 "python2.7-Pygments-2.0.2": {
42 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
39 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
43 40 },
44 "python2.7-Pylons-1.0.2-patch1": {
45 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
41 "python2.7-Pylons-1.0.1-patch1": {
42 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
46 43 },
47 44 "python2.7-Pyro4-4.35": {
48 "MIT": "http://spdx.org/licenses/MIT"
45 "MIT License": "http://spdx.org/licenses/MIT"
49 46 },
50 47 "python2.7-Routes-1.13": {
51 "MIT": "http://spdx.org/licenses/MIT"
48 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
52 49 },
53 50 "python2.7-SQLAlchemy-0.9.9": {
54 "MIT": "http://spdx.org/licenses/MIT"
51 "MIT License": "http://spdx.org/licenses/MIT"
55 52 },
56 53 "python2.7-Tempita-0.5.2": {
57 "MIT": "http://spdx.org/licenses/MIT"
54 "MIT License": "http://spdx.org/licenses/MIT"
58 55 },
59 56 "python2.7-URLObject-2.4.0": {
60 "Unlicense": "http://spdx.org/licenses/Unlicense"
57 "The Unlicense": "http://unlicense.org/"
61 58 },
62 59 "python2.7-WebError-0.10.3": {
63 "MIT": "http://spdx.org/licenses/MIT"
60 "MIT License": "http://spdx.org/licenses/MIT"
64 61 },
65 "python2.7-WebHelpers-1.3-cust1": {
66 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
62 "python2.7-WebHelpers-1.3": {
63 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
67 64 },
68 65 "python2.7-WebHelpers2-2.0": {
69 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
66 "MIT License": "http://spdx.org/licenses/MIT"
70 67 },
71 68 "python2.7-WebOb-1.3.1": {
72 "MIT": "http://spdx.org/licenses/MIT"
69 "MIT License": "http://spdx.org/licenses/MIT"
73 70 },
74 "python2.7-Whoosh-2.7.0-patch1": {
75 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
71 "python2.7-Whoosh-2.7.0": {
72 "BSD 2-clause \"Simplified\" License": "http://spdx.org/licenses/BSD-2-Clause",
73 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
76 74 },
77 75 "python2.7-alembic-0.8.4": {
78 "MIT": "http://spdx.org/licenses/MIT"
76 "MIT License": "http://spdx.org/licenses/MIT"
79 77 },
80 78 "python2.7-amqplib-1.0.2": {
81 "LGPL-3.0": "http://spdx.org/licenses/LGPL-3.0"
79 "GNU Lesser General Public License v3.0 only": "http://spdx.org/licenses/LGPL-3.0"
82 80 },
83 81 "python2.7-anyjson-0.3.3": {
84 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
82 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
83 },
84 "python2.7-appenlight-client-0.6.14": {
85 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
85 86 },
86 "python2.7-appenlight_client-0.6.14": {
87 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
87 "python2.7-authomatic-0.1.0.post1": {
88 "MIT License": "http://spdx.org/licenses/MIT"
88 89 },
89 "python2.7-backport_ipaddress-0.1": {
90 "Python-2.0": "http://spdx.org/licenses/Python-2.0"
90 "python2.7-backport-ipaddress-0.1": {
91 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
91 92 },
92 93 "python2.7-celery-2.2.10": {
93 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
94 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
94 95 },
95 "python2.7-click-4.0": {
96 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
96 "python2.7-click-5.1": {
97 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
98 },
99 "python2.7-colander-1.2": {
100 "Repoze License": "http://www.repoze.org/LICENSE.txt"
97 101 },
98 102 "python2.7-configobj-5.0.6": {
99 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
103 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
100 104 },
101 105 "python2.7-cssselect-0.9.1": {
102 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
106 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
103 107 },
104 108 "python2.7-decorator-3.4.2": {
105 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
109 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
106 110 },
107 111 "python2.7-docutils-0.12": {
108 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
112 "BSD 2-clause \"Simplified\" License": "http://spdx.org/licenses/BSD-2-Clause"
113 },
114 "python2.7-elasticsearch-2.3.0": {
115 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
116 },
117 "python2.7-elasticsearch-dsl-2.0.0": {
118 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
109 119 },
110 120 "python2.7-future-0.14.3": {
111 "MIT": "http://spdx.org/licenses/MIT"
121 "MIT License": "http://spdx.org/licenses/MIT"
112 122 },
113 123 "python2.7-futures-3.0.2": {
114 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
124 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
125 },
126 "python2.7-gnureadline-6.3.3": {
127 "GNU General Public License v1.0 only": "http://spdx.org/licenses/GPL-1.0"
115 128 },
116 "python2.7-greenlet-0.4.7": {
117 "MIT": "http://spdx.org/licenses/MIT"
129 "python2.7-gunicorn-19.6.0": {
130 "MIT License": "http://spdx.org/licenses/MIT"
118 131 },
119 "python2.7-gunicorn-19.3.0": {
120 "MIT": "http://spdx.org/licenses/MIT"
132 "python2.7-infrae.cache-1.0.1": {
133 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
121 134 },
122 135 "python2.7-ipython-3.1.0": {
123 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
124 },
125 "python2.7-kombu-1.5.1-patch1": {
126 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
136 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
127 137 },
128 "python2.7-mccabe-0.3": {
129 "expat": "http://directory.fsf.org/wiki/License:Expat"
138 "python2.7-iso8601-0.1.11": {
139 "MIT License": "http://spdx.org/licenses/MIT"
130 140 },
131 "python2.7-meld3-1.0.2": {
132 "repoze": "http://repoze.org/license.html"
141 "python2.7-kombu-1.5.1": {
142 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
133 143 },
134 144 "python2.7-msgpack-python-0.4.6": {
135 "Apache-2.0": "http://spdx.org/licenses/Apache-2.0"
136 },
137 "python2.7-objgraph-2.0.0": {
138 "MIT": "http://spdx.org/licenses/MIT"
145 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
139 146 },
140 147 "python2.7-packaging-15.2": {
141 "Apache-2.0": "http://spdx.org/licenses/Apache-2.0"
148 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
142 149 },
143 150 "python2.7-psutil-2.2.1": {
144 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
151 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
145 152 },
146 153 "python2.7-psycopg2-2.6": {
147 "LGPL-3.0+": "http://spdx.org/licenses/LGPL-3.0+"
154 "GNU Lesser General Public License v3.0 or later": "http://spdx.org/licenses/LGPL-3.0+"
148 155 },
149 156 "python2.7-py-1.4.29": {
150 "MIT": "http://spdx.org/licenses/MIT"
157 "MIT License": "http://spdx.org/licenses/MIT"
151 158 },
152 159 "python2.7-py-bcrypt-0.4": {
153 "BSD-4-Clause": "http://spdx.org/licenses/BSD-4-Clause"
160 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
154 161 },
155 162 "python2.7-pycrypto-2.6.1": {
156 "publicDomain": null
163 "Public Domain": null
164 },
165 "python2.7-pycurl-7.19.5": {
166 "MIT License": "http://spdx.org/licenses/MIT"
157 167 },
158 168 "python2.7-pyparsing-1.5.7": {
159 "MIT": "http://spdx.org/licenses/MIT"
169 "MIT License": "http://spdx.org/licenses/MIT"
170 },
171 "python2.7-pyramid-1.6.1": {
172 "Repoze License": "http://www.repoze.org/LICENSE.txt"
173 },
174 "python2.7-pyramid-beaker-0.8": {
175 "Repoze License": "http://www.repoze.org/LICENSE.txt"
176 },
177 "python2.7-pyramid-debugtoolbar-2.4.2": {
178 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause",
179 "Repoze License": "http://www.repoze.org/LICENSE.txt"
180 },
181 "python2.7-pyramid-mako-1.0.2": {
182 "Repoze License": "http://www.repoze.org/LICENSE.txt"
160 183 },
161 184 "python2.7-pysqlite-2.6.3": {
162 "Libpng": "http://spdx.org/licenses/Libpng",
163 "Zlib": "http://spdx.org/licenses/Zlib"
185 "libpng License": "http://spdx.org/licenses/Libpng",
186 "zlib License": "http://spdx.org/licenses/Zlib"
164 187 },
165 188 "python2.7-pytest-2.8.5": {
166 "MIT": "http://spdx.org/licenses/MIT"
189 "MIT License": "http://spdx.org/licenses/MIT"
190 },
191 "python2.7-pytest-runner-2.7.1": {
192 "MIT License": "http://spdx.org/licenses/MIT"
167 193 },
168 194 "python2.7-python-dateutil-1.5": {
169 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
195 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
196 },
197 "python2.7-python-editor-1.0.1": {
198 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
170 199 },
171 200 "python2.7-python-ldap-2.4.19": {
172 "Python-2.0": "http://spdx.org/licenses/Python-2.0"
201 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
202 },
203 "python2.7-python-memcached-1.57": {
204 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
173 205 },
174 206 "python2.7-pytz-2015.4": {
175 "MIT": "http://spdx.org/licenses/MIT"
207 "MIT License": "http://spdx.org/licenses/MIT"
176 208 },
177 209 "python2.7-recaptcha-client-1.0.6": {
178 "MIT": "http://spdx.org/licenses/MIT"
210 "MIT License": "http://spdx.org/licenses/MIT"
179 211 },
180 212 "python2.7-repoze.lru-0.6": {
181 "repoze": "http://repoze.org/license.html"
213 "Repoze License": "http://www.repoze.org/LICENSE.txt"
182 214 },
183 "python2.7-requests-2.5.1": {
184 "APSL-2.0": "http://spdx.org/licenses/APSL-2.0"
215 "python2.7-requests-2.9.1": {
216 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
185 217 },
186 "python2.7-serpent-1.11": {
187 "MIT": "http://spdx.org/licenses/MIT"
218 "python2.7-serpent-1.12": {
219 "MIT License": "http://spdx.org/licenses/MIT"
188 220 },
189 "python2.7-setproctitle-1.1.8": {
190 "BSD-2-Clause": "http://spdx.org/licenses/BSD-2-Clause"
221 "python2.7-setuptools-19.4": {
222 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0",
223 "Zope Public License 2.0": "http://spdx.org/licenses/ZPL-2.0"
191 224 },
192 "python2.7-setuptools-18.0.1": {
193 "PSF": null,
194 "ZPL": null
225 "python2.7-setuptools-scm-1.11.0": {
226 "MIT License": "http://spdx.org/licenses/MIT"
195 227 },
196 228 "python2.7-simplejson-3.7.2": {
197 "MIT": "http://spdx.org/licenses/MIT"
229 "Academic Free License": "http://spdx.org/licenses/AFL-2.1",
230 "MIT License": "http://spdx.org/licenses/MIT"
198 231 },
199 232 "python2.7-six-1.9.0": {
200 "MIT": "http://spdx.org/licenses/MIT"
233 "MIT License": "http://spdx.org/licenses/MIT"
201 234 },
202 "python2.7-subprocess32-3.2.6": {
203 "Python-2.0": "http://spdx.org/licenses/Python-2.0"
235 "python2.7-translationstring-1.3": {
236 "Repoze License": "http://www.repoze.org/LICENSE.txt"
204 237 },
205 "python2.7-supervisor-3.1.3": {
206 "repoze": "http://repoze.org/license.html"
238 "python2.7-urllib3-1.16": {
239 "MIT License": "http://spdx.org/licenses/MIT"
207 240 },
208 "python2.7-trollius-1.0.4": {
209 "APSL-2.0": "http://spdx.org/licenses/APSL-2.0"
241 "python2.7-venusian-1.0": {
242 "Repoze License": "http://www.repoze.org/LICENSE.txt"
210 243 },
211 244 "python2.7-waitress-0.8.9": {
212 "ZPL-2.1": "http://spdx.org/licenses/ZPL-2.1"
245 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
213 246 },
214 247 "python2.7-zope.cachedescriptors-4.0.0": {
215 "ZPL-2.1": "http://spdx.org/licenses/ZPL-2.1"
248 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
249 },
250 "python2.7-zope.deprecation-4.1.2": {
251 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
252 },
253 "python2.7-zope.interface-4.1.3": {
254 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
216 255 }
217 }
256 } No newline at end of file
@@ -25,13 +25,15 b' import logging'
25 25
26 26 from paste.registry import RegistryManager
27 27 from paste.gzipper import make_gzip_middleware
28 from pylons.middleware import ErrorHandler, StatusCodeRedirect
29 28 from pylons.wsgiapp import PylonsApp
30 29 from pyramid.authorization import ACLAuthorizationPolicy
31 30 from pyramid.config import Configurator
32 31 from pyramid.static import static_view
33 32 from pyramid.settings import asbool, aslist
34 33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
35 import pyramid.httpexceptions as httpexceptions
36 from pyramid.renderers import render_to_response, render
35 37 from routes.middleware import RoutesMiddleware
36 38 import routes.util
37 39
@@ -87,38 +89,15 b' def make_app(global_conf, full_stack=Tru'
87 89 app = csrf.OriginChecker(app, expected_origin,
88 90 skip_urls=[routes.util.url_for('api')])
89 91
90 # Add RoutesMiddleware. Currently we have two instances in the stack. This
91 # is the lower one to make the StatusCodeRedirect middleware happy.
92 # TODO: johbo: This is not optimal, search for a better solution.
93 app = RoutesMiddleware(app, config['routes.map'])
94
95 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
96 if asbool(config['pdebug']):
97 from rhodecode.lib.profiler import ProfilingMiddleware
98 app = ProfilingMiddleware(app)
99
100 # Protect from VCS Server error related pages when server is not available
101 vcs_server_enabled = asbool(config.get('vcs.server.enable', 'true'))
102 if not vcs_server_enabled:
103 app = DisableVCSPagesWrapper(app)
104 92
105 93 if asbool(full_stack):
106 94
107 95 # Appenlight monitoring and error handler
108 96 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
109 97
110 # Handle Python exceptions
111 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
112
113 98 # we want our low level middleware to get to the request ASAP. We don't
114 99 # need any pylons stack middleware in them
115 100 app = VCSMiddleware(app, config, appenlight_client)
116 # Display error documents for 401, 403, 404 status codes (and
117 # 500 when debug is disabled)
118 if asbool(config['debug']):
119 app = StatusCodeRedirect(app)
120 else:
121 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
122 101
123 102 # Establish the Registry for this application
124 103 app = RegistryManager(app)
@@ -176,16 +155,69 b' def add_pylons_compat_data(registry, glo'
176 155 registry._pylons_compat_settings = settings
177 156
178 157
158 def webob_to_pyramid_http_response(webob_response):
159 ResponseClass = httpexceptions.status_map[webob_response.status_int]
160 pyramid_response = ResponseClass(webob_response.status)
161 pyramid_response.status = webob_response.status
162 pyramid_response.headers.update(webob_response.headers)
163 if pyramid_response.headers['content-type'] == 'text/html':
164 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
165 return pyramid_response
166
167
168 def error_handler(exception, request):
169 # TODO: dan: replace the old pylons error controller with this
170 from rhodecode.model.settings import SettingsModel
171 from rhodecode.lib.utils2 import AttributeDict
172
173 try:
174 rc_config = SettingsModel().get_all_settings()
175 except Exception:
176 log.exception('failed to fetch settings')
177 rc_config = {}
178
179 base_response = HTTPInternalServerError()
180 # prefer original exception for the response since it may have headers set
181 if isinstance(exception, HTTPError):
182 base_response = exception
183
184 c = AttributeDict()
185 c.error_message = base_response.status
186 c.error_explanation = base_response.explanation or str(base_response)
187 c.visual = AttributeDict()
188
189 c.visual.rhodecode_support_url = (
190 request.registry.settings.get('rhodecode_support_url') or
191 request.route_url('rhodecode_support')
192 )
193 c.redirect_time = 0
194 c.rhodecode_name = rc_config.get('rhodecode_title', '')
195 if not c.rhodecode_name:
196 c.rhodecode_name = 'Rhodecode'
197
198 response = render_to_response(
199 '/errors/error_document.html', {'c': c}, request=request,
200 response=base_response)
201
202 return response
203
204
179 205 def includeme(config):
180 206 settings = config.registry.settings
181 207
208 if asbool(settings.get('appenlight', 'false')):
209 config.include('appenlight_client.ext.pyramid_tween')
210
182 211 # Includes which are required. The application would fail without them.
183 212 config.include('pyramid_mako')
184 213 config.include('pyramid_beaker')
214 config.include('rhodecode.admin')
185 215 config.include('rhodecode.authentication')
186 216 config.include('rhodecode.login')
187 217 config.include('rhodecode.tweens')
188 218 config.include('rhodecode.api')
219 config.add_route(
220 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
189 221
190 222 # Set the authorization policy.
191 223 authz_policy = ACLAuthorizationPolicy()
@@ -204,16 +236,52 b' def includeme(config):'
204 236 for inc in includes:
205 237 config.include(inc)
206 238
239 pylons_app = make_app(
240 config.registry._pylons_compat_global_config,
241 **config.registry._pylons_compat_settings)
242 config.registry._pylons_compat_config = pylons_app.config
243
244 pylons_app_as_view = wsgiapp(pylons_app)
245
246 # Protect from VCS Server error related pages when server is not available
247 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
248 if not vcs_server_enabled:
249 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
250
251
252 def pylons_app_with_error_handler(context, request):
253 """
254 Handle exceptions from rc pylons app:
255
256 - old webob type exceptions get converted to pyramid exceptions
257 - pyramid exceptions are passed to the error handler view
258 """
259 try:
260 response = pylons_app_as_view(context, request)
261 if 400 <= response.status_int <= 599: # webob type error responses
262 return error_handler(
263 webob_to_pyramid_http_response(response), request)
264 except HTTPError as e: # pyramid type exceptions
265 return error_handler(e, request)
266 except Exception:
267 if settings.get('debugtoolbar.enabled', False):
268 raise
269 return error_handler(HTTPInternalServerError(), request)
270 return response
271
207 272 # This is the glue which allows us to migrate in chunks. By registering the
208 273 # pylons based application as the "Not Found" view in Pyramid, we will
209 274 # fallback to the old application each time the new one does not yet know
210 275 # how to handle a request.
211 pylons_app = make_app(
212 config.registry._pylons_compat_global_config,
213 **config.registry._pylons_compat_settings)
214 config.registry._pylons_compat_config = pylons_app.config
215 pylons_app_as_view = wsgiapp(pylons_app)
216 config.add_notfound_view(pylons_app_as_view)
276 config.add_notfound_view(pylons_app_with_error_handler)
277
278 if settings.get('debugtoolbar.enabled', False):
279 # if toolbar, then only http type exceptions get caught and rendered
280 ExcClass = HTTPError
281 else:
282 # if no toolbar, then any exception gets caught and rendered
283 ExcClass = Exception
284 config.add_view(error_handler, context=ExcClass)
217 285
218 286
219 287 def includeme_last(config):
@@ -253,13 +321,16 b' def wrap_app_in_wsgi_middlewares(pyramid'
253 321 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
254 322 pyramid_app = HttpsFixup(pyramid_app, settings)
255 323
256 # Add RoutesMiddleware. Currently we have two instances in the stack. This
257 # is the upper one to support the pylons compatibility tween during
324 # Add RoutesMiddleware to support the pylons compatibility tween during
258 325
259 326 # migration to pyramid.
260 327 pyramid_app = RoutesMiddleware(
261 328 pyramid_app, config.registry._pylons_compat_config['routes.map'])
262 329
330 if asbool(settings.get('appenlight', 'false')):
331 pyramid_app, _ = wrap_in_appenlight_if_enabled(
332 pyramid_app, config.registry._pylons_compat_config)
333
263 334 # TODO: johbo: Don't really see why we enable the gzip middleware when
264 335 # serving static files, might be something that should have its own setting
265 336 # as well?
@@ -166,10 +166,6 b' def make_map(config):'
166 166 def check_int(environ, match_dict):
167 167 return match_dict.get('id').isdigit()
168 168
169 # The ErrorController route (handles 404/500 error pages); it should
170 # likely stay at the top, ensuring it can always be resolved
171 rmap.connect('/error/{action}', controller='error')
172 rmap.connect('/error/{action}/{id}', controller='error')
173 169
174 170 #==========================================================================
175 171 # CUSTOM ROUTES HERE
@@ -509,10 +505,6 b' def make_map(config):'
509 505 m.connect('admin_settings_labs', '/settings/labs',
510 506 action='settings_labs', conditions={'method': ['GET']})
511 507
512 m.connect('admin_settings_open_source', '/settings/open_source',
513 action='settings_open_source',
514 conditions={'method': ['GET']})
515
516 508 # ADMIN MY ACCOUNT
517 509 with rmap.submapper(path_prefix=ADMIN_PREFIX,
518 510 controller='admin/my_account') as m:
@@ -68,21 +68,25 b' def configure_vcs(config):'
68 68
69 69
70 70 def initialize_database(config):
71 from rhodecode.lib.utils2 import engine_from_config
71 from rhodecode.lib.utils2 import engine_from_config, get_encryption_key
72 72 engine = engine_from_config(config, 'sqlalchemy.db1.')
73 init_model(engine, encryption_key=config['beaker.session.secret'])
73 init_model(engine, encryption_key=get_encryption_key(config))
74 74
75 75
76 76 def initialize_test_environment(settings, test_env=None):
77 77 if test_env is None:
78 78 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
79 79
80 from rhodecode.lib.utils import create_test_env, create_test_index
80 from rhodecode.lib.utils import (
81 create_test_directory, create_test_database, create_test_repositories,
82 create_test_index)
81 83 from rhodecode.tests import TESTS_TMP_PATH
82 84 # test repos
83 85 if test_env:
84 create_test_env(TESTS_TMP_PATH, settings)
85 create_test_index(TESTS_TMP_PATH, settings, True)
86 create_test_directory(TESTS_TMP_PATH)
87 create_test_database(TESTS_TMP_PATH, settings)
88 create_test_repositories(TESTS_TMP_PATH, settings)
89 create_test_index(TESTS_TMP_PATH, settings)
86 90
87 91
88 92 def get_vcs_server_protocol(config):
@@ -99,7 +99,7 b' class RepoGroupsController(BaseControlle'
99 99 if repo_group.user:
100 100 data.update({'user': repo_group.user.username})
101 101 else:
102 replacement_user = User.get_first_admin().username
102 replacement_user = User.get_first_super_admin().username
103 103 data.update({'user': replacement_user})
104 104
105 105 # fill repository group users
@@ -246,11 +246,10 b' class RepoGroupsController(BaseControlle'
246 246 repo_group=c.repo_group)
247 247
248 248 repo_group_form = RepoGroupForm(
249 edit=True,
250 old_data=c.repo_group.get_dict(),
249 edit=True, old_data=c.repo_group.get_dict(),
251 250 available_groups=c.repo_groups_choices,
252 can_create_in_root=can_create_in_root,
253 )()
251 can_create_in_root=can_create_in_root, allow_disabled=True)()
252
254 253 try:
255 254 form_result = repo_group_form.to_python(dict(request.POST))
256 255 gr_name = form_result['group_name']
@@ -248,9 +248,9 b' class ReposController(BaseRepoController'
248 248 task_id = request.GET.get('task_id')
249 249
250 250 if task_id and task_id not in ['None']:
251 from rhodecode import CELERY_ENABLED
251 import rhodecode
252 252 from celery.result import AsyncResult
253 if CELERY_ENABLED:
253 if rhodecode.CELERY_ENABLED:
254 254 task = AsyncResult(task_id)
255 255 if task.failed():
256 256 msg = self._log_creation_exception(task.result, c.repo)
@@ -307,9 +307,9 b' class ReposController(BaseRepoController'
307 307 'repo_group': repo.group.get_dict() if repo.group else {},
308 308 'repo_type': repo.repo_type,
309 309 }
310 _form = RepoForm(edit=True, old_data=old_data,
311 repo_groups=c.repo_groups_choices,
312 landing_revs=c.landing_revs_choices)()
310 _form = RepoForm(
311 edit=True, old_data=old_data, repo_groups=c.repo_groups_choices,
312 landing_revs=c.landing_revs_choices, allow_disabled=True)()
313 313
314 314 try:
315 315 form_result = _form.to_python(dict(request.POST))
@@ -37,6 +37,7 b' from pylons.i18n.translation import _, l'
37 37 from webob.exc import HTTPBadRequest
38 38
39 39 import rhodecode
40 from rhodecode.admin.navigation import navigation_list
40 41 from rhodecode.lib import auth
41 42 from rhodecode.lib import helpers as h
42 43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
@@ -47,7 +48,7 b' from rhodecode.lib.utils2 import ('
47 48 str2bool, safe_unicode, AttributeDict, safe_int)
48 49 from rhodecode.lib.compat import OrderedDict
49 50 from rhodecode.lib.ext_json import json
50 from rhodecode.lib.utils import jsonify, read_opensource_licenses
51 from rhodecode.lib.utils import jsonify
51 52
52 53 from rhodecode.model.db import RhodeCodeUi, Repository
53 54 from rhodecode.model.forms import ApplicationSettingsForm, \
@@ -60,8 +61,9 b' from rhodecode.model.meta import Session'
60 61 from rhodecode.model.settings import (
61 62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
62 63 SettingsModel)
64
63 65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
64 from rhodecode.model.user import UserModel
66
65 67
66 68 log = logging.getLogger(__name__)
67 69
@@ -78,7 +80,7 b' class SettingsController(BaseController)'
78 80 super(SettingsController, self).__before__()
79 81 c.labs_active = str2bool(
80 82 rhodecode.CONFIG.get('labs_settings_active', 'false'))
81 c.navlist = navigation.get_navlist(request)
83 c.navlist = navigation_list(request)
82 84
83 85 def _get_hg_ui_settings(self):
84 86 ret = RhodeCodeUi.query().all()
@@ -257,8 +259,8 b' class SettingsController(BaseController)'
257 259 Session().add(sett)
258 260
259 261 Session().commit()
262 SettingsModel().invalidate_settings_cache()
260 263 h.flash(_('Updated application settings'), category='success')
261
262 264 except Exception:
263 265 log.exception("Exception while updating application settings")
264 266 h.flash(
@@ -321,7 +323,7 b' class SettingsController(BaseController)'
321 323 Session().add(sett)
322 324
323 325 Session().commit()
324
326 SettingsModel().invalidate_settings_cache()
325 327 h.flash(_('Updated visualisation settings'), category='success')
326 328 except Exception:
327 329 log.exception("Exception updating visualization settings")
@@ -403,6 +405,7 b' class SettingsController(BaseController)'
403 405
404 406 Session().commit()
405 407
408 SettingsModel().invalidate_settings_cache()
406 409 h.flash(_('Updated issue tracker entries'), category='success')
407 410 return redirect(url('admin_settings_issuetracker'))
408 411
@@ -523,6 +526,7 b' class SettingsController(BaseController)'
523 526 def settings_system(self):
524 527 """GET /admin/settings/system: All items in the collection"""
525 528 # url('admin_settings_system')
529 snapshot = str2bool(request.GET.get('snapshot'))
526 530 c.active = 'system'
527 531
528 532 defaults = self._form_defaults()
@@ -557,6 +561,35 b' class SettingsController(BaseController)'
557 561 except TypeError:
558 562 c.system_memory = 'NOT AVAILABLE'
559 563
564 rhodecode_ini_safe = rhodecode.CONFIG.copy()
565 blacklist = [
566 'rhodecode_license_key',
567 'routes.map',
568 'pylons.h',
569 'pylons.app_globals',
570 'pylons.environ_config',
571 'sqlalchemy.db1.url',
572 ('app_conf', 'sqlalchemy.db1.url')
573 ]
574 for k in blacklist:
575 if isinstance(k, tuple):
576 section, key = k
577 if section in rhodecode_ini_safe:
578 rhodecode_ini_safe[section].pop(key, None)
579 else:
580 rhodecode_ini_safe.pop(k, None)
581
582 c.rhodecode_ini_safe = rhodecode_ini_safe
583
584 # TODO: marcink, figure out how to allow only selected users to do this
585 c.allowed_to_snapshot = False
586
587 if snapshot:
588 if c.allowed_to_snapshot:
589 return render('admin/settings/settings_system_snapshot.html')
590 else:
591 h.flash('You are not allowed to do this', category='warning')
592
560 593 return htmlfill.render(
561 594 render('admin/settings/settings.html'),
562 595 defaults=defaults,
@@ -708,6 +741,7 b' class SettingsController(BaseController)'
708 741 category='error')
709 742 else:
710 743 Session().commit()
744 SettingsModel().invalidate_settings_cache()
711 745 h.flash(_('Updated Labs settings'), category='success')
712 746 return redirect(url('admin_settings_labs'))
713 747
@@ -733,20 +767,6 b' class SettingsController(BaseController)'
733 767 encoding='UTF-8',
734 768 force_defaults=False)
735 769
736 @HasPermissionAllDecorator('hg.admin')
737 def settings_open_source(self):
738 # url('admin_settings_open_source')
739
740 c.active = 'open_source'
741 c.opensource_licenses = collections.OrderedDict(
742 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
743
744 return htmlfill.render(
745 render('admin/settings/settings.html'),
746 defaults=self._form_defaults(),
747 encoding='UTF-8',
748 force_defaults=False)
749
750 770 def _form_defaults(self):
751 771 defaults = SettingsModel().get_all_settings()
752 772 defaults.update(self._get_hg_ui_settings())
@@ -791,76 +811,3 b' LabSetting = collections.namedtuple('
791 811 help=lazy_ugettext('e.g. http://localhost:8080/')
792 812 ),
793 813 ]
794
795
796 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
797
798
799 class NavEntry(object):
800
801 def __init__(self, key, name, view_name, pyramid=False):
802 self.key = key
803 self.name = name
804 self.view_name = view_name
805 self.pyramid = pyramid
806
807 def generate_url(self, request):
808 if self.pyramid:
809 if hasattr(request, 'route_path'):
810 return request.route_path(self.view_name)
811 else:
812 # TODO: johbo: Remove this after migrating to pyramid.
813 # We need the pyramid request here to generate URLs to pyramid
814 # views from within pylons views.
815 from pyramid.threadlocal import get_current_request
816 pyramid_request = get_current_request()
817 return pyramid_request.route_path(self.view_name)
818 else:
819 return url(self.view_name)
820
821
822 class NavigationRegistry(object):
823
824 _base_entries = [
825 NavEntry('global', lazy_ugettext('Global'), 'admin_settings_global'),
826 NavEntry('vcs', lazy_ugettext('VCS'), 'admin_settings_vcs'),
827 NavEntry('visual', lazy_ugettext('Visual'), 'admin_settings_visual'),
828 NavEntry('mapping', lazy_ugettext('Remap and Rescan'),
829 'admin_settings_mapping'),
830 NavEntry('issuetracker', lazy_ugettext('Issue Tracker'),
831 'admin_settings_issuetracker'),
832 NavEntry('email', lazy_ugettext('Email'), 'admin_settings_email'),
833 NavEntry('hooks', lazy_ugettext('Hooks'), 'admin_settings_hooks'),
834 NavEntry('search', lazy_ugettext('Full Text Search'),
835 'admin_settings_search'),
836 NavEntry('system', lazy_ugettext('System Info'),
837 'admin_settings_system'),
838 NavEntry('open_source', lazy_ugettext('Open Source Licenses'),
839 'admin_settings_open_source'),
840 # TODO: marcink: we disable supervisor now until the supervisor stats
841 # page is fixed in the nix configuration
842 # NavEntry('supervisor', lazy_ugettext('Supervisor'),
843 # 'admin_settings_supervisor'),
844 ]
845
846 def __init__(self):
847 self._registered_entries = collections.OrderedDict([
848 (item.key, item) for item in self.__class__._base_entries
849 ])
850
851 # Add the labs entry when it's activated.
852 labs_active = str2bool(
853 rhodecode.CONFIG.get('labs_settings_active', 'false'))
854 if labs_active:
855 self.add_entry(
856 NavEntry('labs', lazy_ugettext('Labs'), 'admin_settings_labs'))
857
858 def add_entry(self, entry):
859 self._registered_entries[entry.key] = entry
860
861 def get_navlist(self, request):
862 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
863 for i in self._registered_entries.values()]
864 return navlist
865
866 navigation = NavigationRegistry()
@@ -88,7 +88,7 b' class UserGroupsController(BaseControlle'
88 88 if user_group.user:
89 89 data.update({'user': user_group.user.username})
90 90 else:
91 replacement_user = User.get_first_admin().username
91 replacement_user = User.get_first_super_admin().username
92 92 data.update({'user': replacement_user})
93 93 return data
94 94
@@ -209,9 +209,9 b' class UserGroupsController(BaseControlle'
209 209
210 210 available_members = [safe_unicode(x[0]) for x in c.available_members]
211 211
212 users_group_form = UserGroupForm(edit=True,
213 old_data=c.user_group.get_dict(),
214 available_members=available_members)()
212 users_group_form = UserGroupForm(
213 edit=True, old_data=c.user_group.get_dict(),
214 available_members=available_members, allow_disabled=True)()
215 215
216 216 try:
217 217 form_result = users_group_form.to_python(request.POST)
@@ -216,6 +216,8 b' class UsersController(BaseController):'
216 216 prefix_error=False,
217 217 encoding="UTF-8",
218 218 force_defaults=False)
219 except UserCreationError as e:
220 h.flash(e, 'error')
219 221 except Exception:
220 222 log.exception("Exception updating user")
221 223 h.flash(_('Error occurred during update of user %s')
@@ -401,7 +403,7 b' class UsersController(BaseController):'
401 403 c.active = 'advanced'
402 404 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
403 405 c.personal_repo_group = RepoGroup.get_by_group_name(user.username)
404 c.first_admin = User.get_first_admin()
406 c.first_admin = User.get_first_super_admin()
405 407 defaults = user.get_dict()
406 408
407 409 # Interim workaround if the user participated on any pull requests as a
@@ -37,7 +37,7 b' from rhodecode.lib.base import BaseContr'
37 37 from rhodecode.lib.index import searcher_from_config
38 38 from rhodecode.lib.ext_json import json
39 39 from rhodecode.lib.utils import jsonify
40 from rhodecode.lib.utils2 import safe_unicode
40 from rhodecode.lib.utils2 import safe_unicode, str2bool
41 41 from rhodecode.model.db import Repository, RepoGroup
42 42 from rhodecode.model.repo import RepoModel
43 43 from rhodecode.model.repo_group import RepoGroupModel
@@ -259,13 +259,16 b' class HomeController(BaseController):'
259 259 @jsonify
260 260 def user_autocomplete_data(self):
261 261 query = request.GET.get('query')
262 active = str2bool(request.GET.get('active') or True)
262 263
263 264 repo_model = RepoModel()
264 _users = repo_model.get_users(name_contains=query)
265 _users = repo_model.get_users(
266 name_contains=query, only_active=active)
265 267
266 268 if request.GET.get('user_groups'):
267 269 # extend with user groups
268 _user_groups = repo_model.get_user_groups(name_contains=query)
270 _user_groups = repo_model.get_user_groups(
271 name_contains=query, only_active=active)
269 272 _users = _users + _user_groups
270 273
271 274 return {'suggestions': _users}
@@ -274,4 +277,13 b' class HomeController(BaseController):'
274 277 @XHRRequired()
275 278 @jsonify
276 279 def user_group_autocomplete_data(self):
277 return {'suggestions': []}
280 query = request.GET.get('query')
281 active = str2bool(request.GET.get('active') or True)
282
283 repo_model = RepoModel()
284 _user_groups = repo_model.get_user_groups(
285 name_contains=query, only_active=active)
286 _user_groups = _user_groups
287
288 return {'suggestions': _user_groups}
289
@@ -17,7 +17,8 b''
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 from zope.interface import implementer
20 from rhodecode.interfaces import IUserRegistered
20 from rhodecode.interfaces import (
21 IUserRegistered, IUserPreCreate, IUserPreUpdate)
21 22
22 23
23 24 @implementer(IUserRegistered)
@@ -29,3 +30,24 b' class UserRegistered(object):'
29 30 def __init__(self, user, session):
30 31 self.user = user
31 32 self.session = session
33
34
35 @implementer(IUserPreCreate)
36 class UserPreCreate(object):
37 """
38 An instance of this class is emitted as an :term:`event` before a new user
39 object is created.
40 """
41 def __init__(self, user_data):
42 self.user_data = user_data
43
44
45 @implementer(IUserPreUpdate)
46 class UserPreUpdate(object):
47 """
48 An instance of this class is emitted as an :term:`event` before a user
49 object is updated.
50 """
51 def __init__(self, user, user_data):
52 self.user = user
53 self.user_data = user_data
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -26,3 +26,18 b' class IUserRegistered(Interface):'
26 26 """
27 27 user = Attribute('The user object.')
28 28 session = Attribute('The session while processing the register form post.')
29
30
31 class IUserPreCreate(Interface):
32 """
33 An event type that is emitted before a new user object is created.
34 """
35 user_data = Attribute('Data used to create the new user')
36
37
38 class IUserPreUpdate(Interface):
39 """
40 An event type that is emitted before a user object is updated.
41 """
42 user = Attribute('The not yet updated user object')
43 user_data = Attribute('Data used to update the user')
@@ -255,7 +255,7 b' class BasicAuth(AuthBasicAuthenticator):'
255 255
256 256
257 257 def attach_context_attributes(context):
258 rc_config = SettingsModel().get_all_settings()
258 rc_config = SettingsModel().get_all_settings(cache=True)
259 259
260 260 context.rhodecode_version = rhodecode.__version__
261 261 context.rhodecode_edition = config.get('rhodecode.edition')
@@ -425,7 +425,7 b' class BaseController(WSGIController):'
425 425 _route_name = '.'.join([environ['pylons.routes_dict']['controller'],
426 426 environ['pylons.routes_dict']['action']])
427 427
428 self.rc_config = SettingsModel().get_all_settings()
428 self.rc_config = SettingsModel().get_all_settings(cache=True)
429 429 self.ip_addr = get_ip_addr(environ)
430 430
431 431 # The rhodecode auth user is looked up and passed through the
@@ -87,6 +87,12 b' def get_cache_manager(region_name, cache'
87 87
88 88
89 89 def clear_cache_manager(cache_manager):
90 """
91 namespace = 'foobar'
92 cache_manager = get_cache_manager('repo_cache_long', namespace)
93 clear_cache_manager(cache_manager)
94 """
95
90 96 log.debug('Clearing all values for cache manager %s', cache_manager)
91 97 cache_manager.clear()
92 98
@@ -161,7 +167,7 b' class FreshRegionCache(ActiveRegionCache'
161 167 class InvalidationContext(object):
162 168 def __repr__(self):
163 169 return '<InvalidationContext:{}[{}]>'.format(
164 self.repo_name, self.cache_type)
170 safe_str(self.repo_name), safe_str(self.cache_type))
165 171
166 172 def __init__(self, compute_func, repo_name, cache_type,
167 173 raise_exception=False):
@@ -34,7 +34,6 b' from decorator import decorator'
34 34
35 35 from zope.cachedescriptors.property import Lazy as LazyProperty
36 36
37 from rhodecode import CELERY_ENABLED, CELERY_EAGER
38 37 from rhodecode.config import utils
39 38 from rhodecode.lib.utils2 import safe_str, md5_safe, aslist
40 39 from rhodecode.lib.pidlock import DaemonLock, LockHeld
@@ -54,8 +53,7 b' class ResultWrapper(object):'
54 53
55 54
56 55 def run_task(task, *args, **kwargs):
57 global CELERY_ENABLED
58 if CELERY_ENABLED:
56 if rhodecode.CELERY_ENABLED:
59 57 try:
60 58 t = task.apply_async(args=args, kwargs=kwargs)
61 59 log.info('running task %s:%s', t.task_id, task)
@@ -63,17 +61,17 b' def run_task(task, *args, **kwargs):'
63 61
64 62 except socket.error as e:
65 63 if isinstance(e, IOError) and e.errno == 111:
66 log.debug('Unable to connect to celeryd. Sync execution')
67 CELERY_ENABLED = False
64 log.error('Unable to connect to celeryd. Sync execution')
65 rhodecode.CELERY_ENABLED = False
68 66 else:
69 67 log.exception("Exception while connecting to celeryd.")
70 68 except KeyError as e:
71 log.debug('Unable to connect to celeryd. Sync execution')
69 log.error('Unable to connect to celeryd. Sync execution')
72 70 except Exception as e:
73 71 log.exception(
74 72 "Exception while trying to run task asynchronous. "
75 73 "Fallback to sync execution.")
76
74 else:
77 75 log.debug('executing task %s in sync mode', task)
78 76 return ResultWrapper(task(*args, **kwargs))
79 77
@@ -106,7 +104,7 b' def locked_task(func):'
106 104
107 105
108 106 def get_session():
109 if CELERY_ENABLED:
107 if rhodecode.CELERY_ENABLED:
110 108 utils.initialize_database(config)
111 109 sa = meta.Session()
112 110 return sa
@@ -118,7 +116,7 b' def dbsession(func):'
118 116 ret = func(*fargs, **fkwargs)
119 117 return ret
120 118 finally:
121 if CELERY_ENABLED and not CELERY_EAGER:
119 if rhodecode.CELERY_ENABLED and not rhodecode.CELERY_EAGER:
122 120 meta.Session.remove()
123 121
124 122 return decorator(__wrapper, func)
@@ -126,7 +124,7 b' def dbsession(func):'
126 124
127 125 def vcsconnection(func):
128 126 def __wrapper(func, *fargs, **fkwargs):
129 if CELERY_ENABLED and not CELERY_EAGER:
127 if rhodecode.CELERY_ENABLED and not rhodecode.CELERY_EAGER:
130 128 backends = config['vcs.backends'] = aslist(
131 129 config.get('vcs.backends', 'hg,git'), sep=',')
132 130 for alias in rhodecode.BACKENDS.keys():
@@ -30,7 +30,7 b' import logging'
30 30 from celery.task import task
31 31 from pylons import config
32 32
33 from rhodecode import CELERY_ENABLED
33 import rhodecode
34 34 from rhodecode.lib.celerylib import (
35 35 run_task, dbsession, __get_lockkey, LockHeld, DaemonLock,
36 36 get_session, vcsconnection)
@@ -45,7 +45,7 b' add_cache(config) # pragma: no cover'
45 45
46 46
47 47 def get_logger(cls):
48 if CELERY_ENABLED:
48 if rhodecode.CELERY_ENABLED:
49 49 try:
50 50 log = cls.get_logger()
51 51 except Exception:
@@ -23,31 +23,84 b''
23 23 Generic encryption library for RhodeCode
24 24 """
25 25
26 import hashlib
27 26 import base64
28 27
29 28 from Crypto.Cipher import AES
30 29 from Crypto import Random
30 from Crypto.Hash import HMAC, SHA256
31 31
32 32 from rhodecode.lib.utils2 import safe_str
33 33
34 34
35 class SignatureVerificationError(Exception):
36 pass
37
38
39 class InvalidDecryptedValue(str):
40
41 def __new__(cls, content):
42 """
43 This will generate something like this::
44 <InvalidDecryptedValue(QkWusFgLJXR6m42v...)>
45 And represent a safe indicator that encryption key is broken
46 """
47 content = '<{}({}...)>'.format(cls.__name__, content[:16])
48 return str.__new__(cls, content)
49
50
35 51 class AESCipher(object):
36 def __init__(self, key):
37 # create padding, trim to long enc key
52 def __init__(self, key, hmac=False, strict_verification=True):
38 53 if not key:
39 54 raise ValueError('passed key variable is empty')
55 self.strict_verification = strict_verification
40 56 self.block_size = 32
41 self.key = hashlib.sha256(safe_str(key)).digest()
57 self.hmac_size = 32
58 self.hmac = hmac
59
60 self.key = SHA256.new(safe_str(key)).digest()
61 self.hmac_key = SHA256.new(self.key).digest()
62
63 def verify_hmac_signature(self, raw_data):
64 org_hmac_signature = raw_data[-self.hmac_size:]
65 data_without_sig = raw_data[:-self.hmac_size]
66 recomputed_hmac = HMAC.new(
67 self.hmac_key, data_without_sig, digestmod=SHA256).digest()
68 return org_hmac_signature == recomputed_hmac
42 69
43 70 def encrypt(self, raw):
44 71 raw = self._pad(raw)
45 72 iv = Random.new().read(AES.block_size)
46 73 cipher = AES.new(self.key, AES.MODE_CBC, iv)
47 return base64.b64encode(iv + cipher.encrypt(raw))
74 enc_value = cipher.encrypt(raw)
75
76 hmac_signature = ''
77 if self.hmac:
78 # compute hmac+sha256 on iv + enc text, we use
79 # encrypt then mac method to create the signature
80 hmac_signature = HMAC.new(
81 self.hmac_key, iv + enc_value, digestmod=SHA256).digest()
82
83 return base64.b64encode(iv + enc_value + hmac_signature)
48 84
49 85 def decrypt(self, enc):
86 enc_org = enc
50 87 enc = base64.b64decode(enc)
88
89 if self.hmac and len(enc) > self.hmac_size:
90 if self.verify_hmac_signature(enc):
91 # cut off the HMAC verification digest
92 enc = enc[:-self.hmac_size]
93 else:
94 if self.strict_verification:
95 raise SignatureVerificationError(
96 "Encryption signature verification failed. "
97 "Please check your secret key, and/or encrypted value. "
98 "Secret key is stored as "
99 "`rhodecode.encrypted_values.secret` or "
100 "`beaker.session.secret` inside .ini file")
101
102 return InvalidDecryptedValue(enc_org)
103
51 104 iv = enc[:AES.block_size]
52 105 cipher = AES.new(self.key, AES.MODE_CBC, iv)
53 106 return self._unpad(cipher.decrypt(enc[AES.block_size:]))
@@ -438,15 +438,17 b' def get_matching_line_offsets(lines, ter'
438 438 :param max_lines: cut off for lines of interest
439 439 eg.
440 440
441 >>> get_matching_line_offsets('''
441 text = '''
442 442 words words words
443 443 words words words
444 444 some text some
445 445 words words words
446 446 words words words
447 447 text here what
448 ''', 'text', context=1)
448 '''
449 get_matching_line_offsets(text, 'text', context=1)
449 450 {3: [(5, 9)], 6: [(0, 4)]]
451
450 452 """
451 453 matching_lines = {}
452 454 phrases = [normalize_text_for_matching(phrase)
@@ -460,6 +462,7 b' text here what'
460 462
461 463 return matching_lines
462 464
465
463 466 def get_lexer_safe(mimetype=None, filepath=None):
464 467 """
465 468 Tries to return a relevant pygments lexer using mimetype/filepath name,
@@ -470,7 +473,7 b' def get_lexer_safe(mimetype=None, filepa'
470 473 if mimetype:
471 474 lexer = get_lexer_for_mimetype(mimetype)
472 475 if not lexer:
473 lexer = get_lexer_for_filename(path)
476 lexer = get_lexer_for_filename(filepath)
474 477 except pygments.util.ClassNotFound:
475 478 pass
476 479
@@ -675,11 +678,6 b' def _shorten_commit_id(commit_id):'
675 678 return commit_id[:def_len]
676 679
677 680
678 def get_repo_id_from_name(repo_name):
679 repo = get_by_repo_name(repo_name)
680 return repo.repo_id
681
682
683 681 def show_id(commit):
684 682 """
685 683 Configurable function that shows ID
@@ -744,6 +742,32 b' def is_svn_without_proxy(repository):'
744 742 return False
745 743
746 744
745 def discover_user(author):
746 """
747 Tries to discover RhodeCode User based on the autho string. Author string
748 is typically `FirstName LastName <email@address.com>`
749 """
750
751 # if author is already an instance use it for extraction
752 if isinstance(author, User):
753 return author
754
755 # Valid email in the attribute passed, see if they're in the system
756 _email = author_email(author)
757 if _email != '':
758 user = User.get_by_email(_email, case_insensitive=True, cache=True)
759 if user is not None:
760 return user
761
762 # Maybe it's a username, we try to extract it and fetch by username ?
763 _author = author_name(author)
764 user = User.get_by_username(_author, case_insensitive=True, cache=True)
765 if user is not None:
766 return user
767
768 return None
769
770
747 771 def email_or_none(author):
748 772 # extract email from the commit string
749 773 _email = author_email(author)
@@ -765,30 +789,13 b' def email_or_none(author):'
765 789 return None
766 790
767 791
768 def discover_user(author):
769 # if author is already an instance use it for extraction
770 if isinstance(author, User):
771 return author
772
773 # Valid email in the attribute passed, see if they're in the system
774 _email = email(author)
775 if _email != '':
776 user = User.get_by_email(_email, case_insensitive=True, cache=True)
777 if user is not None:
778 return user
779
780 # Maybe it's a username?
781 _author = author_name(author)
782 user = User.get_by_username(_author, case_insensitive=True,
783 cache=True)
784 if user is not None:
785 return user
786
787 return None
788
789
790 792 def link_to_user(author, length=0, **kwargs):
791 793 user = discover_user(author)
794 # user can be None, but if we have it already it means we can re-use it
795 # in the person() function, so we save 1 intensive-query
796 if user:
797 author = user
798
792 799 display_person = person(author, 'username_or_name_or_email')
793 800 if length:
794 801 display_person = shorter(display_person, length)
@@ -803,11 +810,9 b' def link_to_user(author, length=0, **kwa'
803 810
804 811
805 812 def person(author, show_attr="username_and_name"):
806 # attr to return from fetched user
807 person_getter = lambda usr: getattr(usr, show_attr)
808 813 user = discover_user(author)
809 814 if user:
810 return person_getter(user)
815 return getattr(user, show_attr)
811 816 else:
812 817 _author = author_name(author)
813 818 _email = email(author)
@@ -827,10 +832,10 b' def person_by_id(id_, show_attr="usernam'
827 832 return id_
828 833
829 834
830 def gravatar_with_user(author):
835 def gravatar_with_user(author, show_disabled=False):
831 836 from rhodecode.lib.utils import PartialRenderer
832 837 _render = PartialRenderer('base/base.html')
833 return _render('gravatar_with_user', author)
838 return _render('gravatar_with_user', author, show_disabled=show_disabled)
834 839
835 840
836 841 def desc_stylize(value):
@@ -1647,10 +1652,10 b' def process_patterns(text_string, repo_n'
1647 1652 if repo_name:
1648 1653 # Retrieving repo_name to avoid invalid repo_name to explode on
1649 1654 # IssueTrackerSettingsModel but still passing invalid name further down
1650 repo = Repository.get_by_repo_name(repo_name)
1655 repo = Repository.get_by_repo_name(repo_name, cache=True)
1651 1656
1652 1657 settings_model = IssueTrackerSettingsModel(repo=repo)
1653 active_entries = settings_model.get_settings()
1658 active_entries = settings_model.get_settings(cache=True)
1654 1659
1655 1660 newtext = text_string
1656 1661 for uid, entry in active_entries.items():
@@ -24,15 +24,21 b' Disable VCS pages when VCS Server is not'
24 24
25 25 import logging
26 26 import re
27
27 from pyramid.httpexceptions import HTTPBadGateway
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31
32 class VCSServerUnavailable(HTTPBadGateway):
33 """ HTTP Exception class for when VCS Server is unavailable """
34 code = 502
35 title = 'VCS Server Required'
36 explanation = 'A VCS Server is required for this action. There is currently no VCS Server configured.'
37
32 38 class DisableVCSPagesWrapper(object):
33 39 """
34 Wrapper to disable all pages that require VCS Server to be running,
35 avoiding that errors explode to the user.
40 Pyramid view wrapper to disable all pages that require VCS Server to be
41 running, avoiding that errors explode to the user.
36 42
37 43 This Wrapper should be enabled only in case VCS Server is not available
38 44 for the instance.
@@ -60,11 +66,11 b' class DisableVCSPagesWrapper(object):'
60 66 log.debug('accessing: `%s` with VCS Server disabled', path_info)
61 67 return False
62 68
63 def __init__(self, app):
64 self.application = app
69 def __init__(self, handler):
70 self.handler = handler
65 71
66 def __call__(self, environ, start_response):
67 if not self._check_vcs_requirement(environ['PATH_INFO']):
68 environ['PATH_INFO'] = '/error/vcs_unavailable'
72 def __call__(self, context, request):
73 if not self._check_vcs_requirement(request.path):
74 raise VCSServerUnavailable('VCS Server is not available')
69 75
70 return self.application(environ, start_response)
76 return self.handler(context, request)
@@ -33,14 +33,14 b' import tempfile'
33 33 import traceback
34 34 import tarfile
35 35 import warnings
36 from os.path import abspath
37 from os.path import dirname as dn, join as jn
36 from os.path import join as jn
38 37
39 38 import paste
40 39 import pkg_resources
41 40 from paste.script.command import Command, BadCommand
42 41 from webhelpers.text import collapse, remove_formatting, strip_tags
43 42 from mako import exceptions
43 from pyramid.threadlocal import get_current_registry
44 44
45 45 from rhodecode.lib.fakemod import create_module
46 46 from rhodecode.lib.vcs.backends.base import Config
@@ -52,8 +52,7 b' from rhodecode.model import meta'
52 52 from rhodecode.model.db import (
53 53 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
54 54 from rhodecode.model.meta import Session
55 from rhodecode.model.repo_group import RepoGroupModel
56 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
55
57 56
58 57 log = logging.getLogger(__name__)
59 58
@@ -384,6 +383,8 b' def config_data_from_db(clear_session=Tr'
384 383 Read the configuration data from the database and return configuration
385 384 tuples.
386 385 """
386 from rhodecode.model.settings import VcsSettingsModel
387
387 388 config = []
388 389
389 390 sa = meta.Session()
@@ -467,6 +468,7 b' def set_rhodecode_config(config):'
467 468
468 469 :param config:
469 470 """
471 from rhodecode.model.settings import SettingsModel
470 472 app_settings = SettingsModel().get_all_settings()
471 473
472 474 for k, v in app_settings.items():
@@ -481,6 +483,7 b' def map_groups(path):'
481 483
482 484 :param paths: full path to repository
483 485 """
486 from rhodecode.model.repo_group import RepoGroupModel
484 487 sa = meta.Session()
485 488 groups = path.split(Repository.NAME_SEP)
486 489 parent = None
@@ -489,7 +492,7 b' def map_groups(path):'
489 492 # last element is repo in nested groups structure
490 493 groups = groups[:-1]
491 494 rgm = RepoGroupModel(sa)
492 owner = User.get_first_admin()
495 owner = User.get_first_super_admin()
493 496 for lvl, group_name in enumerate(groups):
494 497 group_name = '/'.join(groups[:lvl] + [group_name])
495 498 group = RepoGroup.get_by_group_name(group_name)
@@ -525,9 +528,12 b' def repo2db_mapper(initial_repo_list, re'
525 528 """
526 529 from rhodecode.model.repo import RepoModel
527 530 from rhodecode.model.scm import ScmModel
531 from rhodecode.model.repo_group import RepoGroupModel
532 from rhodecode.model.settings import SettingsModel
533
528 534 sa = meta.Session()
529 535 repo_model = RepoModel()
530 user = User.get_first_admin()
536 user = User.get_first_super_admin()
531 537 added = []
532 538
533 539 # creation defaults
@@ -701,58 +707,56 b' def get_custom_lexer(extension):'
701 707 #==============================================================================
702 708 # TEST FUNCTIONS AND CREATORS
703 709 #==============================================================================
704 def create_test_index(repo_location, config, full_index):
710 def create_test_index(repo_location, config):
705 711 """
706 Makes default test index
707
708 :param config: test config
709 :param full_index:
710 # start test server:
711 rcserver --with-vcsserver test.ini
712 Makes default test index.
713 """
714 import rc_testdata
712 715
713 # build index and store it in /tmp/rc/index:
714 rhodecode-index --force --api-host=http://vps1.dev:5000 --api-key=xxx --engine-location=/tmp/rc/index
715
716 # package and move new packages
717 tar -zcvf vcs_search_index.tar.gz -C /tmp/rc index
718 mv vcs_search_index.tar.gz rhodecode/tests/fixtures/
719
720 """
721 cur_dir = dn(dn(abspath(__file__)))
722 with tarfile.open(jn(cur_dir, 'tests', 'fixtures',
723 'vcs_search_index.tar.gz')) as tar:
724 tar.extractall(os.path.dirname(config['search.location']))
716 rc_testdata.extract_search_index(
717 'vcs_search_index', os.path.dirname(config['search.location']))
725 718
726 719
727 def create_test_env(repos_test_path, config):
720 def create_test_directory(test_path):
721 """
722 Create test directory if it doesn't exist.
728 723 """
729 Makes a fresh database and
730 installs test repository into tmp dir
724 if not os.path.isdir(test_path):
725 log.debug('Creating testdir %s', test_path)
726 os.makedirs(test_path)
727
728
729 def create_test_database(test_path, config):
730 """
731 Makes a fresh database.
731 732 """
732 733 from rhodecode.lib.db_manage import DbManage
733 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO, TESTS_TMP_PATH
734 734
735 735 # PART ONE create db
736 736 dbconf = config['sqlalchemy.db1.url']
737 737 log.debug('making test db %s', dbconf)
738 738
739 # create test dir if it doesn't exist
740 if not os.path.isdir(repos_test_path):
741 log.debug('Creating testdir %s', repos_test_path)
742 os.makedirs(repos_test_path)
743
744 739 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
745 740 tests=True, cli_args={'force_ask': True})
746 741 dbmanage.create_tables(override=True)
747 742 dbmanage.set_db_version()
748 743 # for tests dynamically set new root paths based on generated content
749 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
744 dbmanage.create_settings(dbmanage.config_prompt(test_path))
750 745 dbmanage.create_default_user()
751 746 dbmanage.create_test_admin_and_users()
752 747 dbmanage.create_permissions()
753 748 dbmanage.populate_default_permissions()
754 749 Session().commit()
755 # PART TWO make test repo
750
751
752 def create_test_repositories(test_path, config):
753 """
754 Creates test repositories in the temporary directory. Repositories are
755 extracted from archives within the rc_testdata package.
756 """
757 import rc_testdata
758 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
759
756 760 log.debug('making test vcs repositories')
757 761
758 762 idx_path = config['search.location']
@@ -767,24 +771,15 b' def create_test_env(repos_test_path, con'
767 771 log.debug('remove %s', data_path)
768 772 shutil.rmtree(data_path)
769 773
770 # CREATE DEFAULT TEST REPOS
771 cur_dir = dn(dn(abspath(__file__)))
772 with tarfile.open(jn(cur_dir, 'tests', 'fixtures',
773 'vcs_test_hg.tar.gz')) as tar:
774 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
775
776 cur_dir = dn(dn(abspath(__file__)))
777 with tarfile.open(jn(cur_dir, 'tests', 'fixtures',
778 'vcs_test_git.tar.gz')) as tar:
779 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
774 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
775 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
780 776
781 777 # Note: Subversion is in the process of being integrated with the system,
782 778 # until we have a properly packed version of the test svn repository, this
783 779 # tries to copy over the repo from a package "rc_testdata"
784 import rc_testdata
785 780 svn_repo_path = rc_testdata.get_svn_repo_archive()
786 781 with tarfile.open(svn_repo_path) as tar:
787 tar.extractall(jn(TESTS_TMP_PATH, SVN_REPO))
782 tar.extractall(jn(test_path, SVN_REPO))
788 783
789 784
790 785 #==============================================================================
@@ -976,7 +971,20 b' def read_opensource_licenses():'
976 971
977 972 if not _license_cache:
978 973 licenses = pkg_resources.resource_string(
979 'rhodecode.config', 'licenses.json')
974 'rhodecode', 'config/licenses.json')
980 975 _license_cache = json.loads(licenses)
981 976
982 977 return _license_cache
978
979
980 def get_registry(request):
981 """
982 Utility to get the pyramid registry from a request. During migration to
983 pyramid we sometimes want to use the pyramid registry from pylons context.
984 Therefore this utility returns `request.registry` for pyramid requests and
985 uses `get_current_registry()` for pylons requests.
986 """
987 try:
988 return request.registry
989 except AttributeError:
990 return get_current_registry()
@@ -321,7 +321,8 b' def engine_from_config(configuration, pr'
321 321 setattr(conn, 'query_start_time', time.time())
322 322 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
323 323 calling_context = find_calling_context(ignore_modules=[
324 'rhodecode.lib.caching_query'
324 'rhodecode.lib.caching_query',
325 'rhodecode.model.settings',
325 326 ])
326 327 if calling_context:
327 328 log.info(color_sql('call context %s:%s' % (
@@ -341,6 +342,12 b' def engine_from_config(configuration, pr'
341 342 return engine
342 343
343 344
345 def get_encryption_key(config):
346 secret = config.get('rhodecode.encrypted_values.secret')
347 default = config['beaker.session.secret']
348 return secret or default
349
350
344 351 def age(prevdate, now=None, show_short_version=False, show_suffix=True,
345 352 short_format=False):
346 353 """
@@ -93,11 +93,14 b' def connect_http(server_and_port):'
93 93 from rhodecode.lib.vcs import connection, client_http
94 94 from rhodecode.lib.middleware.utils import scm_app
95 95
96 session = _create_http_rpc_session()
96 session_factory = client_http.ThreadlocalSessionFactory()
97 97
98 connection.Git = client_http.RepoMaker(server_and_port, '/git', session)
99 connection.Hg = client_http.RepoMaker(server_and_port, '/hg', session)
100 connection.Svn = client_http.RepoMaker(server_and_port, '/svn', session)
98 connection.Git = client_http.RepoMaker(
99 server_and_port, '/git', session_factory)
100 connection.Hg = client_http.RepoMaker(
101 server_and_port, '/hg', session_factory)
102 connection.Svn = client_http.RepoMaker(
103 server_and_port, '/svn', session_factory)
101 104
102 105 scm_app.HG_REMOTE_WSGI = client_http.VcsHttpProxy(
103 106 server_and_port, '/proxy/hg')
@@ -31,6 +31,7 b' implementation.'
31 31
32 32 import copy
33 33 import logging
34 import threading
34 35 import urllib2
35 36 import urlparse
36 37 import uuid
@@ -38,7 +39,7 b' import uuid'
38 39 import msgpack
39 40 import requests
40 41
41 from . import exceptions
42 from . import exceptions, CurlSession
42 43
43 44
44 45 log = logging.getLogger(__name__)
@@ -54,15 +55,16 b' EXCEPTIONS_MAP = {'
54 55
55 56 class RepoMaker(object):
56 57
57 def __init__(self, server_and_port, backend_endpoint, session):
58 def __init__(self, server_and_port, backend_endpoint, session_factory):
58 59 self.url = urlparse.urljoin(
59 60 'http://%s' % server_and_port, backend_endpoint)
60 self._session = session
61 self._session_factory = session_factory
61 62
62 63 def __call__(self, path, config, with_wire=None):
63 64 log.debug('RepoMaker call on %s', path)
64 65 return RemoteRepo(
65 path, config, self.url, self._session, with_wire=with_wire)
66 path, config, self.url, self._session_factory(),
67 with_wire=with_wire)
66 68
67 69 def __getattr__(self, name):
68 70 def f(*args, **kwargs):
@@ -76,7 +78,8 b' class RepoMaker(object):'
76 78 'method': name,
77 79 'params': {'args': args, 'kwargs': kwargs}
78 80 }
79 return _remote_call(self.url, payload, EXCEPTIONS_MAP, self._session)
81 return _remote_call(
82 self.url, payload, EXCEPTIONS_MAP, self._session_factory())
80 83
81 84
82 85 class RemoteRepo(object):
@@ -216,3 +219,17 b' class VcsHttpProxy(object):'
216 219 headers = iterator.next()
217 220
218 221 return iterator, status, headers
222
223
224 class ThreadlocalSessionFactory(object):
225 """
226 Creates one CurlSession per thread on demand.
227 """
228
229 def __init__(self):
230 self._thread_local = threading.local()
231
232 def __call__(self):
233 if not hasattr(self._thread_local, 'curl_session'):
234 self._thread_local.curl_session = CurlSession()
235 return self._thread_local.curl_session
@@ -43,9 +43,10 b" The application's model objects"
43 43 import logging
44 44
45 45 from pylons import config
46 from pyramid.threadlocal import get_current_registry
46 47
47 48 from rhodecode.model import meta, db
48 from rhodecode.lib.utils2 import obfuscate_url_pw
49 from rhodecode.lib.utils2 import obfuscate_url_pw, get_encryption_key
49 50
50 51 log = logging.getLogger(__name__)
51 52
@@ -65,8 +66,8 b' def init_model(engine, encryption_key=No'
65 66
66 67
67 68 def init_model_encryption(migration_models):
68 migration_models.ENCRYPTION_KEY = config['beaker.session.secret']
69 db.ENCRYPTION_KEY = config['beaker.session.secret']
69 migration_models.ENCRYPTION_KEY = get_encryption_key(config)
70 db.ENCRYPTION_KEY = get_encryption_key(config)
70 71
71 72
72 73 class BaseModel(object):
@@ -144,6 +145,17 b' class BaseModel(object):'
144 145 return self._get_instance(
145 146 db.Permission, permission, callback=db.Permission.get_by_key)
146 147
148 def send_event(self, event):
149 """
150 Helper method to send an event. This wraps the pyramid logic to send an
151 event.
152 """
153 # For the first step we are using pyramids thread locals here. If the
154 # event mechanism works out as a good solution we should think about
155 # passing the registry into the constructor to get rid of it.
156 registry = get_current_registry()
157 registry.notify(event)
158
147 159 @classmethod
148 160 def get_all(cls):
149 161 """
@@ -70,7 +70,8 b' log = logging.getLogger(__name__)'
70 70 # BASE CLASSES
71 71 # =============================================================================
72 72
73 # this is propagated from .ini file beaker.session.secret
73 # this is propagated from .ini file rhodecode.encrypted_values.secret or
74 # beaker.session.secret if first is not set.
74 75 # and initialized at environment.py
75 76 ENCRYPTION_KEY = None
76 77
@@ -115,14 +116,17 b' class EncryptedTextValue(TypeDecorator):'
115 116 def process_bind_param(self, value, dialect):
116 117 if not value:
117 118 return value
118 if value.startswith('enc$aes$'):
119 if value.startswith('enc$aes$') or value.startswith('enc$aes_hmac$'):
119 120 # protect against double encrypting if someone manually starts
120 121 # doing
121 122 raise ValueError('value needs to be in unencrypted format, ie. '
122 'not starting with enc$aes$')
123 return 'enc$aes$%s' % AESCipher(ENCRYPTION_KEY).encrypt(value)
123 'not starting with enc$aes')
124 return 'enc$aes_hmac$%s' % AESCipher(
125 ENCRYPTION_KEY, hmac=True).encrypt(value)
124 126
125 127 def process_result_value(self, value, dialect):
128 import rhodecode
129
126 130 if not value:
127 131 return value
128 132
@@ -134,9 +138,19 b' class EncryptedTextValue(TypeDecorator):'
134 138 if parts[0] != 'enc':
135 139 # parts ok but without our header ?
136 140 return value
137
141 enc_strict_mode = str2bool(rhodecode.CONFIG.get(
142 'rhodecode.encrypted_values.strict') or True)
138 143 # at that stage we know it's our encryption
144 if parts[1] == 'aes':
139 145 decrypted_data = AESCipher(ENCRYPTION_KEY).decrypt(parts[2])
146 elif parts[1] == 'aes_hmac':
147 decrypted_data = AESCipher(
148 ENCRYPTION_KEY, hmac=True,
149 strict_verification=enc_strict_mode).decrypt(parts[2])
150 else:
151 raise ValueError(
152 'Encryption type part is wrong, must be `aes` '
153 'or `aes_hmac`, got `%s` instead' % (parts[1]))
140 154 return decrypted_data
141 155
142 156
@@ -220,6 +234,20 b' class BaseModel(object):'
220 234 obj = cls.query().get(id_)
221 235 Session().delete(obj)
222 236
237 @classmethod
238 def identity_cache(cls, session, attr_name, value):
239 exist_in_session = []
240 for (item_cls, pkey), instance in session.identity_map.items():
241 if cls == item_cls and getattr(instance, attr_name) == value:
242 exist_in_session.append(instance)
243 if exist_in_session:
244 if len(exist_in_session) == 1:
245 return exist_in_session[0]
246 log.exception(
247 'multiple objects with attr %s and '
248 'value %s found with same name: %r',
249 attr_name, value, exist_in_session)
250
223 251 def __repr__(self):
224 252 if hasattr(self, '__unicode__'):
225 253 # python repr needs to return str
@@ -639,16 +667,26 b' class User(Base, BaseModel):'
639 667 log.error(traceback.format_exc())
640 668
641 669 @classmethod
642 def get_by_username(cls, username, case_insensitive=False, cache=False):
670 def get_by_username(cls, username, case_insensitive=False,
671 cache=False, identity_cache=False):
672 session = Session()
673
643 674 if case_insensitive:
644 q = cls.query().filter(func.lower(cls.username) == func.lower(username))
675 q = cls.query().filter(
676 func.lower(cls.username) == func.lower(username))
645 677 else:
646 678 q = cls.query().filter(cls.username == username)
647 679
648 680 if cache:
649 q = q.options(FromCache(
650 "sql_cache_short",
651 "get_user_%s" % _hash_key(username)))
681 if identity_cache:
682 val = cls.identity_cache(session, 'username', username)
683 if val:
684 return val
685 else:
686 q = q.options(
687 FromCache("sql_cache_short",
688 "get_user_by_name_%s" % _hash_key(username)))
689
652 690 return q.scalar()
653 691
654 692 @classmethod
@@ -752,10 +790,10 b' class User(Base, BaseModel):'
752 790 Session().add(self)
753 791
754 792 @classmethod
755 def get_first_admin(cls):
756 user = User.query().filter(User.admin == True).first()
793 def get_first_super_admin(cls):
794 user = User.query().filter(User.admin == true()).first()
757 795 if user is None:
758 raise Exception('Missing administrative account!')
796 raise Exception('FATAL: Missing administrative account!')
759 797 return user
760 798
761 799 @classmethod
@@ -770,7 +808,7 b' class User(Base, BaseModel):'
770 808 def get_default_user(cls, cache=False):
771 809 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
772 810 if user is None:
773 raise Exception('Missing default account!')
811 raise Exception('FATAL: Missing default account!')
774 812 return user
775 813
776 814 def _get_default_perms(self, user, suffix=''):
@@ -1264,9 +1302,9 b' class Repository(Base, BaseModel):'
1264 1302 "group_id", Integer(), ForeignKey('groups.group_id'), nullable=True,
1265 1303 unique=False, default=None)
1266 1304
1267 user = relationship('User')
1268 fork = relationship('Repository', remote_side=repo_id)
1269 group = relationship('RepoGroup')
1305 user = relationship('User', lazy='joined')
1306 fork = relationship('Repository', remote_side=repo_id, lazy='joined')
1307 group = relationship('RepoGroup', lazy='joined')
1270 1308 repo_to_perm = relationship(
1271 1309 'UserRepoToPerm', cascade='all',
1272 1310 order_by='UserRepoToPerm.repo_to_perm_id')
@@ -1364,7 +1402,7 b' class Repository(Base, BaseModel):'
1364 1402 def normalize_repo_name(cls, repo_name):
1365 1403 """
1366 1404 Normalizes os specific repo_name to the format internally stored inside
1367 dabatabase using URL_SEP
1405 database using URL_SEP
1368 1406
1369 1407 :param cls:
1370 1408 :param repo_name:
@@ -1372,11 +1410,20 b' class Repository(Base, BaseModel):'
1372 1410 return cls.NAME_SEP.join(repo_name.split(os.sep))
1373 1411
1374 1412 @classmethod
1375 def get_by_repo_name(cls, repo_name):
1376 q = Session().query(cls).filter(cls.repo_name == repo_name)
1377 q = q.options(joinedload(Repository.fork))\
1378 .options(joinedload(Repository.user))\
1379 .options(joinedload(Repository.group))
1413 def get_by_repo_name(cls, repo_name, cache=False, identity_cache=False):
1414 session = Session()
1415 q = session.query(cls).filter(cls.repo_name == repo_name)
1416
1417 if cache:
1418 if identity_cache:
1419 val = cls.identity_cache(session, 'repo_name', repo_name)
1420 if val:
1421 return val
1422 else:
1423 q = q.options(
1424 FromCache("sql_cache_short",
1425 "get_repo_by_name_%s" % _hash_key(repo_name)))
1426
1380 1427 return q.scalar()
1381 1428
1382 1429 @classmethod
@@ -1721,7 +1768,7 b' class Repository(Base, BaseModel):'
1721 1768 clone_uri = self.clone_uri
1722 1769 if clone_uri:
1723 1770 import urlobject
1724 url_obj = urlobject.URLObject(self.clone_uri)
1771 url_obj = urlobject.URLObject(clone_uri)
1725 1772 if url_obj.password:
1726 1773 clone_uri = url_obj.with_password('*****')
1727 1774 return clone_uri
@@ -138,7 +138,11 b' def UserForm(edit=False, available_langu'
138 138 return _UserForm
139 139
140 140
141 def UserGroupForm(edit=False, old_data={}, available_members=[]):
141 def UserGroupForm(edit=False, old_data=None, available_members=None,
142 allow_disabled=False):
143 old_data = old_data or {}
144 available_members = available_members or []
145
142 146 class _UserGroupForm(formencode.Schema):
143 147 allow_extra_fields = True
144 148 filter_extra_fields = True
@@ -158,13 +162,17 b' def UserGroupForm(edit=False, old_data={'
158 162 if_missing=None, not_empty=False
159 163 )
160 164 #this is user group owner
161 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
162
165 user = All(
166 v.UnicodeString(not_empty=True),
167 v.ValidRepoUser(allow_disabled))
163 168 return _UserGroupForm
164 169
165 170
166 def RepoGroupForm(edit=False, old_data={}, available_groups=[],
167 can_create_in_root=False):
171 def RepoGroupForm(edit=False, old_data=None, available_groups=None,
172 can_create_in_root=False, allow_disabled=False):
173 old_data = old_data or {}
174 available_groups = available_groups or []
175
168 176 class _RepoGroupForm(formencode.Schema):
169 177 allow_extra_fields = True
170 178 filter_extra_fields = False
@@ -178,11 +186,14 b' def RepoGroupForm(edit=False, old_data={'
178 186 group_parent_id = v.OneOf(available_groups, hideList=False,
179 187 testValueList=True, not_empty=True)
180 188 enable_locking = v.StringBoolean(if_missing=False)
181 chained_validators = [v.ValidRepoGroup(edit, old_data, can_create_in_root)]
189 chained_validators = [
190 v.ValidRepoGroup(edit, old_data, can_create_in_root)]
182 191
183 192 if edit:
184 193 #this is repo group owner
185 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
194 user = All(
195 v.UnicodeString(not_empty=True),
196 v.ValidRepoUser(allow_disabled))
186 197
187 198 return _RepoGroupForm
188 199
@@ -221,7 +232,8 b' def PasswordResetForm():'
221 232 return _PasswordResetForm
222 233
223 234
224 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None):
235 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None,
236 allow_disabled=False):
225 237 old_data = old_data or {}
226 238 repo_groups = repo_groups or []
227 239 landing_revs = landing_revs or []
@@ -248,7 +260,9 b' def RepoForm(edit=False, old_data=None, '
248 260
249 261 if edit:
250 262 # this is repo owner
251 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
263 user = All(
264 v.UnicodeString(not_empty=True),
265 v.ValidRepoUser(allow_disabled))
252 266 clone_uri_change = v.UnicodeString(
253 267 not_empty=False, if_missing=v.Missing)
254 268
@@ -140,10 +140,12 b' class RepoModel(BaseModel):'
140 140
141 141 return None
142 142
143 def get_users(self, name_contains=None, limit=20):
143 def get_users(self, name_contains=None, limit=20, only_active=True):
144 144 # TODO: mikhail: move this method to the UserModel.
145 145 query = self.sa.query(User)
146 if only_active:
146 147 query = query.filter(User.active == true())
148
147 149 if name_contains:
148 150 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
149 151 query = query.filter(
@@ -165,16 +167,19 b' class RepoModel(BaseModel):'
165 167 'icon_link': h.gravatar_url(user.email, 14),
166 168 'value_display': h.person(user.email),
167 169 'value': user.username,
168 'value_type': 'user'
170 'value_type': 'user',
171 'active': user.active,
169 172 }
170 173 for user in users
171 174 ]
172 175 return _users
173 176
174 def get_user_groups(self, name_contains=None, limit=20):
177 def get_user_groups(self, name_contains=None, limit=20, only_active=True):
175 178 # TODO: mikhail: move this method to the UserGroupModel.
176 179 query = self.sa.query(UserGroup)
180 if only_active:
177 181 query = query.filter(UserGroup.users_group_active == true())
182
178 183 if name_contains:
179 184 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
180 185 query = query.filter(
@@ -196,7 +201,8 b' class RepoModel(BaseModel):'
196 201 'value_display': 'Group: %s (%d members)' % (
197 202 group.users_group_name, len(group.members),),
198 203 'value': group.users_group_name,
199 'value_type': 'user_group'
204 'value_type': 'user_group',
205 'active': group.users_group_active,
200 206 }
201 207 for group in user_groups
202 208 ]
@@ -333,7 +339,7 b' class RepoModel(BaseModel):'
333 339 if repo_info.user:
334 340 defaults.update({'user': repo_info.user.username})
335 341 else:
336 replacement_user = User.get_first_admin().username
342 replacement_user = User.get_first_super_admin().username
337 343 defaults.update({'user': replacement_user})
338 344
339 345 # fill repository users
@@ -23,6 +23,7 b' import logging'
23 23 from collections import namedtuple
24 24 from functools import wraps
25 25
26 from rhodecode.lib import caches
26 27 from rhodecode.lib.caching_query import FromCache
27 28 from rhodecode.lib.utils2 import (
28 29 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
@@ -200,15 +201,14 b' class SettingsModel(BaseModel):'
200 201 Session.add(res)
201 202 return res
202 203
204 def invalidate_settings_cache(self):
205 namespace = 'rhodecode_settings'
206 cache_manager = caches.get_cache_manager('sql_cache_short', namespace)
207 caches.clear_cache_manager(cache_manager)
208
203 209 def get_all_settings(self, cache=False):
210 def _compute():
204 211 q = self._get_settings_query()
205 if cache:
206 repo = self._get_repo(self.repo) if self.repo else None
207 cache_key = (
208 "get_repo_{}_settings".format(repo.repo_id)
209 if repo else "get_hg_settings")
210 q = q.options(FromCache("sql_cache_short", cache_key))
211
212 212 if not q:
213 213 raise Exception('Could not get application settings !')
214 214
@@ -218,6 +218,21 b' class SettingsModel(BaseModel):'
218 218 }
219 219 return settings
220 220
221 if cache:
222 log.debug('Fetching app settings using cache')
223 repo = self._get_repo(self.repo) if self.repo else None
224 namespace = 'rhodecode_settings'
225 cache_manager = caches.get_cache_manager(
226 'sql_cache_short', namespace)
227 _cache_key = (
228 "get_repo_{}_settings".format(repo.repo_id)
229 if repo else "get_app_settings")
230
231 return cache_manager.get(_cache_key, createfunc=_compute)
232
233 else:
234 return _compute()
235
221 236 def get_auth_settings(self):
222 237 q = self._get_settings_query()
223 238 q = q.filter(
@@ -26,13 +26,13 b' import logging'
26 26 import traceback
27 27
28 28 import datetime
29 from pylons import url
30 29 from pylons.i18n.translation import _
31 30
32 31 import ipaddress
33 32 from sqlalchemy.exc import DatabaseError
34 33 from sqlalchemy.sql.expression import true, false
35 34
35 from rhodecode.events import UserPreCreate, UserPreUpdate
36 36 from rhodecode.lib.utils2 import (
37 37 safe_unicode, get_current_rhodecode_user, action_logger_generic,
38 38 AttributeDict)
@@ -270,10 +270,12 b' class UserModel(BaseModel):'
270 270 # raises UserCreationError if it's not allowed for any reason to
271 271 # create new active user, this also executes pre-create hooks
272 272 check_allowed_create_user(user_data, cur_user, strict_check=True)
273 self.send_event(UserPreCreate(user_data))
273 274 new_user = User()
274 275 edit = False
275 276 else:
276 277 log.debug('updating user %s', username)
278 self.send_event(UserPreUpdate(user, user_data))
277 279 new_user = user
278 280 edit = True
279 281
@@ -375,7 +377,7 b' class UserModel(BaseModel):'
375 377 raise
376 378
377 379 def _handle_user_repos(self, username, repositories, handle_mode=None):
378 _superadmin = self.cls.get_first_admin()
380 _superadmin = self.cls.get_first_super_admin()
379 381 left_overs = True
380 382
381 383 from rhodecode.model.repo import RepoModel
@@ -398,7 +400,7 b' class UserModel(BaseModel):'
398 400
399 401 def _handle_user_repo_groups(self, username, repository_groups,
400 402 handle_mode=None):
401 _superadmin = self.cls.get_first_admin()
403 _superadmin = self.cls.get_first_super_admin()
402 404 left_overs = True
403 405
404 406 from rhodecode.model.repo_group import RepoGroupModel
@@ -420,7 +422,7 b' class UserModel(BaseModel):'
420 422 return left_overs
421 423
422 424 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
423 _superadmin = self.cls.get_first_admin()
425 _superadmin = self.cls.get_first_super_admin()
424 426 left_overs = True
425 427
426 428 from rhodecode.model.user_group import UserGroupModel
@@ -498,7 +498,7 b' class UserGroupModel(BaseModel):'
498 498 self.remove_user_from_group(gr, user)
499 499
500 500 # now we calculate in which groups user should be == groups params
501 owner = User.get_first_admin().username
501 owner = User.get_first_super_admin().username
502 502 for gr in set(groups):
503 503 existing_group = UserGroup.get_by_group_name(gr)
504 504 if not existing_group:
@@ -193,21 +193,26 b' def ValidRegex(msg=None):'
193 193 return _validator
194 194
195 195
196 def ValidRepoUser():
196 def ValidRepoUser(allow_disabled=False):
197 197 class _validator(formencode.validators.FancyValidator):
198 198 messages = {
199 'invalid_username': _(u'Username %(username)s is not valid')
199 'invalid_username': _(u'Username %(username)s is not valid'),
200 'disabled_username': _(u'Username %(username)s is disabled')
200 201 }
201 202
202 203 def validate_python(self, value, state):
203 204 try:
204 User.query().filter(User.active == true())\
205 .filter(User.username == value).one()
205 user = User.query().filter(User.username == value).one()
206 206 except Exception:
207 207 msg = M(self, 'invalid_username', state, username=value)
208 208 raise formencode.Invalid(
209 209 msg, value, state, error_dict={'username': msg}
210 210 )
211 if user and (not allow_disabled and not user.active):
212 msg = M(self, 'disabled_username', state, username=value)
213 raise formencode.Invalid(
214 msg, value, state, error_dict={'username': msg}
215 )
211 216
212 217 return _validator
213 218
@@ -269,6 +269,33 b' form.rcform {'
269 269
270 270 }
271 271
272 .badged-field {
273 .user-badge {
274 line-height: 25px;
275 padding: 10px 5px;
276 border-radius: @border-radius;
277 border-top: 1px solid @rclightblue;
278 border-left: 1px solid @rclightblue;
279 border-bottom: 1px solid @rclightblue;
280 font-size: 14px;
281 font-style: normal;
282 color: @text-light;
283 display: inline-block;
284 vertical-align: top;
285 cursor: default;
286 margin-right: -2px;
287 }
288 .badge-input-container {
289 display: flex;
290 position: relative;
291 }
292 .user-disabled {
293 text-decoration: line-through;
294 }
295 .badge-input-wrap {
296 display: inline-block;
297 }
298 }
272 299
273 300 // for situations where we wish to display the form value but not the form input
274 301 input.input-valuedisplay {
@@ -296,30 +296,30 b' ul.auth_plugins {'
296 296 }
297 297 }
298 298
299 // Pull Requests
299
300 // My Account PR list
301
302 #show_closed {
303 margin: 0 1em 0 0;
304 }
300 305
301 306 .pullrequestlist {
302 max-width: @pullrequest-width;
303 margin-bottom: @space;
304
305 // Tweaks for "My Account" / "Pull requests"
306 .prwrapper {
307 clear: left;
308
309 .pr {
310 margin: 0;
311 padding: 0;
312 border-bottom: none;
307 .closed {
308 background-color: @grey6;
309 }
310 .td-status {
311 padding-left: .5em;
313 312 }
314
315 // TODO: johbo: Replace with something that makes up an inline form or
316 // similar.
317 .repolist_actions {
318 display: inline-block;
313 .truncate {
314 height: 2.75em;
315 white-space: pre-line;
316 }
317 table.rctable .user {
318 padding-left: 0;
319 319 }
320 320 }
321 321
322 }
322 // Pull Requests
323 323
324 324 .pullrequests_section_head {
325 325 display: block;
@@ -1086,6 +1086,7 b' table.issuetracker {'
1086 1086 }
1087 1087 }
1088 1088
1089
1089 1090 //Permissions Settings
1090 1091 #add_perm {
1091 1092 margin: 0 0 @padding;
@@ -288,6 +288,11 b''
288 288 }
289 289 }
290 290
291 .navigation li:last-child .submenu {
292 right: -20px;
293 left: auto;
294 }
295
291 296 .submenu {
292 297 position: absolute;
293 298 top: 100%;
@@ -6,7 +6,6 b''
6 6
7 7 width: 100%;
8 8 margin: 0 0 25px 0;
9 border-color: @grey5;
10 9 .border-radius(@border-radius);
11 10 .box-shadow(none);
12 11
@@ -23,7 +22,6 b''
23 22 position: relative;
24 23 min-height: 1em;
25 24 padding: @padding @panel-padding;
26 background-color: @grey6;
27 25 border-bottom: none;
28 26
29 27 .panel-title,
@@ -20,18 +20,24 b''
20 20 * autocomplete formatter that uses gravatar
21 21 * */
22 22 var autocompleteFormatResult = function(data, value, org_formatter) {
23 var value_display = data.value_display;
23 var activeUser = data.active || true;
24 var valueDisplay = data.value_display;
25
26 if (!activeUser) {
27 valueDisplay = '<strong>(disabled)</strong> ' + valueDisplay;
28 }
29
24 30 var escapeRegExChars = function (value) {
25 31 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
26 32 };
27 33 var pattern = '(' + escapeRegExChars(value) + ')';
28 value_display = value_display.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
34 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
29 35 var tmpl = '<div class="ac-container-wrap"><img class="gravatar" src="{0}"/>{1}</div>';
30 36 if (data.icon_link === "") {
31 37 tmpl = '<div class="ac-container-wrap">{0}</div>';
32 return tmpl.format(value_display);
38 return tmpl.format(valueDisplay);
33 39 }
34 return tmpl.format(data.icon_link, value_display);
40 return tmpl.format(data.icon_link, valueDisplay);
35 41 };
36 42
37 43 /**
@@ -1,7 +1,7 b''
1 <%namespace name="base" file="/base/base.html"/>
1 2
2 3 <div class="panel panel-default">
3 4 <div class="panel-body">
4 <div class="field">
5 5 %if c.show_closed:
6 6 ${h.checkbox('show_closed',checked="checked", label=_('Show Closed Pull Requests'))}
7 7 %else:
@@ -9,35 +9,62 b''
9 9 %endif
10 10 </div>
11 11 </div>
12 </div>
13 12
14 13 <div class="panel panel-default">
15 14 <div class="panel-heading">
16 15 <h3 class="panel-title">${_('Pull Requests You Opened')}</h3>
17 16 </div>
18
19 17 <div class="panel-body">
20 18 <div class="pullrequestlist">
21 19 %if c.my_pull_requests:
20 <table class="rctable">
21 <thead>
22 <th class="td-status"></th>
23 <th>${_('Target Repo')}</th>
24 <th>${_('Author')}</th>
25 <th></th>
26 <th>${_('Title')}</th>
27 <th class="td-time">${_('Opened On')}</th>
28 <th></th>
29 </thead>
22 30 %for pull_request in c.my_pull_requests:
23 <div class="${'closed' if pull_request.is_closed() else ''} prwrapper">
24 <div class="pr">
31 <tr class="${'closed' if pull_request.is_closed() else ''} prwrapper">
32 <td class="td-status">
25 33 <div class="${'flag_status %s' % pull_request.calculated_review_status()} pull-left"></div>
26 <a href="${h.url('pullrequest_show',repo_name=pull_request.target_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
27 ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.format_date(pull_request.created_on))}
34 </td>
35 <td class="td-componentname">
36 ${h.link_to(pull_request.target_repo.repo_name,h.url('summary_home',repo_name=pull_request.target_repo.repo_name))}
37 </td>
38 <td class="user">
39 ${base.gravatar_with_user(pull_request.author.email, 16)}
40 </td>
41 <td class="td-message expand_commit" data-pr-id="m${pull_request.pull_request_id}" title="${_('Expand commit message')}">
42 <div class="show_more_col">
43 <i class="show_more"></i>&nbsp;
44 </div>
45 </td>
46 <td class="mid td-description">
47 <div class="log-container truncate-wrap">
48 <div class="message truncate" id="c-m${pull_request.pull_request_id}"><a href="${h.url('pullrequest_show',repo_name=pull_request.target_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">#${pull_request.pull_request_id}: ${pull_request.title}</a>\
28 49 %if pull_request.is_closed():
29 (${_('Closed')})
50 &nbsp;(${_('Closed')})\
30 51 %endif
31 </a>
32 <div class="repolist_actions">
52 <br/>${pull_request.description}</div>
53 </div>
54 </td>
55
56 <td class="td-time">
57 ${h.age_component(pull_request.created_on)}
58 </td>
59 <td class="td-action repolist_actions">
33 60 ${h.secure_form(url('pullrequest_delete', repo_name=pull_request.target_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')}
34 61 ${h.submit('remove_%s' % pull_request.pull_request_id, _('Delete'),
35 62 class_="btn btn-link btn-danger",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
36 63 ${h.end_form()}
37 </div>
38 </div>
39 </div>
64 </td>
65 </tr>
40 66 %endfor
67 </table>
41 68 %else:
42 69 <h2><span class="empty_data">${_('You currently have no open pull requests.')}</span></h2>
43 70 %endif
@@ -53,21 +80,49 b''
53 80 <div class="panel-body">
54 81 <div class="pullrequestlist">
55 82 %if c.participate_in_pull_requests:
83 <table class="rctable">
84 <thead>
85 <th class="td-status"></th>
86 <th>${_('Target Repo')}</th>
87 <th>${_('Author')}</th>
88 <th></th>
89 <th>${_('Title')}</th>
90 <th class="td-time">${_('Opened On')}</th>
91 </thead>
56 92 %for pull_request in c.participate_in_pull_requests:
57 <div class="${'closed' if pull_request.is_closed() else ''} prwrapper">
58 <div class="pr">
93 <tr class="${'closed' if pull_request.is_closed() else ''} prwrapper">
94 <td class="td-status">
59 95 <div class="${'flag_status %s' % pull_request.calculated_review_status()} pull-left"></div>
60 <a href="${h.url('pullrequest_show',repo_name=pull_request.target_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
61 ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.format_date(pull_request.created_on))}
62 </a>
96 </td>
97 <td class="td-componentname">
98 ${h.link_to(pull_request.target_repo.repo_name,h.url('summary_home',repo_name=pull_request.target_repo.repo_name))}
99 </td>
100 <td class="user">
101 ${base.gravatar_with_user(pull_request.author.email, 16)}
102 </td>
103 <td class="td-message expand_commit" data-pr-id="p${pull_request.pull_request_id}" title="${_('Expand commit message')}">
104 <div class="show_more_col">
105 <i class="show_more"></i>&nbsp;
106 </div>
107 </td>
108 <td class="mid td-description">
109 <div class="log-container truncate-wrap">
110 <div class="message truncate" id="c-p${pull_request.pull_request_id}"><a href="${h.url('pullrequest_show',repo_name=pull_request.target_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">#${pull_request.pull_request_id}: ${pull_request.title}</a>\
63 111 %if pull_request.is_closed():
64 (${_('Closed')})
112 &nbsp;(${_('Closed')})\
65 113 %endif
66 </div>
114 <br/>${pull_request.description}</div>
67 115 </div>
116 </td>
117
118 <td class="td-time">
119 ${h.age_component(pull_request.created_on)}
120 </td>
121 </tr>
68 122 %endfor
123 </table>
69 124 %else:
70 <li><span class="empty_data">${_('There are currently no open pull requests requiring your participation.')}</span></li>
125 <h2 class="empty_data">${_('There are currently no open pull requests requiring your participation.')}</h2>
71 126 %endif
72 127 </div>
73 128 </div>
@@ -81,5 +136,18 b''
81 136 else{
82 137 window.location = "${h.url('my_account_pullrequests')}";
83 138 }
84 })
139 });
140 $('.expand_commit').on('click',function(e){
141 var target_expand = $(this);
142 var cid = target_expand.data('prId');
143
144 if (target_expand.hasClass('open')){
145 $('#c-'+cid).css({'height': '2.75em', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
146 target_expand.removeClass('open');
147 }
148 else {
149 $('#c-'+cid).css({'height': 'auto', 'text-overflow': 'initial', 'overflow':'visible'});
150 target_expand.addClass('open');
151 }
152 });
85 153 </script>
@@ -1,4 +1,6 b''
1 1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.html"/>
3
2 4 <div class="panel panel-default">
3 5 <div class="panel-heading">
4 6 <h3 class="panel-title">${_('Settings for Repository Group: %s') % c.repo_group.name}</h3>
@@ -16,15 +18,25 b''
16 18 ${h.text('group_name',class_='medium')}
17 19 </div>
18 20 </div>
19 <div class="field">
21
22 <div class="field badged-field">
20 23 <div class="label">
21 24 <label for="user">${_('Owner')}:</label>
22 25 </div>
23 26 <div class="input">
27 <div class="badge-input-container">
28 <div class="user-badge">
29 ${base.gravatar_with_user(c.repo_group.user.email, show_disabled=not c.repo_group.user.active)}
30 </div>
31 <div class="badge-input-wrap">
24 32 ${h.text('user', class_="medium", autocomplete="off")}
25 <span class="help-block">${_('Change Repository Group Owner.')}</span>
26 33 </div>
27 34 </div>
35 <form:error name="user"/>
36 <p class="help-block">${_('Change owner of this repository group.')}</p>
37 </div>
38 </div>
39
28 40 <div class="field">
29 41 <div class="label label-textarea">
30 42 <label for="group_description">${_('Description')}:</label>
@@ -1,3 +1,6 b''
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.html"/>
3
1 4 <div class="panel panel-default">
2 5 <div class="panel-heading">
3 6 <h3 class="panel-title">${_('Settings for Repository: %s') % c.repo_info.repo_name}</h3>
@@ -69,15 +72,25 b''
69 72 <p class="help-block">${_('Default commit for files page, downloads, whoosh and readme')}</p>
70 73 </div>
71 74 </div>
72 <div class="field">
75
76 <div class="field badged-field">
73 77 <div class="label">
74 78 <label for="user">${_('Owner')}:</label>
75 79 </div>
76 80 <div class="input">
81 <div class="badge-input-container">
82 <div class="user-badge">
83 ${base.gravatar_with_user(c.repo_info.user.email, show_disabled=not c.repo_info.user.active)}
84 </div>
85 <div class="badge-input-wrap">
77 86 ${h.text('user', class_="medium", autocomplete="off")}
87 </div>
88 </div>
89 <form:error name="user"/>
78 90 <p class="help-block">${_('Change owner of this repository.')}</p>
79 91 </div>
80 92 </div>
93
81 94 <div class="field">
82 95 <div class="label label-textarea">
83 96 <label for="repo_description">${_('Description')}:</label>
@@ -20,6 +20,7 b''
20 20 <td>
21 21 ${h.literal(', '.join([
22 22 '<a href="%(link)s" title="%(name)s">%(name)s</a>' % {'link':link, 'name':name}
23 if link else name
23 24 for name,link in licenses.items()]))}
24 25 </td>
25 26 </tr>
@@ -45,6 +45,9 b''
45 45 <div class="panel panel-default">
46 46 <div class="panel-heading">
47 47 <h3 class="panel-title">${_('System Info')}</h3>
48 % if c.allowed_to_snapshot:
49 <a href="${url('admin_settings_system', snapshot=1)}" class="panel-edit">${_('create snapshot')}</a>
50 % endif
48 51 </div>
49 52 <div class="panel-body">
50 53 <dl class="dl-horizontal settings">
@@ -1,3 +1,6 b''
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.html"/>
3
1 4 <div class="panel panel-default">
2 5 <div class="panel-heading">
3 6 <h3 class="panel-title">${_('User Group: %s') % c.user_group.users_group_name}</h3>
@@ -15,15 +18,25 b''
15 18 ${h.text('users_group_name',class_='medium')}
16 19 </div>
17 20 </div>
18 <div class="field">
21
22 <div class="field badged-field">
19 23 <div class="label">
20 24 <label for="user">${_('Owner')}:</label>
21 25 </div>
22 26 <div class="input">
27 <div class="badge-input-container">
28 <div class="user-badge">
29 ${base.gravatar_with_user(c.user_group.user.email, show_disabled=not c.user_group.user.active)}
30 </div>
31 <div class="badge-input-wrap">
23 32 ${h.text('user', class_="medium", autocomplete="off")}
24 <span class="help-block">${_('Change owner of this user group.')}</span>
25 33 </div>
26 34 </div>
35 <form:error name="user"/>
36 <p class="help-block">${_('Change owner of this user group.')}</p>
37 </div>
38 </div>
39
27 40 <div class="field">
28 41 <div class="label label-textarea">
29 42 <label for="user_group_description">${_('Description')}:</label>
@@ -128,7 +128,6 b''
128 128 </div>
129 129 ${h.end_form()}
130 130 </div>
131 </%def>
132 131 <script>
133 132 $(document).ready(function(){
134 133 $('#username').focus();
@@ -142,3 +141,4 b''
142 141 })
143 142 })
144 143 </script>
144 </%def>
@@ -134,10 +134,10 b''
134 134 </%def>
135 135
136 136
137 <%def name="gravatar_with_user(contact, size=16)">
137 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
138 138 <div class="rc-user tooltip" title="${contact}">
139 139 ${self.gravatar(h.email_or_none(contact), size)}
140 <span class="user"> ${h.link_to_user(contact)}</span>
140 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
141 141 </div>
142 142 </%def>
143 143
@@ -195,7 +195,7 b''
195 195 %if repo_instance.clone_uri:
196 196 <p>
197 197 <i class="icon-code-fork"></i> ${_('Clone from')}
198 <a href="${h.url(str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
199 199 </p>
200 200 %endif
201 201
@@ -13,9 +13,10 b' A new user `${user.username}` has regist'
13 13 - Username: ${user.username}
14 14 - Full Name: ${user.firstname} ${user.lastname}
15 15 - Email: ${user.email}
16 - Profile link: ${h.url('user_profile', username=user.username, qualified=True)}
16 17 </%def>
17 18
18 19 ## BODY GOES BELOW
19 20 <div style="white-space: pre-wrap">
20 21 ${body_plaintext()}
21 </div> No newline at end of file
22 </div>
@@ -27,7 +27,7 b''
27 27 <div class="left-column">
28 28 <img class="sign-in-image" src="${h.url('/images/sign-in.png')}" alt="RhodeCode"/>
29 29 </div>
30
30 <%block name="above_login_button" />
31 31 <div id="login" class="right-column">
32 32 <%include file="/base/flash_msg.html"/>
33 33 <!-- login -->
@@ -47,9 +47,9 b''
47 47 <ul class="nav nav-pills nav-stacked">
48 48 <li class="${'active' if c.active=='open' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0)}">${_('Opened')}</a></li>
49 49 <li class="${'active' if c.active=='my' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,my=1)}">${_('Opened by me')}</a></li>
50 <li class="${'active' if c.active=='awaiting' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,awaiting_review=1)}">${_('Awaiting review')}</a></li>
50 51 <li class="${'active' if c.active=='awaiting_my' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,awaiting_my_review=1)}">${_('Awaiting my review')}</a></li>
51 52 <li class="${'active' if c.active=='closed' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,closed=1)}">${_('Closed')}</a></li>
52 <li class="${'active' if c.active=='awaiting' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,awaiting_review=1)}">${_('Awaiting review')}</a></li>
53 53 <li class="${'active' if c.active=='source' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=1)}">${_('From this repo')}</a></li>
54 54 </ul>
55 55 </div>
@@ -27,7 +27,7 b''
27 27 <div class="left-column">
28 28 <img class="sign-in-image" src="${h.url('/images/sign-in.png')}" alt="RhodeCode"/>
29 29 </div>
30
30 <%block name="above_register_button" />
31 31 <div id="register" class="right-column">
32 32 <%include file="/base/flash_msg.html"/>
33 33 <!-- login -->
@@ -85,9 +85,12 b' class TestMyAccountController(TestContro'
85 85 response = self.app.get(url('my_account_pullrequests'))
86 86 response.mustcontain('You currently have no open pull requests.')
87 87
88 pr = pr_util.create_pull_request()
88 pr = pr_util.create_pull_request(title='TestMyAccountPR')
89 89 response = self.app.get(url('my_account_pullrequests'))
90 response.mustcontain('Pull request #%d opened' % pr.pull_request_id)
90 response.mustcontain('There are currently no open pull requests '
91 'requiring your participation')
92
93 response.mustcontain('#%s: TestMyAccountPR' % pr.pull_request_id)
91 94
92 95 def test_my_account_my_emails(self):
93 96 self.log_user()
@@ -460,6 +460,10 b' class TestLabsSettings(object):'
460 460
461 461 @pytest.mark.usefixtures('app')
462 462 class TestOpenSourceLicenses(object):
463
464 def _get_url(self):
465 return ADMIN_PREFIX + '/settings/open_source'
466
463 467 def test_records_are_displayed(self, autologin_user):
464 468 sample_licenses = {
465 469 "python2.7-pytest-2.7.1": {
@@ -470,11 +474,10 b' class TestOpenSourceLicenses(object):'
470 474 }
471 475 }
472 476 read_licenses_patch = mock.patch(
473 'rhodecode.controllers.admin.settings.read_opensource_licenses',
477 'rhodecode.admin.views.read_opensource_licenses',
474 478 return_value=sample_licenses)
475 479 with read_licenses_patch:
476 response = self.app.get(
477 url('admin_settings_open_source'), status=200)
480 response = self.app.get(self._get_url(), status=200)
478 481
479 482 assert_response = AssertResponse(response)
480 483 assert_response.element_contains(
@@ -485,14 +488,13 b' class TestOpenSourceLicenses(object):'
485 488 assert_response.element_contains('.panel-body', license)
486 489
487 490 def test_records_can_be_read(self, autologin_user):
488 response = self.app.get(url('admin_settings_open_source'), status=200)
491 response = self.app.get(self._get_url(), status=200)
489 492 assert_response = AssertResponse(response)
490 493 assert_response.element_contains(
491 494 '.panel-heading', 'Licenses of Third Party Packages')
492 495
493 496 def test_forbidden_when_normal_user(self, autologin_regular_user):
494 self.app.get(
495 url('admin_settings_open_source'), status=403)
497 self.app.get(self._get_url(), status=403)
496 498
497 499
498 500 @pytest.mark.usefixtures("app")
@@ -33,7 +33,7 b' class TestFeedController(TestController)'
33 33 assert """<rss version="2.0">""" in response
34 34
35 35 def test_rss_with_auth_token(self, backend):
36 auth_token = User.get_first_admin().feed_token
36 auth_token = User.get_first_super_admin().feed_token
37 37 assert auth_token != ''
38 38 response = self.app.get(url(controller='feed', action='rss',
39 39 repo_name=backend.repo_name, auth_token=auth_token))
@@ -110,10 +110,12 b' class TestHomeController(TestController)'
110 110 def test_index_show_version(self, autologin_user, name, state):
111 111 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
112 112
113 show = SettingsModel().get_setting_by_name('show_version')
114 show.app_settings_value = state
115 Session().add(show)
113 sett = SettingsModel().create_or_update_setting(
114 'show_version', state, 'bool')
115 Session().add(sett)
116 116 Session().commit()
117 SettingsModel().invalidate_settings_cache()
118
117 119 response = self.app.get(url(controller='home', action='index'))
118 120 if state is True:
119 121 response.mustcontain(version_string)
@@ -133,6 +135,18 b' class TestUserAutocompleteData(TestContr'
133 135 values = [suggestion['value'] for suggestion in result['suggestions']]
134 136 assert user_name in values
135 137
138 def test_returns_inactive_users_when_active_flag_sent(self, user_util):
139 self.log_user()
140 user = user_util.create_user(is_active=False)
141 user_name = user.username
142 response = self.app.get(
143 url(controller='home', action='user_autocomplete_data',
144 user_groups='true', active='0'),
145 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
146 result = json.loads(response.body)
147 values = [suggestion['value'] for suggestion in result['suggestions']]
148 assert user_name in values
149
136 150 def test_returns_groups_when_user_groups_sent(self, user_util):
137 151 self.log_user()
138 152 group = user_util.create_user_group(user_groups_active=True)
@@ -173,8 +187,10 b' class TestUserAutocompleteData(TestContr'
173 187 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
174 188
175 189 result = json.loads(response.body)
176 users_mock.assert_called_once_with(name_contains=query)
177 groups_mock.assert_called_once_with(name_contains=query)
190 users_mock.assert_called_once_with(
191 name_contains=query, only_active=True)
192 groups_mock.assert_called_once_with(
193 name_contains=query, only_active=True)
178 194 assert len(result['suggestions']) == 20
179 195
180 196
@@ -448,7 +448,7 b' class TestLoginController:'
448 448 assert [] == whitelist['api_access_controllers_whitelist']
449 449 if test_name == 'proper_auth_token':
450 450 # use builtin if api_key is None
451 auth_token = User.get_first_admin().api_key
451 auth_token = User.get_first_super_admin().api_key
452 452
453 453 with fixture.anon_access(False):
454 454 self.app.get(url(controller='changeset',
@@ -471,7 +471,7 b' class TestLoginController:'
471 471 assert ['ChangesetController:changeset_raw'] == \
472 472 whitelist['api_access_controllers_whitelist']
473 473 if test_name == 'proper_auth_token':
474 auth_token = User.get_first_admin().api_key
474 auth_token = User.get_first_super_admin().api_key
475 475
476 476 with fixture.anon_access(False):
477 477 self.app.get(url(controller='changeset',
@@ -19,36 +19,35 b''
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
22 from pyramid.response import Response
23 from pyramid.testing import DummyRequest
24 from rhodecode.lib.middleware.disable_vcs import (
25 DisableVCSPagesWrapper, VCSServerUnavailable)
23 26
24 27
25 @pytest.mark.parametrize('url, expected_url', [
26 ('/', '/'),
27 ('/_admin/settings', '/_admin/settings'),
28 ('/_admin/i_am_fine', '/_admin/i_am_fine'),
29 ('/_admin/settings/mappings', '/error/vcs_unavailable'),
30 ('/_admin/my_account/repos', '/error/vcs_unavailable'),
31 ('/_admin/create_repository', '/error/vcs_unavailable'),
32 ('/_admin/gists/1', '/error/vcs_unavailable'),
33 ('/_admin/notifications/1', '/error/vcs_unavailable'),
28 @pytest.mark.parametrize('url, should_raise', [
29 ('/', False),
30 ('/_admin/settings', False),
31 ('/_admin/i_am_fine', False),
32 ('/_admin/settings/mappings', True),
33 ('/_admin/my_account/repos', True),
34 ('/_admin/create_repository', True),
35 ('/_admin/gists/1', True),
36 ('/_admin/notifications/1', True),
34 37 ])
35 def test_vcs_disabled(url, expected_url):
36 app = DisableVCSPagesWrapper(app=SimpleApp())
37 assert expected_url == app(get_environ(url), None)
38
38 def test_vcs_disabled(url, should_raise):
39 wrapped_view = DisableVCSPagesWrapper(pyramid_view)
40 request = DummyRequest(path=url)
39 41
40 def get_environ(url):
41 """Construct a minimum WSGI environ based on the URL."""
42 environ = {
43 'PATH_INFO': url,
44 }
45 return environ
46
42 if should_raise:
43 with pytest.raises(VCSServerUnavailable):
44 response = wrapped_view(None, request)
45 else:
46 response = wrapped_view(None, request)
47 assert response.status_int == 200
47 48
48 class SimpleApp(object):
49 def pyramid_view(context, request):
49 50 """
50 A mock app to be used in the wrapper that returns the modified URL
51 from the middleware
51 A mock pyramid view to be used in the wrapper
52 52 """
53 def __call__(self, environ, start_response):
54 return environ['PATH_INFO']
53 return Response('success')
@@ -20,7 +20,8 b''
20 20
21 21 import pytest
22 22
23 from rhodecode.lib.encrypt import AESCipher
23 from rhodecode.lib.encrypt import (
24 AESCipher, SignatureVerificationError, InvalidDecryptedValue)
24 25
25 26
26 27 class TestEncryptModule(object):
@@ -38,3 +39,38 b' class TestEncryptModule(object):'
38 39 def test_encryption(self, key, text):
39 40 enc = AESCipher(key).encrypt(text)
40 41 assert AESCipher(key).decrypt(enc) == text
42
43 def test_encryption_with_hmac(self):
44 key = 'secret'
45 text = 'ihatemysql'
46 enc = AESCipher(key, hmac=True).encrypt(text)
47 assert AESCipher(key, hmac=True).decrypt(enc) == text
48
49 def test_encryption_with_hmac_with_bad_key(self):
50 key = 'secretstring'
51 text = 'ihatemysql'
52 enc = AESCipher(key, hmac=True).encrypt(text)
53
54 with pytest.raises(SignatureVerificationError) as e:
55 assert AESCipher('differentsecret', hmac=True).decrypt(enc) == ''
56
57 assert 'Encryption signature verification failed' in str(e)
58
59 def test_encryption_with_hmac_with_bad_data(self):
60 key = 'secret'
61 text = 'ihatemysql'
62 enc = AESCipher(key, hmac=True).encrypt(text)
63 enc = 'xyz' + enc[3:]
64 with pytest.raises(SignatureVerificationError) as e:
65 assert AESCipher(key, hmac=True).decrypt(enc) == text
66
67 assert 'Encryption signature verification failed' in str(e)
68
69 def test_encryption_with_hmac_with_bad_key_not_strict(self):
70 key = 'secretstring'
71 text = 'ihatemysql'
72 enc = AESCipher(key, hmac=True).encrypt(text)
73
74 assert isinstance(AESCipher(
75 'differentsecret', hmac=True, strict_verification=False
76 ).decrypt(enc), InvalidDecryptedValue)
@@ -84,8 +84,12 b' def test_process_patterns_repo(backend, '
84 84 'pref': '#',
85 85 }
86 86 }
87
88 def get_settings_mock(self, cache=True):
89 return config
90
87 91 with mock.patch.object(IssueTrackerSettingsModel,
88 'get_settings', lambda s: config):
92 'get_settings', get_settings_mock):
89 93 processed_text = helpers.process_patterns(
90 94 text_string, repo.repo_name, config)
91 95
@@ -106,8 +110,12 b' def test_process_patterns_no_repo(text_s'
106 110 'pref': '#',
107 111 }
108 112 }
113
114 def get_settings_mock(self, cache=True):
115 return config
116
109 117 with mock.patch.object(IssueTrackerSettingsModel,
110 'get_global_settings', lambda s, cache: config):
118 'get_global_settings', get_settings_mock):
111 119 processed_text = helpers.process_patterns(
112 120 text_string, '', config)
113 121
@@ -126,8 +134,12 b' def test_process_patterns_non_existent_r'
126 134 'pref': '#',
127 135 }
128 136 }
137
138 def get_settings_mock(self, cache=True):
139 return config
140
129 141 with mock.patch.object(IssueTrackerSettingsModel,
130 'get_global_settings', lambda s, cache: config):
142 'get_global_settings', get_settings_mock):
131 143 processed_text = helpers.process_patterns(
132 144 text_string, 'do-not-exist', config)
133 145
@@ -182,10 +194,12 b' def test_get_matching_offsets(test_text,'
182 194 assert helpers.get_matching_offsets(
183 195 test_text, text_phrases) == expected_output
184 196
197
185 198 def test_normalize_text_for_matching():
186 199 assert helpers.normalize_text_for_matching(
187 200 'OJjfe)*#$*@)$JF*)3r2f80h') == 'ojjfe jf 3r2f80h'
188 201
202
189 203 def test_get_matching_line_offsets():
190 204 assert helpers.get_matching_line_offsets([
191 205 'words words words',
@@ -193,4 +207,4 b' def test_get_matching_line_offsets():'
193 207 'some text some',
194 208 'words words words',
195 209 'words words words',
196 'text here what'], 'text') == {3: [(5, 9)], 6: [(0, 4)]} No newline at end of file
210 'text here what'], 'text') == {3: [(5, 9)], 6: [(0, 4)]}
@@ -29,6 +29,7 b' import pytest'
29 29 from rhodecode.lib import caching_query
30 30 from rhodecode.lib import utils
31 31 from rhodecode.lib.utils2 import md5
32 from rhodecode.model import settings
32 33 from rhodecode.model import db
33 34 from rhodecode.model import meta
34 35 from rhodecode.model.repo import RepoModel
@@ -402,7 +403,7 b' class TestConfigDataFromDb(object):'
402 403 ]
403 404 repo_name = 'test_repo'
404 405
405 model_patch = mock.patch.object(utils, 'VcsSettingsModel')
406 model_patch = mock.patch.object(settings, 'VcsSettingsModel')
406 407 hooks_patch = mock.patch.object(
407 408 utils, 'get_enabled_hook_classes',
408 409 return_value=['pull', 'push', 'repo_size'])
@@ -432,7 +432,13 b' def test_get_repo_by_id(test, expected):'
432 432 assert _test == expected
433 433
434 434
435 def test_invalidation_context(pylonsapp):
435 @pytest.mark.parametrize("test_repo_name, repo_type", [
436 ("test_repo_1", None),
437 ("repo_group/foobar", None),
438 ("test_non_asci_ąćę", None),
439 (u"test_non_asci_unicode_ąćę", None),
440 ])
441 def test_invalidation_context(pylonsapp, test_repo_name, repo_type):
436 442 from beaker.cache import cache_region
437 443 from rhodecode.lib import caches
438 444 from rhodecode.model.db import CacheKey
@@ -442,7 +448,7 b' def test_invalidation_context(pylonsapp)'
442 448 return 'result'
443 449
444 450 invalidator_context = CacheKey.repo_context_cache(
445 _dummy_func, 'test_repo_1', 'repo')
451 _dummy_func, test_repo_name, 'repo')
446 452
447 453 with invalidator_context as context:
448 454 invalidated = context.invalidate()
@@ -452,6 +458,8 b' def test_invalidation_context(pylonsapp)'
452 458 assert 'result' == result
453 459 assert isinstance(context, caches.FreshRegionCache)
454 460
461 assert 'InvalidationContext' in repr(invalidator_context)
462
455 463 with invalidator_context as context:
456 464 context.invalidate()
457 465 result = context.compute()
@@ -58,30 +58,43 b' def stub_session():'
58 58 return session
59 59
60 60
61 def test_repo_maker_uses_session_for_classmethods(stub_session):
61 @pytest.fixture
62 def stub_session_factory(stub_session):
63 """
64 Stub of `rhodecode.lib.vcs.client_http.ThreadlocalSessionFactory`.
65 """
66 session_factory = mock.Mock()
67 session_factory.return_value = stub_session
68 return session_factory
69
70
71 def test_repo_maker_uses_session_for_classmethods(stub_session_factory):
62 72 repo_maker = client_http.RepoMaker(
63 'server_and_port', 'endpoint', stub_session)
73 'server_and_port', 'endpoint', stub_session_factory)
64 74 repo_maker.example_call()
65 stub_session.post.assert_called_with(
75 stub_session_factory().post.assert_called_with(
66 76 'http://server_and_port/endpoint', data=mock.ANY)
67 77
68 78
69 79 def test_repo_maker_uses_session_for_instance_methods(
70 stub_session, config):
80 stub_session_factory, config):
71 81 repo_maker = client_http.RepoMaker(
72 'server_and_port', 'endpoint', stub_session)
82 'server_and_port', 'endpoint', stub_session_factory)
73 83 repo = repo_maker('stub_path', config)
74 84 repo.example_call()
75 stub_session.post.assert_called_with(
85 stub_session_factory().post.assert_called_with(
76 86 'http://server_and_port/endpoint', data=mock.ANY)
77 87
78 88
89 @mock.patch('rhodecode.lib.vcs.client_http.ThreadlocalSessionFactory')
79 90 @mock.patch('rhodecode.lib.vcs.connection')
80 def test_connect_passes_in_the_same_session(connection, stub_session):
81 session_factory_patcher = mock.patch.object(
82 vcs, '_create_http_rpc_session', return_value=stub_session)
83 with session_factory_patcher:
91 def test_connect_passes_in_the_same_session(
92 connection, session_factory_class, stub_session):
93 session_factory = session_factory_class.return_value
94 session_factory.return_value = stub_session
95
84 96 vcs.connect_http('server_and_port')
85 assert connection.Hg._session == stub_session
86 assert connection.Svn._session == stub_session
87 assert connection.Git._session == stub_session
97
98 assert connection.Hg._session_factory() == stub_session
99 assert connection.Svn._session_factory() == stub_session
100 assert connection.Git._session_factory() == stub_session
@@ -69,7 +69,6 b' def pylons_compatibility_tween_factory(h'
69 69 context.rhodecode_user = auth_user
70 70 attach_context_attributes(context)
71 71 pylons.tmpl_context._push_object(context)
72
73 72 return handler(request)
74 73 finally:
75 74 # Dispose current database session and rollback uncommitted
@@ -518,7 +518,7 b' vcs.connection_timeout = 3600'
518 518 ### LOGGING CONFIGURATION ####
519 519 ################################
520 520 [loggers]
521 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates, whoosh_indexer
521 keys = root, routes, rhodecode, sqlalchemy, beaker, pyro4, templates
522 522
523 523 [handlers]
524 524 keys = console, console_sql
@@ -570,12 +570,6 b' handlers = console_sql'
570 570 qualname = sqlalchemy.engine
571 571 propagate = 0
572 572
573 [logger_whoosh_indexer]
574 level = DEBUG
575 handlers =
576 qualname = whoosh_indexer
577 propagate = 1
578
579 573 ##############
580 574 ## HANDLERS ##
581 575 ##############
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
General Comments 0
You need to be logged in to leave comments. Login now