/ github / Branch.py
Branch.py
  1  ############################ Copyrights and license ############################
  2  #                                                                              #
  3  # Copyright 2012 Vincent Jacques <vincent@vincent-jacques.net>                 #
  4  # Copyright 2012 Zearin <zearin@gonk.net>                                      #
  5  # Copyright 2013 AKFish <akfish@gmail.com>                                     #
  6  # Copyright 2013 Vincent Jacques <vincent@vincent-jacques.net>                 #
  7  # Copyright 2013 martinqt <m.ki2@laposte.net>                                  #
  8  # Copyright 2014 Vincent Jacques <vincent@vincent-jacques.net>                 #
  9  # Copyright 2015 Kyle Hornberg <khornberg@users.noreply.github.com>            #
 10  # Copyright 2016 Jannis Gebauer <ja.geb@me.com>                                #
 11  # Copyright 2016 Peter Buckley <dx-pbuckley@users.noreply.github.com>          #
 12  # Copyright 2018 Steve Kowalik <steven@wedontsleep.org>                        #
 13  # Copyright 2018 Wan Liuyang <tsfdye@gmail.com>                                #
 14  # Copyright 2018 sfdye <tsfdye@gmail.com>                                      #
 15  #                                                                              #
 16  # This file is part of PyGithub.                                               #
 17  # http://pygithub.readthedocs.io/                                              #
 18  #                                                                              #
 19  # PyGithub is free software: you can redistribute it and/or modify it under    #
 20  # the terms of the GNU Lesser General Public License as published by the Free  #
 21  # Software Foundation, either version 3 of the License, or (at your option)    #
 22  # any later version.                                                           #
 23  #                                                                              #
 24  # PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY  #
 25  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    #
 26  # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
 27  # details.                                                                     #
 28  #                                                                              #
 29  # You should have received a copy of the GNU Lesser General Public License     #
 30  # along with PyGithub. If not, see <http://www.gnu.org/licenses/>.             #
 31  #                                                                              #
 32  ################################################################################
 33  from __future__ import annotations
 34  
 35  from typing import TYPE_CHECKING, Any
 36  
 37  import github.BranchProtection
 38  import github.Commit
 39  import github.RequiredPullRequestReviews
 40  import github.RequiredStatusChecks
 41  from github import Consts
 42  from github.GithubObject import (
 43      Attribute,
 44      NonCompletableGithubObject,
 45      NotSet,
 46      Opt,
 47      is_defined,
 48      is_optional,
 49      is_optional_list,
 50      is_undefined,
 51  )
 52  
 53  if TYPE_CHECKING:
 54      from github.BranchProtection import BranchProtection
 55      from github.Commit import Commit
 56      from github.NamedUser import NamedUser
 57      from github.PaginatedList import PaginatedList
 58      from github.RequiredPullRequestReviews import RequiredPullRequestReviews
 59      from github.RequiredStatusChecks import RequiredStatusChecks
 60      from github.Team import Team
 61  
 62  
 63  class Branch(NonCompletableGithubObject):
 64      """
 65      This class represents Branches. The reference can be found here https://docs.github.com/en/rest/reference/repos#branches
 66      """
 67  
 68      def __repr__(self) -> str:
 69          return self.get__repr__({"name": self._name.value})
 70  
 71      @property
 72      def commit(self) -> Commit:
 73          return self._commit.value
 74  
 75      @property
 76      def name(self) -> str:
 77          return self._name.value
 78  
 79      @property
 80      def protected(self) -> bool:
 81          return self._protected.value
 82  
 83      @property
 84      def protection_url(self) -> str:
 85          return self._protection_url.value
 86  
 87      def _initAttributes(self) -> None:
 88          self._commit: Attribute[Commit] = github.GithubObject.NotSet
 89          self._name: Attribute[str] = github.GithubObject.NotSet
 90          self._protection_url: Attribute[str] = github.GithubObject.NotSet
 91          self._protected: Attribute[bool] = github.GithubObject.NotSet
 92  
 93      def _useAttributes(self, attributes: dict[str, Any]) -> None:
 94          if "commit" in attributes:  # pragma no branch
 95              self._commit = self._makeClassAttribute(github.Commit.Commit, attributes["commit"])
 96          if "name" in attributes:  # pragma no branch
 97              self._name = self._makeStringAttribute(attributes["name"])
 98          if "protection_url" in attributes:  # pragma no branch
 99              self._protection_url = self._makeStringAttribute(attributes["protection_url"])
