Changeset - a3efdd61a21f
[Not reviewed]
beta
1 2 1
Marcin Kuzminski - 13 years ago 2012-06-06 21:32:19
marcin@python-works.com
Git Hooks are automatically installed in new repos
- fixed issue with initial push
- changed git pre-receive hook template to py file
3 files changed with 29 insertions and 4 deletions:
0 comments (0 inline, 0 general)
rhodecode/config/pre_receive_tmpl.py
Show inline comments
 
file renamed from rhodecode/config/pre-receive.tmpl to rhodecode/config/pre_receive_tmpl.py
rhodecode/lib/hooks.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.hooks
 
    ~~~~~~~~~~~~~~~~~~~
 

	
 
    Hooks runned by rhodecode
 

	
 
    :created_on: Aug 6, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# 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 General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
import os
 
import sys
 
import binascii
 
from inspect import isfunction
 

	
 
from mercurial.scmutil import revrange
 
from mercurial.node import nullrev
 

	
 
from rhodecode import EXTENSIONS
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.utils import action_logger
 
from rhodecode.lib.vcs.backends.base import EmptyChangeset
 

	
 

	
 
def _get_scm_size(alias, root_path):
 

	
 
    if not alias.startswith('.'):
 
        alias += '.'
 

	
 
    size_scm, size_root = 0, 0
 
    for path, dirs, files in os.walk(root_path):
 
        if path.find(alias) != -1:
 
            for f in files:
 
                try:
 
                    size_scm += os.path.getsize(os.path.join(path, f))
 
                except OSError:
 
                    pass
 
        else:
 
            for f in files:
 
                try:
 
                    size_root += os.path.getsize(os.path.join(path, f))
 
                except OSError:
 
                    pass
 

	
 
    size_scm_f = h.format_byte_size(size_scm)
 
    size_root_f = h.format_byte_size(size_root)
 
    size_total_f = h.format_byte_size(size_root + size_scm)
 

	
 
    return size_scm_f, size_root_f, size_total_f
 

	
 

	
 
def repo_size(ui, repo, hooktype=None, **kwargs):
 
    """
 
    Presents size of repository after push
 

	
 
    :param ui:
 
    :param repo:
 
    :param hooktype:
 
    """
 

	
 
    size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
 

	
 
    last_cs = repo[len(repo) - 1]
 

	
 
    msg = ('Repository size .hg:%s repo:%s total:%s\n'
 
           'Last revision is now r%s:%s\n') % (
 
        size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
 
    )
 

	
 
    sys.stdout.write(msg)
 
@@ -197,54 +198,59 @@ def log_create_repository(repository_dic
 
def handle_git_post_receive(repo_path, revs, env):
 
    """
 
    A really hacky method that is runned by git pre-receive hook and logs
 
    an push action together with pushed revisions. It's runned by subprocess
 
    thus needs all info to be able to create a temp pylons enviroment, connect
 
    to database and run the logging code. Hacky as sh**t but works. ps.
 
    GIT SUCKS
 

	
 
    :param repo_path:
 
    :type repo_path:
 
    :param revs:
 
    :type revs:
 
    :param env:
 
    :type env:
 
    """
 
    from paste.deploy import appconfig
 
    from sqlalchemy import engine_from_config
 
    from rhodecode.config.environment import load_environment
 
    from rhodecode.model import init_model
 
    from rhodecode.model.db import RhodeCodeUi
 
    from rhodecode.lib.utils import make_ui
 
    from rhodecode.model.db import Repository
 

	
 
    path, ini_name = os.path.split(env['RHODECODE_CONFIG_FILE'])
 
    conf = appconfig('config:%s' % ini_name, relative_to=path)
 
    load_environment(conf.global_conf, conf.local_conf)
 

	
 
    engine = engine_from_config(conf, 'sqlalchemy.db1.')
 
    init_model(engine)
 

	
 
    baseui = make_ui('db')
 
    repo = Repository.get_by_full_path(repo_path)
 

	
 
    _hooks = dict(baseui.configitems('hooks')) or {}
 
    # if push hook is enabled via web interface
 
    if _hooks.get(RhodeCodeUi.HOOK_PUSH):
 

	
 
        extras = {
 
         'username': env['RHODECODE_USER'],
 
         'repository': repo.repo_name,
 
         'scm': 'git',
 
         'action': 'push',
 
         'ip': env['RHODECODE_CONFIG_IP'],
 
        }
 
        for k, v in extras.items():
 
            baseui.setconfig('rhodecode_extras', k, v)
 
        repo = repo.scm_instance
 
        repo.ui = baseui
 
        old_rev, new_rev = revs[0:-1]
 

	
 
        cmd = 'log ' + old_rev + '..' + new_rev + ' --reverse --pretty=format:"%H"'
 
        old_rev, new_rev, ref = revs
 
        if old_rev == EmptyChangeset().raw_id:
 
            cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
 
            heads = repo.run_git_command(cmd)[0]
 
            heads = heads.replace(ref, '')
 
            cmd = 'log ' + new_rev + ' --reverse --pretty=format:"%H" --not ' + heads
 
        else:
 
            cmd = 'log ' + old_rev + '..' + new_rev + ' --reverse --pretty=format:"%H"'
 
        git_revs = repo.run_git_command(cmd)[0].splitlines()
 

	
 
        log_push_action(baseui, repo, _git_revs=git_revs)
rhodecode/model/repo.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.model.repo
 
    ~~~~~~~~~~~~~~~~~~~~
 

	
 
    Repository model for rhodecode
 

	
 
    :created_on: Jun 5, 2010
 
    :author: marcink
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# 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 General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
from __future__ import with_statement
 
import os
 
import shutil
 
import logging
 
import traceback
 
import pkg_resources
 
from os.path import dirname as dn, join as jn
 
from datetime import datetime
 

	
 
from rhodecode.lib.vcs.backends import get_backend
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
 
from rhodecode.lib.caching_query import FromCache
 
from rhodecode.lib.hooks import log_create_repository
 

	
 
from rhodecode.model import BaseModel
 
from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
 
    Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
 
from rhodecode.lib import helpers as h
 

	
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class RepoModel(BaseModel):
 

	
 
    def __get_user(self, user):
 
        return self._get_instance(User, user, callback=User.get_by_username)
 

	
 
    def __get_users_group(self, users_group):
 
        return self._get_instance(UsersGroup, users_group,
 
                                  callback=UsersGroup.get_by_group_name)
 

	
 
    def __get_repos_group(self, repos_group):
 
        return self._get_instance(RepoGroup, repos_group,
 
                                  callback=RepoGroup.get_by_group_name)
 

	
 
    def __get_repo(self, repository):
 
        return self._get_instance(Repository, repository,
 
                                  callback=Repository.get_by_repo_name)
 

	
 
    def __get_perm(self, permission):
 
        return self._get_instance(Permission, permission,
 
                                  callback=Permission.get_by_key)
 

	
 
    @LazyProperty
 
    def repos_path(self):
 
        """
 
        Get's the repositories root path from database
 
        """
 

	
 
        q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
 
        return q.ui_value
 

	
 
    def get(self, repo_id, cache=False):
 
@@ -416,93 +419,109 @@ class RepoModel(BaseModel):
 
                    .filter(Statistics.repository ==
 
                            self.get_by_repo_name(repo_name))\
 
                    .one()
 
            self.sa.delete(obj)
 
        except:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
 
        """
 
        makes repository on filesystem. It's group aware means it'll create
 
        a repository within a group, and alter the paths accordingly of
 
        group location
 

	
 
        :param repo_name:
 
        :param alias:
 
        :param parent_id:
 
        :param clone_uri:
 
        """
 
        from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
 

	
 
        if new_parent_id:
 
            paths = RepoGroup.get(new_parent_id)\
 
                .full_path.split(RepoGroup.url_sep())
 
            new_parent_path = os.sep.join(paths)
 
        else:
 
            new_parent_path = ''
 

	
 
        # we need to make it str for mercurial
 
        repo_path = os.path.join(*map(lambda x: safe_str(x),
 
                                [self.repos_path, new_parent_path, repo_name]))
 

	
 
        # check if this path is not a repository
 
        if is_valid_repo(repo_path, self.repos_path):
 
            raise Exception('This path %s is a valid repository' % repo_path)
 

	
 
        # check if this path is a group
 
        if is_valid_repos_group(repo_path, self.repos_path):
 
            raise Exception('This path %s is a valid group' % repo_path)
 

	
 
        log.info('creating repo %s in %s @ %s' % (
 
                     repo_name, safe_unicode(repo_path), clone_uri
 
                )
 
        )
 
        backend = get_backend(alias)
 
        if alias == 'hg':
 
            backend(repo_path, create=True, src_url=clone_uri)
 
        elif alias == 'git':
 
            backend(repo_path, create=True, src_url=clone_uri, bare=True)
 
            r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
 
            # add rhodecode hook into this repo
 

	
 
            loc = jn(r.path, 'hooks')
 
            if not r.bare:
 
                loc = jn(r.path, '.git', 'hooks')
 
            if not os.path.isdir(loc):
 
                os.makedirs(loc)
 

	
 
            tmpl = pkg_resources.resource_string(
 
                'rhodecode', jn('config', 'pre_receive_tmpl.py')
 
            )
 
            _hook_file = jn(loc, 'pre-receive')
 
            with open(_hook_file, 'wb') as f:
 
                f.write(tmpl)
 
            os.chmod(_hook_file, 0555)
 

	
 
        else:
 
            raise Exception('Undefined alias %s' % alias)
 

	
 
    def __rename_repo(self, old, new):
 
        """
 
        renames repository on filesystem
 

	
 
        :param old: old name
 
        :param new: new name
 
        """
 
        log.info('renaming repo from %s to %s' % (old, new))
 

	
 
        old_path = os.path.join(self.repos_path, old)
 
        new_path = os.path.join(self.repos_path, new)
 
        if os.path.isdir(new_path):
 
            raise Exception(
 
                'Was trying to rename to already existing dir %s' % new_path
 
            )
 
        shutil.move(old_path, new_path)
 

	
 
    def __delete_repo(self, repo):
 
        """
 
        removes repo from filesystem, the removal is acctually made by
 
        added rm__ prefix into dir, and rename internat .hg/.git dirs so this
 
        repository is no longer valid for rhodecode, can be undeleted later on
 
        by reverting the renames on this repository
 

	
 
        :param repo: repo object
 
        """
 
        rm_path = os.path.join(self.repos_path, repo.repo_name)
 
        log.info("Removing %s" % (rm_path))
 
        # disable hg/git internal that it doesn't get detected as repo
 
        alias = repo.repo_type
 

	
 
        bare = getattr(repo.scm_instance, 'bare', False)
 

	
 
        if not bare:
 
            # skip this for bare git repos
 
            shutil.move(os.path.join(rm_path, '.%s' % alias),
 
                        os.path.join(rm_path, 'rm__.%s' % alias))
 
        # disable repo
 
        _d = 'rm__%s__%s' % (datetime.now().strftime('%Y%m%d_%H%M%S_%f'),
 
                             repo.repo_name)
 
        shutil.move(rm_path, os.path.join(self.repos_path, _d))
0 comments (0 inline, 0 general)