diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/repository/__init__.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + + +def includeme(config): + + config.add_route( + name='repo_maintenance', + pattern='/{repo_name:.*?[^/]}/maintenance', repo_route=True) + + config.add_route( + name='repo_maintenance_execute', + pattern='/{repo_name:.*?[^/]}/maintenance/execute', repo_route=True) + + # Scan module for configuration decorators. + config.scan() diff --git a/rhodecode/apps/repository/views/__init__.py b/rhodecode/apps/repository/views/__init__.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/repository/views/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2011-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ diff --git a/rhodecode/apps/repository/views/repo_maintainance.py b/rhodecode/apps/repository/views/repo_maintainance.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/repository/views/repo_maintainance.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2011-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import logging + +from pyramid.view import view_config + +from rhodecode.apps._base import RepoAppView +from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator, + NotAnonymous) +from rhodecode.lib import repo_maintenance + +log = logging.getLogger(__name__) + + +class RepoMaintenanceView(RepoAppView): + def load_default_context(self): + c = self._get_local_tmpl_context() + + # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead + c.repo_info = self.db_repo + + self._register_global_c(c) + return c + + @LoginRequired() + @NotAnonymous() + @HasRepoPermissionAnyDecorator('repository.admin') + @view_config( + route_name='repo_maintenance', request_method='GET', + renderer='rhodecode:templates/admin/repos/repo_edit.mako') + def repo_maintenance(self): + c = self.load_default_context() + c.active = 'maintenance' + maintenance = repo_maintenance.RepoMaintenance() + c.executable_tasks = maintenance.get_tasks_for_repo(self.db_repo) + return self._get_template_context(c) + + @LoginRequired() + @NotAnonymous() + @HasRepoPermissionAnyDecorator('repository.admin') + @view_config( + route_name='repo_maintenance_execute', request_method='GET', + renderer='json', xhr=True) + def repo_maintenance_execute(self): + c = self.load_default_context() + c.active = 'maintenance' + _ = self.request.translate + + maintenance = repo_maintenance.RepoMaintenance() + executed_types = maintenance.execute(self.db_repo) + + return executed_types diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -288,6 +288,7 @@ def includeme(config): config.include('rhodecode.apps.admin') config.include('rhodecode.apps.channelstream') config.include('rhodecode.apps.login') + config.include('rhodecode.apps.repository') config.include('rhodecode.apps.user_profile') config.include('rhodecode.apps.my_account') config.include('rhodecode.apps.svn_support') diff --git a/rhodecode/lib/repo_maintenance.py b/rhodecode/lib/repo_maintenance.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/repo_maintenance.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2017-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ +import logging + +log = logging.getLogger(__name__) + + +class MaintenanceTask(object): + human_name = 'undefined' + + def __init__(self, db_repo): + self.db_repo = db_repo + + def run(self): + """Execute task and return task human value""" + raise NotImplementedError() + + +class GitGC(MaintenanceTask): + human_name = 'GIT Garbage collect' + + def _count_objects(self, repo): + stdout, stderr = repo.run_git_command( + ['count-objects', '-v'], fail_on_stderr=False) + + errors = ' ' + objects = ' '.join(stdout.splitlines()) + + if stderr: + errors = '\nSTD ERR:' + '\n'.join(stderr.splitlines()) + return objects + errors + + def run(self): + output = [] + instance = self.db_repo.scm_instance() + + objects = self._count_objects(instance) + output.append(objects) + log.debug('GIT objects:%s', objects) + + stdout, stderr = instance.run_git_command( + ['gc', '--aggressive'], fail_on_stderr=False) + + out = 'executed git gc --aggressive' + if stderr: + out = ''.join(stderr.splitlines()) + + elif stdout: + out = ''.join(stdout.splitlines()) + + output.append(out) + + objects = self._count_objects(instance) + log.debug('GIT objects:%s', objects) + output.append(objects) + + return '\n'.join(output) + + +class HGVerify(MaintenanceTask): + human_name = 'HG Verify repo' + + def run(self): + instance = self.db_repo.scm_instance() + res = instance.verify() + return res + + +class RepoMaintenance(object): + """ + Performs maintenance of repository based on it's type + """ + tasks = { + 'hg': [HGVerify], + 'git': [GitGC], + 'svn': [], + } + + def get_tasks_for_repo(self, db_repo): + """ + fetches human names of tasks pending for execution for given type of repo + """ + tasks = [] + for task in self.tasks[db_repo.repo_type]: + tasks.append(task.human_name) + return tasks + + def execute(self, db_repo): + executed_tasks = [] + for task in self.tasks[db_repo.repo_type]: + executed_tasks.append(task(db_repo).run()) + return executed_tasks diff --git a/rhodecode/templates/admin/repos/repo_edit.mako b/rhodecode/templates/admin/repos/repo_edit.mako --- a/rhodecode/templates/admin/repos/repo_edit.mako +++ b/rhodecode/templates/admin/repos/repo_edit.mako @@ -71,6 +71,9 @@
  • ${_('Integrations')}
  • +
  • + ${_('Maintenance')} +
  • ## TODO: dan: replace repo navigation with navlist registry like with ## admin menu. First must find way to allow runtime configuration ## it to account for the c.repo_info.repo_type != 'svn' call above diff --git a/rhodecode/templates/admin/repos/repo_edit_maintenance.mako b/rhodecode/templates/admin/repos/repo_edit_maintenance.mako new file mode 100644 --- /dev/null +++ b/rhodecode/templates/admin/repos/repo_edit_maintenance.mako @@ -0,0 +1,58 @@ +
    +
    +

    ${_('Maintenance')}

    +
    +
    + +

    + % if c.executable_tasks: + ${_('Perform maintenance tasks for this repo, following tasks will be performed')}: +

      + % for task in c.executable_tasks: +
    1. ${task}
    2. + % endfor +
    + % else: + ${_('No maintenance tasks for this repo available')} + % endif +

    + + + + % if c.executable_tasks: +
    +
    + +
    +
    + % endif + +
    +
    + + + diff --git a/rhodecode/templates/admin/settings/settings_global.mako b/rhodecode/templates/admin/settings/settings_global.mako --- a/rhodecode/templates/admin/settings/settings_global.mako +++ b/rhodecode/templates/admin/settings/settings_global.mako @@ -209,7 +209,7 @@ <%text filter="h">