bugsink 2.1.2__tar.gz → 2.2.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 (591) hide show
  1. {bugsink-2.1.2 → bugsink-2.2.0}/CHANGELOG.md +67 -0
  2. {bugsink-2.1.2 → bugsink-2.2.0}/PKG-INFO +4 -4
  3. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/service_backends/webhook_security.py +62 -4
  4. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/tests.py +21 -0
  5. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/__init__.py +21 -0
  6. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/prestart.py +1 -1
  7. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/vacuum.py +22 -1
  8. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/test_vacuum.py +2 -1
  9. bugsink-2.2.0/bsmain/tests.py +45 -0
  10. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/_version.py +3 -3
  11. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/conf_templates/local.py.template +1 -1
  12. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/conf_templates/singleserver.py.template +1 -2
  13. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/settings/development.py +1 -1
  14. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/tests.py +35 -0
  15. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/utils.py +26 -0
  16. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/views.py +5 -24
  17. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/wsgi.py +1 -1
  18. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink.egg-info/PKG-INFO +4 -4
  19. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink.egg-info/SOURCES.txt +5 -0
  20. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink.egg-info/requires.txt +3 -3
  21. {bugsink-2.1.2 → bugsink-2.2.0}/events/markdown_stacktrace.py +1 -1
  22. {bugsink-2.1.2 → bugsink-2.2.0}/events/models.py +1 -1
  23. {bugsink-2.1.2 → bugsink-2.2.0}/events/tests.py +10 -0
  24. {bugsink-2.1.2 → bugsink-2.2.0}/events/ua_stuff.py +7 -2
  25. {bugsink-2.1.2 → bugsink-2.2.0}/events/utils.py +5 -13
  26. {bugsink-2.1.2 → bugsink-2.2.0}/files/admin.py +3 -3
  27. bugsink-2.2.0/files/management/commands/delete_legacy_sourcemaps.py +30 -0
  28. bugsink-2.2.0/files/migrations/0004_alter_filemetadata_unique_together_and_more.py +34 -0
  29. {bugsink-2.1.2 → bugsink-2.2.0}/files/minidump.py +8 -7
  30. {bugsink-2.1.2 → bugsink-2.2.0}/files/models.py +67 -4
  31. {bugsink-2.1.2 → bugsink-2.2.0}/files/tasks.py +102 -58
  32. {bugsink-2.1.2 → bugsink-2.2.0}/files/test_vacuum.py +17 -4
  33. {bugsink-2.1.2 → bugsink-2.2.0}/files/tests.py +168 -10
  34. {bugsink-2.1.2 → bugsink-2.2.0}/files/views.py +54 -35
  35. bugsink-2.2.0/ingest/event_counter.py +121 -0
  36. bugsink-2.2.0/ingest/management/commands/vacuum_ingest_dir.py +110 -0
  37. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/tests.py +138 -0
  38. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/views.py +46 -6
  39. bugsink-2.2.0/issues/markdown_issue.py +237 -0
  40. {bugsink-2.1.2 → bugsink-2.2.0}/issues/tests.py +154 -3
  41. {bugsink-2.1.2 → bugsink-2.2.0}/issues/urls.py +2 -1
  42. {bugsink-2.1.2 → bugsink-2.2.0}/issues/views.py +32 -8
  43. bugsink-2.2.0/phonehome/migrations/0006_recalculate_installation_next_quota_check.py +32 -0
  44. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/tasks.py +4 -13
  45. bugsink-2.2.0/phonehome/utils.py +55 -0
  46. {bugsink-2.1.2 → bugsink-2.2.0}/projects/tests.py +45 -3
  47. {bugsink-2.1.2 → bugsink-2.2.0}/projects/views.py +2 -0
  48. {bugsink-2.1.2 → bugsink-2.2.0}/requirements.txt +3 -3
  49. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/minidump.py +3 -3
  50. {bugsink-2.1.2 → bugsink-2.2.0}/tags/tests.py +1 -0
  51. {bugsink-2.1.2 → bugsink-2.2.0}/tags/utils.py +1 -1
  52. bugsink-2.2.0/teams/tests.py +30 -0
  53. {bugsink-2.1.2 → bugsink-2.2.0}/theme/static/css/dist/styles.css +1 -1
  54. {bugsink-2.1.2 → bugsink-2.2.0}/theme/static_src/package-lock.json +91 -91
  55. bugsink-2.1.2/bsmain/tests.py +0 -0
  56. bugsink-2.1.2/ingest/event_counter.py +0 -97
  57. bugsink-2.1.2/ingest/management/commands/vacuum_ingest_dir.py +0 -97
  58. bugsink-2.1.2/teams/tests.py +0 -1
  59. {bugsink-2.1.2 → bugsink-2.2.0}/.bandit +0 -0
  60. {bugsink-2.1.2 → bugsink-2.2.0}/.coveragerc +0 -0
  61. {bugsink-2.1.2 → bugsink-2.2.0}/.github/dependabot.yml +0 -0
  62. {bugsink-2.1.2 → bugsink-2.2.0}/.github/workflows/ci.yml +0 -0
  63. {bugsink-2.1.2 → bugsink-2.2.0}/.github/workflows/copilot-setup-steps.yml +0 -0
  64. {bugsink-2.1.2 → bugsink-2.2.0}/.gitignore +0 -0
  65. {bugsink-2.1.2 → bugsink-2.2.0}/AGENTS.md +0 -0
  66. {bugsink-2.1.2 → bugsink-2.2.0}/CLA.md +0 -0
  67. {bugsink-2.1.2 → bugsink-2.2.0}/CONTRIBUTING.md +0 -0
  68. {bugsink-2.1.2 → bugsink-2.2.0}/Dockerfile +0 -0
  69. {bugsink-2.1.2 → bugsink-2.2.0}/Dockerfile.fromwheel +0 -0
  70. {bugsink-2.1.2 → bugsink-2.2.0}/LICENSE +0 -0
  71. {bugsink-2.1.2 → bugsink-2.2.0}/README.md +0 -0
  72. {bugsink-2.1.2 → bugsink-2.2.0}/SECURITY.md +0 -0
  73. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/__init__.py +0 -0
  74. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/admin.py +0 -0
  75. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/apps.py +0 -0
  76. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/forms.py +0 -0
  77. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/migrations/0001_initial.py +0 -0
  78. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/migrations/0002_alter_messagingserviceconfig_project.py +0 -0
  79. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/migrations/0003_messagingserviceconfig_last_failure_error_message_and_more.py +0 -0
  80. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/migrations/0004_alter_messagingserviceconfig_kind.py +0 -0
  81. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/migrations/__init__.py +0 -0
  82. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/models.py +0 -0
  83. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/service_backends/__init__.py +0 -0
  84. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/service_backends/base.py +0 -0
  85. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/service_backends/discord.py +0 -0
  86. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/service_backends/mattermost.py +0 -0
  87. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/service_backends/slack.py +0 -0
  88. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/tasks.py +0 -0
  89. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/templates/mails/issue_alert.html +0 -0
  90. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/templates/mails/issue_alert.txt +0 -0
  91. {bugsink-2.1.2 → bugsink-2.2.0}/alerts/views.py +0 -0
  92. {bugsink-2.1.2 → bugsink-2.2.0}/api/LICENSE +0 -0
  93. {bugsink-2.1.2 → bugsink-2.2.0}/api/README.md +0 -0
  94. {bugsink-2.1.2 → bugsink-2.2.0}/api/event.schema.altered.json +0 -0
  95. {bugsink-2.1.2 → bugsink-2.2.0}/api/event.schema.json +0 -0
  96. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/admin.py +0 -0
  97. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/apps.py +0 -0
  98. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/future_python.py +0 -0
  99. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/__init__.py +0 -0
  100. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/__init__.py +0 -0
  101. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/check_migrations.py +0 -0
  102. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/cleanup_objectstorage.py +0 -0
  103. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/convert_mariadb_uuids.py +0 -0
  104. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/create_auth_token.py +0 -0
  105. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/fetch_event_schema_json.py +0 -0
  106. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/makemigrations.py +0 -0
  107. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/migrate.py +0 -0
  108. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/migrate_to_current_objectstorage.py +0 -0
  109. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/modelcounts.py +0 -0
  110. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/munin.py +0 -0
  111. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/raise_exception.py +0 -0
  112. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/send_bomb.py +0 -0
  113. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/send_json.py +0 -0
  114. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/showstat.py +0 -0
  115. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/commands/stress_test.py +0 -0
  116. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/management/utils.py +0 -0
  117. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/migrations/0001_initial.py +0 -0
  118. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/migrations/0002_cachedmodelcount.py +0 -0
  119. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/migrations/0003_add_description_to_authtoken.py +0 -0
  120. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/migrations/__init__.py +0 -0
  121. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/models.py +0 -0
  122. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/tasks.py +0 -0
  123. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/templates/bsmain/auth_token_list.html +0 -0
  124. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/urls.py +0 -0
  125. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/utils.py +0 -0
  126. {bugsink-2.1.2 → bugsink-2.2.0}/bsmain/views.py +0 -0
  127. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/__init__.py +0 -0
  128. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/api_fields.py +0 -0
  129. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/api_mixins.py +0 -0
  130. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/api_pagination.py +0 -0
  131. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/api_serializers.py +0 -0
  132. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/app_settings.py +0 -0
  133. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/authentication.py +0 -0
  134. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/conf_templates/docker.py.template +0 -0
  135. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/conf_utils.py +0 -0
  136. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/context_processors.py +0 -0
  137. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/debug_views.py +0 -0
  138. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/decorators.py +0 -0
  139. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/email_backends.py +0 -0
  140. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/event_schema.py +0 -0
  141. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/exceptions.py +0 -0
  142. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/gunicorn.docker.conf.py +0 -0
  143. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/middleware.py +0 -0
  144. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/moreiterutils.py +0 -0
  145. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/period_utils.py +0 -0
  146. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/permissions.py +0 -0
  147. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/pygments_extensions.py +0 -0
  148. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/scripts/__init__.py +0 -0
  149. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/scripts/create_conf.py +0 -0
  150. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/scripts/manage.py +0 -0
  151. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/scripts/runsnappea.py +0 -0
  152. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/scripts/show_version.py +0 -0
  153. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/scripts/util.py +0 -0
  154. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/settings/__init__.py +0 -0
  155. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/settings/default.py +0 -0
  156. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/streams.py +0 -0
  157. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/test_api.py +0 -0
  158. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/test_utils.py +0 -0
  159. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/timed_sqlite_backend/__init__.py +0 -0
  160. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/timed_sqlite_backend/base.py +0 -0
  161. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/tooling.py +0 -0
  162. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/transaction.py +0 -0
  163. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/urls.py +0 -0
  164. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/version.py +0 -0
  165. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink/volume_based_condition.py +0 -0
  166. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink.egg-info/dependency_links.txt +0 -0
  167. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink.egg-info/entry_points.txt +0 -0
  168. {bugsink-2.1.2 → bugsink-2.2.0}/bugsink.egg-info/top_level.txt +0 -0
  169. {bugsink-2.1.2 → bugsink-2.2.0}/builddocker.bash +0 -0
  170. {bugsink-2.1.2 → bugsink-2.2.0}/buildxdocker.bash +0 -0
  171. {bugsink-2.1.2 → bugsink-2.2.0}/compat/__init__.py +0 -0
  172. {bugsink-2.1.2 → bugsink-2.2.0}/compat/auth.py +0 -0
  173. {bugsink-2.1.2 → bugsink-2.2.0}/compat/dsn.py +0 -0
  174. {bugsink-2.1.2 → bugsink-2.2.0}/compat/tests.py +0 -0
  175. {bugsink-2.1.2 → bugsink-2.2.0}/compat/timestamp.py +0 -0
  176. {bugsink-2.1.2 → bugsink-2.2.0}/compat/vars.py +0 -0
  177. {bugsink-2.1.2 → bugsink-2.2.0}/docker-compose-sample.yaml +0 -0
  178. {bugsink-2.1.2 → bugsink-2.2.0}/ee/LICENSE +0 -0
  179. {bugsink-2.1.2 → bugsink-2.2.0}/ee/__init__.py +0 -0
  180. {bugsink-2.1.2 → bugsink-2.2.0}/ee/tenants/__init__.py +0 -0
  181. {bugsink-2.1.2 → bugsink-2.2.0}/ee/tenants/base.py +0 -0
  182. {bugsink-2.1.2 → bugsink-2.2.0}/ee/tenants/database_backend/__init__.py +0 -0
  183. {bugsink-2.1.2 → bugsink-2.2.0}/ee/tenants/database_backend/base.py +0 -0
  184. {bugsink-2.1.2 → bugsink-2.2.0}/ee/tenants/middleware.py +0 -0
  185. {bugsink-2.1.2 → bugsink-2.2.0}/ee/tenants/utils.py +0 -0
  186. {bugsink-2.1.2 → bugsink-2.2.0}/events/__init__.py +0 -0
  187. {bugsink-2.1.2 → bugsink-2.2.0}/events/admin.py +0 -0
  188. {bugsink-2.1.2 → bugsink-2.2.0}/events/api_views.py +0 -0
  189. {bugsink-2.1.2 → bugsink-2.2.0}/events/apps.py +0 -0
  190. {bugsink-2.1.2 → bugsink-2.2.0}/events/factories.py +0 -0
  191. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/__init__.py +0 -0
  192. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/__init__.py +0 -0
  193. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/cleanup_eventstorage.py +0 -0
  194. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/delete_by_age_until_under_retention_max.py +0 -0
  195. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/delete_old_events.py +0 -0
  196. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/fix_project_digest_order.py +0 -0
  197. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/make_consistent.py +0 -0
  198. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/migrate_to_current_eventstorage.py +0 -0
  199. {bugsink-2.1.2 → bugsink-2.2.0}/events/management/commands/nuke_events.py +0 -0
  200. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0001_initial.py +0 -0
  201. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0002_initial.py +0 -0
  202. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0003_initial.py +0 -0
  203. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0004_b_squashed.py +0 -0
  204. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0004_event_irrelevance_for_retention.py +0 -0
  205. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0005_event_events_even_project_abe572_idx.py +0 -0
  206. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0006_event_never_evict.py +0 -0
  207. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0007_set_never_evict.py +0 -0
  208. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0008_remove_event_events_even_project_abe572_idx_and_more.py +0 -0
  209. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0009_event_events_even_issue_i_90497b_idx.py +0 -0
  210. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0010_rename_ingest_order_event_digest_order_and_more.py +0 -0
  211. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0011_remove_event_events_even_project_adcdee_idx_and_more.py +0 -0
  212. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0012_event_ingested_at.py +0 -0
  213. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0013_harmonize_foogested_at.py +0 -0
  214. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0014_event_grouping.py +0 -0
  215. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0015_set_event_grouping.py +0 -0
  216. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0016_truncate_exception_type_128.py +0 -0
  217. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0017_alter_event_calculated_type_and_more.py +0 -0
  218. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0018_remove_event_has_exception_remove_event_has_logentry.py +0 -0
  219. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0019_event_storage_backend.py +0 -0
  220. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0020_remove_events_with_null_issue_or_grouping.py +0 -0
  221. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0021_alter_do_nothing.py +0 -0
  222. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0022_alter_event_project.py +0 -0
  223. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0023_event_remote_addr.py +0 -0
  224. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0024_remove_event_debug_info.py +0 -0
  225. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0025_fix_never_evict.py +0 -0
  226. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0026_event_events_even_project_625413_idx.py +0 -0
  227. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0027_event_project_digest_order.py +0 -0
  228. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/0028_event_events_even_digeste_bf21dd_idx.py +0 -0
  229. {bugsink-2.1.2 → bugsink-2.2.0}/events/migrations/__init__.py +0 -0
  230. {bugsink-2.1.2 → bugsink-2.2.0}/events/renderers.py +0 -0
  231. {bugsink-2.1.2 → bugsink-2.2.0}/events/retention.py +0 -0
  232. {bugsink-2.1.2 → bugsink-2.2.0}/events/serializers.py +0 -0
  233. {bugsink-2.1.2 → bugsink-2.2.0}/events/storage.py +0 -0
  234. {bugsink-2.1.2 → bugsink-2.2.0}/events/storage_registry.py +0 -0
  235. {bugsink-2.1.2 → bugsink-2.2.0}/events/tasks.py +0 -0
  236. {bugsink-2.1.2 → bugsink-2.2.0}/events/templates/events/event_stacktrace.txt +0 -0
  237. {bugsink-2.1.2 → bugsink-2.2.0}/events/test_api.py +0 -0
  238. {bugsink-2.1.2 → bugsink-2.2.0}/events/urls.py +0 -0
  239. {bugsink-2.1.2 → bugsink-2.2.0}/events/views.py +0 -0
  240. {bugsink-2.1.2 → bugsink-2.2.0}/files/__init__.py +0 -0
  241. {bugsink-2.1.2 → bugsink-2.2.0}/files/apps.py +0 -0
  242. {bugsink-2.1.2 → bugsink-2.2.0}/files/management/__init__.py +0 -0
  243. {bugsink-2.1.2 → bugsink-2.2.0}/files/management/commands/__init__.py +0 -0
  244. {bugsink-2.1.2 → bugsink-2.2.0}/files/management/commands/vacuum_files.py +0 -0
  245. {bugsink-2.1.2 → bugsink-2.2.0}/files/migrations/0001_initial.py +0 -0
  246. {bugsink-2.1.2 → bugsink-2.2.0}/files/migrations/0002_chunk_created_at_file_accessed_at_file_created_at_and_more.py +0 -0
  247. {bugsink-2.1.2 → bugsink-2.2.0}/files/migrations/0003_file_storage_backend.py +0 -0
  248. {bugsink-2.1.2 → bugsink-2.2.0}/files/migrations/__init__.py +0 -0
  249. {bugsink-2.1.2 → bugsink-2.2.0}/files/object_kinds.py +0 -0
  250. {bugsink-2.1.2 → bugsink-2.2.0}/files/storage.py +0 -0
  251. {bugsink-2.1.2 → bugsink-2.2.0}/files/storage_registry.py +0 -0
  252. {bugsink-2.1.2 → bugsink-2.2.0}/files/urls.py +0 -0
  253. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/__init__.py +0 -0
  254. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/admin.py +0 -0
  255. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/apps.py +0 -0
  256. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/exceptions.py +0 -0
  257. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/filestore.py +0 -0
  258. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/header_validators.py +0 -0
  259. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/management/__init__.py +0 -0
  260. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/management/commands/__init__.py +0 -0
  261. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/migrations/0001_set_sqlite_wal.py +0 -0
  262. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/migrations/0002_initial.py +0 -0
  263. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/migrations/__init__.py +0 -0
  264. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/models.py +0 -0
  265. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/parsers.py +0 -0
  266. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/tasks.py +0 -0
  267. {bugsink-2.1.2 → bugsink-2.2.0}/ingest/urls.py +0 -0
  268. {bugsink-2.1.2 → bugsink-2.2.0}/issues/__init__.py +0 -0
  269. {bugsink-2.1.2 → bugsink-2.2.0}/issues/admin.py +0 -0
  270. {bugsink-2.1.2 → bugsink-2.2.0}/issues/api_views.py +0 -0
  271. {bugsink-2.1.2 → bugsink-2.2.0}/issues/apps.py +0 -0
  272. {bugsink-2.1.2 → bugsink-2.2.0}/issues/factories.py +0 -0
  273. {bugsink-2.1.2 → bugsink-2.2.0}/issues/forms.py +0 -0
  274. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0001_initial.py +0 -0
  275. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0002_initial.py +0 -0
  276. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0003_alter_turningpoint_triggering_event.py +0 -0
  277. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0004_b_squashed.py +0 -0
  278. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0004_rename_event_count_issue_digested_event_count.py +0 -0
  279. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0005_rename_ingest_order_issue_digest_order_and_more.py +0 -0
  280. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0006_issue_next_unmute_check.py +0 -0
  281. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0007_alter_turningpoint_options.py +0 -0
  282. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0008_issue_stored_event_count.py +0 -0
  283. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0009_fill_stored_event_count.py +0 -0
  284. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0010_issue_list_indexes.py +0 -0
  285. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0011_truncate_exception_type_128.py +0 -0
  286. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0012_alter_issue_calculated_type_and_more.py +0 -0
  287. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0013_fix_issue_stored_event_counts.py +0 -0
  288. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0014_grouping_grouping_key_hash.py +0 -0
  289. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0015_set_grouping_hash.py +0 -0
  290. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0016_alter_grouping_unique_together.py +0 -0
  291. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0017_issue_list_indexes_must_start_with_project.py +0 -0
  292. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0018_issue_is_deleted.py +0 -0
  293. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0019_alter_grouping_grouping_key_hash.py +0 -0
  294. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0020_remove_objects_with_null_issue.py +0 -0
  295. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0021_alter_do_nothing.py +0 -0
  296. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0022_turningpoint_project.py +0 -0
  297. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0023_turningpoint_set_project.py +0 -0
  298. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0024_turningpoint_project_alter_not_null.py +0 -0
  299. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/0025_alter_grouping_project_alter_issue_project.py +0 -0
  300. {bugsink-2.1.2 → bugsink-2.2.0}/issues/migrations/__init__.py +0 -0
  301. {bugsink-2.1.2 → bugsink-2.2.0}/issues/models.py +0 -0
  302. {bugsink-2.1.2 → bugsink-2.2.0}/issues/regressions.py +0 -0
  303. {bugsink-2.1.2 → bugsink-2.2.0}/issues/serializers.py +0 -0
  304. {bugsink-2.1.2 → bugsink-2.2.0}/issues/tasks.py +0 -0
  305. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/_event_nav.html +0 -0
  306. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/base.html +0 -0
  307. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/breadcrumbs.html +0 -0
  308. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/event_404.html +0 -0
  309. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/event_details.html +0 -0
  310. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/event_list.html +0 -0
  311. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/grouping.html +0 -0
  312. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/history.html +0 -0
  313. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/issue_list.html +0 -0
  314. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/stacktrace.html +0 -0
  315. {bugsink-2.1.2 → bugsink-2.2.0}/issues/templates/issues/tags.html +0 -0
  316. {bugsink-2.1.2 → bugsink-2.2.0}/issues/test_api.py +0 -0
  317. {bugsink-2.1.2 → bugsink-2.2.0}/issues/utils.py +0 -0
  318. {bugsink-2.1.2 → bugsink-2.2.0}/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  319. {bugsink-2.1.2 → bugsink-2.2.0}/locale/zh_Hans/LC_MESSAGES/django.po +0 -0
  320. {bugsink-2.1.2 → bugsink-2.2.0}/manage.py +0 -0
  321. {bugsink-2.1.2 → bugsink-2.2.0}/performance/__init__.py +0 -0
  322. {bugsink-2.1.2 → bugsink-2.2.0}/performance/bursty_data.py +0 -0
  323. {bugsink-2.1.2 → bugsink-2.2.0}/performance/context_managers.py +0 -0
  324. {bugsink-2.1.2 → bugsink-2.2.0}/performance/management/__init__.py +0 -0
  325. {bugsink-2.1.2 → bugsink-2.2.0}/performance/management/commands/__init__.py +0 -0
  326. {bugsink-2.1.2 → bugsink-2.2.0}/performance/management/commands/pftest_search.py +0 -0
  327. {bugsink-2.1.2 → bugsink-2.2.0}/performance/stress-with-eviction/README.md +0 -0
  328. {bugsink-2.1.2 → bugsink-2.2.0}/performance/stress-with-eviction/df-day.png +0 -0
  329. {bugsink-2.1.2 → bugsink-2.2.0}/performance/stress-with-eviction/event_ingest_count-day.png +0 -0
  330. {bugsink-2.1.2 → bugsink-2.2.0}/performance/stress-with-eviction/response_time_api_avg-day.png +0 -0
  331. {bugsink-2.1.2 → bugsink-2.2.0}/performance/stress-with-eviction/response_time_api_max-day.png +0 -0
  332. {bugsink-2.1.2 → bugsink-2.2.0}/performance/stress-with-eviction/snappea_queue_size-day.png +0 -0
  333. {bugsink-2.1.2 → bugsink-2.2.0}/performance/stress-with-eviction/total_requests-day.png +0 -0
  334. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/__init__.py +0 -0
  335. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/admin.py +0 -0
  336. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/apps.py +0 -0
  337. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/management/__init__.py +0 -0
  338. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/management/commands/__init__.py +0 -0
  339. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/management/commands/print_phonehome.py +0 -0
  340. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0001_b_squashed_initial.py +0 -0
  341. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0001_initial.py +0 -0
  342. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0002_create_installation_id.py +0 -0
  343. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0002_installation_email_quota_usage.py +0 -0
  344. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0003_installation_ingest_quotas.py +0 -0
  345. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0003_outboundmessage.py +0 -0
  346. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0004_installation_created_at.py +0 -0
  347. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0004_installation_quota_exceeded_reason.py +0 -0
  348. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0005_installation_silence_email_system_warning.py +0 -0
  349. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/0005_reset_quota_exceeded_until.py +0 -0
  350. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/migrations/__init__.py +0 -0
  351. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/models.py +0 -0
  352. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/tests.py +0 -0
  353. {bugsink-2.1.2 → bugsink-2.2.0}/phonehome/views.py +0 -0
  354. {bugsink-2.1.2 → bugsink-2.2.0}/pre-commit +0 -0
  355. {bugsink-2.1.2 → bugsink-2.2.0}/projects/__init__.py +0 -0
  356. {bugsink-2.1.2 → bugsink-2.2.0}/projects/admin.py +0 -0
  357. {bugsink-2.1.2 → bugsink-2.2.0}/projects/api_views.py +0 -0
  358. {bugsink-2.1.2 → bugsink-2.2.0}/projects/apps.py +0 -0
  359. {bugsink-2.1.2 → bugsink-2.2.0}/projects/context_processors.py +0 -0
  360. {bugsink-2.1.2 → bugsink-2.2.0}/projects/forms.py +0 -0
  361. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0001_initial.py +0 -0
  362. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0002_b_squashed_initial.py +0 -0
  363. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0002_initial.py +0 -0
  364. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0003_project_projects_pr_name_11d782_idx.py +0 -0
  365. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0003_project_retention_max_event_count.py +0 -0
  366. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0004_project_quota_exceeded_until.py +0 -0
  367. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0005_project_ingested_event_count.py +0 -0
  368. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0006_initial_ingested_count_value.py +0 -0
  369. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0007_rename_ingested_event_count_project_digested_event_count.py +0 -0
  370. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0008_project_next_quota_check.py +0 -0
  371. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0009_alter_project_visibility.py +0 -0
  372. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0010_project_stored_event_count.py +0 -0
  373. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0011_fill_stored_event_count.py +0 -0
  374. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0012_project_is_deleted.py +0 -0
  375. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0013_delete_objects_pointing_to_null_project.py +0 -0
  376. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0014_alter_projectmembership_project.py +0 -0
  377. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0015_project_quota_exceeded_reason.py +0 -0
  378. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0016_reset_quota_exceeded_until.py +0 -0
  379. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/0017_project_issue_count.py +0 -0
  380. {bugsink-2.1.2 → bugsink-2.2.0}/projects/migrations/__init__.py +0 -0
  381. {bugsink-2.1.2 → bugsink-2.2.0}/projects/models.py +0 -0
  382. {bugsink-2.1.2 → bugsink-2.2.0}/projects/serializers.py +0 -0
  383. {bugsink-2.1.2 → bugsink-2.2.0}/projects/tasks.py +0 -0
  384. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/mails/project_membership_invite.html +0 -0
  385. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/mails/project_membership_invite.txt +0 -0
  386. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/mails/project_membership_invite_new_user.html +0 -0
  387. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/mails/project_membership_invite_new_user.txt +0 -0
  388. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_alerts_setup.html +0 -0
  389. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_edit.html +0 -0
  390. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_list.html +0 -0
  391. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_member_settings.html +0 -0
  392. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_members.html +0 -0
  393. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_members_accept.html +0 -0
  394. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_members_invite.html +0 -0
  395. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_messaging_service_edit.html +0 -0
  396. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_messaging_service_new.html +0 -0
  397. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_new.html +0 -0
  398. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_sdk_setup.html +0 -0
  399. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_sdk_setup_javascript.html +0 -0
  400. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_sdk_setup_php.html +0 -0
  401. {bugsink-2.1.2 → bugsink-2.2.0}/projects/templates/projects/project_sdk_setup_python.html +0 -0
  402. {bugsink-2.1.2 → bugsink-2.2.0}/projects/test_api.py +0 -0
  403. {bugsink-2.1.2 → bugsink-2.2.0}/projects/urls.py +0 -0
  404. {bugsink-2.1.2 → bugsink-2.2.0}/pyproject.toml +0 -0
  405. {bugsink-2.1.2 → bugsink-2.2.0}/releases/__init__.py +0 -0
  406. {bugsink-2.1.2 → bugsink-2.2.0}/releases/admin.py +0 -0
  407. {bugsink-2.1.2 → bugsink-2.2.0}/releases/api_views.py +0 -0
  408. {bugsink-2.1.2 → bugsink-2.2.0}/releases/apps.py +0 -0
  409. {bugsink-2.1.2 → bugsink-2.2.0}/releases/migrations/0001_initial.py +0 -0
  410. {bugsink-2.1.2 → bugsink-2.2.0}/releases/migrations/0002_release_releases_re_sort_ep_5c07c8_idx.py +0 -0
  411. {bugsink-2.1.2 → bugsink-2.2.0}/releases/migrations/0003_alter_release_project.py +0 -0
  412. {bugsink-2.1.2 → bugsink-2.2.0}/releases/migrations/0004_fix_indexes.py +0 -0
  413. {bugsink-2.1.2 → bugsink-2.2.0}/releases/migrations/__init__.py +0 -0
  414. {bugsink-2.1.2 → bugsink-2.2.0}/releases/models.py +0 -0
  415. {bugsink-2.1.2 → bugsink-2.2.0}/releases/serializers.py +0 -0
  416. {bugsink-2.1.2 → bugsink-2.2.0}/releases/test_api.py +0 -0
  417. {bugsink-2.1.2 → bugsink-2.2.0}/releases/tests.py +0 -0
  418. {bugsink-2.1.2 → bugsink-2.2.0}/releases/views.py +0 -0
  419. {bugsink-2.1.2 → bugsink-2.2.0}/requirements.development.txt +0 -0
  420. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/LICENSE +0 -0
  421. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/__init__.py +0 -0
  422. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/assemble.py +0 -0
  423. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/stacktraces/functions.py +0 -0
  424. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/stacktraces/platform.py +0 -0
  425. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/stacktraces/processing.py +0 -0
  426. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/utils/safe.py +0 -0
  427. {bugsink-2.1.2 → bugsink-2.2.0}/sentry/utils/strings.py +0 -0
  428. {bugsink-2.1.2 → bugsink-2.2.0}/sentry_sdk_extensions/__init__.py +0 -0
  429. {bugsink-2.1.2 → bugsink-2.2.0}/sentry_sdk_extensions/tests.py +0 -0
  430. {bugsink-2.1.2 → bugsink-2.2.0}/sentry_sdk_extensions/transport.py +0 -0
  431. {bugsink-2.1.2 → bugsink-2.2.0}/setup.cfg +0 -0
  432. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/__init__.py +0 -0
  433. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/admin.py +0 -0
  434. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/apps.py +0 -0
  435. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/datastructures.py +0 -0
  436. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/dbrouters.py +0 -0
  437. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/decorators.py +0 -0
  438. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/example_tasks.py +0 -0
  439. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/foreman.py +0 -0
  440. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/management/__init__.py +0 -0
  441. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/management/commands/__init__.py +0 -0
  442. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/management/commands/checksnappea.py +0 -0
  443. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/management/commands/runsnappea.py +0 -0
  444. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/migrations/0001_initial.py +0 -0
  445. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/migrations/0002_create_models.py +0 -0
  446. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/migrations/0003_task_created_at.py +0 -0
  447. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/migrations/0004_task_snappea_tas_created_eb0824_idx.py +0 -0
  448. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/migrations/0005_stat.py +0 -0
  449. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/migrations/__init__.py +0 -0
  450. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/models.py +0 -0
  451. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/settings.py +0 -0
  452. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/stats.py +0 -0
  453. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/tests.py +0 -0
  454. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/utils.py +0 -0
  455. {bugsink-2.1.2 → bugsink-2.2.0}/snappea/views.py +0 -0
  456. {bugsink-2.1.2 → bugsink-2.2.0}/static/favicon.png +0 -0
  457. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-700.woff2 +0 -0
  458. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-700italic.woff2 +0 -0
  459. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-italic.woff2 +0 -0
  460. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-regular.woff2 +0 -0
  461. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-700.woff2 +0 -0
  462. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-700italic.woff2 +0 -0
  463. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-italic.woff2 +0 -0
  464. {bugsink-2.1.2 → bugsink-2.2.0}/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-regular.woff2 +0 -0
  465. {bugsink-2.1.2 → bugsink-2.2.0}/static/images/bugsink-logo-dark.png +0 -0
  466. {bugsink-2.1.2 → bugsink-2.2.0}/static/images/bugsink-logo.png +0 -0
  467. {bugsink-2.1.2 → bugsink-2.2.0}/static/images/lang-javascript.png +0 -0
  468. {bugsink-2.1.2 → bugsink-2.2.0}/static/images/lang-php.png +0 -0
  469. {bugsink-2.1.2 → bugsink-2.2.0}/static/images/lang-python.png +0 -0
  470. {bugsink-2.1.2 → bugsink-2.2.0}/static/js/entity_edit.js +0 -0
  471. {bugsink-2.1.2 → bugsink-2.2.0}/static/js/issue_history.js +0 -0
  472. {bugsink-2.1.2 → bugsink-2.2.0}/static/js/issue_list.js +0 -0
  473. {bugsink-2.1.2 → bugsink-2.2.0}/static/js/issue_stacktrace.js +0 -0
  474. {bugsink-2.1.2 → bugsink-2.2.0}/static/js/project_list.js +0 -0
  475. {bugsink-2.1.2 → bugsink-2.2.0}/static/js/team_list.js +0 -0
  476. {bugsink-2.1.2 → bugsink-2.2.0}/static/js/user_list.js +0 -0
  477. {bugsink-2.1.2 → bugsink-2.2.0}/tags/__init__.py +0 -0
  478. {bugsink-2.1.2 → bugsink-2.2.0}/tags/admin.py +0 -0
  479. {bugsink-2.1.2 → bugsink-2.2.0}/tags/apps.py +0 -0
  480. {bugsink-2.1.2 → bugsink-2.2.0}/tags/management/__init__.py +0 -0
  481. {bugsink-2.1.2 → bugsink-2.2.0}/tags/management/commands/__init__.py +0 -0
  482. {bugsink-2.1.2 → bugsink-2.2.0}/tags/management/commands/init_tags.py +0 -0
  483. {bugsink-2.1.2 → bugsink-2.2.0}/tags/management/commands/vacuum_eventless_issuetags.py +0 -0
  484. {bugsink-2.1.2 → bugsink-2.2.0}/tags/management/commands/vacuum_tags.py +0 -0
  485. {bugsink-2.1.2 → bugsink-2.2.0}/tags/migrations/0001_initial.py +0 -0
  486. {bugsink-2.1.2 → bugsink-2.2.0}/tags/migrations/0002_no_cascade.py +0 -0
  487. {bugsink-2.1.2 → bugsink-2.2.0}/tags/migrations/0003_remove_objects_with_null_issue.py +0 -0
  488. {bugsink-2.1.2 → bugsink-2.2.0}/tags/migrations/0004_alter_do_nothing.py +0 -0
  489. {bugsink-2.1.2 → bugsink-2.2.0}/tags/migrations/0005_alter_eventtag_project_alter_issuetag_project_and_more.py +0 -0
  490. {bugsink-2.1.2 → bugsink-2.2.0}/tags/migrations/__init__.py +0 -0
  491. {bugsink-2.1.2 → bugsink-2.2.0}/tags/models.py +0 -0
  492. {bugsink-2.1.2 → bugsink-2.2.0}/tags/search.py +0 -0
  493. {bugsink-2.1.2 → bugsink-2.2.0}/tags/tasks.py +0 -0
  494. {bugsink-2.1.2 → bugsink-2.2.0}/teams/__init__.py +0 -0
  495. {bugsink-2.1.2 → bugsink-2.2.0}/teams/admin.py +0 -0
  496. {bugsink-2.1.2 → bugsink-2.2.0}/teams/api_views.py +0 -0
  497. {bugsink-2.1.2 → bugsink-2.2.0}/teams/apps.py +0 -0
  498. {bugsink-2.1.2 → bugsink-2.2.0}/teams/forms.py +0 -0
  499. {bugsink-2.1.2 → bugsink-2.2.0}/teams/migrations/0001_b_squashed_initial.py +0 -0
  500. {bugsink-2.1.2 → bugsink-2.2.0}/teams/migrations/0001_initial.py +0 -0
  501. {bugsink-2.1.2 → bugsink-2.2.0}/teams/migrations/0002_initial.py +0 -0
  502. {bugsink-2.1.2 → bugsink-2.2.0}/teams/migrations/0002_team_teams_team_name_43e047_idx.py +0 -0
  503. {bugsink-2.1.2 → bugsink-2.2.0}/teams/migrations/0003_alter_team_visibility.py +0 -0
  504. {bugsink-2.1.2 → bugsink-2.2.0}/teams/migrations/0004_remove_team_slug.py +0 -0
  505. {bugsink-2.1.2 → bugsink-2.2.0}/teams/migrations/__init__.py +0 -0
  506. {bugsink-2.1.2 → bugsink-2.2.0}/teams/models.py +0 -0
  507. {bugsink-2.1.2 → bugsink-2.2.0}/teams/serializers.py +0 -0
  508. {bugsink-2.1.2 → bugsink-2.2.0}/teams/tasks.py +0 -0
  509. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/mails/team_membership_invite.html +0 -0
  510. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/mails/team_membership_invite.txt +0 -0
  511. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/mails/team_membership_invite_new_user.html +0 -0
  512. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/mails/team_membership_invite_new_user.txt +0 -0
  513. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/teams/team_edit.html +0 -0
  514. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/teams/team_list.html +0 -0
  515. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/teams/team_member_settings.html +0 -0
  516. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/teams/team_members.html +0 -0
  517. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/teams/team_members_accept.html +0 -0
  518. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/teams/team_members_invite.html +0 -0
  519. {bugsink-2.1.2 → bugsink-2.2.0}/teams/templates/teams/team_new.html +0 -0
  520. {bugsink-2.1.2 → bugsink-2.2.0}/teams/test_api.py +0 -0
  521. {bugsink-2.1.2 → bugsink-2.2.0}/teams/urls.py +0 -0
  522. {bugsink-2.1.2 → bugsink-2.2.0}/teams/views.py +0 -0
  523. {bugsink-2.1.2 → bugsink-2.2.0}/templates/400.html +0 -0
  524. {bugsink-2.1.2 → bugsink-2.2.0}/templates/403.html +0 -0
  525. {bugsink-2.1.2 → bugsink-2.2.0}/templates/403_csrf.html +0 -0
  526. {bugsink-2.1.2 → bugsink-2.2.0}/templates/404.html +0 -0
  527. {bugsink-2.1.2 → bugsink-2.2.0}/templates/4xx_5xx_api.txt +0 -0
  528. {bugsink-2.1.2 → bugsink-2.2.0}/templates/500.html +0 -0
  529. {bugsink-2.1.2 → bugsink-2.2.0}/templates/503.html +0 -0
  530. {bugsink-2.1.2 → bugsink-2.2.0}/templates/admin/change_form_object_tools.html +0 -0
  531. {bugsink-2.1.2 → bugsink-2.2.0}/templates/bugsink/counts.html +0 -0
  532. {bugsink-2.1.2 → bugsink-2.2.0}/templates/bugsink/csrf_debug.html +0 -0
  533. {bugsink-2.1.2 → bugsink-2.2.0}/templates/bugsink/login.html +0 -0
  534. {bugsink-2.1.2 → bugsink-2.2.0}/templates/bugsink/settings.html +0 -0
  535. {bugsink-2.1.2 → bugsink-2.2.0}/templates/robots.txt +0 -0
  536. {bugsink-2.1.2 → bugsink-2.2.0}/templates/signup.html +0 -0
  537. {bugsink-2.1.2 → bugsink-2.2.0}/theme/__init__.py +0 -0
  538. {bugsink-2.1.2 → bugsink-2.2.0}/theme/apps.py +0 -0
  539. {bugsink-2.1.2 → bugsink-2.2.0}/theme/static_src/.gitignore +0 -0
  540. {bugsink-2.1.2 → bugsink-2.2.0}/theme/static_src/package.json +0 -0
  541. {bugsink-2.1.2 → bugsink-2.2.0}/theme/static_src/postcss.config.js +0 -0
  542. {bugsink-2.1.2 → bugsink-2.2.0}/theme/static_src/src/styles.css +0 -0
  543. {bugsink-2.1.2 → bugsink-2.2.0}/theme/static_src/tailwind.config.js +0 -0
  544. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templates/bare_base.html +0 -0
  545. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templates/barest_base.html +0 -0
  546. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templates/base.html +0 -0
  547. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templates/tailwind_forms/formfield.html +0 -0
  548. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/__init__.py +0 -0
  549. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/add_to_qs.py +0 -0
  550. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/code.py +0 -0
  551. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/issues.py +0 -0
  552. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/stricter_templates.py +0 -0
  553. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/tailwind_forms.py +0 -0
  554. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/user.py +0 -0
  555. {bugsink-2.1.2 → bugsink-2.2.0}/theme/templatetags/version.py +0 -0
  556. {bugsink-2.1.2 → bugsink-2.2.0}/theme/tests.py +0 -0
  557. {bugsink-2.1.2 → bugsink-2.2.0}/tools/is_tracked_by_tailwind.py +0 -0
  558. {bugsink-2.1.2 → bugsink-2.2.0}/tools/strip-trailing-whitespace.sh +0 -0
  559. {bugsink-2.1.2 → bugsink-2.2.0}/users/__init__.py +0 -0
  560. {bugsink-2.1.2 → bugsink-2.2.0}/users/admin.py +0 -0
  561. {bugsink-2.1.2 → bugsink-2.2.0}/users/apps.py +0 -0
  562. {bugsink-2.1.2 → bugsink-2.2.0}/users/forms.py +0 -0
  563. {bugsink-2.1.2 → bugsink-2.2.0}/users/management/__init__.py +0 -0
  564. {bugsink-2.1.2 → bugsink-2.2.0}/users/management/commands/__init__.py +0 -0
  565. {bugsink-2.1.2 → bugsink-2.2.0}/users/management/commands/send_welcome_email.py +0 -0
  566. {bugsink-2.1.2 → bugsink-2.2.0}/users/migrations/0001_initial.py +0 -0
  567. {bugsink-2.1.2 → bugsink-2.2.0}/users/migrations/0002_user_theme_preference.py +0 -0
  568. {bugsink-2.1.2 → bugsink-2.2.0}/users/migrations/0003_user_language.py +0 -0
  569. {bugsink-2.1.2 → bugsink-2.2.0}/users/migrations/0004_alter_user_language.py +0 -0
  570. {bugsink-2.1.2 → bugsink-2.2.0}/users/migrations/__init__.py +0 -0
  571. {bugsink-2.1.2 → bugsink-2.2.0}/users/models.py +0 -0
  572. {bugsink-2.1.2 → bugsink-2.2.0}/users/tasks.py +0 -0
  573. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/mails/confirm_email.html +0 -0
  574. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/mails/confirm_email.txt +0 -0
  575. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/mails/reset_password_email.html +0 -0
  576. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/mails/reset_password_email.txt +0 -0
  577. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/mails/welcome_email.html +0 -0
  578. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/mails/welcome_email.txt +0 -0
  579. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/confirm_email.html +0 -0
  580. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/confirm_email_sent.html +0 -0
  581. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/logged_out.html +0 -0
  582. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/preferences.html +0 -0
  583. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/request_reset_password.html +0 -0
  584. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/resend_confirmation.html +0 -0
  585. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/reset_password.html +0 -0
  586. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/reset_password_email_sent.html +0 -0
  587. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/user_edit.html +0 -0
  588. {bugsink-2.1.2 → bugsink-2.2.0}/users/templates/users/user_list.html +0 -0
  589. {bugsink-2.1.2 → bugsink-2.2.0}/users/tests.py +0 -0
  590. {bugsink-2.1.2 → bugsink-2.2.0}/users/urls.py +0 -0
  591. {bugsink-2.1.2 → bugsink-2.2.0}/users/views.py +0 -0
