pulp-python 3.13.5__py3-none-any.whl → 3.13.7__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.
- pulp_python/__init__.py +1 -1
- pulp_python/app/__init__.py +5 -3
- pulp_python/app/global_access_conditions.py +0 -1
- pulp_python/app/management/commands/repair-python-metadata.py +5 -4
- pulp_python/app/modelresource.py +1 -0
- pulp_python/app/models.py +8 -7
- pulp_python/app/pypi/serializers.py +11 -9
- pulp_python/app/pypi/views.py +69 -54
- pulp_python/app/replica.py +5 -3
- pulp_python/app/serializers.py +187 -110
- pulp_python/app/settings.py +1 -1
- pulp_python/app/tasks/publish.py +24 -25
- pulp_python/app/tasks/sync.py +15 -29
- pulp_python/app/tasks/upload.py +7 -8
- pulp_python/app/urls.py +5 -5
- pulp_python/app/utils.py +52 -45
- pulp_python/app/viewsets.py +43 -46
- pulp_python/pytest_plugin.py +14 -5
- pulp_python/tests/functional/api/test_consume_content.py +1 -1
- pulp_python/tests/functional/api/test_crud_content_unit.py +8 -5
- pulp_python/tests/functional/api/test_crud_publications.py +9 -6
- pulp_python/tests/functional/api/test_crud_remotes.py +3 -2
- pulp_python/tests/functional/api/test_domains.py +10 -7
- pulp_python/tests/functional/api/test_download_content.py +2 -2
- pulp_python/tests/functional/api/test_export_import.py +7 -4
- pulp_python/tests/functional/api/test_full_mirror.py +4 -4
- pulp_python/tests/functional/api/test_pypi_apis.py +13 -38
- pulp_python/tests/functional/api/test_rbac.py +6 -5
- pulp_python/tests/functional/api/test_repair.py +3 -2
- pulp_python/tests/functional/api/test_sync.py +28 -19
- pulp_python/tests/functional/constants.py +24 -38
- pulp_python/tests/functional/utils.py +15 -6
- {pulp_python-3.13.5.dist-info → pulp_python-3.13.7.dist-info}/METADATA +1 -1
- {pulp_python-3.13.5.dist-info → pulp_python-3.13.7.dist-info}/RECORD +38 -38
- {pulp_python-3.13.5.dist-info → pulp_python-3.13.7.dist-info}/WHEEL +1 -1
- {pulp_python-3.13.5.dist-info → pulp_python-3.13.7.dist-info}/entry_points.txt +0 -0
- {pulp_python-3.13.5.dist-info → pulp_python-3.13.7.dist-info}/licenses/LICENSE +0 -0
- {pulp_python-3.13.5.dist-info → pulp_python-3.13.7.dist-info}/top_level.txt +0 -0
pulp_python/app/serializers.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from gettext import gettext as _
|
|
2
|
+
|
|
2
3
|
from django.conf import settings
|
|
3
4
|
from packaging.requirements import Requirement
|
|
4
5
|
from rest_framework import serializers
|
|
@@ -44,15 +45,14 @@ class PythonDistributionSerializer(core_serializers.DistributionSerializer):
|
|
|
44
45
|
)
|
|
45
46
|
base_url = serializers.SerializerMethodField(read_only=True)
|
|
46
47
|
allow_uploads = serializers.BooleanField(
|
|
47
|
-
default=True,
|
|
48
|
-
help_text=_("Allow packages to be uploaded to this index.")
|
|
48
|
+
default=True, help_text=_("Allow packages to be uploaded to this index.")
|
|
49
49
|
)
|
|
50
50
|
remote = core_serializers.DetailRelatedField(
|
|
51
51
|
required=False,
|
|
52
|
-
help_text=_(
|
|
52
|
+
help_text=_("Remote that can be used to fetch content when using pull-through caching."),
|
|
53
53
|
view_name_pattern=r"remotes(-.*/.*)?-detail",
|
|
54
54
|
queryset=core_models.Remote.objects.all(),
|
|
55
|
-
allow_null=True
|
|
55
|
+
allow_null=True,
|
|
56
56
|
)
|
|
57
57
|
|
|
58
58
|
def get_base_url(self, obj):
|
|
@@ -63,7 +63,9 @@ class PythonDistributionSerializer(core_serializers.DistributionSerializer):
|
|
|
63
63
|
|
|
64
64
|
class Meta:
|
|
65
65
|
fields = core_serializers.DistributionSerializer.Meta.fields + (
|
|
66
|
-
|
|
66
|
+
"publication",
|
|
67
|
+
"allow_uploads",
|
|
68
|
+
"remote",
|
|
67
69
|
)
|
|
68
70
|
model = python_models.PythonDistribution
|
|
69
71
|
|
|
@@ -74,126 +76,162 @@ class PythonPackageContentSerializer(core_serializers.SingleArtifactContentUploa
|
|
|
74
76
|
"""
|
|
75
77
|
|
|
76
78
|
filename = serializers.CharField(
|
|
77
|
-
help_text=_(
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
help_text=_(
|
|
80
|
+
"The name of the distribution package, usually of the format:"
|
|
81
|
+
" {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}"
|
|
82
|
+
"-{platform tag}.{packagetype}"
|
|
83
|
+
),
|
|
80
84
|
read_only=True,
|
|
81
85
|
)
|
|
82
86
|
packagetype = serializers.CharField(
|
|
83
|
-
help_text=_(
|
|
84
|
-
|
|
87
|
+
help_text=_(
|
|
88
|
+
"The type of the distribution package (e.g. sdist, bdist_wheel, bdist_egg, etc)"
|
|
89
|
+
),
|
|
85
90
|
read_only=True,
|
|
86
91
|
)
|
|
87
92
|
name = serializers.CharField(
|
|
88
|
-
help_text=_(
|
|
93
|
+
help_text=_("The name of the python project."),
|
|
89
94
|
read_only=True,
|
|
90
95
|
)
|
|
91
96
|
version = serializers.CharField(
|
|
92
|
-
help_text=_(
|
|
97
|
+
help_text=_("The packages version number."),
|
|
93
98
|
read_only=True,
|
|
94
99
|
)
|
|
95
100
|
sha256 = serializers.CharField(
|
|
96
|
-
default=
|
|
97
|
-
help_text=_(
|
|
101
|
+
default="",
|
|
102
|
+
help_text=_("The SHA256 digest of this package."),
|
|
98
103
|
)
|
|
99
104
|
metadata_version = serializers.CharField(
|
|
100
|
-
help_text=_(
|
|
105
|
+
help_text=_("Version of the file format"),
|
|
101
106
|
read_only=True,
|
|
102
107
|
)
|
|
103
108
|
summary = serializers.CharField(
|
|
104
|
-
required=False,
|
|
105
|
-
|
|
109
|
+
required=False,
|
|
110
|
+
allow_blank=True,
|
|
111
|
+
help_text=_("A one-line summary of what the package does."),
|
|
106
112
|
)
|
|
107
113
|
description = serializers.CharField(
|
|
108
|
-
required=False,
|
|
109
|
-
|
|
114
|
+
required=False,
|
|
115
|
+
allow_blank=True,
|
|
116
|
+
help_text=_("A longer description of the package that can run to several paragraphs."),
|
|
110
117
|
)
|
|
111
118
|
description_content_type = serializers.CharField(
|
|
112
|
-
required=False,
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
required=False,
|
|
120
|
+
allow_blank=True,
|
|
121
|
+
help_text=_(
|
|
122
|
+
"A string stating the markup syntax (if any) used in the distribution’s"
|
|
123
|
+
" description, so that tools can intelligently render the description."
|
|
124
|
+
),
|
|
115
125
|
)
|
|
116
126
|
keywords = serializers.CharField(
|
|
117
|
-
required=False,
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
required=False,
|
|
128
|
+
allow_blank=True,
|
|
129
|
+
help_text=_(
|
|
130
|
+
"Additional keywords to be used to assist searching for the "
|
|
131
|
+
"package in a larger catalog."
|
|
132
|
+
),
|
|
120
133
|
)
|
|
121
134
|
home_page = serializers.CharField(
|
|
122
|
-
required=False, allow_blank=True,
|
|
123
|
-
help_text=_('The URL for the package\'s home page.')
|
|
135
|
+
required=False, allow_blank=True, help_text=_("The URL for the package's home page.")
|
|
124
136
|
)
|
|
125
137
|
download_url = serializers.CharField(
|
|
126
|
-
required=False,
|
|
127
|
-
|
|
138
|
+
required=False,
|
|
139
|
+
allow_blank=True,
|
|
140
|
+
help_text=_("Legacy field denoting the URL from which this package can be downloaded."),
|
|
128
141
|
)
|
|
129
142
|
author = serializers.CharField(
|
|
130
|
-
required=False,
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
required=False,
|
|
144
|
+
allow_blank=True,
|
|
145
|
+
help_text=_(
|
|
146
|
+
"Text containing the author's name. Contact information can also be added,"
|
|
147
|
+
" separated with newlines."
|
|
148
|
+
),
|
|
133
149
|
)
|
|
134
150
|
author_email = serializers.CharField(
|
|
135
|
-
required=False, allow_blank=True,
|
|
136
|
-
help_text=_('The author\'s e-mail address. ')
|
|
151
|
+
required=False, allow_blank=True, help_text=_("The author's e-mail address. ")
|
|
137
152
|
)
|
|
138
153
|
maintainer = serializers.CharField(
|
|
139
|
-
required=False,
|
|
140
|
-
|
|
141
|
-
|
|
154
|
+
required=False,
|
|
155
|
+
allow_blank=True,
|
|
156
|
+
help_text=_(
|
|
157
|
+
"The maintainer's name at a minimum; additional contact information may be provided."
|
|
158
|
+
),
|
|
142
159
|
)
|
|
143
160
|
maintainer_email = serializers.CharField(
|
|
144
|
-
required=False, allow_blank=True,
|
|
145
|
-
help_text=_('The maintainer\'s e-mail address.')
|
|
161
|
+
required=False, allow_blank=True, help_text=_("The maintainer's e-mail address.")
|
|
146
162
|
)
|
|
147
163
|
license = serializers.CharField(
|
|
148
|
-
required=False,
|
|
149
|
-
|
|
164
|
+
required=False,
|
|
165
|
+
allow_blank=True,
|
|
166
|
+
help_text=_("Text indicating the license covering the distribution"),
|
|
150
167
|
)
|
|
151
168
|
requires_python = serializers.CharField(
|
|
152
|
-
required=False,
|
|
153
|
-
|
|
154
|
-
|
|
169
|
+
required=False,
|
|
170
|
+
allow_blank=True,
|
|
171
|
+
help_text=_(
|
|
172
|
+
"The Python version(s) that the distribution is guaranteed to be compatible with."
|
|
173
|
+
),
|
|
155
174
|
)
|
|
156
175
|
project_url = serializers.CharField(
|
|
157
|
-
required=False,
|
|
158
|
-
|
|
176
|
+
required=False,
|
|
177
|
+
allow_blank=True,
|
|
178
|
+
help_text=_("A browsable URL for the project and a label for it, separated by a comma."),
|
|
159
179
|
)
|
|
160
180
|
project_urls = serializers.JSONField(
|
|
161
|
-
required=False,
|
|
162
|
-
|
|
181
|
+
required=False,
|
|
182
|
+
default=dict,
|
|
183
|
+
help_text=_("A dictionary of labels and URLs for the project."),
|
|
163
184
|
)
|
|
164
185
|
platform = serializers.CharField(
|
|
165
|
-
required=False,
|
|
166
|
-
|
|
167
|
-
|
|
186
|
+
required=False,
|
|
187
|
+
allow_blank=True,
|
|
188
|
+
help_text=_(
|
|
189
|
+
"A comma-separated list of platform specifications, "
|
|
190
|
+
"summarizing the operating systems supported by the package."
|
|
191
|
+
),
|
|
168
192
|
)
|
|
169
193
|
supported_platform = serializers.CharField(
|
|
170
|
-
required=False,
|
|
171
|
-
|
|
194
|
+
required=False,
|
|
195
|
+
allow_blank=True,
|
|
196
|
+
help_text=_("Field to specify the OS and CPU for which the binary package was compiled. "),
|
|
172
197
|
)
|
|
173
198
|
requires_dist = serializers.JSONField(
|
|
174
|
-
required=False,
|
|
175
|
-
|
|
176
|
-
|
|
199
|
+
required=False,
|
|
200
|
+
default=list,
|
|
201
|
+
help_text=_(
|
|
202
|
+
"A JSON list containing names of some other distutils project "
|
|
203
|
+
"required by this distribution."
|
|
204
|
+
),
|
|
177
205
|
)
|
|
178
206
|
provides_dist = serializers.JSONField(
|
|
179
|
-
required=False,
|
|
180
|
-
|
|
181
|
-
|
|
207
|
+
required=False,
|
|
208
|
+
default=list,
|
|
209
|
+
help_text=_(
|
|
210
|
+
"A JSON list containing names of a Distutils project which is contained"
|
|
211
|
+
" within this distribution."
|
|
212
|
+
),
|
|
182
213
|
)
|
|
183
214
|
obsoletes_dist = serializers.JSONField(
|
|
184
|
-
required=False,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
215
|
+
required=False,
|
|
216
|
+
default=list,
|
|
217
|
+
help_text=_(
|
|
218
|
+
"A JSON list containing names of a distutils project's distribution which "
|
|
219
|
+
"this distribution renders obsolete, meaning that the two projects should not "
|
|
220
|
+
"be installed at the same time."
|
|
221
|
+
),
|
|
188
222
|
)
|
|
189
223
|
requires_external = serializers.JSONField(
|
|
190
|
-
required=False,
|
|
191
|
-
|
|
192
|
-
|
|
224
|
+
required=False,
|
|
225
|
+
default=list,
|
|
226
|
+
help_text=_(
|
|
227
|
+
"A JSON list containing some dependency in the system that the distribution "
|
|
228
|
+
"is to be used."
|
|
229
|
+
),
|
|
193
230
|
)
|
|
194
231
|
classifiers = serializers.JSONField(
|
|
195
|
-
required=False,
|
|
196
|
-
|
|
232
|
+
required=False,
|
|
233
|
+
default=list,
|
|
234
|
+
help_text=_("A JSON list containing classification values for a Python package."),
|
|
197
235
|
)
|
|
198
236
|
|
|
199
237
|
def deferred_validate(self, data):
|
|
@@ -212,22 +250,26 @@ class PythonPackageContentSerializer(core_serializers.SingleArtifactContentUploa
|
|
|
212
250
|
try:
|
|
213
251
|
filename = data["relative_path"]
|
|
214
252
|
except KeyError:
|
|
215
|
-
raise serializers.ValidationError(detail={"relative_path": _(
|
|
253
|
+
raise serializers.ValidationError(detail={"relative_path": _("This field is required")})
|
|
216
254
|
|
|
217
255
|
artifact = data["artifact"]
|
|
218
256
|
try:
|
|
219
257
|
_data = artifact_to_python_content_data(filename, artifact, domain=get_domain())
|
|
220
258
|
except ValueError:
|
|
221
|
-
raise serializers.ValidationError(
|
|
222
|
-
|
|
223
|
-
|
|
259
|
+
raise serializers.ValidationError(
|
|
260
|
+
_(
|
|
261
|
+
"Extension on {} is not a valid python extension "
|
|
262
|
+
"(.whl, .exe, .egg, .tar.gz, .tar.bz2, .zip)"
|
|
263
|
+
).format(filename)
|
|
224
264
|
)
|
|
225
265
|
|
|
226
266
|
if data.get("sha256") and data["sha256"] != artifact.sha256:
|
|
227
267
|
raise serializers.ValidationError(
|
|
228
|
-
detail={
|
|
229
|
-
"
|
|
230
|
-
|
|
268
|
+
detail={
|
|
269
|
+
"sha256": _(
|
|
270
|
+
"The uploaded artifact's sha256 checksum does not match the one provided"
|
|
271
|
+
)
|
|
272
|
+
}
|
|
231
273
|
)
|
|
232
274
|
|
|
233
275
|
data.update(_data)
|
|
@@ -242,11 +284,33 @@ class PythonPackageContentSerializer(core_serializers.SingleArtifactContentUploa
|
|
|
242
284
|
|
|
243
285
|
class Meta:
|
|
244
286
|
fields = core_serializers.SingleArtifactContentUploadSerializer.Meta.fields + (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
287
|
+
"filename",
|
|
288
|
+
"packagetype",
|
|
289
|
+
"name",
|
|
290
|
+
"version",
|
|
291
|
+
"sha256",
|
|
292
|
+
"metadata_version",
|
|
293
|
+
"summary",
|
|
294
|
+
"description",
|
|
295
|
+
"description_content_type",
|
|
296
|
+
"keywords",
|
|
297
|
+
"home_page",
|
|
298
|
+
"download_url",
|
|
299
|
+
"author",
|
|
300
|
+
"author_email",
|
|
301
|
+
"maintainer",
|
|
302
|
+
"maintainer_email",
|
|
303
|
+
"license",
|
|
304
|
+
"requires_python",
|
|
305
|
+
"project_url",
|
|
306
|
+
"project_urls",
|
|
307
|
+
"platform",
|
|
308
|
+
"supported_platform",
|
|
309
|
+
"requires_dist",
|
|
310
|
+
"provides_dist",
|
|
311
|
+
"obsoletes_dist",
|
|
312
|
+
"requires_external",
|
|
313
|
+
"classifiers",
|
|
250
314
|
)
|
|
251
315
|
model = python_models.PythonPackageContent
|
|
252
316
|
|
|
@@ -258,7 +322,11 @@ class MinimalPythonPackageContentSerializer(PythonPackageContentSerializer):
|
|
|
258
322
|
|
|
259
323
|
class Meta:
|
|
260
324
|
fields = core_serializers.SingleArtifactContentUploadSerializer.Meta.fields + (
|
|
261
|
-
|
|
325
|
+
"filename",
|
|
326
|
+
"packagetype",
|
|
327
|
+
"name",
|
|
328
|
+
"version",
|
|
329
|
+
"sha256",
|
|
262
330
|
)
|
|
263
331
|
model = python_models.PythonPackageContent
|
|
264
332
|
|
|
@@ -282,47 +350,49 @@ class PythonRemoteSerializer(core_serializers.RemoteSerializer):
|
|
|
282
350
|
child=serializers.CharField(allow_blank=False),
|
|
283
351
|
required=False,
|
|
284
352
|
allow_empty=True,
|
|
285
|
-
help_text=_(
|
|
286
|
-
"A list containing project specifiers for Python packages to include."
|
|
287
|
-
),
|
|
353
|
+
help_text=_("A list containing project specifiers for Python packages to include."),
|
|
288
354
|
)
|
|
289
355
|
excludes = serializers.ListField(
|
|
290
356
|
child=serializers.CharField(allow_blank=False),
|
|
291
357
|
required=False,
|
|
292
358
|
allow_empty=True,
|
|
293
|
-
help_text=_(
|
|
294
|
-
"A list containing project specifiers for Python packages to exclude."
|
|
295
|
-
),
|
|
359
|
+
help_text=_("A list containing project specifiers for Python packages to exclude."),
|
|
296
360
|
)
|
|
297
361
|
prereleases = serializers.BooleanField(
|
|
298
|
-
required=False,
|
|
299
|
-
help_text=_('Whether or not to include pre-release packages in the sync.')
|
|
362
|
+
required=False, help_text=_("Whether or not to include pre-release packages in the sync.")
|
|
300
363
|
)
|
|
301
364
|
policy = serializers.ChoiceField(
|
|
302
|
-
help_text=_(
|
|
303
|
-
|
|
365
|
+
help_text=_(
|
|
366
|
+
"The policy to use when downloading content. The possible values include: "
|
|
367
|
+
"'immediate', 'on_demand', and 'streamed'. 'on_demand' is the default."
|
|
368
|
+
),
|
|
304
369
|
choices=core_models.Remote.POLICY_CHOICES,
|
|
305
|
-
default=core_models.Remote.ON_DEMAND
|
|
370
|
+
default=core_models.Remote.ON_DEMAND,
|
|
306
371
|
)
|
|
307
372
|
package_types = MultipleChoiceArrayField(
|
|
308
373
|
required=False,
|
|
309
|
-
help_text=_(
|
|
310
|
-
|
|
374
|
+
help_text=_(
|
|
375
|
+
"The package types to sync for Python content. Leave blank to get everypackage type."
|
|
376
|
+
),
|
|
311
377
|
choices=python_models.PACKAGE_TYPES,
|
|
312
|
-
default=list
|
|
378
|
+
default=list,
|
|
313
379
|
)
|
|
314
380
|
keep_latest_packages = serializers.IntegerField(
|
|
315
381
|
required=False,
|
|
316
|
-
help_text=_(
|
|
317
|
-
|
|
318
|
-
|
|
382
|
+
help_text=_(
|
|
383
|
+
"The amount of latest versions of a package to keep on sync, includes"
|
|
384
|
+
"pre-releases if synced. Default 0 keeps all versions."
|
|
385
|
+
),
|
|
386
|
+
default=0,
|
|
319
387
|
)
|
|
320
388
|
exclude_platforms = MultipleChoiceArrayField(
|
|
321
389
|
required=False,
|
|
322
|
-
help_text=_(
|
|
323
|
-
|
|
390
|
+
help_text=_(
|
|
391
|
+
"List of platforms to exclude syncing Python packages for. Possible values"
|
|
392
|
+
"include: windows, macos, freebsd, and linux."
|
|
393
|
+
),
|
|
324
394
|
choices=python_models.PLATFORMS,
|
|
325
|
-
default=list
|
|
395
|
+
default=list,
|
|
326
396
|
)
|
|
327
397
|
|
|
328
398
|
def validate_includes(self, value):
|
|
@@ -332,7 +402,7 @@ class PythonRemoteSerializer(core_serializers.RemoteSerializer):
|
|
|
332
402
|
Requirement(pkg)
|
|
333
403
|
except ValueError as ve:
|
|
334
404
|
raise serializers.ValidationError(
|
|
335
|
-
_("includes specifier {} is invalid. {}".format(pkg, ve)
|
|
405
|
+
_("includes specifier {} is invalid. {}").format(pkg, ve)
|
|
336
406
|
)
|
|
337
407
|
return value
|
|
338
408
|
|
|
@@ -343,13 +413,17 @@ class PythonRemoteSerializer(core_serializers.RemoteSerializer):
|
|
|
343
413
|
Requirement(pkg)
|
|
344
414
|
except ValueError as ve:
|
|
345
415
|
raise serializers.ValidationError(
|
|
346
|
-
_("excludes specifier {} is invalid. {}".format(pkg, ve)
|
|
416
|
+
_("excludes specifier {} is invalid. {}").format(pkg, ve)
|
|
347
417
|
)
|
|
348
418
|
return value
|
|
349
419
|
|
|
350
420
|
class Meta:
|
|
351
421
|
fields = core_serializers.RemoteSerializer.Meta.fields + (
|
|
352
|
-
"includes",
|
|
422
|
+
"includes",
|
|
423
|
+
"excludes",
|
|
424
|
+
"prereleases",
|
|
425
|
+
"package_types",
|
|
426
|
+
"keep_latest_packages",
|
|
353
427
|
"exclude_platforms",
|
|
354
428
|
)
|
|
355
429
|
model = python_models.PythonRemote
|
|
@@ -371,10 +445,12 @@ class PythonBanderRemoteSerializer(serializers.Serializer):
|
|
|
371
445
|
)
|
|
372
446
|
|
|
373
447
|
policy = serializers.ChoiceField(
|
|
374
|
-
help_text=_(
|
|
375
|
-
|
|
448
|
+
help_text=_(
|
|
449
|
+
"The policy to use when downloading content. The possible values include: "
|
|
450
|
+
"'immediate', 'on_demand', and 'streamed'. 'on_demand' is the default."
|
|
451
|
+
),
|
|
376
452
|
choices=core_models.Remote.POLICY_CHOICES,
|
|
377
|
-
default=core_models.Remote.ON_DEMAND
|
|
453
|
+
default=core_models.Remote.ON_DEMAND,
|
|
378
454
|
)
|
|
379
455
|
|
|
380
456
|
|
|
@@ -384,8 +460,9 @@ class PythonPublicationSerializer(core_serializers.PublicationSerializer):
|
|
|
384
460
|
"""
|
|
385
461
|
|
|
386
462
|
distributions = core_serializers.DetailRelatedField(
|
|
387
|
-
help_text=_(
|
|
388
|
-
|
|
463
|
+
help_text=_(
|
|
464
|
+
"This publication is currently being hosted as configured by these distributions."
|
|
465
|
+
),
|
|
389
466
|
source="distribution_set",
|
|
390
467
|
view_name="pythondistributions-detail",
|
|
391
468
|
many=True,
|
|
@@ -393,5 +470,5 @@ class PythonPublicationSerializer(core_serializers.PublicationSerializer):
|
|
|
393
470
|
)
|
|
394
471
|
|
|
395
472
|
class Meta:
|
|
396
|
-
fields = core_serializers.PublicationSerializer.Meta.fields + (
|
|
473
|
+
fields = core_serializers.PublicationSerializer.Meta.fields + ("distributions",)
|
|
397
474
|
model = python_models.PythonPublication
|
pulp_python/app/settings.py
CHANGED
pulp_python/app/tasks/publish.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from gettext import gettext as _
|
|
2
1
|
import logging
|
|
3
2
|
import os
|
|
3
|
+
from gettext import gettext as _
|
|
4
4
|
|
|
5
5
|
from django.core.files import File
|
|
6
6
|
from packaging.utils import canonicalize_name
|
|
@@ -9,8 +9,7 @@ from pulpcore.plugin import models
|
|
|
9
9
|
from pulpcore.plugin.util import get_domain
|
|
10
10
|
|
|
11
11
|
from pulp_python.app import models as python_models
|
|
12
|
-
from pulp_python.app.utils import
|
|
13
|
-
|
|
12
|
+
from pulp_python.app.utils import write_simple_detail, write_simple_index
|
|
14
13
|
|
|
15
14
|
log = logging.getLogger(__name__)
|
|
16
15
|
|
|
@@ -25,15 +24,17 @@ def publish(repository_version_pk):
|
|
|
25
24
|
"""
|
|
26
25
|
repository_version = models.RepositoryVersion.objects.get(pk=repository_version_pk)
|
|
27
26
|
|
|
28
|
-
log.info(
|
|
29
|
-
repo=
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
log.info(
|
|
28
|
+
_("Publishing: repository={repo}, version={version}").format(
|
|
29
|
+
repo=repository_version.repository.name,
|
|
30
|
+
version=repository_version.number,
|
|
31
|
+
)
|
|
32
|
+
)
|
|
32
33
|
|
|
33
34
|
with python_models.PythonPublication.create(repository_version, pass_through=True) as pub:
|
|
34
35
|
write_simple_api(pub)
|
|
35
36
|
|
|
36
|
-
log.info(_(
|
|
37
|
+
log.info(_("Publication: {pk} created").format(pk=pub.pk))
|
|
37
38
|
return pub
|
|
38
39
|
|
|
39
40
|
|
|
@@ -51,26 +52,24 @@ def write_simple_api(publication):
|
|
|
51
52
|
|
|
52
53
|
"""
|
|
53
54
|
domain = get_domain()
|
|
54
|
-
simple_dir =
|
|
55
|
+
simple_dir = "simple/"
|
|
55
56
|
os.mkdir(simple_dir)
|
|
56
57
|
project_names = (
|
|
57
58
|
python_models.PythonPackageContent.objects.filter(
|
|
58
59
|
pk__in=publication.repository_version.content, _pulp_domain=domain
|
|
59
60
|
)
|
|
60
|
-
.order_by(
|
|
61
|
-
.values_list(
|
|
61
|
+
.order_by("name")
|
|
62
|
+
.values_list("name", flat=True)
|
|
62
63
|
.distinct()
|
|
63
64
|
)
|
|
64
65
|
|
|
65
66
|
# write the root index, which lists all of the projects for which there is a package available
|
|
66
|
-
index_path =
|
|
67
|
-
with open(index_path,
|
|
67
|
+
index_path = "{simple_dir}index.html".format(simple_dir=simple_dir)
|
|
68
|
+
with open(index_path, "w") as index:
|
|
68
69
|
index.write(write_simple_index(project_names))
|
|
69
70
|
|
|
70
71
|
index_metadata = models.PublishedMetadata.create_from_file(
|
|
71
|
-
relative_path=index_path,
|
|
72
|
-
publication=publication,
|
|
73
|
-
file=File(open(index_path, 'rb'))
|
|
72
|
+
relative_path=index_path, publication=publication, file=File(open(index_path, "rb"))
|
|
74
73
|
)
|
|
75
74
|
index_metadata.save()
|
|
76
75
|
|
|
@@ -86,41 +85,41 @@ def write_simple_api(publication):
|
|
|
86
85
|
current_name = project_names[ind]
|
|
87
86
|
package_releases = []
|
|
88
87
|
for release in releases.iterator():
|
|
89
|
-
if release[
|
|
88
|
+
if release["name"] != current_name:
|
|
90
89
|
write_project_page(
|
|
91
90
|
name=canonicalize_name(current_name),
|
|
92
91
|
simple_dir=simple_dir,
|
|
93
92
|
package_releases=package_releases,
|
|
94
|
-
publication=publication
|
|
93
|
+
publication=publication,
|
|
95
94
|
)
|
|
96
95
|
package_releases = []
|
|
97
96
|
ind += 1
|
|
98
97
|
current_name = project_names[ind]
|
|
99
|
-
relative_path = release[
|
|
98
|
+
relative_path = release["filename"]
|
|
100
99
|
path = f"../../{relative_path}"
|
|
101
|
-
checksum = release[
|
|
100
|
+
checksum = release["sha256"]
|
|
102
101
|
package_releases.append((relative_path, path, checksum))
|
|
103
102
|
# Write the final project's page
|
|
104
103
|
write_project_page(
|
|
105
104
|
name=canonicalize_name(current_name),
|
|
106
105
|
simple_dir=simple_dir,
|
|
107
106
|
package_releases=package_releases,
|
|
108
|
-
publication=publication
|
|
107
|
+
publication=publication,
|
|
109
108
|
)
|
|
110
109
|
|
|
111
110
|
|
|
112
111
|
def write_project_page(name, simple_dir, package_releases, publication):
|
|
113
112
|
"""Writes a project's simple page."""
|
|
114
|
-
project_dir = f
|
|
113
|
+
project_dir = f"{simple_dir}{name}/"
|
|
115
114
|
os.mkdir(project_dir)
|
|
116
|
-
metadata_relative_path = f
|
|
115
|
+
metadata_relative_path = f"{project_dir}index.html"
|
|
117
116
|
|
|
118
|
-
with open(metadata_relative_path,
|
|
117
|
+
with open(metadata_relative_path, "w") as simple_metadata:
|
|
119
118
|
simple_metadata.write(write_simple_detail(name, package_releases))
|
|
120
119
|
|
|
121
120
|
project_metadata = models.PublishedMetadata.create_from_file(
|
|
122
121
|
relative_path=metadata_relative_path,
|
|
123
122
|
publication=publication,
|
|
124
|
-
file=File(open(metadata_relative_path,
|
|
123
|
+
file=File(open(metadata_relative_path, "rb")),
|
|
125
124
|
)
|
|
126
125
|
project_metadata.save() # change to bulk create when multi-table supported
|