def create_database_and_dotenv(): env.box_sentry_dsn = prompt('Sentry DSN') env.box_oauth2_client_id = prompt('Google OAuth2 Client ID') env.box_oauth2_client_secret = prompt('Google OAuth2 Client Secret') env.box_database_pw = get_random_string( 20, chars='abcdefghijklmopqrstuvwx01234567890') env.box_secret_key = get_random_string(50) run('psql -c "CREATE ROLE %(box_database)s WITH' ' ENCRYPTED PASSWORD \'%(box_database_pw)s\'' ' LOGIN NOCREATEDB NOCREATEROLE NOSUPERUSER"') run('psql -c "GRANT %(box_database)s TO admin"') run('psql -c "CREATE DATABASE %(box_database)s WITH' ' OWNER %(box_database)s' ' TEMPLATE template0' ' ENCODING \'UTF8\'"') with cd('%(box_domain)s'): put(StringIO('''\ DJANGO_SETTINGS_MODULE=%(box_project_name)s.settings.%(box_environment)s DATABASE_URL=postgres://%(box_database)s:%(box_database_pw)s\ @localhost:5432/%(box_database)s CACHE_URL=hiredis://localhost:6379/1/?key_prefix=%(box_database)s SECRET_KEY=%(box_secret_key)s SENTRY_DSN=%(box_sentry_dsn)s DJANGO_ADMIN_SSO_OAUTH_CLIENT_ID=%(box_oauth2_client_id)s DJANGO_ADMIN_SSO_OAUTH_CLIENT_SECRET=%(box_oauth2_client_secret)s ALLOWED_HOSTS=['%(box_domain)s', '.%(box_domain)s', '.%(host_string_host)s'] # FORCE_DOMAIN = www.%(box_domain)s ''' % dict(env, host_string_host=env.host_string.split('@')[-1])), '.env') run('venv/bin/python manage.py migrate --noinput')
def deploy(): """Checks whether everything is ready for deployment""" step('Checking whether we are on the expected branch...') with settings(warn_only=True), hide('everything'): branch = run_local('git symbolic-ref -q --short HEAD', capture=True) if not branch: abort(red('No branch checked out, cannot continue.', bold=True)) if branch != env.box_branch: puts( red('Warning: The currently checked out branch is \'%s\', but' ' the environment \'%s\' runs on \'%s\'.' % (branch, env.box_environment, env.box_branch))) if not confirm('Continue deployment?', default=False): abort('Aborting.') execute('check.check') execute('check.test') # XXX Maybe even execute('check.primetime') if deploying to production? with cd('%(box_domain)s'): step('\nChecking for uncommitted changes on the server...') result = run('git status --porcelain') if result: abort(red('Uncommitted changes detected, aborting deployment.'))
def deploy(): """Checks whether everything is ready for deployment""" step('Checking whether we are on the expected branch...') with settings(warn_only=True), hide('everything'): branch = run_local('git symbolic-ref -q --short HEAD', capture=True) if not branch: abort(red('No branch checked out, cannot continue.', bold=True)) if branch != env.box_branch: puts(red( 'Warning: The currently checked out branch is \'%s\', but' ' the environment \'%s\' runs on \'%s\'.' % ( branch, env.box_environment, env.box_branch))) if not confirm('Continue deployment?', default=False): abort('Aborting.') execute('check.check') execute('check.test') # XXX Maybe even execute('check.primetime') if deploying to production? with cd('%(box_domain)s'): step('\nChecking for uncommitted changes on the server...') result = run('git status --porcelain') if result: abort(red('Uncommitted changes detected, aborting deployment.'))
def primetime(): """Check whether this project is ready for prime time""" execute('check.check') execute('check.test') step('"noindex" should not hit production servers...') run_local( "! git --no-pager grep -n -C3 -E '^Disallow: /$' -- 'robots.txt'") run_local("! git --no-pager grep -n -C3 -E 'meta.*robots.*noindex'" " -- %(box_project_name)s") step('Checking local settings on server...') with cd('%(box_domain)s'): output = run( "DJANGO_SETTINGS_MODULE=%(box_project_name)s.settings" " venv/bin/python -c \"" "from django.conf import settings as s;" "print('fd:%%s\\ndsn:%%s\\nsso:%%s\\ndebug:%%s\\nsk:%%s' %% (" "getattr(s, 'FORCE_DOMAIN', '-')," "getattr(s, 'RAVEN_CONFIG', {}).get('dsn', '')," "bool(getattr(s, 'DJANGO_ADMIN_SSO_ADD_LOGIN_BUTTON', False))," "bool(s.DEBUG)," "s.SECRET_KEY," "))\"" % env, quiet=True).strip() output = dict(row.strip().split(':', 1) for row in re.split(r'[\r\n]+', output)) if output['fd'] == '': puts(red('Warning: FORCE_DOMAIN is empty.')) elif output['fd'] == '-': puts(red('Warning: FORCE_DOMAIN is not defined.')) if output['dsn'] == '': puts( red('Warning: Sentry is not configured, fill in RAVEN_CONFIG.') ) if output['sso'] != 'True': puts( red('Warning: SSO authentication for the administration is not' ' configured.')) if output['debug'] == 'True': puts(red('Error: DEBUG = True!?', bold=True)) with settings(warn_only=True), hide('everything'): gitgrep = run_local("! git --no-pager grep '%s'" % output['sk'], capture=True) grep = run_local("! grep '%s' */*.py" % output['sk'], capture=True) if gitgrep or grep: puts( red('Error: The remote value of SECRET_KEY also exists in local' ' files. Set a new value for SECRET_KEY in' ' .env on the server!' % env, bold=True))
def primetime(): """Check whether this project is ready for prime time""" execute('check.check') execute('check.test') step('"noindex" should not hit production servers...') run_local( "! git --no-pager grep -n -C3 -E '^Disallow: /$' -- 'robots.txt'") run_local( "! git --no-pager grep -n -C3 -E 'meta.*robots.*noindex'" " -- %(box_project_name)s") step('Checking local settings on server...') with cd('%(box_domain)s'): output = run( "DJANGO_SETTINGS_MODULE=%(box_project_name)s.settings" " venv/bin/python -c \"" "from django.conf import settings as s;" "print('fd:%%s\\ndsn:%%s\\nsso:%%s\\ndebug:%%s\\nsk:%%s' %% (" "getattr(s, 'FORCE_DOMAIN', '-')," "getattr(s, 'RAVEN_CONFIG', {}).get('dsn', '')," "bool(getattr(s, 'DJANGO_ADMIN_SSO_ADD_LOGIN_BUTTON', False))," "bool(s.DEBUG)," "s.SECRET_KEY," "))\"" % env, quiet=True).strip() output = dict( row.strip().split(':', 1) for row in re.split(r'[\r\n]+', output)) if output['fd'] == '': puts(red('Warning: FORCE_DOMAIN is empty.')) elif output['fd'] == '-': puts(red('Warning: FORCE_DOMAIN is not defined.')) if output['dsn'] == '': puts(red( 'Warning: Sentry is not configured, fill in RAVEN_CONFIG.')) if output['sso'] != 'True': puts(red( 'Warning: SSO authentication for the administration is not' ' configured.')) if output['debug'] == 'True': puts(red( 'Error: DEBUG = True!?', bold=True)) with settings(warn_only=True), hide('everything'): gitgrep = run_local( "! git --no-pager grep '%s'" % output['sk'], capture=True) grep = run_local("! grep '%s' */*.py" % output['sk'], capture=True) if gitgrep or grep: puts(red( 'Error: The remote value of SECRET_KEY also exists in local' ' files. Set a new value for SECRET_KEY in' ' .env on the server!' % env, bold=True))
def nginx_vhost_and_supervisor(): run('sudo nine-manage-vhosts virtual-host create %(box_domain)s' ' --template=feinheit --webroot=/home/www-data/%(box_domain)s/htdocs') with cd('%(box_domain)s'): run('mkdir -p media tmp') run('supervisor-create-conf %(box_domain)s wsgi' ' > supervisor/conf.d/%(box_domain)s.conf') run('sctl reload')
def create_virtualenv(): with cd('%(box_domain)s'): run('virtualenv --python %(box_python)s' ' --prompt "(venv:%(box_domain)s)" venv') run('venv/bin/pip install -U virtualenv pip wheel' ' --find-links file:///home/www-data/tmp/wheel/wheelhouse/') run('venv/bin/pip install -U setuptools' ' --find-links file:///home/www-data/tmp/wheel/wheelhouse/') run('venv/bin/pip install -r requirements/production.txt' ' --find-links file:///home/www-data/tmp/wheel/wheelhouse/')
def copy_data_from(environment=None): """ Copy the database from one environment to another. Usually from production to stage. Usage: ``fab s server.copy_data_from:production``. :param environment: the source environment """ if env.get('box_hardwired_environment'): abort(red('Cannot continue with a hardwired environment.')) if environment not in env.box_environments: abort(red('Invalid environment %s.' % environment)) source = env.box_environments[environment] target = env.box_environments[env.get('box_environment')] if source == target: abort(red( 'Source environment %s must not equal target environment %s.' % (environment, env.get('box_environment')))) if source['servers'][0] != target['servers'][0]: abort(red('The environments have to be on the same server, sorry!')) puts('Copying data from {0} to {1}'.format( source['remote'], target['remote'])) if not confirm( 'Completely replace the remote database' ' "%(box_database)s" (if it exists)?', default=False): return for key, value in source.items(): env['source_%s' % key] = value with settings(warn_only=True): run('dropdb %(box_database)s') run( 'createdb %(box_database)s --encoding=UTF8 --template=template0' ' --owner=%(box_database)s') run( 'pg_dump %(source_database)s' ' --no-privileges --no-owner --no-reconnect' ' | psql %(box_database)s') run( 'psql %(box_database)s -c "REASSIGN OWNED BY admin ' ' TO %(box_database)s"') with cd(env.box_domain): run('cp -al ~/%(source_domain)s/media/* media/') run('sctl restart %(box_domain)s:*')
def styles(): """Compiles and compresses the CSS and deploys it to the server""" execute('check.deploy') step('\nBuilding and deploying assets...') if os.path.exists('gulpfile.js'): _deploy_styles_foundation5_gulp() elif os.path.exists('%(box_staticfiles)s/Gulpfile.js' % env): _deploy_styles_foundation5_grunt() elif os.path.exists('%(box_staticfiles)s/config.rb' % env): _deploy_styles_foundation4_bundler() else: abort(red('I do not know how to deploy this frontend code.')) with cd('%(box_domain)s'): run('venv/bin/python manage.py collectstatic --noinput')
def styles(reload=True): """Compiles and compresses the CSS and deploys it to the server""" execute('check.deploy') step('\nBuilding and deploying assets...') if os.path.exists('gulpfile.js'): _deploy_styles_foundation5_gulp() elif os.path.exists('%(box_staticfiles)s/Gulpfile.js' % env): _deploy_styles_foundation5_grunt() elif os.path.exists('%(box_staticfiles)s/config.rb' % env): _deploy_styles_foundation4_bundler() else: abort(red('I do not know how to deploy this frontend code.')) with cd('%(box_domain)s'): run('venv/bin/python manage.py collectstatic --noinput') if reload: execute('deploy.restart_server')
def remove_host(): if not confirm( 'Really remove the host "%(box_domain)s" and all associated data?', default=False): return run('sudo nine-manage-vhosts virtual-host remove %(box_domain)s') run('rm supervisor/conf.d/%(box_domain)s.conf') run('sctl reload') with cd(env.box_domain): env.box_datetime = datetime.now().strftime('%Y-%m-%d-%s') run('pg_dump %(box_database)s' ' --no-privileges --no-owner --no-reconnect' ' > %(box_database)s-%(box_environment)s-%(box_datetime)s.sql') run('dropdb %(box_database)s') run('dropuser %(box_database)s') puts( red('The folder ~/%(box_domain)s on the server has not been removed. The' ' "tmp" subfolder also contains a fresh database dump.' % env))
def remove_host(): if not confirm( 'Really remove the host "%(box_domain)s" and all associated data?', default=False): return run('sudo nine-manage-vhosts virtual-host remove %(box_domain)s') run('rm supervisor/conf.d/%(box_domain)s.conf') run('sctl reload') with cd(env.box_domain): env.box_datetime = datetime.now().strftime('%Y-%m-%d-%s') run( 'pg_dump %(box_database)s' ' --no-privileges --no-owner --no-reconnect' ' > %(box_database)s-%(box_environment)s-%(box_datetime)s.sql') run('dropdb %(box_database)s') run('dropuser %(box_database)s') puts(red( 'The folder ~/%(box_domain)s on the server has not been removed. The' ' "tmp" subfolder also contains a fresh database dump.' % env))
def create_database_and_dotenv(): env.box_sentry_dsn = prompt('Sentry DSN') env.box_oauth2_client_id = prompt('Google OAuth2 Client ID') env.box_oauth2_client_secret = prompt('Google OAuth2 Client Secret') env.box_database_pw = get_random_string( 20, chars='abcdefghijklmopqrstuvwx01234567890') env.box_secret_key = get_random_string(50) run('psql -c "CREATE ROLE %(box_database)s WITH' ' ENCRYPTED PASSWORD \'%(box_database_pw)s\'' ' LOGIN NOCREATEDB NOCREATEROLE NOSUPERUSER"') run('psql -c "GRANT %(box_database)s TO admin"') run('psql -c "CREATE DATABASE %(box_database)s WITH' ' OWNER %(box_database)s' ' TEMPLATE template0' ' ENCODING \'UTF8\'"') with cd('%(box_domain)s'): put( StringIO('''\ DATABASE_URL=postgres://%(box_database)s:%(box_database_pw)s\ @localhost:5432/%(box_database)s CACHE_URL=hiredis://localhost:6379/1/%(box_database)s SECRET_KEY=%(box_secret_key)s SENTRY_DSN=%(box_sentry_dsn)s DJANGO_ADMIN_SSO_OAUTH_CLIENT_ID=%(box_oauth2_client_id)s DJANGO_ADMIN_SSO_OAUTH_CLIENT_SECRET=%(box_oauth2_client_secret)s ALLOWED_HOSTS=['%(box_domain)s', '.%(box_domain)s', '.%(host_string_host)s'] # Do not forget to allow robots to index the site when going live! # - %(box_project_name)s/templates/base.html: Change "noindex" to "index" # - htdocs/robots.txt: Remove the "Disallow: /" line # FORCE_DOMAIN = www.%(box_domain)s ''' % dict(env, host_string_host=env.host_string.split('@')[-1])), '.env') run('venv/bin/python manage.py migrate --noinput')
def code(reload=True): """Deploys the currently committed project state to the server, if there are no uncommitted changes on the server and the checking step did not report any problems""" execute('check.deploy') # XXX Maybe abort deployment if branch-to-be-deployed is not checked out? step('\nPushing changes...') run_local('git push origin %(box_branch)s') step('\nDeploying new code on server...') with cd('%(box_domain)s'): run('git fetch') run('git reset --hard origin/%(box_branch)s') run('find . -name "*.pyc" -delete') run('venv/bin/pip install -r requirements/production.txt' ' --find-links file:///home/www-data/tmp/wheel/wheelhouse/') run('venv/bin/python manage.py migrate --noinput') if reload: execute('deploy.restart_server') execute('git.fetch_remote')
def create_database_and_dotenv(): env.box_sentry_dsn = prompt('Sentry DSN') env.box_oauth2_client_id = prompt('Google OAuth2 Client ID') env.box_oauth2_client_secret = prompt('Google OAuth2 Client Secret') env.box_database_pw = get_random_string( 20, chars='abcdefghijklmopqrstuvwx01234567890') env.box_secret_key = get_random_string(50) run('psql -c "CREATE ROLE %(box_database)s WITH' ' ENCRYPTED PASSWORD \'%(box_database_pw)s\'' ' LOGIN NOCREATEDB NOCREATEROLE NOSUPERUSER"') run('psql -c "GRANT %(box_database)s TO admin"') run('psql -c "CREATE DATABASE %(box_database)s WITH' ' OWNER %(box_database)s' ' TEMPLATE template0' ' ENCODING \'UTF8\'"') with cd('%(box_domain)s'): put(StringIO('''\ DATABASE_URL=postgres://%(box_database)s:%(box_database_pw)s\ @localhost:5432/%(box_database)s CACHE_URL=hiredis://localhost:6379/1/%(box_database)s SECRET_KEY=%(box_secret_key)s SENTRY_DSN=%(box_sentry_dsn)s DJANGO_ADMIN_SSO_OAUTH_CLIENT_ID=%(box_oauth2_client_id)s DJANGO_ADMIN_SSO_OAUTH_CLIENT_SECRET=%(box_oauth2_client_secret)s ALLOWED_HOSTS=['%(box_domain)s', '.%(box_domain)s', '.%(host_string_host)s'] # Do not forget to allow robots to index the site when going live! # - %(box_project_name)s/templates/base.html: Change "noindex" to "index" # - htdocs/robots.txt: Remove the "Disallow: /" line # FORCE_DOMAIN = www.%(box_domain)s ''' % dict(env, host_string_host=env.host_string.split('@')[-1])), '.env') run('venv/bin/python manage.py migrate --noinput')
def restart_server(): with cd('%(box_domain)s'): run('sctl restart %(box_domain)s:*')