Refactor command line utilities to share setup.
Hide internal gitosis-ssh and gitosis-gitweb, it's all in gitosis-run-hook.
This commit is contained in:
parent
e492d76c29
commit
8a0654abe2
7 changed files with 185 additions and 242 deletions
73
gitosis/app.py
Normal file
73
gitosis/app.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
import sys
|
||||
import logging
|
||||
import optparse
|
||||
import ConfigParser
|
||||
|
||||
class App(object):
|
||||
name = None
|
||||
|
||||
@classmethod
|
||||
def run(class_):
|
||||
app = class_()
|
||||
return app.main()
|
||||
|
||||
def main(self):
|
||||
parser = self.create_parser()
|
||||
(options, args) = parser.parse_args()
|
||||
cfg = self.read_config(options)
|
||||
self.setup_logging(cfg)
|
||||
self.handle_args(parser, cfg, options, args)
|
||||
|
||||
def create_parser(self):
|
||||
parser = optparse.OptionParser()
|
||||
parser.set_defaults(
|
||||
config=os.path.expanduser('~/.gitosis.conf'),
|
||||
)
|
||||
parser.add_option('--config',
|
||||
metavar='FILE',
|
||||
help='read config from FILE',
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def read_config(self, options):
|
||||
cfg = ConfigParser.RawConfigParser()
|
||||
try:
|
||||
conffile = file(options.config)
|
||||
except (IOError, OSError), e:
|
||||
# I trust the exception has the path.
|
||||
print >>sys.stderr, '%s: Unable to read config file: %s' \
|
||||
% (options.get_prog_name(), e)
|
||||
sys.exit(1)
|
||||
try:
|
||||
cfg.readfp(conffile)
|
||||
finally:
|
||||
conffile.close()
|
||||
return cfg
|
||||
|
||||
def setup_logging(self, cfg):
|
||||
logging.basicConfig()
|
||||
|
||||
try:
|
||||
loglevel = cfg.get('gitosis', 'loglevel')
|
||||
except (ConfigParser.NoSectionError,
|
||||
ConfigParser.NoOptionError):
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
symbolic = logging._levelNames[loglevel]
|
||||
except KeyError:
|
||||
# need to delay error reporting until we've called
|
||||
# basicConfig
|
||||
log = logging.getLogger('gitosis.app')
|
||||
log.warning(
|
||||
'Ignored invalid loglevel configuration: %r',
|
||||
loglevel,
|
||||
)
|
||||
else:
|
||||
logging.root.setLevel(symbolic)
|
||||
|
||||
def handle_args(self, parser, options, args):
|
||||
if args:
|
||||
parser.error('not expecting arguments')
|
|
@ -27,7 +27,7 @@ To plug this into ``gitweb``, you have two choices.
|
|||
|
||||
import os, urllib, logging
|
||||
|
||||
from ConfigParser import RawConfigParser, NoSectionError, NoOptionError
|
||||
from ConfigParser import NoSectionError, NoOptionError
|
||||
|
||||
from gitosis import util
|
||||
|
||||
|
@ -113,31 +113,3 @@ def generate(config, path):
|
|||
f.close()
|
||||
|
||||
os.rename(tmp, path)
|
||||
|
||||
def _getParser():
|
||||
import optparse
|
||||
parser = optparse.OptionParser(
|
||||
usage="%prog [--config=FILE] PROJECTSLIST")
|
||||
parser.set_defaults(
|
||||
config=os.path.expanduser('~/.gitosis.conf'),
|
||||
)
|
||||
parser.add_option('--config',
|
||||
metavar='FILE',
|
||||
help='read config from FILE (default %s)'
|
||||
% parser.defaults['config'],
|
||||
)
|
||||
return parser
|
||||
|
||||
def main():
|
||||
parser = _getParser()
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error('Expected one command line argument.')
|
||||
|
||||
path, = args
|
||||
|
||||
cfg = RawConfigParser()
|
||||
cfg.read(options.config)
|
||||
|
||||
generate(config=cfg, path=path)
|
||||
|
|
110
gitosis/init.py
110
gitosis/init.py
|
@ -4,7 +4,6 @@ Initialize a user account for use with gitosis.
|
|||
|
||||
import errno
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
@ -16,13 +15,10 @@ from ConfigParser import RawConfigParser
|
|||
|
||||
from gitosis import repository
|
||||
from gitosis import util
|
||||
from gitosis import app
|
||||
|
||||
log = logging.getLogger('gitosis.init')
|
||||
|
||||
def die(msg):
|
||||
log.error(msg)
|
||||
sys.exit(1)
|
||||
|
||||
def read_ssh_pubkey(fp=None):
|
||||
if fp is None:
|
||||
fp = sys.stdin
|
||||
|
@ -55,6 +51,12 @@ def initial_commit(git_dir, cfg, pubkey, user):
|
|||
],
|
||||
)
|
||||
|
||||
class PostUpdateFailedError(Exception):
|
||||
"""post-update hook failed"""
|
||||
|
||||
def __str__(self):
|
||||
return '%s: %s' % (self.__doc__, ': '.join(self.args))
|
||||
|
||||
def run_post_update(git_dir):
|
||||
args = [os.path.join(git_dir, 'hooks', 'post-update')]
|
||||
returncode = subprocess.call(
|
||||
|
@ -64,10 +66,7 @@ def run_post_update(git_dir):
|
|||
env=dict(GIT_DIR='.'),
|
||||
)
|
||||
if returncode != 0:
|
||||
die(
|
||||
("post-update returned non-zero exit status %d"
|
||||
% returncode),
|
||||
)
|
||||
raise PostUpdateFailedError('exit status %d' % returncode)
|
||||
|
||||
def symlink_config(git_dir):
|
||||
dst = os.path.expanduser('~/.gitosis.conf')
|
||||
|
@ -85,20 +84,6 @@ def symlink_config(git_dir):
|
|||
)
|
||||
os.rename(tmp, dst)
|
||||
|
||||
def getParser():
|
||||
parser = optparse.OptionParser(
|
||||
usage='%prog',
|
||||
description='Initialize a user account for use with gitosis',
|
||||
)
|
||||
parser.set_defaults(
|
||||
config=os.path.expanduser('~/.gitosis.conf'),
|
||||
)
|
||||
parser.add_option('--config',
|
||||
metavar='FILE',
|
||||
help='read config from FILE',
|
||||
)
|
||||
return parser
|
||||
|
||||
def init_admin_repository(
|
||||
git_dir,
|
||||
pubkey,
|
||||
|
@ -130,49 +115,42 @@ def init_admin_repository(
|
|||
user=user,
|
||||
)
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
os.umask(0022)
|
||||
class Main(app.App):
|
||||
def create_parser(self):
|
||||
parser = super(Main, self).create_parser()
|
||||
parser.set_usage('%prog [OPTS]')
|
||||
parser.set_description(
|
||||
'Initialize a user account for use with gitosis')
|
||||
return parser
|
||||
|
||||
parser = getParser()
|
||||
(options, args) = parser.parse_args()
|
||||
if args:
|
||||
parser.error('Did not expect arguments.')
|
||||
def handle_args(self, parser, cfg, options, args):
|
||||
super(Main, self).handle_args(parser, cfg, options, args)
|
||||
|
||||
cfg = RawConfigParser()
|
||||
try:
|
||||
conffile = file(options.config)
|
||||
except (IOError, OSError), e:
|
||||
if e.errno == errno.ENOENT:
|
||||
# not existing is ok
|
||||
pass
|
||||
else:
|
||||
# I trust the exception has the path.
|
||||
die("Unable to read config file: %s." % e)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
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 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...')
|
||||
try:
|
||||
cfg.readfp(conffile)
|
||||
finally:
|
||||
conffile.close()
|
||||
|
||||
|
||||
log.info('Reading SSH public key...')
|
||||
pubkey = read_ssh_pubkey()
|
||||
user = ssh_extract_user(pubkey)
|
||||
if user is None:
|
||||
die('Cannot parse user from SSH public key.')
|
||||
log.info('Admin user is %r', user)
|
||||
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...')
|
||||
run_post_update(git_dir=admin_repository)
|
||||
log.info('Symlinking ~/.gitosis.conf to repository...')
|
||||
symlink_config(git_dir=admin_repository)
|
||||
log.info('Done.')
|
||||
run_post_update(git_dir=admin_repository)
|
||||
except PostUpdateFailedError, e:
|
||||
log.error('%s', e)
|
||||
sys.exit(1)
|
||||
log.info('Symlinking ~/.gitosis.conf to repository...')
|
||||
symlink_config(git_dir=admin_repository)
|
||||
log.info('Done.')
|
||||
|
|
|
@ -4,36 +4,14 @@ Perform gitosis actions for a git hook.
|
|||
|
||||
import errno
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
from ConfigParser import RawConfigParser
|
||||
|
||||
from gitosis import repository
|
||||
from gitosis import ssh
|
||||
from gitosis import gitweb
|
||||
|
||||
log = logging.getLogger('gitosis.run_hook')
|
||||
|
||||
def die(msg):
|
||||
log.error(msg)
|
||||
sys.exit(1)
|
||||
|
||||
def getParser():
|
||||
parser = optparse.OptionParser(
|
||||
usage='%prog HOOK',
|
||||
description='Perform gitosis actions for a git hook',
|
||||
)
|
||||
parser.set_defaults(
|
||||
config=os.path.expanduser('~/.gitosis.conf'),
|
||||
)
|
||||
parser.add_option('--config',
|
||||
metavar='FILE',
|
||||
help='read config from FILE',
|
||||
)
|
||||
return parser
|
||||
from gitosis import app
|
||||
|
||||
def post_update(cfg):
|
||||
try:
|
||||
|
@ -51,43 +29,33 @@ def post_update(cfg):
|
|||
keydir='gitosis-export/keydir',
|
||||
)
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
os.umask(0022)
|
||||
class Main(app.App):
|
||||
def create_parser(self):
|
||||
parser = super(Main, self).create_parser()
|
||||
parser.set_usage('%prog [OPTS] HOOK')
|
||||
parser.set_description(
|
||||
'Perform gitosis actions for a git hook')
|
||||
return parser
|
||||
|
||||
parser = getParser()
|
||||
(options, args) = parser.parse_args()
|
||||
try:
|
||||
(hook,) = args
|
||||
except ValueError:
|
||||
parser.error('Missing argument HOOK.')
|
||||
|
||||
cfg = RawConfigParser()
|
||||
try:
|
||||
conffile = file(options.config)
|
||||
except (IOError, OSError), e:
|
||||
if e.errno == errno.ENOENT:
|
||||
# not existing is ok
|
||||
pass
|
||||
else:
|
||||
# I trust the exception has the path.
|
||||
die("Unable to read config file: %s." % e)
|
||||
else:
|
||||
def handle_args(self, parser, cfg, options, args):
|
||||
try:
|
||||
cfg.readfp(conffile)
|
||||
finally:
|
||||
conffile.close()
|
||||
(hook,) = args
|
||||
except ValueError:
|
||||
parser.error('Missing argument HOOK.')
|
||||
|
||||
git_dir = os.environ.get('GIT_DIR')
|
||||
if git_dir is None:
|
||||
die('Must have GIT_DIR set in enviroment.')
|
||||
os.chdir(git_dir)
|
||||
os.environ['GIT_DIR'] = '.'
|
||||
log = logging.getLogger('gitosis.run_hook')
|
||||
os.umask(0022)
|
||||
|
||||
if hook == 'post-update':
|
||||
log.info('Running hook %s', hook)
|
||||
post_update(cfg)
|
||||
log.info('Done.')
|
||||
else:
|
||||
log.warning('Ignoring unknown hook: %r', hook)
|
||||
return 0
|
||||
git_dir = os.environ.get('GIT_DIR')
|
||||
if git_dir is None:
|
||||
log.error('Must have GIT_DIR set in enviroment')
|
||||
sys.exit(1)
|
||||
os.chdir(git_dir)
|
||||
os.environ['GIT_DIR'] = '.'
|
||||
|
||||
if hook == 'post-update':
|
||||
log.info('Running hook %s', hook)
|
||||
post_update(cfg)
|
||||
log.info('Done.')
|
||||
else:
|
||||
log.warning('Ignoring unknown hook: %r', hook)
|
||||
|
|
|
@ -6,29 +6,11 @@ prefix. Repository names are forced to match ALLOW_RE.
|
|||
|
||||
import logging
|
||||
|
||||
import sys, os, optparse, re
|
||||
from ConfigParser import RawConfigParser
|
||||
import sys, os, re
|
||||
|
||||
from gitosis import access
|
||||
from gitosis import repository
|
||||
|
||||
def die(msg):
|
||||
print >>sys.stderr, '%s: %s' % (sys.argv[0], msg)
|
||||
sys.exit(1)
|
||||
|
||||
def getParser():
|
||||
parser = optparse.OptionParser(
|
||||
usage='%prog [--config=FILE] USER',
|
||||
description='Allow restricted git operations under DIR',
|
||||
)
|
||||
parser.set_defaults(
|
||||
config=os.path.expanduser('~/.gitosis.conf'),
|
||||
)
|
||||
parser.add_option('--config',
|
||||
metavar='FILE',
|
||||
help='read config from FILE',
|
||||
)
|
||||
return parser
|
||||
from gitosis import app
|
||||
|
||||
ALLOW_RE = re.compile("^'(?P<path>[a-zA-Z0-9][a-zA-Z0-9@._-]*(/[a-zA-Z0-9][a-zA-Z0-9@._-]*)*)'$")
|
||||
|
||||
|
@ -124,48 +106,45 @@ def serve(
|
|||
)
|
||||
return newcmd
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
log = logging.getLogger('gitosis.serve.main')
|
||||
os.umask(0022)
|
||||
class Main(app.App):
|
||||
def create_parser(self):
|
||||
parser = super(Main, self).create_parser()
|
||||
parser.set_usage('%prog [OPTS] USER')
|
||||
parser.set_description(
|
||||
'Allow restricted git operations under DIR')
|
||||
return parser
|
||||
|
||||
parser = getParser()
|
||||
(options, args) = parser.parse_args()
|
||||
try:
|
||||
(user,) = args
|
||||
except ValueError:
|
||||
parser.error('Missing argument USER.')
|
||||
def handle_args(self, parser, cfg, options, args):
|
||||
try:
|
||||
(user,) = args
|
||||
except ValueError:
|
||||
parser.error('Missing argument USER.')
|
||||
|
||||
cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None)
|
||||
if cmd is None:
|
||||
die("Need SSH_ORIGINAL_COMMAND in environment.")
|
||||
log = logging.getLogger('gitosis.serve.main')
|
||||
os.umask(0022)
|
||||
|
||||
log.debug('Got command %(cmd)r' % dict(
|
||||
cmd=cmd,
|
||||
))
|
||||
cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None)
|
||||
if cmd is None:
|
||||
log.error('Need SSH_ORIGINAL_COMMAND in environment.')
|
||||
sys.exit(1)
|
||||
|
||||
cfg = RawConfigParser()
|
||||
try:
|
||||
conffile = file(options.config)
|
||||
except (IOError, OSError), e:
|
||||
# I trust the exception has the path.
|
||||
die("Unable to read config file: %s." % e)
|
||||
try:
|
||||
cfg.readfp(conffile)
|
||||
finally:
|
||||
conffile.close()
|
||||
log.debug('Got command %(cmd)r' % dict(
|
||||
cmd=cmd,
|
||||
))
|
||||
|
||||
os.chdir(os.path.expanduser('~'))
|
||||
os.chdir(os.path.expanduser('~'))
|
||||
|
||||
try:
|
||||
newcmd = serve(
|
||||
cfg=cfg,
|
||||
user=user,
|
||||
command=cmd,
|
||||
)
|
||||
except ServingError, e:
|
||||
die(str(e))
|
||||
try:
|
||||
newcmd = serve(
|
||||
cfg=cfg,
|
||||
user=user,
|
||||
command=cmd,
|
||||
)
|
||||
except ServingError, e:
|
||||
log.error('%s', e)
|
||||
sys.exit(1)
|
||||
|
||||
log.debug('Serving %s', newcmd)
|
||||
os.execvpe('git-shell', ['git-shell', '-c', newcmd], {})
|
||||
die("Cannot execute git-shell.")
|
||||
log.debug('Serving %s', newcmd)
|
||||
os.execvpe('git-shell', ['git-shell', '-c', newcmd], {})
|
||||
log.error('Cannot execute git-shell.')
|
||||
sys.exit(1)
|
||||
|
|
|
@ -77,28 +77,3 @@ def writeAuthorizedKeys(path, keydir):
|
|||
if in_ is not None:
|
||||
in_.close()
|
||||
os.rename(tmp, path)
|
||||
|
||||
def _getParser():
|
||||
import optparse
|
||||
parser = optparse.OptionParser(
|
||||
usage="%prog [--authkeys=FILE] KEYDIR")
|
||||
parser.set_defaults(
|
||||
authkeys=os.path.expanduser('~/.ssh/authorized_keys'),
|
||||
)
|
||||
parser.add_option(
|
||||
"--authkeys",
|
||||
help="path to SSH authorized keys file")
|
||||
return parser
|
||||
|
||||
def main():
|
||||
parser = _getParser()
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
parser.error('Need one argument on the command line.')
|
||||
|
||||
keydir, = args
|
||||
|
||||
writeAuthorizedKeys(
|
||||
path=options.authkeys,
|
||||
keydir=keydir)
|
||||
|
|
8
setup.py
8
setup.py
|
@ -30,11 +30,9 @@ setup(
|
|||
|
||||
entry_points = {
|
||||
'console_scripts': [
|
||||
'gitosis-ssh = gitosis.ssh:main',
|
||||
'gitosis-serve = gitosis.serve:main',
|
||||
'gitosis-gitweb = gitosis.gitweb:main',
|
||||
'gitosis-run-hook = gitosis.run_hook:main',
|
||||
'gitosis-init = gitosis.init:main',
|
||||
'gitosis-serve = gitosis.serve:Main.run',
|
||||
'gitosis-run-hook = gitosis.run_hook:Main.run',
|
||||
'gitosis-init = gitosis.init:Main.run',
|
||||
],
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in a new issue