"""
Initialize a user account for use with gitosis.
"""
import logging
import os
import sys
from pkg_resources import resource_filename
from cStringIO import StringIO
from gitosis import configutil
from gitosis import repository
from gitosis import run_hook
from gitosis import sshkey
from gitosis import util
from gitosis import app
log = logging.getLogger('gitosis.init')
def read_ssh_pubkey(filename=None):
"""Read an SSH public key from stdin or file."""
if filename is None:
fp = sys.stdin
else:
fp = file(filename)
line = fp.readline()
return line
def initial_commit(git_dir, cfg, pubkey, user):
"""Import the initial files into the gitosis-admin repository."""
repository.fast_import(
git_dir=git_dir,
commit_msg='Automatic creation of gitosis repository.',
committer='Gitosis Admin <%s>' % user,
files=[
('keydir/%s.pub' % user, pubkey),
('gitosis.conf', cfg),
],
)
def symlink_config(git_dir):
"""
Place a symlink for the gitosis.conf file in the homedir of the gitosis
user, to make possible to find initially.
"""
dst = os.path.expanduser('~/.gitosis.conf')
tmp = '%s.%d.tmp' % (dst, os.getpid())
util.unlink(tmp)
os.symlink(
os.path.join(git_dir, 'gitosis.conf'),
tmp,
)
os.rename(tmp, dst)
def init_admin_repository(git_dir, pubkey, user, config):
"""Create the initial gitosis-admin reposistory."""
repository.init(
path=git_dir,
template=resource_filename('gitosis.templates', 'admin')
)
repository.init(
path=git_dir,
)
if not config.has_section('gitosis'):
config.add_section('gitosis')
if not config.has_section('group gitosis-admin'):
config.add_section('group gitosis-admin')
if not config.has_option('group gitosis-admin', 'writable'):
config.set('group gitosis-admin', 'writable', 'gitosis-admin')
adminlist = configutil.get_default(config, 'group gitosis-admin', 'members',' ').split()
if user not in adminlist:
adminlist.append(user)
config.set('group gitosis-admin', 'members', ' '.join(adminlist))
if not repository.has_initial_commit(git_dir):
log.info('Making initial commit...')
cfg_file = StringIO()
config.write(cfg_file)
initial_commit(
git_dir=git_dir,
cfg=cfg_file.getvalue(),
pubkey=pubkey,
user=user,
)
class Main(app.App):
"""gitosis-init program."""
def create_parser(self):
"""Declare the input for this program."""
parser = super(Main, self).create_parser()
parser.set_usage('%prog [OPTS]')
parser.set_description(
'Initialize a user account for use with gitosis'
)
parser.set_defaults(
adminkey=None,
adminname=None,
)
parser.add_option('--adminkey',
metavar='FILE',
help='Admin SSH public key FILE location',
)
parser.add_option('--adminname',
metavar='STRING',
help='Name for administrator public key file',
)
return parser
def read_config(self, options, cfg):
"""Ignore errors that result from non-existent config file."""
try:
super(Main, self).read_config(options, cfg)
except app.ConfigFileDoesNotExistError:
pass
def handle_args(self, parser, cfg, options, args):
"""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 = 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,
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.')