feature: implements repo deletion mechanism
This commit is contained in:
parent
203936cbd8
commit
d4cb542581
6 changed files with 56 additions and 32 deletions
|
|
@ -1371,8 +1371,9 @@ def delete_repo(request, apiuser, repoid, forks=Optional("")):
|
|||
_forks_msg = " " + f"Deleted {len(_forks)} forks"
|
||||
elif _forks:
|
||||
raise JSONRPCError(f"Cannot delete `{repo.repo_name}` it still contains attached forks")
|
||||
|
||||
old_data = repo.get_api_data()
|
||||
RepoModel().delete(repo, forks=forks)
|
||||
RepoModel().delete(repo, forks=RepoModel.ForkAction(handle_forks))
|
||||
|
||||
repo = audit_logger.RepoWrap(repo_id=None, repo_name=repo.repo_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -124,11 +124,8 @@ class RepoSettingsAdvancedView(RepoAppView):
|
|||
because of attached forks or other errors.
|
||||
"""
|
||||
_ = self.request.translate
|
||||
handle_forks = self.request.POST.get("forks", None)
|
||||
if handle_forks == "detach_forks":
|
||||
handle_forks = "detach"
|
||||
elif handle_forks == "delete_forks":
|
||||
handle_forks = "delete"
|
||||
|
||||
fork_action = self._get_fork_action()
|
||||
|
||||
repo_advanced_url = h.route_path("edit_repo_advanced", repo_name=self.db_repo_name, _anchor="advanced-delete")
|
||||
try:
|
||||
|
|
@ -137,13 +134,13 @@ class RepoSettingsAdvancedView(RepoAppView):
|
|||
delete_cache = True
|
||||
self._invalidate_remote_cache(delete=delete_cache)
|
||||
|
||||
RepoModel().delete(self.db_repo, forks=handle_forks)
|
||||
RepoModel().delete(self.db_repo, forks=fork_action)
|
||||
|
||||
_forks = self.db_repo.forks.count()
|
||||
if _forks and handle_forks:
|
||||
if handle_forks == "detach_forks":
|
||||
if _forks and fork_action:
|
||||
if fork_action is RepoModel.ForkAction.DETACH:
|
||||
h.flash(_("Detached %s forks") % _forks, category="success")
|
||||
elif handle_forks == "delete_forks":
|
||||
elif fork_action is RepoModel.ForkAction.DELETE:
|
||||
h.flash(_("Deleted %s forks") % _forks, category="success")
|
||||
|
||||
repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name)
|
||||
|
|
@ -199,6 +196,16 @@ class RepoSettingsAdvancedView(RepoAppView):
|
|||
|
||||
raise HTTPFound(h.route_path("home"))
|
||||
|
||||
def _get_fork_action(self):
|
||||
fork_action = self.request.POST.get("forks", None)
|
||||
if fork_action == "detach_forks":
|
||||
return RepoModel.ForkAction.DETACH
|
||||
|
||||
if fork_action == "delete_forks":
|
||||
return RepoModel.ForkAction.DELETE
|
||||
|
||||
return RepoModel.ForkAction.UNKNOWN
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -688,7 +688,7 @@ def repo2db_cleanup(skip_repos=None, skip_groups=None):
|
|||
if not instance:
|
||||
log.debug("Removing non-existing repository found in db `%s`", db_repo_name)
|
||||
try:
|
||||
RepoModel(sa).delete(db_repo, forks="detach", fs_remove=False, call_events=False)
|
||||
RepoModel(sa).delete(db_repo, forks=RepoModel.ForkAction.DETACH, fs_remove=False, call_events=False)
|
||||
sa.commit()
|
||||
removed.append(db_repo_name)
|
||||
except Exception:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import time
|
|||
import logging
|
||||
import traceback
|
||||
import datetime
|
||||
from enum import StrEnum, auto
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy.orm import aliased
|
||||
from zope.cachedescriptors.property import Lazy as LazyProperty
|
||||
|
|
@ -76,6 +78,15 @@ log = logging.getLogger(__name__)
|
|||
class RepoModel(BaseModel):
|
||||
cls = Repository
|
||||
|
||||
class ForkAction(StrEnum):
|
||||
DELETE = auto()
|
||||
DETACH = auto()
|
||||
UNKNOWN = auto()
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
return cls(cls.UNKNOWN)
|
||||
|
||||
def _get_user_group(self, users_group):
|
||||
return self._get_instance(UserGroup, users_group, callback=UserGroup.get_by_group_name)
|
||||
|
||||
|
|
@ -749,17 +760,20 @@ class RepoModel(BaseModel):
|
|||
raise
|
||||
|
||||
def delete(
|
||||
self, repo, forks=None, pull_requests=None, artifacts=None, fs_remove=True, cur_user=None, call_events=True
|
||||
):
|
||||
self,
|
||||
repo: Repository,
|
||||
forks: ForkAction = None, # left for backward compatibility, do not use it
|
||||
fs_remove: bool = True,
|
||||
cur_user: User = None,
|
||||
call_events: bool = True,
|
||||
) -> bool:
|
||||
"""
|
||||
Delete given repository, forks parameter defines what do do with
|
||||
attached forks. Throws AttachedForksError if deleted repo has attached
|
||||
forks
|
||||
|
||||
:param repo:
|
||||
:param forks: str 'delete' or 'detach'
|
||||
:param pull_requests: str 'delete' or None
|
||||
:param artifacts: str 'delete' or None
|
||||
:param forks: ForkAction 'delete' or 'detach', NOTE: left for backward compatibility, do not use it
|
||||
:param fs_remove: remove(archive) repo from filesystem
|
||||
"""
|
||||
if not cur_user:
|
||||
|
|
@ -768,27 +782,21 @@ class RepoModel(BaseModel):
|
|||
if not repo:
|
||||
return False
|
||||
|
||||
if not forks: # left for backward compatibility
|
||||
if forks is None or forks is self.ForkAction.UNKNOWN:
|
||||
is_private = repo.private
|
||||
forks = "delete" if is_private else "detach"
|
||||
forks = self.ForkAction.DELETE if is_private else self.ForkAction.DETACH
|
||||
|
||||
if forks == "detach":
|
||||
if forks is self.ForkAction.DETACH:
|
||||
for r in repo.forks:
|
||||
r.fork = None
|
||||
self.sa.add(r)
|
||||
elif forks == "delete":
|
||||
elif forks is self.ForkAction.DELETE:
|
||||
for r in repo.forks:
|
||||
self.delete(r, forks="delete")
|
||||
elif [f for f in repo.forks]:
|
||||
self.delete(r, forks=self.ForkAction.DELETE)
|
||||
elif repo.forks.first() is not None:
|
||||
raise AttachedForksError()
|
||||
|
||||
# check for pull requests
|
||||
pr_sources = repo.pull_requests_source
|
||||
pr_targets = repo.pull_requests_target
|
||||
artifacts = repo.artifacts
|
||||
# FIXME: continue here
|
||||
for pr in itertools.chain(pr_sources, pr_targets, artifacts):
|
||||
self.sa.delete(pr)
|
||||
self._delete_repo_related_assets(repo)
|
||||
|
||||
old_repo_dict = repo.get_dict()
|
||||
if call_events:
|
||||
|
|
@ -815,6 +823,14 @@ class RepoModel(BaseModel):
|
|||
|
||||
return True
|
||||
|
||||
def _delete_repo_related_assets(self, repo):
|
||||
pr_sources = repo.pull_requests_source
|
||||
pr_targets = repo.pull_requests_target
|
||||
artifacts = repo.artifacts
|
||||
logs = repo.logs
|
||||
for obj_to_remove in itertools.chain(pr_sources, pr_targets, artifacts, logs):
|
||||
self.sa.delete(obj_to_remove)
|
||||
|
||||
def grant_user_permission(self, repo, user, perm):
|
||||
"""
|
||||
Grant permission for user on given repository, or update existing one
|
||||
|
|
|
|||
2
rhodecode/tests/fixtures/rc_fixture.py
vendored
2
rhodecode/tests/fixtures/rc_fixture.py
vendored
|
|
@ -294,7 +294,7 @@ class Fixture(object):
|
|||
return r
|
||||
|
||||
def destroy_repo(self, repo_name, **kwargs):
|
||||
RepoModel().delete(repo_name, pull_requests="delete", artifacts="delete", **kwargs)
|
||||
RepoModel().delete(repo_name, **kwargs)
|
||||
Session().commit()
|
||||
|
||||
def destroy_repo_on_filesystem(self, repo_name):
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class TestRepoModel(object):
|
|||
fork_of_fork = backend.create_fork()
|
||||
Session().commit()
|
||||
|
||||
RepoModel().delete(repo=repo, forks="delete")
|
||||
RepoModel().delete(repo=repo, forks=RepoModel.ForkAction.DELETE)
|
||||
Session().commit()
|
||||
|
||||
assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
|
||||
|
|
@ -78,7 +78,7 @@ class TestRepoModel(object):
|
|||
fork_of_fork = backend.create_fork()
|
||||
Session().commit()
|
||||
|
||||
RepoModel().delete(repo=repo, forks="detach")
|
||||
RepoModel().delete(repo=repo, forks=RepoModel.ForkAction.DETACH)
|
||||
Session().commit()
|
||||
|
||||
assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue