##// END OF EJS Templates
api: added maintenance command into API.
marcink -
r1742:1fa46212 default
parent child Browse files
Show More
@@ -1,1988 +1,2051 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import time
23 23
24 24 import rhodecode
25 25 from rhodecode.api import (
26 26 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
27 27 from rhodecode.api.utils import (
28 28 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
29 29 get_user_group_or_error, get_user_or_error, validate_repo_permissions,
30 30 get_perm_or_error, parse_args, get_origin, build_commit_data,
31 31 validate_set_owner_permissions)
32 from rhodecode.lib import repo_maintenance
32 33 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
33 34 from rhodecode.lib.utils2 import str2bool, time_to_datetime
34 35 from rhodecode.lib.ext_json import json
35 36 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
36 37 from rhodecode.model.changeset_status import ChangesetStatusModel
37 38 from rhodecode.model.comment import CommentsModel
38 39 from rhodecode.model.db import (
39 40 Session, ChangesetStatus, RepositoryField, Repository, RepoGroup,
40 41 ChangesetComment)
41 42 from rhodecode.model.repo import RepoModel
42 43 from rhodecode.model.scm import ScmModel, RepoList
43 44 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
44 45 from rhodecode.model import validation_schema
45 46 from rhodecode.model.validation_schema.schemas import repo_schema
46 47
47 48 log = logging.getLogger(__name__)
48 49
49 50
50 51 @jsonrpc_method()
51 52 def get_repo(request, apiuser, repoid, cache=Optional(True)):
52 53 """
53 54 Gets an existing repository by its name or repository_id.
54 55
55 56 The members section so the output returns users groups or users
56 57 associated with that repository.
57 58
58 59 This command can only be run using an |authtoken| with admin rights,
59 60 or users with at least read rights to the |repo|.
60 61
61 62 :param apiuser: This is filled automatically from the |authtoken|.
62 63 :type apiuser: AuthUser
63 64 :param repoid: The repository name or repository id.
64 65 :type repoid: str or int
65 66 :param cache: use the cached value for last changeset
66 67 :type: cache: Optional(bool)
67 68
68 69 Example output:
69 70
70 71 .. code-block:: bash
71 72
72 73 {
73 74 "error": null,
74 75 "id": <repo_id>,
75 76 "result": {
76 77 "clone_uri": null,
77 78 "created_on": "timestamp",
78 79 "description": "repo description",
79 80 "enable_downloads": false,
80 81 "enable_locking": false,
81 82 "enable_statistics": false,
82 83 "followers": [
83 84 {
84 85 "active": true,
85 86 "admin": false,
86 87 "api_key": "****************************************",
87 88 "api_keys": [
88 89 "****************************************"
89 90 ],
90 91 "email": "user@example.com",
91 92 "emails": [
92 93 "user@example.com"
93 94 ],
94 95 "extern_name": "rhodecode",
95 96 "extern_type": "rhodecode",
96 97 "firstname": "username",
97 98 "ip_addresses": [],
98 99 "language": null,
99 100 "last_login": "2015-09-16T17:16:35.854",
100 101 "lastname": "surname",
101 102 "user_id": <user_id>,
102 103 "username": "name"
103 104 }
104 105 ],
105 106 "fork_of": "parent-repo",
106 107 "landing_rev": [
107 108 "rev",
108 109 "tip"
109 110 ],
110 111 "last_changeset": {
111 112 "author": "User <user@example.com>",
112 113 "branch": "default",
113 114 "date": "timestamp",
114 115 "message": "last commit message",
115 116 "parents": [
116 117 {
117 118 "raw_id": "commit-id"
118 119 }
119 120 ],
120 121 "raw_id": "commit-id",
121 122 "revision": <revision number>,
122 123 "short_id": "short id"
123 124 },
124 125 "lock_reason": null,
125 126 "locked_by": null,
126 127 "locked_date": null,
127 128 "members": [
128 129 {
129 130 "name": "super-admin-name",
130 131 "origin": "super-admin",
131 132 "permission": "repository.admin",
132 133 "type": "user"
133 134 },
134 135 {
135 136 "name": "owner-name",
136 137 "origin": "owner",
137 138 "permission": "repository.admin",
138 139 "type": "user"
139 140 },
140 141 {
141 142 "name": "user-group-name",
142 143 "origin": "permission",
143 144 "permission": "repository.write",
144 145 "type": "user_group"
145 146 }
146 147 ],
147 148 "owner": "owner-name",
148 149 "permissions": [
149 150 {
150 151 "name": "super-admin-name",
151 152 "origin": "super-admin",
152 153 "permission": "repository.admin",
153 154 "type": "user"
154 155 },
155 156 {
156 157 "name": "owner-name",
157 158 "origin": "owner",
158 159 "permission": "repository.admin",
159 160 "type": "user"
160 161 },
161 162 {
162 163 "name": "user-group-name",
163 164 "origin": "permission",
164 165 "permission": "repository.write",
165 166 "type": "user_group"
166 167 }
167 168 ],
168 169 "private": true,
169 170 "repo_id": 676,
170 171 "repo_name": "user-group/repo-name",
171 172 "repo_type": "hg"
172 173 }
173 174 }
174 175 """
175 176
176 177 repo = get_repo_or_error(repoid)
177 178 cache = Optional.extract(cache)
178 179
179 180 include_secrets = False
180 181 if has_superadmin_permission(apiuser):
181 182 include_secrets = True
182 183 else:
183 184 # check if we have at least read permission for this repo !
184 185 _perms = (
185 186 'repository.admin', 'repository.write', 'repository.read',)
186 187 validate_repo_permissions(apiuser, repoid, repo, _perms)
187 188
188 189 permissions = []
189 190 for _user in repo.permissions():
190 191 user_data = {
191 192 'name': _user.username,
192 193 'permission': _user.permission,
193 194 'origin': get_origin(_user),
194 195 'type': "user",
195 196 }
196 197 permissions.append(user_data)
197 198
198 199 for _user_group in repo.permission_user_groups():
199 200 user_group_data = {
200 201 'name': _user_group.users_group_name,
201 202 'permission': _user_group.permission,
202 203 'origin': get_origin(_user_group),
203 204 'type': "user_group",
204 205 }
205 206 permissions.append(user_group_data)
206 207
207 208 following_users = [
208 209 user.user.get_api_data(include_secrets=include_secrets)
209 210 for user in repo.followers]
210 211
211 212 if not cache:
212 213 repo.update_commit_cache()
213 214 data = repo.get_api_data(include_secrets=include_secrets)
214 215 data['members'] = permissions # TODO: this should be deprecated soon
215 216 data['permissions'] = permissions
216 217 data['followers'] = following_users
217 218 return data
218 219
219 220
220 221 @jsonrpc_method()
221 222 def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)):
222 223 """
223 224 Lists all existing repositories.
224 225
225 226 This command can only be run using an |authtoken| with admin rights,
226 227 or users with at least read rights to |repos|.
227 228
228 229 :param apiuser: This is filled automatically from the |authtoken|.
229 230 :type apiuser: AuthUser
230 231 :param root: specify root repository group to fetch repositories.
231 232 filters the returned repositories to be members of given root group.
232 233 :type root: Optional(None)
233 234 :param traverse: traverse given root into subrepositories. With this flag
234 235 set to False, it will only return top-level repositories from `root`.
235 236 if root is empty it will return just top-level repositories.
236 237 :type traverse: Optional(True)
237 238
238 239
239 240 Example output:
240 241
241 242 .. code-block:: bash
242 243
243 244 id : <id_given_in_input>
244 245 result: [
245 246 {
246 247 "repo_id" : "<repo_id>",
247 248 "repo_name" : "<reponame>"
248 249 "repo_type" : "<repo_type>",
249 250 "clone_uri" : "<clone_uri>",
250 251 "private": : "<bool>",
251 252 "created_on" : "<datetimecreated>",
252 253 "description" : "<description>",
253 254 "landing_rev": "<landing_rev>",
254 255 "owner": "<repo_owner>",
255 256 "fork_of": "<name_of_fork_parent>",
256 257 "enable_downloads": "<bool>",
257 258 "enable_locking": "<bool>",
258 259 "enable_statistics": "<bool>",
259 260 },
260 261 ...
261 262 ]
262 263 error: null
263 264 """
264 265
265 266 include_secrets = has_superadmin_permission(apiuser)
266 267 _perms = ('repository.read', 'repository.write', 'repository.admin',)
267 268 extras = {'user': apiuser}
268 269
269 270 root = Optional.extract(root)
270 271 traverse = Optional.extract(traverse, binary=True)
271 272
272 273 if root:
273 274 # verify parent existance, if it's empty return an error
274 275 parent = RepoGroup.get_by_group_name(root)
275 276 if not parent:
276 277 raise JSONRPCError(
277 278 'Root repository group `{}` does not exist'.format(root))
278 279
279 280 if traverse:
280 281 repos = RepoModel().get_repos_for_root(root=root, traverse=traverse)
281 282 else:
282 283 repos = RepoModel().get_repos_for_root(root=parent)
283 284 else:
284 285 if traverse:
285 286 repos = RepoModel().get_all()
286 287 else:
287 288 # return just top-level
288 289 repos = RepoModel().get_repos_for_root(root=None)
289 290
290 291 repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras)
291 292 return [repo.get_api_data(include_secrets=include_secrets)
292 293 for repo in repo_list]
293 294
294 295
295 296 @jsonrpc_method()
296 297 def get_repo_changeset(request, apiuser, repoid, revision,
297 298 details=Optional('basic')):
298 299 """
299 300 Returns information about a changeset.
300 301
301 302 Additionally parameters define the amount of details returned by
302 303 this function.
303 304
304 305 This command can only be run using an |authtoken| with admin rights,
305 306 or users with at least read rights to the |repo|.
306 307
307 308 :param apiuser: This is filled automatically from the |authtoken|.
308 309 :type apiuser: AuthUser
309 310 :param repoid: The repository name or repository id
310 311 :type repoid: str or int
311 312 :param revision: revision for which listing should be done
312 313 :type revision: str
313 314 :param details: details can be 'basic|extended|full' full gives diff
314 315 info details like the diff itself, and number of changed files etc.
315 316 :type details: Optional(str)
316 317
317 318 """
318 319 repo = get_repo_or_error(repoid)
319 320 if not has_superadmin_permission(apiuser):
320 321 _perms = (
321 322 'repository.admin', 'repository.write', 'repository.read',)
322 323 validate_repo_permissions(apiuser, repoid, repo, _perms)
323 324
324 325 changes_details = Optional.extract(details)
325 326 _changes_details_types = ['basic', 'extended', 'full']
326 327 if changes_details not in _changes_details_types:
327 328 raise JSONRPCError(
328 329 'ret_type must be one of %s' % (
329 330 ','.join(_changes_details_types)))
330 331
331 332 pre_load = ['author', 'branch', 'date', 'message', 'parents',
332 333 'status', '_commit', '_file_paths']
333 334
334 335 try:
335 336 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
336 337 except TypeError as e:
337 338 raise JSONRPCError(e.message)
338 339 _cs_json = cs.__json__()
339 340 _cs_json['diff'] = build_commit_data(cs, changes_details)
340 341 if changes_details == 'full':
341 342 _cs_json['refs'] = {
342 343 'branches': [cs.branch],
343 344 'bookmarks': getattr(cs, 'bookmarks', []),
344 345 'tags': cs.tags
345 346 }
346 347 return _cs_json
347 348
348 349
349 350 @jsonrpc_method()
350 351 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
351 352 details=Optional('basic')):
352 353 """
353 354 Returns a set of commits limited by the number starting
354 355 from the `start_rev` option.
355 356
356 357 Additional parameters define the amount of details returned by this
357 358 function.
358 359
359 360 This command can only be run using an |authtoken| with admin rights,
360 361 or users with at least read rights to |repos|.
361 362
362 363 :param apiuser: This is filled automatically from the |authtoken|.
363 364 :type apiuser: AuthUser
364 365 :param repoid: The repository name or repository ID.
365 366 :type repoid: str or int
366 367 :param start_rev: The starting revision from where to get changesets.
367 368 :type start_rev: str
368 369 :param limit: Limit the number of commits to this amount
369 370 :type limit: str or int
370 371 :param details: Set the level of detail returned. Valid option are:
371 372 ``basic``, ``extended`` and ``full``.
372 373 :type details: Optional(str)
373 374
374 375 .. note::
375 376
376 377 Setting the parameter `details` to the value ``full`` is extensive
377 378 and returns details like the diff itself, and the number
378 379 of changed files.
379 380
380 381 """
381 382 repo = get_repo_or_error(repoid)
382 383 if not has_superadmin_permission(apiuser):
383 384 _perms = (
384 385 'repository.admin', 'repository.write', 'repository.read',)
385 386 validate_repo_permissions(apiuser, repoid, repo, _perms)
386 387
387 388 changes_details = Optional.extract(details)
388 389 _changes_details_types = ['basic', 'extended', 'full']
389 390 if changes_details not in _changes_details_types:
390 391 raise JSONRPCError(
391 392 'ret_type must be one of %s' % (
392 393 ','.join(_changes_details_types)))
393 394
394 395 limit = int(limit)
395 396 pre_load = ['author', 'branch', 'date', 'message', 'parents',
396 397 'status', '_commit', '_file_paths']
397 398
398 399 vcs_repo = repo.scm_instance()
399 400 # SVN needs a special case to distinguish its index and commit id
400 401 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
401 402 start_rev = vcs_repo.commit_ids[0]
402 403
403 404 try:
404 405 commits = vcs_repo.get_commits(
405 406 start_id=start_rev, pre_load=pre_load)
406 407 except TypeError as e:
407 408 raise JSONRPCError(e.message)
408 409 except Exception:
409 410 log.exception('Fetching of commits failed')
410 411 raise JSONRPCError('Error occurred during commit fetching')
411 412
412 413 ret = []
413 414 for cnt, commit in enumerate(commits):
414 415 if cnt >= limit != -1:
415 416 break
416 417 _cs_json = commit.__json__()
417 418 _cs_json['diff'] = build_commit_data(commit, changes_details)
418 419 if changes_details == 'full':
419 420 _cs_json['refs'] = {
420 421 'branches': [commit.branch],
421 422 'bookmarks': getattr(commit, 'bookmarks', []),
422 423 'tags': commit.tags
423 424 }
424 425 ret.append(_cs_json)
425 426 return ret
426 427
427 428
428 429 @jsonrpc_method()
429 430 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
430 431 ret_type=Optional('all'), details=Optional('basic'),
431 432 max_file_bytes=Optional(None)):
432 433 """
433 434 Returns a list of nodes and children in a flat list for a given
434 435 path at given revision.
435 436
436 437 It's possible to specify ret_type to show only `files` or `dirs`.
437 438
438 439 This command can only be run using an |authtoken| with admin rights,
439 440 or users with at least read rights to |repos|.
440 441
441 442 :param apiuser: This is filled automatically from the |authtoken|.
442 443 :type apiuser: AuthUser
443 444 :param repoid: The repository name or repository ID.
444 445 :type repoid: str or int
445 446 :param revision: The revision for which listing should be done.
446 447 :type revision: str
447 448 :param root_path: The path from which to start displaying.
448 449 :type root_path: str
449 450 :param ret_type: Set the return type. Valid options are
450 451 ``all`` (default), ``files`` and ``dirs``.
451 452 :type ret_type: Optional(str)
452 453 :param details: Returns extended information about nodes, such as
453 454 md5, binary, and or content. The valid options are ``basic`` and
454 455 ``full``.
455 456 :type details: Optional(str)
456 457 :param max_file_bytes: Only return file content under this file size bytes
457 458 :type details: Optional(int)
458 459
459 460 Example output:
460 461
461 462 .. code-block:: bash
462 463
463 464 id : <id_given_in_input>
464 465 result: [
465 466 {
466 467 "name" : "<name>"
467 468 "type" : "<type>",
468 469 "binary": "<true|false>" (only in extended mode)
469 470 "md5" : "<md5 of file content>" (only in extended mode)
470 471 },
471 472 ...
472 473 ]
473 474 error: null
474 475 """
475 476
476 477 repo = get_repo_or_error(repoid)
477 478 if not has_superadmin_permission(apiuser):
478 479 _perms = (
479 480 'repository.admin', 'repository.write', 'repository.read',)
480 481 validate_repo_permissions(apiuser, repoid, repo, _perms)
481 482
482 483 ret_type = Optional.extract(ret_type)
483 484 details = Optional.extract(details)
484 485 _extended_types = ['basic', 'full']
485 486 if details not in _extended_types:
486 487 raise JSONRPCError(
487 488 'ret_type must be one of %s' % (','.join(_extended_types)))
488 489 extended_info = False
489 490 content = False
490 491 if details == 'basic':
491 492 extended_info = True
492 493
493 494 if details == 'full':
494 495 extended_info = content = True
495 496
496 497 _map = {}
497 498 try:
498 499 # check if repo is not empty by any chance, skip quicker if it is.
499 500 _scm = repo.scm_instance()
500 501 if _scm.is_empty():
501 502 return []
502 503
503 504 _d, _f = ScmModel().get_nodes(
504 505 repo, revision, root_path, flat=False,
505 506 extended_info=extended_info, content=content,
506 507 max_file_bytes=max_file_bytes)
507 508 _map = {
508 509 'all': _d + _f,
509 510 'files': _f,
510 511 'dirs': _d,
511 512 }
512 513 return _map[ret_type]
513 514 except KeyError:
514 515 raise JSONRPCError(
515 516 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
516 517 except Exception:
517 518 log.exception("Exception occurred while trying to get repo nodes")
518 519 raise JSONRPCError(
519 520 'failed to get repo: `%s` nodes' % repo.repo_name
520 521 )
521 522
522 523
523 524 @jsonrpc_method()
524 525 def get_repo_refs(request, apiuser, repoid):
525 526 """
526 527 Returns a dictionary of current references. It returns
527 528 bookmarks, branches, closed_branches, and tags for given repository
528 529
529 530 It's possible to specify ret_type to show only `files` or `dirs`.
530 531
531 532 This command can only be run using an |authtoken| with admin rights,
532 533 or users with at least read rights to |repos|.
533 534
534 535 :param apiuser: This is filled automatically from the |authtoken|.
535 536 :type apiuser: AuthUser
536 537 :param repoid: The repository name or repository ID.
537 538 :type repoid: str or int
538 539
539 540 Example output:
540 541
541 542 .. code-block:: bash
542 543
543 544 id : <id_given_in_input>
544 545 "result": {
545 546 "bookmarks": {
546 547 "dev": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
547 548 "master": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
548 549 },
549 550 "branches": {
550 551 "default": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
551 552 "stable": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
552 553 },
553 554 "branches_closed": {},
554 555 "tags": {
555 556 "tip": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
556 557 "v4.4.0": "1232313f9e6adac5ce5399c2a891dc1e72b79022",
557 558 "v4.4.1": "cbb9f1d329ae5768379cdec55a62ebdd546c4e27",
558 559 "v4.4.2": "24ffe44a27fcd1c5b6936144e176b9f6dd2f3a17",
559 560 }
560 561 }
561 562 error: null
562 563 """
563 564
564 565 repo = get_repo_or_error(repoid)
565 566 if not has_superadmin_permission(apiuser):
566 567 _perms = ('repository.admin', 'repository.write', 'repository.read',)
567 568 validate_repo_permissions(apiuser, repoid, repo, _perms)
568 569
569 570 try:
570 571 # check if repo is not empty by any chance, skip quicker if it is.
571 572 vcs_instance = repo.scm_instance()
572 573 refs = vcs_instance.refs()
573 574 return refs
574 575 except Exception:
575 576 log.exception("Exception occurred while trying to get repo refs")
576 577 raise JSONRPCError(
577 578 'failed to get repo: `%s` references' % repo.repo_name
578 579 )
579 580
580 581
581 582 @jsonrpc_method()
582 583 def create_repo(
583 584 request, apiuser, repo_name, repo_type,
584 585 owner=Optional(OAttr('apiuser')),
585 586 description=Optional(''),
586 587 private=Optional(False),
587 588 clone_uri=Optional(None),
588 589 landing_rev=Optional('rev:tip'),
589 590 enable_statistics=Optional(False),
590 591 enable_locking=Optional(False),
591 592 enable_downloads=Optional(False),
592 593 copy_permissions=Optional(False)):
593 594 """
594 595 Creates a repository.
595 596
596 597 * If the repository name contains "/", repository will be created inside
597 598 a repository group or nested repository groups
598 599
599 600 For example "foo/bar/repo1" will create |repo| called "repo1" inside
600 601 group "foo/bar". You have to have permissions to access and write to
601 602 the last repository group ("bar" in this example)
602 603
603 604 This command can only be run using an |authtoken| with at least
604 605 permissions to create repositories, or write permissions to
605 606 parent repository groups.
606 607
607 608 :param apiuser: This is filled automatically from the |authtoken|.
608 609 :type apiuser: AuthUser
609 610 :param repo_name: Set the repository name.
610 611 :type repo_name: str
611 612 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
612 613 :type repo_type: str
613 614 :param owner: user_id or username
614 615 :type owner: Optional(str)
615 616 :param description: Set the repository description.
616 617 :type description: Optional(str)
617 618 :param private: set repository as private
618 619 :type private: bool
619 620 :param clone_uri: set clone_uri
620 621 :type clone_uri: str
621 622 :param landing_rev: <rev_type>:<rev>
622 623 :type landing_rev: str
623 624 :param enable_locking:
624 625 :type enable_locking: bool
625 626 :param enable_downloads:
626 627 :type enable_downloads: bool
627 628 :param enable_statistics:
628 629 :type enable_statistics: bool
629 630 :param copy_permissions: Copy permission from group in which the
630 631 repository is being created.
631 632 :type copy_permissions: bool
632 633
633 634
634 635 Example output:
635 636
636 637 .. code-block:: bash
637 638
638 639 id : <id_given_in_input>
639 640 result: {
640 641 "msg": "Created new repository `<reponame>`",
641 642 "success": true,
642 643 "task": "<celery task id or None if done sync>"
643 644 }
644 645 error: null
645 646
646 647
647 648 Example error output:
648 649
649 650 .. code-block:: bash
650 651
651 652 id : <id_given_in_input>
652 653 result : null
653 654 error : {
654 655 'failed to create repository `<repo_name>`'
655 656 }
656 657
657 658 """
658 659
659 660 owner = validate_set_owner_permissions(apiuser, owner)
660 661
661 662 description = Optional.extract(description)
662 663 copy_permissions = Optional.extract(copy_permissions)
663 664 clone_uri = Optional.extract(clone_uri)
664 665 landing_commit_ref = Optional.extract(landing_rev)
665 666
666 667 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
667 668 if isinstance(private, Optional):
668 669 private = defs.get('repo_private') or Optional.extract(private)
669 670 if isinstance(repo_type, Optional):
670 671 repo_type = defs.get('repo_type')
671 672 if isinstance(enable_statistics, Optional):
672 673 enable_statistics = defs.get('repo_enable_statistics')
673 674 if isinstance(enable_locking, Optional):
674 675 enable_locking = defs.get('repo_enable_locking')
675 676 if isinstance(enable_downloads, Optional):
676 677 enable_downloads = defs.get('repo_enable_downloads')
677 678
678 679 schema = repo_schema.RepoSchema().bind(
679 680 repo_type_options=rhodecode.BACKENDS.keys(),
680 681 # user caller
681 682 user=apiuser)
682 683
683 684 try:
684 685 schema_data = schema.deserialize(dict(
685 686 repo_name=repo_name,
686 687 repo_type=repo_type,
687 688 repo_owner=owner.username,
688 689 repo_description=description,
689 690 repo_landing_commit_ref=landing_commit_ref,
690 691 repo_clone_uri=clone_uri,
691 692 repo_private=private,
692 693 repo_copy_permissions=copy_permissions,
693 694 repo_enable_statistics=enable_statistics,
694 695 repo_enable_downloads=enable_downloads,
695 696 repo_enable_locking=enable_locking))
696 697 except validation_schema.Invalid as err:
697 698 raise JSONRPCValidationError(colander_exc=err)
698 699
699 700 try:
700 701 data = {
701 702 'owner': owner,
702 703 'repo_name': schema_data['repo_group']['repo_name_without_group'],
703 704 'repo_name_full': schema_data['repo_name'],
704 705 'repo_group': schema_data['repo_group']['repo_group_id'],
705 706 'repo_type': schema_data['repo_type'],
706 707 'repo_description': schema_data['repo_description'],
707 708 'repo_private': schema_data['repo_private'],
708 709 'clone_uri': schema_data['repo_clone_uri'],
709 710 'repo_landing_rev': schema_data['repo_landing_commit_ref'],
710 711 'enable_statistics': schema_data['repo_enable_statistics'],
711 712 'enable_locking': schema_data['repo_enable_locking'],
712 713 'enable_downloads': schema_data['repo_enable_downloads'],
713 714 'repo_copy_permissions': schema_data['repo_copy_permissions'],
714 715 }
715 716
716 717 task = RepoModel().create(form_data=data, cur_user=owner)
717 718 from celery.result import BaseAsyncResult
718 719 task_id = None
719 720 if isinstance(task, BaseAsyncResult):
720 721 task_id = task.task_id
721 722 # no commit, it's done in RepoModel, or async via celery
722 723 return {
723 724 'msg': "Created new repository `%s`" % (schema_data['repo_name'],),
724 725 'success': True, # cannot return the repo data here since fork
725 726 # can be done async
726 727 'task': task_id
727 728 }
728 729 except Exception:
729 730 log.exception(
730 731 u"Exception while trying to create the repository %s",
731 732 schema_data['repo_name'])
732 733 raise JSONRPCError(
733 734 'failed to create repository `%s`' % (schema_data['repo_name'],))
734 735
735 736
736 737 @jsonrpc_method()
737 738 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
738 739 description=Optional('')):
739 740 """
740 741 Adds an extra field to a repository.
741 742
742 743 This command can only be run using an |authtoken| with at least
743 744 write permissions to the |repo|.
744 745
745 746 :param apiuser: This is filled automatically from the |authtoken|.
746 747 :type apiuser: AuthUser
747 748 :param repoid: Set the repository name or repository id.
748 749 :type repoid: str or int
749 750 :param key: Create a unique field key for this repository.
750 751 :type key: str
751 752 :param label:
752 753 :type label: Optional(str)
753 754 :param description:
754 755 :type description: Optional(str)
755 756 """
756 757 repo = get_repo_or_error(repoid)
757 758 if not has_superadmin_permission(apiuser):
758 759 _perms = ('repository.admin',)
759 760 validate_repo_permissions(apiuser, repoid, repo, _perms)
760 761
761 762 label = Optional.extract(label) or key
762 763 description = Optional.extract(description)
763 764
764 765 field = RepositoryField.get_by_key_name(key, repo)
765 766 if field:
766 767 raise JSONRPCError('Field with key '
767 768 '`%s` exists for repo `%s`' % (key, repoid))
768 769
769 770 try:
770 771 RepoModel().add_repo_field(repo, key, field_label=label,
771 772 field_desc=description)
772 773 Session().commit()
773 774 return {
774 775 'msg': "Added new repository field `%s`" % (key,),
775 776 'success': True,
776 777 }
777 778 except Exception:
778 779 log.exception("Exception occurred while trying to add field to repo")
779 780 raise JSONRPCError(
780 781 'failed to create new field for repository `%s`' % (repoid,))
781 782
782 783
783 784 @jsonrpc_method()
784 785 def remove_field_from_repo(request, apiuser, repoid, key):
785 786 """
786 787 Removes an extra field from a repository.
787 788
788 789 This command can only be run using an |authtoken| with at least
789 790 write permissions to the |repo|.
790 791
791 792 :param apiuser: This is filled automatically from the |authtoken|.
792 793 :type apiuser: AuthUser
793 794 :param repoid: Set the repository name or repository ID.
794 795 :type repoid: str or int
795 796 :param key: Set the unique field key for this repository.
796 797 :type key: str
797 798 """
798 799
799 800 repo = get_repo_or_error(repoid)
800 801 if not has_superadmin_permission(apiuser):
801 802 _perms = ('repository.admin',)
802 803 validate_repo_permissions(apiuser, repoid, repo, _perms)
803 804
804 805 field = RepositoryField.get_by_key_name(key, repo)
805 806 if not field:
806 807 raise JSONRPCError('Field with key `%s` does not '
807 808 'exists for repo `%s`' % (key, repoid))
808 809
809 810 try:
810 811 RepoModel().delete_repo_field(repo, field_key=key)
811 812 Session().commit()
812 813 return {
813 814 'msg': "Deleted repository field `%s`" % (key,),
814 815 'success': True,
815 816 }
816 817 except Exception:
817 818 log.exception(
818 819 "Exception occurred while trying to delete field from repo")
819 820 raise JSONRPCError(
820 821 'failed to delete field for repository `%s`' % (repoid,))
821 822
822 823
823 824 @jsonrpc_method()
824 825 def update_repo(
825 826 request, apiuser, repoid, repo_name=Optional(None),
826 827 owner=Optional(OAttr('apiuser')), description=Optional(''),
827 828 private=Optional(False), clone_uri=Optional(None),
828 829 landing_rev=Optional('rev:tip'), fork_of=Optional(None),
829 830 enable_statistics=Optional(False),
830 831 enable_locking=Optional(False),
831 832 enable_downloads=Optional(False), fields=Optional('')):
832 833 """
833 834 Updates a repository with the given information.
834 835
835 836 This command can only be run using an |authtoken| with at least
836 837 admin permissions to the |repo|.
837 838
838 839 * If the repository name contains "/", repository will be updated
839 840 accordingly with a repository group or nested repository groups
840 841
841 842 For example repoid=repo-test name="foo/bar/repo-test" will update |repo|
842 843 called "repo-test" and place it inside group "foo/bar".
843 844 You have to have permissions to access and write to the last repository
844 845 group ("bar" in this example)
845 846
846 847 :param apiuser: This is filled automatically from the |authtoken|.
847 848 :type apiuser: AuthUser
848 849 :param repoid: repository name or repository ID.
849 850 :type repoid: str or int
850 851 :param repo_name: Update the |repo| name, including the
851 852 repository group it's in.
852 853 :type repo_name: str
853 854 :param owner: Set the |repo| owner.
854 855 :type owner: str
855 856 :param fork_of: Set the |repo| as fork of another |repo|.
856 857 :type fork_of: str
857 858 :param description: Update the |repo| description.
858 859 :type description: str
859 860 :param private: Set the |repo| as private. (True | False)
860 861 :type private: bool
861 862 :param clone_uri: Update the |repo| clone URI.
862 863 :type clone_uri: str
863 864 :param landing_rev: Set the |repo| landing revision. Default is ``rev:tip``.
864 865 :type landing_rev: str
865 866 :param enable_statistics: Enable statistics on the |repo|, (True | False).
866 867 :type enable_statistics: bool
867 868 :param enable_locking: Enable |repo| locking.
868 869 :type enable_locking: bool
869 870 :param enable_downloads: Enable downloads from the |repo|, (True | False).
870 871 :type enable_downloads: bool
871 872 :param fields: Add extra fields to the |repo|. Use the following
872 873 example format: ``field_key=field_val,field_key2=fieldval2``.
873 874 Escape ', ' with \,
874 875 :type fields: str
875 876 """
876 877
877 878 repo = get_repo_or_error(repoid)
878 879
879 880 include_secrets = False
880 881 if not has_superadmin_permission(apiuser):
881 882 validate_repo_permissions(apiuser, repoid, repo, ('repository.admin',))
882 883 else:
883 884 include_secrets = True
884 885
885 886 updates = dict(
886 887 repo_name=repo_name
887 888 if not isinstance(repo_name, Optional) else repo.repo_name,
888 889
889 890 fork_id=fork_of
890 891 if not isinstance(fork_of, Optional) else repo.fork.repo_name if repo.fork else None,
891 892
892 893 user=owner
893 894 if not isinstance(owner, Optional) else repo.user.username,
894 895
895 896 repo_description=description
896 897 if not isinstance(description, Optional) else repo.description,
897 898
898 899 repo_private=private
899 900 if not isinstance(private, Optional) else repo.private,
900 901
901 902 clone_uri=clone_uri
902 903 if not isinstance(clone_uri, Optional) else repo.clone_uri,
903 904
904 905 repo_landing_rev=landing_rev
905 906 if not isinstance(landing_rev, Optional) else repo._landing_revision,
906 907
907 908 repo_enable_statistics=enable_statistics
908 909 if not isinstance(enable_statistics, Optional) else repo.enable_statistics,
909 910
910 911 repo_enable_locking=enable_locking
911 912 if not isinstance(enable_locking, Optional) else repo.enable_locking,
912 913
913 914 repo_enable_downloads=enable_downloads
914 915 if not isinstance(enable_downloads, Optional) else repo.enable_downloads)
915 916
916 917 ref_choices, _labels = ScmModel().get_repo_landing_revs(repo=repo)
917 918
918 919 schema = repo_schema.RepoSchema().bind(
919 920 repo_type_options=rhodecode.BACKENDS.keys(),
920 921 repo_ref_options=ref_choices,
921 922 # user caller
922 923 user=apiuser,
923 924 old_values=repo.get_api_data())
924 925 try:
925 926 schema_data = schema.deserialize(dict(
926 927 # we save old value, users cannot change type
927 928 repo_type=repo.repo_type,
928 929
929 930 repo_name=updates['repo_name'],
930 931 repo_owner=updates['user'],
931 932 repo_description=updates['repo_description'],
932 933 repo_clone_uri=updates['clone_uri'],
933 934 repo_fork_of=updates['fork_id'],
934 935 repo_private=updates['repo_private'],
935 936 repo_landing_commit_ref=updates['repo_landing_rev'],
936 937 repo_enable_statistics=updates['repo_enable_statistics'],
937 938 repo_enable_downloads=updates['repo_enable_downloads'],
938 939 repo_enable_locking=updates['repo_enable_locking']))
939 940 except validation_schema.Invalid as err:
940 941 raise JSONRPCValidationError(colander_exc=err)
941 942
942 943 # save validated data back into the updates dict
943 944 validated_updates = dict(
944 945 repo_name=schema_data['repo_group']['repo_name_without_group'],
945 946 repo_group=schema_data['repo_group']['repo_group_id'],
946 947
947 948 user=schema_data['repo_owner'],
948 949 repo_description=schema_data['repo_description'],
949 950 repo_private=schema_data['repo_private'],
950 951 clone_uri=schema_data['repo_clone_uri'],
951 952 repo_landing_rev=schema_data['repo_landing_commit_ref'],
952 953 repo_enable_statistics=schema_data['repo_enable_statistics'],
953 954 repo_enable_locking=schema_data['repo_enable_locking'],
954 955 repo_enable_downloads=schema_data['repo_enable_downloads'],
955 956 )
956 957
957 958 if schema_data['repo_fork_of']:
958 959 fork_repo = get_repo_or_error(schema_data['repo_fork_of'])
959 960 validated_updates['fork_id'] = fork_repo.repo_id
960 961
961 962 # extra fields
962 963 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
963 964 if fields:
964 965 validated_updates.update(fields)
965 966
966 967 try:
967 968 RepoModel().update(repo, **validated_updates)
968 969 Session().commit()
969 970 return {
970 971 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
971 972 'repository': repo.get_api_data(include_secrets=include_secrets)
972 973 }
973 974 except Exception:
974 975 log.exception(
975 976 u"Exception while trying to update the repository %s",
976 977 repoid)
977 978 raise JSONRPCError('failed to update repo `%s`' % repoid)
978 979
979 980
980 981 @jsonrpc_method()
981 982 def fork_repo(request, apiuser, repoid, fork_name,
982 983 owner=Optional(OAttr('apiuser')),
983 984 description=Optional(''),
984 985 private=Optional(False),
985 986 clone_uri=Optional(None),
986 987 landing_rev=Optional('rev:tip'),
987 988 copy_permissions=Optional(False)):
988 989 """
989 990 Creates a fork of the specified |repo|.
990 991
991 992 * If the fork_name contains "/", fork will be created inside
992 993 a repository group or nested repository groups
993 994
994 995 For example "foo/bar/fork-repo" will create fork called "fork-repo"
995 996 inside group "foo/bar". You have to have permissions to access and
996 997 write to the last repository group ("bar" in this example)
997 998
998 999 This command can only be run using an |authtoken| with minimum
999 1000 read permissions of the forked repo, create fork permissions for an user.
1000 1001
1001 1002 :param apiuser: This is filled automatically from the |authtoken|.
1002 1003 :type apiuser: AuthUser
1003 1004 :param repoid: Set repository name or repository ID.
1004 1005 :type repoid: str or int
1005 1006 :param fork_name: Set the fork name, including it's repository group membership.
1006 1007 :type fork_name: str
1007 1008 :param owner: Set the fork owner.
1008 1009 :type owner: str
1009 1010 :param description: Set the fork description.
1010 1011 :type description: str
1011 1012 :param copy_permissions: Copy permissions from parent |repo|. The
1012 1013 default is False.
1013 1014 :type copy_permissions: bool
1014 1015 :param private: Make the fork private. The default is False.
1015 1016 :type private: bool
1016 1017 :param landing_rev: Set the landing revision. The default is tip.
1017 1018
1018 1019 Example output:
1019 1020
1020 1021 .. code-block:: bash
1021 1022
1022 1023 id : <id_for_response>
1023 1024 api_key : "<api_key>"
1024 1025 args: {
1025 1026 "repoid" : "<reponame or repo_id>",
1026 1027 "fork_name": "<forkname>",
1027 1028 "owner": "<username or user_id = Optional(=apiuser)>",
1028 1029 "description": "<description>",
1029 1030 "copy_permissions": "<bool>",
1030 1031 "private": "<bool>",
1031 1032 "landing_rev": "<landing_rev>"
1032 1033 }
1033 1034
1034 1035 Example error output:
1035 1036
1036 1037 .. code-block:: bash
1037 1038
1038 1039 id : <id_given_in_input>
1039 1040 result: {
1040 1041 "msg": "Created fork of `<reponame>` as `<forkname>`",
1041 1042 "success": true,
1042 1043 "task": "<celery task id or None if done sync>"
1043 1044 }
1044 1045 error: null
1045 1046
1046 1047 """
1047 1048
1048 1049 repo = get_repo_or_error(repoid)
1049 1050 repo_name = repo.repo_name
1050 1051
1051 1052 if not has_superadmin_permission(apiuser):
1052 1053 # check if we have at least read permission for
1053 1054 # this repo that we fork !
1054 1055 _perms = (
1055 1056 'repository.admin', 'repository.write', 'repository.read')
1056 1057 validate_repo_permissions(apiuser, repoid, repo, _perms)
1057 1058
1058 1059 # check if the regular user has at least fork permissions as well
1059 1060 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
1060 1061 raise JSONRPCForbidden()
1061 1062
1062 1063 # check if user can set owner parameter
1063 1064 owner = validate_set_owner_permissions(apiuser, owner)
1064 1065
1065 1066 description = Optional.extract(description)
1066 1067 copy_permissions = Optional.extract(copy_permissions)
1067 1068 clone_uri = Optional.extract(clone_uri)
1068 1069 landing_commit_ref = Optional.extract(landing_rev)
1069 1070 private = Optional.extract(private)
1070 1071
1071 1072 schema = repo_schema.RepoSchema().bind(
1072 1073 repo_type_options=rhodecode.BACKENDS.keys(),
1073 1074 # user caller
1074 1075 user=apiuser)
1075 1076
1076 1077 try:
1077 1078 schema_data = schema.deserialize(dict(
1078 1079 repo_name=fork_name,
1079 1080 repo_type=repo.repo_type,
1080 1081 repo_owner=owner.username,
1081 1082 repo_description=description,
1082 1083 repo_landing_commit_ref=landing_commit_ref,
1083 1084 repo_clone_uri=clone_uri,
1084 1085 repo_private=private,
1085 1086 repo_copy_permissions=copy_permissions))
1086 1087 except validation_schema.Invalid as err:
1087 1088 raise JSONRPCValidationError(colander_exc=err)
1088 1089
1089 1090 try:
1090 1091 data = {
1091 1092 'fork_parent_id': repo.repo_id,
1092 1093
1093 1094 'repo_name': schema_data['repo_group']['repo_name_without_group'],
1094 1095 'repo_name_full': schema_data['repo_name'],
1095 1096 'repo_group': schema_data['repo_group']['repo_group_id'],
1096 1097 'repo_type': schema_data['repo_type'],
1097 1098 'description': schema_data['repo_description'],
1098 1099 'private': schema_data['repo_private'],
1099 1100 'copy_permissions': schema_data['repo_copy_permissions'],
1100 1101 'landing_rev': schema_data['repo_landing_commit_ref'],
1101 1102 }
1102 1103
1103 1104 task = RepoModel().create_fork(data, cur_user=owner)
1104 1105 # no commit, it's done in RepoModel, or async via celery
1105 1106 from celery.result import BaseAsyncResult
1106 1107 task_id = None
1107 1108 if isinstance(task, BaseAsyncResult):
1108 1109 task_id = task.task_id
1109 1110 return {
1110 1111 'msg': 'Created fork of `%s` as `%s`' % (
1111 1112 repo.repo_name, schema_data['repo_name']),
1112 1113 'success': True, # cannot return the repo data here since fork
1113 1114 # can be done async
1114 1115 'task': task_id
1115 1116 }
1116 1117 except Exception:
1117 1118 log.exception(
1118 1119 u"Exception while trying to create fork %s",
1119 1120 schema_data['repo_name'])
1120 1121 raise JSONRPCError(
1121 1122 'failed to fork repository `%s` as `%s`' % (
1122 1123 repo_name, schema_data['repo_name']))
1123 1124
1124 1125
1125 1126 @jsonrpc_method()
1126 1127 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1127 1128 """
1128 1129 Deletes a repository.
1129 1130
1130 1131 * When the `forks` parameter is set it's possible to detach or delete
1131 1132 forks of deleted repository.
1132 1133
1133 1134 This command can only be run using an |authtoken| with admin
1134 1135 permissions on the |repo|.
1135 1136
1136 1137 :param apiuser: This is filled automatically from the |authtoken|.
1137 1138 :type apiuser: AuthUser
1138 1139 :param repoid: Set the repository name or repository ID.
1139 1140 :type repoid: str or int
1140 1141 :param forks: Set to `detach` or `delete` forks from the |repo|.
1141 1142 :type forks: Optional(str)
1142 1143
1143 1144 Example error output:
1144 1145
1145 1146 .. code-block:: bash
1146 1147
1147 1148 id : <id_given_in_input>
1148 1149 result: {
1149 1150 "msg": "Deleted repository `<reponame>`",
1150 1151 "success": true
1151 1152 }
1152 1153 error: null
1153 1154 """
1154 1155
1155 1156 repo = get_repo_or_error(repoid)
1156 1157 if not has_superadmin_permission(apiuser):
1157 1158 _perms = ('repository.admin',)
1158 1159 validate_repo_permissions(apiuser, repoid, repo, _perms)
1159 1160
1160 1161 try:
1161 1162 handle_forks = Optional.extract(forks)
1162 1163 _forks_msg = ''
1163 1164 _forks = [f for f in repo.forks]
1164 1165 if handle_forks == 'detach':
1165 1166 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1166 1167 elif handle_forks == 'delete':
1167 1168 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1168 1169 elif _forks:
1169 1170 raise JSONRPCError(
1170 1171 'Cannot delete `%s` it still contains attached forks' %
1171 1172 (repo.repo_name,)
1172 1173 )
1173 1174
1174 1175 RepoModel().delete(repo, forks=forks)
1175 1176 Session().commit()
1176 1177 return {
1177 1178 'msg': 'Deleted repository `%s`%s' % (
1178 1179 repo.repo_name, _forks_msg),
1179 1180 'success': True
1180 1181 }
1181 1182 except Exception:
1182 1183 log.exception("Exception occurred while trying to delete repo")
1183 1184 raise JSONRPCError(
1184 1185 'failed to delete repository `%s`' % (repo.repo_name,)
1185 1186 )
1186 1187
1187 1188
1188 1189 #TODO: marcink, change name ?
1189 1190 @jsonrpc_method()
1190 1191 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1191 1192 """
1192 1193 Invalidates the cache for the specified repository.
1193 1194
1194 1195 This command can only be run using an |authtoken| with admin rights to
1195 1196 the specified repository.
1196 1197
1197 1198 This command takes the following options:
1198 1199
1199 1200 :param apiuser: This is filled automatically from |authtoken|.
1200 1201 :type apiuser: AuthUser
1201 1202 :param repoid: Sets the repository name or repository ID.
1202 1203 :type repoid: str or int
1203 1204 :param delete_keys: This deletes the invalidated keys instead of
1204 1205 just flagging them.
1205 1206 :type delete_keys: Optional(``True`` | ``False``)
1206 1207
1207 1208 Example output:
1208 1209
1209 1210 .. code-block:: bash
1210 1211
1211 1212 id : <id_given_in_input>
1212 1213 result : {
1213 1214 'msg': Cache for repository `<repository name>` was invalidated,
1214 1215 'repository': <repository name>
1215 1216 }
1216 1217 error : null
1217 1218
1218 1219 Example error output:
1219 1220
1220 1221 .. code-block:: bash
1221 1222
1222 1223 id : <id_given_in_input>
1223 1224 result : null
1224 1225 error : {
1225 1226 'Error occurred during cache invalidation action'
1226 1227 }
1227 1228
1228 1229 """
1229 1230
1230 1231 repo = get_repo_or_error(repoid)
1231 1232 if not has_superadmin_permission(apiuser):
1232 1233 _perms = ('repository.admin', 'repository.write',)
1233 1234 validate_repo_permissions(apiuser, repoid, repo, _perms)
1234 1235
1235 1236 delete = Optional.extract(delete_keys)
1236 1237 try:
1237 1238 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1238 1239 return {
1239 1240 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1240 1241 'repository': repo.repo_name
1241 1242 }
1242 1243 except Exception:
1243 1244 log.exception(
1244 1245 "Exception occurred while trying to invalidate repo cache")
1245 1246 raise JSONRPCError(
1246 1247 'Error occurred during cache invalidation action'
1247 1248 )
1248 1249
1249 1250
1250 1251 #TODO: marcink, change name ?
1251 1252 @jsonrpc_method()
1252 1253 def lock(request, apiuser, repoid, locked=Optional(None),
1253 1254 userid=Optional(OAttr('apiuser'))):
1254 1255 """
1255 1256 Sets the lock state of the specified |repo| by the given user.
1256 1257 From more information, see :ref:`repo-locking`.
1257 1258
1258 1259 * If the ``userid`` option is not set, the repository is locked to the
1259 1260 user who called the method.
1260 1261 * If the ``locked`` parameter is not set, the current lock state of the
1261 1262 repository is displayed.
1262 1263
1263 1264 This command can only be run using an |authtoken| with admin rights to
1264 1265 the specified repository.
1265 1266
1266 1267 This command takes the following options:
1267 1268
1268 1269 :param apiuser: This is filled automatically from the |authtoken|.
1269 1270 :type apiuser: AuthUser
1270 1271 :param repoid: Sets the repository name or repository ID.
1271 1272 :type repoid: str or int
1272 1273 :param locked: Sets the lock state.
1273 1274 :type locked: Optional(``True`` | ``False``)
1274 1275 :param userid: Set the repository lock to this user.
1275 1276 :type userid: Optional(str or int)
1276 1277
1277 1278 Example error output:
1278 1279
1279 1280 .. code-block:: bash
1280 1281
1281 1282 id : <id_given_in_input>
1282 1283 result : {
1283 1284 'repo': '<reponame>',
1284 1285 'locked': <bool: lock state>,
1285 1286 'locked_since': <int: lock timestamp>,
1286 1287 'locked_by': <username of person who made the lock>,
1287 1288 'lock_reason': <str: reason for locking>,
1288 1289 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1289 1290 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1290 1291 or
1291 1292 'msg': 'Repo `<repository name>` not locked.'
1292 1293 or
1293 1294 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1294 1295 }
1295 1296 error : null
1296 1297
1297 1298 Example error output:
1298 1299
1299 1300 .. code-block:: bash
1300 1301
1301 1302 id : <id_given_in_input>
1302 1303 result : null
1303 1304 error : {
1304 1305 'Error occurred locking repository `<reponame>`'
1305 1306 }
1306 1307 """
1307 1308
1308 1309 repo = get_repo_or_error(repoid)
1309 1310 if not has_superadmin_permission(apiuser):
1310 1311 # check if we have at least write permission for this repo !
1311 1312 _perms = ('repository.admin', 'repository.write',)
1312 1313 validate_repo_permissions(apiuser, repoid, repo, _perms)
1313 1314
1314 1315 # make sure normal user does not pass someone else userid,
1315 1316 # he is not allowed to do that
1316 1317 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1317 1318 raise JSONRPCError('userid is not the same as your user')
1318 1319
1319 1320 if isinstance(userid, Optional):
1320 1321 userid = apiuser.user_id
1321 1322
1322 1323 user = get_user_or_error(userid)
1323 1324
1324 1325 if isinstance(locked, Optional):
1325 1326 lockobj = repo.locked
1326 1327
1327 1328 if lockobj[0] is None:
1328 1329 _d = {
1329 1330 'repo': repo.repo_name,
1330 1331 'locked': False,
1331 1332 'locked_since': None,
1332 1333 'locked_by': None,
1333 1334 'lock_reason': None,
1334 1335 'lock_state_changed': False,
1335 1336 'msg': 'Repo `%s` not locked.' % repo.repo_name
1336 1337 }
1337 1338 return _d
1338 1339 else:
1339 1340 _user_id, _time, _reason = lockobj
1340 1341 lock_user = get_user_or_error(userid)
1341 1342 _d = {
1342 1343 'repo': repo.repo_name,
1343 1344 'locked': True,
1344 1345 'locked_since': _time,
1345 1346 'locked_by': lock_user.username,
1346 1347 'lock_reason': _reason,
1347 1348 'lock_state_changed': False,
1348 1349 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1349 1350 % (repo.repo_name, lock_user.username,
1350 1351 json.dumps(time_to_datetime(_time))))
1351 1352 }
1352 1353 return _d
1353 1354
1354 1355 # force locked state through a flag
1355 1356 else:
1356 1357 locked = str2bool(locked)
1357 1358 lock_reason = Repository.LOCK_API
1358 1359 try:
1359 1360 if locked:
1360 1361 lock_time = time.time()
1361 1362 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1362 1363 else:
1363 1364 lock_time = None
1364 1365 Repository.unlock(repo)
1365 1366 _d = {
1366 1367 'repo': repo.repo_name,
1367 1368 'locked': locked,
1368 1369 'locked_since': lock_time,
1369 1370 'locked_by': user.username,
1370 1371 'lock_reason': lock_reason,
1371 1372 'lock_state_changed': True,
1372 1373 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1373 1374 % (user.username, repo.repo_name, locked))
1374 1375 }
1375 1376 return _d
1376 1377 except Exception:
1377 1378 log.exception(
1378 1379 "Exception occurred while trying to lock repository")
1379 1380 raise JSONRPCError(
1380 1381 'Error occurred locking repository `%s`' % repo.repo_name
1381 1382 )
1382 1383
1383 1384
1384 1385 @jsonrpc_method()
1385 1386 def comment_commit(
1386 1387 request, apiuser, repoid, commit_id, message, status=Optional(None),
1387 1388 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
1388 1389 resolves_comment_id=Optional(None),
1389 1390 userid=Optional(OAttr('apiuser'))):
1390 1391 """
1391 1392 Set a commit comment, and optionally change the status of the commit.
1392 1393
1393 1394 :param apiuser: This is filled automatically from the |authtoken|.
1394 1395 :type apiuser: AuthUser
1395 1396 :param repoid: Set the repository name or repository ID.
1396 1397 :type repoid: str or int
1397 1398 :param commit_id: Specify the commit_id for which to set a comment.
1398 1399 :type commit_id: str
1399 1400 :param message: The comment text.
1400 1401 :type message: str
1401 1402 :param status: (**Optional**) status of commit, one of: 'not_reviewed',
1402 1403 'approved', 'rejected', 'under_review'
1403 1404 :type status: str
1404 1405 :param comment_type: Comment type, one of: 'note', 'todo'
1405 1406 :type comment_type: Optional(str), default: 'note'
1406 1407 :param userid: Set the user name of the comment creator.
1407 1408 :type userid: Optional(str or int)
1408 1409
1409 1410 Example error output:
1410 1411
1411 1412 .. code-block:: bash
1412 1413
1413 1414 {
1414 1415 "id" : <id_given_in_input>,
1415 1416 "result" : {
1416 1417 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1417 1418 "status_change": null or <status>,
1418 1419 "success": true
1419 1420 },
1420 1421 "error" : null
1421 1422 }
1422 1423
1423 1424 """
1424 1425 repo = get_repo_or_error(repoid)
1425 1426 if not has_superadmin_permission(apiuser):
1426 1427 _perms = ('repository.read', 'repository.write', 'repository.admin')
1427 1428 validate_repo_permissions(apiuser, repoid, repo, _perms)
1428 1429
1429 1430 try:
1430 1431 commit_id = repo.scm_instance().get_commit(commit_id=commit_id).raw_id
1431 1432 except Exception as e:
1432 1433 log.exception('Failed to fetch commit')
1433 1434 raise JSONRPCError(e.message)
1434 1435
1435 1436 if isinstance(userid, Optional):
1436 1437 userid = apiuser.user_id
1437 1438
1438 1439 user = get_user_or_error(userid)
1439 1440 status = Optional.extract(status)
1440 1441 comment_type = Optional.extract(comment_type)
1441 1442 resolves_comment_id = Optional.extract(resolves_comment_id)
1442 1443
1443 1444 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1444 1445 if status and status not in allowed_statuses:
1445 1446 raise JSONRPCError('Bad status, must be on '
1446 1447 'of %s got %s' % (allowed_statuses, status,))
1447 1448
1448 1449 if resolves_comment_id:
1449 1450 comment = ChangesetComment.get(resolves_comment_id)
1450 1451 if not comment:
1451 1452 raise JSONRPCError(
1452 1453 'Invalid resolves_comment_id `%s` for this commit.'
1453 1454 % resolves_comment_id)
1454 1455 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
1455 1456 raise JSONRPCError(
1456 1457 'Comment `%s` is wrong type for setting status to resolved.'
1457 1458 % resolves_comment_id)
1458 1459
1459 1460 try:
1460 1461 rc_config = SettingsModel().get_all_settings()
1461 1462 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1462 1463 status_change_label = ChangesetStatus.get_status_lbl(status)
1463 1464 comm = CommentsModel().create(
1464 1465 message, repo, user, commit_id=commit_id,
1465 1466 status_change=status_change_label,
1466 1467 status_change_type=status,
1467 1468 renderer=renderer,
1468 1469 comment_type=comment_type,
1469 1470 resolves_comment_id=resolves_comment_id
1470 1471 )
1471 1472 if status:
1472 1473 # also do a status change
1473 1474 try:
1474 1475 ChangesetStatusModel().set_status(
1475 1476 repo, status, user, comm, revision=commit_id,
1476 1477 dont_allow_on_closed_pull_request=True
1477 1478 )
1478 1479 except StatusChangeOnClosedPullRequestError:
1479 1480 log.exception(
1480 1481 "Exception occurred while trying to change repo commit status")
1481 1482 msg = ('Changing status on a changeset associated with '
1482 1483 'a closed pull request is not allowed')
1483 1484 raise JSONRPCError(msg)
1484 1485
1485 1486 Session().commit()
1486 1487 return {
1487 1488 'msg': (
1488 1489 'Commented on commit `%s` for repository `%s`' % (
1489 1490 comm.revision, repo.repo_name)),
1490 1491 'status_change': status,
1491 1492 'success': True,
1492 1493 }
1493 1494 except JSONRPCError:
1494 1495 # catch any inside errors, and re-raise them to prevent from
1495 1496 # below global catch to silence them
1496 1497 raise
1497 1498 except Exception:
1498 1499 log.exception("Exception occurred while trying to comment on commit")
1499 1500 raise JSONRPCError(
1500 1501 'failed to set comment on repository `%s`' % (repo.repo_name,)
1501 1502 )
1502 1503
1503 1504
1504 1505 @jsonrpc_method()
1505 1506 def grant_user_permission(request, apiuser, repoid, userid, perm):
1506 1507 """
1507 1508 Grant permissions for the specified user on the given repository,
1508 1509 or update existing permissions if found.
1509 1510
1510 1511 This command can only be run using an |authtoken| with admin
1511 1512 permissions on the |repo|.
1512 1513
1513 1514 :param apiuser: This is filled automatically from the |authtoken|.
1514 1515 :type apiuser: AuthUser
1515 1516 :param repoid: Set the repository name or repository ID.
1516 1517 :type repoid: str or int
1517 1518 :param userid: Set the user name.
1518 1519 :type userid: str
1519 1520 :param perm: Set the user permissions, using the following format
1520 1521 ``(repository.(none|read|write|admin))``
1521 1522 :type perm: str
1522 1523
1523 1524 Example output:
1524 1525
1525 1526 .. code-block:: bash
1526 1527
1527 1528 id : <id_given_in_input>
1528 1529 result: {
1529 1530 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1530 1531 "success": true
1531 1532 }
1532 1533 error: null
1533 1534 """
1534 1535
1535 1536 repo = get_repo_or_error(repoid)
1536 1537 user = get_user_or_error(userid)
1537 1538 perm = get_perm_or_error(perm)
1538 1539 if not has_superadmin_permission(apiuser):
1539 1540 _perms = ('repository.admin',)
1540 1541 validate_repo_permissions(apiuser, repoid, repo, _perms)
1541 1542
1542 1543 try:
1543 1544
1544 1545 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1545 1546
1546 1547 Session().commit()
1547 1548 return {
1548 1549 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1549 1550 perm.permission_name, user.username, repo.repo_name
1550 1551 ),
1551 1552 'success': True
1552 1553 }
1553 1554 except Exception:
1554 1555 log.exception(
1555 1556 "Exception occurred while trying edit permissions for repo")
1556 1557 raise JSONRPCError(
1557 1558 'failed to edit permission for user: `%s` in repo: `%s`' % (
1558 1559 userid, repoid
1559 1560 )
1560 1561 )
1561 1562
1562 1563
1563 1564 @jsonrpc_method()
1564 1565 def revoke_user_permission(request, apiuser, repoid, userid):
1565 1566 """
1566 1567 Revoke permission for a user on the specified repository.
1567 1568
1568 1569 This command can only be run using an |authtoken| with admin
1569 1570 permissions on the |repo|.
1570 1571
1571 1572 :param apiuser: This is filled automatically from the |authtoken|.
1572 1573 :type apiuser: AuthUser
1573 1574 :param repoid: Set the repository name or repository ID.
1574 1575 :type repoid: str or int
1575 1576 :param userid: Set the user name of revoked user.
1576 1577 :type userid: str or int
1577 1578
1578 1579 Example error output:
1579 1580
1580 1581 .. code-block:: bash
1581 1582
1582 1583 id : <id_given_in_input>
1583 1584 result: {
1584 1585 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1585 1586 "success": true
1586 1587 }
1587 1588 error: null
1588 1589 """
1589 1590
1590 1591 repo = get_repo_or_error(repoid)
1591 1592 user = get_user_or_error(userid)
1592 1593 if not has_superadmin_permission(apiuser):
1593 1594 _perms = ('repository.admin',)
1594 1595 validate_repo_permissions(apiuser, repoid, repo, _perms)
1595 1596
1596 1597 try:
1597 1598 RepoModel().revoke_user_permission(repo=repo, user=user)
1598 1599 Session().commit()
1599 1600 return {
1600 1601 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1601 1602 user.username, repo.repo_name
1602 1603 ),
1603 1604 'success': True
1604 1605 }
1605 1606 except Exception:
1606 1607 log.exception(
1607 1608 "Exception occurred while trying revoke permissions to repo")
1608 1609 raise JSONRPCError(
1609 1610 'failed to edit permission for user: `%s` in repo: `%s`' % (
1610 1611 userid, repoid
1611 1612 )
1612 1613 )
1613 1614
1614 1615
1615 1616 @jsonrpc_method()
1616 1617 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1617 1618 """
1618 1619 Grant permission for a user group on the specified repository,
1619 1620 or update existing permissions.
1620 1621
1621 1622 This command can only be run using an |authtoken| with admin
1622 1623 permissions on the |repo|.
1623 1624
1624 1625 :param apiuser: This is filled automatically from the |authtoken|.
1625 1626 :type apiuser: AuthUser
1626 1627 :param repoid: Set the repository name or repository ID.
1627 1628 :type repoid: str or int
1628 1629 :param usergroupid: Specify the ID of the user group.
1629 1630 :type usergroupid: str or int
1630 1631 :param perm: Set the user group permissions using the following
1631 1632 format: (repository.(none|read|write|admin))
1632 1633 :type perm: str
1633 1634
1634 1635 Example output:
1635 1636
1636 1637 .. code-block:: bash
1637 1638
1638 1639 id : <id_given_in_input>
1639 1640 result : {
1640 1641 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1641 1642 "success": true
1642 1643
1643 1644 }
1644 1645 error : null
1645 1646
1646 1647 Example error output:
1647 1648
1648 1649 .. code-block:: bash
1649 1650
1650 1651 id : <id_given_in_input>
1651 1652 result : null
1652 1653 error : {
1653 1654 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1654 1655 }
1655 1656
1656 1657 """
1657 1658
1658 1659 repo = get_repo_or_error(repoid)
1659 1660 perm = get_perm_or_error(perm)
1660 1661 if not has_superadmin_permission(apiuser):
1661 1662 _perms = ('repository.admin',)
1662 1663 validate_repo_permissions(apiuser, repoid, repo, _perms)
1663 1664
1664 1665 user_group = get_user_group_or_error(usergroupid)
1665 1666 if not has_superadmin_permission(apiuser):
1666 1667 # check if we have at least read permission for this user group !
1667 1668 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1668 1669 if not HasUserGroupPermissionAnyApi(*_perms)(
1669 1670 user=apiuser, user_group_name=user_group.users_group_name):
1670 1671 raise JSONRPCError(
1671 1672 'user group `%s` does not exist' % (usergroupid,))
1672 1673
1673 1674 try:
1674 1675 RepoModel().grant_user_group_permission(
1675 1676 repo=repo, group_name=user_group, perm=perm)
1676 1677
1677 1678 Session().commit()
1678 1679 return {
1679 1680 'msg': 'Granted perm: `%s` for user group: `%s` in '
1680 1681 'repo: `%s`' % (
1681 1682 perm.permission_name, user_group.users_group_name,
1682 1683 repo.repo_name
1683 1684 ),
1684 1685 'success': True
1685 1686 }
1686 1687 except Exception:
1687 1688 log.exception(
1688 1689 "Exception occurred while trying change permission on repo")
1689 1690 raise JSONRPCError(
1690 1691 'failed to edit permission for user group: `%s` in '
1691 1692 'repo: `%s`' % (
1692 1693 usergroupid, repo.repo_name
1693 1694 )
1694 1695 )
1695 1696
1696 1697
1697 1698 @jsonrpc_method()
1698 1699 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1699 1700 """
1700 1701 Revoke the permissions of a user group on a given repository.
1701 1702
1702 1703 This command can only be run using an |authtoken| with admin
1703 1704 permissions on the |repo|.
1704 1705
1705 1706 :param apiuser: This is filled automatically from the |authtoken|.
1706 1707 :type apiuser: AuthUser
1707 1708 :param repoid: Set the repository name or repository ID.
1708 1709 :type repoid: str or int
1709 1710 :param usergroupid: Specify the user group ID.
1710 1711 :type usergroupid: str or int
1711 1712
1712 1713 Example output:
1713 1714
1714 1715 .. code-block:: bash
1715 1716
1716 1717 id : <id_given_in_input>
1717 1718 result: {
1718 1719 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1719 1720 "success": true
1720 1721 }
1721 1722 error: null
1722 1723 """
1723 1724
1724 1725 repo = get_repo_or_error(repoid)
1725 1726 if not has_superadmin_permission(apiuser):
1726 1727 _perms = ('repository.admin',)
1727 1728 validate_repo_permissions(apiuser, repoid, repo, _perms)
1728 1729
1729 1730 user_group = get_user_group_or_error(usergroupid)
1730 1731 if not has_superadmin_permission(apiuser):
1731 1732 # check if we have at least read permission for this user group !
1732 1733 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1733 1734 if not HasUserGroupPermissionAnyApi(*_perms)(
1734 1735 user=apiuser, user_group_name=user_group.users_group_name):
1735 1736 raise JSONRPCError(
1736 1737 'user group `%s` does not exist' % (usergroupid,))
1737 1738
1738 1739 try:
1739 1740 RepoModel().revoke_user_group_permission(
1740 1741 repo=repo, group_name=user_group)
1741 1742
1742 1743 Session().commit()
1743 1744 return {
1744 1745 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1745 1746 user_group.users_group_name, repo.repo_name
1746 1747 ),
1747 1748 'success': True
1748 1749 }
1749 1750 except Exception:
1750 1751 log.exception("Exception occurred while trying revoke "
1751 1752 "user group permission on repo")
1752 1753 raise JSONRPCError(
1753 1754 'failed to edit permission for user group: `%s` in '
1754 1755 'repo: `%s`' % (
1755 1756 user_group.users_group_name, repo.repo_name
1756 1757 )
1757 1758 )
1758 1759
1759 1760
1760 1761 @jsonrpc_method()
1761 1762 def pull(request, apiuser, repoid):
1762 1763 """
1763 1764 Triggers a pull on the given repository from a remote location. You
1764 1765 can use this to keep remote repositories up-to-date.
1765 1766
1766 1767 This command can only be run using an |authtoken| with admin
1767 1768 rights to the specified repository. For more information,
1768 1769 see :ref:`config-token-ref`.
1769 1770
1770 1771 This command takes the following options:
1771 1772
1772 1773 :param apiuser: This is filled automatically from the |authtoken|.
1773 1774 :type apiuser: AuthUser
1774 1775 :param repoid: The repository name or repository ID.
1775 1776 :type repoid: str or int
1776 1777
1777 1778 Example output:
1778 1779
1779 1780 .. code-block:: bash
1780 1781
1781 1782 id : <id_given_in_input>
1782 1783 result : {
1783 1784 "msg": "Pulled from `<repository name>`"
1784 1785 "repository": "<repository name>"
1785 1786 }
1786 1787 error : null
1787 1788
1788 1789 Example error output:
1789 1790
1790 1791 .. code-block:: bash
1791 1792
1792 1793 id : <id_given_in_input>
1793 1794 result : null
1794 1795 error : {
1795 1796 "Unable to pull changes from `<reponame>`"
1796 1797 }
1797 1798
1798 1799 """
1799 1800
1800 1801 repo = get_repo_or_error(repoid)
1801 1802 if not has_superadmin_permission(apiuser):
1802 1803 _perms = ('repository.admin',)
1803 1804 validate_repo_permissions(apiuser, repoid, repo, _perms)
1804 1805
1805 1806 try:
1806 1807 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1807 1808 return {
1808 1809 'msg': 'Pulled from `%s`' % repo.repo_name,
1809 1810 'repository': repo.repo_name
1810 1811 }
1811 1812 except Exception:
1812 1813 log.exception("Exception occurred while trying to "
1813 1814 "pull changes from remote location")
1814 1815 raise JSONRPCError(
1815 1816 'Unable to pull changes from `%s`' % repo.repo_name
1816 1817 )
1817 1818
1818 1819
1819 1820 @jsonrpc_method()
1820 1821 def strip(request, apiuser, repoid, revision, branch):
1821 1822 """
1822 1823 Strips the given revision from the specified repository.
1823 1824
1824 1825 * This will remove the revision and all of its decendants.
1825 1826
1826 1827 This command can only be run using an |authtoken| with admin rights to
1827 1828 the specified repository.
1828 1829
1829 1830 This command takes the following options:
1830 1831
1831 1832 :param apiuser: This is filled automatically from the |authtoken|.
1832 1833 :type apiuser: AuthUser
1833 1834 :param repoid: The repository name or repository ID.
1834 1835 :type repoid: str or int
1835 1836 :param revision: The revision you wish to strip.
1836 1837 :type revision: str
1837 1838 :param branch: The branch from which to strip the revision.
1838 1839 :type branch: str
1839 1840
1840 1841 Example output:
1841 1842
1842 1843 .. code-block:: bash
1843 1844
1844 1845 id : <id_given_in_input>
1845 1846 result : {
1846 1847 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1847 1848 "repository": "<repository name>"
1848 1849 }
1849 1850 error : null
1850 1851
1851 1852 Example error output:
1852 1853
1853 1854 .. code-block:: bash
1854 1855
1855 1856 id : <id_given_in_input>
1856 1857 result : null
1857 1858 error : {
1858 1859 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1859 1860 }
1860 1861
1861 1862 """
1862 1863
1863 1864 repo = get_repo_or_error(repoid)
1864 1865 if not has_superadmin_permission(apiuser):
1865 1866 _perms = ('repository.admin',)
1866 1867 validate_repo_permissions(apiuser, repoid, repo, _perms)
1867 1868
1868 1869 try:
1869 1870 ScmModel().strip(repo, revision, branch)
1870 1871 return {
1871 1872 'msg': 'Stripped commit %s from repo `%s`' % (
1872 1873 revision, repo.repo_name),
1873 1874 'repository': repo.repo_name
1874 1875 }
1875 1876 except Exception:
1876 1877 log.exception("Exception while trying to strip")
1877 1878 raise JSONRPCError(
1878 1879 'Unable to strip commit %s from repo `%s`' % (
1879 1880 revision, repo.repo_name)
1880 1881 )
1881 1882
1882 1883
1883 1884 @jsonrpc_method()
1884 1885 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1885 1886 """
1886 1887 Returns all settings for a repository. If key is given it only returns the
1887 1888 setting identified by the key or null.
1888 1889
1889 1890 :param apiuser: This is filled automatically from the |authtoken|.
1890 1891 :type apiuser: AuthUser
1891 1892 :param repoid: The repository name or repository id.
1892 1893 :type repoid: str or int
1893 1894 :param key: Key of the setting to return.
1894 1895 :type: key: Optional(str)
1895 1896
1896 1897 Example output:
1897 1898
1898 1899 .. code-block:: bash
1899 1900
1900 1901 {
1901 1902 "error": null,
1902 1903 "id": 237,
1903 1904 "result": {
1904 1905 "extensions_largefiles": true,
1905 1906 "extensions_evolve": true,
1906 1907 "hooks_changegroup_push_logger": true,
1907 1908 "hooks_changegroup_repo_size": false,
1908 1909 "hooks_outgoing_pull_logger": true,
1909 1910 "phases_publish": "True",
1910 1911 "rhodecode_hg_use_rebase_for_merging": true,
1911 1912 "rhodecode_pr_merge_enabled": true,
1912 1913 "rhodecode_use_outdated_comments": true
1913 1914 }
1914 1915 }
1915 1916 """
1916 1917
1917 1918 # Restrict access to this api method to admins only.
1918 1919 if not has_superadmin_permission(apiuser):
1919 1920 raise JSONRPCForbidden()
1920 1921
1921 1922 try:
1922 1923 repo = get_repo_or_error(repoid)
1923 1924 settings_model = VcsSettingsModel(repo=repo)
1924 1925 settings = settings_model.get_global_settings()
1925 1926 settings.update(settings_model.get_repo_settings())
1926 1927
1927 1928 # If only a single setting is requested fetch it from all settings.
1928 1929 key = Optional.extract(key)
1929 1930 if key is not None:
1930 1931 settings = settings.get(key, None)
1931 1932 except Exception:
1932 1933 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1933 1934 log.exception(msg)
1934 1935 raise JSONRPCError(msg)
1935 1936
1936 1937 return settings
1937 1938
1938 1939
1939 1940 @jsonrpc_method()
1940 1941 def set_repo_settings(request, apiuser, repoid, settings):
1941 1942 """
1942 1943 Update repository settings. Returns true on success.
1943 1944
1944 1945 :param apiuser: This is filled automatically from the |authtoken|.
1945 1946 :type apiuser: AuthUser
1946 1947 :param repoid: The repository name or repository id.
1947 1948 :type repoid: str or int
1948 1949 :param settings: The new settings for the repository.
1949 1950 :type: settings: dict
1950 1951
1951 1952 Example output:
1952 1953
1953 1954 .. code-block:: bash
1954 1955
1955 1956 {
1956 1957 "error": null,
1957 1958 "id": 237,
1958 1959 "result": true
1959 1960 }
1960 1961 """
1961 1962 # Restrict access to this api method to admins only.
1962 1963 if not has_superadmin_permission(apiuser):
1963 1964 raise JSONRPCForbidden()
1964 1965
1965 1966 if type(settings) is not dict:
1966 1967 raise JSONRPCError('Settings have to be a JSON Object.')
1967 1968
1968 1969 try:
1969 1970 settings_model = VcsSettingsModel(repo=repoid)
1970 1971
1971 1972 # Merge global, repo and incoming settings.
1972 1973 new_settings = settings_model.get_global_settings()
1973 1974 new_settings.update(settings_model.get_repo_settings())
1974 1975 new_settings.update(settings)
1975 1976
1976 1977 # Update the settings.
1977 1978 inherit_global_settings = new_settings.get(
1978 1979 'inherit_global_settings', False)
1979 1980 settings_model.create_or_update_repo_settings(
1980 1981 new_settings, inherit_global_settings=inherit_global_settings)
1981 1982 Session().commit()
1982 1983 except Exception:
1983 1984 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1984 1985 log.exception(msg)
1985 1986 raise JSONRPCError(msg)
1986 1987
1987 1988 # Indicate success.
1988 1989 return True
1990
1991
1992 @jsonrpc_method()
1993 def maintenance(request, apiuser, repoid):
1994 """
1995 Triggers a maintenance on the given repository.
1996
1997 This command can only be run using an |authtoken| with admin
1998 rights to the specified repository. For more information,
1999 see :ref:`config-token-ref`.
2000
2001 This command takes the following options:
2002
2003 :param apiuser: This is filled automatically from the |authtoken|.
2004 :type apiuser: AuthUser
2005 :param repoid: The repository name or repository ID.
2006 :type repoid: str or int
2007
2008 Example output:
2009
2010 .. code-block:: bash
2011
2012 id : <id_given_in_input>
2013 result : {
2014 "msg": "executed maintenance command",
2015 "executed_actions": [
2016 <action_message>, <action_message2>...
2017 ],
2018 "repository": "<repository name>"
2019 }
2020 error : null
2021
2022 Example error output:
2023
2024 .. code-block:: bash
2025
2026 id : <id_given_in_input>
2027 result : null
2028 error : {
2029 "Unable to execute maintenance on `<reponame>`"
2030 }
2031
2032 """
2033
2034 repo = get_repo_or_error(repoid)
2035 if not has_superadmin_permission(apiuser):
2036 _perms = ('repository.admin',)
2037 validate_repo_permissions(apiuser, repoid, repo, _perms)
2038
2039 try:
2040 maintenance = repo_maintenance.RepoMaintenance()
2041 executed_actions = maintenance.execute(repo)
2042
2043 return {
2044 'msg': 'executed maintenance command',
2045 'executed_actions': executed_actions,
2046 'repository': repo.repo_name
2047 }
2048 except Exception:
2049 log.exception("Exception occurred while trying to run maintenance")
2050 raise JSONRPCError(
2051 'Unable to execute maintenance on `%s`' % repo.repo_name)
@@ -1,58 +1,66 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Maintenance')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6
7 % if c.executable_tasks:
8 <h4>${_('Perform maintenance tasks for this repo')}</h4>
9
10 <span>${_('Following tasks will be performed')}:</span>
11 <ol>
12 % for task in c.executable_tasks:
13 <li>${task}</li>
14 % endfor
15 </ol>
7 16 <p>
8 % if c.executable_tasks:
9 ${_('Perform maintenance tasks for this repo, following tasks will be performed')}:
10 <ol>
11 % for task in c.executable_tasks:
12 <li>${task}</li>
13 % endfor
14 </ol>
15 % else:
16 ${_('No maintenance tasks for this repo available')}
17 % endif
17 ${_('Maintenance can be automated by such api call. Can be called periodically in crontab etc.')}
18 <br/>
19 <code>
20 ${h.api_call_example(method='maintenance', args={"repoid": c.repo_info.repo_name})}
21 </code>
18 22 </p>
19 23
24 % else:
25 <h4>${_('No maintenance tasks for this repo available')}</h4>
26 % endif
27
20 28 <div id="results" style="display:none; padding: 10px 0px;"></div>
21 29
22 30 % if c.executable_tasks:
23 31 <div class="form">
24 32 <div class="fields">
25 33 <button class="btn btn-small btn-primary" onclick="executeTask();return false">
26 34 ${_('Run Maintenance')}
27 35 </button>
28 36 </div>
29 37 </div>
30 38 % endif
31 39
32 40 </div>
33 41 </div>
34 42
35 43
36 44 <script>
37 45
38 46 executeTask = function() {
39 47 var btn = $(this);
40 48 $('#results').show();
41 49 $('#results').html('<h4>${_('Performing Maintenance')}...</h4>');
42 50
43 51 btn.attr('disabled', 'disabled');
44 52 btn.addClass('disabled');
45 53
46 54 var url = "${h.route_path('repo_maintenance_execute', repo_name=c.repo_info.repo_name)}";
47 55 var success = function (data) {
48 56 var displayHtml = $('<pre></pre>');
49 57
50 58 $(displayHtml).append(data);
51 59 $('#results').html(displayHtml);
52 60 btn.removeAttr('disabled');
53 61 btn.removeClass('disabled');
54 62 };
55 63 ajaxGET(url, success, null);
56 64
57 65 }
58 66 </script>
General Comments 0
You need to be logged in to leave comments. Login now