qontract-reconcile 0.10.1rc926__py3-none-any.whl → 0.10.1rc927__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc926
3
+ Version: 0.10.1rc927
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -20,7 +20,7 @@ Requires-Dist: PyGithub <1.59,>=1.58
20
20
  Requires-Dist: hvac <0.8.0,>=0.7.0
21
21
  Requires-Dist: ldap3 <2.10.0,>=2.9.1
22
22
  Requires-Dist: anymarkup <0.9.0,>=0.7.0
23
- Requires-Dist: python-gitlab <1.12.0,>=1.11.0
23
+ Requires-Dist: python-gitlab ~=4.6
24
24
  Requires-Dist: semver ~=3.0
25
25
  Requires-Dist: boto3 ==1.34.94
26
26
  Requires-Dist: botocore ==1.34.94
@@ -29,8 +29,8 @@ reconcile/github_repo_invites.py,sha256=3GOBGZq1DKMIuyYsDxHqXDnMTWlJfeu248-m3ajf
29
29
  reconcile/github_repo_permissions_validator.py,sha256=dcbXdUx6imjNchjp3pg9-z1i7lFEGOr_28GvsiwO5Xw,1734
30
30
  reconcile/github_users.py,sha256=nfTq78QRONIfDVj-5O3bD6psllJjzWFnog-EJ1WqFPU,3672
31
31
  reconcile/github_validator.py,sha256=cVTVxJIGR4a1Jz8wrdXEAb_CMpXUzvykVmUURX4cook,917
32
- reconcile/gitlab_fork_compliance.py,sha256=5h9yovk_21SPvv-bDjF02TW4C3uuDBS20I3UQXo7RPI,4154
33
- reconcile/gitlab_housekeeping.py,sha256=3swjgFPhQC5M1Dthyf6Xu-ZcWdE3-2cvDpoLvr3Gvls,21181
32
+ reconcile/gitlab_fork_compliance.py,sha256=EuF86CSUlzUQERu3CFjRujtX_dXAwSXBaWDl9Ij0LM8,4165
33
+ reconcile/gitlab_housekeeping.py,sha256=gwykuBGFpCAWAl6mwbZDuGoN1Zb3zkA4szUrgZ438Eg,21288
34
34
  reconcile/gitlab_labeler.py,sha256=a7HToXqv4S72TBp6Dmu2OkjjO60k81kH3B0J4UXHs3A,4768
35
35
  reconcile/gitlab_members.py,sha256=PrJE9OhDRdGG_gHM_77nQojLb4B18jtUu8DxgLsRS88,8417
36
36
  reconcile/gitlab_mr_sqs_consumer.py,sha256=O46mdziPgGOndbU-0_UJKJVUaiEoVzJPEgKm4_UvYoI,2571
@@ -139,7 +139,7 @@ reconcile/aus/version_gates/ocp_gate_handler.py,sha256=RW1ppDaCZXVegV9AzzqYXxDUu
139
139
  reconcile/aus/version_gates/sts_version_gate_handler.py,sha256=swwwz0YyvrEBf_InqrRRBCt2QzHYNvvq8jz9aYwElh4,3663
140
140
  reconcile/aws_account_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
141
  reconcile/aws_account_manager/integration.py,sha256=tdIXqHyd0QLZOX_7_jyU7eEil-jYJbAv4I4V1c3KhVk,14981
142
- reconcile/aws_account_manager/merge_request_manager.py,sha256=zZct3NxWMBQupl4QfD7ULxnt4ipt_2FBoH_NusboIuw,3781
142
+ reconcile/aws_account_manager/merge_request_manager.py,sha256=eLRf7D6j9sPvJh3xpYRkrckleGrZcr6J0aED7xmNbXI,3815
143
143
  reconcile/aws_account_manager/metrics.py,sha256=YB10ea4kIGwJfs5N14RF-RoXPb-QQWaDBz1jLZ3YWE0,917
144
144
  reconcile/aws_account_manager/reconciler.py,sha256=5YG7nZ4GG1SY2Pe_OxetdtzOnl37MllZMu_ca22vRSo,15025