100          if "protected" in attributes:  # pragma no branch
101              self._protected = self._makeBoolAttribute(attributes["protected"])
102  
103      def get_protection(self) -> BranchProtection:
104          """
105          :calls: `GET /repos/{owner}/{repo}/branches/{branch}/protection <https://docs.github.com/en/rest/reference/repos#branches>`_
106          """
107          headers, data = self._requester.requestJsonAndCheck(
108              "GET",
109              self.protection_url,
110              headers={"Accept": Consts.mediaTypeRequireMultipleApprovingReviews},
111          )
112          return github.BranchProtection.BranchProtection(self._requester, headers, data, completed=True)
113  
114      def edit_protection(
115          self,
116          strict: Opt[bool] = NotSet,
117          contexts: Opt[list[str]] = NotSet,
118          enforce_admins: Opt[bool] = NotSet,
119          dismissal_users: Opt[list[str]] = NotSet,
120          dismissal_teams: Opt[list[str]] = NotSet,
121          dismissal_apps: Opt[list[str]] = NotSet,
122          dismiss_stale_reviews: Opt[bool] = NotSet,
123          require_code_owner_reviews: Opt[bool] = NotSet,
124          required_approving_review_count: Opt[int] = NotSet,
125          user_push_restrictions: Opt[list[str]] = NotSet,
126          team_push_restrictions: Opt[list[str]] = NotSet,
127          app_push_restrictions: Opt[list[str]] = NotSet,
128          required_linear_history: Opt[bool] = NotSet,
129          allow_force_pushes: Opt[bool] = NotSet,
130          required_conversation_resolution: Opt[bool] = NotSet,
131          lock_branch: Opt[bool] = NotSet,
132          allow_fork_syncing: Opt[bool] = NotSet,
133          users_bypass_pull_request_allowances: Opt[list[str]] = NotSet,
134          teams_bypass_pull_request_allowances: Opt[list[str]] = NotSet,
135          apps_bypass_pull_request_allowances: Opt[list[str]] = NotSet,
136          block_creations: Opt[bool] = NotSet,
137      ) -> BranchProtection:
138          """
139          :calls: `PUT /repos/{owner}/{repo}/branches/{branch}/protection <https://docs.github.com/en/rest/reference/repos#get-branch-protection>`_
140  
141          NOTE: The GitHub API groups strict and contexts together, both must
142          be submitted. Take care to pass both as arguments even if only one is
143          changing. Use edit_required_status_checks() to avoid this.
144          """
145          assert is_optional(strict, bool), strict
146          assert is_optional_list(contexts, str), contexts
147          assert is_optional(enforce_admins, bool), enforce_admins
148          assert is_optional_list(dismissal_users, str), dismissal_users
149          assert is_optional_list(dismissal_teams, str), dismissal_teams
150          assert is_optional_list(dismissal_apps, str), dismissal_apps
151          assert is_optional(dismiss_stale_reviews, bool), dismiss_stale_reviews
152          assert is_optional(require_code_owner_reviews, bool), require_code_owner_reviews
153          assert is_optional(required_approving_review_count, int), required_approving_review_count
154          assert is_optional(required_linear_history, bool), required_linear_history
155          assert is_optional(allow_force_pushes, bool), allow_force_pushes
156          assert is_optional(required_conversation_resolution, bool), required_conversation_resolution
157          assert is_optional(lock_branch, bool), lock_branch
158          assert is_optional(allow_fork_syncing, bool), allow_fork_syncing
159          assert is_optional_list(users_bypass_pull_request_allowances, str), users_bypass_pull_request_allowances
160          assert is_optional_list(teams_bypass_pull_request_allowances, str), teams_bypass_pull_request_allowances
161          assert is_optional_list(apps_bypass_pull_request_allowances, str), apps_bypass_pull_request_allowances
162  
163          post_parameters: dict[str, Any] = {}
164          if is_defined(strict) or is_defined(contexts):
165              if is_undefined(strict):
166                  strict = False
167              if is_undefined(contexts):
168                  contexts = []
169              post_parameters["required_status_checks"] = {
170                  "strict": strict,
171                  "contexts": contexts,
172              }
173          else:
174              post_parameters["required_status_checks"] = None
175  
176          if is_defined(enforce_admins):
177              post_parameters["enforce_admins"] = enforce_admins
178          else:
179              post_parameters["enforce_admins"] = None
180  
181          if (
182              is_defined(dismissal_users)
183              or is_defined(dismissal_teams)
184              or is_defined(dismissal_apps)
185              or is_defined(dismiss_stale_reviews)
186              or is_defined(require_code_owner_reviews)
187              or is_defined(required_approving_review_count)
188              or is_defined(users_bypass_pull_request_allowances)
189              or is_defined(teams_bypass_pull_request_allowances)
190              or is_defined(apps_bypass_pull_request_allowances)
191          ):
192              post_parameters["required_pull_request_reviews"] = {}
193              if is_defined(dismiss_stale_reviews):
194                  post_parameters["required_pull_request_reviews"]["dismiss_stale_reviews"] = dismiss_stale_reviews
195              if is_defined(require_code_owner_reviews):
196                  post_parameters["required_pull_request_reviews"][
197                      "require_code_owner_reviews"
198                  ] = require_code_owner_reviews
199              if is_defined(required_approving_review_count):
200                  post_parameters["required_pull_request_reviews"][
201                      "required_approving_review_count"
202                  ] = required_approving_review_count
203  
204              dismissal_restrictions = {}
205              if is_defined(dismissal_users):
206                  dismissal_restrictions["users"] = dismissal_users
207              if is_defined(dismissal_teams):
208                  dismissal_restrictions["teams"] = dismissal_teams
209              if is_defined(dismissal_apps):
210                  dismissal_restrictions["apps"] = dismissal_apps
211  
212              if dismissal_restrictions:
213                  post_parameters["required_pull_request_reviews"]["dismissal_restrictions"] = dismissal_restrictions
214  
215              bypass_pull_request_allowances = {}
216              if is_defined(users_bypass_pull_request_allowances):
217                  bypass_pull_request_allowances["users"] = users_bypass_pull_request_allowances
218              if is_defined(teams_bypass_pull_request_allowances):
219                  bypass_pull_request_allowances["teams"] = teams_bypass_pull_request_allowances
220              if is_defined(apps_bypass_pull_request_allowances):
221                  bypass_pull_request_allowances["apps"] = apps_bypass_pull_request_allowances
222  
223              if bypass_pull_request_allowances:
224                  post_parameters["required_pull_request_reviews"][
225                      "bypass_pull_request_allowances"
226                  ] = bypass_pull_request_allowances
227          else:
228              post_parameters["required_pull_request_reviews"] = None
229          if (
230              is_defined(user_push_restrictions)
231              or is_defined(team_push_restrictions)
232              or is_defined(app_push_restrictions)
233          ):
234              if is_undefined(user_push_restrictions):
235                  user_push_restrictions = []
236              if is_undefined(team_push_restrictions):
237                  team_push_restrictions = []
238              if is_undefined(app_push_restrictions):
239                  app_push_restrictions = []
240              post_parameters["restrictions"] = {
241                  "users": user_push_restrictions,
242                  "teams": team_push_restrictions,
243                  "apps": app_push_restrictions,
244              }
245          else:
246              post_parameters["restrictions"] = None
247          if is_defined(required_linear_history):
248              post_parameters["required_linear_history"] = required_linear_history
249          else:
250              post_parameters["required_linear_history"] = None
251          if is_defined(allow_force_pushes):
252              post_parameters["allow_force_pushes"] = allow_force_pushes
253          else:
254              post_parameters["allow_force_pushes"] = None
255          if is_defined(required_conversation_resolution):
256              post_parameters["required_conversation_resolution"] = required_conversation_resolution
257          else:
258              post_parameters["required_conversation_resolution"] = None
259          if is_defined(lock_branch):
260              post_parameters["lock_branch"] = lock_branch
261          else:
262              post_parameters["lock_branch"] = None
263          if is_defined(allow_fork_syncing):
264              post_parameters["allow_fork_syncing"] = allow_fork_syncing
265          else:
266              post_parameters["allow_fork_syncing"] = None
267          if is_defined(block_creations):
268              post_parameters["block_creations"] = block_creations
269          else:
270              post_parameters["block_creations"] = None
271  
272          headers, data = self._requester.requestJsonAndCheck(
273              "PUT",
274              self.protection_url,
275              headers={"Accept": Consts.mediaTypeRequireMultipleApprovingReviews},
276              input=post_parameters,
277          )
278  
279          return github.BranchProtection.BranchProtection(self._requester, headers, data, completed=True)
280  
281      def remove_protection(self) -> None:
282          """
283          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection <https://docs.github.com/en/rest/reference/repos#branches>`_
284          """
285          headers, data = self._requester.requestJsonAndCheck(
286              "DELETE",
287              self.protection_url,
288          )
289  
290      def get_required_status_checks(self) -> RequiredStatusChecks:
291          """
292          :calls: `GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks <https://docs.github.com/en/rest/reference/repos#branches>`_
293          :rtype: :class:`github.RequiredStatusChecks.RequiredStatusChecks`
294          """
295          headers, data = self._requester.requestJsonAndCheck("GET", f"{self.protection_url}/required_status_checks")
296          return github.RequiredStatusChecks.RequiredStatusChecks(self._requester, headers, data, completed=True)
297  
298      def edit_required_status_checks(
299          self,
300          strict: Opt[bool] = NotSet,
301          contexts: Opt[list[str]] = NotSet,
302      ) -> RequiredStatusChecks:
303          """
304          :calls: `PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks <https://docs.github.com/en/rest/reference/repos#branches>`_
305          """
306          assert is_optional(strict, bool), strict
307          assert is_optional_list(contexts, str), contexts
308  
309          post_parameters: dict[str, Any] = NotSet.remove_unset_items({"strict": strict, "contexts": contexts})
310          headers, data = self._requester.requestJsonAndCheck(
311              "PATCH",
312              f"{self.protection_url}/required_status_checks",
313              input=post_parameters,
314          )
315  
316          return github.RequiredStatusChecks.RequiredStatusChecks(self._requester, headers, data, completed=True)
317  
318      def remove_required_status_checks(self) -> None:
319          """
320          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks <https://docs.github.com/en/rest/reference/repos#branches>`_
321          """
322          headers, data = self._requester.requestJsonAndCheck(
323              "DELETE",
324              f"{self.protection_url}/required_status_checks",
325          )
326  
327      def get_required_pull_request_reviews(self) -> RequiredPullRequestReviews:
328          """
329          :calls: `GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews <https://docs.github.com/en/rest/reference/repos#branches>`_
330          """
331          headers, data = self._requester.requestJsonAndCheck(
332              "GET",
333              f"{self.protection_url}/required_pull_request_reviews",
334              headers={"Accept": Consts.mediaTypeRequireMultipleApprovingReviews},
335          )
336          return github.RequiredPullRequestReviews.RequiredPullRequestReviews(
337              self._requester, headers, data, completed=True
338          )
339  
340      def edit_required_pull_request_reviews(
341          self,
342          dismissal_users: Opt[list[str]] = NotSet,
343          dismissal_teams: Opt[list[str]] = NotSet,
344          dismissal_apps: Opt[list[str]] = NotSet,
345          dismiss_stale_reviews: Opt[bool] = NotSet,
346          require_code_owner_reviews: Opt[bool] = NotSet,
347          required_approving_review_count: Opt[int] = NotSet,
348      ) -> RequiredStatusChecks:
349          """
350          :calls: `PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews <https://docs.github.com/en/rest/reference/repos#branches>`_
351          """
352          assert is_optional_list(dismissal_users, str), dismissal_users
353          assert is_optional_list(dismissal_teams, str), dismissal_teams
354          assert is_optional(dismiss_stale_reviews, bool), dismiss_stale_reviews
355          assert is_optional(require_code_owner_reviews, bool), require_code_owner_reviews
356          assert is_optional(required_approving_review_count, int), required_approving_review_count
357  
358          post_parameters: dict[str, Any] = NotSet.remove_unset_items(
359              {
360                  "dismiss_stale_reviews": dismiss_stale_reviews,
361                  "require_code_owner_reviews": require_code_owner_reviews,
362                  "required_approving_review_count": required_approving_review_count,
363              }
364          )
365  
366          dismissal_restrictions: dict[str, Any] = NotSet.remove_unset_items(
367              {"users": dismissal_users, "teams": dismissal_teams, "apps": dismissal_apps}
368          )
369  
370          if dismissal_restrictions:
371              post_parameters["dismissal_restrictions"] = dismissal_restrictions
372  
373          headers, data = self._requester.requestJsonAndCheck(
374              "PATCH",
375              f"{self.protection_url}/required_pull_request_reviews",
376              headers={"Accept": Consts.mediaTypeRequireMultipleApprovingReviews},
377              input=post_parameters,
378          )
379  
380          return github.RequiredStatusChecks.RequiredStatusChecks(self._requester, headers, data, completed=True)
381  
382      def remove_required_pull_request_reviews(self) -> None:
383          """
384          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews <https://docs.github.com/en/rest/reference/repos#branches>`_
385          """
386          headers, data = self._requester.requestJsonAndCheck(
387              "DELETE",
388              f"{self.protection_url}/required_pull_request_reviews",
389          )
390  
391      def get_admin_enforcement(self) -> bool:
392          """
393          :calls: `GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins <https://docs.github.com/en/rest/reference/repos#branches>`_
394          """
395          headers, data = self._requester.requestJsonAndCheck("GET", f"{self.protection_url}/enforce_admins")
396          return data["enabled"]
397  
398      def set_admin_enforcement(self) -> None:
399          """
400          :calls: `POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins <https://docs.github.com/en/rest/reference/repos#branches>`_
401          """
402          headers, data = self._requester.requestJsonAndCheck("POST", f"{self.protection_url}/enforce_admins")
403  
404      def remove_admin_enforcement(self) -> None:
405          """
406          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins <https://docs.github.com/en/rest/reference/repos#branches>`_
407          """
408          headers, data = self._requester.requestJsonAndCheck("DELETE", f"{self.protection_url}/enforce_admins")
409  
410      def get_user_push_restrictions(self) -> PaginatedList[NamedUser]:
411          """
412          :calls: `GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users <https://docs.github.com/en/rest/reference/repos#branches>`_
413          """
414          return github.PaginatedList.PaginatedList(
415              github.NamedUser.NamedUser,
416              self._requester,
417              f"{self.protection_url}/restrictions/users",
418              None,
419          )
420  
421      def get_team_push_restrictions(self) -> PaginatedList[Team]:
422          """
423          :calls: `GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams <https://docs.github.com/en/rest/reference/repos#branches>`_
424          """
425          return github.PaginatedList.PaginatedList(
426              github.Team.Team,
427              self._requester,
428              f"{self.protection_url}/restrictions/teams",
429              None,
430          )
431  
432      def add_user_push_restrictions(self, *users: str) -> None:
433          """
434          :calls: `POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users <https://docs.github.com/en/rest/reference/repos#branches>`_
435          :users: list of strings (user names)
436          """
437          assert all(isinstance(element, str) for element in users), users
438  
439          headers, data = self._requester.requestJsonAndCheck(
440              "POST", f"{self.protection_url}/restrictions/users", input=users
441          )
442  
443      def replace_user_push_restrictions(self, *users: str) -> None:
444          """
445          :calls: `PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users <https://docs.github.com/en/rest/reference/repos#branches>`_
446          :users: list of strings (user names)
447          """
448          assert all(isinstance(element, str) for element in users), users
449  
450          headers, data = self._requester.requestJsonAndCheck(
451              "PUT", f"{self.protection_url}/restrictions/users", input=users
452          )
453  
454      def remove_user_push_restrictions(self, *users: str) -> None:
455          """
456          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users <https://docs.github.com/en/rest/reference/repos#branches>`_
457          :users: list of strings (user names)
458          """
459          assert all(isinstance(element, str) for element in users), users
460  
461          headers, data = self._requester.requestJsonAndCheck(
462              "DELETE", f"{self.protection_url}/restrictions/users", input=users
463          )
464  
465      def add_team_push_restrictions(self, *teams: str) -> None:
466          """
467          :calls: `POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams <https://docs.github.com/en/rest/reference/repos#branches>`_
468          :teams: list of strings (team slugs)
469          """
470          assert all(isinstance(element, str) for element in teams), teams
471  
472          headers, data = self._requester.requestJsonAndCheck(
473              "POST", f"{self.protection_url}/restrictions/teams", input=teams
474          )
475  
476      def replace_team_push_restrictions(self, *teams: str) -> None:
477          """
478          :calls: `PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams <https://docs.github.com/en/rest/reference/repos#branches>`_
479          :teams: list of strings (team slugs)
480          """
481          assert all(isinstance(element, str) for element in teams), teams
482  
483          headers, data = self._requester.requestJsonAndCheck(
484              "PUT", f"{self.protection_url}/restrictions/teams", input=teams
485          )
486  
487      def remove_team_push_restrictions(self, *teams: str) -> None:
488          """
489          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams <https://docs.github.com/en/rest/reference/repos#branches>`_
490          :teams: list of strings (team slugs)
491          """
492          assert all(isinstance(element, str) for element in teams), teams
493  
494          headers, data = self._requester.requestJsonAndCheck(
495              "DELETE", f"{self.protection_url}/restrictions/teams", input=teams
496          )
497  
498      def remove_push_restrictions(self) -> None:
499          """
500          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions <https://docs.github.com/en/rest/reference/repos#branches>`_
501          """
502          headers, data = self._requester.requestJsonAndCheck("DELETE", f"{self.protection_url}/restrictions")
503  
504      def get_required_signatures(self) -> bool:
505          """
506          :calls: `GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures <https://docs.github.com/en/rest/reference/repos#branches>`_
507          """
508          headers, data = self._requester.requestJsonAndCheck(
509              "GET",
510              f"{self.protection_url}/required_signatures",
511              headers={"Accept": Consts.signaturesProtectedBranchesPreview},
512          )
513          return data["enabled"]
514  
515      def add_required_signatures(self) -> None:
516          """
517          :calls: `POST /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures <https://docs.github.com/en/rest/reference/repos#branches>`_
518          """
519          headers, data = self._requester.requestJsonAndCheck(
520              "POST",
521              f"{self.protection_url}/required_signatures",
522              headers={"Accept": Consts.signaturesProtectedBranchesPreview},
523          )
524  
525      def remove_required_signatures(self) -> None:
526          """
527          :calls: `DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures <https://docs.github.com/en/rest/reference/repos#branches>`_
528          """
529          headers, data = self._requester.requestJsonAndCheck(
530              "DELETE",
531              f"{self.protection_url}/required_signatures",
532              headers={"Accept": Consts.signaturesProtectedBranchesPreview},
533          )