##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r2693:660bcebf merge stable
parent child Browse files
Show More

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

@@ -0,0 +1,80 b''
1 .. _svn-path-permissions:
2
3 |svn| Enabling Path Permissions
4 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5
6 Because |RCEE| uses standard svn apache mod_svn we can take advantage of the
7 authz configuration to protect paths and branches.
8
9
10 Configuring RhodeCode
11 =====================
12
13
14 1. To configure path based permissions first we need to use a customized
15 mod_dav_svn.conf.
16
17 Open :file:`home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
18 And find `svn.proxy.config_template` setting. Now set a new path to read
19 the template from. For example:
20
21 .. code-block:: ini
22
23 svn.proxy.config_template = /home/ubuntu/rhodecode/custom_mod_dav_svn.conf.mako
24
25
26 2. Create the file as in example: `/home/ubuntu/rhodecode/custom_mod_dav_svn.conf.mako`
27 You can download one from:
28
29 `<https://code.rhodecode.com/rhodecode-enterprise-ce/files/default/rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako/>`_
30
31 3. Add (if not yet exists) a section `AuthzSVNReposRelativeAccessFile` in order
32 to read the path auth file.
33
34 Example modified config section enabling reading the authz file relative
35 to repository path. Means located in `/storage_dir/repo_name/conf/authz`
36
37 .. code-block:: text
38
39
40 # snip ...
41
42 # use specific SVN conf/authz file for each repository
43 AuthzSVNReposRelativeAccessFile authz
44
45 Allow from all
46 # snip ...
47
48 .. note::
49
50 The `AuthzSVNReposRelativeAccessFile` should go above the `Allow from all`
51 directive.
52
53
54 4. Restart RhodeCode, Go to
55 the :menuselection:`Admin --> Settings --> VCS` page, and
56 click :guilabel:`Generate Apache Config`.
57 This will now generate a new configuration with enabled changes to read
58 the authz file. You can verify if changes were made by checking the generated
59 mod_dav_svn.conf file which is included in your apache configuration.
60
61 5. Specify new rules in the repository authz configuration.
62 edit a file in :file:`repo_name/conf/authz`. For example, we specify that
63 only admin is allowed to push to develop branch
64
65 .. code-block:: ini
66
67 [/branches/develop]
68 * = r
69 admin = rw
70
71
72 For more example see:
73 `<https://svn.apache.org/repos/asf/subversion/trunk/subversion/mod_authz_svn/INSTALL/>`_
74
75 Those rules also work for paths, so not only branches but all different
76 paths inside the repository can be specified.
77
78 6. Reload Apache. If all is configured correctly it should not be allowed to
79 commit according to specified rules.
80
@@ -0,0 +1,48 b''
1 .. _views-ref:
2
3 views
4 =====
5
6 push (EE only)
7 --------------
8
9 .. py:function:: push(apiuser, repoid, remote_uri=<Optional:None>)
10
11 Triggers a push on the given repository from a remote location. You
12 can use this to keep remote repositories up-to-date.
13
14 This command can only be run using an |authtoken| with admin
15 rights to the specified repository. For more information,
16 see :ref:`config-token-ref`.
17
18 This command takes the following options:
19
20 :param apiuser: This is filled automatically from the |authtoken|.
21 :type apiuser: AuthUser
22 :param repoid: The repository name or repository ID.
23 :type repoid: str or int
24 :param remote_uri: Optional remote URI to pass in for push
25 :type remote_uri: str
26
27 Example output:
28
29 .. code-block:: bash
30
31 id : <id_given_in_input>
32 result : {
33 "msg": "Pushed to url `<remote_url>` on repo `<repository name>`"
34 "repository": "<repository name>"
35 }
36 error : null
37
38 Example error output:
39
40 .. code-block:: bash
41
42 id : <id_given_in_input>
43 result : null
44 error : {
45 "Unable to push changes to `<remote_url>`"
46 }
47
48
@@ -0,0 +1,112 b''
1 .. _config-ldap-groups-ref:
2
3 LDAP/AD With User Groups Sync
4 -----------------------------
5
6 |RCM| supports LDAP (Lightweight Directory Access Protocol) or
7 AD (active Directory) authentication.
8 All LDAP versions are supported, with the following |RCM| plugins managing each:
9
10 * For LDAP/AD with user group sync use ``LDAP + User Groups (egg:rhodecode-enterprise-ee#ldap_group)``
11
12 RhodeCode reads all data defined from plugin and creates corresponding
13 accounts on local database after receiving data from LDAP. This is done on
14 every user log-in including operations like pushing/pulling/checkout.
15 In addition group membership is read from LDAP and following operations are done:
16
17 - automatic addition of user to |RCM| user group
18 - automatic removal of user from any other |RCM| user groups not specified in LDAP.
19 The removal is done *only* on groups that are marked to be synced from ldap.
20 This setting can be changed in advanced settings on user groups
21 - automatic creation of user groups if they aren't yet existing in |RCM|
22 - marking user as super-admins if he is a member of any admin group defined in plugin settings
23
24 This plugin is available only in EE Edition.
25
26 .. important::
27
28 The email used with your |RCE| super-admin account needs to match the email
29 address attached to your admin profile in LDAP. This is because
30 within |RCE| the user email needs to be unique, and multiple users
31 cannot share an email account.
32
33 Likewise, if as an admin you also have a user account, the email address
34 attached to the user account needs to be different.
35
36
37 LDAP Configuration Steps
38 ^^^^^^^^^^^^^^^^^^^^^^^^
39
40 To configure |LDAP|, use the following steps:
41
42 1. From the |RCM| interface, select
43 :menuselection:`Admin --> Authentication`
44 2. Enable the ldap+ groups plugin and select :guilabel:`Save`
45 3. Select the :guilabel:`Enabled` check box in the plugin configuration section
46 4. Add the required LDAP information and :guilabel:`Save`, for more details,
47 see :ref:`config-ldap-groups-examples`
48
49 For a more detailed description of LDAP objects, see :ref:`ldap-gloss-ref`:
50
51 .. _config-ldap-groups-examples:
52
53 Example LDAP configuration
54 ^^^^^^^^^^^^^^^^^^^^^^^^^^
55 .. code-block:: bash
56
57 # Auth Cache TTL, Defines the caching for authentication to offload LDAP server.
58 # This means that cache result will be saved for 3600 before contacting LDAP server to verify the user access
59 3600
60 # Host, comma seperated format is optionally possible to specify more than 1 server
61 https://ldap1.server.com/ldap-admin/,https://ldap2.server.com/ldap-admin/
62 # Default LDAP Port, use 689 for LDAPS
63 389
64 # Account, used for SimpleBind if LDAP server requires an authentication
65 e.g admin@server.com
66 # Password used for simple bind
67 ldap-user-password
68 # LDAP connection security
69 LDAPS
70 # Certificate checks level
71 DEMAND
72 # Base DN
73 cn=Rufus Magillacuddy,ou=users,dc=rhodecode,dc=com
74 # User Search Base
75 ou=groups,ou=users
76 # LDAP search filter to narrow the results
77 (objectClass=person)
78 # LDAP search scope
79 SUBTREE
80 # Login attribute
81 sAMAccountName
82 # First Name Attribute to read
83 givenName
84 # Last Name Attribute to read
85 sn
86 # Email Attribute to read email address from
87 mail
88 # group extraction method
89 rfc2307bis
90 # Group search base
91 ou=RC-Groups
92 # Group Name Attribute, field to read the group name from
93 sAMAAccountName
94 # User Member of Attribute, field in which groups are stored
95 memberOf
96 # LDAP Group Search Filter, allows narrowing the results
97
98 # Admin Groups. Comma separated list of groups. If user is member of
99 # any of those he will be marked as super-admin in RhodeCode
100 admins, management
101
102
103 Below is example setup that can be used with Active Directory and ldap groups.
104
105 .. image:: ../images/ldap-groups-example.png
106 :alt: LDAP/AD setup example
107 :scale: 50 %
108
109 .. toctree::
110
111 ldap-active-directory
112 ldap-authentication No newline at end of file
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,139 b''
1 |RCE| 4.12.0 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2018-04-24
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - Svn: added support for RhodeCode integration framework. All integrations like
14 slack, email, Jenkins now also fully work for SVN.
15 - Integrations: added new dedicated Jenkins integration with the support of
16 CSRF authentication. Available in EE edition only.
17 - Automation: added new bi-directional remote sync. RhodeCode instances can now
18 automatically push or pull from/to remote locations. This feature is powered
19 by the Scheduler of 4.11 release, and it is required to be enabled for this feature to work.
20 Available in EE edition only.
21 - Mercurial: path-based permissions. RhodeCode can now use Mercurials narrowhg
22 to implement path-based permissions. All permissions are read from .hg/hgacl.
23 Thanks to the great contribution from Sandu Turcan.
24 - VCS: added new diff caches. Available as an option under vcs settings.
25 Diff caches work on pull-request, or individual commits for greater
26 performance and reduced memory usage. This feature increases speed of large
27 pull requests significantly. In addition for pull requests it will allow
28 showing old closed pull requests even if commits from source were removed,
29 further enhancing auditing capabilities.
30 - Audit: added few new audit log entries especially around changing permissions.
31 - LDAP: added connection pinning and timeout option to ldap plugin. This should
32 prevent problems when connection to LDAP is not stable causing RhodeCode
33 instances to freeze waiting on LDAP connections.
34 - User groups: expose public user group profiles. Allows to see members of a user
35 groups by other team members, if they have proper permissions.
36 - UI: show pull request page in quick nav menu on my account for quicker access.
37 - UI: hidden/outdated comments now have visible markers next to line numbers.
38 This allows access to them without showing all hidden comments.
39
40
41 General
42 ^^^^^^^
43
44 - Ssh: show conflicting fingerprint when adding an already existing key.
45 Helps to track why adding a key failed.
46 - System info: added ulimit to system info. This is causing lots of problems
47 when we hit any of those limits, that is why it's important to show this.
48 - Repository settings: add hidden view to force re-install hooks.
49 Available under /{repo_name}/settings/advanced/hooks
50 - Integrations: Webhook now handles response errors and show response for
51 easier debugging.
52 - Cli: speed up CLI execution start by skipping auth plugin search/registry.
53 - SVN: added an example in the docs on how to enable path-based permissions.
54 - LDAP: enable connection recycling on LDAP plugin.
55 - Auth plugins: use a nicer visual display of auth plugins that would
56 highlight that order of enabled plugins does matter.
57 - Events: expose shadow repo build url.
58 - Events: expose pull request title and uid in event data.
59 - API: enable setting sync flag for user groups on create/edit.
60 - API: update pull method with a possible specification of the url
61 - Logging: improved consistency of auth plugins logs.
62 - Logging: improved log for ssl required
63 - Dependencies: bumped mercurial to 4.4 series
64 - Dependencies: bumped zope.cachedescriptors==4.3.1
65 - Dependencies: bumped zope.deprecation==4.3.0
66 - Dependencies: bumped zope.event==4.3.0
67 - Dependencies: bumped zope.interface==4.4.3
68 - Dependencies: bumped graphviz 0.8.2
69 - Dependencies: bumped to ipaddress 0.1.19
70 - Dependencies: bumped pyexpect to 4.3.1
71 - Dependencies: bumped ws4py to 0.4.3
72 - Dependencies: bumped bleach to 2.1.2
73 - Dependencies: bumped html5lib 1.0.1
74 - Dependencies: bumped greenlet to 0.4.13
75 - Dependencies: bumped markdown to 2.6.11
76 - Dependencies: bumped psutil to 5.4.3
77 - Dependencies: bumped beaker to 1.9.1
78 - Dependencies: bumped alembic to 0.6.8 release.
79 - Dependencies: bumped supervisor to 3.3.4
80 - Dependencies: bumped pyexpect to 4.4.0 and scandir to 1.7
81 - Dependencies: bumped appenlight client to 0.6.25
82 - Dependencies: don't require full mysql lib for the db driver.
83 Reduces installation package size by around 100MB.
84
85
86 Security
87 ^^^^^^^^
88
89 - My account: changing email in my account now requires providing user
90 access password. This is a case for only RhodeCode built-in accounts.
91 Prevents adding recovery email by unauthorized users who gain
92 access to logged in session of user.
93 - Logging: fix leaking of tokens to logging.
94 - General: serialize the repo name in repo checks to prevent potential
95 html injections by providing a malformed url.
96
97
98 Performance
99 ^^^^^^^^^^^
100
101 - Diffs: don't use recurred diffset attachment in diffs. This makes
102 this structure much harder to garbage collect. Reduces memory usage.
103 - Diff cache: added caching for better performance of large pull requests.
104
105
106 Fixes
107 ^^^^^
108
109 - Age helper: fix issues with proper timezone detection for certain timezones.
110 Fixes wrong age display in few cases.
111 - API: added audit logs for user group related calls that were
112 accidentally missing.
113 - Diffs: fix and improve line selections and anchor links.
114 - Pull requests: fixed cases with default expected refs are closed or unavailable.
115 For Mercurial with closed default branch a compare across forks could fail.
116 - Core: properly report 502 errors for gevent and gunicorn.
117 Gevent wtih Gunicorn doesn't raise normal pycurl errors.
118 - Auth plugins: fixed problem with cache of settings in multi-worker mode.
119 The previous implementation had a bug that cached the settings in each class,
120 caused not refreshing the update of settings in multi-worker mode.
121 Only restart of RhodeCode loaded new settings.
122 - Audit logs: properly handle query syntax in the search field.
123 - Repositories: better handling of missing requirements errors for repositories.
124 - API: fixed problems with repository fork/create using celery backend.
125 - VCS settings: added missing flash message on validation errors to prevent
126 missing out some field input validation problems.
127
128
129 Upgrade notes
130 ^^^^^^^^^^^^^
131
132 - This release adds support for SVN hook. This required lots of changes on how we
133 handle SVN protocol. We did thoughtful tests for SVN compatibility.
134 Please be advised to check the behaviour of SVN repositories during this update.
135
136 - Diff caches are turned off by default for backward compatibility. We however recommend
137 turning them on either individually for bigger repositories or globally for every repository.
138 This setting can be found in admin > settings > vcs, or repository > settings > vcs
139
@@ -0,0 +1,20 b''
1 diff -rup Beaker-1.9.1-orig/beaker/container.py Beaker-1.9.1/beaker/container.py
2 --- Beaker-1.9.1-orig/beaker/container.py 2018-04-10 10:23:04.000000000 +0200
3 +++ Beaker-1.9.1/beaker/container.py 2018-04-10 10:23:34.000000000 +0200
4 @@ -353,13 +353,13 @@ class Value(object):
5 debug("get_value returning old value while new one is created")
6 return value
7 else:
8 - debug("lock_creatfunc (didnt wait)")
9 + debug("lock_creatfunc `%s` (didnt wait)", self.createfunc.__name__)
10 has_createlock = True
11
12 if not has_createlock:
13 - debug("lock_createfunc (waiting)")
14 + debug("lock_createfunc `%s` (waiting)", self.createfunc.__name__)
15 creation_lock.acquire()
16 - debug("lock_createfunc (waited)")
17 + debug("lock_createfunc `%s` (waited)", self.createfunc.__name__)
18
19 try:
20 # see if someone created the value already
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,6 +1,6 b''
1 1 [bumpversion]
2 current_version = 4.11.6
2 current_version = 4.12.0
3 3 message = release: Bump version {current_version} to {new_version}
4 4
5 5 [bumpversion:file:rhodecode/VERSION]
6 6
@@ -1,33 +1,28 b''
1 1 [DEFAULT]
2 2 done = false
3 3
4 4 [task:bump_version]
5 5 done = true
6 6
7 7 [task:rc_tools_pinned]
8 done = true
9 8
10 9 [task:fixes_on_stable]
11 done = true
12 10
13 11 [task:pip2nix_generated]
14 done = true
15 12
16 13 [task:changelog_updated]
17 done = true
18 14
19 15 [task:generate_api_docs]
20 done = true
16
17 [task:updated_translation]
21 18
22 19 [release]
23 state = prepared
24 version = 4.11.6
25
26 [task:updated_translation]
20 state = in_progress
21 version = 4.12.0
27 22
28 23 [task:generate_js_routes]
29 24
30 25 [task:updated_trial_license]
31 26
32 27 [task:generate_oss_licenses]
33 28
@@ -1,54 +1,51 b''
1 1 # top level files
2 2
3 3 include MANIFEST.in
4 4 include README.rst
5 5 include CHANGES.rst
6 6 include LICENSE.txt
7 7
8 8 include rhodecode/VERSION
9 9
10 10 # docs
11 11 recursive-include docs *
12 12
13 13 # all config files
14 14 recursive-include configs *
15 15
16 16 # translations
17 17 recursive-include rhodecode/i18n *
18 18
19 # hook templates
20 recursive-include rhodecode/config/hook_templates *
21
22 19 # non-python core stuff
23 20 recursive-include rhodecode *.cfg
24 21 recursive-include rhodecode *.json
25 22 recursive-include rhodecode *.ini_tmpl
26 23 recursive-include rhodecode *.sh
27 24 recursive-include rhodecode *.mako
28 25
29 26 # 502 page
30 27 include rhodecode/public/502.html
31 28
32 29
33 30 # images, css
34 31 include rhodecode/public/css/*.css
35 32 include rhodecode/public/images/*.*
36 33 include rhodecode/public/images/ee_features/*.*
37 34
38 35 # sound files
39 36 include rhodecode/public/sounds/*.mp3
40 37 include rhodecode/public/sounds/*.wav
41 38
42 39 # fonts
43 40 recursive-include rhodecode/public/fonts/ProximaNova *
44 41 recursive-include rhodecode/public/fonts/RCIcons *
45 42
46 43 # js
47 44 recursive-include rhodecode/public/js *
48 45
49 46 # templates
50 47 recursive-include rhodecode/templates *
51 48
52 49 # skip any tests files
53 50 recursive-exclude rhodecode/tests *
54 51
@@ -1,722 +1,719 b''
1 1
2 2
3 3 ################################################################################
4 4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10
11 11 ################################################################################
12 12 ## EMAIL CONFIGURATION ##
13 13 ## Uncomment and replace with the email address which should receive ##
14 14 ## any error reports after an application crash ##
15 15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 16 ################################################################################
17 17
18 18 ## prefix all emails subjects with given prefix, helps filtering out emails
19 19 #email_prefix = [RhodeCode]
20 20
21 21 ## email FROM address all mails will be sent
22 22 #app_email_from = rhodecode-noreply@localhost
23 23
24 24 ## Uncomment and replace with the address which should receive any error report
25 25 ## note: using appenlight for error handling doesn't need this to be uncommented
26 26 #email_to = admin@localhost
27 27
28 28 ## in case of Application errors, sent an error email form
29 29 #error_email_from = rhodecode_error@localhost
30 30
31 31 ## additional error message to be send in case of server crash
32 32 #error_message =
33 33
34 34
35 35 #smtp_server = mail.server.com
36 36 #smtp_username =
37 37 #smtp_password =
38 38 #smtp_port =
39 39 #smtp_use_tls = false
40 40 #smtp_use_ssl = true
41 41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
42 42 #smtp_auth =
43 43
44 44 [server:main]
45 45 ## COMMON ##
46 46 host = 127.0.0.1
47 47 port = 5000
48 48
49 49 ##################################
50 50 ## WAITRESS WSGI SERVER ##
51 51 ## Recommended for Development ##
52 52 ##################################
53 53
54 54 use = egg:waitress#main
55 55 ## number of worker threads
56 56 threads = 5
57 57 ## MAX BODY SIZE 100GB
58 58 max_request_body_size = 107374182400
59 59 ## Use poll instead of select, fixes file descriptors limits problems.
60 60 ## May not work on old windows systems.
61 61 asyncore_use_poll = true
62 62
63 63
64 64 ##########################
65 65 ## GUNICORN WSGI SERVER ##
66 66 ##########################
67 67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
68 68
69 69 #use = egg:gunicorn#main
70 70 ## Sets the number of process workers. You must set `instance_id = *`
71 71 ## when this option is set to more than one worker, recommended
72 72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
73 73 ## The `instance_id = *` must be set in the [app:main] section below
74 74 #workers = 2
75 75 ## number of threads for each of the worker, must be set to 1 for gevent
76 76 ## generally recommended to be at 1
77 77 #threads = 1
78 78 ## process name
79 79 #proc_name = rhodecode
80 80 ## type of worker class, one of sync, gevent
81 81 ## recommended for bigger setup is using of of other than sync one
82 82 #worker_class = gevent
83 83 ## The maximum number of simultaneous clients. Valid only for Gevent
84 84 #worker_connections = 10
85 85 ## max number of requests that worker will handle before being gracefully
86 86 ## restarted, could prevent memory leaks
87 87 #max_requests = 1000
88 88 #max_requests_jitter = 30
89 89 ## amount of time a worker can spend with handling a request before it
90 90 ## gets killed and restarted. Set to 6hrs
91 91 #timeout = 21600
92 92
93 93
94 94 ## prefix middleware for RhodeCode.
95 95 ## recommended when using proxy setup.
96 96 ## allows to set RhodeCode under a prefix in server.
97 97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
98 98 ## And set your prefix like: `prefix = /custom_prefix`
99 99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
100 100 ## to make your cookies only work on prefix url
101 101 [filter:proxy-prefix]
102 102 use = egg:PasteDeploy#prefix
103 103 prefix = /
104 104
105 105 [app:main]
106 106 use = egg:rhodecode-enterprise-ce
107 107
108 108 ## enable proxy prefix middleware, defined above
109 109 #filter-with = proxy-prefix
110 110
111 111 # During development the we want to have the debug toolbar enabled
112 112 pyramid.includes =
113 113 pyramid_debugtoolbar
114 114 rhodecode.lib.middleware.request_wrapper
115 115
116 116 pyramid.reload_templates = true
117 117
118 118 debugtoolbar.hosts = 0.0.0.0/0
119 119 debugtoolbar.exclude_prefixes =
120 120 /css
121 121 /fonts
122 122 /images
123 123 /js
124 124
125 125 ## RHODECODE PLUGINS ##
126 126 rhodecode.includes =
127 127 rhodecode.api
128 128
129 129
130 130 # api prefix url
131 131 rhodecode.api.url = /_admin/api
132 132
133 133
134 134 ## END RHODECODE PLUGINS ##
135 135
136 136 ## encryption key used to encrypt social plugin tokens,
137 137 ## remote_urls with credentials etc, if not set it defaults to
138 138 ## `beaker.session.secret`
139 139 #rhodecode.encrypted_values.secret =
140 140
141 141 ## decryption strict mode (enabled by default). It controls if decryption raises
142 142 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
143 143 #rhodecode.encrypted_values.strict = false
144 144
145 145 ## return gzipped responses from Rhodecode (static files/application)
146 146 gzip_responses = false
147 147
148 148 ## autogenerate javascript routes file on startup
149 149 generate_js_files = false
150 150
151 151 ## Optional Languages
152 152 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
153 153 lang = en
154 154
155 155 ## perform a full repository scan on each server start, this should be
156 156 ## set to false after first startup, to allow faster server restarts.
157 157 startup.import_repos = false
158 158
159 159 ## Uncomment and set this path to use archive download cache.
160 160 ## Once enabled, generated archives will be cached at this location
161 161 ## and served from the cache during subsequent requests for the same archive of
162 162 ## the repository.
163 163 #archive_cache_dir = /tmp/tarballcache
164 164
165 165 ## URL at which the application is running. This is used for bootstraping
166 166 ## requests in context when no web request is available. Used in ishell, or
167 167 ## SSH calls. Set this for events to receive proper url for SSH calls.
168 168 app.base_url = http://rhodecode.local
169 169
170 170 ## change this to unique ID for security
171 171 app_instance_uuid = rc-production
172 172
173 173 ## cut off limit for large diffs (size in bytes). If overall diff size on
174 174 ## commit, or pull request exceeds this limit this diff will be displayed
175 175 ## partially. E.g 512000 == 512Kb
176 176 cut_off_limit_diff = 512000
177 177
178 178 ## cut off limit for large files inside diffs (size in bytes). Each individual
179 179 ## file inside diff which exceeds this limit will be displayed partially.
180 180 ## E.g 128000 == 128Kb
181 181 cut_off_limit_file = 128000
182 182
183 183 ## use cache version of scm repo everywhere
184 184 vcs_full_cache = true
185 185
186 186 ## force https in RhodeCode, fixes https redirects, assumes it's always https
187 187 ## Normally this is controlled by proper http flags sent from http server
188 188 force_https = false
189 189
190 190 ## use Strict-Transport-Security headers
191 191 use_htsts = false
192 192
193 ## number of commits stats will parse on each iteration
194 commit_parse_limit = 25
195
196 193 ## git rev filter option, --all is the default filter, if you need to
197 194 ## hide all refs in changelog switch this to --branches --tags
198 195 git_rev_filter = --branches --tags
199 196
200 197 # Set to true if your repos are exposed using the dumb protocol
201 198 git_update_server_info = false
202 199
203 200 ## RSS/ATOM feed options
204 201 rss_cut_off_limit = 256000
205 202 rss_items_per_page = 10
206 203 rss_include_diff = false
207 204
208 205 ## gist URL alias, used to create nicer urls for gist. This should be an
209 206 ## url that does rewrites to _admin/gists/{gistid}.
210 207 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
211 208 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
212 209 gist_alias_url =
213 210
214 211 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
215 212 ## used for access.
216 213 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
217 214 ## came from the the logged in user who own this authentication token.
218 215 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
219 216 ## authentication token. Such view would be only accessible when used together
220 217 ## with this authentication token
221 218 ##
222 219 ## list of all views can be found under `/_admin/permissions/auth_token_access`
223 220 ## The list should be "," separated and on a single line.
224 221 ##
225 222 ## Most common views to enable:
226 223 # RepoCommitsView:repo_commit_download
227 224 # RepoCommitsView:repo_commit_patch
228 225 # RepoCommitsView:repo_commit_raw
229 226 # RepoCommitsView:repo_commit_raw@TOKEN
230 227 # RepoFilesView:repo_files_diff
231 228 # RepoFilesView:repo_archivefile
232 229 # RepoFilesView:repo_file_raw
233 230 # GistView:*
234 231 api_access_controllers_whitelist =
235 232
236 233 ## default encoding used to convert from and to unicode
237 234 ## can be also a comma separated list of encoding in case of mixed encodings
238 235 default_encoding = UTF-8
239 236
240 237 ## instance-id prefix
241 238 ## a prefix key for this instance used for cache invalidation when running
242 239 ## multiple instances of rhodecode, make sure it's globally unique for
243 240 ## all running rhodecode instances. Leave empty if you don't use it
244 241 instance_id =
245 242
246 243 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
247 244 ## of an authentication plugin also if it is disabled by it's settings.
248 245 ## This could be useful if you are unable to log in to the system due to broken
249 246 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
250 247 ## module to log in again and fix the settings.
251 248 ##
252 249 ## Available builtin plugin IDs (hash is part of the ID):
253 250 ## egg:rhodecode-enterprise-ce#rhodecode
254 251 ## egg:rhodecode-enterprise-ce#pam
255 252 ## egg:rhodecode-enterprise-ce#ldap
256 253 ## egg:rhodecode-enterprise-ce#jasig_cas
257 254 ## egg:rhodecode-enterprise-ce#headers
258 255 ## egg:rhodecode-enterprise-ce#crowd
259 256 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
260 257
261 258 ## alternative return HTTP header for failed authentication. Default HTTP
262 259 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
263 260 ## handling that causing a series of failed authentication calls.
264 261 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
265 262 ## This will be served instead of default 401 on bad authnetication
266 263 auth_ret_code =
267 264
268 265 ## use special detection method when serving auth_ret_code, instead of serving
269 266 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
270 267 ## and then serve auth_ret_code to clients
271 268 auth_ret_code_detection = false
272 269
273 270 ## locking return code. When repository is locked return this HTTP code. 2XX
274 271 ## codes don't break the transactions while 4XX codes do
275 272 lock_ret_code = 423
276 273
277 274 ## allows to change the repository location in settings page
278 275 allow_repo_location_change = true
279 276
280 277 ## allows to setup custom hooks in settings page
281 278 allow_custom_hooks_settings = true
282 279
283 280 ## generated license token, goto license page in RhodeCode settings to obtain
284 281 ## new token
285 282 license_token =
286 283
287 284 ## supervisor connection uri, for managing supervisor and logs.
288 285 supervisor.uri =
289 286 ## supervisord group name/id we only want this RC instance to handle
290 287 supervisor.group_id = dev
291 288
292 289 ## Display extended labs settings
293 290 labs_settings_active = true
294 291
295 292 ####################################
296 293 ### CELERY CONFIG ####
297 294 ####################################
298 295 ## run: /path/to/celery worker \
299 296 ## -E --beat --app rhodecode.lib.celerylib.loader \
300 297 ## --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler \
301 298 ## --loglevel DEBUG --ini /path/to/rhodecode.ini
302 299
303 300 use_celery = false
304 301
305 302 ## connection url to the message broker (default rabbitmq)
306 303 celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
307 304
308 305 ## maximum tasks to execute before worker restart
309 306 celery.max_tasks_per_child = 100
310 307
311 308 ## tasks will never be sent to the queue, but executed locally instead.
312 309 celery.task_always_eager = false
313 310
314 311 ####################################
315 312 ### BEAKER CACHE ####
316 313 ####################################
317 314 # default cache dir for templates. Putting this into a ramdisk
318 315 ## can boost performance, eg. %(here)s/data_ramdisk
319 316 cache_dir = %(here)s/data
320 317
321 318 ## locking and default file storage for Beaker. Putting this into a ramdisk
322 319 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
323 320 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
324 321 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
325 322
326 323 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
327 324
328 325 beaker.cache.super_short_term.type = memory
329 326 beaker.cache.super_short_term.expire = 10
330 327 beaker.cache.super_short_term.key_length = 256
331 328
332 329 beaker.cache.short_term.type = memory
333 330 beaker.cache.short_term.expire = 60
334 331 beaker.cache.short_term.key_length = 256
335 332
336 333 beaker.cache.long_term.type = memory
337 334 beaker.cache.long_term.expire = 36000
338 335 beaker.cache.long_term.key_length = 256
339 336
340 337 beaker.cache.sql_cache_short.type = memory
341 338 beaker.cache.sql_cache_short.expire = 10
342 339 beaker.cache.sql_cache_short.key_length = 256
343 340
344 341 ## default is memory cache, configure only if required
345 342 ## using multi-node or multi-worker setup
346 343 #beaker.cache.auth_plugins.type = ext:database
347 344 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
348 345 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
349 346 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
350 347 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
351 348 #beaker.cache.auth_plugins.sa.pool_size = 10
352 349 #beaker.cache.auth_plugins.sa.max_overflow = 0
353 350
354 351 beaker.cache.repo_cache_long.type = memorylru_base
355 352 beaker.cache.repo_cache_long.max_items = 4096
356 353 beaker.cache.repo_cache_long.expire = 2592000
357 354
358 355 ## default is memorylru_base cache, configure only if required
359 356 ## using multi-node or multi-worker setup
360 357 #beaker.cache.repo_cache_long.type = ext:memcached
361 358 #beaker.cache.repo_cache_long.url = localhost:11211
362 359 #beaker.cache.repo_cache_long.expire = 1209600
363 360 #beaker.cache.repo_cache_long.key_length = 256
364 361
365 362 ####################################
366 363 ### BEAKER SESSION ####
367 364 ####################################
368 365
369 366 ## .session.type is type of storage options for the session, current allowed
370 367 ## types are file, ext:memcached, ext:database, and memory (default).
371 368 beaker.session.type = file
372 369 beaker.session.data_dir = %(here)s/data/sessions/data
373 370
374 371 ## db based session, fast, and allows easy management over logged in users
375 372 #beaker.session.type = ext:database
376 373 #beaker.session.table_name = db_session
377 374 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
378 375 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
379 376 #beaker.session.sa.pool_recycle = 3600
380 377 #beaker.session.sa.echo = false
381 378
382 379 beaker.session.key = rhodecode
383 380 beaker.session.secret = develop-rc-uytcxaz
384 381 beaker.session.lock_dir = %(here)s/data/sessions/lock
385 382
386 383 ## Secure encrypted cookie. Requires AES and AES python libraries
387 384 ## you must disable beaker.session.secret to use this
388 385 #beaker.session.encrypt_key = key_for_encryption
389 386 #beaker.session.validate_key = validation_key
390 387
391 388 ## sets session as invalid(also logging out user) if it haven not been
392 389 ## accessed for given amount of time in seconds
393 390 beaker.session.timeout = 2592000
394 391 beaker.session.httponly = true
395 392 ## Path to use for the cookie. Set to prefix if you use prefix middleware
396 393 #beaker.session.cookie_path = /custom_prefix
397 394
398 395 ## uncomment for https secure cookie
399 396 beaker.session.secure = false
400 397
401 398 ## auto save the session to not to use .save()
402 399 beaker.session.auto = false
403 400
404 401 ## default cookie expiration time in seconds, set to `true` to set expire
405 402 ## at browser close
406 403 #beaker.session.cookie_expires = 3600
407 404
408 405 ###################################
409 406 ## SEARCH INDEXING CONFIGURATION ##
410 407 ###################################
411 408 ## Full text search indexer is available in rhodecode-tools under
412 409 ## `rhodecode-tools index` command
413 410
414 411 ## WHOOSH Backend, doesn't require additional services to run
415 412 ## it works good with few dozen repos
416 413 search.module = rhodecode.lib.index.whoosh
417 414 search.location = %(here)s/data/index
418 415
419 416 ########################################
420 417 ### CHANNELSTREAM CONFIG ####
421 418 ########################################
422 419 ## channelstream enables persistent connections and live notification
423 420 ## in the system. It's also used by the chat system
424 421 channelstream.enabled = false
425 422
426 423 ## server address for channelstream server on the backend
427 424 channelstream.server = 127.0.0.1:9800
428 425
429 426 ## location of the channelstream server from outside world
430 427 ## use ws:// for http or wss:// for https. This address needs to be handled
431 428 ## by external HTTP server such as Nginx or Apache
432 429 ## see nginx/apache configuration examples in our docs
433 430 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
434 431 channelstream.secret = secret
435 432 channelstream.history.location = %(here)s/channelstream_history
436 433
437 434 ## Internal application path that Javascript uses to connect into.
438 435 ## If you use proxy-prefix the prefix should be added before /_channelstream
439 436 channelstream.proxy_path = /_channelstream
440 437
441 438
442 439 ###################################
443 440 ## APPENLIGHT CONFIG ##
444 441 ###################################
445 442
446 443 ## Appenlight is tailored to work with RhodeCode, see
447 444 ## http://appenlight.com for details how to obtain an account
448 445
449 446 ## appenlight integration enabled
450 447 appenlight = false
451 448
452 449 appenlight.server_url = https://api.appenlight.com
453 450 appenlight.api_key = YOUR_API_KEY
454 451 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
455 452
456 453 # used for JS client
457 454 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
458 455
459 456 ## TWEAK AMOUNT OF INFO SENT HERE
460 457
461 458 ## enables 404 error logging (default False)
462 459 appenlight.report_404 = false
463 460
464 461 ## time in seconds after request is considered being slow (default 1)
465 462 appenlight.slow_request_time = 1
466 463
467 464 ## record slow requests in application
468 465 ## (needs to be enabled for slow datastore recording and time tracking)
469 466 appenlight.slow_requests = true
470 467
471 468 ## enable hooking to application loggers
472 469 appenlight.logging = true
473 470
474 471 ## minimum log level for log capture
475 472 appenlight.logging.level = WARNING
476 473
477 474 ## send logs only from erroneous/slow requests
478 475 ## (saves API quota for intensive logging)
479 476 appenlight.logging_on_error = false
480 477
481 478 ## list of additonal keywords that should be grabbed from environ object
482 479 ## can be string with comma separated list of words in lowercase
483 480 ## (by default client will always send following info:
484 481 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
485 482 ## start with HTTP* this list be extended with additional keywords here
486 483 appenlight.environ_keys_whitelist =
487 484
488 485 ## list of keywords that should be blanked from request object
489 486 ## can be string with comma separated list of words in lowercase
490 487 ## (by default client will always blank keys that contain following words
491 488 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
492 489 ## this list be extended with additional keywords set here
493 490 appenlight.request_keys_blacklist =
494 491
495 492 ## list of namespaces that should be ignores when gathering log entries
496 493 ## can be string with comma separated list of namespaces
497 494 ## (by default the client ignores own entries: appenlight_client.client)
498 495 appenlight.log_namespace_blacklist =
499 496
500 497
501 498 ################################################################################
502 499 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
503 500 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
504 501 ## execute malicious code after an exception is raised. ##
505 502 ################################################################################
506 503 #set debug = false
507 504
508 505
509 506 ##############
510 507 ## STYLING ##
511 508 ##############
512 509 debug_style = true
513 510
514 511 ###########################################
515 512 ### MAIN RHODECODE DATABASE CONFIG ###
516 513 ###########################################
517 514 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
518 515 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
519 516 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
520 517 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
521 518
522 519 # see sqlalchemy docs for other advanced settings
523 520
524 521 ## print the sql statements to output
525 522 sqlalchemy.db1.echo = false
526 523 ## recycle the connections after this amount of seconds
527 524 sqlalchemy.db1.pool_recycle = 3600
528 525 sqlalchemy.db1.convert_unicode = true
529 526
530 527 ## the number of connections to keep open inside the connection pool.
531 528 ## 0 indicates no limit
532 529 #sqlalchemy.db1.pool_size = 5
533 530
534 531 ## the number of connections to allow in connection pool "overflow", that is
535 532 ## connections that can be opened above and beyond the pool_size setting,
536 533 ## which defaults to five.
537 534 #sqlalchemy.db1.max_overflow = 10
538 535
539 536
540 537 ##################
541 538 ### VCS CONFIG ###
542 539 ##################
543 540 vcs.server.enable = true
544 541 vcs.server = localhost:9900
545 542
546 543 ## Web server connectivity protocol, responsible for web based VCS operatations
547 544 ## Available protocols are:
548 545 ## `http` - use http-rpc backend (default)
549 546 vcs.server.protocol = http
550 547
551 548 ## Push/Pull operations protocol, available options are:
552 549 ## `http` - use http-rpc backend (default)
553 550 ##
554 551 vcs.scm_app_implementation = http
555 552
556 553 ## Push/Pull operations hooks protocol, available options are:
557 554 ## `http` - use http-rpc backend (default)
558 555 vcs.hooks.protocol = http
559 556
560 557 vcs.server.log_level = debug
561 558 ## Start VCSServer with this instance as a subprocess, usefull for development
562 559 vcs.start_server = false
563 560
564 561 ## List of enabled VCS backends, available options are:
565 562 ## `hg` - mercurial
566 563 ## `git` - git
567 564 ## `svn` - subversion
568 565 vcs.backends = hg, git, svn
569 566
570 567 vcs.connection_timeout = 3600
571 568 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
572 569 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
573 570 #vcs.svn.compatible_version = pre-1.8-compatible
574 571
575 572
576 573 ############################################################
577 574 ### Subversion proxy support (mod_dav_svn) ###
578 575 ### Maps RhodeCode repo groups into SVN paths for Apache ###
579 576 ############################################################
580 577 ## Enable or disable the config file generation.
581 578 svn.proxy.generate_config = false
582 579 ## Generate config file with `SVNListParentPath` set to `On`.
583 580 svn.proxy.list_parent_path = true
584 581 ## Set location and file name of generated config file.
585 582 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
586 583 ## alternative mod_dav config template. This needs to be a mako template
587 584 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
588 585 ## Used as a prefix to the `Location` block in the generated config file.
589 586 ## In most cases it should be set to `/`.
590 587 svn.proxy.location_root = /
591 588 ## Command to reload the mod dav svn configuration on change.
592 589 ## Example: `/etc/init.d/apache2 reload`
593 590 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
594 591 ## If the timeout expires before the reload command finishes, the command will
595 592 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
596 593 #svn.proxy.reload_timeout = 10
597 594
598 595 ############################################################
599 596 ### SSH Support Settings ###
600 597 ############################################################
601 598
602 599 ## Defines if a custom authorized_keys file should be created and written on
603 600 ## any change user ssh keys. Setting this to false also disables posibility
604 601 ## of adding SSH keys by users from web interface. Super admins can still
605 602 ## manage SSH Keys.
606 603 ssh.generate_authorized_keyfile = false
607 604
608 605 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
609 606 # ssh.authorized_keys_ssh_opts =
610 607
611 608 ## Path to the authrozied_keys file where the generate entries are placed.
612 609 ## It is possible to have multiple key files specified in `sshd_config` e.g.
613 610 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
614 611 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
615 612
616 613 ## Command to execute the SSH wrapper. The binary is available in the
617 614 ## rhodecode installation directory.
618 615 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
619 616 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
620 617
621 618 ## Allow shell when executing the ssh-wrapper command
622 619 ssh.wrapper_cmd_allow_shell = false
623 620
624 621 ## Enables logging, and detailed output send back to the client during SSH
625 622 ## operations. Usefull for debugging, shouldn't be used in production.
626 623 ssh.enable_debug_logging = true
627 624
628 625 ## Paths to binary executable, by default they are the names, but we can
629 626 ## override them if we want to use a custom one
630 627 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
631 628 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
632 629 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
633 630
634 631
635 632 ## Dummy marker to add new entries after.
636 633 ## Add any custom entries below. Please don't remove.
637 634 custom.conf = 1
638 635
639 636
640 637 ################################
641 638 ### LOGGING CONFIGURATION ####
642 639 ################################
643 640 [loggers]
644 641 keys = root, sqlalchemy, beaker, rhodecode, ssh_wrapper, celery
645 642
646 643 [handlers]
647 644 keys = console, console_sql
648 645
649 646 [formatters]
650 647 keys = generic, color_formatter, color_formatter_sql
651 648
652 649 #############
653 650 ## LOGGERS ##
654 651 #############
655 652 [logger_root]
656 653 level = NOTSET
657 654 handlers = console
658 655
659 656 [logger_sqlalchemy]
660 657 level = INFO
661 658 handlers = console_sql
662 659 qualname = sqlalchemy.engine
663 660 propagate = 0
664 661
665 662 [logger_beaker]
666 663 level = DEBUG
667 664 handlers =
668 665 qualname = beaker.container
669 666 propagate = 1
670 667
671 668 [logger_rhodecode]
672 669 level = DEBUG
673 670 handlers =
674 671 qualname = rhodecode
675 672 propagate = 1
676 673
677 674 [logger_ssh_wrapper]
678 675 level = DEBUG
679 676 handlers =
680 677 qualname = ssh_wrapper
681 678 propagate = 1
682 679
683 680 [logger_celery]
684 681 level = DEBUG
685 682 handlers =
686 683 qualname = celery
687 684
688 685
689 686 ##############
690 687 ## HANDLERS ##
691 688 ##############
692 689
693 690 [handler_console]
694 691 class = StreamHandler
695 692 args = (sys.stderr, )
696 693 level = DEBUG
697 694 formatter = color_formatter
698 695
699 696 [handler_console_sql]
700 697 class = StreamHandler
701 698 args = (sys.stderr, )
702 699 level = DEBUG
703 700 formatter = color_formatter_sql
704 701
705 702 ################
706 703 ## FORMATTERS ##
707 704 ################
708 705
709 706 [formatter_generic]
710 707 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
711 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
708 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
712 709 datefmt = %Y-%m-%d %H:%M:%S
713 710
714 711 [formatter_color_formatter]
715 712 class = rhodecode.lib.logging_formatter.ColorFormatter
716 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
713 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
717 714 datefmt = %Y-%m-%d %H:%M:%S
718 715
719 716 [formatter_color_formatter_sql]
720 717 class = rhodecode.lib.logging_formatter.ColorFormatterSql
721 718 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
722 719 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,151 +1,152 b''
1 1 """
2 2 gunicorn config extension and hooks. Sets additional configuration that is
3 3 available post the .ini config.
4 4
5 5 - workers = ${cpu_number}
6 6 - threads = 1
7 7 - proc_name = ${gunicorn_proc_name}
8 8 - worker_class = sync
9 9 - worker_connections = 10
10 10 - max_requests = 1000
11 11 - max_requests_jitter = 30
12 12 - timeout = 21600
13 13
14 14 """
15 15
16 16 import multiprocessing
17 17 import sys
18 18 import time
19 19 import datetime
20 20 import threading
21 21 import traceback
22 22 from gunicorn.glogging import Logger
23 23
24 24
25 25 # GLOBAL
26 26 errorlog = '-'
27 27 accesslog = '-'
28 28 loglevel = 'debug'
29 29
30 30 # SECURITY
31 31
32 32 # The maximum size of HTTP request line in bytes.
33 33 limit_request_line = 4094
34 34
35 35 # Limit the number of HTTP headers fields in a request.
36 36 limit_request_fields = 1024
37 37
38 38 # Limit the allowed size of an HTTP request header field.
39 39 # Value is a positive number or 0.
40 40 # Setting it to 0 will allow unlimited header field sizes.
41 41 limit_request_field_size = 0
42 42
43 43
44 44 # Timeout for graceful workers restart.
45 45 # After receiving a restart signal, workers have this much time to finish
46 46 # serving requests. Workers still alive after the timeout (starting from the
47 47 # receipt of the restart signal) are force killed.
48 48 graceful_timeout = 30
49 49
50 50
51 51 # The number of seconds to wait for requests on a Keep-Alive connection.
52 52 # Generally set in the 1-5 seconds range.
53 53 keepalive = 2
54 54
55 55
56 56 # SERVER MECHANICS
57 57 # None == system temp dir
58 # worker_tmp_dir is recommended to be set to some tmpfs
58 59 worker_tmp_dir = None
59 60 tmp_upload_dir = None
60 61
61 62 # Custom log format
62 63 access_log_format = (
63 '%(t)s GNCRN %(p)-8s %(h)-15s rqt:%(L)s %(s)s %(b)-6s "%(m)s:%(U)s %(q)s" usr:%(u)s "%(f)s" "%(a)s"')
64 '%(t)s [%(p)-8s] GNCRN %(h)-15s rqt:%(L)s %(s)s %(b)-6s "%(m)s:%(U)s %(q)s" usr:%(u)s "%(f)s" "%(a)s"')
64 65
65 66 # self adjust workers based on CPU count
66 67 # workers = multiprocessing.cpu_count() * 2 + 1
67 68
68 69
69 70 def post_fork(server, worker):
70 71 server.log.info("[<%-10s>] WORKER spawned", worker.pid)
71 72
72 73
73 74 def pre_fork(server, worker):
74 75 pass
75 76
76 77
77 78 def pre_exec(server):
78 79 server.log.info("Forked child, re-executing.")
79 80
80 81
81 82 def on_starting(server):
82 83 server.log.info("Server is starting.")
83 84
84 85
85 86 def when_ready(server):
86 87 server.log.info("Server is ready. Spawning workers")
87 88
88 89
89 90 def on_reload(server):
90 91 pass
91 92
92 93
93 94 def worker_int(worker):
94 95 worker.log.info("[<%-10s>] worker received INT or QUIT signal", worker.pid)
95 96
96 97 # get traceback info, on worker crash
97 98 id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
98 99 code = []
99 100 for thread_id, stack in sys._current_frames().items():
100 101 code.append(
101 102 "\n# Thread: %s(%d)" % (id2name.get(thread_id, ""), thread_id))
102 103 for fname, lineno, name, line in traceback.extract_stack(stack):
103 104 code.append('File: "%s", line %d, in %s' % (fname, lineno, name))
104 105 if line:
105 106 code.append(" %s" % (line.strip()))
106 107 worker.log.debug("\n".join(code))
107 108
108 109
109 110 def worker_abort(worker):
110 111 worker.log.info("[<%-10s>] worker received SIGABRT signal", worker.pid)
111 112
112 113
113 114 def worker_exit(server, worker):
114 115 worker.log.info("[<%-10s>] worker exit", worker.pid)
115 116
116 117
117 118 def child_exit(server, worker):
118 119 worker.log.info("[<%-10s>] worker child exit", worker.pid)
119 120
120 121
121 122 def pre_request(worker, req):
122 return
123 worker.log.debug("[<%-10s>] PRE WORKER: %s %s",
124 worker.pid, req.method, req.path)
123 worker.start_time = time.time()
124 worker.log.debug(
125 "GNCRN PRE WORKER [cnt:%s]: %s %s", worker.nr, req.method, req.path)
125 126
126 127
127 128 def post_request(worker, req, environ, resp):
128 return
129 worker.log.debug("[<%-10s>] POST WORKER: %s %s resp: %s", worker.pid,
130 req.method, req.path, resp.status_code)
131
129 total_time = time.time() - worker.start_time
130 worker.log.debug(
131 "GNCRN POST WORKER [cnt:%s]: %s %s resp: %s, Load Time: %.3fs",
132 worker.nr, req.method, req.path, resp.status_code, total_time)
132 133
133 134
134 135 class RhodeCodeLogger(Logger):
135 136 """
136 137 Custom Logger that allows some customization that gunicorn doesn't allow
137 138 """
138 139
139 140 datefmt = r"%Y-%m-%d %H:%M:%S"
140 141
141 142 def __init__(self, cfg):
142 143 Logger.__init__(self, cfg)
143 144
144 145 def now(self):
145 146 """ return date in RhodeCode Log format """
146 147 now = time.time()
147 148 msecs = int((now - long(now)) * 1000)
148 149 return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs)
149 150
150 151
151 152 logger_class = RhodeCodeLogger
@@ -1,692 +1,689 b''
1 1
2 2
3 3 ################################################################################
4 4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10
11 11 ################################################################################
12 12 ## EMAIL CONFIGURATION ##
13 13 ## Uncomment and replace with the email address which should receive ##
14 14 ## any error reports after an application crash ##
15 15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 16 ################################################################################
17 17
18 18 ## prefix all emails subjects with given prefix, helps filtering out emails
19 19 #email_prefix = [RhodeCode]
20 20
21 21 ## email FROM address all mails will be sent
22 22 #app_email_from = rhodecode-noreply@localhost
23 23
24 24 ## Uncomment and replace with the address which should receive any error report
25 25 ## note: using appenlight for error handling doesn't need this to be uncommented
26 26 #email_to = admin@localhost
27 27
28 28 ## in case of Application errors, sent an error email form
29 29 #error_email_from = rhodecode_error@localhost
30 30
31 31 ## additional error message to be send in case of server crash
32 32 #error_message =
33 33
34 34
35 35 #smtp_server = mail.server.com
36 36 #smtp_username =
37 37 #smtp_password =
38 38 #smtp_port =
39 39 #smtp_use_tls = false
40 40 #smtp_use_ssl = true
41 41 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
42 42 #smtp_auth =
43 43
44 44 [server:main]
45 45 ## COMMON ##
46 46 host = 127.0.0.1
47 47 port = 5000
48 48
49 49 ##################################
50 50 ## WAITRESS WSGI SERVER ##
51 51 ## Recommended for Development ##
52 52 ##################################
53 53
54 54 #use = egg:waitress#main
55 55 ## number of worker threads
56 56 #threads = 5
57 57 ## MAX BODY SIZE 100GB
58 58 #max_request_body_size = 107374182400
59 59 ## Use poll instead of select, fixes file descriptors limits problems.
60 60 ## May not work on old windows systems.
61 61 #asyncore_use_poll = true
62 62
63 63
64 64 ##########################
65 65 ## GUNICORN WSGI SERVER ##
66 66 ##########################
67 67 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
68 68
69 69 use = egg:gunicorn#main
70 70 ## Sets the number of process workers. You must set `instance_id = *`
71 71 ## when this option is set to more than one worker, recommended
72 72 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
73 73 ## The `instance_id = *` must be set in the [app:main] section below
74 74 workers = 2
75 75 ## number of threads for each of the worker, must be set to 1 for gevent
76 76 ## generally recommended to be at 1
77 77 #threads = 1
78 78 ## process name
79 79 proc_name = rhodecode
80 80 ## type of worker class, one of sync, gevent
81 81 ## recommended for bigger setup is using of of other than sync one
82 82 worker_class = gevent
83 83 ## The maximum number of simultaneous clients. Valid only for Gevent
84 84 #worker_connections = 10
85 85 ## max number of requests that worker will handle before being gracefully
86 86 ## restarted, could prevent memory leaks
87 87 max_requests = 1000
88 88 max_requests_jitter = 30
89 89 ## amount of time a worker can spend with handling a request before it
90 90 ## gets killed and restarted. Set to 6hrs
91 91 timeout = 21600
92 92
93 93
94 94 ## prefix middleware for RhodeCode.
95 95 ## recommended when using proxy setup.
96 96 ## allows to set RhodeCode under a prefix in server.
97 97 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
98 98 ## And set your prefix like: `prefix = /custom_prefix`
99 99 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
100 100 ## to make your cookies only work on prefix url
101 101 [filter:proxy-prefix]
102 102 use = egg:PasteDeploy#prefix
103 103 prefix = /
104 104
105 105 [app:main]
106 106 use = egg:rhodecode-enterprise-ce
107 107
108 108 ## enable proxy prefix middleware, defined above
109 109 #filter-with = proxy-prefix
110 110
111 111 ## encryption key used to encrypt social plugin tokens,
112 112 ## remote_urls with credentials etc, if not set it defaults to
113 113 ## `beaker.session.secret`
114 114 #rhodecode.encrypted_values.secret =
115 115
116 116 ## decryption strict mode (enabled by default). It controls if decryption raises
117 117 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
118 118 #rhodecode.encrypted_values.strict = false
119 119
120 120 ## return gzipped responses from Rhodecode (static files/application)
121 121 gzip_responses = false
122 122
123 123 ## autogenerate javascript routes file on startup
124 124 generate_js_files = false
125 125
126 126 ## Optional Languages
127 127 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
128 128 lang = en
129 129
130 130 ## perform a full repository scan on each server start, this should be
131 131 ## set to false after first startup, to allow faster server restarts.
132 132 startup.import_repos = false
133 133
134 134 ## Uncomment and set this path to use archive download cache.
135 135 ## Once enabled, generated archives will be cached at this location
136 136 ## and served from the cache during subsequent requests for the same archive of
137 137 ## the repository.
138 138 #archive_cache_dir = /tmp/tarballcache
139 139
140 140 ## URL at which the application is running. This is used for bootstraping
141 141 ## requests in context when no web request is available. Used in ishell, or
142 142 ## SSH calls. Set this for events to receive proper url for SSH calls.
143 143 app.base_url = http://rhodecode.local
144 144
145 145 ## change this to unique ID for security
146 146 app_instance_uuid = rc-production
147 147
148 148 ## cut off limit for large diffs (size in bytes). If overall diff size on
149 149 ## commit, or pull request exceeds this limit this diff will be displayed
150 150 ## partially. E.g 512000 == 512Kb
151 151 cut_off_limit_diff = 512000
152 152
153 153 ## cut off limit for large files inside diffs (size in bytes). Each individual
154 154 ## file inside diff which exceeds this limit will be displayed partially.
155 155 ## E.g 128000 == 128Kb
156 156 cut_off_limit_file = 128000
157 157
158 158 ## use cache version of scm repo everywhere
159 159 vcs_full_cache = true
160 160
161 161 ## force https in RhodeCode, fixes https redirects, assumes it's always https
162 162 ## Normally this is controlled by proper http flags sent from http server
163 163 force_https = false
164 164
165 165 ## use Strict-Transport-Security headers
166 166 use_htsts = false
167 167
168 ## number of commits stats will parse on each iteration
169 commit_parse_limit = 25
170
171 168 ## git rev filter option, --all is the default filter, if you need to
172 169 ## hide all refs in changelog switch this to --branches --tags
173 170 git_rev_filter = --branches --tags
174 171
175 172 # Set to true if your repos are exposed using the dumb protocol
176 173 git_update_server_info = false
177 174
178 175 ## RSS/ATOM feed options
179 176 rss_cut_off_limit = 256000
180 177 rss_items_per_page = 10
181 178 rss_include_diff = false
182 179
183 180 ## gist URL alias, used to create nicer urls for gist. This should be an
184 181 ## url that does rewrites to _admin/gists/{gistid}.
185 182 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
186 183 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
187 184 gist_alias_url =
188 185
189 186 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
190 187 ## used for access.
191 188 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
192 189 ## came from the the logged in user who own this authentication token.
193 190 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
194 191 ## authentication token. Such view would be only accessible when used together
195 192 ## with this authentication token
196 193 ##
197 194 ## list of all views can be found under `/_admin/permissions/auth_token_access`
198 195 ## The list should be "," separated and on a single line.
199 196 ##
200 197 ## Most common views to enable:
201 198 # RepoCommitsView:repo_commit_download
202 199 # RepoCommitsView:repo_commit_patch
203 200 # RepoCommitsView:repo_commit_raw
204 201 # RepoCommitsView:repo_commit_raw@TOKEN
205 202 # RepoFilesView:repo_files_diff
206 203 # RepoFilesView:repo_archivefile
207 204 # RepoFilesView:repo_file_raw
208 205 # GistView:*
209 206 api_access_controllers_whitelist =
210 207
211 208 ## default encoding used to convert from and to unicode
212 209 ## can be also a comma separated list of encoding in case of mixed encodings
213 210 default_encoding = UTF-8
214 211
215 212 ## instance-id prefix
216 213 ## a prefix key for this instance used for cache invalidation when running
217 214 ## multiple instances of rhodecode, make sure it's globally unique for
218 215 ## all running rhodecode instances. Leave empty if you don't use it
219 216 instance_id =
220 217
221 218 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
222 219 ## of an authentication plugin also if it is disabled by it's settings.
223 220 ## This could be useful if you are unable to log in to the system due to broken
224 221 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
225 222 ## module to log in again and fix the settings.
226 223 ##
227 224 ## Available builtin plugin IDs (hash is part of the ID):
228 225 ## egg:rhodecode-enterprise-ce#rhodecode
229 226 ## egg:rhodecode-enterprise-ce#pam
230 227 ## egg:rhodecode-enterprise-ce#ldap
231 228 ## egg:rhodecode-enterprise-ce#jasig_cas
232 229 ## egg:rhodecode-enterprise-ce#headers
233 230 ## egg:rhodecode-enterprise-ce#crowd
234 231 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
235 232
236 233 ## alternative return HTTP header for failed authentication. Default HTTP
237 234 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
238 235 ## handling that causing a series of failed authentication calls.
239 236 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
240 237 ## This will be served instead of default 401 on bad authnetication
241 238 auth_ret_code =
242 239
243 240 ## use special detection method when serving auth_ret_code, instead of serving
244 241 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
245 242 ## and then serve auth_ret_code to clients
246 243 auth_ret_code_detection = false
247 244
248 245 ## locking return code. When repository is locked return this HTTP code. 2XX
249 246 ## codes don't break the transactions while 4XX codes do
250 247 lock_ret_code = 423
251 248
252 249 ## allows to change the repository location in settings page
253 250 allow_repo_location_change = true
254 251
255 252 ## allows to setup custom hooks in settings page
256 253 allow_custom_hooks_settings = true
257 254
258 255 ## generated license token, goto license page in RhodeCode settings to obtain
259 256 ## new token
260 257 license_token =
261 258
262 259 ## supervisor connection uri, for managing supervisor and logs.
263 260 supervisor.uri =
264 261 ## supervisord group name/id we only want this RC instance to handle
265 262 supervisor.group_id = prod
266 263
267 264 ## Display extended labs settings
268 265 labs_settings_active = true
269 266
270 267 ####################################
271 268 ### CELERY CONFIG ####
272 269 ####################################
273 270 ## run: /path/to/celery worker \
274 271 ## -E --beat --app rhodecode.lib.celerylib.loader \
275 272 ## --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler \
276 273 ## --loglevel DEBUG --ini /path/to/rhodecode.ini
277 274
278 275 use_celery = false
279 276
280 277 ## connection url to the message broker (default rabbitmq)
281 278 celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
282 279
283 280 ## maximum tasks to execute before worker restart
284 281 celery.max_tasks_per_child = 100
285 282
286 283 ## tasks will never be sent to the queue, but executed locally instead.
287 284 celery.task_always_eager = false
288 285
289 286 ####################################
290 287 ### BEAKER CACHE ####
291 288 ####################################
292 289 # default cache dir for templates. Putting this into a ramdisk
293 290 ## can boost performance, eg. %(here)s/data_ramdisk
294 291 cache_dir = %(here)s/data
295 292
296 293 ## locking and default file storage for Beaker. Putting this into a ramdisk
297 294 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
298 295 beaker.cache.data_dir = %(here)s/data/cache/beaker_data
299 296 beaker.cache.lock_dir = %(here)s/data/cache/beaker_lock
300 297
301 298 beaker.cache.regions = super_short_term, short_term, long_term, sql_cache_short, auth_plugins, repo_cache_long
302 299
303 300 beaker.cache.super_short_term.type = memory
304 301 beaker.cache.super_short_term.expire = 10
305 302 beaker.cache.super_short_term.key_length = 256
306 303
307 304 beaker.cache.short_term.type = memory
308 305 beaker.cache.short_term.expire = 60
309 306 beaker.cache.short_term.key_length = 256
310 307
311 308 beaker.cache.long_term.type = memory
312 309 beaker.cache.long_term.expire = 36000
313 310 beaker.cache.long_term.key_length = 256
314 311
315 312 beaker.cache.sql_cache_short.type = memory
316 313 beaker.cache.sql_cache_short.expire = 10
317 314 beaker.cache.sql_cache_short.key_length = 256
318 315
319 316 ## default is memory cache, configure only if required
320 317 ## using multi-node or multi-worker setup
321 318 #beaker.cache.auth_plugins.type = ext:database
322 319 #beaker.cache.auth_plugins.lock_dir = %(here)s/data/cache/auth_plugin_lock
323 320 #beaker.cache.auth_plugins.url = postgresql://postgres:secret@localhost/rhodecode
324 321 #beaker.cache.auth_plugins.url = mysql://root:secret@127.0.0.1/rhodecode
325 322 #beaker.cache.auth_plugins.sa.pool_recycle = 3600
326 323 #beaker.cache.auth_plugins.sa.pool_size = 10
327 324 #beaker.cache.auth_plugins.sa.max_overflow = 0
328 325
329 326 beaker.cache.repo_cache_long.type = memorylru_base
330 327 beaker.cache.repo_cache_long.max_items = 4096
331 328 beaker.cache.repo_cache_long.expire = 2592000
332 329
333 330 ## default is memorylru_base cache, configure only if required
334 331 ## using multi-node or multi-worker setup
335 332 #beaker.cache.repo_cache_long.type = ext:memcached
336 333 #beaker.cache.repo_cache_long.url = localhost:11211
337 334 #beaker.cache.repo_cache_long.expire = 1209600
338 335 #beaker.cache.repo_cache_long.key_length = 256
339 336
340 337 ####################################
341 338 ### BEAKER SESSION ####
342 339 ####################################
343 340
344 341 ## .session.type is type of storage options for the session, current allowed
345 342 ## types are file, ext:memcached, ext:database, and memory (default).
346 343 beaker.session.type = file
347 344 beaker.session.data_dir = %(here)s/data/sessions/data
348 345
349 346 ## db based session, fast, and allows easy management over logged in users
350 347 #beaker.session.type = ext:database
351 348 #beaker.session.table_name = db_session
352 349 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
353 350 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
354 351 #beaker.session.sa.pool_recycle = 3600
355 352 #beaker.session.sa.echo = false
356 353
357 354 beaker.session.key = rhodecode
358 355 beaker.session.secret = production-rc-uytcxaz
359 356 beaker.session.lock_dir = %(here)s/data/sessions/lock
360 357
361 358 ## Secure encrypted cookie. Requires AES and AES python libraries
362 359 ## you must disable beaker.session.secret to use this
363 360 #beaker.session.encrypt_key = key_for_encryption
364 361 #beaker.session.validate_key = validation_key
365 362
366 363 ## sets session as invalid(also logging out user) if it haven not been
367 364 ## accessed for given amount of time in seconds
368 365 beaker.session.timeout = 2592000
369 366 beaker.session.httponly = true
370 367 ## Path to use for the cookie. Set to prefix if you use prefix middleware
371 368 #beaker.session.cookie_path = /custom_prefix
372 369
373 370 ## uncomment for https secure cookie
374 371 beaker.session.secure = false
375 372
376 373 ## auto save the session to not to use .save()
377 374 beaker.session.auto = false
378 375
379 376 ## default cookie expiration time in seconds, set to `true` to set expire
380 377 ## at browser close
381 378 #beaker.session.cookie_expires = 3600
382 379
383 380 ###################################
384 381 ## SEARCH INDEXING CONFIGURATION ##
385 382 ###################################
386 383 ## Full text search indexer is available in rhodecode-tools under
387 384 ## `rhodecode-tools index` command
388 385
389 386 ## WHOOSH Backend, doesn't require additional services to run
390 387 ## it works good with few dozen repos
391 388 search.module = rhodecode.lib.index.whoosh
392 389 search.location = %(here)s/data/index
393 390
394 391 ########################################
395 392 ### CHANNELSTREAM CONFIG ####
396 393 ########################################
397 394 ## channelstream enables persistent connections and live notification
398 395 ## in the system. It's also used by the chat system
399 396 channelstream.enabled = false
400 397
401 398 ## server address for channelstream server on the backend
402 399 channelstream.server = 127.0.0.1:9800
403 400
404 401 ## location of the channelstream server from outside world
405 402 ## use ws:// for http or wss:// for https. This address needs to be handled
406 403 ## by external HTTP server such as Nginx or Apache
407 404 ## see nginx/apache configuration examples in our docs
408 405 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
409 406 channelstream.secret = secret
410 407 channelstream.history.location = %(here)s/channelstream_history
411 408
412 409 ## Internal application path that Javascript uses to connect into.
413 410 ## If you use proxy-prefix the prefix should be added before /_channelstream
414 411 channelstream.proxy_path = /_channelstream
415 412
416 413
417 414 ###################################
418 415 ## APPENLIGHT CONFIG ##
419 416 ###################################
420 417
421 418 ## Appenlight is tailored to work with RhodeCode, see
422 419 ## http://appenlight.com for details how to obtain an account
423 420
424 421 ## appenlight integration enabled
425 422 appenlight = false
426 423
427 424 appenlight.server_url = https://api.appenlight.com
428 425 appenlight.api_key = YOUR_API_KEY
429 426 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
430 427
431 428 # used for JS client
432 429 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
433 430
434 431 ## TWEAK AMOUNT OF INFO SENT HERE
435 432
436 433 ## enables 404 error logging (default False)
437 434 appenlight.report_404 = false
438 435
439 436 ## time in seconds after request is considered being slow (default 1)
440 437 appenlight.slow_request_time = 1
441 438
442 439 ## record slow requests in application
443 440 ## (needs to be enabled for slow datastore recording and time tracking)
444 441 appenlight.slow_requests = true
445 442
446 443 ## enable hooking to application loggers
447 444 appenlight.logging = true
448 445
449 446 ## minimum log level for log capture
450 447 appenlight.logging.level = WARNING
451 448
452 449 ## send logs only from erroneous/slow requests
453 450 ## (saves API quota for intensive logging)
454 451 appenlight.logging_on_error = false
455 452
456 453 ## list of additonal keywords that should be grabbed from environ object
457 454 ## can be string with comma separated list of words in lowercase
458 455 ## (by default client will always send following info:
459 456 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
460 457 ## start with HTTP* this list be extended with additional keywords here
461 458 appenlight.environ_keys_whitelist =
462 459
463 460 ## list of keywords that should be blanked from request object
464 461 ## can be string with comma separated list of words in lowercase
465 462 ## (by default client will always blank keys that contain following words
466 463 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
467 464 ## this list be extended with additional keywords set here
468 465 appenlight.request_keys_blacklist =
469 466
470 467 ## list of namespaces that should be ignores when gathering log entries
471 468 ## can be string with comma separated list of namespaces
472 469 ## (by default the client ignores own entries: appenlight_client.client)
473 470 appenlight.log_namespace_blacklist =
474 471
475 472
476 473 ################################################################################
477 474 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
478 475 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
479 476 ## execute malicious code after an exception is raised. ##
480 477 ################################################################################
481 478 set debug = false
482 479
483 480
484 481 ###########################################
485 482 ### MAIN RHODECODE DATABASE CONFIG ###
486 483 ###########################################
487 484 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
488 485 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
489 486 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode
490 487 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
491 488
492 489 # see sqlalchemy docs for other advanced settings
493 490
494 491 ## print the sql statements to output
495 492 sqlalchemy.db1.echo = false
496 493 ## recycle the connections after this amount of seconds
497 494 sqlalchemy.db1.pool_recycle = 3600
498 495 sqlalchemy.db1.convert_unicode = true
499 496
500 497 ## the number of connections to keep open inside the connection pool.
501 498 ## 0 indicates no limit
502 499 #sqlalchemy.db1.pool_size = 5
503 500
504 501 ## the number of connections to allow in connection pool "overflow", that is
505 502 ## connections that can be opened above and beyond the pool_size setting,
506 503 ## which defaults to five.
507 504 #sqlalchemy.db1.max_overflow = 10
508 505
509 506
510 507 ##################
511 508 ### VCS CONFIG ###
512 509 ##################
513 510 vcs.server.enable = true
514 511 vcs.server = localhost:9900
515 512
516 513 ## Web server connectivity protocol, responsible for web based VCS operatations
517 514 ## Available protocols are:
518 515 ## `http` - use http-rpc backend (default)
519 516 vcs.server.protocol = http
520 517
521 518 ## Push/Pull operations protocol, available options are:
522 519 ## `http` - use http-rpc backend (default)
523 520 ##
524 521 vcs.scm_app_implementation = http
525 522
526 523 ## Push/Pull operations hooks protocol, available options are:
527 524 ## `http` - use http-rpc backend (default)
528 525 vcs.hooks.protocol = http
529 526
530 527 vcs.server.log_level = info
531 528 ## Start VCSServer with this instance as a subprocess, usefull for development
532 529 vcs.start_server = false
533 530
534 531 ## List of enabled VCS backends, available options are:
535 532 ## `hg` - mercurial
536 533 ## `git` - git
537 534 ## `svn` - subversion
538 535 vcs.backends = hg, git, svn
539 536
540 537 vcs.connection_timeout = 3600
541 538 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
542 539 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
543 540 #vcs.svn.compatible_version = pre-1.8-compatible
544 541
545 542
546 543 ############################################################
547 544 ### Subversion proxy support (mod_dav_svn) ###
548 545 ### Maps RhodeCode repo groups into SVN paths for Apache ###
549 546 ############################################################
550 547 ## Enable or disable the config file generation.
551 548 svn.proxy.generate_config = false
552 549 ## Generate config file with `SVNListParentPath` set to `On`.
553 550 svn.proxy.list_parent_path = true
554 551 ## Set location and file name of generated config file.
555 552 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
556 553 ## alternative mod_dav config template. This needs to be a mako template
557 554 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
558 555 ## Used as a prefix to the `Location` block in the generated config file.
559 556 ## In most cases it should be set to `/`.
560 557 svn.proxy.location_root = /
561 558 ## Command to reload the mod dav svn configuration on change.
562 559 ## Example: `/etc/init.d/apache2 reload`
563 560 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
564 561 ## If the timeout expires before the reload command finishes, the command will
565 562 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
566 563 #svn.proxy.reload_timeout = 10
567 564
568 565 ############################################################
569 566 ### SSH Support Settings ###
570 567 ############################################################
571 568
572 569 ## Defines if a custom authorized_keys file should be created and written on
573 570 ## any change user ssh keys. Setting this to false also disables posibility
574 571 ## of adding SSH keys by users from web interface. Super admins can still
575 572 ## manage SSH Keys.
576 573 ssh.generate_authorized_keyfile = false
577 574
578 575 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
579 576 # ssh.authorized_keys_ssh_opts =
580 577
581 578 ## Path to the authrozied_keys file where the generate entries are placed.
582 579 ## It is possible to have multiple key files specified in `sshd_config` e.g.
583 580 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
584 581 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
585 582
586 583 ## Command to execute the SSH wrapper. The binary is available in the
587 584 ## rhodecode installation directory.
588 585 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
589 586 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
590 587
591 588 ## Allow shell when executing the ssh-wrapper command
592 589 ssh.wrapper_cmd_allow_shell = false
593 590
594 591 ## Enables logging, and detailed output send back to the client during SSH
595 592 ## operations. Usefull for debugging, shouldn't be used in production.
596 593 ssh.enable_debug_logging = false
597 594
598 595 ## Paths to binary executable, by default they are the names, but we can
599 596 ## override them if we want to use a custom one
600 597 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
601 598 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
602 599 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
603 600
604 601
605 602 ## Dummy marker to add new entries after.
606 603 ## Add any custom entries below. Please don't remove.
607 604 custom.conf = 1
608 605
609 606
610 607 ################################
611 608 ### LOGGING CONFIGURATION ####
612 609 ################################
613 610 [loggers]
614 611 keys = root, sqlalchemy, beaker, rhodecode, ssh_wrapper, celery
615 612
616 613 [handlers]
617 614 keys = console, console_sql
618 615
619 616 [formatters]
620 617 keys = generic, color_formatter, color_formatter_sql
621 618
622 619 #############
623 620 ## LOGGERS ##
624 621 #############
625 622 [logger_root]
626 623 level = NOTSET
627 624 handlers = console
628 625
629 626 [logger_sqlalchemy]
630 627 level = INFO
631 628 handlers = console_sql
632 629 qualname = sqlalchemy.engine
633 630 propagate = 0
634 631
635 632 [logger_beaker]
636 633 level = DEBUG
637 634 handlers =
638 635 qualname = beaker.container
639 636 propagate = 1
640 637
641 638 [logger_rhodecode]
642 639 level = DEBUG
643 640 handlers =
644 641 qualname = rhodecode
645 642 propagate = 1
646 643
647 644 [logger_ssh_wrapper]
648 645 level = DEBUG
649 646 handlers =
650 647 qualname = ssh_wrapper
651 648 propagate = 1
652 649
653 650 [logger_celery]
654 651 level = DEBUG
655 652 handlers =
656 653 qualname = celery
657 654
658 655
659 656 ##############
660 657 ## HANDLERS ##
661 658 ##############
662 659
663 660 [handler_console]
664 661 class = StreamHandler
665 662 args = (sys.stderr, )
666 663 level = INFO
667 664 formatter = generic
668 665
669 666 [handler_console_sql]
670 667 class = StreamHandler
671 668 args = (sys.stderr, )
672 669 level = WARN
673 670 formatter = generic
674 671
675 672 ################
676 673 ## FORMATTERS ##
677 674 ################
678 675
679 676 [formatter_generic]
680 677 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
681 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
678 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
682 679 datefmt = %Y-%m-%d %H:%M:%S
683 680
684 681 [formatter_color_formatter]
685 682 class = rhodecode.lib.logging_formatter.ColorFormatter
686 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
683 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
687 684 datefmt = %Y-%m-%d %H:%M:%S
688 685
689 686 [formatter_color_formatter_sql]
690 687 class = rhodecode.lib.logging_formatter.ColorFormatterSql
691 688 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
692 689 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,101 +1,101 b''
1 .. _sec-your-server:
1 .. _sec-sophos-umc:
2 2
3 3 Securing Your Server via Sophos UTM 9
4 4 -------------------------------------
5 5
6 6
7 7
8 8 Below is an example configuration for Sophos UTM 9 Webserver Protection::
9 9
10 10 Sophos UTM 9 Webserver Protection
11 11 Web Application Firewall based on apache2 modesecurity2
12 12 --------------------------------------------------
13 13 1. Firewall Profiles -> Firewall Profile
14 14 --------------------------------------------------
15 15 Name: RhodeCode (can be anything)
16 16 Mode: Reject
17 17 Hardening & Signing:
18 18 [ ] Static URL hardeninig
19 19 [ ] Form hardening
20 20 [x] Cookie Signing
21 21 Filtering:
22 22 [x] Block clients with bad reputation
23 23 [x] Common Threats Filter
24 24 [ ] Rigid Filtering
25 25 Skip Filter Rules:
26 26 960015
27 27 950120
28 28 981173
29 29 970901
30 30 960010
31 31 960032
32 32 960035
33 33 958291
34 34 970903
35 35 970003
36 36 Common Threat Filter Categories:
37 37 [x] Protocol violations
38 38 [x] Protocol anomalies
39 39 [x] Request limit
40 40 [x] HTTP policy
41 41 [x] Bad robots
42 42 [x] Generic attacks
43 43 [x] SQL injection attacks
44 44 [x] XSS attacks
45 45 [x] Tight security
46 46 [x] Trojans
47 47 [x] Outbound
48 48 Scanning:
49 49 [ ] Enable antivirus scanning
50 50 [ ] Block uploads by MIME type
51 51 --------------------------------------------------
52 52 2. Web Application Firewall -> Real Webservers
53 53 --------------------------------------------------
54 54 Name: RhodeCode (can be anything)
55 55 Host: Your RhodeCode-Server (UTM object)
56 56 Type: Encrypted (HTTPS)
57 57 Port: 443
58 58 --------------------------------------------------
59 59 3. Web Application Firewall -> Virual Webservers
60 60 --------------------------------------------------
61 61 Name: RhodeCode (can be anything)
62 62 Interface: WAN (your WAN interface)
63 63 Type: Encrypted (HTTPS) & redirect
64 64 Certificate: Wildcard or matching domain certificate
65 65 Domains (in case of Wildcard certificate):
66 66 rhodecode.yourcompany.com (match your DNS configuration)
67 67 gist.yourcompany.com (match your DNS & RhodeCode configuration)
68 68 Real Webservers for path '/':
69 69 [x] RhodeCode (created in step 2)
70 70 Firewall: RhodeCode (created in step 1)
71 71 --------------------------------------------------
72 72 4. Firewall Profiles -> Exceptions
73 73 --------------------------------------------------
74 74 Name: RhodeCode exceptions (can be anything)
75 75 Skip these checks:
76 76 [ ] Cookie signing
77 77 [ ] Static URL Hardening
78 78 [ ] Form hardening
79 79 [x] Antivirus scanning
80 80 [x] True file type control
81 81 [ ] Block clients with bad reputation
82 82 Skip these categories:
83 83 [ ] Protocol violations
84 84 [x] Protocol anomalies
85 85 [x] Request limits
86 86 [ ] HTTP policy
87 87 [ ] Bad robots
88 88 [ ] Generic attacks
89 89 [ ] SQL injection attacks
90 90 [ ] XSS attacks
91 91 [ ] Tight security
92 92 [ ] Trojans
93 93 [x] Outbound
94 94 Virtual Webservers:
95 95 [x] RhodeCode (created in step 3)
96 96 For All Requests:
97 97 Web requests matching this pattern:
98 98 /_channelstream/ws
99 99 /Repository1/*
100 100 /Repository2/*
101 101 /Repository3/* No newline at end of file
@@ -1,31 +1,32 b''
1 1 .. _rhodecode-admin-ref:
2 2
3 3 System Administration
4 4 =====================
5 5
6 6 The following are the most common system administration tasks.
7 7
8 8 .. only:: latex
9 9
10 10 * :ref:`vcs-server`
11 11 * :ref:`apache-ws-ref`
12 12 * :ref:`nginx-ws-ref`
13 13 * :ref:`rhodecode-tuning-ref`
14 14 * :ref:`indexing-ref`
15 15 * :ref:`rhodecode-reset-ref`
16 16
17 17 .. toctree::
18 18
19 19 config-files-overview
20 20 vcs-server
21 21 svn-http
22 svn-path-permissions
22 23 gunicorn-ssl-support
23 24 apache-config
24 25 nginx-config
25 26 backup-restore
26 27 tuning-rhodecode
27 28 indexing
28 29 reset-information
29 30 enable-debug
30 31 admin-tricks
31 32 cleanup-cmds
@@ -1,207 +1,208 b''
1 1 .. _api:
2 2
3 3 API Documentation
4 4 =================
5 5
6 6 The |RCE| API uses a single scheme for calling all API methods. The API is
7 7 implemented with JSON protocol in both directions. To send API requests to
8 8 your instance of |RCE|, use the following URL format
9 9 ``<your_server>/_admin``
10 10
11 11 .. note::
12 12
13 13 To use the API, you should configure the :file:`~/.rhoderc` file with
14 14 access details per instance. For more information, see
15 15 :ref:`config-rhoderc`.
16 16
17 17
18 18 API ACCESS FOR WEB VIEWS
19 19 ------------------------
20 20
21 21 API access can also be turned on for each web view in |RCE| that is
22 22 decorated with a `@LoginRequired` decorator. To enable API access, change
23 23 the standard login decorator to `@LoginRequired(api_access=True)`.
24 24
25 25 From |RCM| version 1.7.0 you can configure a white list
26 26 of views that have API access enabled by default. To enable these,
27 27 edit the |RCM| configuration ``.ini`` file. The default location is:
28 28
29 29 * |RCM| Pre-2.2.7 :file:`root/rhodecode/data/production.ini`
30 30 * |RCM| 3.0 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
31 31
32 32 To configure the white list, edit this section of the file. In this
33 33 configuration example, API access is granted to the patch/diff raw file and
34 34 archive.
35 35
36 36 .. code-block:: ini
37 37
38 38 ## List of controllers (using glob syntax) that AUTH TOKENS could be used for access.
39 39 ## Adding ?auth_token = <token> to the url authenticates this request as if it
40 40 ## came from the the logged in user who own this authentication token.
41 41 ##
42 42 ## Syntax is <ControllerClass>:<function_pattern>.
43 43 ## The list should be "," separated and on a single line.
44 44 ##
45 45 api_access_controllers_whitelist = RepoCommitsView:repo_commit_raw,RepoCommitsView:repo_commit_patch,RepoCommitsView:repo_commit_download
46 46
47 47 After this change, a |RCE| view can be accessed without login by adding a
48 48 GET parameter ``?auth_token=<auth_token>`` to a url. For example to
49 49 access the raw diff.
50 50
51 51 .. code-block:: html
52 52
53 53 http://<server>/<repo>/changeset-diff/<sha>?auth_token=<auth_token>
54 54
55 55 By default this is only enabled on RSS/ATOM feed views. Exposing raw diffs is a
56 56 good way to integrate with 3rd party services like code review, or build farms
57 57 that could download archives.
58 58
59 59 API ACCESS
60 60 ----------
61 61
62 62 All clients are required to send JSON-RPC spec JSON data.
63 63
64 64 .. code-block:: bash
65 65
66 66 {
67 67 "id:"<id>",
68 68 "auth_token":"<auth_token>",
69 69 "method":"<method_name>",
70 70 "args":{"<arg_key>":"<arg_val>"}
71 71 }
72 72
73 73 Example call for auto pulling from remote repositories using curl:
74 74
75 75 .. code-block:: bash
76 76
77 77 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,
78 78 "auth_token":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull", "args":{"repoid":"CPython"}}'
79 79
80 80 Provide those parameters:
81 81 - **id** A value of any type, which is used to match the response with the
82 82 request that it is replying to.
83 83 - **auth_token** for access and permission validation.
84 84 - **method** is name of method to call
85 85 - **args** is an ``key:value`` list of arguments to pass to method
86 86
87 87 .. note::
88 88
89 89 To get your |authtoken|, from the |RCE| interface,
90 90 go to:
91 91 :menuselection:`username --> My account --> Auth tokens`
92 92
93 93 For security reasons you should always create a dedicated |authtoken| for
94 94 API use only.
95 95
96 96
97 97 The |RCE| API will always return a JSON-RPC response:
98 98
99 99 .. code-block:: bash
100 100
101 101 {
102 102 "id": <id>, # matching id sent by request
103 103 "result": "<result>"|null, # JSON formatted result, null if any errors
104 104 "error": "null"|<error_message> # JSON formatted error (if any)
105 105 }
106 106
107 107 All responses from API will be with `HTTP/1.0 200 OK` status code.
108 108 If there is an error when calling the API, the *error* key will contain a
109 109 failure description and the *result* will be `null`.
110 110
111 111 API CLIENT
112 112 ----------
113 113
114 114 To install the |RCE| API, see :ref:`install-tools`. To configure the API per
115 115 instance, see the :ref:`rc-tools` section as you need to configure a
116 116 :file:`~/.rhoderc` file with your |authtokens|.
117 117
118 118 Once you have set up your instance API access, use the following examples to
119 119 get started.
120 120
121 121 .. code-block:: bash
122 122
123 123 # Getting the 'rhodecode' repository
124 124 # from a RhodeCode Enterprise instance
125 125 rhodecode-api --instance-name=enterprise-1 get_repo repoid:rhodecode
126 126
127 127 Calling method get_repo => http://127.0.0.1:5000
128 128 Server response
129 129 {
130 130 <json data>
131 131 }
132 132
133 133 # Creating a new mercurial repository called 'brand-new'
134 134 # with a description 'Repo-description'
135 135 rhodecode-api --instance-name=enterprise-1 create_repo repo_name:brand-new repo_type:hg description:Repo-description
136 136 {
137 137 "error": null,
138 138 "id": 1110,
139 139 "result": {
140 140 "msg": "Created new repository `brand-new`",
141 141 "success": true,
142 142 "task": null
143 143 }
144 144 }
145 145
146 146 A broken example, what not to do.
147 147
148 148 .. code-block:: bash
149 149
150 150 # A call missing the required arguments
151 151 # and not specifying the instance
152 152 rhodecode-api get_repo
153 153
154 154 Calling method get_repo => http://127.0.0.1:5000
155 155 Server response
156 156 "Missing non optional `repoid` arg in JSON DATA"
157 157
158 158 You can specify pure JSON using the ``--format`` parameter.
159 159
160 160 .. code-block:: bash
161 161
162 162 rhodecode-api --format=json get_repo repoid:rhodecode
163 163
164 164 In such case only output that this function shows is pure JSON, we can use that
165 165 and pipe output to some json formatter.
166 166
167 167 If output is in pure JSON format, you can pipe output to a JSON formatter.
168 168
169 169 .. code-block:: bash
170 170
171 171 rhodecode-api --instance-name=enterprise-1 --format=json get_repo repoid:rhodecode | python -m json.tool
172 172
173 173 API METHODS
174 174 -----------
175 175
176 176 Each method by default required following arguments.
177 177
178 178 .. code-block:: bash
179 179
180 180 id : "<id_for_response>"
181 181 auth_token : "<auth_token>"
182 182 method : "<method name>"
183 183 args : {}
184 184
185 185 Use each **param** from docs and put it in args, Optional parameters
186 186 are not required in args.
187 187
188 188 .. code-block:: bash
189 189
190 190 args: {"repoid": "rhodecode"}
191 191
192 192 .. Note: From this point on things are generated by the script in
193 193 `scripts/fabfile.py`. To change things below, update the docstrings in the
194 194 ApiController.
195 195
196 196 .. --- API DEFS MARKER ---
197 197 .. toctree::
198 198
199 methods/views
199 200 methods/license-methods
200 201 methods/deprecated-methods
201 202 methods/gist-methods
202 203 methods/pull-request-methods
203 204 methods/repo-methods
204 205 methods/repo-group-methods
205 206 methods/server-methods
206 207 methods/user-methods
207 208 methods/user-group-methods
@@ -1,1024 +1,1028 b''
1 1 .. _repo-methods-ref:
2 2
3 3 repo methods
4 4 ============
5 5
6 6 add_field_to_repo
7 7 -----------------
8 8
9 9 .. py:function:: add_field_to_repo(apiuser, repoid, key, label=<Optional:''>, description=<Optional:''>)
10 10
11 11 Adds an extra field to a repository.
12 12
13 13 This command can only be run using an |authtoken| with at least
14 14 write permissions to the |repo|.
15 15
16 16 :param apiuser: This is filled automatically from the |authtoken|.
17 17 :type apiuser: AuthUser
18 18 :param repoid: Set the repository name or repository id.
19 19 :type repoid: str or int
20 20 :param key: Create a unique field key for this repository.
21 21 :type key: str
22 22 :param label:
23 23 :type label: Optional(str)
24 24 :param description:
25 25 :type description: Optional(str)
26 26
27 27
28 28 comment_commit
29 29 --------------
30 30
31 31 .. py:function:: comment_commit(apiuser, repoid, commit_id, message, status=<Optional:None>, comment_type=<Optional:u'note'>, resolves_comment_id=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
32 32
33 33 Set a commit comment, and optionally change the status of the commit.
34 34
35 35 :param apiuser: This is filled automatically from the |authtoken|.
36 36 :type apiuser: AuthUser
37 37 :param repoid: Set the repository name or repository ID.
38 38 :type repoid: str or int
39 39 :param commit_id: Specify the commit_id for which to set a comment.
40 40 :type commit_id: str
41 41 :param message: The comment text.
42 42 :type message: str
43 43 :param status: (**Optional**) status of commit, one of: 'not_reviewed',
44 44 'approved', 'rejected', 'under_review'
45 45 :type status: str
46 46 :param comment_type: Comment type, one of: 'note', 'todo'
47 47 :type comment_type: Optional(str), default: 'note'
48 48 :param userid: Set the user name of the comment creator.
49 49 :type userid: Optional(str or int)
50 50
51 51 Example error output:
52 52
53 53 .. code-block:: bash
54 54
55 55 {
56 56 "id" : <id_given_in_input>,
57 57 "result" : {
58 58 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
59 59 "status_change": null or <status>,
60 60 "success": true
61 61 },
62 62 "error" : null
63 63 }
64 64
65 65
66 66 create_repo
67 67 -----------
68 68
69 .. py:function:: create_repo(apiuser, repo_name, repo_type, owner=<Optional:<OptionalAttr:apiuser>>, description=<Optional:''>, private=<Optional:False>, clone_uri=<Optional:None>, landing_rev=<Optional:'rev:tip'>, enable_statistics=<Optional:False>, enable_locking=<Optional:False>, enable_downloads=<Optional:False>, copy_permissions=<Optional:False>)
69 .. py:function:: create_repo(apiuser, repo_name, repo_type, owner=<Optional:<OptionalAttr:apiuser>>, description=<Optional:''>, private=<Optional:False>, clone_uri=<Optional:None>, push_uri=<Optional:None>, landing_rev=<Optional:'rev:tip'>, enable_statistics=<Optional:False>, enable_locking=<Optional:False>, enable_downloads=<Optional:False>, copy_permissions=<Optional:False>)
70 70
71 71 Creates a repository.
72 72
73 73 * If the repository name contains "/", repository will be created inside
74 74 a repository group or nested repository groups
75 75
76 76 For example "foo/bar/repo1" will create |repo| called "repo1" inside
77 77 group "foo/bar". You have to have permissions to access and write to
78 78 the last repository group ("bar" in this example)
79 79
80 80 This command can only be run using an |authtoken| with at least
81 81 permissions to create repositories, or write permissions to
82 82 parent repository groups.
83 83
84 84 :param apiuser: This is filled automatically from the |authtoken|.
85 85 :type apiuser: AuthUser
86 86 :param repo_name: Set the repository name.
87 87 :type repo_name: str
88 88 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
89 89 :type repo_type: str
90 90 :param owner: user_id or username
91 91 :type owner: Optional(str)
92 92 :param description: Set the repository description.
93 93 :type description: Optional(str)
94 94 :param private: set repository as private
95 95 :type private: bool
96 96 :param clone_uri: set clone_uri
97 97 :type clone_uri: str
98 :param push_uri: set push_uri
99 :type push_uri: str
98 100 :param landing_rev: <rev_type>:<rev>
99 101 :type landing_rev: str
100 102 :param enable_locking:
101 103 :type enable_locking: bool
102 104 :param enable_downloads:
103 105 :type enable_downloads: bool
104 106 :param enable_statistics:
105 107 :type enable_statistics: bool
106 108 :param copy_permissions: Copy permission from group in which the
107 109 repository is being created.
108 110 :type copy_permissions: bool
109 111
110 112
111 113 Example output:
112 114
113 115 .. code-block:: bash
114 116
115 117 id : <id_given_in_input>
116 118 result: {
117 119 "msg": "Created new repository `<reponame>`",
118 120 "success": true,
119 121 "task": "<celery task id or None if done sync>"
120 122 }
121 123 error: null
122 124
123 125
124 126 Example error output:
125 127
126 128 .. code-block:: bash
127 129
128 130 id : <id_given_in_input>
129 131 result : null
130 132 error : {
131 133 'failed to create repository `<repo_name>`'
132 134 }
133 135
134 136
135 137 delete_repo
136 138 -----------
137 139
138 140 .. py:function:: delete_repo(apiuser, repoid, forks=<Optional:''>)
139 141
140 142 Deletes a repository.
141 143
142 144 * When the `forks` parameter is set it's possible to detach or delete
143 145 forks of deleted repository.
144 146
145 147 This command can only be run using an |authtoken| with admin
146 148 permissions on the |repo|.
147 149
148 150 :param apiuser: This is filled automatically from the |authtoken|.
149 151 :type apiuser: AuthUser
150 152 :param repoid: Set the repository name or repository ID.
151 153 :type repoid: str or int
152 154 :param forks: Set to `detach` or `delete` forks from the |repo|.
153 155 :type forks: Optional(str)
154 156
155 157 Example error output:
156 158
157 159 .. code-block:: bash
158 160
159 161 id : <id_given_in_input>
160 162 result: {
161 163 "msg": "Deleted repository `<reponame>`",
162 164 "success": true
163 165 }
164 166 error: null
165 167
166 168
167 169 fork_repo
168 170 ---------
169 171
170 172 .. py:function:: fork_repo(apiuser, repoid, fork_name, owner=<Optional:<OptionalAttr:apiuser>>, description=<Optional:''>, private=<Optional:False>, clone_uri=<Optional:None>, landing_rev=<Optional:'rev:tip'>, copy_permissions=<Optional:False>)
171 173
172 174 Creates a fork of the specified |repo|.
173 175
174 176 * If the fork_name contains "/", fork will be created inside
175 177 a repository group or nested repository groups
176 178
177 179 For example "foo/bar/fork-repo" will create fork called "fork-repo"
178 180 inside group "foo/bar". You have to have permissions to access and
179 181 write to the last repository group ("bar" in this example)
180 182
181 183 This command can only be run using an |authtoken| with minimum
182 184 read permissions of the forked repo, create fork permissions for an user.
183 185
184 186 :param apiuser: This is filled automatically from the |authtoken|.
185 187 :type apiuser: AuthUser
186 188 :param repoid: Set repository name or repository ID.
187 189 :type repoid: str or int
188 190 :param fork_name: Set the fork name, including it's repository group membership.
189 191 :type fork_name: str
190 192 :param owner: Set the fork owner.
191 193 :type owner: str
192 194 :param description: Set the fork description.
193 195 :type description: str
194 196 :param copy_permissions: Copy permissions from parent |repo|. The
195 197 default is False.
196 198 :type copy_permissions: bool
197 199 :param private: Make the fork private. The default is False.
198 200 :type private: bool
199 201 :param landing_rev: Set the landing revision. The default is tip.
200 202
201 203 Example output:
202 204
203 205 .. code-block:: bash
204 206
205 207 id : <id_for_response>
206 208 api_key : "<api_key>"
207 209 args: {
208 210 "repoid" : "<reponame or repo_id>",
209 211 "fork_name": "<forkname>",
210 212 "owner": "<username or user_id = Optional(=apiuser)>",
211 213 "description": "<description>",
212 214 "copy_permissions": "<bool>",
213 215 "private": "<bool>",
214 216 "landing_rev": "<landing_rev>"
215 217 }
216 218
217 219 Example error output:
218 220
219 221 .. code-block:: bash
220 222
221 223 id : <id_given_in_input>
222 224 result: {
223 225 "msg": "Created fork of `<reponame>` as `<forkname>`",
224 226 "success": true,
225 227 "task": "<celery task id or None if done sync>"
226 228 }
227 229 error: null
228 230
229 231
230 232 get_repo
231 233 --------
232 234
233 235 .. py:function:: get_repo(apiuser, repoid, cache=<Optional:True>)
234 236
235 237 Gets an existing repository by its name or repository_id.
236 238
237 239 The members section so the output returns users groups or users
238 240 associated with that repository.
239 241
240 242 This command can only be run using an |authtoken| with admin rights,
241 243 or users with at least read rights to the |repo|.
242 244
243 245 :param apiuser: This is filled automatically from the |authtoken|.
244 246 :type apiuser: AuthUser
245 247 :param repoid: The repository name or repository id.
246 248 :type repoid: str or int
247 249 :param cache: use the cached value for last changeset
248 250 :type: cache: Optional(bool)
249 251
250 252 Example output:
251 253
252 254 .. code-block:: bash
253 255
254 256 {
255 257 "error": null,
256 258 "id": <repo_id>,
257 259 "result": {
258 260 "clone_uri": null,
259 261 "created_on": "timestamp",
260 262 "description": "repo description",
261 263 "enable_downloads": false,
262 264 "enable_locking": false,
263 265 "enable_statistics": false,
264 266 "followers": [
265 267 {
266 268 "active": true,
267 269 "admin": false,
268 270 "api_key": "****************************************",
269 271 "api_keys": [
270 272 "****************************************"
271 273 ],
272 274 "email": "user@example.com",
273 275 "emails": [
274 276 "user@example.com"
275 277 ],
276 278 "extern_name": "rhodecode",
277 279 "extern_type": "rhodecode",
278 280 "firstname": "username",
279 281 "ip_addresses": [],
280 282 "language": null,
281 283 "last_login": "2015-09-16T17:16:35.854",
282 284 "lastname": "surname",
283 285 "user_id": <user_id>,
284 286 "username": "name"
285 287 }
286 288 ],
287 289 "fork_of": "parent-repo",
288 290 "landing_rev": [
289 291 "rev",
290 292 "tip"
291 293 ],
292 294 "last_changeset": {
293 295 "author": "User <user@example.com>",
294 296 "branch": "default",
295 297 "date": "timestamp",
296 298 "message": "last commit message",
297 299 "parents": [
298 300 {
299 301 "raw_id": "commit-id"
300 302 }
301 303 ],
302 304 "raw_id": "commit-id",
303 305 "revision": <revision number>,
304 306 "short_id": "short id"
305 307 },
306 308 "lock_reason": null,
307 309 "locked_by": null,
308 310 "locked_date": null,
309 311 "owner": "owner-name",
310 312 "permissions": [
311 313 {
312 314 "name": "super-admin-name",
313 315 "origin": "super-admin",
314 316 "permission": "repository.admin",
315 317 "type": "user"
316 318 },
317 319 {
318 320 "name": "owner-name",
319 321 "origin": "owner",
320 322 "permission": "repository.admin",
321 323 "type": "user"
322 324 },
323 325 {
324 326 "name": "user-group-name",
325 327 "origin": "permission",
326 328 "permission": "repository.write",
327 329 "type": "user_group"
328 330 }
329 331 ],
330 332 "private": true,
331 333 "repo_id": 676,
332 334 "repo_name": "user-group/repo-name",
333 335 "repo_type": "hg"
334 336 }
335 337 }
336 338
337 339
338 340 get_repo_changeset
339 341 ------------------
340 342
341 343 .. py:function:: get_repo_changeset(apiuser, repoid, revision, details=<Optional:'basic'>)
342 344
343 345 Returns information about a changeset.
344 346
345 347 Additionally parameters define the amount of details returned by
346 348 this function.
347 349
348 350 This command can only be run using an |authtoken| with admin rights,
349 351 or users with at least read rights to the |repo|.
350 352
351 353 :param apiuser: This is filled automatically from the |authtoken|.
352 354 :type apiuser: AuthUser
353 355 :param repoid: The repository name or repository id
354 356 :type repoid: str or int
355 357 :param revision: revision for which listing should be done
356 358 :type revision: str
357 359 :param details: details can be 'basic|extended|full' full gives diff
358 360 info details like the diff itself, and number of changed files etc.
359 361 :type details: Optional(str)
360 362
361 363
362 364 get_repo_changesets
363 365 -------------------
364 366
365 367 .. py:function:: get_repo_changesets(apiuser, repoid, start_rev, limit, details=<Optional:'basic'>)
366 368
367 369 Returns a set of commits limited by the number starting
368 370 from the `start_rev` option.
369 371
370 372 Additional parameters define the amount of details returned by this
371 373 function.
372 374
373 375 This command can only be run using an |authtoken| with admin rights,
374 376 or users with at least read rights to |repos|.
375 377
376 378 :param apiuser: This is filled automatically from the |authtoken|.
377 379 :type apiuser: AuthUser
378 380 :param repoid: The repository name or repository ID.
379 381 :type repoid: str or int
380 382 :param start_rev: The starting revision from where to get changesets.
381 383 :type start_rev: str
382 384 :param limit: Limit the number of commits to this amount
383 385 :type limit: str or int
384 386 :param details: Set the level of detail returned. Valid option are:
385 387 ``basic``, ``extended`` and ``full``.
386 388 :type details: Optional(str)
387 389
388 390 .. note::
389 391
390 392 Setting the parameter `details` to the value ``full`` is extensive
391 393 and returns details like the diff itself, and the number
392 394 of changed files.
393 395
394 396
395 397 get_repo_nodes
396 398 --------------
397 399
398 400 .. py:function:: get_repo_nodes(apiuser, repoid, revision, root_path, ret_type=<Optional:'all'>, details=<Optional:'basic'>, max_file_bytes=<Optional:None>)
399 401
400 402 Returns a list of nodes and children in a flat list for a given
401 403 path at given revision.
402 404
403 405 It's possible to specify ret_type to show only `files` or `dirs`.
404 406
405 407 This command can only be run using an |authtoken| with admin rights,
406 408 or users with at least read rights to |repos|.
407 409
408 410 :param apiuser: This is filled automatically from the |authtoken|.
409 411 :type apiuser: AuthUser
410 412 :param repoid: The repository name or repository ID.
411 413 :type repoid: str or int
412 414 :param revision: The revision for which listing should be done.
413 415 :type revision: str
414 416 :param root_path: The path from which to start displaying.
415 417 :type root_path: str
416 418 :param ret_type: Set the return type. Valid options are
417 419 ``all`` (default), ``files`` and ``dirs``.
418 420 :type ret_type: Optional(str)
419 421 :param details: Returns extended information about nodes, such as
420 422 md5, binary, and or content. The valid options are ``basic`` and
421 423 ``full``.
422 424 :type details: Optional(str)
423 425 :param max_file_bytes: Only return file content under this file size bytes
424 426 :type details: Optional(int)
425 427
426 428 Example output:
427 429
428 430 .. code-block:: bash
429 431
430 432 id : <id_given_in_input>
431 433 result: [
432 434 {
433 435 "name" : "<name>"
434 436 "type" : "<type>",
435 437 "binary": "<true|false>" (only in extended mode)
436 438 "md5" : "<md5 of file content>" (only in extended mode)
437 439 },
438 440 ...
439 441 ]
440 442 error: null
441 443
442 444
443 445 get_repo_refs
444 446 -------------
445 447
446 448 .. py:function:: get_repo_refs(apiuser, repoid)
447 449
448 450 Returns a dictionary of current references. It returns
449 451 bookmarks, branches, closed_branches, and tags for given repository
450 452
451 453 It's possible to specify ret_type to show only `files` or `dirs`.
452 454
453 455 This command can only be run using an |authtoken| with admin rights,
454 456 or users with at least read rights to |repos|.
455 457
456 458 :param apiuser: This is filled automatically from the |authtoken|.
457 459 :type apiuser: AuthUser
458 460 :param repoid: The repository name or repository ID.
459 461 :type repoid: str or int
460 462
461 463 Example output:
462 464
463 465 .. code-block:: bash
464 466
465 467 id : <id_given_in_input>
466 468 "result": {
467 469 "bookmarks": {
468 470 "dev": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
469 471 "master": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
470 472 },
471 473 "branches": {
472 474 "default": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
473 475 "stable": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
474 476 },
475 477 "branches_closed": {},
476 478 "tags": {
477 479 "tip": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
478 480 "v4.4.0": "1232313f9e6adac5ce5399c2a891dc1e72b79022",
479 481 "v4.4.1": "cbb9f1d329ae5768379cdec55a62ebdd546c4e27",
480 482 "v4.4.2": "24ffe44a27fcd1c5b6936144e176b9f6dd2f3a17",
481 483 }
482 484 }
483 485 error: null
484 486
485 487
486 488 get_repo_settings
487 489 -----------------
488 490
489 491 .. py:function:: get_repo_settings(apiuser, repoid, key=<Optional:None>)
490 492
491 493 Returns all settings for a repository. If key is given it only returns the
492 494 setting identified by the key or null.
493 495
494 496 :param apiuser: This is filled automatically from the |authtoken|.
495 497 :type apiuser: AuthUser
496 498 :param repoid: The repository name or repository id.
497 499 :type repoid: str or int
498 500 :param key: Key of the setting to return.
499 501 :type: key: Optional(str)
500 502
501 503 Example output:
502 504
503 505 .. code-block:: bash
504 506
505 507 {
506 508 "error": null,
507 509 "id": 237,
508 510 "result": {
509 511 "extensions_largefiles": true,
510 512 "extensions_evolve": true,
511 513 "hooks_changegroup_push_logger": true,
512 514 "hooks_changegroup_repo_size": false,
513 515 "hooks_outgoing_pull_logger": true,
514 516 "phases_publish": "True",
515 517 "rhodecode_hg_use_rebase_for_merging": true,
516 518 "rhodecode_pr_merge_enabled": true,
517 519 "rhodecode_use_outdated_comments": true
518 520 }
519 521 }
520 522
521 523
522 524 get_repos
523 525 ---------
524 526
525 527 .. py:function:: get_repos(apiuser, root=<Optional:None>, traverse=<Optional:True>)
526 528
527 529 Lists all existing repositories.
528 530
529 531 This command can only be run using an |authtoken| with admin rights,
530 532 or users with at least read rights to |repos|.
531 533
532 534 :param apiuser: This is filled automatically from the |authtoken|.
533 535 :type apiuser: AuthUser
534 536 :param root: specify root repository group to fetch repositories.
535 537 filters the returned repositories to be members of given root group.
536 538 :type root: Optional(None)
537 539 :param traverse: traverse given root into subrepositories. With this flag
538 540 set to False, it will only return top-level repositories from `root`.
539 541 if root is empty it will return just top-level repositories.
540 542 :type traverse: Optional(True)
541 543
542 544
543 545 Example output:
544 546
545 547 .. code-block:: bash
546 548
547 549 id : <id_given_in_input>
548 550 result: [
549 551 {
550 552 "repo_id" : "<repo_id>",
551 553 "repo_name" : "<reponame>"
552 554 "repo_type" : "<repo_type>",
553 555 "clone_uri" : "<clone_uri>",
554 556 "private": : "<bool>",
555 557 "created_on" : "<datetimecreated>",
556 558 "description" : "<description>",
557 559 "landing_rev": "<landing_rev>",
558 560 "owner": "<repo_owner>",
559 561 "fork_of": "<name_of_fork_parent>",
560 562 "enable_downloads": "<bool>",
561 563 "enable_locking": "<bool>",
562 564 "enable_statistics": "<bool>",
563 565 },
564 566 ...
565 567 ]
566 568 error: null
567 569
568 570
569 571 grant_user_group_permission
570 572 ---------------------------
571 573
572 574 .. py:function:: grant_user_group_permission(apiuser, repoid, usergroupid, perm)
573 575
574 576 Grant permission for a user group on the specified repository,
575 577 or update existing permissions.
576 578
577 579 This command can only be run using an |authtoken| with admin
578 580 permissions on the |repo|.
579 581
580 582 :param apiuser: This is filled automatically from the |authtoken|.
581 583 :type apiuser: AuthUser
582 584 :param repoid: Set the repository name or repository ID.
583 585 :type repoid: str or int
584 586 :param usergroupid: Specify the ID of the user group.
585 587 :type usergroupid: str or int
586 588 :param perm: Set the user group permissions using the following
587 589 format: (repository.(none|read|write|admin))
588 590 :type perm: str
589 591
590 592 Example output:
591 593
592 594 .. code-block:: bash
593 595
594 596 id : <id_given_in_input>
595 597 result : {
596 598 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
597 599 "success": true
598 600
599 601 }
600 602 error : null
601 603
602 604 Example error output:
603 605
604 606 .. code-block:: bash
605 607
606 608 id : <id_given_in_input>
607 609 result : null
608 610 error : {
609 611 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
610 612 }
611 613
612 614
613 615 grant_user_permission
614 616 ---------------------
615 617
616 618 .. py:function:: grant_user_permission(apiuser, repoid, userid, perm)
617 619
618 620 Grant permissions for the specified user on the given repository,
619 621 or update existing permissions if found.
620 622
621 623 This command can only be run using an |authtoken| with admin
622 624 permissions on the |repo|.
623 625
624 626 :param apiuser: This is filled automatically from the |authtoken|.
625 627 :type apiuser: AuthUser
626 628 :param repoid: Set the repository name or repository ID.
627 629 :type repoid: str or int
628 630 :param userid: Set the user name.
629 631 :type userid: str
630 632 :param perm: Set the user permissions, using the following format
631 633 ``(repository.(none|read|write|admin))``
632 634 :type perm: str
633 635
634 636 Example output:
635 637
636 638 .. code-block:: bash
637 639
638 640 id : <id_given_in_input>
639 641 result: {
640 642 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
641 643 "success": true
642 644 }
643 645 error: null
644 646
645 647
646 648 invalidate_cache
647 649 ----------------
648 650
649 651 .. py:function:: invalidate_cache(apiuser, repoid, delete_keys=<Optional:False>)
650 652
651 653 Invalidates the cache for the specified repository.
652 654
653 655 This command can only be run using an |authtoken| with admin rights to
654 656 the specified repository.
655 657
656 658 This command takes the following options:
657 659
658 660 :param apiuser: This is filled automatically from |authtoken|.
659 661 :type apiuser: AuthUser
660 662 :param repoid: Sets the repository name or repository ID.
661 663 :type repoid: str or int
662 664 :param delete_keys: This deletes the invalidated keys instead of
663 665 just flagging them.
664 666 :type delete_keys: Optional(``True`` | ``False``)
665 667
666 668 Example output:
667 669
668 670 .. code-block:: bash
669 671
670 672 id : <id_given_in_input>
671 673 result : {
672 674 'msg': Cache for repository `<repository name>` was invalidated,
673 675 'repository': <repository name>
674 676 }
675 677 error : null
676 678
677 679 Example error output:
678 680
679 681 .. code-block:: bash
680 682
681 683 id : <id_given_in_input>
682 684 result : null
683 685 error : {
684 686 'Error occurred during cache invalidation action'
685 687 }
686 688
687 689
688 690 lock
689 691 ----
690 692
691 693 .. py:function:: lock(apiuser, repoid, locked=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
692 694
693 695 Sets the lock state of the specified |repo| by the given user.
694 696 From more information, see :ref:`repo-locking`.
695 697
696 698 * If the ``userid`` option is not set, the repository is locked to the
697 699 user who called the method.
698 700 * If the ``locked`` parameter is not set, the current lock state of the
699 701 repository is displayed.
700 702
701 703 This command can only be run using an |authtoken| with admin rights to
702 704 the specified repository.
703 705
704 706 This command takes the following options:
705 707
706 708 :param apiuser: This is filled automatically from the |authtoken|.
707 709 :type apiuser: AuthUser
708 710 :param repoid: Sets the repository name or repository ID.
709 711 :type repoid: str or int
710 712 :param locked: Sets the lock state.
711 713 :type locked: Optional(``True`` | ``False``)
712 714 :param userid: Set the repository lock to this user.
713 715 :type userid: Optional(str or int)
714 716
715 717 Example error output:
716 718
717 719 .. code-block:: bash
718 720
719 721 id : <id_given_in_input>
720 722 result : {
721 723 'repo': '<reponame>',
722 724 'locked': <bool: lock state>,
723 725 'locked_since': <int: lock timestamp>,
724 726 'locked_by': <username of person who made the lock>,
725 727 'lock_reason': <str: reason for locking>,
726 728 'lock_state_changed': <bool: True if lock state has been changed in this request>,
727 729 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
728 730 or
729 731 'msg': 'Repo `<repository name>` not locked.'
730 732 or
731 733 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
732 734 }
733 735 error : null
734 736
735 737 Example error output:
736 738
737 739 .. code-block:: bash
738 740
739 741 id : <id_given_in_input>
740 742 result : null
741 743 error : {
742 744 'Error occurred locking repository `<reponame>`'
743 745 }
744 746
745 747
746 748 maintenance
747 749 -----------
748 750
749 751 .. py:function:: maintenance(apiuser, repoid)
750 752
751 753 Triggers a maintenance on the given repository.
752 754
753 755 This command can only be run using an |authtoken| with admin
754 756 rights to the specified repository. For more information,
755 757 see :ref:`config-token-ref`.
756 758
757 759 This command takes the following options:
758 760
759 761 :param apiuser: This is filled automatically from the |authtoken|.
760 762 :type apiuser: AuthUser
761 763 :param repoid: The repository name or repository ID.
762 764 :type repoid: str or int
763 765
764 766 Example output:
765 767
766 768 .. code-block:: bash
767 769
768 770 id : <id_given_in_input>
769 771 result : {
770 772 "msg": "executed maintenance command",
771 773 "executed_actions": [
772 774 <action_message>, <action_message2>...
773 775 ],
774 776 "repository": "<repository name>"
775 777 }
776 778 error : null
777 779
778 780 Example error output:
779 781
780 782 .. code-block:: bash
781 783
782 784 id : <id_given_in_input>
783 785 result : null
784 786 error : {
785 787 "Unable to execute maintenance on `<reponame>`"
786 788 }
787 789
788 790
789 791 pull
790 792 ----
791 793
792 .. py:function:: pull(apiuser, repoid)
794 .. py:function:: pull(apiuser, repoid, remote_uri=<Optional:None>)
793 795
794 796 Triggers a pull on the given repository from a remote location. You
795 797 can use this to keep remote repositories up-to-date.
796 798
797 799 This command can only be run using an |authtoken| with admin
798 800 rights to the specified repository. For more information,
799 801 see :ref:`config-token-ref`.
800 802
801 803 This command takes the following options:
802 804
803 805 :param apiuser: This is filled automatically from the |authtoken|.
804 806 :type apiuser: AuthUser
805 807 :param repoid: The repository name or repository ID.
806 808 :type repoid: str or int
809 :param remote_uri: Optional remote URI to pass in for pull
810 :type remote_uri: str
807 811
808 812 Example output:
809 813
810 814 .. code-block:: bash
811 815
812 816 id : <id_given_in_input>
813 817 result : {
814 "msg": "Pulled from `<repository name>`"
818 "msg": "Pulled from url `<remote_url>` on repo `<repository name>`"
815 819 "repository": "<repository name>"
816 820 }
817 821 error : null
818 822
819 823 Example error output:
820 824
821 825 .. code-block:: bash
822 826
823 827 id : <id_given_in_input>
824 828 result : null
825 829 error : {
826 "Unable to pull changes from `<reponame>`"
830 "Unable to push changes from `<remote_url>`"
827 831 }
828 832
829 833
830 834 remove_field_from_repo
831 835 ----------------------
832 836
833 837 .. py:function:: remove_field_from_repo(apiuser, repoid, key)
834 838
835 839 Removes an extra field from a repository.
836 840
837 841 This command can only be run using an |authtoken| with at least
838 842 write permissions to the |repo|.
839 843
840 844 :param apiuser: This is filled automatically from the |authtoken|.
841 845 :type apiuser: AuthUser
842 846 :param repoid: Set the repository name or repository ID.
843 847 :type repoid: str or int
844 848 :param key: Set the unique field key for this repository.
845 849 :type key: str
846 850
847 851
848 852 revoke_user_group_permission
849 853 ----------------------------
850 854
851 855 .. py:function:: revoke_user_group_permission(apiuser, repoid, usergroupid)
852 856
853 857 Revoke the permissions of a user group on a given repository.
854 858
855 859 This command can only be run using an |authtoken| with admin
856 860 permissions on the |repo|.
857 861
858 862 :param apiuser: This is filled automatically from the |authtoken|.
859 863 :type apiuser: AuthUser
860 864 :param repoid: Set the repository name or repository ID.
861 865 :type repoid: str or int
862 866 :param usergroupid: Specify the user group ID.
863 867 :type usergroupid: str or int
864 868
865 869 Example output:
866 870
867 871 .. code-block:: bash
868 872
869 873 id : <id_given_in_input>
870 874 result: {
871 875 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
872 876 "success": true
873 877 }
874 878 error: null
875 879
876 880
877 881 revoke_user_permission
878 882 ----------------------
879 883
880 884 .. py:function:: revoke_user_permission(apiuser, repoid, userid)
881 885
882 886 Revoke permission for a user on the specified repository.
883 887
884 888 This command can only be run using an |authtoken| with admin
885 889 permissions on the |repo|.
886 890
887 891 :param apiuser: This is filled automatically from the |authtoken|.
888 892 :type apiuser: AuthUser
889 893 :param repoid: Set the repository name or repository ID.
890 894 :type repoid: str or int
891 895 :param userid: Set the user name of revoked user.
892 896 :type userid: str or int
893 897
894 898 Example error output:
895 899
896 900 .. code-block:: bash
897 901
898 902 id : <id_given_in_input>
899 903 result: {
900 904 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
901 905 "success": true
902 906 }
903 907 error: null
904 908
905 909
906 910 set_repo_settings
907 911 -----------------
908 912
909 913 .. py:function:: set_repo_settings(apiuser, repoid, settings)
910 914
911 915 Update repository settings. Returns true on success.
912 916
913 917 :param apiuser: This is filled automatically from the |authtoken|.
914 918 :type apiuser: AuthUser
915 919 :param repoid: The repository name or repository id.
916 920 :type repoid: str or int
917 921 :param settings: The new settings for the repository.
918 922 :type: settings: dict
919 923
920 924 Example output:
921 925
922 926 .. code-block:: bash
923 927
924 928 {
925 929 "error": null,
926 930 "id": 237,
927 931 "result": true
928 932 }
929 933
930 934
931 935 strip
932 936 -----
933 937
934 938 .. py:function:: strip(apiuser, repoid, revision, branch)
935 939
936 940 Strips the given revision from the specified repository.
937 941
938 942 * This will remove the revision and all of its decendants.
939 943
940 944 This command can only be run using an |authtoken| with admin rights to
941 945 the specified repository.
942 946
943 947 This command takes the following options:
944 948
945 949 :param apiuser: This is filled automatically from the |authtoken|.
946 950 :type apiuser: AuthUser
947 951 :param repoid: The repository name or repository ID.
948 952 :type repoid: str or int
949 953 :param revision: The revision you wish to strip.
950 954 :type revision: str
951 955 :param branch: The branch from which to strip the revision.
952 956 :type branch: str
953 957
954 958 Example output:
955 959
956 960 .. code-block:: bash
957 961
958 962 id : <id_given_in_input>
959 963 result : {
960 964 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
961 965 "repository": "<repository name>"
962 966 }
963 967 error : null
964 968
965 969 Example error output:
966 970
967 971 .. code-block:: bash
968 972
969 973 id : <id_given_in_input>
970 974 result : null
971 975 error : {
972 976 "Unable to strip commit <commit_hash> from repo `<repository name>`"
973 977 }
974 978
975 979
976 980 update_repo
977 981 -----------
978 982
979 .. py:function:: update_repo(apiuser, repoid, repo_name=<Optional:None>, owner=<Optional:<OptionalAttr:apiuser>>, description=<Optional:''>, private=<Optional:False>, clone_uri=<Optional:None>, landing_rev=<Optional:'rev:tip'>, fork_of=<Optional:None>, enable_statistics=<Optional:False>, enable_locking=<Optional:False>, enable_downloads=<Optional:False>, fields=<Optional:''>)
983 .. py:function:: update_repo(apiuser, repoid, repo_name=<Optional:None>, owner=<Optional:<OptionalAttr:apiuser>>, description=<Optional:''>, private=<Optional:False>, clone_uri=<Optional:None>, push_uri=<Optional:None>, landing_rev=<Optional:'rev:tip'>, fork_of=<Optional:None>, enable_statistics=<Optional:False>, enable_locking=<Optional:False>, enable_downloads=<Optional:False>, fields=<Optional:''>)
980 984
981 985 Updates a repository with the given information.
982 986
983 987 This command can only be run using an |authtoken| with at least
984 988 admin permissions to the |repo|.
985 989
986 990 * If the repository name contains "/", repository will be updated
987 991 accordingly with a repository group or nested repository groups
988 992
989 993 For example repoid=repo-test name="foo/bar/repo-test" will update |repo|
990 994 called "repo-test" and place it inside group "foo/bar".
991 995 You have to have permissions to access and write to the last repository
992 996 group ("bar" in this example)
993 997
994 998 :param apiuser: This is filled automatically from the |authtoken|.
995 999 :type apiuser: AuthUser
996 1000 :param repoid: repository name or repository ID.
997 1001 :type repoid: str or int
998 1002 :param repo_name: Update the |repo| name, including the
999 1003 repository group it's in.
1000 1004 :type repo_name: str
1001 1005 :param owner: Set the |repo| owner.
1002 1006 :type owner: str
1003 1007 :param fork_of: Set the |repo| as fork of another |repo|.
1004 1008 :type fork_of: str
1005 1009 :param description: Update the |repo| description.
1006 1010 :type description: str
1007 1011 :param private: Set the |repo| as private. (True | False)
1008 1012 :type private: bool
1009 1013 :param clone_uri: Update the |repo| clone URI.
1010 1014 :type clone_uri: str
1011 1015 :param landing_rev: Set the |repo| landing revision. Default is ``rev:tip``.
1012 1016 :type landing_rev: str
1013 1017 :param enable_statistics: Enable statistics on the |repo|, (True | False).
1014 1018 :type enable_statistics: bool
1015 1019 :param enable_locking: Enable |repo| locking.
1016 1020 :type enable_locking: bool
1017 1021 :param enable_downloads: Enable downloads from the |repo|, (True | False).
1018 1022 :type enable_downloads: bool
1019 1023 :param fields: Add extra fields to the |repo|. Use the following
1020 1024 example format: ``field_key=field_val,field_key2=fieldval2``.
1021 1025 Escape ', ' with \,
1022 1026 :type fields: str
1023 1027
1024 1028
@@ -1,412 +1,418 b''
1 1 .. _user-group-methods-ref:
2 2
3 3 user_group methods
4 4 ==================
5 5
6 6 add_user_to_user_group
7 7 ----------------------
8 8
9 9 .. py:function:: add_user_to_user_group(apiuser, usergroupid, userid)
10 10
11 11 Adds a user to a `user group`. If the user already exists in the group
12 12 this command will return false.
13 13
14 14 This command can only be run using an |authtoken| with admin rights to
15 15 the specified user group.
16 16
17 17 This command takes the following options:
18 18
19 19 :param apiuser: This is filled automatically from the |authtoken|.
20 20 :type apiuser: AuthUser
21 21 :param usergroupid: Set the name of the `user group` to which a
22 22 user will be added.
23 23 :type usergroupid: int
24 24 :param userid: Set the `user_id` of the user to add to the group.
25 25 :type userid: int
26 26
27 27 Example output:
28 28
29 29 .. code-block:: bash
30 30
31 31 id : <id_given_in_input>
32 32 result : {
33 33 "success": True|False # depends on if member is in group
34 34 "msg": "added member `<username>` to user group `<groupname>` |
35 35 User is already in that group"
36 36
37 37 }
38 38 error : null
39 39
40 40 Example error output:
41 41
42 42 .. code-block:: bash
43 43
44 44 id : <id_given_in_input>
45 45 result : null
46 46 error : {
47 47 "failed to add member to user group `<user_group_name>`"
48 48 }
49 49
50 50
51 51 create_user_group
52 52 -----------------
53 53
54 .. py:function:: create_user_group(apiuser, group_name, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, active=<Optional:True>)
54 .. py:function:: create_user_group(apiuser, group_name, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, active=<Optional:True>, sync=<Optional:None>)
55 55
56 56 Creates a new user group.
57 57
58 58 This command can only be run using an |authtoken| with admin rights to
59 59 the specified repository.
60 60
61 61 This command takes the following options:
62 62
63 63 :param apiuser: This is filled automatically from the |authtoken|.
64 64 :type apiuser: AuthUser
65 65 :param group_name: Set the name of the new user group.
66 66 :type group_name: str
67 67 :param description: Give a description of the new user group.
68 68 :type description: str
69 69 :param owner: Set the owner of the new user group.
70 70 If not set, the owner is the |authtoken| user.
71 71 :type owner: Optional(str or int)
72 72 :param active: Set this group as active.
73 73 :type active: Optional(``True`` | ``False``)
74 :param sync: Set enabled or disabled the automatically sync from
75 external authentication types like ldap.
76 :type sync: Optional(``True`` | ``False``)
74 77
75 78 Example output:
76 79
77 80 .. code-block:: bash
78 81
79 82 id : <id_given_in_input>
80 83 result: {
81 84 "msg": "created new user group `<groupname>`",
82 85 "user_group": <user_group_object>
83 86 }
84 87 error: null
85 88
86 89 Example error output:
87 90
88 91 .. code-block:: bash
89 92
90 93 id : <id_given_in_input>
91 94 result : null
92 95 error : {
93 96 "user group `<group name>` already exist"
94 97 or
95 98 "failed to create group `<group name>`"
96 99 }
97 100
98 101
99 102 delete_user_group
100 103 -----------------
101 104
102 105 .. py:function:: delete_user_group(apiuser, usergroupid)
103 106
104 107 Deletes the specified `user group`.
105 108
106 109 This command can only be run using an |authtoken| with admin rights to
107 110 the specified repository.
108 111
109 112 This command takes the following options:
110 113
111 114 :param apiuser: filled automatically from apikey
112 115 :type apiuser: AuthUser
113 116 :param usergroupid:
114 117 :type usergroupid: int
115 118
116 119 Example output:
117 120
118 121 .. code-block:: bash
119 122
120 123 id : <id_given_in_input>
121 124 result : {
122 125 "msg": "deleted user group ID:<user_group_id> <user_group_name>"
123 126 }
124 127 error : null
125 128
126 129 Example error output:
127 130
128 131 .. code-block:: bash
129 132
130 133 id : <id_given_in_input>
131 134 result : null
132 135 error : {
133 136 "failed to delete user group ID:<user_group_id> <user_group_name>"
134 137 or
135 138 "RepoGroup assigned to <repo_groups_list>"
136 139 }
137 140
138 141
139 142 get_user_group
140 143 --------------
141 144
142 145 .. py:function:: get_user_group(apiuser, usergroupid)
143 146
144 147 Returns the data of an existing user group.
145 148
146 149 This command can only be run using an |authtoken| with admin rights to
147 150 the specified repository.
148 151
149 152 :param apiuser: This is filled automatically from the |authtoken|.
150 153 :type apiuser: AuthUser
151 154 :param usergroupid: Set the user group from which to return data.
152 155 :type usergroupid: str or int
153 156
154 157 Example error output:
155 158
156 159 .. code-block:: bash
157 160
158 161 {
159 162 "error": null,
160 163 "id": <id>,
161 164 "result": {
162 165 "active": true,
163 166 "group_description": "group description",
164 167 "group_name": "group name",
165 168 "permissions": [
166 169 {
167 170 "name": "owner-name",
168 171 "origin": "owner",
169 172 "permission": "usergroup.admin",
170 173 "type": "user"
171 174 },
172 175 {
173 176 {
174 177 "name": "user name",
175 178 "origin": "permission",
176 179 "permission": "usergroup.admin",
177 180 "type": "user"
178 181 },
179 182 {
180 183 "name": "user group name",
181 184 "origin": "permission",
182 185 "permission": "usergroup.write",
183 186 "type": "user_group"
184 187 }
185 188 ],
186 189 "permissions_summary": {
187 190 "repositories": {
188 191 "aa-root-level-repo-1": "repository.admin"
189 192 },
190 193 "repositories_groups": {}
191 194 },
192 195 "owner": "owner name",
193 196 "users": [],
194 197 "users_group_id": 2
195 198 }
196 199 }
197 200
198 201
199 202 get_user_groups
200 203 ---------------
201 204
202 205 .. py:function:: get_user_groups(apiuser)
203 206
204 207 Lists all the existing user groups within RhodeCode.
205 208
206 209 This command can only be run using an |authtoken| with admin rights to
207 210 the specified repository.
208 211
209 212 This command takes the following options:
210 213
211 214 :param apiuser: This is filled automatically from the |authtoken|.
212 215 :type apiuser: AuthUser
213 216
214 217 Example error output:
215 218
216 219 .. code-block:: bash
217 220
218 221 id : <id_given_in_input>
219 222 result : [<user_group_obj>,...]
220 223 error : null
221 224
222 225
223 226 grant_user_group_permission_to_user_group
224 227 -----------------------------------------
225 228
226 229 .. py:function:: grant_user_group_permission_to_user_group(apiuser, usergroupid, sourceusergroupid, perm)
227 230
228 231 Give one user group permissions to another user group.
229 232
230 233 :param apiuser: This is filled automatically from the |authtoken|.
231 234 :type apiuser: AuthUser
232 235 :param usergroupid: Set the user group on which to edit permissions.
233 236 :type usergroupid: str or int
234 237 :param sourceusergroupid: Set the source user group to which
235 238 access/permissions will be granted.
236 239 :type sourceusergroupid: str or int
237 240 :param perm: (usergroup.(none|read|write|admin))
238 241 :type perm: str
239 242
240 243 Example output:
241 244
242 245 .. code-block:: bash
243 246
244 247 id : <id_given_in_input>
245 248 result : {
246 249 "msg": "Granted perm: `<perm_name>` for user group: `<source_user_group_name>` in user group: `<user_group_name>`",
247 250 "success": true
248 251 }
249 252 error : null
250 253
251 254
252 255 grant_user_permission_to_user_group
253 256 -----------------------------------
254 257
255 258 .. py:function:: grant_user_permission_to_user_group(apiuser, usergroupid, userid, perm)
256 259
257 260 Set permissions for a user in a user group.
258 261
259 262 :param apiuser: This is filled automatically from the |authtoken|.
260 263 :type apiuser: AuthUser
261 264 :param usergroupid: Set the user group to edit permissions on.
262 265 :type usergroupid: str or int
263 266 :param userid: Set the user from whom you wish to set permissions.
264 267 :type userid: str
265 268 :param perm: (usergroup.(none|read|write|admin))
266 269 :type perm: str
267 270
268 271 Example output:
269 272
270 273 .. code-block:: bash
271 274
272 275 id : <id_given_in_input>
273 276 result : {
274 277 "msg": "Granted perm: `<perm_name>` for user: `<username>` in user group: `<user_group_name>`",
275 278 "success": true
276 279 }
277 280 error : null
278 281
279 282
280 283 remove_user_from_user_group
281 284 ---------------------------
282 285
283 286 .. py:function:: remove_user_from_user_group(apiuser, usergroupid, userid)
284 287
285 288 Removes a user from a user group.
286 289
287 290 * If the specified user is not in the group, this command will return
288 291 `false`.
289 292
290 293 This command can only be run using an |authtoken| with admin rights to
291 294 the specified user group.
292 295
293 296 :param apiuser: This is filled automatically from the |authtoken|.
294 297 :type apiuser: AuthUser
295 298 :param usergroupid: Sets the user group name.
296 299 :type usergroupid: str or int
297 300 :param userid: The user you wish to remove from |RCE|.
298 301 :type userid: str or int
299 302
300 303 Example output:
301 304
302 305 .. code-block:: bash
303 306
304 307 id : <id_given_in_input>
305 308 result: {
306 309 "success": True|False, # depends on if member is in group
307 310 "msg": "removed member <username> from user group <groupname> |
308 311 User wasn't in group"
309 312 }
310 313 error: null
311 314
312 315
313 316 revoke_user_group_permission_from_user_group
314 317 --------------------------------------------
315 318
316 319 .. py:function:: revoke_user_group_permission_from_user_group(apiuser, usergroupid, sourceusergroupid)
317 320
318 321 Revoke the permissions that one user group has to another.
319 322
320 323 :param apiuser: This is filled automatically from the |authtoken|.
321 324 :type apiuser: AuthUser
322 325 :param usergroupid: Set the user group on which to edit permissions.
323 326 :type usergroupid: str or int
324 327 :param sourceusergroupid: Set the user group from which permissions
325 328 are revoked.
326 329 :type sourceusergroupid: str or int
327 330
328 331 Example output:
329 332
330 333 .. code-block:: bash
331 334
332 335 id : <id_given_in_input>
333 336 result : {
334 337 "msg": "Revoked perm for user group: `<user_group_name>` in user group: `<target_user_group_name>`",
335 338 "success": true
336 339 }
337 340 error : null
338 341
339 342
340 343 revoke_user_permission_from_user_group
341 344 --------------------------------------
342 345
343 346 .. py:function:: revoke_user_permission_from_user_group(apiuser, usergroupid, userid)
344 347
345 348 Revoke a users permissions in a user group.
346 349
347 350 :param apiuser: This is filled automatically from the |authtoken|.
348 351 :type apiuser: AuthUser
349 352 :param usergroupid: Set the user group from which to revoke the user
350 353 permissions.
351 354 :type: usergroupid: str or int
352 355 :param userid: Set the userid of the user whose permissions will be
353 356 revoked.
354 357 :type userid: str
355 358
356 359 Example output:
357 360
358 361 .. code-block:: bash
359 362
360 363 id : <id_given_in_input>
361 364 result : {
362 365 "msg": "Revoked perm for user: `<username>` in user group: `<user_group_name>`",
363 366 "success": true
364 367 }
365 368 error : null
366 369
367 370
368 371 update_user_group
369 372 -----------------
370 373
371 .. py:function:: update_user_group(apiuser, usergroupid, group_name=<Optional:''>, description=<Optional:''>, owner=<Optional:None>, active=<Optional:True>)
374 .. py:function:: update_user_group(apiuser, usergroupid, group_name=<Optional:''>, description=<Optional:''>, owner=<Optional:None>, active=<Optional:True>, sync=<Optional:None>)
372 375
373 376 Updates the specified `user group` with the details provided.
374 377
375 378 This command can only be run using an |authtoken| with admin rights to
376 379 the specified repository.
377 380
378 381 :param apiuser: This is filled automatically from the |authtoken|.
379 382 :type apiuser: AuthUser
380 383 :param usergroupid: Set the id of the `user group` to update.
381 384 :type usergroupid: str or int
382 385 :param group_name: Set the new name the `user group`
383 386 :type group_name: str
384 387 :param description: Give a description for the `user group`
385 388 :type description: str
386 389 :param owner: Set the owner of the `user group`.
387 390 :type owner: Optional(str or int)
388 391 :param active: Set the group as active.
389 392 :type active: Optional(``True`` | ``False``)
393 :param sync: Set enabled or disabled the automatically sync from
394 external authentication types like ldap.
395 :type sync: Optional(``True`` | ``False``)
390 396
391 397 Example output:
392 398
393 399 .. code-block:: bash
394 400
395 401 id : <id_given_in_input>
396 402 result : {
397 403 "msg": 'updated user group ID:<user group id> <user group name>',
398 404 "user_group": <user_group_object>
399 405 }
400 406 error : null
401 407
402 408 Example error output:
403 409
404 410 .. code-block:: bash
405 411
406 412 id : <id_given_in_input>
407 413 result : null
408 414 error : {
409 415 "failed to update user group `<user group name>`"
410 416 }
411 417
412 418
1 NO CONTENT: file renamed from docs/auth/crowd-auth.rst to docs/auth/auth-crowd.rst
@@ -1,90 +1,89 b''
1 1 .. _config-ldap-ref:
2 2
3 LDAP
4 ----
3 LDAP/AD
4 -------
5 5
6 6 |RCM| supports LDAP (Lightweight Directory Access Protocol) or
7 7 AD (active Directory) authentication.
8 8 All LDAP versions are supported, with the following |RCM| plugins managing each:
9 9
10 * For LDAPv3 use ``LDAP (egg:rhodecode-enterprise-ce#ldap)``
11 * For LDAPv3 with user group sync use ``LDAP + User Groups (egg:rhodecode-enterprise-ee#ldap_group)``
10 * For LDAP or Active Directory use ``LDAP (egg:rhodecode-enterprise-ce#ldap)``
11
12 RhodeCode reads all data defined from plugin and creates corresponding
13 accounts on local database after receiving data from LDAP. This is done on
14 every user log-in including operations like pushing/pulling/checkout.
12 15
13 16
14 17 .. important::
15 18
16 19 The email used with your |RCE| super-admin account needs to match the email
17 20 address attached to your admin profile in LDAP. This is because
18 21 within |RCE| the user email needs to be unique, and multiple users
19 22 cannot share an email account.
20 23
21 24 Likewise, if as an admin you also have a user account, the email address
22 25 attached to the user account needs to be different.
23 26
27
24 28 LDAP Configuration Steps
25 29 ^^^^^^^^^^^^^^^^^^^^^^^^
26 30
27 31 To configure |LDAP|, use the following steps:
28 32
29 33 1. From the |RCM| interface, select
30 34 :menuselection:`Admin --> Authentication`
31 2. Enable the required plugin and select :guilabel:`Save`
35 2. Enable the ldap plugin and select :guilabel:`Save`
32 36 3. Select the :guilabel:`Enabled` check box in the plugin configuration section
33 37 4. Add the required LDAP information and :guilabel:`Save`, for more details,
34 38 see :ref:`config-ldap-examples`
35 39
36 40 For a more detailed description of LDAP objects, see :ref:`ldap-gloss-ref`:
37 41
38 42 .. _config-ldap-examples:
39 43
40 44 Example LDAP configuration
41 45 ^^^^^^^^^^^^^^^^^^^^^^^^^^
42 46 .. code-block:: bash
43 47
44 # Auth Cache TTL
48 # Auth Cache TTL, Defines the caching for authentication to offload LDAP server.
49 # This means that cache result will be saved for 3600 before contacting LDAP server to verify the user access
45 50 3600
46 # Host
51 # Host, comma seperated format is optionally possible to specify more than 1 server
47 52 https://ldap1.server.com/ldap-admin/,https://ldap2.server.com/ldap-admin/
48 # Port
53 # Default LDAP Port, use 689 for LDAPS
49 54 389
50 # Account
51 cn=admin,dc=rhodecode,dc=com
52 # Password
55 # Account, used for SimpleBind if LDAP server requires an authentication
56 e.g admin@server.com
57 # Password used for simple bind
53 58 ldap-user-password
54 59 # LDAP connection security
55 60 LDAPS
56 61 # Certificate checks level
57 62 DEMAND
58 63 # Base DN
59 64 cn=Rufus Magillacuddy,ou=users,dc=rhodecode,dc=com
60 # User Search Base
61 ou=groups,ou=users
62 # LDAP search filter
65 # LDAP search filter to narrow the results
63 66 (objectClass=person)
64 67 # LDAP search scope
65 68 SUBTREE
66 69 # Login attribute
67 rmagillacuddy
68 # First Name Attribute
69 Rufus
70 # Last Name Attribute
71 Magillacuddy
72 # Email Attribute
73 LDAP-Registered@email.ac
74 # User Member of Attribute
75 Organizational Role
76 # Group search base
77 cn=users,ou=groups,dc=rhodecode,dc=com
78 # LDAP Group Search Filter
79 (objectclass=posixGroup)
80 # Group Name Attribute
81 users
82 # Group Member Of Attribute
83 cn
84 # Admin Groups
85 admin,devops,qa
70 sAMAccountName
71 # First Name Attribute to read
72 givenName
73 # Last Name Attribute to read
74 sn
75 # Email Attribute to read email address from
76 mail
77
78
79 Below is example setup that can be used with Active Directory/LDAP server.
80
81 .. image:: ../images/ldap-example.png
82 :alt: LDAP/AD setup example
83 :scale: 50 %
84
86 85
87 86 .. toctree::
88 87
89 88 ldap-active-directory
90 89 ldap-authentication
1 NO CONTENT: file renamed from docs/auth/pam-auth.rst to docs/auth/auth-pam.rst
1 NO CONTENT: file renamed from docs/auth/token-auth.rst to docs/auth/auth-token.rst
@@ -1,37 +1,32 b''
1 1 .. _authentication-ref:
2 2
3 3 Authentication Options
4 4 ======================
5 5
6 |RCE| provides a built in authentication plugin
7 ``rhodecode.lib.auth_rhodecode``. This is enabled by default and accessed
8 through the administrative interface. Additionally,
9 |RCE| provides a Pluggable Authentication System (PAS). This gives the
6 |RCE| provides a built in authentication against its own database. This is
7 implemented using ``rhodecode.lib.auth_rhodecode`` plugin. This plugin is
8 enabled by default.
9 Additionally, |RCE| provides a Pluggable Authentication System. This gives the
10 10 administrator greater control over how users authenticate with the system.
11 11
12 12 .. important::
13 13
14 14 You can disable the built in |RCM| authentication plugin
15 15 ``rhodecode.lib.auth_rhodecode`` and force all authentication to go
16 through your authentication plugin. However, if you do this,
17 and your external authentication tools fails, you will be unable to
18 access |RCM|.
16 through your authentication plugin of choice e.g LDAP only.
17 However, if you do this, and your external authentication tools fails,
18 you will be unable to access |RCM|.
19 19
20 20 |RCM| comes with the following user authentication management plugins:
21 21
22 .. only:: latex
23
24 * :ref:`config-ldap-ref`
25 * :ref:`config-pam-ref`
26 * :ref:`config-crowd-ref`
27 * :ref:`config-token-ref`
28 22
29 23 .. toctree::
30 24
31 ldap-config-steps
32 crowd-auth
33 pam-auth
34 token-auth
25 auth-ldap
26 auth-ldap-groups
27 auth-crowd
28 auth-pam
29 auth-token
35 30 ssh-connection
36 31
37 32
@@ -1,317 +1,316 b''
1 1 # -*- coding: utf-8 -*-
2 2 #
3 3 # RhodeCode Enterprise documentation build configuration file, created by
4 4 # sphinx-quickstart on Tue Nov 4 11:48:37 2014.
5 5 #
6 6 # This file is execfile()d with the current directory set to its
7 7 # containing dir.
8 8 #
9 9 # Note that not all possible configuration values are present in this
10 10 # autogenerated file.
11 11 #
12 12 # All configuration values have a default; values that are commented out
13 13 # serve to show the default.
14 14
15 15 import sys
16 16 import os
17 17 import datetime
18 18 import sphinx_rtd_theme
19 19
20 20 # If extensions (or modules to document with autodoc) are in another directory,
21 21 # add these directories to sys.path here. If the directory is relative to the
22 22 # documentation root, use os.path.abspath to make it absolute, like shown here.
23 23 sys.path.insert(0, os.path.abspath('.'))
24 24 import common
25 25
26 26 # -- General configuration ------------------------------------------------
27 27
28 28 # If your documentation needs a minimal Sphinx version, state it here.
29 29 #needs_sphinx = '1.0'
30 30
31 31 # Add any Sphinx extension module names here, as strings. They can be
32 32 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 33 # ones.
34 34 extensions = [
35 35 'sphinx.ext.intersphinx',
36 36 'sphinx.ext.todo',
37 'sphinx.ext.pngmath'
37 'sphinx.ext.imgmath'
38 38 ]
39 39
40 40 intersphinx_mapping = {
41 41 'enterprise': ('https://docs.rhodecode.com/RhodeCode-Enterprise/', None),
42 42 'control': ('https://docs.rhodecode.com/RhodeCode-Control/', None),
43 43 }
44 44
45 45 if tags.has('dev'):
46 46 intersphinx_mapping.update({
47 47 'enterprise': ('https://ci.rhodecode.com/documentation/Momentum/', None),
48 48 'control': ('https://ci.rhodecode.com/documentation/Control/', None),
49 49 })
50 50
51 51
52 52 # Add any paths that contain templates here, relative to this directory.
53 53 templates_path = ['_templates']
54 54
55 55 # The suffix of source filenames.
56 56 source_suffix = '.rst'
57 57
58 58 # The encoding of source files.
59 59 #source_encoding = 'utf-8-sig'
60 60
61 61 # The master toctree document.
62 62 master_doc = 'index'
63 63
64 64 # The version info for the project you're documenting, acts as replacement for
65 65 # |version| and |release|, also used in various other places throughout the
66 66 # built documents.
67 67
68 68 # TODO: johbo: Move into common package for documentation utilities
69 69 def _get_version():
70 70 with open('../rhodecode/VERSION') as f:
71 71 return f.read().strip()
72 72
73 73 # The full version, including alpha/beta/rc tags.
74 74 release = _get_version()
75 75 # The short X.Y version.
76 76 version = '.'.join(release.split('.', 2)[:2]) # First two parts of release
77 77
78 78 # General information about the project.
79 79 project = u'RhodeCode Enterprise %s ' % _get_version()
80 80 copyright = u'2010-{now.year}, RhodeCode GmbH'.format(
81 81 now=datetime.datetime.today())
82 82
83 83
84 84 # The language for content autogenerated by Sphinx. Refer to documentation
85 85 # for a list of supported languages.
86 86 #language = None
87 87
88 88 rst_epilog = common.rst_epilog + """
89 89 .. |async| replace:: asynchronous
90 90 """
91 91
92 92 # There are two options for replacing |today|: either, you set today to some
93 93 # non-false value, then it is used:
94 94 #today = ''
95 95 # Else, today_fmt is used as the format for a strftime call.
96 96 #today_fmt = '%B %d, %Y'
97 97
98 98 # List of patterns, relative to source directory, that match files and
99 99 # directories to ignore when looking for source files.
100 100 exclude_patterns = [
101 101 # Special directories
102 102 '_build',
103 103 'result',
104 104
105 105 # Other RST files
106 106 'admin/rhodecode-backup.rst',
107 'auth/ldap-configuration-example.rst',
108 107 'issue-trackers/redmine.rst',
109 108 'known-issues/error-msg-guide.rst',
110 109 'tutorials/docs-build.rst',
111 110 'integrations/example-ext.py',
112 111 'collaboration/supported-workflows.rst',
113 112 ]
114 113
115 114
116 115 # The reST default role (used for this markup: `text`) to use for all
117 116 # documents.
118 117 #default_role = None
119 118
120 119 # If true, '()' will be appended to :func: etc. cross-reference text.
121 120 #add_function_parentheses = True
122 121
123 122 # If true, the current module name will be prepended to all description
124 123 # unit titles (such as .. function::).
125 124 #add_module_names = True
126 125
127 126 # If true, sectionauthor and moduleauthor directives will be shown in the
128 127 # output. They are ignored by default.
129 128 #show_authors = False
130 129
131 130 # The name of the Pygments (syntax highlighting) style to use.
132 131 pygments_style = 'sphinx'
133 132
134 133 # A list of ignored prefixes for module index sorting.
135 134 #modindex_common_prefix = []
136 135
137 136 # If true, keep warnings as "system message" paragraphs in the built documents.
138 137 keep_warnings = tags.has("dev")
139 138
140 139
141 140 # -- Options for HTML output ----------------------------------------------
142 141
143 142 # The theme to use for HTML and HTML Help pages. See the documentation for
144 143 # a list of builtin themes.
145 144 #html_theme = 'rctheme'
146 145 html_theme = 'sphinx_rtd_theme'
147 146
148 147 # Theme options are theme-specific and customize the look and feel of a theme
149 148 # further. For a list of options available for each theme, see the
150 149 # documentation.
151 150 #html_theme_options = {}
152 151 html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
153 152
154 153 # Add any paths that contain custom themes here, relative to this directory.
155 154 #html_theme_path = []
156 155
157 156 # The name for this set of Sphinx documents. If None, it defaults to
158 157 # "<project> v<release> documentation".
159 158 #html_title = None
160 159
161 160 # A shorter title for the navigation bar. Default is the same as html_title.
162 161 #html_short_title = None
163 162
164 163 # The name of an image file (relative to this directory) to place at the top
165 164 # of the sidebar.
166 165 #html_logo = None
167 166 html_sidebars = {
168 167 '**': ['globaltoc.html'],
169 168 }
170 169
171 170 # The name of an image file (within the static path) to use as favicon of the
172 171 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
173 172 # pixels large.
174 173 html_favicon = 'images/favicon.ico'
175 174
176 175 # Add any paths that contain custom static files (such as style sheets) here,
177 176 # relative to this directory. They are copied after the builtin static files,
178 177 # so a file named "default.css" will overwrite the builtin "default.css".
179 178 html_static_path = ['static/css/add.css']
180 179
181 180 # Add any extra paths that contain custom files (such as robots.txt or
182 181 # .htaccess) here, relative to this directory. These files are copied
183 182 # directly to the root of the documentation.
184 183 #html_extra_path = []
185 184
186 185 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
187 186 # using the given strftime format.
188 187 #html_last_updated_fmt = '%b %d, %Y'
189 188
190 189 # If true, SmartyPants will be used to convert quotes and dashes to
191 190 # typographically correct entities.
192 191 #html_use_smartypants = True
193 192
194 193 # Custom sidebar templates, maps document names to template names.
195 194 #html_sidebars = {}
196 195
197 196 # Additional templates that should be rendered to pages, maps page names to
198 197 # template names.
199 198 #html_additional_pages = {}
200 199
201 200 # If false, no module index is generated.
202 201 #html_domain_indices = True
203 202
204 203 # If false, no index is generated.
205 204 #html_use_index = True
206 205
207 206 # If true, the index is split into individual pages for each letter.
208 207 #html_split_index = False
209 208
210 209 # If true, links to the reST sources are added to the pages.
211 210 #html_show_sourcelink = True
212 211
213 212 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
214 213 #html_show_sphinx = True
215 214
216 215 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
217 216 #html_show_copyright = True
218 217
219 218 # If true, an OpenSearch description file will be output, and all pages will
220 219 # contain a <link> tag referring to it. The value of this option must be the
221 220 # base URL from which the finished HTML is served.
222 221 #html_use_opensearch = ''
223 222
224 223 # This is the file name suffix for HTML files (e.g. ".xhtml").
225 224 #html_file_suffix = None
226 225
227 226 # Output file base name for HTML help builder.
228 227 htmlhelp_basename = 'rhodecode-enterprise'
229 228
230 229
231 230 # -- Options for LaTeX output ---------------------------------------------
232 231
233 232 latex_elements = {
234 233 'classoptions': ',oneside',
235 234 'babel': '\\usepackage[english]{babel}',
236 235
237 236 # The paper size ('letterpaper' or 'a4paper').
238 237 #'papersize': 'letterpaper',
239 238
240 239 # The font size ('10pt', '11pt' or '12pt').
241 240 #'pointsize': '10pt',
242 241
243 242 # Additional stuff for the LaTeX preamble.
244 243 #'preamble': '',
245 244 }
246 245
247 246 # Grouping the document tree into LaTeX files. List of tuples
248 247 # (source start file, target name, title,
249 248 # author, documentclass [howto, manual, or own class]).
250 249 latex_documents = [
251 250 ('index', 'RhodeCodeEnterprise.tex', u'RhodeCode Enterprise',
252 251 u'RhodeCode GmbH', 'manual'),
253 252 ]
254 253
255 254 # The name of an image file (relative to this directory) to place at the top of
256 255 # the title page.
257 256 #latex_logo = None
258 257
259 258 # For "manual" documents, if this is true, then toplevel headings are parts,
260 259 # not chapters.
261 260 #latex_use_parts = False
262 261
263 262 # If true, show page references after internal links.
264 263 latex_show_pagerefs = True
265 264
266 265 # If true, show URL addresses after external links.
267 266 latex_show_urls = 'footnote'
268 267
269 268 # Documents to append as an appendix to all manuals.
270 269 #latex_appendices = []
271 270
272 271 # If false, no module index is generated.
273 272 #latex_domain_indices = True
274 273
275 274 # Mode for literal blocks wider than the frame. Can be
276 275 # overflow, shrink or truncate
277 276 pdf_fit_mode = "truncate"
278 277
279 278
280 279 # -- Options for manual page output ---------------------------------------
281 280
282 281 # One entry per manual page. List of tuples
283 282 # (source start file, name, description, authors, manual section).
284 283 man_pages = [
285 284 ('index', 'rhodecodeenterprise', u'RhodeCode Enterprise',
286 285 [u'RhodeCode GmbH'], 1)
287 286 ]
288 287
289 288 # If true, show URL addresses after external links.
290 289 #man_show_urls = False
291 290
292 291
293 292 # -- Options for Texinfo output -------------------------------------------
294 293
295 294 # Grouping the document tree into Texinfo files. List of tuples
296 295 # (source start file, target name, title, author,
297 296 # dir menu entry, description, category)
298 297 texinfo_documents = [
299 298 ('index', 'RhodeCodeEnterprise', u'RhodeCode Enterprise',
300 299 u'RhodeCode Docs Team', 'RhodeCodeEnterprise', 'RhodeCode Docs Project',
301 300 'Miscellaneous'),
302 301 ]
303 302
304 303 # Documents to append as an appendix to all manuals.
305 304 #texinfo_appendices = []
306 305
307 306 # If false, no module index is generated.
308 307 #texinfo_domain_indices = True
309 308
310 309 # How to display URL addresses: 'footnote', 'no', or 'inline'.
311 310 #texinfo_show_urls = 'footnote'
312 311
313 312 # If true, do not generate a @detailmenu in the "Top" node's menu.
314 313 #texinfo_no_detailmenu = False
315 314
316 315 # We want to see todo notes in case of a pre-release build of the documentation
317 316 todo_include_todos = tags.has("dev")
@@ -1,219 +1,226 b''
1 1 .. _dev-setup:
2 2
3 3 ===================
4 4 Development setup
5 5 ===================
6 6
7 7
8 8 RhodeCode Enterprise runs inside a Nix managed environment. This ensures build
9 9 environment dependencies are correctly declared and installed during setup.
10 10 It also enables atomic upgrades, rollbacks, and multiple instances of RhodeCode
11 11 Enterprise running with isolation.
12 12
13 13 To set up RhodeCode Enterprise inside the Nix environment, use the following steps:
14 14
15 15
16 16
17 17 Setup Nix Package Manager
18 18 -------------------------
19 19
20 20 To install the Nix Package Manager, please run::
21 21
22 22 $ curl https://nixos.org/nix/install | sh
23 23
24 24 or go to https://nixos.org/nix/ and follow the installation instructions.
25 25 Once this is correctly set up on your system, you should be able to use the
26 26 following commands:
27 27
28 28 * `nix-env`
29 29
30 30 * `nix-shell`
31 31
32 32
33 33 .. tip::
34 34
35 35 Update your channels frequently by running ``nix-channel --update``.
36 36
37 .. note::
38
39 To uninstall nix run the following:
40
41 remove the . "$HOME/.nix-profile/etc/profile.d/nix.sh" line in your ~/.profile or ~/.bash_profile
42 rm -rf $HOME/{.nix-channels,.nix-defexpr,.nix-profile,.config/nixpkgs}
43 sudo rm -rf /nix
37 44
38 45 Switch nix to the latest STABLE channel
39 46 ---------------------------------------
40 47
41 48 run::
42 49
43 50 nix-channel --add https://nixos.org/channels/nixos-16.03 nixpkgs
44 51
45 52 Followed by::
46 53
47 54 nix-channel --update
48 55
49 56
50 57 Install required binaries
51 58 -------------------------
52 59
53 60 We need some handy tools first.
54 61
55 62 run::
56 63
57 64 nix-env -i nix-prefetch-hg
58 65 nix-env -i nix-prefetch-git
59 66
60 67
61 68 Clone the required repositories
62 69 -------------------------------
63 70
64 71 After Nix is set up, clone the RhodeCode Enterprise Community Edition and
65 72 RhodeCode VCSServer repositories into the same directory.
66 73 RhodeCode currently is using Mercurial Version Control System, please make sure
67 74 you have it installed before continuing.
68 75
69 76 To obtain the required sources, use the following commands::
70 77
71 78 mkdir rhodecode-develop && cd rhodecode-develop
72 79 hg clone https://code.rhodecode.com/rhodecode-enterprise-ce
73 80 hg clone https://code.rhodecode.com/rhodecode-vcsserver
74 81
75 82 .. note::
76 83
77 84 If you cannot clone the repository, please contact us via support@rhodecode.com
78 85
79 86
80 87 Install some required libraries
81 88 -------------------------------
82 89
83 90 There are some required drivers and dev libraries that we need to install to
84 91 test RhodeCode under different types of databases. For example in Ubuntu we
85 92 need to install the following.
86 93
87 94 required libraries::
88 95
89 96 sudo apt-get install libapr1-dev libaprutil1-dev
90 97 sudo apt-get install libsvn-dev
91 98 sudo apt-get install mysql-server libmysqlclient-dev
92 99 sudo apt-get install postgresql postgresql-contrib libpq-dev
93 100 sudo apt-get install libcurl4-openssl-dev
94 101
95 102
96 103 Enter the Development Shell
97 104 ---------------------------
98 105
99 106 The final step is to start the development shells. To do this, run the
100 107 following command from inside the cloned repository::
101 108
102 109 #first, the vcsserver
103 110 cd ~/rhodecode-vcsserver
104 111 nix-shell
105 112
106 113 # then enterprise sources
107 114 cd ~/rhodecode-enterprise-ce
108 115 nix-shell
109 116
110 117 .. note::
111 118
112 119 On the first run, this will take a while to download and optionally compile
113 120 a few things. The following runs will be faster. The development shell works
114 121 fine on both MacOS and Linux platforms.
115 122
116 123
117 124 Create config.nix for development
118 125 ---------------------------------
119 126
120 127 In order to run proper tests and setup linking across projects, a config.nix
121 128 file needs to be setup::
122 129
123 130 # create config
124 131 mkdir -p ~/.nixpkgs
125 132 touch ~/.nixpkgs/config.nix
126 133
127 134 # put the below content into the ~/.nixpkgs/config.nix file
128 135 # adjusts, the path to where you cloned your repositories.
129 136
130 137 {
131 138 rc = {
132 139 sources = {
133 140 rhodecode-vcsserver = "/home/dev/rhodecode-vcsserver";
134 141 rhodecode-enterprise-ce = "/home/dev/rhodecode-enterprise-ce";
135 142 rhodecode-enterprise-ee = "/home/dev/rhodecode-enterprise-ee";
136 143 };
137 144 };
138 145 }
139 146
140 147
141 148
142 149 Creating a Development Configuration
143 150 ------------------------------------
144 151
145 152 To create a development environment for RhodeCode Enterprise,
146 153 use the following steps:
147 154
148 155 1. Create a copy of vcsserver config:
149 156 `cp ~/rhodecode-vcsserver/configs/development.ini ~/rhodecode-vcsserver/configs/dev.ini`
150 157 2. Create a copy of rhodocode config:
151 158 `cp ~/rhodecode-enterprise-ce/configs/development.ini ~/rhodecode-enterprise-ce/configs/dev.ini`
152 159 3. Adjust the configuration settings to your needs if needed.
153 160
154 161 .. note::
155 162
156 163 It is recommended to use the name `dev.ini` since it's included in .hgignore file.
157 164
158 165
159 166 Setup the Development Database
160 167 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
161 168
162 169 To create a development database, use the following example. This is a one
163 170 time operation executed from the nix-shell of rhodecode-enterprise-ce sources ::
164 171
165 172 rc-setup-app dev.ini \
166 173 --user=admin --password=secret \
167 174 --email=admin@example.com \
168 175 --repos=~/my_dev_repos
169 176
170 177
171 178 Compile CSS and JavaScript
172 179 ^^^^^^^^^^^^^^^^^^^^^^^^^^
173 180
174 181 To use the application's frontend and prepare it for production deployment,
175 182 you will need to compile the CSS and JavaScript with Grunt.
176 183 This is easily done from within the nix-shell using the following command::
177 184
178 185 grunt
179 186
180 187 When developing new features you will need to recompile following any
181 188 changes made to the CSS or JavaScript files when developing the code::
182 189
183 190 grunt watch
184 191
185 192 This prepares the development (with comments/whitespace) versions of files.
186 193
187 194 Start the Development Servers
188 195 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
189 196
190 197 From the rhodecode-vcsserver directory, start the development server in another
191 198 nix-shell, using the following command::
192 199
193 200 pserve configs/dev.ini
194 201
195 202 In the adjacent nix-shell which you created for your development server, you may
196 203 now start CE with the following command::
197 204
198 205
199 206 pserve --reload configs/dev.ini
200 207
201 208 .. note::
202 209
203 210 `--reload` flag will automatically reload the server when source file changes.
204 211
205 212
206 213 Run the Environment Tests
207 214 ^^^^^^^^^^^^^^^^^^^^^^^^^
208 215
209 216 Please make sure that the tests are passing to verify that your environment is
210 217 set up correctly. RhodeCode uses py.test to run tests.
211 218 While your instance is running, start a new nix-shell and simply run
212 219 ``make test`` to run the basic test suite.
213 220
214 221
215 222 Need Help?
216 223 ^^^^^^^^^^
217 224
218 225 Join us on Slack via https://rhodecode.com/join or post questions in our
219 226 Community Portal at https://community.rhodecode.com
@@ -1,269 +1,261 b''
1 1 { system ? builtins.currentSystem
2 2 }:
3 3
4 4 let
5 5
6 6 pkgs = import <nixpkgs> { inherit system; };
7 7
8 8 inherit (pkgs) fetchurl fetchgit;
9 9
10 10 buildPythonPackage = pkgs.python27Packages.buildPythonPackage;
11 11 python = pkgs.python27Packages.python;
12 12
13 13 Jinja2 = buildPythonPackage rec {
14 14 name = "Jinja2-2.9.6";
15 15 buildInputs = [];
16 16 doCheck = false;
17 17 propagatedBuildInputs = [MarkupSafe];
18 18 src = fetchurl {
19 19 url = "https://pypi.python.org/packages/90/61/f820ff0076a2599dd39406dcb858ecb239438c02ce706c8e91131ab9c7f1/Jinja2-2.9.6.tar.gz";
20 20 md5 = "6411537324b4dba0956aaa8109f3c77b";
21 21 };
22 22 };
23 23
24 24 MarkupSafe = buildPythonPackage rec {
25 25 name = "MarkupSafe-1.0";
26 26 buildInputs = [];
27 27 doCheck = false;
28 28 propagatedBuildInputs = [];
29 29 src = fetchurl {
30 30 url = "https://pypi.python.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz";
31 31 md5 = "2fcedc9284d50e577b5192e8e3578355";
32 32 };
33 33 };
34 34
35 35 Pygments = buildPythonPackage {
36 36 name = "Pygments-2.2.0";
37 37 buildInputs = [];
38 38 doCheck = false;
39 39 propagatedBuildInputs = [];
40 40 src = fetchurl {
41 41 url = "https://pypi.python.org/packages/71/2a/2e4e77803a8bd6408a2903340ac498cb0a2181811af7c9ec92cb70b0308a/Pygments-2.2.0.tar.gz";
42 42 md5 = "13037baca42f16917cbd5ad2fab50844";
43 43 };
44 44 };
45 45
46 46 Sphinx = buildPythonPackage (rec {
47 47 name = "Sphinx-1.6.5";
48 48 src = fetchurl {
49 49 url = "https://pypi.python.org/packages/8b/7e/b188d9a3b9c938e736e02a74c1363c2888e095d770df2c72b4c312f9fdcb/Sphinx-1.6.5.tar.gz";
50 50 md5 = "cd73118c21ec610432e63e6421ec54f1";
51 51 };
52 52 propagatedBuildInputs = [
53 53 six
54 54 Jinja2
55 55 Pygments
56 56 docutils
57 57 snowballstemmer
58 58 babel
59 59 alabaster
60 60 imagesize
61 61 requests
62 62 setuptools
63 63 sphinxcontrib-websupport
64 64 typing
65 65
66 66 # special cases
67 67 pytz
68 68 sphinx_rtd_theme
69 69
70 70 ];
71 71 });
72 72
73 73 alabaster = buildPythonPackage rec {
74 74 name = "alabaster-0.7.10";
75 75 buildInputs = [];
76 76 doCheck = false;
77 77 propagatedBuildInputs = [];
78 78 src = fetchurl {
79 79 url = "https://pypi.python.org/packages/d0/a5/e3a9ad3ee86aceeff71908ae562580643b955ea1b1d4f08ed6f7e8396bd7/alabaster-0.7.10.tar.gz";
80 80 md5 = "7934dccf38801faa105f6e7b4784f493";
81 81 };
82 82 };
83 83
84 84 babel = buildPythonPackage {
85 85 name = "babel-2.5.1";
86 86 buildInputs = [];
87 87 doCheck = false;
88 88 propagatedBuildInputs = [pytz];
89 89 src = fetchurl {
90 90 url = "https://pypi.python.org/packages/5a/22/63f1dbb8514bb7e0d0c8a85cc9b14506599a075e231985f98afd70430e1f/Babel-2.5.1.tar.gz";
91 91 md5 = "60228b3ce93a203357158b909afe8ae1";
92 92 };
93 93 };
94 94
95 95 certifi = buildPythonPackage {
96 96 name = "certifi-2017.11.5";
97 97 buildInputs = [];
98 98 doCheck = false;
99 99 propagatedBuildInputs = [];
100 100 src = fetchurl {
101 101 url = "https://pypi.python.org/packages/23/3f/8be01c50ed24a4bd6b8da799839066ce0288f66f5e11f0367323467f0cbc/certifi-2017.11.5.tar.gz";
102 102 md5 = "c15ac46ed1fe4b607ff3405928f9a992";
103 103 };
104 104 };
105 105
106 106 chardet = buildPythonPackage {
107 107 name = "chardet-3.0.4";
108 108 buildInputs = [];
109 109 doCheck = false;
110 110 propagatedBuildInputs = [];
111 111 src = fetchurl {
112 112 url = "https://pypi.python.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz";
113 113 md5 = "7dd1ba7f9c77e32351b0a0cfacf4055c";
114 114 };
115 115 };
116 116
117 117 docutils = buildPythonPackage {
118 118 name = "docutils-0.14";
119 119 buildInputs = [];
120 120 doCheck = false;
121 121 propagatedBuildInputs = [];
122 122 src = fetchurl {
123 123 url = "https://pypi.python.org/packages/84/f4/5771e41fdf52aabebbadecc9381d11dea0fa34e4759b4071244fa094804c/docutils-0.14.tar.gz";
124 124 md5 = "c53768d63db3873b7d452833553469de";
125 125 };
126 126 };
127 127
128 128 idna = buildPythonPackage {
129 129 name = "idna-2.6";
130 130 buildInputs = [];
131 131 doCheck = false;
132 132 propagatedBuildInputs = [];
133 133 src = fetchurl {
134 134 url = "https://pypi.python.org/packages/f4/bd/0467d62790828c23c47fc1dfa1b1f052b24efdf5290f071c7a91d0d82fd3/idna-2.6.tar.gz";
135 135 md5 = "c706e2790b016bd0ed4edd2d4ba4d147";
136 136 };
137 137 };
138 138
139 139 imagesize = buildPythonPackage {
140 140 name = "imagesize-0.7.1";
141 141 buildInputs = [];
142 142 doCheck = false;
143 143 propagatedBuildInputs = [];
144 144 src = fetchurl {
145 145 url = "https://pypi.python.org/packages/53/72/6c6f1e787d9cab2cc733cf042f125abec07209a58308831c9f292504e826/imagesize-0.7.1.tar.gz";
146 146 md5 = "976148283286a6ba5f69b0f81aef8052";
147 147 };
148 148 };
149 149
150 150 pytz = buildPythonPackage {
151 151 name = "pytz-2017.3";
152 152 buildInputs = [];
153 153 doCheck = false;
154 154 propagatedBuildInputs = [];
155 155 src = fetchurl {
156 156 url = "https://pypi.python.org/packages/60/88/d3152c234da4b2a1f7a989f89609ea488225eaea015bc16fbde2b3fdfefa/pytz-2017.3.zip";
157 157 md5 = "7006b56c0d68a162d9fe57d4249c3171";
158 158 };
159 159 };
160 160
161 161 requests = buildPythonPackage {
162 162 name = "requests-2.18.4";
163 163 buildInputs = [];
164 164 doCheck = false;
165 165 propagatedBuildInputs = [chardet idna urllib3 certifi];
166 166 src = fetchurl {
167 167 url = "https://pypi.python.org/packages/b0/e1/eab4fc3752e3d240468a8c0b284607899d2fbfb236a56b7377a329aa8d09/requests-2.18.4.tar.gz";
168 168 md5 = "081412b2ef79bdc48229891af13f4d82";
169 169 };
170 170 };
171 171
172 setuptools = buildPythonPackage {
173 name = "setuptools-36.6.0";
174 buildInputs = [];
175 doCheck = false;
176 propagatedBuildInputs = [];
177 src = fetchurl {
178 url = "https://pypi.python.org/packages/45/29/8814bf414e7cd1031e1a3c8a4169218376e284ea2553cc0822a6ea1c2d78/setuptools-36.6.0.zip";
179 md5 = "74663b15117d9a2cc5295d76011e6fd1";
180 };
181 };
182
183 172 six = buildPythonPackage {
184 173 name = "six-1.11.0";
185 174 buildInputs = [];
186 175 doCheck = false;
187 176 propagatedBuildInputs = [];
188 177 src = fetchurl {
189 178 url = "https://pypi.python.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
190 179 md5 = "d12789f9baf7e9fb2524c0c64f1773f8";
191 180 };
192 181 };
193 182
194 183 snowballstemmer = buildPythonPackage {
195 184 name = "snowballstemmer-1.2.1";
196 185 buildInputs = [];
197 186 doCheck = false;
198 187 propagatedBuildInputs = [];
199 188 src = fetchurl {
200 189 url = "https://pypi.python.org/packages/20/6b/d2a7cb176d4d664d94a6debf52cd8dbae1f7203c8e42426daa077051d59c/snowballstemmer-1.2.1.tar.gz";
201 190 md5 = "643b019667a708a922172e33a99bf2fa";
202 191 };
203 192 };
204 193
205 194 sphinx-rtd-theme = buildPythonPackage {
206 195 name = "sphinx-rtd-theme-0.2.5b1";
207 196 buildInputs = [];
208 197 doCheck = false;
209 198 propagatedBuildInputs = [];
210 199 src = fetchurl {
211 200 url = "https://pypi.python.org/packages/59/e4/9e3a74a3271e6734911d3f549e8439db53b8ac29adf10c8f698e6c86246b/sphinx_rtd_theme-0.2.5b1.tar.gz";
212 201 md5 = "0923473a43bd2527f32151f195f2a521";
213 202 };
214 203 };
215 204
216 205 sphinxcontrib-websupport = buildPythonPackage {
217 206 name = "sphinxcontrib-websupport-1.0.1";
218 207 buildInputs = [];
219 208 doCheck = false;
220 209 propagatedBuildInputs = [];
221 210 src = fetchurl {
222 211 url = "https://pypi.python.org/packages/c5/6b/f0630436b931ad4f8331a9399ca18a7d447f0fcc0c7178fb56b1aee68d01/sphinxcontrib-websupport-1.0.1.tar.gz";
223 212 md5 = "84df26463b1ba65b07f926dbe2055665";
224 213 };
225 214 };
226 215
227 216 typing = buildPythonPackage {
228 217 name = "typing-3.6.2";
229 218 buildInputs = [];
230 219 doCheck = false;
231 220 propagatedBuildInputs = [];
232 221 src = fetchurl {
233 222 url = "https://pypi.python.org/packages/ca/38/16ba8d542e609997fdcd0214628421c971f8c395084085354b11ff4ac9c3/typing-3.6.2.tar.gz";
234 223 md5 = "143af0bf3afd1887622771f2f1ffe8e1";
235 224 };
236 225 };
237 226
238 227 urllib3 = buildPythonPackage {
239 228 name = "urllib3-1.22";
240 229 buildInputs = [];
241 230 doCheck = false;
242 231 propagatedBuildInputs = [];
243 232 src = fetchurl {
244 233 url = "https://pypi.python.org/packages/ee/11/7c59620aceedcc1ef65e156cc5ce5a24ef87be4107c2b74458464e437a5d/urllib3-1.22.tar.gz";
245 234 md5 = "0da7bed3fe94bf7dc59ae37885cc72f7";
246 235 };
247 236 };
248 237
249 238
250 239 sphinx_rtd_theme = buildPythonPackage rec {
251 240 name = "sphinx-rtd-theme-0.2.5b1";
252 241 buildInputs = [];
253 242 doCheck = false;
254 243 propagatedBuildInputs = [];
255 244 src = fetchurl {
256 245 url = "https://pypi.python.org/packages/59/e4/9e3a74a3271e6734911d3f549e8439db53b8ac29adf10c8f698e6c86246b/sphinx_rtd_theme-0.2.5b1.tar.gz";
257 246 md5 = "0923473a43bd2527f32151f195f2a521";
258 247 };
259 248
260 249
261 250 };
251 # Avoid that setuptools is replaced, this leads to trouble
252 # with buildPythonPackage.
253 setuptools = pkgs.python27Packages.setuptools;
262 254
263 255 in python.buildEnv.override {
264 256 inherit python;
265 257 extraLibs = [
266 258 Sphinx
267 259 sphinx_rtd_theme
268 260 ];
269 261 }
@@ -1,114 +1,115 b''
1 1 .. _rhodecode-release-notes-ref:
2 2
3 3 Release Notes
4 4 =============
5 5
6 6 |RCE| 4.x Versions
7 7 ------------------
8 8
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.12.0.rst
12 13 release-notes-4.11.6.rst
13 14 release-notes-4.11.5.rst
14 15 release-notes-4.11.4.rst
15 16 release-notes-4.11.3.rst
16 17 release-notes-4.11.2.rst
17 18 release-notes-4.11.1.rst
18 19 release-notes-4.11.0.rst
19 20 release-notes-4.10.6.rst
20 21 release-notes-4.10.5.rst
21 22 release-notes-4.10.4.rst
22 23 release-notes-4.10.3.rst
23 24 release-notes-4.10.2.rst
24 25 release-notes-4.10.1.rst
25 26 release-notes-4.10.0.rst
26 27 release-notes-4.9.1.rst
27 28 release-notes-4.9.0.rst
28 29 release-notes-4.8.0.rst
29 30 release-notes-4.7.2.rst
30 31 release-notes-4.7.1.rst
31 32 release-notes-4.7.0.rst
32 33 release-notes-4.6.1.rst
33 34 release-notes-4.6.0.rst
34 35 release-notes-4.5.2.rst
35 36 release-notes-4.5.1.rst
36 37 release-notes-4.5.0.rst
37 38 release-notes-4.4.2.rst
38 39 release-notes-4.4.1.rst
39 40 release-notes-4.4.0.rst
40 41 release-notes-4.3.1.rst
41 42 release-notes-4.3.0.rst
42 43 release-notes-4.2.1.rst
43 44 release-notes-4.2.0.rst
44 45 release-notes-4.1.2.rst
45 46 release-notes-4.1.1.rst
46 47 release-notes-4.1.0.rst
47 48 release-notes-4.0.1.rst
48 49 release-notes-4.0.0.rst
49 50
50 51 |RCE| 3.x Versions
51 52 ------------------
52 53
53 54 .. toctree::
54 55 :maxdepth: 1
55 56
56 57 release-notes-3.8.4.rst
57 58 release-notes-3.8.3.rst
58 59 release-notes-3.8.2.rst
59 60 release-notes-3.8.1.rst
60 61 release-notes-3.8.0.rst
61 62 release-notes-3.7.1.rst
62 63 release-notes-3.7.0.rst
63 64 release-notes-3.6.1.rst
64 65 release-notes-3.6.0.rst
65 66 release-notes-3.5.2.rst
66 67 release-notes-3.5.1.rst
67 68 release-notes-3.5.0.rst
68 69 release-notes-3.4.1.rst
69 70 release-notes-3.4.0.rst
70 71 release-notes-3.3.4.rst
71 72 release-notes-3.3.3.rst
72 73 release-notes-3.3.2.rst
73 74 release-notes-3.3.1.rst
74 75 release-notes-3.3.0.rst
75 76 release-notes-3.2.3.rst
76 77 release-notes-3.2.2.rst
77 78 release-notes-3.2.1.rst
78 79 release-notes-3.2.0.rst
79 80 release-notes-3.1.1.rst
80 81 release-notes-3.1.0.rst
81 82 release-notes-3.0.2.rst
82 83 release-notes-3.0.1.rst
83 84 release-notes-3.0.0.rst
84 85
85 86 |RCE| 2.x Versions
86 87 ------------------
87 88
88 89 .. toctree::
89 90 :maxdepth: 1
90 91
91 92 release-notes-2.2.8.rst
92 93 release-notes-2.2.7.rst
93 94 release-notes-2.2.6.rst
94 95 release-notes-2.2.5.rst
95 96 release-notes-2.2.4.rst
96 97 release-notes-2.2.3.rst
97 98 release-notes-2.2.2.rst
98 99 release-notes-2.2.1.rst
99 100 release-notes-2.2.0.rst
100 101 release-notes-2.1.0.rst
101 102 release-notes-2.0.2.rst
102 103 release-notes-2.0.1.rst
103 104 release-notes-2.0.0.rst
104 105
105 106 |RCE| 1.x Versions
106 107 ------------------
107 108
108 109 .. toctree::
109 110 :maxdepth: 1
110 111
111 112 release-notes-1.7.2.rst
112 113 release-notes-1.7.1.rst
113 114 release-notes-1.7.0.rst
114 115 release-notes-1.6.0.rst
@@ -1,220 +1,220 b''
1 1 .. _hosted-solution:
2 2
3 3 Deploy |RCE| From a Hosted Server
4 4 =================================
5 5
6 6 If you wish to deploy your own |RCE| instance from something like a
7 7 `Digital Ocean`_ droplet, or a `hetzner`_ server use the following
8 8 instructions to get it setup.
9 9
10 10 I'm using an Ubuntu 14.04 image for the purposes of this
11 11 tutorial, but all other Unix environments will be pretty similar. You can
12 12 check out the full lists of supported platforms and versions in the
13 13 :ref:`system-overview-ref` section.
14 14
15 15
16 16 Create a Digital Ocean Droplet
17 17 ------------------------------
18 18
19 19 1. Sign into Digital Ocean.
20 20 2. Create a Droplet choosing Ubuntu 14.04 as your |os|.
21 21 3. (Optional) Add SSH keys if you have them set up.
22 22
23 23 Configure Your Server
24 24 ---------------------
25 25
26 26 Once you have your server created, you need to sign into it and set it up to
27 27 host |RCE|.
28 28
29 29 1. Open a terminal and sign into your server. Digital Ocean will mail you the
30 30 IP address. You'll need to change your password on the first login if you
31 31 don not have SSH keys set up.
32 32
33 33 .. code-block:: bash
34 34
35 35 $ ssh root@203.0.113.113
36 36
37 37 2. It is not advised to install |RCE| as the ``root`` user. So create a user
38 38 with sudo permissions and then carry out the rest of the steps from that user
39 39 account.
40 40
41 41 .. code-block:: bash
42 42
43 43 # Create a user with sudo permissions
44 44 root@rhodecode:~# sudo useradd -m -s /bin/bash -d /home/brian -U brian
45 45 root@rhodecode:~# sudo usermod -a -G sudo brian
46 46
47 47 # Set the password for that user
48 48 root@rhodecode:~# passwd brian
49 49 Enter new UNIX password:
50 50 Retype new UNIX password:
51 51 passwd: password updated successfully
52 52
53 53 # Switch to that user for the rest of the steps
54 54 root@rhodecode:~# su brian
55 55
56 56 # You should see your home dir change to what was set during installation
57 57 brian@rhodecode:~$ cd ~
58 58 brian@rhodecode:~$ pwd
59 59 /home/brian
60 60
61 61 Once you have this set up, you are ready to install |RCC|.
62 62
63 63 Install |RCC|
64 64 -------------
65 65
66 66 |RCC| will install and manage the package dependencies for your |RCE| instance.
67 67
68 68 1. Download the |RCC| installer from https://rhodecode.com/download/
69 69 2. Once downloaded to your computer, transfer the package to your server
70 70
71 71 .. note::
72 72
73 73 These steps happen on your computer, not on the server.
74 74
75 75 .. code-block:: bash
76 76
77 77 # Change to where the file is downloaded
78 78 $ cd Downloads/
79 79
80 80 # SFTP to your server
81 81 $ sftp brian@203.0.113.113
82 82
83 83 # Use mput to transfer the file
84 84 sftp> mput RhodeCode-installer-linux-391_b1a804c4d69b_d6c087d520e3
85 85 Uploading RhodeCode-installer-linux-391_b1a804c4d69b_d6c087d520e3 to /home/brian/RhodeCode-installer-linux-391_b1a804c4d69b_d6c087d520e3
86 86 RhodeCode-installer-linux-391_b1a804c4d69b_d6c087d 100% 289MB 4.1MB/s 01:11
87 87 sftp> exit
88 88
89 89 The |RCC| installer is now on your server, and you can read the full
90 90 instructions here
91 :ref:`Install RhodeCode Control <control:rcc-install-ref>`,
91 :ref:`Install RhodeCode Control <control:rcc-linux-ref>` ,
92 92 but below is the example shortcut.
93 93
94 94 .. code-block:: bash
95 95
96 96 # Check that the script is uploaded to your home directory
97 97 $ ls -1
98 98 RhodeCode-installer-linux-391_b1a804c4d69b_d6c087d520e3
99 99
100 100 # Change the script permissions
101 101 $ chmod 755 RhodeCode-installer-linux*
102 102
103 103 # Run the installer and accept the prompts
104 104 $ ./RhodeCode-installer-linux-*
105 105
106 106 .. important::
107 107
108 108 Once finished, exit the terminal and sign in again. This is to refresh you
109 109 session to pick up the new commands.
110 110
111 111 Install |RCE|
112 112 -------------
113 113
114 114 Now that |RCC| is installed, you can install |RCE|. For the full
115 115 instructions, see
116 116 :ref:`Install RhodeCode Enterprise <control:rce-cli-install-ref>`,
117 117 but the below is an example shortcut.
118 118
119 119 .. code-block:: bash
120 120
121 121 # Install a VCS Server and follow the prompts
122 122 $ rccontrol install VCSServer --start-at-boot
123 123
124 124 Extracting VCSServer ...
125 125 Configuring RhodeCode VCS Server ...
126 126 Supervisord state is: RUNNING
127 127 Added process group vcsserver-1
128 128
129 129 # Install a RhodeCode Enterprise instance and follow the prompts
130 130 $ rccontrol install Enterprise --start-at-boot
131 131
132 132 Configuration of RhodeCode Enterprise passed.
133 133 Supervisord state is: RUNNING
134 134 Added process group enterprise-1
135 135
136 136 |RCE| is now installed on your server, and is running on the port displayed
137 137 by the ``rccontrol status`` command.
138 138
139 139 .. code-block:: bash
140 140
141 141 brian@rhodecode:~$ rccontrol status
142 142
143 143 - NAME: enterprise-1
144 144 - STATUS: RUNNING
145 145 - TYPE: Enterprise
146 146 - VERSION: 3.1.1
147 147 - URL: http://127.0.0.1:10002
148 148
149 149 - NAME: vcsserver-1
150 150 - STATUS: RUNNING
151 151 - TYPE: VCSServer
152 152 - VERSION: 1.1.1
153 153 - URL: http://127.0.0.1:10001
154 154
155 155 Serve |RCE| using Nginx
156 156 -----------------------
157 157
158 158 Now that |RCE| is running, you need to use Nginx or Apache to serve it to
159 159 users. For detailed instructions about setting up your webserver, see the
160 160 :ref:`rhodecode-admin-ref` section. But the below shortcut should help serve
161 161 it.
162 162
163 163 1. Install Nginx on your server.
164 164
165 165 .. code-block:: bash
166 166
167 167 # Install nginx
168 168 $ sudo apt-get install nginx
169 169
170 170 2. Create a virtual hosts file for RhodeCode Enterprise. Create
171 171 the file in this location :file:`/etc/nginx/sites-available`. In this demo
172 172 I have called it ``vcs.conf``
173 173
174 174 .. code-block:: bash
175 175
176 176 # Create the file
177 177 $ sudo vi /etc/nginx/sites-available/vcs.conf
178 178
179 179 Use the following example to create yours.
180 180
181 181 .. code-block:: nginx
182 182
183 183 server {
184 184 listen 80;
185 185 # Change to your IP, or a domain name if you've set that up
186 186 server_name 203.0.113.113 ;
187 187
188 188 location / {
189 189 # Set this line to match the RhodeCode Enterprise Instance URL
190 190 proxy_pass http://127.0.0.1:10002/;
191 191 proxy_set_header Host $Host;
192 192 proxy_buffering off;
193 193 # Setting this to a high number allows large repo pushes
194 194 client_max_body_size 4G;
195 195 }
196 196 }
197 197
198 198 3. Symlink the virtual hosts file to the ``sites-enabled`` folder,
199 199 and then restart Nginx.
200 200
201 201 .. code-block:: bash
202 202
203 203 # Symlink the virtual hosts file
204 204 $ ln -s /etc/nginx/sites-available/vcs.conf /etc/nginx/sites-enabled/vcs.conf
205 205
206 206 # You can also delete the Nginx default symlink
207 207 $ rm /etc/nginx/sites-enabled/default
208 208
209 209 # Restart Nginx
210 210 $ sudo /etc/init.d/nginx restart
211 211 * Restarting nginx nginx [ OK ]
212 212
213 213 Once restarted, you should see a clean |RCE| instance running on the IP
214 214 address, or the domain you have set up.
215 215
216 216 .. image:: ../images/clean-rce.png
217 217 :alt: A fresh RhodeCode Enterprise Instance
218 218
219 219 .. _Digital Ocean: https://www.digitalocean.com/
220 220 .. _hetzner: https://www.hetzner.de/en/
@@ -1,251 +1,257 b''
1 1 # Overrides for the generated python-packages.nix
2 2 #
3 3 # This function is intended to be used as an extension to the generated file
4 4 # python-packages.nix. The main objective is to add needed dependencies of C
5 5 # libraries and tweak the build instructions where needed.
6 6
7 7 { pkgs, basePythonPackages }:
8 8
9 9 let
10 10 sed = "sed -i";
11 11 localLicenses = {
12 12 repoze = {
13 13 fullName = "Repoze License";
14 14 url = http://www.repoze.org/LICENSE.txt;
15 15 };
16 16 };
17 17
18 18 in
19 19
20 20 self: super: {
21 21
22 22 appenlight-client = super.appenlight-client.override (attrs: {
23 23 meta = {
24 24 license = [ pkgs.lib.licenses.bsdOriginal ];
25 25 };
26 26 });
27 27
28 Beaker = super.Beaker.override (attrs: {
29 patches = [
30 ./patch-beaker-lock-func-debug.diff
31 ];
32 });
33
28 34 future = super.future.override (attrs: {
29 35 meta = {
30 36 license = [ pkgs.lib.licenses.mit ];
31 37 };
32 38 });
33 39
34 40 testpath = super.testpath.override (attrs: {
35 41 meta = {
36 42 license = [ pkgs.lib.licenses.mit ];
37 43 };
38 44 });
39 45
40 46 gnureadline = super.gnureadline.override (attrs: {
41 47 buildInputs = attrs.buildInputs ++ [
42 48 pkgs.ncurses
43 49 ];
44 50 patchPhase = ''
45 51 substituteInPlace setup.py --replace "/bin/bash" "${pkgs.bash}/bin/bash"
46 52 '';
47 53 });
48 54
49 55 gunicorn = super.gunicorn.override (attrs: {
50 56 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
51 57 # johbo: futures is needed as long as we are on Python 2, otherwise
52 58 # gunicorn explodes if used with multiple threads per worker.
53 59 self.futures
54 60 ];
55 61 });
56 62
57 63 nbconvert = super.nbconvert.override (attrs: {
58 64 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
59 65 # marcink: plug in jupyter-client for notebook rendering
60 66 self.jupyter-client
61 67 ];
62 68 });
63 69
64 70 ipython = super.ipython.override (attrs: {
65 71 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
66 72 self.gnureadline
67 73 ];
68 74 });
69 75
70 76 lxml = super.lxml.override (attrs: {
71 77 # johbo: On 16.09 we need this to compile on darwin, otherwise compilation
72 78 # fails on Darwin.
73 79 hardeningDisable = if pkgs.stdenv.isDarwin then [ "format" ] else null;
74 80 buildInputs = with self; [
75 81 pkgs.libxml2
76 82 pkgs.libxslt
77 83 ];
78 84 });
79 85
80 86 MySQL-python = super.MySQL-python.override (attrs: {
81 87 buildInputs = attrs.buildInputs ++ [
82 88 pkgs.openssl
83 89 ];
84 90 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
85 pkgs.mysql.lib
91 pkgs.libmysql
86 92 pkgs.zlib
87 93 ];
88 94 });
89 95
90 96 psutil = super.psutil.override (attrs: {
91 97 buildInputs = attrs.buildInputs ++
92 98 pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.darwin.IOKit;
93 99 });
94 100
95 101 psycopg2 = super.psycopg2.override (attrs: {
96 102 buildInputs = attrs.buildInputs ++
97 103 pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.openssl;
98 104 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
99 105 pkgs.postgresql
100 106 ];
101 107 meta = {
102 108 license = pkgs.lib.licenses.lgpl3Plus;
103 109 };
104 110 });
105 111
106 112 pycurl = super.pycurl.override (attrs: {
107 113 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
108 114 pkgs.curl
109 115 pkgs.openssl
110 116 ];
111 117 preConfigure = ''
112 118 substituteInPlace setup.py --replace '--static-libs' '--libs'
113 119 export PYCURL_SSL_LIBRARY=openssl
114 120 '';
115 121 meta = {
116 122 # TODO: It is LGPL and MIT
117 123 license = pkgs.lib.licenses.mit;
118 124 };
119 125 });
120 126
121 127 pyramid = super.pyramid.override (attrs: {
122 128 postFixup = ''
123 129 wrapPythonPrograms
124 130 # TODO: johbo: "wrapPython" adds this magic line which
125 131 # confuses pserve.
126 132 ${sed} '/import sys; sys.argv/d' $out/bin/.pserve-wrapped
127 133 '';
128 134 meta = {
129 135 license = localLicenses.repoze;
130 136 };
131 137 });
132 138
133 139 pyramid-debugtoolbar = super.pyramid-debugtoolbar.override (attrs: {
134 140 meta = {
135 141 license = [ pkgs.lib.licenses.bsdOriginal localLicenses.repoze ];
136 142 };
137 143 });
138 144
139 145 pysqlite = super.pysqlite.override (attrs: {
140 146 propagatedBuildInputs = [
141 147 pkgs.sqlite
142 148 ];
143 149 meta = {
144 150 license = [ pkgs.lib.licenses.zlib pkgs.lib.licenses.libpng ];
145 151 };
146 152 });
147 153
148 154 pytest-runner = super.pytest-runner.override (attrs: {
149 155 propagatedBuildInputs = [
150 156 self.setuptools-scm
151 157 ];
152 158 });
153 159
154 160 python-ldap = super.python-ldap.override (attrs: {
155 161 propagatedBuildInputs = attrs.propagatedBuildInputs ++ [
156 162 pkgs.cyrus_sasl
157 163 pkgs.openldap
158 164 pkgs.openssl
159 165 ];
160 166 # TODO: johbo: Remove the "or" once we drop 16.03 support.
161 167 NIX_CFLAGS_COMPILE = "-I${pkgs.cyrus_sasl.dev or pkgs.cyrus_sasl}/include/sasl";
162 168 });
163 169
164 170 python-pam = super.python-pam.override (attrs:
165 171 let
166 172 includeLibPam = pkgs.stdenv.isLinux;
167 173 in {
168 174 # TODO: johbo: Move the option up into the default.nix, we should
169 175 # include python-pam only on supported platforms.
170 176 propagatedBuildInputs = attrs.propagatedBuildInputs ++
171 177 pkgs.lib.optional includeLibPam [
172 178 pkgs.pam
173 179 ];
174 180 # TODO: johbo: Check if this can be avoided, or transform into
175 181 # a real patch
176 182 patchPhase = pkgs.lib.optionals includeLibPam ''
177 183 substituteInPlace pam.py \
178 184 --replace 'find_library("pam")' '"${pkgs.pam}/lib/libpam.so.0"'
179 185 '';
180 186 });
181 187
182 188 URLObject = super.URLObject.override (attrs: {
183 189 meta = {
184 190 license = {
185 191 spdxId = "Unlicense";
186 192 fullName = "The Unlicense";
187 193 url = http://unlicense.org/;
188 194 };
189 195 };
190 196 });
191 197
192 198 docutils = super.docutils.override (attrs: {
193 199 meta = {
194 200 license = pkgs.lib.licenses.bsd2;
195 201 };
196 202 });
197 203
198 204 colander = super.colander.override (attrs: {
199 205 meta = {
200 206 license = localLicenses.repoze;
201 207 };
202 208 });
203 209
204 210 pyramid-beaker = super.pyramid-beaker.override (attrs: {
205 211 meta = {
206 212 license = localLicenses.repoze;
207 213 };
208 214 });
209 215
210 216 pyramid-mako = super.pyramid-mako.override (attrs: {
211 217 meta = {
212 218 license = localLicenses.repoze;
213 219 };
214 220 });
215 221
216 222 repoze.lru = super.repoze.lru.override (attrs: {
217 223 meta = {
218 224 license = localLicenses.repoze;
219 225 };
220 226 });
221 227
222 228 recaptcha-client = super.recaptcha-client.override (attrs: {
223 229 meta = {
224 230 # TODO: It is MIT/X11
225 231 license = pkgs.lib.licenses.mit;
226 232 };
227 233 });
228 234
229 235 python-editor = super.python-editor.override (attrs: {
230 236 meta = {
231 237 license = pkgs.lib.licenses.asl20;
232 238 };
233 239 });
234 240
235 241 translationstring = super.translationstring.override (attrs: {
236 242 meta = {
237 243 license = localLicenses.repoze;
238 244 };
239 245 });
240 246
241 247 venusian = super.venusian.override (attrs: {
242 248 meta = {
243 249 license = localLicenses.repoze;
244 250 };
245 251 });
246 252
247 253 # Avoid that setuptools is replaced, this leads to trouble
248 254 # with buildPythonPackage.
249 255 setuptools = basePythonPackages.setuptools;
250 256
251 257 }
@@ -1,2086 +1,2099 b''
1 1 # Generated by pip2nix 0.4.0
2 2 # See https://github.com/johbo/pip2nix
3 3
4 4 {
5 5 Babel = super.buildPythonPackage {
6 6 name = "Babel-1.3";
7 7 buildInputs = with self; [];
8 8 doCheck = false;
9 9 propagatedBuildInputs = with self; [pytz];
10 10 src = fetchurl {
11 11 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
12 12 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
13 13 };
14 14 meta = {
15 15 license = [ pkgs.lib.licenses.bsdOriginal ];
16 16 };
17 17 };
18 18 Beaker = super.buildPythonPackage {
19 name = "Beaker-1.9.0";
19 name = "Beaker-1.9.1";
20 20 buildInputs = with self; [];
21 21 doCheck = false;
22 22 propagatedBuildInputs = with self; [funcsigs];
23 23 src = fetchurl {
24 url = "https://pypi.python.org/packages/93/b2/12de6937b06e9615dbb3cb3a1c9af17f133f435bdef59f4ad42032b6eb49/Beaker-1.9.0.tar.gz";
25 md5 = "38b3fcdfa24faf97c6cf66991eb54e9c";
24 url = "https://pypi.python.org/packages/ca/14/a626188d0d0c7b55dd7cf1902046c2743bd392a7078bb53073e13280eb1e/Beaker-1.9.1.tar.gz";
25 md5 = "46fda0a164e2b0d24ccbda51a2310301";
26 26 };
27 27 meta = {
28 28 license = [ pkgs.lib.licenses.bsdOriginal ];
29 29 };
30 30 };
31 31 CProfileV = super.buildPythonPackage {
32 32 name = "CProfileV-1.0.7";
33 33 buildInputs = with self; [];
34 34 doCheck = false;
35 35 propagatedBuildInputs = with self; [bottle];
36 36 src = fetchurl {
37 37 url = "https://pypi.python.org/packages/df/50/d8c1ada7d537c64b0f76453fa31dedb6af6e27b82fcf0331e5f71a4cf98b/CProfileV-1.0.7.tar.gz";
38 38 md5 = "db4c7640438aa3d8887e194c81c7a019";
39 39 };
40 40 meta = {
41 41 license = [ pkgs.lib.licenses.mit ];
42 42 };
43 43 };
44 44 Chameleon = super.buildPythonPackage {
45 45 name = "Chameleon-2.24";
46 46 buildInputs = with self; [];
47 47 doCheck = false;
48 48 propagatedBuildInputs = with self; [];
49 49 src = fetchurl {
50 50 url = "https://pypi.python.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
51 51 md5 = "1b01f1f6533a8a11d0d2f2366dec5342";
52 52 };
53 53 meta = {
54 54 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
55 55 };
56 56 };
57 57 FormEncode = super.buildPythonPackage {
58 58 name = "FormEncode-1.2.4";
59 59 buildInputs = with self; [];
60 60 doCheck = false;
61 61 propagatedBuildInputs = with self; [];
62 62 src = fetchurl {
63 63 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
64 64 md5 = "6bc17fb9aed8aea198975e888e2077f4";
65 65 };
66 66 meta = {
67 67 license = [ pkgs.lib.licenses.psfl ];
68 68 };
69 69 };
70 70 Jinja2 = super.buildPythonPackage {
71 71 name = "Jinja2-2.9.6";
72 72 buildInputs = with self; [];
73 73 doCheck = false;
74 74 propagatedBuildInputs = with self; [MarkupSafe];
75 75 src = fetchurl {
76 76 url = "https://pypi.python.org/packages/90/61/f820ff0076a2599dd39406dcb858ecb239438c02ce706c8e91131ab9c7f1/Jinja2-2.9.6.tar.gz";
77 77 md5 = "6411537324b4dba0956aaa8109f3c77b";
78 78 };
79 79 meta = {
80 80 license = [ pkgs.lib.licenses.bsdOriginal ];
81 81 };
82 82 };
83 83 Mako = super.buildPythonPackage {
84 84 name = "Mako-1.0.7";
85 85 buildInputs = with self; [];
86 86 doCheck = false;
87 87 propagatedBuildInputs = with self; [MarkupSafe];
88 88 src = fetchurl {
89 89 url = "https://pypi.python.org/packages/eb/f3/67579bb486517c0d49547f9697e36582cd19dafb5df9e687ed8e22de57fa/Mako-1.0.7.tar.gz";
90 90 md5 = "5836cc997b1b773ef389bf6629c30e65";
91 91 };
92 92 meta = {
93 93 license = [ pkgs.lib.licenses.mit ];
94 94 };
95 95 };
96 96 Markdown = super.buildPythonPackage {
97 name = "Markdown-2.6.9";
97 name = "Markdown-2.6.11";
98 98 buildInputs = with self; [];
99 99 doCheck = false;
100 100 propagatedBuildInputs = with self; [];
101 101 src = fetchurl {
102 url = "https://pypi.python.org/packages/29/82/dfe242bcfd9eec0e7bf93a80a8f8d8515a95b980c44f5c0b45606397a423/Markdown-2.6.9.tar.gz";
103 md5 = "56547d362a9abcf30955b8950b08b5e3";
102 url = "https://pypi.python.org/packages/b3/73/fc5c850f44af5889192dff783b7b0d8f3fe8d30b65c8e3f78f8f0265fecf/Markdown-2.6.11.tar.gz";
103 md5 = "a67c1b2914f7d74eeede2ebe0fdae470";
104 104 };
105 105 meta = {
106 106 license = [ pkgs.lib.licenses.bsdOriginal ];
107 107 };
108 108 };
109 109 MarkupSafe = super.buildPythonPackage {
110 110 name = "MarkupSafe-1.0";
111 111 buildInputs = with self; [];
112 112 doCheck = false;
113 113 propagatedBuildInputs = with self; [];
114 114 src = fetchurl {
115 115 url = "https://pypi.python.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz";
116 116 md5 = "2fcedc9284d50e577b5192e8e3578355";
117 117 };
118 118 meta = {
119 119 license = [ pkgs.lib.licenses.bsdOriginal ];
120 120 };
121 121 };
122 122 MySQL-python = super.buildPythonPackage {
123 123 name = "MySQL-python-1.2.5";
124 124 buildInputs = with self; [];
125 125 doCheck = false;
126 126 propagatedBuildInputs = with self; [];
127 127 src = fetchurl {
128 128 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
129 129 md5 = "654f75b302db6ed8dc5a898c625e030c";
130 130 };
131 131 meta = {
132 132 license = [ pkgs.lib.licenses.gpl1 ];
133 133 };
134 134 };
135 135 Paste = super.buildPythonPackage {
136 136 name = "Paste-2.0.3";
137 137 buildInputs = with self; [];
138 138 doCheck = false;
139 139 propagatedBuildInputs = with self; [six];
140 140 src = fetchurl {
141 141 url = "https://pypi.python.org/packages/30/c3/5c2f7c7a02e4f58d4454353fa1c32c94f79fa4e36d07a67c0ac295ea369e/Paste-2.0.3.tar.gz";
142 142 md5 = "1231e14eae62fa7ed76e9130b04bc61e";
143 143 };
144 144 meta = {
145 145 license = [ pkgs.lib.licenses.mit ];
146 146 };
147 147 };
148 148 PasteDeploy = super.buildPythonPackage {
149 149 name = "PasteDeploy-1.5.2";
150 150 buildInputs = with self; [];
151 151 doCheck = false;
152 152 propagatedBuildInputs = with self; [];
153 153 src = fetchurl {
154 154 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
155 155 md5 = "352b7205c78c8de4987578d19431af3b";
156 156 };
157 157 meta = {
158 158 license = [ pkgs.lib.licenses.mit ];
159 159 };
160 160 };
161 161 PasteScript = super.buildPythonPackage {
162 162 name = "PasteScript-2.0.2";
163 163 buildInputs = with self; [];
164 164 doCheck = false;
165 165 propagatedBuildInputs = with self; [Paste PasteDeploy six];
166 166 src = fetchurl {
167 167 url = "https://pypi.python.org/packages/e5/f0/78e766c3dcc61a4f3a6f71dd8c95168ae9c7a31722b5663d19c1fdf62cb6/PasteScript-2.0.2.tar.gz";
168 168 md5 = "ccb3045445097192ca71a13b746c77b2";
169 169 };
170 170 meta = {
171 171 license = [ pkgs.lib.licenses.mit ];
172 172 };
173 173 };
174 174 Pygments = super.buildPythonPackage {
175 175 name = "Pygments-2.2.0";
176 176 buildInputs = with self; [];
177 177 doCheck = false;
178 178 propagatedBuildInputs = with self; [];
179 179 src = fetchurl {
180 180 url = "https://pypi.python.org/packages/71/2a/2e4e77803a8bd6408a2903340ac498cb0a2181811af7c9ec92cb70b0308a/Pygments-2.2.0.tar.gz";
181 181 md5 = "13037baca42f16917cbd5ad2fab50844";
182 182 };
183 183 meta = {
184 184 license = [ pkgs.lib.licenses.bsdOriginal ];
185 185 };
186 186 };
187 187 Routes = super.buildPythonPackage {
188 188 name = "Routes-2.4.1";
189 189 buildInputs = with self; [];
190 190 doCheck = false;
191 191 propagatedBuildInputs = with self; [six repoze.lru];
192 192 src = fetchurl {
193 193 url = "https://pypi.python.org/packages/33/38/ea827837e68d9c7dde4cff7ec122a93c319f0effc08ce92a17095576603f/Routes-2.4.1.tar.gz";
194 194 md5 = "c058dff6832941dec47e0d0052548ad8";
195 195 };
196 196 meta = {
197 197 license = [ pkgs.lib.licenses.mit ];
198 198 };
199 199 };
200 200 SQLAlchemy = super.buildPythonPackage {
201 201 name = "SQLAlchemy-1.1.15";
202 202 buildInputs = with self; [];
203 203 doCheck = false;
204 204 propagatedBuildInputs = with self; [];
205 205 src = fetchurl {
206 206 url = "https://pypi.python.org/packages/c2/f6/11fcc1ce19a7cb81b1c9377f4e27ce3813265611922e355905e57c44d164/SQLAlchemy-1.1.15.tar.gz";
207 207 md5 = "077f9bd3339957f53068b5572a152674";
208 208 };
209 209 meta = {
210 210 license = [ pkgs.lib.licenses.mit ];
211 211 };
212 212 };
213 213 Tempita = super.buildPythonPackage {
214 214 name = "Tempita-0.5.2";
215 215 buildInputs = with self; [];
216 216 doCheck = false;
217 217 propagatedBuildInputs = with self; [];
218 218 src = fetchurl {
219 219 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
220 220 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
221 221 };
222 222 meta = {
223 223 license = [ pkgs.lib.licenses.mit ];
224 224 };
225 225 };
226 226 URLObject = super.buildPythonPackage {
227 227 name = "URLObject-2.4.0";
228 228 buildInputs = with self; [];
229 229 doCheck = false;
230 230 propagatedBuildInputs = with self; [];
231 231 src = fetchurl {
232 232 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
233 233 md5 = "2ed819738a9f0a3051f31dc9924e3065";
234 234 };
235 235 meta = {
236 236 license = [ ];
237 237 };
238 238 };
239 239 WebError = super.buildPythonPackage {
240 240 name = "WebError-0.10.3";
241 241 buildInputs = with self; [];
242 242 doCheck = false;
243 243 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
244 244 src = fetchurl {
245 245 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
246 246 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
247 247 };
248 248 meta = {
249 249 license = [ pkgs.lib.licenses.mit ];
250 250 };
251 251 };
252 252 WebHelpers = super.buildPythonPackage {
253 253 name = "WebHelpers-1.3";
254 254 buildInputs = with self; [];
255 255 doCheck = false;
256 256 propagatedBuildInputs = with self; [MarkupSafe];
257 257 src = fetchurl {
258 258 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
259 259 md5 = "32749ffadfc40fea51075a7def32588b";
260 260 };
261 261 meta = {
262 262 license = [ pkgs.lib.licenses.bsdOriginal ];
263 263 };
264 264 };
265 265 WebHelpers2 = super.buildPythonPackage {
266 266 name = "WebHelpers2-2.0";
267 267 buildInputs = with self; [];
268 268 doCheck = false;
269 269 propagatedBuildInputs = with self; [MarkupSafe six];
270 270 src = fetchurl {
271 271 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
272 272 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
273 273 };
274 274 meta = {
275 275 license = [ pkgs.lib.licenses.mit ];
276 276 };
277 277 };
278 278 WebOb = super.buildPythonPackage {
279 279 name = "WebOb-1.7.4";
280 280 buildInputs = with self; [];
281 281 doCheck = false;
282 282 propagatedBuildInputs = with self; [];
283 283 src = fetchurl {
284 284 url = "https://pypi.python.org/packages/75/34/731e23f52371852dfe7490a61644826ba7fe70fd52a377aaca0f4956ba7f/WebOb-1.7.4.tar.gz";
285 285 md5 = "397e46892d7f199b1a07eb20a2d3d9bd";
286 286 };
287 287 meta = {
288 288 license = [ pkgs.lib.licenses.mit ];
289 289 };
290 290 };
291 291 WebTest = super.buildPythonPackage {
292 292 name = "WebTest-2.0.29";
293 293 buildInputs = with self; [];
294 294 doCheck = false;
295 295 propagatedBuildInputs = with self; [six WebOb waitress beautifulsoup4];
296 296 src = fetchurl {
297 297 url = "https://pypi.python.org/packages/94/de/8f94738be649997da99c47b104aa3c3984ecec51a1d8153ed09638253d56/WebTest-2.0.29.tar.gz";
298 298 md5 = "30b4cf0d340b9a5335fac4389e6f84fc";
299 299 };
300 300 meta = {
301 301 license = [ pkgs.lib.licenses.mit ];
302 302 };
303 303 };
304 304 Whoosh = super.buildPythonPackage {
305 305 name = "Whoosh-2.7.4";
306 306 buildInputs = with self; [];
307 307 doCheck = false;
308 308 propagatedBuildInputs = with self; [];
309 309 src = fetchurl {
310 310 url = "https://pypi.python.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
311 311 md5 = "c2710105f20b3e29936bd2357383c325";
312 312 };
313 313 meta = {
314 314 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
315 315 };
316 316 };
317 317 alembic = super.buildPythonPackage {
318 name = "alembic-0.9.6";
318 name = "alembic-0.9.8";
319 319 buildInputs = with self; [];
320 320 doCheck = false;
321 321 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor python-dateutil];
322 322 src = fetchurl {
323 url = "https://pypi.python.org/packages/bf/b3/b28ea715824f8455635ece3c12f59d5d205f98cc378858e414e3aa6ebdbc/alembic-0.9.6.tar.gz";
324 md5 = "fcb096bccc87c8770bd07a04606cb989";
323 url = "https://pypi.python.org/packages/a1/95/2252783859df9ec76b9a25d968c2827ed75a43ba34c6e8d38f87a5c0fb26/alembic-0.9.8.tar.gz";
324 md5 = "5cfef58641c9a94d4a5d547e951a7dda";
325 325 };
326 326 meta = {
327 327 license = [ pkgs.lib.licenses.mit ];
328 328 };
329 329 };
330 330 amqp = super.buildPythonPackage {
331 331 name = "amqp-2.2.2";
332 332 buildInputs = with self; [];
333 333 doCheck = false;
334 334 propagatedBuildInputs = with self; [vine];
335 335 src = fetchurl {
336 336 url = "https://pypi.python.org/packages/e0/70/9ab9ccd8247fb7d2adb717e9f6a0ed358c9e1ab2c349048b0352f9e80ee2/amqp-2.2.2.tar.gz";
337 337 md5 = "0971a3fd2d635ded45c349cfc17106bd";
338 338 };
339 339 meta = {
340 340 license = [ pkgs.lib.licenses.bsdOriginal ];
341 341 };
342 342 };
343 343 appenlight-client = super.buildPythonPackage {
344 name = "appenlight-client-0.6.22";
344 name = "appenlight-client-0.6.25";
345 345 buildInputs = with self; [];
346 346 doCheck = false;
347 347 propagatedBuildInputs = with self; [WebOb requests six];
348 348 src = fetchurl {
349 url = "https://pypi.python.org/packages/73/37/0a64460fa9670b17c061adc433bc8be5079cba21e8b3a92d824adccb12bc/appenlight_client-0.6.22.tar.gz";
350 md5 = "641afc114a9a3b3af4f75b11c70968ee";
349 url = "https://pypi.python.org/packages/fa/44/2911ef85ea4f4fe65058fd22959d8dad598fab6a3c84e5bcb569d15c8783/appenlight_client-0.6.25.tar.gz";
350 md5 = "76dd2f9d42659fae8f290982078dc880";
351 351 };
352 352 meta = {
353 353 license = [ pkgs.lib.licenses.bsdOriginal ];
354 354 };
355 355 };
356 356 authomatic = super.buildPythonPackage {
357 357 name = "authomatic-0.1.0.post1";
358 358 buildInputs = with self; [];
359 359 doCheck = false;
360 360 propagatedBuildInputs = with self; [];
361 361 src = fetchurl {
362 362 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
363 363 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
364 364 };
365 365 meta = {
366 366 license = [ pkgs.lib.licenses.mit ];
367 367 };
368 368 };
369 369 backports.shutil-get-terminal-size = super.buildPythonPackage {
370 370 name = "backports.shutil-get-terminal-size-1.0.0";
371 371 buildInputs = with self; [];
372 372 doCheck = false;
373 373 propagatedBuildInputs = with self; [];
374 374 src = fetchurl {
375 375 url = "https://pypi.python.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
376 376 md5 = "03267762480bd86b50580dc19dff3c66";
377 377 };
378 378 meta = {
379 379 license = [ pkgs.lib.licenses.mit ];
380 380 };
381 381 };
382 382 beautifulsoup4 = super.buildPythonPackage {
383 383 name = "beautifulsoup4-4.6.0";
384 384 buildInputs = with self; [];
385 385 doCheck = false;
386 386 propagatedBuildInputs = with self; [];
387 387 src = fetchurl {
388 388 url = "https://pypi.python.org/packages/fa/8d/1d14391fdaed5abada4e0f63543fef49b8331a34ca60c88bd521bcf7f782/beautifulsoup4-4.6.0.tar.gz";
389 389 md5 = "c17714d0f91a23b708a592cb3c697728";
390 390 };
391 391 meta = {
392 392 license = [ pkgs.lib.licenses.mit ];
393 393 };
394 394 };
395 395 billiard = super.buildPythonPackage {
396 396 name = "billiard-3.5.0.3";
397 397 buildInputs = with self; [];
398 398 doCheck = false;
399 399 propagatedBuildInputs = with self; [];
400 400 src = fetchurl {
401 401 url = "https://pypi.python.org/packages/39/ac/f5571210cca2e4f4532e38aaff242f26c8654c5e2436bee966c230647ccc/billiard-3.5.0.3.tar.gz";
402 402 md5 = "113ba481e48400adbf6fbbf59a2f8554";
403 403 };
404 404 meta = {
405 405 license = [ pkgs.lib.licenses.bsdOriginal ];
406 406 };
407 407 };
408 408 bleach = super.buildPythonPackage {
409 name = "bleach-2.1.1";
409 name = "bleach-2.1.2";
410 410 buildInputs = with self; [];
411 411 doCheck = false;
412 412 propagatedBuildInputs = with self; [six html5lib];
413 413 src = fetchurl {
414 url = "https://pypi.python.org/packages/d4/3f/d517089af35b01bb9bc4eac5ea04bae342b37a5e9abbb27b7c3ce0eae070/bleach-2.1.1.tar.gz";
415 md5 = "7c5dfb1d66ea979b5a465afb12c82ec4";
414 url = "https://pypi.python.org/packages/b3/5f/0da670d30d3ffbc57cc97fa82947f81bbe3eab8d441e2d42e661f215baf2/bleach-2.1.2.tar.gz";
415 md5 = "d0b14ae43a437ee0c650e04c6063eedd";
416 416 };
417 417 meta = {
418 418 license = [ pkgs.lib.licenses.asl20 ];
419 419 };
420 420 };
421 421 bottle = super.buildPythonPackage {
422 422 name = "bottle-0.12.13";
423 423 buildInputs = with self; [];
424 424 doCheck = false;
425 425 propagatedBuildInputs = with self; [];
426 426 src = fetchurl {
427 427 url = "https://pypi.python.org/packages/bd/99/04dc59ced52a8261ee0f965a8968717a255ea84a36013e527944dbf3468c/bottle-0.12.13.tar.gz";
428 428 md5 = "d2fe1b48c1d49217e78bf326b1cad437";
429 429 };
430 430 meta = {
431 431 license = [ pkgs.lib.licenses.mit ];
432 432 };
433 433 };
434 434 bumpversion = super.buildPythonPackage {
435 435 name = "bumpversion-0.5.3";
436 436 buildInputs = with self; [];
437 437 doCheck = false;
438 438 propagatedBuildInputs = with self; [];
439 439 src = fetchurl {
440 440 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
441 441 md5 = "c66a3492eafcf5ad4b024be9fca29820";
442 442 };
443 443 meta = {
444 444 license = [ pkgs.lib.licenses.mit ];
445 445 };
446 446 };
447 447 celery = super.buildPythonPackage {
448 448 name = "celery-4.1.0";
449 449 buildInputs = with self; [];
450 450 doCheck = false;
451 451 propagatedBuildInputs = with self; [pytz billiard kombu];
452 452 src = fetchurl {
453 453 url = "https://pypi.python.org/packages/07/65/88a2a45fc80f487872c93121a701a53bbbc3d3d832016876fac84fc8d46a/celery-4.1.0.tar.gz";
454 454 md5 = "db91e1d26936381127f01e150fe3054a";
455 455 };
456 456 meta = {
457 457 license = [ pkgs.lib.licenses.bsdOriginal ];
458 458 };
459 459 };
460 460 channelstream = super.buildPythonPackage {
461 461 name = "channelstream-0.5.2";
462 462 buildInputs = with self; [];
463 463 doCheck = false;
464 464 propagatedBuildInputs = with self; [gevent ws4py pyramid pyramid-jinja2 itsdangerous requests six];
465 465 src = fetchurl {
466 466 url = "https://pypi.python.org/packages/2b/31/29a8e085cf5bf97fa88e7b947adabfc581a18a3463adf77fb6dada34a65f/channelstream-0.5.2.tar.gz";
467 467 md5 = "1c5eb2a8a405be6f1073da94da6d81d3";
468 468 };
469 469 meta = {
470 470 license = [ pkgs.lib.licenses.bsdOriginal ];
471 471 };
472 472 };
473 473 click = super.buildPythonPackage {
474 474 name = "click-6.6";
475 475 buildInputs = with self; [];
476 476 doCheck = false;
477 477 propagatedBuildInputs = with self; [];
478 478 src = fetchurl {
479 479 url = "https://pypi.python.org/packages/7a/00/c14926d8232b36b08218067bcd5853caefb4737cda3f0a47437151344792/click-6.6.tar.gz";
480 480 md5 = "d0b09582123605220ad6977175f3e51d";
481 481 };
482 482 meta = {
483 483 license = [ pkgs.lib.licenses.bsdOriginal ];
484 484 };
485 485 };
486 486 colander = super.buildPythonPackage {
487 487 name = "colander-1.4";
488 488 buildInputs = with self; [];
489 489 doCheck = false;
490 490 propagatedBuildInputs = with self; [translationstring iso8601];
491 491 src = fetchurl {
492 492 url = "https://pypi.python.org/packages/cc/e2/c4e716ac4a426d8ad4dfe306c34f0018a22275d2420815784005bf771c84/colander-1.4.tar.gz";
493 493 md5 = "cbb8e403c2ba05aeaa419d51fdb93736";
494 494 };
495 495 meta = {
496 496 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
497 497 };
498 498 };
499 499 configobj = super.buildPythonPackage {
500 500 name = "configobj-5.0.6";
501 501 buildInputs = with self; [];
502 502 doCheck = false;
503 503 propagatedBuildInputs = with self; [six];
504 504 src = fetchurl {
505 505 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
506 506 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
507 507 };
508 508 meta = {
509 509 license = [ pkgs.lib.licenses.bsdOriginal ];
510 510 };
511 511 };
512 512 configparser = super.buildPythonPackage {
513 513 name = "configparser-3.5.0";
514 514 buildInputs = with self; [];
515 515 doCheck = false;
516 516 propagatedBuildInputs = with self; [];
517 517 src = fetchurl {
518 518 url = "https://pypi.python.org/packages/7c/69/c2ce7e91c89dc073eb1aa74c0621c3eefbffe8216b3f9af9d3885265c01c/configparser-3.5.0.tar.gz";
519 519 md5 = "cfdd915a5b7a6c09917a64a573140538";
520 520 };
521 521 meta = {
522 522 license = [ pkgs.lib.licenses.mit ];
523 523 };
524 524 };
525 525 cov-core = super.buildPythonPackage {
526 526 name = "cov-core-1.15.0";
527 527 buildInputs = with self; [];
528 528 doCheck = false;
529 529 propagatedBuildInputs = with self; [coverage];
530 530 src = fetchurl {
531 531 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
532 532 md5 = "f519d4cb4c4e52856afb14af52919fe6";
533 533 };
534 534 meta = {
535 535 license = [ pkgs.lib.licenses.mit ];
536 536 };
537 537 };
538 538 coverage = super.buildPythonPackage {
539 539 name = "coverage-3.7.1";
540 540 buildInputs = with self; [];
541 541 doCheck = false;
542 542 propagatedBuildInputs = with self; [];
543 543 src = fetchurl {
544 544 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
545 545 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
546 546 };
547 547 meta = {
548 548 license = [ pkgs.lib.licenses.bsdOriginal ];
549 549 };
550 550 };
551 551 cssselect = super.buildPythonPackage {
552 552 name = "cssselect-1.0.1";
553 553 buildInputs = with self; [];
554 554 doCheck = false;
555 555 propagatedBuildInputs = with self; [];
556 556 src = fetchurl {
557 557 url = "https://pypi.python.org/packages/77/ff/9c865275cd19290feba56344eba570e719efb7ca5b34d67ed12b22ebbb0d/cssselect-1.0.1.tar.gz";
558 558 md5 = "3fa03bf82a9f0b1223c0f1eb1369e139";
559 559 };
560 560 meta = {
561 561 license = [ pkgs.lib.licenses.bsdOriginal ];
562 562 };
563 563 };
564 564 decorator = super.buildPythonPackage {
565 565 name = "decorator-4.1.2";
566 566 buildInputs = with self; [];
567 567 doCheck = false;
568 568 propagatedBuildInputs = with self; [];
569 569 src = fetchurl {
570 570 url = "https://pypi.python.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
571 571 md5 = "a0f7f4fe00ae2dde93494d90c192cf8c";
572 572 };
573 573 meta = {
574 574 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
575 575 };
576 576 };
577 577 deform = super.buildPythonPackage {
578 578 name = "deform-2.0.4";
579 579 buildInputs = with self; [];
580 580 doCheck = false;
581 581 propagatedBuildInputs = with self; [Chameleon colander iso8601 peppercorn translationstring zope.deprecation];
582 582 src = fetchurl {
583 583 url = "https://pypi.python.org/packages/66/3b/eefcb07abcab7a97f6665aa2d0cf1af741d9d6e78a2e4657fd2b89f89880/deform-2.0.4.tar.gz";
584 584 md5 = "34756e42cf50dd4b4430809116c4ec0a";
585 585 };
586 586 meta = {
587 587 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
588 588 };
589 589 };
590 590 docutils = super.buildPythonPackage {
591 591 name = "docutils-0.14";
592 592 buildInputs = with self; [];
593 593 doCheck = false;
594 594 propagatedBuildInputs = with self; [];
595 595 src = fetchurl {
596 596 url = "https://pypi.python.org/packages/84/f4/5771e41fdf52aabebbadecc9381d11dea0fa34e4759b4071244fa094804c/docutils-0.14.tar.gz";
597 597 md5 = "c53768d63db3873b7d452833553469de";
598 598 };
599 599 meta = {
600 600 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 ];
601 601 };
602 602 };
603 603 dogpile.cache = super.buildPythonPackage {
604 604 name = "dogpile.cache-0.6.4";
605 605 buildInputs = with self; [];
606 606 doCheck = false;
607 607 propagatedBuildInputs = with self; [];
608 608 src = fetchurl {
609 609 url = "https://pypi.python.org/packages/b6/3d/35c05ca01c070bb70d9d422f2c4858ecb021b05b21af438fec5ccd7b945c/dogpile.cache-0.6.4.tar.gz";
610 610 md5 = "66e0a6cae6c08cb1ea25f89d0eadfeb0";
611 611 };
612 612 meta = {
613 613 license = [ pkgs.lib.licenses.bsdOriginal ];
614 614 };
615 615 };
616 616 dogpile.core = super.buildPythonPackage {
617 617 name = "dogpile.core-0.4.1";
618 618 buildInputs = with self; [];
619 619 doCheck = false;
620 620 propagatedBuildInputs = with self; [];
621 621 src = fetchurl {
622 622 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
623 623 md5 = "01cb19f52bba3e95c9b560f39341f045";
624 624 };
625 625 meta = {
626 626 license = [ pkgs.lib.licenses.bsdOriginal ];
627 627 };
628 628 };
629 629 ecdsa = super.buildPythonPackage {
630 630 name = "ecdsa-0.13";
631 631 buildInputs = with self; [];
632 632 doCheck = false;
633 633 propagatedBuildInputs = with self; [];
634 634 src = fetchurl {
635 635 url = "https://pypi.python.org/packages/f9/e5/99ebb176e47f150ac115ffeda5fedb6a3dbb3c00c74a59fd84ddf12f5857/ecdsa-0.13.tar.gz";
636 636 md5 = "1f60eda9cb5c46722856db41a3ae6670";
637 637 };
638 638 meta = {
639 639 license = [ pkgs.lib.licenses.mit ];
640 640 };
641 641 };
642 642 elasticsearch = super.buildPythonPackage {
643 643 name = "elasticsearch-2.3.0";
644 644 buildInputs = with self; [];
645 645 doCheck = false;
646 646 propagatedBuildInputs = with self; [urllib3];
647 647 src = fetchurl {
648 648 url = "https://pypi.python.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
649 649 md5 = "2550f3b51629cf1ef9636608af92c340";
650 650 };
651 651 meta = {
652 652 license = [ pkgs.lib.licenses.asl20 ];
653 653 };
654 654 };
655 655 elasticsearch-dsl = super.buildPythonPackage {
656 656 name = "elasticsearch-dsl-2.2.0";
657 657 buildInputs = with self; [];
658 658 doCheck = false;
659 659 propagatedBuildInputs = with self; [six python-dateutil elasticsearch];
660 660 src = fetchurl {
661 661 url = "https://pypi.python.org/packages/66/2f/52a086968788e58461641570f45c3207a52d46ebbe9b77dc22b6a8ffda66/elasticsearch-dsl-2.2.0.tar.gz";
662 662 md5 = "fa6bd3c87ea3caa8f0f051bc37c53221";
663 663 };
664 664 meta = {
665 665 license = [ pkgs.lib.licenses.asl20 ];
666 666 };
667 667 };
668 668 entrypoints = super.buildPythonPackage {
669 669 name = "entrypoints-0.2.2";
670 670 buildInputs = with self; [];
671 671 doCheck = false;
672 672 propagatedBuildInputs = with self; [configparser];
673 673 src = fetchurl {
674 674 url = "https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313";
675 675 md5 = "7db37771aea9ac9fefe093e5d6987313";
676 676 };
677 677 meta = {
678 678 license = [ pkgs.lib.licenses.mit ];
679 679 };
680 680 };
681 681 enum34 = super.buildPythonPackage {
682 682 name = "enum34-1.1.6";
683 683 buildInputs = with self; [];
684 684 doCheck = false;
685 685 propagatedBuildInputs = with self; [];
686 686 src = fetchurl {
687 687 url = "https://pypi.python.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
688 688 md5 = "5f13a0841a61f7fc295c514490d120d0";
689 689 };
690 690 meta = {
691 691 license = [ pkgs.lib.licenses.bsdOriginal ];
692 692 };
693 693 };
694 694 funcsigs = super.buildPythonPackage {
695 695 name = "funcsigs-1.0.2";
696 696 buildInputs = with self; [];
697 697 doCheck = false;
698 698 propagatedBuildInputs = with self; [];
699 699 src = fetchurl {
700 700 url = "https://pypi.python.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
701 701 md5 = "7e583285b1fb8a76305d6d68f4ccc14e";
702 702 };
703 703 meta = {
704 704 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
705 705 };
706 706 };
707 707 functools32 = super.buildPythonPackage {
708 708 name = "functools32-3.2.3.post2";
709 709 buildInputs = with self; [];
710 710 doCheck = false;
711 711 propagatedBuildInputs = with self; [];
712 712 src = fetchurl {
713 url = "https://pypi.python.org/packages/5e/1a/0aa2c8195a204a9f51284018562dea77e25511f02fe924fac202fc012172/functools32-3.2.3-2.zip";
714 md5 = "d55232eb132ec779e6893c902a0bc5ad";
713 url = "https://pypi.python.org/packages/c5/60/6ac26ad05857c601308d8fb9e87fa36d0ebf889423f47c3502ef034365db/functools32-3.2.3-2.tar.gz";
714 md5 = "09f24ffd9af9f6cd0f63cb9f4e23d4b2";
715 715 };
716 716 meta = {
717 717 license = [ pkgs.lib.licenses.psfl ];
718 718 };
719 719 };
720 720 future = super.buildPythonPackage {
721 721 name = "future-0.14.3";
722 722 buildInputs = with self; [];
723 723 doCheck = false;
724 724 propagatedBuildInputs = with self; [];
725 725 src = fetchurl {
726 726 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
727 727 md5 = "e94079b0bd1fc054929e8769fc0f6083";
728 728 };
729 729 meta = {
730 730 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
731 731 };
732 732 };
733 733 futures = super.buildPythonPackage {
734 734 name = "futures-3.0.2";
735 735 buildInputs = with self; [];
736 736 doCheck = false;
737 737 propagatedBuildInputs = with self; [];
738 738 src = fetchurl {
739 739 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
740 740 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
741 741 };
742 742 meta = {
743 743 license = [ pkgs.lib.licenses.bsdOriginal ];
744 744 };
745 745 };
746 746 gevent = super.buildPythonPackage {
747 747 name = "gevent-1.2.2";
748 748 buildInputs = with self; [];
749 749 doCheck = false;
750 750 propagatedBuildInputs = with self; [greenlet];
751 751 src = fetchurl {
752 752 url = "https://pypi.python.org/packages/1b/92/b111f76e54d2be11375b47b213b56687214f258fd9dae703546d30b837be/gevent-1.2.2.tar.gz";
753 753 md5 = "7f0baf355384fe5ff2ecf66853422554";
754 754 };
755 755 meta = {
756 756 license = [ pkgs.lib.licenses.mit ];
757 757 };
758 758 };
759 759 gnureadline = super.buildPythonPackage {
760 760 name = "gnureadline-6.3.8";
761 761 buildInputs = with self; [];
762 762 doCheck = false;
763 763 propagatedBuildInputs = with self; [];
764 764 src = fetchurl {
765 765 url = "https://pypi.python.org/packages/50/64/86085c823cd78f9df9d8e33dce0baa71618016f8860460b82cf6610e1eb3/gnureadline-6.3.8.tar.gz";
766 766 md5 = "ba341f4b907250bd1f47dbc06290604f";
767 767 };
768 768 meta = {
769 769 license = [ { fullName = "GNU General Public License v3 (GPLv3)"; } pkgs.lib.licenses.gpl1 ];
770 770 };
771 771 };
772 772 gprof2dot = super.buildPythonPackage {
773 773 name = "gprof2dot-2017.9.19";
774 774 buildInputs = with self; [];
775 775 doCheck = false;
776 776 propagatedBuildInputs = with self; [];
777 777 src = fetchurl {
778 778 url = "https://pypi.python.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
779 779 md5 = "cda2d552bb0d0b9f16e6824a9aabd225";
780 780 };
781 781 meta = {
782 782 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
783 783 };
784 784 };
785 785 graphviz = super.buildPythonPackage {
786 name = "graphviz-0.8.1";
786 name = "graphviz-0.8.2";
787 787 buildInputs = with self; [];
788 788 doCheck = false;
789 789 propagatedBuildInputs = with self; [];
790 790 src = fetchurl {
791 url = "https://pypi.python.org/packages/a9/a6/ee6721349489a2da6eedd3dba124f2b5ac15ee1e0a7bd4d3cfdc4fff0327/graphviz-0.8.1.zip";
792 md5 = "88d8efa88c02a735b3659fe0feaf0b96";
791 url = "https://pypi.python.org/packages/fa/d1/63b62dee9e55368f60b5ea445e6afb361bb47e692fc27553f3672e16efb8/graphviz-0.8.2.zip";
792 md5 = "50866e780f43e1cb0d073c70424fcaff";
793 793 };
794 794 meta = {
795 795 license = [ pkgs.lib.licenses.mit ];
796 796 };
797 797 };
798 798 greenlet = super.buildPythonPackage {
799 name = "greenlet-0.4.12";
799 name = "greenlet-0.4.13";
800 800 buildInputs = with self; [];
801 801 doCheck = false;
802 802 propagatedBuildInputs = with self; [];
803 803 src = fetchurl {
804 url = "https://pypi.python.org/packages/be/76/82af375d98724054b7e273b5d9369346937324f9bcc20980b45b068ef0b0/greenlet-0.4.12.tar.gz";
805 md5 = "e8637647d58a26c4a1f51ca393e53c00";
804 url = "https://pypi.python.org/packages/13/de/ba92335e9e76040ca7274224942282a80d54f85e342a5e33c5277c7f87eb/greenlet-0.4.13.tar.gz";
805 md5 = "6e0b9dd5385f81d478451ec8ed1d62b3";
806 806 };
807 807 meta = {
808 808 license = [ pkgs.lib.licenses.mit ];
809 809 };
810 810 };
811 811 gunicorn = super.buildPythonPackage {
812 812 name = "gunicorn-19.7.1";
813 813 buildInputs = with self; [];
814 814 doCheck = false;
815 815 propagatedBuildInputs = with self; [];
816 816 src = fetchurl {
817 817 url = "https://pypi.python.org/packages/30/3a/10bb213cede0cc4d13ac2263316c872a64bf4c819000c8ccd801f1d5f822/gunicorn-19.7.1.tar.gz";
818 818 md5 = "174d3c3cd670a5be0404d84c484e590c";
819 819 };
820 820 meta = {
821 821 license = [ pkgs.lib.licenses.mit ];
822 822 };
823 823 };
824 824 html5lib = super.buildPythonPackage {
825 name = "html5lib-1.0b10";
825 name = "html5lib-1.0.1";
826 826 buildInputs = with self; [];
827 827 doCheck = false;
828 propagatedBuildInputs = with self; [six webencodings setuptools];
828 propagatedBuildInputs = with self; [six webencodings];
829 829 src = fetchurl {
830 url = "https://pypi.python.org/packages/97/16/982214624095c1420c75f3bd295d9e658794aafb95fc075823de107e0ae4/html5lib-1.0b10.tar.gz";
831 md5 = "5ada1243b7a863624b2f35245b2186e9";
830 url = "https://pypi.python.org/packages/85/3e/cf449cf1b5004e87510b9368e7a5f1acd8831c2d6691edd3c62a0823f98f/html5lib-1.0.1.tar.gz";
831 md5 = "942a0688d6bdf20d087c9805c40182ad";
832 832 };
833 833 meta = {
834 834 license = [ pkgs.lib.licenses.mit ];
835 835 };
836 836 };
837 837 hupper = super.buildPythonPackage {
838 838 name = "hupper-1.0";
839 839 buildInputs = with self; [];
840 840 doCheck = false;
841 841 propagatedBuildInputs = with self; [];
842 842 src = fetchurl {
843 843 url = "https://pypi.python.org/packages/2e/07/df892c564dc09bb3cf6f6deb976c26adf9117db75ba218cb4353dbc9d826/hupper-1.0.tar.gz";
844 844 md5 = "26e77da7d5ac5858f59af050d1a6eb5a";
845 845 };
846 846 meta = {
847 847 license = [ pkgs.lib.licenses.mit ];
848 848 };
849 849 };
850 850 infrae.cache = super.buildPythonPackage {
851 851 name = "infrae.cache-1.0.1";
852 852 buildInputs = with self; [];
853 853 doCheck = false;
854 854 propagatedBuildInputs = with self; [Beaker repoze.lru];
855 855 src = fetchurl {
856 856 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
857 857 md5 = "b09076a766747e6ed2a755cc62088e32";
858 858 };
859 859 meta = {
860 860 license = [ pkgs.lib.licenses.zpt21 ];
861 861 };
862 862 };
863 863 invoke = super.buildPythonPackage {
864 864 name = "invoke-0.13.0";
865 865 buildInputs = with self; [];
866 866 doCheck = false;
867 867 propagatedBuildInputs = with self; [];
868 868 src = fetchurl {
869 869 url = "https://pypi.python.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
870 870 md5 = "c0d1ed4bfb34eaab551662d8cfee6540";
871 871 };
872 872 meta = {
873 873 license = [ pkgs.lib.licenses.bsdOriginal ];
874 874 };
875 875 };
876 876 ipaddress = super.buildPythonPackage {
877 name = "ipaddress-1.0.18";
877 name = "ipaddress-1.0.19";
878 878 buildInputs = with self; [];
879 879 doCheck = false;
880 880 propagatedBuildInputs = with self; [];
881 881 src = fetchurl {
882 url = "https://pypi.python.org/packages/4e/13/774faf38b445d0b3a844b65747175b2e0500164b7c28d78e34987a5bfe06/ipaddress-1.0.18.tar.gz";
883 md5 = "310c2dfd64eb6f0df44aa8c59f2334a7";
882 url = "https://pypi.python.org/packages/f0/ba/860a4a3e283456d6b7e2ab39ce5cf11a3490ee1a363652ac50abf9f0f5df/ipaddress-1.0.19.tar.gz";
883 md5 = "d0687efaf93a32476d81e90ba0609c57";
884 884 };
885 885 meta = {
886 886 license = [ pkgs.lib.licenses.psfl ];
887 887 };
888 888 };
889 889 ipdb = super.buildPythonPackage {
890 890 name = "ipdb-0.10.3";
891 891 buildInputs = with self; [];
892 892 doCheck = false;
893 893 propagatedBuildInputs = with self; [setuptools ipython];
894 894 src = fetchurl {
895 895 url = "https://pypi.python.org/packages/ad/cc/0e7298e1fbf2efd52667c9354a12aa69fb6f796ce230cca03525051718ef/ipdb-0.10.3.tar.gz";
896 896 md5 = "def1f6ac075d54bdee07e6501263d4fa";
897 897 };
898 898 meta = {
899 899 license = [ pkgs.lib.licenses.bsdOriginal ];
900 900 };
901 901 };
902 902 ipython = super.buildPythonPackage {
903 903 name = "ipython-5.1.0";
904 904 buildInputs = with self; [];
905 905 doCheck = false;
906 906 propagatedBuildInputs = with self; [setuptools decorator pickleshare simplegeneric traitlets prompt-toolkit Pygments pexpect backports.shutil-get-terminal-size pathlib2 pexpect];
907 907 src = fetchurl {
908 908 url = "https://pypi.python.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
909 909 md5 = "47c8122420f65b58784cb4b9b4af35e3";
910 910 };
911 911 meta = {
912 912 license = [ pkgs.lib.licenses.bsdOriginal ];
913 913 };
914 914 };
915 915 ipython-genutils = super.buildPythonPackage {
916 916 name = "ipython-genutils-0.2.0";
917 917 buildInputs = with self; [];
918 918 doCheck = false;
919 919 propagatedBuildInputs = with self; [];
920 920 src = fetchurl {
921 921 url = "https://pypi.python.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
922 922 md5 = "5a4f9781f78466da0ea1a648f3e1f79f";
923 923 };
924 924 meta = {
925 925 license = [ pkgs.lib.licenses.bsdOriginal ];
926 926 };
927 927 };
928 928 iso8601 = super.buildPythonPackage {
929 929 name = "iso8601-0.1.12";
930 930 buildInputs = with self; [];
931 931 doCheck = false;
932 932 propagatedBuildInputs = with self; [];
933 933 src = fetchurl {
934 934 url = "https://pypi.python.org/packages/45/13/3db24895497345fb44c4248c08b16da34a9eb02643cea2754b21b5ed08b0/iso8601-0.1.12.tar.gz";
935 935 md5 = "4de940f691c5ea759fb254384c8ddcf6";
936 936 };
937 937 meta = {
938 938 license = [ pkgs.lib.licenses.mit ];
939 939 };
940 940 };
941 941 itsdangerous = super.buildPythonPackage {
942 942 name = "itsdangerous-0.24";
943 943 buildInputs = with self; [];
944 944 doCheck = false;
945 945 propagatedBuildInputs = with self; [];
946 946 src = fetchurl {
947 947 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
948 948 md5 = "a3d55aa79369aef5345c036a8a26307f";
949 949 };
950 950 meta = {
951 951 license = [ pkgs.lib.licenses.bsdOriginal ];
952 952 };
953 953 };
954 954 jsonschema = super.buildPythonPackage {
955 955 name = "jsonschema-2.6.0";
956 956 buildInputs = with self; [];
957 957 doCheck = false;
958 958 propagatedBuildInputs = with self; [functools32];
959 959 src = fetchurl {
960 960 url = "https://pypi.python.org/packages/58/b9/171dbb07e18c6346090a37f03c7e74410a1a56123f847efed59af260a298/jsonschema-2.6.0.tar.gz";
961 961 md5 = "50c6b69a373a8b55ff1e0ec6e78f13f4";
962 962 };
963 963 meta = {
964 964 license = [ pkgs.lib.licenses.mit ];
965 965 };
966 966 };
967 967 jupyter-client = super.buildPythonPackage {
968 968 name = "jupyter-client-5.0.0";
969 969 buildInputs = with self; [];
970 970 doCheck = false;
971 971 propagatedBuildInputs = with self; [traitlets jupyter-core pyzmq python-dateutil];
972 972 src = fetchurl {
973 973 url = "https://pypi.python.org/packages/e5/6f/65412ed462202b90134b7e761b0b7e7f949e07a549c1755475333727b3d0/jupyter_client-5.0.0.tar.gz";
974 974 md5 = "1acd331b5c9fb4d79dae9939e79f2426";
975 975 };
976 976 meta = {
977 977 license = [ pkgs.lib.licenses.bsdOriginal ];
978 978 };
979 979 };
980 980 jupyter-core = super.buildPythonPackage {
981 981 name = "jupyter-core-4.4.0";
982 982 buildInputs = with self; [];
983 983 doCheck = false;
984 984 propagatedBuildInputs = with self; [traitlets];
985 985 src = fetchurl {
986 986 url = "https://pypi.python.org/packages/b6/2d/2804f4de3a95583f65e5dcb4d7c8c7183124882323758996e867f47e72af/jupyter_core-4.4.0.tar.gz";
987 987 md5 = "7829fc07884ed98459e170f217e2a5ba";
988 988 };
989 989 meta = {
990 990 license = [ pkgs.lib.licenses.bsdOriginal ];
991 991 };
992 992 };
993 993 kombu = super.buildPythonPackage {
994 994 name = "kombu-4.1.0";
995 995 buildInputs = with self; [];
996 996 doCheck = false;
997 997 propagatedBuildInputs = with self; [amqp];
998 998 src = fetchurl {
999 999 url = "https://pypi.python.org/packages/03/5e/1a47d1e543d4943d65330af4e4406049f443878818fb65bfdc651bb93a96/kombu-4.1.0.tar.gz";
1000 1000 md5 = "2fb2be9fec0e6514231bba23a3779439";
1001 1001 };
1002 1002 meta = {
1003 1003 license = [ pkgs.lib.licenses.bsdOriginal ];
1004 1004 };
1005 1005 };
1006 1006 lxml = super.buildPythonPackage {
1007 1007 name = "lxml-3.7.3";
1008 1008 buildInputs = with self; [];
1009 1009 doCheck = false;
1010 1010 propagatedBuildInputs = with self; [];
1011 1011 src = fetchurl {
1012 1012 url = "https://pypi.python.org/packages/39/e8/a8e0b1fa65dd021d48fe21464f71783655f39a41f218293c1c590d54eb82/lxml-3.7.3.tar.gz";
1013 1013 md5 = "075692ce442e69bbd604d44e21c02753";
1014 1014 };
1015 1015 meta = {
1016 1016 license = [ pkgs.lib.licenses.bsdOriginal ];
1017 1017 };
1018 1018 };
1019 1019 meld3 = super.buildPythonPackage {
1020 1020 name = "meld3-1.0.2";
1021 1021 buildInputs = with self; [];
1022 1022 doCheck = false;
1023 1023 propagatedBuildInputs = with self; [];
1024 1024 src = fetchurl {
1025 1025 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
1026 1026 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
1027 1027 };
1028 1028 meta = {
1029 1029 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1030 1030 };
1031 1031 };
1032 1032 mistune = super.buildPythonPackage {
1033 1033 name = "mistune-0.8.3";
1034 1034 buildInputs = with self; [];
1035 1035 doCheck = false;
1036 1036 propagatedBuildInputs = with self; [];
1037 1037 src = fetchurl {
1038 1038 url = "https://pypi.python.org/packages/9d/be/e06d4346cc608a01dec6bf770d7d0303a4fd6db588b318ced18f5f257145/mistune-0.8.3.tar.gz";
1039 1039 md5 = "a5e4043e93fb8f9082e27f29eeb5e054";
1040 1040 };
1041 1041 meta = {
1042 1042 license = [ pkgs.lib.licenses.bsdOriginal ];
1043 1043 };
1044 1044 };
1045 1045 mock = super.buildPythonPackage {
1046 1046 name = "mock-1.0.1";
1047 1047 buildInputs = with self; [];
1048 1048 doCheck = false;
1049 1049 propagatedBuildInputs = with self; [];
1050 1050 src = fetchurl {
1051 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
1052 md5 = "869f08d003c289a97c1a6610faf5e913";
1051 url = "https://pypi.python.org/packages/a2/52/7edcd94f0afb721a2d559a5b9aae8af4f8f2c79bc63fdbe8a8a6c9b23bbe/mock-1.0.1.tar.gz";
1052 md5 = "c3971991738caa55ec7c356bbc154ee2";
1053 1053 };
1054 1054 meta = {
1055 1055 license = [ pkgs.lib.licenses.bsdOriginal ];
1056 1056 };
1057 1057 };
1058 1058 msgpack-python = super.buildPythonPackage {
1059 1059 name = "msgpack-python-0.4.8";
1060 1060 buildInputs = with self; [];
1061 1061 doCheck = false;
1062 1062 propagatedBuildInputs = with self; [];
1063 1063 src = fetchurl {
1064 1064 url = "https://pypi.python.org/packages/21/27/8a1d82041c7a2a51fcc73675875a5f9ea06c2663e02fcfeb708be1d081a0/msgpack-python-0.4.8.tar.gz";
1065 1065 md5 = "dcd854fb41ee7584ebbf35e049e6be98";
1066 1066 };
1067 1067 meta = {
1068 1068 license = [ pkgs.lib.licenses.asl20 ];
1069 1069 };
1070 1070 };
1071 1071 nbconvert = super.buildPythonPackage {
1072 1072 name = "nbconvert-5.3.1";
1073 1073 buildInputs = with self; [];
1074 1074 doCheck = false;
1075 1075 propagatedBuildInputs = with self; [mistune Jinja2 Pygments traitlets jupyter-core nbformat entrypoints bleach pandocfilters testpath];
1076 1076 src = fetchurl {
1077 1077 url = "https://pypi.python.org/packages/b9/a4/d0a0938ad6f5eeb4dea4e73d255c617ef94b0b2849d51194c9bbdb838412/nbconvert-5.3.1.tar.gz";
1078 1078 md5 = "c128d0d93d02f70a85429a383dae96d2";
1079 1079 };
1080 1080 meta = {
1081 1081 license = [ pkgs.lib.licenses.bsdOriginal ];
1082 1082 };
1083 1083 };
1084 1084 nbformat = super.buildPythonPackage {
1085 1085 name = "nbformat-4.4.0";
1086 1086 buildInputs = with self; [];
1087 1087 doCheck = false;
1088 1088 propagatedBuildInputs = with self; [ipython-genutils traitlets jsonschema jupyter-core];
1089 1089 src = fetchurl {
1090 1090 url = "https://pypi.python.org/packages/6e/0e/160754f7ae3e984863f585a3743b0ed1702043a81245907c8fae2d537155/nbformat-4.4.0.tar.gz";
1091 1091 md5 = "2d5f873138d9fbc2a3f9eaaebca3b8a1";
1092 1092 };
1093 1093 meta = {
1094 1094 license = [ pkgs.lib.licenses.bsdOriginal ];
1095 1095 };
1096 1096 };
1097 1097 objgraph = super.buildPythonPackage {
1098 1098 name = "objgraph-3.1.1";
1099 1099 buildInputs = with self; [];
1100 1100 doCheck = false;
1101 1101 propagatedBuildInputs = with self; [graphviz];
1102 1102 src = fetchurl {
1103 1103 url = "https://pypi.python.org/packages/be/58/9ca81a20cc837054e94866df1475d899caaa94f3732b8a46006858b015f7/objgraph-3.1.1.tar.gz";
1104 1104 md5 = "253af9944763377877c3678d8aaebb8b";
1105 1105 };
1106 1106 meta = {
1107 1107 license = [ pkgs.lib.licenses.mit ];
1108 1108 };
1109 1109 };
1110 1110 packaging = super.buildPythonPackage {
1111 1111 name = "packaging-15.2";
1112 1112 buildInputs = with self; [];
1113 1113 doCheck = false;
1114 1114 propagatedBuildInputs = with self; [];
1115 1115 src = fetchurl {
1116 1116 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
1117 1117 md5 = "c16093476f6ced42128bf610e5db3784";
1118 1118 };
1119 1119 meta = {
1120 1120 license = [ pkgs.lib.licenses.asl20 ];
1121 1121 };
1122 1122 };
1123 1123 pandocfilters = super.buildPythonPackage {
1124 1124 name = "pandocfilters-1.4.2";
1125 1125 buildInputs = with self; [];
1126 1126 doCheck = false;
1127 1127 propagatedBuildInputs = with self; [];
1128 1128 src = fetchurl {
1129 1129 url = "https://pypi.python.org/packages/4c/ea/236e2584af67bb6df960832731a6e5325fd4441de001767da328c33368ce/pandocfilters-1.4.2.tar.gz";
1130 1130 md5 = "dc391791ef54c7de1572d7b46b63361f";
1131 1131 };
1132 1132 meta = {
1133 1133 license = [ pkgs.lib.licenses.bsdOriginal ];
1134 1134 };
1135 1135 };
1136 1136 pathlib2 = super.buildPythonPackage {
1137 1137 name = "pathlib2-2.3.0";
1138 1138 buildInputs = with self; [];
1139 1139 doCheck = false;
1140 1140 propagatedBuildInputs = with self; [six scandir];
1141 1141 src = fetchurl {
1142 1142 url = "https://pypi.python.org/packages/a1/14/df0deb867c2733f7d857523c10942b3d6612a1b222502fdffa9439943dfb/pathlib2-2.3.0.tar.gz";
1143 1143 md5 = "89c90409d11fd5947966b6a30a47d18c";
1144 1144 };
1145 1145 meta = {
1146 1146 license = [ pkgs.lib.licenses.mit ];
1147 1147 };
1148 1148 };
1149 1149 peppercorn = super.buildPythonPackage {
1150 1150 name = "peppercorn-0.5";
1151 1151 buildInputs = with self; [];
1152 1152 doCheck = false;
1153 1153 propagatedBuildInputs = with self; [];
1154 1154 src = fetchurl {
1155 1155 url = "https://pypi.python.org/packages/45/ec/a62ec317d1324a01567c5221b420742f094f05ee48097e5157d32be3755c/peppercorn-0.5.tar.gz";
1156 1156 md5 = "f08efbca5790019ab45d76b7244abd40";
1157 1157 };
1158 1158 meta = {
1159 1159 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1160 1160 };
1161 1161 };
1162 1162 pexpect = super.buildPythonPackage {
1163 name = "pexpect-4.3.0";
1163 name = "pexpect-4.4.0";
1164 1164 buildInputs = with self; [];
1165 1165 doCheck = false;
1166 1166 propagatedBuildInputs = with self; [ptyprocess];
1167 1167 src = fetchurl {
1168 url = "https://pypi.python.org/packages/f8/44/5466c30e49762bb92e442bbdf4472d6904608d211258eb3198a11f0309a4/pexpect-4.3.0.tar.gz";
1169 md5 = "047a486dcd26134b74f2e67046bb61a0";
1168 url = "https://pypi.python.org/packages/fa/c3/60c0cbf96f242d0b47a82e9ca634dcd6dcb043832cf05e17540812e1c707/pexpect-4.4.0.tar.gz";
1169 md5 = "e9b07f0765df8245ac72201d757baaef";
1170 1170 };
1171 1171 meta = {
1172 1172 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1173 1173 };
1174 1174 };
1175 1175 pickleshare = super.buildPythonPackage {
1176 1176 name = "pickleshare-0.7.4";
1177 1177 buildInputs = with self; [];
1178 1178 doCheck = false;
1179 1179 propagatedBuildInputs = with self; [pathlib2];
1180 1180 src = fetchurl {
1181 1181 url = "https://pypi.python.org/packages/69/fe/dd137d84daa0fd13a709e448138e310d9ea93070620c9db5454e234af525/pickleshare-0.7.4.tar.gz";
1182 1182 md5 = "6a9e5dd8dfc023031f6b7b3f824cab12";
1183 1183 };
1184 1184 meta = {
1185 1185 license = [ pkgs.lib.licenses.mit ];
1186 1186 };
1187 1187 };
1188 1188 plaster = super.buildPythonPackage {
1189 1189 name = "plaster-1.0";
1190 1190 buildInputs = with self; [];
1191 1191 doCheck = false;
1192 1192 propagatedBuildInputs = with self; [setuptools];
1193 1193 src = fetchurl {
1194 1194 url = "https://pypi.python.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
1195 1195 md5 = "80e6beb4760c16fea31754babcc0576e";
1196 1196 };
1197 1197 meta = {
1198 1198 license = [ pkgs.lib.licenses.mit ];
1199 1199 };
1200 1200 };
1201 1201 plaster-pastedeploy = super.buildPythonPackage {
1202 1202 name = "plaster-pastedeploy-0.4.2";
1203 1203 buildInputs = with self; [];
1204 1204 doCheck = false;
1205 1205 propagatedBuildInputs = with self; [PasteDeploy plaster];
1206 1206 src = fetchurl {
1207 1207 url = "https://pypi.python.org/packages/2c/62/0daf9c0be958e785023e583e51baac15863699e956bfb3d448898d80edd8/plaster_pastedeploy-0.4.2.tar.gz";
1208 1208 md5 = "58fd7852002909378e818c9d5b71e90a";
1209 1209 };
1210 1210 meta = {
1211 1211 license = [ pkgs.lib.licenses.mit ];
1212 1212 };
1213 1213 };
1214 1214 prompt-toolkit = super.buildPythonPackage {
1215 1215 name = "prompt-toolkit-1.0.15";
1216 1216 buildInputs = with self; [];
1217 1217 doCheck = false;
1218 1218 propagatedBuildInputs = with self; [six wcwidth];
1219 1219 src = fetchurl {
1220 1220 url = "https://pypi.python.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz";
1221 1221 md5 = "8fe70295006dbc8afedd43e5eba99032";
1222 1222 };
1223 1223 meta = {
1224 1224 license = [ pkgs.lib.licenses.bsdOriginal ];
1225 1225 };
1226 1226 };
1227 1227 psutil = super.buildPythonPackage {
1228 name = "psutil-5.4.0";
1228 name = "psutil-5.4.3";
1229 1229 buildInputs = with self; [];
1230 1230 doCheck = false;
1231 1231 propagatedBuildInputs = with self; [];
1232 1232 src = fetchurl {
1233 url = "https://pypi.python.org/packages/8d/96/1fc6468be91521192861966c40bd73fdf8b065eae6d82dd0f870b9825a65/psutil-5.4.0.tar.gz";
1234 md5 = "01af6219b1e8fcfd53603023967713bf";
1233 url = "https://pypi.python.org/packages/e2/e1/600326635f97fee89bf8426fef14c5c29f4849c79f68fd79f433d8c1bd96/psutil-5.4.3.tar.gz";
1234 md5 = "3b291833dbea631db9d271aa602a169a";
1235 1235 };
1236 1236 meta = {
1237 1237 license = [ pkgs.lib.licenses.bsdOriginal ];
1238 1238 };
1239 1239 };
1240 1240 psycopg2 = super.buildPythonPackage {
1241 1241 name = "psycopg2-2.7.3.2";
1242 1242 buildInputs = with self; [];
1243 1243 doCheck = false;
1244 1244 propagatedBuildInputs = with self; [];
1245 1245 src = fetchurl {
1246 1246 url = "https://pypi.python.org/packages/dd/47/000b405d73ca22980684fd7bd3318690cc03cfa3b2ae1c5b7fff8050b28a/psycopg2-2.7.3.2.tar.gz";
1247 1247 md5 = "8114e672d5f23fa5329874a4314fbd6f";
1248 1248 };
1249 1249 meta = {
1250 1250 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1251 1251 };
1252 1252 };
1253 1253 ptyprocess = super.buildPythonPackage {
1254 1254 name = "ptyprocess-0.5.2";
1255 1255 buildInputs = with self; [];
1256 1256 doCheck = false;
1257 1257 propagatedBuildInputs = with self; [];
1258 1258 src = fetchurl {
1259 1259 url = "https://pypi.python.org/packages/51/83/5d07dc35534640b06f9d9f1a1d2bc2513fb9cc7595a1b0e28ae5477056ce/ptyprocess-0.5.2.tar.gz";
1260 1260 md5 = "d3b8febae1b8c53b054bd818d0bb8665";
1261 1261 };
1262 1262 meta = {
1263 1263 license = [ ];
1264 1264 };
1265 1265 };
1266 1266 py = super.buildPythonPackage {
1267 1267 name = "py-1.5.2";
1268 1268 buildInputs = with self; [];
1269 1269 doCheck = false;
1270 1270 propagatedBuildInputs = with self; [];
1271 1271 src = fetchurl {
1272 1272 url = "https://pypi.python.org/packages/90/e3/e075127d39d35f09a500ebb4a90afd10f9ef0a1d28a6d09abeec0e444fdd/py-1.5.2.tar.gz";
1273 1273 md5 = "279ca69c632069e1b71e11b14641ca28";
1274 1274 };
1275 1275 meta = {
1276 1276 license = [ pkgs.lib.licenses.mit ];
1277 1277 };
1278 1278 };
1279 1279 py-bcrypt = super.buildPythonPackage {
1280 1280 name = "py-bcrypt-0.4";
1281 1281 buildInputs = with self; [];
1282 1282 doCheck = false;
1283 1283 propagatedBuildInputs = with self; [];
1284 1284 src = fetchurl {
1285 1285 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1286 1286 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
1287 1287 };
1288 1288 meta = {
1289 1289 license = [ pkgs.lib.licenses.bsdOriginal ];
1290 1290 };
1291 1291 };
1292 1292 py-gfm = super.buildPythonPackage {
1293 1293 name = "py-gfm-0.1.3";
1294 1294 buildInputs = with self; [];
1295 1295 doCheck = false;
1296 1296 propagatedBuildInputs = with self; [setuptools Markdown];
1297 1297 src = fetchurl {
1298 1298 url = "https://pypi.python.org/packages/12/e4/6b3d8678da04f97d7490d8264d8de51c2dc9fb91209ccee9c515c95e14c5/py-gfm-0.1.3.tar.gz";
1299 1299 md5 = "e588d9e69640a241b97e2c59c22527a6";
1300 1300 };
1301 1301 meta = {
1302 1302 license = [ pkgs.lib.licenses.bsdOriginal ];
1303 1303 };
1304 1304 };
1305 1305 pycrypto = super.buildPythonPackage {
1306 1306 name = "pycrypto-2.6.1";
1307 1307 buildInputs = with self; [];
1308 1308 doCheck = false;
1309 1309 propagatedBuildInputs = with self; [];
1310 1310 src = fetchurl {
1311 1311 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1312 1312 md5 = "55a61a054aa66812daf5161a0d5d7eda";
1313 1313 };
1314 1314 meta = {
1315 1315 license = [ pkgs.lib.licenses.publicDomain ];
1316 1316 };
1317 1317 };
1318 1318 pycurl = super.buildPythonPackage {
1319 1319 name = "pycurl-7.19.5";
1320 1320 buildInputs = with self; [];
1321 1321 doCheck = false;
1322 1322 propagatedBuildInputs = with self; [];
1323 1323 src = fetchurl {
1324 1324 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
1325 1325 md5 = "47b4eac84118e2606658122104e62072";
1326 1326 };
1327 1327 meta = {
1328 1328 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1329 1329 };
1330 1330 };
1331 1331 pyflakes = super.buildPythonPackage {
1332 1332 name = "pyflakes-0.8.1";
1333 1333 buildInputs = with self; [];
1334 1334 doCheck = false;
1335 1335 propagatedBuildInputs = with self; [];
1336 1336 src = fetchurl {
1337 1337 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1338 1338 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
1339 1339 };
1340 1340 meta = {
1341 1341 license = [ pkgs.lib.licenses.mit ];
1342 1342 };
1343 1343 };
1344 1344 pygments-markdown-lexer = super.buildPythonPackage {
1345 1345 name = "pygments-markdown-lexer-0.1.0.dev39";
1346 1346 buildInputs = with self; [];
1347 1347 doCheck = false;
1348 1348 propagatedBuildInputs = with self; [Pygments];
1349 1349 src = fetchurl {
1350 1350 url = "https://pypi.python.org/packages/c3/12/674cdee66635d638cedb2c5d9c85ce507b7b2f91bdba29e482f1b1160ff6/pygments-markdown-lexer-0.1.0.dev39.zip";
1351 1351 md5 = "6360fe0f6d1f896e35b7a0142ce6459c";
1352 1352 };
1353 1353 meta = {
1354 1354 license = [ pkgs.lib.licenses.asl20 ];
1355 1355 };
1356 1356 };
1357 1357 pyparsing = super.buildPythonPackage {
1358 1358 name = "pyparsing-1.5.7";
1359 1359 buildInputs = with self; [];
1360 1360 doCheck = false;
1361 1361 propagatedBuildInputs = with self; [];
1362 1362 src = fetchurl {
1363 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
1364 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
1363 url = "https://pypi.python.org/packages/6f/2c/47457771c02a8ff0f302b695e094ec309e30452232bd79198ee94fda689f/pyparsing-1.5.7.tar.gz";
1364 md5 = "9be0fcdcc595199c646ab317c1d9a709";
1365 1365 };
1366 1366 meta = {
1367 1367 license = [ pkgs.lib.licenses.mit ];
1368 1368 };
1369 1369 };
1370 1370 pyramid = super.buildPythonPackage {
1371 1371 name = "pyramid-1.9.1";
1372 1372 buildInputs = with self; [];
1373 1373 doCheck = false;
1374 1374 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy plaster plaster-pastedeploy hupper];
1375 1375 src = fetchurl {
1376 1376 url = "https://pypi.python.org/packages/9a/57/73447be9e7d0512d601e3f0a1fb9d7d1efb941911f49efdfe036d2826507/pyramid-1.9.1.tar.gz";
1377 1377 md5 = "0163e19c58c2d12976a3b6fdb57e052d";
1378 1378 };
1379 1379 meta = {
1380 1380 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1381 1381 };
1382 1382 };
1383 1383 pyramid-beaker = super.buildPythonPackage {
1384 1384 name = "pyramid-beaker-0.8";
1385 1385 buildInputs = with self; [];
1386 1386 doCheck = false;
1387 1387 propagatedBuildInputs = with self; [pyramid Beaker];
1388 1388 src = fetchurl {
1389 1389 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1390 1390 md5 = "22f14be31b06549f80890e2c63a93834";
1391 1391 };
1392 1392 meta = {
1393 1393 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1394 1394 };
1395 1395 };
1396 1396 pyramid-debugtoolbar = super.buildPythonPackage {
1397 1397 name = "pyramid-debugtoolbar-4.3";
1398 1398 buildInputs = with self; [];
1399 1399 doCheck = false;
1400 1400 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments ipaddress];
1401 1401 src = fetchurl {
1402 1402 url = "https://pypi.python.org/packages/a4/40/f09d8800bfc3c09bdb6c95f37bb61c890dc62c19c4e7caa304da7aa77403/pyramid_debugtoolbar-4.3.tar.gz";
1403 1403 md5 = "9c49029e9f0695130499ef6416ffaaf8";
1404 1404 };
1405 1405 meta = {
1406 1406 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1407 1407 };
1408 1408 };
1409 1409 pyramid-jinja2 = super.buildPythonPackage {
1410 1410 name = "pyramid-jinja2-2.7";
1411 1411 buildInputs = with self; [];
1412 1412 doCheck = false;
1413 1413 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
1414 1414 src = fetchurl {
1415 1415 url = "https://pypi.python.org/packages/d8/80/d60a7233823de22ce77bd864a8a83736a1fe8b49884b08303a2e68b2c853/pyramid_jinja2-2.7.tar.gz";
1416 1416 md5 = "c2f8b2cd7b73a6f1d9a311fcfaf4fb92";
1417 1417 };
1418 1418 meta = {
1419 1419 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1420 1420 };
1421 1421 };
1422 1422 pyramid-mako = super.buildPythonPackage {
1423 1423 name = "pyramid-mako-1.0.2";
1424 1424 buildInputs = with self; [];
1425 1425 doCheck = false;
1426 1426 propagatedBuildInputs = with self; [pyramid Mako];
1427 1427 src = fetchurl {
1428 1428 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1429 1429 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
1430 1430 };
1431 1431 meta = {
1432 1432 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1433 1433 };
1434 1434 };
1435 1435 pysqlite = super.buildPythonPackage {
1436 1436 name = "pysqlite-2.8.3";
1437 1437 buildInputs = with self; [];
1438 1438 doCheck = false;
1439 1439 propagatedBuildInputs = with self; [];
1440 1440 src = fetchurl {
1441 1441 url = "https://pypi.python.org/packages/42/02/981b6703e3c83c5b25a829c6e77aad059f9481b0bbacb47e6e8ca12bd731/pysqlite-2.8.3.tar.gz";
1442 1442 md5 = "033f17b8644577715aee55e8832ac9fc";
1443 1443 };
1444 1444 meta = {
1445 1445 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1446 1446 };
1447 1447 };
1448 1448 pytest = super.buildPythonPackage {
1449 1449 name = "pytest-3.2.5";
1450 1450 buildInputs = with self; [];
1451 1451 doCheck = false;
1452 1452 propagatedBuildInputs = with self; [py setuptools];
1453 1453 src = fetchurl {
1454 1454 url = "https://pypi.python.org/packages/1f/f8/8cd74c16952163ce0db0bd95fdd8810cbf093c08be00e6e665ebf0dc3138/pytest-3.2.5.tar.gz";
1455 1455 md5 = "6dbe9bb093883f75394a689a1426ac6f";
1456 1456 };
1457 1457 meta = {
1458 1458 license = [ pkgs.lib.licenses.mit ];
1459 1459 };
1460 1460 };
1461 1461 pytest-catchlog = super.buildPythonPackage {
1462 1462 name = "pytest-catchlog-1.2.2";
1463 1463 buildInputs = with self; [];
1464 1464 doCheck = false;
1465 1465 propagatedBuildInputs = with self; [py pytest];
1466 1466 src = fetchurl {
1467 1467 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
1468 1468 md5 = "09d890c54c7456c818102b7ff8c182c8";
1469 1469 };
1470 1470 meta = {
1471 1471 license = [ pkgs.lib.licenses.mit ];
1472 1472 };
1473 1473 };
1474 1474 pytest-cov = super.buildPythonPackage {
1475 1475 name = "pytest-cov-2.5.1";
1476 1476 buildInputs = with self; [];
1477 1477 doCheck = false;
1478 1478 propagatedBuildInputs = with self; [pytest coverage];
1479 1479 src = fetchurl {
1480 1480 url = "https://pypi.python.org/packages/24/b4/7290d65b2f3633db51393bdf8ae66309b37620bc3ec116c5e357e3e37238/pytest-cov-2.5.1.tar.gz";
1481 1481 md5 = "5acf38d4909e19819eb5c1754fbfc0ac";
1482 1482 };
1483 1483 meta = {
1484 1484 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1485 1485 };
1486 1486 };
1487 1487 pytest-profiling = super.buildPythonPackage {
1488 1488 name = "pytest-profiling-1.2.11";
1489 1489 buildInputs = with self; [];
1490 1490 doCheck = false;
1491 1491 propagatedBuildInputs = with self; [six pytest gprof2dot];
1492 1492 src = fetchurl {
1493 1493 url = "https://pypi.python.org/packages/c0/4a/b4aa786e93c07a86f1f87c581a36bf355a9e06a9da7e00dbd05047626bd2/pytest-profiling-1.2.11.tar.gz";
1494 1494 md5 = "9ef6b60248731be5d44477980408e8f7";
1495 1495 };
1496 1496 meta = {
1497 1497 license = [ pkgs.lib.licenses.mit ];
1498 1498 };
1499 1499 };
1500 1500 pytest-runner = super.buildPythonPackage {
1501 1501 name = "pytest-runner-3.0";
1502 1502 buildInputs = with self; [];
1503 1503 doCheck = false;
1504 1504 propagatedBuildInputs = with self; [];
1505 1505 src = fetchurl {
1506 1506 url = "https://pypi.python.org/packages/65/b4/ae89338cd2d81e2cc54bd6db2e962bfe948f612303610d68ab24539ac2d1/pytest-runner-3.0.tar.gz";
1507 1507 md5 = "8f8363a52bbabc4cedd5e239beb2ba11";
1508 1508 };
1509 1509 meta = {
1510 1510 license = [ pkgs.lib.licenses.mit ];
1511 1511 };
1512 1512 };
1513 1513 pytest-sugar = super.buildPythonPackage {
1514 1514 name = "pytest-sugar-0.9.0";
1515 1515 buildInputs = with self; [];
1516 1516 doCheck = false;
1517 1517 propagatedBuildInputs = with self; [pytest termcolor];
1518 1518 src = fetchurl {
1519 1519 url = "https://pypi.python.org/packages/49/d8/c5ff6cca3ce2ebd8b73eec89779bf6b4a7737456a70e8ea4d44c1ff90f71/pytest-sugar-0.9.0.tar.gz";
1520 1520 md5 = "89fbff17277fa6a95a560a04b68cb9f9";
1521 1521 };
1522 1522 meta = {
1523 1523 license = [ pkgs.lib.licenses.bsdOriginal ];
1524 1524 };
1525 1525 };
1526 1526 pytest-timeout = super.buildPythonPackage {
1527 1527 name = "pytest-timeout-1.2.0";
1528 1528 buildInputs = with self; [];
1529 1529 doCheck = false;
1530 1530 propagatedBuildInputs = with self; [pytest];
1531 1531 src = fetchurl {
1532 1532 url = "https://pypi.python.org/packages/cc/b7/b2a61365ea6b6d2e8881360ae7ed8dad0327ad2df89f2f0be4a02304deb2/pytest-timeout-1.2.0.tar.gz";
1533 1533 md5 = "83607d91aa163562c7ee835da57d061d";
1534 1534 };
1535 1535 meta = {
1536 1536 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1537 1537 };
1538 1538 };
1539 1539 python-dateutil = super.buildPythonPackage {
1540 1540 name = "python-dateutil-2.6.1";
1541 1541 buildInputs = with self; [];
1542 1542 doCheck = false;
1543 1543 propagatedBuildInputs = with self; [six];
1544 1544 src = fetchurl {
1545 1545 url = "https://pypi.python.org/packages/54/bb/f1db86504f7a49e1d9b9301531181b00a1c7325dc85a29160ee3eaa73a54/python-dateutil-2.6.1.tar.gz";
1546 1546 md5 = "db38f6b4511cefd76014745bb0cc45a4";
1547 1547 };
1548 1548 meta = {
1549 1549 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "Simplified BSD"; } ];
1550 1550 };
1551 1551 };
1552 1552 python-editor = super.buildPythonPackage {
1553 1553 name = "python-editor-1.0.3";
1554 1554 buildInputs = with self; [];
1555 1555 doCheck = false;
1556 1556 propagatedBuildInputs = with self; [];
1557 1557 src = fetchurl {
1558 1558 url = "https://pypi.python.org/packages/65/1e/adf6e000ea5dc909aa420352d6ba37f16434c8a3c2fa030445411a1ed545/python-editor-1.0.3.tar.gz";
1559 1559 md5 = "0aca5f2ef176ce68e98a5b7e31372835";
1560 1560 };
1561 1561 meta = {
1562 1562 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1563 1563 };
1564 1564 };
1565 1565 python-ldap = super.buildPythonPackage {
1566 1566 name = "python-ldap-2.4.45";
1567 1567 buildInputs = with self; [];
1568 1568 doCheck = false;
1569 1569 propagatedBuildInputs = with self; [setuptools];
1570 1570 src = fetchurl {
1571 1571 url = "https://pypi.python.org/packages/ce/52/6b5372d0166820f4a4b0a88ed73dc7504219355049fc1d266d8ccdb7942e/python-ldap-2.4.45.tar.gz";
1572 1572 md5 = "6108e189a44eea8bc7d1cc281c222978";
1573 1573 };
1574 1574 meta = {
1575 1575 license = [ pkgs.lib.licenses.psfl ];
1576 1576 };
1577 1577 };
1578 1578 python-memcached = super.buildPythonPackage {
1579 1579 name = "python-memcached-1.58";
1580 1580 buildInputs = with self; [];
1581 1581 doCheck = false;
1582 1582 propagatedBuildInputs = with self; [six];
1583 1583 src = fetchurl {
1584 1584 url = "https://pypi.python.org/packages/f7/62/14b2448cfb04427366f24104c9da97cf8ea380d7258a3233f066a951a8d8/python-memcached-1.58.tar.gz";
1585 1585 md5 = "23b258105013d14d899828d334e6b044";
1586 1586 };
1587 1587 meta = {
1588 1588 license = [ pkgs.lib.licenses.psfl ];
1589 1589 };
1590 1590 };
1591 1591 python-pam = super.buildPythonPackage {
1592 1592 name = "python-pam-1.8.2";
1593 1593 buildInputs = with self; [];
1594 1594 doCheck = false;
1595 1595 propagatedBuildInputs = with self; [];
1596 1596 src = fetchurl {
1597 1597 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
1598 1598 md5 = "db71b6b999246fb05d78ecfbe166629d";
1599 1599 };
1600 1600 meta = {
1601 1601 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1602 1602 };
1603 1603 };
1604 1604 pytz = super.buildPythonPackage {
1605 name = "pytz-2017.3";
1605 name = "pytz-2018.3";
1606 1606 buildInputs = with self; [];
1607 1607 doCheck = false;
1608 1608 propagatedBuildInputs = with self; [];
1609 1609 src = fetchurl {
1610 url = "https://pypi.python.org/packages/60/88/d3152c234da4b2a1f7a989f89609ea488225eaea015bc16fbde2b3fdfefa/pytz-2017.3.zip";
1611 md5 = "7006b56c0d68a162d9fe57d4249c3171";
1610 url = "https://pypi.python.org/packages/1b/50/4cdc62fc0753595fc16c8f722a89740f487c6e5670c644eb8983946777be/pytz-2018.3.tar.gz";
1611 md5 = "abb07c09c79f78d7c04f222a550c99ef";
1612 1612 };
1613 1613 meta = {
1614 1614 license = [ pkgs.lib.licenses.mit ];
1615 1615 };
1616 1616 };
1617 1617 pyzmq = super.buildPythonPackage {
1618 1618 name = "pyzmq-14.6.0";
1619 1619 buildInputs = with self; [];
1620 1620 doCheck = false;
1621 1621 propagatedBuildInputs = with self; [];
1622 1622 src = fetchurl {
1623 1623 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1624 1624 md5 = "395b5de95a931afa5b14c9349a5b8024";
1625 1625 };
1626 1626 meta = {
1627 1627 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1628 1628 };
1629 1629 };
1630 1630 recaptcha-client = super.buildPythonPackage {
1631 1631 name = "recaptcha-client-1.0.6";
1632 1632 buildInputs = with self; [];
1633 1633 doCheck = false;
1634 1634 propagatedBuildInputs = with self; [];
1635 1635 src = fetchurl {
1636 1636 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1637 1637 md5 = "74228180f7e1fb76c4d7089160b0d919";
1638 1638 };
1639 1639 meta = {
1640 1640 license = [ { fullName = "MIT/X11"; } ];
1641 1641 };
1642 1642 };
1643 1643 redis = super.buildPythonPackage {
1644 1644 name = "redis-2.10.6";
1645 1645 buildInputs = with self; [];
1646 1646 doCheck = false;
1647 1647 propagatedBuildInputs = with self; [];
1648 1648 src = fetchurl {
1649 1649 url = "https://pypi.python.org/packages/09/8d/6d34b75326bf96d4139a2ddd8e74b80840f800a0a79f9294399e212cb9a7/redis-2.10.6.tar.gz";
1650 1650 md5 = "048348d8cfe0b5d0bba2f4d835005c3b";
1651 1651 };
1652 1652 meta = {
1653 1653 license = [ pkgs.lib.licenses.mit ];
1654 1654 };
1655 1655 };
1656 1656 repoze.lru = super.buildPythonPackage {
1657 1657 name = "repoze.lru-0.7";
1658 1658 buildInputs = with self; [];
1659 1659 doCheck = false;
1660 1660 propagatedBuildInputs = with self; [];
1661 1661 src = fetchurl {
1662 1662 url = "https://pypi.python.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
1663 1663 md5 = "c08cc030387e0b1fc53c5c7d964b35e2";
1664 1664 };
1665 1665 meta = {
1666 1666 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1667 1667 };
1668 1668 };
1669 1669 requests = super.buildPythonPackage {
1670 1670 name = "requests-2.9.1";
1671 1671 buildInputs = with self; [];
1672 1672 doCheck = false;
1673 1673 propagatedBuildInputs = with self; [];
1674 1674 src = fetchurl {
1675 1675 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1676 1676 md5 = "0b7f480d19012ec52bab78292efd976d";
1677 1677 };
1678 1678 meta = {
1679 1679 license = [ pkgs.lib.licenses.asl20 ];
1680 1680 };
1681 1681 };
1682 1682 rhodecode-enterprise-ce = super.buildPythonPackage {
1683 name = "rhodecode-enterprise-ce-4.11.6";
1683 name = "rhodecode-enterprise-ce-4.12.0";
1684 1684 buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj];
1685 1685 doCheck = true;
1686 propagatedBuildInputs = with self; [setuptools-scm amqp authomatic Babel Beaker celery Chameleon channelstream click colander configobj cssselect decorator deform docutils dogpile.cache dogpile.core ecdsa FormEncode future futures gnureadline infrae.cache iso8601 itsdangerous Jinja2 billiard kombu lxml Mako Markdown MarkupSafe msgpack-python MySQL-python objgraph packaging Paste PasteDeploy PasteScript pathlib2 peppercorn psutil psycopg2 py-bcrypt pycrypto pycurl pyflakes pygments-markdown-lexer Pygments pyparsing pyramid-beaker pyramid-debugtoolbar pyramid-jinja2 pyramid-mako pyramid pysqlite python-dateutil python-ldap python-memcached python-pam pytz pyzmq py-gfm recaptcha-client redis repoze.lru requests Routes setproctitle simplejson six SQLAlchemy sshpubkeys subprocess32 supervisor Tempita translationstring trollius urllib3 URLObject venusian WebError WebHelpers2 WebHelpers WebOb Whoosh wsgiref zope.cachedescriptors zope.deprecation zope.event zope.interface nbconvert bleach nbformat jupyter-client alembic invoke bumpversion transifex-client gevent greenlet gunicorn waitress uWSGI ipdb ipython CProfileV bottle rhodecode-tools appenlight-client pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage];
1686 propagatedBuildInputs = with self; [setuptools-scm amqp authomatic Babel Beaker celery Chameleon channelstream click colander configobj cssselect decorator deform docutils dogpile.cache dogpile.core ecdsa FormEncode future futures gnureadline infrae.cache iso8601 itsdangerous Jinja2 billiard kombu lxml Mako Markdown MarkupSafe msgpack-python MySQL-python objgraph packaging Paste PasteDeploy PasteScript pathlib2 peppercorn psutil psycopg2 py-bcrypt pycrypto pycurl pyflakes pygments-markdown-lexer Pygments pyparsing pyramid-beaker pyramid-debugtoolbar pyramid-jinja2 pyramid-mako pyramid pysqlite python-dateutil python-ldap python-memcached python-pam pytz tzlocal pyzmq py-gfm recaptcha-client redis repoze.lru requests Routes setproctitle simplejson six SQLAlchemy sshpubkeys subprocess32 supervisor Tempita translationstring trollius urllib3 URLObject venusian WebError WebHelpers2 WebHelpers WebOb Whoosh wsgiref zope.cachedescriptors zope.deprecation zope.event zope.interface nbconvert bleach nbformat jupyter-client alembic invoke bumpversion transifex-client gevent greenlet gunicorn waitress uWSGI ipdb ipython CProfileV bottle rhodecode-tools appenlight-client pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage];
1687 1687 src = ./.;
1688 1688 meta = {
1689 1689 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
1690 1690 };
1691 1691 };
1692 1692 rhodecode-tools = super.buildPythonPackage {
1693 1693 name = "rhodecode-tools-0.14.1";
1694 1694 buildInputs = with self; [];
1695 1695 doCheck = false;
1696 1696 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests elasticsearch elasticsearch-dsl urllib3 Whoosh];
1697 1697 src = fetchurl {
1698 1698 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.14.1.tar.gz?md5=0b9c2caad160b68889f8172ea54af7b2";
1699 1699 md5 = "0b9c2caad160b68889f8172ea54af7b2";
1700 1700 };
1701 1701 meta = {
1702 1702 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1703 1703 };
1704 1704 };
1705 1705 scandir = super.buildPythonPackage {
1706 name = "scandir-1.6";
1706 name = "scandir-1.7";
1707 1707 buildInputs = with self; [];
1708 1708 doCheck = false;
1709 1709 propagatedBuildInputs = with self; [];
1710 1710 src = fetchurl {
1711 url = "https://pypi.python.org/packages/77/3f/916f524f50ee65e3f465a280d2851bd63685250fddb3020c212b3977664d/scandir-1.6.tar.gz";
1712 md5 = "0180ddb97c96cbb2d4f25d2ae11c64ac";
1711 url = "https://pypi.python.org/packages/13/bb/e541b74230bbf7a20a3949a2ee6631be299378a784f5445aa5d0047c192b/scandir-1.7.tar.gz";
1712 md5 = "037e5f24d1a0e78b17faca72dea9555f";
1713 1713 };
1714 1714 meta = {
1715 1715 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
1716 1716 };
1717 1717 };
1718 1718 setproctitle = super.buildPythonPackage {
1719 1719 name = "setproctitle-1.1.10";
1720 1720 buildInputs = with self; [];
1721 1721 doCheck = false;
1722 1722 propagatedBuildInputs = with self; [];
1723 1723 src = fetchurl {
1724 1724 url = "https://pypi.python.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
1725 1725 md5 = "2dcdd1b761700a5a13252fea3dfd1977";
1726 1726 };
1727 1727 meta = {
1728 1728 license = [ pkgs.lib.licenses.bsdOriginal ];
1729 1729 };
1730 1730 };
1731 1731 setuptools = super.buildPythonPackage {
1732 1732 name = "setuptools-30.1.0";
1733 1733 buildInputs = with self; [];
1734 1734 doCheck = false;
1735 1735 propagatedBuildInputs = with self; [];
1736 1736 src = fetchurl {
1737 1737 url = "https://pypi.python.org/packages/1e/43/002c8616db9a3e7be23c2556e39b90a32bb40ba0dc652de1999d5334d372/setuptools-30.1.0.tar.gz";
1738 1738 md5 = "cac497f42e5096ac8df29e38d3f81c3e";
1739 1739 };
1740 1740 meta = {
1741 1741 license = [ pkgs.lib.licenses.mit ];
1742 1742 };
1743 1743 };
1744 1744 setuptools-scm = super.buildPythonPackage {
1745 1745 name = "setuptools-scm-1.15.6";
1746 1746 buildInputs = with self; [];
1747 1747 doCheck = false;
1748 1748 propagatedBuildInputs = with self; [];
1749 1749 src = fetchurl {
1750 1750 url = "https://pypi.python.org/packages/03/6d/aafdd01edd227ee879b691455bf19895091872af7e48192bea1758c82032/setuptools_scm-1.15.6.tar.gz";
1751 1751 md5 = "f17493d53f0d842bb0152f214775640b";
1752 1752 };
1753 1753 meta = {
1754 1754 license = [ pkgs.lib.licenses.mit ];
1755 1755 };
1756 1756 };
1757 1757 simplegeneric = super.buildPythonPackage {
1758 1758 name = "simplegeneric-0.8.1";
1759 1759 buildInputs = with self; [];
1760 1760 doCheck = false;
1761 1761 propagatedBuildInputs = with self; [];
1762 1762 src = fetchurl {
1763 1763 url = "https://pypi.python.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
1764 1764 md5 = "f9c1fab00fd981be588fc32759f474e3";
1765 1765 };
1766 1766 meta = {
1767 1767 license = [ pkgs.lib.licenses.zpt21 ];
1768 1768 };
1769 1769 };
1770 1770 simplejson = super.buildPythonPackage {
1771 1771 name = "simplejson-3.11.1";
1772 1772 buildInputs = with self; [];
1773 1773 doCheck = false;
1774 1774 propagatedBuildInputs = with self; [];
1775 1775 src = fetchurl {
1776 1776 url = "https://pypi.python.org/packages/08/48/c97b668d6da7d7bebe7ea1817a6f76394b0ec959cb04214ca833c34359df/simplejson-3.11.1.tar.gz";
1777 1777 md5 = "6e2f1bd5fb0a926facf5d89d217a7183";
1778 1778 };
1779 1779 meta = {
1780 1780 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
1781 1781 };
1782 1782 };
1783 1783 six = super.buildPythonPackage {
1784 1784 name = "six-1.11.0";
1785 1785 buildInputs = with self; [];
1786 1786 doCheck = false;
1787 1787 propagatedBuildInputs = with self; [];
1788 1788 src = fetchurl {
1789 1789 url = "https://pypi.python.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
1790 1790 md5 = "d12789f9baf7e9fb2524c0c64f1773f8";
1791 1791 };
1792 1792 meta = {
1793 1793 license = [ pkgs.lib.licenses.mit ];
1794 1794 };
1795 1795 };
1796 1796 sshpubkeys = super.buildPythonPackage {
1797 1797 name = "sshpubkeys-2.2.0";
1798 1798 buildInputs = with self; [];
1799 1799 doCheck = false;
1800 1800 propagatedBuildInputs = with self; [pycrypto ecdsa];
1801 1801 src = fetchurl {
1802 1802 url = "https://pypi.python.org/packages/27/da/337fabeb3dca6b62039a93ceaa636f25065e0ae92b575b1235342076cf0a/sshpubkeys-2.2.0.tar.gz";
1803 1803 md5 = "458e45f6b92b1afa84f0ffe1f1c90935";
1804 1804 };
1805 1805 meta = {
1806 1806 license = [ pkgs.lib.licenses.bsdOriginal ];
1807 1807 };
1808 1808 };
1809 1809 subprocess32 = super.buildPythonPackage {
1810 1810 name = "subprocess32-3.2.7";
1811 1811 buildInputs = with self; [];
1812 1812 doCheck = false;
1813 1813 propagatedBuildInputs = with self; [];
1814 1814 src = fetchurl {
1815 1815 url = "https://pypi.python.org/packages/b8/2f/49e53b0d0e94611a2dc624a1ad24d41b6d94d0f1b0a078443407ea2214c2/subprocess32-3.2.7.tar.gz";
1816 1816 md5 = "824c801e479d3e916879aae3e9c15e16";
1817 1817 };
1818 1818 meta = {
1819 1819 license = [ pkgs.lib.licenses.psfl ];
1820 1820 };
1821 1821 };
1822 1822 supervisor = super.buildPythonPackage {
1823 name = "supervisor-3.3.3";
1823 name = "supervisor-3.3.4";
1824 1824 buildInputs = with self; [];
1825 1825 doCheck = false;
1826 1826 propagatedBuildInputs = with self; [meld3];
1827 1827 src = fetchurl {
1828 url = "https://pypi.python.org/packages/31/7e/788fc6566211e77c395ea272058eb71299c65cc5e55b6214d479c6c2ec9a/supervisor-3.3.3.tar.gz";
1829 md5 = "0fe86dfec4e5c5d98324d24c4cf944bd";
1828 url = "https://pypi.python.org/packages/44/60/698e54b4a4a9b956b2d709b4b7b676119c833d811d53ee2500f1b5e96dc3/supervisor-3.3.4.tar.gz";
1829 md5 = "f1814d71d820ddfa8c86d46a72314cec";
1830 1830 };
1831 1831 meta = {
1832 1832 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1833 1833 };
1834 1834 };
1835 1835 termcolor = super.buildPythonPackage {
1836 1836 name = "termcolor-1.1.0";
1837 1837 buildInputs = with self; [];
1838 1838 doCheck = false;
1839 1839 propagatedBuildInputs = with self; [];
1840 1840 src = fetchurl {
1841 1841 url = "https://pypi.python.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
1842 1842 md5 = "043e89644f8909d462fbbfa511c768df";
1843 1843 };
1844 1844 meta = {
1845 1845 license = [ pkgs.lib.licenses.mit ];
1846 1846 };
1847 1847 };
1848 1848 testpath = super.buildPythonPackage {
1849 1849 name = "testpath-0.3.1";
1850 1850 buildInputs = with self; [];
1851 1851 doCheck = false;
1852 1852 propagatedBuildInputs = with self; [];
1853 1853 src = fetchurl {
1854 1854 url = "https://pypi.python.org/packages/f4/8b/b71e9ee10e5f751e9d959bc750ab122ba04187f5aa52aabdc4e63b0e31a7/testpath-0.3.1.tar.gz";
1855 1855 md5 = "2cd5ed5522fda781bb497c9d80ae2fc9";
1856 1856 };
1857 1857 meta = {
1858 1858 license = [ pkgs.lib.licenses.mit ];
1859 1859 };
1860 1860 };
1861 1861 traitlets = super.buildPythonPackage {
1862 1862 name = "traitlets-4.3.2";
1863 1863 buildInputs = with self; [];
1864 1864 doCheck = false;
1865 1865 propagatedBuildInputs = with self; [ipython-genutils six decorator enum34];
1866 1866 src = fetchurl {
1867 1867 url = "https://pypi.python.org/packages/a5/98/7f5ef2fe9e9e071813aaf9cb91d1a732e0a68b6c44a32b38cb8e14c3f069/traitlets-4.3.2.tar.gz";
1868 1868 md5 = "3068663f2f38fd939a9eb3a500ccc154";
1869 1869 };
1870 1870 meta = {
1871 1871 license = [ pkgs.lib.licenses.bsdOriginal ];
1872 1872 };
1873 1873 };
1874 1874 transifex-client = super.buildPythonPackage {
1875 1875 name = "transifex-client-0.12.5";
1876 1876 buildInputs = with self; [];
1877 1877 doCheck = false;
1878 1878 propagatedBuildInputs = with self; [urllib3 six];
1879 1879 src = fetchurl {
1880 1880 url = "https://pypi.python.org/packages/7b/86/60f31a0c9b8d0b1266ce15b6c80b55f88522140c8acfc395d5aec5e23475/transifex-client-0.12.5.tar.gz";
1881 1881 md5 = "e6e278117b23f60702c06e203b7e51ae";
1882 1882 };
1883 1883 meta = {
1884 1884 license = [ pkgs.lib.licenses.gpl2 ];
1885 1885 };
1886 1886 };
1887 1887 translationstring = super.buildPythonPackage {
1888 1888 name = "translationstring-1.3";
1889 1889 buildInputs = with self; [];
1890 1890 doCheck = false;
1891 1891 propagatedBuildInputs = with self; [];
1892 1892 src = fetchurl {
1893 1893 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1894 1894 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1895 1895 };
1896 1896 meta = {
1897 1897 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
1898 1898 };
1899 1899 };
1900 1900 trollius = super.buildPythonPackage {
1901 1901 name = "trollius-1.0.4";
1902 1902 buildInputs = with self; [];
1903 1903 doCheck = false;
1904 1904 propagatedBuildInputs = with self; [futures];
1905 1905 src = fetchurl {
1906 1906 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1907 1907 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1908 1908 };
1909 1909 meta = {
1910 1910 license = [ pkgs.lib.licenses.asl20 ];
1911 1911 };
1912 1912 };
1913 tzlocal = super.buildPythonPackage {
1914 name = "tzlocal-1.5.1";
1915 buildInputs = with self; [];
1916 doCheck = false;
1917 propagatedBuildInputs = with self; [pytz];
1918 src = fetchurl {
1919 url = "https://pypi.python.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz";
1920 md5 = "4553be891efa0812c4adfb0c6e818eec";
1921 };
1922 meta = {
1923 license = [ pkgs.lib.licenses.mit ];
1924 };
1925 };
1913 1926 uWSGI = super.buildPythonPackage {
1914 1927 name = "uWSGI-2.0.15";
1915 1928 buildInputs = with self; [];
1916 1929 doCheck = false;
1917 1930 propagatedBuildInputs = with self; [];
1918 1931 src = fetchurl {
1919 1932 url = "https://pypi.python.org/packages/bb/0a/45e5aa80dc135889594bb371c082d20fb7ee7303b174874c996888cc8511/uwsgi-2.0.15.tar.gz";
1920 1933 md5 = "fc50bd9e83b7602fa474b032167010a7";
1921 1934 };
1922 1935 meta = {
1923 1936 license = [ pkgs.lib.licenses.gpl2 ];
1924 1937 };
1925 1938 };
1926 1939 urllib3 = super.buildPythonPackage {
1927 1940 name = "urllib3-1.16";
1928 1941 buildInputs = with self; [];
1929 1942 doCheck = false;
1930 1943 propagatedBuildInputs = with self; [];
1931 1944 src = fetchurl {
1932 1945 url = "https://pypi.python.org/packages/3b/f0/e763169124e3f5db0926bc3dbfcd580a105f9ca44cf5d8e6c7a803c9f6b5/urllib3-1.16.tar.gz";
1933 1946 md5 = "fcaab1c5385c57deeb7053d3d7d81d59";
1934 1947 };
1935 1948 meta = {
1936 1949 license = [ pkgs.lib.licenses.mit ];
1937 1950 };
1938 1951 };
1939 1952 venusian = super.buildPythonPackage {
1940 1953 name = "venusian-1.1.0";
1941 1954 buildInputs = with self; [];
1942 1955 doCheck = false;
1943 1956 propagatedBuildInputs = with self; [];
1944 1957 src = fetchurl {
1945 1958 url = "https://pypi.python.org/packages/38/24/b4b470ab9e0a2e2e9b9030c7735828c8934b4c6b45befd1bb713ec2aeb2d/venusian-1.1.0.tar.gz";
1946 1959 md5 = "56bc5e6756e4bda37bcdb94f74a72b8f";
1947 1960 };
1948 1961 meta = {
1949 1962 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1950 1963 };
1951 1964 };
1952 1965 vine = super.buildPythonPackage {
1953 1966 name = "vine-1.1.4";
1954 1967 buildInputs = with self; [];
1955 1968 doCheck = false;
1956 1969 propagatedBuildInputs = with self; [];
1957 1970 src = fetchurl {
1958 1971 url = "https://pypi.python.org/packages/32/23/36284986e011f3c130d802c3c66abd8f1aef371eae110ddf80c5ae22e1ff/vine-1.1.4.tar.gz";
1959 1972 md5 = "9fdb971e7fd15b181b84f3bfcf20d11c";
1960 1973 };
1961 1974 meta = {
1962 1975 license = [ pkgs.lib.licenses.bsdOriginal ];
1963 1976 };
1964 1977 };
1965 1978 waitress = super.buildPythonPackage {
1966 1979 name = "waitress-1.1.0";
1967 1980 buildInputs = with self; [];
1968 1981 doCheck = false;
1969 1982 propagatedBuildInputs = with self; [];
1970 1983 src = fetchurl {
1971 1984 url = "https://pypi.python.org/packages/3c/68/1c10dd5c556872ceebe88483b0436140048d39de83a84a06a8baa8136f4f/waitress-1.1.0.tar.gz";
1972 1985 md5 = "0f1eb7fdfdbf2e6d18decbda1733045c";
1973 1986 };
1974 1987 meta = {
1975 1988 license = [ pkgs.lib.licenses.zpt21 ];
1976 1989 };
1977 1990 };
1978 1991 wcwidth = super.buildPythonPackage {
1979 1992 name = "wcwidth-0.1.7";
1980 1993 buildInputs = with self; [];
1981 1994 doCheck = false;
1982 1995 propagatedBuildInputs = with self; [];
1983 1996 src = fetchurl {
1984 1997 url = "https://pypi.python.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
1985 1998 md5 = "b3b6a0a08f0c8a34d1de8cf44150a4ad";
1986 1999 };
1987 2000 meta = {
1988 2001 license = [ pkgs.lib.licenses.mit ];
1989 2002 };
1990 2003 };
1991 2004 webencodings = super.buildPythonPackage {
1992 2005 name = "webencodings-0.5.1";
1993 2006 buildInputs = with self; [];
1994 2007 doCheck = false;
1995 2008 propagatedBuildInputs = with self; [];
1996 2009 src = fetchurl {
1997 2010 url = "https://pypi.python.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz";
1998 2011 md5 = "32f6e261d52e57bf7e1c4d41546d15b8";
1999 2012 };
2000 2013 meta = {
2001 2014 license = [ pkgs.lib.licenses.bsdOriginal ];
2002 2015 };
2003 2016 };
2004 2017 ws4py = super.buildPythonPackage {
2005 name = "ws4py-0.4.2";
2018 name = "ws4py-0.4.3";
2006 2019 buildInputs = with self; [];
2007 2020 doCheck = false;
2008 2021 propagatedBuildInputs = with self; [];
2009 2022 src = fetchurl {
2010 url = "https://pypi.python.org/packages/b8/98/a90f1d96ffcb15dfc220af524ce23e0a5881258dafa197673357ce1683dd/ws4py-0.4.2.tar.gz";
2011 md5 = "f0603ae376707a58d205bd87a67758a2";
2023 url = "https://pypi.python.org/packages/fa/a1/33c43a4304ac3b4dc81deb93cbd329de9297dd034d75c47cce64fda806bc/ws4py-0.4.3.tar.gz";
2024 md5 = "d5834cf7d3965bb0da31bbb02bd8513a";
2012 2025 };
2013 2026 meta = {
2014 2027 license = [ pkgs.lib.licenses.bsdOriginal ];
2015 2028 };
2016 2029 };
2017 2030 wsgiref = super.buildPythonPackage {
2018 2031 name = "wsgiref-0.1.2";
2019 2032 buildInputs = with self; [];
2020 2033 doCheck = false;
2021 2034 propagatedBuildInputs = with self; [];
2022 2035 src = fetchurl {
2023 2036 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
2024 2037 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
2025 2038 };
2026 2039 meta = {
2027 2040 license = [ { fullName = "PSF or ZPL"; } ];
2028 2041 };
2029 2042 };
2030 2043 zope.cachedescriptors = super.buildPythonPackage {
2031 name = "zope.cachedescriptors-4.0.0";
2044 name = "zope.cachedescriptors-4.3.1";
2032 2045 buildInputs = with self; [];
2033 2046 doCheck = false;
2034 2047 propagatedBuildInputs = with self; [setuptools];
2035 2048 src = fetchurl {
2036 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
2037 md5 = "8d308de8c936792c8e758058fcb7d0f0";
2049 url = "https://pypi.python.org/packages/2f/89/ebe1890cc6d3291ebc935558fa764d5fffe571018dbbee200e9db78762cb/zope.cachedescriptors-4.3.1.tar.gz";
2050 md5 = "42f3693f43bc93f3b1eb86940f58acf3";
2038 2051 };
2039 2052 meta = {
2040 2053 license = [ pkgs.lib.licenses.zpt21 ];
2041 2054 };
2042 2055 };
2043 2056 zope.deprecation = super.buildPythonPackage {
2044 name = "zope.deprecation-4.1.2";
2057 name = "zope.deprecation-4.3.0";
2045 2058 buildInputs = with self; [];
2046 2059 doCheck = false;
2047 2060 propagatedBuildInputs = with self; [setuptools];
2048 2061 src = fetchurl {
2049 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
2050 md5 = "e9a663ded58f4f9f7881beb56cae2782";
2062 url = "https://pypi.python.org/packages/a1/18/2dc5e6bfe64fdc3b79411b67464c55bb0b43b127051a20f7f492ab767758/zope.deprecation-4.3.0.tar.gz";
2063 md5 = "2166b2cb7e0e96a21104e6f8f9b696bb";
2051 2064 };
2052 2065 meta = {
2053 2066 license = [ pkgs.lib.licenses.zpt21 ];
2054 2067 };
2055 2068 };
2056 2069 zope.event = super.buildPythonPackage {
2057 name = "zope.event-4.0.3";
2070 name = "zope.event-4.3.0";
2058 2071 buildInputs = with self; [];
2059 2072 doCheck = false;
2060 2073 propagatedBuildInputs = with self; [setuptools];
2061 2074 src = fetchurl {
2062 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
2063 md5 = "9a3780916332b18b8b85f522bcc3e249";
2075 url = "https://pypi.python.org/packages/9e/d0/54ba59f19a0635f6591b74be259cf6fbf67e73f4edda27b5cd0cf4d26efa/zope.event-4.3.0.tar.gz";
2076 md5 = "8ca737960741c6fd112972f3313303bd";
2064 2077 };
2065 2078 meta = {
2066 2079 license = [ pkgs.lib.licenses.zpt21 ];
2067 2080 };
2068 2081 };
2069 2082 zope.interface = super.buildPythonPackage {
2070 name = "zope.interface-4.1.3";
2083 name = "zope.interface-4.4.3";
2071 2084 buildInputs = with self; [];
2072 2085 doCheck = false;
2073 2086 propagatedBuildInputs = with self; [setuptools];
2074 2087 src = fetchurl {
2075 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
2076 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
2088 url = "https://pypi.python.org/packages/bd/d2/25349ed41f9dcff7b3baf87bd88a4c82396cf6e02f1f42bb68657a3132af/zope.interface-4.4.3.tar.gz";
2089 md5 = "8700a4f527c1203b34b10c2b4e7a6912";
2077 2090 };
2078 2091 meta = {
2079 2092 license = [ pkgs.lib.licenses.zpt21 ];
2080 2093 };
2081 2094 };
2082 2095
2083 2096 ### Test requirements
2084 2097
2085 2098
2086 2099 }
@@ -1,131 +1,132 b''
1 1 ## core
2 2 setuptools==30.1.0
3 3 setuptools-scm==1.15.6
4 4
5 5 amqp==2.2.2
6 6 authomatic==0.1.0.post1
7 7 Babel==1.3
8 Beaker==1.9.0
8 Beaker==1.9.1
9 9 celery==4.1.0
10 10 Chameleon==2.24
11 11 channelstream==0.5.2
12 12 click==6.6
13 13 colander==1.4.0
14 14 configobj==5.0.6
15 15 cssselect==1.0.1
16 16 decorator==4.1.2
17 17 deform==2.0.4
18 18 docutils==0.14.0
19 19 dogpile.cache==0.6.4
20 20 dogpile.core==0.4.1
21 21 ecdsa==0.13
22 22 FormEncode==1.2.4
23 23 future==0.14.3
24 24 futures==3.0.2
25 25 gnureadline==6.3.8
26 26 infrae.cache==1.0.1
27 27 iso8601==0.1.12
28 28 itsdangerous==0.24
29 29 Jinja2==2.9.6
30 30 billiard==3.5.0.3
31 31 kombu==4.1.0
32 32 lxml==3.7.3
33 33 Mako==1.0.7
34 Markdown==2.6.9
34 Markdown==2.6.11
35 35 MarkupSafe==1.0.0
36 36 msgpack-python==0.4.8
37 37 MySQL-python==1.2.5
38 38 objgraph==3.1.1
39 39 packaging==15.2
40 40 Paste==2.0.3
41 41 PasteDeploy==1.5.2
42 42 PasteScript==2.0.2
43 43 pathlib2==2.3.0
44 44 peppercorn==0.5
45 psutil==5.4.0
45 psutil==5.4.3
46 46 psycopg2==2.7.3.2
47 47 py-bcrypt==0.4
48 48 pycrypto==2.6.1
49 49 pycurl==7.19.5
50 50 pyflakes==0.8.1
51 51 pygments-markdown-lexer==0.1.0.dev39
52 52 Pygments==2.2.0
53 53 pyparsing==1.5.7
54 54 pyramid-beaker==0.8
55 55 pyramid-debugtoolbar==4.3.0
56 56 pyramid-jinja2==2.7
57 57 pyramid-mako==1.0.2
58 58 pyramid==1.9.1
59 59 pysqlite==2.8.3
60 60 python-dateutil
61 61 python-ldap==2.4.45
62 62 python-memcached==1.58
63 63 python-pam==1.8.2
64 pytz==2017.3
64 pytz==2018.3
65 tzlocal==1.5.1
65 66 pyzmq==14.6.0
66 67 py-gfm==0.1.3
67 68 recaptcha-client==1.0.6
68 69 redis==2.10.6
69 70 repoze.lru==0.7
70 71 requests==2.9.1
71 72 Routes==2.4.1
72 73 setproctitle==1.1.10
73 74 simplejson==3.11.1
74 75 six==1.11.0
75 76 SQLAlchemy==1.1.15
76 77 sshpubkeys==2.2.0
77 78 subprocess32==3.2.7
78 supervisor==3.3.3
79 supervisor==3.3.4
79 80 Tempita==0.5.2
80 81 translationstring==1.3
81 82 trollius==1.0.4
82 83 urllib3==1.16
83 84 URLObject==2.4.0
84 85 venusian==1.1.0
85 86 WebError==0.10.3
86 87 WebHelpers2==2.0
87 88 WebHelpers==1.3
88 89 WebOb==1.7.4
89 90 Whoosh==2.7.4
90 91 wsgiref==0.1.2
91 zope.cachedescriptors==4.0.0
92 zope.deprecation==4.1.2
93 zope.event==4.0.3
94 zope.interface==4.1.3
92 zope.cachedescriptors==4.3.1
93 zope.deprecation==4.3.0
94 zope.event==4.3.0
95 zope.interface==4.4.3
95 96
96 97
97 98 # IPYTHON RENDERING
98 99 # entrypoints backport, pypi version doesn't support egg installs
99 100 https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313#egg=entrypoints==0.2.2.rhodecode-upstream1
100 101 nbconvert==5.3.1
101 bleach==2.1.1
102 bleach==2.1.2
102 103 nbformat==4.4.0
103 104 jupyter_client==5.0.0
104 105
105 106 ## cli tools
106 alembic==0.9.6
107 alembic==0.9.8
107 108 invoke==0.13.0
108 109 bumpversion==0.5.3
109 110 transifex-client==0.12.5
110 111
111 112 ## http servers
112 113 gevent==1.2.2
113 greenlet==0.4.12
114 greenlet==0.4.13
114 115 gunicorn==19.7.1
115 116 waitress==1.1.0
116 117 uWSGI==2.0.15
117 118
118 119 ## debug
119 120 ipdb==0.10.3
120 121 ipython==5.1.0
121 122 CProfileV==1.0.7
122 123 bottle==0.12.13
123 124
124 125 ## rhodecode-tools, special case
125 126 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.14.1.tar.gz?md5=0b9c2caad160b68889f8172ea54af7b2#egg=rhodecode-tools==0.14.1
126 127
127 128 ## appenlight
128 appenlight-client==0.6.22
129 appenlight-client==0.6.25
129 130
130 131 ## test related requirements
131 132 -r requirements_test.txt
@@ -1,1 +1,1 b''
1 4.11.6 No newline at end of file
1 4.12.0 No newline at end of file
@@ -1,63 +1,63 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22
23 23 RhodeCode, a web based repository management software
24 24 versioning implementation: http://www.python.org/dev/peps/pep-0386/
25 25 """
26 26
27 27 import os
28 28 import sys
29 29 import platform
30 30
31 31 VERSION = tuple(open(os.path.join(
32 32 os.path.dirname(__file__), 'VERSION')).read().split('.'))
33 33
34 34 BACKENDS = {
35 35 'hg': 'Mercurial repository',
36 36 'git': 'Git repository',
37 37 'svn': 'Subversion repository',
38 38 }
39 39
40 40 CELERY_ENABLED = False
41 41 CELERY_EAGER = False
42 42
43 43 # link to config for pyramid
44 44 CONFIG = {}
45 45
46 46 # Populated with the settings dictionary from application init in
47 47 # rhodecode.conf.environment.load_pyramid_environment
48 48 PYRAMID_SETTINGS = {}
49 49
50 50 # Linked module for extensions
51 51 EXTENSIONS = {}
52 52
53 53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
54 __dbversion__ = 85 # defines current db version for migrations
54 __dbversion__ = 86 # defines current db version for migrations
55 55 __platform__ = platform.system()
56 56 __license__ = 'AGPLv3, and Commercial License'
57 57 __author__ = 'RhodeCode GmbH'
58 58 __url__ = 'https://code.rhodecode.com'
59 59
60 60 is_windows = __platform__ in ['Windows']
61 61 is_unix = not is_windows
62 62 is_test = False
63 63 disable_error_handler = False
@@ -1,140 +1,141 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import pytest
23 23
24 24 from rhodecode.api.tests.utils import build_data, api_call, assert_error
25 25
26 26
27 27 @pytest.mark.usefixtures("testuser_api", "app")
28 28 class TestGetRepoChangeset(object):
29 29 @pytest.mark.parametrize("details", ['basic', 'extended', 'full'])
30 30 def test_get_repo_changeset(self, details, backend):
31 31 commit = backend.repo.get_commit(commit_idx=0)
32 32 __, params = build_data(
33 33 self.apikey, 'get_repo_changeset',
34 34 repoid=backend.repo_name, revision=commit.raw_id,
35 35 details=details,
36 36 )
37 37 response = api_call(self.app, params)
38 38 result = response.json['result']
39 39 assert result['revision'] == 0
40 40 assert result['raw_id'] == commit.raw_id
41 41
42 42 if details == 'full':
43 43 assert result['refs']['bookmarks'] == getattr(
44 44 commit, 'bookmarks', [])
45 assert result['refs']['branches'] == [commit.branch]
45 branches = [commit.branch] if commit.branch else []
46 assert result['refs']['branches'] == branches
46 47 assert result['refs']['tags'] == commit.tags
47 48
48 49 @pytest.mark.parametrize("details", ['basic', 'extended', 'full'])
49 50 def test_get_repo_changeset_bad_type(self, details, backend):
50 51 id_, params = build_data(
51 52 self.apikey, 'get_repo_changeset',
52 53 repoid=backend.repo_name, revision=0,
53 54 details=details,
54 55 )
55 56 response = api_call(self.app, params)
56 57 expected = 'commit_id must be a string value'
57 58 assert_error(id_, expected, given=response.body)
58 59
59 60 @pytest.mark.parametrize("details", ['basic', 'extended', 'full'])
60 61 def test_get_repo_changesets(self, details, backend):
61 62 limit = 2
62 63 commit = backend.repo.get_commit(commit_idx=0)
63 64 __, params = build_data(
64 65 self.apikey, 'get_repo_changesets',
65 66 repoid=backend.repo_name, start_rev=commit.raw_id, limit=limit,
66 67 details=details,
67 68 )
68 69 response = api_call(self.app, params)
69 70 result = response.json['result']
70 71 assert result
71 72 assert len(result) == limit
72 73 for x in xrange(limit):
73 74 assert result[x]['revision'] == x
74 75
75 76 if details == 'full':
76 77 for x in xrange(limit):
77 78 assert 'bookmarks' in result[x]['refs']
78 79 assert 'branches' in result[x]['refs']
79 80 assert 'tags' in result[x]['refs']
80 81
81 82 @pytest.mark.parametrize("details", ['basic', 'extended', 'full'])
82 83 @pytest.mark.parametrize("start_rev, expected_revision", [
83 84 ("0", 0),
84 85 ("10", 10),
85 86 ("20", 20),
86 87 ])
87 88 @pytest.mark.backends("hg", "git")
88 89 def test_get_repo_changesets_commit_range(
89 90 self, details, backend, start_rev, expected_revision):
90 91 limit = 10
91 92 __, params = build_data(
92 93 self.apikey, 'get_repo_changesets',
93 94 repoid=backend.repo_name, start_rev=start_rev, limit=limit,
94 95 details=details,
95 96 )
96 97 response = api_call(self.app, params)
97 98 result = response.json['result']
98 99 assert result
99 100 assert len(result) == limit
100 101 for i in xrange(limit):
101 102 assert result[i]['revision'] == int(expected_revision) + i
102 103
103 104 @pytest.mark.parametrize("details", ['basic', 'extended', 'full'])
104 105 @pytest.mark.parametrize("start_rev, expected_revision", [
105 106 ("0", 0),
106 107 ("10", 9),
107 108 ("20", 19),
108 109 ])
109 110 def test_get_repo_changesets_commit_range_svn(
110 111 self, details, backend_svn, start_rev, expected_revision):
111 112
112 113 # TODO: johbo: SVN showed a problem here: The parameter "start_rev"
113 114 # in our API allows to pass in a "Commit ID" as well as a
114 115 # "Commit Index". In the case of Subversion it is not possible to
115 116 # distinguish these cases. As a workaround we implemented this
116 117 # behavior which gives a preference to see it as a "Commit ID".
117 118
118 119 limit = 10
119 120 __, params = build_data(
120 121 self.apikey, 'get_repo_changesets',
121 122 repoid=backend_svn.repo_name, start_rev=start_rev, limit=limit,
122 123 details=details,
123 124 )
124 125 response = api_call(self.app, params)
125 126 result = response.json['result']
126 127 assert result
127 128 assert len(result) == limit
128 129 for i in xrange(limit):
129 130 assert result[i]['revision'] == int(expected_revision) + i
130 131
131 132 @pytest.mark.parametrize("details", ['basic', 'extended', 'full'])
132 133 def test_get_repo_changesets_bad_type(self, details, backend):
133 134 id_, params = build_data(
134 135 self.apikey, 'get_repo_changesets',
135 136 repoid=backend.repo_name, start_rev=0, limit=2,
136 137 details=details,
137 138 )
138 139 response = api_call(self.app, params)
139 140 expected = 'commit_id must be a string value'
140 141 assert_error(id_, expected, given=response.body)
@@ -1,51 +1,53 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22
23 23 import pytest
24 24
25 25 from rhodecode.tests import TESTS_TMP_PATH
26 26 from rhodecode.api.tests.utils import (
27 27 build_data, api_call, assert_ok, assert_error)
28 28
29 29
30 30 @pytest.mark.usefixtures("testuser_api", "app")
31 31 class TestPull(object):
32 32 @pytest.mark.backends("git", "hg")
33 33 def test_api_pull(self, backend):
34 34 r = backend.create_repo()
35 35 repo_name = r.repo_name
36 r.clone_uri = os.path.join(TESTS_TMP_PATH, backend.repo_name)
36 clone_uri = os.path.join(TESTS_TMP_PATH, backend.repo_name)
37 r.clone_uri = clone_uri
37 38
38 39 id_, params = build_data(self.apikey, 'pull', repoid=repo_name,)
39 40 response = api_call(self.app, params)
40
41 expected = {'msg': 'Pulled from `%s`' % (repo_name,),
41 msg = 'Pulled from url `%s` on repo `%s`' % (
42 clone_uri, repo_name)
43 expected = {'msg': msg,
42 44 'repository': repo_name}
43 45 assert_ok(id_, expected, given=response.body)
44 46
45 47 def test_api_pull_error(self, backend):
46 48 id_, params = build_data(
47 49 self.apikey, 'pull', repoid=backend.repo_name)
48 50 response = api_call(self.app, params)
49 51
50 expected = 'Unable to pull changes from `%s`' % (backend.repo_name,)
52 expected = 'Unable to pull changes from `None`'
51 53 assert_error(id_, expected, given=response.body)
@@ -1,194 +1,197 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import mock
22 22 import pytest
23 23
24 24 from rhodecode.model.repo import RepoModel
25 25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
26 26 from rhodecode.api.tests.utils import (
27 27 build_data, api_call, assert_error, assert_ok, crash, jsonify)
28 28 from rhodecode.tests.fixture import Fixture
29 29 from rhodecode.tests.plugin import http_host_stub, http_host_only_stub
30 30
31 31 fixture = Fixture()
32 32
33 33 UPDATE_REPO_NAME = 'api_update_me'
34 34
35 35
36 36 class SAME_AS_UPDATES(object):
37 37 """ Constant used for tests below """
38 38
39 39
40 40 @pytest.mark.usefixtures("testuser_api", "app")
41 41 class TestApiUpdateRepo(object):
42 42
43 43 @pytest.mark.parametrize("updates, expected", [
44 44 ({'owner': TEST_USER_REGULAR_LOGIN},
45 45 SAME_AS_UPDATES),
46 46
47 47 ({'description': 'new description'},
48 48 SAME_AS_UPDATES),
49 49
50 50 ({'clone_uri': 'http://foo.com/repo'},
51 51 SAME_AS_UPDATES),
52 52
53 53 ({'clone_uri': None},
54 54 {'clone_uri': ''}),
55 55
56 56 ({'clone_uri': ''},
57 57 {'clone_uri': ''}),
58 58
59 ({'push_uri': ''},
60 {'push_uri': ''}),
61
59 62 ({'landing_rev': 'rev:tip'},
60 63 {'landing_rev': ['rev', 'tip']}),
61 64
62 65 ({'enable_statistics': True},
63 66 SAME_AS_UPDATES),
64 67
65 68 ({'enable_locking': True},
66 69 SAME_AS_UPDATES),
67 70
68 71 ({'enable_downloads': True},
69 72 SAME_AS_UPDATES),
70 73
71 74 ({'repo_name': 'new_repo_name'},
72 75 {
73 76 'repo_name': 'new_repo_name',
74 77 'url': 'http://{}/new_repo_name'.format(http_host_only_stub())
75 78 }),
76 79
77 80 ({'repo_name': 'test_group_for_update/{}'.format(UPDATE_REPO_NAME),
78 81 '_group': 'test_group_for_update'},
79 82 {
80 83 'repo_name': 'test_group_for_update/{}'.format(UPDATE_REPO_NAME),
81 84 'url': 'http://{}/test_group_for_update/{}'.format(
82 85 http_host_only_stub(), UPDATE_REPO_NAME)
83 86 }),
84 87 ])
85 88 def test_api_update_repo(self, updates, expected, backend):
86 89 repo_name = UPDATE_REPO_NAME
87 90 repo = fixture.create_repo(repo_name, repo_type=backend.alias)
88 91 if updates.get('_group'):
89 92 fixture.create_repo_group(updates['_group'])
90 93
91 94 expected_api_data = repo.get_api_data(include_secrets=True)
92 95 if expected is SAME_AS_UPDATES:
93 96 expected_api_data.update(updates)
94 97 else:
95 98 expected_api_data.update(expected)
96 99
97 100 id_, params = build_data(
98 101 self.apikey, 'update_repo', repoid=repo_name, **updates)
99 102
100 103 with mock.patch('rhodecode.model.validation_schema.validators.url_validator'):
101 104 response = api_call(self.app, params)
102 105
103 106 if updates.get('repo_name'):
104 107 repo_name = updates['repo_name']
105 108
106 109 try:
107 110 expected = {
108 111 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo_name),
109 112 'repository': jsonify(expected_api_data)
110 113 }
111 114 assert_ok(id_, expected, given=response.body)
112 115 finally:
113 116 fixture.destroy_repo(repo_name)
114 117 if updates.get('_group'):
115 118 fixture.destroy_repo_group(updates['_group'])
116 119
117 120 def test_api_update_repo_fork_of_field(self, backend):
118 121 master_repo = backend.create_repo()
119 122 repo = backend.create_repo()
120 123 updates = {
121 124 'fork_of': master_repo.repo_name,
122 125 'fork_of_id': master_repo.repo_id
123 126 }
124 127 expected_api_data = repo.get_api_data(include_secrets=True)
125 128 expected_api_data.update(updates)
126 129
127 130 id_, params = build_data(
128 131 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
129 132 response = api_call(self.app, params)
130 133 expected = {
131 134 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
132 135 'repository': jsonify(expected_api_data)
133 136 }
134 137 assert_ok(id_, expected, given=response.body)
135 138 result = response.json['result']['repository']
136 139 assert result['fork_of'] == master_repo.repo_name
137 140 assert result['fork_of_id'] == master_repo.repo_id
138 141
139 142 def test_api_update_repo_fork_of_not_found(self, backend):
140 143 master_repo_name = 'fake-parent-repo'
141 144 repo = backend.create_repo()
142 145 updates = {
143 146 'fork_of': master_repo_name
144 147 }
145 148 id_, params = build_data(
146 149 self.apikey, 'update_repo', repoid=repo.repo_name, **updates)
147 150 response = api_call(self.app, params)
148 151 expected = {
149 152 'repo_fork_of': 'Fork with id `{}` does not exists'.format(
150 153 master_repo_name)}
151 154 assert_error(id_, expected, given=response.body)
152 155
153 156 def test_api_update_repo_with_repo_group_not_existing(self):
154 157 repo_name = 'admin_owned'
155 158 fake_repo_group = 'test_group_for_update'
156 159 fixture.create_repo(repo_name)
157 160 updates = {'repo_name': '{}/{}'.format(fake_repo_group, repo_name)}
158 161 id_, params = build_data(
159 162 self.apikey, 'update_repo', repoid=repo_name, **updates)
160 163 response = api_call(self.app, params)
161 164 try:
162 165 expected = {
163 166 'repo_group': 'Repository group `{}` does not exist'.format(fake_repo_group)
164 167 }
165 168 assert_error(id_, expected, given=response.body)
166 169 finally:
167 170 fixture.destroy_repo(repo_name)
168 171
169 172 def test_api_update_repo_regular_user_not_allowed(self):
170 173 repo_name = 'admin_owned'
171 174 fixture.create_repo(repo_name)
172 175 updates = {'active': False}
173 176 id_, params = build_data(
174 177 self.apikey_regular, 'update_repo', repoid=repo_name, **updates)
175 178 response = api_call(self.app, params)
176 179 try:
177 180 expected = 'repository `%s` does not exist' % (repo_name,)
178 181 assert_error(id_, expected, given=response.body)
179 182 finally:
180 183 fixture.destroy_repo(repo_name)
181 184
182 185 @mock.patch.object(RepoModel, 'update', crash)
183 186 def test_api_update_repo_exception_occurred(self, backend):
184 187 repo_name = UPDATE_REPO_NAME
185 188 fixture.create_repo(repo_name, repo_type=backend.alias)
186 189 id_, params = build_data(
187 190 self.apikey, 'update_repo', repoid=repo_name,
188 191 owner=TEST_USER_ADMIN_LOGIN,)
189 192 response = api_call(self.app, params)
190 193 try:
191 194 expected = 'failed to update repo `%s`' % (repo_name,)
192 195 assert_error(id_, expected, given=response.body)
193 196 finally:
194 197 fixture.destroy_repo(repo_name)
@@ -1,110 +1,125 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import mock
22 22 import pytest
23 23
24 24 from rhodecode.model.user import UserModel
25 25 from rhodecode.model.user_group import UserGroupModel
26 26 from rhodecode.tests import TEST_USER_ADMIN_EMAIL
27 27 from rhodecode.api.tests.utils import (
28 28 build_data, api_call, assert_error, assert_ok, crash, jsonify)
29 29
30 30
31 31 @pytest.mark.usefixtures("testuser_api", "app")
32 32 class TestUpdateUserGroup(object):
33 33 @pytest.mark.parametrize("changing_attr, updates", [
34 34 ('group_name', {'group_name': 'new_group_name'}),
35 35 ('group_name', {'group_name': 'test_group_for_update'}),
36 36 # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
37 37 ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}),
38 38 ('active', {'active': False}),
39 ('active', {'active': True})
39 ('active', {'active': True}),
40 ('sync', {'sync': False}),
41 ('sync', {'sync': True})
40 42 ])
41 43 def test_api_update_user_group(self, changing_attr, updates, user_util):
42 44 user_group = user_util.create_user_group()
43 45 group_name = user_group.users_group_name
44 46 expected_api_data = user_group.get_api_data()
45 47 expected_api_data.update(updates)
46 48
47 49 id_, params = build_data(
48 50 self.apikey, 'update_user_group', usergroupid=group_name,
49 51 **updates)
50 52 response = api_call(self.app, params)
51 53
54 # special case for sync
55 if changing_attr == 'sync' and updates['sync'] is False:
56 expected_api_data['sync'] = None
57 elif changing_attr == 'sync' and updates['sync'] is True:
58 expected_api_data['sync'] = 'manual_api'
59
52 60 expected = {
53 61 'msg': 'updated user group ID:%s %s' % (
54 62 user_group.users_group_id, user_group.users_group_name),
55 63 'user_group': jsonify(expected_api_data)
56 64 }
57 65 assert_ok(id_, expected, given=response.body)
58 66
59 67 @pytest.mark.parametrize("changing_attr, updates", [
60 68 # TODO: mikhail: decide if we need to test against the commented params
61 69 # ('group_name', {'group_name': 'new_group_name'}),
62 70 # ('group_name', {'group_name': 'test_group_for_update'}),
63 71 # ('owner', {'owner': TEST_USER_REGULAR_LOGIN}),
64 72 ('owner_email', {'owner_email': TEST_USER_ADMIN_EMAIL}),
65 73 ('active', {'active': False}),
66 ('active', {'active': True})
74 ('active', {'active': True}),
75 ('sync', {'sync': False}),
76 ('sync', {'sync': True})
67 77 ])
68 78 def test_api_update_user_group_regular_user(
69 79 self, changing_attr, updates, user_util):
70 80 user_group = user_util.create_user_group()
71 81 group_name = user_group.users_group_name
72 82 expected_api_data = user_group.get_api_data()
73 83 expected_api_data.update(updates)
74 84
75
76 85 # grant permission to this user
77 86 user = UserModel().get_by_username(self.TEST_USER_LOGIN)
78 87
79 88 user_util.grant_user_permission_to_user_group(
80 89 user_group, user, 'usergroup.admin')
81 90 id_, params = build_data(
82 91 self.apikey_regular, 'update_user_group',
83 92 usergroupid=group_name, **updates)
84 93 response = api_call(self.app, params)
94 # special case for sync
95 if changing_attr == 'sync' and updates['sync'] is False:
96 expected_api_data['sync'] = None
97 elif changing_attr == 'sync' and updates['sync'] is True:
98 expected_api_data['sync'] = 'manual_api'
99
85 100 expected = {
86 101 'msg': 'updated user group ID:%s %s' % (
87 102 user_group.users_group_id, user_group.users_group_name),
88 103 'user_group': jsonify(expected_api_data)
89 104 }
90 105 assert_ok(id_, expected, given=response.body)
91 106
92 107 def test_api_update_user_group_regular_user_no_permission(self, user_util):
93 108 user_group = user_util.create_user_group()
94 109 group_name = user_group.users_group_name
95 110 id_, params = build_data(
96 111 self.apikey_regular, 'update_user_group', usergroupid=group_name)
97 112 response = api_call(self.app, params)
98 113
99 114 expected = 'user group `%s` does not exist' % (group_name)
100 115 assert_error(id_, expected, given=response.body)
101 116
102 117 @mock.patch.object(UserGroupModel, 'update', crash)
103 118 def test_api_update_user_group_exception_occurred(self, user_util):
104 119 user_group = user_util.create_user_group()
105 120 group_name = user_group.users_group_name
106 121 id_, params = build_data(
107 122 self.apikey, 'update_user_group', usergroupid=group_name)
108 123 response = api_call(self.app, params)
109 124 expected = 'failed to update user group `%s`' % (group_name,)
110 125 assert_error(id_, expected, given=response.body)
@@ -1,2046 +1,2064 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import time
23 23
24 24 import rhodecode
25 25 from rhodecode.api import (
26 26 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
27 27 from rhodecode.api.utils import (
28 28 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
29 29 get_user_group_or_error, get_user_or_error, validate_repo_permissions,
30 30 get_perm_or_error, parse_args, get_origin, build_commit_data,
31 31 validate_set_owner_permissions)
32 32 from rhodecode.lib import audit_logger
33 33 from rhodecode.lib import repo_maintenance
34 34 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
35 35 from rhodecode.lib.celerylib.utils import get_task_id
36 36 from rhodecode.lib.utils2 import str2bool, time_to_datetime
37 37 from rhodecode.lib.ext_json import json
38 38 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
39 39 from rhodecode.model.changeset_status import ChangesetStatusModel
40 40 from rhodecode.model.comment import CommentsModel
41 41 from rhodecode.model.db import (
42 42 Session, ChangesetStatus, RepositoryField, Repository, RepoGroup,
43 43 ChangesetComment)
44 44 from rhodecode.model.repo import RepoModel
45 45 from rhodecode.model.scm import ScmModel, RepoList
46 46 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
47 47 from rhodecode.model import validation_schema
48 48 from rhodecode.model.validation_schema.schemas import repo_schema
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52
53 53 @jsonrpc_method()
54 54 def get_repo(request, apiuser, repoid, cache=Optional(True)):
55 55 """
56 56 Gets an existing repository by its name or repository_id.
57 57
58 58 The members section so the output returns users groups or users
59 59 associated with that repository.
60 60
61 61 This command can only be run using an |authtoken| with admin rights,
62 62 or users with at least read rights to the |repo|.
63 63
64 64 :param apiuser: This is filled automatically from the |authtoken|.
65 65 :type apiuser: AuthUser
66 66 :param repoid: The repository name or repository id.
67 67 :type repoid: str or int
68 68 :param cache: use the cached value for last changeset
69 69 :type: cache: Optional(bool)
70 70
71 71 Example output:
72 72
73 73 .. code-block:: bash
74 74
75 75 {
76 76 "error": null,
77 77 "id": <repo_id>,
78 78 "result": {
79 79 "clone_uri": null,
80 80 "created_on": "timestamp",
81 81 "description": "repo description",
82 82 "enable_downloads": false,
83 83 "enable_locking": false,
84 84 "enable_statistics": false,
85 85 "followers": [
86 86 {
87 87 "active": true,
88 88 "admin": false,
89 89 "api_key": "****************************************",
90 90 "api_keys": [
91 91 "****************************************"
92 92 ],
93 93 "email": "user@example.com",
94 94 "emails": [
95 95 "user@example.com"
96 96 ],
97 97 "extern_name": "rhodecode",
98 98 "extern_type": "rhodecode",
99 99 "firstname": "username",
100 100 "ip_addresses": [],
101 101 "language": null,
102 102 "last_login": "2015-09-16T17:16:35.854",
103 103 "lastname": "surname",
104 104 "user_id": <user_id>,
105 105 "username": "name"
106 106 }
107 107 ],
108 108 "fork_of": "parent-repo",
109 109 "landing_rev": [
110 110 "rev",
111 111 "tip"
112 112 ],
113 113 "last_changeset": {
114 114 "author": "User <user@example.com>",
115 115 "branch": "default",
116 116 "date": "timestamp",
117 117 "message": "last commit message",
118 118 "parents": [
119 119 {
120 120 "raw_id": "commit-id"
121 121 }
122 122 ],
123 123 "raw_id": "commit-id",
124 124 "revision": <revision number>,
125 125 "short_id": "short id"
126 126 },
127 127 "lock_reason": null,
128 128 "locked_by": null,
129 129 "locked_date": null,
130 130 "owner": "owner-name",
131 131 "permissions": [
132 132 {
133 133 "name": "super-admin-name",
134 134 "origin": "super-admin",
135 135 "permission": "repository.admin",
136 136 "type": "user"
137 137 },
138 138 {
139 139 "name": "owner-name",
140 140 "origin": "owner",
141 141 "permission": "repository.admin",
142 142 "type": "user"
143 143 },
144 144 {
145 145 "name": "user-group-name",
146 146 "origin": "permission",
147 147 "permission": "repository.write",
148 148 "type": "user_group"
149 149 }
150 150 ],
151 151 "private": true,
152 152 "repo_id": 676,
153 153 "repo_name": "user-group/repo-name",
154 154 "repo_type": "hg"
155 155 }
156 156 }
157 157 """
158 158
159 159 repo = get_repo_or_error(repoid)
160 160 cache = Optional.extract(cache)
161 161
162 162 include_secrets = False
163 163 if has_superadmin_permission(apiuser):
164 164 include_secrets = True
165 165 else:
166 166 # check if we have at least read permission for this repo !
167 167 _perms = (
168 168 'repository.admin', 'repository.write', 'repository.read',)
169 169 validate_repo_permissions(apiuser, repoid, repo, _perms)
170 170
171 171 permissions = []
172 172 for _user in repo.permissions():
173 173 user_data = {
174 174 'name': _user.username,
175 175 'permission': _user.permission,
176 176 'origin': get_origin(_user),
177 177 'type': "user",
178 178 }
179 179 permissions.append(user_data)
180 180
181 181 for _user_group in repo.permission_user_groups():
182 182 user_group_data = {
183 183 'name': _user_group.users_group_name,
184 184 'permission': _user_group.permission,
185 185 'origin': get_origin(_user_group),
186 186 'type': "user_group",
187 187 }
188 188 permissions.append(user_group_data)
189 189
190 190 following_users = [
191 191 user.user.get_api_data(include_secrets=include_secrets)
192 192 for user in repo.followers]
193 193
194 194 if not cache:
195 195 repo.update_commit_cache()
196 196 data = repo.get_api_data(include_secrets=include_secrets)
197 197 data['permissions'] = permissions
198 198 data['followers'] = following_users
199 199 return data
200 200
201 201
202 202 @jsonrpc_method()
203 203 def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)):
204 204 """
205 205 Lists all existing repositories.
206 206
207 207 This command can only be run using an |authtoken| with admin rights,
208 208 or users with at least read rights to |repos|.
209 209
210 210 :param apiuser: This is filled automatically from the |authtoken|.
211 211 :type apiuser: AuthUser
212 212 :param root: specify root repository group to fetch repositories.
213 213 filters the returned repositories to be members of given root group.
214 214 :type root: Optional(None)
215 215 :param traverse: traverse given root into subrepositories. With this flag
216 216 set to False, it will only return top-level repositories from `root`.
217 217 if root is empty it will return just top-level repositories.
218 218 :type traverse: Optional(True)
219 219
220 220
221 221 Example output:
222 222
223 223 .. code-block:: bash
224 224
225 225 id : <id_given_in_input>
226 226 result: [
227 227 {
228 228 "repo_id" : "<repo_id>",
229 229 "repo_name" : "<reponame>"
230 230 "repo_type" : "<repo_type>",
231 231 "clone_uri" : "<clone_uri>",
232 232 "private": : "<bool>",
233 233 "created_on" : "<datetimecreated>",
234 234 "description" : "<description>",
235 235 "landing_rev": "<landing_rev>",
236 236 "owner": "<repo_owner>",
237 237 "fork_of": "<name_of_fork_parent>",
238 238 "enable_downloads": "<bool>",
239 239 "enable_locking": "<bool>",
240 240 "enable_statistics": "<bool>",
241 241 },
242 242 ...
243 243 ]
244 244 error: null
245 245 """
246 246
247 247 include_secrets = has_superadmin_permission(apiuser)
248 248 _perms = ('repository.read', 'repository.write', 'repository.admin',)
249 249 extras = {'user': apiuser}
250 250
251 251 root = Optional.extract(root)
252 252 traverse = Optional.extract(traverse, binary=True)
253 253
254 254 if root:
255 255 # verify parent existance, if it's empty return an error
256 256 parent = RepoGroup.get_by_group_name(root)
257 257 if not parent:
258 258 raise JSONRPCError(
259 259 'Root repository group `{}` does not exist'.format(root))
260 260
261 261 if traverse:
262 262 repos = RepoModel().get_repos_for_root(root=root, traverse=traverse)
263 263 else:
264 264 repos = RepoModel().get_repos_for_root(root=parent)
265 265 else:
266 266 if traverse:
267 267 repos = RepoModel().get_all()
268 268 else:
269 269 # return just top-level
270 270 repos = RepoModel().get_repos_for_root(root=None)
271 271
272 272 repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras)
273 273 return [repo.get_api_data(include_secrets=include_secrets)
274 274 for repo in repo_list]
275 275
276 276
277 277 @jsonrpc_method()
278 278 def get_repo_changeset(request, apiuser, repoid, revision,
279 279 details=Optional('basic')):
280 280 """
281 281 Returns information about a changeset.
282 282
283 283 Additionally parameters define the amount of details returned by
284 284 this function.
285 285
286 286 This command can only be run using an |authtoken| with admin rights,
287 287 or users with at least read rights to the |repo|.
288 288
289 289 :param apiuser: This is filled automatically from the |authtoken|.
290 290 :type apiuser: AuthUser
291 291 :param repoid: The repository name or repository id
292 292 :type repoid: str or int
293 293 :param revision: revision for which listing should be done
294 294 :type revision: str
295 295 :param details: details can be 'basic|extended|full' full gives diff
296 296 info details like the diff itself, and number of changed files etc.
297 297 :type details: Optional(str)
298 298
299 299 """
300 300 repo = get_repo_or_error(repoid)
301 301 if not has_superadmin_permission(apiuser):
302 302 _perms = (
303 303 'repository.admin', 'repository.write', 'repository.read',)
304 304 validate_repo_permissions(apiuser, repoid, repo, _perms)
305 305
306 306 changes_details = Optional.extract(details)
307 307 _changes_details_types = ['basic', 'extended', 'full']
308 308 if changes_details not in _changes_details_types:
309 309 raise JSONRPCError(
310 310 'ret_type must be one of %s' % (
311 311 ','.join(_changes_details_types)))
312 312
313 313 pre_load = ['author', 'branch', 'date', 'message', 'parents',
314 314 'status', '_commit', '_file_paths']
315 315
316 316 try:
317 317 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
318 318 except TypeError as e:
319 319 raise JSONRPCError(e.message)
320 320 _cs_json = cs.__json__()
321 321 _cs_json['diff'] = build_commit_data(cs, changes_details)
322 322 if changes_details == 'full':
323 323 _cs_json['refs'] = cs._get_refs()
324 324 return _cs_json
325 325
326 326
327 327 @jsonrpc_method()
328 328 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
329 329 details=Optional('basic')):
330 330 """
331 331 Returns a set of commits limited by the number starting
332 332 from the `start_rev` option.
333 333
334 334 Additional parameters define the amount of details returned by this
335 335 function.
336 336
337 337 This command can only be run using an |authtoken| with admin rights,
338 338 or users with at least read rights to |repos|.
339 339
340 340 :param apiuser: This is filled automatically from the |authtoken|.
341 341 :type apiuser: AuthUser
342 342 :param repoid: The repository name or repository ID.
343 343 :type repoid: str or int
344 344 :param start_rev: The starting revision from where to get changesets.
345 345 :type start_rev: str
346 346 :param limit: Limit the number of commits to this amount
347 347 :type limit: str or int
348 348 :param details: Set the level of detail returned. Valid option are:
349 349 ``basic``, ``extended`` and ``full``.
350 350 :type details: Optional(str)
351 351
352 352 .. note::
353 353
354 354 Setting the parameter `details` to the value ``full`` is extensive
355 355 and returns details like the diff itself, and the number
356 356 of changed files.
357 357
358 358 """
359 359 repo = get_repo_or_error(repoid)
360 360 if not has_superadmin_permission(apiuser):
361 361 _perms = (
362 362 'repository.admin', 'repository.write', 'repository.read',)
363 363 validate_repo_permissions(apiuser, repoid, repo, _perms)
364 364
365 365 changes_details = Optional.extract(details)
366 366 _changes_details_types = ['basic', 'extended', 'full']
367 367 if changes_details not in _changes_details_types:
368 368 raise JSONRPCError(
369 369 'ret_type must be one of %s' % (
370 370 ','.join(_changes_details_types)))
371 371
372 372 limit = int(limit)
373 373 pre_load = ['author', 'branch', 'date', 'message', 'parents',
374 374 'status', '_commit', '_file_paths']
375 375
376 376 vcs_repo = repo.scm_instance()
377 377 # SVN needs a special case to distinguish its index and commit id
378 378 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
379 379 start_rev = vcs_repo.commit_ids[0]
380 380
381 381 try:
382 382 commits = vcs_repo.get_commits(
383 383 start_id=start_rev, pre_load=pre_load)
384 384 except TypeError as e:
385 385 raise JSONRPCError(e.message)
386 386 except Exception:
387 387 log.exception('Fetching of commits failed')
388 388 raise JSONRPCError('Error occurred during commit fetching')
389 389
390 390 ret = []
391 391 for cnt, commit in enumerate(commits):
392 392 if cnt >= limit != -1:
393 393 break
394 394 _cs_json = commit.__json__()
395 395 _cs_json['diff'] = build_commit_data(commit, changes_details)
396 396 if changes_details == 'full':
397 397 _cs_json['refs'] = {
398 398 'branches': [commit.branch],
399 399 'bookmarks': getattr(commit, 'bookmarks', []),
400 400 'tags': commit.tags
401 401 }
402 402 ret.append(_cs_json)
403 403 return ret
404 404
405 405
406 406 @jsonrpc_method()
407 407 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
408 408 ret_type=Optional('all'), details=Optional('basic'),
409 409 max_file_bytes=Optional(None)):
410 410 """
411 411 Returns a list of nodes and children in a flat list for a given
412 412 path at given revision.
413 413
414 414 It's possible to specify ret_type to show only `files` or `dirs`.
415 415
416 416 This command can only be run using an |authtoken| with admin rights,
417 417 or users with at least read rights to |repos|.
418 418
419 419 :param apiuser: This is filled automatically from the |authtoken|.
420 420 :type apiuser: AuthUser
421 421 :param repoid: The repository name or repository ID.
422 422 :type repoid: str or int
423 423 :param revision: The revision for which listing should be done.
424 424 :type revision: str
425 425 :param root_path: The path from which to start displaying.
426 426 :type root_path: str
427 427 :param ret_type: Set the return type. Valid options are
428 428 ``all`` (default), ``files`` and ``dirs``.
429 429 :type ret_type: Optional(str)
430 430 :param details: Returns extended information about nodes, such as
431 431 md5, binary, and or content. The valid options are ``basic`` and
432 432 ``full``.
433 433 :type details: Optional(str)
434 434 :param max_file_bytes: Only return file content under this file size bytes
435 435 :type details: Optional(int)
436 436
437 437 Example output:
438 438
439 439 .. code-block:: bash
440 440
441 441 id : <id_given_in_input>
442 442 result: [
443 443 {
444 444 "name" : "<name>"
445 445 "type" : "<type>",
446 446 "binary": "<true|false>" (only in extended mode)
447 447 "md5" : "<md5 of file content>" (only in extended mode)
448 448 },
449 449 ...
450 450 ]
451 451 error: null
452 452 """
453 453
454 454 repo = get_repo_or_error(repoid)
455 455 if not has_superadmin_permission(apiuser):
456 456 _perms = (
457 457 'repository.admin', 'repository.write', 'repository.read',)
458 458 validate_repo_permissions(apiuser, repoid, repo, _perms)
459 459
460 460 ret_type = Optional.extract(ret_type)
461 461 details = Optional.extract(details)
462 462 _extended_types = ['basic', 'full']
463 463 if details not in _extended_types:
464 464 raise JSONRPCError(
465 465 'ret_type must be one of %s' % (','.join(_extended_types)))
466 466 extended_info = False
467 467 content = False
468 468 if details == 'basic':
469 469 extended_info = True
470 470
471 471 if details == 'full':
472 472 extended_info = content = True
473 473
474 474 _map = {}
475 475 try:
476 476 # check if repo is not empty by any chance, skip quicker if it is.
477 477 _scm = repo.scm_instance()
478 478 if _scm.is_empty():
479 479 return []
480 480
481 481 _d, _f = ScmModel().get_nodes(
482 482 repo, revision, root_path, flat=False,
483 483 extended_info=extended_info, content=content,
484 484 max_file_bytes=max_file_bytes)
485 485 _map = {
486 486 'all': _d + _f,
487 487 'files': _f,
488 488 'dirs': _d,
489 489 }
490 490 return _map[ret_type]
491 491 except KeyError:
492 492 raise JSONRPCError(
493 493 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
494 494 except Exception:
495 495 log.exception("Exception occurred while trying to get repo nodes")
496 496 raise JSONRPCError(
497 497 'failed to get repo: `%s` nodes' % repo.repo_name
498 498 )
499 499
500 500
501 501 @jsonrpc_method()
502 502 def get_repo_refs(request, apiuser, repoid):
503 503 """
504 504 Returns a dictionary of current references. It returns
505 505 bookmarks, branches, closed_branches, and tags for given repository
506 506
507 507 It's possible to specify ret_type to show only `files` or `dirs`.
508 508
509 509 This command can only be run using an |authtoken| with admin rights,
510 510 or users with at least read rights to |repos|.
511 511
512 512 :param apiuser: This is filled automatically from the |authtoken|.
513 513 :type apiuser: AuthUser
514 514 :param repoid: The repository name or repository ID.
515 515 :type repoid: str or int
516 516
517 517 Example output:
518 518
519 519 .. code-block:: bash
520 520
521 521 id : <id_given_in_input>
522 522 "result": {
523 523 "bookmarks": {
524 524 "dev": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
525 525 "master": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
526 526 },
527 527 "branches": {
528 528 "default": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
529 529 "stable": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
530 530 },
531 531 "branches_closed": {},
532 532 "tags": {
533 533 "tip": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
534 534 "v4.4.0": "1232313f9e6adac5ce5399c2a891dc1e72b79022",
535 535 "v4.4.1": "cbb9f1d329ae5768379cdec55a62ebdd546c4e27",
536 536 "v4.4.2": "24ffe44a27fcd1c5b6936144e176b9f6dd2f3a17",
537 537 }
538 538 }
539 539 error: null
540 540 """
541 541
542 542 repo = get_repo_or_error(repoid)
543 543 if not has_superadmin_permission(apiuser):
544 544 _perms = ('repository.admin', 'repository.write', 'repository.read',)
545 545 validate_repo_permissions(apiuser, repoid, repo, _perms)
546 546
547 547 try:
548 548 # check if repo is not empty by any chance, skip quicker if it is.
549 549 vcs_instance = repo.scm_instance()
550 550 refs = vcs_instance.refs()
551 551 return refs
552 552 except Exception:
553 553 log.exception("Exception occurred while trying to get repo refs")
554 554 raise JSONRPCError(
555 555 'failed to get repo: `%s` references' % repo.repo_name
556 556 )
557 557
558 558
559 559 @jsonrpc_method()
560 560 def create_repo(
561 561 request, apiuser, repo_name, repo_type,
562 562 owner=Optional(OAttr('apiuser')),
563 563 description=Optional(''),
564 564 private=Optional(False),
565 565 clone_uri=Optional(None),
566 push_uri=Optional(None),
566 567 landing_rev=Optional('rev:tip'),
567 568 enable_statistics=Optional(False),
568 569 enable_locking=Optional(False),
569 570 enable_downloads=Optional(False),
570 571 copy_permissions=Optional(False)):
571 572 """
572 573 Creates a repository.
573 574
574 575 * If the repository name contains "/", repository will be created inside
575 576 a repository group or nested repository groups
576 577
577 578 For example "foo/bar/repo1" will create |repo| called "repo1" inside
578 579 group "foo/bar". You have to have permissions to access and write to
579 580 the last repository group ("bar" in this example)
580 581
581 582 This command can only be run using an |authtoken| with at least
582 583 permissions to create repositories, or write permissions to
583 584 parent repository groups.
584 585
585 586 :param apiuser: This is filled automatically from the |authtoken|.
586 587 :type apiuser: AuthUser
587 588 :param repo_name: Set the repository name.
588 589 :type repo_name: str
589 590 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
590 591 :type repo_type: str
591 592 :param owner: user_id or username
592 593 :type owner: Optional(str)
593 594 :param description: Set the repository description.
594 595 :type description: Optional(str)
595 596 :param private: set repository as private
596 597 :type private: bool
597 598 :param clone_uri: set clone_uri
598 599 :type clone_uri: str
600 :param push_uri: set push_uri
601 :type push_uri: str
599 602 :param landing_rev: <rev_type>:<rev>
600 603 :type landing_rev: str
601 604 :param enable_locking:
602 605 :type enable_locking: bool
603 606 :param enable_downloads:
604 607 :type enable_downloads: bool
605 608 :param enable_statistics:
606 609 :type enable_statistics: bool
607 610 :param copy_permissions: Copy permission from group in which the
608 611 repository is being created.
609 612 :type copy_permissions: bool
610 613
611 614
612 615 Example output:
613 616
614 617 .. code-block:: bash
615 618
616 619 id : <id_given_in_input>
617 620 result: {
618 621 "msg": "Created new repository `<reponame>`",
619 622 "success": true,
620 623 "task": "<celery task id or None if done sync>"
621 624 }
622 625 error: null
623 626
624 627
625 628 Example error output:
626 629
627 630 .. code-block:: bash
628 631
629 632 id : <id_given_in_input>
630 633 result : null
631 634 error : {
632 635 'failed to create repository `<repo_name>`'
633 636 }
634 637
635 638 """
636 639
637 640 owner = validate_set_owner_permissions(apiuser, owner)
638 641
639 642 description = Optional.extract(description)
640 643 copy_permissions = Optional.extract(copy_permissions)
641 644 clone_uri = Optional.extract(clone_uri)
645 push_uri = Optional.extract(push_uri)
642 646 landing_commit_ref = Optional.extract(landing_rev)
643 647
644 648 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
645 649 if isinstance(private, Optional):
646 650 private = defs.get('repo_private') or Optional.extract(private)
647 651 if isinstance(repo_type, Optional):
648 652 repo_type = defs.get('repo_type')
649 653 if isinstance(enable_statistics, Optional):
650 654 enable_statistics = defs.get('repo_enable_statistics')
651 655 if isinstance(enable_locking, Optional):
652 656 enable_locking = defs.get('repo_enable_locking')
653 657 if isinstance(enable_downloads, Optional):
654 658 enable_downloads = defs.get('repo_enable_downloads')
655 659
656 660 schema = repo_schema.RepoSchema().bind(
657 661 repo_type_options=rhodecode.BACKENDS.keys(),
658 662 repo_type=repo_type,
659 663 # user caller
660 664 user=apiuser)
661 665
662 666 try:
663 667 schema_data = schema.deserialize(dict(
664 668 repo_name=repo_name,
665 669 repo_type=repo_type,
666 670 repo_owner=owner.username,
667 671 repo_description=description,
668 672 repo_landing_commit_ref=landing_commit_ref,
669 673 repo_clone_uri=clone_uri,
674 repo_push_uri=push_uri,
670 675 repo_private=private,
671 676 repo_copy_permissions=copy_permissions,
672 677 repo_enable_statistics=enable_statistics,
673 678 repo_enable_downloads=enable_downloads,
674 679 repo_enable_locking=enable_locking))
675 680 except validation_schema.Invalid as err:
676 681 raise JSONRPCValidationError(colander_exc=err)
677 682
678 683 try:
679 684 data = {
680 685 'owner': owner,
681 686 'repo_name': schema_data['repo_group']['repo_name_without_group'],
682 687 'repo_name_full': schema_data['repo_name'],
683 688 'repo_group': schema_data['repo_group']['repo_group_id'],
684 689 'repo_type': schema_data['repo_type'],
685 690 'repo_description': schema_data['repo_description'],
686 691 'repo_private': schema_data['repo_private'],
687 692 'clone_uri': schema_data['repo_clone_uri'],
693 'push_uri': schema_data['repo_push_uri'],
688 694 'repo_landing_rev': schema_data['repo_landing_commit_ref'],
689 695 'enable_statistics': schema_data['repo_enable_statistics'],
690 696 'enable_locking': schema_data['repo_enable_locking'],
691 697 'enable_downloads': schema_data['repo_enable_downloads'],
692 698 'repo_copy_permissions': schema_data['repo_copy_permissions'],
693 699 }
694 700
695 task = RepoModel().create(form_data=data, cur_user=owner)
701 task = RepoModel().create(form_data=data, cur_user=owner.user_id)
696 702 task_id = get_task_id(task)
697 703 # no commit, it's done in RepoModel, or async via celery
698 704 return {
699 705 'msg': "Created new repository `%s`" % (schema_data['repo_name'],),
700 706 'success': True, # cannot return the repo data here since fork
701 707 # can be done async
702 708 'task': task_id
703 709 }
704 710 except Exception:
705 711 log.exception(
706 712 u"Exception while trying to create the repository %s",
707 713 schema_data['repo_name'])
708 714 raise JSONRPCError(
709 715 'failed to create repository `%s`' % (schema_data['repo_name'],))
710 716
711 717
712 718 @jsonrpc_method()
713 719 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
714 720 description=Optional('')):
715 721 """
716 722 Adds an extra field to a repository.
717 723
718 724 This command can only be run using an |authtoken| with at least
719 725 write permissions to the |repo|.
720 726
721 727 :param apiuser: This is filled automatically from the |authtoken|.
722 728 :type apiuser: AuthUser
723 729 :param repoid: Set the repository name or repository id.
724 730 :type repoid: str or int
725 731 :param key: Create a unique field key for this repository.
726 732 :type key: str
727 733 :param label:
728 734 :type label: Optional(str)
729 735 :param description:
730 736 :type description: Optional(str)
731 737 """
732 738 repo = get_repo_or_error(repoid)
733 739 if not has_superadmin_permission(apiuser):
734 740 _perms = ('repository.admin',)
735 741 validate_repo_permissions(apiuser, repoid, repo, _perms)
736 742
737 743 label = Optional.extract(label) or key
738 744 description = Optional.extract(description)
739 745
740 746 field = RepositoryField.get_by_key_name(key, repo)
741 747 if field:
742 748 raise JSONRPCError('Field with key '
743 749 '`%s` exists for repo `%s`' % (key, repoid))
744 750
745 751 try:
746 752 RepoModel().add_repo_field(repo, key, field_label=label,
747 753 field_desc=description)
748 754 Session().commit()
749 755 return {
750 756 'msg': "Added new repository field `%s`" % (key,),
751 757 'success': True,
752 758 }
753 759 except Exception:
754 760 log.exception("Exception occurred while trying to add field to repo")
755 761 raise JSONRPCError(
756 762 'failed to create new field for repository `%s`' % (repoid,))
757 763
758 764
759 765 @jsonrpc_method()
760 766 def remove_field_from_repo(request, apiuser, repoid, key):
761 767 """
762 768 Removes an extra field from a repository.
763 769
764 770 This command can only be run using an |authtoken| with at least
765 771 write permissions to the |repo|.
766 772
767 773 :param apiuser: This is filled automatically from the |authtoken|.
768 774 :type apiuser: AuthUser
769 775 :param repoid: Set the repository name or repository ID.
770 776 :type repoid: str or int
771 777 :param key: Set the unique field key for this repository.
772 778 :type key: str
773 779 """
774 780
775 781 repo = get_repo_or_error(repoid)
776 782 if not has_superadmin_permission(apiuser):
777 783 _perms = ('repository.admin',)
778 784 validate_repo_permissions(apiuser, repoid, repo, _perms)
779 785
780 786 field = RepositoryField.get_by_key_name(key, repo)
781 787 if not field:
782 788 raise JSONRPCError('Field with key `%s` does not '
783 789 'exists for repo `%s`' % (key, repoid))
784 790
785 791 try:
786 792 RepoModel().delete_repo_field(repo, field_key=key)
787 793 Session().commit()
788 794 return {
789 795 'msg': "Deleted repository field `%s`" % (key,),
790 796 'success': True,
791 797 }
792 798 except Exception:
793 799 log.exception(
794 800 "Exception occurred while trying to delete field from repo")
795 801 raise JSONRPCError(
796 802 'failed to delete field for repository `%s`' % (repoid,))
797 803
798 804
799 805 @jsonrpc_method()
800 806 def update_repo(
801 807 request, apiuser, repoid, repo_name=Optional(None),
802 808 owner=Optional(OAttr('apiuser')), description=Optional(''),
803 private=Optional(False), clone_uri=Optional(None),
809 private=Optional(False),
810 clone_uri=Optional(None), push_uri=Optional(None),
804 811 landing_rev=Optional('rev:tip'), fork_of=Optional(None),
805 812 enable_statistics=Optional(False),
806 813 enable_locking=Optional(False),
807 814 enable_downloads=Optional(False), fields=Optional('')):
808 815 """
809 816 Updates a repository with the given information.
810 817
811 818 This command can only be run using an |authtoken| with at least
812 819 admin permissions to the |repo|.
813 820
814 821 * If the repository name contains "/", repository will be updated
815 822 accordingly with a repository group or nested repository groups
816 823
817 824 For example repoid=repo-test name="foo/bar/repo-test" will update |repo|
818 825 called "repo-test" and place it inside group "foo/bar".
819 826 You have to have permissions to access and write to the last repository
820 827 group ("bar" in this example)
821 828
822 829 :param apiuser: This is filled automatically from the |authtoken|.
823 830 :type apiuser: AuthUser
824 831 :param repoid: repository name or repository ID.
825 832 :type repoid: str or int
826 833 :param repo_name: Update the |repo| name, including the
827 834 repository group it's in.
828 835 :type repo_name: str
829 836 :param owner: Set the |repo| owner.
830 837 :type owner: str
831 838 :param fork_of: Set the |repo| as fork of another |repo|.
832 839 :type fork_of: str
833 840 :param description: Update the |repo| description.
834 841 :type description: str
835 842 :param private: Set the |repo| as private. (True | False)
836 843 :type private: bool
837 844 :param clone_uri: Update the |repo| clone URI.
838 845 :type clone_uri: str
839 846 :param landing_rev: Set the |repo| landing revision. Default is ``rev:tip``.
840 847 :type landing_rev: str
841 848 :param enable_statistics: Enable statistics on the |repo|, (True | False).
842 849 :type enable_statistics: bool
843 850 :param enable_locking: Enable |repo| locking.
844 851 :type enable_locking: bool
845 852 :param enable_downloads: Enable downloads from the |repo|, (True | False).
846 853 :type enable_downloads: bool
847 854 :param fields: Add extra fields to the |repo|. Use the following
848 855 example format: ``field_key=field_val,field_key2=fieldval2``.
849 856 Escape ', ' with \,
850 857 :type fields: str
851 858 """
852 859
853 860 repo = get_repo_or_error(repoid)
854 861
855 862 include_secrets = False
856 863 if not has_superadmin_permission(apiuser):
857 864 validate_repo_permissions(apiuser, repoid, repo, ('repository.admin',))
858 865 else:
859 866 include_secrets = True
860 867
861 868 updates = dict(
862 869 repo_name=repo_name
863 870 if not isinstance(repo_name, Optional) else repo.repo_name,
864 871
865 872 fork_id=fork_of
866 873 if not isinstance(fork_of, Optional) else repo.fork.repo_name if repo.fork else None,
867 874
868 875 user=owner
869 876 if not isinstance(owner, Optional) else repo.user.username,
870 877
871 878 repo_description=description
872 879 if not isinstance(description, Optional) else repo.description,
873 880
874 881 repo_private=private
875 882 if not isinstance(private, Optional) else repo.private,
876 883
877 884 clone_uri=clone_uri
878 885 if not isinstance(clone_uri, Optional) else repo.clone_uri,
879 886
887 push_uri=push_uri
888 if not isinstance(push_uri, Optional) else repo.push_uri,
889
880 890 repo_landing_rev=landing_rev
881 891 if not isinstance(landing_rev, Optional) else repo._landing_revision,
882 892
883 893 repo_enable_statistics=enable_statistics
884 894 if not isinstance(enable_statistics, Optional) else repo.enable_statistics,
885 895
886 896 repo_enable_locking=enable_locking
887 897 if not isinstance(enable_locking, Optional) else repo.enable_locking,
888 898
889 899 repo_enable_downloads=enable_downloads
890 900 if not isinstance(enable_downloads, Optional) else repo.enable_downloads)
891 901
892 902 ref_choices, _labels = ScmModel().get_repo_landing_revs(
893 903 request.translate, repo=repo)
894 904
895 905 old_values = repo.get_api_data()
896 906 repo_type = repo.repo_type
897 907 schema = repo_schema.RepoSchema().bind(
898 908 repo_type_options=rhodecode.BACKENDS.keys(),
899 909 repo_ref_options=ref_choices,
900 910 repo_type=repo_type,
901 911 # user caller
902 912 user=apiuser,
903 913 old_values=old_values)
904 914 try:
905 915 schema_data = schema.deserialize(dict(
906 916 # we save old value, users cannot change type
907 917 repo_type=repo_type,
908 918
909 919 repo_name=updates['repo_name'],
910 920 repo_owner=updates['user'],
911 921 repo_description=updates['repo_description'],
912 922 repo_clone_uri=updates['clone_uri'],
923 repo_push_uri=updates['push_uri'],
913 924 repo_fork_of=updates['fork_id'],
914 925 repo_private=updates['repo_private'],
915 926 repo_landing_commit_ref=updates['repo_landing_rev'],
916 927 repo_enable_statistics=updates['repo_enable_statistics'],
917 928 repo_enable_downloads=updates['repo_enable_downloads'],
918 929 repo_enable_locking=updates['repo_enable_locking']))
919 930 except validation_schema.Invalid as err:
920 931 raise JSONRPCValidationError(colander_exc=err)
921 932
922 933 # save validated data back into the updates dict
923 934 validated_updates = dict(
924 935 repo_name=schema_data['repo_group']['repo_name_without_group'],
925 936 repo_group=schema_data['repo_group']['repo_group_id'],
926 937
927 938 user=schema_data['repo_owner'],
928 939 repo_description=schema_data['repo_description'],
929 940 repo_private=schema_data['repo_private'],
930 941 clone_uri=schema_data['repo_clone_uri'],
942 push_uri=schema_data['repo_push_uri'],
931 943 repo_landing_rev=schema_data['repo_landing_commit_ref'],
932 944 repo_enable_statistics=schema_data['repo_enable_statistics'],
933 945 repo_enable_locking=schema_data['repo_enable_locking'],
934 946 repo_enable_downloads=schema_data['repo_enable_downloads'],
935 947 )
936 948
937 949 if schema_data['repo_fork_of']:
938 950 fork_repo = get_repo_or_error(schema_data['repo_fork_of'])
939 951 validated_updates['fork_id'] = fork_repo.repo_id
940 952
941 953 # extra fields
942 954 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
943 955 if fields:
944 956 validated_updates.update(fields)
945 957
946 958 try:
947 959 RepoModel().update(repo, **validated_updates)
948 960 audit_logger.store_api(
949 961 'repo.edit', action_data={'old_data': old_values},
950 962 user=apiuser, repo=repo)
951 963 Session().commit()
952 964 return {
953 965 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
954 966 'repository': repo.get_api_data(include_secrets=include_secrets)
955 967 }
956 968 except Exception:
957 969 log.exception(
958 970 u"Exception while trying to update the repository %s",
959 971 repoid)
960 972 raise JSONRPCError('failed to update repo `%s`' % repoid)
961 973
962 974
963 975 @jsonrpc_method()
964 976 def fork_repo(request, apiuser, repoid, fork_name,
965 977 owner=Optional(OAttr('apiuser')),
966 978 description=Optional(''),
967 979 private=Optional(False),
968 980 clone_uri=Optional(None),
969 981 landing_rev=Optional('rev:tip'),
970 982 copy_permissions=Optional(False)):
971 983 """
972 984 Creates a fork of the specified |repo|.
973 985
974 986 * If the fork_name contains "/", fork will be created inside
975 987 a repository group or nested repository groups
976 988
977 989 For example "foo/bar/fork-repo" will create fork called "fork-repo"
978 990 inside group "foo/bar". You have to have permissions to access and
979 991 write to the last repository group ("bar" in this example)
980 992
981 993 This command can only be run using an |authtoken| with minimum
982 994 read permissions of the forked repo, create fork permissions for an user.
983 995
984 996 :param apiuser: This is filled automatically from the |authtoken|.
985 997 :type apiuser: AuthUser
986 998 :param repoid: Set repository name or repository ID.
987 999 :type repoid: str or int
988 1000 :param fork_name: Set the fork name, including it's repository group membership.
989 1001 :type fork_name: str
990 1002 :param owner: Set the fork owner.
991 1003 :type owner: str
992 1004 :param description: Set the fork description.
993 1005 :type description: str
994 1006 :param copy_permissions: Copy permissions from parent |repo|. The
995 1007 default is False.
996 1008 :type copy_permissions: bool
997 1009 :param private: Make the fork private. The default is False.
998 1010 :type private: bool
999 1011 :param landing_rev: Set the landing revision. The default is tip.
1000 1012
1001 1013 Example output:
1002 1014
1003 1015 .. code-block:: bash
1004 1016
1005 1017 id : <id_for_response>
1006 1018 api_key : "<api_key>"
1007 1019 args: {
1008 1020 "repoid" : "<reponame or repo_id>",
1009 1021 "fork_name": "<forkname>",
1010 1022 "owner": "<username or user_id = Optional(=apiuser)>",
1011 1023 "description": "<description>",
1012 1024 "copy_permissions": "<bool>",
1013 1025 "private": "<bool>",
1014 1026 "landing_rev": "<landing_rev>"
1015 1027 }
1016 1028
1017 1029 Example error output:
1018 1030
1019 1031 .. code-block:: bash
1020 1032
1021 1033 id : <id_given_in_input>
1022 1034 result: {
1023 1035 "msg": "Created fork of `<reponame>` as `<forkname>`",
1024 1036 "success": true,
1025 1037 "task": "<celery task id or None if done sync>"
1026 1038 }
1027 1039 error: null
1028 1040
1029 1041 """
1030 1042
1031 1043 repo = get_repo_or_error(repoid)
1032 1044 repo_name = repo.repo_name
1033 1045
1034 1046 if not has_superadmin_permission(apiuser):
1035 1047 # check if we have at least read permission for
1036 1048 # this repo that we fork !
1037 1049 _perms = (
1038 1050 'repository.admin', 'repository.write', 'repository.read')
1039 1051 validate_repo_permissions(apiuser, repoid, repo, _perms)
1040 1052
1041 1053 # check if the regular user has at least fork permissions as well
1042 1054 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
1043 1055 raise JSONRPCForbidden()
1044 1056
1045 1057 # check if user can set owner parameter
1046 1058 owner = validate_set_owner_permissions(apiuser, owner)
1047 1059
1048 1060 description = Optional.extract(description)
1049 1061 copy_permissions = Optional.extract(copy_permissions)
1050 1062 clone_uri = Optional.extract(clone_uri)
1051 1063 landing_commit_ref = Optional.extract(landing_rev)
1052 1064 private = Optional.extract(private)
1053 1065
1054 1066 schema = repo_schema.RepoSchema().bind(
1055 1067 repo_type_options=rhodecode.BACKENDS.keys(),
1056 1068 repo_type=repo.repo_type,
1057 1069 # user caller
1058 1070 user=apiuser)
1059 1071
1060 1072 try:
1061 1073 schema_data = schema.deserialize(dict(
1062 1074 repo_name=fork_name,
1063 1075 repo_type=repo.repo_type,
1064 1076 repo_owner=owner.username,
1065 1077 repo_description=description,
1066 1078 repo_landing_commit_ref=landing_commit_ref,
1067 1079 repo_clone_uri=clone_uri,
1068 1080 repo_private=private,
1069 1081 repo_copy_permissions=copy_permissions))
1070 1082 except validation_schema.Invalid as err:
1071 1083 raise JSONRPCValidationError(colander_exc=err)
1072 1084
1073 1085 try:
1074 1086 data = {
1075 1087 'fork_parent_id': repo.repo_id,
1076 1088
1077 1089 'repo_name': schema_data['repo_group']['repo_name_without_group'],
1078 1090 'repo_name_full': schema_data['repo_name'],
1079 1091 'repo_group': schema_data['repo_group']['repo_group_id'],
1080 1092 'repo_type': schema_data['repo_type'],
1081 1093 'description': schema_data['repo_description'],
1082 1094 'private': schema_data['repo_private'],
1083 1095 'copy_permissions': schema_data['repo_copy_permissions'],
1084 1096 'landing_rev': schema_data['repo_landing_commit_ref'],
1085 1097 }
1086 1098
1087 task = RepoModel().create_fork(data, cur_user=owner)
1099 task = RepoModel().create_fork(data, cur_user=owner.user_id)
1088 1100 # no commit, it's done in RepoModel, or async via celery
1089 1101 task_id = get_task_id(task)
1090 1102
1091 1103 return {
1092 1104 'msg': 'Created fork of `%s` as `%s`' % (
1093 1105 repo.repo_name, schema_data['repo_name']),
1094 1106 'success': True, # cannot return the repo data here since fork
1095 1107 # can be done async
1096 1108 'task': task_id
1097 1109 }
1098 1110 except Exception:
1099 1111 log.exception(
1100 1112 u"Exception while trying to create fork %s",
1101 1113 schema_data['repo_name'])
1102 1114 raise JSONRPCError(
1103 1115 'failed to fork repository `%s` as `%s`' % (
1104 1116 repo_name, schema_data['repo_name']))
1105 1117
1106 1118
1107 1119 @jsonrpc_method()
1108 1120 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1109 1121 """
1110 1122 Deletes a repository.
1111 1123
1112 1124 * When the `forks` parameter is set it's possible to detach or delete
1113 1125 forks of deleted repository.
1114 1126
1115 1127 This command can only be run using an |authtoken| with admin
1116 1128 permissions on the |repo|.
1117 1129
1118 1130 :param apiuser: This is filled automatically from the |authtoken|.
1119 1131 :type apiuser: AuthUser
1120 1132 :param repoid: Set the repository name or repository ID.
1121 1133 :type repoid: str or int
1122 1134 :param forks: Set to `detach` or `delete` forks from the |repo|.
1123 1135 :type forks: Optional(str)
1124 1136
1125 1137 Example error output:
1126 1138
1127 1139 .. code-block:: bash
1128 1140
1129 1141 id : <id_given_in_input>
1130 1142 result: {
1131 1143 "msg": "Deleted repository `<reponame>`",
1132 1144 "success": true
1133 1145 }
1134 1146 error: null
1135 1147 """
1136 1148
1137 1149 repo = get_repo_or_error(repoid)
1138 1150 repo_name = repo.repo_name
1139 1151 if not has_superadmin_permission(apiuser):
1140 1152 _perms = ('repository.admin',)
1141 1153 validate_repo_permissions(apiuser, repoid, repo, _perms)
1142 1154
1143 1155 try:
1144 1156 handle_forks = Optional.extract(forks)
1145 1157 _forks_msg = ''
1146 1158 _forks = [f for f in repo.forks]
1147 1159 if handle_forks == 'detach':
1148 1160 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1149 1161 elif handle_forks == 'delete':
1150 1162 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1151 1163 elif _forks:
1152 1164 raise JSONRPCError(
1153 1165 'Cannot delete `%s` it still contains attached forks' %
1154 1166 (repo.repo_name,)
1155 1167 )
1156 1168 old_data = repo.get_api_data()
1157 1169 RepoModel().delete(repo, forks=forks)
1158 1170
1159 1171 repo = audit_logger.RepoWrap(repo_id=None,
1160 1172 repo_name=repo.repo_name)
1161 1173
1162 1174 audit_logger.store_api(
1163 1175 'repo.delete', action_data={'old_data': old_data},
1164 1176 user=apiuser, repo=repo)
1165 1177
1166 1178 ScmModel().mark_for_invalidation(repo_name, delete=True)
1167 1179 Session().commit()
1168 1180 return {
1169 1181 'msg': 'Deleted repository `%s`%s' % (repo_name, _forks_msg),
1170 1182 'success': True
1171 1183 }
1172 1184 except Exception:
1173 1185 log.exception("Exception occurred while trying to delete repo")
1174 1186 raise JSONRPCError(
1175 1187 'failed to delete repository `%s`' % (repo_name,)
1176 1188 )
1177 1189
1178 1190
1179 1191 #TODO: marcink, change name ?
1180 1192 @jsonrpc_method()
1181 1193 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1182 1194 """
1183 1195 Invalidates the cache for the specified repository.
1184 1196
1185 1197 This command can only be run using an |authtoken| with admin rights to
1186 1198 the specified repository.
1187 1199
1188 1200 This command takes the following options:
1189 1201
1190 1202 :param apiuser: This is filled automatically from |authtoken|.
1191 1203 :type apiuser: AuthUser
1192 1204 :param repoid: Sets the repository name or repository ID.
1193 1205 :type repoid: str or int
1194 1206 :param delete_keys: This deletes the invalidated keys instead of
1195 1207 just flagging them.
1196 1208 :type delete_keys: Optional(``True`` | ``False``)
1197 1209
1198 1210 Example output:
1199 1211
1200 1212 .. code-block:: bash
1201 1213
1202 1214 id : <id_given_in_input>
1203 1215 result : {
1204 1216 'msg': Cache for repository `<repository name>` was invalidated,
1205 1217 'repository': <repository name>
1206 1218 }
1207 1219 error : null
1208 1220
1209 1221 Example error output:
1210 1222
1211 1223 .. code-block:: bash
1212 1224
1213 1225 id : <id_given_in_input>
1214 1226 result : null
1215 1227 error : {
1216 1228 'Error occurred during cache invalidation action'
1217 1229 }
1218 1230
1219 1231 """
1220 1232
1221 1233 repo = get_repo_or_error(repoid)
1222 1234 if not has_superadmin_permission(apiuser):
1223 1235 _perms = ('repository.admin', 'repository.write',)
1224 1236 validate_repo_permissions(apiuser, repoid, repo, _perms)
1225 1237
1226 1238 delete = Optional.extract(delete_keys)
1227 1239 try:
1228 1240 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1229 1241 return {
1230 1242 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1231 1243 'repository': repo.repo_name
1232 1244 }
1233 1245 except Exception:
1234 1246 log.exception(
1235 1247 "Exception occurred while trying to invalidate repo cache")
1236 1248 raise JSONRPCError(
1237 1249 'Error occurred during cache invalidation action'
1238 1250 )
1239 1251
1240 1252
1241 1253 #TODO: marcink, change name ?
1242 1254 @jsonrpc_method()
1243 1255 def lock(request, apiuser, repoid, locked=Optional(None),
1244 1256 userid=Optional(OAttr('apiuser'))):
1245 1257 """
1246 1258 Sets the lock state of the specified |repo| by the given user.
1247 1259 From more information, see :ref:`repo-locking`.
1248 1260
1249 1261 * If the ``userid`` option is not set, the repository is locked to the
1250 1262 user who called the method.
1251 1263 * If the ``locked`` parameter is not set, the current lock state of the
1252 1264 repository is displayed.
1253 1265
1254 1266 This command can only be run using an |authtoken| with admin rights to
1255 1267 the specified repository.
1256 1268
1257 1269 This command takes the following options:
1258 1270
1259 1271 :param apiuser: This is filled automatically from the |authtoken|.
1260 1272 :type apiuser: AuthUser
1261 1273 :param repoid: Sets the repository name or repository ID.
1262 1274 :type repoid: str or int
1263 1275 :param locked: Sets the lock state.
1264 1276 :type locked: Optional(``True`` | ``False``)
1265 1277 :param userid: Set the repository lock to this user.
1266 1278 :type userid: Optional(str or int)
1267 1279
1268 1280 Example error output:
1269 1281
1270 1282 .. code-block:: bash
1271 1283
1272 1284 id : <id_given_in_input>
1273 1285 result : {
1274 1286 'repo': '<reponame>',
1275 1287 'locked': <bool: lock state>,
1276 1288 'locked_since': <int: lock timestamp>,
1277 1289 'locked_by': <username of person who made the lock>,
1278 1290 'lock_reason': <str: reason for locking>,
1279 1291 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1280 1292 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1281 1293 or
1282 1294 'msg': 'Repo `<repository name>` not locked.'
1283 1295 or
1284 1296 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1285 1297 }
1286 1298 error : null
1287 1299
1288 1300 Example error output:
1289 1301
1290 1302 .. code-block:: bash
1291 1303
1292 1304 id : <id_given_in_input>
1293 1305 result : null
1294 1306 error : {
1295 1307 'Error occurred locking repository `<reponame>`'
1296 1308 }
1297 1309 """
1298 1310
1299 1311 repo = get_repo_or_error(repoid)
1300 1312 if not has_superadmin_permission(apiuser):
1301 1313 # check if we have at least write permission for this repo !
1302 1314 _perms = ('repository.admin', 'repository.write',)
1303 1315 validate_repo_permissions(apiuser, repoid, repo, _perms)
1304 1316
1305 1317 # make sure normal user does not pass someone else userid,
1306 1318 # he is not allowed to do that
1307 1319 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1308 1320 raise JSONRPCError('userid is not the same as your user')
1309 1321
1310 1322 if isinstance(userid, Optional):
1311 1323 userid = apiuser.user_id
1312 1324
1313 1325 user = get_user_or_error(userid)
1314 1326
1315 1327 if isinstance(locked, Optional):
1316 1328 lockobj = repo.locked
1317 1329
1318 1330 if lockobj[0] is None:
1319 1331 _d = {
1320 1332 'repo': repo.repo_name,
1321 1333 'locked': False,
1322 1334 'locked_since': None,
1323 1335 'locked_by': None,
1324 1336 'lock_reason': None,
1325 1337 'lock_state_changed': False,
1326 1338 'msg': 'Repo `%s` not locked.' % repo.repo_name
1327 1339 }
1328 1340 return _d
1329 1341 else:
1330 1342 _user_id, _time, _reason = lockobj
1331 1343 lock_user = get_user_or_error(userid)
1332 1344 _d = {
1333 1345 'repo': repo.repo_name,
1334 1346 'locked': True,
1335 1347 'locked_since': _time,
1336 1348 'locked_by': lock_user.username,
1337 1349 'lock_reason': _reason,
1338 1350 'lock_state_changed': False,
1339 1351 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1340 1352 % (repo.repo_name, lock_user.username,
1341 1353 json.dumps(time_to_datetime(_time))))
1342 1354 }
1343 1355 return _d
1344 1356
1345 1357 # force locked state through a flag
1346 1358 else:
1347 1359 locked = str2bool(locked)
1348 1360 lock_reason = Repository.LOCK_API
1349 1361 try:
1350 1362 if locked:
1351 1363 lock_time = time.time()
1352 1364 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1353 1365 else:
1354 1366 lock_time = None
1355 1367 Repository.unlock(repo)
1356 1368 _d = {
1357 1369 'repo': repo.repo_name,
1358 1370 'locked': locked,
1359 1371 'locked_since': lock_time,
1360 1372 'locked_by': user.username,
1361 1373 'lock_reason': lock_reason,
1362 1374 'lock_state_changed': True,
1363 1375 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1364 1376 % (user.username, repo.repo_name, locked))
1365 1377 }
1366 1378 return _d
1367 1379 except Exception:
1368 1380 log.exception(
1369 1381 "Exception occurred while trying to lock repository")
1370 1382 raise JSONRPCError(
1371 1383 'Error occurred locking repository `%s`' % repo.repo_name
1372 1384 )
1373 1385
1374 1386
1375 1387 @jsonrpc_method()
1376 1388 def comment_commit(
1377 1389 request, apiuser, repoid, commit_id, message, status=Optional(None),
1378 1390 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
1379 1391 resolves_comment_id=Optional(None),
1380 1392 userid=Optional(OAttr('apiuser'))):
1381 1393 """
1382 1394 Set a commit comment, and optionally change the status of the commit.
1383 1395
1384 1396 :param apiuser: This is filled automatically from the |authtoken|.
1385 1397 :type apiuser: AuthUser
1386 1398 :param repoid: Set the repository name or repository ID.
1387 1399 :type repoid: str or int
1388 1400 :param commit_id: Specify the commit_id for which to set a comment.
1389 1401 :type commit_id: str
1390 1402 :param message: The comment text.
1391 1403 :type message: str
1392 1404 :param status: (**Optional**) status of commit, one of: 'not_reviewed',
1393 1405 'approved', 'rejected', 'under_review'
1394 1406 :type status: str
1395 1407 :param comment_type: Comment type, one of: 'note', 'todo'
1396 1408 :type comment_type: Optional(str), default: 'note'
1397 1409 :param userid: Set the user name of the comment creator.
1398 1410 :type userid: Optional(str or int)
1399 1411
1400 1412 Example error output:
1401 1413
1402 1414 .. code-block:: bash
1403 1415
1404 1416 {
1405 1417 "id" : <id_given_in_input>,
1406 1418 "result" : {
1407 1419 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1408 1420 "status_change": null or <status>,
1409 1421 "success": true
1410 1422 },
1411 1423 "error" : null
1412 1424 }
1413 1425
1414 1426 """
1415 1427 repo = get_repo_or_error(repoid)
1416 1428 if not has_superadmin_permission(apiuser):
1417 1429 _perms = ('repository.read', 'repository.write', 'repository.admin')
1418 1430 validate_repo_permissions(apiuser, repoid, repo, _perms)
1419 1431
1420 1432 try:
1421 1433 commit_id = repo.scm_instance().get_commit(commit_id=commit_id).raw_id
1422 1434 except Exception as e:
1423 1435 log.exception('Failed to fetch commit')
1424 1436 raise JSONRPCError(e.message)
1425 1437
1426 1438 if isinstance(userid, Optional):
1427 1439 userid = apiuser.user_id
1428 1440
1429 1441 user = get_user_or_error(userid)
1430 1442 status = Optional.extract(status)
1431 1443 comment_type = Optional.extract(comment_type)
1432 1444 resolves_comment_id = Optional.extract(resolves_comment_id)
1433 1445
1434 1446 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1435 1447 if status and status not in allowed_statuses:
1436 1448 raise JSONRPCError('Bad status, must be on '
1437 1449 'of %s got %s' % (allowed_statuses, status,))
1438 1450
1439 1451 if resolves_comment_id:
1440 1452 comment = ChangesetComment.get(resolves_comment_id)
1441 1453 if not comment:
1442 1454 raise JSONRPCError(
1443 1455 'Invalid resolves_comment_id `%s` for this commit.'
1444 1456 % resolves_comment_id)
1445 1457 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
1446 1458 raise JSONRPCError(
1447 1459 'Comment `%s` is wrong type for setting status to resolved.'
1448 1460 % resolves_comment_id)
1449 1461
1450 1462 try:
1451 1463 rc_config = SettingsModel().get_all_settings()
1452 1464 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1453 1465 status_change_label = ChangesetStatus.get_status_lbl(status)
1454 1466 comment = CommentsModel().create(
1455 1467 message, repo, user, commit_id=commit_id,
1456 1468 status_change=status_change_label,
1457 1469 status_change_type=status,
1458 1470 renderer=renderer,
1459 1471 comment_type=comment_type,
1460 1472 resolves_comment_id=resolves_comment_id
1461 1473 )
1462 1474 if status:
1463 1475 # also do a status change
1464 1476 try:
1465 1477 ChangesetStatusModel().set_status(
1466 1478 repo, status, user, comment, revision=commit_id,
1467 1479 dont_allow_on_closed_pull_request=True
1468 1480 )
1469 1481 except StatusChangeOnClosedPullRequestError:
1470 1482 log.exception(
1471 1483 "Exception occurred while trying to change repo commit status")
1472 1484 msg = ('Changing status on a changeset associated with '
1473 1485 'a closed pull request is not allowed')
1474 1486 raise JSONRPCError(msg)
1475 1487
1476 1488 Session().commit()
1477 1489 return {
1478 1490 'msg': (
1479 1491 'Commented on commit `%s` for repository `%s`' % (
1480 1492 comment.revision, repo.repo_name)),
1481 1493 'status_change': status,
1482 1494 'success': True,
1483 1495 }
1484 1496 except JSONRPCError:
1485 1497 # catch any inside errors, and re-raise them to prevent from
1486 1498 # below global catch to silence them
1487 1499 raise
1488 1500 except Exception:
1489 1501 log.exception("Exception occurred while trying to comment on commit")
1490 1502 raise JSONRPCError(
1491 1503 'failed to set comment on repository `%s`' % (repo.repo_name,)
1492 1504 )
1493 1505
1494 1506
1495 1507 @jsonrpc_method()
1496 1508 def grant_user_permission(request, apiuser, repoid, userid, perm):
1497 1509 """
1498 1510 Grant permissions for the specified user on the given repository,
1499 1511 or update existing permissions if found.
1500 1512
1501 1513 This command can only be run using an |authtoken| with admin
1502 1514 permissions on the |repo|.
1503 1515
1504 1516 :param apiuser: This is filled automatically from the |authtoken|.
1505 1517 :type apiuser: AuthUser
1506 1518 :param repoid: Set the repository name or repository ID.
1507 1519 :type repoid: str or int
1508 1520 :param userid: Set the user name.
1509 1521 :type userid: str
1510 1522 :param perm: Set the user permissions, using the following format
1511 1523 ``(repository.(none|read|write|admin))``
1512 1524 :type perm: str
1513 1525
1514 1526 Example output:
1515 1527
1516 1528 .. code-block:: bash
1517 1529
1518 1530 id : <id_given_in_input>
1519 1531 result: {
1520 1532 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1521 1533 "success": true
1522 1534 }
1523 1535 error: null
1524 1536 """
1525 1537
1526 1538 repo = get_repo_or_error(repoid)
1527 1539 user = get_user_or_error(userid)
1528 1540 perm = get_perm_or_error(perm)
1529 1541 if not has_superadmin_permission(apiuser):
1530 1542 _perms = ('repository.admin',)
1531 1543 validate_repo_permissions(apiuser, repoid, repo, _perms)
1532 1544
1533 1545 try:
1534 1546
1535 1547 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1536 1548
1537 1549 Session().commit()
1538 1550 return {
1539 1551 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1540 1552 perm.permission_name, user.username, repo.repo_name
1541 1553 ),
1542 1554 'success': True
1543 1555 }
1544 1556 except Exception:
1545 1557 log.exception(
1546 1558 "Exception occurred while trying edit permissions for repo")
1547 1559 raise JSONRPCError(
1548 1560 'failed to edit permission for user: `%s` in repo: `%s`' % (
1549 1561 userid, repoid
1550 1562 )
1551 1563 )
1552 1564
1553 1565
1554 1566 @jsonrpc_method()
1555 1567 def revoke_user_permission(request, apiuser, repoid, userid):
1556 1568 """
1557 1569 Revoke permission for a user on the specified repository.
1558 1570
1559 1571 This command can only be run using an |authtoken| with admin
1560 1572 permissions on the |repo|.
1561 1573
1562 1574 :param apiuser: This is filled automatically from the |authtoken|.
1563 1575 :type apiuser: AuthUser
1564 1576 :param repoid: Set the repository name or repository ID.
1565 1577 :type repoid: str or int
1566 1578 :param userid: Set the user name of revoked user.
1567 1579 :type userid: str or int
1568 1580
1569 1581 Example error output:
1570 1582
1571 1583 .. code-block:: bash
1572 1584
1573 1585 id : <id_given_in_input>
1574 1586 result: {
1575 1587 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1576 1588 "success": true
1577 1589 }
1578 1590 error: null
1579 1591 """
1580 1592
1581 1593 repo = get_repo_or_error(repoid)
1582 1594 user = get_user_or_error(userid)
1583 1595 if not has_superadmin_permission(apiuser):
1584 1596 _perms = ('repository.admin',)
1585 1597 validate_repo_permissions(apiuser, repoid, repo, _perms)
1586 1598
1587 1599 try:
1588 1600 RepoModel().revoke_user_permission(repo=repo, user=user)
1589 1601 Session().commit()
1590 1602 return {
1591 1603 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1592 1604 user.username, repo.repo_name
1593 1605 ),
1594 1606 'success': True
1595 1607 }
1596 1608 except Exception:
1597 1609 log.exception(
1598 1610 "Exception occurred while trying revoke permissions to repo")
1599 1611 raise JSONRPCError(
1600 1612 'failed to edit permission for user: `%s` in repo: `%s`' % (
1601 1613 userid, repoid
1602 1614 )
1603 1615 )
1604 1616
1605 1617
1606 1618 @jsonrpc_method()
1607 1619 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1608 1620 """
1609 1621 Grant permission for a user group on the specified repository,
1610 1622 or update existing permissions.
1611 1623
1612 1624 This command can only be run using an |authtoken| with admin
1613 1625 permissions on the |repo|.
1614 1626
1615 1627 :param apiuser: This is filled automatically from the |authtoken|.
1616 1628 :type apiuser: AuthUser
1617 1629 :param repoid: Set the repository name or repository ID.
1618 1630 :type repoid: str or int
1619 1631 :param usergroupid: Specify the ID of the user group.
1620 1632 :type usergroupid: str or int
1621 1633 :param perm: Set the user group permissions using the following
1622 1634 format: (repository.(none|read|write|admin))
1623 1635 :type perm: str
1624 1636
1625 1637 Example output:
1626 1638
1627 1639 .. code-block:: bash
1628 1640
1629 1641 id : <id_given_in_input>
1630 1642 result : {
1631 1643 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1632 1644 "success": true
1633 1645
1634 1646 }
1635 1647 error : null
1636 1648
1637 1649 Example error output:
1638 1650
1639 1651 .. code-block:: bash
1640 1652
1641 1653 id : <id_given_in_input>
1642 1654 result : null
1643 1655 error : {
1644 1656 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1645 1657 }
1646 1658
1647 1659 """
1648 1660
1649 1661 repo = get_repo_or_error(repoid)
1650 1662 perm = get_perm_or_error(perm)
1651 1663 if not has_superadmin_permission(apiuser):
1652 1664 _perms = ('repository.admin',)
1653 1665 validate_repo_permissions(apiuser, repoid, repo, _perms)
1654 1666
1655 1667 user_group = get_user_group_or_error(usergroupid)
1656 1668 if not has_superadmin_permission(apiuser):
1657 1669 # check if we have at least read permission for this user group !
1658 1670 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1659 1671 if not HasUserGroupPermissionAnyApi(*_perms)(
1660 1672 user=apiuser, user_group_name=user_group.users_group_name):
1661 1673 raise JSONRPCError(
1662 1674 'user group `%s` does not exist' % (usergroupid,))
1663 1675
1664 1676 try:
1665 1677 RepoModel().grant_user_group_permission(
1666 1678 repo=repo, group_name=user_group, perm=perm)
1667 1679
1668 1680 Session().commit()
1669 1681 return {
1670 1682 'msg': 'Granted perm: `%s` for user group: `%s` in '
1671 1683 'repo: `%s`' % (
1672 1684 perm.permission_name, user_group.users_group_name,
1673 1685 repo.repo_name
1674 1686 ),
1675 1687 'success': True
1676 1688 }
1677 1689 except Exception:
1678 1690 log.exception(
1679 1691 "Exception occurred while trying change permission on repo")
1680 1692 raise JSONRPCError(
1681 1693 'failed to edit permission for user group: `%s` in '
1682 1694 'repo: `%s`' % (
1683 1695 usergroupid, repo.repo_name
1684 1696 )
1685 1697 )
1686 1698
1687 1699
1688 1700 @jsonrpc_method()
1689 1701 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1690 1702 """
1691 1703 Revoke the permissions of a user group on a given repository.
1692 1704
1693 1705 This command can only be run using an |authtoken| with admin
1694 1706 permissions on the |repo|.
1695 1707
1696 1708 :param apiuser: This is filled automatically from the |authtoken|.
1697 1709 :type apiuser: AuthUser
1698 1710 :param repoid: Set the repository name or repository ID.
1699 1711 :type repoid: str or int
1700 1712 :param usergroupid: Specify the user group ID.
1701 1713 :type usergroupid: str or int
1702 1714
1703 1715 Example output:
1704 1716
1705 1717 .. code-block:: bash
1706 1718
1707 1719 id : <id_given_in_input>
1708 1720 result: {
1709 1721 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1710 1722 "success": true
1711 1723 }
1712 1724 error: null
1713 1725 """
1714 1726
1715 1727 repo = get_repo_or_error(repoid)
1716 1728 if not has_superadmin_permission(apiuser):
1717 1729 _perms = ('repository.admin',)
1718 1730 validate_repo_permissions(apiuser, repoid, repo, _perms)
1719 1731
1720 1732 user_group = get_user_group_or_error(usergroupid)
1721 1733 if not has_superadmin_permission(apiuser):
1722 1734 # check if we have at least read permission for this user group !
1723 1735 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1724 1736 if not HasUserGroupPermissionAnyApi(*_perms)(
1725 1737 user=apiuser, user_group_name=user_group.users_group_name):
1726 1738 raise JSONRPCError(
1727 1739 'user group `%s` does not exist' % (usergroupid,))
1728 1740
1729 1741 try:
1730 1742 RepoModel().revoke_user_group_permission(
1731 1743 repo=repo, group_name=user_group)
1732 1744
1733 1745 Session().commit()
1734 1746 return {
1735 1747 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1736 1748 user_group.users_group_name, repo.repo_name
1737 1749 ),
1738 1750 'success': True
1739 1751 }
1740 1752 except Exception:
1741 1753 log.exception("Exception occurred while trying revoke "
1742 1754 "user group permission on repo")
1743 1755 raise JSONRPCError(
1744 1756 'failed to edit permission for user group: `%s` in '
1745 1757 'repo: `%s`' % (
1746 1758 user_group.users_group_name, repo.repo_name
1747 1759 )
1748 1760 )
1749 1761
1750 1762
1751 1763 @jsonrpc_method()
1752 def pull(request, apiuser, repoid):
1764 def pull(request, apiuser, repoid, remote_uri=Optional(None)):
1753 1765 """
1754 1766 Triggers a pull on the given repository from a remote location. You
1755 1767 can use this to keep remote repositories up-to-date.
1756 1768
1757 1769 This command can only be run using an |authtoken| with admin
1758 1770 rights to the specified repository. For more information,
1759 1771 see :ref:`config-token-ref`.
1760 1772
1761 1773 This command takes the following options:
1762 1774
1763 1775 :param apiuser: This is filled automatically from the |authtoken|.
1764 1776 :type apiuser: AuthUser
1765 1777 :param repoid: The repository name or repository ID.
1766 1778 :type repoid: str or int
1779 :param remote_uri: Optional remote URI to pass in for pull
1780 :type remote_uri: str
1767 1781
1768 1782 Example output:
1769 1783
1770 1784 .. code-block:: bash
1771 1785
1772 1786 id : <id_given_in_input>
1773 1787 result : {
1774 "msg": "Pulled from `<repository name>`"
1788 "msg": "Pulled from url `<remote_url>` on repo `<repository name>`"
1775 1789 "repository": "<repository name>"
1776 1790 }
1777 1791 error : null
1778 1792
1779 1793 Example error output:
1780 1794
1781 1795 .. code-block:: bash
1782 1796
1783 1797 id : <id_given_in_input>
1784 1798 result : null
1785 1799 error : {
1786 "Unable to pull changes from `<reponame>`"
1800 "Unable to push changes from `<remote_url>`"
1787 1801 }
1788 1802
1789 1803 """
1790 1804
1791 1805 repo = get_repo_or_error(repoid)
1806 remote_uri = Optional.extract(remote_uri)
1807 remote_uri_display = remote_uri or repo.clone_uri_hidden
1792 1808 if not has_superadmin_permission(apiuser):
1793 1809 _perms = ('repository.admin',)
1794 1810 validate_repo_permissions(apiuser, repoid, repo, _perms)
1795 1811
1796 1812 try:
1797 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1813 ScmModel().pull_changes(
1814 repo.repo_name, apiuser.username, remote_uri=remote_uri)
1798 1815 return {
1799 'msg': 'Pulled from `%s`' % repo.repo_name,
1816 'msg': 'Pulled from url `%s` on repo `%s`' % (
1817 remote_uri_display, repo.repo_name),
1800 1818 'repository': repo.repo_name
1801 1819 }
1802 1820 except Exception:
1803 1821 log.exception("Exception occurred while trying to "
1804 1822 "pull changes from remote location")
1805 1823 raise JSONRPCError(
1806 'Unable to pull changes from `%s`' % repo.repo_name
1824 'Unable to pull changes from `%s`' % remote_uri_display
1807 1825 )
1808 1826
1809 1827
1810 1828 @jsonrpc_method()
1811 1829 def strip(request, apiuser, repoid, revision, branch):
1812 1830 """
1813 1831 Strips the given revision from the specified repository.
1814 1832
1815 1833 * This will remove the revision and all of its decendants.
1816 1834
1817 1835 This command can only be run using an |authtoken| with admin rights to
1818 1836 the specified repository.
1819 1837
1820 1838 This command takes the following options:
1821 1839
1822 1840 :param apiuser: This is filled automatically from the |authtoken|.
1823 1841 :type apiuser: AuthUser
1824 1842 :param repoid: The repository name or repository ID.
1825 1843 :type repoid: str or int
1826 1844 :param revision: The revision you wish to strip.
1827 1845 :type revision: str
1828 1846 :param branch: The branch from which to strip the revision.
1829 1847 :type branch: str
1830 1848
1831 1849 Example output:
1832 1850
1833 1851 .. code-block:: bash
1834 1852
1835 1853 id : <id_given_in_input>
1836 1854 result : {
1837 1855 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1838 1856 "repository": "<repository name>"
1839 1857 }
1840 1858 error : null
1841 1859
1842 1860 Example error output:
1843 1861
1844 1862 .. code-block:: bash
1845 1863
1846 1864 id : <id_given_in_input>
1847 1865 result : null
1848 1866 error : {
1849 1867 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1850 1868 }
1851 1869
1852 1870 """
1853 1871
1854 1872 repo = get_repo_or_error(repoid)
1855 1873 if not has_superadmin_permission(apiuser):
1856 1874 _perms = ('repository.admin',)
1857 1875 validate_repo_permissions(apiuser, repoid, repo, _perms)
1858 1876
1859 1877 try:
1860 1878 ScmModel().strip(repo, revision, branch)
1861 1879 audit_logger.store_api(
1862 1880 'repo.commit.strip', action_data={'commit_id': revision},
1863 1881 repo=repo,
1864 1882 user=apiuser, commit=True)
1865 1883
1866 1884 return {
1867 1885 'msg': 'Stripped commit %s from repo `%s`' % (
1868 1886 revision, repo.repo_name),
1869 1887 'repository': repo.repo_name
1870 1888 }
1871 1889 except Exception:
1872 1890 log.exception("Exception while trying to strip")
1873 1891 raise JSONRPCError(
1874 1892 'Unable to strip commit %s from repo `%s`' % (
1875 1893 revision, repo.repo_name)
1876 1894 )
1877 1895
1878 1896
1879 1897 @jsonrpc_method()
1880 1898 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1881 1899 """
1882 1900 Returns all settings for a repository. If key is given it only returns the
1883 1901 setting identified by the key or null.
1884 1902
1885 1903 :param apiuser: This is filled automatically from the |authtoken|.
1886 1904 :type apiuser: AuthUser
1887 1905 :param repoid: The repository name or repository id.
1888 1906 :type repoid: str or int
1889 1907 :param key: Key of the setting to return.
1890 1908 :type: key: Optional(str)
1891 1909
1892 1910 Example output:
1893 1911
1894 1912 .. code-block:: bash
1895 1913
1896 1914 {
1897 1915 "error": null,
1898 1916 "id": 237,
1899 1917 "result": {
1900 1918 "extensions_largefiles": true,
1901 1919 "extensions_evolve": true,
1902 1920 "hooks_changegroup_push_logger": true,
1903 1921 "hooks_changegroup_repo_size": false,
1904 1922 "hooks_outgoing_pull_logger": true,
1905 1923 "phases_publish": "True",
1906 1924 "rhodecode_hg_use_rebase_for_merging": true,
1907 1925 "rhodecode_pr_merge_enabled": true,
1908 1926 "rhodecode_use_outdated_comments": true
1909 1927 }
1910 1928 }
1911 1929 """
1912 1930
1913 1931 # Restrict access to this api method to admins only.
1914 1932 if not has_superadmin_permission(apiuser):
1915 1933 raise JSONRPCForbidden()
1916 1934
1917 1935 try:
1918 1936 repo = get_repo_or_error(repoid)
1919 1937 settings_model = VcsSettingsModel(repo=repo)
1920 1938 settings = settings_model.get_global_settings()
1921 1939 settings.update(settings_model.get_repo_settings())
1922 1940
1923 1941 # If only a single setting is requested fetch it from all settings.
1924 1942 key = Optional.extract(key)
1925 1943 if key is not None:
1926 1944 settings = settings.get(key, None)
1927 1945 except Exception:
1928 1946 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1929 1947 log.exception(msg)
1930 1948 raise JSONRPCError(msg)
1931 1949
1932 1950 return settings
1933 1951
1934 1952
1935 1953 @jsonrpc_method()
1936 1954 def set_repo_settings(request, apiuser, repoid, settings):
1937 1955 """
1938 1956 Update repository settings. Returns true on success.
1939 1957
1940 1958 :param apiuser: This is filled automatically from the |authtoken|.
1941 1959 :type apiuser: AuthUser
1942 1960 :param repoid: The repository name or repository id.
1943 1961 :type repoid: str or int
1944 1962 :param settings: The new settings for the repository.
1945 1963 :type: settings: dict
1946 1964
1947 1965 Example output:
1948 1966
1949 1967 .. code-block:: bash
1950 1968
1951 1969 {
1952 1970 "error": null,
1953 1971 "id": 237,
1954 1972 "result": true
1955 1973 }
1956 1974 """
1957 1975 # Restrict access to this api method to admins only.
1958 1976 if not has_superadmin_permission(apiuser):
1959 1977 raise JSONRPCForbidden()
1960 1978
1961 1979 if type(settings) is not dict:
1962 1980 raise JSONRPCError('Settings have to be a JSON Object.')
1963 1981
1964 1982 try:
1965 1983 settings_model = VcsSettingsModel(repo=repoid)
1966 1984
1967 1985 # Merge global, repo and incoming settings.
1968 1986 new_settings = settings_model.get_global_settings()
1969 1987 new_settings.update(settings_model.get_repo_settings())
1970 1988 new_settings.update(settings)
1971 1989
1972 1990 # Update the settings.
1973 1991 inherit_global_settings = new_settings.get(
1974 1992 'inherit_global_settings', False)
1975 1993 settings_model.create_or_update_repo_settings(
1976 1994 new_settings, inherit_global_settings=inherit_global_settings)
1977 1995 Session().commit()
1978 1996 except Exception:
1979 1997 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1980 1998 log.exception(msg)
1981 1999 raise JSONRPCError(msg)
1982 2000
1983 2001 # Indicate success.
1984 2002 return True
1985 2003
1986 2004
1987 2005 @jsonrpc_method()
1988 2006 def maintenance(request, apiuser, repoid):
1989 2007 """
1990 2008 Triggers a maintenance on the given repository.
1991 2009
1992 2010 This command can only be run using an |authtoken| with admin
1993 2011 rights to the specified repository. For more information,
1994 2012 see :ref:`config-token-ref`.
1995 2013
1996 2014 This command takes the following options:
1997 2015
1998 2016 :param apiuser: This is filled automatically from the |authtoken|.
1999 2017 :type apiuser: AuthUser
2000 2018 :param repoid: The repository name or repository ID.
2001 2019 :type repoid: str or int
2002 2020
2003 2021 Example output:
2004 2022
2005 2023 .. code-block:: bash
2006 2024
2007 2025 id : <id_given_in_input>
2008 2026 result : {
2009 2027 "msg": "executed maintenance command",
2010 2028 "executed_actions": [
2011 2029 <action_message>, <action_message2>...
2012 2030 ],
2013 2031 "repository": "<repository name>"
2014 2032 }
2015 2033 error : null
2016 2034
2017 2035 Example error output:
2018 2036
2019 2037 .. code-block:: bash
2020 2038
2021 2039 id : <id_given_in_input>
2022 2040 result : null
2023 2041 error : {
2024 2042 "Unable to execute maintenance on `<reponame>`"
2025 2043 }
2026 2044
2027 2045 """
2028 2046
2029 2047 repo = get_repo_or_error(repoid)
2030 2048 if not has_superadmin_permission(apiuser):
2031 2049 _perms = ('repository.admin',)
2032 2050 validate_repo_permissions(apiuser, repoid, repo, _perms)
2033 2051
2034 2052 try:
2035 2053 maintenance = repo_maintenance.RepoMaintenance()
2036 2054 executed_actions = maintenance.execute(repo)
2037 2055
2038 2056 return {
2039 2057 'msg': 'executed maintenance command',
2040 2058 'executed_actions': executed_actions,
2041 2059 'repository': repo.repo_name
2042 2060 }
2043 2061 except Exception:
2044 2062 log.exception("Exception occurred while trying to run maintenance")
2045 2063 raise JSONRPCError(
2046 2064 'Unable to execute maintenance on `%s`' % repo.repo_name)
@@ -1,829 +1,895 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from rhodecode.api import (
24 24 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
25 25 from rhodecode.api.utils import (
26 26 Optional, OAttr, store_update, has_superadmin_permission, get_origin,
27 27 get_user_or_error, get_user_group_or_error, get_perm_or_error)
28 28 from rhodecode.lib import audit_logger
29 29 from rhodecode.lib.auth import HasUserGroupPermissionAnyApi, HasPermissionAnyApi
30 30 from rhodecode.lib.exceptions import UserGroupAssignedException
31 31 from rhodecode.model.db import Session
32 32 from rhodecode.model.scm import UserGroupList
33 33 from rhodecode.model.user_group import UserGroupModel
34 34 from rhodecode.model import validation_schema
35 35 from rhodecode.model.validation_schema.schemas import user_group_schema
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 @jsonrpc_method()
41 41 def get_user_group(request, apiuser, usergroupid):
42 42 """
43 43 Returns the data of an existing user group.
44 44
45 45 This command can only be run using an |authtoken| with admin rights to
46 46 the specified repository.
47 47
48 48 :param apiuser: This is filled automatically from the |authtoken|.
49 49 :type apiuser: AuthUser
50 50 :param usergroupid: Set the user group from which to return data.
51 51 :type usergroupid: str or int
52 52
53 53 Example error output:
54 54
55 55 .. code-block:: bash
56 56
57 57 {
58 58 "error": null,
59 59 "id": <id>,
60 60 "result": {
61 61 "active": true,
62 62 "group_description": "group description",
63 63 "group_name": "group name",
64 64 "permissions": [
65 65 {
66 66 "name": "owner-name",
67 67 "origin": "owner",
68 68 "permission": "usergroup.admin",
69 69 "type": "user"
70 70 },
71 71 {
72 72 {
73 73 "name": "user name",
74 74 "origin": "permission",
75 75 "permission": "usergroup.admin",
76 76 "type": "user"
77 77 },
78 78 {
79 79 "name": "user group name",
80 80 "origin": "permission",
81 81 "permission": "usergroup.write",
82 82 "type": "user_group"
83 83 }
84 84 ],
85 85 "permissions_summary": {
86 86 "repositories": {
87 87 "aa-root-level-repo-1": "repository.admin"
88 88 },
89 89 "repositories_groups": {}
90 90 },
91 91 "owner": "owner name",
92 92 "users": [],
93 93 "users_group_id": 2
94 94 }
95 95 }
96 96
97 97 """
98 98
99 99 user_group = get_user_group_or_error(usergroupid)
100 100 if not has_superadmin_permission(apiuser):
101 101 # check if we have at least read permission for this user group !
102 102 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
103 103 if not HasUserGroupPermissionAnyApi(*_perms)(
104 104 user=apiuser, user_group_name=user_group.users_group_name):
105 105 raise JSONRPCError('user group `%s` does not exist' % (
106 106 usergroupid,))
107 107
108 108 permissions = []
109 109 for _user in user_group.permissions():
110 110 user_data = {
111 111 'name': _user.username,
112 112 'permission': _user.permission,
113 113 'origin': get_origin(_user),
114 114 'type': "user",
115 115 }
116 116 permissions.append(user_data)
117 117
118 118 for _user_group in user_group.permission_user_groups():
119 119 user_group_data = {
120 120 'name': _user_group.users_group_name,
121 121 'permission': _user_group.permission,
122 122 'origin': get_origin(_user_group),
123 123 'type': "user_group",
124 124 }
125 125 permissions.append(user_group_data)
126 126
127 127 data = user_group.get_api_data()
128 128 data["permissions"] = permissions
129 129 data["permissions_summary"] = UserGroupModel().get_perms_summary(
130 130 user_group.users_group_id)
131 131 return data
132 132
133 133
134 134 @jsonrpc_method()
135 135 def get_user_groups(request, apiuser):
136 136 """
137 137 Lists all the existing user groups within RhodeCode.
138 138
139 139 This command can only be run using an |authtoken| with admin rights to
140 140 the specified repository.
141 141
142 142 This command takes the following options:
143 143
144 144 :param apiuser: This is filled automatically from the |authtoken|.
145 145 :type apiuser: AuthUser
146 146
147 147 Example error output:
148 148
149 149 .. code-block:: bash
150 150
151 151 id : <id_given_in_input>
152 152 result : [<user_group_obj>,...]
153 153 error : null
154 154 """
155 155
156 156 include_secrets = has_superadmin_permission(apiuser)
157 157
158 158 result = []
159 159 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
160 160 extras = {'user': apiuser}
161 161 for user_group in UserGroupList(UserGroupModel().get_all(),
162 162 perm_set=_perms, extra_kwargs=extras):
163 163 result.append(
164 164 user_group.get_api_data(include_secrets=include_secrets))
165 165 return result
166 166
167 167
168 168 @jsonrpc_method()
169 169 def create_user_group(
170 170 request, apiuser, group_name, description=Optional(''),
171 owner=Optional(OAttr('apiuser')), active=Optional(True)):
171 owner=Optional(OAttr('apiuser')), active=Optional(True),
172 sync=Optional(None)):
172 173 """
173 174 Creates a new user group.
174 175
175 176 This command can only be run using an |authtoken| with admin rights to
176 177 the specified repository.
177 178
178 179 This command takes the following options:
179 180
180 181 :param apiuser: This is filled automatically from the |authtoken|.
181 182 :type apiuser: AuthUser
182 183 :param group_name: Set the name of the new user group.
183 184 :type group_name: str
184 185 :param description: Give a description of the new user group.
185 186 :type description: str
186 187 :param owner: Set the owner of the new user group.
187 188 If not set, the owner is the |authtoken| user.
188 189 :type owner: Optional(str or int)
189 190 :param active: Set this group as active.
190 191 :type active: Optional(``True`` | ``False``)
192 :param sync: Set enabled or disabled the automatically sync from
193 external authentication types like ldap.
194 :type sync: Optional(``True`` | ``False``)
191 195
192 196 Example output:
193 197
194 198 .. code-block:: bash
195 199
196 200 id : <id_given_in_input>
197 201 result: {
198 202 "msg": "created new user group `<groupname>`",
199 203 "user_group": <user_group_object>
200 204 }
201 205 error: null
202 206
203 207 Example error output:
204 208
205 209 .. code-block:: bash
206 210
207 211 id : <id_given_in_input>
208 212 result : null
209 213 error : {
210 214 "user group `<group name>` already exist"
211 215 or
212 216 "failed to create group `<group name>`"
213 217 }
214 218
215 219 """
216 220
217 221 if not has_superadmin_permission(apiuser):
218 222 if not HasPermissionAnyApi('hg.usergroup.create.true')(user=apiuser):
219 223 raise JSONRPCForbidden()
220 224
221 225 if UserGroupModel().get_by_name(group_name):
222 226 raise JSONRPCError("user group `%s` already exist" % (group_name,))
223 227
224 228 if isinstance(owner, Optional):
225 229 owner = apiuser.user_id
226 230
227 231 owner = get_user_or_error(owner)
228 232 active = Optional.extract(active)
229 233 description = Optional.extract(description)
234 sync = Optional.extract(sync)
235
236 # set the sync option based on group_data
237 group_data = None
238 if sync:
239 group_data = {
240 'extern_type': 'manual_api',
241 'extern_type_set_by': apiuser.username
242 }
230 243
231 244 schema = user_group_schema.UserGroupSchema().bind(
232 245 # user caller
233 246 user=apiuser)
234 247 try:
235 248 schema_data = schema.deserialize(dict(
236 249 user_group_name=group_name,
237 250 user_group_description=description,
238 251 user_group_owner=owner.username,
239 252 user_group_active=active,
240 253 ))
241 254 except validation_schema.Invalid as err:
242 255 raise JSONRPCValidationError(colander_exc=err)
243 256
244 257 try:
245 258 user_group = UserGroupModel().create(
246 259 name=schema_data['user_group_name'],
247 260 description=schema_data['user_group_description'],
248 261 owner=owner,
249 active=schema_data['user_group_active'])
262 active=schema_data['user_group_active'], group_data=group_data)
250 263 Session().flush()
251 264 creation_data = user_group.get_api_data()
252 265 audit_logger.store_api(
253 266 'user_group.create', action_data={'data': creation_data},
254 267 user=apiuser)
255 268 Session().commit()
256 269 return {
257 270 'msg': 'created new user group `%s`' % group_name,
258 271 'user_group': creation_data
259 272 }
260 273 except Exception:
261 274 log.exception("Error occurred during creation of user group")
262 275 raise JSONRPCError('failed to create group `%s`' % (group_name,))
263 276
264 277
265 278 @jsonrpc_method()
266 279 def update_user_group(request, apiuser, usergroupid, group_name=Optional(''),
267 280 description=Optional(''), owner=Optional(None),
268 active=Optional(True)):
281 active=Optional(True), sync=Optional(None)):
269 282 """
270 283 Updates the specified `user group` with the details provided.
271 284
272 285 This command can only be run using an |authtoken| with admin rights to
273 286 the specified repository.
274 287
275 288 :param apiuser: This is filled automatically from the |authtoken|.
276 289 :type apiuser: AuthUser
277 290 :param usergroupid: Set the id of the `user group` to update.
278 291 :type usergroupid: str or int
279 292 :param group_name: Set the new name the `user group`
280 293 :type group_name: str
281 294 :param description: Give a description for the `user group`
282 295 :type description: str
283 296 :param owner: Set the owner of the `user group`.
284 297 :type owner: Optional(str or int)
285 298 :param active: Set the group as active.
286 299 :type active: Optional(``True`` | ``False``)
300 :param sync: Set enabled or disabled the automatically sync from
301 external authentication types like ldap.
302 :type sync: Optional(``True`` | ``False``)
287 303
288 304 Example output:
289 305
290 306 .. code-block:: bash
291 307
292 308 id : <id_given_in_input>
293 309 result : {
294 310 "msg": 'updated user group ID:<user group id> <user group name>',
295 311 "user_group": <user_group_object>
296 312 }
297 313 error : null
298 314
299 315 Example error output:
300 316
301 317 .. code-block:: bash
302 318
303 319 id : <id_given_in_input>
304 320 result : null
305 321 error : {
306 322 "failed to update user group `<user group name>`"
307 323 }
308 324
309 325 """
310 326
311 327 user_group = get_user_group_or_error(usergroupid)
312 328 include_secrets = False
313 329 if not has_superadmin_permission(apiuser):
314 330 # check if we have admin permission for this user group !
315 331 _perms = ('usergroup.admin',)
316 332 if not HasUserGroupPermissionAnyApi(*_perms)(
317 333 user=apiuser, user_group_name=user_group.users_group_name):
318 334 raise JSONRPCError(
319 335 'user group `%s` does not exist' % (usergroupid,))
320 336 else:
321 337 include_secrets = True
322 338
323 339 if not isinstance(owner, Optional):
324 340 owner = get_user_or_error(owner)
325 341
326 342 old_data = user_group.get_api_data()
327 343 updates = {}
328 344 store_update(updates, group_name, 'users_group_name')
329 345 store_update(updates, description, 'user_group_description')
330 346 store_update(updates, owner, 'user')
331 347 store_update(updates, active, 'users_group_active')
348
349 sync = Optional.extract(sync)
350 group_data = None
351 if sync is True:
352 group_data = {
353 'extern_type': 'manual_api',
354 'extern_type_set_by': apiuser.username
355 }
356 if sync is False:
357 group_data = user_group.group_data
358 if group_data and "extern_type" in group_data:
359 del group_data["extern_type"]
360
332 361 try:
333 UserGroupModel().update(user_group, updates)
362 UserGroupModel().update(user_group, updates, group_data=group_data)
334 363 audit_logger.store_api(
335 364 'user_group.edit', action_data={'old_data': old_data},
336 365 user=apiuser)
337 366 Session().commit()
338 367 return {
339 368 'msg': 'updated user group ID:%s %s' % (
340 369 user_group.users_group_id, user_group.users_group_name),
341 370 'user_group': user_group.get_api_data(
342 371 include_secrets=include_secrets)
343 372 }
344 373 except Exception:
345 374 log.exception("Error occurred during update of user group")
346 375 raise JSONRPCError(
347 376 'failed to update user group `%s`' % (usergroupid,))
348 377
349 378
350 379 @jsonrpc_method()
351 380 def delete_user_group(request, apiuser, usergroupid):
352 381 """
353 382 Deletes the specified `user group`.
354 383
355 384 This command can only be run using an |authtoken| with admin rights to
356 385 the specified repository.
357 386
358 387 This command takes the following options:
359 388
360 389 :param apiuser: filled automatically from apikey
361 390 :type apiuser: AuthUser
362 391 :param usergroupid:
363 392 :type usergroupid: int
364 393
365 394 Example output:
366 395
367 396 .. code-block:: bash
368 397
369 398 id : <id_given_in_input>
370 399 result : {
371 400 "msg": "deleted user group ID:<user_group_id> <user_group_name>"
372 401 }
373 402 error : null
374 403
375 404 Example error output:
376 405
377 406 .. code-block:: bash
378 407
379 408 id : <id_given_in_input>
380 409 result : null
381 410 error : {
382 411 "failed to delete user group ID:<user_group_id> <user_group_name>"
383 412 or
384 413 "RepoGroup assigned to <repo_groups_list>"
385 414 }
386 415
387 416 """
388 417
389 418 user_group = get_user_group_or_error(usergroupid)
390 419 if not has_superadmin_permission(apiuser):
391 420 # check if we have admin permission for this user group !
392 421 _perms = ('usergroup.admin',)
393 422 if not HasUserGroupPermissionAnyApi(*_perms)(
394 423 user=apiuser, user_group_name=user_group.users_group_name):
395 424 raise JSONRPCError(
396 425 'user group `%s` does not exist' % (usergroupid,))
397 426
398 427 old_data = user_group.get_api_data()
399 428 try:
400 429 UserGroupModel().delete(user_group)
401 430 audit_logger.store_api(
402 431 'user_group.delete', action_data={'old_data': old_data},
403 432 user=apiuser)
404 433 Session().commit()
405 434 return {
406 435 'msg': 'deleted user group ID:%s %s' % (
407 436 user_group.users_group_id, user_group.users_group_name),
408 437 'user_group': None
409 438 }
410 439 except UserGroupAssignedException as e:
411 440 log.exception("UserGroupAssigned error")
412 441 raise JSONRPCError(str(e))
413 442 except Exception:
414 443 log.exception("Error occurred during deletion of user group")
415 444 raise JSONRPCError(
416 445 'failed to delete user group ID:%s %s' %(
417 446 user_group.users_group_id, user_group.users_group_name))
418 447
419 448
420 449 @jsonrpc_method()
421 450 def add_user_to_user_group(request, apiuser, usergroupid, userid):
422 451 """
423 452 Adds a user to a `user group`. If the user already exists in the group
424 453 this command will return false.
425 454
426 455 This command can only be run using an |authtoken| with admin rights to
427 456 the specified user group.
428 457
429 458 This command takes the following options:
430 459
431 460 :param apiuser: This is filled automatically from the |authtoken|.
432 461 :type apiuser: AuthUser
433 462 :param usergroupid: Set the name of the `user group` to which a
434 463 user will be added.
435 464 :type usergroupid: int
436 465 :param userid: Set the `user_id` of the user to add to the group.
437 466 :type userid: int
438 467
439 468 Example output:
440 469
441 470 .. code-block:: bash
442 471
443 472 id : <id_given_in_input>
444 473 result : {
445 474 "success": True|False # depends on if member is in group
446 475 "msg": "added member `<username>` to user group `<groupname>` |
447 476 User is already in that group"
448 477
449 478 }
450 479 error : null
451 480
452 481 Example error output:
453 482
454 483 .. code-block:: bash
455 484
456 485 id : <id_given_in_input>
457 486 result : null
458 487 error : {
459 488 "failed to add member to user group `<user_group_name>`"
460 489 }
461 490
462 491 """
463 492
464 493 user = get_user_or_error(userid)
465 494 user_group = get_user_group_or_error(usergroupid)
466 495 if not has_superadmin_permission(apiuser):
467 496 # check if we have admin permission for this user group !
468 497 _perms = ('usergroup.admin',)
469 498 if not HasUserGroupPermissionAnyApi(*_perms)(
470 499 user=apiuser, user_group_name=user_group.users_group_name):
471 500 raise JSONRPCError('user group `%s` does not exist' % (
472 501 usergroupid,))
473 502
474 503 old_values = user_group.get_api_data()
475 504 try:
476 505 ugm = UserGroupModel().add_user_to_group(user_group, user)
477 506 success = True if ugm is not True else False
478 507 msg = 'added member `%s` to user group `%s`' % (
479 508 user.username, user_group.users_group_name
480 509 )
481 510 msg = msg if success else 'User is already in that group'
482 511 if success:
483 512 user_data = user.get_api_data()
484 513 audit_logger.store_api(
485 514 'user_group.edit.member.add',
486 515 action_data={'user': user_data, 'old_data': old_values},
487 516 user=apiuser)
488 517
489 518 Session().commit()
490 519
491 520 return {
492 521 'success': success,
493 522 'msg': msg
494 523 }
495 524 except Exception:
496 525 log.exception("Error occurred during adding a member to user group")
497 526 raise JSONRPCError(
498 527 'failed to add member to user group `%s`' % (
499 528 user_group.users_group_name,
500 529 )
501 530 )
502 531
503 532
504 533 @jsonrpc_method()
505 534 def remove_user_from_user_group(request, apiuser, usergroupid, userid):
506 535 """
507 536 Removes a user from a user group.
508 537
509 538 * If the specified user is not in the group, this command will return
510 539 `false`.
511 540
512 541 This command can only be run using an |authtoken| with admin rights to
513 542 the specified user group.
514 543
515 544 :param apiuser: This is filled automatically from the |authtoken|.
516 545 :type apiuser: AuthUser
517 546 :param usergroupid: Sets the user group name.
518 547 :type usergroupid: str or int
519 548 :param userid: The user you wish to remove from |RCE|.
520 549 :type userid: str or int
521 550
522 551 Example output:
523 552
524 553 .. code-block:: bash
525 554
526 555 id : <id_given_in_input>
527 556 result: {
528 557 "success": True|False, # depends on if member is in group
529 558 "msg": "removed member <username> from user group <groupname> |
530 559 User wasn't in group"
531 560 }
532 561 error: null
533 562
534 563 """
535 564
536 565 user = get_user_or_error(userid)
537 566 user_group = get_user_group_or_error(usergroupid)
538 567 if not has_superadmin_permission(apiuser):
539 568 # check if we have admin permission for this user group !
540 569 _perms = ('usergroup.admin',)
541 570 if not HasUserGroupPermissionAnyApi(*_perms)(
542 571 user=apiuser, user_group_name=user_group.users_group_name):
543 572 raise JSONRPCError(
544 573 'user group `%s` does not exist' % (usergroupid,))
545 574
546 575 old_values = user_group.get_api_data()
547 576 try:
548 577 success = UserGroupModel().remove_user_from_group(user_group, user)
549 578 msg = 'removed member `%s` from user group `%s`' % (
550 579 user.username, user_group.users_group_name
551 580 )
552 581 msg = msg if success else "User wasn't in group"
553 582 if success:
554 583 user_data = user.get_api_data()
555 584 audit_logger.store_api(
556 585 'user_group.edit.member.delete',
557 586 action_data={'user': user_data, 'old_data': old_values},
558 587 user=apiuser)
559 588
560 589 Session().commit()
561 590 return {'success': success, 'msg': msg}
562 591 except Exception:
563 592 log.exception("Error occurred during removing an member from user group")
564 593 raise JSONRPCError(
565 594 'failed to remove member from user group `%s`' % (
566 595 user_group.users_group_name,
567 596 )
568 597 )
569 598
570 599
571 600 @jsonrpc_method()
572 601 def grant_user_permission_to_user_group(
573 602 request, apiuser, usergroupid, userid, perm):
574 603 """
575 604 Set permissions for a user in a user group.
576 605
577 606 :param apiuser: This is filled automatically from the |authtoken|.
578 607 :type apiuser: AuthUser
579 608 :param usergroupid: Set the user group to edit permissions on.
580 609 :type usergroupid: str or int
581 610 :param userid: Set the user from whom you wish to set permissions.
582 611 :type userid: str
583 612 :param perm: (usergroup.(none|read|write|admin))
584 613 :type perm: str
585 614
586 615 Example output:
587 616
588 617 .. code-block:: bash
589 618
590 619 id : <id_given_in_input>
591 620 result : {
592 621 "msg": "Granted perm: `<perm_name>` for user: `<username>` in user group: `<user_group_name>`",
593 622 "success": true
594 623 }
595 624 error : null
596 625 """
597 626
598 627 user_group = get_user_group_or_error(usergroupid)
599 628
600 629 if not has_superadmin_permission(apiuser):
601 630 # check if we have admin permission for this user group !
602 631 _perms = ('usergroup.admin',)
603 632 if not HasUserGroupPermissionAnyApi(*_perms)(
604 633 user=apiuser, user_group_name=user_group.users_group_name):
605 634 raise JSONRPCError(
606 635 'user group `%s` does not exist' % (usergroupid,))
607 636
608 637 user = get_user_or_error(userid)
609 638 perm = get_perm_or_error(perm, prefix='usergroup.')
610 639
611 640 try:
612 UserGroupModel().grant_user_permission(
641 changes = UserGroupModel().grant_user_permission(
613 642 user_group=user_group, user=user, perm=perm)
643
644 action_data = {
645 'added': changes['added'],
646 'updated': changes['updated'],
647 'deleted': changes['deleted'],
648 }
649 audit_logger.store_api(
650 'user_group.edit.permissions', action_data=action_data,
651 user=apiuser)
652
614 653 Session().commit()
615 654 return {
616 655 'msg':
617 656 'Granted perm: `%s` for user: `%s` in user group: `%s`' % (
618 657 perm.permission_name, user.username,
619 658 user_group.users_group_name
620 659 ),
621 660 'success': True
622 661 }
623 662 except Exception:
624 663 log.exception("Error occurred during editing permissions "
625 664 "for user in user group")
626 665 raise JSONRPCError(
627 666 'failed to edit permission for user: '
628 667 '`%s` in user group: `%s`' % (
629 668 userid, user_group.users_group_name))
630 669
631 670
632 671 @jsonrpc_method()
633 672 def revoke_user_permission_from_user_group(
634 673 request, apiuser, usergroupid, userid):
635 674 """
636 675 Revoke a users permissions in a user group.
637 676
638 677 :param apiuser: This is filled automatically from the |authtoken|.
639 678 :type apiuser: AuthUser
640 679 :param usergroupid: Set the user group from which to revoke the user
641 680 permissions.
642 681 :type: usergroupid: str or int
643 682 :param userid: Set the userid of the user whose permissions will be
644 683 revoked.
645 684 :type userid: str
646 685
647 686 Example output:
648 687
649 688 .. code-block:: bash
650 689
651 690 id : <id_given_in_input>
652 691 result : {
653 692 "msg": "Revoked perm for user: `<username>` in user group: `<user_group_name>`",
654 693 "success": true
655 694 }
656 695 error : null
657 696 """
658 697
659 698 user_group = get_user_group_or_error(usergroupid)
660 699
661 700 if not has_superadmin_permission(apiuser):
662 701 # check if we have admin permission for this user group !
663 702 _perms = ('usergroup.admin',)
664 703 if not HasUserGroupPermissionAnyApi(*_perms)(
665 704 user=apiuser, user_group_name=user_group.users_group_name):
666 705 raise JSONRPCError(
667 706 'user group `%s` does not exist' % (usergroupid,))
668 707
669 708 user = get_user_or_error(userid)
670 709
671 710 try:
672 UserGroupModel().revoke_user_permission(
711 changes = UserGroupModel().revoke_user_permission(
673 712 user_group=user_group, user=user)
713 action_data = {
714 'added': changes['added'],
715 'updated': changes['updated'],
716 'deleted': changes['deleted'],
717 }
718 audit_logger.store_api(
719 'user_group.edit.permissions', action_data=action_data,
720 user=apiuser)
721
674 722 Session().commit()
675 723 return {
676 724 'msg': 'Revoked perm for user: `%s` in user group: `%s`' % (
677 725 user.username, user_group.users_group_name
678 726 ),
679 727 'success': True
680 728 }
681 729 except Exception:
682 730 log.exception("Error occurred during editing permissions "
683 731 "for user in user group")
684 732 raise JSONRPCError(
685 733 'failed to edit permission for user: `%s` in user group: `%s`'
686 734 % (userid, user_group.users_group_name))
687 735
688 736
689 737 @jsonrpc_method()
690 738 def grant_user_group_permission_to_user_group(
691 739 request, apiuser, usergroupid, sourceusergroupid, perm):
692 740 """
693 741 Give one user group permissions to another user group.
694 742
695 743 :param apiuser: This is filled automatically from the |authtoken|.
696 744 :type apiuser: AuthUser
697 745 :param usergroupid: Set the user group on which to edit permissions.
698 746 :type usergroupid: str or int
699 747 :param sourceusergroupid: Set the source user group to which
700 748 access/permissions will be granted.
701 749 :type sourceusergroupid: str or int
702 750 :param perm: (usergroup.(none|read|write|admin))
703 751 :type perm: str
704 752
705 753 Example output:
706 754
707 755 .. code-block:: bash
708 756
709 757 id : <id_given_in_input>
710 758 result : {
711 759 "msg": "Granted perm: `<perm_name>` for user group: `<source_user_group_name>` in user group: `<user_group_name>`",
712 760 "success": true
713 761 }
714 762 error : null
715 763 """
716 764
717 765 user_group = get_user_group_or_error(sourceusergroupid)
718 766 target_user_group = get_user_group_or_error(usergroupid)
719 767 perm = get_perm_or_error(perm, prefix='usergroup.')
720 768
721 769 if not has_superadmin_permission(apiuser):
722 770 # check if we have admin permission for this user group !
723 771 _perms = ('usergroup.admin',)
724 772 if not HasUserGroupPermissionAnyApi(*_perms)(
725 773 user=apiuser,
726 774 user_group_name=target_user_group.users_group_name):
727 775 raise JSONRPCError(
728 776 'to user group `%s` does not exist' % (usergroupid,))
729 777
730 778 # check if we have at least read permission for source user group !
731 779 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
732 780 if not HasUserGroupPermissionAnyApi(*_perms)(
733 781 user=apiuser, user_group_name=user_group.users_group_name):
734 782 raise JSONRPCError(
735 783 'user group `%s` does not exist' % (sourceusergroupid,))
736 784
737 785 try:
738 UserGroupModel().grant_user_group_permission(
786 changes = UserGroupModel().grant_user_group_permission(
739 787 target_user_group=target_user_group,
740 788 user_group=user_group, perm=perm)
789
790 action_data = {
791 'added': changes['added'],
792 'updated': changes['updated'],
793 'deleted': changes['deleted'],
794 }
795 audit_logger.store_api(
796 'user_group.edit.permissions', action_data=action_data,
797 user=apiuser)
798
741 799 Session().commit()
742
743 800 return {
744 801 'msg': 'Granted perm: `%s` for user group: `%s` '
745 802 'in user group: `%s`' % (
746 803 perm.permission_name, user_group.users_group_name,
747 804 target_user_group.users_group_name
748 805 ),
749 806 'success': True
750 807 }
751 808 except Exception:
752 809 log.exception("Error occurred during editing permissions "
753 810 "for user group in user group")
754 811 raise JSONRPCError(
755 812 'failed to edit permission for user group: `%s` in '
756 813 'user group: `%s`' % (
757 814 sourceusergroupid, target_user_group.users_group_name
758 815 )
759 816 )
760 817
761 818
762 819 @jsonrpc_method()
763 820 def revoke_user_group_permission_from_user_group(
764 821 request, apiuser, usergroupid, sourceusergroupid):
765 822 """
766 823 Revoke the permissions that one user group has to another.
767 824
768 825 :param apiuser: This is filled automatically from the |authtoken|.
769 826 :type apiuser: AuthUser
770 827 :param usergroupid: Set the user group on which to edit permissions.
771 828 :type usergroupid: str or int
772 829 :param sourceusergroupid: Set the user group from which permissions
773 830 are revoked.
774 831 :type sourceusergroupid: str or int
775 832
776 833 Example output:
777 834
778 835 .. code-block:: bash
779 836
780 837 id : <id_given_in_input>
781 838 result : {
782 839 "msg": "Revoked perm for user group: `<user_group_name>` in user group: `<target_user_group_name>`",
783 840 "success": true
784 841 }
785 842 error : null
786 843 """
787 844
788 845 user_group = get_user_group_or_error(sourceusergroupid)
789 846 target_user_group = get_user_group_or_error(usergroupid)
790 847
791 848 if not has_superadmin_permission(apiuser):
792 849 # check if we have admin permission for this user group !
793 850 _perms = ('usergroup.admin',)
794 851 if not HasUserGroupPermissionAnyApi(*_perms)(
795 852 user=apiuser,
796 853 user_group_name=target_user_group.users_group_name):
797 854 raise JSONRPCError(
798 855 'to user group `%s` does not exist' % (usergroupid,))
799 856
800 857 # check if we have at least read permission
801 858 # for the source user group !
802 859 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
803 860 if not HasUserGroupPermissionAnyApi(*_perms)(
804 861 user=apiuser, user_group_name=user_group.users_group_name):
805 862 raise JSONRPCError(
806 863 'user group `%s` does not exist' % (sourceusergroupid,))
807 864
808 865 try:
809 UserGroupModel().revoke_user_group_permission(
866 changes = UserGroupModel().revoke_user_group_permission(
810 867 target_user_group=target_user_group, user_group=user_group)
868 action_data = {
869 'added': changes['added'],
870 'updated': changes['updated'],
871 'deleted': changes['deleted'],
872 }
873 audit_logger.store_api(
874 'user_group.edit.permissions', action_data=action_data,
875 user=apiuser)
876
811 877 Session().commit()
812 878
813 879 return {
814 880 'msg': 'Revoked perm for user group: '
815 881 '`%s` in user group: `%s`' % (
816 882 user_group.users_group_name,
817 883 target_user_group.users_group_name
818 884 ),
819 885 'success': True
820 886 }
821 887 except Exception:
822 888 log.exception("Error occurred during editing permissions "
823 889 "for user group in user group")
824 890 raise JSONRPCError(
825 891 'failed to edit permission for user group: '
826 892 '`%s` in user group: `%s`' % (
827 893 sourceusergroupid, target_user_group.users_group_name
828 894 )
829 895 )
@@ -1,564 +1,641 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import time
22 22 import logging
23 23 import operator
24 24
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 26
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h, diffs
28 28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
29 29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
30 30 from rhodecode.model import repo
31 31 from rhodecode.model import repo_group
32 32 from rhodecode.model import user_group
33 33 from rhodecode.model import user
34 34 from rhodecode.model.db import User
35 35 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.settings import VcsSettingsModel
36 37
37 38 log = logging.getLogger(__name__)
38 39
39 40
40 41 ADMIN_PREFIX = '/_admin'
41 42 STATIC_FILE_PREFIX = '/_static'
42 43
43 44 URL_NAME_REQUIREMENTS = {
44 45 # group name can have a slash in them, but they must not end with a slash
45 46 'group_name': r'.*?[^/]',
46 47 'repo_group_name': r'.*?[^/]',
47 48 # repo names can have a slash in them, but they must not end with a slash
48 49 'repo_name': r'.*?[^/]',
49 50 # file path eats up everything at the end
50 51 'f_path': r'.*',
51 52 # reference types
52 53 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
53 54 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
54 55 }
55 56
56 57
57 58 def add_route_with_slash(config,name, pattern, **kw):
58 59 config.add_route(name, pattern, **kw)
59 60 if not pattern.endswith('/'):
60 61 config.add_route(name + '_slash', pattern + '/', **kw)
61 62
62 63
63 64 def add_route_requirements(route_path, requirements=URL_NAME_REQUIREMENTS):
64 65 """
65 66 Adds regex requirements to pyramid routes using a mapping dict
66 67 e.g::
67 68 add_route_requirements('{repo_name}/settings')
68 69 """
69 70 for key, regex in requirements.items():
70 71 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
71 72 return route_path
72 73
73 74
74 75 def get_format_ref_id(repo):
75 76 """Returns a `repo` specific reference formatter function"""
76 77 if h.is_svn(repo):
77 78 return _format_ref_id_svn
78 79 else:
79 80 return _format_ref_id
80 81
81 82
82 83 def _format_ref_id(name, raw_id):
83 84 """Default formatting of a given reference `name`"""
84 85 return name
85 86
86 87
87 88 def _format_ref_id_svn(name, raw_id):
88 89 """Special way of formatting a reference for Subversion including path"""
89 90 return '%s@%s' % (name, raw_id)
90 91
91 92
92 93 class TemplateArgs(StrictAttributeDict):
93 94 pass
94 95
95 96
96 97 class BaseAppView(object):
97 98
98 99 def __init__(self, context, request):
99 100 self.request = request
100 101 self.context = context
101 102 self.session = request.session
102 103 self._rhodecode_user = request.user # auth user
103 104 self._rhodecode_db_user = self._rhodecode_user.get_instance()
104 105 self._maybe_needs_password_change(
105 106 request.matched_route.name, self._rhodecode_db_user)
106 107
107 108 def _maybe_needs_password_change(self, view_name, user_obj):
108 109 log.debug('Checking if user %s needs password change on view %s',
109 110 user_obj, view_name)
110 111 skip_user_views = [
111 112 'logout', 'login',
112 113 'my_account_password', 'my_account_password_update'
113 114 ]
114 115
115 116 if not user_obj:
116 117 return
117 118
118 119 if user_obj.username == User.DEFAULT_USER:
119 120 return
120 121
121 122 now = time.time()
122 123 should_change = user_obj.user_data.get('force_password_change')
123 124 change_after = safe_int(should_change) or 0
124 125 if should_change and now > change_after:
125 126 log.debug('User %s requires password change', user_obj)
126 127 h.flash('You are required to change your password', 'warning',
127 128 ignore_duplicate=True)
128 129
129 130 if view_name not in skip_user_views:
130 131 raise HTTPFound(
131 132 self.request.route_path('my_account_password'))
132 133
133 134 def _log_creation_exception(self, e, repo_name):
134 135 _ = self.request.translate
135 136 reason = None
136 137 if len(e.args) == 2:
137 138 reason = e.args[1]
138 139
139 140 if reason == 'INVALID_CERTIFICATE':
140 141 log.exception(
141 142 'Exception creating a repository: invalid certificate')
142 143 msg = (_('Error creating repository %s: invalid certificate')
143 144 % repo_name)
144 145 else:
145 146 log.exception("Exception creating a repository")
146 147 msg = (_('Error creating repository %s')
147 148 % repo_name)
148 149 return msg
149 150
150 151 def _get_local_tmpl_context(self, include_app_defaults=True):
151 152 c = TemplateArgs()
152 153 c.auth_user = self.request.user
153 154 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
154 155 c.rhodecode_user = self.request.user
155 156
156 157 if include_app_defaults:
157 158 from rhodecode.lib.base import attach_context_attributes
158 159 attach_context_attributes(c, self.request, self.request.user.user_id)
159 160
160 161 return c
161 162
162 163 def _get_template_context(self, tmpl_args, **kwargs):
163 164
164 165 local_tmpl_args = {
165 166 'defaults': {},
166 167 'errors': {},
167 168 'c': tmpl_args
168 169 }
169 170 local_tmpl_args.update(kwargs)
170 171 return local_tmpl_args
171 172
172 173 def load_default_context(self):
173 174 """
174 175 example:
175 176
176 177 def load_default_context(self):
177 178 c = self._get_local_tmpl_context()
178 179 c.custom_var = 'foobar'
179 180
180 181 return c
181 182 """
182 183 raise NotImplementedError('Needs implementation in view class')
183 184
184 185
185 186 class RepoAppView(BaseAppView):
186 187
187 188 def __init__(self, context, request):
188 189 super(RepoAppView, self).__init__(context, request)
189 190 self.db_repo = request.db_repo
190 191 self.db_repo_name = self.db_repo.repo_name
191 192 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
192 193
193 194 def _handle_missing_requirements(self, error):
194 195 log.error(
195 196 'Requirements are missing for repository %s: %s',
196 197 self.db_repo_name, error.message)
197 198
198 199 def _get_local_tmpl_context(self, include_app_defaults=True):
199 200 _ = self.request.translate
200 201 c = super(RepoAppView, self)._get_local_tmpl_context(
201 202 include_app_defaults=include_app_defaults)
202 203
203 204 # register common vars for this type of view
204 205 c.rhodecode_db_repo = self.db_repo
205 206 c.repo_name = self.db_repo_name
206 207 c.repository_pull_requests = self.db_repo_pull_requests
208 self.path_filter = PathFilter(None)
207 209
208 c.repository_requirements_missing = False
210 c.repository_requirements_missing = {}
209 211 try:
210 212 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
213 if self.rhodecode_vcs_repo:
214 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
215 c.auth_user.username)
216 self.path_filter = PathFilter(path_perms)
211 217 except RepositoryRequirementError as e:
212 c.repository_requirements_missing = True
218 c.repository_requirements_missing = {'error': str(e)}
213 219 self._handle_missing_requirements(e)
214 220 self.rhodecode_vcs_repo = None
215 221
216 if (not c.repository_requirements_missing
217 and self.rhodecode_vcs_repo is None):
222 c.path_filter = self.path_filter # used by atom_feed_entry.mako
223
224 if self.rhodecode_vcs_repo is None:
218 225 # unable to fetch this repo as vcs instance, report back to user
219 226 h.flash(_(
220 227 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
221 228 "Please check if it exist, or is not damaged.") %
222 229 {'repo_name': c.repo_name},
223 230 category='error', ignore_duplicate=True)
231 if c.repository_requirements_missing:
232 route = self.request.matched_route.name
233 if route.startswith(('edit_repo', 'repo_summary')):
234 # allow summary and edit repo on missing requirements
235 return c
236
237 raise HTTPFound(
238 h.route_path('repo_summary', repo_name=self.db_repo_name))
239
240 else: # redirect if we don't show missing requirements
224 241 raise HTTPFound(h.route_path('home'))
225 242
226 243 return c
227 244
228 def _get_f_path(self, matchdict, default=None):
245 def _get_f_path_unchecked(self, matchdict, default=None):
246 """
247 Should only be used by redirects, everything else should call _get_f_path
248 """
229 249 f_path = matchdict.get('f_path')
230 250 if f_path:
231 251 # fix for multiple initial slashes that causes errors for GIT
232 252 return f_path.lstrip('/')
233 253
234 254 return default
235 255
256 def _get_f_path(self, matchdict, default=None):
257 f_path_match = self._get_f_path_unchecked(matchdict, default)
258 return self.path_filter.assert_path_permissions(f_path_match)
259
260 def _get_general_setting(self, target_repo, settings_key, default=False):
261 settings_model = VcsSettingsModel(repo=target_repo)
262 settings = settings_model.get_general_settings()
263 return settings.get(settings_key, default)
264
265
266 class PathFilter(object):
267
268 # Expects and instance of BasePathPermissionChecker or None
269 def __init__(self, permission_checker):
270 self.permission_checker = permission_checker
271
272 def assert_path_permissions(self, path):
273 if path and self.permission_checker and not self.permission_checker.has_access(path):
274 raise HTTPForbidden()
275 return path
276
277 def filter_patchset(self, patchset):
278 if not self.permission_checker or not patchset:
279 return patchset, False
280 had_filtered = False
281 filtered_patchset = []
282 for patch in patchset:
283 filename = patch.get('filename', None)
284 if not filename or self.permission_checker.has_access(filename):
285 filtered_patchset.append(patch)
286 else:
287 had_filtered = True
288 if had_filtered:
289 if isinstance(patchset, diffs.LimitedDiffContainer):
290 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
291 return filtered_patchset, True
292 else:
293 return patchset, False
294
295 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
296 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
297 result = diffset.render_patchset(filtered_patchset, source_ref=source_ref, target_ref=target_ref)
298 result.has_hidden_changes = has_hidden_changes
299 return result
300
301 def get_raw_patch(self, diff_processor):
302 if self.permission_checker is None:
303 return diff_processor.as_raw()
304 elif self.permission_checker.has_full_access:
305 return diff_processor.as_raw()
306 else:
307 return '# Repository has user-specific filters, raw patch generation is disabled.'
308
309 @property
310 def is_enabled(self):
311 return self.permission_checker is not None
312
236 313
237 314 class RepoGroupAppView(BaseAppView):
238 315 def __init__(self, context, request):
239 316 super(RepoGroupAppView, self).__init__(context, request)
240 317 self.db_repo_group = request.db_repo_group
241 318 self.db_repo_group_name = self.db_repo_group.group_name
242 319
243 320 def _revoke_perms_on_yourself(self, form_result):
244 321 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
245 322 form_result['perm_updates'])
246 323 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
247 324 form_result['perm_additions'])
248 325 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
249 326 form_result['perm_deletions'])
250 327 admin_perm = 'group.admin'
251 328 if _updates and _updates[0][1] != admin_perm or \
252 329 _additions and _additions[0][1] != admin_perm or \
253 330 _deletions and _deletions[0][1] != admin_perm:
254 331 return True
255 332 return False
256 333
257 334
258 335 class UserGroupAppView(BaseAppView):
259 336 def __init__(self, context, request):
260 337 super(UserGroupAppView, self).__init__(context, request)
261 338 self.db_user_group = request.db_user_group
262 339 self.db_user_group_name = self.db_user_group.users_group_name
263 340
264 341
265 342 class UserAppView(BaseAppView):
266 343 def __init__(self, context, request):
267 344 super(UserAppView, self).__init__(context, request)
268 345 self.db_user = request.db_user
269 346 self.db_user_id = self.db_user.user_id
270 347
271 348 _ = self.request.translate
272 349 if not request.db_user_supports_default:
273 350 if self.db_user.username == User.DEFAULT_USER:
274 351 h.flash(_("Editing user `{}` is disabled.".format(
275 352 User.DEFAULT_USER)), category='warning')
276 353 raise HTTPFound(h.route_path('users'))
277 354
278 355
279 356 class DataGridAppView(object):
280 357 """
281 358 Common class to have re-usable grid rendering components
282 359 """
283 360
284 361 def _extract_ordering(self, request, column_map=None):
285 362 column_map = column_map or {}
286 363 column_index = safe_int(request.GET.get('order[0][column]'))
287 364 order_dir = request.GET.get(
288 365 'order[0][dir]', 'desc')
289 366 order_by = request.GET.get(
290 367 'columns[%s][data][sort]' % column_index, 'name_raw')
291 368
292 369 # translate datatable to DB columns
293 370 order_by = column_map.get(order_by) or order_by
294 371
295 372 search_q = request.GET.get('search[value]')
296 373 return search_q, order_by, order_dir
297 374
298 375 def _extract_chunk(self, request):
299 376 start = safe_int(request.GET.get('start'), 0)
300 377 length = safe_int(request.GET.get('length'), 25)
301 378 draw = safe_int(request.GET.get('draw'))
302 379 return draw, start, length
303 380
304 381 def _get_order_col(self, order_by, model):
305 382 if isinstance(order_by, basestring):
306 383 try:
307 384 return operator.attrgetter(order_by)(model)
308 385 except AttributeError:
309 386 return None
310 387 else:
311 388 return order_by
312 389
313 390
314 391 class BaseReferencesView(RepoAppView):
315 392 """
316 393 Base for reference view for branches, tags and bookmarks.
317 394 """
318 395 def load_default_context(self):
319 396 c = self._get_local_tmpl_context()
320 397
321 398
322 399 return c
323 400
324 401 def load_refs_context(self, ref_items, partials_template):
325 402 _render = self.request.get_partial_renderer(partials_template)
326 403 pre_load = ["author", "date", "message"]
327 404
328 405 is_svn = h.is_svn(self.rhodecode_vcs_repo)
329 406 is_hg = h.is_hg(self.rhodecode_vcs_repo)
330 407
331 408 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
332 409
333 410 closed_refs = {}
334 411 if is_hg:
335 412 closed_refs = self.rhodecode_vcs_repo.branches_closed
336 413
337 414 data = []
338 415 for ref_name, commit_id in ref_items:
339 416 commit = self.rhodecode_vcs_repo.get_commit(
340 417 commit_id=commit_id, pre_load=pre_load)
341 418 closed = ref_name in closed_refs
342 419
343 420 # TODO: johbo: Unify generation of reference links
344 421 use_commit_id = '/' in ref_name or is_svn
345 422
346 423 if use_commit_id:
347 424 files_url = h.route_path(
348 425 'repo_files',
349 426 repo_name=self.db_repo_name,
350 427 f_path=ref_name if is_svn else '',
351 428 commit_id=commit_id)
352 429
353 430 else:
354 431 files_url = h.route_path(
355 432 'repo_files',
356 433 repo_name=self.db_repo_name,
357 434 f_path=ref_name if is_svn else '',
358 435 commit_id=ref_name,
359 436 _query=dict(at=ref_name))
360 437
361 438 data.append({
362 439 "name": _render('name', ref_name, files_url, closed),
363 440 "name_raw": ref_name,
364 441 "date": _render('date', commit.date),
365 442 "date_raw": datetime_to_time(commit.date),
366 443 "author": _render('author', commit.author),
367 444 "commit": _render(
368 445 'commit', commit.message, commit.raw_id, commit.idx),
369 446 "commit_raw": commit.idx,
370 447 "compare": _render(
371 448 'compare', format_ref_id(ref_name, commit.raw_id)),
372 449 })
373 450
374 451 return data
375 452
376 453
377 454 class RepoRoutePredicate(object):
378 455 def __init__(self, val, config):
379 456 self.val = val
380 457
381 458 def text(self):
382 459 return 'repo_route = %s' % self.val
383 460
384 461 phash = text
385 462
386 463 def __call__(self, info, request):
387 464
388 465 if hasattr(request, 'vcs_call'):
389 466 # skip vcs calls
390 467 return
391 468
392 469 repo_name = info['match']['repo_name']
393 470 repo_model = repo.RepoModel()
394 471 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
395 472
396 473 def redirect_if_creating(db_repo):
397 474 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
398 475 raise HTTPFound(
399 476 request.route_path('repo_creating',
400 477 repo_name=db_repo.repo_name))
401 478
402 479 if by_name_match:
403 480 # register this as request object we can re-use later
404 481 request.db_repo = by_name_match
405 482 redirect_if_creating(by_name_match)
406 483 return True
407 484
408 485 by_id_match = repo_model.get_repo_by_id(repo_name)
409 486 if by_id_match:
410 487 request.db_repo = by_id_match
411 488 redirect_if_creating(by_id_match)
412 489 return True
413 490
414 491 return False
415 492
416 493
417 494 class RepoTypeRoutePredicate(object):
418 495 def __init__(self, val, config):
419 496 self.val = val or ['hg', 'git', 'svn']
420 497
421 498 def text(self):
422 499 return 'repo_accepted_type = %s' % self.val
423 500
424 501 phash = text
425 502
426 503 def __call__(self, info, request):
427 504 if hasattr(request, 'vcs_call'):
428 505 # skip vcs calls
429 506 return
430 507
431 508 rhodecode_db_repo = request.db_repo
432 509
433 510 log.debug(
434 511 '%s checking repo type for %s in %s',
435 512 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
436 513
437 514 if rhodecode_db_repo.repo_type in self.val:
438 515 return True
439 516 else:
440 517 log.warning('Current view is not supported for repo type:%s',
441 518 rhodecode_db_repo.repo_type)
442 519 #
443 520 # h.flash(h.literal(
444 521 # _('Action not supported for %s.' % rhodecode_repo.alias)),
445 522 # category='warning')
446 523 # return redirect(
447 524 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
448 525
449 526 return False
450 527
451 528
452 529 class RepoGroupRoutePredicate(object):
453 530 def __init__(self, val, config):
454 531 self.val = val
455 532
456 533 def text(self):
457 534 return 'repo_group_route = %s' % self.val
458 535
459 536 phash = text
460 537
461 538 def __call__(self, info, request):
462 539 if hasattr(request, 'vcs_call'):
463 540 # skip vcs calls
464 541 return
465 542
466 543 repo_group_name = info['match']['repo_group_name']
467 544 repo_group_model = repo_group.RepoGroupModel()
468 545 by_name_match = repo_group_model.get_by_group_name(
469 546 repo_group_name, cache=True)
470 547
471 548 if by_name_match:
472 549 # register this as request object we can re-use later
473 550 request.db_repo_group = by_name_match
474 551 return True
475 552
476 553 return False
477 554
478 555
479 556 class UserGroupRoutePredicate(object):
480 557 def __init__(self, val, config):
481 558 self.val = val
482 559
483 560 def text(self):
484 561 return 'user_group_route = %s' % self.val
485 562
486 563 phash = text
487 564
488 565 def __call__(self, info, request):
489 566 if hasattr(request, 'vcs_call'):
490 567 # skip vcs calls
491 568 return
492 569
493 570 user_group_id = info['match']['user_group_id']
494 571 user_group_model = user_group.UserGroup()
495 572 by_id_match = user_group_model.get(
496 573 user_group_id, cache=True)
497 574
498 575 if by_id_match:
499 576 # register this as request object we can re-use later
500 577 request.db_user_group = by_id_match
501 578 return True
502 579
503 580 return False
504 581
505 582
506 583 class UserRoutePredicateBase(object):
507 584 supports_default = None
508 585
509 586 def __init__(self, val, config):
510 587 self.val = val
511 588
512 589 def text(self):
513 590 raise NotImplementedError()
514 591
515 592 def __call__(self, info, request):
516 593 if hasattr(request, 'vcs_call'):
517 594 # skip vcs calls
518 595 return
519 596
520 597 user_id = info['match']['user_id']
521 598 user_model = user.User()
522 599 by_id_match = user_model.get(
523 600 user_id, cache=True)
524 601
525 602 if by_id_match:
526 603 # register this as request object we can re-use later
527 604 request.db_user = by_id_match
528 605 request.db_user_supports_default = self.supports_default
529 606 return True
530 607
531 608 return False
532 609
533 610
534 611 class UserRoutePredicate(UserRoutePredicateBase):
535 612 supports_default = False
536 613
537 614 def text(self):
538 615 return 'user_route = %s' % self.val
539 616
540 617 phash = text
541 618
542 619
543 620 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
544 621 supports_default = True
545 622
546 623 def text(self):
547 624 return 'user_with_default_route = %s' % self.val
548 625
549 626 phash = text
550 627
551 628
552 629 def includeme(config):
553 630 config.add_route_predicate(
554 631 'repo_route', RepoRoutePredicate)
555 632 config.add_route_predicate(
556 633 'repo_accepted_types', RepoTypeRoutePredicate)
557 634 config.add_route_predicate(
558 635 'repo_group_route', RepoGroupRoutePredicate)
559 636 config.add_route_predicate(
560 637 'user_group_route', UserGroupRoutePredicate)
561 638 config.add_route_predicate(
562 639 'user_route_with_default', UserRouteWithDefaultPredicate)
563 640 config.add_route_predicate(
564 641 'user_route', UserRoutePredicate) No newline at end of file
@@ -1,409 +1,414 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 from rhodecode.apps._base import ADMIN_PREFIX
23 23
24 24
25 25 def admin_routes(config):
26 26 """
27 27 Admin prefixed routes
28 28 """
29 29
30 30 config.add_route(
31 31 name='admin_audit_logs',
32 32 pattern='/audit_logs')
33 33
34 34 config.add_route(
35 35 name='admin_audit_log_entry',
36 36 pattern='/audit_logs/{audit_log_id}')
37 37
38 38 config.add_route(
39 39 name='pull_requests_global_0', # backward compat
40 40 pattern='/pull_requests/{pull_request_id:\d+}')
41 41 config.add_route(
42 42 name='pull_requests_global_1', # backward compat
43 43 pattern='/pull-requests/{pull_request_id:\d+}')
44 44 config.add_route(
45 45 name='pull_requests_global',
46 46 pattern='/pull-request/{pull_request_id:\d+}')
47 47
48 48 config.add_route(
49 49 name='admin_settings_open_source',
50 50 pattern='/settings/open_source')
51 51 config.add_route(
52 52 name='admin_settings_vcs_svn_generate_cfg',
53 53 pattern='/settings/vcs/svn_generate_cfg')
54 54
55 55 config.add_route(
56 56 name='admin_settings_system',
57 57 pattern='/settings/system')
58 58 config.add_route(
59 59 name='admin_settings_system_update',
60 60 pattern='/settings/system/updates')
61 61
62 62 config.add_route(
63 63 name='admin_settings_sessions',
64 64 pattern='/settings/sessions')
65 65 config.add_route(
66 66 name='admin_settings_sessions_cleanup',
67 67 pattern='/settings/sessions/cleanup')
68 68
69 69 config.add_route(
70 70 name='admin_settings_process_management',
71 71 pattern='/settings/process_management')
72 72 config.add_route(
73 73 name='admin_settings_process_management_data',
74 74 pattern='/settings/process_management/data')
75 75 config.add_route(
76 76 name='admin_settings_process_management_signal',
77 77 pattern='/settings/process_management/signal')
78 78 config.add_route(
79 79 name='admin_settings_process_management_master_signal',
80 80 pattern='/settings/process_management/master_signal')
81 81
82 82 # default settings
83 83 config.add_route(
84 84 name='admin_defaults_repositories',
85 85 pattern='/defaults/repositories')
86 86 config.add_route(
87 87 name='admin_defaults_repositories_update',
88 88 pattern='/defaults/repositories/update')
89 89
90 90 # admin settings
91 91
92 92 config.add_route(
93 93 name='admin_settings',
94 94 pattern='/settings')
95 95 config.add_route(
96 96 name='admin_settings_update',
97 97 pattern='/settings/update')
98 98
99 99 config.add_route(
100 100 name='admin_settings_global',
101 101 pattern='/settings/global')
102 102 config.add_route(
103 103 name='admin_settings_global_update',
104 104 pattern='/settings/global/update')
105 105
106 106 config.add_route(
107 107 name='admin_settings_vcs',
108 108 pattern='/settings/vcs')
109 109 config.add_route(
110 110 name='admin_settings_vcs_update',
111 111 pattern='/settings/vcs/update')
112 112 config.add_route(
113 113 name='admin_settings_vcs_svn_pattern_delete',
114 114 pattern='/settings/vcs/svn_pattern_delete')
115 115
116 116 config.add_route(
117 117 name='admin_settings_mapping',
118 118 pattern='/settings/mapping')
119 119 config.add_route(
120 120 name='admin_settings_mapping_update',
121 121 pattern='/settings/mapping/update')
122 122
123 123 config.add_route(
124 124 name='admin_settings_visual',
125 125 pattern='/settings/visual')
126 126 config.add_route(
127 127 name='admin_settings_visual_update',
128 128 pattern='/settings/visual/update')
129 129
130 130
131 131 config.add_route(
132 132 name='admin_settings_issuetracker',
133 133 pattern='/settings/issue-tracker')
134 134 config.add_route(
135 135 name='admin_settings_issuetracker_update',
136 136 pattern='/settings/issue-tracker/update')
137 137 config.add_route(
138 138 name='admin_settings_issuetracker_test',
139 139 pattern='/settings/issue-tracker/test')
140 140 config.add_route(
141 141 name='admin_settings_issuetracker_delete',
142 142 pattern='/settings/issue-tracker/delete')
143 143
144 144 config.add_route(
145 145 name='admin_settings_email',
146 146 pattern='/settings/email')
147 147 config.add_route(
148 148 name='admin_settings_email_update',
149 149 pattern='/settings/email/update')
150 150
151 151 config.add_route(
152 152 name='admin_settings_hooks',
153 153 pattern='/settings/hooks')
154 154 config.add_route(
155 155 name='admin_settings_hooks_update',
156 156 pattern='/settings/hooks/update')
157 157 config.add_route(
158 158 name='admin_settings_hooks_delete',
159 159 pattern='/settings/hooks/delete')
160 160
161 161 config.add_route(
162 162 name='admin_settings_search',
163 163 pattern='/settings/search')
164 164
165 165 config.add_route(
166 166 name='admin_settings_labs',
167 167 pattern='/settings/labs')
168 168 config.add_route(
169 169 name='admin_settings_labs_update',
170 170 pattern='/settings/labs/update')
171 171
172 # Automation EE feature
173 config.add_route(
174 'admin_settings_automation',
175 pattern=ADMIN_PREFIX + '/settings/automation')
176
172 177 # global permissions
173 178
174 179 config.add_route(
175 180 name='admin_permissions_application',
176 181 pattern='/permissions/application')
177 182 config.add_route(
178 183 name='admin_permissions_application_update',
179 184 pattern='/permissions/application/update')
180 185
181 186 config.add_route(
182 187 name='admin_permissions_global',
183 188 pattern='/permissions/global')
184 189 config.add_route(
185 190 name='admin_permissions_global_update',
186 191 pattern='/permissions/global/update')
187 192
188 193 config.add_route(
189 194 name='admin_permissions_object',
190 195 pattern='/permissions/object')
191 196 config.add_route(
192 197 name='admin_permissions_object_update',
193 198 pattern='/permissions/object/update')
194 199
195 200 config.add_route(
196 201 name='admin_permissions_ips',
197 202 pattern='/permissions/ips')
198 203
199 204 config.add_route(
200 205 name='admin_permissions_overview',
201 206 pattern='/permissions/overview')
202 207
203 208 config.add_route(
204 209 name='admin_permissions_auth_token_access',
205 210 pattern='/permissions/auth_token_access')
206 211
207 212 config.add_route(
208 213 name='admin_permissions_ssh_keys',
209 214 pattern='/permissions/ssh_keys')
210 215 config.add_route(
211 216 name='admin_permissions_ssh_keys_data',
212 217 pattern='/permissions/ssh_keys/data')
213 218 config.add_route(
214 219 name='admin_permissions_ssh_keys_update',
215 220 pattern='/permissions/ssh_keys/update')
216 221
217 222 # users admin
218 223 config.add_route(
219 224 name='users',
220 225 pattern='/users')
221 226
222 227 config.add_route(
223 228 name='users_data',
224 229 pattern='/users_data')
225 230
226 231 config.add_route(
227 232 name='users_create',
228 233 pattern='/users/create')
229 234
230 235 config.add_route(
231 236 name='users_new',
232 237 pattern='/users/new')
233 238
234 239 # user management
235 240 config.add_route(
236 241 name='user_edit',
237 242 pattern='/users/{user_id:\d+}/edit',
238 243 user_route=True)
239 244 config.add_route(
240 245 name='user_edit_advanced',
241 246 pattern='/users/{user_id:\d+}/edit/advanced',
242 247 user_route=True)
243 248 config.add_route(
244 249 name='user_edit_global_perms',
245 250 pattern='/users/{user_id:\d+}/edit/global_permissions',
246 251 user_route=True)
247 252 config.add_route(
248 253 name='user_edit_global_perms_update',
249 254 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
250 255 user_route=True)
251 256 config.add_route(
252 257 name='user_update',
253 258 pattern='/users/{user_id:\d+}/update',
254 259 user_route=True)
255 260 config.add_route(
256 261 name='user_delete',
257 262 pattern='/users/{user_id:\d+}/delete',
258 263 user_route=True)
259 264 config.add_route(
260 265 name='user_force_password_reset',
261 266 pattern='/users/{user_id:\d+}/password_reset',
262 267 user_route=True)
263 268 config.add_route(
264 269 name='user_create_personal_repo_group',
265 270 pattern='/users/{user_id:\d+}/create_repo_group',
266 271 user_route=True)
267 272
268 273 # user auth tokens
269 274 config.add_route(
270 275 name='edit_user_auth_tokens',
271 276 pattern='/users/{user_id:\d+}/edit/auth_tokens',
272 277 user_route=True)
273 278 config.add_route(
274 279 name='edit_user_auth_tokens_add',
275 280 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
276 281 user_route=True)
277 282 config.add_route(
278 283 name='edit_user_auth_tokens_delete',
279 284 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
280 285 user_route=True)
281 286
282 287 # user ssh keys
283 288 config.add_route(
284 289 name='edit_user_ssh_keys',
285 290 pattern='/users/{user_id:\d+}/edit/ssh_keys',
286 291 user_route=True)
287 292 config.add_route(
288 293 name='edit_user_ssh_keys_generate_keypair',
289 294 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
290 295 user_route=True)
291 296 config.add_route(
292 297 name='edit_user_ssh_keys_add',
293 298 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
294 299 user_route=True)
295 300 config.add_route(
296 301 name='edit_user_ssh_keys_delete',
297 302 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
298 303 user_route=True)
299 304
300 305 # user emails
301 306 config.add_route(
302 307 name='edit_user_emails',
303 308 pattern='/users/{user_id:\d+}/edit/emails',
304 309 user_route=True)
305 310 config.add_route(
306 311 name='edit_user_emails_add',
307 312 pattern='/users/{user_id:\d+}/edit/emails/new',
308 313 user_route=True)
309 314 config.add_route(
310 315 name='edit_user_emails_delete',
311 316 pattern='/users/{user_id:\d+}/edit/emails/delete',
312 317 user_route=True)
313 318
314 319 # user IPs
315 320 config.add_route(
316 321 name='edit_user_ips',
317 322 pattern='/users/{user_id:\d+}/edit/ips',
318 323 user_route=True)
319 324 config.add_route(
320 325 name='edit_user_ips_add',
321 326 pattern='/users/{user_id:\d+}/edit/ips/new',
322 327 user_route_with_default=True) # enabled for default user too
323 328 config.add_route(
324 329 name='edit_user_ips_delete',
325 330 pattern='/users/{user_id:\d+}/edit/ips/delete',
326 331 user_route_with_default=True) # enabled for default user too
327 332
328 333 # user perms
329 334 config.add_route(
330 335 name='edit_user_perms_summary',
331 336 pattern='/users/{user_id:\d+}/edit/permissions_summary',
332 337 user_route=True)
333 338 config.add_route(
334 339 name='edit_user_perms_summary_json',
335 340 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
336 341 user_route=True)
337 342
338 343 # user user groups management
339 344 config.add_route(
340 345 name='edit_user_groups_management',
341 346 pattern='/users/{user_id:\d+}/edit/groups_management',
342 347 user_route=True)
343 348
344 349 config.add_route(
345 350 name='edit_user_groups_management_updates',
346 351 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
347 352 user_route=True)
348 353
349 354 # user audit logs
350 355 config.add_route(
351 356 name='edit_user_audit_logs',
352 357 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
353 358
354 359 # user-groups admin
355 360 config.add_route(
356 361 name='user_groups',
357 362 pattern='/user_groups')
358 363
359 364 config.add_route(
360 365 name='user_groups_data',
361 366 pattern='/user_groups_data')
362 367
363 368 config.add_route(
364 369 name='user_groups_new',
365 370 pattern='/user_groups/new')
366 371
367 372 config.add_route(
368 373 name='user_groups_create',
369 374 pattern='/user_groups/create')
370 375
371 376 # repos admin
372 377 config.add_route(
373 378 name='repos',
374 379 pattern='/repos')
375 380
376 381 config.add_route(
377 382 name='repo_new',
378 383 pattern='/repos/new')
379 384
380 385 config.add_route(
381 386 name='repo_create',
382 387 pattern='/repos/create')
383 388
384 389 # repo groups admin
385 390 config.add_route(
386 391 name='repo_groups',
387 392 pattern='/repo_groups')
388 393
389 394 config.add_route(
390 395 name='repo_group_new',
391 396 pattern='/repo_group/new')
392 397
393 398 config.add_route(
394 399 name='repo_group_create',
395 400 pattern='/repo_group/create')
396 401
397 402
398 403 def includeme(config):
399 404 from rhodecode.apps.admin.navigation import includeme as nav_includeme
400 405
401 406 # Create admin navigation registry and add it to the pyramid registry.
402 407 nav_includeme(config)
403 408
404 409 # main admin routes
405 410 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
406 411 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
407 412
408 413 # Scan module for configuration decorators.
409 414 config.scan('.views', ignore='.tests')
@@ -1,143 +1,144 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import logging
23 23 import collections
24 24
25 25 from zope.interface import implementer
26 26
27 27 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
28 28 from rhodecode.lib.utils2 import str2bool
29 29 from rhodecode.translation import _
30 30
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34 NavListEntry = collections.namedtuple(
35 35 'NavListEntry', ['key', 'name', 'url', 'active_list'])
36 36
37 37
38 38 class NavEntry(object):
39 39 """
40 40 Represents an entry in the admin navigation.
41 41
42 42 :param key: Unique identifier used to store reference in an OrderedDict.
43 43 :param name: Display name, usually a translation string.
44 44 :param view_name: Name of the view, used generate the URL.
45 45 :param active_list: list of urls that we select active for this element
46 46 """
47 47
48 48 def __init__(self, key, name, view_name, active_list=None):
49 49 self.key = key
50 50 self.name = name
51 51 self.view_name = view_name
52 52 self._active_list = active_list or []
53 53
54 54 def generate_url(self, request):
55 55 return request.route_path(self.view_name)
56 56
57 57 def get_localized_name(self, request):
58 58 return request.translate(self.name)
59 59
60 60 @property
61 61 def active_list(self):
62 62 active_list = [self.key]
63 63 if self._active_list:
64 64 active_list = self._active_list
65 65 return active_list
66 66
67 67
68 68 @implementer(IAdminNavigationRegistry)
69 69 class NavigationRegistry(object):
70 70
71 71 _base_entries = [
72 72 NavEntry('global', _('Global'),
73 73 'admin_settings_global'),
74 74 NavEntry('vcs', _('VCS'),
75 75 'admin_settings_vcs'),
76 76 NavEntry('visual', _('Visual'),
77 77 'admin_settings_visual'),
78 78 NavEntry('mapping', _('Remap and Rescan'),
79 79 'admin_settings_mapping'),
80 80 NavEntry('issuetracker', _('Issue Tracker'),
81 81 'admin_settings_issuetracker'),
82 82 NavEntry('email', _('Email'),
83 83 'admin_settings_email'),
84 84 NavEntry('hooks', _('Hooks'),
85 85 'admin_settings_hooks'),
86 86 NavEntry('search', _('Full Text Search'),
87 87 'admin_settings_search'),
88 88 NavEntry('integrations', _('Integrations'),
89 89 'global_integrations_home'),
90 90 NavEntry('system', _('System Info'),
91 91 'admin_settings_system'),
92 92 NavEntry('process_management', _('Processes'),
93 93 'admin_settings_process_management'),
94 94 NavEntry('sessions', _('User Sessions'),
95 95 'admin_settings_sessions'),
96 96 NavEntry('open_source', _('Open Source Licenses'),
97 97 'admin_settings_open_source'),
98
98 NavEntry('automation', _('Automation'),
99 'admin_settings_automation')
99 100 ]
100 101
101 102 _labs_entry = NavEntry('labs', _('Labs'),
102 103 'admin_settings_labs')
103 104
104 105 def __init__(self, labs_active=False):
105 106 self._registered_entries = collections.OrderedDict()
106 107 for item in self.__class__._base_entries:
107 108 self._registered_entries[item.key] = item
108 109
109 110 if labs_active:
110 111 self.add_entry(self._labs_entry)
111 112
112 113 def add_entry(self, entry):
113 114 self._registered_entries[entry.key] = entry
114 115
115 116 def get_navlist(self, request):
116 117 navlist = [NavListEntry(i.key, i.get_localized_name(request),
117 118 i.generate_url(request), i.active_list)
118 119 for i in self._registered_entries.values()]
119 120 return navlist
120 121
121 122
122 123 def navigation_registry(request, registry=None):
123 124 """
124 125 Helper that returns the admin navigation registry.
125 126 """
126 127 pyramid_registry = registry or request.registry
127 128 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
128 129 return nav_registry
129 130
130 131
131 132 def navigation_list(request):
132 133 """
133 134 Helper that returns the admin navigation as list of NavListEntry objects.
134 135 """
135 136 return navigation_registry(request).get_navlist(request)
136 137
137 138
138 139 def includeme(config):
139 140 # Create admin navigation registry and add it to the pyramid registry.
140 141 settings = config.get_settings()
141 142 labs_active = str2bool(settings.get('labs_settings_active', False))
142 143 navigation_registry = NavigationRegistry(labs_active=labs_active)
143 144 config.registry.registerUtility(navigation_registry) No newline at end of file
@@ -1,201 +1,202 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22
23 23 from rhodecode.tests import assert_session_flash
24 24 from rhodecode.tests.utils import AssertResponse
25 25 from rhodecode.model.db import Session
26 26 from rhodecode.model.settings import SettingsModel
27 27
28 28
29 29 def assert_auth_settings_updated(response):
30 30 assert response.status_int == 302, 'Expected response HTTP Found 302'
31 31 assert_session_flash(response, 'Auth settings updated successfully')
32 32
33 33
34 34 @pytest.mark.usefixtures("autologin_user", "app")
35 35 class TestAuthSettingsView(object):
36 36
37 37 def _enable_plugins(self, plugins_list, csrf_token, override=None,
38 38 verify_response=False):
39 39 test_url = '/_admin/auth'
40 40 params = {
41 41 'auth_plugins': plugins_list,
42 42 'csrf_token': csrf_token,
43 43 }
44 44 if override:
45 45 params.update(override)
46 46 _enabled_plugins = []
47 47 for plugin in plugins_list.split(','):
48 48 plugin_name = plugin.partition('#')[-1]
49 49 enabled_plugin = '%s_enabled' % plugin_name
50 50 cache_ttl = '%s_cache_ttl' % plugin_name
51 51
52 52 # default params that are needed for each plugin,
53 53 # `enabled` and `cache_ttl`
54 54 params.update({
55 55 enabled_plugin: True,
56 56 cache_ttl: 0
57 57 })
58 58 _enabled_plugins.append(enabled_plugin)
59 59
60 60 # we need to clean any enabled plugin before, since they require
61 61 # form params to be present
62 62 db_plugin = SettingsModel().get_setting_by_name('auth_plugins')
63 63 db_plugin.app_settings_value = \
64 64 'egg:rhodecode-enterprise-ce#rhodecode'
65 65 Session().add(db_plugin)
66 66 Session().commit()
67 67 for _plugin in _enabled_plugins:
68 68 db_plugin = SettingsModel().get_setting_by_name(_plugin)
69 69 if db_plugin:
70 70 Session().delete(db_plugin)
71 71 Session().commit()
72 72
73 73 response = self.app.post(url=test_url, params=params)
74 74
75 75 if verify_response:
76 76 assert_auth_settings_updated(response)
77 77 return params
78 78
79 79 def _post_ldap_settings(self, params, override=None, force=False):
80 80
81 81 params.update({
82 82 'filter': 'user',
83 83 'user_member_of': '',
84 84 'user_search_base': '',
85 85 'user_search_filter': 'test_filter',
86 86
87 87 'host': 'dc.example.com',
88 88 'port': '999',
89 'timeout': 3600,
89 90 'tls_kind': 'PLAIN',
90 91 'tls_reqcert': 'NEVER',
91 92
92 93 'dn_user': 'test_user',
93 94 'dn_pass': 'test_pass',
94 95 'base_dn': 'test_base_dn',
95 96 'search_scope': 'BASE',
96 97 'attr_login': 'test_attr_login',
97 98 'attr_firstname': 'ima',
98 99 'attr_lastname': 'tester',
99 100 'attr_email': 'test@example.com',
100 101 'cache_ttl': '0',
101 102 })
102 103 if force:
103 104 params = {}
104 105 params.update(override or {})
105 106
106 107 test_url = '/_admin/auth/ldap/'
107 108
108 109 response = self.app.post(url=test_url, params=params)
109 110 return response
110 111
111 112 def test_index(self):
112 113 response = self.app.get('/_admin/auth')
113 114 response.mustcontain('Authentication Plugins')
114 115
115 116 @pytest.mark.parametrize("disable_plugin, needs_import", [
116 117 ('egg:rhodecode-enterprise-ce#headers', None),
117 118 ('egg:rhodecode-enterprise-ce#crowd', None),
118 119 ('egg:rhodecode-enterprise-ce#jasig_cas', None),
119 120 ('egg:rhodecode-enterprise-ce#ldap', None),
120 121 ('egg:rhodecode-enterprise-ce#pam', "pam"),
121 122 ])
122 123 def test_disable_plugin(self, csrf_token, disable_plugin, needs_import):
123 124 # TODO: johbo: "pam" is currently not available on darwin,
124 125 # although the docs state that it should work on darwin.
125 126 if needs_import:
126 127 pytest.importorskip(needs_import)
127 128
128 129 self._enable_plugins(
129 130 'egg:rhodecode-enterprise-ce#rhodecode,' + disable_plugin,
130 131 csrf_token, verify_response=True)
131 132
132 133 self._enable_plugins(
133 134 'egg:rhodecode-enterprise-ce#rhodecode', csrf_token,
134 135 verify_response=True)
135 136
136 137 def test_ldap_save_settings(self, csrf_token):
137 138 params = self._enable_plugins(
138 139 'egg:rhodecode-enterprise-ce#rhodecode,'
139 140 'egg:rhodecode-enterprise-ce#ldap',
140 141 csrf_token)
141 142 response = self._post_ldap_settings(params)
142 143 assert_auth_settings_updated(response)
143 144
144 145 new_settings = SettingsModel().get_auth_settings()
145 146 assert new_settings['auth_ldap_host'] == u'dc.example.com', \
146 147 'fail db write compare'
147 148
148 149 def test_ldap_error_form_wrong_port_number(self, csrf_token):
149 150 params = self._enable_plugins(
150 151 'egg:rhodecode-enterprise-ce#rhodecode,'
151 152 'egg:rhodecode-enterprise-ce#ldap',
152 153 csrf_token)
153 154 invalid_port_value = 'invalid-port-number'
154 155 response = self._post_ldap_settings(params, override={
155 156 'port': invalid_port_value,
156 157 })
157 158 assertr = AssertResponse(response)
158 159 assertr.element_contains(
159 160 '.form .field #port ~ .error-message',
160 161 invalid_port_value)
161 162
162 163 def test_ldap_error_form(self, csrf_token):
163 164 params = self._enable_plugins(
164 165 'egg:rhodecode-enterprise-ce#rhodecode,'
165 166 'egg:rhodecode-enterprise-ce#ldap',
166 167 csrf_token)
167 168 response = self._post_ldap_settings(params, override={
168 169 'attr_login': '',
169 170 })
170 171 response.mustcontain("""<span class="error-message">The LDAP Login"""
171 172 """ attribute of the CN must be specified""")
172 173
173 174 def test_post_ldap_group_settings(self, csrf_token):
174 175 params = self._enable_plugins(
175 176 'egg:rhodecode-enterprise-ce#rhodecode,'
176 177 'egg:rhodecode-enterprise-ce#ldap',
177 178 csrf_token)
178 179
179 180 response = self._post_ldap_settings(params, override={
180 181 'host': 'dc-legacy.example.com',
181 182 'port': '999',
182 183 'tls_kind': 'PLAIN',
183 184 'tls_reqcert': 'NEVER',
184 185 'dn_user': 'test_user',
185 186 'dn_pass': 'test_pass',
186 187 'base_dn': 'test_base_dn',
187 188 'filter': 'test_filter',
188 189 'search_scope': 'BASE',
189 190 'attr_login': 'test_attr_login',
190 191 'attr_firstname': 'ima',
191 192 'attr_lastname': 'tester',
192 193 'attr_email': 'test@example.com',
193 194 'cache_ttl': '60',
194 195 'csrf_token': csrf_token,
195 196 }
196 197 )
197 198 assert_auth_settings_updated(response)
198 199
199 200 new_settings = SettingsModel().get_auth_settings()
200 201 assert new_settings['auth_ldap_host'] == u'dc-legacy.example.com', \
201 202 'fail db write compare'
@@ -1,173 +1,176 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22
23 23 from rhodecode.model.db import User, UserSshKeys
24 24
25 25 from rhodecode.tests import TestController, assert_session_flash
26 26 from rhodecode.tests.fixture import Fixture
27 27
28 28 fixture = Fixture()
29 29
30 30
31 31 def route_path(name, params=None, **kwargs):
32 32 import urllib
33 33 from rhodecode.apps._base import ADMIN_PREFIX
34 34
35 35 base_url = {
36 36 'edit_user_ssh_keys':
37 37 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys',
38 38 'edit_user_ssh_keys_generate_keypair':
39 39 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/generate',
40 40 'edit_user_ssh_keys_add':
41 41 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/new',
42 42 'edit_user_ssh_keys_delete':
43 43 ADMIN_PREFIX + '/users/{user_id}/edit/ssh_keys/delete',
44 44
45 45 }[name].format(**kwargs)
46 46
47 47 if params:
48 48 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
49 49 return base_url
50 50
51 51
52 52 class TestAdminUsersSshKeysView(TestController):
53 53 INVALID_KEY = """\
54 54 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vevJsuZds1iNU5
55 55 LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSykfR1D1TdluyIpQLrwgH5kb
56 56 n8FkVI8zBMCKakxowvN67B0R7b1BT4PPzW2JlOXei/m9W12ZY484VTow6/B+kf2Q8
57 57 cP8tmCJmKWZma5Em7OTUhvjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6
58 58 jvdphZTc30I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zP
59 59 qPFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL
60 60 your_email@example.com
61 61 """
62 62 VALID_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDk+77sjDzVeB6vev' \
63 63 'JsuZds1iNU5LANOa5CU5G/9JYIA6RYsWWMO7mbsR82IUckdqOHmxSy' \
64 64 'kfR1D1TdluyIpQLrwgH5kbn8FkVI8zBMCKakxowvN67B0R7b1BT4PP' \
65 65 'zW2JlOXei/m9W12ZY484VTow6/B+kf2Q8cP8tmCJmKWZma5Em7OTUh' \
66 66 'vjyQVNz3v7HfeY5Hq0Ci4ECJ59hepFDabJvtAXg9XrI6jvdphZTc30' \
67 67 'I4fG8+hBHzpeFxUGvSGNtXPUbwaAY8j/oHYrTpMgkj6pUEFsiKfC5zPq' \
68 68 'PFR5HyKTCHW0nFUJnZsbyFT5hMiF/hZkJc9A0ZbdSvJwCRQ/g3bmdL ' \
69 69 'your_email@example.com'
70 FINGERPRINT = 'MD5:01:4f:ad:29:22:6e:01:37:c9:d2:52:26:52:b0:2d:93'
70 71
71 72 def test_ssh_keys_default_user(self):
72 73 self.log_user()
73 74 user = User.get_default_user()
74 75 self.app.get(
75 76 route_path('edit_user_ssh_keys', user_id=user.user_id),
76 77 status=302)
77 78
78 79 def test_add_ssh_key_error(self, user_util):
79 80 self.log_user()
80 81 user = user_util.create_user()
81 82 user_id = user.user_id
82 83
83 84 key_data = self.INVALID_KEY
84 85
85 86 desc = 'MY SSH KEY'
86 87 response = self.app.post(
87 88 route_path('edit_user_ssh_keys_add', user_id=user_id),
88 89 {'description': desc, 'key_data': key_data,
89 90 'csrf_token': self.csrf_token})
90 91 assert_session_flash(response, 'An error occurred during ssh '
91 92 'key saving: Unable to decode the key')
92 93
93 94 def test_ssh_key_duplicate(self, user_util):
94 95 self.log_user()
95 96 user = user_util.create_user()
96 97 user_id = user.user_id
97 98
98 99 key_data = self.VALID_KEY
99 100
100 101 desc = 'MY SSH KEY'
101 102 response = self.app.post(
102 103 route_path('edit_user_ssh_keys_add', user_id=user_id),
103 104 {'description': desc, 'key_data': key_data,
104 105 'csrf_token': self.csrf_token})
105 106 assert_session_flash(response, 'Ssh Key successfully created')
106 107 response.follow() # flush session flash
107 108
108 109 # add the same key AGAIN
109 110 desc = 'MY SSH KEY'
110 111 response = self.app.post(
111 112 route_path('edit_user_ssh_keys_add', user_id=user_id),
112 113 {'description': desc, 'key_data': key_data,
113 114 'csrf_token': self.csrf_token})
115
116 err = 'Such key with fingerprint `{}` already exists, ' \
117 'please use a different one'.format(self.FINGERPRINT)
114 118 assert_session_flash(response, 'An error occurred during ssh key '
115 'saving: Such key already exists, '
116 'please use a different one')
119 'saving: {}'.format(err))
117 120
118 121 def test_add_ssh_key(self, user_util):
119 122 self.log_user()
120 123 user = user_util.create_user()
121 124 user_id = user.user_id
122 125
123 126 key_data = self.VALID_KEY
124 127
125 128 desc = 'MY SSH KEY'
126 129 response = self.app.post(
127 130 route_path('edit_user_ssh_keys_add', user_id=user_id),
128 131 {'description': desc, 'key_data': key_data,
129 132 'csrf_token': self.csrf_token})
130 133 assert_session_flash(response, 'Ssh Key successfully created')
131 134
132 135 response = response.follow()
133 136 response.mustcontain(desc)
134 137
135 138 def test_delete_ssh_key(self, user_util):
136 139 self.log_user()
137 140 user = user_util.create_user()
138 141 user_id = user.user_id
139 142
140 143 key_data = self.VALID_KEY
141 144
142 145 desc = 'MY SSH KEY'
143 146 response = self.app.post(
144 147 route_path('edit_user_ssh_keys_add', user_id=user_id),
145 148 {'description': desc, 'key_data': key_data,
146 149 'csrf_token': self.csrf_token})
147 150 assert_session_flash(response, 'Ssh Key successfully created')
148 151 response = response.follow() # flush the Session flash
149 152
150 153 # now delete our key
151 154 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
152 155 assert 1 == len(keys)
153 156
154 157 response = self.app.post(
155 158 route_path('edit_user_ssh_keys_delete', user_id=user_id),
156 159 {'del_ssh_key': keys[0].ssh_key_id,
157 160 'csrf_token': self.csrf_token})
158 161
159 162 assert_session_flash(response, 'Ssh key successfully deleted')
160 163 keys = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).all()
161 164 assert 0 == len(keys)
162 165
163 166 def test_generate_keypair(self, user_util):
164 167 self.log_user()
165 168 user = user_util.create_user()
166 169 user_id = user.user_id
167 170
168 171 response = self.app.get(
169 172 route_path('edit_user_ssh_keys_generate_keypair', user_id=user_id))
170 173
171 174 response.mustcontain('Private key')
172 175 response.mustcontain('Public key')
173 176 response.mustcontain('-----BEGIN RSA PRIVATE KEY-----')
@@ -1,133 +1,171 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 import psutil
24 24 import signal
25 25 from pyramid.view import view_config
26 26
27 27 from rhodecode.apps._base import BaseAppView
28 28 from rhodecode.apps.admin.navigation import navigation_list
29 29 from rhodecode.lib.auth import (
30 30 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
32 32
33 33 log = logging.getLogger(__name__)
34 34
35 35
36 36 class AdminProcessManagementView(BaseAppView):
37 37 def load_default_context(self):
38 38 c = self._get_local_tmpl_context()
39 return c
39 40
40 return c
41 def _format_proc(self, proc, with_children=False):
42 try:
43 mem = proc.memory_info()
44 proc_formatted = StrictAttributeDict({
45 'pid': proc.pid,
46 'name': proc.name(),
47 'mem_rss': mem.rss,
48 'mem_vms': mem.vms,
49 'cpu_percent': proc.cpu_percent(),
50 'create_time': proc.create_time(),
51 'cmd': ' '.join(proc.cmdline()),
52 })
53
54 if with_children:
55 proc_formatted.update({
56 'children': [self._format_proc(x)
57 for x in proc.children(recursive=True)]
58 })
59 except Exception:
60 log.exception('Failed to load proc')
61 proc_formatted = None
62 return proc_formatted
63
64 def get_processes(self):
65 proc_list = []
66 for p in psutil.process_iter():
67 if 'gunicorn' in p.name():
68 proc = self._format_proc(p, with_children=True)
69 if proc:
70 proc_list.append(proc)
71
72 return proc_list
41 73
42 74 @LoginRequired()
43 75 @HasPermissionAllDecorator('hg.admin')
44 76 @view_config(
45 77 route_name='admin_settings_process_management', request_method='GET',
46 78 renderer='rhodecode:templates/admin/settings/settings.mako')
47 79 def process_management(self):
48 80 _ = self.request.translate
49 81 c = self.load_default_context()
50 82
51 83 c.active = 'process_management'
52 84 c.navlist = navigation_list(self.request)
53 c.gunicorn_processes = (
54 p for p in psutil.process_iter() if 'gunicorn' in p.name())
85 c.gunicorn_processes = self.get_processes()
55 86 return self._get_template_context(c)
56 87
57 88 @LoginRequired()
58 89 @HasPermissionAllDecorator('hg.admin')
59 90 @view_config(
60 91 route_name='admin_settings_process_management_data', request_method='GET',
61 92 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
62 93 def process_management_data(self):
63 94 _ = self.request.translate
64 95 c = self.load_default_context()
65 c.gunicorn_processes = (
66 p for p in psutil.process_iter() if 'gunicorn' in p.name())
96 c.gunicorn_processes = self.get_processes()
67 97 return self._get_template_context(c)
68 98
69 99 @LoginRequired()
70 100 @HasPermissionAllDecorator('hg.admin')
71 101 @CSRFRequired()
72 102 @view_config(
73 103 route_name='admin_settings_process_management_signal',
74 104 request_method='POST', renderer='json_ext')
75 105 def process_management_signal(self):
76 106 pids = self.request.json.get('pids', [])
77 107 result = []
108
78 109 def on_terminate(proc):
79 110 msg = "process `PID:{}` terminated with exit code {}".format(
80 proc.pid, proc.returncode)
111 proc.pid, proc.returncode or 0)
81 112 result.append(msg)
82 113
83 114 procs = []
84 115 for pid in pids:
85 116 pid = safe_int(pid)
86 117 if pid:
87 118 try:
88 119 proc = psutil.Process(pid)
89 120 except psutil.NoSuchProcess:
90 121 continue
91 122
92 123 children = proc.children(recursive=True)
93 124 if children:
94 print('Wont kill Master Process')
125 log.warning('Wont kill Master Process')
95 126 else:
96 127 procs.append(proc)
97 128
98 129 for p in procs:
130 try:
99 131 p.terminate()
132 except psutil.AccessDenied as e:
133 log.warning('Access denied: {}'.format(e))
134
100 135 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
101 136 for p in alive:
137 try:
102 138 p.kill()
139 except psutil.AccessDenied as e:
140 log.warning('Access denied: {}'.format(e))
103 141
104 142 return {'result': result}
105 143
106 144 @LoginRequired()
107 145 @HasPermissionAllDecorator('hg.admin')
108 146 @CSRFRequired()
109 147 @view_config(
110 148 route_name='admin_settings_process_management_master_signal',
111 149 request_method='POST', renderer='json_ext')
112 150 def process_management_master_signal(self):
113 151 pid_data = self.request.json.get('pid_data', {})
114 152 pid = safe_int(pid_data['pid'])
115 153 action = pid_data['action']
116 154 if pid:
117 155 try:
118 156 proc = psutil.Process(pid)
119 157 except psutil.NoSuchProcess:
120 158 return {'result': 'failure_no_such_process'}
121 159
122 160 children = proc.children(recursive=True)
123 161 if children:
124 162 # master process
125 163 if action == '+' and len(children) <= 20:
126 164 proc.send_signal(signal.SIGTTIN)
127 165 elif action == '-' and len(children) >= 2:
128 166 proc.send_signal(signal.SIGTTOU)
129 167 else:
130 168 return {'result': 'failure_wrong_action'}
131 169 return {'result': 'success'}
132 170
133 171 return {'result': 'failure_not_master'}
@@ -1,764 +1,781 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import logging
23 23 import collections
24 24
25 25 import datetime
26 26 import formencode
27 27 import formencode.htmlfill
28 28
29 29 import rhodecode
30 30 from pyramid.view import view_config
31 31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
32 32 from pyramid.renderers import render
33 33 from pyramid.response import Response
34 34
35 35 from rhodecode.apps._base import BaseAppView
36 36 from rhodecode.apps.admin.navigation import navigation_list
37 37 from rhodecode.apps.svn_support.config_keys import generate_config
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import (
40 40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
41 41 from rhodecode.lib.celerylib import tasks, run_task
42 42 from rhodecode.lib.utils import repo2db_mapper
43 43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
44 44 from rhodecode.lib.index import searcher_from_config
45 45
46 46 from rhodecode.model.db import RhodeCodeUi, Repository
47 47 from rhodecode.model.forms import (ApplicationSettingsForm,
48 48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
49 49 LabsSettingsForm, IssueTrackerPatternsForm)
50 50 from rhodecode.model.repo_group import RepoGroupModel
51 51
52 52 from rhodecode.model.scm import ScmModel
53 53 from rhodecode.model.notification import EmailNotificationModel
54 54 from rhodecode.model.meta import Session
55 55 from rhodecode.model.settings import (
56 56 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
57 57 SettingsModel)
58 58
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62
63 63 class AdminSettingsView(BaseAppView):
64 64
65 65 def load_default_context(self):
66 66 c = self._get_local_tmpl_context()
67 67 c.labs_active = str2bool(
68 68 rhodecode.CONFIG.get('labs_settings_active', 'true'))
69 69 c.navlist = navigation_list(self.request)
70 70
71 71 return c
72 72
73 73 @classmethod
74 74 def _get_ui_settings(cls):
75 75 ret = RhodeCodeUi.query().all()
76 76
77 77 if not ret:
78 78 raise Exception('Could not get application ui settings !')
79 79 settings = {}
80 80 for each in ret:
81 81 k = each.ui_key
82 82 v = each.ui_value
83 83 if k == '/':
84 84 k = 'root_path'
85 85
86 86 if k in ['push_ssl', 'publish', 'enabled']:
87 87 v = str2bool(v)
88 88
89 89 if k.find('.') != -1:
90 90 k = k.replace('.', '_')
91 91
92 92 if each.ui_section in ['hooks', 'extensions']:
93 93 v = each.ui_active
94 94
95 95 settings[each.ui_section + '_' + k] = v
96 96 return settings
97 97
98 98 @classmethod
99 99 def _form_defaults(cls):
100 100 defaults = SettingsModel().get_all_settings()
101 101 defaults.update(cls._get_ui_settings())
102 102
103 103 defaults.update({
104 104 'new_svn_branch': '',
105 105 'new_svn_tag': '',
106 106 })
107 107 return defaults
108 108
109 109 @LoginRequired()
110 110 @HasPermissionAllDecorator('hg.admin')
111 111 @view_config(
112 112 route_name='admin_settings_vcs', request_method='GET',
113 113 renderer='rhodecode:templates/admin/settings/settings.mako')
114 114 def settings_vcs(self):
115 115 c = self.load_default_context()
116 116 c.active = 'vcs'
117 117 model = VcsSettingsModel()
118 118 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
119 119 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
120 120
121 121 settings = self.request.registry.settings
122 122 c.svn_proxy_generate_config = settings[generate_config]
123 123
124 124 defaults = self._form_defaults()
125 125
126 126 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
127 127
128 128 data = render('rhodecode:templates/admin/settings/settings.mako',
129 129 self._get_template_context(c), self.request)
130 130 html = formencode.htmlfill.render(
131 131 data,
132 132 defaults=defaults,
133 133 encoding="UTF-8",
134 134 force_defaults=False
135 135 )
136 136 return Response(html)
137 137
138 138 @LoginRequired()
139 139 @HasPermissionAllDecorator('hg.admin')
140 140 @CSRFRequired()
141 141 @view_config(
142 142 route_name='admin_settings_vcs_update', request_method='POST',
143 143 renderer='rhodecode:templates/admin/settings/settings.mako')
144 144 def settings_vcs_update(self):
145 145 _ = self.request.translate
146 146 c = self.load_default_context()
147 147 c.active = 'vcs'
148 148
149 149 model = VcsSettingsModel()
150 150 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
151 151 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
152 152
153 153 settings = self.request.registry.settings
154 154 c.svn_proxy_generate_config = settings[generate_config]
155 155
156 156 application_form = ApplicationUiSettingsForm(self.request.translate)()
157 157
158 158 try:
159 159 form_result = application_form.to_python(dict(self.request.POST))
160 160 except formencode.Invalid as errors:
161 161 h.flash(
162 162 _("Some form inputs contain invalid data."),
163 163 category='error')
164 164 data = render('rhodecode:templates/admin/settings/settings.mako',
165 165 self._get_template_context(c), self.request)
166 166 html = formencode.htmlfill.render(
167 167 data,
168 168 defaults=errors.value,
169 169 errors=errors.error_dict or {},
170 170 prefix_error=False,
171 171 encoding="UTF-8",
172 172 force_defaults=False
173 173 )
174 174 return Response(html)
175 175
176 176 try:
177 177 if c.visual.allow_repo_location_change:
178 178 model.update_global_path_setting(
179 179 form_result['paths_root_path'])
180 180
181 181 model.update_global_ssl_setting(form_result['web_push_ssl'])
182 182 model.update_global_hook_settings(form_result)
183 183
184 184 model.create_or_update_global_svn_settings(form_result)
185 185 model.create_or_update_global_hg_settings(form_result)
186 186 model.create_or_update_global_git_settings(form_result)
187 187 model.create_or_update_global_pr_settings(form_result)
188 188 except Exception:
189 189 log.exception("Exception while updating settings")
190 190 h.flash(_('Error occurred during updating '
191 191 'application settings'), category='error')
192 192 else:
193 193 Session().commit()
194 194 h.flash(_('Updated VCS settings'), category='success')
195 195 raise HTTPFound(h.route_path('admin_settings_vcs'))
196 196
197 197 data = render('rhodecode:templates/admin/settings/settings.mako',
198 198 self._get_template_context(c), self.request)
199 199 html = formencode.htmlfill.render(
200 200 data,
201 201 defaults=self._form_defaults(),
202 202 encoding="UTF-8",
203 203 force_defaults=False
204 204 )
205 205 return Response(html)
206 206
207 207 @LoginRequired()
208 208 @HasPermissionAllDecorator('hg.admin')
209 209 @CSRFRequired()
210 210 @view_config(
211 211 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
212 212 renderer='json_ext', xhr=True)
213 213 def settings_vcs_delete_svn_pattern(self):
214 214 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
215 215 model = VcsSettingsModel()
216 216 try:
217 217 model.delete_global_svn_pattern(delete_pattern_id)
218 218 except SettingNotFound:
219 219 log.exception(
220 220 'Failed to delete svn_pattern with id %s', delete_pattern_id)
221 221 raise HTTPNotFound()
222 222
223 223 Session().commit()
224 224 return True
225 225
226 226 @LoginRequired()
227 227 @HasPermissionAllDecorator('hg.admin')
228 228 @view_config(
229 229 route_name='admin_settings_mapping', request_method='GET',
230 230 renderer='rhodecode:templates/admin/settings/settings.mako')
231 231 def settings_mapping(self):
232 232 c = self.load_default_context()
233 233 c.active = 'mapping'
234 234
235 235 data = render('rhodecode:templates/admin/settings/settings.mako',
236 236 self._get_template_context(c), self.request)
237 237 html = formencode.htmlfill.render(
238 238 data,
239 239 defaults=self._form_defaults(),
240 240 encoding="UTF-8",
241 241 force_defaults=False
242 242 )
243 243 return Response(html)
244 244
245 245 @LoginRequired()
246 246 @HasPermissionAllDecorator('hg.admin')
247 247 @CSRFRequired()
248 248 @view_config(
249 249 route_name='admin_settings_mapping_update', request_method='POST',
250 250 renderer='rhodecode:templates/admin/settings/settings.mako')
251 251 def settings_mapping_update(self):
252 252 _ = self.request.translate
253 253 c = self.load_default_context()
254 254 c.active = 'mapping'
255 255 rm_obsolete = self.request.POST.get('destroy', False)
256 256 invalidate_cache = self.request.POST.get('invalidate', False)
257 257 log.debug(
258 258 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
259 259
260 260 if invalidate_cache:
261 261 log.debug('invalidating all repositories cache')
262 262 for repo in Repository.get_all():
263 263 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
264 264
265 265 filesystem_repos = ScmModel().repo_scan()
266 266 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
267 267 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
268 268 h.flash(_('Repositories successfully '
269 269 'rescanned added: %s ; removed: %s') %
270 270 (_repr(added), _repr(removed)),
271 271 category='success')
272 272 raise HTTPFound(h.route_path('admin_settings_mapping'))
273 273
274 274 @LoginRequired()
275 275 @HasPermissionAllDecorator('hg.admin')
276 276 @view_config(
277 277 route_name='admin_settings', request_method='GET',
278 278 renderer='rhodecode:templates/admin/settings/settings.mako')
279 279 @view_config(
280 280 route_name='admin_settings_global', request_method='GET',
281 281 renderer='rhodecode:templates/admin/settings/settings.mako')
282 282 def settings_global(self):
283 283 c = self.load_default_context()
284 284 c.active = 'global'
285 285 c.personal_repo_group_default_pattern = RepoGroupModel()\
286 286 .get_personal_group_name_pattern()
287 287
288 288 data = render('rhodecode:templates/admin/settings/settings.mako',
289 289 self._get_template_context(c), self.request)
290 290 html = formencode.htmlfill.render(
291 291 data,
292 292 defaults=self._form_defaults(),
293 293 encoding="UTF-8",
294 294 force_defaults=False
295 295 )
296 296 return Response(html)
297 297
298 298 @LoginRequired()
299 299 @HasPermissionAllDecorator('hg.admin')
300 300 @CSRFRequired()
301 301 @view_config(
302 302 route_name='admin_settings_update', request_method='POST',
303 303 renderer='rhodecode:templates/admin/settings/settings.mako')
304 304 @view_config(
305 305 route_name='admin_settings_global_update', request_method='POST',
306 306 renderer='rhodecode:templates/admin/settings/settings.mako')
307 307 def settings_global_update(self):
308 308 _ = self.request.translate
309 309 c = self.load_default_context()
310 310 c.active = 'global'
311 311 c.personal_repo_group_default_pattern = RepoGroupModel()\
312 312 .get_personal_group_name_pattern()
313 313 application_form = ApplicationSettingsForm(self.request.translate)()
314 314 try:
315 315 form_result = application_form.to_python(dict(self.request.POST))
316 316 except formencode.Invalid as errors:
317 h.flash(
318 _("Some form inputs contain invalid data."),
319 category='error')
317 320 data = render('rhodecode:templates/admin/settings/settings.mako',
318 321 self._get_template_context(c), self.request)
319 322 html = formencode.htmlfill.render(
320 323 data,
321 324 defaults=errors.value,
322 325 errors=errors.error_dict or {},
323 326 prefix_error=False,
324 327 encoding="UTF-8",
325 328 force_defaults=False
326 329 )
327 330 return Response(html)
328 331
329 332 settings = [
330 333 ('title', 'rhodecode_title', 'unicode'),
331 334 ('realm', 'rhodecode_realm', 'unicode'),
332 335 ('pre_code', 'rhodecode_pre_code', 'unicode'),
333 336 ('post_code', 'rhodecode_post_code', 'unicode'),
334 337 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
335 338 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
336 339 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
337 340 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
338 341 ]
339 342 try:
340 343 for setting, form_key, type_ in settings:
341 344 sett = SettingsModel().create_or_update_setting(
342 345 setting, form_result[form_key], type_)
343 346 Session().add(sett)
344 347
345 348 Session().commit()
346 349 SettingsModel().invalidate_settings_cache()
347 350 h.flash(_('Updated application settings'), category='success')
348 351 except Exception:
349 352 log.exception("Exception while updating application settings")
350 353 h.flash(
351 354 _('Error occurred during updating application settings'),
352 355 category='error')
353 356
354 357 raise HTTPFound(h.route_path('admin_settings_global'))
355 358
356 359 @LoginRequired()
357 360 @HasPermissionAllDecorator('hg.admin')
358 361 @view_config(
359 362 route_name='admin_settings_visual', request_method='GET',
360 363 renderer='rhodecode:templates/admin/settings/settings.mako')
361 364 def settings_visual(self):
362 365 c = self.load_default_context()
363 366 c.active = 'visual'
364 367
365 368 data = render('rhodecode:templates/admin/settings/settings.mako',
366 369 self._get_template_context(c), self.request)
367 370 html = formencode.htmlfill.render(
368 371 data,
369 372 defaults=self._form_defaults(),
370 373 encoding="UTF-8",
371 374 force_defaults=False
372 375 )
373 376 return Response(html)
374 377
375 378 @LoginRequired()
376 379 @HasPermissionAllDecorator('hg.admin')
377 380 @CSRFRequired()
378 381 @view_config(
379 382 route_name='admin_settings_visual_update', request_method='POST',
380 383 renderer='rhodecode:templates/admin/settings/settings.mako')
381 384 def settings_visual_update(self):
382 385 _ = self.request.translate
383 386 c = self.load_default_context()
384 387 c.active = 'visual'
385 388 application_form = ApplicationVisualisationForm(self.request.translate)()
386 389 try:
387 390 form_result = application_form.to_python(dict(self.request.POST))
388 391 except formencode.Invalid as errors:
392 h.flash(
393 _("Some form inputs contain invalid data."),
394 category='error')
389 395 data = render('rhodecode:templates/admin/settings/settings.mako',
390 396 self._get_template_context(c), self.request)
391 397 html = formencode.htmlfill.render(
392 398 data,
393 399 defaults=errors.value,
394 400 errors=errors.error_dict or {},
395 401 prefix_error=False,
396 402 encoding="UTF-8",
397 403 force_defaults=False
398 404 )
399 405 return Response(html)
400 406
401 407 try:
402 408 settings = [
403 409 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
404 410 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
405 411 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
406 412 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
407 413 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
408 414 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
409 415 ('show_version', 'rhodecode_show_version', 'bool'),
410 416 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
411 417 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
412 418 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
413 419 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
414 420 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
415 421 ('support_url', 'rhodecode_support_url', 'unicode'),
416 422 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
417 423 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
418 424 ]
419 425 for setting, form_key, type_ in settings:
420 426 sett = SettingsModel().create_or_update_setting(
421 427 setting, form_result[form_key], type_)
422 428 Session().add(sett)
423 429
424 430 Session().commit()
425 431 SettingsModel().invalidate_settings_cache()
426 432 h.flash(_('Updated visualisation settings'), category='success')
427 433 except Exception:
428 434 log.exception("Exception updating visualization settings")
429 435 h.flash(_('Error occurred during updating '
430 436 'visualisation settings'),
431 437 category='error')
432 438
433 439 raise HTTPFound(h.route_path('admin_settings_visual'))
434 440
435 441 @LoginRequired()
436 442 @HasPermissionAllDecorator('hg.admin')
437 443 @view_config(
438 444 route_name='admin_settings_issuetracker', request_method='GET',
439 445 renderer='rhodecode:templates/admin/settings/settings.mako')
440 446 def settings_issuetracker(self):
441 447 c = self.load_default_context()
442 448 c.active = 'issuetracker'
443 449 defaults = SettingsModel().get_all_settings()
444 450
445 451 entry_key = 'rhodecode_issuetracker_pat_'
446 452
447 453 c.issuetracker_entries = {}
448 454 for k, v in defaults.items():
449 455 if k.startswith(entry_key):
450 456 uid = k[len(entry_key):]
451 457 c.issuetracker_entries[uid] = None
452 458
453 459 for uid in c.issuetracker_entries:
454 460 c.issuetracker_entries[uid] = AttributeDict({
455 461 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
456 462 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
457 463 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
458 464 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
459 465 })
460 466
461 467 return self._get_template_context(c)
462 468
463 469 @LoginRequired()
464 470 @HasPermissionAllDecorator('hg.admin')
465 471 @CSRFRequired()
466 472 @view_config(
467 473 route_name='admin_settings_issuetracker_test', request_method='POST',
468 474 renderer='string', xhr=True)
469 475 def settings_issuetracker_test(self):
470 476 return h.urlify_commit_message(
471 477 self.request.POST.get('test_text', ''),
472 478 'repo_group/test_repo1')
473 479
474 480 @LoginRequired()
475 481 @HasPermissionAllDecorator('hg.admin')
476 482 @CSRFRequired()
477 483 @view_config(
478 484 route_name='admin_settings_issuetracker_update', request_method='POST',
479 485 renderer='rhodecode:templates/admin/settings/settings.mako')
480 486 def settings_issuetracker_update(self):
481 487 _ = self.request.translate
482 488 self.load_default_context()
483 489 settings_model = IssueTrackerSettingsModel()
484 490
485 491 try:
486 492 form = IssueTrackerPatternsForm(self.request.translate)()
487 493 data = form.to_python(self.request.POST)
488 494 except formencode.Invalid as errors:
489 495 log.exception('Failed to add new pattern')
490 496 error = errors
491 497 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
492 498 category='error')
493 499 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
494 500
495 501 if data:
496 502 for uid in data.get('delete_patterns', []):
497 503 settings_model.delete_entries(uid)
498 504
499 505 for pattern in data.get('patterns', []):
500 506 for setting, value, type_ in pattern:
501 507 sett = settings_model.create_or_update_setting(
502 508 setting, value, type_)
503 509 Session().add(sett)
504 510
505 511 Session().commit()
506 512
507 513 SettingsModel().invalidate_settings_cache()
508 514 h.flash(_('Updated issue tracker entries'), category='success')
509 515 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
510 516
511 517 @LoginRequired()
512 518 @HasPermissionAllDecorator('hg.admin')
513 519 @CSRFRequired()
514 520 @view_config(
515 521 route_name='admin_settings_issuetracker_delete', request_method='POST',
516 522 renderer='rhodecode:templates/admin/settings/settings.mako')
517 523 def settings_issuetracker_delete(self):
518 524 _ = self.request.translate
519 525 self.load_default_context()
520 526 uid = self.request.POST.get('uid')
521 527 try:
522 528 IssueTrackerSettingsModel().delete_entries(uid)
523 529 except Exception:
524 530 log.exception('Failed to delete issue tracker setting %s', uid)
525 531 raise HTTPNotFound()
526 532 h.flash(_('Removed issue tracker entry'), category='success')
527 533 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
528 534
529 535 @LoginRequired()
530 536 @HasPermissionAllDecorator('hg.admin')
531 537 @view_config(
532 538 route_name='admin_settings_email', request_method='GET',
533 539 renderer='rhodecode:templates/admin/settings/settings.mako')
534 540 def settings_email(self):
535 541 c = self.load_default_context()
536 542 c.active = 'email'
537 543 c.rhodecode_ini = rhodecode.CONFIG
538 544
539 545 data = render('rhodecode:templates/admin/settings/settings.mako',
540 546 self._get_template_context(c), self.request)
541 547 html = formencode.htmlfill.render(
542 548 data,
543 549 defaults=self._form_defaults(),
544 550 encoding="UTF-8",
545 551 force_defaults=False
546 552 )
547 553 return Response(html)
548 554
549 555 @LoginRequired()
550 556 @HasPermissionAllDecorator('hg.admin')
551 557 @CSRFRequired()
552 558 @view_config(
553 559 route_name='admin_settings_email_update', request_method='POST',
554 560 renderer='rhodecode:templates/admin/settings/settings.mako')
555 561 def settings_email_update(self):
556 562 _ = self.request.translate
557 563 c = self.load_default_context()
558 564 c.active = 'email'
559 565
560 566 test_email = self.request.POST.get('test_email')
561 567
562 568 if not test_email:
563 569 h.flash(_('Please enter email address'), category='error')
564 570 raise HTTPFound(h.route_path('admin_settings_email'))
565 571
566 572 email_kwargs = {
567 573 'date': datetime.datetime.now(),
568 574 'user': c.rhodecode_user,
569 575 'rhodecode_version': c.rhodecode_version
570 576 }
571 577
572 578 (subject, headers, email_body,
573 579 email_body_plaintext) = EmailNotificationModel().render_email(
574 580 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
575 581
576 582 recipients = [test_email] if test_email else None
577 583
578 584 run_task(tasks.send_email, recipients, subject,
579 585 email_body_plaintext, email_body)
580 586
581 587 h.flash(_('Send email task created'), category='success')
582 588 raise HTTPFound(h.route_path('admin_settings_email'))
583 589
584 590 @LoginRequired()
585 591 @HasPermissionAllDecorator('hg.admin')
586 592 @view_config(
587 593 route_name='admin_settings_hooks', request_method='GET',
588 594 renderer='rhodecode:templates/admin/settings/settings.mako')
589 595 def settings_hooks(self):
590 596 c = self.load_default_context()
591 597 c.active = 'hooks'
592 598
593 599 model = SettingsModel()
594 600 c.hooks = model.get_builtin_hooks()
595 601 c.custom_hooks = model.get_custom_hooks()
596 602
597 603 data = render('rhodecode:templates/admin/settings/settings.mako',
598 604 self._get_template_context(c), self.request)
599 605 html = formencode.htmlfill.render(
600 606 data,
601 607 defaults=self._form_defaults(),
602 608 encoding="UTF-8",
603 609 force_defaults=False
604 610 )
605 611 return Response(html)
606 612
607 613 @LoginRequired()
608 614 @HasPermissionAllDecorator('hg.admin')
609 615 @CSRFRequired()
610 616 @view_config(
611 617 route_name='admin_settings_hooks_update', request_method='POST',
612 618 renderer='rhodecode:templates/admin/settings/settings.mako')
613 619 @view_config(
614 620 route_name='admin_settings_hooks_delete', request_method='POST',
615 621 renderer='rhodecode:templates/admin/settings/settings.mako')
616 622 def settings_hooks_update(self):
617 623 _ = self.request.translate
618 624 c = self.load_default_context()
619 625 c.active = 'hooks'
620 626 if c.visual.allow_custom_hooks_settings:
621 627 ui_key = self.request.POST.get('new_hook_ui_key')
622 628 ui_value = self.request.POST.get('new_hook_ui_value')
623 629
624 630 hook_id = self.request.POST.get('hook_id')
625 631 new_hook = False
626 632
627 633 model = SettingsModel()
628 634 try:
629 635 if ui_value and ui_key:
630 636 model.create_or_update_hook(ui_key, ui_value)
631 637 h.flash(_('Added new hook'), category='success')
632 638 new_hook = True
633 639 elif hook_id:
634 640 RhodeCodeUi.delete(hook_id)
635 641 Session().commit()
636 642
637 643 # check for edits
638 644 update = False
639 645 _d = self.request.POST.dict_of_lists()
640 646 for k, v in zip(_d.get('hook_ui_key', []),
641 647 _d.get('hook_ui_value_new', [])):
642 648 model.create_or_update_hook(k, v)
643 649 update = True
644 650
645 651 if update and not new_hook:
646 652 h.flash(_('Updated hooks'), category='success')
647 653 Session().commit()
648 654 except Exception:
649 655 log.exception("Exception during hook creation")
650 656 h.flash(_('Error occurred during hook creation'),
651 657 category='error')
652 658
653 659 raise HTTPFound(h.route_path('admin_settings_hooks'))
654 660
655 661 @LoginRequired()
656 662 @HasPermissionAllDecorator('hg.admin')
657 663 @view_config(
658 664 route_name='admin_settings_search', request_method='GET',
659 665 renderer='rhodecode:templates/admin/settings/settings.mako')
660 666 def settings_search(self):
661 667 c = self.load_default_context()
662 668 c.active = 'search'
663 669
664 670 searcher = searcher_from_config(self.request.registry.settings)
665 671 c.statistics = searcher.statistics(self.request.translate)
666 672
667 673 return self._get_template_context(c)
668 674
669 675 @LoginRequired()
670 676 @HasPermissionAllDecorator('hg.admin')
671 677 @view_config(
678 route_name='admin_settings_automation', request_method='GET',
679 renderer='rhodecode:templates/admin/settings/settings.mako')
680 def settings_automation(self):
681 c = self.load_default_context()
682 c.active = 'automation'
683
684 return self._get_template_context(c)
685
686 @LoginRequired()
687 @HasPermissionAllDecorator('hg.admin')
688 @view_config(
672 689 route_name='admin_settings_labs', request_method='GET',
673 690 renderer='rhodecode:templates/admin/settings/settings.mako')
674 691 def settings_labs(self):
675 692 c = self.load_default_context()
676 693 if not c.labs_active:
677 694 raise HTTPFound(h.route_path('admin_settings'))
678 695
679 696 c.active = 'labs'
680 697 c.lab_settings = _LAB_SETTINGS
681 698
682 699 data = render('rhodecode:templates/admin/settings/settings.mako',
683 700 self._get_template_context(c), self.request)
684 701 html = formencode.htmlfill.render(
685 702 data,
686 703 defaults=self._form_defaults(),
687 704 encoding="UTF-8",
688 705 force_defaults=False
689 706 )
690 707 return Response(html)
691 708
692 709 @LoginRequired()
693 710 @HasPermissionAllDecorator('hg.admin')
694 711 @CSRFRequired()
695 712 @view_config(
696 713 route_name='admin_settings_labs_update', request_method='POST',
697 714 renderer='rhodecode:templates/admin/settings/settings.mako')
698 715 def settings_labs_update(self):
699 716 _ = self.request.translate
700 717 c = self.load_default_context()
701 718 c.active = 'labs'
702 719
703 720 application_form = LabsSettingsForm(self.request.translate)()
704 721 try:
705 722 form_result = application_form.to_python(dict(self.request.POST))
706 723 except formencode.Invalid as errors:
707 724 h.flash(
708 _('Some form inputs contain invalid data.'),
725 _("Some form inputs contain invalid data."),
709 726 category='error')
710 727 data = render('rhodecode:templates/admin/settings/settings.mako',
711 728 self._get_template_context(c), self.request)
712 729 html = formencode.htmlfill.render(
713 730 data,
714 731 defaults=errors.value,
715 732 errors=errors.error_dict or {},
716 733 prefix_error=False,
717 734 encoding="UTF-8",
718 735 force_defaults=False
719 736 )
720 737 return Response(html)
721 738
722 739 try:
723 740 session = Session()
724 741 for setting in _LAB_SETTINGS:
725 742 setting_name = setting.key[len('rhodecode_'):]
726 743 sett = SettingsModel().create_or_update_setting(
727 744 setting_name, form_result[setting.key], setting.type)
728 745 session.add(sett)
729 746
730 747 except Exception:
731 748 log.exception('Exception while updating lab settings')
732 749 h.flash(_('Error occurred during updating labs settings'),
733 750 category='error')
734 751 else:
735 752 Session().commit()
736 753 SettingsModel().invalidate_settings_cache()
737 754 h.flash(_('Updated Labs settings'), category='success')
738 755 raise HTTPFound(h.route_path('admin_settings_labs'))
739 756
740 757 data = render('rhodecode:templates/admin/settings/settings.mako',
741 758 self._get_template_context(c), self.request)
742 759 html = formencode.htmlfill.render(
743 760 data,
744 761 defaults=self._form_defaults(),
745 762 encoding="UTF-8",
746 763 force_defaults=False
747 764 )
748 765 return Response(html)
749 766
750 767
751 768 # :param key: name of the setting including the 'rhodecode_' prefix
752 769 # :param type: the RhodeCodeSetting type to use.
753 770 # :param group: the i18ned group in which we should dispaly this setting
754 771 # :param label: the i18ned label we should display for this setting
755 772 # :param help: the i18ned help we should dispaly for this setting
756 773 LabSetting = collections.namedtuple(
757 774 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
758 775
759 776
760 777 # This list has to be kept in sync with the form
761 778 # rhodecode.model.forms.LabsSettingsForm.
762 779 _LAB_SETTINGS = [
763 780
764 781 ]
@@ -1,197 +1,200 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import urllib2
23 23
24 24 from pyramid.view import view_config
25 25
26 26 import rhodecode
27 27 from rhodecode.apps._base import BaseAppView
28 28 from rhodecode.apps.admin.navigation import navigation_list
29 29 from rhodecode.lib import helpers as h
30 30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
31 31 from rhodecode.lib.utils2 import str2bool
32 32 from rhodecode.lib import system_info
33 33 from rhodecode.model.update import UpdateModel
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class AdminSystemInfoSettingsView(BaseAppView):
39 39 def load_default_context(self):
40 40 c = self._get_local_tmpl_context()
41 41 return c
42 42
43 43 @LoginRequired()
44 44 @HasPermissionAllDecorator('hg.admin')
45 45 @view_config(
46 46 route_name='admin_settings_system', request_method='GET',
47 47 renderer='rhodecode:templates/admin/settings/settings.mako')
48 48 def settings_system_info(self):
49 49 _ = self.request.translate
50 50 c = self.load_default_context()
51 51
52 52 c.active = 'system'
53 53 c.navlist = navigation_list(self.request)
54 54
55 55 # TODO(marcink), figure out how to allow only selected users to do this
56 56 c.allowed_to_snapshot = self._rhodecode_user.admin
57 57
58 58 snapshot = str2bool(self.request.params.get('snapshot'))
59 59
60 60 c.rhodecode_update_url = UpdateModel().get_update_url()
61 61 server_info = system_info.get_system_info(self.request.environ)
62 62
63 63 for key, val in server_info.items():
64 64 setattr(c, key, val)
65 65
66 66 def val(name, subkey='human_value'):
67 67 return server_info[name][subkey]
68 68
69 69 def state(name):
70 70 return server_info[name]['state']
71 71
72 72 def val2(name):
73 73 val = server_info[name]['human_value']
74 74 state = server_info[name]['state']
75 75 return val, state
76 76
77 77 update_info_msg = _('Note: please make sure this server can '
78 78 'access `${url}` for the update link to work',
79 79 mapping=dict(url=c.rhodecode_update_url))
80 80 version = UpdateModel().get_stored_version()
81 81 is_outdated = UpdateModel().is_outdated(
82 82 rhodecode.__version__, version)
83 83 update_state = {
84 84 'type': 'warning',
85 85 'message': 'New version available: {}'.format(version)
86 86 } \
87 87 if is_outdated else {}
88 88 c.data_items = [
89 89 # update info
90 90 (_('Update info'), h.literal(
91 91 '<span class="link" id="check_for_update" >%s.</span>' % (
92 92 _('Check for updates')) +
93 93 '<br/> <span >%s.</span>' % (update_info_msg)
94 94 ), ''),
95 95
96 96 # RhodeCode specific
97 97 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
98 98 (_('Latest version'), version, update_state),
99 99 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
100 100 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
101 101 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
102 102 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
103 103 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
104 104 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
105 105 ('', '', ''), # spacer
106 106
107 107 # Database
108 108 (_('Database'), val('database')['url'], state('database')),
109 109 (_('Database version'), val('database')['version'], state('database')),
110 110 ('', '', ''), # spacer
111 111
112 112 # Platform/Python
113 113 (_('Platform'), val('platform')['name'], state('platform')),
114 114 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
115 115 (_('Python version'), val('python')['version'], state('python')),
116 116 (_('Python path'), val('python')['executable'], state('python')),
117 117 ('', '', ''), # spacer
118 118
119 119 # Systems stats
120 120 (_('CPU'), val('cpu')['text'], state('cpu')),
121 121 (_('Load'), val('load')['text'], state('load')),
122 122 (_('Memory'), val('memory')['text'], state('memory')),
123 123 (_('Uptime'), val('uptime')['text'], state('uptime')),
124 124 ('', '', ''), # spacer
125 125
126 # ulimit
127 (_('Ulimit'), val('ulimit')['text'], state('ulimit')),
128
126 129 # Repo storage
127 130 (_('Storage location'), val('storage')['path'], state('storage')),
128 131 (_('Storage info'), val('storage')['text'], state('storage')),
129 132 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
130 133
131 134 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
132 135 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
133 136
134 137 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
135 138 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
136 139
137 140 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
138 141 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
139 142
140 143 (_('Search info'), val('search')['text'], state('search')),
141 144 (_('Search location'), val('search')['location'], state('search')),
142 145 ('', '', ''), # spacer
143 146
144 147 # VCS specific
145 148 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
146 149 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
147 150 (_('GIT'), val('git'), state('git')),
148 151 (_('HG'), val('hg'), state('hg')),
149 152 (_('SVN'), val('svn'), state('svn')),
150 153
151 154 ]
152 155
153 156 if snapshot:
154 157 if c.allowed_to_snapshot:
155 158 c.data_items.pop(0) # remove server info
156 159 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
157 160 else:
158 161 h.flash('You are not allowed to do this', category='warning')
159 162 return self._get_template_context(c)
160 163
161 164 @LoginRequired()
162 165 @HasPermissionAllDecorator('hg.admin')
163 166 @view_config(
164 167 route_name='admin_settings_system_update', request_method='GET',
165 168 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
166 169 def settings_system_info_check_update(self):
167 170 _ = self.request.translate
168 171 c = self.load_default_context()
169 172
170 173 update_url = UpdateModel().get_update_url()
171 174
172 175 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
173 176 try:
174 177 data = UpdateModel().get_update_data(update_url)
175 178 except urllib2.URLError as e:
176 179 log.exception("Exception contacting upgrade server")
177 180 self.request.override_renderer = 'string'
178 181 return _err('Failed to contact upgrade server: %r' % e)
179 182 except ValueError as e:
180 183 log.exception("Bad data sent from update server")
181 184 self.request.override_renderer = 'string'
182 185 return _err('Bad data sent from update server')
183 186
184 187 latest = data['versions'][0]
185 188
186 189 c.update_url = update_url
187 190 c.latest_data = latest
188 191 c.latest_ver = latest['version']
189 192 c.cur_ver = rhodecode.__version__
190 193 c.should_upgrade = False
191 194
192 195 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
193 196 if is_oudated:
194 197 c.should_upgrade = True
195 198 c.important_notices = latest['general']
196 199 UpdateModel().store_version(latest['version'])
197 200 return self._get_template_context(c)
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now