Merge pull request !2935 from rhodecode-enterprise-ce fefature/RCCE-371_If-repo-is-damaged-redirect-to-deletion-page

feature: redirect to repo remove if repo is damaged
This commit is contained in:
Andrii Verbytskyi 2025-12-29 13:30:49 +00:00
commit 7bc3381ddb
4 changed files with 57 additions and 8 deletions

View file

@ -333,7 +333,9 @@ class RepoAppView(BaseAppView):
c.path_filter = self.path_filter # used by atom_feed_entry.mako
if self.rhodecode_vcs_repo is None:
is_broken = self.request.params.get("broken") # needed to prevent infinit loop
if self.rhodecode_vcs_repo is None and is_broken is None:
# unable to fetch this repo as vcs instance, report back to user
log.debug("Repository was not found on filesystem, check if it exists or is not damaged")
h.flash(
@ -353,8 +355,15 @@ class RepoAppView(BaseAppView):
raise HTTPFound(h.route_path("repo_summary", repo_name=self.db_repo_name))
else: # redirect if we don't show missing requirements
raise HTTPFound(h.route_path("home"))
else: # give user possibility to remove broken repo
raise HTTPFound(
h.route_path(
"edit_repo_advanced",
repo_name=self.db_repo_name,
_anchor="advanced-delete",
_query={"broken": True},
)
)
c.has_origin_repo_read_perm = False
if self.db_repo.fork:

View file

@ -115,6 +115,29 @@ class TestAdminRepoSettingsAdvanced(object):
assert RepoModel().get_by_repo_name(repo_name) is None
assert not repo_on_filesystem(repo_name_str)
@pytest.mark.parametrize("suffix", ["", "ąęł", "123"], ids=no_newline_id_generator)
def test_delete_broken_repo(self, autologin_user, backend, suffix, csrf_token):
repo = backend.create_repo(name_suffix=suffix, private_repo=True)
repo.private = True
Session().commit()
repo_name = repo.repo_name
repo_name_str = safe_str(repo.repo_name)
# delete from file system
RepoModel()._delete_filesystem_repo(repo)
assert not repo_on_filesystem(repo_name_str)
response = self.app.post(
route_path("edit_repo_advanced_delete", repo_name=repo_name_str), params={"csrf_token": csrf_token}
)
assert_session_flash(response, "Deleted repository `{}`".format(repo_name))
response.follow()
# check if repo was deleted from db
assert RepoModel().get_by_repo_name(repo_name) is None
assert not repo_on_filesystem(repo_name_str)
@patch.object(RepoSettingsAdvancedView, "_maybe_needs_password_change")
@patch.object(RepoSettingsAdvancedView, "_maybe_needs_2fa_configuration")
@patch.object(RepoSettingsAdvancedView, "_maybe_needs_2fa_check")
@ -128,6 +151,7 @@ class TestAdminRepoSettingsAdvanced(object):
db_repo.scm_instance.return_value = scm_instance
request.db_repo = db_repo
request.params = {}
obj = RepoSettingsAdvancedView(MagicMock(), request)
# bypass decorators

View file

@ -17,6 +17,7 @@
# and proprietary license terms, please see https://rhodecode.com/licenses/
import re
import urllib
import mock
import pytest
@ -232,6 +233,9 @@ class TestRepoLocation(object):
f"The repository `{repo_name}` cannot be loaded in filesystem. Please check if it exist, or is not damaged."
)
assert_session_flash(response, msg)
path = route_path("edit_repo_advanced", repo_name=safe_str(repo_name))
path = urllib.parse.quote(path) + "?broken=True#advanced-delete"
assert response.location.endswith(path)
@pytest.mark.parametrize("suffix", ["", "ąęł"], ids=["", "non-ascii"])
def test_missing_filesystem_repo_on_repo_check(self, autologin_user, backend, suffix, csrf_token):

View file

@ -57,6 +57,17 @@ class RepoSettingsAdvancedView(RepoAppView):
c = self.load_default_context()
c.active = "advanced"
is_broken = self.request.params.get("broken")
if is_broken:
# Special case: the repository is missing on the filesystem, so its state is unknown.
# Only allow deletion in this mode.
c.fork_links = []
c.pr_link = None
c.in_public_journal = None
c.ver_info_dict = {}
c.hooks_outdated = False
return self._get_template_context(c)
c.fork_links = self._resolve_repo_forks_links()
c.pr_link = h.route_path("pullrequest_show_all", repo_name=self.db_repo_name)
@ -139,8 +150,6 @@ class RepoSettingsAdvancedView(RepoAppView):
repo_advanced_url = h.route_path("edit_repo_advanced", repo_name=self.db_repo_name, _anchor="advanced-delete")
try:
old_data = self.db_repo.get_api_data()
delete_cache = True
self._invalidate_remote_cache(delete=delete_cache)
@ -159,6 +168,7 @@ class RepoSettingsAdvancedView(RepoAppView):
h.flash(_("Deleted %s forks") % fork_cnt, category="success")
repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name)
old_data = self.db_repo.get_api_data()
audit_logger.store_web(
"repo.delete", action_data={"old_data": old_data}, user=self._rhodecode_user, repo=repo
)
@ -203,8 +213,8 @@ class RepoSettingsAdvancedView(RepoAppView):
# redirect to advanced for forks handle action ?
raise HTTPFound(repo_advanced_url)
except Exception:
log.exception("Exception during deletion of repository")
except Exception as e:
log.exception(f"Exception during deletion of repository: {str(e)}")
h.flash(_("An error occurred during deletion of `%s`") % self.db_repo_name, category="error")
# redirect to advanced for more deletion options
raise HTTPFound(h.route_path("edit_repo_advanced", repo_name=self.db_repo_name, _anchor="advanced-delete"))
@ -214,7 +224,9 @@ class RepoSettingsAdvancedView(RepoAppView):
def _invalidate_remote_cache(self, delete: bool):
log.debug(f"Invalidating remote cache, for repository: {self.db_repo_name}")
scm_repo = self.db_repo.scm_instance(cache=False)
scm_repo.vcsserver_invalidate_cache(delete=delete)
if scm_repo is not None:
# if it's None - probably repo is broken and physically absent on the disk
scm_repo.vcsserver_invalidate_cache(delete=delete)
@LoginRequired()
@HasRepoPermissionAnyDecorator("repository.admin")