devpi-server 6.11.0__tar.gz → 6.12.0__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 (131) hide show
  1. {devpi_server-6.11.0 → devpi_server-6.12.0}/CHANGELOG +19 -0
  2. {devpi_server-6.11.0 → devpi_server-6.12.0}/CHANGELOG.short.rst +21 -19
  3. {devpi_server-6.11.0 → devpi_server-6.12.0}/PKG-INFO +20 -26
  4. devpi_server-6.12.0/devpi_server/__init__.py +1 -0
  5. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/hookspecs.py +31 -1
  6. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/views.py +48 -7
  7. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server.egg-info/PKG-INFO +20 -26
  8. {devpi_server-6.11.0 → devpi_server-6.12.0}/setup.py +1 -1
  9. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/functional.py +23 -0
  10. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_views.py +58 -0
  11. devpi_server-6.11.0/devpi_server/__init__.py +0 -1
  12. {devpi_server-6.11.0 → devpi_server-6.12.0}/.flake8 +0 -0
  13. {devpi_server-6.11.0 → devpi_server-6.12.0}/AUTHORS +0 -0
  14. {devpi_server-6.11.0 → devpi_server-6.12.0}/LICENSE +0 -0
  15. {devpi_server-6.11.0 → devpi_server-6.12.0}/MANIFEST.in +0 -0
  16. {devpi_server-6.11.0 → devpi_server-6.12.0}/README.rst +0 -0
  17. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/__main__.py +0 -0
  18. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/auth.py +0 -0
  19. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/auth_basic.py +0 -0
  20. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/auth_devpi.py +0 -0
  21. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/__init__.py +0 -0
  22. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/crontab.template +0 -0
  23. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/devpi.service.template +0 -0
  24. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/launchd-macos.txt.template +0 -0
  25. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/nginx-devpi-caching-http.conf.template +0 -0
  26. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/nginx-devpi-caching-location.conf.template +0 -0
  27. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/nginx-devpi-caching-proxy.conf.template +0 -0
  28. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/nginx-devpi-caching-server.conf.template +0 -0
  29. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/nginx-devpi.conf.template +0 -0
  30. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/supervisor-devpi.conf.template +0 -0
  31. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/supervisord.conf.template +0 -0
  32. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/cfg/windows-service.txt.template +0 -0
  33. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/config.py +0 -0
  34. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/exceptions.py +0 -0
  35. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/filestore.py +0 -0
  36. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/filestore_fs.py +0 -0
  37. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/fileutil.py +0 -0
  38. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/fsck.py +0 -0
  39. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/genconfig.py +0 -0
  40. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/importexport.py +0 -0
  41. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/init.py +0 -0
  42. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/interfaces.py +0 -0
  43. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/keyfs.py +0 -0
  44. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/keyfs_sqlite.py +0 -0
  45. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/keyfs_sqlite_fs.py +0 -0
  46. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/keyfs_types.py +0 -0
  47. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/log.py +0 -0
  48. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/main.py +0 -0
  49. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/markers.py +0 -0
  50. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/middleware.py +0 -0
  51. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/mirror.py +0 -0
  52. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/model.py +0 -0
  53. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/mythread.py +0 -0
  54. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/passwd.py +0 -0
  55. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/readonly.py +0 -0
  56. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/replica.py +0 -0
  57. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/sizeof.py +0 -0
  58. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/vendor/__init__.py +0 -0
  59. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/vendor/_pip.py +0 -0
  60. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server/view_auth.py +0 -0
  61. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server.egg-info/SOURCES.txt +0 -0
  62. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server.egg-info/dependency_links.txt +0 -0
  63. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server.egg-info/entry_points.txt +0 -0
  64. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server.egg-info/not-zip-safe +0 -0
  65. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server.egg-info/requires.txt +0 -0
  66. {devpi_server-6.11.0 → devpi_server-6.12.0}/devpi_server.egg-info/top_level.txt +0 -0
  67. {devpi_server-6.11.0 → devpi_server-6.12.0}/pyproject.toml +0 -0
  68. {devpi_server-6.11.0 → devpi_server-6.12.0}/pytest_devpi_server/__init__.py +0 -0
  69. {devpi_server-6.11.0 → devpi_server-6.12.0}/setup.cfg +0 -0
  70. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/__init__.py +0 -0
  71. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/conftest.py +0 -0
  72. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/example.py +0 -0
  73. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/badindexname/dataindex.json +0 -0
  74. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/badusername/dataindex.json +0 -0
  75. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/basescycle/dataindex.json +0 -0
  76. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/createdmodified/dataindex.json +0 -0
  77. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/deletedbase/dataindex.json +0 -0
  78. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/mirrordata/dataindex.json +0 -0
  79. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/mirrordata/root/pypi/dddttt/0.1.dev1/dddttt-0.1.dev1.tar.gz +0 -0
  80. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/modifiedpypi/dataindex.json +0 -0
  81. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/nocreatedmodified/dataindex.json +0 -0
  82. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/normalization/dataindex.json +0 -0
  83. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/normalization/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  84. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/normalization_merge/dataindex.json +0 -0
  85. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello-pkg/hello.pkg-1.1.tar.gz +0 -0
  86. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/normalization_merge/root/dev/hello.pkg/hello.pkg-1.0.tar.gz +0 -0
  87. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/norootpypi/dataindex.json +0 -0
  88. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/nouser/dataindex.json +0 -0
  89. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/removedindexplugin/dataindex.json +0 -0
  90. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/removedindexplugin/user/dev/pkg/pkg-1.0.tar.gz +0 -0
  91. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/dataindex.json +0 -0
  92. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/0.9/hello-0.9.tar.gz +0 -0
  93. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/toxresult_naming_scheme/root/dev/hello/sha256=ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73/hello-0.9.tar.gz.toxresult0 +0 -0
  94. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/importexportdata/toxresult_upload_default/dataindex.json +0 -0
  95. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/plugin.py +0 -0
  96. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/reqmock.py +0 -0
  97. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/simpypi.py +0 -0
  98. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_auth.py +0 -0
  99. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_authcheck.py +0 -0
  100. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_config.py +0 -0
  101. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_conftest.py +0 -0
  102. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_filestore.py +0 -0
  103. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_fileutil.py +0 -0
  104. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_fsck.py +0 -0
  105. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_genconfig.py +0 -0
  106. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_importexport.py +0 -0
  107. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_keyfs.py +0 -0
  108. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_keyfs_sqlite_fs.py +0 -0
  109. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_log.py +0 -0
  110. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_main.py +0 -0
  111. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_mirror.py +0 -0
  112. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_mirror_no_project_list.py +0 -0
  113. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_model.py +0 -0
  114. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_mythread.py +0 -0
  115. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_nginx.py +0 -0
  116. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_nginx_replica.py +0 -0
  117. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_permissions.py +0 -0
  118. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_readonly.py +0 -0
  119. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_replica.py +0 -0
  120. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_replica_functional.py +0 -0
  121. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_reqmock.py +0 -0
  122. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_stage_customizer.py +0 -0
  123. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_streaming.py +0 -0
  124. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_streaming_nginx.py +0 -0
  125. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_streaming_replica.py +0 -0
  126. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_streaming_replica_nginx.py +0 -0
  127. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_view_auth.py +0 -0
  128. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_views_patch.py +0 -0
  129. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_views_push_external.py +0 -0
  130. {devpi_server-6.11.0 → devpi_server-6.12.0}/test_devpi_server/test_views_status.py +0 -0
  131. {devpi_server-6.11.0 → devpi_server-6.12.0}/tox.ini +0 -0
