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:
Tommi Virtanen 2007-09-03 17:04:41 -07:00
parent e492d76c29
commit 8a0654abe2
7 changed files with 185 additions and 242 deletions

73
gitosis/app.py Normal file
View 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')

View file

@ -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)

View file

@ -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.')

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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',
],
},