@@ -1,5 +1,72 @@
1
1
  # Changes
2
2
 
3
+ ## 2.2.0 (19 May 2026)
4
+
5
+ ### Security
6
+
7
+ Fix: scope issue actions and event lookups to the authorized project/issue.
8
+
9
+ A project member who knew UUIDs from another project could use some issue-list
10
+ bulk actions and issue event views through a project or issue they were allowed
11
+ to access. These views now require the selected issues/events to belong to the
12
+ authorized parent. See:
13
+
14
+ https://github.com/bugsink/bugsink/security/advisories/GHSA-g5vc-q7qc-v939
15
+ https://github.com/bugsink/bugsink/security/advisories/GHSA-vx2f-6m6h-9frf
16
+
17
+ Fix: scope sourcemap and minidump debug-file metadata to projects.
18
+
19
+ Sourcemap and debug-file IDs are client-provided and were previously resolved
20
+ globally. That could let events in one project use uploaded debug metadata from
21
+ another project. Newly uploaded files now store project information and lookup
22
+ prefers project-scoped metadata. Already-uploaded legacy sourcemaps/debug files
23
+ keep working through a fallback. See:
24
+
25
+ https://github.com/bugsink/bugsink/security/advisories/GHSA-5389-f7vh-wxj8
26
+
27
+ ### Smaller fixes
28
+
29
+ * Generate an `event_id` on `/store/` when the SDK does not send one, see #383.
30
+ * Refresh issue title fields on every event digest, see #378.
31
+ * Include ingest-dir cleanup in the `vacuum` command and warn about stale ingest-dir files, see 772fb1a9bff6 and
32
+ 1ee34c574b7d.
33
+ * Add more verbose output to file vacuuming, see #372.
34
+ * Broaden phonehome triggers and avoid unnecessary queueing, see 2f76eacfbf68.
35
+ * Fix API catch-all logging for non-JSON bodies, see d13e5eff132b.
36
+ * Ensure `release` is a string before ingesting, see 374914c96f62.
37
+ * Fix direct minidump endpoint calls, see 5324d802cc50.
38
+
39
+ ### Upgrading
40
+
41
+ Sourcemap uploads should include a meaningful project slug. Existing unscoped
42
+ sourcemaps keep working, but installations that prefer to remove that fallback
43
+ can run `delete_legacy_sourcemaps` and re-upload sourcemaps with project slugs.
44
+
45
+ ## 2.1.3 (2 May 2026)
46
+
47
+ ### Security
48
+
49
+ Fix: harden webhook URL validation parsing and reject non-RFC characters.
50
+
51
+ In some malformed URLs, Python’s standard URL parser (urllib) and the HTTP
52
+ client stack (requests / urllib3) do not agree on which host is actually being
53
+ targeted. That could allow a webhook URL to pass Bugsink’s outbound-host checks
54
+ while the actual HTTP request is sent somewhere else. See:
55
+
56
+ https://github.com/bugsink/bugsink/security/advisories/GHSA-fp53-qcf8-2xx2
57
+
58
+ ### Smaller fixes
59
+
60
+ * Add issue-level markdown, see #334.
61
+ * Fix installation quota counting across projects, see #359.
62
+ * When vacuuming files, don't load them in memory, and allow long-running totals queries, see #363, #373 and #372.
63
+ * Refuse to send email as something@bugsink.com for self-hosters, see 3ff3a6fbeb6d.
64
+ * Fix `MultipleObjectsReturned` when user has unaccepted project memberships, see 653be6968f6e.
65
+ * Cleanup lingering files for `MAX_EVENT_SIZE` overshoots, see #370.
66
+ * Fix some `.get(context, {})` usages and an exception-path double-exception, see #369.
67
+ * Upgrade `gunicorn` requirement from `==25.1.*` to `==25.3.*`, see 2d5e0071cf66.
68
+ * Upgrade monofy, see #367.
69
+
3
70
  ## 2.1.2 (11 April 2026)
