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
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
|
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)
|
|
||||||
|
|
|
@ -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.')
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
8
setup.py
8
setup.py
|
@ -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',
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue