Exemplo n.º 1
0
def init(path, template=None, _git=None):
    """
    Create a git repository at C{path} (if missing).

    Leading directories of C{path} must exist.

    @param path: Path of repository create.

    @type path: str

    @param template: Template directory, to pass to C{git init}.

    @type template: str
    """
    if _git is None:
        _git = 'git'

    util.mkdir(path, 0750)
    args = [
        _git,
        '--git-dir=.',
        'init',
        ]
    if template is not None:
        args.append('--template=%s' % template)
    returncode = subprocess.call(
        args=args,
        cwd=path,
        stdout=sys.stderr,
        close_fds=True,
        )
    if returncode != 0:
        raise GitInitError('exit status %d' % returncode)
Exemplo n.º 2
0
def export(git_dir, path):
    """Export a Git repository to a given path."""
    util.mkdir(path)
    returncode = subprocess.call(
        args=[
            'git',
            '--git-dir=%s' % git_dir,
            'read-tree',
            'HEAD',
            ],
        close_fds=True,
        )
    if returncode != 0: #pragma: no cover
        raise GitError("read-tree", 'exit status %d' % returncode)
    # jumping through hoops to be compatible with git versions
    # that don't have --work-tree=
    env = {}
    env.update(os.environ)
    env['GIT_WORK_TREE'] = '.'
    returncode = subprocess.call(
        args=[
            'git',
            '--git-dir=%s' % os.path.abspath(git_dir),
            'checkout-index',
            '-a',
            '-f',
            ],
        cwd=path,
        close_fds=True,
        env=env,
        )
    if returncode != 0: #pragma: no cover
        raise GitError("checkout-index", 'exit status %d' % returncode)
Exemplo n.º 3
0
    def handle_args(self, parser, cfg, options, args):
        super(Main, self).handle_args(parser, cfg, options, args)

        os.umask(0022)

        log.info('Reading SSH public key...')
        pubkey = read_ssh_pubkey()
        user = ssh_extract_user(pubkey)
        if user is None:
            log.error('Cannot parse user from SSH public key.')
            sys.exit(1)
        log.info('Admin user is %r', user)
        log.info('Creating generated files directory...')
        generated = util.getGeneratedFilesDir(config=cfg)
        util.mkdir(generated)
        log.info('Creating repository structure...')
        repositories = util.getRepositoryDir(cfg)
        util.mkdir(repositories)
        #admin_repository = os.path.join(repositories, 'gitosis-admin.git')
        #init_admin_repository(
        #    git_dir=admin_repository,
        #    pubkey=pubkey,
        #    user=user,
        #    )
        #log.info('Running post-update hook...')
        #util.mkdir(os.path.expanduser('~/.ssh'), 0700)
        #run_hook.post_update(cfg=cfg, git_dir=admin_repository)
        #log.info('Symlinking ~/.gitosis.conf to repository...')
        #symlink_config(git_dir=admin_repository)
        log.info('Done.')
Exemplo n.º 4
0
def auto_init_repo(cfg,topdir,repopath):
    # create leading directories
    p = topdir

    assert repopath.endswith('.git'), 'must have .git extension'
    newdirmode = util.getConfigDefault(cfg,
                                       'repo %s' % repopath[:-4],
                                       'dirmode',
                                       None,
                                       'defaults')
    if newdirmode is not None:
        newdirmode = int(newdirmode, 8)
    else:
        newdirmode = 0750

    for segment in repopath.split(os.sep)[:-1]:
        p = os.path.join(p, segment)
        util.mkdir(p, newdirmode)

    fullpath = os.path.join(topdir, repopath)

    # init using a custom template, if required
    try:
        template = cfg.get('gitosis', 'init-template')
        repository.init(path=fullpath, template=template, mode=newdirmode)
    except (NoSectionError, NoOptionError):
        pass

    repository.init(path=fullpath, mode=newdirmode)
Exemplo n.º 5
0
def init(
    path,
    template=None,
    _git=None,
    ):
    """
    Create a git repository at C{path} (if missing).

    Leading directories of C{path} must exist.

    @param path: Path of repository create.

    @type path: str

    @param template: Template directory, to pass to C{git init}.

    @type template: str
    """
    if _git is None:
        _git = 'git'

    if template is None:
        template = resource_filename('gitosis.templates', 'default')


    util.mkdir(path, 0755)
    args = [
        _git,
        '--git-dir=.',
        'init',
        ]
        
    hooks = []
    if template:
        args.append('--template=%s' % template)
        template_hooks_dir = os.path.join(template, 'hooks')
        if os.path.isdir(template_hooks_dir):
            hooks = os.listdir(template_hooks_dir)

    returncode = subprocess.call(
        args=args,
        cwd=path,
        stdout=sys.stderr,
        close_fds=True,
        )
    if returncode != 0:
        raise GitInitError('exit status %d' % returncode)
    
    hooks_dir = os.path.join(path, 'hooks')
    if not os.path.exists(hooks_dir):
        hooks_dir = os.path.join(path, '.git', 'hooks')
    if not os.path.exists(hooks_dir):
        raise
    for hook in hooks:
        mode = os.stat(os.path.join(hooks_dir, hook))[ST_MODE]
        if not (mode & 0755) == 0755:
            os.chmod(
                os.path.join(hooks_dir, hook),
                0755)
Exemplo n.º 6
0
def auto_init_repo(cfg,topdir,repopath):
    # create leading directories
    p = topdir
    for segment in repopath.split(os.sep)[:-1]:
        p = os.path.join(p, segment)
        util.mkdir(p, 0750)

    fullpath = os.path.join(topdir, repopath)

    # init using a custom template, if required
    try:
        template = cfg.get('gitosis', 'init-template')
        repository.init(path=fullpath,template=template)
    except (NoSectionError, NoOptionError):
        pass

    repository.init(path=fullpath)
Exemplo n.º 7
0
    def handle_args(self, parser, cfg, options, args):
        super(Main, self).handle_args(parser, cfg, options, args)

        os.umask(0022)

        log.info("Reading SSH public key...")
        pubkey = read_ssh_pubkey()
        user = ssh_extract_user(pubkey)
        if user is None:
            log.error("Cannot parse user from SSH public key.")
            sys.exit(1)
        log.info("Admin user is %r", user)
        log.info("Creating generated files directory...")
        generated = util.getGeneratedFilesDir(config=cfg)
        util.mkdir(generated)
        log.info("Creating repository structure...")
        repositories = util.getRepositoryDir(cfg)
        util.mkdir(repositories)
        admin_repository = os.path.join(repositories, "gitosis-admin.git")
        init_admin_repository(git_dir=admin_repository, pubkey=pubkey, user=user)
        log.info("Running post-update hook...")
        util.mkdir(os.path.expanduser("~/.ssh"), 0700)
        run_hook.post_update(cfg=cfg, git_dir=admin_repository)
        log.info("Symlinking ~/.gitosis.conf to repository...")
        symlink_config(git_dir=admin_repository)
        log.info("Done.")
Exemplo n.º 8
0
    def handle_args(self, parser, cfg, options, args):
        super(Main, self).handle_args(parser, cfg, options, args)

        os.umask(0022)

        log.info('Reading SSH public key...')
        pubkey = read_ssh_pubkey()
        user = ssh_extract_user(pubkey)
        if user is None:
            log.error('Cannot parse user from SSH public key.')
            sys.exit(1)
        log.info('Admin user is %r', user)
        log.info('Creating generated files directory...')
        generated = util.getGeneratedFilesDir(config=cfg)
        util.mkdir(generated)
        log.info('Creating repository structure...')
        repositories = util.getRepositoryDir(cfg)
        util.mkdir(repositories)
        admin_repository = os.path.join(repositories, 'gitosis-admin') # CQDE change: removed .git extension.
        init_admin_repository(
            git_dir=admin_repository,
            pubkey=pubkey,
            user=user,
            )
        log.info('Running post-update hook...')
        util.mkdir(os.path.expanduser('~/.ssh'), 0700)
        run_hook.post_update(cfg=cfg, git_dir=admin_repository)
        log.info('Symlinking ~/.gitosis.conf to repository...')
        symlink_config(git_dir=admin_repository)
        log.info('Done.')
Exemplo n.º 9
0
def init(
    path,
    template=None,
    _git=None,
):
    """
    Create a git repository at C{path} (if missing).

    Leading directories of C{path} must exist.

    @param path: Path of repository create.

    @type path: str

    @param template: Template directory, to pass to C{git init}.

    @type template: str
    """
    if _git is None:
        _git = 'git'

    util.mkdir(path, 0o750)
    args = [
        _git,
        '--git-dir=.',
        'init',
        '--quiet',
    ]
    if template is not None:
        args.append('--template=%s' % template)
    returncode = subprocess.call(
        args=args,
        cwd=path,
        stdout=sys.stderr,
        close_fds=True,
    )
    if returncode != 0:
        raise GitInitError('exit status %d' % returncode)
Exemplo n.º 10
0
    def handle_args(self, parser, cfg, options, args): #pragma: no cover
        """Parse the input for this program."""
        super(Main, self).handle_args(parser, cfg, options, args)

        os.umask(0022)

        log.info('Reading SSH public key...')
        pubkey = read_ssh_pubkey(options.adminkey)
        if options.adminname is None:
            _ = sshkey.get_ssh_pubkey(pubkey)
            user = _.username
        else:
            user = options.adminname
        user = user.strip()
        if user is None:
            log.error('Cannot parse user from SSH public key.')
            sys.exit(1)
        log.info('Admin user is %r', user)
        log.info('Creating generated files directory...')
        generated = cfg.generated_files_dir
        util.mkdir(generated)
        log.info('Creating repository structure...')
        repositories = cfg.repository_dir
        util.mkdir(repositories)
        admin_repository = os.path.join(repositories, 'gitosis-admin.git')
        init_admin_repository(
            git_dir=admin_repository,
            pubkey=pubkey,
            user=user,
            config=cfg,
            )
        log.info('Running post-update hook...')
        util.mkdir(os.path.expanduser('~/.ssh'), 0700)
        run_hook.post_update(cfg=cfg, git_dir=admin_repository)
        log.info('Symlinking ~/.gitosis.conf to repository...')
        symlink_config(git_dir=admin_repository)
        log.info('Done.')
Exemplo n.º 11
0
def serve(
    cfg,
    user,
    command,
    ):
    if '\n' in command:
        raise CommandMayNotContainNewlineError()

    try:
        verb, args = command.split(None, 1)
    except ValueError:
        # all known "git-foo" commands take one argument; improve
        # if/when needed
        raise UnknownCommandError()

    if verb == 'git':
        try:
            subverb, args = args.split(None, 1)
        except ValueError:
            # all known "git foo" commands take one argument; improve
            # if/when needed
            raise UnknownCommandError()
        verb = '%s %s' % (verb, subverb)

    if (verb not in COMMANDS_WRITE
        and verb not in COMMANDS_READONLY):
        raise UnknownCommandError()

    match = ALLOW_RE.match(args)
    if match is None:
        raise UnsafeArgumentsError()

    path = match.group('path')

    # write access is always sufficient
    newpath = access.haveAccess(
        config=cfg,
        user=user,
        mode='writable',
        path=path)

    if newpath is None:
        # didn't have write access; try once more with the popular
        # misspelling
        newpath = access.haveAccess(
            config=cfg,
            user=user,
            mode='writeable',
            path=path)
        if newpath is not None:
            log.warning(
                'Repository %r config has typo "writeable", '
                +'should be "writable"',
                path,
                )

    if newpath is None:
        # didn't have write access

        newpath = access.haveAccess(
            config=cfg,
            user=user,
            mode='readonly',
            path=path)

        if newpath is None:
            raise ReadAccessDenied()

        if verb in COMMANDS_WRITE:
            # didn't have write access and tried to write
            raise WriteAccessDenied()

    (topdir, relpath) = newpath
    assert not relpath.endswith('.git'), \
           'git extension should have been stripped: %r' % relpath
    repopath = '%s.git' % relpath
    fullpath = os.path.join(topdir, repopath)
    if (not os.path.exists(fullpath)
        and verb in COMMANDS_WRITE):
        # it doesn't exist on the filesystem, but the configuration
        # refers to it, we're serving a write request, and the user is
        # authorized to do that: create the repository on the fly

        # create leading directories
        p = topdir
        for segment in repopath.split(os.sep)[:-1]:
            p = os.path.join(p, segment)
            util.mkdir(p, 0755)

        repository.init(path=fullpath)
        gitweb.set_descriptions(
            config=cfg,
            )
        generated = util.getGeneratedFilesDir(config=cfg)
        gitweb.generate_project_list(
            config=cfg,
            path=os.path.join(generated, 'projects.list'),
            )
        gitdaemon.set_export_ok(
            config=cfg,
            )

    # put the verb back together with the new path
    newcmd = "%(verb)s '%(path)s'" % dict(
        verb=verb,
        path=fullpath,
        )
    return newcmd
Exemplo n.º 12
0
def serve(
    cfg,
    user,
    command,
    ):
    if '\n' in command:
        raise CommandMayNotContainNewlineError()

    try:
        verb, args = command.split(None, 1)
    except ValueError:
        # all known "git-foo" commands take one argument; improve
        # if/when needed
        raise UnknownCommandError()

    if verb == 'git':
        try:
            subverb, args = args.split(None, 1)
        except ValueError:
            # all known "git foo" commands take one argument; improve
            # if/when needed
            raise UnknownCommandError()
        verb = '%s %s' % (verb, subverb)

    if (verb not in COMMANDS_WRITE
        and verb not in COMMANDS_READONLY):
        raise UnknownCommandError()

    try:
        match = ALLOW_RE.match(unicode(args, 'utf-8'))
    except UnicodeDecodeError:
        raise UnsafeArgumentsError()

    if match is None:
        raise UnsafeArgumentsError()

    path = match.group('path')

    # admin access is always sufficient
    newpath = access.haveAccess(
        config=cfg,
        user=user,
        mode='admin',
        path=path)

    # write access is always sufficient
    if newpath is None:
        newpath = access.haveAccess(
            config=cfg,
            user=user,
            mode='write',
            path=path)

    if newpath is None:
        # didn't have write access

        newpath = access.haveAccess(
            config=cfg,
            user=user,
            mode='read',
            path=path)

        if newpath is None:
            raise ReadAccessDenied()

        if verb in COMMANDS_WRITE:
            # didn't have write access and tried to write
            raise WriteAccessDenied()

    (topdir, relpath, mode) = newpath
    assert not relpath.endswith('.git'), \
           'git extension should have been stripped: %r' % relpath
    repopath = '%s.git' % relpath
    fullpath = os.path.join(topdir, repopath)
    if not os.path.exists(fullpath) and verb in COMMANDS_WRITE:
        # it doesn't exist on the filesystem, but the configuration
        # refers to it, we're serving a write request, and the user is
        # authorized to do that: create the repository on the fly

        # If user not has admin permission, raise a Error
        if mode != 'admin':
            raise InitAccessDenied()

        # create leading directories
        p = topdir
        for segment in repopath.split(os.sep)[:-1]:
            p = os.path.join(p, segment)
            util.mkdir(p, 0755)

        repository.init(path=fullpath)

        # only update description of this repository
        gitweb.set_repo_descriptions(
            config=cfg,
            path=fullpath,
            )

        # not update projects.list here, but in gitosis-admin post-update

        # only update export flag of this repository
        gitdaemon.set_repo_export_ok(
            config=cfg,
            path=fullpath,
            )

    # put the verb back together with the new path
    newcmd = "%(verb)s '%(path)s'" % dict(
        verb=verb,
        path=fullpath,
        )
    return newcmd
