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 import os, urllib, logging
from ConfigParser import RawConfigParser, NoSectionError, NoOptionError from ConfigParser import NoSectionError, NoOptionError
from gitosis import util from gitosis import util
@ -113,31 +113,3 @@ def generate(config, path):
f.close() f.close()
os.rename(tmp, path) 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 errno
import logging import logging
import optparse
import os import os
import re import re
import subprocess import subprocess
@ -16,13 +15,10 @@ from ConfigParser import RawConfigParser
from gitosis import repository from gitosis import repository
from gitosis import util from gitosis import util
from gitosis import app
log = logging.getLogger('gitosis.init') log = logging.getLogger('gitosis.init')
def die(msg):
log.error(msg)
sys.exit(1)
def read_ssh_pubkey(fp=None): def read_ssh_pubkey(fp=None):
if fp is None: if fp is None:
fp = sys.stdin 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): def run_post_update(git_dir):
args = [os.path.join(git_dir, 'hooks', 'post-update')] args = [os.path.join(git_dir, 'hooks', 'post-update')]
returncode = subprocess.call( returncode = subprocess.call(
@ -64,10 +66,7 @@ def run_post_update(git_dir):
env=dict(GIT_DIR='.'), env=dict(GIT_DIR='.'),
) )
if returncode != 0: if returncode != 0:
die( raise PostUpdateFailedError('exit status %d' % returncode)
("post-update returned non-zero exit status %d"
% returncode),
)
def symlink_config(git_dir): def symlink_config(git_dir):
dst = os.path.expanduser('~/.gitosis.conf') dst = os.path.expanduser('~/.gitosis.conf')
@ -85,20 +84,6 @@ def symlink_config(git_dir):
) )
os.rename(tmp, dst) 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( def init_admin_repository(
git_dir, git_dir,
pubkey, pubkey,
@ -130,37 +115,26 @@ def init_admin_repository(
user=user, user=user,
) )
def main(): 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
def handle_args(self, parser, cfg, options, args):
super(Main, self).handle_args(parser, cfg, options, args)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
os.umask(0022) os.umask(0022)
parser = getParser()
(options, args) = parser.parse_args()
if args:
parser.error('Did not expect arguments.')
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:
try:
cfg.readfp(conffile)
finally:
conffile.close()
log.info('Reading SSH public key...') log.info('Reading SSH public key...')
pubkey = read_ssh_pubkey() pubkey = read_ssh_pubkey()
user = ssh_extract_user(pubkey) user = ssh_extract_user(pubkey)
if user is None: if user is None:
die('Cannot parse user from SSH public key.') log.error('Cannot parse user from SSH public key.')
sys.exit(1)
log.info('Admin user is %r', user) log.info('Admin user is %r', user)
log.info('Creating repository structure...') log.info('Creating repository structure...')
repositories = util.getRepositoryDir(cfg) repositories = util.getRepositoryDir(cfg)
@ -172,7 +146,11 @@ def main():
user=user, user=user,
) )
log.info('Running post-update hook...') log.info('Running post-update hook...')
try:
run_post_update(git_dir=admin_repository) run_post_update(git_dir=admin_repository)
except PostUpdateFailedError, e:
log.error('%s', e)
sys.exit(1)
log.info('Symlinking ~/.gitosis.conf to repository...') log.info('Symlinking ~/.gitosis.conf to repository...')
symlink_config(git_dir=admin_repository) symlink_config(git_dir=admin_repository)
log.info('Done.') log.info('Done.')

View file

@ -4,36 +4,14 @@ Perform gitosis actions for a git hook.
import errno import errno
import logging import logging
import optparse
import os import os
import sys import sys
import shutil import shutil
from ConfigParser import RawConfigParser
from gitosis import repository from gitosis import repository
from gitosis import ssh from gitosis import ssh
from gitosis import gitweb from gitosis import gitweb
from gitosis import app
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
def post_update(cfg): def post_update(cfg):
try: try:
@ -51,36 +29,27 @@ def post_update(cfg):
keydir='gitosis-export/keydir', keydir='gitosis-export/keydir',
) )
def main(): class Main(app.App):
logging.basicConfig(level=logging.INFO) def create_parser(self):
os.umask(0022) 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() def handle_args(self, parser, cfg, options, args):
(options, args) = parser.parse_args()
try: try:
(hook,) = args (hook,) = args
except ValueError: except ValueError:
parser.error('Missing argument HOOK.') parser.error('Missing argument HOOK.')
cfg = RawConfigParser() log = logging.getLogger('gitosis.run_hook')
try: os.umask(0022)
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:
try:
cfg.readfp(conffile)
finally:
conffile.close()
git_dir = os.environ.get('GIT_DIR') git_dir = os.environ.get('GIT_DIR')
if git_dir is None: if git_dir is None:
die('Must have GIT_DIR set in enviroment.') log.error('Must have GIT_DIR set in enviroment')
sys.exit(1)
os.chdir(git_dir) os.chdir(git_dir)
os.environ['GIT_DIR'] = '.' os.environ['GIT_DIR'] = '.'
@ -90,4 +59,3 @@ def main():
log.info('Done.') log.info('Done.')
else: else:
log.warning('Ignoring unknown hook: %r', hook) log.warning('Ignoring unknown hook: %r', hook)
return 0

View file

@ -6,29 +6,11 @@ prefix. Repository names are forced to match ALLOW_RE.
import logging import logging
import sys, os, optparse, re import sys, os, re
from ConfigParser import RawConfigParser
from gitosis import access from gitosis import access
from gitosis import repository from gitosis import repository
from gitosis import app
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
ALLOW_RE = re.compile("^'(?P<path>[a-zA-Z0-9][a-zA-Z0-9@._-]*(/[a-zA-Z0-9][a-zA-Z0-9@._-]*)*)'$") ALLOW_RE = re.compile("^'(?P<path>[a-zA-Z0-9][a-zA-Z0-9@._-]*(/[a-zA-Z0-9][a-zA-Z0-9@._-]*)*)'$")
@ -124,37 +106,32 @@ def serve(
) )
return newcmd return newcmd
def main(): class Main(app.App):
logging.basicConfig(level=logging.DEBUG) def create_parser(self):
log = logging.getLogger('gitosis.serve.main') parser = super(Main, self).create_parser()
os.umask(0022) parser.set_usage('%prog [OPTS] USER')
parser.set_description(
'Allow restricted git operations under DIR')
return parser
parser = getParser() def handle_args(self, parser, cfg, options, args):
(options, args) = parser.parse_args()
try: try:
(user,) = args (user,) = args
except ValueError: except ValueError:
parser.error('Missing argument USER.') parser.error('Missing argument USER.')
log = logging.getLogger('gitosis.serve.main')
os.umask(0022)
cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None) cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None)
if cmd is None: if cmd is None:
die("Need SSH_ORIGINAL_COMMAND in environment.") log.error('Need SSH_ORIGINAL_COMMAND in environment.')
sys.exit(1)
log.debug('Got command %(cmd)r' % dict( log.debug('Got command %(cmd)r' % dict(
cmd=cmd, cmd=cmd,
)) ))
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()
os.chdir(os.path.expanduser('~')) os.chdir(os.path.expanduser('~'))
try: try:
@ -164,8 +141,10 @@ def main():
command=cmd, command=cmd,
) )
except ServingError, e: except ServingError, e:
die(str(e)) log.error('%s', e)
sys.exit(1)
log.debug('Serving %s', newcmd) log.debug('Serving %s', newcmd)
os.execvpe('git-shell', ['git-shell', '-c', newcmd], {}) os.execvpe('git-shell', ['git-shell', '-c', newcmd], {})
die("Cannot execute git-shell.") log.error('Cannot execute git-shell.')
sys.exit(1)

View file

@ -77,28 +77,3 @@ def writeAuthorizedKeys(path, keydir):
if in_ is not None: if in_ is not None:
in_.close() in_.close()
os.rename(tmp, path) 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 = { entry_points = {
'console_scripts': [ 'console_scripts': [
'gitosis-ssh = gitosis.ssh:main', 'gitosis-serve = gitosis.serve:Main.run',
'gitosis-serve = gitosis.serve:main', 'gitosis-run-hook = gitosis.run_hook:Main.run',
'gitosis-gitweb = gitosis.gitweb:main', 'gitosis-init = gitosis.init:Main.run',
'gitosis-run-hook = gitosis.run_hook:main',
'gitosis-init = gitosis.init:main',
], ],
}, },