qontract-reconcile 0.10.1rc1048__py3-none-any.whl → 0.10.1rc1050__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.
- {qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/RECORD +12 -12
- reconcile/openshift_resources_base.py +12 -0
- reconcile/test/test_utils_jinja2.py +7 -0
- reconcile/utils/aws_api.py +18 -0
- reconcile/utils/jinja2/filters.py +4 -0
- reconcile/utils/jinja2/utils.py +23 -0
- reconcile/utils/mr/base.py +23 -0
- reconcile/utils/mr/promote_qontract.py +15 -23
- {qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc1050
|
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
|
{qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/RECORD
RENAMED
@@ -72,7 +72,7 @@ reconcile/openshift_network_policies.py,sha256=DyjaeJvSFHmslbM8nyHCxpF9EtU2m-MJo
|
|
72
72
|
reconcile/openshift_prometheus_rules.py,sha256=onowXab248zmHH8SbYDTc1W1bl7JiqRFU1xdTkZyLFg,1332
|
73
73
|
reconcile/openshift_resourcequotas.py,sha256=yUi56PiOn3inMMfq_x_FEHmaW-reGipzoorjdar372g,2415
|
74
74
|
reconcile/openshift_resources.py,sha256=I2nO_C37mG3rfyGrd4cGwN3mVseVGuTAHAyhFzLyqF4,1518
|
75
|
-
reconcile/openshift_resources_base.py,sha256=
|
75
|
+
reconcile/openshift_resources_base.py,sha256=1A5_699p0rdsMwRQRPzePEfjhhq5eB2Obwxx4Ibr8jA,41205
|
76
76
|
reconcile/openshift_rolebindings.py,sha256=9mlJ2FjWUoH-rsjtasreA_hV-K5Z_YR00qR_RR60OZM,6555
|
77
77
|
reconcile/openshift_routes.py,sha256=fXvuPSjcjVw1X3j2EQvUAdbOepmIFdKk-M3qP8QzPiw,1075
|
78
78
|
reconcile/openshift_saas_deploy.py,sha256=UZlm29JujJVS3MzSm6uehlC3y-jZxl6WVwMeKRdN11U,12773
|
@@ -571,7 +571,7 @@ reconcile/test/test_terraform_users.py,sha256=XOAfGvITCJPI1LTlISmHbA4ONMQMkxYUMT
|
|
571
571
|
reconcile/test/test_terraform_vpc_peerings.py,sha256=bpjCjhmic07cw3XKSHf-2JvmLuWlyQG8laXlC-H7qtI,20796
|
572
572
|
reconcile/test/test_terraform_vpc_peerings_build_desired_state.py,sha256=cHmr1_yhRgfdqlFX6TMw-aiKXebaRv0szl16M9YRJic,49988
|
573
573
|
reconcile/test/test_three_way_diff_strategy.py,sha256=v3rNkQFNy5e1uyfeNSlNBA07fvrPGD0aXD91Lgv8oxc,4062
|
574
|
-
reconcile/test/test_utils_jinja2.py,sha256=
|
574
|
+
reconcile/test/test_utils_jinja2.py,sha256=rKugJEPl0qFC9joenJBXyk2qe-9md31-4EdxvQ2h5cs,4058
|
575
575
|
reconcile/test/test_vault_replication.py,sha256=wlc4jm9f8P641UvvxIFFFc5_unJysNkOVrKJscjhQr0,16867
|
576
576
|
reconcile/test/test_vault_utils.py,sha256=vbJnc89XAuE07qbTuWxHM5o9F6R9SO5aHXA38fwxT7A,1122
|
577
577
|
reconcile/test/test_version_bump.py,sha256=q6-3Y1roriI6YWpFwaHOMN7emEP3yL33sh_0VdbmG7E,511
|
@@ -652,7 +652,7 @@ reconcile/unleash_feature_toggles/integration.py,sha256=nx7BhtzCsTfPbOp60vI5MkNw
|
|
652
652
|
reconcile/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
653
653
|
reconcile/utils/aggregated_list.py,sha256=km0xadW0jO4G_CqZPsXmoBURQ8c90FaTu5x4X1K1cZs,3357
|
654
654
|
reconcile/utils/amtool.py,sha256=ngtBuVPETH6oAy5RnKzvreVbjwQCaATS_PYYwBprzjQ,2288
|
655
|
-
reconcile/utils/aws_api.py,sha256=
|
655
|
+
reconcile/utils/aws_api.py,sha256=1EC9l2cxMW2IvnhsXaIcq6iohZ4rRgqyHJsXPs9Vtes,67398
|
656
656
|
reconcile/utils/aws_helper.py,sha256=MDbv5jrNdqqJ5pfBxniGdJXBBO_EYc2_Uf2w9ZzeMNs,2854
|
657
657
|
reconcile/utils/batches.py,sha256=TtEm64a8lWhFuNbUVpFEmXVdU2Q0sTBrP_I0Cjbgh7g,320
|
658
658
|
reconcile/utils/binary.py,sha256=7MaAFBpzuBUTJ_aA6G6-eult_BPMVyiXbBLD0Y6F-DM,2301
|
@@ -761,8 +761,8 @@ reconcile/utils/internal_groups/client.py,sha256=V3uubJJHqW_wjwrwbC0halZrhOy7_yx
|
|
761
761
|
reconcile/utils/internal_groups/models.py,sha256=y_IqBVqfGqNXiu0VudvBWFrm_-uafVm5KgLG-ca8XAs,2281
|
762
762
|
reconcile/utils/jinja2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
763
763
|
reconcile/utils/jinja2/extensions.py,sha256=7K-uo6G2eCWa98MHT8fRPYIKCLQB_5D2keqQ_LyAfHM,1293
|
764
|
-
reconcile/utils/jinja2/filters.py,sha256=
|
765
|
-
reconcile/utils/jinja2/utils.py,sha256=
|
764
|
+
reconcile/utils/jinja2/filters.py,sha256=JfO_14APySBPidsMvHXG-8dULNPddZCE15Umjk_aSBk,4830
|
765
|
+
reconcile/utils/jinja2/utils.py,sha256=adxVFY4WvBGIToEEr8KwqLzp6uDxSipTBzZWwnRqbNQ,8700
|
766
766
|
reconcile/utils/jobcontroller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
767
767
|
reconcile/utils/jobcontroller/controller.py,sha256=n9twSH_xZtp5vknM3qByp0eWCn9ED4U1a6yOFBS-GTg,14488
|
768
768
|
reconcile/utils/jobcontroller/models.py,sha256=x9YIvWfYOOvXNKToFVx1H7qDrZb0Sa1KI_4Y0gl7rMM,6336
|
@@ -776,14 +776,14 @@ reconcile/utils/merge_request_manager/parser.py,sha256=5pGoz8Q6EuYXlUc1z-D0FahdR
|
|
776
776
|
reconcile/utils/mr/__init__.py,sha256=hcfHDIIIsJT4C0BnzDnyeZEfZdamrqHzMLcBzIT1ibI,2578
|
777
777
|
reconcile/utils/mr/app_interface_reporter.py,sha256=6Kpg93V9FvcOke9Jimkva359MQ-ZyBIkUpf8QIA6-to,1793
|
778
778
|
reconcile/utils/mr/aws_access.py,sha256=w-UJutB_OfBJOvr-gsPzhtBPkDfKcNZZWGGuI9cN3HI,2605
|
779
|
-
reconcile/utils/mr/base.py,sha256=
|
779
|
+
reconcile/utils/mr/base.py,sha256=UwA_jm_2vMlWg-Y3oYlGWoXLgrTLBQGKtwIasJSK9_8,8105
|
780
780
|
reconcile/utils/mr/clusters_updates.py,sha256=pcusPAwRUkvyk_-bixsRNTzSvpTLypJ1kflq5UEVgcM,2271
|
781
781
|
reconcile/utils/mr/glitchtip_access_reporter.py,sha256=i5vo9jjBifX5wIcLwEMH5_VFVg-NY-pBKe0HysSw4CQ,5031
|
782
782
|
reconcile/utils/mr/labels.py,sha256=9QRTRjZAtq45zELd9SwavaraczMjwjn5no3RK1YxFTg,825
|
783
783
|
reconcile/utils/mr/notificator.py,sha256=cp80wFzu_ZzrJPye7L1pI0H6JRGb7hOGuNxJYUq4Yr8,2967
|
784
784
|
reconcile/utils/mr/ocm_update_recommended_version.py,sha256=p_aVP0TGrlKk9WBwgQnYWqUDsED_Hg6G5Bqj0UvtRwA,1536
|
785
785
|
reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=ojnIjw-8vRnmCCxOGBOEgPZLH4nC1hcuef74LWw2Rpk,3004
|
786
|
-
reconcile/utils/mr/promote_qontract.py,sha256=
|
786
|
+
reconcile/utils/mr/promote_qontract.py,sha256=704SOkLBioEwXITEr2mNdqpXA30J7kuUq1kQ4Q_mN8c,6693
|
787
787
|
reconcile/utils/mr/user_maintenance.py,sha256=cHPBn8zrReWLHalyk-EFdkFJe9zjVjRoZhT4t2zZfGE,3956
|
788
788
|
reconcile/utils/ocm/__init__.py,sha256=Y-bp8GomMpyCo0tFW6kJ78-ZG1UIupYRtBzbMWU0kwM,798
|
789
789
|
reconcile/utils/ocm/addons.py,sha256=_LDdJ-gapM3s5exKlIUt-MlXZTAUoHezbYBU0QmvfWQ,7335
|
@@ -867,8 +867,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
|
|
867
867
|
tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
|
868
868
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
869
869
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
870
|
-
qontract_reconcile-0.10.
|
871
|
-
qontract_reconcile-0.10.
|
872
|
-
qontract_reconcile-0.10.
|
873
|
-
qontract_reconcile-0.10.
|
874
|
-
qontract_reconcile-0.10.
|
870
|
+
qontract_reconcile-0.10.1rc1050.dist-info/METADATA,sha256=jaTxzV1Ruwt2BUFyy5WMHueI5w-NoghTWsBX968vJqg,2213
|
871
|
+
qontract_reconcile-0.10.1rc1050.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
872
|
+
qontract_reconcile-0.10.1rc1050.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
873
|
+
qontract_reconcile-0.10.1rc1050.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
874
|
+
qontract_reconcile-0.10.1rc1050.dist-info/RECORD,,
|
@@ -1194,6 +1194,7 @@ def early_exit_monkey_patch() -> Generator:
|
|
1194
1194
|
lookup_github_file_content=DEFAULT,
|
1195
1195
|
url_makes_sense=DEFAULT,
|
1196
1196
|
lookup_s3_object=DEFAULT,
|
1197
|
+
list_s3_objects=DEFAULT,
|
1197
1198
|
) as mocks:
|
1198
1199
|
# mock lookup_secret
|
1199
1200
|
mocks["lookup_secret"].side_effect = (
|
@@ -1239,6 +1240,17 @@ def early_exit_monkey_patch() -> Generator:
|
|
1239
1240
|
mocks["lookup_s3_object"].unsafe_callable = False
|
1240
1241
|
mocks["lookup_s3_object"].alters_data = False
|
1241
1242
|
|
1243
|
+
# mock list_s3_objects
|
1244
|
+
mocks["list_s3_objects"].side_effect = (
|
1245
|
+
lambda account_name,
|
1246
|
+
bucket_name,
|
1247
|
+
path,
|
1248
|
+
region_name=None: f"list_s3_objects({account_name}, {bucket_name}, {path}, {region_name})"
|
1249
|
+
)
|
1250
|
+
# needed for jinja2 `is_safe_callable`
|
1251
|
+
mocks["list_s3_objects"].unsafe_callable = False
|
1252
|
+
mocks["list_s3_objects"].alters_data = False
|
1253
|
+
|
1242
1254
|
with patch(
|
1243
1255
|
"reconcile.openshift_resources_base.check_alertmanager_config",
|
1244
1256
|
return_value=True,
|
@@ -6,6 +6,7 @@ from reconcile.utils.jinja2.filters import (
|
|
6
6
|
hash_list,
|
7
7
|
json_pointers,
|
8
8
|
matches_jsonpath,
|
9
|
+
str_format,
|
9
10
|
)
|
10
11
|
|
11
12
|
|
@@ -121,3 +122,9 @@ def test_json_pointers() -> None:
|
|
121
122
|
assert json_pointers(input, "items[?@.name=='a']") == ["/items/0"]
|
122
123
|
assert json_pointers(input, "items[?@.name=='b']") == ["/items/1", "/items/2"]
|
123
124
|
assert json_pointers(input, "items[?@.name=='c']") == []
|
125
|
+
|
126
|
+
|
127
|
+
def test_str_format() -> None:
|
128
|
+
value = "path/to/object"
|
129
|
+
format = "s3://%s"
|
130
|
+
assert str_format(value, format) == "s3://path/to/object"
|
reconcile/utils/aws_api.py
CHANGED
@@ -1665,6 +1665,24 @@ class AWSApi: # pylint: disable=too-many-public-methods
|
|
1665
1665
|
s3.get_object(Bucket=bucket_name, Key=path)["Body"].read().decode("utf-8")
|
1666
1666
|
)
|
1667
1667
|
|
1668
|
+
def list_s3_objects(
|
1669
|
+
self,
|
1670
|
+
account_name: str,
|
1671
|
+
bucket_name: str,
|
1672
|
+
path: str,
|
1673
|
+
region_name: str | None = None,
|
1674
|
+
) -> list[str]:
|
1675
|
+
s3 = self._account_s3_client(account_name, region_name=region_name)
|
1676
|
+
objects = s3.list_objects_v2(Bucket=bucket_name, Prefix=path, Delimiter="/")[
|
1677
|
+
"Contents"
|
1678
|
+
]
|
1679
|
+
return [
|
1680
|
+
obj["Key"]
|
1681
|
+
for obj in sorted(
|
1682
|
+
objects, key=lambda obj: obj["LastModified"], reverse=True
|
1683
|
+
)
|
1684
|
+
]
|
1685
|
+
|
1668
1686
|
|
1669
1687
|
def aws_config_file_path() -> str | None:
|
1670
1688
|
config_file_path = os.path.expanduser(
|
@@ -136,3 +136,7 @@ def json_pointers(input: Any, jsonpath: str) -> list[str]:
|
|
136
136
|
Finds the RFC6901 JSON pointers of the input elements matching the given jsonpath
|
137
137
|
"""
|
138
138
|
return [_convert_pointer(str(i.full_path)) for i in _find_jsonpath(input, jsonpath)]
|
139
|
+
|
140
|
+
|
141
|
+
def str_format(value: str, format: str) -> str:
|
142
|
+
return format % value
|
reconcile/utils/jinja2/utils.py
CHANGED
@@ -21,6 +21,7 @@ from reconcile.utils.jinja2.filters import (
|
|
21
21
|
json_pointers,
|
22
22
|
json_to_dict,
|
23
23
|
matches_jsonpath,
|
24
|
+
str_format,
|
24
25
|
urlescape,
|
25
26
|
urlunescape,
|
26
27
|
yaml_to_dict,
|
@@ -89,6 +90,7 @@ def compile_jinja2_template(
|
|
89
90
|
"extract_jsonpath": extract_jsonpath,
|
90
91
|
"matches_jsonpath": matches_jsonpath,
|
91
92
|
"json_pointers": json_pointers,
|
93
|
+
"str_format": str_format,
|
92
94
|
})
|
93
95
|
|
94
96
|
return jinja_env.from_string(body)
|
@@ -146,6 +148,26 @@ def lookup_s3_object(
|
|
146
148
|
)
|
147
149
|
|
148
150
|
|
151
|
+
def list_s3_objects(
|
152
|
+
account_name: str,
|
153
|
+
bucket_name: str,
|
154
|
+
path: str,
|
155
|
+
region_name: str | None = None,
|
156
|
+
) -> list[str]:
|
157
|
+
settings = queries.get_app_interface_settings()
|
158
|
+
accounts = queries.get_aws_accounts(name=account_name)
|
159
|
+
if not accounts:
|
160
|
+
raise Exception(f"aws account not found: {account_name}")
|
161
|
+
|
162
|
+
with AWSApi(1, accounts, settings=settings, init_users=False) as aws_api:
|
163
|
+
return aws_api.list_s3_objects(
|
164
|
+
account_name,
|
165
|
+
bucket_name,
|
166
|
+
path,
|
167
|
+
region_name=region_name,
|
168
|
+
)
|
169
|
+
|
170
|
+
|
149
171
|
@retry()
|
150
172
|
def lookup_secret(
|
151
173
|
path: str,
|
@@ -214,6 +236,7 @@ def process_jinja2_template(
|
|
214
236
|
"query": lookup_graphql_query_results,
|
215
237
|
"url": url_makes_sense,
|
216
238
|
"s3": lookup_s3_object,
|
239
|
+
"s3_ls": list_s3_objects,
|
217
240
|
"flatten_dict": flatten,
|
218
241
|
"yesterday": lambda: (datetime.datetime.now() - datetime.timedelta(1)).strftime(
|
219
242
|
"%Y-%m-%d"
|
reconcile/utils/mr/base.py
CHANGED
@@ -4,12 +4,15 @@ from abc import (
|
|
4
4
|
ABC,
|
5
5
|
abstractmethod,
|
6
6
|
)
|
7
|
+
from collections.abc import Iterable
|
7
8
|
from typing import Any
|
8
9
|
from uuid import uuid4
|
9
10
|
|
10
11
|
from gitlab.exceptions import GitlabError
|
11
12
|
from jinja2 import Template
|
12
13
|
|
14
|
+
from reconcile import typed_queries
|
15
|
+
from reconcile.gql_definitions.fragments.user import User
|
13
16
|
from reconcile.utils.constants import PROJ_ROOT
|
14
17
|
from reconcile.utils.gitlab_api import GitLabApi
|
15
18
|
from reconcile.utils.mr.labels import DO_NOT_MERGE_HOLD
|
@@ -108,6 +111,26 @@ class MergeRequestBase(ABC):
|
|
108
111
|
**self.sqs_msg_data,
|
109
112
|
}
|
110
113
|
|
114
|
+
def infer_author(
|
115
|
+
self, author_email: str | None, all_users: Iterable[User] | None = None
|
116
|
+
) -> str | None:
|
117
|
+
if not author_email:
|
118
|
+
return None
|
119
|
+
if not all_users:
|
120
|
+
return None
|
121
|
+
|
122
|
+
username = author_email.split("@")[0]
|
123
|
+
users = None
|
124
|
+
if author_email.endswith(typed_queries.smtp.settings().mail_address):
|
125
|
+
users = [u for u in all_users if username == u.org_username]
|
126
|
+
elif author_email.endswith("users.noreply.github.com"):
|
127
|
+
users = [u for u in all_users if username == u.github_username]
|
128
|
+
|
129
|
+
if users:
|
130
|
+
return users[0].org_username
|
131
|
+
|
132
|
+
return None
|
133
|
+
|
111
134
|
def submit_to_sqs(self, sqs_cli: SQSGateway) -> None:
|
112
135
|
"""
|
113
136
|
Sends the MR message to SQS.
|
@@ -1,8 +1,6 @@
|
|
1
1
|
from jsonpath_ng.ext import parser
|
2
2
|
from ruamel.yaml.compat import StringIO
|
3
3
|
|
4
|
-
from reconcile import typed_queries
|
5
|
-
from reconcile.gql_definitions.fragments.user import User
|
6
4
|
from reconcile.typed_queries.users import get_users
|
7
5
|
from reconcile.utils.gitlab_api import GitLabApi
|
8
6
|
from reconcile.utils.mr.base import MergeRequestBase
|
@@ -12,9 +10,10 @@ from reconcile.utils.ruamel import create_ruamel_instance
|
|
12
10
|
class PromoteQontractSchemas(MergeRequestBase):
|
13
11
|
name = "promote_qontract_schemas"
|
14
12
|
|
15
|
-
def __init__(self, version: str):
|
13
|
+
def __init__(self, version: str, author_email: str | None = None):
|
16
14
|
self.path = ".env"
|
17
15
|
self.version = version
|
16
|
+
self.author_email = author_email
|
18
17
|
|
19
18
|
super().__init__()
|
20
19
|
|
@@ -22,7 +21,16 @@ class PromoteQontractSchemas(MergeRequestBase):
|
|
22
21
|
|
23
22
|
@property
|
24
23
|
def title(self) -> str:
|
25
|
-
|
24
|
+
author = self.infer_author(
|
25
|
+
author_email=self.author_email, all_users=get_users()
|
26
|
+
)
|
27
|
+
return f"[{self.name}] promote qontract-schemas to version {self.version}" + (
|
28
|
+
f" by @{author}"
|
29
|
+
if author
|
30
|
+
else f" by {self.author_email}"
|
31
|
+
if self.author_email
|
32
|
+
else ""
|
33
|
+
)
|
26
34
|
|
27
35
|
@property
|
28
36
|
def description(self) -> str:
|
@@ -60,27 +68,11 @@ class PromoteQontractReconcileCommercial(MergeRequestBase):
|
|
60
68
|
|
61
69
|
self.labels = []
|
62
70
|
|
63
|
-
def author(self, all_users: list[User] | None = None) -> str | None:
|
64
|
-
if not self.author_email:
|
65
|
-
return None
|
66
|
-
if not all_users:
|
67
|
-
return None
|
68
|
-
|
69
|
-
username = self.author_email.split("@")[0]
|
70
|
-
users = None
|
71
|
-
if self.author_email.endswith(typed_queries.smtp.settings().mail_address):
|
72
|
-
users = [u for u in all_users if username == u.org_username]
|
73
|
-
elif self.author_email.endswith("users.noreply.github.com"):
|
74
|
-
users = [u for u in all_users if username == u.github_username]
|
75
|
-
|
76
|
-
if users:
|
77
|
-
return users[0].org_username
|
78
|
-
|
79
|
-
return None
|
80
|
-
|
81
71
|
@property
|
82
72
|
def title(self) -> str:
|
83
|
-
author = self.
|
73
|
+
author = self.infer_author(
|
74
|
+
author_email=self.author_email, all_users=get_users()
|
75
|
+
)
|
84
76
|
return f"[{self.name}] promote qontract-reconcile to version {self.version}" + (
|
85
77
|
f" by @{author}"
|
86
78
|
if author
|
{qontract_reconcile-0.10.1rc1048.dist-info → qontract_reconcile-0.10.1rc1050.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|