def _lazy(key, default=None): question = _lazy_queries[key]['question'] default = default or _lazy_queries[key]['default'] val = _lazy_dict.get(key, None) if not val: val = query_input(question, default) _lazy_dict[key] = val return val
def restore_tracenv_from_backup_tarball(site_dir, bin_dir): # FIXME stop trac if it is running already filename_tarball = query_input('tarball path?') run(flo('mkdir -p {site_dir}/tmp')) run(flo('tar xf {filename_tarball} --directory={site_dir}/tmp')) # save tracenv if it exists run(flo('mv {site_dir}/tracenv {site_dir}/tracenv.before_$(date +%F).bak' ' || true')) run(flo('mv {site_dir}/tmp/tracenv_hotcopy {site_dir}/tracenv')) run(flo('rmdir {site_dir}/tmp'))
def selfoss_password(sitename): password = query_input('selfoss password:'******'curl -d password="******" -d form_submit="send" ' '--insecure https://{sitename}/password 2>/dev/null | ' 'grep "Generated Password"'), capture=True) match = re.search(r'value="([^"]+)"', res) pw_hash = match.group(1) print(flo('{pw_hash}')) update_or_append_line(flo('~/sites/{sitename}/selfoss/config.ini'), prefix='password='******'password={pw_hash}'))
def revealjs(basedir=None, title=None, subtitle=None, description=None, github_user=None, github_repo=None): '''Set up or update a reveals.js presentation with slides written in markdown. Several reveal.js plugins will be set up, too. More info: Demo: https://theno.github.io/revealjs_template http://lab.hakim.se/reveal-js/ https://github.com/hakimel/reveal.js plugins: https://github.com/hakimel/reveal.js/wiki/Plugins,-Tools-and-Hardware https://github.com/rajgoel/reveal.js-plugins/ https://github.com/e-gor/Reveal.js-TOC-Progress https://github.com/e-gor/Reveal.js-Title-Footer ''' basedir = basedir or query_input('Base dir of the presentation?', default='~/repos/my_presi') revealjs_repo_name = 'reveal.js' revealjs_dir = flo('{basedir}/{revealjs_repo_name}') _lazy_dict['presi_title'] = title _lazy_dict['presi_subtitle'] = subtitle _lazy_dict['presi_description'] = description _lazy_dict['github_user'] = github_user _lazy_dict['github_repo'] = github_repo question = flo( "Base dir already contains a sub dir '{revealjs_repo_name}'." ' Reset (and re-download) reveal.js codebase?') if not exists(revealjs_dir) or query_yes_no(question, default='no'): run(flo('mkdir -p {basedir}')) set_up_revealjs_codebase(basedir, revealjs_repo_name) install_plugins(revealjs_dir) apply_customizations(repo_dir=revealjs_dir) if exists(revealjs_dir): install_files_in_basedir(basedir, repo_dir=revealjs_dir) init_git_repo(basedir) create_github_remote_repo(basedir) setup_npm(revealjs_dir) else: print('abort')
def revealjs(basedir=None, title=None, subtitle=None, description=None, github_user=None, github_repo=None): '''Set up or update a reveals.js presentation with slides written in markdown. Several reveal.js plugins will be set up, too. More info: Demo: https://theno.github.io/revealjs_template http://lab.hakim.se/reveal-js/ https://github.com/hakimel/reveal.js plugins: https://github.com/hakimel/reveal.js/wiki/Plugins,-Tools-and-Hardware https://github.com/rajgoel/reveal.js-plugins/ https://github.com/e-gor/Reveal.js-TOC-Progress https://github.com/e-gor/Reveal.js-Title-Footer ''' basedir = basedir or query_input('Base dir of the presentation?', default='~/repos/my_presi') revealjs_repo_name = 'reveal.js' revealjs_dir = flo('{basedir}/{revealjs_repo_name}') _lazy_dict['presi_title'] = title _lazy_dict['presi_subtitle'] = subtitle _lazy_dict['presi_description'] = description _lazy_dict['github_user'] = github_user _lazy_dict['github_repo'] = github_repo question = flo("Base dir already contains a sub dir '{revealjs_repo_name}'." ' Reset (and re-download) reveal.js codebase?') if not exists(revealjs_dir) or query_yes_no(question, default='no'): run(flo('mkdir -p {basedir}')) set_up_revealjs_codebase(basedir, revealjs_repo_name) install_plugins(revealjs_dir) apply_customizations(repo_dir=revealjs_dir) if exists(revealjs_dir): install_files_in_basedir(basedir, repo_dir=revealjs_dir) init_git_repo(basedir) create_github_remote_repo(basedir) setup_npm(revealjs_dir) else: print('abort')
def selfoss(reset_password=False): '''Install, update and set up selfoss. This selfoss installation uses sqlite (selfoss-default), php5-fpm and nginx. The connection is https-only and secured by a letsencrypt certificate. This certificate must be created separately with task setup.server_letsencrypt. More infos: https://selfoss.aditu.de/ https://github.com/SSilence/selfoss/wiki https://www.heise.de/ct/ausgabe/2016-13-RSS-Reader-Selfoss-hat-die-Nachrichtenlage-im-Blick-3228045.html https://ct.de/yqp7 ''' hostname = re.sub(r'^[^@]+@', '', env.host) # without username if any sitename = query_input( question='\nEnter site-name of Your trac web service', default=flo('selfoss.{hostname}')) username = env.user site_dir = flo('/home/{username}/sites/{sitename}') checkout_latest_release_of_selfoss() create_directory_structure(site_dir) restored = install_selfoss(sitename, site_dir, username) nginx_site_config(username, sitename, hostname) enable_php5_socket_file() if not restored or reset_password: setup_selfoss_user(username, sitename, site_dir) print_msg('\n## reload nginx and restart php\n') run('sudo service nginx reload') run('sudo service php5-fpm restart')
def owncloud(): '''Set up owncloud. Package 'owncloud' pulls package 'mysql' which asks for a password. ''' hostname = re.sub(r'^[^@]+@', '', env.host) # without username if any sitename = query_input( question='\nEnter site-name of Your Owncloud web service', default=flo('owncloud.{hostname}'), color=cyan) username = env.user fabfile_data_dir = FABFILE_DATA_DIR print(magenta(' install owncloud')) repository = ''.join([ 'http://download.opensuse.org/repositories/', 'isv:/ownCloud:/community/Debian_7.0/', ]) with hide('output'): sudo(flo('wget -O - {repository}Release.key | apt-key add -')) filename = '/etc/apt/sources.list.d/owncloud.list' sudo(flo("echo 'deb {repository} /' > {filename}")) sudo('apt-get update') install_packages([ 'owncloud', 'php5-fpm', 'php-apc', 'memcached', 'php5-memcache', ]) # This server uses nginx. owncloud pulls apache2 => Disable apache2 print(magenta(' disable apache')) with hide('output'): sudo('service apache2 stop') sudo('update-rc.d apache2 disable') print(magenta(' nginx setup for owncloud')) filename = 'owncloud_site_config.template' path = flo('{fabfile_data_dir}/files/etc/nginx/sites-available/{filename}') from_str = filled_out_template(path, username=username, sitename=sitename, hostname=hostname) with tempfile.NamedTemporaryFile(prefix=filename) as tmp_file: with open(tmp_file.name, 'w') as fp: fp.write(from_str) put(tmp_file.name, flo('/tmp/{filename}')) to = flo('/etc/nginx/sites-available/{sitename}') sudo(flo('mv /tmp/{filename} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) sudo( flo(' '.join([ 'ln -snf ../sites-available/{sitename}', '/etc/nginx/sites-enabled/{sitename}', ]))) # php5 fpm fast-cgi config template = 'www.conf' to = flo('/etc/php5/fpm/pool.d/{template}') from_ = flo('{fabfile_data_dir}/files{to}') put(from_, '/tmp/') sudo(flo('mv /tmp/{template} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) template = 'php.ini' to = flo('/etc/php5/fpm/{template}') from_ = flo('{fabfile_data_dir}/files{to}') put(from_, '/tmp/') sudo(flo('mv /tmp/{template} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) sudo('service php5-fpm restart') sudo('service nginx reload')
def fdroid(): '''Set up an F-Droid App Repo. More infos: * https://f-droid.org/wiki/page/Setup_an_FDroid_App_Repo * https://f-droid.org/wiki/page/Installing_the_Server_and_Repo_Tools ''' hostname = re.sub(r'^[^@]+@', '', env.host) # without username if any sitename = query_input( question='\nEnter site-name of Your F-Droid web service', default=flo('fdroid.{hostname}')) username = env.user fabfile_data_dir = FABFILE_DATA_DIR print(magenta(' install fdroidserver')) res = run( 'dpkg --get-selections | ' 'grep -q "^fdroidserver[[:space:]]*install$" >/dev/null', warn_only=True) package_installed = res.return_code == 0 question = 'package fdroidserver already installed, update? ' \ '(needs some time)' if package_installed and not query_yes_no(question, default='no'): print('skip update') else: with hide('output'): sudo('yes "" | add-apt-repository ppa:guardianproject/ppa') sudo('apt-get update') # why 'android-libhost-dev' (avoid "Failed to get apk information" # on 'fdroid update --create-metadata'): # https://f-droid.org/forums/topic/failed-to-get-apk-information-2/#post-15777 install_packages(['fdroidserver', 'android-libhost-dev']) sudo('yes "" | add-apt-repository --remove ' 'ppa:guardianproject/ppa') sudo('apt-get update') site_dir = flo('/home/{username}/sites/{sitename}') apks_dir = flo('{site_dir}/apks') fdroid_dir = flo('{site_dir}/fdroid') repo_dir = flo('{site_dir}/fdroid/repo') print(magenta(' init f-droid repo')) question = ' '.join( ['already initialized, initialize again?', '(creates a new repo key)']) if exists(repo_dir) and not query_yes_no(question, default='no'): print('skip initialization') else: with warn_only(): run(flo('rm -rf {fdroid_dir}')) run(flo('mkdir -p {repo_dir}')) run(flo('cd {fdroid_dir} && fdroid init')) run(flo('cd {site_dir} && tree')) print(magenta(' update apk files of the fdroid repo')) run(flo('mkdir -p {apks_dir}')) run(flo('rm -rf {repo_dir}/*.apk')) run(flo("find {apks_dir} -type f | rename 's/ /_/g'")) run(flo("find {apks_dir} -type f | rename 's/[^[:ascii:]]//g'")) run(flo('chmod 644 {apks_dir}/*.apk')) run(flo('cp -v {apks_dir}/*.apk {repo_dir}'), warn_only=True) run(flo('cd {fdroid_dir} && fdroid update --create-metadata')) print(magenta(' setup nginx for F-Droid')) run(flo('echo -e "User-agent: *\\nDisallow: /" > {fdroid_dir}/robots.txt')) filename = 'fdroid_site_config.template' path = flo('{fabfile_data_dir}/files/etc/nginx/sites-available/{filename}') from_str = filled_out_template(path, username=username, sitename=sitename, hostname=hostname) with tempfile.NamedTemporaryFile(prefix=filename) as tmp_file: with open(tmp_file.name, 'w') as fp: fp.write(from_str) put(tmp_file.name, flo('/tmp/{filename}')) to = flo('/etc/nginx/sites-available/{sitename}') sudo(flo('mv /tmp/{filename} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) sudo( flo(' '.join([ 'ln -snf ../sites-available/{sitename}', '/etc/nginx/sites-enabled/{sitename}', ]))) sudo('service nginx reload')
def owncloud(): '''Set up owncloud. Package 'owncloud' pulls package 'mysql' which asks for a password. ''' hostname = re.sub(r'^[^@]+@', '', env.host) # without username if any sitename = query_input( question='\nEnter site-name of Your Owncloud web service', default=flo('owncloud.{hostname}'), color=cyan) username = env.user fabfile_data_dir = FABFILE_DATA_DIR print(magenta(' install owncloud')) repository = ''.join([ 'http://download.opensuse.org/repositories/', 'isv:/ownCloud:/community/Debian_7.0/', ]) with hide('output'): sudo(flo('wget -O - {repository}Release.key | apt-key add -')) filename = '/etc/apt/sources.list.d/owncloud.list' sudo(flo("echo 'deb {repository} /' > {filename}")) sudo('apt-get update') install_packages([ 'owncloud', 'php5-fpm', 'php-apc', 'memcached', 'php5-memcache', ]) # This server uses nginx. owncloud pulls apache2 => Disable apache2 print(magenta(' disable apache')) with hide('output'): sudo('service apache2 stop') sudo('update-rc.d apache2 disable') print(magenta(' nginx setup for owncloud')) filename = 'owncloud_site_config.template' path = flo('{fabfile_data_dir}/files/etc/nginx/sites-available/{filename}') from_str = filled_out_template(path, username=username, sitename=sitename, hostname=hostname) with tempfile.NamedTemporaryFile(prefix=filename) as tmp_file: with open(tmp_file.name, 'w') as fp: fp.write(from_str) put(tmp_file.name, flo('/tmp/{filename}')) to = flo('/etc/nginx/sites-available/{sitename}') sudo(flo('mv /tmp/{filename} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) sudo(flo(' '.join([ 'ln -snf ../sites-available/{sitename}', '/etc/nginx/sites-enabled/{sitename}', ]))) # php5 fpm fast-cgi config template = 'www.conf' to = flo('/etc/php5/fpm/pool.d/{template}') from_ = flo('{fabfile_data_dir}/files{to}') put(from_, '/tmp/') sudo(flo('mv /tmp/{template} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) template = 'php.ini' to = flo('/etc/php5/fpm/{template}') from_ = flo('{fabfile_data_dir}/files{to}') put(from_, '/tmp/') sudo(flo('mv /tmp/{template} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) sudo('service php5-fpm restart') sudo('service nginx reload')
def fdroid(): '''Set up an F-Droid App Repo. More infos: * https://f-droid.org/wiki/page/Setup_an_FDroid_App_Repo * https://f-droid.org/wiki/page/Installing_the_Server_and_Repo_Tools ''' hostname = re.sub(r'^[^@]+@', '', env.host) # without username if any sitename = query_input( question='\nEnter site-name of Your F-Droid web service', default=flo('fdroid.{hostname}')) username = env.user fabfile_data_dir = FABFILE_DATA_DIR print(magenta(' install fdroidserver')) res = run('dpkg --get-selections | ' 'grep -q "^fdroidserver[[:space:]]*install$" >/dev/null', warn_only=True) package_installed = res.return_code == 0 question = 'package fdroidserver already installed, update? ' \ '(needs some time)' if package_installed and not query_yes_no(question, default='no'): print('skip update') else: with hide('output'): sudo('yes "" | add-apt-repository ppa:guardianproject/ppa') sudo('apt-get update') # why 'android-libhost-dev' (avoid "Failed to get apk information" # on 'fdroid update --create-metadata'): # https://f-droid.org/forums/topic/failed-to-get-apk-information-2/#post-15777 install_packages(['fdroidserver', 'android-libhost-dev']) sudo('yes "" | add-apt-repository --remove ' 'ppa:guardianproject/ppa') sudo('apt-get update') site_dir = flo('/home/{username}/sites/{sitename}') apks_dir = flo('{site_dir}/apks') fdroid_dir = flo('{site_dir}/fdroid') repo_dir = flo('{site_dir}/fdroid/repo') print(magenta(' init f-droid repo')) question = ' '.join(['already initialized, initialize again?', '(creates a new repo key)']) if exists(repo_dir) and not query_yes_no(question, default='no'): print('skip initialization') else: with warn_only(): run(flo('rm -rf {fdroid_dir}')) run(flo('mkdir -p {repo_dir}')) run(flo('cd {fdroid_dir} && fdroid init')) run(flo('cd {site_dir} && tree')) print(magenta(' update apk files of the fdroid repo')) run(flo('mkdir -p {apks_dir}')) run(flo('rm -rf {repo_dir}/*.apk')) run(flo("find {apks_dir} -type f | rename 's/ /_/g'")) run(flo("find {apks_dir} -type f | rename 's/[^[:ascii:]]//g'")) run(flo('chmod 644 {apks_dir}/*.apk')) run(flo('cp -v {apks_dir}/*.apk {repo_dir}'), warn_only=True) run(flo('cd {fdroid_dir} && fdroid update --create-metadata')) print(magenta(' setup nginx for F-Droid')) run(flo('echo -e "User-agent: *\\nDisallow: /" > {fdroid_dir}/robots.txt')) filename = 'fdroid_site_config.template' path = flo('{fabfile_data_dir}/files/etc/nginx/sites-available/{filename}') from_str = filled_out_template(path, username=username, sitename=sitename, hostname=hostname) with tempfile.NamedTemporaryFile(prefix=filename) as tmp_file: with open(tmp_file.name, 'w') as fp: fp.write(from_str) put(tmp_file.name, flo('/tmp/{filename}')) to = flo('/etc/nginx/sites-available/{sitename}') sudo(flo('mv /tmp/{filename} {to}')) sudo(flo('chown root.root {to}')) sudo(flo('chmod 644 {to}')) sudo(flo(' '.join([ 'ln -snf ../sites-available/{sitename}', '/etc/nginx/sites-enabled/{sitename}', ]))) sudo('service nginx reload')
def selfoss_username(username, sitename): selfoss_username = query_input('selfoss user name:', default=username) update_or_append_line(flo('~/sites/{sitename}/selfoss/config.ini'), prefix='username='******'username={selfoss_username}'))
def new_addon(): '''Create a repository for a new fabsetup-task addon. The repo will contain the fabsetup addon boilerplate. Running this task you have to enter: * your github user account (your pypi account should be the same or similar) * addon name * task name * headline, short description, and touched (and created) files and dirs for the task docstring and the README.md Created files and dirs: ~/.fabsetup-addon-repos/fabsetup-{user}-{addon} ├── fabfile-dev.py ├── fabfile.py ├── fabsetup_{user}_{task} │ ├── files │ │ └── home │ │ └── USERNAME │ │ └── bin │ │ └── termdown.template │ ├── __init__.py <--. │ └── _version.py `- task definition ├── .git │ ├── ... │ ├── config │ └── ... ├── .gitignore ├── README.md ├── requirements.txt └── setup.py ''' author, author_email = git_name_and_email_or_die() username = query_input('github username:'******'\naddon name:', default='termdown') addonname = addonname.replace('_', '-').replace(' ', '-') # minus only full_addonname = flo('fabsetup-{username}-{addonname}') print('└─> full addon name: {0}\n'.format( cyan(full_addonname))) taskname = query_input('task name:', default=addonname.replace('-', '_')) taskname = taskname.replace('-', '_').replace(' ', '_') # underscores only print('└─> full task name: {0}'.format( cyan(flo('{username}.{taskname}\n')))) addon_dir = os.path.expanduser(flo( '~/.fabsetup-addon-repos/fabsetup-{username}-{addonname}')) if os.path.exists(addon_dir): print(red(flo('\n{addon_dir} already exists.'))) print('abort') else: print('~/.gitconfig') print('├─> author: {0} ─> LICENSE, setup.py'.format(cyan(author))) print('└─> author email: {0} ─> setup.py'.format(cyan(author_email))) headline = query_input( '\nshort task headline:', default='Install or update termdown.') description = query_input( '\ndescribing infos:', default='''Termdown (https://github.com/trehn/termdown) is a "[c]ountdown timer and stopwatch in your terminal". It installs termdown via `pip install --user termdown`. Also, it installs a bash-wrapper script at `~/bin/termdown` which is convenient to time pomodoro sessions and pops up a notification when the timer finishes.''') touched_files = query_input( '\naffected files, dirs, and installed packages:', default='~/bin/termdown\n ' 'pip-package termdown (`--user` install)') print('\naddon git-repository dir: {0}'.format(cyan(addon_dir))) if not query_yes_no('\ncreate new addon?', default='yes'): print('abort') else: create_files(addon_dir, username, addonname, taskname, author, author_email, headline, description, touched_files) init_git_repo(addon_dir) create_github_remote_repo(basedir=addon_dir, github_user=username, github_repo=full_addonname) summary(addon_dir, username, taskname)
def trac(): '''Set up or update a trac project. This trac installation uses python2, git, sqlite (trac-default), gunicorn, and nginx. The connection is https-only and secured by a letsencrypt certificate. This certificate must be created separately with task setup.server_letsencrypt. This task installes or updates to the latest trac version hosted at https://pypi.python.org/pypi/Trac Created and modified files and dirs of this task: ``` ~/sites/<sitename> <--- example: sitename = trac.example.com │ ├── backup.sh <--- create a local backup (deletes dir backup/ ├── backup | before it creates │ └── <sitename>_tracenv_hotcopy.tar.gz <--´ the tarball) ├── run │ └── trac.sock <--- file-socket for binding to nginx ├── scripts │ └── tracwsgi.py ├── tracenv │ ├── conf │ │ ├── trac.htpasswd <--- trac user password hashes │ │ └── trac.ini <--- trac config file │ ├── db │ │ └── trac.db <--- sqlite database │ ├── files │ ├── git │ ├── htdocs │ ├── log │ ├── plugins │ ├── README │ ├── templates │ └── VERSION └── virtualenv ├── bin ├── include ├── lib ├── local └── pip-selfcheck.json ``` How to create a backup tarball "manually": `~/sites/<sitename>/backup/tracenv_hotcopy_<yyyy-mm-dd>.tar.gz`: ``` cd ~/sites/<sitename> && rm -rf ./backup ./virtualenv/bin/trac-admin ./tracenv hotcopy ./backup/tracenv_hotcopy mkdir -p ./backup && cd ./backup tar czf <sitename>_tracenv_hotcopy_$(date +%F).tar.gz tracenv_hotcopy/ rm -rf tracenv_hotcopy; ls -hl ``` More infos: https://trac.edgewall.org/wiki/TracInstall https://trac.edgewall.org/wiki/TracFastCgi#NginxConfiguration https://trac.edgewall.org/wiki/TracNginxRecipe https://trac.edgewall.org/wiki/Gunicorn http://www.obeythetestinggoat.com/book/chapter_08.html#_getting_to_a_production_ready_deployment Setting REMOTE_USER for Trac in Gunicorn behind Nginx: http://serverfault.com/a/392096 https://trac.edgewall.org/wiki/TracBackup ''' hostname = re.sub(r'^[^@]+@', '', env.host) # without username if any sitename = query_input( question='\nEnter site-name of Your trac web service', default=flo('trac.{hostname}')) username = env.user site_dir = flo('/home/{username}/sites/{sitename}') bin_dir = flo('{site_dir}/virtualenv/bin') # provisioning steps install_or_upgrade_virtualenv_pip_package() create_directory_structure(site_dir) update_virtualenv(site_dir, sitename) set_up_trac_plugins(sitename, site_dir, bin_dir) set_up_gunicorn(site_dir, sitename) configure_nginx(username, sitename, hostname) if query_yes_no('\nRestore trac environment from backup tarball?', default='no'): restore_tracenv_from_backup_tarball(site_dir, bin_dir) elif not tracenv_exists(site_dir): init_tracenv(site_dir, bin_dir, username) upgrade_tracenv(site_dir, bin_dir) set_up_upstart_for_gunicorn(sitename, username, site_dir)