##// END OF EJS Templates
api: add a max_file_bytes parameter to get_nodes so that large...
dan -
r502:8bb872ec default
parent child Browse files
Show More
@@ -1,2824 +1,2825 b''
1 .. _api:
1 .. _api:
2
2
3 API Documentation
3 API Documentation
4 =================
4 =================
5
5
6 The |RCE| API uses a single scheme for calling all API methods. The API is
6 The |RCE| API uses a single scheme for calling all API methods. The API is
7 implemented with JSON protocol in both directions. To send API requests to
7 implemented with JSON protocol in both directions. To send API requests to
8 your instance of |RCE|, use the following URL format
8 your instance of |RCE|, use the following URL format
9 ``<your_server>/_admin``
9 ``<your_server>/_admin``
10
10
11 .. note::
11 .. note::
12
12
13 To use the API, you should configure the :file:`~/.rhoderc` file with
13 To use the API, you should configure the :file:`~/.rhoderc` file with
14 access details per instance. For more information, see
14 access details per instance. For more information, see
15 :ref:`config-rhoderc`.
15 :ref:`config-rhoderc`.
16
16
17
17
18 API ACCESS FOR WEB VIEWS
18 API ACCESS FOR WEB VIEWS
19 ------------------------
19 ------------------------
20
20
21 API access can also be turned on for each web view in |RCE| that is
21 API access can also be turned on for each web view in |RCE| that is
22 decorated with a `@LoginRequired` decorator. To enable API access, change
22 decorated with a `@LoginRequired` decorator. To enable API access, change
23 the standard login decorator to `@LoginRequired(api_access=True)`.
23 the standard login decorator to `@LoginRequired(api_access=True)`.
24
24
25 From |RCM| version 1.7.0 you can configure a white list
25 From |RCM| version 1.7.0 you can configure a white list
26 of views that have API access enabled by default. To enable these,
26 of views that have API access enabled by default. To enable these,
27 edit the |RCM| configuration ``.ini`` file. The default location is:
27 edit the |RCM| configuration ``.ini`` file. The default location is:
28
28
29 * |RCM| Pre-2.2.7 :file:`root/rhodecode/data/production.ini`
29 * |RCM| Pre-2.2.7 :file:`root/rhodecode/data/production.ini`
30 * |RCM| 3.0 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
30 * |RCM| 3.0 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini`
31
31
32 To configure the white list, edit this section of the file. In this
32 To configure the white list, edit this section of the file. In this
33 configuration example, API access is granted to the patch/diff raw file and
33 configuration example, API access is granted to the patch/diff raw file and
34 archive.
34 archive.
35
35
36 .. code-block:: ini
36 .. code-block:: ini
37
37
38 ## List of controllers (using glob syntax) that AUTH TOKENS could be used for access.
38 ## List of controllers (using glob syntax) that AUTH TOKENS could be used for access.
39 ## Adding ?auth_token = <token> to the url authenticates this request as if it
39 ## Adding ?auth_token = <token> to the url authenticates this request as if it
40 ## came from the the logged in user who own this authentication token.
40 ## came from the the logged in user who own this authentication token.
41 ##
41 ##
42 ## Syntax is <ControllerClass>:<function_pattern>.
42 ## Syntax is <ControllerClass>:<function_pattern>.
43 ## The list should be "," separated and on a single line.
43 ## The list should be "," separated and on a single line.
44 ##
44 ##
45 api_access_controllers_whitelist = ChangesetController:changeset_patch,ChangesetController:changeset_raw,ilesController:raw,FilesController:archivefile,
45 api_access_controllers_whitelist = ChangesetController:changeset_patch,ChangesetController:changeset_raw,ilesController:raw,FilesController:archivefile,
46
46
47 After this change, a |RCE| view can be accessed without login by adding a
47 After this change, a |RCE| view can be accessed without login by adding a
48 GET parameter ``?auth_token=<auth_token>`` to a url. For example to
48 GET parameter ``?auth_token=<auth_token>`` to a url. For example to
49 access the raw diff.
49 access the raw diff.
50
50
51 .. code-block:: html
51 .. code-block:: html
52
52
53 http://<server>/<repo>/changeset-diff/<sha>?auth_token=<auth_token>
53 http://<server>/<repo>/changeset-diff/<sha>?auth_token=<auth_token>
54
54
55 By default this is only enabled on RSS/ATOM feed views. Exposing raw diffs is a
55 By default this is only enabled on RSS/ATOM feed views. Exposing raw diffs is a
56 good way to integrate with 3rd party services like code review, or build farms
56 good way to integrate with 3rd party services like code review, or build farms
57 that could download archives.
57 that could download archives.
58
58
59 API ACCESS
59 API ACCESS
60 ----------
60 ----------
61
61
62 All clients are required to send JSON-RPC spec JSON data.
62 All clients are required to send JSON-RPC spec JSON data.
63
63
64 .. code-block:: bash
64 .. code-block:: bash
65
65
66 {
66 {
67 "id:"<id>",
67 "id:"<id>",
68 "auth_token":"<auth_token>",
68 "auth_token":"<auth_token>",
69 "method":"<method_name>",
69 "method":"<method_name>",
70 "args":{"<arg_key>":"<arg_val>"}
70 "args":{"<arg_key>":"<arg_val>"}
71 }
71 }
72
72
73 Example call for auto pulling from remote repositories using curl:
73 Example call for auto pulling from remote repositories using curl:
74
74
75 .. code-block:: bash
75 .. code-block:: bash
76
76
77 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,
77 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,
78 "auth_token":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull", "args":{"repo":"CPython"}}'
78 "auth_token":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull", "args":{"repo":"CPython"}}'
79
79
80 Provide those parameters:
80 Provide those parameters:
81 - **id** A value of any type, which is used to match the response with the
81 - **id** A value of any type, which is used to match the response with the
82 request that it is replying to.
82 request that it is replying to.
83 - **auth_token** for access and permission validation.
83 - **auth_token** for access and permission validation.
84 - **method** is name of method to call
84 - **method** is name of method to call
85 - **args** is an ``key:value`` list of arguments to pass to method
85 - **args** is an ``key:value`` list of arguments to pass to method
86
86
87 .. note::
87 .. note::
88
88
89 To get your |authtoken|, from the |RCE| interface,
89 To get your |authtoken|, from the |RCE| interface,
90 go to:
90 go to:
91 :menuselection:`username --> My account --> Auth tokens`
91 :menuselection:`username --> My account --> Auth tokens`
92
92
93 For security reasons you should always create a dedicated |authtoken| for
93 For security reasons you should always create a dedicated |authtoken| for
94 API use only.
94 API use only.
95
95
96
96
97 The |RCE| API will always return a JSON-RPC response:
97 The |RCE| API will always return a JSON-RPC response:
98
98
99 .. code-block:: bash
99 .. code-block:: bash
100
100
101 {
101 {
102 "id": <id>, # matching id sent by request
102 "id": <id>, # matching id sent by request
103 "result": "<result>"|null, # JSON formatted result, null if any errors
103 "result": "<result>"|null, # JSON formatted result, null if any errors
104 "error": "null"|<error_message> # JSON formatted error (if any)
104 "error": "null"|<error_message> # JSON formatted error (if any)
105 }
105 }
106
106
107 All responses from API will be with `HTTP/1.0 200 OK` status code.
107 All responses from API will be with `HTTP/1.0 200 OK` status code.
108 If there is an error when calling the API, the *error* key will contain a
108 If there is an error when calling the API, the *error* key will contain a
109 failure description and the *result* will be `null`.
109 failure description and the *result* will be `null`.
110
110
111 API CLIENT
111 API CLIENT
112 ----------
112 ----------
113
113
114 To install the |RCE| API, see :ref:`install-tools`. To configure the API per
114 To install the |RCE| API, see :ref:`install-tools`. To configure the API per
115 instance, see the :ref:`rc-tools` section as you need to configure a
115 instance, see the :ref:`rc-tools` section as you need to configure a
116 :file:`~/.rhoderc` file with your |authtokens|.
116 :file:`~/.rhoderc` file with your |authtokens|.
117
117
118 Once you have set up your instance API access, use the following examples to
118 Once you have set up your instance API access, use the following examples to
119 get started.
119 get started.
120
120
121 .. code-block:: bash
121 .. code-block:: bash
122
122
123 # Getting the 'rhodecode' repository
123 # Getting the 'rhodecode' repository
124 # from a RhodeCode Enterprise instance
124 # from a RhodeCode Enterprise instance
125 rhodecode-api --instance-name=enterprise-1 get_repo repoid:rhodecode
125 rhodecode-api --instance-name=enterprise-1 get_repo repoid:rhodecode
126
126
127 Calling method get_repo => http://127.0.0.1:5000
127 Calling method get_repo => http://127.0.0.1:5000
128 Server response
128 Server response
129 {
129 {
130 <json data>
130 <json data>
131 }
131 }
132
132
133 # Creating a new mercurial repository called 'brand-new'
133 # Creating a new mercurial repository called 'brand-new'
134 # with a description 'Repo-description'
134 # with a description 'Repo-description'
135 rhodecode-api --instance-name=enterprise-1 create_repo repo_name:brand-new repo_type:hg description:Repo-description
135 rhodecode-api --instance-name=enterprise-1 create_repo repo_name:brand-new repo_type:hg description:Repo-description
136 {
136 {
137 "error": null,
137 "error": null,
138 "id": 1110,
138 "id": 1110,
139 "result": {
139 "result": {
140 "msg": "Created new repository `brand-new`",
140 "msg": "Created new repository `brand-new`",
141 "success": true,
141 "success": true,
142 "task": null
142 "task": null
143 }
143 }
144 }
144 }
145
145
146 A broken example, what not to do.
146 A broken example, what not to do.
147
147
148 .. code-block:: bash
148 .. code-block:: bash
149
149
150 # A call missing the required arguments
150 # A call missing the required arguments
151 # and not specifying the instance
151 # and not specifying the instance
152 rhodecode-api get_repo
152 rhodecode-api get_repo
153
153
154 Calling method get_repo => http://127.0.0.1:5000
154 Calling method get_repo => http://127.0.0.1:5000
155 Server response
155 Server response
156 "Missing non optional `repoid` arg in JSON DATA"
156 "Missing non optional `repoid` arg in JSON DATA"
157
157
158 You can specify pure JSON using the ``--format`` parameter.
158 You can specify pure JSON using the ``--format`` parameter.
159
159
160 .. code-block:: bash
160 .. code-block:: bash
161
161
162 rhodecode-api --format=json get_repo repoid:rhodecode
162 rhodecode-api --format=json get_repo repoid:rhodecode
163
163
164 In such case only output that this function shows is pure JSON, we can use that
164 In such case only output that this function shows is pure JSON, we can use that
165 and pipe output to some json formatter.
165 and pipe output to some json formatter.
166
166
167 If output is in pure JSON format, you can pipe output to a JSON formatter.
167 If output is in pure JSON format, you can pipe output to a JSON formatter.
168
168
169 .. code-block:: bash
169 .. code-block:: bash
170
170
171 rhodecode-api --instance-name=enterprise-1 --format=json get_repo repoid:rhodecode | python -m json.tool
171 rhodecode-api --instance-name=enterprise-1 --format=json get_repo repoid:rhodecode | python -m json.tool
172
172
173 API METHODS
173 API METHODS
174 -----------
174 -----------
175
175
176 Each method by default required following arguments.
176 Each method by default required following arguments.
177
177
178 .. code-block:: bash
178 .. code-block:: bash
179
179
180 id : "<id_for_response>"
180 id : "<id_for_response>"
181 auth_token : "<auth_token>"
181 auth_token : "<auth_token>"
182 method : "<method name>"
182 method : "<method name>"
183 args : {}
183 args : {}
184
184
185 Use each **param** from docs and put it in args, Optional parameters
185 Use each **param** from docs and put it in args, Optional parameters
186 are not required in args.
186 are not required in args.
187
187
188 .. code-block:: bash
188 .. code-block:: bash
189
189
190 args: {"repoid": "rhodecode"}
190 args: {"repoid": "rhodecode"}
191
191
192 .. Note: From this point on things are generated by the script in
192 .. Note: From this point on things are generated by the script in
193 `scripts/fabfile.py`. To change things below, update the docstrings in the
193 `scripts/fabfile.py`. To change things below, update the docstrings in the
194 ApiController.
194 ApiController.
195
195
196 .. --- API DEFS MARKER ---
196 .. --- API DEFS MARKER ---
197
197
198 pull
198 pull
199 ----
199 ----
200
200
201 .. py:function:: pull(apiuser, repoid)
201 .. py:function:: pull(apiuser, repoid)
202
202
203 Triggers a pull on the given repository from a remote location. You
203 Triggers a pull on the given repository from a remote location. You
204 can use this to keep remote repositories up-to-date.
204 can use this to keep remote repositories up-to-date.
205
205
206 This command can only be run using an |authtoken| with admin
206 This command can only be run using an |authtoken| with admin
207 rights to the specified repository. For more information,
207 rights to the specified repository. For more information,
208 see :ref:`config-token-ref`.
208 see :ref:`config-token-ref`.
209
209
210 This command takes the following options:
210 This command takes the following options:
211
211
212 :param apiuser: This is filled automatically from the |authtoken|.
212 :param apiuser: This is filled automatically from the |authtoken|.
213 :type apiuser: AuthUser
213 :type apiuser: AuthUser
214 :param repoid: The repository name or repository ID.
214 :param repoid: The repository name or repository ID.
215 :type repoid: str or int
215 :type repoid: str or int
216
216
217 Example output:
217 Example output:
218
218
219 .. code-block:: bash
219 .. code-block:: bash
220
220
221 id : <id_given_in_input>
221 id : <id_given_in_input>
222 result : {
222 result : {
223 "msg": "Pulled from `<repository name>`"
223 "msg": "Pulled from `<repository name>`"
224 "repository": "<repository name>"
224 "repository": "<repository name>"
225 }
225 }
226 error : null
226 error : null
227
227
228 Example error output:
228 Example error output:
229
229
230 .. code-block:: bash
230 .. code-block:: bash
231
231
232 id : <id_given_in_input>
232 id : <id_given_in_input>
233 result : null
233 result : null
234 error : {
234 error : {
235 "Unable to pull changes from `<reponame>`"
235 "Unable to pull changes from `<reponame>`"
236 }
236 }
237
237
238
238
239 strip
239 strip
240 -----
240 -----
241
241
242 .. py:function:: strip(apiuser, repoid, revision, branch)
242 .. py:function:: strip(apiuser, repoid, revision, branch)
243
243
244 Strips the given revision from the specified repository.
244 Strips the given revision from the specified repository.
245
245
246 * This will remove the revision and all of its decendants.
246 * This will remove the revision and all of its decendants.
247
247
248 This command can only be run using an |authtoken| with admin rights to
248 This command can only be run using an |authtoken| with admin rights to
249 the specified repository.
249 the specified repository.
250
250
251 This command takes the following options:
251 This command takes the following options:
252
252
253 :param apiuser: This is filled automatically from the |authtoken|.
253 :param apiuser: This is filled automatically from the |authtoken|.
254 :type apiuser: AuthUser
254 :type apiuser: AuthUser
255 :param repoid: The repository name or repository ID.
255 :param repoid: The repository name or repository ID.
256 :type repoid: str or int
256 :type repoid: str or int
257 :param revision: The revision you wish to strip.
257 :param revision: The revision you wish to strip.
258 :type revision: str
258 :type revision: str
259 :param branch: The branch from which to strip the revision.
259 :param branch: The branch from which to strip the revision.
260 :type branch: str
260 :type branch: str
261
261
262 Example output:
262 Example output:
263
263
264 .. code-block:: bash
264 .. code-block:: bash
265
265
266 id : <id_given_in_input>
266 id : <id_given_in_input>
267 result : {
267 result : {
268 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
268 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
269 "repository": "<repository name>"
269 "repository": "<repository name>"
270 }
270 }
271 error : null
271 error : null
272
272
273 Example error output:
273 Example error output:
274
274
275 .. code-block:: bash
275 .. code-block:: bash
276
276
277 id : <id_given_in_input>
277 id : <id_given_in_input>
278 result : null
278 result : null
279 error : {
279 error : {
280 "Unable to strip commit <commit_hash> from repo `<repository name>`"
280 "Unable to strip commit <commit_hash> from repo `<repository name>`"
281 }
281 }
282
282
283
283
284 rescan_repos
284 rescan_repos
285 ------------
285 ------------
286
286
287 .. py:function:: rescan_repos(apiuser, remove_obsolete=<Optional:False>)
287 .. py:function:: rescan_repos(apiuser, remove_obsolete=<Optional:False>)
288
288
289 Triggers a rescan of the specified repositories.
289 Triggers a rescan of the specified repositories.
290
290
291 * If the ``remove_obsolete`` option is set, it also deletes repositories
291 * If the ``remove_obsolete`` option is set, it also deletes repositories
292 that are found in the database but not on the file system, so called
292 that are found in the database but not on the file system, so called
293 "clean zombies".
293 "clean zombies".
294
294
295 This command can only be run using an |authtoken| with admin rights to
295 This command can only be run using an |authtoken| with admin rights to
296 the specified repository.
296 the specified repository.
297
297
298 This command takes the following options:
298 This command takes the following options:
299
299
300 :param apiuser: This is filled automatically from the |authtoken|.
300 :param apiuser: This is filled automatically from the |authtoken|.
301 :type apiuser: AuthUser
301 :type apiuser: AuthUser
302 :param remove_obsolete: Deletes repositories from the database that
302 :param remove_obsolete: Deletes repositories from the database that
303 are not found on the filesystem.
303 are not found on the filesystem.
304 :type remove_obsolete: Optional(``True`` | ``False``)
304 :type remove_obsolete: Optional(``True`` | ``False``)
305
305
306 Example output:
306 Example output:
307
307
308 .. code-block:: bash
308 .. code-block:: bash
309
309
310 id : <id_given_in_input>
310 id : <id_given_in_input>
311 result : {
311 result : {
312 'added': [<added repository name>,...]
312 'added': [<added repository name>,...]
313 'removed': [<removed repository name>,...]
313 'removed': [<removed repository name>,...]
314 }
314 }
315 error : null
315 error : null
316
316
317 Example error output:
317 Example error output:
318
318
319 .. code-block:: bash
319 .. code-block:: bash
320
320
321 id : <id_given_in_input>
321 id : <id_given_in_input>
322 result : null
322 result : null
323 error : {
323 error : {
324 'Error occurred during rescan repositories action'
324 'Error occurred during rescan repositories action'
325 }
325 }
326
326
327
327
328 invalidate_cache
328 invalidate_cache
329 ----------------
329 ----------------
330
330
331 .. py:function:: invalidate_cache(apiuser, repoid, delete_keys=<Optional:False>)
331 .. py:function:: invalidate_cache(apiuser, repoid, delete_keys=<Optional:False>)
332
332
333 Invalidates the cache for the specified repository.
333 Invalidates the cache for the specified repository.
334
334
335 This command can only be run using an |authtoken| with admin rights to
335 This command can only be run using an |authtoken| with admin rights to
336 the specified repository.
336 the specified repository.
337
337
338 This command takes the following options:
338 This command takes the following options:
339
339
340 :param apiuser: This is filled automatically from |authtoken|.
340 :param apiuser: This is filled automatically from |authtoken|.
341 :type apiuser: AuthUser
341 :type apiuser: AuthUser
342 :param repoid: Sets the repository name or repository ID.
342 :param repoid: Sets the repository name or repository ID.
343 :type repoid: str or int
343 :type repoid: str or int
344 :param delete_keys: This deletes the invalidated keys instead of
344 :param delete_keys: This deletes the invalidated keys instead of
345 just flagging them.
345 just flagging them.
346 :type delete_keys: Optional(``True`` | ``False``)
346 :type delete_keys: Optional(``True`` | ``False``)
347
347
348 Example output:
348 Example output:
349
349
350 .. code-block:: bash
350 .. code-block:: bash
351
351
352 id : <id_given_in_input>
352 id : <id_given_in_input>
353 result : {
353 result : {
354 'msg': Cache for repository `<repository name>` was invalidated,
354 'msg': Cache for repository `<repository name>` was invalidated,
355 'repository': <repository name>
355 'repository': <repository name>
356 }
356 }
357 error : null
357 error : null
358
358
359 Example error output:
359 Example error output:
360
360
361 .. code-block:: bash
361 .. code-block:: bash
362
362
363 id : <id_given_in_input>
363 id : <id_given_in_input>
364 result : null
364 result : null
365 error : {
365 error : {
366 'Error occurred during cache invalidation action'
366 'Error occurred during cache invalidation action'
367 }
367 }
368
368
369
369
370 lock
370 lock
371 ----
371 ----
372
372
373 .. py:function:: lock(apiuser, repoid, locked=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
373 .. py:function:: lock(apiuser, repoid, locked=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
374
374
375 Sets the lock state of the specified |repo| by the given user.
375 Sets the lock state of the specified |repo| by the given user.
376 From more information, see :ref:`repo-locking`.
376 From more information, see :ref:`repo-locking`.
377
377
378 * If the ``userid`` option is not set, the repository is locked to the
378 * If the ``userid`` option is not set, the repository is locked to the
379 user who called the method.
379 user who called the method.
380 * If the ``locked`` parameter is not set, the current lock state of the
380 * If the ``locked`` parameter is not set, the current lock state of the
381 repository is displayed.
381 repository is displayed.
382
382
383 This command can only be run using an |authtoken| with admin rights to
383 This command can only be run using an |authtoken| with admin rights to
384 the specified repository.
384 the specified repository.
385
385
386 This command takes the following options:
386 This command takes the following options:
387
387
388 :param apiuser: This is filled automatically from the |authtoken|.
388 :param apiuser: This is filled automatically from the |authtoken|.
389 :type apiuser: AuthUser
389 :type apiuser: AuthUser
390 :param repoid: Sets the repository name or repository ID.
390 :param repoid: Sets the repository name or repository ID.
391 :type repoid: str or int
391 :type repoid: str or int
392 :param locked: Sets the lock state.
392 :param locked: Sets the lock state.
393 :type locked: Optional(``True`` | ``False``)
393 :type locked: Optional(``True`` | ``False``)
394 :param userid: Set the repository lock to this user.
394 :param userid: Set the repository lock to this user.
395 :type userid: Optional(str or int)
395 :type userid: Optional(str or int)
396
396
397 Example error output:
397 Example error output:
398
398
399 .. code-block:: bash
399 .. code-block:: bash
400
400
401 id : <id_given_in_input>
401 id : <id_given_in_input>
402 result : {
402 result : {
403 'repo': '<reponame>',
403 'repo': '<reponame>',
404 'locked': <bool: lock state>,
404 'locked': <bool: lock state>,
405 'locked_since': <int: lock timestamp>,
405 'locked_since': <int: lock timestamp>,
406 'locked_by': <username of person who made the lock>,
406 'locked_by': <username of person who made the lock>,
407 'lock_reason': <str: reason for locking>,
407 'lock_reason': <str: reason for locking>,
408 'lock_state_changed': <bool: True if lock state has been changed in this request>,
408 'lock_state_changed': <bool: True if lock state has been changed in this request>,
409 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
409 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
410 or
410 or
411 'msg': 'Repo `<repository name>` not locked.'
411 'msg': 'Repo `<repository name>` not locked.'
412 or
412 or
413 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
413 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
414 }
414 }
415 error : null
415 error : null
416
416
417 Example error output:
417 Example error output:
418
418
419 .. code-block:: bash
419 .. code-block:: bash
420
420
421 id : <id_given_in_input>
421 id : <id_given_in_input>
422 result : null
422 result : null
423 error : {
423 error : {
424 'Error occurred locking repository `<reponame>`
424 'Error occurred locking repository `<reponame>`
425 }
425 }
426
426
427
427
428 get_locks
428 get_locks
429 ---------
429 ---------
430
430
431 .. py:function:: get_locks(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
431 .. py:function:: get_locks(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
432
432
433 Displays all repositories locked by the specified user.
433 Displays all repositories locked by the specified user.
434
434
435 * If this command is run by a non-admin user, it returns
435 * If this command is run by a non-admin user, it returns
436 a list of |repos| locked by that user.
436 a list of |repos| locked by that user.
437
437
438 This command takes the following options:
438 This command takes the following options:
439
439
440 :param apiuser: This is filled automatically from the |authtoken|.
440 :param apiuser: This is filled automatically from the |authtoken|.
441 :type apiuser: AuthUser
441 :type apiuser: AuthUser
442 :param userid: Sets the userid whose list of locked |repos| will be
442 :param userid: Sets the userid whose list of locked |repos| will be
443 displayed.
443 displayed.
444 :type userid: Optional(str or int)
444 :type userid: Optional(str or int)
445
445
446 Example output:
446 Example output:
447
447
448 .. code-block:: bash
448 .. code-block:: bash
449
449
450 id : <id_given_in_input>
450 id : <id_given_in_input>
451 result : {
451 result : {
452 [repo_object, repo_object,...]
452 [repo_object, repo_object,...]
453 }
453 }
454 error : null
454 error : null
455
455
456
456
457 get_ip
457 get_ip
458 ------
458 ------
459
459
460 .. py:function:: get_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
460 .. py:function:: get_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
461
461
462 Displays the IP Address as seen from the |RCE| server.
462 Displays the IP Address as seen from the |RCE| server.
463
463
464 * This command displays the IP Address, as well as all the defined IP
464 * This command displays the IP Address, as well as all the defined IP
465 addresses for the specified user. If the ``userid`` is not set, the
465 addresses for the specified user. If the ``userid`` is not set, the
466 data returned is for the user calling the method.
466 data returned is for the user calling the method.
467
467
468 This command can only be run using an |authtoken| with admin rights to
468 This command can only be run using an |authtoken| with admin rights to
469 the specified repository.
469 the specified repository.
470
470
471 This command takes the following options:
471 This command takes the following options:
472
472
473 :param apiuser: This is filled automatically from |authtoken|.
473 :param apiuser: This is filled automatically from |authtoken|.
474 :type apiuser: AuthUser
474 :type apiuser: AuthUser
475 :param userid: Sets the userid for which associated IP Address data
475 :param userid: Sets the userid for which associated IP Address data
476 is returned.
476 is returned.
477 :type userid: Optional(str or int)
477 :type userid: Optional(str or int)
478
478
479 Example output:
479 Example output:
480
480
481 .. code-block:: bash
481 .. code-block:: bash
482
482
483 id : <id_given_in_input>
483 id : <id_given_in_input>
484 result : {
484 result : {
485 "server_ip_addr": "<ip_from_clien>",
485 "server_ip_addr": "<ip_from_clien>",
486 "user_ips": [
486 "user_ips": [
487 {
487 {
488 "ip_addr": "<ip_with_mask>",
488 "ip_addr": "<ip_with_mask>",
489 "ip_range": ["<start_ip>", "<end_ip>"],
489 "ip_range": ["<start_ip>", "<end_ip>"],
490 },
490 },
491 ...
491 ...
492 ]
492 ]
493 }
493 }
494
494
495
495
496 show_ip
496 show_ip
497 -------
497 -------
498
498
499 .. py:function:: show_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
499 .. py:function:: show_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
500
500
501 Displays the IP Address as seen from the |RCE| server.
501 Displays the IP Address as seen from the |RCE| server.
502
502
503 * This command displays the IP Address, as well as all the defined IP
503 * This command displays the IP Address, as well as all the defined IP
504 addresses for the specified user. If the ``userid`` is not set, the
504 addresses for the specified user. If the ``userid`` is not set, the
505 data returned is for the user calling the method.
505 data returned is for the user calling the method.
506
506
507 This command can only be run using an |authtoken| with admin rights to
507 This command can only be run using an |authtoken| with admin rights to
508 the specified repository.
508 the specified repository.
509
509
510 This command takes the following options:
510 This command takes the following options:
511
511
512 :param apiuser: This is filled automatically from |authtoken|.
512 :param apiuser: This is filled automatically from |authtoken|.
513 :type apiuser: AuthUser
513 :type apiuser: AuthUser
514 :param userid: Sets the userid for which associated IP Address data
514 :param userid: Sets the userid for which associated IP Address data
515 is returned.
515 is returned.
516 :type userid: Optional(str or int)
516 :type userid: Optional(str or int)
517
517
518 Example output:
518 Example output:
519
519
520 .. code-block:: bash
520 .. code-block:: bash
521
521
522 id : <id_given_in_input>
522 id : <id_given_in_input>
523 result : {
523 result : {
524 "server_ip_addr": "<ip_from_clien>",
524 "server_ip_addr": "<ip_from_clien>",
525 "user_ips": [
525 "user_ips": [
526 {
526 {
527 "ip_addr": "<ip_with_mask>",
527 "ip_addr": "<ip_with_mask>",
528 "ip_range": ["<start_ip>", "<end_ip>"],
528 "ip_range": ["<start_ip>", "<end_ip>"],
529 },
529 },
530 ...
530 ...
531 ]
531 ]
532 }
532 }
533
533
534
534
535 get_license_info
535 get_license_info
536 ----------------
536 ----------------
537
537
538 .. py:function:: get_license_info(apiuser)
538 .. py:function:: get_license_info(apiuser)
539
539
540 Returns the |RCE| license information.
540 Returns the |RCE| license information.
541
541
542 :param apiuser: This is filled automatically from the |authtoken|.
542 :param apiuser: This is filled automatically from the |authtoken|.
543 :type apiuser: AuthUser
543 :type apiuser: AuthUser
544
544
545 Example output:
545 Example output:
546
546
547 .. code-block:: bash
547 .. code-block:: bash
548
548
549 id : <id_given_in_input>
549 id : <id_given_in_input>
550 result : {
550 result : {
551 'rhodecode_version': <rhodecode version>,
551 'rhodecode_version': <rhodecode version>,
552 'token': <license token>,
552 'token': <license token>,
553 'issued_to': <license owner>,
553 'issued_to': <license owner>,
554 'issued_on': <license issue date>,
554 'issued_on': <license issue date>,
555 'expires_on': <license expiration date>,
555 'expires_on': <license expiration date>,
556 'type': <license type>,
556 'type': <license type>,
557 'users_limit': <license users limit>,
557 'users_limit': <license users limit>,
558 'key': <license key>
558 'key': <license key>
559 }
559 }
560 error : null
560 error : null
561
561
562
562
563 set_license_key
563 set_license_key
564 ---------------
564 ---------------
565
565
566 .. py:function:: set_license_key(apiuser, key)
566 .. py:function:: set_license_key(apiuser, key)
567
567
568 Sets the |RCE| license key.
568 Sets the |RCE| license key.
569
569
570 :param apiuser: This is filled automatically from the |authtoken|.
570 :param apiuser: This is filled automatically from the |authtoken|.
571 :type apiuser: AuthUser
571 :type apiuser: AuthUser
572 :param key: This is the license key to be set.
572 :param key: This is the license key to be set.
573 :type key: str
573 :type key: str
574
574
575 Example output:
575 Example output:
576
576
577 .. code-block:: bash
577 .. code-block:: bash
578
578
579 id : <id_given_in_input>
579 id : <id_given_in_input>
580 result: {
580 result: {
581 "msg" : "updated license information",
581 "msg" : "updated license information",
582 "key": <key>
582 "key": <key>
583 }
583 }
584 error: null
584 error: null
585
585
586 Example error output:
586 Example error output:
587
587
588 .. code-block:: bash
588 .. code-block:: bash
589
589
590 id : <id_given_in_input>
590 id : <id_given_in_input>
591 result : null
591 result : null
592 error : {
592 error : {
593 "license key is not valid"
593 "license key is not valid"
594 or
594 or
595 "trial licenses cannot be uploaded"
595 "trial licenses cannot be uploaded"
596 or
596 or
597 "error occurred while updating license"
597 "error occurred while updating license"
598 }
598 }
599
599
600
600
601 get_server_info
601 get_server_info
602 ---------------
602 ---------------
603
603
604 .. py:function:: get_server_info(apiuser)
604 .. py:function:: get_server_info(apiuser)
605
605
606 Returns the |RCE| server information.
606 Returns the |RCE| server information.
607
607
608 This includes the running version of |RCE| and all installed
608 This includes the running version of |RCE| and all installed
609 packages. This command takes the following options:
609 packages. This command takes the following options:
610
610
611 :param apiuser: This is filled automatically from the |authtoken|.
611 :param apiuser: This is filled automatically from the |authtoken|.
612 :type apiuser: AuthUser
612 :type apiuser: AuthUser
613
613
614 Example output:
614 Example output:
615
615
616 .. code-block:: bash
616 .. code-block:: bash
617
617
618 id : <id_given_in_input>
618 id : <id_given_in_input>
619 result : {
619 result : {
620 'modules': [<module name>,...]
620 'modules': [<module name>,...]
621 'py_version': <python version>,
621 'py_version': <python version>,
622 'platform': <platform type>,
622 'platform': <platform type>,
623 'rhodecode_version': <rhodecode version>
623 'rhodecode_version': <rhodecode version>
624 }
624 }
625 error : null
625 error : null
626
626
627
627
628 get_user
628 get_user
629 --------
629 --------
630
630
631 .. py:function:: get_user(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
631 .. py:function:: get_user(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
632
632
633 Returns the information associated with a username or userid.
633 Returns the information associated with a username or userid.
634
634
635 * If the ``userid`` is not set, this command returns the information
635 * If the ``userid`` is not set, this command returns the information
636 for the ``userid`` calling the method.
636 for the ``userid`` calling the method.
637
637
638 .. note::
638 .. note::
639
639
640 Normal users may only run this command against their ``userid``. For
640 Normal users may only run this command against their ``userid``. For
641 full privileges you must run this command using an |authtoken| with
641 full privileges you must run this command using an |authtoken| with
642 admin rights.
642 admin rights.
643
643
644 This command takes the following options:
644 This command takes the following options:
645
645
646 :param apiuser: This is filled automatically from the |authtoken|.
646 :param apiuser: This is filled automatically from the |authtoken|.
647 :type apiuser: AuthUser
647 :type apiuser: AuthUser
648 :param userid: Sets the userid for which data will be returned.
648 :param userid: Sets the userid for which data will be returned.
649 :type userid: Optional(str or int)
649 :type userid: Optional(str or int)
650
650
651 Example output:
651 Example output:
652
652
653 .. code-block:: bash
653 .. code-block:: bash
654
654
655 {
655 {
656 "error": null,
656 "error": null,
657 "id": <id>,
657 "id": <id>,
658 "result": {
658 "result": {
659 "active": true,
659 "active": true,
660 "admin": false,
660 "admin": false,
661 "api_key": "api-key",
661 "api_key": "api-key",
662 "api_keys": [ list of keys ],
662 "api_keys": [ list of keys ],
663 "email": "user@example.com",
663 "email": "user@example.com",
664 "emails": [
664 "emails": [
665 "user@example.com"
665 "user@example.com"
666 ],
666 ],
667 "extern_name": "rhodecode",
667 "extern_name": "rhodecode",
668 "extern_type": "rhodecode",
668 "extern_type": "rhodecode",
669 "firstname": "username",
669 "firstname": "username",
670 "ip_addresses": [],
670 "ip_addresses": [],
671 "language": null,
671 "language": null,
672 "last_login": "Timestamp",
672 "last_login": "Timestamp",
673 "lastname": "surnae",
673 "lastname": "surnae",
674 "permissions": {
674 "permissions": {
675 "global": [
675 "global": [
676 "hg.inherit_default_perms.true",
676 "hg.inherit_default_perms.true",
677 "usergroup.read",
677 "usergroup.read",
678 "hg.repogroup.create.false",
678 "hg.repogroup.create.false",
679 "hg.create.none",
679 "hg.create.none",
680 "hg.extern_activate.manual",
680 "hg.extern_activate.manual",
681 "hg.create.write_on_repogroup.false",
681 "hg.create.write_on_repogroup.false",
682 "hg.usergroup.create.false",
682 "hg.usergroup.create.false",
683 "group.none",
683 "group.none",
684 "repository.none",
684 "repository.none",
685 "hg.register.none",
685 "hg.register.none",
686 "hg.fork.repository"
686 "hg.fork.repository"
687 ],
687 ],
688 "repositories": { "username/example": "repository.write"},
688 "repositories": { "username/example": "repository.write"},
689 "repositories_groups": { "user-group/repo": "group.none" },
689 "repositories_groups": { "user-group/repo": "group.none" },
690 "user_groups": { "user_group_name": "usergroup.read" }
690 "user_groups": { "user_group_name": "usergroup.read" }
691 },
691 },
692 "user_id": 32,
692 "user_id": 32,
693 "username": "username"
693 "username": "username"
694 }
694 }
695 }
695 }
696
696
697
697
698 get_users
698 get_users
699 ---------
699 ---------
700
700
701 .. py:function:: get_users(apiuser)
701 .. py:function:: get_users(apiuser)
702
702
703 Lists all users in the |RCE| user database.
703 Lists all users in the |RCE| user database.
704
704
705 This command can only be run using an |authtoken| with admin rights to
705 This command can only be run using an |authtoken| with admin rights to
706 the specified repository.
706 the specified repository.
707
707
708 This command takes the following options:
708 This command takes the following options:
709
709
710 :param apiuser: This is filled automatically from the |authtoken|.
710 :param apiuser: This is filled automatically from the |authtoken|.
711 :type apiuser: AuthUser
711 :type apiuser: AuthUser
712
712
713 Example output:
713 Example output:
714
714
715 .. code-block:: bash
715 .. code-block:: bash
716
716
717 id : <id_given_in_input>
717 id : <id_given_in_input>
718 result: [<user_object>, ...]
718 result: [<user_object>, ...]
719 error: null
719 error: null
720
720
721
721
722 create_user
722 create_user
723 -----------
723 -----------
724
724
725 .. py:function:: create_user(apiuser, username, email, password=<Optional:''>, firstname=<Optional:''>, lastname=<Optional:''>, active=<Optional:True>, admin=<Optional:False>, extern_name=<Optional:'rhodecode'>, extern_type=<Optional:'rhodecode'>, force_password_change=<Optional:False>)
725 .. py:function:: create_user(apiuser, username, email, password=<Optional:''>, firstname=<Optional:''>, lastname=<Optional:''>, active=<Optional:True>, admin=<Optional:False>, extern_name=<Optional:'rhodecode'>, extern_type=<Optional:'rhodecode'>, force_password_change=<Optional:False>)
726
726
727 Creates a new user and returns the new user object.
727 Creates a new user and returns the new user object.
728
728
729 This command can only be run using an |authtoken| with admin rights to
729 This command can only be run using an |authtoken| with admin rights to
730 the specified repository.
730 the specified repository.
731
731
732 This command takes the following options:
732 This command takes the following options:
733
733
734 :param apiuser: This is filled automatically from the |authtoken|.
734 :param apiuser: This is filled automatically from the |authtoken|.
735 :type apiuser: AuthUser
735 :type apiuser: AuthUser
736 :param username: Set the new username.
736 :param username: Set the new username.
737 :type username: str or int
737 :type username: str or int
738 :param email: Set the user email address.
738 :param email: Set the user email address.
739 :type email: str
739 :type email: str
740 :param password: Set the new user password.
740 :param password: Set the new user password.
741 :type password: Optional(str)
741 :type password: Optional(str)
742 :param firstname: Set the new user firstname.
742 :param firstname: Set the new user firstname.
743 :type firstname: Optional(str)
743 :type firstname: Optional(str)
744 :param lastname: Set the new user surname.
744 :param lastname: Set the new user surname.
745 :type lastname: Optional(str)
745 :type lastname: Optional(str)
746 :param active: Set the user as active.
746 :param active: Set the user as active.
747 :type active: Optional(``True`` | ``False``)
747 :type active: Optional(``True`` | ``False``)
748 :param admin: Give the new user admin rights.
748 :param admin: Give the new user admin rights.
749 :type admin: Optional(``True`` | ``False``)
749 :type admin: Optional(``True`` | ``False``)
750 :param extern_name: Set the authentication plugin name.
750 :param extern_name: Set the authentication plugin name.
751 Using LDAP this is filled with LDAP UID.
751 Using LDAP this is filled with LDAP UID.
752 :type extern_name: Optional(str)
752 :type extern_name: Optional(str)
753 :param extern_type: Set the new user authentication plugin.
753 :param extern_type: Set the new user authentication plugin.
754 :type extern_type: Optional(str)
754 :type extern_type: Optional(str)
755 :param force_password_change: Force the new user to change password
755 :param force_password_change: Force the new user to change password
756 on next login.
756 on next login.
757 :type force_password_change: Optional(``True`` | ``False``)
757 :type force_password_change: Optional(``True`` | ``False``)
758
758
759 Example output:
759 Example output:
760
760
761 .. code-block:: bash
761 .. code-block:: bash
762
762
763 id : <id_given_in_input>
763 id : <id_given_in_input>
764 result: {
764 result: {
765 "msg" : "created new user `<username>`",
765 "msg" : "created new user `<username>`",
766 "user": <user_obj>
766 "user": <user_obj>
767 }
767 }
768 error: null
768 error: null
769
769
770 Example error output:
770 Example error output:
771
771
772 .. code-block:: bash
772 .. code-block:: bash
773
773
774 id : <id_given_in_input>
774 id : <id_given_in_input>
775 result : null
775 result : null
776 error : {
776 error : {
777 "user `<username>` already exist"
777 "user `<username>` already exist"
778 or
778 or
779 "email `<email>` already exist"
779 "email `<email>` already exist"
780 or
780 or
781 "failed to create user `<username>`"
781 "failed to create user `<username>`"
782 }
782 }
783
783
784
784
785 update_user
785 update_user
786 -----------
786 -----------
787
787
788 .. py:function:: update_user(apiuser, userid, username=<Optional:None>, email=<Optional:None>, password=<Optional:None>, firstname=<Optional:None>, lastname=<Optional:None>, active=<Optional:None>, admin=<Optional:None>, extern_type=<Optional:None>, extern_name=<Optional:None>)
788 .. py:function:: update_user(apiuser, userid, username=<Optional:None>, email=<Optional:None>, password=<Optional:None>, firstname=<Optional:None>, lastname=<Optional:None>, active=<Optional:None>, admin=<Optional:None>, extern_type=<Optional:None>, extern_name=<Optional:None>)
789
789
790 Updates the details for the specified user, if that user exists.
790 Updates the details for the specified user, if that user exists.
791
791
792 This command can only be run using an |authtoken| with admin rights to
792 This command can only be run using an |authtoken| with admin rights to
793 the specified repository.
793 the specified repository.
794
794
795 This command takes the following options:
795 This command takes the following options:
796
796
797 :param apiuser: This is filled automatically from |authtoken|.
797 :param apiuser: This is filled automatically from |authtoken|.
798 :type apiuser: AuthUser
798 :type apiuser: AuthUser
799 :param userid: Set the ``userid`` to update.
799 :param userid: Set the ``userid`` to update.
800 :type userid: str or int
800 :type userid: str or int
801 :param username: Set the new username.
801 :param username: Set the new username.
802 :type username: str or int
802 :type username: str or int
803 :param email: Set the new email.
803 :param email: Set the new email.
804 :type email: str
804 :type email: str
805 :param password: Set the new password.
805 :param password: Set the new password.
806 :type password: Optional(str)
806 :type password: Optional(str)
807 :param firstname: Set the new first name.
807 :param firstname: Set the new first name.
808 :type firstname: Optional(str)
808 :type firstname: Optional(str)
809 :param lastname: Set the new surname.
809 :param lastname: Set the new surname.
810 :type lastname: Optional(str)
810 :type lastname: Optional(str)
811 :param active: Set the new user as active.
811 :param active: Set the new user as active.
812 :type active: Optional(``True`` | ``False``)
812 :type active: Optional(``True`` | ``False``)
813 :param admin: Give the user admin rights.
813 :param admin: Give the user admin rights.
814 :type admin: Optional(``True`` | ``False``)
814 :type admin: Optional(``True`` | ``False``)
815 :param extern_name: Set the authentication plugin user name.
815 :param extern_name: Set the authentication plugin user name.
816 Using LDAP this is filled with LDAP UID.
816 Using LDAP this is filled with LDAP UID.
817 :type extern_name: Optional(str)
817 :type extern_name: Optional(str)
818 :param extern_type: Set the authentication plugin type.
818 :param extern_type: Set the authentication plugin type.
819 :type extern_type: Optional(str)
819 :type extern_type: Optional(str)
820
820
821
821
822 Example output:
822 Example output:
823
823
824 .. code-block:: bash
824 .. code-block:: bash
825
825
826 id : <id_given_in_input>
826 id : <id_given_in_input>
827 result: {
827 result: {
828 "msg" : "updated user ID:<userid> <username>",
828 "msg" : "updated user ID:<userid> <username>",
829 "user": <user_object>,
829 "user": <user_object>,
830 }
830 }
831 error: null
831 error: null
832
832
833 Example error output:
833 Example error output:
834
834
835 .. code-block:: bash
835 .. code-block:: bash
836
836
837 id : <id_given_in_input>
837 id : <id_given_in_input>
838 result : null
838 result : null
839 error : {
839 error : {
840 "failed to update user `<username>`"
840 "failed to update user `<username>`"
841 }
841 }
842
842
843
843
844 delete_user
844 delete_user
845 -----------
845 -----------
846
846
847 .. py:function:: delete_user(apiuser, userid)
847 .. py:function:: delete_user(apiuser, userid)
848
848
849 Deletes the specified user from the |RCE| user database.
849 Deletes the specified user from the |RCE| user database.
850
850
851 This command can only be run using an |authtoken| with admin rights to
851 This command can only be run using an |authtoken| with admin rights to
852 the specified repository.
852 the specified repository.
853
853
854 .. important::
854 .. important::
855
855
856 Ensure all open pull requests and open code review
856 Ensure all open pull requests and open code review
857 requests to this user are close.
857 requests to this user are close.
858
858
859 Also ensure all repositories, or repository groups owned by this
859 Also ensure all repositories, or repository groups owned by this
860 user are reassigned before deletion.
860 user are reassigned before deletion.
861
861
862 This command takes the following options:
862 This command takes the following options:
863
863
864 :param apiuser: This is filled automatically from the |authtoken|.
864 :param apiuser: This is filled automatically from the |authtoken|.
865 :type apiuser: AuthUser
865 :type apiuser: AuthUser
866 :param userid: Set the user to delete.
866 :param userid: Set the user to delete.
867 :type userid: str or int
867 :type userid: str or int
868
868
869 Example output:
869 Example output:
870
870
871 .. code-block:: bash
871 .. code-block:: bash
872
872
873 id : <id_given_in_input>
873 id : <id_given_in_input>
874 result: {
874 result: {
875 "msg" : "deleted user ID:<userid> <username>",
875 "msg" : "deleted user ID:<userid> <username>",
876 "user": null
876 "user": null
877 }
877 }
878 error: null
878 error: null
879
879
880 Example error output:
880 Example error output:
881
881
882 .. code-block:: bash
882 .. code-block:: bash
883
883
884 id : <id_given_in_input>
884 id : <id_given_in_input>
885 result : null
885 result : null
886 error : {
886 error : {
887 "failed to delete user ID:<userid> <username>"
887 "failed to delete user ID:<userid> <username>"
888 }
888 }
889
889
890
890
891 get_user_group
891 get_user_group
892 --------------
892 --------------
893
893
894 .. py:function:: get_user_group(apiuser, usergroupid)
894 .. py:function:: get_user_group(apiuser, usergroupid)
895
895
896 Returns the data of an existing user group.
896 Returns the data of an existing user group.
897
897
898 This command can only be run using an |authtoken| with admin rights to
898 This command can only be run using an |authtoken| with admin rights to
899 the specified repository.
899 the specified repository.
900
900
901 :param apiuser: This is filled automatically from the |authtoken|.
901 :param apiuser: This is filled automatically from the |authtoken|.
902 :type apiuser: AuthUser
902 :type apiuser: AuthUser
903 :param usergroupid: Set the user group from which to return data.
903 :param usergroupid: Set the user group from which to return data.
904 :type usergroupid: str or int
904 :type usergroupid: str or int
905
905
906 Example error output:
906 Example error output:
907
907
908 .. code-block:: bash
908 .. code-block:: bash
909
909
910 {
910 {
911 "error": null,
911 "error": null,
912 "id": <id>,
912 "id": <id>,
913 "result": {
913 "result": {
914 "active": true,
914 "active": true,
915 "group_description": "group description",
915 "group_description": "group description",
916 "group_name": "group name",
916 "group_name": "group name",
917 "members": [
917 "members": [
918 {
918 {
919 "name": "owner-name",
919 "name": "owner-name",
920 "origin": "owner",
920 "origin": "owner",
921 "permission": "usergroup.admin",
921 "permission": "usergroup.admin",
922 "type": "user"
922 "type": "user"
923 },
923 },
924 {
924 {
925 {
925 {
926 "name": "user name",
926 "name": "user name",
927 "origin": "permission",
927 "origin": "permission",
928 "permission": "usergroup.admin",
928 "permission": "usergroup.admin",
929 "type": "user"
929 "type": "user"
930 },
930 },
931 {
931 {
932 "name": "user group name",
932 "name": "user group name",
933 "origin": "permission",
933 "origin": "permission",
934 "permission": "usergroup.write",
934 "permission": "usergroup.write",
935 "type": "user_group"
935 "type": "user_group"
936 }
936 }
937 ],
937 ],
938 "owner": "owner name",
938 "owner": "owner name",
939 "users": [],
939 "users": [],
940 "users_group_id": 2
940 "users_group_id": 2
941 }
941 }
942 }
942 }
943
943
944
944
945 get_user_groups
945 get_user_groups
946 ---------------
946 ---------------
947
947
948 .. py:function:: get_user_groups(apiuser)
948 .. py:function:: get_user_groups(apiuser)
949
949
950 Lists all the existing user groups within RhodeCode.
950 Lists all the existing user groups within RhodeCode.
951
951
952 This command can only be run using an |authtoken| with admin rights to
952 This command can only be run using an |authtoken| with admin rights to
953 the specified repository.
953 the specified repository.
954
954
955 This command takes the following options:
955 This command takes the following options:
956
956
957 :param apiuser: This is filled automatically from the |authtoken|.
957 :param apiuser: This is filled automatically from the |authtoken|.
958 :type apiuser: AuthUser
958 :type apiuser: AuthUser
959
959
960 Example error output:
960 Example error output:
961
961
962 .. code-block:: bash
962 .. code-block:: bash
963
963
964 id : <id_given_in_input>
964 id : <id_given_in_input>
965 result : [<user_group_obj>,...]
965 result : [<user_group_obj>,...]
966 error : null
966 error : null
967
967
968
968
969 create_user_group
969 create_user_group
970 -----------------
970 -----------------
971
971
972 .. py:function:: create_user_group(apiuser, group_name, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, active=<Optional:True>)
972 .. py:function:: create_user_group(apiuser, group_name, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, active=<Optional:True>)
973
973
974 Creates a new user group.
974 Creates a new user group.
975
975
976 This command can only be run using an |authtoken| with admin rights to
976 This command can only be run using an |authtoken| with admin rights to
977 the specified repository.
977 the specified repository.
978
978
979 This command takes the following options:
979 This command takes the following options:
980
980
981 :param apiuser: This is filled automatically from the |authtoken|.
981 :param apiuser: This is filled automatically from the |authtoken|.
982 :type apiuser: AuthUser
982 :type apiuser: AuthUser
983 :param group_name: Set the name of the new user group.
983 :param group_name: Set the name of the new user group.
984 :type group_name: str
984 :type group_name: str
985 :param description: Give a description of the new user group.
985 :param description: Give a description of the new user group.
986 :type description: str
986 :type description: str
987 :param owner: Set the owner of the new user group.
987 :param owner: Set the owner of the new user group.
988 If not set, the owner is the |authtoken| user.
988 If not set, the owner is the |authtoken| user.
989 :type owner: Optional(str or int)
989 :type owner: Optional(str or int)
990 :param active: Set this group as active.
990 :param active: Set this group as active.
991 :type active: Optional(``True`` | ``False``)
991 :type active: Optional(``True`` | ``False``)
992
992
993 Example output:
993 Example output:
994
994
995 .. code-block:: bash
995 .. code-block:: bash
996
996
997 id : <id_given_in_input>
997 id : <id_given_in_input>
998 result: {
998 result: {
999 "msg": "created new user group `<groupname>`",
999 "msg": "created new user group `<groupname>`",
1000 "user_group": <user_group_object>
1000 "user_group": <user_group_object>
1001 }
1001 }
1002 error: null
1002 error: null
1003
1003
1004 Example error output:
1004 Example error output:
1005
1005
1006 .. code-block:: bash
1006 .. code-block:: bash
1007
1007
1008 id : <id_given_in_input>
1008 id : <id_given_in_input>
1009 result : null
1009 result : null
1010 error : {
1010 error : {
1011 "user group `<group name>` already exist"
1011 "user group `<group name>` already exist"
1012 or
1012 or
1013 "failed to create group `<group name>`"
1013 "failed to create group `<group name>`"
1014 }
1014 }
1015
1015
1016
1016
1017 update_user_group
1017 update_user_group
1018 -----------------
1018 -----------------
1019
1019
1020 .. py:function:: update_user_group(apiuser, usergroupid, group_name=<Optional:''>, description=<Optional:''>, owner=<Optional:None>, active=<Optional:True>)
1020 .. py:function:: update_user_group(apiuser, usergroupid, group_name=<Optional:''>, description=<Optional:''>, owner=<Optional:None>, active=<Optional:True>)
1021
1021
1022 Updates the specified `user group` with the details provided.
1022 Updates the specified `user group` with the details provided.
1023
1023
1024 This command can only be run using an |authtoken| with admin rights to
1024 This command can only be run using an |authtoken| with admin rights to
1025 the specified repository.
1025 the specified repository.
1026
1026
1027 :param apiuser: This is filled automatically from the |authtoken|.
1027 :param apiuser: This is filled automatically from the |authtoken|.
1028 :type apiuser: AuthUser
1028 :type apiuser: AuthUser
1029 :param usergroupid: Set the id of the `user group` to update.
1029 :param usergroupid: Set the id of the `user group` to update.
1030 :type usergroupid: str or int
1030 :type usergroupid: str or int
1031 :param group_name: Set the new name the `user group`
1031 :param group_name: Set the new name the `user group`
1032 :type group_name: str
1032 :type group_name: str
1033 :param description: Give a description for the `user group`
1033 :param description: Give a description for the `user group`
1034 :type description: str
1034 :type description: str
1035 :param owner: Set the owner of the `user group`.
1035 :param owner: Set the owner of the `user group`.
1036 :type owner: Optional(str or int)
1036 :type owner: Optional(str or int)
1037 :param active: Set the group as active.
1037 :param active: Set the group as active.
1038 :type active: Optional(``True`` | ``False``)
1038 :type active: Optional(``True`` | ``False``)
1039
1039
1040 Example output:
1040 Example output:
1041
1041
1042 .. code-block:: bash
1042 .. code-block:: bash
1043
1043
1044 id : <id_given_in_input>
1044 id : <id_given_in_input>
1045 result : {
1045 result : {
1046 "msg": 'updated user group ID:<user group id> <user group name>',
1046 "msg": 'updated user group ID:<user group id> <user group name>',
1047 "user_group": <user_group_object>
1047 "user_group": <user_group_object>
1048 }
1048 }
1049 error : null
1049 error : null
1050
1050
1051 Example error output:
1051 Example error output:
1052
1052
1053 .. code-block:: bash
1053 .. code-block:: bash
1054
1054
1055 id : <id_given_in_input>
1055 id : <id_given_in_input>
1056 result : null
1056 result : null
1057 error : {
1057 error : {
1058 "failed to update user group `<user group name>`"
1058 "failed to update user group `<user group name>`"
1059 }
1059 }
1060
1060
1061
1061
1062 delete_user_group
1062 delete_user_group
1063 -----------------
1063 -----------------
1064
1064
1065 .. py:function:: delete_user_group(apiuser, usergroupid)
1065 .. py:function:: delete_user_group(apiuser, usergroupid)
1066
1066
1067 Deletes the specified `user group`.
1067 Deletes the specified `user group`.
1068
1068
1069 This command can only be run using an |authtoken| with admin rights to
1069 This command can only be run using an |authtoken| with admin rights to
1070 the specified repository.
1070 the specified repository.
1071
1071
1072 This command takes the following options:
1072 This command takes the following options:
1073
1073
1074 :param apiuser: filled automatically from apikey
1074 :param apiuser: filled automatically from apikey
1075 :type apiuser: AuthUser
1075 :type apiuser: AuthUser
1076 :param usergroupid:
1076 :param usergroupid:
1077 :type usergroupid: int
1077 :type usergroupid: int
1078
1078
1079 Example output:
1079 Example output:
1080
1080
1081 .. code-block:: bash
1081 .. code-block:: bash
1082
1082
1083 id : <id_given_in_input>
1083 id : <id_given_in_input>
1084 result : {
1084 result : {
1085 "msg": "deleted user group ID:<user_group_id> <user_group_name>"
1085 "msg": "deleted user group ID:<user_group_id> <user_group_name>"
1086 }
1086 }
1087 error : null
1087 error : null
1088
1088
1089 Example error output:
1089 Example error output:
1090
1090
1091 .. code-block:: bash
1091 .. code-block:: bash
1092
1092
1093 id : <id_given_in_input>
1093 id : <id_given_in_input>
1094 result : null
1094 result : null
1095 error : {
1095 error : {
1096 "failed to delete user group ID:<user_group_id> <user_group_name>"
1096 "failed to delete user group ID:<user_group_id> <user_group_name>"
1097 or
1097 or
1098 "RepoGroup assigned to <repo_groups_list>"
1098 "RepoGroup assigned to <repo_groups_list>"
1099 }
1099 }
1100
1100
1101
1101
1102 add_user_to_user_group
1102 add_user_to_user_group
1103 ----------------------
1103 ----------------------
1104
1104
1105 .. py:function:: add_user_to_user_group(apiuser, usergroupid, userid)
1105 .. py:function:: add_user_to_user_group(apiuser, usergroupid, userid)
1106
1106
1107 Adds a user to a `user group`. If the user already exists in the group
1107 Adds a user to a `user group`. If the user already exists in the group
1108 this command will return false.
1108 this command will return false.
1109
1109
1110 This command can only be run using an |authtoken| with admin rights to
1110 This command can only be run using an |authtoken| with admin rights to
1111 the specified user group.
1111 the specified user group.
1112
1112
1113 This command takes the following options:
1113 This command takes the following options:
1114
1114
1115 :param apiuser: This is filled automatically from the |authtoken|.
1115 :param apiuser: This is filled automatically from the |authtoken|.
1116 :type apiuser: AuthUser
1116 :type apiuser: AuthUser
1117 :param usergroupid: Set the name of the `user group` to which a
1117 :param usergroupid: Set the name of the `user group` to which a
1118 user will be added.
1118 user will be added.
1119 :type usergroupid: int
1119 :type usergroupid: int
1120 :param userid: Set the `user_id` of the user to add to the group.
1120 :param userid: Set the `user_id` of the user to add to the group.
1121 :type userid: int
1121 :type userid: int
1122
1122
1123 Example output:
1123 Example output:
1124
1124
1125 .. code-block:: bash
1125 .. code-block:: bash
1126
1126
1127 id : <id_given_in_input>
1127 id : <id_given_in_input>
1128 result : {
1128 result : {
1129 "success": True|False # depends on if member is in group
1129 "success": True|False # depends on if member is in group
1130 "msg": "added member `<username>` to user group `<groupname>` |
1130 "msg": "added member `<username>` to user group `<groupname>` |
1131 User is already in that group"
1131 User is already in that group"
1132
1132
1133 }
1133 }
1134 error : null
1134 error : null
1135
1135
1136 Example error output:
1136 Example error output:
1137
1137
1138 .. code-block:: bash
1138 .. code-block:: bash
1139
1139
1140 id : <id_given_in_input>
1140 id : <id_given_in_input>
1141 result : null
1141 result : null
1142 error : {
1142 error : {
1143 "failed to add member to user group `<user_group_name>`"
1143 "failed to add member to user group `<user_group_name>`"
1144 }
1144 }
1145
1145
1146
1146
1147 remove_user_from_user_group
1147 remove_user_from_user_group
1148 ---------------------------
1148 ---------------------------
1149
1149
1150 .. py:function:: remove_user_from_user_group(apiuser, usergroupid, userid)
1150 .. py:function:: remove_user_from_user_group(apiuser, usergroupid, userid)
1151
1151
1152 Removes a user from a user group.
1152 Removes a user from a user group.
1153
1153
1154 * If the specified user is not in the group, this command will return
1154 * If the specified user is not in the group, this command will return
1155 `false`.
1155 `false`.
1156
1156
1157 This command can only be run using an |authtoken| with admin rights to
1157 This command can only be run using an |authtoken| with admin rights to
1158 the specified user group.
1158 the specified user group.
1159
1159
1160 :param apiuser: This is filled automatically from the |authtoken|.
1160 :param apiuser: This is filled automatically from the |authtoken|.
1161 :type apiuser: AuthUser
1161 :type apiuser: AuthUser
1162 :param usergroupid: Sets the user group name.
1162 :param usergroupid: Sets the user group name.
1163 :type usergroupid: str or int
1163 :type usergroupid: str or int
1164 :param userid: The user you wish to remove from |RCE|.
1164 :param userid: The user you wish to remove from |RCE|.
1165 :type userid: str or int
1165 :type userid: str or int
1166
1166
1167 Example output:
1167 Example output:
1168
1168
1169 .. code-block:: bash
1169 .. code-block:: bash
1170
1170
1171 id : <id_given_in_input>
1171 id : <id_given_in_input>
1172 result: {
1172 result: {
1173 "success": True|False, # depends on if member is in group
1173 "success": True|False, # depends on if member is in group
1174 "msg": "removed member <username> from user group <groupname> |
1174 "msg": "removed member <username> from user group <groupname> |
1175 User wasn't in group"
1175 User wasn't in group"
1176 }
1176 }
1177 error: null
1177 error: null
1178
1178
1179
1179
1180 grant_user_permission_to_user_group
1180 grant_user_permission_to_user_group
1181 -----------------------------------
1181 -----------------------------------
1182
1182
1183 .. py:function:: grant_user_permission_to_user_group(apiuser, usergroupid, userid, perm)
1183 .. py:function:: grant_user_permission_to_user_group(apiuser, usergroupid, userid, perm)
1184
1184
1185 Set permissions for a user in a user group.
1185 Set permissions for a user in a user group.
1186
1186
1187 :param apiuser: This is filled automatically from the |authtoken|.
1187 :param apiuser: This is filled automatically from the |authtoken|.
1188 :type apiuser: AuthUser
1188 :type apiuser: AuthUser
1189 :param usergroupid: Set the user group to edit permissions on.
1189 :param usergroupid: Set the user group to edit permissions on.
1190 :type usergroupid: str or int
1190 :type usergroupid: str or int
1191 :param userid: Set the user from whom you wish to set permissions.
1191 :param userid: Set the user from whom you wish to set permissions.
1192 :type userid: str
1192 :type userid: str
1193 :param perm: (usergroup.(none|read|write|admin))
1193 :param perm: (usergroup.(none|read|write|admin))
1194 :type perm: str
1194 :type perm: str
1195
1195
1196 Example output:
1196 Example output:
1197
1197
1198 .. code-block:: bash
1198 .. code-block:: bash
1199
1199
1200 id : <id_given_in_input>
1200 id : <id_given_in_input>
1201 result : {
1201 result : {
1202 "msg": "Granted perm: `<perm_name>` for user: `<username>` in user group: `<user_group_name>`",
1202 "msg": "Granted perm: `<perm_name>` for user: `<username>` in user group: `<user_group_name>`",
1203 "success": true
1203 "success": true
1204 }
1204 }
1205 error : null
1205 error : null
1206
1206
1207
1207
1208 revoke_user_permission_from_user_group
1208 revoke_user_permission_from_user_group
1209 --------------------------------------
1209 --------------------------------------
1210
1210
1211 .. py:function:: revoke_user_permission_from_user_group(apiuser, usergroupid, userid)
1211 .. py:function:: revoke_user_permission_from_user_group(apiuser, usergroupid, userid)
1212
1212
1213 Revoke a users permissions in a user group.
1213 Revoke a users permissions in a user group.
1214
1214
1215 :param apiuser: This is filled automatically from the |authtoken|.
1215 :param apiuser: This is filled automatically from the |authtoken|.
1216 :type apiuser: AuthUser
1216 :type apiuser: AuthUser
1217 :param usergroupid: Set the user group from which to revoke the user
1217 :param usergroupid: Set the user group from which to revoke the user
1218 permissions.
1218 permissions.
1219 :type: usergroupid: str or int
1219 :type: usergroupid: str or int
1220 :param userid: Set the userid of the user whose permissions will be
1220 :param userid: Set the userid of the user whose permissions will be
1221 revoked.
1221 revoked.
1222 :type userid: str
1222 :type userid: str
1223
1223
1224 Example output:
1224 Example output:
1225
1225
1226 .. code-block:: bash
1226 .. code-block:: bash
1227
1227
1228 id : <id_given_in_input>
1228 id : <id_given_in_input>
1229 result : {
1229 result : {
1230 "msg": "Revoked perm for user: `<username>` in user group: `<user_group_name>`",
1230 "msg": "Revoked perm for user: `<username>` in user group: `<user_group_name>`",
1231 "success": true
1231 "success": true
1232 }
1232 }
1233 error : null
1233 error : null
1234
1234
1235
1235
1236 grant_user_group_permission_to_user_group
1236 grant_user_group_permission_to_user_group
1237 -----------------------------------------
1237 -----------------------------------------
1238
1238
1239 .. py:function:: grant_user_group_permission_to_user_group(apiuser, usergroupid, sourceusergroupid, perm)
1239 .. py:function:: grant_user_group_permission_to_user_group(apiuser, usergroupid, sourceusergroupid, perm)
1240
1240
1241 Give one user group permissions to another user group.
1241 Give one user group permissions to another user group.
1242
1242
1243 :param apiuser: This is filled automatically from the |authtoken|.
1243 :param apiuser: This is filled automatically from the |authtoken|.
1244 :type apiuser: AuthUser
1244 :type apiuser: AuthUser
1245 :param usergroupid: Set the user group on which to edit permissions.
1245 :param usergroupid: Set the user group on which to edit permissions.
1246 :type usergroupid: str or int
1246 :type usergroupid: str or int
1247 :param sourceusergroupid: Set the source user group to which
1247 :param sourceusergroupid: Set the source user group to which
1248 access/permissions will be granted.
1248 access/permissions will be granted.
1249 :type sourceusergroupid: str or int
1249 :type sourceusergroupid: str or int
1250 :param perm: (usergroup.(none|read|write|admin))
1250 :param perm: (usergroup.(none|read|write|admin))
1251 :type perm: str
1251 :type perm: str
1252
1252
1253 Example output:
1253 Example output:
1254
1254
1255 .. code-block:: bash
1255 .. code-block:: bash
1256
1256
1257 id : <id_given_in_input>
1257 id : <id_given_in_input>
1258 result : {
1258 result : {
1259 "msg": "Granted perm: `<perm_name>` for user group: `<source_user_group_name>` in user group: `<user_group_name>`",
1259 "msg": "Granted perm: `<perm_name>` for user group: `<source_user_group_name>` in user group: `<user_group_name>`",
1260 "success": true
1260 "success": true
1261 }
1261 }
1262 error : null
1262 error : null
1263
1263
1264
1264
1265 revoke_user_group_permission_from_user_group
1265 revoke_user_group_permission_from_user_group
1266 --------------------------------------------
1266 --------------------------------------------
1267
1267
1268 .. py:function:: revoke_user_group_permission_from_user_group(apiuser, usergroupid, sourceusergroupid)
1268 .. py:function:: revoke_user_group_permission_from_user_group(apiuser, usergroupid, sourceusergroupid)
1269
1269
1270 Revoke the permissions that one user group has to another.
1270 Revoke the permissions that one user group has to another.
1271
1271
1272 :param apiuser: This is filled automatically from the |authtoken|.
1272 :param apiuser: This is filled automatically from the |authtoken|.
1273 :type apiuser: AuthUser
1273 :type apiuser: AuthUser
1274 :param usergroupid: Set the user group on which to edit permissions.
1274 :param usergroupid: Set the user group on which to edit permissions.
1275 :type usergroupid: str or int
1275 :type usergroupid: str or int
1276 :param sourceusergroupid: Set the user group from which permissions
1276 :param sourceusergroupid: Set the user group from which permissions
1277 are revoked.
1277 are revoked.
1278 :type sourceusergroupid: str or int
1278 :type sourceusergroupid: str or int
1279
1279
1280 Example output:
1280 Example output:
1281
1281
1282 .. code-block:: bash
1282 .. code-block:: bash
1283
1283
1284 id : <id_given_in_input>
1284 id : <id_given_in_input>
1285 result : {
1285 result : {
1286 "msg": "Revoked perm for user group: `<user_group_name>` in user group: `<target_user_group_name>`",
1286 "msg": "Revoked perm for user group: `<user_group_name>` in user group: `<target_user_group_name>`",
1287 "success": true
1287 "success": true
1288 }
1288 }
1289 error : null
1289 error : null
1290
1290
1291
1291
1292 get_pull_request
1292 get_pull_request
1293 ----------------
1293 ----------------
1294
1294
1295 .. py:function:: get_pull_request(apiuser, repoid, pullrequestid)
1295 .. py:function:: get_pull_request(apiuser, repoid, pullrequestid)
1296
1296
1297 Get a pull request based on the given ID.
1297 Get a pull request based on the given ID.
1298
1298
1299 :param apiuser: This is filled automatically from the |authtoken|.
1299 :param apiuser: This is filled automatically from the |authtoken|.
1300 :type apiuser: AuthUser
1300 :type apiuser: AuthUser
1301 :param repoid: Repository name or repository ID from where the pull
1301 :param repoid: Repository name or repository ID from where the pull
1302 request was opened.
1302 request was opened.
1303 :type repoid: str or int
1303 :type repoid: str or int
1304 :param pullrequestid: ID of the requested pull request.
1304 :param pullrequestid: ID of the requested pull request.
1305 :type pullrequestid: int
1305 :type pullrequestid: int
1306
1306
1307 Example output:
1307 Example output:
1308
1308
1309 .. code-block:: bash
1309 .. code-block:: bash
1310
1310
1311 "id": <id_given_in_input>,
1311 "id": <id_given_in_input>,
1312 "result":
1312 "result":
1313 {
1313 {
1314 "pull_request_id": "<pull_request_id>",
1314 "pull_request_id": "<pull_request_id>",
1315 "url": "<url>",
1315 "url": "<url>",
1316 "title": "<title>",
1316 "title": "<title>",
1317 "description": "<description>",
1317 "description": "<description>",
1318 "status" : "<status>",
1318 "status" : "<status>",
1319 "created_on": "<date_time_created>",
1319 "created_on": "<date_time_created>",
1320 "updated_on": "<date_time_updated>",
1320 "updated_on": "<date_time_updated>",
1321 "commit_ids": [
1321 "commit_ids": [
1322 ...
1322 ...
1323 "<commit_id>",
1323 "<commit_id>",
1324 "<commit_id>",
1324 "<commit_id>",
1325 ...
1325 ...
1326 ],
1326 ],
1327 "review_status": "<review_status>",
1327 "review_status": "<review_status>",
1328 "mergeable": {
1328 "mergeable": {
1329 "status": "<bool>",
1329 "status": "<bool>",
1330 "message": "<message>",
1330 "message": "<message>",
1331 },
1331 },
1332 "source": {
1332 "source": {
1333 "clone_url": "<clone_url>",
1333 "clone_url": "<clone_url>",
1334 "repository": "<repository_name>",
1334 "repository": "<repository_name>",
1335 "reference":
1335 "reference":
1336 {
1336 {
1337 "name": "<name>",
1337 "name": "<name>",
1338 "type": "<type>",
1338 "type": "<type>",
1339 "commit_id": "<commit_id>",
1339 "commit_id": "<commit_id>",
1340 }
1340 }
1341 },
1341 },
1342 "target": {
1342 "target": {
1343 "clone_url": "<clone_url>",
1343 "clone_url": "<clone_url>",
1344 "repository": "<repository_name>",
1344 "repository": "<repository_name>",
1345 "reference":
1345 "reference":
1346 {
1346 {
1347 "name": "<name>",
1347 "name": "<name>",
1348 "type": "<type>",
1348 "type": "<type>",
1349 "commit_id": "<commit_id>",
1349 "commit_id": "<commit_id>",
1350 }
1350 }
1351 },
1351 },
1352 "author": <user_obj>,
1352 "author": <user_obj>,
1353 "reviewers": [
1353 "reviewers": [
1354 ...
1354 ...
1355 {
1355 {
1356 "user": "<user_obj>",
1356 "user": "<user_obj>",
1357 "review_status": "<review_status>",
1357 "review_status": "<review_status>",
1358 }
1358 }
1359 ...
1359 ...
1360 ]
1360 ]
1361 },
1361 },
1362 "error": null
1362 "error": null
1363
1363
1364
1364
1365 get_pull_requests
1365 get_pull_requests
1366 -----------------
1366 -----------------
1367
1367
1368 .. py:function:: get_pull_requests(apiuser, repoid, status=<Optional:'new'>)
1368 .. py:function:: get_pull_requests(apiuser, repoid, status=<Optional:'new'>)
1369
1369
1370 Get all pull requests from the repository specified in `repoid`.
1370 Get all pull requests from the repository specified in `repoid`.
1371
1371
1372 :param apiuser: This is filled automatically from the |authtoken|.
1372 :param apiuser: This is filled automatically from the |authtoken|.
1373 :type apiuser: AuthUser
1373 :type apiuser: AuthUser
1374 :param repoid: Repository name or repository ID.
1374 :param repoid: Repository name or repository ID.
1375 :type repoid: str or int
1375 :type repoid: str or int
1376 :param status: Only return pull requests with the specified status.
1376 :param status: Only return pull requests with the specified status.
1377 Valid options are.
1377 Valid options are.
1378 * ``new`` (default)
1378 * ``new`` (default)
1379 * ``open``
1379 * ``open``
1380 * ``closed``
1380 * ``closed``
1381 :type status: str
1381 :type status: str
1382
1382
1383 Example output:
1383 Example output:
1384
1384
1385 .. code-block:: bash
1385 .. code-block:: bash
1386
1386
1387 "id": <id_given_in_input>,
1387 "id": <id_given_in_input>,
1388 "result":
1388 "result":
1389 [
1389 [
1390 ...
1390 ...
1391 {
1391 {
1392 "pull_request_id": "<pull_request_id>",
1392 "pull_request_id": "<pull_request_id>",
1393 "url": "<url>",
1393 "url": "<url>",
1394 "title" : "<title>",
1394 "title" : "<title>",
1395 "description": "<description>",
1395 "description": "<description>",
1396 "status": "<status>",
1396 "status": "<status>",
1397 "created_on": "<date_time_created>",
1397 "created_on": "<date_time_created>",
1398 "updated_on": "<date_time_updated>",
1398 "updated_on": "<date_time_updated>",
1399 "commit_ids": [
1399 "commit_ids": [
1400 ...
1400 ...
1401 "<commit_id>",
1401 "<commit_id>",
1402 "<commit_id>",
1402 "<commit_id>",
1403 ...
1403 ...
1404 ],
1404 ],
1405 "review_status": "<review_status>",
1405 "review_status": "<review_status>",
1406 "mergeable": {
1406 "mergeable": {
1407 "status": "<bool>",
1407 "status": "<bool>",
1408 "message: "<message>",
1408 "message: "<message>",
1409 },
1409 },
1410 "source": {
1410 "source": {
1411 "clone_url": "<clone_url>",
1411 "clone_url": "<clone_url>",
1412 "reference":
1412 "reference":
1413 {
1413 {
1414 "name": "<name>",
1414 "name": "<name>",
1415 "type": "<type>",
1415 "type": "<type>",
1416 "commit_id": "<commit_id>",
1416 "commit_id": "<commit_id>",
1417 }
1417 }
1418 },
1418 },
1419 "target": {
1419 "target": {
1420 "clone_url": "<clone_url>",
1420 "clone_url": "<clone_url>",
1421 "reference":
1421 "reference":
1422 {
1422 {
1423 "name": "<name>",
1423 "name": "<name>",
1424 "type": "<type>",
1424 "type": "<type>",
1425 "commit_id": "<commit_id>",
1425 "commit_id": "<commit_id>",
1426 }
1426 }
1427 },
1427 },
1428 "author": <user_obj>,
1428 "author": <user_obj>,
1429 "reviewers": [
1429 "reviewers": [
1430 ...
1430 ...
1431 {
1431 {
1432 "user": "<user_obj>",
1432 "user": "<user_obj>",
1433 "review_status": "<review_status>",
1433 "review_status": "<review_status>",
1434 }
1434 }
1435 ...
1435 ...
1436 ]
1436 ]
1437 }
1437 }
1438 ...
1438 ...
1439 ],
1439 ],
1440 "error": null
1440 "error": null
1441
1441
1442
1442
1443 merge_pull_request
1443 merge_pull_request
1444 ------------------
1444 ------------------
1445
1445
1446 .. py:function:: merge_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
1446 .. py:function:: merge_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
1447
1447
1448 Merge the pull request specified by `pullrequestid` into its target
1448 Merge the pull request specified by `pullrequestid` into its target
1449 repository.
1449 repository.
1450
1450
1451 :param apiuser: This is filled automatically from the |authtoken|.
1451 :param apiuser: This is filled automatically from the |authtoken|.
1452 :type apiuser: AuthUser
1452 :type apiuser: AuthUser
1453 :param repoid: The Repository name or repository ID of the
1453 :param repoid: The Repository name or repository ID of the
1454 target repository to which the |pr| is to be merged.
1454 target repository to which the |pr| is to be merged.
1455 :type repoid: str or int
1455 :type repoid: str or int
1456 :param pullrequestid: ID of the pull request which shall be merged.
1456 :param pullrequestid: ID of the pull request which shall be merged.
1457 :type pullrequestid: int
1457 :type pullrequestid: int
1458 :param userid: Merge the pull request as this user.
1458 :param userid: Merge the pull request as this user.
1459 :type userid: Optional(str or int)
1459 :type userid: Optional(str or int)
1460
1460
1461 Example output:
1461 Example output:
1462
1462
1463 .. code-block:: bash
1463 .. code-block:: bash
1464
1464
1465 "id": <id_given_in_input>,
1465 "id": <id_given_in_input>,
1466 "result":
1466 "result":
1467 {
1467 {
1468 "executed": "<bool>",
1468 "executed": "<bool>",
1469 "failure_reason": "<int>",
1469 "failure_reason": "<int>",
1470 "merge_commit_id": "<merge_commit_id>",
1470 "merge_commit_id": "<merge_commit_id>",
1471 "possible": "<bool>"
1471 "possible": "<bool>"
1472 },
1472 },
1473 "error": null
1473 "error": null
1474
1474
1475
1475
1476 close_pull_request
1476 close_pull_request
1477 ------------------
1477 ------------------
1478
1478
1479 .. py:function:: close_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
1479 .. py:function:: close_pull_request(apiuser, repoid, pullrequestid, userid=<Optional:<OptionalAttr:apiuser>>)
1480
1480
1481 Close the pull request specified by `pullrequestid`.
1481 Close the pull request specified by `pullrequestid`.
1482
1482
1483 :param apiuser: This is filled automatically from the |authtoken|.
1483 :param apiuser: This is filled automatically from the |authtoken|.
1484 :type apiuser: AuthUser
1484 :type apiuser: AuthUser
1485 :param repoid: Repository name or repository ID to which the pull
1485 :param repoid: Repository name or repository ID to which the pull
1486 request belongs.
1486 request belongs.
1487 :type repoid: str or int
1487 :type repoid: str or int
1488 :param pullrequestid: ID of the pull request to be closed.
1488 :param pullrequestid: ID of the pull request to be closed.
1489 :type pullrequestid: int
1489 :type pullrequestid: int
1490 :param userid: Close the pull request as this user.
1490 :param userid: Close the pull request as this user.
1491 :type userid: Optional(str or int)
1491 :type userid: Optional(str or int)
1492
1492
1493 Example output:
1493 Example output:
1494
1494
1495 .. code-block:: bash
1495 .. code-block:: bash
1496
1496
1497 "id": <id_given_in_input>,
1497 "id": <id_given_in_input>,
1498 "result":
1498 "result":
1499 {
1499 {
1500 "pull_request_id": "<int>",
1500 "pull_request_id": "<int>",
1501 "closed": "<bool>"
1501 "closed": "<bool>"
1502 },
1502 },
1503 "error": null
1503 "error": null
1504
1504
1505
1505
1506 comment_pull_request
1506 comment_pull_request
1507 --------------------
1507 --------------------
1508
1508
1509 .. py:function:: comment_pull_request(apiuser, repoid, pullrequestid, message=<Optional:None>, status=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
1509 .. py:function:: comment_pull_request(apiuser, repoid, pullrequestid, message=<Optional:None>, status=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
1510
1510
1511 Comment on the pull request specified with the `pullrequestid`,
1511 Comment on the pull request specified with the `pullrequestid`,
1512 in the |repo| specified by the `repoid`, and optionally change the
1512 in the |repo| specified by the `repoid`, and optionally change the
1513 review status.
1513 review status.
1514
1514
1515 :param apiuser: This is filled automatically from the |authtoken|.
1515 :param apiuser: This is filled automatically from the |authtoken|.
1516 :type apiuser: AuthUser
1516 :type apiuser: AuthUser
1517 :param repoid: The repository name or repository ID.
1517 :param repoid: The repository name or repository ID.
1518 :type repoid: str or int
1518 :type repoid: str or int
1519 :param pullrequestid: The pull request ID.
1519 :param pullrequestid: The pull request ID.
1520 :type pullrequestid: int
1520 :type pullrequestid: int
1521 :param message: The text content of the comment.
1521 :param message: The text content of the comment.
1522 :type message: str
1522 :type message: str
1523 :param status: (**Optional**) Set the approval status of the pull
1523 :param status: (**Optional**) Set the approval status of the pull
1524 request. Valid options are:
1524 request. Valid options are:
1525 * not_reviewed
1525 * not_reviewed
1526 * approved
1526 * approved
1527 * rejected
1527 * rejected
1528 * under_review
1528 * under_review
1529 :type status: str
1529 :type status: str
1530 :param userid: Comment on the pull request as this user
1530 :param userid: Comment on the pull request as this user
1531 :type userid: Optional(str or int)
1531 :type userid: Optional(str or int)
1532
1532
1533 Example output:
1533 Example output:
1534
1534
1535 .. code-block:: bash
1535 .. code-block:: bash
1536
1536
1537 id : <id_given_in_input>
1537 id : <id_given_in_input>
1538 result :
1538 result :
1539 {
1539 {
1540 "pull_request_id": "<Integer>",
1540 "pull_request_id": "<Integer>",
1541 "comment_id": "<Integer>"
1541 "comment_id": "<Integer>"
1542 }
1542 }
1543 error : null
1543 error : null
1544
1544
1545
1545
1546 create_pull_request
1546 create_pull_request
1547 -------------------
1547 -------------------
1548
1548
1549 .. py:function:: create_pull_request(apiuser, source_repo, target_repo, source_ref, target_ref, title, description=<Optional:''>, reviewers=<Optional:None>)
1549 .. py:function:: create_pull_request(apiuser, source_repo, target_repo, source_ref, target_ref, title, description=<Optional:''>, reviewers=<Optional:None>)
1550
1550
1551 Creates a new pull request.
1551 Creates a new pull request.
1552
1552
1553 Accepts refs in the following formats:
1553 Accepts refs in the following formats:
1554
1554
1555 * branch:<branch_name>:<sha>
1555 * branch:<branch_name>:<sha>
1556 * branch:<branch_name>
1556 * branch:<branch_name>
1557 * bookmark:<bookmark_name>:<sha> (Mercurial only)
1557 * bookmark:<bookmark_name>:<sha> (Mercurial only)
1558 * bookmark:<bookmark_name> (Mercurial only)
1558 * bookmark:<bookmark_name> (Mercurial only)
1559
1559
1560 :param apiuser: This is filled automatically from the |authtoken|.
1560 :param apiuser: This is filled automatically from the |authtoken|.
1561 :type apiuser: AuthUser
1561 :type apiuser: AuthUser
1562 :param source_repo: Set the source repository name.
1562 :param source_repo: Set the source repository name.
1563 :type source_repo: str
1563 :type source_repo: str
1564 :param target_repo: Set the target repository name.
1564 :param target_repo: Set the target repository name.
1565 :type target_repo: str
1565 :type target_repo: str
1566 :param source_ref: Set the source ref name.
1566 :param source_ref: Set the source ref name.
1567 :type source_ref: str
1567 :type source_ref: str
1568 :param target_ref: Set the target ref name.
1568 :param target_ref: Set the target ref name.
1569 :type target_ref: str
1569 :type target_ref: str
1570 :param title: Set the pull request title.
1570 :param title: Set the pull request title.
1571 :type title: str
1571 :type title: str
1572 :param description: Set the pull request description.
1572 :param description: Set the pull request description.
1573 :type description: Optional(str)
1573 :type description: Optional(str)
1574 :param reviewers: Set the new pull request reviewers list.
1574 :param reviewers: Set the new pull request reviewers list.
1575 :type reviewers: Optional(list)
1575 :type reviewers: Optional(list)
1576
1576
1577
1577
1578 update_pull_request
1578 update_pull_request
1579 -------------------
1579 -------------------
1580
1580
1581 .. py:function:: update_pull_request(apiuser, repoid, pullrequestid, title=<Optional:''>, description=<Optional:''>, reviewers=<Optional:None>, update_commits=<Optional:None>, close_pull_request=<Optional:None>)
1581 .. py:function:: update_pull_request(apiuser, repoid, pullrequestid, title=<Optional:''>, description=<Optional:''>, reviewers=<Optional:None>, update_commits=<Optional:None>, close_pull_request=<Optional:None>)
1582
1582
1583 Updates a pull request.
1583 Updates a pull request.
1584
1584
1585 :param apiuser: This is filled automatically from the |authtoken|.
1585 :param apiuser: This is filled automatically from the |authtoken|.
1586 :type apiuser: AuthUser
1586 :type apiuser: AuthUser
1587 :param repoid: The repository name or repository ID.
1587 :param repoid: The repository name or repository ID.
1588 :type repoid: str or int
1588 :type repoid: str or int
1589 :param pullrequestid: The pull request ID.
1589 :param pullrequestid: The pull request ID.
1590 :type pullrequestid: int
1590 :type pullrequestid: int
1591 :param title: Set the pull request title.
1591 :param title: Set the pull request title.
1592 :type title: str
1592 :type title: str
1593 :param description: Update pull request description.
1593 :param description: Update pull request description.
1594 :type description: Optional(str)
1594 :type description: Optional(str)
1595 :param reviewers: Update pull request reviewers list with new value.
1595 :param reviewers: Update pull request reviewers list with new value.
1596 :type reviewers: Optional(list)
1596 :type reviewers: Optional(list)
1597 :param update_commits: Trigger update of commits for this pull request
1597 :param update_commits: Trigger update of commits for this pull request
1598 :type: update_commits: Optional(bool)
1598 :type: update_commits: Optional(bool)
1599 :param close_pull_request: Close this pull request with rejected state
1599 :param close_pull_request: Close this pull request with rejected state
1600 :type: close_pull_request: Optional(bool)
1600 :type: close_pull_request: Optional(bool)
1601
1601
1602 Example output:
1602 Example output:
1603
1603
1604 .. code-block:: bash
1604 .. code-block:: bash
1605
1605
1606 id : <id_given_in_input>
1606 id : <id_given_in_input>
1607 result :
1607 result :
1608 {
1608 {
1609 "msg": "Updated pull request `63`",
1609 "msg": "Updated pull request `63`",
1610 "pull_request": <pull_request_object>,
1610 "pull_request": <pull_request_object>,
1611 "updated_reviewers": {
1611 "updated_reviewers": {
1612 "added": [
1612 "added": [
1613 "username"
1613 "username"
1614 ],
1614 ],
1615 "removed": []
1615 "removed": []
1616 },
1616 },
1617 "updated_commits": {
1617 "updated_commits": {
1618 "added": [
1618 "added": [
1619 "<sha1_hash>"
1619 "<sha1_hash>"
1620 ],
1620 ],
1621 "common": [
1621 "common": [
1622 "<sha1_hash>",
1622 "<sha1_hash>",
1623 "<sha1_hash>",
1623 "<sha1_hash>",
1624 ],
1624 ],
1625 "removed": []
1625 "removed": []
1626 }
1626 }
1627 }
1627 }
1628 error : null
1628 error : null
1629
1629
1630
1630
1631 get_repo
1631 get_repo
1632 --------
1632 --------
1633
1633
1634 .. py:function:: get_repo(apiuser, repoid, cache=<Optional:True>)
1634 .. py:function:: get_repo(apiuser, repoid, cache=<Optional:True>)
1635
1635
1636 Gets an existing repository by its name or repository_id.
1636 Gets an existing repository by its name or repository_id.
1637
1637
1638 The members section so the output returns users groups or users
1638 The members section so the output returns users groups or users
1639 associated with that repository.
1639 associated with that repository.
1640
1640
1641 This command can only be run using an |authtoken| with admin rights,
1641 This command can only be run using an |authtoken| with admin rights,
1642 or users with at least read rights to the |repo|.
1642 or users with at least read rights to the |repo|.
1643
1643
1644 :param apiuser: This is filled automatically from the |authtoken|.
1644 :param apiuser: This is filled automatically from the |authtoken|.
1645 :type apiuser: AuthUser
1645 :type apiuser: AuthUser
1646 :param repoid: The repository name or repository id.
1646 :param repoid: The repository name or repository id.
1647 :type repoid: str or int
1647 :type repoid: str or int
1648 :param cache: use the cached value for last changeset
1648 :param cache: use the cached value for last changeset
1649 :type: cache: Optional(bool)
1649 :type: cache: Optional(bool)
1650
1650
1651 Example output:
1651 Example output:
1652
1652
1653 .. code-block:: bash
1653 .. code-block:: bash
1654
1654
1655 {
1655 {
1656 "error": null,
1656 "error": null,
1657 "id": <repo_id>,
1657 "id": <repo_id>,
1658 "result": {
1658 "result": {
1659 "clone_uri": null,
1659 "clone_uri": null,
1660 "created_on": "timestamp",
1660 "created_on": "timestamp",
1661 "description": "repo description",
1661 "description": "repo description",
1662 "enable_downloads": false,
1662 "enable_downloads": false,
1663 "enable_locking": false,
1663 "enable_locking": false,
1664 "enable_statistics": false,
1664 "enable_statistics": false,
1665 "followers": [
1665 "followers": [
1666 {
1666 {
1667 "active": true,
1667 "active": true,
1668 "admin": false,
1668 "admin": false,
1669 "api_key": "****************************************",
1669 "api_key": "****************************************",
1670 "api_keys": [
1670 "api_keys": [
1671 "****************************************"
1671 "****************************************"
1672 ],
1672 ],
1673 "email": "user@example.com",
1673 "email": "user@example.com",
1674 "emails": [
1674 "emails": [
1675 "user@example.com"
1675 "user@example.com"
1676 ],
1676 ],
1677 "extern_name": "rhodecode",
1677 "extern_name": "rhodecode",
1678 "extern_type": "rhodecode",
1678 "extern_type": "rhodecode",
1679 "firstname": "username",
1679 "firstname": "username",
1680 "ip_addresses": [],
1680 "ip_addresses": [],
1681 "language": null,
1681 "language": null,
1682 "last_login": "2015-09-16T17:16:35.854",
1682 "last_login": "2015-09-16T17:16:35.854",
1683 "lastname": "surname",
1683 "lastname": "surname",
1684 "user_id": <user_id>,
1684 "user_id": <user_id>,
1685 "username": "name"
1685 "username": "name"
1686 }
1686 }
1687 ],
1687 ],
1688 "fork_of": "parent-repo",
1688 "fork_of": "parent-repo",
1689 "landing_rev": [
1689 "landing_rev": [
1690 "rev",
1690 "rev",
1691 "tip"
1691 "tip"
1692 ],
1692 ],
1693 "last_changeset": {
1693 "last_changeset": {
1694 "author": "User <user@example.com>",
1694 "author": "User <user@example.com>",
1695 "branch": "default",
1695 "branch": "default",
1696 "date": "timestamp",
1696 "date": "timestamp",
1697 "message": "last commit message",
1697 "message": "last commit message",
1698 "parents": [
1698 "parents": [
1699 {
1699 {
1700 "raw_id": "commit-id"
1700 "raw_id": "commit-id"
1701 }
1701 }
1702 ],
1702 ],
1703 "raw_id": "commit-id",
1703 "raw_id": "commit-id",
1704 "revision": <revision number>,
1704 "revision": <revision number>,
1705 "short_id": "short id"
1705 "short_id": "short id"
1706 },
1706 },
1707 "lock_reason": null,
1707 "lock_reason": null,
1708 "locked_by": null,
1708 "locked_by": null,
1709 "locked_date": null,
1709 "locked_date": null,
1710 "members": [
1710 "members": [
1711 {
1711 {
1712 "name": "super-admin-name",
1712 "name": "super-admin-name",
1713 "origin": "super-admin",
1713 "origin": "super-admin",
1714 "permission": "repository.admin",
1714 "permission": "repository.admin",
1715 "type": "user"
1715 "type": "user"
1716 },
1716 },
1717 {
1717 {
1718 "name": "owner-name",
1718 "name": "owner-name",
1719 "origin": "owner",
1719 "origin": "owner",
1720 "permission": "repository.admin",
1720 "permission": "repository.admin",
1721 "type": "user"
1721 "type": "user"
1722 },
1722 },
1723 {
1723 {
1724 "name": "user-group-name",
1724 "name": "user-group-name",
1725 "origin": "permission",
1725 "origin": "permission",
1726 "permission": "repository.write",
1726 "permission": "repository.write",
1727 "type": "user_group"
1727 "type": "user_group"
1728 }
1728 }
1729 ],
1729 ],
1730 "owner": "owner-name",
1730 "owner": "owner-name",
1731 "permissions": [
1731 "permissions": [
1732 {
1732 {
1733 "name": "super-admin-name",
1733 "name": "super-admin-name",
1734 "origin": "super-admin",
1734 "origin": "super-admin",
1735 "permission": "repository.admin",
1735 "permission": "repository.admin",
1736 "type": "user"
1736 "type": "user"
1737 },
1737 },
1738 {
1738 {
1739 "name": "owner-name",
1739 "name": "owner-name",
1740 "origin": "owner",
1740 "origin": "owner",
1741 "permission": "repository.admin",
1741 "permission": "repository.admin",
1742 "type": "user"
1742 "type": "user"
1743 },
1743 },
1744 {
1744 {
1745 "name": "user-group-name",
1745 "name": "user-group-name",
1746 "origin": "permission",
1746 "origin": "permission",
1747 "permission": "repository.write",
1747 "permission": "repository.write",
1748 "type": "user_group"
1748 "type": "user_group"
1749 }
1749 }
1750 ],
1750 ],
1751 "private": true,
1751 "private": true,
1752 "repo_id": 676,
1752 "repo_id": 676,
1753 "repo_name": "user-group/repo-name",
1753 "repo_name": "user-group/repo-name",
1754 "repo_type": "hg"
1754 "repo_type": "hg"
1755 }
1755 }
1756 }
1756 }
1757
1757
1758
1758
1759 get_repos
1759 get_repos
1760 ---------
1760 ---------
1761
1761
1762 .. py:function:: get_repos(apiuser)
1762 .. py:function:: get_repos(apiuser)
1763
1763
1764 Lists all existing repositories.
1764 Lists all existing repositories.
1765
1765
1766 This command can only be run using an |authtoken| with admin rights,
1766 This command can only be run using an |authtoken| with admin rights,
1767 or users with at least read rights to |repos|.
1767 or users with at least read rights to |repos|.
1768
1768
1769 :param apiuser: This is filled automatically from the |authtoken|.
1769 :param apiuser: This is filled automatically from the |authtoken|.
1770 :type apiuser: AuthUser
1770 :type apiuser: AuthUser
1771
1771
1772 Example output:
1772 Example output:
1773
1773
1774 .. code-block:: bash
1774 .. code-block:: bash
1775
1775
1776 id : <id_given_in_input>
1776 id : <id_given_in_input>
1777 result: [
1777 result: [
1778 {
1778 {
1779 "repo_id" : "<repo_id>",
1779 "repo_id" : "<repo_id>",
1780 "repo_name" : "<reponame>"
1780 "repo_name" : "<reponame>"
1781 "repo_type" : "<repo_type>",
1781 "repo_type" : "<repo_type>",
1782 "clone_uri" : "<clone_uri>",
1782 "clone_uri" : "<clone_uri>",
1783 "private": : "<bool>",
1783 "private": : "<bool>",
1784 "created_on" : "<datetimecreated>",
1784 "created_on" : "<datetimecreated>",
1785 "description" : "<description>",
1785 "description" : "<description>",
1786 "landing_rev": "<landing_rev>",
1786 "landing_rev": "<landing_rev>",
1787 "owner": "<repo_owner>",
1787 "owner": "<repo_owner>",
1788 "fork_of": "<name_of_fork_parent>",
1788 "fork_of": "<name_of_fork_parent>",
1789 "enable_downloads": "<bool>",
1789 "enable_downloads": "<bool>",
1790 "enable_locking": "<bool>",
1790 "enable_locking": "<bool>",
1791 "enable_statistics": "<bool>",
1791 "enable_statistics": "<bool>",
1792 },
1792 },
1793 ...
1793 ...
1794 ]
1794 ]
1795 error: null
1795 error: null
1796
1796
1797
1797
1798 get_repo_changeset
1798 get_repo_changeset
1799 ------------------
1799 ------------------
1800
1800
1801 .. py:function:: get_repo_changeset(apiuser, repoid, revision, details=<Optional:'basic'>)
1801 .. py:function:: get_repo_changeset(apiuser, repoid, revision, details=<Optional:'basic'>)
1802
1802
1803 Returns information about a changeset.
1803 Returns information about a changeset.
1804
1804
1805 Additionally parameters define the amount of details returned by
1805 Additionally parameters define the amount of details returned by
1806 this function.
1806 this function.
1807
1807
1808 This command can only be run using an |authtoken| with admin rights,
1808 This command can only be run using an |authtoken| with admin rights,
1809 or users with at least read rights to the |repo|.
1809 or users with at least read rights to the |repo|.
1810
1810
1811 :param apiuser: This is filled automatically from the |authtoken|.
1811 :param apiuser: This is filled automatically from the |authtoken|.
1812 :type apiuser: AuthUser
1812 :type apiuser: AuthUser
1813 :param repoid: The repository name or repository id
1813 :param repoid: The repository name or repository id
1814 :type repoid: str or int
1814 :type repoid: str or int
1815 :param revision: revision for which listing should be done
1815 :param revision: revision for which listing should be done
1816 :type revision: str
1816 :type revision: str
1817 :param details: details can be 'basic|extended|full' full gives diff
1817 :param details: details can be 'basic|extended|full' full gives diff
1818 info details like the diff itself, and number of changed files etc.
1818 info details like the diff itself, and number of changed files etc.
1819 :type details: Optional(str)
1819 :type details: Optional(str)
1820
1820
1821
1821
1822 get_repo_changesets
1822 get_repo_changesets
1823 -------------------
1823 -------------------
1824
1824
1825 .. py:function:: get_repo_changesets(apiuser, repoid, start_rev, limit, details=<Optional:'basic'>)
1825 .. py:function:: get_repo_changesets(apiuser, repoid, start_rev, limit, details=<Optional:'basic'>)
1826
1826
1827 Returns a set of changesets limited by the number of commits starting
1827 Returns a set of changesets limited by the number of commits starting
1828 from the `start_rev` option.
1828 from the `start_rev` option.
1829
1829
1830 Additional parameters define the amount of details returned by this
1830 Additional parameters define the amount of details returned by this
1831 function.
1831 function.
1832
1832
1833 This command can only be run using an |authtoken| with admin rights,
1833 This command can only be run using an |authtoken| with admin rights,
1834 or users with at least read rights to |repos|.
1834 or users with at least read rights to |repos|.
1835
1835
1836 :param apiuser: This is filled automatically from the |authtoken|.
1836 :param apiuser: This is filled automatically from the |authtoken|.
1837 :type apiuser: AuthUser
1837 :type apiuser: AuthUser
1838 :param repoid: The repository name or repository ID.
1838 :param repoid: The repository name or repository ID.
1839 :type repoid: str or int
1839 :type repoid: str or int
1840 :param start_rev: The starting revision from where to get changesets.
1840 :param start_rev: The starting revision from where to get changesets.
1841 :type start_rev: str
1841 :type start_rev: str
1842 :param limit: Limit the number of changesets to this amount
1842 :param limit: Limit the number of changesets to this amount
1843 :type limit: str or int
1843 :type limit: str or int
1844 :param details: Set the level of detail returned. Valid option are:
1844 :param details: Set the level of detail returned. Valid option are:
1845 ``basic``, ``extended`` and ``full``.
1845 ``basic``, ``extended`` and ``full``.
1846 :type details: Optional(str)
1846 :type details: Optional(str)
1847
1847
1848 .. note::
1848 .. note::
1849
1849
1850 Setting the parameter `details` to the value ``full`` is extensive
1850 Setting the parameter `details` to the value ``full`` is extensive
1851 and returns details like the diff itself, and the number
1851 and returns details like the diff itself, and the number
1852 of changed files.
1852 of changed files.
1853
1853
1854
1854
1855 get_repo_nodes
1855 get_repo_nodes
1856 --------------
1856 --------------
1857
1857
1858 .. py:function:: get_repo_nodes(apiuser, repoid, revision, root_path, ret_type=<Optional:'all'>, details=<Optional:'basic'>)
1858 .. py:function:: get_repo_nodes(apiuser, repoid, revision, root_path, ret_type=<Optional:'all'>, details=<Optional:'basic'>)
1859
1859
1860 Returns a list of nodes and children in a flat list for a given
1860 Returns a list of nodes and children in a flat list for a given
1861 path at given revision.
1861 path at given revision.
1862
1862
1863 It's possible to specify ret_type to show only `files` or `dirs`.
1863 It's possible to specify ret_type to show only `files` or `dirs`.
1864
1864
1865 This command can only be run using an |authtoken| with admin rights,
1865 This command can only be run using an |authtoken| with admin rights,
1866 or users with at least read rights to |repos|.
1866 or users with at least read rights to |repos|.
1867
1867
1868 :param apiuser: This is filled automatically from the |authtoken|.
1868 :param apiuser: This is filled automatically from the |authtoken|.
1869 :type apiuser: AuthUser
1869 :type apiuser: AuthUser
1870 :param repoid: The repository name or repository ID.
1870 :param repoid: The repository name or repository ID.
1871 :type repoid: str or int
1871 :type repoid: str or int
1872 :param revision: The revision for which listing should be done.
1872 :param revision: The revision for which listing should be done.
1873 :type revision: str
1873 :type revision: str
1874 :param root_path: The path from which to start displaying.
1874 :param root_path: The path from which to start displaying.
1875 :type root_path: str
1875 :type root_path: str
1876 :param ret_type: Set the return type. Valid options are
1876 :param ret_type: Set the return type. Valid options are
1877 ``all`` (default), ``files`` and ``dirs``.
1877 ``all`` (default), ``files`` and ``dirs``.
1878 :type ret_type: Optional(str)
1878 :type ret_type: Optional(str)
1879 :param details: Returns extended information about nodes, such as
1879 :param details: Returns extended information about nodes, such as
1880 md5, binary, and or content. The valid options are ``basic`` and
1880 md5, binary, and or content. The valid options are ``basic`` and
1881 ``full``.
1881 ``full``.
1882 :type details: Optional(str)
1882 :type details: Optional(str)
1883 :param max_file_bytes: Only return file content under this file size bytes
1884 :type details: Optional(int)
1883
1885
1884 Example output:
1886 Example output:
1885
1887
1886 .. code-block:: bash
1888 .. code-block:: bash
1887
1889
1888 id : <id_given_in_input>
1890 id : <id_given_in_input>
1889 result: [
1891 result: [
1890 {
1892 {
1891 "name" : "<name>"
1893 "name" : "<name>"
1892 "type" : "<type>",
1894 "type" : "<type>",
1893 "binary": "<true|false>" (only in extended mode)
1895 "binary": "<true|false>" (only in extended mode)
1894 "md5" : "<md5 of file content>" (only in extended mode)
1896 "md5" : "<md5 of file content>" (only in extended mode)
1895 },
1897 },
1896 ...
1898 ...
1897 ]
1899 ]
1898 error: null
1900 error: null
1899
1901
1900
1902
1901 create_repo
1903 create_repo
1902 -----------
1904 -----------
1903
1905
1904 .. 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>)
1906 .. 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>)
1905
1907
1906 Creates a repository.
1908 Creates a repository.
1907
1909
1908 * If the repository name contains "/", all the required repository
1910 * If the repository name contains "/", all the required repository
1909 groups will be created.
1911 groups will be created.
1910
1912
1911 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
1913 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
1912 (with "foo" as parent). It will also create the "baz" repository
1914 (with "foo" as parent). It will also create the "baz" repository
1913 with "bar" as |repo| group.
1915 with "bar" as |repo| group.
1914
1916
1915 This command can only be run using an |authtoken| with at least
1917 This command can only be run using an |authtoken| with at least
1916 write permissions to the |repo|.
1918 write permissions to the |repo|.
1917
1919
1918 :param apiuser: This is filled automatically from the |authtoken|.
1920 :param apiuser: This is filled automatically from the |authtoken|.
1919 :type apiuser: AuthUser
1921 :type apiuser: AuthUser
1920 :param repo_name: Set the repository name.
1922 :param repo_name: Set the repository name.
1921 :type repo_name: str
1923 :type repo_name: str
1922 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
1924 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
1923 :type repo_type: str
1925 :type repo_type: str
1924 :param owner: user_id or username
1926 :param owner: user_id or username
1925 :type owner: Optional(str)
1927 :type owner: Optional(str)
1926 :param description: Set the repository description.
1928 :param description: Set the repository description.
1927 :type description: Optional(str)
1929 :type description: Optional(str)
1928 :param private:
1930 :param private:
1929 :type private: bool
1931 :type private: bool
1930 :param clone_uri:
1932 :param clone_uri:
1931 :type clone_uri: str
1933 :type clone_uri: str
1932 :param landing_rev: <rev_type>:<rev>
1934 :param landing_rev: <rev_type>:<rev>
1933 :type landing_rev: str
1935 :type landing_rev: str
1934 :param enable_locking:
1936 :param enable_locking:
1935 :type enable_locking: bool
1937 :type enable_locking: bool
1936 :param enable_downloads:
1938 :param enable_downloads:
1937 :type enable_downloads: bool
1939 :type enable_downloads: bool
1938 :param enable_statistics:
1940 :param enable_statistics:
1939 :type enable_statistics: bool
1941 :type enable_statistics: bool
1940 :param copy_permissions: Copy permission from group in which the
1942 :param copy_permissions: Copy permission from group in which the
1941 repository is being created.
1943 repository is being created.
1942 :type copy_permissions: bool
1944 :type copy_permissions: bool
1943
1945
1944
1946
1945 Example output:
1947 Example output:
1946
1948
1947 .. code-block:: bash
1949 .. code-block:: bash
1948
1950
1949 id : <id_given_in_input>
1951 id : <id_given_in_input>
1950 result: {
1952 result: {
1951 "msg": "Created new repository `<reponame>`",
1953 "msg": "Created new repository `<reponame>`",
1952 "success": true,
1954 "success": true,
1953 "task": "<celery task id or None if done sync>"
1955 "task": "<celery task id or None if done sync>"
1954 }
1956 }
1955 error: null
1957 error: null
1956
1958
1957
1959
1958 Example error output:
1960 Example error output:
1959
1961
1960 .. code-block:: bash
1962 .. code-block:: bash
1961
1963
1962 id : <id_given_in_input>
1964 id : <id_given_in_input>
1963 result : null
1965 result : null
1964 error : {
1966 error : {
1965 'failed to create repository `<repo_name>`
1967 'failed to create repository `<repo_name>`
1966 }
1968 }
1967
1969
1968
1970
1969 add_field_to_repo
1971 add_field_to_repo
1970 -----------------
1972 -----------------
1971
1973
1972 .. py:function:: add_field_to_repo(apiuser, repoid, key, label=<Optional:''>, description=<Optional:''>)
1974 .. py:function:: add_field_to_repo(apiuser, repoid, key, label=<Optional:''>, description=<Optional:''>)
1973
1975
1974 Adds an extra field to a repository.
1976 Adds an extra field to a repository.
1975
1977
1976 This command can only be run using an |authtoken| with at least
1978 This command can only be run using an |authtoken| with at least
1977 write permissions to the |repo|.
1979 write permissions to the |repo|.
1978
1980
1979 :param apiuser: This is filled automatically from the |authtoken|.
1981 :param apiuser: This is filled automatically from the |authtoken|.
1980 :type apiuser: AuthUser
1982 :type apiuser: AuthUser
1981 :param repoid: Set the repository name or repository id.
1983 :param repoid: Set the repository name or repository id.
1982 :type repoid: str or int
1984 :type repoid: str or int
1983 :param key: Create a unique field key for this repository.
1985 :param key: Create a unique field key for this repository.
1984 :type key: str
1986 :type key: str
1985 :param label:
1987 :param label:
1986 :type label: Optional(str)
1988 :type label: Optional(str)
1987 :param description:
1989 :param description:
1988 :type description: Optional(str)
1990 :type description: Optional(str)
1989
1991
1990
1992
1991 remove_field_from_repo
1993 remove_field_from_repo
1992 ----------------------
1994 ----------------------
1993
1995
1994 .. py:function:: remove_field_from_repo(apiuser, repoid, key)
1996 .. py:function:: remove_field_from_repo(apiuser, repoid, key)
1995
1997
1996 Removes an extra field from a repository.
1998 Removes an extra field from a repository.
1997
1999
1998 This command can only be run using an |authtoken| with at least
2000 This command can only be run using an |authtoken| with at least
1999 write permissions to the |repo|.
2001 write permissions to the |repo|.
2000
2002
2001 :param apiuser: This is filled automatically from the |authtoken|.
2003 :param apiuser: This is filled automatically from the |authtoken|.
2002 :type apiuser: AuthUser
2004 :type apiuser: AuthUser
2003 :param repoid: Set the repository name or repository ID.
2005 :param repoid: Set the repository name or repository ID.
2004 :type repoid: str or int
2006 :type repoid: str or int
2005 :param key: Set the unique field key for this repository.
2007 :param key: Set the unique field key for this repository.
2006 :type key: str
2008 :type key: str
2007
2009
2008
2010
2009 update_repo
2011 update_repo
2010 -----------
2012 -----------
2011
2013
2012 .. py:function:: update_repo(apiuser, repoid, name=<Optional:None>, owner=<Optional:<OptionalAttr:apiuser>>, group=<Optional:None>, fork_of=<Optional:None>, 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>, fields=<Optional:''>)
2014 .. py:function:: update_repo(apiuser, repoid, name=<Optional:None>, owner=<Optional:<OptionalAttr:apiuser>>, group=<Optional:None>, fork_of=<Optional:None>, 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>, fields=<Optional:''>)
2013
2015
2014 Updates a repository with the given information.
2016 Updates a repository with the given information.
2015
2017
2016 This command can only be run using an |authtoken| with at least
2018 This command can only be run using an |authtoken| with at least
2017 write permissions to the |repo|.
2019 write permissions to the |repo|.
2018
2020
2019 :param apiuser: This is filled automatically from the |authtoken|.
2021 :param apiuser: This is filled automatically from the |authtoken|.
2020 :type apiuser: AuthUser
2022 :type apiuser: AuthUser
2021 :param repoid: repository name or repository ID.
2023 :param repoid: repository name or repository ID.
2022 :type repoid: str or int
2024 :type repoid: str or int
2023 :param name: Update the |repo| name.
2025 :param name: Update the |repo| name.
2024 :type name: str
2026 :type name: str
2025 :param owner: Set the |repo| owner.
2027 :param owner: Set the |repo| owner.
2026 :type owner: str
2028 :type owner: str
2027 :param group: Set the |repo| group the |repo| belongs to.
2029 :param group: Set the |repo| group the |repo| belongs to.
2028 :type group: str
2030 :type group: str
2029 :param fork_of: Set the master |repo| name.
2031 :param fork_of: Set the master |repo| name.
2030 :type fork_of: str
2032 :type fork_of: str
2031 :param description: Update the |repo| description.
2033 :param description: Update the |repo| description.
2032 :type description: str
2034 :type description: str
2033 :param private: Set the |repo| as private. (True | False)
2035 :param private: Set the |repo| as private. (True | False)
2034 :type private: bool
2036 :type private: bool
2035 :param clone_uri: Update the |repo| clone URI.
2037 :param clone_uri: Update the |repo| clone URI.
2036 :type clone_uri: str
2038 :type clone_uri: str
2037 :param landing_rev: Set the |repo| landing revision. Default is
2039 :param landing_rev: Set the |repo| landing revision. Default is
2038 ``tip``.
2040 ``tip``.
2039 :type landing_rev: str
2041 :type landing_rev: str
2040 :param enable_statistics: Enable statistics on the |repo|,
2042 :param enable_statistics: Enable statistics on the |repo|,
2041 (True | False).
2043 (True | False).
2042 :type enable_statistics: bool
2044 :type enable_statistics: bool
2043 :param enable_locking: Enable |repo| locking.
2045 :param enable_locking: Enable |repo| locking.
2044 :type enable_locking: bool
2046 :type enable_locking: bool
2045 :param enable_downloads: Enable downloads from the |repo|,
2047 :param enable_downloads: Enable downloads from the |repo|,
2046 (True | False).
2048 (True | False).
2047 :type enable_downloads: bool
2049 :type enable_downloads: bool
2048 :param fields: Add extra fields to the |repo|. Use the following
2050 :param fields: Add extra fields to the |repo|. Use the following
2049 example format: ``field_key=field_val,field_key2=fieldval2``.
2051 example format: ``field_key=field_val,field_key2=fieldval2``.
2050 Escape ', ' with \,
2052 Escape ', ' with \,
2051 :type fields: str
2053 :type fields: str
2052
2054
2053
2055
2054 fork_repo
2056 fork_repo
2055 ---------
2057 ---------
2056
2058
2057 .. py:function:: fork_repo(apiuser, repoid, fork_name, owner=<Optional:<OptionalAttr:apiuser>>, description=<Optional:''>, copy_permissions=<Optional:False>, private=<Optional:False>, landing_rev=<Optional:'rev:tip'>)
2059 .. py:function:: fork_repo(apiuser, repoid, fork_name, owner=<Optional:<OptionalAttr:apiuser>>, description=<Optional:''>, copy_permissions=<Optional:False>, private=<Optional:False>, landing_rev=<Optional:'rev:tip'>)
2058
2060
2059 Creates a fork of the specified |repo|.
2061 Creates a fork of the specified |repo|.
2060
2062
2061 * If using |RCE| with Celery this will immediately return a success
2063 * If using |RCE| with Celery this will immediately return a success
2062 message, even though the fork will be created asynchronously.
2064 message, even though the fork will be created asynchronously.
2063
2065
2064 This command can only be run using an |authtoken| with fork
2066 This command can only be run using an |authtoken| with fork
2065 permissions on the |repo|.
2067 permissions on the |repo|.
2066
2068
2067 :param apiuser: This is filled automatically from the |authtoken|.
2069 :param apiuser: This is filled automatically from the |authtoken|.
2068 :type apiuser: AuthUser
2070 :type apiuser: AuthUser
2069 :param repoid: Set repository name or repository ID.
2071 :param repoid: Set repository name or repository ID.
2070 :type repoid: str or int
2072 :type repoid: str or int
2071 :param fork_name: Set the fork name.
2073 :param fork_name: Set the fork name.
2072 :type fork_name: str
2074 :type fork_name: str
2073 :param owner: Set the fork owner.
2075 :param owner: Set the fork owner.
2074 :type owner: str
2076 :type owner: str
2075 :param description: Set the fork descripton.
2077 :param description: Set the fork descripton.
2076 :type description: str
2078 :type description: str
2077 :param copy_permissions: Copy permissions from parent |repo|. The
2079 :param copy_permissions: Copy permissions from parent |repo|. The
2078 default is False.
2080 default is False.
2079 :type copy_permissions: bool
2081 :type copy_permissions: bool
2080 :param private: Make the fork private. The default is False.
2082 :param private: Make the fork private. The default is False.
2081 :type private: bool
2083 :type private: bool
2082 :param landing_rev: Set the landing revision. The default is tip.
2084 :param landing_rev: Set the landing revision. The default is tip.
2083
2085
2084 Example output:
2086 Example output:
2085
2087
2086 .. code-block:: bash
2088 .. code-block:: bash
2087
2089
2088 id : <id_for_response>
2090 id : <id_for_response>
2089 api_key : "<api_key>"
2091 api_key : "<api_key>"
2090 args: {
2092 args: {
2091 "repoid" : "<reponame or repo_id>",
2093 "repoid" : "<reponame or repo_id>",
2092 "fork_name": "<forkname>",
2094 "fork_name": "<forkname>",
2093 "owner": "<username or user_id = Optional(=apiuser)>",
2095 "owner": "<username or user_id = Optional(=apiuser)>",
2094 "description": "<description>",
2096 "description": "<description>",
2095 "copy_permissions": "<bool>",
2097 "copy_permissions": "<bool>",
2096 "private": "<bool>",
2098 "private": "<bool>",
2097 "landing_rev": "<landing_rev>"
2099 "landing_rev": "<landing_rev>"
2098 }
2100 }
2099
2101
2100 Example error output:
2102 Example error output:
2101
2103
2102 .. code-block:: bash
2104 .. code-block:: bash
2103
2105
2104 id : <id_given_in_input>
2106 id : <id_given_in_input>
2105 result: {
2107 result: {
2106 "msg": "Created fork of `<reponame>` as `<forkname>`",
2108 "msg": "Created fork of `<reponame>` as `<forkname>`",
2107 "success": true,
2109 "success": true,
2108 "task": "<celery task id or None if done sync>"
2110 "task": "<celery task id or None if done sync>"
2109 }
2111 }
2110 error: null
2112 error: null
2111
2113
2112
2114
2113 delete_repo
2115 delete_repo
2114 -----------
2116 -----------
2115
2117
2116 .. py:function:: delete_repo(apiuser, repoid, forks=<Optional:''>)
2118 .. py:function:: delete_repo(apiuser, repoid, forks=<Optional:''>)
2117
2119
2118 Deletes a repository.
2120 Deletes a repository.
2119
2121
2120 * When the `forks` parameter is set it's possible to detach or delete
2122 * When the `forks` parameter is set it's possible to detach or delete
2121 forks of deleted repository.
2123 forks of deleted repository.
2122
2124
2123 This command can only be run using an |authtoken| with admin
2125 This command can only be run using an |authtoken| with admin
2124 permissions on the |repo|.
2126 permissions on the |repo|.
2125
2127
2126 :param apiuser: This is filled automatically from the |authtoken|.
2128 :param apiuser: This is filled automatically from the |authtoken|.
2127 :type apiuser: AuthUser
2129 :type apiuser: AuthUser
2128 :param repoid: Set the repository name or repository ID.
2130 :param repoid: Set the repository name or repository ID.
2129 :type repoid: str or int
2131 :type repoid: str or int
2130 :param forks: Set to `detach` or `delete` forks from the |repo|.
2132 :param forks: Set to `detach` or `delete` forks from the |repo|.
2131 :type forks: Optional(str)
2133 :type forks: Optional(str)
2132
2134
2133 Example error output:
2135 Example error output:
2134
2136
2135 .. code-block:: bash
2137 .. code-block:: bash
2136
2138
2137 id : <id_given_in_input>
2139 id : <id_given_in_input>
2138 result: {
2140 result: {
2139 "msg": "Deleted repository `<reponame>`",
2141 "msg": "Deleted repository `<reponame>`",
2140 "success": true
2142 "success": true
2141 }
2143 }
2142 error: null
2144 error: null
2143
2145
2144
2146
2145 comment_commit
2147 comment_commit
2146 --------------
2148 --------------
2147
2149
2148 .. py:function:: comment_commit(apiuser, repoid, commit_id, message, userid=<Optional:<OptionalAttr:apiuser>>, status=<Optional:None>)
2150 .. py:function:: comment_commit(apiuser, repoid, commit_id, message, userid=<Optional:<OptionalAttr:apiuser>>, status=<Optional:None>)
2149
2151
2150 Set a commit comment, and optionally change the status of the commit.
2152 Set a commit comment, and optionally change the status of the commit.
2151 This command can be executed only using api_key belonging to user
2153 This command can be executed only using api_key belonging to user
2152 with admin rights, or repository administrator.
2154 with admin rights, or repository administrator.
2153
2155
2154 :param apiuser: This is filled automatically from the |authtoken|.
2156 :param apiuser: This is filled automatically from the |authtoken|.
2155 :type apiuser: AuthUser
2157 :type apiuser: AuthUser
2156 :param repoid: Set the repository name or repository ID.
2158 :param repoid: Set the repository name or repository ID.
2157 :type repoid: str or int
2159 :type repoid: str or int
2158 :param commit_id: Specify the commit_id for which to set a comment.
2160 :param commit_id: Specify the commit_id for which to set a comment.
2159 :type commit_id: str
2161 :type commit_id: str
2160 :param message: The comment text.
2162 :param message: The comment text.
2161 :type message: str
2163 :type message: str
2162 :param userid: Set the user name of the comment creator.
2164 :param userid: Set the user name of the comment creator.
2163 :type userid: Optional(str or int)
2165 :type userid: Optional(str or int)
2164 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
2166 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
2165 'under_review'
2167 'under_review'
2166 :type status: str
2168 :type status: str
2167
2169
2168 Example error output:
2170 Example error output:
2169
2171
2170 .. code-block:: json
2172 .. code-block:: json
2171
2173
2172 {
2174 {
2173 "id" : <id_given_in_input>,
2175 "id" : <id_given_in_input>,
2174 "result" : {
2176 "result" : {
2175 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
2177 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
2176 "status_change": null or <status>,
2178 "status_change": null or <status>,
2177 "success": true
2179 "success": true
2178 },
2180 },
2179 "error" : null
2181 "error" : null
2180 }
2182 }
2181
2183
2182
2184
2183 changeset_comment
2185 changeset_comment
2184 -----------------
2186 -----------------
2185
2187
2186 .. py:function:: changeset_comment(apiuser, repoid, revision, message, userid=<Optional:<OptionalAttr:apiuser>>, status=<Optional:None>)
2188 .. py:function:: changeset_comment(apiuser, repoid, revision, message, userid=<Optional:<OptionalAttr:apiuser>>, status=<Optional:None>)
2187
2189
2188 .. deprecated:: 3.4.0
2190 .. deprecated:: 3.4.0
2189
2191
2190 Please use method `comment_commit` instead.
2192 Please use method `comment_commit` instead.
2191
2193
2192
2194
2193 Set a changeset comment, and optionally change the status of the
2195 Set a changeset comment, and optionally change the status of the
2194 changeset.
2196 changeset.
2195
2197
2196 This command can only be run using an |authtoken| with admin
2198 This command can only be run using an |authtoken| with admin
2197 permissions on the |repo|.
2199 permissions on the |repo|.
2198
2200
2199 :param apiuser: This is filled automatically from the |authtoken|.
2201 :param apiuser: This is filled automatically from the |authtoken|.
2200 :type apiuser: AuthUser
2202 :type apiuser: AuthUser
2201 :param repoid: Set the repository name or repository ID.
2203 :param repoid: Set the repository name or repository ID.
2202 :type repoid: str or int
2204 :type repoid: str or int
2203 :param revision: Specify the revision for which to set a comment.
2205 :param revision: Specify the revision for which to set a comment.
2204 :type revision: str
2206 :type revision: str
2205 :param message: The comment text.
2207 :param message: The comment text.
2206 :type message: str
2208 :type message: str
2207 :param userid: Set the user name of the comment creator.
2209 :param userid: Set the user name of the comment creator.
2208 :type userid: Optional(str or int)
2210 :type userid: Optional(str or int)
2209 :param status: Set the comment status. The following are valid options:
2211 :param status: Set the comment status. The following are valid options:
2210 * not_reviewed
2212 * not_reviewed
2211 * approved
2213 * approved
2212 * rejected
2214 * rejected
2213 * under_review
2215 * under_review
2214 :type status: str
2216 :type status: str
2215
2217
2216 Example error output:
2218 Example error output:
2217
2219
2218 .. code-block:: json
2220 .. code-block:: json
2219
2221
2220 {
2222 {
2221 "id" : <id_given_in_input>,
2223 "id" : <id_given_in_input>,
2222 "result" : {
2224 "result" : {
2223 "msg": "Commented on commit `<revision>` for repository `<repoid>`",
2225 "msg": "Commented on commit `<revision>` for repository `<repoid>`",
2224 "status_change": null or <status>,
2226 "status_change": null or <status>,
2225 "success": true
2227 "success": true
2226 },
2228 },
2227 "error" : null
2229 "error" : null
2228 }
2230 }
2229
2231
2230
2232
2231 grant_user_permission
2233 grant_user_permission
2232 ---------------------
2234 ---------------------
2233
2235
2234 .. py:function:: grant_user_permission(apiuser, repoid, userid, perm)
2236 .. py:function:: grant_user_permission(apiuser, repoid, userid, perm)
2235
2237
2236 Grant permissions for the specified user on the given repository,
2238 Grant permissions for the specified user on the given repository,
2237 or update existing permissions if found.
2239 or update existing permissions if found.
2238
2240
2239 This command can only be run using an |authtoken| with admin
2241 This command can only be run using an |authtoken| with admin
2240 permissions on the |repo|.
2242 permissions on the |repo|.
2241
2243
2242 :param apiuser: This is filled automatically from the |authtoken|.
2244 :param apiuser: This is filled automatically from the |authtoken|.
2243 :type apiuser: AuthUser
2245 :type apiuser: AuthUser
2244 :param repoid: Set the repository name or repository ID.
2246 :param repoid: Set the repository name or repository ID.
2245 :type repoid: str or int
2247 :type repoid: str or int
2246 :param userid: Set the user name.
2248 :param userid: Set the user name.
2247 :type userid: str
2249 :type userid: str
2248 :param perm: Set the user permissions, using the following format
2250 :param perm: Set the user permissions, using the following format
2249 ``(repository.(none|read|write|admin))``
2251 ``(repository.(none|read|write|admin))``
2250 :type perm: str
2252 :type perm: str
2251
2253
2252 Example output:
2254 Example output:
2253
2255
2254 .. code-block:: bash
2256 .. code-block:: bash
2255
2257
2256 id : <id_given_in_input>
2258 id : <id_given_in_input>
2257 result: {
2259 result: {
2258 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
2260 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
2259 "success": true
2261 "success": true
2260 }
2262 }
2261 error: null
2263 error: null
2262
2264
2263
2265
2264 revoke_user_permission
2266 revoke_user_permission
2265 ----------------------
2267 ----------------------
2266
2268
2267 .. py:function:: revoke_user_permission(apiuser, repoid, userid)
2269 .. py:function:: revoke_user_permission(apiuser, repoid, userid)
2268
2270
2269 Revoke permission for a user on the specified repository.
2271 Revoke permission for a user on the specified repository.
2270
2272
2271 This command can only be run using an |authtoken| with admin
2273 This command can only be run using an |authtoken| with admin
2272 permissions on the |repo|.
2274 permissions on the |repo|.
2273
2275
2274 :param apiuser: This is filled automatically from the |authtoken|.
2276 :param apiuser: This is filled automatically from the |authtoken|.
2275 :type apiuser: AuthUser
2277 :type apiuser: AuthUser
2276 :param repoid: Set the repository name or repository ID.
2278 :param repoid: Set the repository name or repository ID.
2277 :type repoid: str or int
2279 :type repoid: str or int
2278 :param userid: Set the user name of revoked user.
2280 :param userid: Set the user name of revoked user.
2279 :type userid: str or int
2281 :type userid: str or int
2280
2282
2281 Example error output:
2283 Example error output:
2282
2284
2283 .. code-block:: bash
2285 .. code-block:: bash
2284
2286
2285 id : <id_given_in_input>
2287 id : <id_given_in_input>
2286 result: {
2288 result: {
2287 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
2289 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
2288 "success": true
2290 "success": true
2289 }
2291 }
2290 error: null
2292 error: null
2291
2293
2292
2294
2293 grant_user_group_permission
2295 grant_user_group_permission
2294 ---------------------------
2296 ---------------------------
2295
2297
2296 .. py:function:: grant_user_group_permission(apiuser, repoid, usergroupid, perm)
2298 .. py:function:: grant_user_group_permission(apiuser, repoid, usergroupid, perm)
2297
2299
2298 Grant permission for a user group on the specified repository,
2300 Grant permission for a user group on the specified repository,
2299 or update existing permissions.
2301 or update existing permissions.
2300
2302
2301 This command can only be run using an |authtoken| with admin
2303 This command can only be run using an |authtoken| with admin
2302 permissions on the |repo|.
2304 permissions on the |repo|.
2303
2305
2304 :param apiuser: This is filled automatically from the |authtoken|.
2306 :param apiuser: This is filled automatically from the |authtoken|.
2305 :type apiuser: AuthUser
2307 :type apiuser: AuthUser
2306 :param repoid: Set the repository name or repository ID.
2308 :param repoid: Set the repository name or repository ID.
2307 :type repoid: str or int
2309 :type repoid: str or int
2308 :param usergroupid: Specify the ID of the user group.
2310 :param usergroupid: Specify the ID of the user group.
2309 :type usergroupid: str or int
2311 :type usergroupid: str or int
2310 :param perm: Set the user group permissions using the following
2312 :param perm: Set the user group permissions using the following
2311 format: (repository.(none|read|write|admin))
2313 format: (repository.(none|read|write|admin))
2312 :type perm: str
2314 :type perm: str
2313
2315
2314 Example output:
2316 Example output:
2315
2317
2316 .. code-block:: bash
2318 .. code-block:: bash
2317
2319
2318 id : <id_given_in_input>
2320 id : <id_given_in_input>
2319 result : {
2321 result : {
2320 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
2322 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
2321 "success": true
2323 "success": true
2322
2324
2323 }
2325 }
2324 error : null
2326 error : null
2325
2327
2326 Example error output:
2328 Example error output:
2327
2329
2328 .. code-block:: bash
2330 .. code-block:: bash
2329
2331
2330 id : <id_given_in_input>
2332 id : <id_given_in_input>
2331 result : null
2333 result : null
2332 error : {
2334 error : {
2333 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
2335 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
2334 }
2336 }
2335
2337
2336
2338
2337 revoke_user_group_permission
2339 revoke_user_group_permission
2338 ----------------------------
2340 ----------------------------
2339
2341
2340 .. py:function:: revoke_user_group_permission(apiuser, repoid, usergroupid)
2342 .. py:function:: revoke_user_group_permission(apiuser, repoid, usergroupid)
2341
2343
2342 Revoke the permissions of a user group on a given repository.
2344 Revoke the permissions of a user group on a given repository.
2343
2345
2344 This command can only be run using an |authtoken| with admin
2346 This command can only be run using an |authtoken| with admin
2345 permissions on the |repo|.
2347 permissions on the |repo|.
2346
2348
2347 :param apiuser: This is filled automatically from the |authtoken|.
2349 :param apiuser: This is filled automatically from the |authtoken|.
2348 :type apiuser: AuthUser
2350 :type apiuser: AuthUser
2349 :param repoid: Set the repository name or repository ID.
2351 :param repoid: Set the repository name or repository ID.
2350 :type repoid: str or int
2352 :type repoid: str or int
2351 :param usergroupid: Specify the user group ID.
2353 :param usergroupid: Specify the user group ID.
2352 :type usergroupid: str or int
2354 :type usergroupid: str or int
2353
2355
2354 Example output:
2356 Example output:
2355
2357
2356 .. code-block:: bash
2358 .. code-block:: bash
2357
2359
2358 id : <id_given_in_input>
2360 id : <id_given_in_input>
2359 result: {
2361 result: {
2360 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
2362 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
2361 "success": true
2363 "success": true
2362 }
2364 }
2363 error: null
2365 error: null
2364
2366
2365
2367
2366 get_repo_group
2368 get_repo_group
2367 --------------
2369 --------------
2368
2370
2369 .. py:function:: get_repo_group(apiuser, repogroupid)
2371 .. py:function:: get_repo_group(apiuser, repogroupid)
2370
2372
2371 Return the specified |repo| group, along with permissions,
2373 Return the specified |repo| group, along with permissions,
2372 and repositories inside the group
2374 and repositories inside the group
2373
2375
2374 :param apiuser: This is filled automatically from the |authtoken|.
2376 :param apiuser: This is filled automatically from the |authtoken|.
2375 :type apiuser: AuthUser
2377 :type apiuser: AuthUser
2376 :param repogroupid: Specify the name of ID of the repository group.
2378 :param repogroupid: Specify the name of ID of the repository group.
2377 :type repogroupid: str or int
2379 :type repogroupid: str or int
2378
2380
2379
2381
2380 Example output:
2382 Example output:
2381
2383
2382 .. code-block:: bash
2384 .. code-block:: bash
2383
2385
2384 {
2386 {
2385 "error": null,
2387 "error": null,
2386 "id": repo-group-id,
2388 "id": repo-group-id,
2387 "result": {
2389 "result": {
2388 "group_description": "repo group description",
2390 "group_description": "repo group description",
2389 "group_id": 14,
2391 "group_id": 14,
2390 "group_name": "group name",
2392 "group_name": "group name",
2391 "members": [
2393 "members": [
2392 {
2394 {
2393 "name": "super-admin-username",
2395 "name": "super-admin-username",
2394 "origin": "super-admin",
2396 "origin": "super-admin",
2395 "permission": "group.admin",
2397 "permission": "group.admin",
2396 "type": "user"
2398 "type": "user"
2397 },
2399 },
2398 {
2400 {
2399 "name": "owner-name",
2401 "name": "owner-name",
2400 "origin": "owner",
2402 "origin": "owner",
2401 "permission": "group.admin",
2403 "permission": "group.admin",
2402 "type": "user"
2404 "type": "user"
2403 },
2405 },
2404 {
2406 {
2405 "name": "user-group-name",
2407 "name": "user-group-name",
2406 "origin": "permission",
2408 "origin": "permission",
2407 "permission": "group.write",
2409 "permission": "group.write",
2408 "type": "user_group"
2410 "type": "user_group"
2409 }
2411 }
2410 ],
2412 ],
2411 "owner": "owner-name",
2413 "owner": "owner-name",
2412 "parent_group": null,
2414 "parent_group": null,
2413 "repositories": [ repo-list ]
2415 "repositories": [ repo-list ]
2414 }
2416 }
2415 }
2417 }
2416
2418
2417
2419
2418 get_repo_groups
2420 get_repo_groups
2419 ---------------
2421 ---------------
2420
2422
2421 .. py:function:: get_repo_groups(apiuser)
2423 .. py:function:: get_repo_groups(apiuser)
2422
2424
2423 Returns all repository groups.
2425 Returns all repository groups.
2424
2426
2425 :param apiuser: This is filled automatically from the |authtoken|.
2427 :param apiuser: This is filled automatically from the |authtoken|.
2426 :type apiuser: AuthUser
2428 :type apiuser: AuthUser
2427
2429
2428
2430
2429 create_repo_group
2431 create_repo_group
2430 -----------------
2432 -----------------
2431
2433
2432 .. py:function:: create_repo_group(apiuser, group_name, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, copy_permissions=<Optional:False>)
2434 .. py:function:: create_repo_group(apiuser, group_name, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, copy_permissions=<Optional:False>)
2433
2435
2434 Creates a repository group.
2436 Creates a repository group.
2435
2437
2436 * If the repository group name contains "/", all the required repository
2438 * If the repository group name contains "/", all the required repository
2437 groups will be created.
2439 groups will be created.
2438
2440
2439 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
2441 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
2440 (with "foo" as parent). It will also create the "baz" repository
2442 (with "foo" as parent). It will also create the "baz" repository
2441 with "bar" as |repo| group.
2443 with "bar" as |repo| group.
2442
2444
2443 This command can only be run using an |authtoken| with admin
2445 This command can only be run using an |authtoken| with admin
2444 permissions.
2446 permissions.
2445
2447
2446 :param apiuser: This is filled automatically from the |authtoken|.
2448 :param apiuser: This is filled automatically from the |authtoken|.
2447 :type apiuser: AuthUser
2449 :type apiuser: AuthUser
2448 :param group_name: Set the repository group name.
2450 :param group_name: Set the repository group name.
2449 :type group_name: str
2451 :type group_name: str
2450 :param description: Set the |repo| group description.
2452 :param description: Set the |repo| group description.
2451 :type description: str
2453 :type description: str
2452 :param owner: Set the |repo| group owner.
2454 :param owner: Set the |repo| group owner.
2453 :type owner: str
2455 :type owner: str
2454 :param copy_permissions:
2456 :param copy_permissions:
2455 :type copy_permissions:
2457 :type copy_permissions:
2456
2458
2457 Example output:
2459 Example output:
2458
2460
2459 .. code-block:: bash
2461 .. code-block:: bash
2460
2462
2461 id : <id_given_in_input>
2463 id : <id_given_in_input>
2462 result : {
2464 result : {
2463 "msg": "Created new repo group `<repo_group_name>`"
2465 "msg": "Created new repo group `<repo_group_name>`"
2464 "repo_group": <repogroup_object>
2466 "repo_group": <repogroup_object>
2465 }
2467 }
2466 error : null
2468 error : null
2467
2469
2468
2470
2469 Example error output:
2471 Example error output:
2470
2472
2471 .. code-block:: bash
2473 .. code-block:: bash
2472
2474
2473 id : <id_given_in_input>
2475 id : <id_given_in_input>
2474 result : null
2476 result : null
2475 error : {
2477 error : {
2476 failed to create repo group `<repogroupid>`
2478 failed to create repo group `<repogroupid>`
2477 }
2479 }
2478
2480
2479
2481
2480 update_repo_group
2482 update_repo_group
2481 -----------------
2483 -----------------
2482
2484
2483 .. py:function:: update_repo_group(apiuser, repogroupid, group_name=<Optional:''>, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, parent=<Optional:None>, enable_locking=<Optional:False>)
2485 .. py:function:: update_repo_group(apiuser, repogroupid, group_name=<Optional:''>, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, parent=<Optional:None>, enable_locking=<Optional:False>)
2484
2486
2485 Updates repository group with the details given.
2487 Updates repository group with the details given.
2486
2488
2487 This command can only be run using an |authtoken| with admin
2489 This command can only be run using an |authtoken| with admin
2488 permissions.
2490 permissions.
2489
2491
2490 :param apiuser: This is filled automatically from the |authtoken|.
2492 :param apiuser: This is filled automatically from the |authtoken|.
2491 :type apiuser: AuthUser
2493 :type apiuser: AuthUser
2492 :param repogroupid: Set the ID of repository group.
2494 :param repogroupid: Set the ID of repository group.
2493 :type repogroupid: str or int
2495 :type repogroupid: str or int
2494 :param group_name: Set the name of the |repo| group.
2496 :param group_name: Set the name of the |repo| group.
2495 :type group_name: str
2497 :type group_name: str
2496 :param description: Set a description for the group.
2498 :param description: Set a description for the group.
2497 :type description: str
2499 :type description: str
2498 :param owner: Set the |repo| group owner.
2500 :param owner: Set the |repo| group owner.
2499 :type owner: str
2501 :type owner: str
2500 :param parent: Set the |repo| group parent.
2502 :param parent: Set the |repo| group parent.
2501 :type parent: str or int
2503 :type parent: str or int
2502 :param enable_locking: Enable |repo| locking. The default is false.
2504 :param enable_locking: Enable |repo| locking. The default is false.
2503 :type enable_locking: bool
2505 :type enable_locking: bool
2504
2506
2505
2507
2506 delete_repo_group
2508 delete_repo_group
2507 -----------------
2509 -----------------
2508
2510
2509 .. py:function:: delete_repo_group(apiuser, repogroupid)
2511 .. py:function:: delete_repo_group(apiuser, repogroupid)
2510
2512
2511 Deletes a |repo| group.
2513 Deletes a |repo| group.
2512
2514
2513 :param apiuser: This is filled automatically from the |authtoken|.
2515 :param apiuser: This is filled automatically from the |authtoken|.
2514 :type apiuser: AuthUser
2516 :type apiuser: AuthUser
2515 :param repogroupid: Set the name or ID of repository group to be
2517 :param repogroupid: Set the name or ID of repository group to be
2516 deleted.
2518 deleted.
2517 :type repogroupid: str or int
2519 :type repogroupid: str or int
2518
2520
2519 Example output:
2521 Example output:
2520
2522
2521 .. code-block:: bash
2523 .. code-block:: bash
2522
2524
2523 id : <id_given_in_input>
2525 id : <id_given_in_input>
2524 result : {
2526 result : {
2525 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>
2527 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>
2526 'repo_group': null
2528 'repo_group': null
2527 }
2529 }
2528 error : null
2530 error : null
2529
2531
2530 Example error output:
2532 Example error output:
2531
2533
2532 .. code-block:: bash
2534 .. code-block:: bash
2533
2535
2534 id : <id_given_in_input>
2536 id : <id_given_in_input>
2535 result : null
2537 result : null
2536 error : {
2538 error : {
2537 "failed to delete repo group ID:<repogroupid> <repogroupname>"
2539 "failed to delete repo group ID:<repogroupid> <repogroupname>"
2538 }
2540 }
2539
2541
2540
2542
2541 grant_user_permission_to_repo_group
2543 grant_user_permission_to_repo_group
2542 -----------------------------------
2544 -----------------------------------
2543
2545
2544 .. py:function:: grant_user_permission_to_repo_group(apiuser, repogroupid, userid, perm, apply_to_children=<Optional:'none'>)
2546 .. py:function:: grant_user_permission_to_repo_group(apiuser, repogroupid, userid, perm, apply_to_children=<Optional:'none'>)
2545
2547
2546 Grant permission for a user on the given repository group, or update
2548 Grant permission for a user on the given repository group, or update
2547 existing permissions if found.
2549 existing permissions if found.
2548
2550
2549 This command can only be run using an |authtoken| with admin
2551 This command can only be run using an |authtoken| with admin
2550 permissions.
2552 permissions.
2551
2553
2552 :param apiuser: This is filled automatically from the |authtoken|.
2554 :param apiuser: This is filled automatically from the |authtoken|.
2553 :type apiuser: AuthUser
2555 :type apiuser: AuthUser
2554 :param repogroupid: Set the name or ID of repository group.
2556 :param repogroupid: Set the name or ID of repository group.
2555 :type repogroupid: str or int
2557 :type repogroupid: str or int
2556 :param userid: Set the user name.
2558 :param userid: Set the user name.
2557 :type userid: str
2559 :type userid: str
2558 :param perm: (group.(none|read|write|admin))
2560 :param perm: (group.(none|read|write|admin))
2559 :type perm: str
2561 :type perm: str
2560 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2562 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2561 :type apply_to_children: str
2563 :type apply_to_children: str
2562
2564
2563 Example output:
2565 Example output:
2564
2566
2565 .. code-block:: bash
2567 .. code-block:: bash
2566
2568
2567 id : <id_given_in_input>
2569 id : <id_given_in_input>
2568 result: {
2570 result: {
2569 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
2571 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
2570 "success": true
2572 "success": true
2571 }
2573 }
2572 error: null
2574 error: null
2573
2575
2574 Example error output:
2576 Example error output:
2575
2577
2576 .. code-block:: bash
2578 .. code-block:: bash
2577
2579
2578 id : <id_given_in_input>
2580 id : <id_given_in_input>
2579 result : null
2581 result : null
2580 error : {
2582 error : {
2581 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
2583 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
2582 }
2584 }
2583
2585
2584
2586
2585 revoke_user_permission_from_repo_group
2587 revoke_user_permission_from_repo_group
2586 --------------------------------------
2588 --------------------------------------
2587
2589
2588 .. py:function:: revoke_user_permission_from_repo_group(apiuser, repogroupid, userid, apply_to_children=<Optional:'none'>)
2590 .. py:function:: revoke_user_permission_from_repo_group(apiuser, repogroupid, userid, apply_to_children=<Optional:'none'>)
2589
2591
2590 Revoke permission for a user in a given repository group.
2592 Revoke permission for a user in a given repository group.
2591
2593
2592 This command can only be run using an |authtoken| with admin
2594 This command can only be run using an |authtoken| with admin
2593 permissions on the |repo| group.
2595 permissions on the |repo| group.
2594
2596
2595 :param apiuser: This is filled automatically from the |authtoken|.
2597 :param apiuser: This is filled automatically from the |authtoken|.
2596 :type apiuser: AuthUser
2598 :type apiuser: AuthUser
2597 :param repogroupid: Set the name or ID of the repository group.
2599 :param repogroupid: Set the name or ID of the repository group.
2598 :type repogroupid: str or int
2600 :type repogroupid: str or int
2599 :param userid: Set the user name to revoke.
2601 :param userid: Set the user name to revoke.
2600 :type userid: str
2602 :type userid: str
2601 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2603 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2602 :type apply_to_children: str
2604 :type apply_to_children: str
2603
2605
2604 Example output:
2606 Example output:
2605
2607
2606 .. code-block:: bash
2608 .. code-block:: bash
2607
2609
2608 id : <id_given_in_input>
2610 id : <id_given_in_input>
2609 result: {
2611 result: {
2610 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
2612 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
2611 "success": true
2613 "success": true
2612 }
2614 }
2613 error: null
2615 error: null
2614
2616
2615 Example error output:
2617 Example error output:
2616
2618
2617 .. code-block:: bash
2619 .. code-block:: bash
2618
2620
2619 id : <id_given_in_input>
2621 id : <id_given_in_input>
2620 result : null
2622 result : null
2621 error : {
2623 error : {
2622 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
2624 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
2623 }
2625 }
2624
2626
2625
2627
2626 grant_user_group_permission_to_repo_group
2628 grant_user_group_permission_to_repo_group
2627 -----------------------------------------
2629 -----------------------------------------
2628
2630
2629 .. py:function:: grant_user_group_permission_to_repo_group(apiuser, repogroupid, usergroupid, perm, apply_to_children=<Optional:'none'>)
2631 .. py:function:: grant_user_group_permission_to_repo_group(apiuser, repogroupid, usergroupid, perm, apply_to_children=<Optional:'none'>)
2630
2632
2631 Grant permission for a user group on given repository group, or update
2633 Grant permission for a user group on given repository group, or update
2632 existing permissions if found.
2634 existing permissions if found.
2633
2635
2634 This command can only be run using an |authtoken| with admin
2636 This command can only be run using an |authtoken| with admin
2635 permissions on the |repo| group.
2637 permissions on the |repo| group.
2636
2638
2637 :param apiuser: This is filled automatically from the |authtoken|.
2639 :param apiuser: This is filled automatically from the |authtoken|.
2638 :type apiuser: AuthUser
2640 :type apiuser: AuthUser
2639 :param repogroupid: Set the name or id of repository group
2641 :param repogroupid: Set the name or id of repository group
2640 :type repogroupid: str or int
2642 :type repogroupid: str or int
2641 :param usergroupid: id of usergroup
2643 :param usergroupid: id of usergroup
2642 :type usergroupid: str or int
2644 :type usergroupid: str or int
2643 :param perm: (group.(none|read|write|admin))
2645 :param perm: (group.(none|read|write|admin))
2644 :type perm: str
2646 :type perm: str
2645 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2647 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2646 :type apply_to_children: str
2648 :type apply_to_children: str
2647
2649
2648 Example output:
2650 Example output:
2649
2651
2650 .. code-block:: bash
2652 .. code-block:: bash
2651
2653
2652 id : <id_given_in_input>
2654 id : <id_given_in_input>
2653 result : {
2655 result : {
2654 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
2656 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
2655 "success": true
2657 "success": true
2656
2658
2657 }
2659 }
2658 error : null
2660 error : null
2659
2661
2660 Example error output:
2662 Example error output:
2661
2663
2662 .. code-block:: bash
2664 .. code-block:: bash
2663
2665
2664 id : <id_given_in_input>
2666 id : <id_given_in_input>
2665 result : null
2667 result : null
2666 error : {
2668 error : {
2667 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2669 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2668 }
2670 }
2669
2671
2670
2672
2671 revoke_user_group_permission_from_repo_group
2673 revoke_user_group_permission_from_repo_group
2672 --------------------------------------------
2674 --------------------------------------------
2673
2675
2674 .. py:function:: revoke_user_group_permission_from_repo_group(apiuser, repogroupid, usergroupid, apply_to_children=<Optional:'none'>)
2676 .. py:function:: revoke_user_group_permission_from_repo_group(apiuser, repogroupid, usergroupid, apply_to_children=<Optional:'none'>)
2675
2677
2676 Revoke permission for user group on given repository.
2678 Revoke permission for user group on given repository.
2677
2679
2678 This command can only be run using an |authtoken| with admin
2680 This command can only be run using an |authtoken| with admin
2679 permissions on the |repo| group.
2681 permissions on the |repo| group.
2680
2682
2681 :param apiuser: This is filled automatically from the |authtoken|.
2683 :param apiuser: This is filled automatically from the |authtoken|.
2682 :type apiuser: AuthUser
2684 :type apiuser: AuthUser
2683 :param repogroupid: name or id of repository group
2685 :param repogroupid: name or id of repository group
2684 :type repogroupid: str or int
2686 :type repogroupid: str or int
2685 :param usergroupid:
2687 :param usergroupid:
2686 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2688 :param apply_to_children: 'none', 'repos', 'groups', 'all'
2687 :type apply_to_children: str
2689 :type apply_to_children: str
2688
2690
2689 Example output:
2691 Example output:
2690
2692
2691 .. code-block:: bash
2693 .. code-block:: bash
2692
2694
2693 id : <id_given_in_input>
2695 id : <id_given_in_input>
2694 result: {
2696 result: {
2695 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
2697 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
2696 "success": true
2698 "success": true
2697 }
2699 }
2698 error: null
2700 error: null
2699
2701
2700 Example error output:
2702 Example error output:
2701
2703
2702 .. code-block:: bash
2704 .. code-block:: bash
2703
2705
2704 id : <id_given_in_input>
2706 id : <id_given_in_input>
2705 result : null
2707 result : null
2706 error : {
2708 error : {
2707 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2709 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
2708 }
2710 }
2709
2711
2710
2712
2711 get_gist
2713 get_gist
2712 --------
2714 --------
2713
2715
2714 .. py:function:: get_gist(apiuser, gistid, content=<Optional:False>)
2716 .. py:function:: get_gist(apiuser, gistid, content=<Optional:False>)
2715
2717
2716 Get the specified gist, based on the gist ID.
2718 Get the specified gist, based on the gist ID.
2717
2719
2718 :param apiuser: This is filled automatically from the |authtoken|.
2720 :param apiuser: This is filled automatically from the |authtoken|.
2719 :type apiuser: AuthUser
2721 :type apiuser: AuthUser
2720 :param gistid: Set the id of the private or public gist
2722 :param gistid: Set the id of the private or public gist
2721 :type gistid: str
2723 :type gistid: str
2722 :param content: Return the gist content. Default is false.
2724 :param content: Return the gist content. Default is false.
2723 :type content: Optional(bool)
2725 :type content: Optional(bool)
2724
2726
2725
2727
2726 get_gists
2728 get_gists
2727 ---------
2729 ---------
2728
2730
2729 .. py:function:: get_gists(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
2731 .. py:function:: get_gists(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
2730
2732
2731 Get all gists for given user. If userid is empty returned gists
2733 Get all gists for given user. If userid is empty returned gists
2732 are for user who called the api
2734 are for user who called the api
2733
2735
2734 :param apiuser: This is filled automatically from the |authtoken|.
2736 :param apiuser: This is filled automatically from the |authtoken|.
2735 :type apiuser: AuthUser
2737 :type apiuser: AuthUser
2736 :param userid: user to get gists for
2738 :param userid: user to get gists for
2737 :type userid: Optional(str or int)
2739 :type userid: Optional(str or int)
2738
2740
2739
2741
2740 create_gist
2742 create_gist
2741 -----------
2743 -----------
2742
2744
2743 .. py:function:: create_gist(apiuser, files, owner=<Optional:<OptionalAttr:apiuser>>, gist_type=<Optional:u'public'>, lifetime=<Optional:-1>, acl_level=<Optional:u'acl_public'>, description=<Optional:''>)
2745 .. py:function:: create_gist(apiuser, files, owner=<Optional:<OptionalAttr:apiuser>>, gist_type=<Optional:u'public'>, lifetime=<Optional:-1>, acl_level=<Optional:u'acl_public'>, description=<Optional:''>)
2744
2746
2745 Creates a new Gist.
2747 Creates a new Gist.
2746
2748
2747 :param apiuser: This is filled automatically from the |authtoken|.
2749 :param apiuser: This is filled automatically from the |authtoken|.
2748 :type apiuser: AuthUser
2750 :type apiuser: AuthUser
2749 :param files: files to be added to the gist. The data structure has
2751 :param files: files to be added to the gist. The data structure has
2750 to match the following example::
2752 to match the following example::
2751
2753
2752 {'filename': {'content':'...', 'lexer': null},
2754 {'filename': {'content':'...', 'lexer': null},
2753 'filename2': {'content':'...', 'lexer': null}}
2755 'filename2': {'content':'...', 'lexer': null}}
2754
2756
2755 :type files: dict
2757 :type files: dict
2756 :param owner: Set the gist owner, defaults to api method caller
2758 :param owner: Set the gist owner, defaults to api method caller
2757 :type owner: Optional(str or int)
2759 :type owner: Optional(str or int)
2758 :param gist_type: type of gist ``public`` or ``private``
2760 :param gist_type: type of gist ``public`` or ``private``
2759 :type gist_type: Optional(str)
2761 :type gist_type: Optional(str)
2760 :param lifetime: time in minutes of gist lifetime
2762 :param lifetime: time in minutes of gist lifetime
2761 :type lifetime: Optional(int)
2763 :type lifetime: Optional(int)
2762 :param acl_level: acl level for this gist, can be
2764 :param acl_level: acl level for this gist, can be
2763 ``acl_public`` or ``acl_private`` If the value is set to
2765 ``acl_public`` or ``acl_private`` If the value is set to
2764 ``acl_private`` only logged in users are able to access this gist.
2766 ``acl_private`` only logged in users are able to access this gist.
2765 If not set it defaults to ``acl_public``.
2767 If not set it defaults to ``acl_public``.
2766 :type acl_level: Optional(str)
2768 :type acl_level: Optional(str)
2767 :param description: gist description
2769 :param description: gist description
2768 :type description: Optional(str)
2770 :type description: Optional(str)
2769
2771
2770 Example output:
2772 Example output:
2771
2773
2772 .. code-block:: bash
2774 .. code-block:: bash
2773
2775
2774 id : <id_given_in_input>
2776 id : <id_given_in_input>
2775 result : {
2777 result : {
2776 "msg": "created new gist",
2778 "msg": "created new gist",
2777 "gist": {}
2779 "gist": {}
2778 }
2780 }
2779 error : null
2781 error : null
2780
2782
2781 Example error output:
2783 Example error output:
2782
2784
2783 .. code-block:: bash
2785 .. code-block:: bash
2784
2786
2785 id : <id_given_in_input>
2787 id : <id_given_in_input>
2786 result : null
2788 result : null
2787 error : {
2789 error : {
2788 "failed to create gist"
2790 "failed to create gist"
2789 }
2791 }
2790
2792
2791
2793
2792 delete_gist
2794 delete_gist
2793 -----------
2795 -----------
2794
2796
2795 .. py:function:: delete_gist(apiuser, gistid)
2797 .. py:function:: delete_gist(apiuser, gistid)
2796
2798
2797 Deletes existing gist
2799 Deletes existing gist
2798
2800
2799 :param apiuser: filled automatically from apikey
2801 :param apiuser: filled automatically from apikey
2800 :type apiuser: AuthUser
2802 :type apiuser: AuthUser
2801 :param gistid: id of gist to delete
2803 :param gistid: id of gist to delete
2802 :type gistid: str
2804 :type gistid: str
2803
2805
2804 Example output:
2806 Example output:
2805
2807
2806 .. code-block:: bash
2808 .. code-block:: bash
2807
2809
2808 id : <id_given_in_input>
2810 id : <id_given_in_input>
2809 result : {
2811 result : {
2810 "deleted gist ID: <gist_id>",
2812 "deleted gist ID: <gist_id>",
2811 "gist": null
2813 "gist": null
2812 }
2814 }
2813 error : null
2815 error : null
2814
2816
2815 Example error output:
2817 Example error output:
2816
2818
2817 .. code-block:: bash
2819 .. code-block:: bash
2818
2820
2819 id : <id_given_in_input>
2821 id : <id_given_in_input>
2820 result : null
2822 result : null
2821 error : {
2823 error : {
2822 "failed to delete gist ID:<gist_id>"
2824 "failed to delete gist ID:<gist_id>"
2823 }
2825 }
2824
@@ -1,119 +1,142 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.repo import RepoModel
25 from rhodecode.model.repo import RepoModel
26 from rhodecode.api.tests.utils import (
26 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error, assert_ok)
27 build_data, api_call, assert_error, assert_ok)
28
28
29
29
30 @pytest.mark.usefixtures("testuser_api", "app")
30 @pytest.mark.usefixtures("testuser_api", "app")
31 class TestGetRepoNodes(object):
31 class TestGetRepoNodes(object):
32 @pytest.mark.parametrize("name, ret_type", [
32 @pytest.mark.parametrize("name, ret_type", [
33 ('all', 'all'),
33 ('all', 'all'),
34 ('dirs', 'dirs'),
34 ('dirs', 'dirs'),
35 ('files', 'files'),
35 ('files', 'files'),
36 ])
36 ])
37 def test_api_get_repo_nodes(self, name, ret_type, backend):
37 def test_api_get_repo_nodes(self, name, ret_type, backend):
38 commit_id = 'tip'
38 commit_id = 'tip'
39 path = '/'
39 path = '/'
40 id_, params = build_data(
40 id_, params = build_data(
41 self.apikey, 'get_repo_nodes',
41 self.apikey, 'get_repo_nodes',
42 repoid=backend.repo_name, revision=commit_id,
42 repoid=backend.repo_name, revision=commit_id,
43 root_path=path,
43 root_path=path,
44 ret_type=ret_type)
44 ret_type=ret_type)
45 response = api_call(self.app, params)
45 response = api_call(self.app, params)
46
46
47 # we don't the actual return types here since it's tested somewhere
47 # we don't the actual return types here since it's tested somewhere
48 # else
48 # else
49 expected = response.json['result']
49 expected = response.json['result']
50 assert_ok(id_, expected, given=response.body)
50 assert_ok(id_, expected, given=response.body)
51
51
52 def test_api_get_repo_nodes_bad_commits(self, backend):
52 def test_api_get_repo_nodes_bad_commits(self, backend):
53 commit_id = 'i-dont-exist'
53 commit_id = 'i-dont-exist'
54 path = '/'
54 path = '/'
55 id_, params = build_data(
55 id_, params = build_data(
56 self.apikey, 'get_repo_nodes',
56 self.apikey, 'get_repo_nodes',
57 repoid=backend.repo_name, revision=commit_id,
57 repoid=backend.repo_name, revision=commit_id,
58 root_path=path, )
58 root_path=path, )
59 response = api_call(self.app, params)
59 response = api_call(self.app, params)
60
60
61 expected = 'failed to get repo: `%s` nodes' % (backend.repo_name,)
61 expected = 'failed to get repo: `%s` nodes' % (backend.repo_name,)
62 assert_error(id_, expected, given=response.body)
62 assert_error(id_, expected, given=response.body)
63
63
64 def test_api_get_repo_nodes_bad_path(self, backend):
64 def test_api_get_repo_nodes_bad_path(self, backend):
65 commit_id = 'tip'
65 commit_id = 'tip'
66 path = '/idontexits'
66 path = '/idontexits'
67 id_, params = build_data(
67 id_, params = build_data(
68 self.apikey, 'get_repo_nodes',
68 self.apikey, 'get_repo_nodes',
69 repoid=backend.repo_name, revision=commit_id,
69 repoid=backend.repo_name, revision=commit_id,
70 root_path=path, )
70 root_path=path, )
71 response = api_call(self.app, params)
71 response = api_call(self.app, params)
72
72
73 expected = 'failed to get repo: `%s` nodes' % (backend.repo_name,)
73 expected = 'failed to get repo: `%s` nodes' % (backend.repo_name,)
74 assert_error(id_, expected, given=response.body)
74 assert_error(id_, expected, given=response.body)
75
75
76 def test_api_get_repo_nodes_max_file_bytes(self, backend):
77 commit_id = 'tip'
78 path = '/'
79 max_file_bytes = 500
80
81 id_, params = build_data(
82 self.apikey, 'get_repo_nodes',
83 repoid=backend.repo_name, revision=commit_id, details='full',
84 root_path=path)
85 response = api_call(self.app, params)
86 assert any(file['content'] and len(file['content']) > max_file_bytes
87 for file in response.json['result'])
88
89 id_, params = build_data(
90 self.apikey, 'get_repo_nodes',
91 repoid=backend.repo_name, revision=commit_id,
92 root_path=path, details='full',
93 max_file_bytes=max_file_bytes)
94 response = api_call(self.app, params)
95 assert all(
96 file['content'] is None if file['size'] > max_file_bytes else True
97 for file in response.json['result'])
98
76 def test_api_get_repo_nodes_bad_ret_type(self, backend):
99 def test_api_get_repo_nodes_bad_ret_type(self, backend):
77 commit_id = 'tip'
100 commit_id = 'tip'
78 path = '/'
101 path = '/'
79 ret_type = 'error'
102 ret_type = 'error'
80 id_, params = build_data(
103 id_, params = build_data(
81 self.apikey, 'get_repo_nodes',
104 self.apikey, 'get_repo_nodes',
82 repoid=backend.repo_name, revision=commit_id,
105 repoid=backend.repo_name, revision=commit_id,
83 root_path=path,
106 root_path=path,
84 ret_type=ret_type)
107 ret_type=ret_type)
85 response = api_call(self.app, params)
108 response = api_call(self.app, params)
86
109
87 expected = ('ret_type must be one of %s'
110 expected = ('ret_type must be one of %s'
88 % (','.join(['all', 'dirs', 'files'])))
111 % (','.join(['all', 'dirs', 'files'])))
89 assert_error(id_, expected, given=response.body)
112 assert_error(id_, expected, given=response.body)
90
113
91 @pytest.mark.parametrize("name, ret_type, grant_perm", [
114 @pytest.mark.parametrize("name, ret_type, grant_perm", [
92 ('all', 'all', 'repository.write'),
115 ('all', 'all', 'repository.write'),
93 ('dirs', 'dirs', 'repository.admin'),
116 ('dirs', 'dirs', 'repository.admin'),
94 ('files', 'files', 'repository.read'),
117 ('files', 'files', 'repository.read'),
95 ])
118 ])
96 def test_api_get_repo_nodes_by_regular_user(
119 def test_api_get_repo_nodes_by_regular_user(
97 self, name, ret_type, grant_perm, backend):
120 self, name, ret_type, grant_perm, backend):
98 RepoModel().grant_user_permission(repo=backend.repo_name,
121 RepoModel().grant_user_permission(repo=backend.repo_name,
99 user=self.TEST_USER_LOGIN,
122 user=self.TEST_USER_LOGIN,
100 perm=grant_perm)
123 perm=grant_perm)
101 Session().commit()
124 Session().commit()
102
125
103 commit_id = 'tip'
126 commit_id = 'tip'
104 path = '/'
127 path = '/'
105 id_, params = build_data(
128 id_, params = build_data(
106 self.apikey_regular, 'get_repo_nodes',
129 self.apikey_regular, 'get_repo_nodes',
107 repoid=backend.repo_name, revision=commit_id,
130 repoid=backend.repo_name, revision=commit_id,
108 root_path=path,
131 root_path=path,
109 ret_type=ret_type)
132 ret_type=ret_type)
110 response = api_call(self.app, params)
133 response = api_call(self.app, params)
111
134
112 # we don't the actual return types here since it's tested somewhere
135 # we don't the actual return types here since it's tested somewhere
113 # else
136 # else
114 expected = response.json['result']
137 expected = response.json['result']
115 try:
138 try:
116 assert_ok(id_, expected, given=response.body)
139 assert_ok(id_, expected, given=response.body)
117 finally:
140 finally:
118 RepoModel().revoke_user_permission(
141 RepoModel().revoke_user_permission(
119 backend.repo_name, self.TEST_USER_LOGIN)
142 backend.repo_name, self.TEST_USER_LOGIN)
@@ -1,1882 +1,1886 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2016 RhodeCode GmbH
3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import time
22 import time
23
23
24 import colander
24 import colander
25
25
26 from rhodecode import BACKENDS
26 from rhodecode import BACKENDS
27 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, json
27 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, json
28 from rhodecode.api.utils import (
28 from rhodecode.api.utils import (
29 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
29 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
30 get_user_group_or_error, get_user_or_error, has_repo_permissions,
30 get_user_group_or_error, get_user_or_error, has_repo_permissions,
31 get_perm_or_error, store_update, get_repo_group_or_error, parse_args,
31 get_perm_or_error, store_update, get_repo_group_or_error, parse_args,
32 get_origin, build_commit_data)
32 get_origin, build_commit_data)
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
34 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
35 HasUserGroupPermissionAnyApi)
35 HasUserGroupPermissionAnyApi)
36 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
36 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
37 from rhodecode.lib.utils import map_groups
37 from rhodecode.lib.utils import map_groups
38 from rhodecode.lib.utils2 import str2bool, time_to_datetime
38 from rhodecode.lib.utils2 import str2bool, time_to_datetime
39 from rhodecode.model.changeset_status import ChangesetStatusModel
39 from rhodecode.model.changeset_status import ChangesetStatusModel
40 from rhodecode.model.comment import ChangesetCommentsModel
40 from rhodecode.model.comment import ChangesetCommentsModel
41 from rhodecode.model.db import (
41 from rhodecode.model.db import (
42 Session, ChangesetStatus, RepositoryField, Repository)
42 Session, ChangesetStatus, RepositoryField, Repository)
43 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo_group import RepoGroupModel
44 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.scm import ScmModel, RepoList
45 from rhodecode.model.scm import ScmModel, RepoList
46 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
46 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
47 from rhodecode.model.validation_schema import RepoSchema
47 from rhodecode.model.validation_schema import RepoSchema
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 @jsonrpc_method()
52 @jsonrpc_method()
53 def get_repo(request, apiuser, repoid, cache=Optional(True)):
53 def get_repo(request, apiuser, repoid, cache=Optional(True)):
54 """
54 """
55 Gets an existing repository by its name or repository_id.
55 Gets an existing repository by its name or repository_id.
56
56
57 The members section so the output returns users groups or users
57 The members section so the output returns users groups or users
58 associated with that repository.
58 associated with that repository.
59
59
60 This command can only be run using an |authtoken| with admin rights,
60 This command can only be run using an |authtoken| with admin rights,
61 or users with at least read rights to the |repo|.
61 or users with at least read rights to the |repo|.
62
62
63 :param apiuser: This is filled automatically from the |authtoken|.
63 :param apiuser: This is filled automatically from the |authtoken|.
64 :type apiuser: AuthUser
64 :type apiuser: AuthUser
65 :param repoid: The repository name or repository id.
65 :param repoid: The repository name or repository id.
66 :type repoid: str or int
66 :type repoid: str or int
67 :param cache: use the cached value for last changeset
67 :param cache: use the cached value for last changeset
68 :type: cache: Optional(bool)
68 :type: cache: Optional(bool)
69
69
70 Example output:
70 Example output:
71
71
72 .. code-block:: bash
72 .. code-block:: bash
73
73
74 {
74 {
75 "error": null,
75 "error": null,
76 "id": <repo_id>,
76 "id": <repo_id>,
77 "result": {
77 "result": {
78 "clone_uri": null,
78 "clone_uri": null,
79 "created_on": "timestamp",
79 "created_on": "timestamp",
80 "description": "repo description",
80 "description": "repo description",
81 "enable_downloads": false,
81 "enable_downloads": false,
82 "enable_locking": false,
82 "enable_locking": false,
83 "enable_statistics": false,
83 "enable_statistics": false,
84 "followers": [
84 "followers": [
85 {
85 {
86 "active": true,
86 "active": true,
87 "admin": false,
87 "admin": false,
88 "api_key": "****************************************",
88 "api_key": "****************************************",
89 "api_keys": [
89 "api_keys": [
90 "****************************************"
90 "****************************************"
91 ],
91 ],
92 "email": "user@example.com",
92 "email": "user@example.com",
93 "emails": [
93 "emails": [
94 "user@example.com"
94 "user@example.com"
95 ],
95 ],
96 "extern_name": "rhodecode",
96 "extern_name": "rhodecode",
97 "extern_type": "rhodecode",
97 "extern_type": "rhodecode",
98 "firstname": "username",
98 "firstname": "username",
99 "ip_addresses": [],
99 "ip_addresses": [],
100 "language": null,
100 "language": null,
101 "last_login": "2015-09-16T17:16:35.854",
101 "last_login": "2015-09-16T17:16:35.854",
102 "lastname": "surname",
102 "lastname": "surname",
103 "user_id": <user_id>,
103 "user_id": <user_id>,
104 "username": "name"
104 "username": "name"
105 }
105 }
106 ],
106 ],
107 "fork_of": "parent-repo",
107 "fork_of": "parent-repo",
108 "landing_rev": [
108 "landing_rev": [
109 "rev",
109 "rev",
110 "tip"
110 "tip"
111 ],
111 ],
112 "last_changeset": {
112 "last_changeset": {
113 "author": "User <user@example.com>",
113 "author": "User <user@example.com>",
114 "branch": "default",
114 "branch": "default",
115 "date": "timestamp",
115 "date": "timestamp",
116 "message": "last commit message",
116 "message": "last commit message",
117 "parents": [
117 "parents": [
118 {
118 {
119 "raw_id": "commit-id"
119 "raw_id": "commit-id"
120 }
120 }
121 ],
121 ],
122 "raw_id": "commit-id",
122 "raw_id": "commit-id",
123 "revision": <revision number>,
123 "revision": <revision number>,
124 "short_id": "short id"
124 "short_id": "short id"
125 },
125 },
126 "lock_reason": null,
126 "lock_reason": null,
127 "locked_by": null,
127 "locked_by": null,
128 "locked_date": null,
128 "locked_date": null,
129 "members": [
129 "members": [
130 {
130 {
131 "name": "super-admin-name",
131 "name": "super-admin-name",
132 "origin": "super-admin",
132 "origin": "super-admin",
133 "permission": "repository.admin",
133 "permission": "repository.admin",
134 "type": "user"
134 "type": "user"
135 },
135 },
136 {
136 {
137 "name": "owner-name",
137 "name": "owner-name",
138 "origin": "owner",
138 "origin": "owner",
139 "permission": "repository.admin",
139 "permission": "repository.admin",
140 "type": "user"
140 "type": "user"
141 },
141 },
142 {
142 {
143 "name": "user-group-name",
143 "name": "user-group-name",
144 "origin": "permission",
144 "origin": "permission",
145 "permission": "repository.write",
145 "permission": "repository.write",
146 "type": "user_group"
146 "type": "user_group"
147 }
147 }
148 ],
148 ],
149 "owner": "owner-name",
149 "owner": "owner-name",
150 "permissions": [
150 "permissions": [
151 {
151 {
152 "name": "super-admin-name",
152 "name": "super-admin-name",
153 "origin": "super-admin",
153 "origin": "super-admin",
154 "permission": "repository.admin",
154 "permission": "repository.admin",
155 "type": "user"
155 "type": "user"
156 },
156 },
157 {
157 {
158 "name": "owner-name",
158 "name": "owner-name",
159 "origin": "owner",
159 "origin": "owner",
160 "permission": "repository.admin",
160 "permission": "repository.admin",
161 "type": "user"
161 "type": "user"
162 },
162 },
163 {
163 {
164 "name": "user-group-name",
164 "name": "user-group-name",
165 "origin": "permission",
165 "origin": "permission",
166 "permission": "repository.write",
166 "permission": "repository.write",
167 "type": "user_group"
167 "type": "user_group"
168 }
168 }
169 ],
169 ],
170 "private": true,
170 "private": true,
171 "repo_id": 676,
171 "repo_id": 676,
172 "repo_name": "user-group/repo-name",
172 "repo_name": "user-group/repo-name",
173 "repo_type": "hg"
173 "repo_type": "hg"
174 }
174 }
175 }
175 }
176 """
176 """
177
177
178 repo = get_repo_or_error(repoid)
178 repo = get_repo_or_error(repoid)
179 cache = Optional.extract(cache)
179 cache = Optional.extract(cache)
180 include_secrets = False
180 include_secrets = False
181 if has_superadmin_permission(apiuser):
181 if has_superadmin_permission(apiuser):
182 include_secrets = True
182 include_secrets = True
183 else:
183 else:
184 # check if we have at least read permission for this repo !
184 # check if we have at least read permission for this repo !
185 _perms = (
185 _perms = (
186 'repository.admin', 'repository.write', 'repository.read',)
186 'repository.admin', 'repository.write', 'repository.read',)
187 has_repo_permissions(apiuser, repoid, repo, _perms)
187 has_repo_permissions(apiuser, repoid, repo, _perms)
188
188
189 permissions = []
189 permissions = []
190 for _user in repo.permissions():
190 for _user in repo.permissions():
191 user_data = {
191 user_data = {
192 'name': _user.username,
192 'name': _user.username,
193 'permission': _user.permission,
193 'permission': _user.permission,
194 'origin': get_origin(_user),
194 'origin': get_origin(_user),
195 'type': "user",
195 'type': "user",
196 }
196 }
197 permissions.append(user_data)
197 permissions.append(user_data)
198
198
199 for _user_group in repo.permission_user_groups():
199 for _user_group in repo.permission_user_groups():
200 user_group_data = {
200 user_group_data = {
201 'name': _user_group.users_group_name,
201 'name': _user_group.users_group_name,
202 'permission': _user_group.permission,
202 'permission': _user_group.permission,
203 'origin': get_origin(_user_group),
203 'origin': get_origin(_user_group),
204 'type': "user_group",
204 'type': "user_group",
205 }
205 }
206 permissions.append(user_group_data)
206 permissions.append(user_group_data)
207
207
208 following_users = [
208 following_users = [
209 user.user.get_api_data(include_secrets=include_secrets)
209 user.user.get_api_data(include_secrets=include_secrets)
210 for user in repo.followers]
210 for user in repo.followers]
211
211
212 if not cache:
212 if not cache:
213 repo.update_commit_cache()
213 repo.update_commit_cache()
214 data = repo.get_api_data(include_secrets=include_secrets)
214 data = repo.get_api_data(include_secrets=include_secrets)
215 data['members'] = permissions # TODO: this should be deprecated soon
215 data['members'] = permissions # TODO: this should be deprecated soon
216 data['permissions'] = permissions
216 data['permissions'] = permissions
217 data['followers'] = following_users
217 data['followers'] = following_users
218 return data
218 return data
219
219
220
220
221 @jsonrpc_method()
221 @jsonrpc_method()
222 def get_repos(request, apiuser):
222 def get_repos(request, apiuser):
223 """
223 """
224 Lists all existing repositories.
224 Lists all existing repositories.
225
225
226 This command can only be run using an |authtoken| with admin rights,
226 This command can only be run using an |authtoken| with admin rights,
227 or users with at least read rights to |repos|.
227 or users with at least read rights to |repos|.
228
228
229 :param apiuser: This is filled automatically from the |authtoken|.
229 :param apiuser: This is filled automatically from the |authtoken|.
230 :type apiuser: AuthUser
230 :type apiuser: AuthUser
231
231
232 Example output:
232 Example output:
233
233
234 .. code-block:: bash
234 .. code-block:: bash
235
235
236 id : <id_given_in_input>
236 id : <id_given_in_input>
237 result: [
237 result: [
238 {
238 {
239 "repo_id" : "<repo_id>",
239 "repo_id" : "<repo_id>",
240 "repo_name" : "<reponame>"
240 "repo_name" : "<reponame>"
241 "repo_type" : "<repo_type>",
241 "repo_type" : "<repo_type>",
242 "clone_uri" : "<clone_uri>",
242 "clone_uri" : "<clone_uri>",
243 "private": : "<bool>",
243 "private": : "<bool>",
244 "created_on" : "<datetimecreated>",
244 "created_on" : "<datetimecreated>",
245 "description" : "<description>",
245 "description" : "<description>",
246 "landing_rev": "<landing_rev>",
246 "landing_rev": "<landing_rev>",
247 "owner": "<repo_owner>",
247 "owner": "<repo_owner>",
248 "fork_of": "<name_of_fork_parent>",
248 "fork_of": "<name_of_fork_parent>",
249 "enable_downloads": "<bool>",
249 "enable_downloads": "<bool>",
250 "enable_locking": "<bool>",
250 "enable_locking": "<bool>",
251 "enable_statistics": "<bool>",
251 "enable_statistics": "<bool>",
252 },
252 },
253 ...
253 ...
254 ]
254 ]
255 error: null
255 error: null
256 """
256 """
257
257
258 include_secrets = has_superadmin_permission(apiuser)
258 include_secrets = has_superadmin_permission(apiuser)
259 _perms = ('repository.read', 'repository.write', 'repository.admin',)
259 _perms = ('repository.read', 'repository.write', 'repository.admin',)
260 extras = {'user': apiuser}
260 extras = {'user': apiuser}
261
261
262 repo_list = RepoList(
262 repo_list = RepoList(
263 RepoModel().get_all(), perm_set=_perms, extra_kwargs=extras)
263 RepoModel().get_all(), perm_set=_perms, extra_kwargs=extras)
264 return [repo.get_api_data(include_secrets=include_secrets)
264 return [repo.get_api_data(include_secrets=include_secrets)
265 for repo in repo_list]
265 for repo in repo_list]
266
266
267
267
268 @jsonrpc_method()
268 @jsonrpc_method()
269 def get_repo_changeset(request, apiuser, repoid, revision,
269 def get_repo_changeset(request, apiuser, repoid, revision,
270 details=Optional('basic')):
270 details=Optional('basic')):
271 """
271 """
272 Returns information about a changeset.
272 Returns information about a changeset.
273
273
274 Additionally parameters define the amount of details returned by
274 Additionally parameters define the amount of details returned by
275 this function.
275 this function.
276
276
277 This command can only be run using an |authtoken| with admin rights,
277 This command can only be run using an |authtoken| with admin rights,
278 or users with at least read rights to the |repo|.
278 or users with at least read rights to the |repo|.
279
279
280 :param apiuser: This is filled automatically from the |authtoken|.
280 :param apiuser: This is filled automatically from the |authtoken|.
281 :type apiuser: AuthUser
281 :type apiuser: AuthUser
282 :param repoid: The repository name or repository id
282 :param repoid: The repository name or repository id
283 :type repoid: str or int
283 :type repoid: str or int
284 :param revision: revision for which listing should be done
284 :param revision: revision for which listing should be done
285 :type revision: str
285 :type revision: str
286 :param details: details can be 'basic|extended|full' full gives diff
286 :param details: details can be 'basic|extended|full' full gives diff
287 info details like the diff itself, and number of changed files etc.
287 info details like the diff itself, and number of changed files etc.
288 :type details: Optional(str)
288 :type details: Optional(str)
289
289
290 """
290 """
291 repo = get_repo_or_error(repoid)
291 repo = get_repo_or_error(repoid)
292 if not has_superadmin_permission(apiuser):
292 if not has_superadmin_permission(apiuser):
293 _perms = (
293 _perms = (
294 'repository.admin', 'repository.write', 'repository.read',)
294 'repository.admin', 'repository.write', 'repository.read',)
295 has_repo_permissions(apiuser, repoid, repo, _perms)
295 has_repo_permissions(apiuser, repoid, repo, _perms)
296
296
297 changes_details = Optional.extract(details)
297 changes_details = Optional.extract(details)
298 _changes_details_types = ['basic', 'extended', 'full']
298 _changes_details_types = ['basic', 'extended', 'full']
299 if changes_details not in _changes_details_types:
299 if changes_details not in _changes_details_types:
300 raise JSONRPCError(
300 raise JSONRPCError(
301 'ret_type must be one of %s' % (
301 'ret_type must be one of %s' % (
302 ','.join(_changes_details_types)))
302 ','.join(_changes_details_types)))
303
303
304 pre_load = ['author', 'branch', 'date', 'message', 'parents',
304 pre_load = ['author', 'branch', 'date', 'message', 'parents',
305 'status', '_commit', '_file_paths']
305 'status', '_commit', '_file_paths']
306
306
307 try:
307 try:
308 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
308 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
309 except TypeError as e:
309 except TypeError as e:
310 raise JSONRPCError(e.message)
310 raise JSONRPCError(e.message)
311 _cs_json = cs.__json__()
311 _cs_json = cs.__json__()
312 _cs_json['diff'] = build_commit_data(cs, changes_details)
312 _cs_json['diff'] = build_commit_data(cs, changes_details)
313 if changes_details == 'full':
313 if changes_details == 'full':
314 _cs_json['refs'] = {
314 _cs_json['refs'] = {
315 'branches': [cs.branch],
315 'branches': [cs.branch],
316 'bookmarks': getattr(cs, 'bookmarks', []),
316 'bookmarks': getattr(cs, 'bookmarks', []),
317 'tags': cs.tags
317 'tags': cs.tags
318 }
318 }
319 return _cs_json
319 return _cs_json
320
320
321
321
322 @jsonrpc_method()
322 @jsonrpc_method()
323 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
323 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
324 details=Optional('basic')):
324 details=Optional('basic')):
325 """
325 """
326 Returns a set of commits limited by the number starting
326 Returns a set of commits limited by the number starting
327 from the `start_rev` option.
327 from the `start_rev` option.
328
328
329 Additional parameters define the amount of details returned by this
329 Additional parameters define the amount of details returned by this
330 function.
330 function.
331
331
332 This command can only be run using an |authtoken| with admin rights,
332 This command can only be run using an |authtoken| with admin rights,
333 or users with at least read rights to |repos|.
333 or users with at least read rights to |repos|.
334
334
335 :param apiuser: This is filled automatically from the |authtoken|.
335 :param apiuser: This is filled automatically from the |authtoken|.
336 :type apiuser: AuthUser
336 :type apiuser: AuthUser
337 :param repoid: The repository name or repository ID.
337 :param repoid: The repository name or repository ID.
338 :type repoid: str or int
338 :type repoid: str or int
339 :param start_rev: The starting revision from where to get changesets.
339 :param start_rev: The starting revision from where to get changesets.
340 :type start_rev: str
340 :type start_rev: str
341 :param limit: Limit the number of commits to this amount
341 :param limit: Limit the number of commits to this amount
342 :type limit: str or int
342 :type limit: str or int
343 :param details: Set the level of detail returned. Valid option are:
343 :param details: Set the level of detail returned. Valid option are:
344 ``basic``, ``extended`` and ``full``.
344 ``basic``, ``extended`` and ``full``.
345 :type details: Optional(str)
345 :type details: Optional(str)
346
346
347 .. note::
347 .. note::
348
348
349 Setting the parameter `details` to the value ``full`` is extensive
349 Setting the parameter `details` to the value ``full`` is extensive
350 and returns details like the diff itself, and the number
350 and returns details like the diff itself, and the number
351 of changed files.
351 of changed files.
352
352
353 """
353 """
354 repo = get_repo_or_error(repoid)
354 repo = get_repo_or_error(repoid)
355 if not has_superadmin_permission(apiuser):
355 if not has_superadmin_permission(apiuser):
356 _perms = (
356 _perms = (
357 'repository.admin', 'repository.write', 'repository.read',)
357 'repository.admin', 'repository.write', 'repository.read',)
358 has_repo_permissions(apiuser, repoid, repo, _perms)
358 has_repo_permissions(apiuser, repoid, repo, _perms)
359
359
360 changes_details = Optional.extract(details)
360 changes_details = Optional.extract(details)
361 _changes_details_types = ['basic', 'extended', 'full']
361 _changes_details_types = ['basic', 'extended', 'full']
362 if changes_details not in _changes_details_types:
362 if changes_details not in _changes_details_types:
363 raise JSONRPCError(
363 raise JSONRPCError(
364 'ret_type must be one of %s' % (
364 'ret_type must be one of %s' % (
365 ','.join(_changes_details_types)))
365 ','.join(_changes_details_types)))
366
366
367 limit = int(limit)
367 limit = int(limit)
368 pre_load = ['author', 'branch', 'date', 'message', 'parents',
368 pre_load = ['author', 'branch', 'date', 'message', 'parents',
369 'status', '_commit', '_file_paths']
369 'status', '_commit', '_file_paths']
370
370
371 vcs_repo = repo.scm_instance()
371 vcs_repo = repo.scm_instance()
372 # SVN needs a special case to distinguish its index and commit id
372 # SVN needs a special case to distinguish its index and commit id
373 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
373 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
374 start_rev = vcs_repo.commit_ids[0]
374 start_rev = vcs_repo.commit_ids[0]
375
375
376 try:
376 try:
377 commits = vcs_repo.get_commits(
377 commits = vcs_repo.get_commits(
378 start_id=start_rev, pre_load=pre_load)
378 start_id=start_rev, pre_load=pre_load)
379 except TypeError as e:
379 except TypeError as e:
380 raise JSONRPCError(e.message)
380 raise JSONRPCError(e.message)
381 except Exception:
381 except Exception:
382 log.exception('Fetching of commits failed')
382 log.exception('Fetching of commits failed')
383 raise JSONRPCError('Error occurred during commit fetching')
383 raise JSONRPCError('Error occurred during commit fetching')
384
384
385 ret = []
385 ret = []
386 for cnt, commit in enumerate(commits):
386 for cnt, commit in enumerate(commits):
387 if cnt >= limit != -1:
387 if cnt >= limit != -1:
388 break
388 break
389 _cs_json = commit.__json__()
389 _cs_json = commit.__json__()
390 _cs_json['diff'] = build_commit_data(commit, changes_details)
390 _cs_json['diff'] = build_commit_data(commit, changes_details)
391 if changes_details == 'full':
391 if changes_details == 'full':
392 _cs_json['refs'] = {
392 _cs_json['refs'] = {
393 'branches': [commit.branch],
393 'branches': [commit.branch],
394 'bookmarks': getattr(commit, 'bookmarks', []),
394 'bookmarks': getattr(commit, 'bookmarks', []),
395 'tags': commit.tags
395 'tags': commit.tags
396 }
396 }
397 ret.append(_cs_json)
397 ret.append(_cs_json)
398 return ret
398 return ret
399
399
400
400
401 @jsonrpc_method()
401 @jsonrpc_method()
402 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
402 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
403 ret_type=Optional('all'), details=Optional('basic')):
403 ret_type=Optional('all'), details=Optional('basic'),
404 max_file_bytes=Optional(None)):
404 """
405 """
405 Returns a list of nodes and children in a flat list for a given
406 Returns a list of nodes and children in a flat list for a given
406 path at given revision.
407 path at given revision.
407
408
408 It's possible to specify ret_type to show only `files` or `dirs`.
409 It's possible to specify ret_type to show only `files` or `dirs`.
409
410
410 This command can only be run using an |authtoken| with admin rights,
411 This command can only be run using an |authtoken| with admin rights,
411 or users with at least read rights to |repos|.
412 or users with at least read rights to |repos|.
412
413
413 :param apiuser: This is filled automatically from the |authtoken|.
414 :param apiuser: This is filled automatically from the |authtoken|.
414 :type apiuser: AuthUser
415 :type apiuser: AuthUser
415 :param repoid: The repository name or repository ID.
416 :param repoid: The repository name or repository ID.
416 :type repoid: str or int
417 :type repoid: str or int
417 :param revision: The revision for which listing should be done.
418 :param revision: The revision for which listing should be done.
418 :type revision: str
419 :type revision: str
419 :param root_path: The path from which to start displaying.
420 :param root_path: The path from which to start displaying.
420 :type root_path: str
421 :type root_path: str
421 :param ret_type: Set the return type. Valid options are
422 :param ret_type: Set the return type. Valid options are
422 ``all`` (default), ``files`` and ``dirs``.
423 ``all`` (default), ``files`` and ``dirs``.
423 :type ret_type: Optional(str)
424 :type ret_type: Optional(str)
424 :param details: Returns extended information about nodes, such as
425 :param details: Returns extended information about nodes, such as
425 md5, binary, and or content. The valid options are ``basic`` and
426 md5, binary, and or content. The valid options are ``basic`` and
426 ``full``.
427 ``full``.
427 :type details: Optional(str)
428 :type details: Optional(str)
429 :param max_file_bytes: Only return file content under this file size bytes
430 :type details: Optional(int)
428
431
429 Example output:
432 Example output:
430
433
431 .. code-block:: bash
434 .. code-block:: bash
432
435
433 id : <id_given_in_input>
436 id : <id_given_in_input>
434 result: [
437 result: [
435 {
438 {
436 "name" : "<name>"
439 "name" : "<name>"
437 "type" : "<type>",
440 "type" : "<type>",
438 "binary": "<true|false>" (only in extended mode)
441 "binary": "<true|false>" (only in extended mode)
439 "md5" : "<md5 of file content>" (only in extended mode)
442 "md5" : "<md5 of file content>" (only in extended mode)
440 },
443 },
441 ...
444 ...
442 ]
445 ]
443 error: null
446 error: null
444 """
447 """
445
448
446 repo = get_repo_or_error(repoid)
449 repo = get_repo_or_error(repoid)
447 if not has_superadmin_permission(apiuser):
450 if not has_superadmin_permission(apiuser):
448 _perms = (
451 _perms = (
449 'repository.admin', 'repository.write', 'repository.read',)
452 'repository.admin', 'repository.write', 'repository.read',)
450 has_repo_permissions(apiuser, repoid, repo, _perms)
453 has_repo_permissions(apiuser, repoid, repo, _perms)
451
454
452 ret_type = Optional.extract(ret_type)
455 ret_type = Optional.extract(ret_type)
453 details = Optional.extract(details)
456 details = Optional.extract(details)
454 _extended_types = ['basic', 'full']
457 _extended_types = ['basic', 'full']
455 if details not in _extended_types:
458 if details not in _extended_types:
456 raise JSONRPCError(
459 raise JSONRPCError(
457 'ret_type must be one of %s' % (','.join(_extended_types)))
460 'ret_type must be one of %s' % (','.join(_extended_types)))
458 extended_info = False
461 extended_info = False
459 content = False
462 content = False
460 if details == 'basic':
463 if details == 'basic':
461 extended_info = True
464 extended_info = True
462
465
463 if details == 'full':
466 if details == 'full':
464 extended_info = content = True
467 extended_info = content = True
465
468
466 _map = {}
469 _map = {}
467 try:
470 try:
468 # check if repo is not empty by any chance, skip quicker if it is.
471 # check if repo is not empty by any chance, skip quicker if it is.
469 _scm = repo.scm_instance()
472 _scm = repo.scm_instance()
470 if _scm.is_empty():
473 if _scm.is_empty():
471 return []
474 return []
472
475
473 _d, _f = ScmModel().get_nodes(
476 _d, _f = ScmModel().get_nodes(
474 repo, revision, root_path, flat=False,
477 repo, revision, root_path, flat=False,
475 extended_info=extended_info, content=content)
478 extended_info=extended_info, content=content,
479 max_file_bytes=max_file_bytes)
476 _map = {
480 _map = {
477 'all': _d + _f,
481 'all': _d + _f,
478 'files': _f,
482 'files': _f,
479 'dirs': _d,
483 'dirs': _d,
480 }
484 }
481 return _map[ret_type]
485 return _map[ret_type]
482 except KeyError:
486 except KeyError:
483 raise JSONRPCError(
487 raise JSONRPCError(
484 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
488 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
485 except Exception:
489 except Exception:
486 log.exception("Exception occurred while trying to get repo nodes")
490 log.exception("Exception occurred while trying to get repo nodes")
487 raise JSONRPCError(
491 raise JSONRPCError(
488 'failed to get repo: `%s` nodes' % repo.repo_name
492 'failed to get repo: `%s` nodes' % repo.repo_name
489 )
493 )
490
494
491
495
492 @jsonrpc_method()
496 @jsonrpc_method()
493 def get_repo_refs(request, apiuser, repoid):
497 def get_repo_refs(request, apiuser, repoid):
494 """
498 """
495 Returns a dictionary of current references. It returns
499 Returns a dictionary of current references. It returns
496 bookmarks, branches, closed_branches, and tags for given repository
500 bookmarks, branches, closed_branches, and tags for given repository
497
501
498 It's possible to specify ret_type to show only `files` or `dirs`.
502 It's possible to specify ret_type to show only `files` or `dirs`.
499
503
500 This command can only be run using an |authtoken| with admin rights,
504 This command can only be run using an |authtoken| with admin rights,
501 or users with at least read rights to |repos|.
505 or users with at least read rights to |repos|.
502
506
503 :param apiuser: This is filled automatically from the |authtoken|.
507 :param apiuser: This is filled automatically from the |authtoken|.
504 :type apiuser: AuthUser
508 :type apiuser: AuthUser
505 :param repoid: The repository name or repository ID.
509 :param repoid: The repository name or repository ID.
506 :type repoid: str or int
510 :type repoid: str or int
507
511
508 Example output:
512 Example output:
509
513
510 .. code-block:: bash
514 .. code-block:: bash
511
515
512 id : <id_given_in_input>
516 id : <id_given_in_input>
513 result: [
517 result: [
514 TODO...
518 TODO...
515 ]
519 ]
516 error: null
520 error: null
517 """
521 """
518
522
519 repo = get_repo_or_error(repoid)
523 repo = get_repo_or_error(repoid)
520 if not has_superadmin_permission(apiuser):
524 if not has_superadmin_permission(apiuser):
521 _perms = ('repository.admin', 'repository.write', 'repository.read',)
525 _perms = ('repository.admin', 'repository.write', 'repository.read',)
522 has_repo_permissions(apiuser, repoid, repo, _perms)
526 has_repo_permissions(apiuser, repoid, repo, _perms)
523
527
524 try:
528 try:
525 # check if repo is not empty by any chance, skip quicker if it is.
529 # check if repo is not empty by any chance, skip quicker if it is.
526 vcs_instance = repo.scm_instance()
530 vcs_instance = repo.scm_instance()
527 refs = vcs_instance.refs()
531 refs = vcs_instance.refs()
528 return refs
532 return refs
529 except Exception:
533 except Exception:
530 log.exception("Exception occurred while trying to get repo refs")
534 log.exception("Exception occurred while trying to get repo refs")
531 raise JSONRPCError(
535 raise JSONRPCError(
532 'failed to get repo: `%s` references' % repo.repo_name
536 'failed to get repo: `%s` references' % repo.repo_name
533 )
537 )
534
538
535
539
536 @jsonrpc_method()
540 @jsonrpc_method()
537 def create_repo(request, apiuser, repo_name, repo_type,
541 def create_repo(request, apiuser, repo_name, repo_type,
538 owner=Optional(OAttr('apiuser')), description=Optional(''),
542 owner=Optional(OAttr('apiuser')), description=Optional(''),
539 private=Optional(False), clone_uri=Optional(None),
543 private=Optional(False), clone_uri=Optional(None),
540 landing_rev=Optional('rev:tip'),
544 landing_rev=Optional('rev:tip'),
541 enable_statistics=Optional(False),
545 enable_statistics=Optional(False),
542 enable_locking=Optional(False),
546 enable_locking=Optional(False),
543 enable_downloads=Optional(False),
547 enable_downloads=Optional(False),
544 copy_permissions=Optional(False)):
548 copy_permissions=Optional(False)):
545 """
549 """
546 Creates a repository.
550 Creates a repository.
547
551
548 * If the repository name contains "/", all the required repository
552 * If the repository name contains "/", all the required repository
549 groups will be created.
553 groups will be created.
550
554
551 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
555 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
552 (with "foo" as parent). It will also create the "baz" repository
556 (with "foo" as parent). It will also create the "baz" repository
553 with "bar" as |repo| group.
557 with "bar" as |repo| group.
554
558
555 This command can only be run using an |authtoken| with at least
559 This command can only be run using an |authtoken| with at least
556 write permissions to the |repo|.
560 write permissions to the |repo|.
557
561
558 :param apiuser: This is filled automatically from the |authtoken|.
562 :param apiuser: This is filled automatically from the |authtoken|.
559 :type apiuser: AuthUser
563 :type apiuser: AuthUser
560 :param repo_name: Set the repository name.
564 :param repo_name: Set the repository name.
561 :type repo_name: str
565 :type repo_name: str
562 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
566 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
563 :type repo_type: str
567 :type repo_type: str
564 :param owner: user_id or username
568 :param owner: user_id or username
565 :type owner: Optional(str)
569 :type owner: Optional(str)
566 :param description: Set the repository description.
570 :param description: Set the repository description.
567 :type description: Optional(str)
571 :type description: Optional(str)
568 :param private:
572 :param private:
569 :type private: bool
573 :type private: bool
570 :param clone_uri:
574 :param clone_uri:
571 :type clone_uri: str
575 :type clone_uri: str
572 :param landing_rev: <rev_type>:<rev>
576 :param landing_rev: <rev_type>:<rev>
573 :type landing_rev: str
577 :type landing_rev: str
574 :param enable_locking:
578 :param enable_locking:
575 :type enable_locking: bool
579 :type enable_locking: bool
576 :param enable_downloads:
580 :param enable_downloads:
577 :type enable_downloads: bool
581 :type enable_downloads: bool
578 :param enable_statistics:
582 :param enable_statistics:
579 :type enable_statistics: bool
583 :type enable_statistics: bool
580 :param copy_permissions: Copy permission from group in which the
584 :param copy_permissions: Copy permission from group in which the
581 repository is being created.
585 repository is being created.
582 :type copy_permissions: bool
586 :type copy_permissions: bool
583
587
584
588
585 Example output:
589 Example output:
586
590
587 .. code-block:: bash
591 .. code-block:: bash
588
592
589 id : <id_given_in_input>
593 id : <id_given_in_input>
590 result: {
594 result: {
591 "msg": "Created new repository `<reponame>`",
595 "msg": "Created new repository `<reponame>`",
592 "success": true,
596 "success": true,
593 "task": "<celery task id or None if done sync>"
597 "task": "<celery task id or None if done sync>"
594 }
598 }
595 error: null
599 error: null
596
600
597
601
598 Example error output:
602 Example error output:
599
603
600 .. code-block:: bash
604 .. code-block:: bash
601
605
602 id : <id_given_in_input>
606 id : <id_given_in_input>
603 result : null
607 result : null
604 error : {
608 error : {
605 'failed to create repository `<repo_name>`
609 'failed to create repository `<repo_name>`
606 }
610 }
607
611
608 """
612 """
609 schema = RepoSchema()
613 schema = RepoSchema()
610 try:
614 try:
611 data = schema.deserialize({
615 data = schema.deserialize({
612 'repo_name': repo_name
616 'repo_name': repo_name
613 })
617 })
614 except colander.Invalid as e:
618 except colander.Invalid as e:
615 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
619 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
616 repo_name = data['repo_name']
620 repo_name = data['repo_name']
617
621
618 (repo_name_cleaned,
622 (repo_name_cleaned,
619 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
623 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
620 repo_name)
624 repo_name)
621
625
622 if not HasPermissionAnyApi(
626 if not HasPermissionAnyApi(
623 'hg.admin', 'hg.create.repository')(user=apiuser):
627 'hg.admin', 'hg.create.repository')(user=apiuser):
624 # check if we have admin permission for this repo group if given !
628 # check if we have admin permission for this repo group if given !
625
629
626 if parent_group_name:
630 if parent_group_name:
627 repogroupid = parent_group_name
631 repogroupid = parent_group_name
628 repo_group = get_repo_group_or_error(parent_group_name)
632 repo_group = get_repo_group_or_error(parent_group_name)
629
633
630 _perms = ('group.admin',)
634 _perms = ('group.admin',)
631 if not HasRepoGroupPermissionAnyApi(*_perms)(
635 if not HasRepoGroupPermissionAnyApi(*_perms)(
632 user=apiuser, group_name=repo_group.group_name):
636 user=apiuser, group_name=repo_group.group_name):
633 raise JSONRPCError(
637 raise JSONRPCError(
634 'repository group `%s` does not exist' % (
638 'repository group `%s` does not exist' % (
635 repogroupid,))
639 repogroupid,))
636 else:
640 else:
637 raise JSONRPCForbidden()
641 raise JSONRPCForbidden()
638
642
639 if not has_superadmin_permission(apiuser):
643 if not has_superadmin_permission(apiuser):
640 if not isinstance(owner, Optional):
644 if not isinstance(owner, Optional):
641 # forbid setting owner for non-admins
645 # forbid setting owner for non-admins
642 raise JSONRPCError(
646 raise JSONRPCError(
643 'Only RhodeCode admin can specify `owner` param')
647 'Only RhodeCode admin can specify `owner` param')
644
648
645 if isinstance(owner, Optional):
649 if isinstance(owner, Optional):
646 owner = apiuser.user_id
650 owner = apiuser.user_id
647
651
648 owner = get_user_or_error(owner)
652 owner = get_user_or_error(owner)
649
653
650 if RepoModel().get_by_repo_name(repo_name):
654 if RepoModel().get_by_repo_name(repo_name):
651 raise JSONRPCError("repo `%s` already exist" % repo_name)
655 raise JSONRPCError("repo `%s` already exist" % repo_name)
652
656
653 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
657 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
654 if isinstance(private, Optional):
658 if isinstance(private, Optional):
655 private = defs.get('repo_private') or Optional.extract(private)
659 private = defs.get('repo_private') or Optional.extract(private)
656 if isinstance(repo_type, Optional):
660 if isinstance(repo_type, Optional):
657 repo_type = defs.get('repo_type')
661 repo_type = defs.get('repo_type')
658 if isinstance(enable_statistics, Optional):
662 if isinstance(enable_statistics, Optional):
659 enable_statistics = defs.get('repo_enable_statistics')
663 enable_statistics = defs.get('repo_enable_statistics')
660 if isinstance(enable_locking, Optional):
664 if isinstance(enable_locking, Optional):
661 enable_locking = defs.get('repo_enable_locking')
665 enable_locking = defs.get('repo_enable_locking')
662 if isinstance(enable_downloads, Optional):
666 if isinstance(enable_downloads, Optional):
663 enable_downloads = defs.get('repo_enable_downloads')
667 enable_downloads = defs.get('repo_enable_downloads')
664
668
665 clone_uri = Optional.extract(clone_uri)
669 clone_uri = Optional.extract(clone_uri)
666 description = Optional.extract(description)
670 description = Optional.extract(description)
667 landing_rev = Optional.extract(landing_rev)
671 landing_rev = Optional.extract(landing_rev)
668 copy_permissions = Optional.extract(copy_permissions)
672 copy_permissions = Optional.extract(copy_permissions)
669
673
670 try:
674 try:
671 # create structure of groups and return the last group
675 # create structure of groups and return the last group
672 repo_group = map_groups(repo_name)
676 repo_group = map_groups(repo_name)
673 data = {
677 data = {
674 'repo_name': repo_name_cleaned,
678 'repo_name': repo_name_cleaned,
675 'repo_name_full': repo_name,
679 'repo_name_full': repo_name,
676 'repo_type': repo_type,
680 'repo_type': repo_type,
677 'repo_description': description,
681 'repo_description': description,
678 'owner': owner,
682 'owner': owner,
679 'repo_private': private,
683 'repo_private': private,
680 'clone_uri': clone_uri,
684 'clone_uri': clone_uri,
681 'repo_group': repo_group.group_id if repo_group else None,
685 'repo_group': repo_group.group_id if repo_group else None,
682 'repo_landing_rev': landing_rev,
686 'repo_landing_rev': landing_rev,
683 'enable_statistics': enable_statistics,
687 'enable_statistics': enable_statistics,
684 'enable_locking': enable_locking,
688 'enable_locking': enable_locking,
685 'enable_downloads': enable_downloads,
689 'enable_downloads': enable_downloads,
686 'repo_copy_permissions': copy_permissions,
690 'repo_copy_permissions': copy_permissions,
687 }
691 }
688
692
689 if repo_type not in BACKENDS.keys():
693 if repo_type not in BACKENDS.keys():
690 raise Exception("Invalid backend type %s" % repo_type)
694 raise Exception("Invalid backend type %s" % repo_type)
691 task = RepoModel().create(form_data=data, cur_user=owner)
695 task = RepoModel().create(form_data=data, cur_user=owner)
692 from celery.result import BaseAsyncResult
696 from celery.result import BaseAsyncResult
693 task_id = None
697 task_id = None
694 if isinstance(task, BaseAsyncResult):
698 if isinstance(task, BaseAsyncResult):
695 task_id = task.task_id
699 task_id = task.task_id
696 # no commit, it's done in RepoModel, or async via celery
700 # no commit, it's done in RepoModel, or async via celery
697 return {
701 return {
698 'msg': "Created new repository `%s`" % (repo_name,),
702 'msg': "Created new repository `%s`" % (repo_name,),
699 'success': True, # cannot return the repo data here since fork
703 'success': True, # cannot return the repo data here since fork
700 # cann be done async
704 # cann be done async
701 'task': task_id
705 'task': task_id
702 }
706 }
703 except Exception:
707 except Exception:
704 log.exception(
708 log.exception(
705 u"Exception while trying to create the repository %s",
709 u"Exception while trying to create the repository %s",
706 repo_name)
710 repo_name)
707 raise JSONRPCError(
711 raise JSONRPCError(
708 'failed to create repository `%s`' % (repo_name,))
712 'failed to create repository `%s`' % (repo_name,))
709
713
710
714
711 @jsonrpc_method()
715 @jsonrpc_method()
712 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
716 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
713 description=Optional('')):
717 description=Optional('')):
714 """
718 """
715 Adds an extra field to a repository.
719 Adds an extra field to a repository.
716
720
717 This command can only be run using an |authtoken| with at least
721 This command can only be run using an |authtoken| with at least
718 write permissions to the |repo|.
722 write permissions to the |repo|.
719
723
720 :param apiuser: This is filled automatically from the |authtoken|.
724 :param apiuser: This is filled automatically from the |authtoken|.
721 :type apiuser: AuthUser
725 :type apiuser: AuthUser
722 :param repoid: Set the repository name or repository id.
726 :param repoid: Set the repository name or repository id.
723 :type repoid: str or int
727 :type repoid: str or int
724 :param key: Create a unique field key for this repository.
728 :param key: Create a unique field key for this repository.
725 :type key: str
729 :type key: str
726 :param label:
730 :param label:
727 :type label: Optional(str)
731 :type label: Optional(str)
728 :param description:
732 :param description:
729 :type description: Optional(str)
733 :type description: Optional(str)
730 """
734 """
731 repo = get_repo_or_error(repoid)
735 repo = get_repo_or_error(repoid)
732 if not has_superadmin_permission(apiuser):
736 if not has_superadmin_permission(apiuser):
733 _perms = ('repository.admin',)
737 _perms = ('repository.admin',)
734 has_repo_permissions(apiuser, repoid, repo, _perms)
738 has_repo_permissions(apiuser, repoid, repo, _perms)
735
739
736 label = Optional.extract(label) or key
740 label = Optional.extract(label) or key
737 description = Optional.extract(description)
741 description = Optional.extract(description)
738
742
739 field = RepositoryField.get_by_key_name(key, repo)
743 field = RepositoryField.get_by_key_name(key, repo)
740 if field:
744 if field:
741 raise JSONRPCError('Field with key '
745 raise JSONRPCError('Field with key '
742 '`%s` exists for repo `%s`' % (key, repoid))
746 '`%s` exists for repo `%s`' % (key, repoid))
743
747
744 try:
748 try:
745 RepoModel().add_repo_field(repo, key, field_label=label,
749 RepoModel().add_repo_field(repo, key, field_label=label,
746 field_desc=description)
750 field_desc=description)
747 Session().commit()
751 Session().commit()
748 return {
752 return {
749 'msg': "Added new repository field `%s`" % (key,),
753 'msg': "Added new repository field `%s`" % (key,),
750 'success': True,
754 'success': True,
751 }
755 }
752 except Exception:
756 except Exception:
753 log.exception("Exception occurred while trying to add field to repo")
757 log.exception("Exception occurred while trying to add field to repo")
754 raise JSONRPCError(
758 raise JSONRPCError(
755 'failed to create new field for repository `%s`' % (repoid,))
759 'failed to create new field for repository `%s`' % (repoid,))
756
760
757
761
758 @jsonrpc_method()
762 @jsonrpc_method()
759 def remove_field_from_repo(request, apiuser, repoid, key):
763 def remove_field_from_repo(request, apiuser, repoid, key):
760 """
764 """
761 Removes an extra field from a repository.
765 Removes an extra field from a repository.
762
766
763 This command can only be run using an |authtoken| with at least
767 This command can only be run using an |authtoken| with at least
764 write permissions to the |repo|.
768 write permissions to the |repo|.
765
769
766 :param apiuser: This is filled automatically from the |authtoken|.
770 :param apiuser: This is filled automatically from the |authtoken|.
767 :type apiuser: AuthUser
771 :type apiuser: AuthUser
768 :param repoid: Set the repository name or repository ID.
772 :param repoid: Set the repository name or repository ID.
769 :type repoid: str or int
773 :type repoid: str or int
770 :param key: Set the unique field key for this repository.
774 :param key: Set the unique field key for this repository.
771 :type key: str
775 :type key: str
772 """
776 """
773
777
774 repo = get_repo_or_error(repoid)
778 repo = get_repo_or_error(repoid)
775 if not has_superadmin_permission(apiuser):
779 if not has_superadmin_permission(apiuser):
776 _perms = ('repository.admin',)
780 _perms = ('repository.admin',)
777 has_repo_permissions(apiuser, repoid, repo, _perms)
781 has_repo_permissions(apiuser, repoid, repo, _perms)
778
782
779 field = RepositoryField.get_by_key_name(key, repo)
783 field = RepositoryField.get_by_key_name(key, repo)
780 if not field:
784 if not field:
781 raise JSONRPCError('Field with key `%s` does not '
785 raise JSONRPCError('Field with key `%s` does not '
782 'exists for repo `%s`' % (key, repoid))
786 'exists for repo `%s`' % (key, repoid))
783
787
784 try:
788 try:
785 RepoModel().delete_repo_field(repo, field_key=key)
789 RepoModel().delete_repo_field(repo, field_key=key)
786 Session().commit()
790 Session().commit()
787 return {
791 return {
788 'msg': "Deleted repository field `%s`" % (key,),
792 'msg': "Deleted repository field `%s`" % (key,),
789 'success': True,
793 'success': True,
790 }
794 }
791 except Exception:
795 except Exception:
792 log.exception(
796 log.exception(
793 "Exception occurred while trying to delete field from repo")
797 "Exception occurred while trying to delete field from repo")
794 raise JSONRPCError(
798 raise JSONRPCError(
795 'failed to delete field for repository `%s`' % (repoid,))
799 'failed to delete field for repository `%s`' % (repoid,))
796
800
797
801
798 @jsonrpc_method()
802 @jsonrpc_method()
799 def update_repo(request, apiuser, repoid, name=Optional(None),
803 def update_repo(request, apiuser, repoid, name=Optional(None),
800 owner=Optional(OAttr('apiuser')),
804 owner=Optional(OAttr('apiuser')),
801 group=Optional(None),
805 group=Optional(None),
802 fork_of=Optional(None),
806 fork_of=Optional(None),
803 description=Optional(''), private=Optional(False),
807 description=Optional(''), private=Optional(False),
804 clone_uri=Optional(None), landing_rev=Optional('rev:tip'),
808 clone_uri=Optional(None), landing_rev=Optional('rev:tip'),
805 enable_statistics=Optional(False),
809 enable_statistics=Optional(False),
806 enable_locking=Optional(False),
810 enable_locking=Optional(False),
807 enable_downloads=Optional(False),
811 enable_downloads=Optional(False),
808 fields=Optional('')):
812 fields=Optional('')):
809 """
813 """
810 Updates a repository with the given information.
814 Updates a repository with the given information.
811
815
812 This command can only be run using an |authtoken| with at least
816 This command can only be run using an |authtoken| with at least
813 write permissions to the |repo|.
817 write permissions to the |repo|.
814
818
815 :param apiuser: This is filled automatically from the |authtoken|.
819 :param apiuser: This is filled automatically from the |authtoken|.
816 :type apiuser: AuthUser
820 :type apiuser: AuthUser
817 :param repoid: repository name or repository ID.
821 :param repoid: repository name or repository ID.
818 :type repoid: str or int
822 :type repoid: str or int
819 :param name: Update the |repo| name.
823 :param name: Update the |repo| name.
820 :type name: str
824 :type name: str
821 :param owner: Set the |repo| owner.
825 :param owner: Set the |repo| owner.
822 :type owner: str
826 :type owner: str
823 :param group: Set the |repo| group the |repo| belongs to.
827 :param group: Set the |repo| group the |repo| belongs to.
824 :type group: str
828 :type group: str
825 :param fork_of: Set the master |repo| name.
829 :param fork_of: Set the master |repo| name.
826 :type fork_of: str
830 :type fork_of: str
827 :param description: Update the |repo| description.
831 :param description: Update the |repo| description.
828 :type description: str
832 :type description: str
829 :param private: Set the |repo| as private. (True | False)
833 :param private: Set the |repo| as private. (True | False)
830 :type private: bool
834 :type private: bool
831 :param clone_uri: Update the |repo| clone URI.
835 :param clone_uri: Update the |repo| clone URI.
832 :type clone_uri: str
836 :type clone_uri: str
833 :param landing_rev: Set the |repo| landing revision. Default is
837 :param landing_rev: Set the |repo| landing revision. Default is
834 ``tip``.
838 ``tip``.
835 :type landing_rev: str
839 :type landing_rev: str
836 :param enable_statistics: Enable statistics on the |repo|,
840 :param enable_statistics: Enable statistics on the |repo|,
837 (True | False).
841 (True | False).
838 :type enable_statistics: bool
842 :type enable_statistics: bool
839 :param enable_locking: Enable |repo| locking.
843 :param enable_locking: Enable |repo| locking.
840 :type enable_locking: bool
844 :type enable_locking: bool
841 :param enable_downloads: Enable downloads from the |repo|,
845 :param enable_downloads: Enable downloads from the |repo|,
842 (True | False).
846 (True | False).
843 :type enable_downloads: bool
847 :type enable_downloads: bool
844 :param fields: Add extra fields to the |repo|. Use the following
848 :param fields: Add extra fields to the |repo|. Use the following
845 example format: ``field_key=field_val,field_key2=fieldval2``.
849 example format: ``field_key=field_val,field_key2=fieldval2``.
846 Escape ', ' with \,
850 Escape ', ' with \,
847 :type fields: str
851 :type fields: str
848 """
852 """
849 repo = get_repo_or_error(repoid)
853 repo = get_repo_or_error(repoid)
850 include_secrets = False
854 include_secrets = False
851 if has_superadmin_permission(apiuser):
855 if has_superadmin_permission(apiuser):
852 include_secrets = True
856 include_secrets = True
853 else:
857 else:
854 _perms = ('repository.admin',)
858 _perms = ('repository.admin',)
855 has_repo_permissions(apiuser, repoid, repo, _perms)
859 has_repo_permissions(apiuser, repoid, repo, _perms)
856
860
857 updates = {
861 updates = {
858 # update function requires this.
862 # update function requires this.
859 'repo_name': repo.just_name
863 'repo_name': repo.just_name
860 }
864 }
861 repo_group = group
865 repo_group = group
862 if not isinstance(repo_group, Optional):
866 if not isinstance(repo_group, Optional):
863 repo_group = get_repo_group_or_error(repo_group)
867 repo_group = get_repo_group_or_error(repo_group)
864 repo_group = repo_group.group_id
868 repo_group = repo_group.group_id
865
869
866 repo_fork_of = fork_of
870 repo_fork_of = fork_of
867 if not isinstance(repo_fork_of, Optional):
871 if not isinstance(repo_fork_of, Optional):
868 repo_fork_of = get_repo_or_error(repo_fork_of)
872 repo_fork_of = get_repo_or_error(repo_fork_of)
869 repo_fork_of = repo_fork_of.repo_id
873 repo_fork_of = repo_fork_of.repo_id
870
874
871 try:
875 try:
872 store_update(updates, name, 'repo_name')
876 store_update(updates, name, 'repo_name')
873 store_update(updates, repo_group, 'repo_group')
877 store_update(updates, repo_group, 'repo_group')
874 store_update(updates, repo_fork_of, 'fork_id')
878 store_update(updates, repo_fork_of, 'fork_id')
875 store_update(updates, owner, 'user')
879 store_update(updates, owner, 'user')
876 store_update(updates, description, 'repo_description')
880 store_update(updates, description, 'repo_description')
877 store_update(updates, private, 'repo_private')
881 store_update(updates, private, 'repo_private')
878 store_update(updates, clone_uri, 'clone_uri')
882 store_update(updates, clone_uri, 'clone_uri')
879 store_update(updates, landing_rev, 'repo_landing_rev')
883 store_update(updates, landing_rev, 'repo_landing_rev')
880 store_update(updates, enable_statistics, 'repo_enable_statistics')
884 store_update(updates, enable_statistics, 'repo_enable_statistics')
881 store_update(updates, enable_locking, 'repo_enable_locking')
885 store_update(updates, enable_locking, 'repo_enable_locking')
882 store_update(updates, enable_downloads, 'repo_enable_downloads')
886 store_update(updates, enable_downloads, 'repo_enable_downloads')
883
887
884 # extra fields
888 # extra fields
885 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
889 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
886 if fields:
890 if fields:
887 updates.update(fields)
891 updates.update(fields)
888
892
889 RepoModel().update(repo, **updates)
893 RepoModel().update(repo, **updates)
890 Session().commit()
894 Session().commit()
891 return {
895 return {
892 'msg': 'updated repo ID:%s %s' % (
896 'msg': 'updated repo ID:%s %s' % (
893 repo.repo_id, repo.repo_name),
897 repo.repo_id, repo.repo_name),
894 'repository': repo.get_api_data(
898 'repository': repo.get_api_data(
895 include_secrets=include_secrets)
899 include_secrets=include_secrets)
896 }
900 }
897 except Exception:
901 except Exception:
898 log.exception(
902 log.exception(
899 u"Exception while trying to update the repository %s",
903 u"Exception while trying to update the repository %s",
900 repoid)
904 repoid)
901 raise JSONRPCError('failed to update repo `%s`' % repoid)
905 raise JSONRPCError('failed to update repo `%s`' % repoid)
902
906
903
907
904 @jsonrpc_method()
908 @jsonrpc_method()
905 def fork_repo(request, apiuser, repoid, fork_name,
909 def fork_repo(request, apiuser, repoid, fork_name,
906 owner=Optional(OAttr('apiuser')),
910 owner=Optional(OAttr('apiuser')),
907 description=Optional(''), copy_permissions=Optional(False),
911 description=Optional(''), copy_permissions=Optional(False),
908 private=Optional(False), landing_rev=Optional('rev:tip')):
912 private=Optional(False), landing_rev=Optional('rev:tip')):
909 """
913 """
910 Creates a fork of the specified |repo|.
914 Creates a fork of the specified |repo|.
911
915
912 * If using |RCE| with Celery this will immediately return a success
916 * If using |RCE| with Celery this will immediately return a success
913 message, even though the fork will be created asynchronously.
917 message, even though the fork will be created asynchronously.
914
918
915 This command can only be run using an |authtoken| with fork
919 This command can only be run using an |authtoken| with fork
916 permissions on the |repo|.
920 permissions on the |repo|.
917
921
918 :param apiuser: This is filled automatically from the |authtoken|.
922 :param apiuser: This is filled automatically from the |authtoken|.
919 :type apiuser: AuthUser
923 :type apiuser: AuthUser
920 :param repoid: Set repository name or repository ID.
924 :param repoid: Set repository name or repository ID.
921 :type repoid: str or int
925 :type repoid: str or int
922 :param fork_name: Set the fork name.
926 :param fork_name: Set the fork name.
923 :type fork_name: str
927 :type fork_name: str
924 :param owner: Set the fork owner.
928 :param owner: Set the fork owner.
925 :type owner: str
929 :type owner: str
926 :param description: Set the fork descripton.
930 :param description: Set the fork descripton.
927 :type description: str
931 :type description: str
928 :param copy_permissions: Copy permissions from parent |repo|. The
932 :param copy_permissions: Copy permissions from parent |repo|. The
929 default is False.
933 default is False.
930 :type copy_permissions: bool
934 :type copy_permissions: bool
931 :param private: Make the fork private. The default is False.
935 :param private: Make the fork private. The default is False.
932 :type private: bool
936 :type private: bool
933 :param landing_rev: Set the landing revision. The default is tip.
937 :param landing_rev: Set the landing revision. The default is tip.
934
938
935 Example output:
939 Example output:
936
940
937 .. code-block:: bash
941 .. code-block:: bash
938
942
939 id : <id_for_response>
943 id : <id_for_response>
940 api_key : "<api_key>"
944 api_key : "<api_key>"
941 args: {
945 args: {
942 "repoid" : "<reponame or repo_id>",
946 "repoid" : "<reponame or repo_id>",
943 "fork_name": "<forkname>",
947 "fork_name": "<forkname>",
944 "owner": "<username or user_id = Optional(=apiuser)>",
948 "owner": "<username or user_id = Optional(=apiuser)>",
945 "description": "<description>",
949 "description": "<description>",
946 "copy_permissions": "<bool>",
950 "copy_permissions": "<bool>",
947 "private": "<bool>",
951 "private": "<bool>",
948 "landing_rev": "<landing_rev>"
952 "landing_rev": "<landing_rev>"
949 }
953 }
950
954
951 Example error output:
955 Example error output:
952
956
953 .. code-block:: bash
957 .. code-block:: bash
954
958
955 id : <id_given_in_input>
959 id : <id_given_in_input>
956 result: {
960 result: {
957 "msg": "Created fork of `<reponame>` as `<forkname>`",
961 "msg": "Created fork of `<reponame>` as `<forkname>`",
958 "success": true,
962 "success": true,
959 "task": "<celery task id or None if done sync>"
963 "task": "<celery task id or None if done sync>"
960 }
964 }
961 error: null
965 error: null
962
966
963 """
967 """
964 if not has_superadmin_permission(apiuser):
968 if not has_superadmin_permission(apiuser):
965 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
969 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
966 raise JSONRPCForbidden()
970 raise JSONRPCForbidden()
967
971
968 repo = get_repo_or_error(repoid)
972 repo = get_repo_or_error(repoid)
969 repo_name = repo.repo_name
973 repo_name = repo.repo_name
970
974
971 (fork_name_cleaned,
975 (fork_name_cleaned,
972 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
976 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
973 fork_name)
977 fork_name)
974
978
975 if not has_superadmin_permission(apiuser):
979 if not has_superadmin_permission(apiuser):
976 # check if we have at least read permission for
980 # check if we have at least read permission for
977 # this repo that we fork !
981 # this repo that we fork !
978 _perms = (
982 _perms = (
979 'repository.admin', 'repository.write', 'repository.read')
983 'repository.admin', 'repository.write', 'repository.read')
980 has_repo_permissions(apiuser, repoid, repo, _perms)
984 has_repo_permissions(apiuser, repoid, repo, _perms)
981
985
982 if not isinstance(owner, Optional):
986 if not isinstance(owner, Optional):
983 # forbid setting owner for non super admins
987 # forbid setting owner for non super admins
984 raise JSONRPCError(
988 raise JSONRPCError(
985 'Only RhodeCode admin can specify `owner` param'
989 'Only RhodeCode admin can specify `owner` param'
986 )
990 )
987 # check if we have a create.repo permission if not maybe the parent
991 # check if we have a create.repo permission if not maybe the parent
988 # group permission
992 # group permission
989 if not HasPermissionAnyApi('hg.create.repository')(user=apiuser):
993 if not HasPermissionAnyApi('hg.create.repository')(user=apiuser):
990 if parent_group_name:
994 if parent_group_name:
991 repogroupid = parent_group_name
995 repogroupid = parent_group_name
992 repo_group = get_repo_group_or_error(parent_group_name)
996 repo_group = get_repo_group_or_error(parent_group_name)
993
997
994 _perms = ('group.admin',)
998 _perms = ('group.admin',)
995 if not HasRepoGroupPermissionAnyApi(*_perms)(
999 if not HasRepoGroupPermissionAnyApi(*_perms)(
996 user=apiuser, group_name=repo_group.group_name):
1000 user=apiuser, group_name=repo_group.group_name):
997 raise JSONRPCError(
1001 raise JSONRPCError(
998 'repository group `%s` does not exist' % (
1002 'repository group `%s` does not exist' % (
999 repogroupid,))
1003 repogroupid,))
1000 else:
1004 else:
1001 raise JSONRPCForbidden()
1005 raise JSONRPCForbidden()
1002
1006
1003 _repo = RepoModel().get_by_repo_name(fork_name)
1007 _repo = RepoModel().get_by_repo_name(fork_name)
1004 if _repo:
1008 if _repo:
1005 type_ = 'fork' if _repo.fork else 'repo'
1009 type_ = 'fork' if _repo.fork else 'repo'
1006 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1010 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1007
1011
1008 if isinstance(owner, Optional):
1012 if isinstance(owner, Optional):
1009 owner = apiuser.user_id
1013 owner = apiuser.user_id
1010
1014
1011 owner = get_user_or_error(owner)
1015 owner = get_user_or_error(owner)
1012
1016
1013 try:
1017 try:
1014 # create structure of groups and return the last group
1018 # create structure of groups and return the last group
1015 repo_group = map_groups(fork_name)
1019 repo_group = map_groups(fork_name)
1016 form_data = {
1020 form_data = {
1017 'repo_name': fork_name_cleaned,
1021 'repo_name': fork_name_cleaned,
1018 'repo_name_full': fork_name,
1022 'repo_name_full': fork_name,
1019 'repo_group': repo_group.group_id if repo_group else None,
1023 'repo_group': repo_group.group_id if repo_group else None,
1020 'repo_type': repo.repo_type,
1024 'repo_type': repo.repo_type,
1021 'description': Optional.extract(description),
1025 'description': Optional.extract(description),
1022 'private': Optional.extract(private),
1026 'private': Optional.extract(private),
1023 'copy_permissions': Optional.extract(copy_permissions),
1027 'copy_permissions': Optional.extract(copy_permissions),
1024 'landing_rev': Optional.extract(landing_rev),
1028 'landing_rev': Optional.extract(landing_rev),
1025 'fork_parent_id': repo.repo_id,
1029 'fork_parent_id': repo.repo_id,
1026 }
1030 }
1027
1031
1028 task = RepoModel().create_fork(form_data, cur_user=owner)
1032 task = RepoModel().create_fork(form_data, cur_user=owner)
1029 # no commit, it's done in RepoModel, or async via celery
1033 # no commit, it's done in RepoModel, or async via celery
1030 from celery.result import BaseAsyncResult
1034 from celery.result import BaseAsyncResult
1031 task_id = None
1035 task_id = None
1032 if isinstance(task, BaseAsyncResult):
1036 if isinstance(task, BaseAsyncResult):
1033 task_id = task.task_id
1037 task_id = task.task_id
1034 return {
1038 return {
1035 'msg': 'Created fork of `%s` as `%s`' % (
1039 'msg': 'Created fork of `%s` as `%s`' % (
1036 repo.repo_name, fork_name),
1040 repo.repo_name, fork_name),
1037 'success': True, # cannot return the repo data here since fork
1041 'success': True, # cannot return the repo data here since fork
1038 # can be done async
1042 # can be done async
1039 'task': task_id
1043 'task': task_id
1040 }
1044 }
1041 except Exception:
1045 except Exception:
1042 log.exception("Exception occurred while trying to fork a repo")
1046 log.exception("Exception occurred while trying to fork a repo")
1043 raise JSONRPCError(
1047 raise JSONRPCError(
1044 'failed to fork repository `%s` as `%s`' % (
1048 'failed to fork repository `%s` as `%s`' % (
1045 repo_name, fork_name))
1049 repo_name, fork_name))
1046
1050
1047
1051
1048 @jsonrpc_method()
1052 @jsonrpc_method()
1049 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1053 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1050 """
1054 """
1051 Deletes a repository.
1055 Deletes a repository.
1052
1056
1053 * When the `forks` parameter is set it's possible to detach or delete
1057 * When the `forks` parameter is set it's possible to detach or delete
1054 forks of deleted repository.
1058 forks of deleted repository.
1055
1059
1056 This command can only be run using an |authtoken| with admin
1060 This command can only be run using an |authtoken| with admin
1057 permissions on the |repo|.
1061 permissions on the |repo|.
1058
1062
1059 :param apiuser: This is filled automatically from the |authtoken|.
1063 :param apiuser: This is filled automatically from the |authtoken|.
1060 :type apiuser: AuthUser
1064 :type apiuser: AuthUser
1061 :param repoid: Set the repository name or repository ID.
1065 :param repoid: Set the repository name or repository ID.
1062 :type repoid: str or int
1066 :type repoid: str or int
1063 :param forks: Set to `detach` or `delete` forks from the |repo|.
1067 :param forks: Set to `detach` or `delete` forks from the |repo|.
1064 :type forks: Optional(str)
1068 :type forks: Optional(str)
1065
1069
1066 Example error output:
1070 Example error output:
1067
1071
1068 .. code-block:: bash
1072 .. code-block:: bash
1069
1073
1070 id : <id_given_in_input>
1074 id : <id_given_in_input>
1071 result: {
1075 result: {
1072 "msg": "Deleted repository `<reponame>`",
1076 "msg": "Deleted repository `<reponame>`",
1073 "success": true
1077 "success": true
1074 }
1078 }
1075 error: null
1079 error: null
1076 """
1080 """
1077
1081
1078 repo = get_repo_or_error(repoid)
1082 repo = get_repo_or_error(repoid)
1079 if not has_superadmin_permission(apiuser):
1083 if not has_superadmin_permission(apiuser):
1080 _perms = ('repository.admin',)
1084 _perms = ('repository.admin',)
1081 has_repo_permissions(apiuser, repoid, repo, _perms)
1085 has_repo_permissions(apiuser, repoid, repo, _perms)
1082
1086
1083 try:
1087 try:
1084 handle_forks = Optional.extract(forks)
1088 handle_forks = Optional.extract(forks)
1085 _forks_msg = ''
1089 _forks_msg = ''
1086 _forks = [f for f in repo.forks]
1090 _forks = [f for f in repo.forks]
1087 if handle_forks == 'detach':
1091 if handle_forks == 'detach':
1088 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1092 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1089 elif handle_forks == 'delete':
1093 elif handle_forks == 'delete':
1090 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1094 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1091 elif _forks:
1095 elif _forks:
1092 raise JSONRPCError(
1096 raise JSONRPCError(
1093 'Cannot delete `%s` it still contains attached forks' %
1097 'Cannot delete `%s` it still contains attached forks' %
1094 (repo.repo_name,)
1098 (repo.repo_name,)
1095 )
1099 )
1096
1100
1097 RepoModel().delete(repo, forks=forks)
1101 RepoModel().delete(repo, forks=forks)
1098 Session().commit()
1102 Session().commit()
1099 return {
1103 return {
1100 'msg': 'Deleted repository `%s`%s' % (
1104 'msg': 'Deleted repository `%s`%s' % (
1101 repo.repo_name, _forks_msg),
1105 repo.repo_name, _forks_msg),
1102 'success': True
1106 'success': True
1103 }
1107 }
1104 except Exception:
1108 except Exception:
1105 log.exception("Exception occurred while trying to delete repo")
1109 log.exception("Exception occurred while trying to delete repo")
1106 raise JSONRPCError(
1110 raise JSONRPCError(
1107 'failed to delete repository `%s`' % (repo.repo_name,)
1111 'failed to delete repository `%s`' % (repo.repo_name,)
1108 )
1112 )
1109
1113
1110
1114
1111 #TODO: marcink, change name ?
1115 #TODO: marcink, change name ?
1112 @jsonrpc_method()
1116 @jsonrpc_method()
1113 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1117 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1114 """
1118 """
1115 Invalidates the cache for the specified repository.
1119 Invalidates the cache for the specified repository.
1116
1120
1117 This command can only be run using an |authtoken| with admin rights to
1121 This command can only be run using an |authtoken| with admin rights to
1118 the specified repository.
1122 the specified repository.
1119
1123
1120 This command takes the following options:
1124 This command takes the following options:
1121
1125
1122 :param apiuser: This is filled automatically from |authtoken|.
1126 :param apiuser: This is filled automatically from |authtoken|.
1123 :type apiuser: AuthUser
1127 :type apiuser: AuthUser
1124 :param repoid: Sets the repository name or repository ID.
1128 :param repoid: Sets the repository name or repository ID.
1125 :type repoid: str or int
1129 :type repoid: str or int
1126 :param delete_keys: This deletes the invalidated keys instead of
1130 :param delete_keys: This deletes the invalidated keys instead of
1127 just flagging them.
1131 just flagging them.
1128 :type delete_keys: Optional(``True`` | ``False``)
1132 :type delete_keys: Optional(``True`` | ``False``)
1129
1133
1130 Example output:
1134 Example output:
1131
1135
1132 .. code-block:: bash
1136 .. code-block:: bash
1133
1137
1134 id : <id_given_in_input>
1138 id : <id_given_in_input>
1135 result : {
1139 result : {
1136 'msg': Cache for repository `<repository name>` was invalidated,
1140 'msg': Cache for repository `<repository name>` was invalidated,
1137 'repository': <repository name>
1141 'repository': <repository name>
1138 }
1142 }
1139 error : null
1143 error : null
1140
1144
1141 Example error output:
1145 Example error output:
1142
1146
1143 .. code-block:: bash
1147 .. code-block:: bash
1144
1148
1145 id : <id_given_in_input>
1149 id : <id_given_in_input>
1146 result : null
1150 result : null
1147 error : {
1151 error : {
1148 'Error occurred during cache invalidation action'
1152 'Error occurred during cache invalidation action'
1149 }
1153 }
1150
1154
1151 """
1155 """
1152
1156
1153 repo = get_repo_or_error(repoid)
1157 repo = get_repo_or_error(repoid)
1154 if not has_superadmin_permission(apiuser):
1158 if not has_superadmin_permission(apiuser):
1155 _perms = ('repository.admin', 'repository.write',)
1159 _perms = ('repository.admin', 'repository.write',)
1156 has_repo_permissions(apiuser, repoid, repo, _perms)
1160 has_repo_permissions(apiuser, repoid, repo, _perms)
1157
1161
1158 delete = Optional.extract(delete_keys)
1162 delete = Optional.extract(delete_keys)
1159 try:
1163 try:
1160 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1164 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1161 return {
1165 return {
1162 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1166 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1163 'repository': repo.repo_name
1167 'repository': repo.repo_name
1164 }
1168 }
1165 except Exception:
1169 except Exception:
1166 log.exception(
1170 log.exception(
1167 "Exception occurred while trying to invalidate repo cache")
1171 "Exception occurred while trying to invalidate repo cache")
1168 raise JSONRPCError(
1172 raise JSONRPCError(
1169 'Error occurred during cache invalidation action'
1173 'Error occurred during cache invalidation action'
1170 )
1174 )
1171
1175
1172
1176
1173 #TODO: marcink, change name ?
1177 #TODO: marcink, change name ?
1174 @jsonrpc_method()
1178 @jsonrpc_method()
1175 def lock(request, apiuser, repoid, locked=Optional(None),
1179 def lock(request, apiuser, repoid, locked=Optional(None),
1176 userid=Optional(OAttr('apiuser'))):
1180 userid=Optional(OAttr('apiuser'))):
1177 """
1181 """
1178 Sets the lock state of the specified |repo| by the given user.
1182 Sets the lock state of the specified |repo| by the given user.
1179 From more information, see :ref:`repo-locking`.
1183 From more information, see :ref:`repo-locking`.
1180
1184
1181 * If the ``userid`` option is not set, the repository is locked to the
1185 * If the ``userid`` option is not set, the repository is locked to the
1182 user who called the method.
1186 user who called the method.
1183 * If the ``locked`` parameter is not set, the current lock state of the
1187 * If the ``locked`` parameter is not set, the current lock state of the
1184 repository is displayed.
1188 repository is displayed.
1185
1189
1186 This command can only be run using an |authtoken| with admin rights to
1190 This command can only be run using an |authtoken| with admin rights to
1187 the specified repository.
1191 the specified repository.
1188
1192
1189 This command takes the following options:
1193 This command takes the following options:
1190
1194
1191 :param apiuser: This is filled automatically from the |authtoken|.
1195 :param apiuser: This is filled automatically from the |authtoken|.
1192 :type apiuser: AuthUser
1196 :type apiuser: AuthUser
1193 :param repoid: Sets the repository name or repository ID.
1197 :param repoid: Sets the repository name or repository ID.
1194 :type repoid: str or int
1198 :type repoid: str or int
1195 :param locked: Sets the lock state.
1199 :param locked: Sets the lock state.
1196 :type locked: Optional(``True`` | ``False``)
1200 :type locked: Optional(``True`` | ``False``)
1197 :param userid: Set the repository lock to this user.
1201 :param userid: Set the repository lock to this user.
1198 :type userid: Optional(str or int)
1202 :type userid: Optional(str or int)
1199
1203
1200 Example error output:
1204 Example error output:
1201
1205
1202 .. code-block:: bash
1206 .. code-block:: bash
1203
1207
1204 id : <id_given_in_input>
1208 id : <id_given_in_input>
1205 result : {
1209 result : {
1206 'repo': '<reponame>',
1210 'repo': '<reponame>',
1207 'locked': <bool: lock state>,
1211 'locked': <bool: lock state>,
1208 'locked_since': <int: lock timestamp>,
1212 'locked_since': <int: lock timestamp>,
1209 'locked_by': <username of person who made the lock>,
1213 'locked_by': <username of person who made the lock>,
1210 'lock_reason': <str: reason for locking>,
1214 'lock_reason': <str: reason for locking>,
1211 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1215 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1212 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1216 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1213 or
1217 or
1214 'msg': 'Repo `<repository name>` not locked.'
1218 'msg': 'Repo `<repository name>` not locked.'
1215 or
1219 or
1216 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1220 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1217 }
1221 }
1218 error : null
1222 error : null
1219
1223
1220 Example error output:
1224 Example error output:
1221
1225
1222 .. code-block:: bash
1226 .. code-block:: bash
1223
1227
1224 id : <id_given_in_input>
1228 id : <id_given_in_input>
1225 result : null
1229 result : null
1226 error : {
1230 error : {
1227 'Error occurred locking repository `<reponame>`
1231 'Error occurred locking repository `<reponame>`
1228 }
1232 }
1229 """
1233 """
1230
1234
1231 repo = get_repo_or_error(repoid)
1235 repo = get_repo_or_error(repoid)
1232 if not has_superadmin_permission(apiuser):
1236 if not has_superadmin_permission(apiuser):
1233 # check if we have at least write permission for this repo !
1237 # check if we have at least write permission for this repo !
1234 _perms = ('repository.admin', 'repository.write',)
1238 _perms = ('repository.admin', 'repository.write',)
1235 has_repo_permissions(apiuser, repoid, repo, _perms)
1239 has_repo_permissions(apiuser, repoid, repo, _perms)
1236
1240
1237 # make sure normal user does not pass someone else userid,
1241 # make sure normal user does not pass someone else userid,
1238 # he is not allowed to do that
1242 # he is not allowed to do that
1239 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1243 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1240 raise JSONRPCError('userid is not the same as your user')
1244 raise JSONRPCError('userid is not the same as your user')
1241
1245
1242 if isinstance(userid, Optional):
1246 if isinstance(userid, Optional):
1243 userid = apiuser.user_id
1247 userid = apiuser.user_id
1244
1248
1245 user = get_user_or_error(userid)
1249 user = get_user_or_error(userid)
1246
1250
1247 if isinstance(locked, Optional):
1251 if isinstance(locked, Optional):
1248 lockobj = repo.locked
1252 lockobj = repo.locked
1249
1253
1250 if lockobj[0] is None:
1254 if lockobj[0] is None:
1251 _d = {
1255 _d = {
1252 'repo': repo.repo_name,
1256 'repo': repo.repo_name,
1253 'locked': False,
1257 'locked': False,
1254 'locked_since': None,
1258 'locked_since': None,
1255 'locked_by': None,
1259 'locked_by': None,
1256 'lock_reason': None,
1260 'lock_reason': None,
1257 'lock_state_changed': False,
1261 'lock_state_changed': False,
1258 'msg': 'Repo `%s` not locked.' % repo.repo_name
1262 'msg': 'Repo `%s` not locked.' % repo.repo_name
1259 }
1263 }
1260 return _d
1264 return _d
1261 else:
1265 else:
1262 _user_id, _time, _reason = lockobj
1266 _user_id, _time, _reason = lockobj
1263 lock_user = get_user_or_error(userid)
1267 lock_user = get_user_or_error(userid)
1264 _d = {
1268 _d = {
1265 'repo': repo.repo_name,
1269 'repo': repo.repo_name,
1266 'locked': True,
1270 'locked': True,
1267 'locked_since': _time,
1271 'locked_since': _time,
1268 'locked_by': lock_user.username,
1272 'locked_by': lock_user.username,
1269 'lock_reason': _reason,
1273 'lock_reason': _reason,
1270 'lock_state_changed': False,
1274 'lock_state_changed': False,
1271 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1275 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1272 % (repo.repo_name, lock_user.username,
1276 % (repo.repo_name, lock_user.username,
1273 json.dumps(time_to_datetime(_time))))
1277 json.dumps(time_to_datetime(_time))))
1274 }
1278 }
1275 return _d
1279 return _d
1276
1280
1277 # force locked state through a flag
1281 # force locked state through a flag
1278 else:
1282 else:
1279 locked = str2bool(locked)
1283 locked = str2bool(locked)
1280 lock_reason = Repository.LOCK_API
1284 lock_reason = Repository.LOCK_API
1281 try:
1285 try:
1282 if locked:
1286 if locked:
1283 lock_time = time.time()
1287 lock_time = time.time()
1284 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1288 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1285 else:
1289 else:
1286 lock_time = None
1290 lock_time = None
1287 Repository.unlock(repo)
1291 Repository.unlock(repo)
1288 _d = {
1292 _d = {
1289 'repo': repo.repo_name,
1293 'repo': repo.repo_name,
1290 'locked': locked,
1294 'locked': locked,
1291 'locked_since': lock_time,
1295 'locked_since': lock_time,
1292 'locked_by': user.username,
1296 'locked_by': user.username,
1293 'lock_reason': lock_reason,
1297 'lock_reason': lock_reason,
1294 'lock_state_changed': True,
1298 'lock_state_changed': True,
1295 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1299 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1296 % (user.username, repo.repo_name, locked))
1300 % (user.username, repo.repo_name, locked))
1297 }
1301 }
1298 return _d
1302 return _d
1299 except Exception:
1303 except Exception:
1300 log.exception(
1304 log.exception(
1301 "Exception occurred while trying to lock repository")
1305 "Exception occurred while trying to lock repository")
1302 raise JSONRPCError(
1306 raise JSONRPCError(
1303 'Error occurred locking repository `%s`' % repo.repo_name
1307 'Error occurred locking repository `%s`' % repo.repo_name
1304 )
1308 )
1305
1309
1306
1310
1307 @jsonrpc_method()
1311 @jsonrpc_method()
1308 def comment_commit(
1312 def comment_commit(
1309 request, apiuser, repoid, commit_id, message,
1313 request, apiuser, repoid, commit_id, message,
1310 userid=Optional(OAttr('apiuser')), status=Optional(None)):
1314 userid=Optional(OAttr('apiuser')), status=Optional(None)):
1311 """
1315 """
1312 Set a commit comment, and optionally change the status of the commit.
1316 Set a commit comment, and optionally change the status of the commit.
1313
1317
1314 :param apiuser: This is filled automatically from the |authtoken|.
1318 :param apiuser: This is filled automatically from the |authtoken|.
1315 :type apiuser: AuthUser
1319 :type apiuser: AuthUser
1316 :param repoid: Set the repository name or repository ID.
1320 :param repoid: Set the repository name or repository ID.
1317 :type repoid: str or int
1321 :type repoid: str or int
1318 :param commit_id: Specify the commit_id for which to set a comment.
1322 :param commit_id: Specify the commit_id for which to set a comment.
1319 :type commit_id: str
1323 :type commit_id: str
1320 :param message: The comment text.
1324 :param message: The comment text.
1321 :type message: str
1325 :type message: str
1322 :param userid: Set the user name of the comment creator.
1326 :param userid: Set the user name of the comment creator.
1323 :type userid: Optional(str or int)
1327 :type userid: Optional(str or int)
1324 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
1328 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
1325 'under_review'
1329 'under_review'
1326 :type status: str
1330 :type status: str
1327
1331
1328 Example error output:
1332 Example error output:
1329
1333
1330 .. code-block:: json
1334 .. code-block:: json
1331
1335
1332 {
1336 {
1333 "id" : <id_given_in_input>,
1337 "id" : <id_given_in_input>,
1334 "result" : {
1338 "result" : {
1335 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1339 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1336 "status_change": null or <status>,
1340 "status_change": null or <status>,
1337 "success": true
1341 "success": true
1338 },
1342 },
1339 "error" : null
1343 "error" : null
1340 }
1344 }
1341
1345
1342 """
1346 """
1343 repo = get_repo_or_error(repoid)
1347 repo = get_repo_or_error(repoid)
1344 if not has_superadmin_permission(apiuser):
1348 if not has_superadmin_permission(apiuser):
1345 _perms = ('repository.read', 'repository.write', 'repository.admin')
1349 _perms = ('repository.read', 'repository.write', 'repository.admin')
1346 has_repo_permissions(apiuser, repoid, repo, _perms)
1350 has_repo_permissions(apiuser, repoid, repo, _perms)
1347
1351
1348 if isinstance(userid, Optional):
1352 if isinstance(userid, Optional):
1349 userid = apiuser.user_id
1353 userid = apiuser.user_id
1350
1354
1351 user = get_user_or_error(userid)
1355 user = get_user_or_error(userid)
1352 status = Optional.extract(status)
1356 status = Optional.extract(status)
1353
1357
1354 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1358 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1355 if status and status not in allowed_statuses:
1359 if status and status not in allowed_statuses:
1356 raise JSONRPCError('Bad status, must be on '
1360 raise JSONRPCError('Bad status, must be on '
1357 'of %s got %s' % (allowed_statuses, status,))
1361 'of %s got %s' % (allowed_statuses, status,))
1358
1362
1359 try:
1363 try:
1360 rc_config = SettingsModel().get_all_settings()
1364 rc_config = SettingsModel().get_all_settings()
1361 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1365 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1362
1366
1363 comm = ChangesetCommentsModel().create(
1367 comm = ChangesetCommentsModel().create(
1364 message, repo, user, revision=commit_id, status_change=status,
1368 message, repo, user, revision=commit_id, status_change=status,
1365 renderer=renderer)
1369 renderer=renderer)
1366 if status:
1370 if status:
1367 # also do a status change
1371 # also do a status change
1368 try:
1372 try:
1369 ChangesetStatusModel().set_status(
1373 ChangesetStatusModel().set_status(
1370 repo, status, user, comm, revision=commit_id,
1374 repo, status, user, comm, revision=commit_id,
1371 dont_allow_on_closed_pull_request=True
1375 dont_allow_on_closed_pull_request=True
1372 )
1376 )
1373 except StatusChangeOnClosedPullRequestError:
1377 except StatusChangeOnClosedPullRequestError:
1374 log.exception(
1378 log.exception(
1375 "Exception occurred while trying to change repo commit status")
1379 "Exception occurred while trying to change repo commit status")
1376 msg = ('Changing status on a changeset associated with '
1380 msg = ('Changing status on a changeset associated with '
1377 'a closed pull request is not allowed')
1381 'a closed pull request is not allowed')
1378 raise JSONRPCError(msg)
1382 raise JSONRPCError(msg)
1379
1383
1380 Session().commit()
1384 Session().commit()
1381 return {
1385 return {
1382 'msg': (
1386 'msg': (
1383 'Commented on commit `%s` for repository `%s`' % (
1387 'Commented on commit `%s` for repository `%s`' % (
1384 comm.revision, repo.repo_name)),
1388 comm.revision, repo.repo_name)),
1385 'status_change': status,
1389 'status_change': status,
1386 'success': True,
1390 'success': True,
1387 }
1391 }
1388 except JSONRPCError:
1392 except JSONRPCError:
1389 # catch any inside errors, and re-raise them to prevent from
1393 # catch any inside errors, and re-raise them to prevent from
1390 # below global catch to silence them
1394 # below global catch to silence them
1391 raise
1395 raise
1392 except Exception:
1396 except Exception:
1393 log.exception("Exception occurred while trying to comment on commit")
1397 log.exception("Exception occurred while trying to comment on commit")
1394 raise JSONRPCError(
1398 raise JSONRPCError(
1395 'failed to set comment on repository `%s`' % (repo.repo_name,)
1399 'failed to set comment on repository `%s`' % (repo.repo_name,)
1396 )
1400 )
1397
1401
1398
1402
1399 @jsonrpc_method()
1403 @jsonrpc_method()
1400 def grant_user_permission(request, apiuser, repoid, userid, perm):
1404 def grant_user_permission(request, apiuser, repoid, userid, perm):
1401 """
1405 """
1402 Grant permissions for the specified user on the given repository,
1406 Grant permissions for the specified user on the given repository,
1403 or update existing permissions if found.
1407 or update existing permissions if found.
1404
1408
1405 This command can only be run using an |authtoken| with admin
1409 This command can only be run using an |authtoken| with admin
1406 permissions on the |repo|.
1410 permissions on the |repo|.
1407
1411
1408 :param apiuser: This is filled automatically from the |authtoken|.
1412 :param apiuser: This is filled automatically from the |authtoken|.
1409 :type apiuser: AuthUser
1413 :type apiuser: AuthUser
1410 :param repoid: Set the repository name or repository ID.
1414 :param repoid: Set the repository name or repository ID.
1411 :type repoid: str or int
1415 :type repoid: str or int
1412 :param userid: Set the user name.
1416 :param userid: Set the user name.
1413 :type userid: str
1417 :type userid: str
1414 :param perm: Set the user permissions, using the following format
1418 :param perm: Set the user permissions, using the following format
1415 ``(repository.(none|read|write|admin))``
1419 ``(repository.(none|read|write|admin))``
1416 :type perm: str
1420 :type perm: str
1417
1421
1418 Example output:
1422 Example output:
1419
1423
1420 .. code-block:: bash
1424 .. code-block:: bash
1421
1425
1422 id : <id_given_in_input>
1426 id : <id_given_in_input>
1423 result: {
1427 result: {
1424 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1428 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1425 "success": true
1429 "success": true
1426 }
1430 }
1427 error: null
1431 error: null
1428 """
1432 """
1429
1433
1430 repo = get_repo_or_error(repoid)
1434 repo = get_repo_or_error(repoid)
1431 user = get_user_or_error(userid)
1435 user = get_user_or_error(userid)
1432 perm = get_perm_or_error(perm)
1436 perm = get_perm_or_error(perm)
1433 if not has_superadmin_permission(apiuser):
1437 if not has_superadmin_permission(apiuser):
1434 _perms = ('repository.admin',)
1438 _perms = ('repository.admin',)
1435 has_repo_permissions(apiuser, repoid, repo, _perms)
1439 has_repo_permissions(apiuser, repoid, repo, _perms)
1436
1440
1437 try:
1441 try:
1438
1442
1439 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1443 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1440
1444
1441 Session().commit()
1445 Session().commit()
1442 return {
1446 return {
1443 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1447 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1444 perm.permission_name, user.username, repo.repo_name
1448 perm.permission_name, user.username, repo.repo_name
1445 ),
1449 ),
1446 'success': True
1450 'success': True
1447 }
1451 }
1448 except Exception:
1452 except Exception:
1449 log.exception(
1453 log.exception(
1450 "Exception occurred while trying edit permissions for repo")
1454 "Exception occurred while trying edit permissions for repo")
1451 raise JSONRPCError(
1455 raise JSONRPCError(
1452 'failed to edit permission for user: `%s` in repo: `%s`' % (
1456 'failed to edit permission for user: `%s` in repo: `%s`' % (
1453 userid, repoid
1457 userid, repoid
1454 )
1458 )
1455 )
1459 )
1456
1460
1457
1461
1458 @jsonrpc_method()
1462 @jsonrpc_method()
1459 def revoke_user_permission(request, apiuser, repoid, userid):
1463 def revoke_user_permission(request, apiuser, repoid, userid):
1460 """
1464 """
1461 Revoke permission for a user on the specified repository.
1465 Revoke permission for a user on the specified repository.
1462
1466
1463 This command can only be run using an |authtoken| with admin
1467 This command can only be run using an |authtoken| with admin
1464 permissions on the |repo|.
1468 permissions on the |repo|.
1465
1469
1466 :param apiuser: This is filled automatically from the |authtoken|.
1470 :param apiuser: This is filled automatically from the |authtoken|.
1467 :type apiuser: AuthUser
1471 :type apiuser: AuthUser
1468 :param repoid: Set the repository name or repository ID.
1472 :param repoid: Set the repository name or repository ID.
1469 :type repoid: str or int
1473 :type repoid: str or int
1470 :param userid: Set the user name of revoked user.
1474 :param userid: Set the user name of revoked user.
1471 :type userid: str or int
1475 :type userid: str or int
1472
1476
1473 Example error output:
1477 Example error output:
1474
1478
1475 .. code-block:: bash
1479 .. code-block:: bash
1476
1480
1477 id : <id_given_in_input>
1481 id : <id_given_in_input>
1478 result: {
1482 result: {
1479 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1483 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1480 "success": true
1484 "success": true
1481 }
1485 }
1482 error: null
1486 error: null
1483 """
1487 """
1484
1488
1485 repo = get_repo_or_error(repoid)
1489 repo = get_repo_or_error(repoid)
1486 user = get_user_or_error(userid)
1490 user = get_user_or_error(userid)
1487 if not has_superadmin_permission(apiuser):
1491 if not has_superadmin_permission(apiuser):
1488 _perms = ('repository.admin',)
1492 _perms = ('repository.admin',)
1489 has_repo_permissions(apiuser, repoid, repo, _perms)
1493 has_repo_permissions(apiuser, repoid, repo, _perms)
1490
1494
1491 try:
1495 try:
1492 RepoModel().revoke_user_permission(repo=repo, user=user)
1496 RepoModel().revoke_user_permission(repo=repo, user=user)
1493 Session().commit()
1497 Session().commit()
1494 return {
1498 return {
1495 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1499 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1496 user.username, repo.repo_name
1500 user.username, repo.repo_name
1497 ),
1501 ),
1498 'success': True
1502 'success': True
1499 }
1503 }
1500 except Exception:
1504 except Exception:
1501 log.exception(
1505 log.exception(
1502 "Exception occurred while trying revoke permissions to repo")
1506 "Exception occurred while trying revoke permissions to repo")
1503 raise JSONRPCError(
1507 raise JSONRPCError(
1504 'failed to edit permission for user: `%s` in repo: `%s`' % (
1508 'failed to edit permission for user: `%s` in repo: `%s`' % (
1505 userid, repoid
1509 userid, repoid
1506 )
1510 )
1507 )
1511 )
1508
1512
1509
1513
1510 @jsonrpc_method()
1514 @jsonrpc_method()
1511 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1515 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1512 """
1516 """
1513 Grant permission for a user group on the specified repository,
1517 Grant permission for a user group on the specified repository,
1514 or update existing permissions.
1518 or update existing permissions.
1515
1519
1516 This command can only be run using an |authtoken| with admin
1520 This command can only be run using an |authtoken| with admin
1517 permissions on the |repo|.
1521 permissions on the |repo|.
1518
1522
1519 :param apiuser: This is filled automatically from the |authtoken|.
1523 :param apiuser: This is filled automatically from the |authtoken|.
1520 :type apiuser: AuthUser
1524 :type apiuser: AuthUser
1521 :param repoid: Set the repository name or repository ID.
1525 :param repoid: Set the repository name or repository ID.
1522 :type repoid: str or int
1526 :type repoid: str or int
1523 :param usergroupid: Specify the ID of the user group.
1527 :param usergroupid: Specify the ID of the user group.
1524 :type usergroupid: str or int
1528 :type usergroupid: str or int
1525 :param perm: Set the user group permissions using the following
1529 :param perm: Set the user group permissions using the following
1526 format: (repository.(none|read|write|admin))
1530 format: (repository.(none|read|write|admin))
1527 :type perm: str
1531 :type perm: str
1528
1532
1529 Example output:
1533 Example output:
1530
1534
1531 .. code-block:: bash
1535 .. code-block:: bash
1532
1536
1533 id : <id_given_in_input>
1537 id : <id_given_in_input>
1534 result : {
1538 result : {
1535 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1539 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1536 "success": true
1540 "success": true
1537
1541
1538 }
1542 }
1539 error : null
1543 error : null
1540
1544
1541 Example error output:
1545 Example error output:
1542
1546
1543 .. code-block:: bash
1547 .. code-block:: bash
1544
1548
1545 id : <id_given_in_input>
1549 id : <id_given_in_input>
1546 result : null
1550 result : null
1547 error : {
1551 error : {
1548 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1552 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1549 }
1553 }
1550
1554
1551 """
1555 """
1552
1556
1553 repo = get_repo_or_error(repoid)
1557 repo = get_repo_or_error(repoid)
1554 perm = get_perm_or_error(perm)
1558 perm = get_perm_or_error(perm)
1555 if not has_superadmin_permission(apiuser):
1559 if not has_superadmin_permission(apiuser):
1556 _perms = ('repository.admin',)
1560 _perms = ('repository.admin',)
1557 has_repo_permissions(apiuser, repoid, repo, _perms)
1561 has_repo_permissions(apiuser, repoid, repo, _perms)
1558
1562
1559 user_group = get_user_group_or_error(usergroupid)
1563 user_group = get_user_group_or_error(usergroupid)
1560 if not has_superadmin_permission(apiuser):
1564 if not has_superadmin_permission(apiuser):
1561 # check if we have at least read permission for this user group !
1565 # check if we have at least read permission for this user group !
1562 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1566 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1563 if not HasUserGroupPermissionAnyApi(*_perms)(
1567 if not HasUserGroupPermissionAnyApi(*_perms)(
1564 user=apiuser, user_group_name=user_group.users_group_name):
1568 user=apiuser, user_group_name=user_group.users_group_name):
1565 raise JSONRPCError(
1569 raise JSONRPCError(
1566 'user group `%s` does not exist' % (usergroupid,))
1570 'user group `%s` does not exist' % (usergroupid,))
1567
1571
1568 try:
1572 try:
1569 RepoModel().grant_user_group_permission(
1573 RepoModel().grant_user_group_permission(
1570 repo=repo, group_name=user_group, perm=perm)
1574 repo=repo, group_name=user_group, perm=perm)
1571
1575
1572 Session().commit()
1576 Session().commit()
1573 return {
1577 return {
1574 'msg': 'Granted perm: `%s` for user group: `%s` in '
1578 'msg': 'Granted perm: `%s` for user group: `%s` in '
1575 'repo: `%s`' % (
1579 'repo: `%s`' % (
1576 perm.permission_name, user_group.users_group_name,
1580 perm.permission_name, user_group.users_group_name,
1577 repo.repo_name
1581 repo.repo_name
1578 ),
1582 ),
1579 'success': True
1583 'success': True
1580 }
1584 }
1581 except Exception:
1585 except Exception:
1582 log.exception(
1586 log.exception(
1583 "Exception occurred while trying change permission on repo")
1587 "Exception occurred while trying change permission on repo")
1584 raise JSONRPCError(
1588 raise JSONRPCError(
1585 'failed to edit permission for user group: `%s` in '
1589 'failed to edit permission for user group: `%s` in '
1586 'repo: `%s`' % (
1590 'repo: `%s`' % (
1587 usergroupid, repo.repo_name
1591 usergroupid, repo.repo_name
1588 )
1592 )
1589 )
1593 )
1590
1594
1591
1595
1592 @jsonrpc_method()
1596 @jsonrpc_method()
1593 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1597 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1594 """
1598 """
1595 Revoke the permissions of a user group on a given repository.
1599 Revoke the permissions of a user group on a given repository.
1596
1600
1597 This command can only be run using an |authtoken| with admin
1601 This command can only be run using an |authtoken| with admin
1598 permissions on the |repo|.
1602 permissions on the |repo|.
1599
1603
1600 :param apiuser: This is filled automatically from the |authtoken|.
1604 :param apiuser: This is filled automatically from the |authtoken|.
1601 :type apiuser: AuthUser
1605 :type apiuser: AuthUser
1602 :param repoid: Set the repository name or repository ID.
1606 :param repoid: Set the repository name or repository ID.
1603 :type repoid: str or int
1607 :type repoid: str or int
1604 :param usergroupid: Specify the user group ID.
1608 :param usergroupid: Specify the user group ID.
1605 :type usergroupid: str or int
1609 :type usergroupid: str or int
1606
1610
1607 Example output:
1611 Example output:
1608
1612
1609 .. code-block:: bash
1613 .. code-block:: bash
1610
1614
1611 id : <id_given_in_input>
1615 id : <id_given_in_input>
1612 result: {
1616 result: {
1613 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1617 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1614 "success": true
1618 "success": true
1615 }
1619 }
1616 error: null
1620 error: null
1617 """
1621 """
1618
1622
1619 repo = get_repo_or_error(repoid)
1623 repo = get_repo_or_error(repoid)
1620 if not has_superadmin_permission(apiuser):
1624 if not has_superadmin_permission(apiuser):
1621 _perms = ('repository.admin',)
1625 _perms = ('repository.admin',)
1622 has_repo_permissions(apiuser, repoid, repo, _perms)
1626 has_repo_permissions(apiuser, repoid, repo, _perms)
1623
1627
1624 user_group = get_user_group_or_error(usergroupid)
1628 user_group = get_user_group_or_error(usergroupid)
1625 if not has_superadmin_permission(apiuser):
1629 if not has_superadmin_permission(apiuser):
1626 # check if we have at least read permission for this user group !
1630 # check if we have at least read permission for this user group !
1627 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1631 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1628 if not HasUserGroupPermissionAnyApi(*_perms)(
1632 if not HasUserGroupPermissionAnyApi(*_perms)(
1629 user=apiuser, user_group_name=user_group.users_group_name):
1633 user=apiuser, user_group_name=user_group.users_group_name):
1630 raise JSONRPCError(
1634 raise JSONRPCError(
1631 'user group `%s` does not exist' % (usergroupid,))
1635 'user group `%s` does not exist' % (usergroupid,))
1632
1636
1633 try:
1637 try:
1634 RepoModel().revoke_user_group_permission(
1638 RepoModel().revoke_user_group_permission(
1635 repo=repo, group_name=user_group)
1639 repo=repo, group_name=user_group)
1636
1640
1637 Session().commit()
1641 Session().commit()
1638 return {
1642 return {
1639 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1643 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1640 user_group.users_group_name, repo.repo_name
1644 user_group.users_group_name, repo.repo_name
1641 ),
1645 ),
1642 'success': True
1646 'success': True
1643 }
1647 }
1644 except Exception:
1648 except Exception:
1645 log.exception("Exception occurred while trying revoke "
1649 log.exception("Exception occurred while trying revoke "
1646 "user group permission on repo")
1650 "user group permission on repo")
1647 raise JSONRPCError(
1651 raise JSONRPCError(
1648 'failed to edit permission for user group: `%s` in '
1652 'failed to edit permission for user group: `%s` in '
1649 'repo: `%s`' % (
1653 'repo: `%s`' % (
1650 user_group.users_group_name, repo.repo_name
1654 user_group.users_group_name, repo.repo_name
1651 )
1655 )
1652 )
1656 )
1653
1657
1654
1658
1655 @jsonrpc_method()
1659 @jsonrpc_method()
1656 def pull(request, apiuser, repoid):
1660 def pull(request, apiuser, repoid):
1657 """
1661 """
1658 Triggers a pull on the given repository from a remote location. You
1662 Triggers a pull on the given repository from a remote location. You
1659 can use this to keep remote repositories up-to-date.
1663 can use this to keep remote repositories up-to-date.
1660
1664
1661 This command can only be run using an |authtoken| with admin
1665 This command can only be run using an |authtoken| with admin
1662 rights to the specified repository. For more information,
1666 rights to the specified repository. For more information,
1663 see :ref:`config-token-ref`.
1667 see :ref:`config-token-ref`.
1664
1668
1665 This command takes the following options:
1669 This command takes the following options:
1666
1670
1667 :param apiuser: This is filled automatically from the |authtoken|.
1671 :param apiuser: This is filled automatically from the |authtoken|.
1668 :type apiuser: AuthUser
1672 :type apiuser: AuthUser
1669 :param repoid: The repository name or repository ID.
1673 :param repoid: The repository name or repository ID.
1670 :type repoid: str or int
1674 :type repoid: str or int
1671
1675
1672 Example output:
1676 Example output:
1673
1677
1674 .. code-block:: bash
1678 .. code-block:: bash
1675
1679
1676 id : <id_given_in_input>
1680 id : <id_given_in_input>
1677 result : {
1681 result : {
1678 "msg": "Pulled from `<repository name>`"
1682 "msg": "Pulled from `<repository name>`"
1679 "repository": "<repository name>"
1683 "repository": "<repository name>"
1680 }
1684 }
1681 error : null
1685 error : null
1682
1686
1683 Example error output:
1687 Example error output:
1684
1688
1685 .. code-block:: bash
1689 .. code-block:: bash
1686
1690
1687 id : <id_given_in_input>
1691 id : <id_given_in_input>
1688 result : null
1692 result : null
1689 error : {
1693 error : {
1690 "Unable to pull changes from `<reponame>`"
1694 "Unable to pull changes from `<reponame>`"
1691 }
1695 }
1692
1696
1693 """
1697 """
1694
1698
1695 repo = get_repo_or_error(repoid)
1699 repo = get_repo_or_error(repoid)
1696 if not has_superadmin_permission(apiuser):
1700 if not has_superadmin_permission(apiuser):
1697 _perms = ('repository.admin',)
1701 _perms = ('repository.admin',)
1698 has_repo_permissions(apiuser, repoid, repo, _perms)
1702 has_repo_permissions(apiuser, repoid, repo, _perms)
1699
1703
1700 try:
1704 try:
1701 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1705 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1702 return {
1706 return {
1703 'msg': 'Pulled from `%s`' % repo.repo_name,
1707 'msg': 'Pulled from `%s`' % repo.repo_name,
1704 'repository': repo.repo_name
1708 'repository': repo.repo_name
1705 }
1709 }
1706 except Exception:
1710 except Exception:
1707 log.exception("Exception occurred while trying to "
1711 log.exception("Exception occurred while trying to "
1708 "pull changes from remote location")
1712 "pull changes from remote location")
1709 raise JSONRPCError(
1713 raise JSONRPCError(
1710 'Unable to pull changes from `%s`' % repo.repo_name
1714 'Unable to pull changes from `%s`' % repo.repo_name
1711 )
1715 )
1712
1716
1713
1717
1714 @jsonrpc_method()
1718 @jsonrpc_method()
1715 def strip(request, apiuser, repoid, revision, branch):
1719 def strip(request, apiuser, repoid, revision, branch):
1716 """
1720 """
1717 Strips the given revision from the specified repository.
1721 Strips the given revision from the specified repository.
1718
1722
1719 * This will remove the revision and all of its decendants.
1723 * This will remove the revision and all of its decendants.
1720
1724
1721 This command can only be run using an |authtoken| with admin rights to
1725 This command can only be run using an |authtoken| with admin rights to
1722 the specified repository.
1726 the specified repository.
1723
1727
1724 This command takes the following options:
1728 This command takes the following options:
1725
1729
1726 :param apiuser: This is filled automatically from the |authtoken|.
1730 :param apiuser: This is filled automatically from the |authtoken|.
1727 :type apiuser: AuthUser
1731 :type apiuser: AuthUser
1728 :param repoid: The repository name or repository ID.
1732 :param repoid: The repository name or repository ID.
1729 :type repoid: str or int
1733 :type repoid: str or int
1730 :param revision: The revision you wish to strip.
1734 :param revision: The revision you wish to strip.
1731 :type revision: str
1735 :type revision: str
1732 :param branch: The branch from which to strip the revision.
1736 :param branch: The branch from which to strip the revision.
1733 :type branch: str
1737 :type branch: str
1734
1738
1735 Example output:
1739 Example output:
1736
1740
1737 .. code-block:: bash
1741 .. code-block:: bash
1738
1742
1739 id : <id_given_in_input>
1743 id : <id_given_in_input>
1740 result : {
1744 result : {
1741 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1745 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1742 "repository": "<repository name>"
1746 "repository": "<repository name>"
1743 }
1747 }
1744 error : null
1748 error : null
1745
1749
1746 Example error output:
1750 Example error output:
1747
1751
1748 .. code-block:: bash
1752 .. code-block:: bash
1749
1753
1750 id : <id_given_in_input>
1754 id : <id_given_in_input>
1751 result : null
1755 result : null
1752 error : {
1756 error : {
1753 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1757 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1754 }
1758 }
1755
1759
1756 """
1760 """
1757
1761
1758 repo = get_repo_or_error(repoid)
1762 repo = get_repo_or_error(repoid)
1759 if not has_superadmin_permission(apiuser):
1763 if not has_superadmin_permission(apiuser):
1760 _perms = ('repository.admin',)
1764 _perms = ('repository.admin',)
1761 has_repo_permissions(apiuser, repoid, repo, _perms)
1765 has_repo_permissions(apiuser, repoid, repo, _perms)
1762
1766
1763 try:
1767 try:
1764 ScmModel().strip(repo, revision, branch)
1768 ScmModel().strip(repo, revision, branch)
1765 return {
1769 return {
1766 'msg': 'Stripped commit %s from repo `%s`' % (
1770 'msg': 'Stripped commit %s from repo `%s`' % (
1767 revision, repo.repo_name),
1771 revision, repo.repo_name),
1768 'repository': repo.repo_name
1772 'repository': repo.repo_name
1769 }
1773 }
1770 except Exception:
1774 except Exception:
1771 log.exception("Exception while trying to strip")
1775 log.exception("Exception while trying to strip")
1772 raise JSONRPCError(
1776 raise JSONRPCError(
1773 'Unable to strip commit %s from repo `%s`' % (
1777 'Unable to strip commit %s from repo `%s`' % (
1774 revision, repo.repo_name)
1778 revision, repo.repo_name)
1775 )
1779 )
1776
1780
1777
1781
1778 @jsonrpc_method()
1782 @jsonrpc_method()
1779 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1783 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1780 """
1784 """
1781 Returns all settings for a repository. If key is given it only returns the
1785 Returns all settings for a repository. If key is given it only returns the
1782 setting identified by the key or null.
1786 setting identified by the key or null.
1783
1787
1784 :param apiuser: This is filled automatically from the |authtoken|.
1788 :param apiuser: This is filled automatically from the |authtoken|.
1785 :type apiuser: AuthUser
1789 :type apiuser: AuthUser
1786 :param repoid: The repository name or repository id.
1790 :param repoid: The repository name or repository id.
1787 :type repoid: str or int
1791 :type repoid: str or int
1788 :param key: Key of the setting to return.
1792 :param key: Key of the setting to return.
1789 :type: key: Optional(str)
1793 :type: key: Optional(str)
1790
1794
1791 Example output:
1795 Example output:
1792
1796
1793 .. code-block:: bash
1797 .. code-block:: bash
1794
1798
1795 {
1799 {
1796 "error": null,
1800 "error": null,
1797 "id": 237,
1801 "id": 237,
1798 "result": {
1802 "result": {
1799 "extensions_largefiles": true,
1803 "extensions_largefiles": true,
1800 "hooks_changegroup_push_logger": true,
1804 "hooks_changegroup_push_logger": true,
1801 "hooks_changegroup_repo_size": false,
1805 "hooks_changegroup_repo_size": false,
1802 "hooks_outgoing_pull_logger": true,
1806 "hooks_outgoing_pull_logger": true,
1803 "phases_publish": "True",
1807 "phases_publish": "True",
1804 "rhodecode_hg_use_rebase_for_merging": true,
1808 "rhodecode_hg_use_rebase_for_merging": true,
1805 "rhodecode_pr_merge_enabled": true,
1809 "rhodecode_pr_merge_enabled": true,
1806 "rhodecode_use_outdated_comments": true
1810 "rhodecode_use_outdated_comments": true
1807 }
1811 }
1808 }
1812 }
1809 """
1813 """
1810
1814
1811 # Restrict access to this api method to admins only.
1815 # Restrict access to this api method to admins only.
1812 if not has_superadmin_permission(apiuser):
1816 if not has_superadmin_permission(apiuser):
1813 raise JSONRPCForbidden()
1817 raise JSONRPCForbidden()
1814
1818
1815 try:
1819 try:
1816 repo = get_repo_or_error(repoid)
1820 repo = get_repo_or_error(repoid)
1817 settings_model = VcsSettingsModel(repo=repo)
1821 settings_model = VcsSettingsModel(repo=repo)
1818 settings = settings_model.get_global_settings()
1822 settings = settings_model.get_global_settings()
1819 settings.update(settings_model.get_repo_settings())
1823 settings.update(settings_model.get_repo_settings())
1820
1824
1821 # If only a single setting is requested fetch it from all settings.
1825 # If only a single setting is requested fetch it from all settings.
1822 key = Optional.extract(key)
1826 key = Optional.extract(key)
1823 if key is not None:
1827 if key is not None:
1824 settings = settings.get(key, None)
1828 settings = settings.get(key, None)
1825 except Exception:
1829 except Exception:
1826 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1830 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1827 log.exception(msg)
1831 log.exception(msg)
1828 raise JSONRPCError(msg)
1832 raise JSONRPCError(msg)
1829
1833
1830 return settings
1834 return settings
1831
1835
1832
1836
1833 @jsonrpc_method()
1837 @jsonrpc_method()
1834 def set_repo_settings(request, apiuser, repoid, settings):
1838 def set_repo_settings(request, apiuser, repoid, settings):
1835 """
1839 """
1836 Update repository settings. Returns true on success.
1840 Update repository settings. Returns true on success.
1837
1841
1838 :param apiuser: This is filled automatically from the |authtoken|.
1842 :param apiuser: This is filled automatically from the |authtoken|.
1839 :type apiuser: AuthUser
1843 :type apiuser: AuthUser
1840 :param repoid: The repository name or repository id.
1844 :param repoid: The repository name or repository id.
1841 :type repoid: str or int
1845 :type repoid: str or int
1842 :param settings: The new settings for the repository.
1846 :param settings: The new settings for the repository.
1843 :type: settings: dict
1847 :type: settings: dict
1844
1848
1845 Example output:
1849 Example output:
1846
1850
1847 .. code-block:: bash
1851 .. code-block:: bash
1848
1852
1849 {
1853 {
1850 "error": null,
1854 "error": null,
1851 "id": 237,
1855 "id": 237,
1852 "result": true
1856 "result": true
1853 }
1857 }
1854 """
1858 """
1855 # Restrict access to this api method to admins only.
1859 # Restrict access to this api method to admins only.
1856 if not has_superadmin_permission(apiuser):
1860 if not has_superadmin_permission(apiuser):
1857 raise JSONRPCForbidden()
1861 raise JSONRPCForbidden()
1858
1862
1859 if type(settings) is not dict:
1863 if type(settings) is not dict:
1860 raise JSONRPCError('Settings have to be a JSON Object.')
1864 raise JSONRPCError('Settings have to be a JSON Object.')
1861
1865
1862 try:
1866 try:
1863 settings_model = VcsSettingsModel(repo=repoid)
1867 settings_model = VcsSettingsModel(repo=repoid)
1864
1868
1865 # Merge global, repo and incoming settings.
1869 # Merge global, repo and incoming settings.
1866 new_settings = settings_model.get_global_settings()
1870 new_settings = settings_model.get_global_settings()
1867 new_settings.update(settings_model.get_repo_settings())
1871 new_settings.update(settings_model.get_repo_settings())
1868 new_settings.update(settings)
1872 new_settings.update(settings)
1869
1873
1870 # Update the settings.
1874 # Update the settings.
1871 inherit_global_settings = new_settings.get(
1875 inherit_global_settings = new_settings.get(
1872 'inherit_global_settings', False)
1876 'inherit_global_settings', False)
1873 settings_model.create_or_update_repo_settings(
1877 settings_model.create_or_update_repo_settings(
1874 new_settings, inherit_global_settings=inherit_global_settings)
1878 new_settings, inherit_global_settings=inherit_global_settings)
1875 Session().commit()
1879 Session().commit()
1876 except Exception:
1880 except Exception:
1877 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1881 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1878 log.exception(msg)
1882 log.exception(msg)
1879 raise JSONRPCError(msg)
1883 raise JSONRPCError(msg)
1880
1884
1881 # Indicate success.
1885 # Indicate success.
1882 return True
1886 return True
@@ -1,1099 +1,1102 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Scm model for RhodeCode
22 Scm model for RhodeCode
23 """
23 """
24
24
25 import os.path
25 import os.path
26 import re
26 import re
27 import sys
27 import sys
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31 import cStringIO
31 import cStringIO
32 import pkg_resources
32 import pkg_resources
33
33
34 import pylons
34 import pylons
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36 from sqlalchemy import func
36 from sqlalchemy import func
37 from zope.cachedescriptors.property import Lazy as LazyProperty
37 from zope.cachedescriptors.property import Lazy as LazyProperty
38
38
39 import rhodecode
39 import rhodecode
40 from rhodecode.lib.vcs import get_backend
40 from rhodecode.lib.vcs import get_backend
41 from rhodecode.lib.vcs.exceptions import RepositoryError, NodeNotChangedError
41 from rhodecode.lib.vcs.exceptions import RepositoryError, NodeNotChangedError
42 from rhodecode.lib.vcs.nodes import FileNode
42 from rhodecode.lib.vcs.nodes import FileNode
43 from rhodecode.lib.vcs.backends.base import EmptyCommit
43 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 from rhodecode.lib import helpers as h
44 from rhodecode.lib import helpers as h
45
45
46 from rhodecode.lib.auth import (
46 from rhodecode.lib.auth import (
47 HasRepoPermissionAny, HasRepoGroupPermissionAny,
47 HasRepoPermissionAny, HasRepoGroupPermissionAny,
48 HasUserGroupPermissionAny)
48 HasUserGroupPermissionAny)
49 from rhodecode.lib.exceptions import NonRelativePathError, IMCCommitError
49 from rhodecode.lib.exceptions import NonRelativePathError, IMCCommitError
50 from rhodecode.lib import hooks_utils, caches
50 from rhodecode.lib import hooks_utils, caches
51 from rhodecode.lib.utils import (
51 from rhodecode.lib.utils import (
52 get_filesystem_repos, action_logger, make_db_config)
52 get_filesystem_repos, action_logger, make_db_config)
53 from rhodecode.lib.utils2 import (
53 from rhodecode.lib.utils2 import (
54 safe_str, safe_unicode, get_server_url, md5)
54 safe_str, safe_unicode, get_server_url, md5)
55 from rhodecode.model import BaseModel
55 from rhodecode.model import BaseModel
56 from rhodecode.model.db import (
56 from rhodecode.model.db import (
57 Repository, CacheKey, UserFollowing, UserLog, User, RepoGroup,
57 Repository, CacheKey, UserFollowing, UserLog, User, RepoGroup,
58 PullRequest, DbMigrateVersion)
58 PullRequest, DbMigrateVersion)
59 from rhodecode.model.settings import VcsSettingsModel
59 from rhodecode.model.settings import VcsSettingsModel
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63
63
64 class UserTemp(object):
64 class UserTemp(object):
65 def __init__(self, user_id):
65 def __init__(self, user_id):
66 self.user_id = user_id
66 self.user_id = user_id
67
67
68 def __repr__(self):
68 def __repr__(self):
69 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
69 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
70
70
71
71
72 class RepoTemp(object):
72 class RepoTemp(object):
73 def __init__(self, repo_id):
73 def __init__(self, repo_id):
74 self.repo_id = repo_id
74 self.repo_id = repo_id
75
75
76 def __repr__(self):
76 def __repr__(self):
77 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
77 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
78
78
79
79
80 class SimpleCachedRepoList(object):
80 class SimpleCachedRepoList(object):
81 """
81 """
82 Lighter version of of iteration of repos without the scm initialisation,
82 Lighter version of of iteration of repos without the scm initialisation,
83 and with cache usage
83 and with cache usage
84 """
84 """
85 def __init__(self, db_repo_list, repos_path, order_by=None, perm_set=None):
85 def __init__(self, db_repo_list, repos_path, order_by=None, perm_set=None):
86 self.db_repo_list = db_repo_list
86 self.db_repo_list = db_repo_list
87 self.repos_path = repos_path
87 self.repos_path = repos_path
88 self.order_by = order_by
88 self.order_by = order_by
89 self.reversed = (order_by or '').startswith('-')
89 self.reversed = (order_by or '').startswith('-')
90 if not perm_set:
90 if not perm_set:
91 perm_set = ['repository.read', 'repository.write',
91 perm_set = ['repository.read', 'repository.write',
92 'repository.admin']
92 'repository.admin']
93 self.perm_set = perm_set
93 self.perm_set = perm_set
94
94
95 def __len__(self):
95 def __len__(self):
96 return len(self.db_repo_list)
96 return len(self.db_repo_list)
97
97
98 def __repr__(self):
98 def __repr__(self):
99 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
99 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
100
100
101 def __iter__(self):
101 def __iter__(self):
102 for dbr in self.db_repo_list:
102 for dbr in self.db_repo_list:
103 # check permission at this level
103 # check permission at this level
104 has_perm = HasRepoPermissionAny(*self.perm_set)(
104 has_perm = HasRepoPermissionAny(*self.perm_set)(
105 dbr.repo_name, 'SimpleCachedRepoList check')
105 dbr.repo_name, 'SimpleCachedRepoList check')
106 if not has_perm:
106 if not has_perm:
107 continue
107 continue
108
108
109 tmp_d = {
109 tmp_d = {
110 'name': dbr.repo_name,
110 'name': dbr.repo_name,
111 'dbrepo': dbr.get_dict(),
111 'dbrepo': dbr.get_dict(),
112 'dbrepo_fork': dbr.fork.get_dict() if dbr.fork else {}
112 'dbrepo_fork': dbr.fork.get_dict() if dbr.fork else {}
113 }
113 }
114 yield tmp_d
114 yield tmp_d
115
115
116
116
117 class _PermCheckIterator(object):
117 class _PermCheckIterator(object):
118
118
119 def __init__(
119 def __init__(
120 self, obj_list, obj_attr, perm_set, perm_checker,
120 self, obj_list, obj_attr, perm_set, perm_checker,
121 extra_kwargs=None):
121 extra_kwargs=None):
122 """
122 """
123 Creates iterator from given list of objects, additionally
123 Creates iterator from given list of objects, additionally
124 checking permission for them from perm_set var
124 checking permission for them from perm_set var
125
125
126 :param obj_list: list of db objects
126 :param obj_list: list of db objects
127 :param obj_attr: attribute of object to pass into perm_checker
127 :param obj_attr: attribute of object to pass into perm_checker
128 :param perm_set: list of permissions to check
128 :param perm_set: list of permissions to check
129 :param perm_checker: callable to check permissions against
129 :param perm_checker: callable to check permissions against
130 """
130 """
131 self.obj_list = obj_list
131 self.obj_list = obj_list
132 self.obj_attr = obj_attr
132 self.obj_attr = obj_attr
133 self.perm_set = perm_set
133 self.perm_set = perm_set
134 self.perm_checker = perm_checker
134 self.perm_checker = perm_checker
135 self.extra_kwargs = extra_kwargs or {}
135 self.extra_kwargs = extra_kwargs or {}
136
136
137 def __len__(self):
137 def __len__(self):
138 return len(self.obj_list)
138 return len(self.obj_list)
139
139
140 def __repr__(self):
140 def __repr__(self):
141 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
141 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
142
142
143 def __iter__(self):
143 def __iter__(self):
144 checker = self.perm_checker(*self.perm_set)
144 checker = self.perm_checker(*self.perm_set)
145 for db_obj in self.obj_list:
145 for db_obj in self.obj_list:
146 # check permission at this level
146 # check permission at this level
147 name = getattr(db_obj, self.obj_attr, None)
147 name = getattr(db_obj, self.obj_attr, None)
148 if not checker(name, self.__class__.__name__, **self.extra_kwargs):
148 if not checker(name, self.__class__.__name__, **self.extra_kwargs):
149 continue
149 continue
150
150
151 yield db_obj
151 yield db_obj
152
152
153
153
154 class RepoList(_PermCheckIterator):
154 class RepoList(_PermCheckIterator):
155
155
156 def __init__(self, db_repo_list, perm_set=None, extra_kwargs=None):
156 def __init__(self, db_repo_list, perm_set=None, extra_kwargs=None):
157 if not perm_set:
157 if not perm_set:
158 perm_set = [
158 perm_set = [
159 'repository.read', 'repository.write', 'repository.admin']
159 'repository.read', 'repository.write', 'repository.admin']
160
160
161 super(RepoList, self).__init__(
161 super(RepoList, self).__init__(
162 obj_list=db_repo_list,
162 obj_list=db_repo_list,
163 obj_attr='repo_name', perm_set=perm_set,
163 obj_attr='repo_name', perm_set=perm_set,
164 perm_checker=HasRepoPermissionAny,
164 perm_checker=HasRepoPermissionAny,
165 extra_kwargs=extra_kwargs)
165 extra_kwargs=extra_kwargs)
166
166
167
167
168 class RepoGroupList(_PermCheckIterator):
168 class RepoGroupList(_PermCheckIterator):
169
169
170 def __init__(self, db_repo_group_list, perm_set=None, extra_kwargs=None):
170 def __init__(self, db_repo_group_list, perm_set=None, extra_kwargs=None):
171 if not perm_set:
171 if not perm_set:
172 perm_set = ['group.read', 'group.write', 'group.admin']
172 perm_set = ['group.read', 'group.write', 'group.admin']
173
173
174 super(RepoGroupList, self).__init__(
174 super(RepoGroupList, self).__init__(
175 obj_list=db_repo_group_list,
175 obj_list=db_repo_group_list,
176 obj_attr='group_name', perm_set=perm_set,
176 obj_attr='group_name', perm_set=perm_set,
177 perm_checker=HasRepoGroupPermissionAny,
177 perm_checker=HasRepoGroupPermissionAny,
178 extra_kwargs=extra_kwargs)
178 extra_kwargs=extra_kwargs)
179
179
180
180
181 class UserGroupList(_PermCheckIterator):
181 class UserGroupList(_PermCheckIterator):
182
182
183 def __init__(self, db_user_group_list, perm_set=None, extra_kwargs=None):
183 def __init__(self, db_user_group_list, perm_set=None, extra_kwargs=None):
184 if not perm_set:
184 if not perm_set:
185 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
185 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
186
186
187 super(UserGroupList, self).__init__(
187 super(UserGroupList, self).__init__(
188 obj_list=db_user_group_list,
188 obj_list=db_user_group_list,
189 obj_attr='users_group_name', perm_set=perm_set,
189 obj_attr='users_group_name', perm_set=perm_set,
190 perm_checker=HasUserGroupPermissionAny,
190 perm_checker=HasUserGroupPermissionAny,
191 extra_kwargs=extra_kwargs)
191 extra_kwargs=extra_kwargs)
192
192
193
193
194 class ScmModel(BaseModel):
194 class ScmModel(BaseModel):
195 """
195 """
196 Generic Scm Model
196 Generic Scm Model
197 """
197 """
198
198
199 @LazyProperty
199 @LazyProperty
200 def repos_path(self):
200 def repos_path(self):
201 """
201 """
202 Gets the repositories root path from database
202 Gets the repositories root path from database
203 """
203 """
204
204
205 settings_model = VcsSettingsModel(sa=self.sa)
205 settings_model = VcsSettingsModel(sa=self.sa)
206 return settings_model.get_repos_location()
206 return settings_model.get_repos_location()
207
207
208 def repo_scan(self, repos_path=None):
208 def repo_scan(self, repos_path=None):
209 """
209 """
210 Listing of repositories in given path. This path should not be a
210 Listing of repositories in given path. This path should not be a
211 repository itself. Return a dictionary of repository objects
211 repository itself. Return a dictionary of repository objects
212
212
213 :param repos_path: path to directory containing repositories
213 :param repos_path: path to directory containing repositories
214 """
214 """
215
215
216 if repos_path is None:
216 if repos_path is None:
217 repos_path = self.repos_path
217 repos_path = self.repos_path
218
218
219 log.info('scanning for repositories in %s', repos_path)
219 log.info('scanning for repositories in %s', repos_path)
220
220
221 config = make_db_config()
221 config = make_db_config()
222 config.set('extensions', 'largefiles', '')
222 config.set('extensions', 'largefiles', '')
223 repos = {}
223 repos = {}
224
224
225 for name, path in get_filesystem_repos(repos_path, recursive=True):
225 for name, path in get_filesystem_repos(repos_path, recursive=True):
226 # name need to be decomposed and put back together using the /
226 # name need to be decomposed and put back together using the /
227 # since this is internal storage separator for rhodecode
227 # since this is internal storage separator for rhodecode
228 name = Repository.normalize_repo_name(name)
228 name = Repository.normalize_repo_name(name)
229
229
230 try:
230 try:
231 if name in repos:
231 if name in repos:
232 raise RepositoryError('Duplicate repository name %s '
232 raise RepositoryError('Duplicate repository name %s '
233 'found in %s' % (name, path))
233 'found in %s' % (name, path))
234 elif path[0] in rhodecode.BACKENDS:
234 elif path[0] in rhodecode.BACKENDS:
235 klass = get_backend(path[0])
235 klass = get_backend(path[0])
236 repos[name] = klass(path[1], config=config)
236 repos[name] = klass(path[1], config=config)
237 except OSError:
237 except OSError:
238 continue
238 continue
239 log.debug('found %s paths with repositories', len(repos))
239 log.debug('found %s paths with repositories', len(repos))
240 return repos
240 return repos
241
241
242 def get_repos(self, all_repos=None, sort_key=None):
242 def get_repos(self, all_repos=None, sort_key=None):
243 """
243 """
244 Get all repositories from db and for each repo create it's
244 Get all repositories from db and for each repo create it's
245 backend instance and fill that backed with information from database
245 backend instance and fill that backed with information from database
246
246
247 :param all_repos: list of repository names as strings
247 :param all_repos: list of repository names as strings
248 give specific repositories list, good for filtering
248 give specific repositories list, good for filtering
249
249
250 :param sort_key: initial sorting of repositories
250 :param sort_key: initial sorting of repositories
251 """
251 """
252 if all_repos is None:
252 if all_repos is None:
253 all_repos = self.sa.query(Repository)\
253 all_repos = self.sa.query(Repository)\
254 .filter(Repository.group_id == None)\
254 .filter(Repository.group_id == None)\
255 .order_by(func.lower(Repository.repo_name)).all()
255 .order_by(func.lower(Repository.repo_name)).all()
256 repo_iter = SimpleCachedRepoList(
256 repo_iter = SimpleCachedRepoList(
257 all_repos, repos_path=self.repos_path, order_by=sort_key)
257 all_repos, repos_path=self.repos_path, order_by=sort_key)
258 return repo_iter
258 return repo_iter
259
259
260 def get_repo_groups(self, all_groups=None):
260 def get_repo_groups(self, all_groups=None):
261 if all_groups is None:
261 if all_groups is None:
262 all_groups = RepoGroup.query()\
262 all_groups = RepoGroup.query()\
263 .filter(RepoGroup.group_parent_id == None).all()
263 .filter(RepoGroup.group_parent_id == None).all()
264 return [x for x in RepoGroupList(all_groups)]
264 return [x for x in RepoGroupList(all_groups)]
265
265
266 def mark_for_invalidation(self, repo_name, delete=False):
266 def mark_for_invalidation(self, repo_name, delete=False):
267 """
267 """
268 Mark caches of this repo invalid in the database. `delete` flag
268 Mark caches of this repo invalid in the database. `delete` flag
269 removes the cache entries
269 removes the cache entries
270
270
271 :param repo_name: the repo_name for which caches should be marked
271 :param repo_name: the repo_name for which caches should be marked
272 invalid, or deleted
272 invalid, or deleted
273 :param delete: delete the entry keys instead of setting bool
273 :param delete: delete the entry keys instead of setting bool
274 flag on them
274 flag on them
275 """
275 """
276 CacheKey.set_invalidate(repo_name, delete=delete)
276 CacheKey.set_invalidate(repo_name, delete=delete)
277 repo = Repository.get_by_repo_name(repo_name)
277 repo = Repository.get_by_repo_name(repo_name)
278
278
279 if repo:
279 if repo:
280 config = repo._config
280 config = repo._config
281 config.set('extensions', 'largefiles', '')
281 config.set('extensions', 'largefiles', '')
282 repo.update_commit_cache(config=config, cs_cache=None)
282 repo.update_commit_cache(config=config, cs_cache=None)
283 caches.clear_repo_caches(repo_name)
283 caches.clear_repo_caches(repo_name)
284
284
285 def toggle_following_repo(self, follow_repo_id, user_id):
285 def toggle_following_repo(self, follow_repo_id, user_id):
286
286
287 f = self.sa.query(UserFollowing)\
287 f = self.sa.query(UserFollowing)\
288 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
288 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
289 .filter(UserFollowing.user_id == user_id).scalar()
289 .filter(UserFollowing.user_id == user_id).scalar()
290
290
291 if f is not None:
291 if f is not None:
292 try:
292 try:
293 self.sa.delete(f)
293 self.sa.delete(f)
294 action_logger(UserTemp(user_id),
294 action_logger(UserTemp(user_id),
295 'stopped_following_repo',
295 'stopped_following_repo',
296 RepoTemp(follow_repo_id))
296 RepoTemp(follow_repo_id))
297 return
297 return
298 except Exception:
298 except Exception:
299 log.error(traceback.format_exc())
299 log.error(traceback.format_exc())
300 raise
300 raise
301
301
302 try:
302 try:
303 f = UserFollowing()
303 f = UserFollowing()
304 f.user_id = user_id
304 f.user_id = user_id
305 f.follows_repo_id = follow_repo_id
305 f.follows_repo_id = follow_repo_id
306 self.sa.add(f)
306 self.sa.add(f)
307
307
308 action_logger(UserTemp(user_id),
308 action_logger(UserTemp(user_id),
309 'started_following_repo',
309 'started_following_repo',
310 RepoTemp(follow_repo_id))
310 RepoTemp(follow_repo_id))
311 except Exception:
311 except Exception:
312 log.error(traceback.format_exc())
312 log.error(traceback.format_exc())
313 raise
313 raise
314
314
315 def toggle_following_user(self, follow_user_id, user_id):
315 def toggle_following_user(self, follow_user_id, user_id):
316 f = self.sa.query(UserFollowing)\
316 f = self.sa.query(UserFollowing)\
317 .filter(UserFollowing.follows_user_id == follow_user_id)\
317 .filter(UserFollowing.follows_user_id == follow_user_id)\
318 .filter(UserFollowing.user_id == user_id).scalar()
318 .filter(UserFollowing.user_id == user_id).scalar()
319
319
320 if f is not None:
320 if f is not None:
321 try:
321 try:
322 self.sa.delete(f)
322 self.sa.delete(f)
323 return
323 return
324 except Exception:
324 except Exception:
325 log.error(traceback.format_exc())
325 log.error(traceback.format_exc())
326 raise
326 raise
327
327
328 try:
328 try:
329 f = UserFollowing()
329 f = UserFollowing()
330 f.user_id = user_id
330 f.user_id = user_id
331 f.follows_user_id = follow_user_id
331 f.follows_user_id = follow_user_id
332 self.sa.add(f)
332 self.sa.add(f)
333 except Exception:
333 except Exception:
334 log.error(traceback.format_exc())
334 log.error(traceback.format_exc())
335 raise
335 raise
336
336
337 def is_following_repo(self, repo_name, user_id, cache=False):
337 def is_following_repo(self, repo_name, user_id, cache=False):
338 r = self.sa.query(Repository)\
338 r = self.sa.query(Repository)\
339 .filter(Repository.repo_name == repo_name).scalar()
339 .filter(Repository.repo_name == repo_name).scalar()
340
340
341 f = self.sa.query(UserFollowing)\
341 f = self.sa.query(UserFollowing)\
342 .filter(UserFollowing.follows_repository == r)\
342 .filter(UserFollowing.follows_repository == r)\
343 .filter(UserFollowing.user_id == user_id).scalar()
343 .filter(UserFollowing.user_id == user_id).scalar()
344
344
345 return f is not None
345 return f is not None
346
346
347 def is_following_user(self, username, user_id, cache=False):
347 def is_following_user(self, username, user_id, cache=False):
348 u = User.get_by_username(username)
348 u = User.get_by_username(username)
349
349
350 f = self.sa.query(UserFollowing)\
350 f = self.sa.query(UserFollowing)\
351 .filter(UserFollowing.follows_user == u)\
351 .filter(UserFollowing.follows_user == u)\
352 .filter(UserFollowing.user_id == user_id).scalar()
352 .filter(UserFollowing.user_id == user_id).scalar()
353
353
354 return f is not None
354 return f is not None
355
355
356 def get_followers(self, repo):
356 def get_followers(self, repo):
357 repo = self._get_repo(repo)
357 repo = self._get_repo(repo)
358
358
359 return self.sa.query(UserFollowing)\
359 return self.sa.query(UserFollowing)\
360 .filter(UserFollowing.follows_repository == repo).count()
360 .filter(UserFollowing.follows_repository == repo).count()
361
361
362 def get_forks(self, repo):
362 def get_forks(self, repo):
363 repo = self._get_repo(repo)
363 repo = self._get_repo(repo)
364 return self.sa.query(Repository)\
364 return self.sa.query(Repository)\
365 .filter(Repository.fork == repo).count()
365 .filter(Repository.fork == repo).count()
366
366
367 def get_pull_requests(self, repo):
367 def get_pull_requests(self, repo):
368 repo = self._get_repo(repo)
368 repo = self._get_repo(repo)
369 return self.sa.query(PullRequest)\
369 return self.sa.query(PullRequest)\
370 .filter(PullRequest.target_repo == repo)\
370 .filter(PullRequest.target_repo == repo)\
371 .filter(PullRequest.status != PullRequest.STATUS_CLOSED).count()
371 .filter(PullRequest.status != PullRequest.STATUS_CLOSED).count()
372
372
373 def mark_as_fork(self, repo, fork, user):
373 def mark_as_fork(self, repo, fork, user):
374 repo = self._get_repo(repo)
374 repo = self._get_repo(repo)
375 fork = self._get_repo(fork)
375 fork = self._get_repo(fork)
376 if fork and repo.repo_id == fork.repo_id:
376 if fork and repo.repo_id == fork.repo_id:
377 raise Exception("Cannot set repository as fork of itself")
377 raise Exception("Cannot set repository as fork of itself")
378
378
379 if fork and repo.repo_type != fork.repo_type:
379 if fork and repo.repo_type != fork.repo_type:
380 raise RepositoryError(
380 raise RepositoryError(
381 "Cannot set repository as fork of repository with other type")
381 "Cannot set repository as fork of repository with other type")
382
382
383 repo.fork = fork
383 repo.fork = fork
384 self.sa.add(repo)
384 self.sa.add(repo)
385 return repo
385 return repo
386
386
387 def pull_changes(self, repo, username):
387 def pull_changes(self, repo, username):
388 dbrepo = self._get_repo(repo)
388 dbrepo = self._get_repo(repo)
389 clone_uri = dbrepo.clone_uri
389 clone_uri = dbrepo.clone_uri
390 if not clone_uri:
390 if not clone_uri:
391 raise Exception("This repository doesn't have a clone uri")
391 raise Exception("This repository doesn't have a clone uri")
392
392
393 repo = dbrepo.scm_instance(cache=False)
393 repo = dbrepo.scm_instance(cache=False)
394 # TODO: marcink fix this an re-enable since we need common logic
394 # TODO: marcink fix this an re-enable since we need common logic
395 # for hg/git remove hooks so we don't trigger them on fetching
395 # for hg/git remove hooks so we don't trigger them on fetching
396 # commits from remote
396 # commits from remote
397 repo.config.clear_section('hooks')
397 repo.config.clear_section('hooks')
398
398
399 repo_name = dbrepo.repo_name
399 repo_name = dbrepo.repo_name
400 try:
400 try:
401 # TODO: we need to make sure those operations call proper hooks !
401 # TODO: we need to make sure those operations call proper hooks !
402 repo.pull(clone_uri)
402 repo.pull(clone_uri)
403
403
404 self.mark_for_invalidation(repo_name)
404 self.mark_for_invalidation(repo_name)
405 except Exception:
405 except Exception:
406 log.error(traceback.format_exc())
406 log.error(traceback.format_exc())
407 raise
407 raise
408
408
409 def commit_change(self, repo, repo_name, commit, user, author, message,
409 def commit_change(self, repo, repo_name, commit, user, author, message,
410 content, f_path):
410 content, f_path):
411 """
411 """
412 Commits changes
412 Commits changes
413
413
414 :param repo: SCM instance
414 :param repo: SCM instance
415
415
416 """
416 """
417 user = self._get_user(user)
417 user = self._get_user(user)
418
418
419 # decoding here will force that we have proper encoded values
419 # decoding here will force that we have proper encoded values
420 # in any other case this will throw exceptions and deny commit
420 # in any other case this will throw exceptions and deny commit
421 content = safe_str(content)
421 content = safe_str(content)
422 path = safe_str(f_path)
422 path = safe_str(f_path)
423 # message and author needs to be unicode
423 # message and author needs to be unicode
424 # proper backend should then translate that into required type
424 # proper backend should then translate that into required type
425 message = safe_unicode(message)
425 message = safe_unicode(message)
426 author = safe_unicode(author)
426 author = safe_unicode(author)
427 imc = repo.in_memory_commit
427 imc = repo.in_memory_commit
428 imc.change(FileNode(path, content, mode=commit.get_file_mode(f_path)))
428 imc.change(FileNode(path, content, mode=commit.get_file_mode(f_path)))
429 try:
429 try:
430 # TODO: handle pre-push action !
430 # TODO: handle pre-push action !
431 tip = imc.commit(
431 tip = imc.commit(
432 message=message, author=author, parents=[commit],
432 message=message, author=author, parents=[commit],
433 branch=commit.branch)
433 branch=commit.branch)
434 except Exception as e:
434 except Exception as e:
435 log.error(traceback.format_exc())
435 log.error(traceback.format_exc())
436 raise IMCCommitError(str(e))
436 raise IMCCommitError(str(e))
437 finally:
437 finally:
438 # always clear caches, if commit fails we want fresh object also
438 # always clear caches, if commit fails we want fresh object also
439 self.mark_for_invalidation(repo_name)
439 self.mark_for_invalidation(repo_name)
440
440
441 # We trigger the post-push action
441 # We trigger the post-push action
442 hooks_utils.trigger_post_push_hook(
442 hooks_utils.trigger_post_push_hook(
443 username=user.username, action='push_local', repo_name=repo_name,
443 username=user.username, action='push_local', repo_name=repo_name,
444 repo_alias=repo.alias, commit_ids=[tip.raw_id])
444 repo_alias=repo.alias, commit_ids=[tip.raw_id])
445 return tip
445 return tip
446
446
447 def _sanitize_path(self, f_path):
447 def _sanitize_path(self, f_path):
448 if f_path.startswith('/') or f_path.startswith('./') or '../' in f_path:
448 if f_path.startswith('/') or f_path.startswith('./') or '../' in f_path:
449 raise NonRelativePathError('%s is not an relative path' % f_path)
449 raise NonRelativePathError('%s is not an relative path' % f_path)
450 if f_path:
450 if f_path:
451 f_path = os.path.normpath(f_path)
451 f_path = os.path.normpath(f_path)
452 return f_path
452 return f_path
453
453
454 def get_dirnode_metadata(self, commit, dir_node):
454 def get_dirnode_metadata(self, commit, dir_node):
455 if not dir_node.is_dir():
455 if not dir_node.is_dir():
456 return []
456 return []
457
457
458 data = []
458 data = []
459 for node in dir_node:
459 for node in dir_node:
460 if not node.is_file():
460 if not node.is_file():
461 # we skip file-nodes
461 # we skip file-nodes
462 continue
462 continue
463
463
464 last_commit = node.last_commit
464 last_commit = node.last_commit
465 last_commit_date = last_commit.date
465 last_commit_date = last_commit.date
466 data.append({
466 data.append({
467 'name': node.name,
467 'name': node.name,
468 'size': h.format_byte_size_binary(node.size),
468 'size': h.format_byte_size_binary(node.size),
469 'modified_at': h.format_date(last_commit_date),
469 'modified_at': h.format_date(last_commit_date),
470 'modified_ts': last_commit_date.isoformat(),
470 'modified_ts': last_commit_date.isoformat(),
471 'revision': last_commit.revision,
471 'revision': last_commit.revision,
472 'short_id': last_commit.short_id,
472 'short_id': last_commit.short_id,
473 'message': h.escape(last_commit.message),
473 'message': h.escape(last_commit.message),
474 'author': h.escape(last_commit.author),
474 'author': h.escape(last_commit.author),
475 'user_profile': h.gravatar_with_user(last_commit.author),
475 'user_profile': h.gravatar_with_user(last_commit.author),
476 })
476 })
477
477
478 return data
478 return data
479
479
480 def get_nodes(self, repo_name, commit_id, root_path='/', flat=True,
480 def get_nodes(self, repo_name, commit_id, root_path='/', flat=True,
481 extended_info=False, content=False):
481 extended_info=False, content=False, max_file_bytes=None):
482 """
482 """
483 recursive walk in root dir and return a set of all path in that dir
483 recursive walk in root dir and return a set of all path in that dir
484 based on repository walk function
484 based on repository walk function
485
485
486 :param repo_name: name of repository
486 :param repo_name: name of repository
487 :param commit_id: commit id for which to list nodes
487 :param commit_id: commit id for which to list nodes
488 :param root_path: root path to list
488 :param root_path: root path to list
489 :param flat: return as a list, if False returns a dict with description
489 :param flat: return as a list, if False returns a dict with description
490 :param max_file_bytes: will not return file contents over this limit
490
491
491 """
492 """
492 _files = list()
493 _files = list()
493 _dirs = list()
494 _dirs = list()
494 try:
495 try:
495 _repo = self._get_repo(repo_name)
496 _repo = self._get_repo(repo_name)
496 commit = _repo.scm_instance().get_commit(commit_id=commit_id)
497 commit = _repo.scm_instance().get_commit(commit_id=commit_id)
497 root_path = root_path.lstrip('/')
498 root_path = root_path.lstrip('/')
498 for __, dirs, files in commit.walk(root_path):
499 for __, dirs, files in commit.walk(root_path):
499 for f in files:
500 for f in files:
500 _content = None
501 _content = None
501 _data = f.unicode_path
502 _data = f.unicode_path
503 over_size_limit = (max_file_bytes is not None
504 and f.size > max_file_bytes)
502
505
503 if not flat:
506 if not flat:
504 _data = {
507 _data = {
505 "name": f.unicode_path,
508 "name": f.unicode_path,
506 "type": "file",
509 "type": "file",
507 }
510 }
508 if extended_info:
511 if extended_info:
509 _data.update({
512 _data.update({
510 "md5": f.md5,
513 "md5": f.md5,
511 "binary": f.is_binary,
514 "binary": f.is_binary,
512 "size": f.size,
515 "size": f.size,
513 "extension": f.extension,
516 "extension": f.extension,
514 "mimetype": f.mimetype,
517 "mimetype": f.mimetype,
515 "lines": f.lines()[0]
518 "lines": f.lines()[0]
516 })
519 })
517
520
518 if content:
521 if content:
519 full_content = None
522 full_content = None
520 if not f.is_binary:
523 if not f.is_binary and not over_size_limit:
521 full_content = safe_str(f.content)
524 full_content = safe_str(f.content)
522
525
523 _data.update({
526 _data.update({
524 "content": full_content,
527 "content": full_content,
525 })
528 })
526 _files.append(_data)
529 _files.append(_data)
527 for d in dirs:
530 for d in dirs:
528 _data = d.unicode_path
531 _data = d.unicode_path
529 if not flat:
532 if not flat:
530 _data = {
533 _data = {
531 "name": d.unicode_path,
534 "name": d.unicode_path,
532 "type": "dir",
535 "type": "dir",
533 }
536 }
534 if extended_info:
537 if extended_info:
535 _data.update({
538 _data.update({
536 "md5": None,
539 "md5": None,
537 "binary": None,
540 "binary": None,
538 "size": None,
541 "size": None,
539 "extension": None,
542 "extension": None,
540 })
543 })
541 if content:
544 if content:
542 _data.update({
545 _data.update({
543 "content": None
546 "content": None
544 })
547 })
545 _dirs.append(_data)
548 _dirs.append(_data)
546 except RepositoryError:
549 except RepositoryError:
547 log.debug("Exception in get_nodes", exc_info=True)
550 log.debug("Exception in get_nodes", exc_info=True)
548 raise
551 raise
549
552
550 return _dirs, _files
553 return _dirs, _files
551
554
552 def create_nodes(self, user, repo, message, nodes, parent_commit=None,
555 def create_nodes(self, user, repo, message, nodes, parent_commit=None,
553 author=None, trigger_push_hook=True):
556 author=None, trigger_push_hook=True):
554 """
557 """
555 Commits given multiple nodes into repo
558 Commits given multiple nodes into repo
556
559
557 :param user: RhodeCode User object or user_id, the commiter
560 :param user: RhodeCode User object or user_id, the commiter
558 :param repo: RhodeCode Repository object
561 :param repo: RhodeCode Repository object
559 :param message: commit message
562 :param message: commit message
560 :param nodes: mapping {filename:{'content':content},...}
563 :param nodes: mapping {filename:{'content':content},...}
561 :param parent_commit: parent commit, can be empty than it's
564 :param parent_commit: parent commit, can be empty than it's
562 initial commit
565 initial commit
563 :param author: author of commit, cna be different that commiter
566 :param author: author of commit, cna be different that commiter
564 only for git
567 only for git
565 :param trigger_push_hook: trigger push hooks
568 :param trigger_push_hook: trigger push hooks
566
569
567 :returns: new commited commit
570 :returns: new commited commit
568 """
571 """
569
572
570 user = self._get_user(user)
573 user = self._get_user(user)
571 scm_instance = repo.scm_instance(cache=False)
574 scm_instance = repo.scm_instance(cache=False)
572
575
573 processed_nodes = []
576 processed_nodes = []
574 for f_path in nodes:
577 for f_path in nodes:
575 f_path = self._sanitize_path(f_path)
578 f_path = self._sanitize_path(f_path)
576 content = nodes[f_path]['content']
579 content = nodes[f_path]['content']
577 f_path = safe_str(f_path)
580 f_path = safe_str(f_path)
578 # decoding here will force that we have proper encoded values
581 # decoding here will force that we have proper encoded values
579 # in any other case this will throw exceptions and deny commit
582 # in any other case this will throw exceptions and deny commit
580 if isinstance(content, (basestring,)):
583 if isinstance(content, (basestring,)):
581 content = safe_str(content)
584 content = safe_str(content)
582 elif isinstance(content, (file, cStringIO.OutputType,)):
585 elif isinstance(content, (file, cStringIO.OutputType,)):
583 content = content.read()
586 content = content.read()
584 else:
587 else:
585 raise Exception('Content is of unrecognized type %s' % (
588 raise Exception('Content is of unrecognized type %s' % (
586 type(content)
589 type(content)
587 ))
590 ))
588 processed_nodes.append((f_path, content))
591 processed_nodes.append((f_path, content))
589
592
590 message = safe_unicode(message)
593 message = safe_unicode(message)
591 commiter = user.full_contact
594 commiter = user.full_contact
592 author = safe_unicode(author) if author else commiter
595 author = safe_unicode(author) if author else commiter
593
596
594 imc = scm_instance.in_memory_commit
597 imc = scm_instance.in_memory_commit
595
598
596 if not parent_commit:
599 if not parent_commit:
597 parent_commit = EmptyCommit(alias=scm_instance.alias)
600 parent_commit = EmptyCommit(alias=scm_instance.alias)
598
601
599 if isinstance(parent_commit, EmptyCommit):
602 if isinstance(parent_commit, EmptyCommit):
600 # EmptyCommit means we we're editing empty repository
603 # EmptyCommit means we we're editing empty repository
601 parents = None
604 parents = None
602 else:
605 else:
603 parents = [parent_commit]
606 parents = [parent_commit]
604 # add multiple nodes
607 # add multiple nodes
605 for path, content in processed_nodes:
608 for path, content in processed_nodes:
606 imc.add(FileNode(path, content=content))
609 imc.add(FileNode(path, content=content))
607 # TODO: handle pre push scenario
610 # TODO: handle pre push scenario
608 tip = imc.commit(message=message,
611 tip = imc.commit(message=message,
609 author=author,
612 author=author,
610 parents=parents,
613 parents=parents,
611 branch=parent_commit.branch)
614 branch=parent_commit.branch)
612
615
613 self.mark_for_invalidation(repo.repo_name)
616 self.mark_for_invalidation(repo.repo_name)
614 if trigger_push_hook:
617 if trigger_push_hook:
615 hooks_utils.trigger_post_push_hook(
618 hooks_utils.trigger_post_push_hook(
616 username=user.username, action='push_local',
619 username=user.username, action='push_local',
617 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
620 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
618 commit_ids=[tip.raw_id])
621 commit_ids=[tip.raw_id])
619 return tip
622 return tip
620
623
621 def update_nodes(self, user, repo, message, nodes, parent_commit=None,
624 def update_nodes(self, user, repo, message, nodes, parent_commit=None,
622 author=None, trigger_push_hook=True):
625 author=None, trigger_push_hook=True):
623 user = self._get_user(user)
626 user = self._get_user(user)
624 scm_instance = repo.scm_instance(cache=False)
627 scm_instance = repo.scm_instance(cache=False)
625
628
626 message = safe_unicode(message)
629 message = safe_unicode(message)
627 commiter = user.full_contact
630 commiter = user.full_contact
628 author = safe_unicode(author) if author else commiter
631 author = safe_unicode(author) if author else commiter
629
632
630 imc = scm_instance.in_memory_commit
633 imc = scm_instance.in_memory_commit
631
634
632 if not parent_commit:
635 if not parent_commit:
633 parent_commit = EmptyCommit(alias=scm_instance.alias)
636 parent_commit = EmptyCommit(alias=scm_instance.alias)
634
637
635 if isinstance(parent_commit, EmptyCommit):
638 if isinstance(parent_commit, EmptyCommit):
636 # EmptyCommit means we we're editing empty repository
639 # EmptyCommit means we we're editing empty repository
637 parents = None
640 parents = None
638 else:
641 else:
639 parents = [parent_commit]
642 parents = [parent_commit]
640
643
641 # add multiple nodes
644 # add multiple nodes
642 for _filename, data in nodes.items():
645 for _filename, data in nodes.items():
643 # new filename, can be renamed from the old one, also sanitaze
646 # new filename, can be renamed from the old one, also sanitaze
644 # the path for any hack around relative paths like ../../ etc.
647 # the path for any hack around relative paths like ../../ etc.
645 filename = self._sanitize_path(data['filename'])
648 filename = self._sanitize_path(data['filename'])
646 old_filename = self._sanitize_path(_filename)
649 old_filename = self._sanitize_path(_filename)
647 content = data['content']
650 content = data['content']
648
651
649 filenode = FileNode(old_filename, content=content)
652 filenode = FileNode(old_filename, content=content)
650 op = data['op']
653 op = data['op']
651 if op == 'add':
654 if op == 'add':
652 imc.add(filenode)
655 imc.add(filenode)
653 elif op == 'del':
656 elif op == 'del':
654 imc.remove(filenode)
657 imc.remove(filenode)
655 elif op == 'mod':
658 elif op == 'mod':
656 if filename != old_filename:
659 if filename != old_filename:
657 # TODO: handle renames more efficient, needs vcs lib
660 # TODO: handle renames more efficient, needs vcs lib
658 # changes
661 # changes
659 imc.remove(filenode)
662 imc.remove(filenode)
660 imc.add(FileNode(filename, content=content))
663 imc.add(FileNode(filename, content=content))
661 else:
664 else:
662 imc.change(filenode)
665 imc.change(filenode)
663
666
664 try:
667 try:
665 # TODO: handle pre push scenario
668 # TODO: handle pre push scenario
666 # commit changes
669 # commit changes
667 tip = imc.commit(message=message,
670 tip = imc.commit(message=message,
668 author=author,
671 author=author,
669 parents=parents,
672 parents=parents,
670 branch=parent_commit.branch)
673 branch=parent_commit.branch)
671 except NodeNotChangedError:
674 except NodeNotChangedError:
672 raise
675 raise
673 except Exception as e:
676 except Exception as e:
674 log.exception("Unexpected exception during call to imc.commit")
677 log.exception("Unexpected exception during call to imc.commit")
675 raise IMCCommitError(str(e))
678 raise IMCCommitError(str(e))
676 finally:
679 finally:
677 # always clear caches, if commit fails we want fresh object also
680 # always clear caches, if commit fails we want fresh object also
678 self.mark_for_invalidation(repo.repo_name)
681 self.mark_for_invalidation(repo.repo_name)
679
682
680 if trigger_push_hook:
683 if trigger_push_hook:
681 hooks_utils.trigger_post_push_hook(
684 hooks_utils.trigger_post_push_hook(
682 username=user.username, action='push_local',
685 username=user.username, action='push_local',
683 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
686 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
684 commit_ids=[tip.raw_id])
687 commit_ids=[tip.raw_id])
685
688
686 def delete_nodes(self, user, repo, message, nodes, parent_commit=None,
689 def delete_nodes(self, user, repo, message, nodes, parent_commit=None,
687 author=None, trigger_push_hook=True):
690 author=None, trigger_push_hook=True):
688 """
691 """
689 Deletes given multiple nodes into `repo`
692 Deletes given multiple nodes into `repo`
690
693
691 :param user: RhodeCode User object or user_id, the committer
694 :param user: RhodeCode User object or user_id, the committer
692 :param repo: RhodeCode Repository object
695 :param repo: RhodeCode Repository object
693 :param message: commit message
696 :param message: commit message
694 :param nodes: mapping {filename:{'content':content},...}
697 :param nodes: mapping {filename:{'content':content},...}
695 :param parent_commit: parent commit, can be empty than it's initial
698 :param parent_commit: parent commit, can be empty than it's initial
696 commit
699 commit
697 :param author: author of commit, cna be different that commiter only
700 :param author: author of commit, cna be different that commiter only
698 for git
701 for git
699 :param trigger_push_hook: trigger push hooks
702 :param trigger_push_hook: trigger push hooks
700
703
701 :returns: new commit after deletion
704 :returns: new commit after deletion
702 """
705 """
703
706
704 user = self._get_user(user)
707 user = self._get_user(user)
705 scm_instance = repo.scm_instance(cache=False)
708 scm_instance = repo.scm_instance(cache=False)
706
709
707 processed_nodes = []
710 processed_nodes = []
708 for f_path in nodes:
711 for f_path in nodes:
709 f_path = self._sanitize_path(f_path)
712 f_path = self._sanitize_path(f_path)
710 # content can be empty but for compatabilty it allows same dicts
713 # content can be empty but for compatabilty it allows same dicts
711 # structure as add_nodes
714 # structure as add_nodes
712 content = nodes[f_path].get('content')
715 content = nodes[f_path].get('content')
713 processed_nodes.append((f_path, content))
716 processed_nodes.append((f_path, content))
714
717
715 message = safe_unicode(message)
718 message = safe_unicode(message)
716 commiter = user.full_contact
719 commiter = user.full_contact
717 author = safe_unicode(author) if author else commiter
720 author = safe_unicode(author) if author else commiter
718
721
719 imc = scm_instance.in_memory_commit
722 imc = scm_instance.in_memory_commit
720
723
721 if not parent_commit:
724 if not parent_commit:
722 parent_commit = EmptyCommit(alias=scm_instance.alias)
725 parent_commit = EmptyCommit(alias=scm_instance.alias)
723
726
724 if isinstance(parent_commit, EmptyCommit):
727 if isinstance(parent_commit, EmptyCommit):
725 # EmptyCommit means we we're editing empty repository
728 # EmptyCommit means we we're editing empty repository
726 parents = None
729 parents = None
727 else:
730 else:
728 parents = [parent_commit]
731 parents = [parent_commit]
729 # add multiple nodes
732 # add multiple nodes
730 for path, content in processed_nodes:
733 for path, content in processed_nodes:
731 imc.remove(FileNode(path, content=content))
734 imc.remove(FileNode(path, content=content))
732
735
733 # TODO: handle pre push scenario
736 # TODO: handle pre push scenario
734 tip = imc.commit(message=message,
737 tip = imc.commit(message=message,
735 author=author,
738 author=author,
736 parents=parents,
739 parents=parents,
737 branch=parent_commit.branch)
740 branch=parent_commit.branch)
738
741
739 self.mark_for_invalidation(repo.repo_name)
742 self.mark_for_invalidation(repo.repo_name)
740 if trigger_push_hook:
743 if trigger_push_hook:
741 hooks_utils.trigger_post_push_hook(
744 hooks_utils.trigger_post_push_hook(
742 username=user.username, action='push_local',
745 username=user.username, action='push_local',
743 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
746 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
744 commit_ids=[tip.raw_id])
747 commit_ids=[tip.raw_id])
745 return tip
748 return tip
746
749
747 def strip(self, repo, commit_id, branch):
750 def strip(self, repo, commit_id, branch):
748 scm_instance = repo.scm_instance(cache=False)
751 scm_instance = repo.scm_instance(cache=False)
749 scm_instance.config.clear_section('hooks')
752 scm_instance.config.clear_section('hooks')
750 scm_instance.strip(commit_id, branch)
753 scm_instance.strip(commit_id, branch)
751 self.mark_for_invalidation(repo.repo_name)
754 self.mark_for_invalidation(repo.repo_name)
752
755
753 def get_unread_journal(self):
756 def get_unread_journal(self):
754 return self.sa.query(UserLog).count()
757 return self.sa.query(UserLog).count()
755
758
756 def get_repo_landing_revs(self, repo=None):
759 def get_repo_landing_revs(self, repo=None):
757 """
760 """
758 Generates select option with tags branches and bookmarks (for hg only)
761 Generates select option with tags branches and bookmarks (for hg only)
759 grouped by type
762 grouped by type
760
763
761 :param repo:
764 :param repo:
762 """
765 """
763
766
764 hist_l = []
767 hist_l = []
765 choices = []
768 choices = []
766 repo = self._get_repo(repo)
769 repo = self._get_repo(repo)
767 hist_l.append(['rev:tip', _('latest tip')])
770 hist_l.append(['rev:tip', _('latest tip')])
768 choices.append('rev:tip')
771 choices.append('rev:tip')
769 if not repo:
772 if not repo:
770 return choices, hist_l
773 return choices, hist_l
771
774
772 repo = repo.scm_instance()
775 repo = repo.scm_instance()
773
776
774 branches_group = (
777 branches_group = (
775 [(u'branch:%s' % safe_unicode(b), safe_unicode(b))
778 [(u'branch:%s' % safe_unicode(b), safe_unicode(b))
776 for b in repo.branches],
779 for b in repo.branches],
777 _("Branches"))
780 _("Branches"))
778 hist_l.append(branches_group)
781 hist_l.append(branches_group)
779 choices.extend([x[0] for x in branches_group[0]])
782 choices.extend([x[0] for x in branches_group[0]])
780
783
781 if repo.alias == 'hg':
784 if repo.alias == 'hg':
782 bookmarks_group = (
785 bookmarks_group = (
783 [(u'book:%s' % safe_unicode(b), safe_unicode(b))
786 [(u'book:%s' % safe_unicode(b), safe_unicode(b))
784 for b in repo.bookmarks],
787 for b in repo.bookmarks],
785 _("Bookmarks"))
788 _("Bookmarks"))
786 hist_l.append(bookmarks_group)
789 hist_l.append(bookmarks_group)
787 choices.extend([x[0] for x in bookmarks_group[0]])
790 choices.extend([x[0] for x in bookmarks_group[0]])
788
791
789 tags_group = (
792 tags_group = (
790 [(u'tag:%s' % safe_unicode(t), safe_unicode(t))
793 [(u'tag:%s' % safe_unicode(t), safe_unicode(t))
791 for t in repo.tags],
794 for t in repo.tags],
792 _("Tags"))
795 _("Tags"))
793 hist_l.append(tags_group)
796 hist_l.append(tags_group)
794 choices.extend([x[0] for x in tags_group[0]])
797 choices.extend([x[0] for x in tags_group[0]])
795
798
796 return choices, hist_l
799 return choices, hist_l
797
800
798 def install_git_hook(self, repo, force_create=False):
801 def install_git_hook(self, repo, force_create=False):
799 """
802 """
800 Creates a rhodecode hook inside a git repository
803 Creates a rhodecode hook inside a git repository
801
804
802 :param repo: Instance of VCS repo
805 :param repo: Instance of VCS repo
803 :param force_create: Create even if same name hook exists
806 :param force_create: Create even if same name hook exists
804 """
807 """
805
808
806 loc = os.path.join(repo.path, 'hooks')
809 loc = os.path.join(repo.path, 'hooks')
807 if not repo.bare:
810 if not repo.bare:
808 loc = os.path.join(repo.path, '.git', 'hooks')
811 loc = os.path.join(repo.path, '.git', 'hooks')
809 if not os.path.isdir(loc):
812 if not os.path.isdir(loc):
810 os.makedirs(loc, mode=0777)
813 os.makedirs(loc, mode=0777)
811
814
812 tmpl_post = pkg_resources.resource_string(
815 tmpl_post = pkg_resources.resource_string(
813 'rhodecode', '/'.join(
816 'rhodecode', '/'.join(
814 ('config', 'hook_templates', 'git_post_receive.py.tmpl')))
817 ('config', 'hook_templates', 'git_post_receive.py.tmpl')))
815 tmpl_pre = pkg_resources.resource_string(
818 tmpl_pre = pkg_resources.resource_string(
816 'rhodecode', '/'.join(
819 'rhodecode', '/'.join(
817 ('config', 'hook_templates', 'git_pre_receive.py.tmpl')))
820 ('config', 'hook_templates', 'git_pre_receive.py.tmpl')))
818
821
819 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
822 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
820 _hook_file = os.path.join(loc, '%s-receive' % h_type)
823 _hook_file = os.path.join(loc, '%s-receive' % h_type)
821 log.debug('Installing git hook in repo %s', repo)
824 log.debug('Installing git hook in repo %s', repo)
822 _rhodecode_hook = _check_rhodecode_hook(_hook_file)
825 _rhodecode_hook = _check_rhodecode_hook(_hook_file)
823
826
824 if _rhodecode_hook or force_create:
827 if _rhodecode_hook or force_create:
825 log.debug('writing %s hook file !', h_type)
828 log.debug('writing %s hook file !', h_type)
826 try:
829 try:
827 with open(_hook_file, 'wb') as f:
830 with open(_hook_file, 'wb') as f:
828 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
831 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
829 tmpl = tmpl.replace('_ENV_', sys.executable)
832 tmpl = tmpl.replace('_ENV_', sys.executable)
830 f.write(tmpl)
833 f.write(tmpl)
831 os.chmod(_hook_file, 0755)
834 os.chmod(_hook_file, 0755)
832 except IOError:
835 except IOError:
833 log.exception('error writing hook file %s', _hook_file)
836 log.exception('error writing hook file %s', _hook_file)
834 else:
837 else:
835 log.debug('skipping writing hook file')
838 log.debug('skipping writing hook file')
836
839
837 def install_svn_hooks(self, repo, force_create=False):
840 def install_svn_hooks(self, repo, force_create=False):
838 """
841 """
839 Creates rhodecode hooks inside a svn repository
842 Creates rhodecode hooks inside a svn repository
840
843
841 :param repo: Instance of VCS repo
844 :param repo: Instance of VCS repo
842 :param force_create: Create even if same name hook exists
845 :param force_create: Create even if same name hook exists
843 """
846 """
844 hooks_path = os.path.join(repo.path, 'hooks')
847 hooks_path = os.path.join(repo.path, 'hooks')
845 if not os.path.isdir(hooks_path):
848 if not os.path.isdir(hooks_path):
846 os.makedirs(hooks_path)
849 os.makedirs(hooks_path)
847 post_commit_tmpl = pkg_resources.resource_string(
850 post_commit_tmpl = pkg_resources.resource_string(
848 'rhodecode', '/'.join(
851 'rhodecode', '/'.join(
849 ('config', 'hook_templates', 'svn_post_commit_hook.py.tmpl')))
852 ('config', 'hook_templates', 'svn_post_commit_hook.py.tmpl')))
850 pre_commit_template = pkg_resources.resource_string(
853 pre_commit_template = pkg_resources.resource_string(
851 'rhodecode', '/'.join(
854 'rhodecode', '/'.join(
852 ('config', 'hook_templates', 'svn_pre_commit_hook.py.tmpl')))
855 ('config', 'hook_templates', 'svn_pre_commit_hook.py.tmpl')))
853 templates = {
856 templates = {
854 'post-commit': post_commit_tmpl,
857 'post-commit': post_commit_tmpl,
855 'pre-commit': pre_commit_template
858 'pre-commit': pre_commit_template
856 }
859 }
857 for filename in templates:
860 for filename in templates:
858 _hook_file = os.path.join(hooks_path, filename)
861 _hook_file = os.path.join(hooks_path, filename)
859 _rhodecode_hook = _check_rhodecode_hook(_hook_file)
862 _rhodecode_hook = _check_rhodecode_hook(_hook_file)
860 if _rhodecode_hook or force_create:
863 if _rhodecode_hook or force_create:
861 log.debug('writing %s hook file !', filename)
864 log.debug('writing %s hook file !', filename)
862 template = templates[filename]
865 template = templates[filename]
863 try:
866 try:
864 with open(_hook_file, 'wb') as f:
867 with open(_hook_file, 'wb') as f:
865 template = template.replace(
868 template = template.replace(
866 '_TMPL_', rhodecode.__version__)
869 '_TMPL_', rhodecode.__version__)
867 template = template.replace('_ENV_', sys.executable)
870 template = template.replace('_ENV_', sys.executable)
868 f.write(template)
871 f.write(template)
869 os.chmod(_hook_file, 0755)
872 os.chmod(_hook_file, 0755)
870 except IOError:
873 except IOError:
871 log.exception('error writing hook file %s', filename)
874 log.exception('error writing hook file %s', filename)
872 else:
875 else:
873 log.debug('skipping writing hook file')
876 log.debug('skipping writing hook file')
874
877
875 def install_hooks(self, repo, repo_type):
878 def install_hooks(self, repo, repo_type):
876 if repo_type == 'git':
879 if repo_type == 'git':
877 self.install_git_hook(repo)
880 self.install_git_hook(repo)
878 elif repo_type == 'svn':
881 elif repo_type == 'svn':
879 self.install_svn_hooks(repo)
882 self.install_svn_hooks(repo)
880
883
881 def get_server_info(self, environ=None):
884 def get_server_info(self, environ=None):
882 import platform
885 import platform
883 import rhodecode
886 import rhodecode
884 import pkg_resources
887 import pkg_resources
885 from rhodecode.model.meta import Base as sql_base, Session
888 from rhodecode.model.meta import Base as sql_base, Session
886 from sqlalchemy.engine import url
889 from sqlalchemy.engine import url
887 from rhodecode.lib.base import get_server_ip_addr, get_server_port
890 from rhodecode.lib.base import get_server_ip_addr, get_server_port
888 from rhodecode.lib.vcs.backends.git import discover_git_version
891 from rhodecode.lib.vcs.backends.git import discover_git_version
889 from rhodecode.model.gist import GIST_STORE_LOC
892 from rhodecode.model.gist import GIST_STORE_LOC
890
893
891 try:
894 try:
892 # cygwin cannot have yet psutil support.
895 # cygwin cannot have yet psutil support.
893 import psutil
896 import psutil
894 except ImportError:
897 except ImportError:
895 psutil = None
898 psutil = None
896
899
897 environ = environ or {}
900 environ = environ or {}
898 _NA = 'NOT AVAILABLE'
901 _NA = 'NOT AVAILABLE'
899 _memory = _NA
902 _memory = _NA
900 _uptime = _NA
903 _uptime = _NA
901 _boot_time = _NA
904 _boot_time = _NA
902 _cpu = _NA
905 _cpu = _NA
903 _disk = dict(percent=0, used=0, total=0, error='')
906 _disk = dict(percent=0, used=0, total=0, error='')
904 _load = {'1_min': _NA, '5_min': _NA, '15_min': _NA}
907 _load = {'1_min': _NA, '5_min': _NA, '15_min': _NA}
905
908
906 model = VcsSettingsModel()
909 model = VcsSettingsModel()
907 storage_path = model.get_repos_location()
910 storage_path = model.get_repos_location()
908 gist_storage_path = os.path.join(storage_path, GIST_STORE_LOC)
911 gist_storage_path = os.path.join(storage_path, GIST_STORE_LOC)
909 archive_storage_path = rhodecode.CONFIG.get('archive_cache_dir', '')
912 archive_storage_path = rhodecode.CONFIG.get('archive_cache_dir', '')
910 search_index_storage_path = rhodecode.CONFIG.get('search.location', '')
913 search_index_storage_path = rhodecode.CONFIG.get('search.location', '')
911
914
912 if psutil:
915 if psutil:
913 # disk storage
916 # disk storage
914 try:
917 try:
915 _disk = dict(psutil.disk_usage(storage_path)._asdict())
918 _disk = dict(psutil.disk_usage(storage_path)._asdict())
916 except Exception as e:
919 except Exception as e:
917 log.exception('Failed to fetch disk info')
920 log.exception('Failed to fetch disk info')
918 _disk = {'percent': 0, 'used': 0, 'total': 0, 'error': str(e)}
921 _disk = {'percent': 0, 'used': 0, 'total': 0, 'error': str(e)}
919
922
920 # memory
923 # memory
921 _memory = dict(psutil.virtual_memory()._asdict())
924 _memory = dict(psutil.virtual_memory()._asdict())
922 _memory['percent2'] = psutil._common.usage_percent(
925 _memory['percent2'] = psutil._common.usage_percent(
923 (_memory['total'] - _memory['free']),
926 (_memory['total'] - _memory['free']),
924 _memory['total'], 1)
927 _memory['total'], 1)
925
928
926 # load averages
929 # load averages
927 if hasattr(psutil.os, 'getloadavg'):
930 if hasattr(psutil.os, 'getloadavg'):
928 _load = dict(zip(
931 _load = dict(zip(
929 ['1_min', '5_min', '15_min'], psutil.os.getloadavg()))
932 ['1_min', '5_min', '15_min'], psutil.os.getloadavg()))
930 _uptime = time.time() - psutil.boot_time()
933 _uptime = time.time() - psutil.boot_time()
931 _boot_time = psutil.boot_time()
934 _boot_time = psutil.boot_time()
932 _cpu = psutil.cpu_percent(0.5)
935 _cpu = psutil.cpu_percent(0.5)
933
936
934 mods = dict([(p.project_name, p.version)
937 mods = dict([(p.project_name, p.version)
935 for p in pkg_resources.working_set])
938 for p in pkg_resources.working_set])
936
939
937 def get_storage_size(storage_path):
940 def get_storage_size(storage_path):
938 sizes = []
941 sizes = []
939 for file_ in os.listdir(storage_path):
942 for file_ in os.listdir(storage_path):
940 storage_file = os.path.join(storage_path, file_)
943 storage_file = os.path.join(storage_path, file_)
941 if os.path.isfile(storage_file):
944 if os.path.isfile(storage_file):
942 try:
945 try:
943 sizes.append(os.path.getsize(storage_file))
946 sizes.append(os.path.getsize(storage_file))
944 except OSError:
947 except OSError:
945 log.exception('Failed to get size of storage file %s',
948 log.exception('Failed to get size of storage file %s',
946 storage_file)
949 storage_file)
947 pass
950 pass
948
951
949 return sum(sizes)
952 return sum(sizes)
950
953
951 # archive cache storage
954 # archive cache storage
952 _disk_archive = {'percent': 0, 'used': 0, 'total': 0}
955 _disk_archive = {'percent': 0, 'used': 0, 'total': 0}
953 try:
956 try:
954 archive_storage_path_exists = os.path.isdir(
957 archive_storage_path_exists = os.path.isdir(
955 archive_storage_path)
958 archive_storage_path)
956 if archive_storage_path and archive_storage_path_exists:
959 if archive_storage_path and archive_storage_path_exists:
957 used = get_storage_size(archive_storage_path)
960 used = get_storage_size(archive_storage_path)
958 _disk_archive.update({
961 _disk_archive.update({
959 'used': used,
962 'used': used,
960 'total': used,
963 'total': used,
961 })
964 })
962 except Exception as e:
965 except Exception as e:
963 log.exception('failed to fetch archive cache storage')
966 log.exception('failed to fetch archive cache storage')
964 _disk_archive['error'] = str(e)
967 _disk_archive['error'] = str(e)
965
968
966 # search index storage
969 # search index storage
967 _disk_index = {'percent': 0, 'used': 0, 'total': 0}
970 _disk_index = {'percent': 0, 'used': 0, 'total': 0}
968 try:
971 try:
969 search_index_storage_path_exists = os.path.isdir(
972 search_index_storage_path_exists = os.path.isdir(
970 search_index_storage_path)
973 search_index_storage_path)
971 if search_index_storage_path_exists:
974 if search_index_storage_path_exists:
972 used = get_storage_size(search_index_storage_path)
975 used = get_storage_size(search_index_storage_path)
973 _disk_index.update({
976 _disk_index.update({
974 'percent': 100,
977 'percent': 100,
975 'used': used,
978 'used': used,
976 'total': used,
979 'total': used,
977 })
980 })
978 except Exception as e:
981 except Exception as e:
979 log.exception('failed to fetch search index storage')
982 log.exception('failed to fetch search index storage')
980 _disk_index['error'] = str(e)
983 _disk_index['error'] = str(e)
981
984
982 # gist storage
985 # gist storage
983 _disk_gist = {'percent': 0, 'used': 0, 'total': 0, 'items': 0}
986 _disk_gist = {'percent': 0, 'used': 0, 'total': 0, 'items': 0}
984 try:
987 try:
985 items_count = 0
988 items_count = 0
986 used = 0
989 used = 0
987 for root, dirs, files in os.walk(safe_str(gist_storage_path)):
990 for root, dirs, files in os.walk(safe_str(gist_storage_path)):
988 if root == gist_storage_path:
991 if root == gist_storage_path:
989 items_count = len(dirs)
992 items_count = len(dirs)
990
993
991 for f in files:
994 for f in files:
992 try:
995 try:
993 used += os.path.getsize(os.path.join(root, f))
996 used += os.path.getsize(os.path.join(root, f))
994 except OSError:
997 except OSError:
995 pass
998 pass
996 _disk_gist.update({
999 _disk_gist.update({
997 'percent': 100,
1000 'percent': 100,
998 'used': used,
1001 'used': used,
999 'total': used,
1002 'total': used,
1000 'items': items_count
1003 'items': items_count
1001 })
1004 })
1002 except Exception as e:
1005 except Exception as e:
1003 log.exception('failed to fetch gist storage items')
1006 log.exception('failed to fetch gist storage items')
1004 _disk_gist['error'] = str(e)
1007 _disk_gist['error'] = str(e)
1005
1008
1006 # GIT info
1009 # GIT info
1007 git_ver = discover_git_version()
1010 git_ver = discover_git_version()
1008
1011
1009 # SVN info
1012 # SVN info
1010 # TODO: johbo: Add discover_svn_version to replace this code.
1013 # TODO: johbo: Add discover_svn_version to replace this code.
1011 try:
1014 try:
1012 import svn.core
1015 import svn.core
1013 svn_ver = svn.core.SVN_VERSION
1016 svn_ver = svn.core.SVN_VERSION
1014 except ImportError:
1017 except ImportError:
1015 svn_ver = None
1018 svn_ver = None
1016
1019
1017 # DB stuff
1020 # DB stuff
1018 db_info = url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
1021 db_info = url.make_url(rhodecode.CONFIG['sqlalchemy.db1.url'])
1019 db_type = db_info.__to_string__()
1022 db_type = db_info.__to_string__()
1020 try:
1023 try:
1021 engine = sql_base.metadata.bind
1024 engine = sql_base.metadata.bind
1022 db_server_info = engine.dialect._get_server_version_info(
1025 db_server_info = engine.dialect._get_server_version_info(
1023 Session.connection(bind=engine))
1026 Session.connection(bind=engine))
1024 db_version = '%s %s' % (db_info.drivername,
1027 db_version = '%s %s' % (db_info.drivername,
1025 '.'.join(map(str, db_server_info)))
1028 '.'.join(map(str, db_server_info)))
1026 except Exception:
1029 except Exception:
1027 log.exception('failed to fetch db version')
1030 log.exception('failed to fetch db version')
1028 db_version = '%s %s' % (db_info.drivername, '?')
1031 db_version = '%s %s' % (db_info.drivername, '?')
1029
1032
1030 db_migrate = DbMigrateVersion.query().filter(
1033 db_migrate = DbMigrateVersion.query().filter(
1031 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
1034 DbMigrateVersion.repository_id == 'rhodecode_db_migrations').one()
1032 db_migrate_version = db_migrate.version
1035 db_migrate_version = db_migrate.version
1033
1036
1034 info = {
1037 info = {
1035 'py_version': ' '.join(platform._sys_version()),
1038 'py_version': ' '.join(platform._sys_version()),
1036 'py_path': sys.executable,
1039 'py_path': sys.executable,
1037 'py_modules': sorted(mods.items(), key=lambda k: k[0].lower()),
1040 'py_modules': sorted(mods.items(), key=lambda k: k[0].lower()),
1038
1041
1039 'platform': safe_unicode(platform.platform()),
1042 'platform': safe_unicode(platform.platform()),
1040 'storage': storage_path,
1043 'storage': storage_path,
1041 'archive_storage': archive_storage_path,
1044 'archive_storage': archive_storage_path,
1042 'index_storage': search_index_storage_path,
1045 'index_storage': search_index_storage_path,
1043 'gist_storage': gist_storage_path,
1046 'gist_storage': gist_storage_path,
1044
1047
1045
1048
1046 'db_type': db_type,
1049 'db_type': db_type,
1047 'db_version': db_version,
1050 'db_version': db_version,
1048 'db_migrate_version': db_migrate_version,
1051 'db_migrate_version': db_migrate_version,
1049
1052
1050 'rhodecode_version': rhodecode.__version__,
1053 'rhodecode_version': rhodecode.__version__,
1051 'rhodecode_config_ini': rhodecode.CONFIG.get('__file__'),
1054 'rhodecode_config_ini': rhodecode.CONFIG.get('__file__'),
1052 'server_ip': '%s:%s' % (
1055 'server_ip': '%s:%s' % (
1053 get_server_ip_addr(environ, log_errors=False),
1056 get_server_ip_addr(environ, log_errors=False),
1054 get_server_port(environ)
1057 get_server_port(environ)
1055 ),
1058 ),
1056 'server_id': rhodecode.CONFIG.get('instance_id'),
1059 'server_id': rhodecode.CONFIG.get('instance_id'),
1057
1060
1058 'git_version': safe_unicode(git_ver),
1061 'git_version': safe_unicode(git_ver),
1059 'hg_version': mods.get('mercurial'),
1062 'hg_version': mods.get('mercurial'),
1060 'svn_version': svn_ver,
1063 'svn_version': svn_ver,
1061
1064
1062 'uptime': _uptime,
1065 'uptime': _uptime,
1063 'boot_time': _boot_time,
1066 'boot_time': _boot_time,
1064 'load': _load,
1067 'load': _load,
1065 'cpu': _cpu,
1068 'cpu': _cpu,
1066 'memory': _memory,
1069 'memory': _memory,
1067 'disk': _disk,
1070 'disk': _disk,
1068 'disk_archive': _disk_archive,
1071 'disk_archive': _disk_archive,
1069 'disk_gist': _disk_gist,
1072 'disk_gist': _disk_gist,
1070 'disk_index': _disk_index,
1073 'disk_index': _disk_index,
1071 }
1074 }
1072 return info
1075 return info
1073
1076
1074
1077
1075 def _check_rhodecode_hook(hook_path):
1078 def _check_rhodecode_hook(hook_path):
1076 """
1079 """
1077 Check if the hook was created by RhodeCode
1080 Check if the hook was created by RhodeCode
1078 """
1081 """
1079 if not os.path.exists(hook_path):
1082 if not os.path.exists(hook_path):
1080 return True
1083 return True
1081
1084
1082 log.debug('hook exists, checking if it is from rhodecode')
1085 log.debug('hook exists, checking if it is from rhodecode')
1083 hook_content = _read_hook(hook_path)
1086 hook_content = _read_hook(hook_path)
1084 matches = re.search(r'(?:RC_HOOK_VER)\s*=\s*(.*)', hook_content)
1087 matches = re.search(r'(?:RC_HOOK_VER)\s*=\s*(.*)', hook_content)
1085 if matches:
1088 if matches:
1086 try:
1089 try:
1087 version = matches.groups()[0]
1090 version = matches.groups()[0]
1088 log.debug('got %s, it is rhodecode', version)
1091 log.debug('got %s, it is rhodecode', version)
1089 return True
1092 return True
1090 except Exception:
1093 except Exception:
1091 log.exception("Exception while reading the hook version.")
1094 log.exception("Exception while reading the hook version.")
1092
1095
1093 return False
1096 return False
1094
1097
1095
1098
1096 def _read_hook(hook_path):
1099 def _read_hook(hook_path):
1097 with open(hook_path, 'rb') as f:
1100 with open(hook_path, 'rb') as f:
1098 content = f.read()
1101 content = f.read()
1099 return content No newline at end of file
1102 return content
@@ -1,318 +1,335 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import stat
22 import stat
23 import sys
23 import sys
24
24
25 import pytest
25 import pytest
26 from mock import Mock, patch, DEFAULT
26 from mock import Mock, patch, DEFAULT
27
27
28 import rhodecode
28 import rhodecode
29 from rhodecode.model import db, scm
29 from rhodecode.model import db, scm
30
30
31
31
32 def test_scm_instance_config(backend):
32 def test_scm_instance_config(backend):
33 repo = backend.create_repo()
33 repo = backend.create_repo()
34 with patch.multiple('rhodecode.model.db.Repository',
34 with patch.multiple('rhodecode.model.db.Repository',
35 _get_instance=DEFAULT,
35 _get_instance=DEFAULT,
36 _get_instance_cached=DEFAULT) as mocks:
36 _get_instance_cached=DEFAULT) as mocks:
37 repo.scm_instance()
37 repo.scm_instance()
38 mocks['_get_instance'].assert_called_with(
38 mocks['_get_instance'].assert_called_with(
39 config=None, cache=False)
39 config=None, cache=False)
40
40
41 config = {'some': 'value'}
41 config = {'some': 'value'}
42 repo.scm_instance(config=config)
42 repo.scm_instance(config=config)
43 mocks['_get_instance'].assert_called_with(
43 mocks['_get_instance'].assert_called_with(
44 config=config, cache=False)
44 config=config, cache=False)
45
45
46 with patch.dict(rhodecode.CONFIG, {'vcs_full_cache': 'true'}):
46 with patch.dict(rhodecode.CONFIG, {'vcs_full_cache': 'true'}):
47 repo.scm_instance(config=config)
47 repo.scm_instance(config=config)
48 mocks['_get_instance_cached'].assert_called()
48 mocks['_get_instance_cached'].assert_called()
49
49
50
50
51 def test__get_instance_config(backend):
51 def test__get_instance_config(backend):
52 repo = backend.create_repo()
52 repo = backend.create_repo()
53 vcs_class = Mock()
53 vcs_class = Mock()
54 with patch.multiple('rhodecode.lib.vcs.backends',
54 with patch.multiple('rhodecode.lib.vcs.backends',
55 get_scm=DEFAULT,
55 get_scm=DEFAULT,
56 get_backend=DEFAULT) as mocks:
56 get_backend=DEFAULT) as mocks:
57 mocks['get_scm'].return_value = backend.alias
57 mocks['get_scm'].return_value = backend.alias
58 mocks['get_backend'].return_value = vcs_class
58 mocks['get_backend'].return_value = vcs_class
59 with patch('rhodecode.model.db.Repository._config') as config_mock:
59 with patch('rhodecode.model.db.Repository._config') as config_mock:
60 repo._get_instance()
60 repo._get_instance()
61 vcs_class.assert_called_with(
61 vcs_class.assert_called_with(
62 repo_path=repo.repo_full_path, config=config_mock,
62 repo_path=repo.repo_full_path, config=config_mock,
63 create=False, with_wire={'cache': True})
63 create=False, with_wire={'cache': True})
64
64
65 new_config = {'override': 'old_config'}
65 new_config = {'override': 'old_config'}
66 repo._get_instance(config=new_config)
66 repo._get_instance(config=new_config)
67 vcs_class.assert_called_with(
67 vcs_class.assert_called_with(
68 repo_path=repo.repo_full_path, config=new_config, create=False,
68 repo_path=repo.repo_full_path, config=new_config, create=False,
69 with_wire={'cache': True})
69 with_wire={'cache': True})
70
70
71
71
72 def test_mark_for_invalidation_config(backend):
72 def test_mark_for_invalidation_config(backend):
73 repo = backend.create_repo()
73 repo = backend.create_repo()
74 with patch('rhodecode.model.db.Repository.update_commit_cache') as _mock:
74 with patch('rhodecode.model.db.Repository.update_commit_cache') as _mock:
75 scm.ScmModel().mark_for_invalidation(repo.repo_name)
75 scm.ScmModel().mark_for_invalidation(repo.repo_name)
76 _, kwargs = _mock.call_args
76 _, kwargs = _mock.call_args
77 assert kwargs['config'].__dict__ == repo._config.__dict__
77 assert kwargs['config'].__dict__ == repo._config.__dict__
78
78
79
79
80 def test_mark_for_invalidation_with_delete_updates_last_commit(backend):
80 def test_mark_for_invalidation_with_delete_updates_last_commit(backend):
81 commits = [{'message': 'A'}, {'message': 'B'}]
81 commits = [{'message': 'A'}, {'message': 'B'}]
82 repo = backend.create_repo(commits=commits)
82 repo = backend.create_repo(commits=commits)
83 scm.ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
83 scm.ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
84 assert repo.changeset_cache['revision'] == 1
84 assert repo.changeset_cache['revision'] == 1
85
85
86
86
87 def test_mark_for_invalidation_with_delete_updates_last_commit_empty(backend):
87 def test_mark_for_invalidation_with_delete_updates_last_commit_empty(backend):
88 repo = backend.create_repo()
88 repo = backend.create_repo()
89 scm.ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
89 scm.ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
90 assert repo.changeset_cache['revision'] == -1
90 assert repo.changeset_cache['revision'] == -1
91
91
92
92
93 def test_strip_with_multiple_heads(backend_hg):
93 def test_strip_with_multiple_heads(backend_hg):
94 commits = [
94 commits = [
95 {'message': 'A'},
95 {'message': 'A'},
96 {'message': 'a'},
96 {'message': 'a'},
97 {'message': 'b'},
97 {'message': 'b'},
98 {'message': 'B', 'parents': ['A']},
98 {'message': 'B', 'parents': ['A']},
99 {'message': 'a1'},
99 {'message': 'a1'},
100 ]
100 ]
101 repo = backend_hg.create_repo(commits=commits)
101 repo = backend_hg.create_repo(commits=commits)
102 commit_ids = backend_hg.commit_ids
102 commit_ids = backend_hg.commit_ids
103
103
104 model = scm.ScmModel()
104 model = scm.ScmModel()
105 model.strip(repo, commit_ids['b'], branch=None)
105 model.strip(repo, commit_ids['b'], branch=None)
106
106
107 vcs_repo = repo.scm_instance()
107 vcs_repo = repo.scm_instance()
108 rest_commit_ids = [c.raw_id for c in vcs_repo.get_changesets()]
108 rest_commit_ids = [c.raw_id for c in vcs_repo.get_changesets()]
109 assert len(rest_commit_ids) == 4
109 assert len(rest_commit_ids) == 4
110 assert commit_ids['b'] not in rest_commit_ids
110 assert commit_ids['b'] not in rest_commit_ids
111
111
112
112
113 def test_strip_with_single_heads(backend_hg):
113 def test_strip_with_single_heads(backend_hg):
114 commits = [
114 commits = [
115 {'message': 'A'},
115 {'message': 'A'},
116 {'message': 'a'},
116 {'message': 'a'},
117 {'message': 'b'},
117 {'message': 'b'},
118 ]
118 ]
119 repo = backend_hg.create_repo(commits=commits)
119 repo = backend_hg.create_repo(commits=commits)
120 commit_ids = backend_hg.commit_ids
120 commit_ids = backend_hg.commit_ids
121
121
122 model = scm.ScmModel()
122 model = scm.ScmModel()
123 model.strip(repo, commit_ids['b'], branch=None)
123 model.strip(repo, commit_ids['b'], branch=None)
124
124
125 vcs_repo = repo.scm_instance()
125 vcs_repo = repo.scm_instance()
126 rest_commit_ids = [c.raw_id for c in vcs_repo.get_changesets()]
126 rest_commit_ids = [c.raw_id for c in vcs_repo.get_changesets()]
127 assert len(rest_commit_ids) == 2
127 assert len(rest_commit_ids) == 2
128 assert commit_ids['b'] not in rest_commit_ids
128 assert commit_ids['b'] not in rest_commit_ids
129
129
130
130
131 def test_get_nodes_returns_unicode_flat(backend_random):
131 def test_get_nodes_returns_unicode_flat(backend_random):
132 repo = backend_random.repo
132 repo = backend_random.repo
133 directories, files = scm.ScmModel().get_nodes(
133 directories, files = scm.ScmModel().get_nodes(
134 repo.repo_name, repo.get_commit(commit_idx=0).raw_id,
134 repo.repo_name, repo.get_commit(commit_idx=0).raw_id,
135 flat=True)
135 flat=True)
136 assert_contains_only_unicode(directories)
136 assert_contains_only_unicode(directories)
137 assert_contains_only_unicode(files)
137 assert_contains_only_unicode(files)
138
138
139
139
140 def test_get_nodes_returns_unicode_non_flat(backend_random):
140 def test_get_nodes_returns_unicode_non_flat(backend_random):
141 repo = backend_random.repo
141 repo = backend_random.repo
142 directories, files = scm.ScmModel().get_nodes(
142 directories, files = scm.ScmModel().get_nodes(
143 repo.repo_name, repo.get_commit(commit_idx=0).raw_id,
143 repo.repo_name, repo.get_commit(commit_idx=0).raw_id,
144 flat=False)
144 flat=False)
145 # johbo: Checking only the names for now, since that is the critical
145 # johbo: Checking only the names for now, since that is the critical
146 # part.
146 # part.
147 assert_contains_only_unicode([d['name'] for d in directories])
147 assert_contains_only_unicode([d['name'] for d in directories])
148 assert_contains_only_unicode([f['name'] for f in files])
148 assert_contains_only_unicode([f['name'] for f in files])
149
149
150
150
151 def test_get_nodes_max_file_bytes(backend_random):
152 repo = backend_random.repo
153 max_file_bytes = 10
154 directories, files = scm.ScmModel().get_nodes(
155 repo.repo_name, repo.get_commit(commit_idx=0).raw_id, content=True,
156 extended_info=True, flat=False)
157 assert any(file['content'] and len(file['content']) > max_file_bytes
158 for file in files)
159
160 directories, files = scm.ScmModel().get_nodes(
161 repo.repo_name, repo.get_commit(commit_idx=0).raw_id, content=True,
162 extended_info=True, flat=False, max_file_bytes=max_file_bytes)
163 assert all(
164 file['content'] is None if file['size'] > max_file_bytes else True
165 for file in files)
166
167
151 def assert_contains_only_unicode(structure):
168 def assert_contains_only_unicode(structure):
152 assert structure
169 assert structure
153 for value in structure:
170 for value in structure:
154 assert isinstance(value, unicode)
171 assert isinstance(value, unicode)
155
172
156
173
157 @pytest.mark.backends("hg", "git")
174 @pytest.mark.backends("hg", "git")
158 def test_get_non_unicode_reference(backend):
175 def test_get_non_unicode_reference(backend):
159 model = scm.ScmModel()
176 model = scm.ScmModel()
160 non_unicode_list = ["AdΔ±nΔ±".decode("cp1254")]
177 non_unicode_list = ["AdΔ±nΔ±".decode("cp1254")]
161
178
162 def scm_instance():
179 def scm_instance():
163 return Mock(
180 return Mock(
164 branches=non_unicode_list, bookmarks=non_unicode_list,
181 branches=non_unicode_list, bookmarks=non_unicode_list,
165 tags=non_unicode_list, alias=backend.alias)
182 tags=non_unicode_list, alias=backend.alias)
166
183
167 repo = Mock(__class__=db.Repository, scm_instance=scm_instance)
184 repo = Mock(__class__=db.Repository, scm_instance=scm_instance)
168 choices, __ = model.get_repo_landing_revs(repo=repo)
185 choices, __ = model.get_repo_landing_revs(repo=repo)
169 if backend.alias == 'hg':
186 if backend.alias == 'hg':
170 valid_choices = [
187 valid_choices = [
171 'rev:tip', u'branch:Ad\xc4\xb1n\xc4\xb1',
188 'rev:tip', u'branch:Ad\xc4\xb1n\xc4\xb1',
172 u'book:Ad\xc4\xb1n\xc4\xb1', u'tag:Ad\xc4\xb1n\xc4\xb1']
189 u'book:Ad\xc4\xb1n\xc4\xb1', u'tag:Ad\xc4\xb1n\xc4\xb1']
173 else:
190 else:
174 valid_choices = [
191 valid_choices = [
175 'rev:tip', u'branch:Ad\xc4\xb1n\xc4\xb1',
192 'rev:tip', u'branch:Ad\xc4\xb1n\xc4\xb1',
176 u'tag:Ad\xc4\xb1n\xc4\xb1']
193 u'tag:Ad\xc4\xb1n\xc4\xb1']
177
194
178 assert choices == valid_choices
195 assert choices == valid_choices
179
196
180
197
181 class TestInstallSvnHooks(object):
198 class TestInstallSvnHooks(object):
182 HOOK_FILES = ('pre-commit', 'post-commit')
199 HOOK_FILES = ('pre-commit', 'post-commit')
183
200
184 def test_new_hooks_are_created(self, backend_svn):
201 def test_new_hooks_are_created(self, backend_svn):
185 model = scm.ScmModel()
202 model = scm.ScmModel()
186 repo = backend_svn.create_repo()
203 repo = backend_svn.create_repo()
187 vcs_repo = repo.scm_instance()
204 vcs_repo = repo.scm_instance()
188 model.install_svn_hooks(vcs_repo)
205 model.install_svn_hooks(vcs_repo)
189
206
190 hooks_path = os.path.join(vcs_repo.path, 'hooks')
207 hooks_path = os.path.join(vcs_repo.path, 'hooks')
191 assert os.path.isdir(hooks_path)
208 assert os.path.isdir(hooks_path)
192 for file_name in self.HOOK_FILES:
209 for file_name in self.HOOK_FILES:
193 file_path = os.path.join(hooks_path, file_name)
210 file_path = os.path.join(hooks_path, file_name)
194 self._check_hook_file_mode(file_path)
211 self._check_hook_file_mode(file_path)
195 self._check_hook_file_content(file_path)
212 self._check_hook_file_content(file_path)
196
213
197 def test_rc_hooks_are_replaced(self, backend_svn):
214 def test_rc_hooks_are_replaced(self, backend_svn):
198 model = scm.ScmModel()
215 model = scm.ScmModel()
199 repo = backend_svn.create_repo()
216 repo = backend_svn.create_repo()
200 vcs_repo = repo.scm_instance()
217 vcs_repo = repo.scm_instance()
201 hooks_path = os.path.join(vcs_repo.path, 'hooks')
218 hooks_path = os.path.join(vcs_repo.path, 'hooks')
202 file_paths = [os.path.join(hooks_path, f) for f in self.HOOK_FILES]
219 file_paths = [os.path.join(hooks_path, f) for f in self.HOOK_FILES]
203
220
204 for file_path in file_paths:
221 for file_path in file_paths:
205 self._create_fake_hook(
222 self._create_fake_hook(
206 file_path, content="RC_HOOK_VER = 'abcde'\n")
223 file_path, content="RC_HOOK_VER = 'abcde'\n")
207
224
208 model.install_svn_hooks(vcs_repo)
225 model.install_svn_hooks(vcs_repo)
209
226
210 for file_path in file_paths:
227 for file_path in file_paths:
211 self._check_hook_file_content(file_path)
228 self._check_hook_file_content(file_path)
212
229
213 def test_non_rc_hooks_are_not_replaced_without_force_create(
230 def test_non_rc_hooks_are_not_replaced_without_force_create(
214 self, backend_svn):
231 self, backend_svn):
215 model = scm.ScmModel()
232 model = scm.ScmModel()
216 repo = backend_svn.create_repo()
233 repo = backend_svn.create_repo()
217 vcs_repo = repo.scm_instance()
234 vcs_repo = repo.scm_instance()
218 hooks_path = os.path.join(vcs_repo.path, 'hooks')
235 hooks_path = os.path.join(vcs_repo.path, 'hooks')
219 file_paths = [os.path.join(hooks_path, f) for f in self.HOOK_FILES]
236 file_paths = [os.path.join(hooks_path, f) for f in self.HOOK_FILES]
220 non_rc_content = "exit 0\n"
237 non_rc_content = "exit 0\n"
221
238
222 for file_path in file_paths:
239 for file_path in file_paths:
223 self._create_fake_hook(file_path, content=non_rc_content)
240 self._create_fake_hook(file_path, content=non_rc_content)
224
241
225 model.install_svn_hooks(vcs_repo)
242 model.install_svn_hooks(vcs_repo)
226
243
227 for file_path in file_paths:
244 for file_path in file_paths:
228 with open(file_path, 'rt') as hook_file:
245 with open(file_path, 'rt') as hook_file:
229 content = hook_file.read()
246 content = hook_file.read()
230 assert content == non_rc_content
247 assert content == non_rc_content
231
248
232 def test_non_rc_hooks_are_replaced_with_force_create(self, backend_svn):
249 def test_non_rc_hooks_are_replaced_with_force_create(self, backend_svn):
233 model = scm.ScmModel()
250 model = scm.ScmModel()
234 repo = backend_svn.create_repo()
251 repo = backend_svn.create_repo()
235 vcs_repo = repo.scm_instance()
252 vcs_repo = repo.scm_instance()
236 hooks_path = os.path.join(vcs_repo.path, 'hooks')
253 hooks_path = os.path.join(vcs_repo.path, 'hooks')
237 file_paths = [os.path.join(hooks_path, f) for f in self.HOOK_FILES]
254 file_paths = [os.path.join(hooks_path, f) for f in self.HOOK_FILES]
238 non_rc_content = "exit 0\n"
255 non_rc_content = "exit 0\n"
239
256
240 for file_path in file_paths:
257 for file_path in file_paths:
241 self._create_fake_hook(file_path, content=non_rc_content)
258 self._create_fake_hook(file_path, content=non_rc_content)
242
259
243 model.install_svn_hooks(vcs_repo, force_create=True)
260 model.install_svn_hooks(vcs_repo, force_create=True)
244
261
245 for file_path in file_paths:
262 for file_path in file_paths:
246 self._check_hook_file_content(file_path)
263 self._check_hook_file_content(file_path)
247
264
248 def _check_hook_file_mode(self, file_path):
265 def _check_hook_file_mode(self, file_path):
249 assert os.path.exists(file_path)
266 assert os.path.exists(file_path)
250 stat_info = os.stat(file_path)
267 stat_info = os.stat(file_path)
251
268
252 file_mode = stat.S_IMODE(stat_info.st_mode)
269 file_mode = stat.S_IMODE(stat_info.st_mode)
253 expected_mode = int('755', 8)
270 expected_mode = int('755', 8)
254 assert expected_mode == file_mode
271 assert expected_mode == file_mode
255
272
256 def _check_hook_file_content(self, file_path):
273 def _check_hook_file_content(self, file_path):
257 with open(file_path, 'rt') as hook_file:
274 with open(file_path, 'rt') as hook_file:
258 content = hook_file.read()
275 content = hook_file.read()
259
276
260 expected_env = '#!{}'.format(sys.executable)
277 expected_env = '#!{}'.format(sys.executable)
261 expected_rc_version = "\nRC_HOOK_VER = '{}'\n".format(
278 expected_rc_version = "\nRC_HOOK_VER = '{}'\n".format(
262 rhodecode.__version__)
279 rhodecode.__version__)
263 assert content.strip().startswith(expected_env)
280 assert content.strip().startswith(expected_env)
264 assert expected_rc_version in content
281 assert expected_rc_version in content
265
282
266 def _create_fake_hook(self, file_path, content):
283 def _create_fake_hook(self, file_path, content):
267 with open(file_path, 'w') as hook_file:
284 with open(file_path, 'w') as hook_file:
268 hook_file.write(content)
285 hook_file.write(content)
269
286
270
287
271 class TestCheckRhodecodeHook(object):
288 class TestCheckRhodecodeHook(object):
272
289
273 @patch('os.path.exists', Mock(return_value=False))
290 @patch('os.path.exists', Mock(return_value=False))
274 def test_returns_true_when_no_hook_found(self):
291 def test_returns_true_when_no_hook_found(self):
275 result = scm._check_rhodecode_hook('/tmp/fake_hook_file.py')
292 result = scm._check_rhodecode_hook('/tmp/fake_hook_file.py')
276 assert result
293 assert result
277
294
278 @pytest.mark.parametrize("file_content, expected_result", [
295 @pytest.mark.parametrize("file_content, expected_result", [
279 ("RC_HOOK_VER = '3.3.3'\n", True),
296 ("RC_HOOK_VER = '3.3.3'\n", True),
280 ("RC_HOOK = '3.3.3'\n", False),
297 ("RC_HOOK = '3.3.3'\n", False),
281 ])
298 ])
282 @patch('os.path.exists', Mock(return_value=True))
299 @patch('os.path.exists', Mock(return_value=True))
283 def test_signatures(self, file_content, expected_result):
300 def test_signatures(self, file_content, expected_result):
284 hook_content_patcher = patch.object(
301 hook_content_patcher = patch.object(
285 scm, '_read_hook', return_value=file_content)
302 scm, '_read_hook', return_value=file_content)
286 with hook_content_patcher:
303 with hook_content_patcher:
287 result = scm._check_rhodecode_hook('/tmp/fake_hook_file.py')
304 result = scm._check_rhodecode_hook('/tmp/fake_hook_file.py')
288
305
289 assert result is expected_result
306 assert result is expected_result
290
307
291
308
292 class TestInstallHooks(object):
309 class TestInstallHooks(object):
293 def test_hooks_are_installed_for_git_repo(self, backend_git):
310 def test_hooks_are_installed_for_git_repo(self, backend_git):
294 repo = backend_git.create_repo()
311 repo = backend_git.create_repo()
295 model = scm.ScmModel()
312 model = scm.ScmModel()
296 scm_repo = repo.scm_instance()
313 scm_repo = repo.scm_instance()
297 with patch.object(model, 'install_git_hook') as hooks_mock:
314 with patch.object(model, 'install_git_hook') as hooks_mock:
298 model.install_hooks(scm_repo, repo_type='git')
315 model.install_hooks(scm_repo, repo_type='git')
299 hooks_mock.assert_called_once_with(scm_repo)
316 hooks_mock.assert_called_once_with(scm_repo)
300
317
301 def test_hooks_are_installed_for_svn_repo(self, backend_svn):
318 def test_hooks_are_installed_for_svn_repo(self, backend_svn):
302 repo = backend_svn.create_repo()
319 repo = backend_svn.create_repo()
303 scm_repo = repo.scm_instance()
320 scm_repo = repo.scm_instance()
304 model = scm.ScmModel()
321 model = scm.ScmModel()
305 with patch.object(scm.ScmModel, 'install_svn_hooks') as hooks_mock:
322 with patch.object(scm.ScmModel, 'install_svn_hooks') as hooks_mock:
306 model.install_hooks(scm_repo, repo_type='svn')
323 model.install_hooks(scm_repo, repo_type='svn')
307 hooks_mock.assert_called_once_with(scm_repo)
324 hooks_mock.assert_called_once_with(scm_repo)
308
325
309 @pytest.mark.parametrize('hook_method', [
326 @pytest.mark.parametrize('hook_method', [
310 'install_svn_hooks',
327 'install_svn_hooks',
311 'install_git_hook'])
328 'install_git_hook'])
312 def test_mercurial_doesnt_trigger_hooks(self, backend_hg, hook_method):
329 def test_mercurial_doesnt_trigger_hooks(self, backend_hg, hook_method):
313 repo = backend_hg.create_repo()
330 repo = backend_hg.create_repo()
314 scm_repo = repo.scm_instance()
331 scm_repo = repo.scm_instance()
315 model = scm.ScmModel()
332 model = scm.ScmModel()
316 with patch.object(scm.ScmModel, hook_method) as hooks_mock:
333 with patch.object(scm.ScmModel, hook_method) as hooks_mock:
317 model.install_hooks(scm_repo, repo_type='hg')
334 model.install_hooks(scm_repo, repo_type='hg')
318 assert hooks_mock.call_count == 0
335 assert hooks_mock.call_count == 0
General Comments 0
You need to be logged in to leave comments. Login now