145
145
  reconcile/aws_account_manager/utils.py,sha256=iYPPOtbZ7FiKkz9v5f1YXRIHw5YFOtSavUkF8oMwfJY,1439
@@ -437,7 +437,7 @@ reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py,sh
437
437
  reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request.py,sha256=BeAJWLow7b4HQyZ9zz398sQkPeIz8chpMkCts2NU27c,1282
438
438
  reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager_v2.py,sha256=-zWK-cqr_8o9ZEnNfD3cfkv3hrrU4HJ4UMzi4J2Hjhw,6223
439
439
  reconcile/saas_auto_promotions_manager/merge_request_manager/metrics.py,sha256=sdHp71Wl87tFM-Z_QvqvdHhyrppFLGi4ekksCi_e_bs,977
440
- reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py,sha256=WDpClngRA7o9j9o8WsTDDU42p_xVqkmr6dERxQXBIAA,8579
440
+ reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py,sha256=UoTw_UpyIsqCJoER77xtTI8kZTVaWhMJqSrkOfAQU_I,8629
441
441
  reconcile/saas_auto_promotions_manager/merge_request_manager/open_merge_requests.py,sha256=-qGQOh6Jdp4lomNDij3zWVC0pl6uPHFWS5Woqcp5HQk,410
442
442
  reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py,sha256=IZ7cuH6uOi7f0aIPVi1irBmP0CIK5vmEuhKBJz4YA1s,7235
443
443
  reconcile/saas_auto_promotions_manager/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -501,7 +501,7 @@ reconcile/test/test_gabi_authorized_users.py,sha256=6XnV5Q9inxP81ktGMVKyWucjBTUj
501
501
  reconcile/test/test_gcr_mirror.py,sha256=A0y8auKZzr62-mGoxSQ__JnN0-ijZUltzjwR5miBgso,490
502
502
  reconcile/test/test_github_org.py,sha256=j3KeB4OnSln1gm2hidce49xdMru-j75NS3cM-AEgzZc,4511
503
503
  reconcile/test/test_github_repo_invites.py,sha256=UVaDlxSxi5iooyUbz8F11d7cvINHLK32Qx9b7xrDd-k,3347
504
- reconcile/test/test_gitlab_housekeeping.py,sha256=Sn5rERCp28sMiBx5vJaQ5yy80y37GouMClejmXocsT8,10068
504
+ reconcile/test/test_gitlab_housekeeping.py,sha256=3n5X7aedrwFULpEtzSpfJf2Ky0cNn26zybh9KWr6N0k,10048
505
505
  reconcile/test/test_gitlab_labeler.py,sha256=PmYXiU2g0_O5OTdMGPzdeqBAfat92U9bhjjfeyiGSmQ,4336
506
506
  reconcile/test/test_gitlab_members.py,sha256=kaCOA02eZSrSMkzHmaLwWW3LY6Af0ciLSEP4PlMLvOU,9949
507
507
  reconcile/test/test_gitlab_permissions.py,sha256=vPFEsdjyP-lO8pc2rN6acMns3Sjz9YJs16msbBR8DZc,2736
@@ -657,7 +657,7 @@ reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,
657
657
  reconcile/utils/git.py,sha256=actOWI2HiNpMIV6nHCzinhRa6b04Y9plWOCcPQa8lNA,1437
658
658
  reconcile/utils/git_secrets.py,sha256=y1rEhwA8DyDpBSAEuhMS7Y2X3mpxT2zQ4zyDFkhLe_g,1936
659
659
  reconcile/utils/github_api.py,sha256=R8OvqyPdnRqvP-Efnv9RvIcbBlb4M0KC4RlbnJMD0Tg,2426
660
- reconcile/utils/gitlab_api.py,sha256=T4kAaiXRztgxTMpyR0XND_6Wm0WThRoHUNULzNZxNBA,29285
660
+ reconcile/utils/gitlab_api.py,sha256=ZR71p2WcKtyb4aq_K-CkWPcjtaBf64Lv6Vi79LIHvIw,30180
661
661
  reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
662
662
  reconcile/utils/gql.py,sha256=IGhxzBcuebbapDKLseevEThSsxa_eDCPNpo3A4VnOS4,14066
663
663
  reconcile/utils/grouping.py,sha256=vr9SFHZ7bqmHYrvYcEZt-Er3-yQYfAAdq5sHLZVmXPY,456
@@ -750,7 +750,7 @@ reconcile/utils/membershipsources/app_interface_resolver.py,sha256=MqDFvK3aXhmmM
750
750
  reconcile/utils/membershipsources/models.py,sha256=r733cJUTne1ZKBBB8iMyxZxlk6ut2OmJUK68ui8OUq0,1929
751
751
  reconcile/utils/membershipsources/resolver.py,sha256=meERrCZqeVJZR2lHdZEznaZDsCsUb194UEMOjbE4aYA,3168
752
752
  reconcile/utils/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
753
- reconcile/utils/merge_request_manager/merge_request_manager.py,sha256=6i2uwSJoeYiZSRwcnBG5ICQXyIC8umvQ_TzubR7_jvA,3374
753
+ reconcile/utils/merge_request_manager/merge_request_manager.py,sha256=UVGkCo9RZJaCynBqqITW7cTDXFsHIoyh9kSvobmQVT0,3385
754
754
  reconcile/utils/merge_request_manager/parser.py,sha256=5pGoz8Q6EuYXlUc1z-D0FahdRP2YLO8CpACoa9HcgtQ,2098
755
755
  reconcile/utils/mr/__init__.py,sha256=JQS8xmLZdG4TgjAFZp2ltuV9c1sDFHIHJmpn1bkY9rM,2386
756
756
  reconcile/utils/mr/app_interface_reporter.py,sha256=6Kpg93V9FvcOke9Jimkva359MQ-ZyBIkUpf8QIA6-to,1793
@@ -844,8 +844,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
844
844
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
845
845
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
846
846
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
847
- qontract_reconcile-0.10.1rc926.dist-info/METADATA,sha256=eqpGD1LsAnd-3_U8CIfVPdBtlGf52mIqcTnfrhGHdFE,2273
848
- qontract_reconcile-0.10.1rc926.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
849
- qontract_reconcile-0.10.1rc926.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
850
- qontract_reconcile-0.10.1rc926.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
851
- qontract_reconcile-0.10.1rc926.dist-info/RECORD,,
847
+ qontract_reconcile-0.10.1rc927.dist-info/METADATA,sha256=HBpJZqOcu2pa9PE_ZhB7OJrO3SGCKtUWQtZ6absL3gs,2262
848
+ qontract_reconcile-0.10.1rc927.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
849
+ qontract_reconcile-0.10.1rc927.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
850
+ qontract_reconcile-0.10.1rc927.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
851
+ qontract_reconcile-0.10.1rc927.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from typing import cast
2
3
 
3
4
  from gitlab.exceptions import GitlabGetError
4
5
  from gitlab.v4.objects import ProjectMergeRequest
@@ -61,11 +62,11 @@ class MergeRequestManager:
61
62
  self._vcs = vcs
62
63
  self._auto_merge_enabled = auto_merge_enabled
63
64
 
64
- def _merge_request_already_exists(self, aws_acccount_file_path: str) -> bool:
65
+ def _merge_request_already_exists(self, aws_account_file_path: str) -> bool:
65
66
  return any(
66
- aws_acccount_file_path == diff["new_path"]
67
+ aws_account_file_path == diff["new_path"]
67
68
  for mr in self._open_mrs
68
- for diff in mr.changes()["changes"]
69
+ for diff in cast(dict, mr.changes())["changes"]
69
70
  )
70
71
 
71
72
  def fetch_open_merge_requests(self) -> None:
@@ -1,10 +1,8 @@
1
1
  import logging
2
2
  import sys
3
3
 
4
- from gitlab import (
5
- MAINTAINER_ACCESS,
6
- GitlabGetError,
7
- )
4
+ from gitlab import GitlabGetError
5
+ from gitlab.const import MAINTAINER_ACCESS
8
6
 
9
7
  from reconcile import queries
10
8
  from reconcile.utils.gitlab_api import GitLabApi
@@ -232,9 +232,10 @@ def handle_stale_items(
232
232
  continue
233
233
 
234
234
  # if item has 'stale' label - check the notes
235
+ # TODO: add request count metrics and maybe server side filter to reduce requests
235
236
  cancel_notes = [
236
237
  n
237
- for n in item.notes.list()
238
+ for n in item.notes.list(iterator=True)
238
239
  if n.attributes.get("body") == f"/{LABEL} cancel"
239
240
  ]
240
241
  if not cancel_notes:
@@ -68,13 +68,15 @@ class MRParser:
68
68
  - MR has merge conflicts
69
69
  """
70
70
  all_open_mrs = self._vcs.get_open_app_interface_merge_requests()
71
- sapm_mrs = [mr for mr in all_open_mrs if label in mr.attributes.get("labels")]
71
+ sapm_mrs = [
72
+ mr for mr in all_open_mrs if label in mr.attributes.get("labels", [])
73
+ ]
72
74
  open_batcher_mrs: list[ProjectMergeRequest] = []
73
75
  open_scheduler_mrs: list[ProjectMergeRequest] = []
74
76
 
75
77
  for mr in sapm_mrs:
76
78
  attrs = mr.attributes
77
- desc = attrs.get("description")
79
+ desc = str(attrs.get("description", ""))
78
80
  parts = desc.split(PROMOTION_DATA_SEPARATOR)
79
81
  if not len(parts) == 2:
80
82
  logging.info(
@@ -142,10 +144,10 @@ class MRParser:
142
144
  seen: set[tuple[str, str]] = set()
143
145
  for mr in mrs:
144
146
  attrs = mr.attributes
145
- desc = attrs.get("description")
147
+ desc = str(attrs.get("description", ""))
146
148
  parts = desc.split(PROMOTION_DATA_SEPARATOR)
147
149
  promotion_data = parts[1]
148
- has_conflicts = attrs.get("has_conflicts", False)
150
+ has_conflicts = bool(attrs.get("has_conflicts", False))
149
151
  if has_conflicts:
150
152
  logging.info(
151
153
  "Merge-conflict detected. Closing %s",
@@ -3,6 +3,7 @@ from datetime import (
3
3
  timedelta,
4
4
  )
5
5
  from unittest.mock import (
6
+ Mock,
6
7
  create_autospec,
7
8
  patch,
8
9
  )
@@ -172,7 +173,7 @@ def project() -> Project:
172
173
 
173
174
 
174
175
  @pytest.fixture
175
- def can_be_merged_merge_request() -> ProjectMergeRequest:
176
+ def can_be_merged_merge_request() -> Mock:
176
177
  mr = create_autospec(ProjectMergeRequest)
177
178
  mr.merge_status = "can_be_merged"
178
179
  mr.work_in_progress = False
@@ -205,7 +206,7 @@ def success_merge_request_pipeline() -> dict:
205
206
 
206
207
  def test_merge_merge_requests(
207
208
  project: Project,
208
- can_be_merged_merge_request: ProjectMergeRequest,
209
+ can_be_merged_merge_request: Mock,
209
210
  add_lgtm_merge_request_resource_label_event: ProjectMergeRequestResourceLabelEvent,
210
211
  success_merge_request_pipeline: dict,
211
212
  ) -> None:
@@ -13,6 +13,7 @@ from operator import (
13
13
  from typing import (
14
14
  Any,
15
15
  TypedDict,
16
+ cast,
16
17
  )
17
18
  from urllib.parse import urlparse
18
19
 
@@ -30,7 +31,9 @@ from gitlab.v4.objects import (
30
31
  Group,
31
32
  Project,
32
33
  ProjectIssue,
34
+ ProjectIssueManager,
33
35
  ProjectMergeRequest,
36
+ ProjectMergeRequestManager,
34
37
  ProjectMergeRequestNote,
35
38
  )
36
39
  from sretoolbox.utils import retry
@@ -242,9 +245,9 @@ class GitLabApi: # pylint: disable=too-many-public-methods
242
245
  if project is None:
243
246
  return None
244
247
  if query:
245
- members = self.get_items(project.members.all, query_parameters=query)
248
+ members = self.get_items(project.members_all.list, query_parameters=query)
246
249
  else:
247
- members = self.get_items(project.members.all)
250
+ members = self.get_items(project.members_all.list)
248
251
  return [m.username for m in members if m.access_level >= 40]
249
252
 
250
253
  def get_app_sre_group_users(self):
@@ -273,7 +276,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
273
276
  access_level = self.get_access_level(access)
274
277
  # check if we have 'access_level' access so we can add the group with same role.
275
278
  members = self.get_items(
276
- project.members.all, query_parameters={"user_ids": self.user.id}
279
+ project.members_all.list, query_parameters={"user_ids": self.user.id}
277
280
  )
278
281
  if not any(
279
282
  self.user.id == member.id and member.access_level >= access_level
@@ -450,8 +453,13 @@ class GitLabApi: # pylint: disable=too-many-public-methods
450
453
  return self.get_items(mr.resourcelabelevents.list)
451
454
 
452
455
  def get_merge_request_pipelines(self, mr: ProjectMergeRequest) -> list[dict]:
456
+ # TODO: use typed object in return
457
+ # TODO: use server side order_by
458
+ items = self.get_items(mr.pipelines.list)
453
459
  return sorted(
454
- self.get_items(mr.pipelines), key=lambda x: x["created_at"], reverse=True
460
+ [i.asdict() for i in items],
461
+ key=lambda x: x["created_at"],
462
+ reverse=True,
455
463
  )
456
464
 
457
465
  @staticmethod
@@ -459,7 +467,8 @@ class GitLabApi: # pylint: disable=too-many-public-methods
459
467
  merge_request: ProjectMergeRequest,
460
468
  ) -> list[str]:
461
469
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
462
- changes = merge_request.changes()["changes"]
470
+ result = merge_request.changes()
471
+ changes = cast(dict, result)["changes"]
463
472
  changed_paths = set()
464
473
  for change in changes:
465
474
  old_path = change["old_path"]
@@ -587,6 +596,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
587
596
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
588
597
  merge_request.notes.create({"body": body})
589
598
 
599
+ # TODO: deprecated this method as new support of list(get_all=True), and figure out request counter metrics
590
600
  @staticmethod
591
601
  def get_items(method, **kwargs):
592
602
  all_items = []
@@ -608,7 +618,18 @@ class GitLabApi: # pylint: disable=too-many-public-methods
608
618
  @staticmethod
609
619
  def refresh_labels(item: ProjectMergeRequest | ProjectIssue):
610
620
  gitlab_request.labels(integration=INTEGRATION_NAME).inc()
611
- refreshed_item = item.manager.get(item.get_id())
621
+ manager: ProjectMergeRequestManager | ProjectIssueManager
622
+ match item:
623
+ case ProjectMergeRequest():
624
+ manager = cast(ProjectMergeRequestManager, item.manager)
625
+ case ProjectIssue():
626
+ manager = cast(ProjectIssueManager, item.manager)
627
+ case _:
628
+ raise ValueError("item must be a ProjectMergeRequest or ProjectIssue")
629
+ item_id = item.get_id()
630
+ if item_id is None:
631
+ raise ValueError("item must have an id")
632
+ refreshed_item = manager.get(item_id)
612
633
  item.labels = refreshed_item.labels
613
634
 
614
635
  @staticmethod
@@ -67,7 +67,7 @@ class MergeRequestManagerBase(Generic[T]):
67
67
  """
68
68
  for mr in self._fetch_managed_open_merge_requests():
69
69
  attrs = mr.attributes
70
- desc = attrs.get("description")
70
+ desc = str(attrs.get("description") or "")
71
71
  has_conflicts = attrs.get("has_conflicts", False)
72
72
  if has_conflicts:
73
73
  logging.info(