4
71
 
5
72
  * Add stored file count and byte caps, see #355
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bugsink
3
- Version: 2.1.2
3
+ Version: 2.2.0
4
4
  Summary: Self-hosted Error Tracking
5
5
  Author-email: "Bugsink B.V." <info@bugsink.com>
6
6
  Project-URL: homepage, https://www.bugsink.com/
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.14
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: gunicorn==25.1.*
19
+ Requires-Dist: gunicorn==26.0.*
20
20
  Requires-Dist: Django==5.2.*
21
21
  Requires-Dist: verbose_csrf_middleware==5.2.*
22
22
  Requires-Dist: sentry-sdk==2.*
@@ -29,8 +29,8 @@ Requires-Dist: inotify_simple==2.0.*
29
29
  Requires-Dist: Brotli==1.2.*
30
30
  Requires-Dist: python-dateutil==2.9.*
31
31
  Requires-Dist: whitenoise==6.12.*
32
- Requires-Dist: requests==2.33.*
33
- Requires-Dist: monofy==1.1.*
32
+ Requires-Dist: requests==2.34.*
33
+ Requires-Dist: monofy==1.2.*
34
34
  Requires-Dist: user-agents==2.2.*
35
35
  Requires-Dist: fastjsonschema==2.21.*
36
36
  Requires-Dist: ecma426>=0.2.0
@@ -1,10 +1,25 @@
1
1
  import ipaddress
2
2
  import socket
3
- from urllib.parse import urlparse
3
+
4
+ import requests
5
+ from requests import RequestException
6
+ from urllib3.exceptions import LocationParseError
7
+
8
+ from urllib3.util import parse_url as parse_url_from_urllib3
4
9
 
5
10
  from bugsink.app_settings import get_settings
6
11
 
7
12
 
13
+ _URL_ALLOWED_CHARACTERS = set(
14
+ "abcdefghijklmnopqrstuvwxyz" +
15
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + # ASCII letters
16
+ "0123456789" + # ASCII digits
17
+ "-._~" + # RFC 3986 unreserved marks
18
+ ":/?#[]@" + # RFC 3986 gen-delims
19
+ "!$&'()*+,;=" + # RFC 3986 sub-delims
20
+ "%" # Percent-encoding marker
21
+ )
22
+
8
23
  def _parse_hosts_and_networks(entries, setting_name):
9
24
  hosts = set()
10
25
  networks = []
@@ -41,14 +56,57 @@ def _match_entries(target_hostname, resolved_ips, hosts, networks):
41
56
  return False
42
57
 
43
58
 
59
+ def _validate_raw_url_characters(webhook_url):
60
+ # There's no need to be user-friendly in accepting malformed URLs in code that is security-sensitive; we just want
61
+ # to be strict and reject anything that isn't a valid URL character. People will be able to set up their webhooks
62
+ # without the "friendlyness" of a browser's URL bar.
63
+ for char in webhook_url:
64
+ if char in _URL_ALLOWED_CHARACTERS:
65
+ continue
66
+ raise ValueError("Webhook URL must contain only ASCII URL characters.")
67
+
68
+ def _prepare_webhook_url(webhook_url):
69
+ try:
70
+ return requests.Request("POST", webhook_url).prepare().url
71
+ except RequestException as e:
72
+ raise ValueError("Webhook URL is malformed.") from e
73
+
74
+
44
75
  def validate_webhook_url(webhook_url):
45
- parsed = urlparse(webhook_url)
76
+ _validate_raw_url_characters(webhook_url)
77
+
78
+ # Both requests and urllib3 make some attempts to normalize malformed URLs. We must apply the
79
+ # same normalization before our own parsing and checks, to avoid discrepancies between what we
80
+ # check and what then actually happens.
81
+ prepared_webhook_url = _prepare_webhook_url(webhook_url)
82
+
83
+ try:
84
+ # NOTE: requests uses urllib3's URL parsing logic, so we must use the same to ensure consistency; do not change
85
+ # this import to something else, doing so may have security implications if the URL parsing logic differs in how
86
+ # it normalizes or rejects certain inputs.
87
+ parsed = parse_url_from_urllib3(prepared_webhook_url)
88
+ except LocationParseError as e:
89
+ raise ValueError("Webhook URL is malformed.") from e
90
+
46
91
  if parsed.scheme not in {"http", "https"}:
47
92
  raise ValueError("Webhook URL must use http:// or https://.")
48
93
  if parsed.hostname is None:
49
94
  raise ValueError("Webhook URL must include a hostname.")
50
95
 
51
96
  hostname = parsed.hostname.lower()
97
+ if hostname.startswith("[") and hostname.endswith("]"):
98
+ # In URL authority syntax, brackets are only used for IP literals. urllib3 keeps those brackets in `hostname`,
99
+ # but the rest of this function expects the plain host value so we must strip them back out.
100
+ inner_hostname = hostname[1:-1]
101
+ try:
102
+ # Rather than "just strip brackets if present", we verify that the inner value is actually a valid IP
103
+ # address; we don't want to "just generally strip stuff" in security-sentitive code.
104
+ ipaddress.ip_address(inner_hostname)
105
+ except ValueError:
106
+ pass
107
+ else:
108
+ hostname = inner_hostname
109
+
52
110
  settings = get_settings()