Exemplo n.º 13
0
    @param template: Template directory, to pass to C{git init}.

    @type template: str

    @param mode: Permissions for the new reposistory

    @type mode: int
    """
    if _git is None:
        _git = 'git'

    if template is None:
        template = resource_filename('gitosis.templates', 'default')


    util.mkdir(path, mode)
    args = [
        _git,
        '--git-dir=.',
        'init',
        ]
        
    hooks = []
    if template:
        args.append('--template=%s' % template)
        template_hooks_dir = os.path.join(template, 'hooks')
        if os.path.isdir(template_hooks_dir):
            hooks = os.listdir(template_hooks_dir)

    returncode = subprocess.call(
        args=args,
Exemplo n.º 14
0
def serve(
    cfg,
    user,
    command,
    ):
    if '\n' in command:
        raise CommandMayNotContainNewlineError()

    try:
        verb, args = command.split(None, 1)
    except ValueError:
        # all known "git-foo" commands take one argument; improve
        # if/when needed
        raise UnknownCommandError()

    if verb == 'git':
        try:
            subverb, args = args.split(None, 1)
        except ValueError:
            # all known "git foo" commands take one argument; improve
            # if/when needed
            raise UnknownCommandError()
        verb = '%s %s' % (verb, subverb)

    if (verb not in COMMANDS_WRITE
        and verb not in COMMANDS_READONLY):
        raise UnknownCommandError()

    match = ALLOW_RE.match(args)
    if match is None:
        raise UnsafeArgumentsError()

    path = match.group('path')

    # write access is always sufficient
    newpath = access.haveAccess(
        config=cfg,
        user=user,
        mode='writable',
        path=path)

    if newpath is None:
        # didn't have write access; try once more with the popular
        # misspelling
        newpath = access.haveAccess(
            config=cfg,
            user=user,
            mode='writeable',
            path=path)
        if newpath is not None:
            log.warning(
                'Repository %r config has typo "writeable", '
                +'should be "writable"',
                path,
                )

    if newpath is None:
        # didn't have write access

        newpath = access.haveAccess(
            config=cfg,
            user=user,
            mode='readonly',
            path=path)

        if newpath is None:
            raise ReadAccessDenied()

        if verb in COMMANDS_WRITE:
            # didn't have write access and tried to write
            raise WriteAccessDenied()

    (topdir, relpath) = newpath
    assert not relpath.endswith('.git'), \
           'git extension should have been stripped: %r' % relpath
    repopath = '%s.git' % relpath
    fullpath = os.path.join(topdir, repopath)
    if not os.path.exists(fullpath):
        # it doesn't exist on the filesystem, but the configuration
        # refers to it, we're serving a write request, and the user is
        # authorized to do that: create the repository on the fly

        # create leading directories
        p = topdir
        for segment in repopath.split(os.sep)[:-1]:
            p = os.path.join(p, segment)
            util.mkdir(p, 0o750)

        repository.init(path=fullpath)
        gitweb.set_descriptions(
            config=cfg,
            )
        generated = util.getGeneratedFilesDir(config=cfg)
        gitweb.generate_project_list(
            config=cfg,
            path=os.path.join(generated, 'projects.list'),
            )
        gitdaemon.set_export_ok(
            config=cfg,
            )

    # put the verb back together with the new path
    newcmd = "%(verb)s '%(path)s'" % dict(
        verb=verb,
        path=fullpath,
        )
    return newcmd
Exemplo n.º 15
0
def serve(cfg, user, command):
    """Check the git command for sanity, and then run the git command."""

    log = logging.getLogger('gitosis.serve.serve')

    if '\n' in command:
        raise CommandMayNotContainNewlineError()

    try:
        verb, args = command.split(None, 1)
    except ValueError:
        # all known "git-foo" commands take one argument; improve
        # if/when needed
        raise UnknownCommandError()

    if verb == 'git':
        try:
            subverb, args = args.split(None, 1)
        except ValueError:
            # all known "git foo" commands take one argument; improve
            # if/when needed
            raise UnknownCommandError()
        verb = '%s %s' % (verb, subverb)

    if (verb not in COMMANDS_WRITE
        and verb not in COMMANDS_READONLY):
        raise UnknownCommandError()

    log.debug('Got command %(cmd)r and args %(args)r' % dict(
                cmd=verb,
                args=args,
                ))

    if args.startswith("'/") and args.endswith("'"):
        args = args[1:-1]
        repos = cfg.repository_dir
        reposreal = os.path.realpath(repos)
        if args.startswith(repos):
            args = os.path.realpath(args)[len(repos)+1:]
        elif args.startswith(reposreal):
            args = os.path.realpath(args)[len(reposreal)+1:]
        else:
            args = args[1:]
        args = "'%s'" % (args, )

    match = ALLOW_RE.match(args)
    if match is None:
        raise UnsafeArgumentsError()

    path = match.group('path')

    # write access is always sufficient
    newpath = access.allowed(
        config=cfg,
        user=user,
        mode='writable',
        path=path)

    if newpath is None:
        # didn't have write access; try once more with the popular
        # misspelling
        newpath = access.allowed(
            config=cfg,
            user=user,
            mode='writeable',
            path=path)
        if newpath is not None:
            log.warning('Repository %r config has typo "writeable", '
                +'should be "writable"',
                path,
                )

    if newpath is None:
        # didn't have write access

        newpath = access.allowed(
            config=cfg,
            user=user,
            mode='readonly',
            path=path)

        if newpath is None:
            raise ReadAccessDenied()

        if verb in COMMANDS_WRITE:
            # didn't have write access and tried to write
            raise WriteAccessDenied()

    (topdir, relpath) = newpath
    assert not relpath.endswith('.git'), \
           'git extension should have been stripped: %r' % relpath
    repopath = '%s.git' % relpath
    fullpath = os.path.join(topdir, repopath)
    if not os.path.exists(fullpath):
        # it doesn't exist on the filesystem, but the configuration
        # refers to it, we're serving a write request, and the user is
        # authorized to do that: create the repository on the fly

        # create leading directories
        path = topdir
        newdirmode = cfg.get('repo %s' % (relpath, ), 'dirmode')
        if newdirmode is None:
            newdirmode = cfg.get('gitosis', 'dirmode', default='0750')

        # Convert string as octal to a number
        newdirmode = int(newdirmode, 8)

        for segment in repopath.split(os.sep)[:-1]:
            path = os.path.join(path, segment)
            util.mkdir(path, newdirmode)

        repository.init(path=fullpath, mode=newdirmode)
        run_hook.build_reposistory_data(cfg)

    # put the verb back together with the new path
    newcmd = "%(verb)s '%(path)s'" % dict(
        verb=verb,
        path=fullpath,
        )
    return newcmd
Exemplo n.º 16
0
def serve(cfg, user, command):
    if "\n" in command:
        raise CommandMayNotContainNewlineError()

    try:
        verb, args = command.split(None, 1)
    except ValueError:
        # all known "git-foo" commands take one argument; improve
        # if/when needed
        raise UnknownCommandError()

    if verb == "git":
        try:
            subverb, args = args.split(None, 1)
        except ValueError:
            # all known "git foo" commands take one argument; improve
            # if/when needed
            raise UnknownCommandError()
        verb = "%s %s" % (verb, subverb)

    if verb not in COMMANDS_WRITE and verb not in COMMANDS_READONLY:
        raise UnknownCommandError()

    match = ALLOW_RE.match(args)
    if match is None:
        raise UnsafeArgumentsError()

    path = match.group("path")

    decode_id = cfg.get_gitosis("decodeID")
    if decode_id and util.parse_bool(decode_id):
        encoded_user = user
        user = util.decode_id(encoded_user)

        log.debug("decodeID = yes, decode '%s' as '%s'" % (encoded_user, user))

        if not user:
            raise BadEncodedID()

    newpath = access.haveAccess(config=cfg, user=user, mode="RW+", path=path)

    if newpath == None:
        if verb in COMMANDS_WRITE:
            raise WriteAccessDenied()
        else:
            newpath = access.haveAccess(config=cfg, user=user, mode="R", path=path)

            if newpath == None:
                raise ReadAccessDenied()

    (repobase, reponame) = newpath
    assert not reponame.endswith(".git"), "git extension should have been stripped: %r" % reponame
    repopath = reponame + ".git"
    fullpath = os.path.join(repobase, repopath)
    if not os.path.exists(fullpath):
        # it doesn't exist on the filesystem, but the configuration
        # refers to it, we're serving a write request, and the user is
        # authorized to do that: create the repository on the fly

        # create leading directories
        p = repobase
        components = repopath.split(os.sep)[:-1]
        for c in components:  # Check
            if c.endswith(".git"):
                raise BadRepositoryPath()
        for c in components:
            p = os.path.join(p, c)
            util.mkdir(p, 0750)

        props = (gitdaemon.DaemonProp(), gitweb.GitwebProp(), gitweb.DescriptionProp(), gitweb.OwnerProp())

        ext_props = cfg.get_gitosis("extProps") or ()
        if ext_props:
            try:
                ext_props_expanded = os.path.join(os.path.expanduser("~"), ext_props)
                dir_ = os.path.dirname(ext_props_expanded)
                file_ = os.path.basename(ext_props_expanded)
                mod_, ext_ = os.path.splitext(file_)
                assert mod_, "'%s': empty module name" % file_
                assert ext_ == ".py", "the extname of '%s' is not '.py'" % file_

                sys.path.append(dir_)
                mod_ = __import__(mod_)
                ext_props_ = mod_.get_props()
            except (AssertionError, ImportError) as e:
                log.warning("Invalid extProps value '%s': %s" % (ext_props, str(e)))
                ext_props = ()
            except:
                log.warning("Bad module '%s': %s" % (ext_props, str(sys.exc_info()[1])))
                ext_props = ()
            else:
                ext_props = ext_props_

        repository.init(path=fullpath)
        util.RepositoryDir(cfg, props + ext_props).visit_one(reponame)
        generated = util.getGeneratedFilesDir(config=cfg)
        gitweb.ProjectList(os.path.join(generated, "projects.list")).update()

    # put the verb back together with the new path
    newcmd = "%(verb)s '%(path)s'" % dict(verb=verb, path=fullpath)
    return newcmd