Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
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'))
Esempio n. 4
0
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}'))
Esempio n. 5
0
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')
Esempio n. 6
0
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')
Esempio n. 7
0
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')
Esempio n. 8
0
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')
Esempio n. 9
0
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')
Esempio n. 10
0
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')
Esempio n. 11
0
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')
Esempio n. 12
0
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}'))
Esempio n. 13
0
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)
Esempio n. 14
0
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)