53
111
  mode = settings.ALERTS_WEBHOOK_OUTBOUND_MODE
54
112
 
@@ -63,9 +121,9 @@ def validate_webhook_url(webhook_url):
63
121
 
64
122
  # Resolve on every send to defend against DNS changes after configuration time.
65
123
  try:
66
- resolved_ips = {ipaddress.ip_address(ip) for ip in _resolve_ip_addresses(parsed.hostname, port)}
124
+ resolved_ips = {ipaddress.ip_address(ip) for ip in _resolve_ip_addresses(hostname, port)}
67
125
  except OSError as e:
68
- raise ValueError(f"Webhook hostname could not be resolved: {parsed.hostname}") from e
126
+ raise ValueError(f"Webhook hostname could not be resolved: {hostname}") from e
69
127
 
70
128
  allow_match = _match_entries(hostname, resolved_ips, allow_hosts, allow_networks)
71
129
  deny_match = _match_entries(hostname, resolved_ips, deny_hosts, deny_networks)
@@ -527,6 +527,27 @@ class TestWebhookSecurityValidation(DjangoTestCase):
527
527
  mock_resolve.return_value = {"93.184.216.34"}
528
528
  validate_webhook_url("https://hooks.example.com/webhook")
529
529
 
530
+ @patch("alerts.service_backends.webhook_security._validate_raw_url_characters")
531
+ def test_rejects_backslash_exploit_that_tries_to_bypass_allowlist(self, mock_validate_raw_characters):
532
+ # We can't think of an exploit that would exploit discrepancies between requests' parser and other parsers but
533
+ # not already be caught by our raw character validation. Hence we need to turn off raw character validation in
534
+ # the test to test against the case of mismatch-exploiting that we do know to exist.
535
+ mock_validate_raw_characters.return_value = None
536
+
537
+ with override_bugsink_settings(
538
+ ALERTS_WEBHOOK_OUTBOUND_MODE="allowlist_only",
539
+ ALERTS_WEBHOOK_ALLOW_LIST=["whitelist.com"]):
540
+ with self.assertRaisesRegex(ValueError, "not allowlisted"):
541
+ validate_webhook_url(r"http://127.0.0.1:6666\@whitelist.com")
542
+
543
+ def test_rejects_raw_unicode_character(self):
544
+ with self.assertRaisesRegex(ValueError, "must contain only ASCII URL characters"):
545
+ validate_webhook_url("https://hooks.example.com/caf\xe9")
546
+
547
+ def test_rejects_raw_backslash_character(self):
548
+ with self.assertRaisesRegex(ValueError, "must contain only ASCII URL characters"):
549
+ validate_webhook_url(r"https://hooks.example.com\path")
550
+
530
551
  @patch("alerts.service_backends.webhook_security._resolve_ip_addresses")
531
552
  def test_denied_when_allow_and_deny_both_match(self, mock_resolve):
532
553
  mock_resolve.return_value = {"93.184.216.34"}
@@ -5,6 +5,7 @@ from django.core.checks import Warning, register
5
5
  from django.conf import settings
6
6
 
7
7
  from bugsink.app_settings import get_settings
8
+ from bugsink.utils import sends_email_as_bugsink_but_is_not_hosted_on_bugsink
8
9
  from events.storage_registry import get_write_storage
9
10
  from files.storage_registry import get_write_storage as get_object_write_storage
10
11
 
@@ -91,3 +92,23 @@ def check_proxy_env_vars_consistency(app_configs, **kwargs):
91
92
  )]
92
93
 
93
94
  return []
95
+
96
+
97
+ @register("bsmain")
98
+ def check_email_sender_domain(app_configs, **kwargs):
99
+ errors = []
100
+
101
+ for setting_name in ["DEFAULT_FROM_EMAIL", "SERVER_EMAIL"]:
102
+ if sends_email_as_bugsink_but_is_not_hosted_on_bugsink(
103
+ getattr(settings, setting_name),
104
+ settings.ALLOWED_HOSTS,
105
+ ):
106
+ errors.append(Warning(
107
+ f"{setting_name} uses the bugsink.com domain, but ALLOWED_HOSTS does not. This looks like a "
108
+ f"self-hosted Bugsink sending email as bugsink.com. That is effectively spam, deliverability will "
109
+ f"suffer badly, and those messages show up in Bugsink's DKIM reports. Configure your own sender "
110
+ f"address instead.",
111
+ id="bsmain.W006",
112
+ ))
113
+
114
+ return errors
@@ -34,7 +34,7 @@ class Command(BaseCommand):
34
34
  def handle(self, *args, **options):
35
35
  self._create_superuser_if_needed()
36
36
 
37
- # Similar considerations apply here as those which are documented in bugsink.views._phone_home().
37
+ # Similar considerations apply here as those which are documented in phonehome.utils.phone_home().
38
38
  # By putting this in prestart, we add one more location to the list of kick-off locations; with the added
39
39
  # benefit that this particular location also gives some signal for (Docker) installations that are prematurely
40
40
  # aborted (i.e. we get a ping even if 'home' is never even reached).
@@ -6,6 +6,7 @@ from django.utils import timezone
6
6
  from bugsink.app_settings import get_settings
7
7
  from events.tasks import delete_events_older_than_sync
8
8
  from files.tasks import vacuum_files_sync
9
+ from ingest.management.commands.vacuum_ingest_dir import vacuum_ingest_dir_sync
9
10
  from tags.tasks import vacuum_eventless_issuetags_sync, vacuum_tags_sync
10
11
 
11
12
 
@@ -33,6 +34,11 @@ class Command(BaseCommand):
33
34
  action='store_true',
34
35
  help="Delete events older than the configured maximum age.",
35
36
  )
37
+ parser.add_argument(
38
+ '--ingest-dir',
39
+ action='store_true',
40
+ help="Clean up stale files from the ingest directory.",
41
+ )
36
42
  parser.add_argument(
37
43
  '--chunk-max-days',
38
44
  type=int,
@@ -60,30 +66,41 @@ class Command(BaseCommand):
60
66
  type=int,
61
67
  help="Keep at most this many bytes across stored File objects. Defaults to MAX_STORED_FILE_BYTES.",
62
68
  )
69
+ parser.add_argument(
70
+ '--ingest-max-days',
71
+ type=int,
72
+ default=7,
73
+ help="Delete ingest-dir files older than this many days (default: 7).",
74
+ )
63
75
 
64
76
  def handle(self, *args, **options):
65
77
  run_files = options['files']
66
78
  run_tags = options['tags']
67
79
  run_eventless_issuetags = options['eventless_issuetags']
68
80
  run_old_events = options['old_events']
81
+ run_ingest_dir = options['ingest_dir']
69
82
 
70
- if not any([run_files, run_tags, run_eventless_issuetags, run_old_events]):
83
+ if not any([run_files, run_tags, run_eventless_issuetags, run_old_events, run_ingest_dir]):
71
84
  # If no specific options were provided, run all vacuum tasks by default.
72
85
  run_files = True
73
86
  run_tags = True
74
87
  run_eventless_issuetags = True
75
88
  run_old_events = True
89
+ run_ingest_dir = True
76
90
 
77
91
  if run_files:
78
92
  settings = get_settings()
79
93
  max_file_count = options['max_file_count']
80
94
  max_file_bytes = options['max_file_bytes']
81
95
  self.stdout.write("Vacuuming files...")
96
+
97
+ log_progress = self.stdout.write if options["verbosity"] >= 2 else lambda _message: None
82
98
  vacuum_files_sync(
83
99
  chunk_max_days=options['chunk_max_days'],
84
100
  file_max_days=options['file_max_days'],
85
101
  max_file_count=settings.MAX_STORED_FILE_COUNT if max_file_count is None else max_file_count,
86
102
  max_file_bytes=settings.MAX_STORED_FILE_BYTES if max_file_bytes is None else max_file_bytes,
103
+ log_progress=log_progress,
87
104
  )
88
105
 
89
106
  if run_eventless_issuetags:
@@ -107,4 +124,8 @@ class Command(BaseCommand):
107
124
  cutoff=timezone.now() - timedelta(days=days),
108
125
  )
109
126
 
127
+ if run_ingest_dir:
128
+ self.stdout.write("Vacuuming ingest dir...")
129
+ vacuum_ingest_dir_sync(days=options["ingest_max_days"], stdout=self.stdout, stderr=self.stderr)
130
+
110
131
  self.stdout.write(self.style.SUCCESS("Vacuum complete."))
@@ -217,8 +217,9 @@ class VacuumFilesBatchTestCase(TransactionTestCase):
217
217
  File.objects.filter(id=recent.id).update(accessed_at=now - timedelta(days=2))
218
218
  File.objects.filter(id=newest.id).update(accessed_at=now - timedelta(days=1))
219
219
 
220
- has_more_work = vacuum_files_batch(file_max_days=90, max_file_count=1)
220
+ has_more_work, num_deleted = vacuum_files_batch(file_max_days=90, max_file_count=1)
221
221
 
222
222
  self.assertFalse(has_more_work)
223
+ self.assertEqual(3, num_deleted)
223
224
  self.assertEqual([newest.id], list(File.objects.values_list("id", flat=True)))
224
225
  self.assertEqual([newest.id], list(FileMetadata.objects.values_list("file_id", flat=True)))
@@ -0,0 +1,45 @@
1
+ from django.core.checks import run_checks
2
+ from django.test import SimpleTestCase, override_settings
3
+
4
+
5
+ class SystemChecksTestCase(SimpleTestCase):
6
+ def _warnings(self):
7
+ return [warning for warning in run_checks(tags=["bsmain"]) if warning.id == "bsmain.W006"]
8
+
9
+ @override_settings(DEFAULT_FROM_EMAIL="Bugsink <bugsink@example.org>", SERVER_EMAIL="server@example.org")
10
+ def test_email_sender_domain_check_allows_non_bugsink_domain(self):
11
+ self.assertEqual([], self._warnings())
12
+
13
+ @override_settings(DEFAULT_FROM_EMAIL="Bugsink <alerts@bugsink.com>", SERVER_EMAIL="server@example.org")
14
+ def test_email_sender_domain_check_warns_for_default_from_email(self):
15
+ warnings = self._warnings()
16
+
17
+ self.assertEqual(1, len(warnings))
18
+ self.assertEqual(
19
+ "DEFAULT_FROM_EMAIL uses the bugsink.com domain, but ALLOWED_HOSTS does not. This looks like a "
20
+ "self-hosted Bugsink sending email as bugsink.com. That is effectively spam, deliverability will "
21
+ "suffer badly, and those messages show up in Bugsink's DKIM reports. Configure your own sender "
22
+ "address instead.",
23
+ warnings[0].msg,
24
+ )
25
+
26
+ @override_settings(DEFAULT_FROM_EMAIL="Bugsink <bugsink@example.org>", SERVER_EMAIL="server@bugsink.com")
27
+ def test_email_sender_domain_check_warns_for_server_email(self):
28
+ warnings = self._warnings()
29
+
30
+ self.assertEqual(1, len(warnings))
31
+ self.assertEqual(
32
+ "SERVER_EMAIL uses the bugsink.com domain, but ALLOWED_HOSTS does not. This looks like a "
33
+ "self-hosted Bugsink sending email as bugsink.com. That is effectively spam, deliverability will "
34
+ "suffer badly, and those messages show up in Bugsink's DKIM reports. Configure your own sender "
35
+ "address instead.",
36
+ warnings[0].msg,
37
+ )
38
+
39
+ @override_settings(
40
+ DEFAULT_FROM_EMAIL="alerts@bugsink.com",
41
+ SERVER_EMAIL="server@example.org",
42
+ ALLOWED_HOSTS=["selfhosted.bugsink.com"],
43
+ )
44
+ def test_email_sender_domain_check_allows_bugsink_domain_when_allowed_hosts_match(self):
45
+ self.assertEqual([], self._warnings())
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.1.2'
22
- __version_tuple__ = version_tuple = (2, 1, 2)
21
+ __version__ = version = '2.2.0'
22
+ __version_tuple__ = version_tuple = (2, 2, 0)
23
23
 
24
- __commit_id__ = commit_id = 'ge1114c9fa'
24
+ __commit_id__ = commit_id = 'gdac28a708'
@@ -52,7 +52,7 @@ EMAIL_BACKEND = "bugsink.email_backends.QuietConsoleEmailBackend" # instead of
52
52
  # EMAIL_PORT = ...
53
53
  # EMAIL_USE_TLS = ...
54
54
  # EMAIL_USE_SSL = ...
55
- # SERVER_EMAIL = DEFAULT_FROM_EMAIL = "Bugsink <bugsink@example.org>"
55
+ # SERVER_EMAIL = DEFAULT_FROM_EMAIL = "Bugsink <bugsink@example.org>" # don't use ...@bugsink.com if you're not bugsink
56
56
 
57
57
  # constants for "create by" (user/team/project) settings
58
58
  CB_ANYBODY = "CB_ANYBODY"
@@ -77,8 +77,7 @@ EMAIL_BACKEND = "bugsink.email_backends.QuietConsoleEmailBackend" # instead of
77
77
  # Uncomment the line below to show all sent emails in the logs
78
78
  # LOGGING['loggers']['bugsink.email']['level'] = "INFO"
79
79
 
80
-
81
- SERVER_EMAIL = DEFAULT_FROM_EMAIL = "Bugsink <bugsink@example.org>"
80
+ SERVER_EMAIL = DEFAULT_FROM_EMAIL = "Bugsink <bugsink@example.org>" # don't use ...@bugsink.com if you're not bugsink
82
81
 
83
82
  # constants for "create by" (user/team/project) settings
84
83
  CB_ANYBODY = "CB_ANYBODY"
@@ -79,7 +79,7 @@ EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
79
79
  EMAIL_PORT = 587
80
80
  EMAIL_USE_TLS = True
81
81
 
82
- SERVER_EMAIL = DEFAULT_FROM_EMAIL = 'Klaas van Schelven <klaas@bugsink.com>'
82
+ SERVER_EMAIL = DEFAULT_FROM_EMAIL = 'Bugsink Development Server <bugsink@example.org>'
83
83
 
84
84
 
85
85
  BUGSINK = {
@@ -4,9 +4,12 @@ import re
4
4
  import brotli
5
5
 
6
6
  from unittest import TestCase as RegularTestCase
7
+ from unittest.mock import patch
7
8
  from django.test import TestCase as DjangoTestCase
9
+ from django.test import SimpleTestCase
8
10
  from django.test import override_settings
9
11
  from django.core.exceptions import SuspiciousOperation
12
+ from django.core import mail
10
13
  from django.contrib.auth import get_user_model
11
14
  from django.test.utils import CaptureQueriesContext
12
15
  from django.db import connection
@@ -15,6 +18,7 @@ from .wsgi import allowed_hosts_error_message
15
18
  from .test_utils import TransactionTestCase25251 as TransactionTestCase
16
19
  from .transaction import immediate_atomic
17
20
  from .volume_based_condition import VolumeBasedCondition
21
+ from .utils import send_rendered_email
18
22
  from .streams import (
19
23
  compress_with_zlib, GeneratorReader, WBITS_PARAM_FOR_GZIP, WBITS_PARAM_FOR_DEFLATE, MaxDataReader,
20
24
  MaxDataWriter, zlib_generator, brotli_generator, BrotliError)
@@ -38,6 +42,37 @@ class VolumeBasedConditionTestCase(RegularTestCase):
38
42
  self.assertEqual(vbc, vbc2)
39
43
 
40
44
 
45
+ @override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend")
46
+ class SendRenderedEmailTestCase(SimpleTestCase):
47
+ def setUp(self):
48
+ mail.outbox = []
49
+
50
+ @override_settings(DEFAULT_FROM_EMAIL="Bugsink <alerts@bugsink.com>", ALLOWED_HOSTS=["selfhosted.example.com"])
51
+ def test_send_rendered_email_refuses_self_hosted_bugsink_sender_domain(self):
52
+ with self.assertLogs("bugsink.email", level="ERROR") as logs:
53
+ send_rendered_email(
54
+ "Subject",
55
+ "mails/welcome_email",
56
+ ["user@example.com"],
57
+ {"reason": "Reason", "reset_url": "https://example.com/reset"},
58
+ )
59
+
60
+ self.assertEqual([], mail.outbox)
61
+ self.assertIn("Refusing to send email with subject 'Subject'", logs.output[0])
62
+
63
+ @override_settings(DEFAULT_FROM_EMAIL="Bugsink <alerts@bugsink.com>", ALLOWED_HOSTS=["tenant.bugsink.com"])
64
+ @patch("phonehome.models.Installation.check_and_inc_email_quota", return_value=True)
65
+ def test_send_rendered_email_allows_bugsink_sender_domain_when_hosted_on_bugsink_domain(self, _quota_ok):
66
+ send_rendered_email(
67
+ "Subject",
68
+ "mails/welcome_email",
69
+ ["user@example.com"],
70
+ {"reason": "Reason", "reset_url": "https://example.com/reset"},
71
+ )
72
+
73
+ self.assertEqual(1, len(mail.outbox))
74
+
75
+
41
76
  class StreamsTestCase(RegularTestCase):
42
77
 
43
78
  def test_compress_decompress_gzip(self):
@@ -5,6 +5,7 @@ from collections import defaultdict
5
5
  from django.utils import timezone
6
6
  from django.utils.http import url_has_allowed_host_and_scheme
7
7
  from django.core.mail import EmailMultiAlternatives
8
+ from django.conf import settings
8
9
  from django.template.loader import get_template
9
10
  from django.apps import apps
10
11
  from django.db.models import ForeignKey, F
@@ -33,6 +34,20 @@ def is_safe_next_url(url, request):
33
34
  )
34
35
 
35
36
 
37
+ def sends_email_as_bugsink_but_is_not_hosted_on_bugsink(from_email, allowed_hosts):
38
+ if from_email is None:
39
+ return False
40
+
41
+ normalized_from_email = str(from_email).strip().lower()
42
+ if not (
43
+ normalized_from_email.endswith("@bugsink.com") or
44
+ normalized_from_email.endswith("@bugsink.com>")
45
+ ):
46
+ return False
47
+
48
+ return not any(str(host).strip().lower().lstrip(".").endswith("bugsink.com") for host in allowed_hosts)
49
+
50
+
36
51
  def send_rendered_email(subject, base_template_name, recipient_list, context=None):
37
52
  from phonehome.models import Installation
38
53
 
@@ -42,6 +57,17 @@ def send_rendered_email(subject, base_template_name, recipient_list, context=Non
42
57
  if not recipient_list:
43
58
  return
44
59
 
60
+ if sends_email_as_bugsink_but_is_not_hosted_on_bugsink(settings.DEFAULT_FROM_EMAIL, settings.ALLOWED_HOSTS):
61
+ logger.error(
62
+ "Refusing to send email with subject '%s' to %s because DEFAULT_FROM_EMAIL=%r uses bugsink.com while "
63
+ "ALLOWED_HOSTS=%r does not. This looks like a self-hosted Bugsink trying to send as bugsink.com.",
64
+ subject,
65
+ recipient_list,
66
+ settings.DEFAULT_FROM_EMAIL,
67
+ settings.ALLOWED_HOSTS,
68
+ )
69
+ return
70
+
45
71
  if not Installation.check_and_inc_email_quota(timezone.now()):
46
72
  logger.warning(
47
73
  "Email quota exceeded; not sending email with subject '%s' to %s",
@@ -31,7 +31,7 @@ from bugsink.decorators import atomic_for_request_method
31
31
  from bugsink.timed_sqlite_backend.base import different_runtime_limit
32
32
  from bugsink.utils import is_safe_next_url
33
33
 
34
- from phonehome.tasks import send_if_due
34
+ from phonehome.utils import phone_home
35
35
  from phonehome.models import Installation
36
36
 
37
37
  from ingest.views import BaseIngestAPIView
@@ -58,31 +58,12 @@ debug.technical_404_response = cors_for_api_view(debug.technical_404_response)
58
58
  debug.technical_500_response = cors_for_api_view(debug.technical_500_response)
59
59
 
60
60
 
61
- def _phone_home():
62
- # I need a way to cron-like run tasks that works for the setup with and without snappea. With snappea it's straight-
63
- # forward (though not part of snappea _yet_). Without snappea, you'd need _some_ location to do a "poor man's cron"
64
- # check. Server-start would be the first thing to consider, but how to do this across gunicorn, debugserver, and
65
- # possibly even non-standard (for Bugsink) wsgi servers? Better go the "just pick some request to do the check"
66
- # route. I've picked "home", because [a] it's assumed to be somewhat regularly visited [b] there's no transaction
67
- # logic in it, which leaves space for transaction-logic in the phone-home task itself and [c] some alternatives are
68
- # a no-go (ingestion: on a tight budget; login: not visited when a long-lived session is active).
69
- #
70
- # having chosen the solution for the non-snappea case, I got the crazy idea of using it for the snappea case too,
71
- # i.e. just put a .delay() here and let the config choose. Not so crazy though, because [a] saves us from new
72
- # features in snappea, [b] we introduce a certain symmetry of measurement between the 2 setups, i.e. the choice of
73
- # lazyness does not influence counting and [c] do I really want to get pings for sites where nobody visits home()?
74
-
75
- # NOTE: each time this function is called, it will schedule a new task, even when the task would quickly return
76
- # (nothing due, or configured to never send). We _could_ improve that, but doesn't seem performance-critical enough.
77
- send_if_due.delay() # _phone_home() wrapper serves as a place for the comment above
78
-
79
-
61
+ @phone_home
80
62
  def home(request):
81
- _phone_home()
82
-
83
- if request.user.project_set.filter(projectmembership__accepted=True).distinct().count() == 1:
63
+ accepted_projects = request.user.project_set.filter(projectmembership__accepted=True).distinct()
64
+ if accepted_projects.count() == 1:
84
65
  # if the user has exactly one project, we redirect them to that project
85
- project = request.user.project_set.get()
66
+ project = accepted_projects.get()
86
67
  return redirect("issue_list_open", project_pk=project.id)
87
68
 
88
69
  if request.user.project_set.all().distinct().count() > 0:
@@ -94,7 +94,7 @@ class CustomWSGIRequest(WSGIRequest):
94
94
  We're leaking a bit of information here, but I don't think it's too much TBH -- especially in the light of ssl
95
95
  certificates being specifically tied to the domain name.
96
96
  """
97
- if self.path.startswith == "/health/":
97
+ if self.path.startswith("/health/"):
98
98
  # For /health/ endpoints, we skip the ALLOWED_HOSTS validation (see #140).
99
99
  return self._get_raw_host()
100
100
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bugsink
3
- Version: 2.1.2
3
+ Version: 2.2.0
4
4
  Summary: Self-hosted Error Tracking
5
5
  Author-email: "Bugsink B.V." <info@bugsink.com>
6
6
  Project-URL: homepage, https://www.bugsink.com/
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.14
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: gunicorn==25.1.*
19
+ Requires-Dist: gunicorn==26.0.*
20
20
  Requires-Dist: Django==5.2.*
21
21
  Requires-Dist: verbose_csrf_middleware==5.2.*
22
22
  Requires-Dist: sentry-sdk==2.*
@@ -29,8 +29,8 @@ Requires-Dist: inotify_simple==2.0.*
29
29
  Requires-Dist: Brotli==1.2.*
30
30
  Requires-Dist: python-dateutil==2.9.*
31
31
  Requires-Dist: whitenoise==6.12.*
32
- Requires-Dist: requests==2.33.*
33
- Requires-Dist: monofy==1.1.*
32
+ Requires-Dist: requests==2.34.*
33
+ Requires-Dist: monofy==1.2.*
34
34
  Requires-Dist: user-agents==2.2.*
35
35
  Requires-Dist: fastjsonschema==2.21.*
36
36
  Requires-Dist: ecma426>=0.2.0