pulp-python 3.14.0__tar.gz → 3.15.1__tar.gz

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.
Files changed (78) hide show
  1. {pulp_python-3.14.0 → pulp_python-3.15.1}/CHANGES.md +41 -6
  2. {pulp_python-3.14.0 → pulp_python-3.15.1}/PKG-INFO +1 -1
  3. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/__init__.py +1 -1
  4. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/pypi/views.py +2 -2
  5. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/tasks/__init__.py +1 -0
  6. pulp_python-3.15.1/pulp_python/app/tasks/repair.py +89 -0
  7. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/viewsets.py +20 -1
  8. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_repair.py +49 -1
  9. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python.egg-info/PKG-INFO +1 -1
  10. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python.egg-info/SOURCES.txt +1 -0
  11. {pulp_python-3.14.0 → pulp_python-3.15.1}/pyproject.toml +2 -2
  12. {pulp_python-3.14.0 → pulp_python-3.15.1}/COMMITMENT +0 -0
  13. {pulp_python-3.14.0 → pulp_python-3.15.1}/COPYRIGHT +0 -0
  14. {pulp_python-3.14.0 → pulp_python-3.15.1}/LICENSE +0 -0
  15. {pulp_python-3.14.0 → pulp_python-3.15.1}/MANIFEST.in +0 -0
  16. {pulp_python-3.14.0 → pulp_python-3.15.1}/README.md +0 -0
  17. {pulp_python-3.14.0 → pulp_python-3.15.1}/functest_requirements.txt +0 -0
  18. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/__init__.py +0 -0
  19. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/global_access_conditions.py +0 -0
  20. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/management/__init__.py +0 -0
  21. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/management/commands/__init__.py +0 -0
  22. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/management/commands/repair-python-metadata.py +0 -0
  23. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0001_initial.py +0 -0
  24. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0002_pythonpackagecontent_python_version.py +0 -0
  25. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0003_new_sync_filters.py +0 -0
  26. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0004_DATA_swap_distribution_model.py +0 -0
  27. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0005_pythonpackagecontent_sha256.py +0 -0
  28. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0006_pythonrepository_autopublish.py +0 -0
  29. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0007_pythonpackagecontent_mv-2-1.py +0 -0
  30. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0008_pythonpackagecontent_unique_sha256.py +0 -0
  31. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0009_pythondistribution_allow_uploads.py +0 -0
  32. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0010_update_json_field.py +0 -0
  33. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0011_alter_pythondistribution_distribution_ptr_and_more.py +0 -0
  34. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0012_add_domain.py +0 -0
  35. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/0013_add_rbac_permissions.py +0 -0
  36. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/migrations/__init__.py +0 -0
  37. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/modelresource.py +0 -0
  38. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/models.py +0 -0
  39. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/pypi/__init__.py +0 -0
  40. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/pypi/serializers.py +0 -0
  41. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/replica.py +0 -0
  42. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/serializers.py +0 -0
  43. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/settings.py +0 -0
  44. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/tasks/publish.py +0 -0
  45. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/tasks/sync.py +0 -0
  46. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/tasks/upload.py +0 -0
  47. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/urls.py +0 -0
  48. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/utils.py +0 -0
  49. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/webserver_snippets/__init__.py +0 -0
  50. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/webserver_snippets/apache.conf +0 -0
  51. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/app/webserver_snippets/nginx.conf +0 -0
  52. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/pytest_plugin.py +0 -0
  53. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/__init__.py +0 -0
  54. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/__init__.py +0 -0
  55. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/__init__.py +0 -0
  56. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_auto_publish.py +0 -0
  57. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_consume_content.py +0 -0
  58. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_crud_content_unit.py +0 -0
  59. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_crud_publications.py +0 -0
  60. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_crud_remotes.py +0 -0
  61. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_domains.py +0 -0
  62. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_download_content.py +0 -0
  63. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_export_import.py +0 -0
  64. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_full_mirror.py +0 -0
  65. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_pypi_apis.py +0 -0
  66. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_rbac.py +0 -0
  67. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/api/test_sync.py +0 -0
  68. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/constants.py +0 -0
  69. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/functional/utils.py +0 -0
  70. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/unit/__init__.py +0 -0
  71. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python/tests/unit/test_models.py +0 -0
  72. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python.egg-info/dependency_links.txt +0 -0
  73. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python.egg-info/entry_points.txt +0 -0
  74. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python.egg-info/requires.txt +0 -0
  75. {pulp_python-3.14.0 → pulp_python-3.15.1}/pulp_python.egg-info/top_level.txt +0 -0
  76. {pulp_python-3.14.0 → pulp_python-3.15.1}/setup.cfg +0 -0
  77. {pulp_python-3.14.0 → pulp_python-3.15.1}/test_requirements.txt +0 -0
  78. {pulp_python-3.14.0 → pulp_python-3.15.1}/unittest_requirements.txt +0 -0
@@ -8,6 +8,24 @@
8
8
 
9
9
  [//]: # (towncrier release notes start)
10
10
 
11
+ ## 3.15.1 (2025-06-10) {: #3.15.1 }
12
+
13
+ #### Bugfixes {: #3.15.1-bugfix }
14
+
15
+ - Fixed pull-through caching not working for indexes that use relative URLs.
16
+ [#842](https://github.com/pulp/pulp_python/issues/842)
17
+
18
+ ---
19
+
20
+ ## 3.15.0 (2025-05-13) {: #3.15.0 }
21
+
22
+ #### Features {: #3.15.0-feature }
23
+
24
+ - Added new `repair_metadata` endpoint to `Repository` for fixing packages' metadata.
25
+ [#805](https://github.com/pulp/pulp_python/issues/805)
26
+
27
+ ---
28
+
11
29
  ## 3.14.0 (2025-04-10) {: #3.14.0 }
12
30
 
13
31
  #### Features {: #3.14.0-feature }
@@ -28,6 +46,20 @@
28
46
 
29
47
  ---
30
48
 
49
+ ## 3.13.5 (2025-04-23) {: #3.13.5 }
50
+
51
+ No significant changes.
52
+
53
+ ---
54
+
55
+ ## 3.13.4 (2025-04-10) {: #3.13.4 }
56
+
57
+ #### Misc {: #3.13.4-misc }
58
+
59
+ - [#809](https://github.com/pulp/pulp_python/issues/809)
60
+
61
+ ---
62
+
31
63
  ## 3.13.3 (2025-04-07) {: #3.13.3 }
32
64
 
33
65
  #### Bugfixes {: #3.13.3-bugfix }
@@ -115,7 +147,7 @@ No significant changes.
115
147
 
116
148
  ---
117
149
 
118
- # ## 3.12.2 (2024-08-21) {: #3.12.2 }
150
+ ## 3.12.2 (2024-08-21) {: #3.12.2 }
119
151
 
120
152
  #### Bugfixes {: #3.12.2-bugfix }
121
153
 
@@ -126,7 +158,6 @@ No significant changes.
126
158
 
127
159
  ## 3.12.1 (2024-06-27) {: #3.12.1 }
128
160
 
129
-
130
161
  #### Bugfixes {: #3.12.1-bugfix }
131
162
 
132
163
  - Fixed the `package_types` filter breaking other remote filters.
@@ -136,7 +167,6 @@ No significant changes.
136
167
 
137
168
  ## 3.12.0 (2024-06-25) {: #3.12.0 }
138
169
 
139
-
140
170
  #### Features {: #3.12.0-feature }
141
171
 
142
172
  - Added RBAC support.
@@ -162,6 +192,12 @@ No significant changes.
162
192
 
163
193
  ---
164
194
 
195
+ ## 3.11.5 (2025-04-15) {: #3.11.5 }
196
+
197
+ No significant changes.
198
+
199
+ ---
200
+
165
201
  ## 3.11.4 (2025-02-20) {: #3.11.4 }
166
202
 
167
203
  #### Bugfixes {: #3.11.4-bugfix }
@@ -175,7 +211,7 @@ No significant changes.
175
211
 
176
212
  ---
177
213
 
178
- # ## 3.11.3 (2024-08-21) {: #3.11.3 }
214
+ ## 3.11.3 (2024-08-21) {: #3.11.3 }
179
215
 
180
216
  #### Bugfixes {: #3.11.3-bugfix }
181
217
 
@@ -188,7 +224,6 @@ No significant changes.
188
224
 
189
225
  ## 3.11.2 (2024-06-27) {: #3.11.2 }
190
226
 
191
-
192
227
  #### Bugfixes {: #3.11.2-bugfix }
193
228
 
194
229
  - Fixed the `package_types` filter breaking other remote filters.
@@ -406,7 +441,7 @@ No significant changes.
406
441
 
407
442
  ---
408
443
 
409
- 3.4.0 (2021-06-17)
444
+ ## 3.4.0 (2021-06-17) {: #3.4.0 }
410
445
 
411
446
  ### Features
412
447
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pulp-python
3
- Version: 3.14.0
3
+ Version: 3.15.1
4
4
  Summary: pulp-python plugin for the Pulp Project
5
5
  Author-email: Pulp Team <pulp-list@redhat.com>
6
6
  Project-URL: Homepage, https://pulpproject.org
@@ -10,7 +10,7 @@ class PulpPythonPluginAppConfig(PulpPluginAppConfig):
10
10
 
11
11
  name = "pulp_python.app"
12
12
  label = "python"
13
- version = "3.14.0"
13
+ version = "3.15.1"
14
14
  python_package_name = "pulp-python"
15
15
  domain_compatible = True
16
16
 
@@ -260,9 +260,9 @@ class SimpleView(PackageUploadMixin, ViewSet):
260
260
  return HttpResponse(f"{remote.url} timed out while fetching {package}.", status=504)
261
261
 
262
262
  if d.headers["content-type"] == "application/vnd.pypi.simple.v1+json":
263
- page = ProjectPage.from_json_data(json.load(open(d.path, "rb")), base_url=remote.url)
263
+ page = ProjectPage.from_json_data(json.load(open(d.path, "rb")), base_url=url)
264
264
  else:
265
- page = ProjectPage.from_html(package, open(d.path, "rb").read(), base_url=remote.url)
265
+ page = ProjectPage.from_html(package, open(d.path, "rb").read(), base_url=url)
266
266
  packages = [
267
267
  parse_package(p) for p in page.packages if rfilter.filter_release(package, p.version)
268
268
  ]
@@ -3,5 +3,6 @@ Asynchronous task definitions.
3
3
  """
4
4
 
5
5
  from .publish import publish # noqa:F401
6
+ from .repair import repair # noqa:F401
6
7
  from .sync import sync # noqa:F401
7
8
  from .upload import upload, upload_group # noqa:F401
@@ -0,0 +1,89 @@
1
+ import logging
2
+ import uuid
3
+ from gettext import gettext as _
4
+
5
+ from django.db.models.query import QuerySet
6
+ from pulpcore.plugin.models import ProgressReport
7
+ from pulpcore.plugin.util import get_domain
8
+
9
+ from pulp_python.app.models import PythonPackageContent, PythonRepository
10
+ from pulp_python.app.utils import artifact_to_python_content_data
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ def repair(repository_pk: uuid.UUID) -> None:
16
+ """
17
+ Repairs metadata of all packages for the specified repository.
18
+
19
+ Args:
20
+ repository_pk (uuid.UUID): The primary key of the repository to repair.
21
+
22
+ Returns:
23
+ None
24
+ """
25
+ repository = PythonRepository.objects.get(pk=repository_pk)
26
+
27
+ log.info(
28
+ _(
29
+ "Repairing packages' metadata for the latest version of repository {}."
30
+ ).format(repository.name)
31
+ )
32
+ content_set = repository.latest_version().content.values_list("pk", flat=True)
33
+ content = PythonPackageContent.objects.filter(pk__in=content_set)
34
+
35
+ num_repaired = repair_metadata(content)
36
+ log.info(_("{} packages' metadata repaired.").format(num_repaired))
37
+
38
+
39
+ def repair_metadata(content: QuerySet[PythonPackageContent]) -> int:
40
+ """
41
+ Repairs metadata for a queryset of PythonPackageContent objects
42
+ and updates the progress report.
43
+
44
+ Args:
45
+ content (QuerySet[PythonPackageContent]): The queryset of items to repair.
46
+
47
+ Returns:
48
+ int: The number of packages that were repaired.
49
+ """
50
+ # TODO: Add on_demand content repair
51
+ immediate_content = content.filter(contentartifact__artifact__isnull=False)
52
+ domain = get_domain()
53
+
54
+ batch = []
55
+ set_of_update_fields = set()
56
+ total_repaired = 0
57
+
58
+ progress_report = ProgressReport(
59
+ message="Repairing packages' metadata",
60
+ code="repair.metadata",
61
+ total=immediate_content.count(),
62
+ )
63
+ progress_report.save()
64
+ with progress_report:
65
+ for package in progress_report.iter(
66
+ immediate_content.prefetch_related("_artifacts").iterator(chunk_size=1000)
67
+ ):
68
+ new_data = artifact_to_python_content_data(
69
+ package.filename, package._artifacts.get(), domain
70
+ )
71
+ changed = False
72
+ for field, value in new_data.items():
73
+ if getattr(package, field) != value:
74
+ setattr(package, field, value)
75
+ set_of_update_fields.add(field)
76
+ changed = True
77
+ if changed:
78
+ batch.append(package)
79
+ if len(batch) == 1000:
80
+ total_repaired += len(batch)
81
+ PythonPackageContent.objects.bulk_update(batch, set_of_update_fields)
82
+ batch = []
83
+ set_of_update_fields.clear()
84
+
85
+ if batch:
86
+ total_repaired += len(batch)
87
+ PythonPackageContent.objects.bulk_update(batch, set_of_update_fields)
88
+
89
+ return total_repaired
@@ -83,7 +83,7 @@ class PythonRepositoryViewSet(
83
83
  ],
84
84
  },
85
85
  {
86
- "action": ["modify"],
86
+ "action": ["modify", "repair_metadata"],
87
87
  "principal": "authenticated",
88
88
  "effect": "allow",
89
89
  "condition": [
@@ -122,6 +122,25 @@ class PythonRepositoryViewSet(
122
122
  "python.pythonrepository_viewer": ["python.view_pythonrepository"],
123
123
  }
124
124
 
125
+ @extend_schema(
126
+ summary="Repair metadata",
127
+ responses={202: AsyncOperationResponseSerializer},
128
+ )
129
+ @action(detail=True, methods=["post"], serializer_class=None)
130
+ def repair_metadata(self, request, pk):
131
+ """
132
+ Trigger an asynchronous task to repair Python metadata. This task will repair metadata
133
+ of all packages for the specified `Repository`, without creating a new `RepositoryVersion`.
134
+ """
135
+ repository = self.get_object()
136
+
137
+ result = dispatch(
138
+ tasks.repair,
139
+ exclusive_resources=[repository],
140
+ kwargs={"repository_pk": str(repository.pk)},
141
+ )
142
+ return core_viewsets.OperationPostponedResponse(result, request)
143
+
125
144
  @extend_schema(
126
145
  summary="Sync from remote",
127
146
  responses={202: AsyncOperationResponseSerializer}
@@ -1,7 +1,11 @@
1
1
  import pytest
2
2
  import subprocess
3
+ from urllib.parse import urljoin
3
4
 
4
- from pulp_python.tests.functional.constants import PYTHON_EGG_FILENAME
5
+ from pulp_python.tests.functional.constants import (
6
+ PYTHON_EGG_FILENAME,
7
+ PYTHON_FIXTURES_URL,
8
+ )
5
9
 
6
10
 
7
11
  @pytest.fixture
@@ -76,3 +80,47 @@ def test_metadata_repair_command(
76
80
  assert content.packagetype == "sdist"
77
81
  assert content.requires_python == "" # technically null
78
82
  assert content.author == "Austin Macdonald"
83
+
84
+
85
+ def test_metadata_repair_endpoint(
86
+ create_content_direct,
87
+ download_python_file,
88
+ monitor_task,
89
+ move_to_repository,
90
+ python_bindings,
91
+ python_repo,
92
+ ):
93
+ """
94
+ Test repairing of package metadata via `Repositories.repair_metadata` endpoint.
95
+ """
96
+ python_egg_filename = "scipy-1.1.0.tar.gz"
97
+ python_egg_url = urljoin(
98
+ urljoin(PYTHON_FIXTURES_URL, "packages/"), python_egg_filename
99
+ )
100
+ python_file = download_python_file(python_egg_filename, python_egg_url)
101
+
102
+ data = {
103
+ "name": "scipy",
104
+ # Wrong metadata
105
+ "author": "ME",
106
+ "packagetype": "bdist",
107
+ "requires_python": ">=3.8",
108
+ "version": "0.2",
109
+ }
110
+ content = create_content_direct(python_file, python_egg_filename, data)
111
+ for field, wrong_value in data.items():
112
+ if field == "python_version":
113
+ continue
114
+ assert getattr(content, field) == wrong_value
115
+ move_to_repository(python_repo.pulp_href, [content.pulp_href])
116
+
117
+ response = python_bindings.RepositoriesPythonApi.repair_metadata(
118
+ python_repo.pulp_href
119
+ )
120
+ monitor_task(response.task)
121
+
122
+ content = python_bindings.ContentPackagesApi.read(content.pulp_href)
123
+ assert content.version == "1.1.0"
124
+ assert content.packagetype == "sdist"
125
+ assert content.requires_python == ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
126
+ assert content.author == ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pulp-python
3
- Version: 3.14.0
3
+ Version: 3.15.1
4
4
  Summary: pulp-python plugin for the Pulp Project
5
5
  Author-email: Pulp Team <pulp-list@redhat.com>
6
6
  Project-URL: Homepage, https://pulpproject.org
@@ -48,6 +48,7 @@ pulp_python/app/pypi/serializers.py
48
48
  pulp_python/app/pypi/views.py
49
49
  pulp_python/app/tasks/__init__.py
50
50
  pulp_python/app/tasks/publish.py
51
+ pulp_python/app/tasks/repair.py
51
52
  pulp_python/app/tasks/sync.py
52
53
  pulp_python/app/tasks/upload.py
53
54
  pulp_python/app/webserver_snippets/__init__.py
@@ -7,7 +7,7 @@ build-backend = 'setuptools.build_meta'
7
7
 
8
8
  [project]
9
9
  name = "pulp-python"
10
- version = "3.14.0"
10
+ version = "3.15.1"
11
11
  description = "pulp-python plugin for the Pulp Project"
12
12
  readme = "README.md"
13
13
  authors = [
@@ -77,7 +77,7 @@ ignore = [
77
77
  [tool.bumpversion]
78
78
  # This section is managed by the plugin template. Do not edit manually.
79
79
 
80
- current_version = "3.14.0"
80
+ current_version = "3.15.1"
81
81
  commit = false
82
82
  tag = false
83
83
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<alpha>0a)?(?P<patch>\\d+)(\\.(?P<release>[a-z]+))?"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes