Skip to content

Commit 306194f

Browse files
authored
Feature flag: remove unused ones (#10423)
* Feature flag: remove `USE_SPHINX_BUILDERS` We can safely remove this feature flag since it's marked as `default_true=True` and `future_default_true=True` in both platforms. This means that it applies to all the projects. * Feature flag: remove `SPHINX_PARALLEL` It was a failed attempt to make Sphinx to run fast. * Feature flag: remove `CLEAN_AFTER_BUILD` It became a Django setting and it's integrated in the application now. * Feature flag: remove `CANCEL_OLD_BUILDS` It's enabled by default to all the projects and it has been working fine. * Feature flag: remove `LIST_PACKAGES_INSTALLED_ENV` This was an attempt to show debugging information. This can be achieved now with `build.jobs` in case the user want to display this data. Internally, we are storing some of this data in BuildData from `telemetry` database. * Feature flag: remove `VCS_REMOTE_LISTING` We have been using this by default in all projects. * Feature flag: remove `USE_RCLONE` We have been using this for a long time and we are happy. * Feature flag: remove `CACHED_ENVIRONMENT` We removed this code completely and we are not doing this anymore. * Feature flag: remove `ALLOW_DEPRECATED_WEBHOOKS` It's not used. Only leftovers. * Feature flag: remove `USE_TESTING_BUILD_IMAGE` Only used by 1 projects. Its last build was 2 years ago. * Feature flag: remove `LIMIT_CONCURRENT_BUILDS` We have been using this by default to everybody for months now and it's working fine. It's safe to remove. * Test: replace `storage.sync_directory` with `storage.rclone_sync_directory` * Remove leftovers from deprecated webhooks * Test: check for the original builder * Test: fix them all (hopefully) * Lint * More linting * Test: mock required function
1 parent bfeb6d8 commit 306194f

20 files changed

+81
-405
lines changed

readthedocs/builds/storage.py

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -128,60 +128,6 @@ def _check_suspicious_path(self, path):
128128
log.error(msg, path=str(path), resolved_path=str(resolved_path))
129129
raise SuspiciousFileOperation(msg)
130130

131-
def sync_directory(self, source, destination):
132-
"""
133-
Sync a directory recursively to storage.
134-
135-
Overwrites files in remote storage with files from ``source`` (no timstamp/hash checking).
136-
Removes files and folders in remote storage that are not present in ``source``.
137-
138-
:param source: the source path on the local disk
139-
:param destination: the destination path in storage
140-
"""
141-
if destination in ('', '/'):
142-
raise SuspiciousFileOperation('Syncing all storage cannot be right')
143-
144-
source = Path(source)
145-
self._check_suspicious_path(source)
146-
147-
log.debug(
148-
'Syncing to media storage.',
149-
source=str(source),
150-
destination=destination,
151-
)
152-
153-
copied_files = set()
154-
copied_dirs = set()
155-
for filepath in source.iterdir():
156-
sub_destination = self.join(destination, filepath.name)
157-
# Don't follow symlinks when uploading to storage.
158-
if filepath.is_symlink():
159-
log.info(
160-
"Skipping symlink upload.",
161-
path_resolved=str(filepath.resolve()),
162-
)
163-
continue
164-
165-
if filepath.is_dir():
166-
# Recursively sync the subdirectory
167-
self.sync_directory(filepath, sub_destination)
168-
copied_dirs.add(filepath.name)
169-
elif filepath.is_file():
170-
with safe_open(filepath, "rb") as fd:
171-
self.save(sub_destination, fd)
172-
copied_files.add(filepath.name)
173-
174-
# Remove files that are not present in ``source``
175-
dest_folders, dest_files = self.listdir(self._dirpath(destination))
176-
for folder in dest_folders:
177-
if folder not in copied_dirs:
178-
self.delete_directory(self.join(destination, folder))
179-
for filename in dest_files:
180-
if filename not in copied_files:
181-
filepath = self.join(destination, filename)
182-
log.debug('Deleting file from media storage.', filepath=filepath)
183-
self.delete(filepath)
184-
185131
@cached_property
186132
def _rclone(self):
187133
raise NotImplementedError

readthedocs/builds/tests/test_trigger_build.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import pytest
55

66
from readthedocs.builds.constants import (
7-
BUILD_FINAL_STATES,
87
BUILD_STATE_BUILDING,
98
BUILD_STATE_CANCELLED,
109
BUILD_STATE_CLONING,
@@ -15,7 +14,7 @@
1514
)
1615
from readthedocs.builds.models import Build, Version
1716
from readthedocs.core.utils import trigger_build
18-
from readthedocs.projects.models import Feature, Project
17+
from readthedocs.projects.models import Project
1918

2019

2120
@pytest.mark.django_db
@@ -24,8 +23,6 @@ class TestCancelOldBuilds:
2423
def setup(self):
2524
self.project = fixture.get(Project)
2625
self.version = fixture.get(Version, project=self.project)
27-
self.feature = fixture.get(Feature, feature_id=Feature.CANCEL_OLD_BUILDS)
28-
self.feature.projects.add(self.project)
2926

3027
@pytest.mark.parametrize(
3128
"state",

readthedocs/core/utils/__init__.py

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def prepare_build(
4545
# Avoid circular import
4646
from readthedocs.builds.models import Build
4747
from readthedocs.builds.tasks import send_build_notifications
48-
from readthedocs.projects.models import Feature, Project, WebHookEvent
48+
from readthedocs.projects.models import Project, WebHookEvent
4949
from readthedocs.projects.tasks.builds import update_docs_task
5050
from readthedocs.projects.tasks.utils import send_external_build_status
5151

@@ -118,45 +118,44 @@ def prepare_build(
118118
)
119119

120120
# Reduce overhead when doing multiple push on the same version.
121-
if project.has_feature(Feature.CANCEL_OLD_BUILDS):
122-
running_builds = (
123-
Build.objects
124-
.filter(
125-
project=project,
126-
version=version,
127-
).exclude(
128-
state__in=BUILD_FINAL_STATES,
129-
).exclude(
130-
pk=build.pk,
131-
)
121+
running_builds = (
122+
Build.objects.filter(
123+
project=project,
124+
version=version,
125+
)
126+
.exclude(
127+
state__in=BUILD_FINAL_STATES,
128+
)
129+
.exclude(
130+
pk=build.pk,
131+
)
132+
)
133+
if running_builds.count() > 0:
134+
log.warning(
135+
"Canceling running builds automatically due a new one arrived.",
136+
running_builds=running_builds.count(),
132137
)
133-
if running_builds.count() > 0:
134-
log.warning(
135-
"Canceling running builds automatically due a new one arrived.",
136-
running_builds=running_builds.count(),
137-
)
138138

139-
# If there are builds triggered/running for this particular project and version,
140-
# we cancel all of them and trigger a new one for the latest commit received.
141-
for running_build in running_builds:
142-
cancel_build(running_build)
139+
# If there are builds triggered/running for this particular project and version,
140+
# we cancel all of them and trigger a new one for the latest commit received.
141+
for running_build in running_builds:
142+
cancel_build(running_build)
143143

144144
# Start the build in X minutes and mark it as limited
145-
if project.has_feature(Feature.LIMIT_CONCURRENT_BUILDS):
146-
limit_reached, _, max_concurrent_builds = Build.objects.concurrent(project)
147-
if limit_reached:
148-
log.warning(
149-
'Delaying tasks at trigger step due to concurrency limit.',
150-
)
151-
# Delay the start of the build for the build retry delay.
152-
# We're still triggering the task, but it won't run immediately,
153-
# and the user will be alerted in the UI from the Error below.
154-
options['countdown'] = settings.RTD_BUILDS_RETRY_DELAY
155-
options['max_retries'] = settings.RTD_BUILDS_MAX_RETRIES
156-
build.error = BuildMaxConcurrencyError.message.format(
157-
limit=max_concurrent_builds,
158-
)
159-
build.save()
145+
limit_reached, _, max_concurrent_builds = Build.objects.concurrent(project)
146+
if limit_reached:
147+
log.warning(
148+
"Delaying tasks at trigger step due to concurrency limit.",
149+
)
150+
# Delay the start of the build for the build retry delay.
151+
# We're still triggering the task, but it won't run immediately,
152+
# and the user will be alerted in the UI from the Error below.
153+
options["countdown"] = settings.RTD_BUILDS_RETRY_DELAY
154+
options["max_retries"] = settings.RTD_BUILDS_MAX_RETRIES
155+
build.error = BuildMaxConcurrencyError.message.format(
156+
limit=max_concurrent_builds,
157+
)
158+
build.save()
160159

161160
return (
162161
update_docs_task.signature(
@@ -263,7 +262,7 @@ def cancel_build(build):
263262
def send_email(
264263
recipient, subject, template, template_html, context=None, request=None,
265264
from_email=None, **kwargs
266-
): # pylint: disable=unused-argument
265+
):
267266
"""
268267
Alter context passed in and call email send task.
269268

readthedocs/doc_builder/backends/sphinx.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,10 @@ def append_conf(self):
292292
raise UserFileNotFound(
293293
UserFileNotFound.FILE_NOT_FOUND.format(self.config_file)
294294
)
295-
except IOError:
296-
raise ProjectConfigurationError(ProjectConfigurationError.NOT_FOUND)
295+
except IOError as exc:
296+
raise ProjectConfigurationError(
297+
ProjectConfigurationError.NOT_FOUND
298+
) from exc
297299

298300
# Append config to project conf file
299301
tmpl = template_loader.get_template('doc_builder/conf.py.tmpl')
@@ -320,7 +322,6 @@ def build(self):
320322
*self.get_sphinx_cmd(),
321323
'-T',
322324
'-E',
323-
*self.sphinx_parallel_arg(),
324325
]
325326
if self.config.sphinx.fail_on_warning:
326327
build_command.extend(["-W", "--keep-going"])
@@ -359,38 +360,27 @@ def get_sphinx_cmd(self):
359360
'sphinx',
360361
)
361362

362-
def sphinx_parallel_arg(self):
363-
if self.project.has_feature(Feature.SPHINX_PARALLEL):
364-
return ['-j', 'auto']
365-
return []
366-
367363

368364
class HtmlBuilder(BaseSphinx):
369365
relative_output_dir = "html"
370366

371367
def __init__(self, *args, **kwargs):
372368
super().__init__(*args, **kwargs)
373-
self.sphinx_builder = 'readthedocs'
374-
if self.project.has_feature(Feature.USE_SPHINX_BUILDERS):
375-
self.sphinx_builder = 'html'
369+
self.sphinx_builder = "html"
376370

377371

378372
class HtmlDirBuilder(HtmlBuilder):
379373

380374
def __init__(self, *args, **kwargs):
381375
super().__init__(*args, **kwargs)
382-
self.sphinx_builder = 'readthedocsdirhtml'
383-
if self.project.has_feature(Feature.USE_SPHINX_BUILDERS):
384-
self.sphinx_builder = 'dirhtml'
376+
self.sphinx_builder = "dirhtml"
385377

386378

387379
class SingleHtmlBuilder(HtmlBuilder):
388380

389381
def __init__(self, *args, **kwargs):
390382
super().__init__(*args, **kwargs)
391-
self.sphinx_builder = 'readthedocssinglehtml'
392-
if self.project.has_feature(Feature.USE_SPHINX_BUILDERS):
393-
self.sphinx_builder = 'singlehtml'
383+
self.sphinx_builder = "singlehtml"
394384

395385

396386
class LocalMediaBuilder(BaseSphinx):
@@ -518,7 +508,6 @@ def build(self):
518508
*self.get_sphinx_cmd(),
519509
"-T",
520510
"-E",
521-
*self.sphinx_parallel_arg(),
522511
"-b",
523512
self.sphinx_builder,
524513
"-d",

readthedocs/doc_builder/director.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,6 @@ def setup_environment(self):
180180
self.install()
181181
self.run_build_job("post_install")
182182

183-
# TODO: remove this and document how to do it on `build.jobs.post_install`
184-
if self.data.project.has_feature(Feature.LIST_PACKAGES_INSTALLED_ENV):
185-
self.language_environment.list_packages_installed()
186-
187183
def build(self):
188184
"""
189185
Build all the formats specified by the user.

readthedocs/doc_builder/environments.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def run(self):
156156
command = self.get_command()
157157

158158
stderr = subprocess.PIPE if self.demux else subprocess.STDOUT
159-
proc = subprocess.Popen(
159+
proc = subprocess.Popen( # pylint: disable=consider-using-with
160160
command,
161161
shell=self.shell,
162162
cwd=self.cwd,
@@ -574,10 +574,7 @@ def __init__(self, *args, **kwargs):
574574
self.use_gvisor = True
575575

576576
# Decide what Docker image to use, based on priorities:
577-
# Use the Docker image set by our feature flag: ``testing`` or,
578-
if self.project.has_feature(Feature.USE_TESTING_BUILD_IMAGE):
579-
self.container_image = 'readthedocs/build:testing'
580-
# the image set by user or,
577+
# The image set by user or,
581578
if self.config and self.config.docker_image:
582579
self.container_image = self.config.docker_image
583580
# the image overridden by the project (manually set by an admin).
@@ -633,8 +630,8 @@ def __enter__(self):
633630
)
634631
client = self.get_client()
635632
client.remove_container(self.container_id)
636-
except (DockerAPIError, ConnectionError) as e:
637-
raise BuildAppError(e.explanation)
633+
except (DockerAPIError, ConnectionError) as exc:
634+
raise BuildAppError(exc.explanation) from exc
638635

639636
# Create the checkout path if it doesn't exist to avoid Docker creation
640637
if not os.path.exists(self.project.doc_path):
@@ -707,8 +704,8 @@ def get_client(self):
707704
version=DOCKER_VERSION,
708705
)
709706
return self.client
710-
except DockerException as e:
711-
raise BuildAppError(e.explanation)
707+
except DockerException as exc:
708+
raise BuildAppError(exc.explanation) from exc
712709

713710
def _get_binds(self):
714711
"""
@@ -825,5 +822,5 @@ def create_container(self):
825822
runtime=docker_runtime,
826823
)
827824
client.start(container=self.container_id)
828-
except (DockerAPIError, ConnectionError) as e:
829-
raise BuildAppError(e.explanation)
825+
except (DockerAPIError, ConnectionError) as exc:
826+
raise BuildAppError(exc.explanation) from exc

readthedocs/doc_builder/python_environments.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -307,22 +307,6 @@ def install_requirements_file(self, install):
307307
bin_path=self.venv_bin(),
308308
)
309309

310-
def list_packages_installed(self):
311-
"""List packages installed in pip."""
312-
args = [
313-
self.venv_bin(filename='python'),
314-
'-m',
315-
'pip',
316-
'list',
317-
# Include pre-release versions.
318-
'--pre',
319-
]
320-
self.build_env.run(
321-
*args,
322-
cwd=self.checkout_path,
323-
bin_path=self.venv_bin(),
324-
)
325-
326310

327311
class Conda(PythonEnvironment):
328312

@@ -542,17 +526,3 @@ def install_requirements_file(self, install):
542526
# as the conda environment was created by using the ``environment.yml``
543527
# defined by the user, there is nothing to update at this point
544528
pass
545-
546-
def list_packages_installed(self):
547-
"""List packages installed in conda."""
548-
args = [
549-
self.conda_bin_name(),
550-
'list',
551-
'--name',
552-
self.version.slug,
553-
]
554-
self.build_env.run(
555-
*args,
556-
cwd=self.checkout_path,
557-
bin_path=self.venv_bin(),
558-
)

readthedocs/projects/admin.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@
2828
WebHook,
2929
WebHookEvent,
3030
)
31-
from .notifications import (
32-
DeprecatedBuildWebhookNotification,
33-
DeprecatedGitHubWebhookNotification,
34-
ResourceUsageNotification,
35-
)
31+
from .notifications import ResourceUsageNotification
3632
from .tag_utils import import_tags
3733
from .tasks.utils import clean_project_resources
3834

@@ -56,8 +52,6 @@ def has_delete_permission(self, request, obj=None):
5652
class ProjectSendNotificationView(SendNotificationView):
5753
notification_classes = [
5854
ResourceUsageNotification,
59-
DeprecatedBuildWebhookNotification,
60-
DeprecatedGitHubWebhookNotification,
6155
]
6256

6357
def get_object_recipients(self, obj):

0 commit comments

Comments
 (0)