Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add project: use an instance variable to avoid 500 #11795

Merged
merged 10 commits into from
Dec 4, 2024
5 changes: 4 additions & 1 deletion readthedocs/projects/urls/private.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
),
path(
"import/manual/",
ImportWizardView.as_view(),
ImportWizardView.as_view(
ImportWizardView.form_list,
condition_dict={"config": ImportWizardView.show_config_step},
humitos marked this conversation as resolved.
Show resolved Hide resolved
),
name="projects_import_manual",
),
re_path(
Expand Down
123 changes: 68 additions & 55 deletions readthedocs/projects/views/private.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Project views for authenticated users."""


import structlog
from allauth.socialaccount.models import SocialAccount
from django.conf import settings
Expand Down Expand Up @@ -323,12 +324,78 @@ class ImportWizardView(ProjectImportMixin, PrivateViewMixin, SessionWizardView):
per session (since it's per class).
"""

initial_dict_key = "initial-data"
form_list = [
("basics", ProjectBasicsForm),
("config", ProjectConfigForm),
]

initial_dict_key = "initial-data"
@staticmethod
def show_config_step(wizard):
# try to get the cleaned data of step 1
cleaned_data = wizard.get_cleaned_data_for_step("basics") or {}
repo = cleaned_data.get("repo")
remote_repository = cleaned_data.get("remote_repository")
default_branch = cleaned_data.get("default_branch")

if (
repo
and default_branch
and remote_repository
and remote_repository.vcs_provider == GITHUB
):
# I don't know why `show_config_step` is called multiple times (at least 4).
# This is a problem for us because we perform external calls here and add messages to the request.
# Due to that, we are adding this instance variable to prevent this function to run multiple times.
if hasattr(wizard, "_show_config_step_executed"):
return False

remote_repository_relations = (
remote_repository.remote_repository_relations.filter(
user=wizard.request.user,
account__isnull=False,
)
.select_related("account", "user")
.only("user", "account")
)
for relation in remote_repository_relations:
service = GitHubService(relation.user, relation.account)
session = service.get_session()

for yaml in [
".readthedocs.yaml",
".readthedocs.yml",
"readthedocs.yaml",
"readthedocs.yml",
]:
try:
querystrings = (
f"?ref={default_branch}" if default_branch else ""
)
response = session.head(
f"https://api.github.com/repos/{remote_repository.full_name}/contents/{yaml}{querystrings}",
timeout=1,
)
if response.ok:
log.info(
"Read the Docs YAML file found for this repository.",
filename=yaml,
)
messages.success(
wizard.request,
_(
"We detected a configuration file in your repository and started your project's first build."
),
)
wizard._show_config_step_executed = True
return False
except Exception:
log.warning(
"Failed when hitting GitHub API to check for .readthedocs.yaml file.",
filename=yaml,
)
continue
return True

def get(self, *args, **kwargs):
# The method from the parent should run first,
Expand Down Expand Up @@ -366,60 +433,6 @@ def get_template_names(self):
"""Return template names based on step name."""
return f"projects/import_{self.steps.current}.html"

def process_step(self, form):
# pylint: disable=too-many-nested-blocks
if isinstance(form, ProjectBasicsForm):
remote_repository = form.cleaned_data.get("remote_repository")
default_branch = form.cleaned_data.get("default_branch")
if remote_repository and remote_repository.vcs_provider == GITHUB:
remote_repository_relations = (
remote_repository.remote_repository_relations.filter(
user=self.request.user,
account__isnull=False,
)
.select_related("account", "user")
.only("user", "account")
)
for relation in remote_repository_relations:
service = GitHubService(relation.user, relation.account)
session = service.get_session()

for yaml in [
".readthedocs.yaml",
".readthedocs.yml",
"readthedocs.yaml",
"readthedocs.yml",
]:
try:
querystrings = (
f"?ref={default_branch}" if default_branch else ""
)
response = session.head(
f"https://api.github.com/repos/{remote_repository.full_name}/contents/{yaml}{querystrings}",
timeout=1,
)
if response.ok:
log.info(
"Read the Docs YAML file found for this repository.",
filename=yaml,
)
messages.success(
self.request,
_(
"We detected a configuration file in your repository and started your project's first build."
),
)
self.form_list.pop("config")
break
except Exception:
log.warning(
"Failed when hitting GitHub API to check for .readthedocs.yaml file.",
filename=yaml,
)
continue

return super().process_step(form)

def done(self, form_list, **kwargs):
"""
Save form data as object instance.
Expand Down