@@ -2,6 +2,25 @@
2
2
 
3
3
  .. towncrier release notes start
4
4
 
5
+ 6.12.0 (2024-06-25)
6
+ ===================
7
+
8
+ Features
9
+ --------
10
+
11
+ - Added ``devpiserver_on_toxresult_store`` hook to allow blocking or skipping a toxresult upload on more specific conditions as ``acl_toxresult_upload`` would allow.
12
+
13
+ - Added ``devpiserver_on_toxresult_upload_forbidden`` hook to allow returning a custom message and result (403 or 200).
14
+
15
+
16
+
17
+ Bug Fixes
18
+ ---------
19
+
20
+ - Return json data if toxresult upload is forbidden.
21
+
22
+
23
+
5
24
  6.11.0 (2024-04-20)
6
25
  ===================
7
26
 
@@ -9,6 +9,27 @@ Changelog
9
9
 
10
10
  .. towncrier release notes start
11
11
 
12
+ 6.11.0 (2024-04-20)
13
+ ===================
14
+
15
+ Features
16
+ --------
17
+
18
+ - The ``devpi-fsck`` script now returns an error code when there have been missing files or checksum errors.
19
+
20
+ - Fix #983: Add plugin hook for mirror authentication header.
21
+
22
+
23
+
24
+ Bug Fixes
25
+ ---------
26
+
27
+ - Preserve last modified of docs and toxresults during export/import.
28
+
29
+ - Fix #1033: Use ``int`` for ``--mirror-cache-expiry`` to fix value of ``proxy_cache_valid`` in nginx caching config.
30
+
31
+
32
+
12
33
  6.10.0 (2023-12-19)
13
34
  ===================
14
35
 
@@ -74,22 +95,3 @@ Bug Fixes
74
95
 
75
96
  - Fix wrong hash metadata introduced in 6.5.0 for toxresults which prevents replication. The metadata can be fixed by an export/import cycle.
76
97
 
77
-
78
- 6.8.0 (2022-12-05)
79
- ==================
80
-
81
- Features
82
- --------
83
-
84
- - Fix #929: Cache normalized project names per transaction on mirror index instances.
85
-
86
-
87
- Bug Fixes
88
- ---------
89
-
90
- - Fix #914: add locking to list_projects_perstage of mirror indexes to prevent multiple slow concurrent updates of the full project name list.
91
-
92
- - Catch exceptions in async_httpget analog to httpget.
93
-
94
- - Add locking to mirror name cache to prevent race condition on updates.
95
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devpi-server
3
- Version: 6.11.0
3
+ Version: 6.12.0
4
4
  Summary: devpi-server: reliable private and pypi.org caching server
5
5
  Home-page: https://devpi.net
6
6
  Maintainer: Florian Schulze
@@ -119,6 +119,25 @@ Changelog
119
119
 
120
120
  .. towncrier release notes start
121
121
 
122
+ 6.12.0 (2024-06-25)
123
+ ===================
124
+
125
+ Features
126
+ --------
127
+
128
+ - Added ``devpiserver_on_toxresult_store`` hook to allow blocking or skipping a toxresult upload on more specific conditions as ``acl_toxresult_upload`` would allow.
129
+
130
+ - Added ``devpiserver_on_toxresult_upload_forbidden`` hook to allow returning a custom message and result (403 or 200).
131
+
132
+
133
+
134
+ Bug Fixes
135
+ ---------
136
+
137
+ - Return json data if toxresult upload is forbidden.
138
+
139
+
140
+
122
141
  6.11.0 (2024-04-20)
123
142
  ===================
124
143
 
@@ -180,28 +199,3 @@ Bug Fixes
180
199
 
181
200
  - Fix #980: Remove long deprecated backward compatibility for old pluggy versions to fix error with pluggy 1.1.0.
182
201
 
183
-
184
- 6.9.0 (2023-05-23)
185
- ==================
186
-
187
- Features
188
- --------
189
-
190
- - Support export directory layout for ``--replica-file-search-path`` option.
191
-
192
- - Fix #931: Add ``mirror_no_project_list`` setting for mirror indexes that have no full project list like google cloud artifacts or if you want to prevent downloading the full list for huge indexes like PyPI.
193
-
194
-
195
- Bug Fixes
196
- ---------
197
-
198
- - Keep a reference to async tasks to avoid their removal mid execution.
199
-
200
- - Support changed default of ``enforce_content_length`` in urllib3 >= 2.
201
-
202
- - Fix #934: Properly set PATH_INFO when outside URL is used with sub-path.
203
-
204
- - Fix #945: Adapt FatalError to be usable as an async HTTP response when updating a project on a mirror.
205
-
206
- - Fix wrong hash metadata introduced in 6.5.0 for toxresults which prevents replication. The metadata can be fixed by an export/import cycle.
207
-
@@ -0,0 +1 @@
1
+ __version__ = '6.12.0'
@@ -1,5 +1,13 @@
1
-
1
+ from __future__ import annotations
2
2
  from pluggy import HookspecMarker
3
+ from typing import Optional
4
+ from typing import TYPE_CHECKING
5
+
6
+
7
+ if TYPE_CHECKING:
8
+ from devpi_server.views import ToxResultHandling
9
+ from pyramid.request import Request
10
+
3
11
 
4
12
  hookspec = HookspecMarker("devpiserver")
5
13
 
@@ -255,6 +263,28 @@ def devpiserver_on_replicated_file(stage, project, version, link, serial, back_s
255
263
  """Called when a file was downloaded from master on replica."""
256
264
 
257
265
 
266
+ @hookspec(firstresult=True)
267
+ def devpiserver_on_toxresult_store(request: Request, tox_result_handling: ToxResultHandling) -> Optional[ToxResultHandling]:
268
+ """Called when a toxresult is about to be stored.
269
+
270
+ Stops at first non-None result.
271
+
272
+ :returns: A ToxResultHandling object which determines how the upload is processed.
273
+ """
274
+
275
+
276
+ @hookspec(firstresult=True)
277
+ def devpiserver_on_toxresult_upload_forbidden(request: Request, tox_result_handling: ToxResultHandling) -> Optional[str]:
278
+ """Called when the permission check for toxresult upload failed.
279
+
280
+ Stops at first non-None result.
281
+
282
+ :returns: A ToxResultHandling object which determines whether an error
283
+ with message (default using ``block``) or a success with a message
284
+ (using ``skip``) is returned.
285
+ """
286
+
287
+
258
288
  @hookspec
259
289
  def devpiserver_metrics(request):
260
290
  """ called for status view.
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import contextlib
2
3
  import os
3
4
  import re
@@ -50,6 +51,7 @@ from .readonly import get_mutable_deepcopy
50
51
  from .log import thread_push_log, thread_pop_log, threadlog
51
52
 
52
53
  from .auth import Auth
54
+ import attrs
53
55
 
54
56
  devpiweb_hookimpl = HookimplMarker("devpiweb")
55
57
  server_version = devpi_server.__version__
@@ -412,6 +414,34 @@ def devpiserver_authcheck_forbidden(request):
412
414
  return True
413
415
 
414
416
 
417
+ TOXRESULT_UPLOAD_FORBIDDEN = (
418
+ "No permission to upload tox results. "
419
+ "You can use the devpi test --no-upload option to skip the upload.")
420
+
421
+
422
+ @attrs.define(frozen=True)
423
+ class ToxResultHandling:
424
+ _block: bool = attrs.field(default=False, alias="_block")
425
+ _skip: bool = attrs.field(default=False, alias="_skip")
426
+ msg: None | str = None
427
+
428
+ def block(self, msg=TOXRESULT_UPLOAD_FORBIDDEN):
429
+ return ToxResultHandling(_block=True, msg=msg)
430
+
431
+ def skip(self, msg=None):
432
+ return ToxResultHandling(_skip=True, msg=msg)
433
+
434
+
435
+ @hookimpl(trylast=True)
436
+ def devpiserver_on_toxresult_store(request, tox_result_handling):
437
+ return tox_result_handling
438
+
439
+
440
+ @hookimpl(trylast=True)
441
+ def devpiserver_on_toxresult_upload_forbidden(request, tox_result_handling):
442
+ return tox_result_handling
443
+
444
+
415
445
  def version_in_filename(version, filename):
416
446
  if version is None:
417
447
  # no version set, so skip check
@@ -530,14 +560,25 @@ class PyPIView:
530
560
 
531
561
  @view_config(
532
562
  route_name="/{user}/{index}/+f/{relpath:.*}",
533
- request_method="POST",
534
- permission="toxresult_upload")
563
+ request_method="POST")
535
564
  def post_toxresult(self):
536
- stage = self.context.stage
537
- relpath = self.request.path_info.strip("/")
538
- link = stage.get_link_from_entrypath(relpath)
539
- if link is None or link.rel != "releasefile":
540
- apireturn(404, message="no release file found at %s" % relpath)
565
+ if not self.request.has_permission("toxresult_upload"):
566
+ default_tox_result_handling = ToxResultHandling().block(TOXRESULT_UPLOAD_FORBIDDEN)
567
+ tox_result_handling = self.xom.config.hook.devpiserver_on_toxresult_upload_forbidden(
568
+ request=self.request, tox_result_handling=default_tox_result_handling)
569
+ else:
570
+ stage = self.context.stage
571
+ relpath = self.request.path_info.strip("/")
572
+ link = stage.get_link_from_entrypath(relpath)
573
+ if link is None or link.rel != "releasefile":
574
+ apireturn(404, message="no release file found at %s" % relpath)
575
+ default_tox_result_handling = ToxResultHandling()
576
+ tox_result_handling = self.xom.config.hook.devpiserver_on_toxresult_store(
577
+ request=self.request, tox_result_handling=default_tox_result_handling)
578
+ if tox_result_handling._block:
579
+ apireturn(403, tox_result_handling.msg)
580
+ if tox_result_handling._skip:
581
+ apireturn(200, tox_result_handling.msg)
541
582
  # the getjson call validates that we got valid json
542
583
  getjson(self.request)
543
584
  # but we store the original body
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devpi-server
3
- Version: 6.11.0
3
+ Version: 6.12.0
4
4
  Summary: devpi-server: reliable private and pypi.org caching server
5
5
  Home-page: https://devpi.net
6
6
  Maintainer: Florian Schulze
@@ -119,6 +119,25 @@ Changelog
119
119
 
120
120
  .. towncrier release notes start
121
121
 
122
+ 6.12.0 (2024-06-25)
123
+ ===================
124
+
125
+ Features
126
+ --------
127
+
128
+ - Added ``devpiserver_on_toxresult_store`` hook to allow blocking or skipping a toxresult upload on more specific conditions as ``acl_toxresult_upload`` would allow.
129
+
130
+ - Added ``devpiserver_on_toxresult_upload_forbidden`` hook to allow returning a custom message and result (403 or 200).
131
+
132
+
133
+
134
+ Bug Fixes
135
+ ---------
136
+
137
+ - Return json data if toxresult upload is forbidden.
138
+
139
+
140
+
122
141
  6.11.0 (2024-04-20)
123
142
  ===================
124
143
 
@@ -180,28 +199,3 @@ Bug Fixes
180
199
 
181
200
  - Fix #980: Remove long deprecated backward compatibility for old pluggy versions to fix error with pluggy 1.1.0.
182
201
 
183
-
184
- 6.9.0 (2023-05-23)
185
- ==================
186
-
187
- Features
188
- --------
189
-
190
- - Support export directory layout for ``--replica-file-search-path`` option.
191
-
192
- - Fix #931: Add ``mirror_no_project_list`` setting for mirror indexes that have no full project list like google cloud artifacts or if you want to prevent downloading the full list for huge indexes like PyPI.
193
-
194
-
195
- Bug Fixes
196
- ---------
197
-
198
- - Keep a reference to async tasks to avoid their removal mid execution.
199
-
200
- - Support changed default of ``enforce_content_length`` in urllib3 >= 2.
201
-
202
- - Fix #934: Properly set PATH_INFO when outside URL is used with sub-path.
203
-
204
- - Fix #945: Adapt FatalError to be usable as an async HTTP response when updating a project on a mirror.
205
-
206
- - Fix wrong hash metadata introduced in 6.5.0 for toxresults which prevents replication. The metadata can be fixed by an export/import cycle.
207
-
@@ -57,7 +57,7 @@ if __name__ == "__main__":
57
57
  'Documentation': 'https://doc.devpi.net',
58
58
  'Source Code': 'https://github.com/devpi/devpi'
59
59
  },
60
- version='6.11.0',
60
+ version='6.12.0',
61
61
  maintainer="Florian Schulze",
62
62
  maintainer_email="mail@pyfidelity.com",
63
63
  packages=[
@@ -367,6 +367,29 @@ class TestProjectThings:
367
367
  r = mapp.downloadrelease(200, toxresult_url)
368
368
  assert json.loads(r.decode('utf-8')) == tox_result_data
369
369
 
370
+ def test_toxresult_forbidden(self, mapp, server_version):
371
+ import json
372
+ mapp.create_and_use('pruser2/dev')
373
+ mapp.modify_index("pruser2/dev", ['acl_toxresult_upload='])
374
+ info = mapp.getjson(f"/{mapp.api.stagename}")
375
+ assert info['result']['acl_toxresult_upload'] == []
376
+ content = mapp.makepkg("hello-1.0.tar.gz", b"content", "hello", "1.0")
377
+ mapp.upload_file_pypi("hello-1.0.tar.gz", content, "hello", "1.0")
378
+ (pkg_url,) = mapp.getreleaseslist("hello")
379
+ tox_result_data = dict(foo="bar")
380
+ result = mapp.upload_toxresult(pkg_url, json.dumps(tox_result_data), code=403)
381
+ if server_version >= parse_version("6.11dev"):
382
+ # in devpi-client tests we don't have the result.content_type helper
383
+ assert result.headers.get('content-type', '').lower().startswith('application/json')
384
+ data = result.json
385
+ if callable(data):
386
+ # in devpi-client .json is a method
387
+ data = data()
388
+ assert 'no permission to upload tox results' in data['message'].lower()
389
+ info = mapp.getjson(f"/{mapp.api.stagename}/hello")
390
+ (href,) = (x['href'] for x in info["result"]["1.0"]["+links"])
391
+ assert 'toxresult' not in href
392
+
370
393
 
371
394
  @pytest.mark.nomocking
372
395
  class TestMirrorIndexThings:
@@ -2178,6 +2178,64 @@ def test_delete_removed_toxresult(mapp, testapp, tox_result_data):
2178
2178
  testapp.delete(toxlink2.href, status=410)
2179
2179
 
2180
2180
 
2181
+ def test_plugin_toxresult_upload_block(makemapp, maketestapp, makexom, tox_result_data):
2182
+ class Plugin:
2183
+ @hookimpl
2184
+ def devpiserver_on_toxresult_store(self, tox_result_handling):
2185
+ return tox_result_handling.block("custom block")
2186
+ xom = makexom(plugins=[Plugin()])
2187
+ testapp = maketestapp(xom)
2188
+ mapp = makemapp(testapp)
2189
+ api = mapp.create_and_use()
2190
+ mapp.upload_file_pypi("pkg6-2.6.tgz", b"123", "pkg6", "2.6")
2191
+ vv = get_view_version_links(testapp, api.index, "pkg6", "2.6")
2192
+ (link1,) = vv.get_links()
2193
+ r = mapp.upload_toxresult(link1.href, json.dumps(tox_result_data), code=403)
2194
+ assert r.json['message'] == 'custom block'
2195
+ # make sure the upload was blocked
2196
+ (link2,) = vv.get_links()
2197
+ assert link1.href == link2.href
2198
+
2199
+
2200
+ def test_plugin_toxresult_upload_forbidden_skip(makemapp, maketestapp, makexom, tox_result_data):
2201
+ class Plugin:
2202
+ @hookimpl
2203
+ def devpiserver_on_toxresult_upload_forbidden(self, tox_result_handling):
2204
+ return tox_result_handling.skip("custom message")
2205
+ xom = makexom(plugins=[Plugin()])
2206
+ testapp = maketestapp(xom)
2207
+ mapp = makemapp(testapp)
2208
+ api = mapp.create_and_use(indexconfig=dict(acl_toxresult_upload=''))
2209
+ mapp.upload_file_pypi("pkg6-2.6.tgz", b"123", "pkg6", "2.6")
2210
+ vv = get_view_version_links(testapp, api.index, "pkg6", "2.6")
2211
+ (link1,) = vv.get_links()
2212
+ r = mapp.upload_toxresult(link1.href, json.dumps(tox_result_data), code=200)
2213
+ assert r.json['message'] == 'custom message'
2214
+ # make sure the upload was blocked
2215
+ (link2,) = vv.get_links()
2216
+ assert link1.href == link2.href
2217
+
2218
+
2219
+ def test_plugin_toxresult_upload_skip(makemapp, maketestapp, makexom, tox_result_data):
2220
+ class Plugin:
2221
+ @hookimpl
2222
+ def devpiserver_on_toxresult_store(self, tox_result_handling):
2223
+ return tox_result_handling.skip("custom skip")
2224
+ xom = makexom(plugins=[Plugin()])
2225
+ testapp = maketestapp(xom)
2226
+ mapp = makemapp(testapp)
2227
+ api = mapp.create_and_use()
2228
+ mapp.upload_file_pypi("pkg6-2.6.tgz", b"123", "pkg6", "2.6")
2229
+ vv = get_view_version_links(testapp, api.index, "pkg6", "2.6")
2230
+ (link1,) = vv.get_links()
2231
+ r = mapp.upload_toxresult(link1.href, json.dumps(tox_result_data), code=200)
2232
+ assert r.json['message'] == 'custom skip'
2233
+ vv = get_view_version_links(testapp, api.index, "pkg6", "2.6")
2234
+ # make sure the upload was skipped
2235
+ (link2,) = vv.get_links()
2236
+ assert link1.href == link2.href
2237
+
2238
+
2181
2239
  @proj
2182
2240
  def test_upload_docs_no_version(mapp, testapp, proj):
2183
2241
  api = mapp.create_and_use()
@@ -1 +0,0 @@
1
- __version__ = '6.11.0'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes