|
3 | 3 |
|
4 | 4 | import structlog
|
5 | 5 | from celery.worker.request import Request
|
| 6 | +from django.core.cache import cache |
6 | 7 | from django.db.models import Q
|
7 | 8 | from django.utils import timezone
|
8 | 9 | from django.utils.translation import gettext_lazy as _
|
| 10 | +from messages_extends.constants import WARNING_PERSISTENT |
9 | 11 |
|
10 | 12 | from readthedocs.builds.constants import (
|
11 | 13 | BUILD_FINAL_STATES,
|
|
14 | 16 | )
|
15 | 17 | from readthedocs.builds.models import Build
|
16 | 18 | from readthedocs.builds.tasks import send_build_status
|
| 19 | +from readthedocs.core.permissions import AdminPermission |
17 | 20 | from readthedocs.core.utils.filesystem import safe_rmtree
|
| 21 | +from readthedocs.notifications import Notification, SiteNotification |
| 22 | +from readthedocs.notifications.backends import EmailBackend |
| 23 | +from readthedocs.notifications.constants import REQUIREMENT |
18 | 24 | from readthedocs.storage import build_media_storage
|
19 | 25 | from readthedocs.worker import app
|
20 | 26 |
|
@@ -154,6 +160,81 @@ def send_external_build_status(version_type, build_pk, commit, status):
|
154 | 160 | send_build_status.delay(build_pk, commit, status)
|
155 | 161 |
|
156 | 162 |
|
| 163 | +class DeprecatedConfigFileSiteNotification(SiteNotification): |
| 164 | + |
| 165 | + failure_message = ( |
| 166 | + "Your project '{{ object.slug }}' doesn't have a " |
| 167 | + '<a href="https://docs.readthedocs.io/en/stable/config-file/v2.html">.readthedocs.yaml</a> ' |
| 168 | + "configuration file. " |
| 169 | + "This feature is <strong>deprecated and will be removed soon</strong>. " |
| 170 | + "Make sure to create one for your project to keep your builds working." |
| 171 | + ) |
| 172 | + failure_level = WARNING_PERSISTENT |
| 173 | + |
| 174 | + |
| 175 | +class DeprecatedConfigFileEmailNotification(Notification): |
| 176 | + |
| 177 | + app_templates = "projects" |
| 178 | + name = "deprecated_config_file_used" |
| 179 | + context_object_name = "project" |
| 180 | + subject = "Your project will start failing soon" |
| 181 | + level = REQUIREMENT |
| 182 | + |
| 183 | + def send(self): |
| 184 | + """Method overwritten to remove on-site backend.""" |
| 185 | + backend = EmailBackend(self.request) |
| 186 | + backend.send(self) |
| 187 | + |
| 188 | + |
| 189 | +@app.task(queue="web") |
| 190 | +def deprecated_config_file_used_notification(build_pk): |
| 191 | + """ |
| 192 | + Create a notification about not using a config file for all the maintainers of the project. |
| 193 | +
|
| 194 | + This task is triggered by the build process to be executed on the webs, |
| 195 | + since we don't have access to the db from the build. |
| 196 | + """ |
| 197 | + build = Build.objects.filter(pk=build_pk).first() |
| 198 | + if not build or not build.deprecated_config_used: |
| 199 | + return |
| 200 | + |
| 201 | + log.bind( |
| 202 | + build_pk=build_pk, |
| 203 | + project_slug=build.project.slug, |
| 204 | + ) |
| 205 | + |
| 206 | + users = AdminPermission.owners(build.project) |
| 207 | + log.bind(users=len(users)) |
| 208 | + |
| 209 | + log.info("Sending deprecation config file onsite notification.") |
| 210 | + for user in users: |
| 211 | + n = DeprecatedConfigFileSiteNotification( |
| 212 | + user=user, |
| 213 | + context_object=build.project, |
| 214 | + success=False, |
| 215 | + ) |
| 216 | + n.send() |
| 217 | + |
| 218 | + # Send email notifications only once a week |
| 219 | + cache_prefix = "deprecated-config-file-notification" |
| 220 | + cached = cache.get(f"{cache_prefix}-{build.project.slug}") |
| 221 | + if cached: |
| 222 | + log.info("Deprecation config file email sent recently. Skipping.") |
| 223 | + return |
| 224 | + |
| 225 | + log.info("Sending deprecation config file email notification.") |
| 226 | + for user in users: |
| 227 | + n = DeprecatedConfigFileEmailNotification( |
| 228 | + user=user, |
| 229 | + context_object=build.project, |
| 230 | + ) |
| 231 | + n.send() |
| 232 | + |
| 233 | + # Cache this notification for a week |
| 234 | + # TODO: reduce this notification period to 3 days after having this deployed for some weeks |
| 235 | + cache.set(f"{cache_prefix}-{build.project.slug}", "sent", timeout=7 * 24 * 60 * 60) |
| 236 | + |
| 237 | + |
157 | 238 | class BuildRequest(Request):
|
158 | 239 |
|
159 | 240 | def on_timeout(self, soft, timeout):
|
|
0 commit comments