##// END OF EJS Templates
docs: fixed small wanrings/errors during build.
marcink -
r1120:d4155363 default
parent child Browse files
Show More
@@ -0,0 +1,17 b''
1 .. _checklist-pull-request:
2
3 =======================
4 Pull Request Checklists
5 =======================
6
7
8
9 Checklists for Pull Request
10 ===========================
11
12
13 - Informative description
14 - Linear commit history
15 - Rebased on top of latest changes
16 - Add ticket references. eg fixes #123, references #123 etc.
17
1 NO CONTENT: new file 100644, binary diff hidden
@@ -1,151 +1,151 b''
1 1 .. _backup-ref:
2 2
3 3 Backup and Restore
4 4 ==================
5 5
6 6 *β€œThe condition of any backup is unknown until a restore is attempted.”*
7 7 `SchrΓΆdinger's Backup`_
8 8
9 9 To snapshot an instance of |RCE|, and save its settings, you need to backup the
10 10 following parts of the system at the same time.
11 11
12 12 * The |repos| managed by the instance together with the stored Gists.
13 13 * The |RCE| database.
14 14 * Any configuration files or extensions that you've configured. In most
15 15 cases it's only the :file:`rhodecode.ini` file.
16 16 * Installer files such as those in `/opt/rhodecode` can be backed-up, however
17 17 it's not required since in case of a recovery installer simply
18 18 re-creates those.
19 19
20 20
21 21 .. important::
22 22
23 23 Ideally you should script all of these functions so that it creates a
24 24 backup snapshot of your system at a particular timestamp and then run that
25 25 script regularly.
26 26
27 27 Backup Details
28 28 --------------
29 29
30 30 To backup the relevant parts of |RCE| required to restore your system, use
31 31 the information in this section to identify what is important to you.
32 32
33 33 Repository Backup
34 34 ^^^^^^^^^^^^^^^^^
35 35
36 36 To back up your |repos|, use the API to get a list of all |repos| managed,
37 37 and then clone them to your backup location. This is the most safe backup option.
38 38 Backing up the storage directory could potentially result in a backup of
39 39 partially committed files or commits. (Backup taking place during a big push)
40 40 As an alternative you could use a rsync or simple `cp` commands if you can
41 41 ensure your instance is only in read-only mode or stopped at the moment.
42 42
43 43
44 44 Use the ``get_repos`` method to list all your managed |repos|,
45 45 and use the ``clone_uri`` information that is returned. See the :ref:`api`
46 46 for more information. Be sure to keep the structure or repositories with their
47 47 repository groups.
48 48
49 49 .. important::
50 50
51 51 This will not work for |svn| |repos|. Currently the only way to back up
52 52 your |svn| |repos| is to make a copy of them.
53 53
54 54 It is also important to note, that you can only restore the |svn| |repos|
55 55 using the same version as they were saved with.
56 56
57 57 Database Backup
58 58 ^^^^^^^^^^^^^^^
59 59
60 60 The instance database contains all the |RCE| permissions settings,
61 61 and user management information. To backup your database,
62 62 export it using the following appropriate example, and then move it to your
63 63 backup location:
64 64
65 65 .. code-block:: bash
66 66
67 67 # For MySQL DBs
68 68 $ mysqldump -u <uname> -p <pass> rhodecode_db_name > mysql-db-backup
69 69 # MySQL restore command
70 70 $ mysql -u <uname> -p <pass> rhodecode_db_name < mysql-db-backup
71 71
72 72 # For PostgreSQL DBs
73 73 $ PGPASSWORD=<pass> pg_dump rhodecode_db_name > postgresql-db-backup
74 74 # PosgreSQL restore
75 75 $ PGPASSWORD=<pass> psql -U <uname> -h localhost -d rhodecode_db_name -1 -f postgresql-db-backup
76 76
77 77 # For SQLite
78 78 $ sqlite3 rhodecode.db β€˜.dump’ > sqlite-db-backup
79 79 # SQLite restore
80 80 $ copy sqlite-db-backup rhodecode.db
81 81
82 82
83 83 The default |RCE| SQLite database location is
84 84 :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.db`
85 85
86 86 If running MySQL or PostgreSQL databases, you will have configured these
87 87 separately, for more information see :ref:`rhodecode-database-ref`
88 88
89 89 Configuration File Backup
90 90 ^^^^^^^^^^^^^^^^^^^^^^^^^
91 91
92 92 Depending on your setup, you could have a number of configuration files that
93 93 should be backed up. You may have some, or all of the configuration files
94 94 listed in the :ref:`config-rce-files` section. Ideally you should back these
95 95 up at the same time as the database and |repos|. It really depends on if you need
96 96 the configuration file like logs, custom modules. We always recommend backing
97 97 those up.
98 98
99 99 Gist Backup
100 100 ^^^^^^^^^^^
101 101
102 102 To backup the gists on your |RCE| instance you usually have to backup the
103 103 gist storage path. If this haven't been changed it's located inside
104 104 :file:`.rc_gist_store` and the metadata in :file:`.rc_gist_metadata`.
105 105 You can use the ``get_users`` and ``get_gists`` API methods to fetch the
106 106 gists for each user on the instance.
107 107
108 108 Extension Backups
109 109 ^^^^^^^^^^^^^^^^^
110 110
111 111 You should also backup any extensions added in the
112 112 :file:`home/{user}/.rccontrol/{instance-id}/rcextensions` directory.
113 113
114 114 Full-text Search Backup
115 115 ^^^^^^^^^^^^^^^^^^^^^^^
116 116
117 117 You may also have full text search set up, but the index can be rebuild from
118 118 re-imported |repos| if necessary. You will most likely want to backup your
119 119 :file:`mapping.ini` file if you've configured that. For more information, see
120 120 the :ref:`indexing-ref` section.
121 121
122 122 Restoration Steps
123 123 -----------------
124 124
125 125 To restore an instance of |RCE| from its backed up components, to a fresh
126 126 system use the following steps.
127 127
128 128 1. Install a new instance of |RCE| using sqlite option as database.
129 129 2. Restore your database.
130 2. Once installed, replace you backed up the :file:`rhodecode.ini` with your
130 3. Once installed, replace you backed up the :file:`rhodecode.ini` with your
131 131 backup version. Ensure this file points to the restored
132 132 database, see the :ref:`config-database` section.
133 3. Restart |RCE| and remap and rescan your |repos| to verify filesystem access,
133 4. Restart |RCE| and remap and rescan your |repos| to verify filesystem access,
134 134 see the :ref:`remap-rescan` section.
135 135
136 136
137 137 Post Restoration Steps
138 138 ^^^^^^^^^^^^^^^^^^^^^^
139 139
140 140 Once you have restored your |RCE| instance to basic functionality, you can
141 141 then work on restoring any specific setup changes you had made.
142 142
143 143 * To recreate the |RCE| index, use the backed up :file:`mapping.ini` file if
144 144 you had made changes and rerun the indexer. See the
145 145 :ref:`indexing-ref` section for details.
146 146 * To reconfigure any extensions, copy the backed up extensions into the
147 147 :file:`/home/{user}/.rccontrol/{instance-id}/rcextensions` and also specify
148 148 any custom hooks if necessary. See the :ref:`extensions-hooks-ref` section for
149 149 details.
150 150
151 151 .. _SchrΓΆdinger's Backup: http://novabackup.novastor.com/blog/schrodingers-backup-good-bad-backup/
@@ -1,179 +1,179 b''
1 1 .. _sec-your-server:
2 2
3 3 Securing Your Server
4 4 --------------------
5 5
6 6 |RCE| runs on your hardware, and while it is developed with security in mind
7 7 it is also important that you ensure your servers are well secured. In this
8 8 section we will cover some basic security practices that are best to
9 9 configure when setting up your |RCE| instances.
10 10
11 11 SSH Keys
12 12 ^^^^^^^^
13 13
14 14 Using SSH keys to access your server provides more security than using the
15 15 standard username and password combination. To set up your SSH Keys, use the
16 16 following steps:
17 17
18 18 1. On your local machine create the public/private key combination. The
19 19 private key you will keep, and the matching public key is copied to the
20 20 server. Setting a passphrase here is optional, if you set one you will
21 21 always be prompted for it when logging in.
22 22
23 23 .. code-block:: bash
24 24
25 25 # Generate SSH Keys
26 26 user@ubuntu:~$ ssh-keygen -t rsa
27 27
28 28 .. code-block:: bash
29 29
30 30 Generating public/private rsa key pair.
31 31 Enter file in which to save the key (/home/user/.ssh/id_rsa):
32 32 Created directory '/home/user/.ssh'.
33 33 Enter passphrase (empty for no passphrase):
34 34 Enter same passphrase again:
35 35 Your identification has been saved in /home/user/.ssh/id_rsa.
36 36 Your public key has been saved in /home/user/.ssh/id_rsa.pub.
37 37 The key fingerprint is:
38 38 02:82:38:95:e5:30:d2:ad:17:60:15:7f:94:17:9f:30 user@ubuntu
39 The key's randomart image is:
39 The key\'s randomart image is:
40 40 +--[ RSA 2048]----+
41 41
42 42 2. SFTP to your server, and copy the public key to the ``~/.ssh`` folder.
43 43
44 44 .. code-block:: bash
45 45
46 46 # SFTP to your server
47 47 $ sftp user@hostname
48 48
49 49 # copy your public key
50 50 sftp> mput /home/user/.ssh/id_rsa.pub /home/user/.ssh
51 51 Uploading /home/user/.ssh/id_rsa.pub to /home/user/.ssh/id_rsa.pub
52 52 /home/user/.ssh/id_rsa.pub 100% 394 0.4KB/s 00:00
53 53
54 54 3. On your server, add the public key to the :file:`~/.ssh/authorized_keys`
55 55 file.
56 56
57 57 .. code-block:: bash
58 58
59 59 $ cat /home/user/.ssh/id_rsa.pub > /home/user/.ssh/authorized_keys
60 60
61 61 You should now be able to log into your server using your SSH
62 62 Keys. If you've added a passphrase you'll be asked for it. For more
63 63 information about using SSH keys with |RCE| |repos|, see the
64 64 :ref:`ssh-connection` section.
65 65
66 66 VPN Whitelist
67 67 ^^^^^^^^^^^^^
68 68
69 69 Most company networks will have a VPN. If you need to set one up, there are
70 70 many tutorials online for how to do that. Getting it right requires good
71 71 knowledge and attention to detail. Once set up, you can configure your
72 72 |RCE| instances to only allow user access from the VPN, to do this see the
73 73 :ref:`settip-ip-white` section.
74 74
75 75 Public Key Infrastructure and SSL/TLS Encryption
76 76 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
77 77
78 78 Public key infrastructure (PKI) is a system that creates, manages, and
79 79 validates certificates for identifying nodes on a network and encrypting
80 80 communication between them. SSL or TLS certificates can be used to
81 81 authenticate different entities with one another. To read more about PKIs,
82 82 see the `OpenSSL PKI tutorial`_ site, or this `Cloudflare PKI post`_.
83 83
84 84 If the network you are running is SSL/TLS encrypted, you can configure |RCE|
85 85 to always use secure connections using the ``force_https`` and ``use_htsts``
86 86 options in the :file:`/home/user/.rccontrol/instance-id/rhodecode.ini` file.
87 87 For more details, see the :ref:`x-frame` section.
88 88
89 89 FireWalls and Ports
90 90 ^^^^^^^^^^^^^^^^^^^
91 91
92 92 Setting up a network firewall for your internal traffic is a good way
93 93 of keeping it secure by blocking off any ports that should not be used.
94 94 Additionally, you can set non-default ports for certain functions which adds
95 95 an extra layer of security to your setup.
96 96
97 97 A well configured firewall will restrict access to everything except the
98 98 services you need to remain open. By exposing fewer services you reduce the
99 99 number of potential vulnerabilities.
100 100
101 101 There are a number of different firewall solutions, but for most Linux systems
102 102 using the built in `IpTables`_ firewall should suffice. On BSD systems you
103 103 can use `IPFILTER`_ or `IPFW`_. Use the following examples, and the IpTables
104 104 documentation to configure your IP Tables on Ubuntu.
105 105
106 106 Changing the default SSH port.
107 107
108 108 .. code-block:: bash
109 109
110 110 # Open SSH config file and change to port 10022
111 111 vi /etc/ssh/sshd_config
112 112
113 113 # What ports, IPs and protocols we listen for
114 114 Port 10022
115 115
116 116 Setting IP Table rules for SSH traffic. It is important to note that the
117 117 default policy of your IpTables can differ and it is worth checking how each
118 118 is configured. The options are *ACCEPT*, *REJECT*, *DROP*, or *LOG*. The
119 119 usual practice is to block access on all ports and then enable access only on
120 120 the ports you with to expose.
121 121
122 122 .. code-block:: bash
123 123
124 124 # Check iptables policy
125 125 $ sudo iptables -L
126 126
127 127 Chain INPUT (policy ACCEPT)
128 128 target prot opt source destination
129 129
130 130 Chain FORWARD (policy ACCEPT)
131 131 target prot opt source destination
132 132
133 133 Chain OUTPUT (policy ACCEPT)
134 134 target prot opt source destination
135 135
136 136 # Close all ports by default
137 137 $ sudo iptables -P INPUT DROP
138 138
139 139 $ sudo iptables -L
140 140 Chain INPUT (policy DROP)
141 141 target prot opt source destination
142 142 DROP all -- anywhere anywhere
143 143
144 144 Chain FORWARD (policy ACCEPT)
145 145 target prot opt source destination
146 146
147 147 Chain OUTPUT (policy ACCEPT)
148 148 target prot opt source destination
149 149
150 150 .. code-block:: bash
151 151
152 152 # Deny outbound SSH traffic
153 153 sudo iptables -A OUTPUT -p tcp --dport 10022 -j DROP
154 154
155 155 # Allow incoming SSH traffic on port 10022
156 156 sudo iptables -A INPUT -p tcp --dport 10022 -j ACCEPT
157 157
158 158 # Allow incoming HTML traffic on port 80 and 443
159 159 iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
160 160 iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
161 161
162 162 Saving your IP Table rules, and restoring them from file.
163 163
164 164 .. code-block:: bash
165 165
166 166 # Save you IP Table Rules
167 167 iptables-save
168 168
169 169 # Save your IP Table Rules to a file
170 170 sudo sh -c "iptables-save > /etc/iptables.rules"
171 171
172 172 # Restore your IP Table rules from file
173 173 iptables-restore < /etc/iptables.rules
174 174
175 175 .. _OpenSSL PKI tutorial: https://pki-tutorial.readthedocs.org/en/latest/#
176 176 .. _Cloudflare PKI post: https://blog.cloudflare.com/how-to-build-your-own-public-key-infrastructure/
177 177 .. _IpTables: https://help.ubuntu.com/community/IptablesHowTo
178 178 .. _IPFW: https://www.freebsd.org/doc/handbook/firewalls-ipfw.html
179 179 .. _IPFILTER: https://www.freebsd.org/doc/handbook/firewalls-ipf.html
@@ -1,32 +1,32 b''
1 1 .. _hg-lrg-loc:
2 2
3 3 Change the |hg| Large Files Location
4 4 ------------------------------------
5 5
6 6 |RCE| manages |hg| larges files from the following default location
7 7 :file:`/home/{user}/repos/.cache/largefiles`. If you wish to change this, use
8 8 the following steps:
9 9
10 10 1. Open ishell from the terminal and use it to log into the |RCE| database by
11 11 specifying the instance :file:`rhodecode.ini` file.
12 12
13 13 .. code-block:: bash
14 14
15 15 # Open iShell from the terminal and set ini file
16 16 $ .rccontrol/enterprise-1/profile/bin/paster ishell .rccontrol/enterprise-1/rhodecode.ini
17 17
18 18 2. Run the following commands, and ensure that |RCE| has write access to the
19 19 new directory:
20 20
21 .. code-block:: mysql
21 .. code-block:: bash
22 22
23 23 # Once logged into the database, use SQL to redirect
24 24 # the large files location
25 25 In [1]: from rhodecode.model.settings import SettingsModel
26 26 In [2]: SettingsModel().get_ui_by_key('usercache')
27 27 Out[2]: <RhodeCodeUi[largefiles]usercache=>/mnt/hgfs/shared/workspace/xxxx/.cache/largefiles]>
28 28
29 29 In [3]: largefiles_cache = SettingsModel().get_ui_by_key('usercache')
30 30 In [4]: largefiles_cache.ui_value = '/new/path’
31 31 In [5]: Session().add(largefiles_cache);Session().commit()
32 32
@@ -1,77 +1,77 b''
1 1 .. _deprecated-methods-ref:
2 2
3 3 deprecated methods
4 4 ==================
5 5
6 6 changeset_comment
7 7 -----------------
8 8
9 9 .. py:function:: changeset_comment(apiuser, repoid, revision, message, userid=<Optional:<OptionalAttr:apiuser>>, status=<Optional:None>)
10 10
11 11 .. deprecated:: 3.4.0
12 12
13 13 Please use method `comment_commit` instead.
14 14
15 15
16 16 Set a changeset comment, and optionally change the status of the
17 17 changeset.
18 18
19 19 This command can only be run using an |authtoken| with admin
20 20 permissions on the |repo|.
21 21
22 22 :param apiuser: This is filled automatically from the |authtoken|.
23 23 :type apiuser: AuthUser
24 24 :param repoid: Set the repository name or repository ID.
25 25 :type repoid: str or int
26 26 :param revision: Specify the revision for which to set a comment.
27 27 :type revision: str
28 28 :param message: The comment text.
29 29 :type message: str
30 30 :param userid: Set the user name of the comment creator.
31 31 :type userid: Optional(str or int)
32 32 :param status: Set the comment status. The following are valid options:
33 33 * not_reviewed
34 34 * approved
35 35 * rejected
36 36 * under_review
37 37 :type status: str
38 38
39 39 Example error output:
40 40
41 .. code-block:: json
41 .. code-block:: bash
42 42
43 43 {
44 44 "id" : <id_given_in_input>,
45 45 "result" : {
46 46 "msg": "Commented on commit `<revision>` for repository `<repoid>`",
47 47 "status_change": null or <status>,
48 48 "success": true
49 49 },
50 50 "error" : null
51 51 }
52 52
53 53
54 54 get_locks
55 55 ---------
56 56
57 57 .. py:function:: get_locks(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
58 58
59 59 .. deprecated:: 4.0.0
60 60
61 61 Please use method `get_user_locks` instead.
62 62
63 63 None
64 64
65 65
66 66 show_ip
67 67 -------
68 68
69 69 .. py:function:: show_ip(apiuser, userid=<Optional:<OptionalAttr:apiuser>>)
70 70
71 71 .. deprecated:: 4.0.0
72 72
73 73 Please use method `get_ip` instead.
74 74
75 75 None
76 76
77 77
@@ -1,71 +1,71 b''
1 1 .. _license-methods-ref:
2 2
3 3 license methods
4 4 ===============
5 5
6 6 get_license_info (EE only)
7 ----------------
7 --------------------------
8 8
9 9 .. py:function:: get_license_info(apiuser)
10 10
11 11 Returns the |RCE| license information.
12 12
13 13 :param apiuser: This is filled automatically from the |authtoken|.
14 14 :type apiuser: AuthUser
15 15
16 16 Example output:
17 17
18 18 .. code-block:: bash
19 19
20 20 id : <id_given_in_input>
21 21 result : {
22 22 'rhodecode_version': <rhodecode version>,
23 23 'token': <license token>,
24 24 'issued_to': <license owner>,
25 25 'issued_on': <license issue date>,
26 26 'expires_on': <license expiration date>,
27 27 'type': <license type>,
28 28 'users_limit': <license users limit>,
29 29 'key': <license key>
30 30 }
31 31 error : null
32 32
33 33
34 34 set_license_key (EE only)
35 ---------------
35 -------------------------
36 36
37 37 .. py:function:: set_license_key(apiuser, key)
38 38
39 39 Sets the |RCE| license key.
40 40
41 41 :param apiuser: This is filled automatically from the |authtoken|.
42 42 :type apiuser: AuthUser
43 43 :param key: This is the license key to be set.
44 44 :type key: str
45 45
46 46 Example output:
47 47
48 48 .. code-block:: bash
49 49
50 50 id : <id_given_in_input>
51 51 result: {
52 52 "msg" : "updated license information",
53 53 "key": <key>
54 54 }
55 55 error: null
56 56
57 57 Example error output:
58 58
59 59 .. code-block:: bash
60 60
61 61 id : <id_given_in_input>
62 62 result : null
63 63 error : {
64 64 "license key is not valid"
65 65 or
66 66 "trial licenses cannot be uploaded"
67 67 or
68 68 "error occurred while updating license"
69 69 }
70 70
71 71
@@ -1,350 +1,350 b''
1 1 .. _repo-group-methods-ref:
2 2
3 3 repo_group methods
4 4 ==================
5 5
6 6 create_repo_group
7 7 -----------------
8 8
9 9 .. py:function:: create_repo_group(apiuser, group_name, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, copy_permissions=<Optional:False>)
10 10
11 11 Creates a repository group.
12 12
13 13 * If the repository group name contains "/", all the required repository
14 14 groups will be created.
15 15
16 16 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
17 17 (with "foo" as parent). It will also create the "baz" repository
18 18 with "bar" as |repo| group.
19 19
20 20 This command can only be run using an |authtoken| with admin
21 21 permissions.
22 22
23 23 :param apiuser: This is filled automatically from the |authtoken|.
24 24 :type apiuser: AuthUser
25 25 :param group_name: Set the repository group name.
26 26 :type group_name: str
27 27 :param description: Set the |repo| group description.
28 28 :type description: str
29 29 :param owner: Set the |repo| group owner.
30 30 :type owner: str
31 31 :param copy_permissions:
32 32 :type copy_permissions:
33 33
34 34 Example output:
35 35
36 36 .. code-block:: bash
37 37
38 38 id : <id_given_in_input>
39 39 result : {
40 40 "msg": "Created new repo group `<repo_group_name>`"
41 41 "repo_group": <repogroup_object>
42 42 }
43 43 error : null
44 44
45 45
46 46 Example error output:
47 47
48 48 .. code-block:: bash
49 49
50 50 id : <id_given_in_input>
51 51 result : null
52 52 error : {
53 53 failed to create repo group `<repogroupid>`
54 54 }
55 55
56 56
57 57 delete_repo_group
58 58 -----------------
59 59
60 60 .. py:function:: delete_repo_group(apiuser, repogroupid)
61 61
62 62 Deletes a |repo| group.
63 63
64 64 :param apiuser: This is filled automatically from the |authtoken|.
65 65 :type apiuser: AuthUser
66 66 :param repogroupid: Set the name or ID of repository group to be
67 67 deleted.
68 68 :type repogroupid: str or int
69 69
70 70 Example output:
71 71
72 72 .. code-block:: bash
73 73
74 74 id : <id_given_in_input>
75 75 result : {
76 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>
76 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>'
77 77 'repo_group': null
78 78 }
79 79 error : null
80 80
81 81 Example error output:
82 82
83 83 .. code-block:: bash
84 84
85 85 id : <id_given_in_input>
86 86 result : null
87 87 error : {
88 88 "failed to delete repo group ID:<repogroupid> <repogroupname>"
89 89 }
90 90
91 91
92 92 get_repo_group
93 93 --------------
94 94
95 95 .. py:function:: get_repo_group(apiuser, repogroupid)
96 96
97 97 Return the specified |repo| group, along with permissions,
98 98 and repositories inside the group
99 99
100 100 :param apiuser: This is filled automatically from the |authtoken|.
101 101 :type apiuser: AuthUser
102 102 :param repogroupid: Specify the name of ID of the repository group.
103 103 :type repogroupid: str or int
104 104
105 105
106 106 Example output:
107 107
108 108 .. code-block:: bash
109 109
110 110 {
111 111 "error": null,
112 112 "id": repo-group-id,
113 113 "result": {
114 114 "group_description": "repo group description",
115 115 "group_id": 14,
116 116 "group_name": "group name",
117 117 "members": [
118 118 {
119 119 "name": "super-admin-username",
120 120 "origin": "super-admin",
121 121 "permission": "group.admin",
122 122 "type": "user"
123 123 },
124 124 {
125 125 "name": "owner-name",
126 126 "origin": "owner",
127 127 "permission": "group.admin",
128 128 "type": "user"
129 129 },
130 130 {
131 131 "name": "user-group-name",
132 132 "origin": "permission",
133 133 "permission": "group.write",
134 134 "type": "user_group"
135 135 }
136 136 ],
137 137 "owner": "owner-name",
138 138 "parent_group": null,
139 139 "repositories": [ repo-list ]
140 140 }
141 141 }
142 142
143 143
144 144 get_repo_groups
145 145 ---------------
146 146
147 147 .. py:function:: get_repo_groups(apiuser)
148 148
149 149 Returns all repository groups.
150 150
151 151 :param apiuser: This is filled automatically from the |authtoken|.
152 152 :type apiuser: AuthUser
153 153
154 154
155 155 grant_user_group_permission_to_repo_group
156 156 -----------------------------------------
157 157
158 158 .. py:function:: grant_user_group_permission_to_repo_group(apiuser, repogroupid, usergroupid, perm, apply_to_children=<Optional:'none'>)
159 159
160 160 Grant permission for a user group on given repository group, or update
161 161 existing permissions if found.
162 162
163 163 This command can only be run using an |authtoken| with admin
164 164 permissions on the |repo| group.
165 165
166 166 :param apiuser: This is filled automatically from the |authtoken|.
167 167 :type apiuser: AuthUser
168 168 :param repogroupid: Set the name or id of repository group
169 169 :type repogroupid: str or int
170 170 :param usergroupid: id of usergroup
171 171 :type usergroupid: str or int
172 172 :param perm: (group.(none|read|write|admin))
173 173 :type perm: str
174 174 :param apply_to_children: 'none', 'repos', 'groups', 'all'
175 175 :type apply_to_children: str
176 176
177 177 Example output:
178 178
179 179 .. code-block:: bash
180 180
181 181 id : <id_given_in_input>
182 182 result : {
183 183 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
184 184 "success": true
185 185
186 186 }
187 187 error : null
188 188
189 189 Example error output:
190 190
191 191 .. code-block:: bash
192 192
193 193 id : <id_given_in_input>
194 194 result : null
195 195 error : {
196 196 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
197 197 }
198 198
199 199
200 200 grant_user_permission_to_repo_group
201 201 -----------------------------------
202 202
203 203 .. py:function:: grant_user_permission_to_repo_group(apiuser, repogroupid, userid, perm, apply_to_children=<Optional:'none'>)
204 204
205 205 Grant permission for a user on the given repository group, or update
206 206 existing permissions if found.
207 207
208 208 This command can only be run using an |authtoken| with admin
209 209 permissions.
210 210
211 211 :param apiuser: This is filled automatically from the |authtoken|.
212 212 :type apiuser: AuthUser
213 213 :param repogroupid: Set the name or ID of repository group.
214 214 :type repogroupid: str or int
215 215 :param userid: Set the user name.
216 216 :type userid: str
217 217 :param perm: (group.(none|read|write|admin))
218 218 :type perm: str
219 219 :param apply_to_children: 'none', 'repos', 'groups', 'all'
220 220 :type apply_to_children: str
221 221
222 222 Example output:
223 223
224 224 .. code-block:: bash
225 225
226 226 id : <id_given_in_input>
227 227 result: {
228 228 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
229 229 "success": true
230 230 }
231 231 error: null
232 232
233 233 Example error output:
234 234
235 235 .. code-block:: bash
236 236
237 237 id : <id_given_in_input>
238 238 result : null
239 239 error : {
240 240 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
241 241 }
242 242
243 243
244 244 revoke_user_group_permission_from_repo_group
245 245 --------------------------------------------
246 246
247 247 .. py:function:: revoke_user_group_permission_from_repo_group(apiuser, repogroupid, usergroupid, apply_to_children=<Optional:'none'>)
248 248
249 249 Revoke permission for user group on given repository.
250 250
251 251 This command can only be run using an |authtoken| with admin
252 252 permissions on the |repo| group.
253 253
254 254 :param apiuser: This is filled automatically from the |authtoken|.
255 255 :type apiuser: AuthUser
256 256 :param repogroupid: name or id of repository group
257 257 :type repogroupid: str or int
258 258 :param usergroupid:
259 259 :param apply_to_children: 'none', 'repos', 'groups', 'all'
260 260 :type apply_to_children: str
261 261
262 262 Example output:
263 263
264 264 .. code-block:: bash
265 265
266 266 id : <id_given_in_input>
267 267 result: {
268 268 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
269 269 "success": true
270 270 }
271 271 error: null
272 272
273 273 Example error output:
274 274
275 275 .. code-block:: bash
276 276
277 277 id : <id_given_in_input>
278 278 result : null
279 279 error : {
280 280 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
281 281 }
282 282
283 283
284 284 revoke_user_permission_from_repo_group
285 285 --------------------------------------
286 286
287 287 .. py:function:: revoke_user_permission_from_repo_group(apiuser, repogroupid, userid, apply_to_children=<Optional:'none'>)
288 288
289 289 Revoke permission for a user in a given repository group.
290 290
291 291 This command can only be run using an |authtoken| with admin
292 292 permissions on the |repo| group.
293 293
294 294 :param apiuser: This is filled automatically from the |authtoken|.
295 295 :type apiuser: AuthUser
296 296 :param repogroupid: Set the name or ID of the repository group.
297 297 :type repogroupid: str or int
298 298 :param userid: Set the user name to revoke.
299 299 :type userid: str
300 300 :param apply_to_children: 'none', 'repos', 'groups', 'all'
301 301 :type apply_to_children: str
302 302
303 303 Example output:
304 304
305 305 .. code-block:: bash
306 306
307 307 id : <id_given_in_input>
308 308 result: {
309 309 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
310 310 "success": true
311 311 }
312 312 error: null
313 313
314 314 Example error output:
315 315
316 316 .. code-block:: bash
317 317
318 318 id : <id_given_in_input>
319 319 result : null
320 320 error : {
321 321 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
322 322 }
323 323
324 324
325 325 update_repo_group
326 326 -----------------
327 327
328 328 .. py:function:: update_repo_group(apiuser, repogroupid, group_name=<Optional:''>, description=<Optional:''>, owner=<Optional:<OptionalAttr:apiuser>>, parent=<Optional:None>, enable_locking=<Optional:False>)
329 329
330 330 Updates repository group with the details given.
331 331
332 332 This command can only be run using an |authtoken| with admin
333 333 permissions.
334 334
335 335 :param apiuser: This is filled automatically from the |authtoken|.
336 336 :type apiuser: AuthUser
337 337 :param repogroupid: Set the ID of repository group.
338 338 :type repogroupid: str or int
339 339 :param group_name: Set the name of the |repo| group.
340 340 :type group_name: str
341 341 :param description: Set a description for the group.
342 342 :type description: str
343 343 :param owner: Set the |repo| group owner.
344 344 :type owner: str
345 345 :param parent: Set the |repo| group parent.
346 346 :type parent: str or int
347 347 :param enable_locking: Enable |repo| locking. The default is false.
348 348 :type enable_locking: bool
349 349
350 350
@@ -1,967 +1,967 b''
1 1 .. _repo-methods-ref:
2 2
3 3 repo methods
4 4 ============
5 5
6 6 add_field_to_repo
7 7 -----------------
8 8
9 9 .. py:function:: add_field_to_repo(apiuser, repoid, key, label=<Optional:''>, description=<Optional:''>)
10 10
11 11 Adds an extra field to a repository.
12 12
13 13 This command can only be run using an |authtoken| with at least
14 14 write permissions to the |repo|.
15 15
16 16 :param apiuser: This is filled automatically from the |authtoken|.
17 17 :type apiuser: AuthUser
18 18 :param repoid: Set the repository name or repository id.
19 19 :type repoid: str or int
20 20 :param key: Create a unique field key for this repository.
21 21 :type key: str
22 22 :param label:
23 23 :type label: Optional(str)
24 24 :param description:
25 25 :type description: Optional(str)
26 26
27 27
28 28 comment_commit
29 29 --------------
30 30
31 31 .. py:function:: comment_commit(apiuser, repoid, commit_id, message, userid=<Optional:<OptionalAttr:apiuser>>, status=<Optional:None>)
32 32
33 33 Set a commit comment, and optionally change the status of the commit.
34 34
35 35 :param apiuser: This is filled automatically from the |authtoken|.
36 36 :type apiuser: AuthUser
37 37 :param repoid: Set the repository name or repository ID.
38 38 :type repoid: str or int
39 39 :param commit_id: Specify the commit_id for which to set a comment.
40 40 :type commit_id: str
41 41 :param message: The comment text.
42 42 :type message: str
43 43 :param userid: Set the user name of the comment creator.
44 44 :type userid: Optional(str or int)
45 45 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
46 46 'under_review'
47 47 :type status: str
48 48
49 49 Example error output:
50 50
51 .. code-block:: json
51 .. code-block:: bash
52 52
53 53 {
54 54 "id" : <id_given_in_input>,
55 55 "result" : {
56 56 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
57 57 "status_change": null or <status>,
58 58 "success": true
59 59 },
60 60 "error" : null
61 61 }
62 62
63 63
64 64 create_repo
65 65 -----------
66 66
67 67 .. 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>)
68 68
69 69 Creates a repository.
70 70
71 71 * If the repository name contains "/", all the required repository
72 72 groups will be created.
73 73
74 74 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
75 75 (with "foo" as parent). It will also create the "baz" repository
76 76 with "bar" as |repo| group.
77 77
78 78 This command can only be run using an |authtoken| with at least
79 79 write permissions to the |repo|.
80 80
81 81 :param apiuser: This is filled automatically from the |authtoken|.
82 82 :type apiuser: AuthUser
83 83 :param repo_name: Set the repository name.
84 84 :type repo_name: str
85 85 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
86 86 :type repo_type: str
87 87 :param owner: user_id or username
88 88 :type owner: Optional(str)
89 89 :param description: Set the repository description.
90 90 :type description: Optional(str)
91 91 :param private:
92 92 :type private: bool
93 93 :param clone_uri:
94 94 :type clone_uri: str
95 95 :param landing_rev: <rev_type>:<rev>
96 96 :type landing_rev: str
97 97 :param enable_locking:
98 98 :type enable_locking: bool
99 99 :param enable_downloads:
100 100 :type enable_downloads: bool
101 101 :param enable_statistics:
102 102 :type enable_statistics: bool
103 103 :param copy_permissions: Copy permission from group in which the
104 104 repository is being created.
105 105 :type copy_permissions: bool
106 106
107 107
108 108 Example output:
109 109
110 110 .. code-block:: bash
111 111
112 112 id : <id_given_in_input>
113 113 result: {
114 114 "msg": "Created new repository `<reponame>`",
115 115 "success": true,
116 116 "task": "<celery task id or None if done sync>"
117 117 }
118 118 error: null
119 119
120 120
121 121 Example error output:
122 122
123 123 .. code-block:: bash
124 124
125 125 id : <id_given_in_input>
126 126 result : null
127 127 error : {
128 'failed to create repository `<repo_name>`
128 'failed to create repository `<repo_name>`'
129 129 }
130 130
131 131
132 132 delete_repo
133 133 -----------
134 134
135 135 .. py:function:: delete_repo(apiuser, repoid, forks=<Optional:''>)
136 136
137 137 Deletes a repository.
138 138
139 139 * When the `forks` parameter is set it's possible to detach or delete
140 140 forks of deleted repository.
141 141
142 142 This command can only be run using an |authtoken| with admin
143 143 permissions on the |repo|.
144 144
145 145 :param apiuser: This is filled automatically from the |authtoken|.
146 146 :type apiuser: AuthUser
147 147 :param repoid: Set the repository name or repository ID.
148 148 :type repoid: str or int
149 149 :param forks: Set to `detach` or `delete` forks from the |repo|.
150 150 :type forks: Optional(str)
151 151
152 152 Example error output:
153 153
154 154 .. code-block:: bash
155 155
156 156 id : <id_given_in_input>
157 157 result: {
158 158 "msg": "Deleted repository `<reponame>`",
159 159 "success": true
160 160 }
161 161 error: null
162 162
163 163
164 164 fork_repo
165 165 ---------
166 166
167 167 .. 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'>)
168 168
169 169 Creates a fork of the specified |repo|.
170 170
171 171 * If using |RCE| with Celery this will immediately return a success
172 172 message, even though the fork will be created asynchronously.
173 173
174 174 This command can only be run using an |authtoken| with fork
175 175 permissions on the |repo|.
176 176
177 177 :param apiuser: This is filled automatically from the |authtoken|.
178 178 :type apiuser: AuthUser
179 179 :param repoid: Set repository name or repository ID.
180 180 :type repoid: str or int
181 181 :param fork_name: Set the fork name.
182 182 :type fork_name: str
183 183 :param owner: Set the fork owner.
184 184 :type owner: str
185 185 :param description: Set the fork descripton.
186 186 :type description: str
187 187 :param copy_permissions: Copy permissions from parent |repo|. The
188 188 default is False.
189 189 :type copy_permissions: bool
190 190 :param private: Make the fork private. The default is False.
191 191 :type private: bool
192 192 :param landing_rev: Set the landing revision. The default is tip.
193 193
194 194 Example output:
195 195
196 196 .. code-block:: bash
197 197
198 198 id : <id_for_response>
199 199 api_key : "<api_key>"
200 200 args: {
201 201 "repoid" : "<reponame or repo_id>",
202 202 "fork_name": "<forkname>",
203 203 "owner": "<username or user_id = Optional(=apiuser)>",
204 204 "description": "<description>",
205 205 "copy_permissions": "<bool>",
206 206 "private": "<bool>",
207 207 "landing_rev": "<landing_rev>"
208 208 }
209 209
210 210 Example error output:
211 211
212 212 .. code-block:: bash
213 213
214 214 id : <id_given_in_input>
215 215 result: {
216 216 "msg": "Created fork of `<reponame>` as `<forkname>`",
217 217 "success": true,
218 218 "task": "<celery task id or None if done sync>"
219 219 }
220 220 error: null
221 221
222 222
223 223 get_repo
224 224 --------
225 225
226 226 .. py:function:: get_repo(apiuser, repoid, cache=<Optional:True>)
227 227
228 228 Gets an existing repository by its name or repository_id.
229 229
230 230 The members section so the output returns users groups or users
231 231 associated with that repository.
232 232
233 233 This command can only be run using an |authtoken| with admin rights,
234 234 or users with at least read rights to the |repo|.
235 235
236 236 :param apiuser: This is filled automatically from the |authtoken|.
237 237 :type apiuser: AuthUser
238 238 :param repoid: The repository name or repository id.
239 239 :type repoid: str or int
240 240 :param cache: use the cached value for last changeset
241 241 :type: cache: Optional(bool)
242 242
243 243 Example output:
244 244
245 245 .. code-block:: bash
246 246
247 247 {
248 248 "error": null,
249 249 "id": <repo_id>,
250 250 "result": {
251 251 "clone_uri": null,
252 252 "created_on": "timestamp",
253 253 "description": "repo description",
254 254 "enable_downloads": false,
255 255 "enable_locking": false,
256 256 "enable_statistics": false,
257 257 "followers": [
258 258 {
259 259 "active": true,
260 260 "admin": false,
261 261 "api_key": "****************************************",
262 262 "api_keys": [
263 263 "****************************************"
264 264 ],
265 265 "email": "user@example.com",
266 266 "emails": [
267 267 "user@example.com"
268 268 ],
269 269 "extern_name": "rhodecode",
270 270 "extern_type": "rhodecode",
271 271 "firstname": "username",
272 272 "ip_addresses": [],
273 273 "language": null,
274 274 "last_login": "2015-09-16T17:16:35.854",
275 275 "lastname": "surname",
276 276 "user_id": <user_id>,
277 277 "username": "name"
278 278 }
279 279 ],
280 280 "fork_of": "parent-repo",
281 281 "landing_rev": [
282 282 "rev",
283 283 "tip"
284 284 ],
285 285 "last_changeset": {
286 286 "author": "User <user@example.com>",
287 287 "branch": "default",
288 288 "date": "timestamp",
289 289 "message": "last commit message",
290 290 "parents": [
291 291 {
292 292 "raw_id": "commit-id"
293 293 }
294 294 ],
295 295 "raw_id": "commit-id",
296 296 "revision": <revision number>,
297 297 "short_id": "short id"
298 298 },
299 299 "lock_reason": null,
300 300 "locked_by": null,
301 301 "locked_date": null,
302 302 "members": [
303 303 {
304 304 "name": "super-admin-name",
305 305 "origin": "super-admin",
306 306 "permission": "repository.admin",
307 307 "type": "user"
308 308 },
309 309 {
310 310 "name": "owner-name",
311 311 "origin": "owner",
312 312 "permission": "repository.admin",
313 313 "type": "user"
314 314 },
315 315 {
316 316 "name": "user-group-name",
317 317 "origin": "permission",
318 318 "permission": "repository.write",
319 319 "type": "user_group"
320 320 }
321 321 ],
322 322 "owner": "owner-name",
323 323 "permissions": [
324 324 {
325 325 "name": "super-admin-name",
326 326 "origin": "super-admin",
327 327 "permission": "repository.admin",
328 328 "type": "user"
329 329 },
330 330 {
331 331 "name": "owner-name",
332 332 "origin": "owner",
333 333 "permission": "repository.admin",
334 334 "type": "user"
335 335 },
336 336 {
337 337 "name": "user-group-name",
338 338 "origin": "permission",
339 339 "permission": "repository.write",
340 340 "type": "user_group"
341 341 }
342 342 ],
343 343 "private": true,
344 344 "repo_id": 676,
345 345 "repo_name": "user-group/repo-name",
346 346 "repo_type": "hg"
347 347 }
348 348 }
349 349
350 350
351 351 get_repo_changeset
352 352 ------------------
353 353
354 354 .. py:function:: get_repo_changeset(apiuser, repoid, revision, details=<Optional:'basic'>)
355 355
356 356 Returns information about a changeset.
357 357
358 358 Additionally parameters define the amount of details returned by
359 359 this function.
360 360
361 361 This command can only be run using an |authtoken| with admin rights,
362 362 or users with at least read rights to the |repo|.
363 363
364 364 :param apiuser: This is filled automatically from the |authtoken|.
365 365 :type apiuser: AuthUser
366 366 :param repoid: The repository name or repository id
367 367 :type repoid: str or int
368 368 :param revision: revision for which listing should be done
369 369 :type revision: str
370 370 :param details: details can be 'basic|extended|full' full gives diff
371 371 info details like the diff itself, and number of changed files etc.
372 372 :type details: Optional(str)
373 373
374 374
375 375 get_repo_changesets
376 376 -------------------
377 377
378 378 .. py:function:: get_repo_changesets(apiuser, repoid, start_rev, limit, details=<Optional:'basic'>)
379 379
380 380 Returns a set of commits limited by the number starting
381 381 from the `start_rev` option.
382 382
383 383 Additional parameters define the amount of details returned by this
384 384 function.
385 385
386 386 This command can only be run using an |authtoken| with admin rights,
387 387 or users with at least read rights to |repos|.
388 388
389 389 :param apiuser: This is filled automatically from the |authtoken|.
390 390 :type apiuser: AuthUser
391 391 :param repoid: The repository name or repository ID.
392 392 :type repoid: str or int
393 393 :param start_rev: The starting revision from where to get changesets.
394 394 :type start_rev: str
395 395 :param limit: Limit the number of commits to this amount
396 396 :type limit: str or int
397 397 :param details: Set the level of detail returned. Valid option are:
398 398 ``basic``, ``extended`` and ``full``.
399 399 :type details: Optional(str)
400 400
401 401 .. note::
402 402
403 403 Setting the parameter `details` to the value ``full`` is extensive
404 404 and returns details like the diff itself, and the number
405 405 of changed files.
406 406
407 407
408 408 get_repo_nodes
409 409 --------------
410 410
411 411 .. py:function:: get_repo_nodes(apiuser, repoid, revision, root_path, ret_type=<Optional:'all'>, details=<Optional:'basic'>, max_file_bytes=<Optional:None>)
412 412
413 413 Returns a list of nodes and children in a flat list for a given
414 414 path at given revision.
415 415
416 416 It's possible to specify ret_type to show only `files` or `dirs`.
417 417
418 418 This command can only be run using an |authtoken| with admin rights,
419 419 or users with at least read rights to |repos|.
420 420
421 421 :param apiuser: This is filled automatically from the |authtoken|.
422 422 :type apiuser: AuthUser
423 423 :param repoid: The repository name or repository ID.
424 424 :type repoid: str or int
425 425 :param revision: The revision for which listing should be done.
426 426 :type revision: str
427 427 :param root_path: The path from which to start displaying.
428 428 :type root_path: str
429 429 :param ret_type: Set the return type. Valid options are
430 430 ``all`` (default), ``files`` and ``dirs``.
431 431 :type ret_type: Optional(str)
432 432 :param details: Returns extended information about nodes, such as
433 433 md5, binary, and or content. The valid options are ``basic`` and
434 434 ``full``.
435 435 :type details: Optional(str)
436 436 :param max_file_bytes: Only return file content under this file size bytes
437 437 :type details: Optional(int)
438 438
439 439 Example output:
440 440
441 441 .. code-block:: bash
442 442
443 443 id : <id_given_in_input>
444 444 result: [
445 445 {
446 446 "name" : "<name>"
447 447 "type" : "<type>",
448 448 "binary": "<true|false>" (only in extended mode)
449 449 "md5" : "<md5 of file content>" (only in extended mode)
450 450 },
451 451 ...
452 452 ]
453 453 error: null
454 454
455 455
456 456 get_repo_refs
457 457 -------------
458 458
459 459 .. py:function:: get_repo_refs(apiuser, repoid)
460 460
461 461 Returns a dictionary of current references. It returns
462 462 bookmarks, branches, closed_branches, and tags for given repository
463 463
464 464 It's possible to specify ret_type to show only `files` or `dirs`.
465 465
466 466 This command can only be run using an |authtoken| with admin rights,
467 467 or users with at least read rights to |repos|.
468 468
469 469 :param apiuser: This is filled automatically from the |authtoken|.
470 470 :type apiuser: AuthUser
471 471 :param repoid: The repository name or repository ID.
472 472 :type repoid: str or int
473 473
474 474 Example output:
475 475
476 476 .. code-block:: bash
477 477
478 478 id : <id_given_in_input>
479 479 result: [
480 480 TODO...
481 481 ]
482 482 error: null
483 483
484 484
485 485 get_repo_settings
486 486 -----------------
487 487
488 488 .. py:function:: get_repo_settings(apiuser, repoid, key=<Optional:None>)
489 489
490 490 Returns all settings for a repository. If key is given it only returns the
491 491 setting identified by the key or null.
492 492
493 493 :param apiuser: This is filled automatically from the |authtoken|.
494 494 :type apiuser: AuthUser
495 495 :param repoid: The repository name or repository id.
496 496 :type repoid: str or int
497 497 :param key: Key of the setting to return.
498 498 :type: key: Optional(str)
499 499
500 500 Example output:
501 501
502 502 .. code-block:: bash
503 503
504 504 {
505 505 "error": null,
506 506 "id": 237,
507 507 "result": {
508 508 "extensions_largefiles": true,
509 509 "hooks_changegroup_push_logger": true,
510 510 "hooks_changegroup_repo_size": false,
511 511 "hooks_outgoing_pull_logger": true,
512 512 "phases_publish": "True",
513 513 "rhodecode_hg_use_rebase_for_merging": true,
514 514 "rhodecode_pr_merge_enabled": true,
515 515 "rhodecode_use_outdated_comments": true
516 516 }
517 517 }
518 518
519 519
520 520 get_repos
521 521 ---------
522 522
523 523 .. py:function:: get_repos(apiuser)
524 524
525 525 Lists all existing repositories.
526 526
527 527 This command can only be run using an |authtoken| with admin rights,
528 528 or users with at least read rights to |repos|.
529 529
530 530 :param apiuser: This is filled automatically from the |authtoken|.
531 531 :type apiuser: AuthUser
532 532
533 533 Example output:
534 534
535 535 .. code-block:: bash
536 536
537 537 id : <id_given_in_input>
538 538 result: [
539 539 {
540 540 "repo_id" : "<repo_id>",
541 541 "repo_name" : "<reponame>"
542 542 "repo_type" : "<repo_type>",
543 543 "clone_uri" : "<clone_uri>",
544 544 "private": : "<bool>",
545 545 "created_on" : "<datetimecreated>",
546 546 "description" : "<description>",
547 547 "landing_rev": "<landing_rev>",
548 548 "owner": "<repo_owner>",
549 549 "fork_of": "<name_of_fork_parent>",
550 550 "enable_downloads": "<bool>",
551 551 "enable_locking": "<bool>",
552 552 "enable_statistics": "<bool>",
553 553 },
554 554 ...
555 555 ]
556 556 error: null
557 557
558 558
559 559 grant_user_group_permission
560 560 ---------------------------
561 561
562 562 .. py:function:: grant_user_group_permission(apiuser, repoid, usergroupid, perm)
563 563
564 564 Grant permission for a user group on the specified repository,
565 565 or update existing permissions.
566 566
567 567 This command can only be run using an |authtoken| with admin
568 568 permissions on the |repo|.
569 569
570 570 :param apiuser: This is filled automatically from the |authtoken|.
571 571 :type apiuser: AuthUser
572 572 :param repoid: Set the repository name or repository ID.
573 573 :type repoid: str or int
574 574 :param usergroupid: Specify the ID of the user group.
575 575 :type usergroupid: str or int
576 576 :param perm: Set the user group permissions using the following
577 577 format: (repository.(none|read|write|admin))
578 578 :type perm: str
579 579
580 580 Example output:
581 581
582 582 .. code-block:: bash
583 583
584 584 id : <id_given_in_input>
585 585 result : {
586 586 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
587 587 "success": true
588 588
589 589 }
590 590 error : null
591 591
592 592 Example error output:
593 593
594 594 .. code-block:: bash
595 595
596 596 id : <id_given_in_input>
597 597 result : null
598 598 error : {
599 599 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
600 600 }
601 601
602 602
603 603 grant_user_permission
604 604 ---------------------
605 605
606 606 .. py:function:: grant_user_permission(apiuser, repoid, userid, perm)
607 607
608 608 Grant permissions for the specified user on the given repository,
609 609 or update existing permissions if found.
610 610
611 611 This command can only be run using an |authtoken| with admin
612 612 permissions on the |repo|.
613 613
614 614 :param apiuser: This is filled automatically from the |authtoken|.
615 615 :type apiuser: AuthUser
616 616 :param repoid: Set the repository name or repository ID.
617 617 :type repoid: str or int
618 618 :param userid: Set the user name.
619 619 :type userid: str
620 620 :param perm: Set the user permissions, using the following format
621 621 ``(repository.(none|read|write|admin))``
622 622 :type perm: str
623 623
624 624 Example output:
625 625
626 626 .. code-block:: bash
627 627
628 628 id : <id_given_in_input>
629 629 result: {
630 630 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
631 631 "success": true
632 632 }
633 633 error: null
634 634
635 635
636 636 invalidate_cache
637 637 ----------------
638 638
639 639 .. py:function:: invalidate_cache(apiuser, repoid, delete_keys=<Optional:False>)
640 640
641 641 Invalidates the cache for the specified repository.
642 642
643 643 This command can only be run using an |authtoken| with admin rights to
644 644 the specified repository.
645 645
646 646 This command takes the following options:
647 647
648 648 :param apiuser: This is filled automatically from |authtoken|.
649 649 :type apiuser: AuthUser
650 650 :param repoid: Sets the repository name or repository ID.
651 651 :type repoid: str or int
652 652 :param delete_keys: This deletes the invalidated keys instead of
653 653 just flagging them.
654 654 :type delete_keys: Optional(``True`` | ``False``)
655 655
656 656 Example output:
657 657
658 658 .. code-block:: bash
659 659
660 660 id : <id_given_in_input>
661 661 result : {
662 662 'msg': Cache for repository `<repository name>` was invalidated,
663 663 'repository': <repository name>
664 664 }
665 665 error : null
666 666
667 667 Example error output:
668 668
669 669 .. code-block:: bash
670 670
671 671 id : <id_given_in_input>
672 672 result : null
673 673 error : {
674 674 'Error occurred during cache invalidation action'
675 675 }
676 676
677 677
678 678 lock
679 679 ----
680 680
681 681 .. py:function:: lock(apiuser, repoid, locked=<Optional:None>, userid=<Optional:<OptionalAttr:apiuser>>)
682 682
683 683 Sets the lock state of the specified |repo| by the given user.
684 684 From more information, see :ref:`repo-locking`.
685 685
686 686 * If the ``userid`` option is not set, the repository is locked to the
687 687 user who called the method.
688 688 * If the ``locked`` parameter is not set, the current lock state of the
689 689 repository is displayed.
690 690
691 691 This command can only be run using an |authtoken| with admin rights to
692 692 the specified repository.
693 693
694 694 This command takes the following options:
695 695
696 696 :param apiuser: This is filled automatically from the |authtoken|.
697 697 :type apiuser: AuthUser
698 698 :param repoid: Sets the repository name or repository ID.
699 699 :type repoid: str or int
700 700 :param locked: Sets the lock state.
701 701 :type locked: Optional(``True`` | ``False``)
702 702 :param userid: Set the repository lock to this user.
703 703 :type userid: Optional(str or int)
704 704
705 705 Example error output:
706 706
707 707 .. code-block:: bash
708 708
709 709 id : <id_given_in_input>
710 710 result : {
711 711 'repo': '<reponame>',
712 712 'locked': <bool: lock state>,
713 713 'locked_since': <int: lock timestamp>,
714 714 'locked_by': <username of person who made the lock>,
715 715 'lock_reason': <str: reason for locking>,
716 716 'lock_state_changed': <bool: True if lock state has been changed in this request>,
717 717 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
718 718 or
719 719 'msg': 'Repo `<repository name>` not locked.'
720 720 or
721 721 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
722 722 }
723 723 error : null
724 724
725 725 Example error output:
726 726
727 727 .. code-block:: bash
728 728
729 729 id : <id_given_in_input>
730 730 result : null
731 731 error : {
732 'Error occurred locking repository `<reponame>`
732 'Error occurred locking repository `<reponame>`'
733 733 }
734 734
735 735
736 736 pull
737 737 ----
738 738
739 739 .. py:function:: pull(apiuser, repoid)
740 740
741 741 Triggers a pull on the given repository from a remote location. You
742 742 can use this to keep remote repositories up-to-date.
743 743
744 744 This command can only be run using an |authtoken| with admin
745 745 rights to the specified repository. For more information,
746 746 see :ref:`config-token-ref`.
747 747
748 748 This command takes the following options:
749 749
750 750 :param apiuser: This is filled automatically from the |authtoken|.
751 751 :type apiuser: AuthUser
752 752 :param repoid: The repository name or repository ID.
753 753 :type repoid: str or int
754 754
755 755 Example output:
756 756
757 757 .. code-block:: bash
758 758
759 759 id : <id_given_in_input>
760 760 result : {
761 761 "msg": "Pulled from `<repository name>`"
762 762 "repository": "<repository name>"
763 763 }
764 764 error : null
765 765
766 766 Example error output:
767 767
768 768 .. code-block:: bash
769 769
770 770 id : <id_given_in_input>
771 771 result : null
772 772 error : {
773 773 "Unable to pull changes from `<reponame>`"
774 774 }
775 775
776 776
777 777 remove_field_from_repo
778 778 ----------------------
779 779
780 780 .. py:function:: remove_field_from_repo(apiuser, repoid, key)
781 781
782 782 Removes an extra field from a repository.
783 783
784 784 This command can only be run using an |authtoken| with at least
785 785 write permissions to the |repo|.
786 786
787 787 :param apiuser: This is filled automatically from the |authtoken|.
788 788 :type apiuser: AuthUser
789 789 :param repoid: Set the repository name or repository ID.
790 790 :type repoid: str or int
791 791 :param key: Set the unique field key for this repository.
792 792 :type key: str
793 793
794 794
795 795 revoke_user_group_permission
796 796 ----------------------------
797 797
798 798 .. py:function:: revoke_user_group_permission(apiuser, repoid, usergroupid)
799 799
800 800 Revoke the permissions of a user group on a given repository.
801 801
802 802 This command can only be run using an |authtoken| with admin
803 803 permissions on the |repo|.
804 804
805 805 :param apiuser: This is filled automatically from the |authtoken|.
806 806 :type apiuser: AuthUser
807 807 :param repoid: Set the repository name or repository ID.
808 808 :type repoid: str or int
809 809 :param usergroupid: Specify the user group ID.
810 810 :type usergroupid: str or int
811 811
812 812 Example output:
813 813
814 814 .. code-block:: bash
815 815
816 816 id : <id_given_in_input>
817 817 result: {
818 818 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
819 819 "success": true
820 820 }
821 821 error: null
822 822
823 823
824 824 revoke_user_permission
825 825 ----------------------
826 826
827 827 .. py:function:: revoke_user_permission(apiuser, repoid, userid)
828 828
829 829 Revoke permission for a user on the specified repository.
830 830
831 831 This command can only be run using an |authtoken| with admin
832 832 permissions on the |repo|.
833 833
834 834 :param apiuser: This is filled automatically from the |authtoken|.
835 835 :type apiuser: AuthUser
836 836 :param repoid: Set the repository name or repository ID.
837 837 :type repoid: str or int
838 838 :param userid: Set the user name of revoked user.
839 839 :type userid: str or int
840 840
841 841 Example error output:
842 842
843 843 .. code-block:: bash
844 844
845 845 id : <id_given_in_input>
846 846 result: {
847 847 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
848 848 "success": true
849 849 }
850 850 error: null
851 851
852 852
853 853 set_repo_settings
854 854 -----------------
855 855
856 856 .. py:function:: set_repo_settings(apiuser, repoid, settings)
857 857
858 858 Update repository settings. Returns true on success.
859 859
860 860 :param apiuser: This is filled automatically from the |authtoken|.
861 861 :type apiuser: AuthUser
862 862 :param repoid: The repository name or repository id.
863 863 :type repoid: str or int
864 864 :param settings: The new settings for the repository.
865 865 :type: settings: dict
866 866
867 867 Example output:
868 868
869 869 .. code-block:: bash
870 870
871 871 {
872 872 "error": null,
873 873 "id": 237,
874 874 "result": true
875 875 }
876 876
877 877
878 878 strip
879 879 -----
880 880
881 881 .. py:function:: strip(apiuser, repoid, revision, branch)
882 882
883 883 Strips the given revision from the specified repository.
884 884
885 885 * This will remove the revision and all of its decendants.
886 886
887 887 This command can only be run using an |authtoken| with admin rights to
888 888 the specified repository.
889 889
890 890 This command takes the following options:
891 891
892 892 :param apiuser: This is filled automatically from the |authtoken|.
893 893 :type apiuser: AuthUser
894 894 :param repoid: The repository name or repository ID.
895 895 :type repoid: str or int
896 896 :param revision: The revision you wish to strip.
897 897 :type revision: str
898 898 :param branch: The branch from which to strip the revision.
899 899 :type branch: str
900 900
901 901 Example output:
902 902
903 903 .. code-block:: bash
904 904
905 905 id : <id_given_in_input>
906 906 result : {
907 907 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
908 908 "repository": "<repository name>"
909 909 }
910 910 error : null
911 911
912 912 Example error output:
913 913
914 914 .. code-block:: bash
915 915
916 916 id : <id_given_in_input>
917 917 result : null
918 918 error : {
919 919 "Unable to strip commit <commit_hash> from repo `<repository name>`"
920 920 }
921 921
922 922
923 923 update_repo
924 924 -----------
925 925
926 926 .. 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:''>)
927 927
928 928 Updates a repository with the given information.
929 929
930 930 This command can only be run using an |authtoken| with at least
931 931 write permissions to the |repo|.
932 932
933 933 :param apiuser: This is filled automatically from the |authtoken|.
934 934 :type apiuser: AuthUser
935 935 :param repoid: repository name or repository ID.
936 936 :type repoid: str or int
937 937 :param name: Update the |repo| name.
938 938 :type name: str
939 939 :param owner: Set the |repo| owner.
940 940 :type owner: str
941 941 :param group: Set the |repo| group the |repo| belongs to.
942 942 :type group: str
943 943 :param fork_of: Set the master |repo| name.
944 944 :type fork_of: str
945 945 :param description: Update the |repo| description.
946 946 :type description: str
947 947 :param private: Set the |repo| as private. (True | False)
948 948 :type private: bool
949 949 :param clone_uri: Update the |repo| clone URI.
950 950 :type clone_uri: str
951 951 :param landing_rev: Set the |repo| landing revision. Default is
952 952 ``tip``.
953 953 :type landing_rev: str
954 954 :param enable_statistics: Enable statistics on the |repo|,
955 955 (True | False).
956 956 :type enable_statistics: bool
957 957 :param enable_locking: Enable |repo| locking.
958 958 :type enable_locking: bool
959 959 :param enable_downloads: Enable downloads from the |repo|,
960 960 (True | False).
961 961 :type enable_downloads: bool
962 962 :param fields: Add extra fields to the |repo|. Use the following
963 963 example format: ``field_key=field_val,field_key2=fieldval2``.
964 964 Escape ', ' with \,
965 965 :type fields: str
966 966
967 967
@@ -1,60 +1,60 b''
1 1
2 =====
3 API
4 =====
2 ===================
3 CONTRIBUTING TO API
4 ===================
5 5
6 6
7 7
8 8 Naming conventions
9 9 ==================
10 10
11 11 We keep the calls in the form ``{verb}_{noun}``.
12 12
13 13
14 14
15 15 Change and Deprecation
16 16 ======================
17 17
18 18 API deprecation is documented in the section :ref:`deprecated` together with
19 19 other notes about deprecated parts of the application.
20 20
21 21
22 22 Deprecated API calls
23 23 --------------------
24 24
25 25 - Make sure to add them into the section :ref:`deprecated`.
26 26
27 27 - Use `deprecated` inside of the call docstring to make our users aware of the
28 28 deprecation::
29 29
30 30 .. deprecated:: 1.2.3
31 31
32 32 Use `new_call_name` instead to fetch this information.
33 33
34 34 - Make sure to log on level `logging.WARNING` a message that the API call or
35 35 specific parameters are deprecated.
36 36
37 37 - If possible return deprecation information inside of the result from the API
38 38 call. Use the attribute `_warning_` to contain a message.
39 39
40 40
41 41 Changed API calls
42 42 -----------------
43 43
44 44 - If the change is significant, consider to use `versionchanged` in the
45 45 docstring::
46 46
47 47 .. versionchanged:: 1.2.3
48 48
49 49 Optional explanation if reasonable.
50 50
51 51
52 52 Added API calls
53 53 ---------------
54 54
55 55 - Use `versionadded` to document since which version this API call is
56 56 available::
57 57
58 58 .. versionadded:: 1.2.3
59 59
60 60 Optional explanation if reasonable.
@@ -1,137 +1,137 b''
1 1 .. _checklist-tickets:
2 2
3 3 =================
4 4 Ticket Checklists
5 5 =================
6 6
7 7
8 8 Ticket Description
9 9 ==================
10 10
11 11 In general these things really matter in the description:
12 12
13 13 - Reasoning / Rationale. Explain "WHY" it makes sense and is important.
14 14
15 15 - How to reproduce. Easy to follow steps, that’s important.
16 16
17 17 - Observation: The problem (short)
18 18
19 19 - Expectation: How it should be (short)
20 20
21 21 - Specs: It is fine to draft them as good as it works.
22 22
23 23 If anything is unclear, please ask for a review or help on this via the
24 24 Community Portal or Slack channel.
25 25
26 26
27 27 Checklists for Tickets
28 28 ======================
29 29
30 30 BUG
31 31 ---
32 32
33 33 Definition: An existing function that does not work as expected for the user.
34 34
35 35 - Problem description
36 36 - Steps needed to recreate (gherkin)
37 37 - Link to the screen in question and/or description of how to find it via
38 38 navigation
39 39 - Explanation of what the expected outcome is
40 40 - Any hints into the source of the problem
41 41 - Information about platform/browser/db/etc. where applicable
42 42 - Examples of other similar cases which have different behaviour
43 43
44 44 DESIGN
45 45 ------
46 46
47 47 Definition: Styling and user interface issues, including cosmetic improvements
48 48 or appearance and behaviour of frontend functionality.
49 49
50 50 - Screenshot/animation of existing page/behaviour
51 51 - Sketches or wireframes if available
52 52 - Link to the screen in question and/or description of how to find it via
53 53 navigation
54 54 - Problem description
55 55 - Explanation of what the expected outcome is
56 56 - Since this may be examined by a designer; it should be written in a way that a
57 57 non-developer can understand
58 58
59 59 EPIC
60 60 ----
61 61
62 62 Definition: A collection of tickets which together complete a larger overall
63 63 project.
64 64
65 65 - Benefit explanation
66 66 - Clear objective - when is this complete?
67 67 - Explanations of exceptions/corner cases
68 68 - Documentation subtask
69 69 - Comprehensive wireframes and/or design subtasks
70 70 - Links to subtasks
71 71
72 72 FEATURE
73 73 -------
74 74
75 75 Definition: A new function in the software which previously did not exist.
76 76
77 77 - Benefit explanation
78 78 - Clear objective
79 79 - Explanations of exceptions/corner cases
80 80 - Documentation subtask
81 81 - Comprehensive wireframes and/or design subtasks
82 82
83 83 SUPPORT
84 84 -------
85 85
86 86 Definition: An issue related to a customer report.
87 87
88 88 - Link to support ticket, if available
89 89 - Problem description
90 90 - Steps needed to recreate (gherkin)
91 91 - Link to the screen in question and/or description of how to find it via
92 92 navigation
93 93 - Explanation of what the expected outcome is
94 94 - Any hints into the source of the problem
95 95 - Information about platform/browser/db/etc. where applicable
96 96 - Examples of other similar cases which have different behaviour
97 97
98 98 TASK
99 99 ----
100 100
101 101 Definition: An improvement or step towards implementing a feature or fixing
102 102 a bug. Includes refactoring and other tech debt.
103 103
104 104 - Clear objective
105 105 - Benefit explanation
106 106 - Links to parent/related tickets
107 107
108 108
109 109 All details below.
110 110
111 111
112 112 External links:
113 113
114 114 - Avoid linking to external images; they disappear over time. Please attach any
115 115 relevant images to the ticket itself.
116 116
117 117 - External links in general: They also disappear over time, consider copying the
118 118 relevant bit of information into a comment or write a paragraph to sum up the
119 119 general idea.
120 120
121 121
122 122 Hints
123 123 =====
124 124
125 125 Change Description
126 126 ------------------
127 127
128 128 It can be tricky to figure out how to change the description of a ticket. There
129 129 is a very small pencil which has to be clicked once you see the edit form of a
130 130 ticket.
131 131
132 132
133 .. figure:: images/redmine-description.png
133 .. figure:: ../images/redmine-description.png
134 134 :alt: Example of pencil to change the ticket description
135 135
136 136 Shows an example of the pencil which lets you change the description.
137 137
@@ -1,75 +1,72 b''
1 1
2 2 .. _test-spec-by-example:
3 3
4 4 ==========================
5 5 Specification by Example
6 6 ==========================
7 7
8 8
9 9 .. Avoid duplicating the quickstart instructions by importing the README
10 10 file.
11 11
12 .. include:: ../../../acceptance_tests/README.rst
13
14
15 12
16 13 Choices of technology and tools
17 14 ===============================
18 15
19 16
20 17 `nix` as runtime environment
21 18 ----------------------------
22 19
23 20 We settled to use the `nix` tools to provide us the needed environment for
24 21 running the tests.
25 22
26 23
27 24
28 25 `Gherkins` as specification language
29 26 ------------------------------------
30 27
31 28 To specify by example, we settled on Gherkins as the semi-formal specification
32 29 language.
33 30
34 31
35 32 `py.test` as a runner
36 33 ---------------------
37 34
38 35 After experimenting with `behave` and `py.test` our choice was `pytest-bdd`
39 36 because it allows us to use our existing knowledge about `py.test` and avoids
40 37 that we have to learn another tool.
41 38
42 39
43 40
44 41 Concepts
45 42 ========
46 43
47 44 The logic is structured around the design pattern of "page objects". The
48 45 documentation of `python-selemium` contains a few more details about this
49 46 pattern.
50 47
51 48
52 49
53 50 Page Objects
54 51 ------------
55 52
56 53 We introduce an abstraction class for every page which we have to interact with
57 54 in order to validate the specifications.
58 55
59 56 The implementation for the page objects is inside of the module
60 57 :mod:`page_objects`. The class :class:`page_objects.base.BasePage` should be
61 58 used as a base for all page object implementations.
62 59
63 60
64 61
65 62 Locators
66 63 --------
67 64
68 65 The specific information how to locate an element inside of the DOM tree of a
69 66 page is kept in a separate class. This class serves mainly as a data container;
70 67 it shall not contain any logic.
71 68
72 69 The reason for keeping the locators separate is that we expect a frequent need
73 70 for change whenever we work on our templates. In such a case, it is more
74 71 efficient to have all of thelocators together and update them there instead of
75 72 having to find every locator inside of the logic of a page object.
@@ -1,144 +1,144 b''
1 1 { system ? builtins.currentSystem
2 2 }:
3 3
4 4 let
5 5
6 6 pkgs = import <nixpkgs> { inherit system; };
7 7
8 8 inherit (pkgs) fetchurl fetchgit;
9 9
10 10 buildPythonPackage = pkgs.python27Packages.buildPythonPackage;
11 11 python = pkgs.python27Packages.python;
12 12
13 13 Jinja2 = buildPythonPackage rec {
14 14 name = "Jinja2-2.7.3";
15 15 src = fetchurl {
16 16 url = "http://pypi.python.org/packages/source/J/Jinja2/${name}.tar.gz";
17 17 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
18 18 };
19 19 propagatedBuildInputs = [ MarkupSafe ];
20 20 };
21 21
22 22 MarkupSafe = buildPythonPackage rec {
23 23 name = "MarkupSafe-0.23";
24 24 src = fetchurl {
25 25 url = "https://pypi.python.org/packages/source/M/MarkupSafe/${name}.tar.gz";
26 26 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
27 27 };
28 28 };
29 29
30 30 Pygments = buildPythonPackage rec {
31 31 name = "Pygments-2.1.3";
32 32 doCheck = false;
33 33 src = fetchurl {
34 34 url = "https://pypi.python.org/packages/b8/67/ab177979be1c81bc99c8d0592ef22d547e70bb4c6815c383286ed5dec504/Pygments-2.1.3.tar.gz";
35 35 md5 = "ed3fba2467c8afcda4d317e4ef2c6150";
36 36 };
37 37 };
38 38
39 39 alabaster = buildPythonPackage rec {
40 40 name = "alabaster-0.7.3";
41 41 src = fetchurl {
42 42 url = "https://pypi.python.org/packages/source/a/alabaster/${name}.tar.gz";
43 43 md5 = "67428d1383fd833f1282fed5deba0898";
44 44 };
45 45 };
46 46
47 47 six = buildPythonPackage rec {
48 48 name = "six-1.9.0";
49 49 src = fetchurl {
50 50 url = "https://pypi.python.org/packages/source/s/six/${name}.tar.gz";
51 51 md5 = "476881ef4012262dfc8adc645ee786c4";
52 52 };
53 53 };
54 54
55 55 snowballstemmer = buildPythonPackage rec {
56 56 name = "snowballstemmer-1.2.0";
57 57 src = fetchurl {
58 58 url = "https://pypi.python.org/packages/source/s/snowballstemmer/${name}.tar.gz";
59 59 md5 = "51f2ef829db8129dd0f2354f0b209970";
60 60 };
61 61 };
62 62
63 63 pytz = buildPythonPackage rec {
64 64 name = "pytz-2015.2";
65 65 src = fetchurl {
66 66 url = "https://pypi.python.org/packages/source/p/pytz/${name}.tar.gz";
67 67 md5 = "08440d994cfbbf13d3343362cc3173f7";
68 68 };
69 69 };
70 70
71 71 babel = buildPythonPackage rec {
72 72 name = "Babel-1.3";
73 73 src = fetchurl {
74 74 url = "https://pypi.python.org/packages/source/B/Babel/${name}.tar.gz";
75 75 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
76 76 };
77 77 propagatedBuildInputs = [
78 78 pytz
79 79 ];
80 80 };
81 81
82 82 imagesize = buildPythonPackage rec {
83 83 name = "imagesize-0.7.1";
84 84 src = fetchurl {
85 85 url = "https://pypi.python.org/packages/53/72/6c6f1e787d9cab2cc733cf042f125abec07209a58308831c9f292504e826/${name}.tar.gz";
86 86 md5 = "976148283286a6ba5f69b0f81aef8052";
87 87 };
88 88 };
89 89
90 90 Sphinx = buildPythonPackage (rec {
91 name = "Sphinx-1.4.4";
91 name = "Sphinx-1.4.8";
92 92 src = fetchurl {
93 url = "https://pypi.python.org/packages/20/a2/72f44c84f6c4115e3fef58d36d657ec311d80196eab9fd5ec7bcde76143b/${name}.tar.gz";
94 md5 = "64ce2ec08d37ed56313a98232cbe2aee";
93 url = "https://pypi.python.org/packages/1f/f6/e54a7aad73e35232356103771ae76306dadd8546b024c646fbe75135571c/${name}.tar.gz";
94 md5 = "5ec718a4855917e149498bba91b74e67";
95 95 };
96 96 propagatedBuildInputs = [
97 97 docutils
98 98 Jinja2
99 99 Pygments
100 100 alabaster
101 101 six
102 102 snowballstemmer
103 103 pytz
104 104 babel
105 105 imagesize
106 106
107 107 # TODO: johbo: Had to include it here so that can be imported
108 108 sphinx_rtd_theme
109 109 ];
110 110 });
111 111
112 112 docutils = buildPythonPackage rec {
113 113 name = "docutils-0.12";
114 114 src = fetchurl {
115 115 url = "https://pypi.python.org/packages/source/d/docutils/${name}.tar.gz";
116 116 md5 = "4622263b62c5c771c03502afa3157768";
117 117 };
118 118 };
119 119
120 120 sphinx_rtd_theme = buildPythonPackage rec {
121 121 name = "sphinx_rtd_theme-0.1.9";
122 122 src = fetchurl {
123 123 url = "https://pypi.python.org/packages/source/s/sphinx_rtd_theme/${name}.tar.gz";
124 124 md5 = "86a25c8d47147c872e42dc84cc66f97b";
125 125 };
126 126
127 127 # Note: johbo: Sphinx needs this package and this package needs sphinx,
128 128 # ignore the requirements file to solve this cycle.
129 129 postPatch = ''
130 130 rm requirements.txt
131 131 touch requirements.txt
132 132 '';
133 133
134 134 # TODO: johbo: Tests would require sphinx and this creates recursion issues
135 135 doCheck = false;
136 136 };
137 137
138 138 in python.buildEnv.override {
139 139 inherit python;
140 140 extraLibs = [
141 141 Sphinx
142 142 sphinx_rtd_theme
143 143 ];
144 144 }
@@ -1,52 +1,53 b''
1 1 .. _integrations:
2 2
3 3 Integrations
4 4 ------------
5 5
6 6 Rhodecode supports integrations with external services for various events,
7 7 such as commit pushes and pull requests. Multiple integrations of the same type
8 8 can be added at the same time; this is useful for posting different events to
9 9 different Slack channels, for example.
10 10
11 11 Supported integrations
12 12 ^^^^^^^^^^^^^^^^^^^^^^
13 13
14 14 ============================ ============ =====================================
15 15 Type/Name |RC| Edition Description
16 16 ============================ ============ =====================================
17 17 :ref:`integrations-slack` |RCCEshort| https://slack.com/
18 18 :ref:`integrations-hipchat` |RCCEshort| https://www.hipchat.com/
19 19 :ref:`integrations-webhook` |RCCEshort| POST events as `json` to a custom url
20 20 :ref:`integrations-email` |RCEEshort| Send repo push commits by email
21 21 :ref:`integrations-redmine` |RCEEshort| Close/Resolve/Reference redmine issues
22 22 :ref:`integrations-jira` |RCEEshort| Close/Resolve/Reference JIRA issues
23 23 ============================ ============ =====================================
24 24
25 25 .. _creating-integrations:
26 26
27 27 Creating an Integration
28 28 ^^^^^^^^^^^^^^^^^^^^^^^
29 29
30 30 Integrations can be added globally via the admin UI:
31 31
32 32 :menuselection:`Admin --> Integrations`
33 33
34 34 or per repository in each repository's settings:
35 35
36 36 :menuselection:`Admin --> Repositories --> Edit --> Integrations`
37 37
38 38 To create an integration, select the type from the list in the *Create New
39 39 Integration* section.
40 40
41 41 The *Current Integrations* section shows existing integrations that have been
42 42 created along with their type (eg. Slack) and enabled status.
43 43
44 44 See pages specific to each type of integration for more instructions:
45 45
46 46 .. toctree::
47 47
48 48 slack
49 49 hipchat
50 50 redmine
51 51 jira
52 52 webhook
53 email
@@ -1,1888 +1,1888 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2016 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 colander
25 25
26 26 from rhodecode import BACKENDS
27 27 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden, json
28 28 from rhodecode.api.utils import (
29 29 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
30 30 get_user_group_or_error, get_user_or_error, has_repo_permissions,
31 31 get_perm_or_error, store_update, get_repo_group_or_error, parse_args,
32 32 get_origin, build_commit_data)
33 33 from rhodecode.lib.auth import (
34 34 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
35 35 HasUserGroupPermissionAnyApi)
36 36 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
37 37 from rhodecode.lib.utils import map_groups
38 38 from rhodecode.lib.utils2 import str2bool, time_to_datetime
39 39 from rhodecode.model.changeset_status import ChangesetStatusModel
40 40 from rhodecode.model.comment import ChangesetCommentsModel
41 41 from rhodecode.model.db import (
42 42 Session, ChangesetStatus, RepositoryField, Repository)
43 43 from rhodecode.model.repo import RepoModel
44 44 from rhodecode.model.repo_group import RepoGroupModel
45 45 from rhodecode.model.scm import ScmModel, RepoList
46 46 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
47 47 from rhodecode.model.validation_schema.schemas import repo_schema
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 @jsonrpc_method()
53 53 def get_repo(request, apiuser, repoid, cache=Optional(True)):
54 54 """
55 55 Gets an existing repository by its name or repository_id.
56 56
57 57 The members section so the output returns users groups or users
58 58 associated with that repository.
59 59
60 60 This command can only be run using an |authtoken| with admin rights,
61 61 or users with at least read rights to the |repo|.
62 62
63 63 :param apiuser: This is filled automatically from the |authtoken|.
64 64 :type apiuser: AuthUser
65 65 :param repoid: The repository name or repository id.
66 66 :type repoid: str or int
67 67 :param cache: use the cached value for last changeset
68 68 :type: cache: Optional(bool)
69 69
70 70 Example output:
71 71
72 72 .. code-block:: bash
73 73
74 74 {
75 75 "error": null,
76 76 "id": <repo_id>,
77 77 "result": {
78 78 "clone_uri": null,
79 79 "created_on": "timestamp",
80 80 "description": "repo description",
81 81 "enable_downloads": false,
82 82 "enable_locking": false,
83 83 "enable_statistics": false,
84 84 "followers": [
85 85 {
86 86 "active": true,
87 87 "admin": false,
88 88 "api_key": "****************************************",
89 89 "api_keys": [
90 90 "****************************************"
91 91 ],
92 92 "email": "user@example.com",
93 93 "emails": [
94 94 "user@example.com"
95 95 ],
96 96 "extern_name": "rhodecode",
97 97 "extern_type": "rhodecode",
98 98 "firstname": "username",
99 99 "ip_addresses": [],
100 100 "language": null,
101 101 "last_login": "2015-09-16T17:16:35.854",
102 102 "lastname": "surname",
103 103 "user_id": <user_id>,
104 104 "username": "name"
105 105 }
106 106 ],
107 107 "fork_of": "parent-repo",
108 108 "landing_rev": [
109 109 "rev",
110 110 "tip"
111 111 ],
112 112 "last_changeset": {
113 113 "author": "User <user@example.com>",
114 114 "branch": "default",
115 115 "date": "timestamp",
116 116 "message": "last commit message",
117 117 "parents": [
118 118 {
119 119 "raw_id": "commit-id"
120 120 }
121 121 ],
122 122 "raw_id": "commit-id",
123 123 "revision": <revision number>,
124 124 "short_id": "short id"
125 125 },
126 126 "lock_reason": null,
127 127 "locked_by": null,
128 128 "locked_date": null,
129 129 "members": [
130 130 {
131 131 "name": "super-admin-name",
132 132 "origin": "super-admin",
133 133 "permission": "repository.admin",
134 134 "type": "user"
135 135 },
136 136 {
137 137 "name": "owner-name",
138 138 "origin": "owner",
139 139 "permission": "repository.admin",
140 140 "type": "user"
141 141 },
142 142 {
143 143 "name": "user-group-name",
144 144 "origin": "permission",
145 145 "permission": "repository.write",
146 146 "type": "user_group"
147 147 }
148 148 ],
149 149 "owner": "owner-name",
150 150 "permissions": [
151 151 {
152 152 "name": "super-admin-name",
153 153 "origin": "super-admin",
154 154 "permission": "repository.admin",
155 155 "type": "user"
156 156 },
157 157 {
158 158 "name": "owner-name",
159 159 "origin": "owner",
160 160 "permission": "repository.admin",
161 161 "type": "user"
162 162 },
163 163 {
164 164 "name": "user-group-name",
165 165 "origin": "permission",
166 166 "permission": "repository.write",
167 167 "type": "user_group"
168 168 }
169 169 ],
170 170 "private": true,
171 171 "repo_id": 676,
172 172 "repo_name": "user-group/repo-name",
173 173 "repo_type": "hg"
174 174 }
175 175 }
176 176 """
177 177
178 178 repo = get_repo_or_error(repoid)
179 179 cache = Optional.extract(cache)
180 180 include_secrets = False
181 181 if has_superadmin_permission(apiuser):
182 182 include_secrets = True
183 183 else:
184 184 # check if we have at least read permission for this repo !
185 185 _perms = (
186 186 'repository.admin', 'repository.write', 'repository.read',)
187 187 has_repo_permissions(apiuser, repoid, repo, _perms)
188 188
189 189 permissions = []
190 190 for _user in repo.permissions():
191 191 user_data = {
192 192 'name': _user.username,
193 193 'permission': _user.permission,
194 194 'origin': get_origin(_user),
195 195 'type': "user",
196 196 }
197 197 permissions.append(user_data)
198 198
199 199 for _user_group in repo.permission_user_groups():
200 200 user_group_data = {
201 201 'name': _user_group.users_group_name,
202 202 'permission': _user_group.permission,
203 203 'origin': get_origin(_user_group),
204 204 'type': "user_group",
205 205 }
206 206 permissions.append(user_group_data)
207 207
208 208 following_users = [
209 209 user.user.get_api_data(include_secrets=include_secrets)
210 210 for user in repo.followers]
211 211
212 212 if not cache:
213 213 repo.update_commit_cache()
214 214 data = repo.get_api_data(include_secrets=include_secrets)
215 215 data['members'] = permissions # TODO: this should be deprecated soon
216 216 data['permissions'] = permissions
217 217 data['followers'] = following_users
218 218 return data
219 219
220 220
221 221 @jsonrpc_method()
222 222 def get_repos(request, apiuser):
223 223 """
224 224 Lists all existing repositories.
225 225
226 226 This command can only be run using an |authtoken| with admin rights,
227 227 or users with at least read rights to |repos|.
228 228
229 229 :param apiuser: This is filled automatically from the |authtoken|.
230 230 :type apiuser: AuthUser
231 231
232 232 Example output:
233 233
234 234 .. code-block:: bash
235 235
236 236 id : <id_given_in_input>
237 237 result: [
238 238 {
239 239 "repo_id" : "<repo_id>",
240 240 "repo_name" : "<reponame>"
241 241 "repo_type" : "<repo_type>",
242 242 "clone_uri" : "<clone_uri>",
243 243 "private": : "<bool>",
244 244 "created_on" : "<datetimecreated>",
245 245 "description" : "<description>",
246 246 "landing_rev": "<landing_rev>",
247 247 "owner": "<repo_owner>",
248 248 "fork_of": "<name_of_fork_parent>",
249 249 "enable_downloads": "<bool>",
250 250 "enable_locking": "<bool>",
251 251 "enable_statistics": "<bool>",
252 252 },
253 253 ...
254 254 ]
255 255 error: null
256 256 """
257 257
258 258 include_secrets = has_superadmin_permission(apiuser)
259 259 _perms = ('repository.read', 'repository.write', 'repository.admin',)
260 260 extras = {'user': apiuser}
261 261
262 262 repo_list = RepoList(
263 263 RepoModel().get_all(), perm_set=_perms, extra_kwargs=extras)
264 264 return [repo.get_api_data(include_secrets=include_secrets)
265 265 for repo in repo_list]
266 266
267 267
268 268 @jsonrpc_method()
269 269 def get_repo_changeset(request, apiuser, repoid, revision,
270 270 details=Optional('basic')):
271 271 """
272 272 Returns information about a changeset.
273 273
274 274 Additionally parameters define the amount of details returned by
275 275 this function.
276 276
277 277 This command can only be run using an |authtoken| with admin rights,
278 278 or users with at least read rights to the |repo|.
279 279
280 280 :param apiuser: This is filled automatically from the |authtoken|.
281 281 :type apiuser: AuthUser
282 282 :param repoid: The repository name or repository id
283 283 :type repoid: str or int
284 284 :param revision: revision for which listing should be done
285 285 :type revision: str
286 286 :param details: details can be 'basic|extended|full' full gives diff
287 287 info details like the diff itself, and number of changed files etc.
288 288 :type details: Optional(str)
289 289
290 290 """
291 291 repo = get_repo_or_error(repoid)
292 292 if not has_superadmin_permission(apiuser):
293 293 _perms = (
294 294 'repository.admin', 'repository.write', 'repository.read',)
295 295 has_repo_permissions(apiuser, repoid, repo, _perms)
296 296
297 297 changes_details = Optional.extract(details)
298 298 _changes_details_types = ['basic', 'extended', 'full']
299 299 if changes_details not in _changes_details_types:
300 300 raise JSONRPCError(
301 301 'ret_type must be one of %s' % (
302 302 ','.join(_changes_details_types)))
303 303
304 304 pre_load = ['author', 'branch', 'date', 'message', 'parents',
305 305 'status', '_commit', '_file_paths']
306 306
307 307 try:
308 308 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
309 309 except TypeError as e:
310 310 raise JSONRPCError(e.message)
311 311 _cs_json = cs.__json__()
312 312 _cs_json['diff'] = build_commit_data(cs, changes_details)
313 313 if changes_details == 'full':
314 314 _cs_json['refs'] = {
315 315 'branches': [cs.branch],
316 316 'bookmarks': getattr(cs, 'bookmarks', []),
317 317 'tags': cs.tags
318 318 }
319 319 return _cs_json
320 320
321 321
322 322 @jsonrpc_method()
323 323 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
324 324 details=Optional('basic')):
325 325 """
326 326 Returns a set of commits limited by the number starting
327 327 from the `start_rev` option.
328 328
329 329 Additional parameters define the amount of details returned by this
330 330 function.
331 331
332 332 This command can only be run using an |authtoken| with admin rights,
333 333 or users with at least read rights to |repos|.
334 334
335 335 :param apiuser: This is filled automatically from the |authtoken|.
336 336 :type apiuser: AuthUser
337 337 :param repoid: The repository name or repository ID.
338 338 :type repoid: str or int
339 339 :param start_rev: The starting revision from where to get changesets.
340 340 :type start_rev: str
341 341 :param limit: Limit the number of commits to this amount
342 342 :type limit: str or int
343 343 :param details: Set the level of detail returned. Valid option are:
344 344 ``basic``, ``extended`` and ``full``.
345 345 :type details: Optional(str)
346 346
347 347 .. note::
348 348
349 349 Setting the parameter `details` to the value ``full`` is extensive
350 350 and returns details like the diff itself, and the number
351 351 of changed files.
352 352
353 353 """
354 354 repo = get_repo_or_error(repoid)
355 355 if not has_superadmin_permission(apiuser):
356 356 _perms = (
357 357 'repository.admin', 'repository.write', 'repository.read',)
358 358 has_repo_permissions(apiuser, repoid, repo, _perms)
359 359
360 360 changes_details = Optional.extract(details)
361 361 _changes_details_types = ['basic', 'extended', 'full']
362 362 if changes_details not in _changes_details_types:
363 363 raise JSONRPCError(
364 364 'ret_type must be one of %s' % (
365 365 ','.join(_changes_details_types)))
366 366
367 367 limit = int(limit)
368 368 pre_load = ['author', 'branch', 'date', 'message', 'parents',
369 369 'status', '_commit', '_file_paths']
370 370
371 371 vcs_repo = repo.scm_instance()
372 372 # SVN needs a special case to distinguish its index and commit id
373 373 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
374 374 start_rev = vcs_repo.commit_ids[0]
375 375
376 376 try:
377 377 commits = vcs_repo.get_commits(
378 378 start_id=start_rev, pre_load=pre_load)
379 379 except TypeError as e:
380 380 raise JSONRPCError(e.message)
381 381 except Exception:
382 382 log.exception('Fetching of commits failed')
383 383 raise JSONRPCError('Error occurred during commit fetching')
384 384
385 385 ret = []
386 386 for cnt, commit in enumerate(commits):
387 387 if cnt >= limit != -1:
388 388 break
389 389 _cs_json = commit.__json__()
390 390 _cs_json['diff'] = build_commit_data(commit, changes_details)
391 391 if changes_details == 'full':
392 392 _cs_json['refs'] = {
393 393 'branches': [commit.branch],
394 394 'bookmarks': getattr(commit, 'bookmarks', []),
395 395 'tags': commit.tags
396 396 }
397 397 ret.append(_cs_json)
398 398 return ret
399 399
400 400
401 401 @jsonrpc_method()
402 402 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
403 403 ret_type=Optional('all'), details=Optional('basic'),
404 404 max_file_bytes=Optional(None)):
405 405 """
406 406 Returns a list of nodes and children in a flat list for a given
407 407 path at given revision.
408 408
409 409 It's possible to specify ret_type to show only `files` or `dirs`.
410 410
411 411 This command can only be run using an |authtoken| with admin rights,
412 412 or users with at least read rights to |repos|.
413 413
414 414 :param apiuser: This is filled automatically from the |authtoken|.
415 415 :type apiuser: AuthUser
416 416 :param repoid: The repository name or repository ID.
417 417 :type repoid: str or int
418 418 :param revision: The revision for which listing should be done.
419 419 :type revision: str
420 420 :param root_path: The path from which to start displaying.
421 421 :type root_path: str
422 422 :param ret_type: Set the return type. Valid options are
423 423 ``all`` (default), ``files`` and ``dirs``.
424 424 :type ret_type: Optional(str)
425 425 :param details: Returns extended information about nodes, such as
426 426 md5, binary, and or content. The valid options are ``basic`` and
427 427 ``full``.
428 428 :type details: Optional(str)
429 429 :param max_file_bytes: Only return file content under this file size bytes
430 430 :type details: Optional(int)
431 431
432 432 Example output:
433 433
434 434 .. code-block:: bash
435 435
436 436 id : <id_given_in_input>
437 437 result: [
438 438 {
439 439 "name" : "<name>"
440 440 "type" : "<type>",
441 441 "binary": "<true|false>" (only in extended mode)
442 442 "md5" : "<md5 of file content>" (only in extended mode)
443 443 },
444 444 ...
445 445 ]
446 446 error: null
447 447 """
448 448
449 449 repo = get_repo_or_error(repoid)
450 450 if not has_superadmin_permission(apiuser):
451 451 _perms = (
452 452 'repository.admin', 'repository.write', 'repository.read',)
453 453 has_repo_permissions(apiuser, repoid, repo, _perms)
454 454
455 455 ret_type = Optional.extract(ret_type)
456 456 details = Optional.extract(details)
457 457 _extended_types = ['basic', 'full']
458 458 if details not in _extended_types:
459 459 raise JSONRPCError(
460 460 'ret_type must be one of %s' % (','.join(_extended_types)))
461 461 extended_info = False
462 462 content = False
463 463 if details == 'basic':
464 464 extended_info = True
465 465
466 466 if details == 'full':
467 467 extended_info = content = True
468 468
469 469 _map = {}
470 470 try:
471 471 # check if repo is not empty by any chance, skip quicker if it is.
472 472 _scm = repo.scm_instance()
473 473 if _scm.is_empty():
474 474 return []
475 475
476 476 _d, _f = ScmModel().get_nodes(
477 477 repo, revision, root_path, flat=False,
478 478 extended_info=extended_info, content=content,
479 479 max_file_bytes=max_file_bytes)
480 480 _map = {
481 481 'all': _d + _f,
482 482 'files': _f,
483 483 'dirs': _d,
484 484 }
485 485 return _map[ret_type]
486 486 except KeyError:
487 487 raise JSONRPCError(
488 488 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
489 489 except Exception:
490 490 log.exception("Exception occurred while trying to get repo nodes")
491 491 raise JSONRPCError(
492 492 'failed to get repo: `%s` nodes' % repo.repo_name
493 493 )
494 494
495 495
496 496 @jsonrpc_method()
497 497 def get_repo_refs(request, apiuser, repoid):
498 498 """
499 499 Returns a dictionary of current references. It returns
500 500 bookmarks, branches, closed_branches, and tags for given repository
501 501
502 502 It's possible to specify ret_type to show only `files` or `dirs`.
503 503
504 504 This command can only be run using an |authtoken| with admin rights,
505 505 or users with at least read rights to |repos|.
506 506
507 507 :param apiuser: This is filled automatically from the |authtoken|.
508 508 :type apiuser: AuthUser
509 509 :param repoid: The repository name or repository ID.
510 510 :type repoid: str or int
511 511
512 512 Example output:
513 513
514 514 .. code-block:: bash
515 515
516 516 id : <id_given_in_input>
517 517 result: [
518 518 TODO...
519 519 ]
520 520 error: null
521 521 """
522 522
523 523 repo = get_repo_or_error(repoid)
524 524 if not has_superadmin_permission(apiuser):
525 525 _perms = ('repository.admin', 'repository.write', 'repository.read',)
526 526 has_repo_permissions(apiuser, repoid, repo, _perms)
527 527
528 528 try:
529 529 # check if repo is not empty by any chance, skip quicker if it is.
530 530 vcs_instance = repo.scm_instance()
531 531 refs = vcs_instance.refs()
532 532 return refs
533 533 except Exception:
534 534 log.exception("Exception occurred while trying to get repo refs")
535 535 raise JSONRPCError(
536 536 'failed to get repo: `%s` references' % repo.repo_name
537 537 )
538 538
539 539
540 540 @jsonrpc_method()
541 541 def create_repo(request, apiuser, repo_name, repo_type,
542 542 owner=Optional(OAttr('apiuser')), description=Optional(''),
543 543 private=Optional(False), clone_uri=Optional(None),
544 544 landing_rev=Optional('rev:tip'),
545 545 enable_statistics=Optional(False),
546 546 enable_locking=Optional(False),
547 547 enable_downloads=Optional(False),
548 548 copy_permissions=Optional(False)):
549 549 """
550 550 Creates a repository.
551 551
552 552 * If the repository name contains "/", all the required repository
553 553 groups will be created.
554 554
555 555 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
556 556 (with "foo" as parent). It will also create the "baz" repository
557 557 with "bar" as |repo| group.
558 558
559 559 This command can only be run using an |authtoken| with at least
560 560 write permissions to the |repo|.
561 561
562 562 :param apiuser: This is filled automatically from the |authtoken|.
563 563 :type apiuser: AuthUser
564 564 :param repo_name: Set the repository name.
565 565 :type repo_name: str
566 566 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
567 567 :type repo_type: str
568 568 :param owner: user_id or username
569 569 :type owner: Optional(str)
570 570 :param description: Set the repository description.
571 571 :type description: Optional(str)
572 572 :param private:
573 573 :type private: bool
574 574 :param clone_uri:
575 575 :type clone_uri: str
576 576 :param landing_rev: <rev_type>:<rev>
577 577 :type landing_rev: str
578 578 :param enable_locking:
579 579 :type enable_locking: bool
580 580 :param enable_downloads:
581 581 :type enable_downloads: bool
582 582 :param enable_statistics:
583 583 :type enable_statistics: bool
584 584 :param copy_permissions: Copy permission from group in which the
585 585 repository is being created.
586 586 :type copy_permissions: bool
587 587
588 588
589 589 Example output:
590 590
591 591 .. code-block:: bash
592 592
593 593 id : <id_given_in_input>
594 594 result: {
595 595 "msg": "Created new repository `<reponame>`",
596 596 "success": true,
597 597 "task": "<celery task id or None if done sync>"
598 598 }
599 599 error: null
600 600
601 601
602 602 Example error output:
603 603
604 604 .. code-block:: bash
605 605
606 606 id : <id_given_in_input>
607 607 result : null
608 608 error : {
609 'failed to create repository `<repo_name>`
609 'failed to create repository `<repo_name>`'
610 610 }
611 611
612 612 """
613 613 schema = repo_schema.RepoSchema()
614 614 try:
615 615 data = schema.deserialize({
616 616 'repo_name': repo_name
617 617 })
618 618 except colander.Invalid as e:
619 619 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
620 620 repo_name = data['repo_name']
621 621
622 622 (repo_name_cleaned,
623 623 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
624 624 repo_name)
625 625
626 626 if not HasPermissionAnyApi(
627 627 'hg.admin', 'hg.create.repository')(user=apiuser):
628 628 # check if we have admin permission for this repo group if given !
629 629
630 630 if parent_group_name:
631 631 repogroupid = parent_group_name
632 632 repo_group = get_repo_group_or_error(parent_group_name)
633 633
634 634 _perms = ('group.admin',)
635 635 if not HasRepoGroupPermissionAnyApi(*_perms)(
636 636 user=apiuser, group_name=repo_group.group_name):
637 637 raise JSONRPCError(
638 638 'repository group `%s` does not exist' % (
639 639 repogroupid,))
640 640 else:
641 641 raise JSONRPCForbidden()
642 642
643 643 if not has_superadmin_permission(apiuser):
644 644 if not isinstance(owner, Optional):
645 645 # forbid setting owner for non-admins
646 646 raise JSONRPCError(
647 647 'Only RhodeCode admin can specify `owner` param')
648 648
649 649 if isinstance(owner, Optional):
650 650 owner = apiuser.user_id
651 651
652 652 owner = get_user_or_error(owner)
653 653
654 654 if RepoModel().get_by_repo_name(repo_name):
655 655 raise JSONRPCError("repo `%s` already exist" % repo_name)
656 656
657 657 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
658 658 if isinstance(private, Optional):
659 659 private = defs.get('repo_private') or Optional.extract(private)
660 660 if isinstance(repo_type, Optional):
661 661 repo_type = defs.get('repo_type')
662 662 if isinstance(enable_statistics, Optional):
663 663 enable_statistics = defs.get('repo_enable_statistics')
664 664 if isinstance(enable_locking, Optional):
665 665 enable_locking = defs.get('repo_enable_locking')
666 666 if isinstance(enable_downloads, Optional):
667 667 enable_downloads = defs.get('repo_enable_downloads')
668 668
669 669 clone_uri = Optional.extract(clone_uri)
670 670 description = Optional.extract(description)
671 671 landing_rev = Optional.extract(landing_rev)
672 672 copy_permissions = Optional.extract(copy_permissions)
673 673
674 674 try:
675 675 # create structure of groups and return the last group
676 676 repo_group = map_groups(repo_name)
677 677 data = {
678 678 'repo_name': repo_name_cleaned,
679 679 'repo_name_full': repo_name,
680 680 'repo_type': repo_type,
681 681 'repo_description': description,
682 682 'owner': owner,
683 683 'repo_private': private,
684 684 'clone_uri': clone_uri,
685 685 'repo_group': repo_group.group_id if repo_group else None,
686 686 'repo_landing_rev': landing_rev,
687 687 'enable_statistics': enable_statistics,
688 688 'enable_locking': enable_locking,
689 689 'enable_downloads': enable_downloads,
690 690 'repo_copy_permissions': copy_permissions,
691 691 }
692 692
693 693 if repo_type not in BACKENDS.keys():
694 694 raise Exception("Invalid backend type %s" % repo_type)
695 695 task = RepoModel().create(form_data=data, cur_user=owner)
696 696 from celery.result import BaseAsyncResult
697 697 task_id = None
698 698 if isinstance(task, BaseAsyncResult):
699 699 task_id = task.task_id
700 700 # no commit, it's done in RepoModel, or async via celery
701 701 return {
702 702 'msg': "Created new repository `%s`" % (repo_name,),
703 703 'success': True, # cannot return the repo data here since fork
704 704 # cann be done async
705 705 'task': task_id
706 706 }
707 707 except Exception:
708 708 log.exception(
709 709 u"Exception while trying to create the repository %s",
710 710 repo_name)
711 711 raise JSONRPCError(
712 712 'failed to create repository `%s`' % (repo_name,))
713 713
714 714
715 715 @jsonrpc_method()
716 716 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
717 717 description=Optional('')):
718 718 """
719 719 Adds an extra field to a repository.
720 720
721 721 This command can only be run using an |authtoken| with at least
722 722 write permissions to the |repo|.
723 723
724 724 :param apiuser: This is filled automatically from the |authtoken|.
725 725 :type apiuser: AuthUser
726 726 :param repoid: Set the repository name or repository id.
727 727 :type repoid: str or int
728 728 :param key: Create a unique field key for this repository.
729 729 :type key: str
730 730 :param label:
731 731 :type label: Optional(str)
732 732 :param description:
733 733 :type description: Optional(str)
734 734 """
735 735 repo = get_repo_or_error(repoid)
736 736 if not has_superadmin_permission(apiuser):
737 737 _perms = ('repository.admin',)
738 738 has_repo_permissions(apiuser, repoid, repo, _perms)
739 739
740 740 label = Optional.extract(label) or key
741 741 description = Optional.extract(description)
742 742
743 743 field = RepositoryField.get_by_key_name(key, repo)
744 744 if field:
745 745 raise JSONRPCError('Field with key '
746 746 '`%s` exists for repo `%s`' % (key, repoid))
747 747
748 748 try:
749 749 RepoModel().add_repo_field(repo, key, field_label=label,
750 750 field_desc=description)
751 751 Session().commit()
752 752 return {
753 753 'msg': "Added new repository field `%s`" % (key,),
754 754 'success': True,
755 755 }
756 756 except Exception:
757 757 log.exception("Exception occurred while trying to add field to repo")
758 758 raise JSONRPCError(
759 759 'failed to create new field for repository `%s`' % (repoid,))
760 760
761 761
762 762 @jsonrpc_method()
763 763 def remove_field_from_repo(request, apiuser, repoid, key):
764 764 """
765 765 Removes an extra field from a repository.
766 766
767 767 This command can only be run using an |authtoken| with at least
768 768 write permissions to the |repo|.
769 769
770 770 :param apiuser: This is filled automatically from the |authtoken|.
771 771 :type apiuser: AuthUser
772 772 :param repoid: Set the repository name or repository ID.
773 773 :type repoid: str or int
774 774 :param key: Set the unique field key for this repository.
775 775 :type key: str
776 776 """
777 777
778 778 repo = get_repo_or_error(repoid)
779 779 if not has_superadmin_permission(apiuser):
780 780 _perms = ('repository.admin',)
781 781 has_repo_permissions(apiuser, repoid, repo, _perms)
782 782
783 783 field = RepositoryField.get_by_key_name(key, repo)
784 784 if not field:
785 785 raise JSONRPCError('Field with key `%s` does not '
786 786 'exists for repo `%s`' % (key, repoid))
787 787
788 788 try:
789 789 RepoModel().delete_repo_field(repo, field_key=key)
790 790 Session().commit()
791 791 return {
792 792 'msg': "Deleted repository field `%s`" % (key,),
793 793 'success': True,
794 794 }
795 795 except Exception:
796 796 log.exception(
797 797 "Exception occurred while trying to delete field from repo")
798 798 raise JSONRPCError(
799 799 'failed to delete field for repository `%s`' % (repoid,))
800 800
801 801
802 802 @jsonrpc_method()
803 803 def update_repo(request, apiuser, repoid, name=Optional(None),
804 804 owner=Optional(OAttr('apiuser')),
805 805 group=Optional(None),
806 806 fork_of=Optional(None),
807 807 description=Optional(''), private=Optional(False),
808 808 clone_uri=Optional(None), landing_rev=Optional('rev:tip'),
809 809 enable_statistics=Optional(False),
810 810 enable_locking=Optional(False),
811 811 enable_downloads=Optional(False),
812 812 fields=Optional('')):
813 813 """
814 814 Updates a repository with the given information.
815 815
816 816 This command can only be run using an |authtoken| with at least
817 817 write permissions to the |repo|.
818 818
819 819 :param apiuser: This is filled automatically from the |authtoken|.
820 820 :type apiuser: AuthUser
821 821 :param repoid: repository name or repository ID.
822 822 :type repoid: str or int
823 823 :param name: Update the |repo| name.
824 824 :type name: str
825 825 :param owner: Set the |repo| owner.
826 826 :type owner: str
827 827 :param group: Set the |repo| group the |repo| belongs to.
828 828 :type group: str
829 829 :param fork_of: Set the master |repo| name.
830 830 :type fork_of: str
831 831 :param description: Update the |repo| description.
832 832 :type description: str
833 833 :param private: Set the |repo| as private. (True | False)
834 834 :type private: bool
835 835 :param clone_uri: Update the |repo| clone URI.
836 836 :type clone_uri: str
837 837 :param landing_rev: Set the |repo| landing revision. Default is
838 838 ``tip``.
839 839 :type landing_rev: str
840 840 :param enable_statistics: Enable statistics on the |repo|,
841 841 (True | False).
842 842 :type enable_statistics: bool
843 843 :param enable_locking: Enable |repo| locking.
844 844 :type enable_locking: bool
845 845 :param enable_downloads: Enable downloads from the |repo|,
846 846 (True | False).
847 847 :type enable_downloads: bool
848 848 :param fields: Add extra fields to the |repo|. Use the following
849 849 example format: ``field_key=field_val,field_key2=fieldval2``.
850 850 Escape ', ' with \,
851 851 :type fields: str
852 852 """
853 853 repo = get_repo_or_error(repoid)
854 854 include_secrets = False
855 855 if has_superadmin_permission(apiuser):
856 856 include_secrets = True
857 857 else:
858 858 _perms = ('repository.admin',)
859 859 has_repo_permissions(apiuser, repoid, repo, _perms)
860 860
861 861 updates = {
862 862 # update function requires this.
863 863 'repo_name': repo.just_name
864 864 }
865 865 repo_group = group
866 866 if not isinstance(repo_group, Optional):
867 867 repo_group = get_repo_group_or_error(repo_group)
868 868 repo_group = repo_group.group_id
869 869
870 870 repo_fork_of = fork_of
871 871 if not isinstance(repo_fork_of, Optional):
872 872 repo_fork_of = get_repo_or_error(repo_fork_of)
873 873 repo_fork_of = repo_fork_of.repo_id
874 874
875 875 try:
876 876 store_update(updates, name, 'repo_name')
877 877 store_update(updates, repo_group, 'repo_group')
878 878 store_update(updates, repo_fork_of, 'fork_id')
879 879 store_update(updates, owner, 'user')
880 880 store_update(updates, description, 'repo_description')
881 881 store_update(updates, private, 'repo_private')
882 882 store_update(updates, clone_uri, 'clone_uri')
883 883 store_update(updates, landing_rev, 'repo_landing_rev')
884 884 store_update(updates, enable_statistics, 'repo_enable_statistics')
885 885 store_update(updates, enable_locking, 'repo_enable_locking')
886 886 store_update(updates, enable_downloads, 'repo_enable_downloads')
887 887
888 888 # extra fields
889 889 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
890 890 if fields:
891 891 updates.update(fields)
892 892
893 893 RepoModel().update(repo, **updates)
894 894 Session().commit()
895 895 return {
896 896 'msg': 'updated repo ID:%s %s' % (
897 897 repo.repo_id, repo.repo_name),
898 898 'repository': repo.get_api_data(
899 899 include_secrets=include_secrets)
900 900 }
901 901 except Exception:
902 902 log.exception(
903 903 u"Exception while trying to update the repository %s",
904 904 repoid)
905 905 raise JSONRPCError('failed to update repo `%s`' % repoid)
906 906
907 907
908 908 @jsonrpc_method()
909 909 def fork_repo(request, apiuser, repoid, fork_name,
910 910 owner=Optional(OAttr('apiuser')),
911 911 description=Optional(''), copy_permissions=Optional(False),
912 912 private=Optional(False), landing_rev=Optional('rev:tip')):
913 913 """
914 914 Creates a fork of the specified |repo|.
915 915
916 916 * If using |RCE| with Celery this will immediately return a success
917 917 message, even though the fork will be created asynchronously.
918 918
919 919 This command can only be run using an |authtoken| with fork
920 920 permissions on the |repo|.
921 921
922 922 :param apiuser: This is filled automatically from the |authtoken|.
923 923 :type apiuser: AuthUser
924 924 :param repoid: Set repository name or repository ID.
925 925 :type repoid: str or int
926 926 :param fork_name: Set the fork name.
927 927 :type fork_name: str
928 928 :param owner: Set the fork owner.
929 929 :type owner: str
930 930 :param description: Set the fork descripton.
931 931 :type description: str
932 932 :param copy_permissions: Copy permissions from parent |repo|. The
933 933 default is False.
934 934 :type copy_permissions: bool
935 935 :param private: Make the fork private. The default is False.
936 936 :type private: bool
937 937 :param landing_rev: Set the landing revision. The default is tip.
938 938
939 939 Example output:
940 940
941 941 .. code-block:: bash
942 942
943 943 id : <id_for_response>
944 944 api_key : "<api_key>"
945 945 args: {
946 946 "repoid" : "<reponame or repo_id>",
947 947 "fork_name": "<forkname>",
948 948 "owner": "<username or user_id = Optional(=apiuser)>",
949 949 "description": "<description>",
950 950 "copy_permissions": "<bool>",
951 951 "private": "<bool>",
952 952 "landing_rev": "<landing_rev>"
953 953 }
954 954
955 955 Example error output:
956 956
957 957 .. code-block:: bash
958 958
959 959 id : <id_given_in_input>
960 960 result: {
961 961 "msg": "Created fork of `<reponame>` as `<forkname>`",
962 962 "success": true,
963 963 "task": "<celery task id or None if done sync>"
964 964 }
965 965 error: null
966 966
967 967 """
968 968 if not has_superadmin_permission(apiuser):
969 969 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
970 970 raise JSONRPCForbidden()
971 971
972 972 repo = get_repo_or_error(repoid)
973 973 repo_name = repo.repo_name
974 974
975 975 (fork_name_cleaned,
976 976 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
977 977 fork_name)
978 978
979 979 if not has_superadmin_permission(apiuser):
980 980 # check if we have at least read permission for
981 981 # this repo that we fork !
982 982 _perms = (
983 983 'repository.admin', 'repository.write', 'repository.read')
984 984 has_repo_permissions(apiuser, repoid, repo, _perms)
985 985
986 986 if not isinstance(owner, Optional):
987 987 # forbid setting owner for non super admins
988 988 raise JSONRPCError(
989 989 'Only RhodeCode admin can specify `owner` param'
990 990 )
991 991 # check if we have a create.repo permission if not maybe the parent
992 992 # group permission
993 993 if not HasPermissionAnyApi('hg.create.repository')(user=apiuser):
994 994 if parent_group_name:
995 995 repogroupid = parent_group_name
996 996 repo_group = get_repo_group_or_error(parent_group_name)
997 997
998 998 _perms = ('group.admin',)
999 999 if not HasRepoGroupPermissionAnyApi(*_perms)(
1000 1000 user=apiuser, group_name=repo_group.group_name):
1001 1001 raise JSONRPCError(
1002 1002 'repository group `%s` does not exist' % (
1003 1003 repogroupid,))
1004 1004 else:
1005 1005 raise JSONRPCForbidden()
1006 1006
1007 1007 _repo = RepoModel().get_by_repo_name(fork_name)
1008 1008 if _repo:
1009 1009 type_ = 'fork' if _repo.fork else 'repo'
1010 1010 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
1011 1011
1012 1012 if isinstance(owner, Optional):
1013 1013 owner = apiuser.user_id
1014 1014
1015 1015 owner = get_user_or_error(owner)
1016 1016
1017 1017 try:
1018 1018 # create structure of groups and return the last group
1019 1019 repo_group = map_groups(fork_name)
1020 1020 form_data = {
1021 1021 'repo_name': fork_name_cleaned,
1022 1022 'repo_name_full': fork_name,
1023 1023 'repo_group': repo_group.group_id if repo_group else None,
1024 1024 'repo_type': repo.repo_type,
1025 1025 'description': Optional.extract(description),
1026 1026 'private': Optional.extract(private),
1027 1027 'copy_permissions': Optional.extract(copy_permissions),
1028 1028 'landing_rev': Optional.extract(landing_rev),
1029 1029 'fork_parent_id': repo.repo_id,
1030 1030 }
1031 1031
1032 1032 task = RepoModel().create_fork(form_data, cur_user=owner)
1033 1033 # no commit, it's done in RepoModel, or async via celery
1034 1034 from celery.result import BaseAsyncResult
1035 1035 task_id = None
1036 1036 if isinstance(task, BaseAsyncResult):
1037 1037 task_id = task.task_id
1038 1038 return {
1039 1039 'msg': 'Created fork of `%s` as `%s`' % (
1040 1040 repo.repo_name, fork_name),
1041 1041 'success': True, # cannot return the repo data here since fork
1042 1042 # can be done async
1043 1043 'task': task_id
1044 1044 }
1045 1045 except Exception:
1046 1046 log.exception("Exception occurred while trying to fork a repo")
1047 1047 raise JSONRPCError(
1048 1048 'failed to fork repository `%s` as `%s`' % (
1049 1049 repo_name, fork_name))
1050 1050
1051 1051
1052 1052 @jsonrpc_method()
1053 1053 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1054 1054 """
1055 1055 Deletes a repository.
1056 1056
1057 1057 * When the `forks` parameter is set it's possible to detach or delete
1058 1058 forks of deleted repository.
1059 1059
1060 1060 This command can only be run using an |authtoken| with admin
1061 1061 permissions on the |repo|.
1062 1062
1063 1063 :param apiuser: This is filled automatically from the |authtoken|.
1064 1064 :type apiuser: AuthUser
1065 1065 :param repoid: Set the repository name or repository ID.
1066 1066 :type repoid: str or int
1067 1067 :param forks: Set to `detach` or `delete` forks from the |repo|.
1068 1068 :type forks: Optional(str)
1069 1069
1070 1070 Example error output:
1071 1071
1072 1072 .. code-block:: bash
1073 1073
1074 1074 id : <id_given_in_input>
1075 1075 result: {
1076 1076 "msg": "Deleted repository `<reponame>`",
1077 1077 "success": true
1078 1078 }
1079 1079 error: null
1080 1080 """
1081 1081
1082 1082 repo = get_repo_or_error(repoid)
1083 1083 if not has_superadmin_permission(apiuser):
1084 1084 _perms = ('repository.admin',)
1085 1085 has_repo_permissions(apiuser, repoid, repo, _perms)
1086 1086
1087 1087 try:
1088 1088 handle_forks = Optional.extract(forks)
1089 1089 _forks_msg = ''
1090 1090 _forks = [f for f in repo.forks]
1091 1091 if handle_forks == 'detach':
1092 1092 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1093 1093 elif handle_forks == 'delete':
1094 1094 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1095 1095 elif _forks:
1096 1096 raise JSONRPCError(
1097 1097 'Cannot delete `%s` it still contains attached forks' %
1098 1098 (repo.repo_name,)
1099 1099 )
1100 1100
1101 1101 RepoModel().delete(repo, forks=forks)
1102 1102 Session().commit()
1103 1103 return {
1104 1104 'msg': 'Deleted repository `%s`%s' % (
1105 1105 repo.repo_name, _forks_msg),
1106 1106 'success': True
1107 1107 }
1108 1108 except Exception:
1109 1109 log.exception("Exception occurred while trying to delete repo")
1110 1110 raise JSONRPCError(
1111 1111 'failed to delete repository `%s`' % (repo.repo_name,)
1112 1112 )
1113 1113
1114 1114
1115 1115 #TODO: marcink, change name ?
1116 1116 @jsonrpc_method()
1117 1117 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1118 1118 """
1119 1119 Invalidates the cache for the specified repository.
1120 1120
1121 1121 This command can only be run using an |authtoken| with admin rights to
1122 1122 the specified repository.
1123 1123
1124 1124 This command takes the following options:
1125 1125
1126 1126 :param apiuser: This is filled automatically from |authtoken|.
1127 1127 :type apiuser: AuthUser
1128 1128 :param repoid: Sets the repository name or repository ID.
1129 1129 :type repoid: str or int
1130 1130 :param delete_keys: This deletes the invalidated keys instead of
1131 1131 just flagging them.
1132 1132 :type delete_keys: Optional(``True`` | ``False``)
1133 1133
1134 1134 Example output:
1135 1135
1136 1136 .. code-block:: bash
1137 1137
1138 1138 id : <id_given_in_input>
1139 1139 result : {
1140 1140 'msg': Cache for repository `<repository name>` was invalidated,
1141 1141 'repository': <repository name>
1142 1142 }
1143 1143 error : null
1144 1144
1145 1145 Example error output:
1146 1146
1147 1147 .. code-block:: bash
1148 1148
1149 1149 id : <id_given_in_input>
1150 1150 result : null
1151 1151 error : {
1152 1152 'Error occurred during cache invalidation action'
1153 1153 }
1154 1154
1155 1155 """
1156 1156
1157 1157 repo = get_repo_or_error(repoid)
1158 1158 if not has_superadmin_permission(apiuser):
1159 1159 _perms = ('repository.admin', 'repository.write',)
1160 1160 has_repo_permissions(apiuser, repoid, repo, _perms)
1161 1161
1162 1162 delete = Optional.extract(delete_keys)
1163 1163 try:
1164 1164 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1165 1165 return {
1166 1166 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1167 1167 'repository': repo.repo_name
1168 1168 }
1169 1169 except Exception:
1170 1170 log.exception(
1171 1171 "Exception occurred while trying to invalidate repo cache")
1172 1172 raise JSONRPCError(
1173 1173 'Error occurred during cache invalidation action'
1174 1174 )
1175 1175
1176 1176
1177 1177 #TODO: marcink, change name ?
1178 1178 @jsonrpc_method()
1179 1179 def lock(request, apiuser, repoid, locked=Optional(None),
1180 1180 userid=Optional(OAttr('apiuser'))):
1181 1181 """
1182 1182 Sets the lock state of the specified |repo| by the given user.
1183 1183 From more information, see :ref:`repo-locking`.
1184 1184
1185 1185 * If the ``userid`` option is not set, the repository is locked to the
1186 1186 user who called the method.
1187 1187 * If the ``locked`` parameter is not set, the current lock state of the
1188 1188 repository is displayed.
1189 1189
1190 1190 This command can only be run using an |authtoken| with admin rights to
1191 1191 the specified repository.
1192 1192
1193 1193 This command takes the following options:
1194 1194
1195 1195 :param apiuser: This is filled automatically from the |authtoken|.
1196 1196 :type apiuser: AuthUser
1197 1197 :param repoid: Sets the repository name or repository ID.
1198 1198 :type repoid: str or int
1199 1199 :param locked: Sets the lock state.
1200 1200 :type locked: Optional(``True`` | ``False``)
1201 1201 :param userid: Set the repository lock to this user.
1202 1202 :type userid: Optional(str or int)
1203 1203
1204 1204 Example error output:
1205 1205
1206 1206 .. code-block:: bash
1207 1207
1208 1208 id : <id_given_in_input>
1209 1209 result : {
1210 1210 'repo': '<reponame>',
1211 1211 'locked': <bool: lock state>,
1212 1212 'locked_since': <int: lock timestamp>,
1213 1213 'locked_by': <username of person who made the lock>,
1214 1214 'lock_reason': <str: reason for locking>,
1215 1215 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1216 1216 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1217 1217 or
1218 1218 'msg': 'Repo `<repository name>` not locked.'
1219 1219 or
1220 1220 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1221 1221 }
1222 1222 error : null
1223 1223
1224 1224 Example error output:
1225 1225
1226 1226 .. code-block:: bash
1227 1227
1228 1228 id : <id_given_in_input>
1229 1229 result : null
1230 1230 error : {
1231 'Error occurred locking repository `<reponame>`
1231 'Error occurred locking repository `<reponame>`'
1232 1232 }
1233 1233 """
1234 1234
1235 1235 repo = get_repo_or_error(repoid)
1236 1236 if not has_superadmin_permission(apiuser):
1237 1237 # check if we have at least write permission for this repo !
1238 1238 _perms = ('repository.admin', 'repository.write',)
1239 1239 has_repo_permissions(apiuser, repoid, repo, _perms)
1240 1240
1241 1241 # make sure normal user does not pass someone else userid,
1242 1242 # he is not allowed to do that
1243 1243 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1244 1244 raise JSONRPCError('userid is not the same as your user')
1245 1245
1246 1246 if isinstance(userid, Optional):
1247 1247 userid = apiuser.user_id
1248 1248
1249 1249 user = get_user_or_error(userid)
1250 1250
1251 1251 if isinstance(locked, Optional):
1252 1252 lockobj = repo.locked
1253 1253
1254 1254 if lockobj[0] is None:
1255 1255 _d = {
1256 1256 'repo': repo.repo_name,
1257 1257 'locked': False,
1258 1258 'locked_since': None,
1259 1259 'locked_by': None,
1260 1260 'lock_reason': None,
1261 1261 'lock_state_changed': False,
1262 1262 'msg': 'Repo `%s` not locked.' % repo.repo_name
1263 1263 }
1264 1264 return _d
1265 1265 else:
1266 1266 _user_id, _time, _reason = lockobj
1267 1267 lock_user = get_user_or_error(userid)
1268 1268 _d = {
1269 1269 'repo': repo.repo_name,
1270 1270 'locked': True,
1271 1271 'locked_since': _time,
1272 1272 'locked_by': lock_user.username,
1273 1273 'lock_reason': _reason,
1274 1274 'lock_state_changed': False,
1275 1275 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1276 1276 % (repo.repo_name, lock_user.username,
1277 1277 json.dumps(time_to_datetime(_time))))
1278 1278 }
1279 1279 return _d
1280 1280
1281 1281 # force locked state through a flag
1282 1282 else:
1283 1283 locked = str2bool(locked)
1284 1284 lock_reason = Repository.LOCK_API
1285 1285 try:
1286 1286 if locked:
1287 1287 lock_time = time.time()
1288 1288 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1289 1289 else:
1290 1290 lock_time = None
1291 1291 Repository.unlock(repo)
1292 1292 _d = {
1293 1293 'repo': repo.repo_name,
1294 1294 'locked': locked,
1295 1295 'locked_since': lock_time,
1296 1296 'locked_by': user.username,
1297 1297 'lock_reason': lock_reason,
1298 1298 'lock_state_changed': True,
1299 1299 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1300 1300 % (user.username, repo.repo_name, locked))
1301 1301 }
1302 1302 return _d
1303 1303 except Exception:
1304 1304 log.exception(
1305 1305 "Exception occurred while trying to lock repository")
1306 1306 raise JSONRPCError(
1307 1307 'Error occurred locking repository `%s`' % repo.repo_name
1308 1308 )
1309 1309
1310 1310
1311 1311 @jsonrpc_method()
1312 1312 def comment_commit(
1313 1313 request, apiuser, repoid, commit_id, message,
1314 1314 userid=Optional(OAttr('apiuser')), status=Optional(None)):
1315 1315 """
1316 1316 Set a commit comment, and optionally change the status of the commit.
1317 1317
1318 1318 :param apiuser: This is filled automatically from the |authtoken|.
1319 1319 :type apiuser: AuthUser
1320 1320 :param repoid: Set the repository name or repository ID.
1321 1321 :type repoid: str or int
1322 1322 :param commit_id: Specify the commit_id for which to set a comment.
1323 1323 :type commit_id: str
1324 1324 :param message: The comment text.
1325 1325 :type message: str
1326 1326 :param userid: Set the user name of the comment creator.
1327 1327 :type userid: Optional(str or int)
1328 1328 :param status: status, one of 'not_reviewed', 'approved', 'rejected',
1329 1329 'under_review'
1330 1330 :type status: str
1331 1331
1332 1332 Example error output:
1333 1333
1334 1334 .. code-block:: json
1335 1335
1336 1336 {
1337 1337 "id" : <id_given_in_input>,
1338 1338 "result" : {
1339 1339 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1340 1340 "status_change": null or <status>,
1341 1341 "success": true
1342 1342 },
1343 1343 "error" : null
1344 1344 }
1345 1345
1346 1346 """
1347 1347 repo = get_repo_or_error(repoid)
1348 1348 if not has_superadmin_permission(apiuser):
1349 1349 _perms = ('repository.read', 'repository.write', 'repository.admin')
1350 1350 has_repo_permissions(apiuser, repoid, repo, _perms)
1351 1351
1352 1352 if isinstance(userid, Optional):
1353 1353 userid = apiuser.user_id
1354 1354
1355 1355 user = get_user_or_error(userid)
1356 1356 status = Optional.extract(status)
1357 1357
1358 1358 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1359 1359 if status and status not in allowed_statuses:
1360 1360 raise JSONRPCError('Bad status, must be on '
1361 1361 'of %s got %s' % (allowed_statuses, status,))
1362 1362
1363 1363 try:
1364 1364 rc_config = SettingsModel().get_all_settings()
1365 1365 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1366 1366 status_change_label = ChangesetStatus.get_status_lbl(status)
1367 1367 comm = ChangesetCommentsModel().create(
1368 1368 message, repo, user, revision=commit_id,
1369 1369 status_change=status_change_label,
1370 1370 status_change_type=status,
1371 1371 renderer=renderer)
1372 1372 if status:
1373 1373 # also do a status change
1374 1374 try:
1375 1375 ChangesetStatusModel().set_status(
1376 1376 repo, status, user, comm, revision=commit_id,
1377 1377 dont_allow_on_closed_pull_request=True
1378 1378 )
1379 1379 except StatusChangeOnClosedPullRequestError:
1380 1380 log.exception(
1381 1381 "Exception occurred while trying to change repo commit status")
1382 1382 msg = ('Changing status on a changeset associated with '
1383 1383 'a closed pull request is not allowed')
1384 1384 raise JSONRPCError(msg)
1385 1385
1386 1386 Session().commit()
1387 1387 return {
1388 1388 'msg': (
1389 1389 'Commented on commit `%s` for repository `%s`' % (
1390 1390 comm.revision, repo.repo_name)),
1391 1391 'status_change': status,
1392 1392 'success': True,
1393 1393 }
1394 1394 except JSONRPCError:
1395 1395 # catch any inside errors, and re-raise them to prevent from
1396 1396 # below global catch to silence them
1397 1397 raise
1398 1398 except Exception:
1399 1399 log.exception("Exception occurred while trying to comment on commit")
1400 1400 raise JSONRPCError(
1401 1401 'failed to set comment on repository `%s`' % (repo.repo_name,)
1402 1402 )
1403 1403
1404 1404
1405 1405 @jsonrpc_method()
1406 1406 def grant_user_permission(request, apiuser, repoid, userid, perm):
1407 1407 """
1408 1408 Grant permissions for the specified user on the given repository,
1409 1409 or update existing permissions if found.
1410 1410
1411 1411 This command can only be run using an |authtoken| with admin
1412 1412 permissions on the |repo|.
1413 1413
1414 1414 :param apiuser: This is filled automatically from the |authtoken|.
1415 1415 :type apiuser: AuthUser
1416 1416 :param repoid: Set the repository name or repository ID.
1417 1417 :type repoid: str or int
1418 1418 :param userid: Set the user name.
1419 1419 :type userid: str
1420 1420 :param perm: Set the user permissions, using the following format
1421 1421 ``(repository.(none|read|write|admin))``
1422 1422 :type perm: str
1423 1423
1424 1424 Example output:
1425 1425
1426 1426 .. code-block:: bash
1427 1427
1428 1428 id : <id_given_in_input>
1429 1429 result: {
1430 1430 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1431 1431 "success": true
1432 1432 }
1433 1433 error: null
1434 1434 """
1435 1435
1436 1436 repo = get_repo_or_error(repoid)
1437 1437 user = get_user_or_error(userid)
1438 1438 perm = get_perm_or_error(perm)
1439 1439 if not has_superadmin_permission(apiuser):
1440 1440 _perms = ('repository.admin',)
1441 1441 has_repo_permissions(apiuser, repoid, repo, _perms)
1442 1442
1443 1443 try:
1444 1444
1445 1445 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1446 1446
1447 1447 Session().commit()
1448 1448 return {
1449 1449 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1450 1450 perm.permission_name, user.username, repo.repo_name
1451 1451 ),
1452 1452 'success': True
1453 1453 }
1454 1454 except Exception:
1455 1455 log.exception(
1456 1456 "Exception occurred while trying edit permissions for repo")
1457 1457 raise JSONRPCError(
1458 1458 'failed to edit permission for user: `%s` in repo: `%s`' % (
1459 1459 userid, repoid
1460 1460 )
1461 1461 )
1462 1462
1463 1463
1464 1464 @jsonrpc_method()
1465 1465 def revoke_user_permission(request, apiuser, repoid, userid):
1466 1466 """
1467 1467 Revoke permission for a user on the specified repository.
1468 1468
1469 1469 This command can only be run using an |authtoken| with admin
1470 1470 permissions on the |repo|.
1471 1471
1472 1472 :param apiuser: This is filled automatically from the |authtoken|.
1473 1473 :type apiuser: AuthUser
1474 1474 :param repoid: Set the repository name or repository ID.
1475 1475 :type repoid: str or int
1476 1476 :param userid: Set the user name of revoked user.
1477 1477 :type userid: str or int
1478 1478
1479 1479 Example error output:
1480 1480
1481 1481 .. code-block:: bash
1482 1482
1483 1483 id : <id_given_in_input>
1484 1484 result: {
1485 1485 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1486 1486 "success": true
1487 1487 }
1488 1488 error: null
1489 1489 """
1490 1490
1491 1491 repo = get_repo_or_error(repoid)
1492 1492 user = get_user_or_error(userid)
1493 1493 if not has_superadmin_permission(apiuser):
1494 1494 _perms = ('repository.admin',)
1495 1495 has_repo_permissions(apiuser, repoid, repo, _perms)
1496 1496
1497 1497 try:
1498 1498 RepoModel().revoke_user_permission(repo=repo, user=user)
1499 1499 Session().commit()
1500 1500 return {
1501 1501 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1502 1502 user.username, repo.repo_name
1503 1503 ),
1504 1504 'success': True
1505 1505 }
1506 1506 except Exception:
1507 1507 log.exception(
1508 1508 "Exception occurred while trying revoke permissions to repo")
1509 1509 raise JSONRPCError(
1510 1510 'failed to edit permission for user: `%s` in repo: `%s`' % (
1511 1511 userid, repoid
1512 1512 )
1513 1513 )
1514 1514
1515 1515
1516 1516 @jsonrpc_method()
1517 1517 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1518 1518 """
1519 1519 Grant permission for a user group on the specified repository,
1520 1520 or update existing permissions.
1521 1521
1522 1522 This command can only be run using an |authtoken| with admin
1523 1523 permissions on the |repo|.
1524 1524
1525 1525 :param apiuser: This is filled automatically from the |authtoken|.
1526 1526 :type apiuser: AuthUser
1527 1527 :param repoid: Set the repository name or repository ID.
1528 1528 :type repoid: str or int
1529 1529 :param usergroupid: Specify the ID of the user group.
1530 1530 :type usergroupid: str or int
1531 1531 :param perm: Set the user group permissions using the following
1532 1532 format: (repository.(none|read|write|admin))
1533 1533 :type perm: str
1534 1534
1535 1535 Example output:
1536 1536
1537 1537 .. code-block:: bash
1538 1538
1539 1539 id : <id_given_in_input>
1540 1540 result : {
1541 1541 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1542 1542 "success": true
1543 1543
1544 1544 }
1545 1545 error : null
1546 1546
1547 1547 Example error output:
1548 1548
1549 1549 .. code-block:: bash
1550 1550
1551 1551 id : <id_given_in_input>
1552 1552 result : null
1553 1553 error : {
1554 1554 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1555 1555 }
1556 1556
1557 1557 """
1558 1558
1559 1559 repo = get_repo_or_error(repoid)
1560 1560 perm = get_perm_or_error(perm)
1561 1561 if not has_superadmin_permission(apiuser):
1562 1562 _perms = ('repository.admin',)
1563 1563 has_repo_permissions(apiuser, repoid, repo, _perms)
1564 1564
1565 1565 user_group = get_user_group_or_error(usergroupid)
1566 1566 if not has_superadmin_permission(apiuser):
1567 1567 # check if we have at least read permission for this user group !
1568 1568 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1569 1569 if not HasUserGroupPermissionAnyApi(*_perms)(
1570 1570 user=apiuser, user_group_name=user_group.users_group_name):
1571 1571 raise JSONRPCError(
1572 1572 'user group `%s` does not exist' % (usergroupid,))
1573 1573
1574 1574 try:
1575 1575 RepoModel().grant_user_group_permission(
1576 1576 repo=repo, group_name=user_group, perm=perm)
1577 1577
1578 1578 Session().commit()
1579 1579 return {
1580 1580 'msg': 'Granted perm: `%s` for user group: `%s` in '
1581 1581 'repo: `%s`' % (
1582 1582 perm.permission_name, user_group.users_group_name,
1583 1583 repo.repo_name
1584 1584 ),
1585 1585 'success': True
1586 1586 }
1587 1587 except Exception:
1588 1588 log.exception(
1589 1589 "Exception occurred while trying change permission on repo")
1590 1590 raise JSONRPCError(
1591 1591 'failed to edit permission for user group: `%s` in '
1592 1592 'repo: `%s`' % (
1593 1593 usergroupid, repo.repo_name
1594 1594 )
1595 1595 )
1596 1596
1597 1597
1598 1598 @jsonrpc_method()
1599 1599 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1600 1600 """
1601 1601 Revoke the permissions of a user group on a given repository.
1602 1602
1603 1603 This command can only be run using an |authtoken| with admin
1604 1604 permissions on the |repo|.
1605 1605
1606 1606 :param apiuser: This is filled automatically from the |authtoken|.
1607 1607 :type apiuser: AuthUser
1608 1608 :param repoid: Set the repository name or repository ID.
1609 1609 :type repoid: str or int
1610 1610 :param usergroupid: Specify the user group ID.
1611 1611 :type usergroupid: str or int
1612 1612
1613 1613 Example output:
1614 1614
1615 1615 .. code-block:: bash
1616 1616
1617 1617 id : <id_given_in_input>
1618 1618 result: {
1619 1619 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1620 1620 "success": true
1621 1621 }
1622 1622 error: null
1623 1623 """
1624 1624
1625 1625 repo = get_repo_or_error(repoid)
1626 1626 if not has_superadmin_permission(apiuser):
1627 1627 _perms = ('repository.admin',)
1628 1628 has_repo_permissions(apiuser, repoid, repo, _perms)
1629 1629
1630 1630 user_group = get_user_group_or_error(usergroupid)
1631 1631 if not has_superadmin_permission(apiuser):
1632 1632 # check if we have at least read permission for this user group !
1633 1633 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1634 1634 if not HasUserGroupPermissionAnyApi(*_perms)(
1635 1635 user=apiuser, user_group_name=user_group.users_group_name):
1636 1636 raise JSONRPCError(
1637 1637 'user group `%s` does not exist' % (usergroupid,))
1638 1638
1639 1639 try:
1640 1640 RepoModel().revoke_user_group_permission(
1641 1641 repo=repo, group_name=user_group)
1642 1642
1643 1643 Session().commit()
1644 1644 return {
1645 1645 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1646 1646 user_group.users_group_name, repo.repo_name
1647 1647 ),
1648 1648 'success': True
1649 1649 }
1650 1650 except Exception:
1651 1651 log.exception("Exception occurred while trying revoke "
1652 1652 "user group permission on repo")
1653 1653 raise JSONRPCError(
1654 1654 'failed to edit permission for user group: `%s` in '
1655 1655 'repo: `%s`' % (
1656 1656 user_group.users_group_name, repo.repo_name
1657 1657 )
1658 1658 )
1659 1659
1660 1660
1661 1661 @jsonrpc_method()
1662 1662 def pull(request, apiuser, repoid):
1663 1663 """
1664 1664 Triggers a pull on the given repository from a remote location. You
1665 1665 can use this to keep remote repositories up-to-date.
1666 1666
1667 1667 This command can only be run using an |authtoken| with admin
1668 1668 rights to the specified repository. For more information,
1669 1669 see :ref:`config-token-ref`.
1670 1670
1671 1671 This command takes the following options:
1672 1672
1673 1673 :param apiuser: This is filled automatically from the |authtoken|.
1674 1674 :type apiuser: AuthUser
1675 1675 :param repoid: The repository name or repository ID.
1676 1676 :type repoid: str or int
1677 1677
1678 1678 Example output:
1679 1679
1680 1680 .. code-block:: bash
1681 1681
1682 1682 id : <id_given_in_input>
1683 1683 result : {
1684 1684 "msg": "Pulled from `<repository name>`"
1685 1685 "repository": "<repository name>"
1686 1686 }
1687 1687 error : null
1688 1688
1689 1689 Example error output:
1690 1690
1691 1691 .. code-block:: bash
1692 1692
1693 1693 id : <id_given_in_input>
1694 1694 result : null
1695 1695 error : {
1696 1696 "Unable to pull changes from `<reponame>`"
1697 1697 }
1698 1698
1699 1699 """
1700 1700
1701 1701 repo = get_repo_or_error(repoid)
1702 1702 if not has_superadmin_permission(apiuser):
1703 1703 _perms = ('repository.admin',)
1704 1704 has_repo_permissions(apiuser, repoid, repo, _perms)
1705 1705
1706 1706 try:
1707 1707 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1708 1708 return {
1709 1709 'msg': 'Pulled from `%s`' % repo.repo_name,
1710 1710 'repository': repo.repo_name
1711 1711 }
1712 1712 except Exception:
1713 1713 log.exception("Exception occurred while trying to "
1714 1714 "pull changes from remote location")
1715 1715 raise JSONRPCError(
1716 1716 'Unable to pull changes from `%s`' % repo.repo_name
1717 1717 )
1718 1718
1719 1719
1720 1720 @jsonrpc_method()
1721 1721 def strip(request, apiuser, repoid, revision, branch):
1722 1722 """
1723 1723 Strips the given revision from the specified repository.
1724 1724
1725 1725 * This will remove the revision and all of its decendants.
1726 1726
1727 1727 This command can only be run using an |authtoken| with admin rights to
1728 1728 the specified repository.
1729 1729
1730 1730 This command takes the following options:
1731 1731
1732 1732 :param apiuser: This is filled automatically from the |authtoken|.
1733 1733 :type apiuser: AuthUser
1734 1734 :param repoid: The repository name or repository ID.
1735 1735 :type repoid: str or int
1736 1736 :param revision: The revision you wish to strip.
1737 1737 :type revision: str
1738 1738 :param branch: The branch from which to strip the revision.
1739 1739 :type branch: str
1740 1740
1741 1741 Example output:
1742 1742
1743 1743 .. code-block:: bash
1744 1744
1745 1745 id : <id_given_in_input>
1746 1746 result : {
1747 1747 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1748 1748 "repository": "<repository name>"
1749 1749 }
1750 1750 error : null
1751 1751
1752 1752 Example error output:
1753 1753
1754 1754 .. code-block:: bash
1755 1755
1756 1756 id : <id_given_in_input>
1757 1757 result : null
1758 1758 error : {
1759 1759 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1760 1760 }
1761 1761
1762 1762 """
1763 1763
1764 1764 repo = get_repo_or_error(repoid)
1765 1765 if not has_superadmin_permission(apiuser):
1766 1766 _perms = ('repository.admin',)
1767 1767 has_repo_permissions(apiuser, repoid, repo, _perms)
1768 1768
1769 1769 try:
1770 1770 ScmModel().strip(repo, revision, branch)
1771 1771 return {
1772 1772 'msg': 'Stripped commit %s from repo `%s`' % (
1773 1773 revision, repo.repo_name),
1774 1774 'repository': repo.repo_name
1775 1775 }
1776 1776 except Exception:
1777 1777 log.exception("Exception while trying to strip")
1778 1778 raise JSONRPCError(
1779 1779 'Unable to strip commit %s from repo `%s`' % (
1780 1780 revision, repo.repo_name)
1781 1781 )
1782 1782
1783 1783
1784 1784 @jsonrpc_method()
1785 1785 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1786 1786 """
1787 1787 Returns all settings for a repository. If key is given it only returns the
1788 1788 setting identified by the key or null.
1789 1789
1790 1790 :param apiuser: This is filled automatically from the |authtoken|.
1791 1791 :type apiuser: AuthUser
1792 1792 :param repoid: The repository name or repository id.
1793 1793 :type repoid: str or int
1794 1794 :param key: Key of the setting to return.
1795 1795 :type: key: Optional(str)
1796 1796
1797 1797 Example output:
1798 1798
1799 1799 .. code-block:: bash
1800 1800
1801 1801 {
1802 1802 "error": null,
1803 1803 "id": 237,
1804 1804 "result": {
1805 1805 "extensions_largefiles": true,
1806 1806 "hooks_changegroup_push_logger": true,
1807 1807 "hooks_changegroup_repo_size": false,
1808 1808 "hooks_outgoing_pull_logger": true,
1809 1809 "phases_publish": "True",
1810 1810 "rhodecode_hg_use_rebase_for_merging": true,
1811 1811 "rhodecode_pr_merge_enabled": true,
1812 1812 "rhodecode_use_outdated_comments": true
1813 1813 }
1814 1814 }
1815 1815 """
1816 1816
1817 1817 # Restrict access to this api method to admins only.
1818 1818 if not has_superadmin_permission(apiuser):
1819 1819 raise JSONRPCForbidden()
1820 1820
1821 1821 try:
1822 1822 repo = get_repo_or_error(repoid)
1823 1823 settings_model = VcsSettingsModel(repo=repo)
1824 1824 settings = settings_model.get_global_settings()
1825 1825 settings.update(settings_model.get_repo_settings())
1826 1826
1827 1827 # If only a single setting is requested fetch it from all settings.
1828 1828 key = Optional.extract(key)
1829 1829 if key is not None:
1830 1830 settings = settings.get(key, None)
1831 1831 except Exception:
1832 1832 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1833 1833 log.exception(msg)
1834 1834 raise JSONRPCError(msg)
1835 1835
1836 1836 return settings
1837 1837
1838 1838
1839 1839 @jsonrpc_method()
1840 1840 def set_repo_settings(request, apiuser, repoid, settings):
1841 1841 """
1842 1842 Update repository settings. Returns true on success.
1843 1843
1844 1844 :param apiuser: This is filled automatically from the |authtoken|.
1845 1845 :type apiuser: AuthUser
1846 1846 :param repoid: The repository name or repository id.
1847 1847 :type repoid: str or int
1848 1848 :param settings: The new settings for the repository.
1849 1849 :type: settings: dict
1850 1850
1851 1851 Example output:
1852 1852
1853 1853 .. code-block:: bash
1854 1854
1855 1855 {
1856 1856 "error": null,
1857 1857 "id": 237,
1858 1858 "result": true
1859 1859 }
1860 1860 """
1861 1861 # Restrict access to this api method to admins only.
1862 1862 if not has_superadmin_permission(apiuser):
1863 1863 raise JSONRPCForbidden()
1864 1864
1865 1865 if type(settings) is not dict:
1866 1866 raise JSONRPCError('Settings have to be a JSON Object.')
1867 1867
1868 1868 try:
1869 1869 settings_model = VcsSettingsModel(repo=repoid)
1870 1870
1871 1871 # Merge global, repo and incoming settings.
1872 1872 new_settings = settings_model.get_global_settings()
1873 1873 new_settings.update(settings_model.get_repo_settings())
1874 1874 new_settings.update(settings)
1875 1875
1876 1876 # Update the settings.
1877 1877 inherit_global_settings = new_settings.get(
1878 1878 'inherit_global_settings', False)
1879 1879 settings_model.create_or_update_repo_settings(
1880 1880 new_settings, inherit_global_settings=inherit_global_settings)
1881 1881 Session().commit()
1882 1882 except Exception:
1883 1883 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1884 1884 log.exception(msg)
1885 1885 raise JSONRPCError(msg)
1886 1886
1887 1887 # Indicate success.
1888 1888 return True
@@ -1,699 +1,699 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import logging
23 23
24 24 import colander
25 25
26 26 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
27 27 from rhodecode.api.utils import (
28 28 has_superadmin_permission, Optional, OAttr, get_user_or_error,
29 29 store_update, get_repo_group_or_error,
30 30 get_perm_or_error, get_user_group_or_error, get_origin)
31 31 from rhodecode.lib.auth import (
32 32 HasPermissionAnyApi, HasRepoGroupPermissionAnyApi,
33 33 HasUserGroupPermissionAnyApi)
34 34 from rhodecode.model.db import Session, RepoGroup
35 35 from rhodecode.model.repo_group import RepoGroupModel
36 36 from rhodecode.model.scm import RepoGroupList
37 37 from rhodecode.model.validation_schema.schemas import repo_group_schema
38 38
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42
43 43 @jsonrpc_method()
44 44 def get_repo_group(request, apiuser, repogroupid):
45 45 """
46 46 Return the specified |repo| group, along with permissions,
47 47 and repositories inside the group
48 48
49 49 :param apiuser: This is filled automatically from the |authtoken|.
50 50 :type apiuser: AuthUser
51 51 :param repogroupid: Specify the name of ID of the repository group.
52 52 :type repogroupid: str or int
53 53
54 54
55 55 Example output:
56 56
57 57 .. code-block:: bash
58 58
59 59 {
60 60 "error": null,
61 61 "id": repo-group-id,
62 62 "result": {
63 63 "group_description": "repo group description",
64 64 "group_id": 14,
65 65 "group_name": "group name",
66 66 "members": [
67 67 {
68 68 "name": "super-admin-username",
69 69 "origin": "super-admin",
70 70 "permission": "group.admin",
71 71 "type": "user"
72 72 },
73 73 {
74 74 "name": "owner-name",
75 75 "origin": "owner",
76 76 "permission": "group.admin",
77 77 "type": "user"
78 78 },
79 79 {
80 80 "name": "user-group-name",
81 81 "origin": "permission",
82 82 "permission": "group.write",
83 83 "type": "user_group"
84 84 }
85 85 ],
86 86 "owner": "owner-name",
87 87 "parent_group": null,
88 88 "repositories": [ repo-list ]
89 89 }
90 90 }
91 91 """
92 92
93 93 repo_group = get_repo_group_or_error(repogroupid)
94 94 if not has_superadmin_permission(apiuser):
95 95 # check if we have at least read permission for this repo group !
96 96 _perms = ('group.admin', 'group.write', 'group.read',)
97 97 if not HasRepoGroupPermissionAnyApi(*_perms)(
98 98 user=apiuser, group_name=repo_group.group_name):
99 99 raise JSONRPCError(
100 100 'repository group `%s` does not exist' % (repogroupid,))
101 101
102 102 permissions = []
103 103 for _user in repo_group.permissions():
104 104 user_data = {
105 105 'name': _user.username,
106 106 'permission': _user.permission,
107 107 'origin': get_origin(_user),
108 108 'type': "user",
109 109 }
110 110 permissions.append(user_data)
111 111
112 112 for _user_group in repo_group.permission_user_groups():
113 113 user_group_data = {
114 114 'name': _user_group.users_group_name,
115 115 'permission': _user_group.permission,
116 116 'origin': get_origin(_user_group),
117 117 'type': "user_group",
118 118 }
119 119 permissions.append(user_group_data)
120 120
121 121 data = repo_group.get_api_data()
122 122 data["members"] = permissions # TODO: this should be named permissions
123 123 return data
124 124
125 125
126 126 @jsonrpc_method()
127 127 def get_repo_groups(request, apiuser):
128 128 """
129 129 Returns all repository groups.
130 130
131 131 :param apiuser: This is filled automatically from the |authtoken|.
132 132 :type apiuser: AuthUser
133 133 """
134 134
135 135 result = []
136 136 _perms = ('group.read', 'group.write', 'group.admin',)
137 137 extras = {'user': apiuser}
138 138 for repo_group in RepoGroupList(RepoGroupModel().get_all(),
139 139 perm_set=_perms, extra_kwargs=extras):
140 140 result.append(repo_group.get_api_data())
141 141 return result
142 142
143 143
144 144 @jsonrpc_method()
145 145 def create_repo_group(request, apiuser, group_name, description=Optional(''),
146 146 owner=Optional(OAttr('apiuser')),
147 147 copy_permissions=Optional(False)):
148 148 """
149 149 Creates a repository group.
150 150
151 151 * If the repository group name contains "/", all the required repository
152 152 groups will be created.
153 153
154 154 For example "foo/bar/baz" will create |repo| groups "foo" and "bar"
155 155 (with "foo" as parent). It will also create the "baz" repository
156 156 with "bar" as |repo| group.
157 157
158 158 This command can only be run using an |authtoken| with admin
159 159 permissions.
160 160
161 161 :param apiuser: This is filled automatically from the |authtoken|.
162 162 :type apiuser: AuthUser
163 163 :param group_name: Set the repository group name.
164 164 :type group_name: str
165 165 :param description: Set the |repo| group description.
166 166 :type description: str
167 167 :param owner: Set the |repo| group owner.
168 168 :type owner: str
169 169 :param copy_permissions:
170 170 :type copy_permissions:
171 171
172 172 Example output:
173 173
174 174 .. code-block:: bash
175 175
176 176 id : <id_given_in_input>
177 177 result : {
178 178 "msg": "Created new repo group `<repo_group_name>`"
179 179 "repo_group": <repogroup_object>
180 180 }
181 181 error : null
182 182
183 183
184 184 Example error output:
185 185
186 186 .. code-block:: bash
187 187
188 188 id : <id_given_in_input>
189 189 result : null
190 190 error : {
191 191 failed to create repo group `<repogroupid>`
192 192 }
193 193
194 194 """
195 195
196 196 schema = repo_group_schema.RepoGroupSchema()
197 197 try:
198 198 data = schema.deserialize({
199 199 'group_name': group_name
200 200 })
201 201 except colander.Invalid as e:
202 202 raise JSONRPCError("Validation failed: %s" % (e.asdict(),))
203 203 group_name = data['group_name']
204 204
205 205 if isinstance(owner, Optional):
206 206 owner = apiuser.user_id
207 207
208 208 group_description = Optional.extract(description)
209 209 copy_permissions = Optional.extract(copy_permissions)
210 210
211 211 # get by full name with parents, check if it already exist
212 212 if RepoGroup.get_by_group_name(group_name):
213 213 raise JSONRPCError("repo group `%s` already exist" % (group_name,))
214 214
215 215 (group_name_cleaned,
216 216 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(
217 217 group_name)
218 218
219 219 parent_group = None
220 220 if parent_group_name:
221 221 parent_group = get_repo_group_or_error(parent_group_name)
222 222
223 223 if not HasPermissionAnyApi(
224 224 'hg.admin', 'hg.repogroup.create.true')(user=apiuser):
225 225 # check if we have admin permission for this parent repo group !
226 226 # users without admin or hg.repogroup.create can only create other
227 227 # groups in groups they own so this is a required, but can be empty
228 228 parent_group = getattr(parent_group, 'group_name', '')
229 229 _perms = ('group.admin',)
230 230 if not HasRepoGroupPermissionAnyApi(*_perms)(
231 231 user=apiuser, group_name=parent_group):
232 232 raise JSONRPCForbidden()
233 233
234 234 try:
235 235 repo_group = RepoGroupModel().create(
236 236 group_name=group_name,
237 237 group_description=group_description,
238 238 owner=owner,
239 239 copy_permissions=copy_permissions)
240 240 Session().commit()
241 241 return {
242 242 'msg': 'Created new repo group `%s`' % group_name,
243 243 'repo_group': repo_group.get_api_data()
244 244 }
245 245 except Exception:
246 246 log.exception("Exception occurred while trying create repo group")
247 247 raise JSONRPCError(
248 248 'failed to create repo group `%s`' % (group_name,))
249 249
250 250
251 251 @jsonrpc_method()
252 252 def update_repo_group(
253 253 request, apiuser, repogroupid, group_name=Optional(''),
254 254 description=Optional(''), owner=Optional(OAttr('apiuser')),
255 255 parent=Optional(None), enable_locking=Optional(False)):
256 256 """
257 257 Updates repository group with the details given.
258 258
259 259 This command can only be run using an |authtoken| with admin
260 260 permissions.
261 261
262 262 :param apiuser: This is filled automatically from the |authtoken|.
263 263 :type apiuser: AuthUser
264 264 :param repogroupid: Set the ID of repository group.
265 265 :type repogroupid: str or int
266 266 :param group_name: Set the name of the |repo| group.
267 267 :type group_name: str
268 268 :param description: Set a description for the group.
269 269 :type description: str
270 270 :param owner: Set the |repo| group owner.
271 271 :type owner: str
272 272 :param parent: Set the |repo| group parent.
273 273 :type parent: str or int
274 274 :param enable_locking: Enable |repo| locking. The default is false.
275 275 :type enable_locking: bool
276 276 """
277 277
278 278 repo_group = get_repo_group_or_error(repogroupid)
279 279 if not has_superadmin_permission(apiuser):
280 280 # check if we have admin permission for this repo group !
281 281 _perms = ('group.admin',)
282 282 if not HasRepoGroupPermissionAnyApi(*_perms)(
283 283 user=apiuser, group_name=repo_group.group_name):
284 284 raise JSONRPCError(
285 285 'repository group `%s` does not exist' % (repogroupid,))
286 286
287 287 updates = {}
288 288 try:
289 289 store_update(updates, group_name, 'group_name')
290 290 store_update(updates, description, 'group_description')
291 291 store_update(updates, owner, 'user')
292 292 store_update(updates, parent, 'group_parent_id')
293 293 store_update(updates, enable_locking, 'enable_locking')
294 294 repo_group = RepoGroupModel().update(repo_group, updates)
295 295 Session().commit()
296 296 return {
297 297 'msg': 'updated repository group ID:%s %s' % (
298 298 repo_group.group_id, repo_group.group_name),
299 299 'repo_group': repo_group.get_api_data()
300 300 }
301 301 except Exception:
302 302 log.exception("Exception occurred while trying update repo group")
303 303 raise JSONRPCError('failed to update repository group `%s`'
304 304 % (repogroupid,))
305 305
306 306
307 307 @jsonrpc_method()
308 308 def delete_repo_group(request, apiuser, repogroupid):
309 309 """
310 310 Deletes a |repo| group.
311 311
312 312 :param apiuser: This is filled automatically from the |authtoken|.
313 313 :type apiuser: AuthUser
314 314 :param repogroupid: Set the name or ID of repository group to be
315 315 deleted.
316 316 :type repogroupid: str or int
317 317
318 318 Example output:
319 319
320 320 .. code-block:: bash
321 321
322 322 id : <id_given_in_input>
323 323 result : {
324 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>
324 'msg': 'deleted repo group ID:<repogroupid> <repogroupname>'
325 325 'repo_group': null
326 326 }
327 327 error : null
328 328
329 329 Example error output:
330 330
331 331 .. code-block:: bash
332 332
333 333 id : <id_given_in_input>
334 334 result : null
335 335 error : {
336 336 "failed to delete repo group ID:<repogroupid> <repogroupname>"
337 337 }
338 338
339 339 """
340 340
341 341 repo_group = get_repo_group_or_error(repogroupid)
342 342 if not has_superadmin_permission(apiuser):
343 343 # check if we have admin permission for this repo group !
344 344 _perms = ('group.admin',)
345 345 if not HasRepoGroupPermissionAnyApi(*_perms)(
346 346 user=apiuser, group_name=repo_group.group_name):
347 347 raise JSONRPCError(
348 348 'repository group `%s` does not exist' % (repogroupid,))
349 349 try:
350 350 RepoGroupModel().delete(repo_group)
351 351 Session().commit()
352 352 return {
353 353 'msg': 'deleted repo group ID:%s %s' %
354 354 (repo_group.group_id, repo_group.group_name),
355 355 'repo_group': None
356 356 }
357 357 except Exception:
358 358 log.exception("Exception occurred while trying to delete repo group")
359 359 raise JSONRPCError('failed to delete repo group ID:%s %s' %
360 360 (repo_group.group_id, repo_group.group_name))
361 361
362 362
363 363 @jsonrpc_method()
364 364 def grant_user_permission_to_repo_group(
365 365 request, apiuser, repogroupid, userid, perm,
366 366 apply_to_children=Optional('none')):
367 367 """
368 368 Grant permission for a user on the given repository group, or update
369 369 existing permissions if found.
370 370
371 371 This command can only be run using an |authtoken| with admin
372 372 permissions.
373 373
374 374 :param apiuser: This is filled automatically from the |authtoken|.
375 375 :type apiuser: AuthUser
376 376 :param repogroupid: Set the name or ID of repository group.
377 377 :type repogroupid: str or int
378 378 :param userid: Set the user name.
379 379 :type userid: str
380 380 :param perm: (group.(none|read|write|admin))
381 381 :type perm: str
382 382 :param apply_to_children: 'none', 'repos', 'groups', 'all'
383 383 :type apply_to_children: str
384 384
385 385 Example output:
386 386
387 387 .. code-block:: bash
388 388
389 389 id : <id_given_in_input>
390 390 result: {
391 391 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
392 392 "success": true
393 393 }
394 394 error: null
395 395
396 396 Example error output:
397 397
398 398 .. code-block:: bash
399 399
400 400 id : <id_given_in_input>
401 401 result : null
402 402 error : {
403 403 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
404 404 }
405 405
406 406 """
407 407
408 408 repo_group = get_repo_group_or_error(repogroupid)
409 409
410 410 if not has_superadmin_permission(apiuser):
411 411 # check if we have admin permission for this repo group !
412 412 _perms = ('group.admin',)
413 413 if not HasRepoGroupPermissionAnyApi(*_perms)(
414 414 user=apiuser, group_name=repo_group.group_name):
415 415 raise JSONRPCError(
416 416 'repository group `%s` does not exist' % (repogroupid,))
417 417
418 418 user = get_user_or_error(userid)
419 419 perm = get_perm_or_error(perm, prefix='group.')
420 420 apply_to_children = Optional.extract(apply_to_children)
421 421
422 422 perm_additions = [[user.user_id, perm, "user"]]
423 423 try:
424 424 RepoGroupModel().update_permissions(repo_group=repo_group,
425 425 perm_additions=perm_additions,
426 426 recursive=apply_to_children,
427 427 cur_user=apiuser)
428 428 Session().commit()
429 429 return {
430 430 'msg': 'Granted perm: `%s` (recursive:%s) for user: '
431 431 '`%s` in repo group: `%s`' % (
432 432 perm.permission_name, apply_to_children, user.username,
433 433 repo_group.name
434 434 ),
435 435 'success': True
436 436 }
437 437 except Exception:
438 438 log.exception("Exception occurred while trying to grant "
439 439 "user permissions to repo group")
440 440 raise JSONRPCError(
441 441 'failed to edit permission for user: '
442 442 '`%s` in repo group: `%s`' % (userid, repo_group.name))
443 443
444 444
445 445 @jsonrpc_method()
446 446 def revoke_user_permission_from_repo_group(
447 447 request, apiuser, repogroupid, userid,
448 448 apply_to_children=Optional('none')):
449 449 """
450 450 Revoke permission for a user in a given repository group.
451 451
452 452 This command can only be run using an |authtoken| with admin
453 453 permissions on the |repo| group.
454 454
455 455 :param apiuser: This is filled automatically from the |authtoken|.
456 456 :type apiuser: AuthUser
457 457 :param repogroupid: Set the name or ID of the repository group.
458 458 :type repogroupid: str or int
459 459 :param userid: Set the user name to revoke.
460 460 :type userid: str
461 461 :param apply_to_children: 'none', 'repos', 'groups', 'all'
462 462 :type apply_to_children: str
463 463
464 464 Example output:
465 465
466 466 .. code-block:: bash
467 467
468 468 id : <id_given_in_input>
469 469 result: {
470 470 "msg" : "Revoked perm (recursive:<apply_to_children>) for user: `<username>` in repo group: `<repo_group_name>`",
471 471 "success": true
472 472 }
473 473 error: null
474 474
475 475 Example error output:
476 476
477 477 .. code-block:: bash
478 478
479 479 id : <id_given_in_input>
480 480 result : null
481 481 error : {
482 482 "failed to edit permission for user: `<userid>` in repo group: `<repo_group_name>`"
483 483 }
484 484
485 485 """
486 486
487 487 repo_group = get_repo_group_or_error(repogroupid)
488 488
489 489 if not has_superadmin_permission(apiuser):
490 490 # check if we have admin permission for this repo group !
491 491 _perms = ('group.admin',)
492 492 if not HasRepoGroupPermissionAnyApi(*_perms)(
493 493 user=apiuser, group_name=repo_group.group_name):
494 494 raise JSONRPCError(
495 495 'repository group `%s` does not exist' % (repogroupid,))
496 496
497 497 user = get_user_or_error(userid)
498 498 apply_to_children = Optional.extract(apply_to_children)
499 499
500 500 perm_deletions = [[user.user_id, None, "user"]]
501 501 try:
502 502 RepoGroupModel().update_permissions(repo_group=repo_group,
503 503 perm_deletions=perm_deletions,
504 504 recursive=apply_to_children,
505 505 cur_user=apiuser)
506 506 Session().commit()
507 507 return {
508 508 'msg': 'Revoked perm (recursive:%s) for user: '
509 509 '`%s` in repo group: `%s`' % (
510 510 apply_to_children, user.username, repo_group.name
511 511 ),
512 512 'success': True
513 513 }
514 514 except Exception:
515 515 log.exception("Exception occurred while trying revoke user "
516 516 "permission from repo group")
517 517 raise JSONRPCError(
518 518 'failed to edit permission for user: '
519 519 '`%s` in repo group: `%s`' % (userid, repo_group.name))
520 520
521 521
522 522 @jsonrpc_method()
523 523 def grant_user_group_permission_to_repo_group(
524 524 request, apiuser, repogroupid, usergroupid, perm,
525 525 apply_to_children=Optional('none'), ):
526 526 """
527 527 Grant permission for a user group on given repository group, or update
528 528 existing permissions if found.
529 529
530 530 This command can only be run using an |authtoken| with admin
531 531 permissions on the |repo| group.
532 532
533 533 :param apiuser: This is filled automatically from the |authtoken|.
534 534 :type apiuser: AuthUser
535 535 :param repogroupid: Set the name or id of repository group
536 536 :type repogroupid: str or int
537 537 :param usergroupid: id of usergroup
538 538 :type usergroupid: str or int
539 539 :param perm: (group.(none|read|write|admin))
540 540 :type perm: str
541 541 :param apply_to_children: 'none', 'repos', 'groups', 'all'
542 542 :type apply_to_children: str
543 543
544 544 Example output:
545 545
546 546 .. code-block:: bash
547 547
548 548 id : <id_given_in_input>
549 549 result : {
550 550 "msg" : "Granted perm: `<perm>` (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
551 551 "success": true
552 552
553 553 }
554 554 error : null
555 555
556 556 Example error output:
557 557
558 558 .. code-block:: bash
559 559
560 560 id : <id_given_in_input>
561 561 result : null
562 562 error : {
563 563 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
564 564 }
565 565
566 566 """
567 567
568 568 repo_group = get_repo_group_or_error(repogroupid)
569 569 perm = get_perm_or_error(perm, prefix='group.')
570 570 user_group = get_user_group_or_error(usergroupid)
571 571 if not has_superadmin_permission(apiuser):
572 572 # check if we have admin permission for this repo group !
573 573 _perms = ('group.admin',)
574 574 if not HasRepoGroupPermissionAnyApi(*_perms)(
575 575 user=apiuser, group_name=repo_group.group_name):
576 576 raise JSONRPCError(
577 577 'repository group `%s` does not exist' % (repogroupid,))
578 578
579 579 # check if we have at least read permission for this user group !
580 580 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
581 581 if not HasUserGroupPermissionAnyApi(*_perms)(
582 582 user=apiuser, user_group_name=user_group.users_group_name):
583 583 raise JSONRPCError(
584 584 'user group `%s` does not exist' % (usergroupid,))
585 585
586 586 apply_to_children = Optional.extract(apply_to_children)
587 587
588 588 perm_additions = [[user_group.users_group_id, perm, "user_group"]]
589 589 try:
590 590 RepoGroupModel().update_permissions(repo_group=repo_group,
591 591 perm_additions=perm_additions,
592 592 recursive=apply_to_children,
593 593 cur_user=apiuser)
594 594 Session().commit()
595 595 return {
596 596 'msg': 'Granted perm: `%s` (recursive:%s) '
597 597 'for user group: `%s` in repo group: `%s`' % (
598 598 perm.permission_name, apply_to_children,
599 599 user_group.users_group_name, repo_group.name
600 600 ),
601 601 'success': True
602 602 }
603 603 except Exception:
604 604 log.exception("Exception occurred while trying to grant user "
605 605 "group permissions to repo group")
606 606 raise JSONRPCError(
607 607 'failed to edit permission for user group: `%s` in '
608 608 'repo group: `%s`' % (
609 609 usergroupid, repo_group.name
610 610 )
611 611 )
612 612
613 613
614 614 @jsonrpc_method()
615 615 def revoke_user_group_permission_from_repo_group(
616 616 request, apiuser, repogroupid, usergroupid,
617 617 apply_to_children=Optional('none')):
618 618 """
619 619 Revoke permission for user group on given repository.
620 620
621 621 This command can only be run using an |authtoken| with admin
622 622 permissions on the |repo| group.
623 623
624 624 :param apiuser: This is filled automatically from the |authtoken|.
625 625 :type apiuser: AuthUser
626 626 :param repogroupid: name or id of repository group
627 627 :type repogroupid: str or int
628 628 :param usergroupid:
629 629 :param apply_to_children: 'none', 'repos', 'groups', 'all'
630 630 :type apply_to_children: str
631 631
632 632 Example output:
633 633
634 634 .. code-block:: bash
635 635
636 636 id : <id_given_in_input>
637 637 result: {
638 638 "msg" : "Revoked perm (recursive:<apply_to_children>) for user group: `<usersgroupname>` in repo group: `<repo_group_name>`",
639 639 "success": true
640 640 }
641 641 error: null
642 642
643 643 Example error output:
644 644
645 645 .. code-block:: bash
646 646
647 647 id : <id_given_in_input>
648 648 result : null
649 649 error : {
650 650 "failed to edit permission for user group: `<usergroup>` in repo group: `<repo_group_name>`"
651 651 }
652 652
653 653
654 654 """
655 655
656 656 repo_group = get_repo_group_or_error(repogroupid)
657 657 user_group = get_user_group_or_error(usergroupid)
658 658 if not has_superadmin_permission(apiuser):
659 659 # check if we have admin permission for this repo group !
660 660 _perms = ('group.admin',)
661 661 if not HasRepoGroupPermissionAnyApi(*_perms)(
662 662 user=apiuser, group_name=repo_group.group_name):
663 663 raise JSONRPCError(
664 664 'repository group `%s` does not exist' % (repogroupid,))
665 665
666 666 # check if we have at least read permission for this user group !
667 667 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
668 668 if not HasUserGroupPermissionAnyApi(*_perms)(
669 669 user=apiuser, user_group_name=user_group.users_group_name):
670 670 raise JSONRPCError(
671 671 'user group `%s` does not exist' % (usergroupid,))
672 672
673 673 apply_to_children = Optional.extract(apply_to_children)
674 674
675 675 perm_deletions = [[user_group.users_group_id, None, "user_group"]]
676 676 try:
677 677 RepoGroupModel().update_permissions(repo_group=repo_group,
678 678 perm_deletions=perm_deletions,
679 679 recursive=apply_to_children,
680 680 cur_user=apiuser)
681 681 Session().commit()
682 682 return {
683 683 'msg': 'Revoked perm (recursive:%s) for user group: '
684 684 '`%s` in repo group: `%s`' % (
685 685 apply_to_children, user_group.users_group_name,
686 686 repo_group.name
687 687 ),
688 688 'success': True
689 689 }
690 690 except Exception:
691 691 log.exception("Exception occurred while trying revoke user group "
692 692 "permissions from repo group")
693 693 raise JSONRPCError(
694 694 'failed to edit permission for user group: '
695 695 '`%s` in repo group: `%s`' % (
696 696 user_group.users_group_name, repo_group.name
697 697 )
698 698 )
699 699
General Comments 0
You need to be logged in to leave comments. Login now