def _change_cwd(which, path): path = path.replace(' ', '\ ') if env.get(which) and not path.startswith('/'): new_cwd = env.get(which) + '/' + path else: new_cwd = path return _setenv(**{which: new_cwd})
def run(command, shell=True, pty=True, combine_stderr=None, use_sudo=False, user=None): use_sudo = use_sudo or user is not None or env.get('use_sudo') if not use_sudo: return fabric_run(command, shell=shell, pty=pty, combine_stderr=combine_stderr) else: user = user or env.get('sudo_user', env.user) # Make SSH agent socket available to the sudo user with silent(): fabric_sudo('chown -R {}: $(dirname $SSH_AUTH_SOCK)'.format(user), user='******') if user == env.user: user = None return fabric_sudo(command, shell=shell, pty=pty, combine_stderr=combine_stderr, user=user)
def deploy(): """ Deploy local copy of repository to target WP Engine environment. """ require('settings', provided_by=[ "production", "staging", ]) if env.branch != 'rollback': rollback_sha1 = deployed_commit() if rollback_sha1: print(colors.cyan("Setting rollback point...")) capture('git tag -af rollback %s -m "rollback tag"' % rollback_sha1) else: print( colors.yellow( "No rollback commit found. Unable to set rollback point.")) if env.get('sftp_deploy', False): print(colors.cyan("Checking out branch: %s" % env.branch)) capture('git checkout %s' % env.branch) capture('git submodule update --init --recursive') with settings(warn_only=True): print(colors.cyan("Deploying...")) if env.get('sftp_deploy', False): ret = do_sftp_deploy(env.path) if ret.return_code and ret.return_code > 0: if ret.return_code in [ 8, 5, ]: print( colors.cyan( "Found no existing git repo on ftp host, initializing..." )) ret = initial_deploy(env.path) if ret.return_code and ret.return_code > 0: print(colors.red("An error occurred...")) if not env.verbose: print( colors.yellow( 'Try deploying with `verbose` for more information...' )) else: if not remote_exists(env.settings): added = add_git_remote(env.settings) if added.return_code is 0: ret = do_git_deploy() else: ret = do_git_deploy() return ret
def rq_dashboard(): """Starts the RQ Dashboard webserver""" # Stop the web process stop('web') env = _read_env() with lcd(root_dir): local('rq-dashboard -p %s -u %s' % (env.get('PORT', '9181'), env.get('REDIS_URL', 'redis://localhost:6379')))
def run(self): self.JENKINS_URL = env.get('JENKINS_URL', '').strip('/') if not self.JENKINS_URL: abort(red('Provide fabric env variable JENKINS_URL pointing to your jenkins instance.')) JENKINS_LOGIN = env.get('JENKINS_LOGIN', '') JENKINS_PASSWORD = env.get('JENKINS_PASSWORD', '') if not all([JENKINS_LOGIN, JENKINS_PASSWORD]): abort(red('Provide both JENKINS_LOGIN and JENKINS_PASSWORD to connect to api')) self.api = jenkins.Jenkins(self.JENKINS_URL, JENKINS_LOGIN, JENKINS_PASSWORD)
def notify_hipchat(): """ Send notification to a HipChat room """ if env.get('hipchat_token', False) and env.get('hipchat_room_id', False) and not env.dry_run: hp = HipChatNotifier(env.hipchat_token) name = helpers.capture('git config user.name') if name is '': name = 'Someone' message = '%s just deployed "%s" (branch: %s) to %s' % ( name, env.project_name, env.branch, env.settings) hp.message(env.hipchat_room_id, 'Deployment', message, True)
def connect(self, *args, **kwargs): REDMINE_URL = env.get('REDMINE_URL', '').strip('/') if not REDMINE_URL: abort(red('Provide REDMINE_URL pointing to your redmine instance')) REDMINE_API_KEY = env.get('REDMINE_API_KEY', '') if not REDMINE_API_KEY: abort(red('Go to %s/my/account and grab yourself a api key' % REDMINE_URL)) self.api = Hammock(env.REDMINE_URL, headers={'Content-Type': 'application/json', 'X-Redmine-API-Key': env.REDMINE_API_KEY})
def __getitem__(self, item): """ Returns value of configuration key based on current host or global section For example, if current task is connected to 10.12.43.2 and it can be found from config:: [DEFAULT] mykey = 'defaultval' [setup:testenv] fe_hosts = 10.12.43.2 db_hosts = 10.12.43.2 mc_hosts = 10.12.43.2 mykey = 'myvalue' Then: >>> config = Config() >>> config['mykey'] 'myvalue' """ # Default section where to read section = 'DEFAULT' # Try to determine the section based on current host name host_string = env.get('host_string', '') if host_string: default = {} # Iterate roledefs and match hostname with them for rolename, hosts in env.get('roledefs', default).items(): # Skip the layer specific roles if rolename[-2:] in ['fe', 'db', 'mc']: pass elif host_string in hosts: section = 'setup:%s' % rolename # Check if option can be found and fallback to 'DEFAULT' if not self.config.has_option(section, item): logger.debug('Option cannot be found: %s, falling back' % item) section = 'DEFAULT' value = self.config.get(section, item) logger.debug('Reading config [%s] %s = %s' % (section, item, value)) return value
def __getitem__(self, item): """ Returns value of configuration key based on current host or global section For example, if current task is connected to 10.12.43.2 and it can be found from config:: [DEFAULT] mykey = 'defaultval' [setup:testenv] fe_hosts = 10.12.43.2 db_hosts = 10.12.43.2 mc_hosts = 10.12.43.2 mykey = 'myvalue' Then: >>> config = Config() >>> config['mykey'] 'myvalue' """ # Default section where to read section = "DEFAULT" # Try to determine the section based on current host name host_string = env.get("host_string", "") if host_string: default = {} # Iterate roledefs and match hostname with them for rolename, hosts in env.get("roledefs", default).items(): # Skip the layer specific roles if rolename[-2:] in ["fe", "db", "mc"]: pass elif host_string in hosts: section = "setup:%s" % rolename # Check if option can be found and fallback to 'DEFAULT' if not self.config.has_option(section, item): logger.debug("Option cannot be found: %s, falling back" % item) section = "DEFAULT" value = self.config.get(section, item) logger.debug("Reading config [%s] %s = %s" % (section, item, value)) return value
def run(cls, cmd, *args, **kwargs): """ Unified command execution - calls fabric's local(), run() or sudo operation() as required. Caller needs to set env via settings(). :param cmd: the command to execute """ from fabric.state import env from fabric.operations import run, sudo, local if env.get("host_string").endswith('@localhost'): return local(cmd, capture=True) if env.get("sudo_user") in (None, env.get("user")): return run(cmd, *args, **kwargs) else: return sudo(cmd, *args, **kwargs)
def host_prompting_wrapper(*args, **kwargs): while not env.get('host_string', False): handle_prompt_abort("the target host connection string") host_string = raw_input("No hosts found. Please specify (single)" " host string for connection: ") env.update(to_dict(host_string)) return func(*args, **kwargs)
def sitemap(): """ Render and deploy sitemap. """ require('settings', provided_by=[staging, production]) app_config.configure_targets(env.get('settings', None)) with flat_app.app.test_request_context(path='sitemap.xml'): print 'Rendering sitemap.xml' view = flat_app.__dict__['_sitemap'] content = view().data with open('.sitemap.xml', 'w') as f: f.write(content) s3 = boto.connect_s3() flat.deploy_file( s3, '.sitemap.xml', app_config.PROJECT_SLUG, app_config.DEFAULT_MAX_AGE )
def render(): from flask import g require('static_path', provided_by=['tumblr']) require('settings', provided_by=['staging', 'production', 'development']) less() app_config_js() copytext_js('theme') compiled_includes = {} app_config.configure_targets(env.get('settings', None)) with app.app.test_request_context(): path = 'tumblr/www/index.html' with app.app.test_request_context(path=env.static_path): print 'Rendering %s' % path if env.settings == 'development': g.compile_includes = False else: g.compile_includes = True g.compiled_includes = compiled_includes view = static_theme.__dict__['_theme'] content = view().data with open(path, 'w') as f: f.write(content) local('pbcopy < tumblr/www/index.html') print 'The Tumblr theme HTML has been copied to your clipboard.' local('open https://www.tumblr.com/customize/%s' % app_config.TUMBLR_NAME)
def create_application_folder(): app_folder_prefix = env.get("app_folder_prefix", None) virtualenv_name = env.get("virtualenv_name", None) if not virtualenv_name: raise ValueError("We need the virtualenv_name variable to create the application folder.") app_folder = None if app_folder_prefix: app_folder = os.path.join(app_folder_prefix, virtualenv_name) else: app_folder = virtualenv_name if not exists(app_folder): sudo("mkdir -p {0}".format(app_folder), shell=False)
def _render_graphics(paths): """ Render a set of graphics """ # Fake out deployment target app_config.configure_targets(env.get('settings', None)) for path in paths: slug = path.split('%s/' % app_config.GRAPHICS_PATH)[1].split('/')[0] with flat_app.app.test_request_context(path='graphics/%s/' % slug): view = flat_app.__dict__['_graphics_detail'] content = view(slug).data with open('%s/index.html' % path, 'w') as writefile: writefile.write(content) # Fallback for legacy projects w/o child templates if not os.path.exists('%s/child_template.html' % path): continue download_copy(slug) with flat_app.app.test_request_context(path='graphics/%s/child.html' % slug): view = flat_app.__dict__['_graphics_child'] content = view(slug).data with open('%s/child.html' % path, 'w') as writefile: writefile.write(content) # Un-fake-out deployment target app_config.configure_targets(app_config.DEPLOYMENT_TARGET)
def run(self, origin=None, version=None, build_dir=None, dest_dir=None, excludes=None): if excludes is not None: self.excludes = excludes elif self.excludes is None: self.excludes = env.get('%s_excludes' % self.prefix) super(BuildJqueryTask, self).run(origin, version, build_dir, dest_dir)
def _render_graphics(paths): """ Render a set of graphics """ # Fake out deployment target app_config.configure_targets(env.get('settings', None)) for path in paths: slug = path.split('%s/' % app_config.GRAPHICS_PATH)[1].split('/')[0] with app.app.test_request_context(path='graphics/%s/' % slug): view = app.__dict__['_graphics_detail'] content = view(slug) with open('%s/index.html' % path, 'w') as writefile: writefile.write(content.encode('utf-8')) # Fallback for legacy projects w/o child templates if not os.path.exists('%s/child_template.html' % path): continue download_copy(slug) with app.app.test_request_context(path='graphics/%s/child.html' % slug): view = app.__dict__['_graphics_child'] content = view(slug) with open('%s/child.html' % path, 'w') as writefile: writefile.write(content.encode('utf-8')) # Un-fake-out deployment target app_config.configure_targets(app_config.DEPLOYMENT_TARGET)
def local(command, capture=True): """ Run a command on the local system. `local` is simply a convenience wrapper around the use of the builtin Python ``subprocess`` module with ``shell=True`` activated. If you need to do anything special, consider using the ``subprocess`` module directly. `local` will, by default, capture and return the contents of the command's stdout as a string, and will not print anything to the user (the command's stderr is captured but discarded.) .. note:: This differs from the default behavior of `run` and `sudo` due to the different mechanisms involved: it is difficult to simultaneously capture and print local commands, so we have to choose one or the other. We hope to address this in later releases. If you need full interactivity with the command being run (and are willing to accept the loss of captured stdout) you may specify ``capture=False`` so that the subprocess' stdout and stderr pipes are connected to your terminal instead of captured by Fabric. When ``capture`` is False, global output controls (``output.stdout`` and ``output.stderr`` will be used to determine what is printed and what is discarded. """ # Handle cd() context manager cwd = env.get('cwd', '') if cwd: cwd = 'cd %s && ' % _shell_escape(cwd) # Construct real command real_command = cwd + command if output.debug: print("[localhost] run: %s" % (real_command)) elif output.running: print("[localhost] run: " + command) # By default, capture both stdout and stderr PIPE = subprocess.PIPE out_stream = PIPE err_stream = PIPE # Tie in to global output controls as best we can; our capture argument # takes precedence over the output settings. if not capture: if output.stdout: out_stream = None if output.stderr: err_stream = None p = subprocess.Popen([real_command], shell=True, stdout=out_stream, stderr=err_stream) (stdout, stderr) = p.communicate() # Handle error condition (deal with stdout being None, too) out = _AttributeString(stdout or "") out.failed = False if p.returncode != 0: out.failed = True msg = "local() encountered an error (return code %s) while executing '%s'" % (p.returncode, command) _handle_failure(message=msg) # If we were capturing, this will be a string; otherwise it will be None. return out
def render_all(): """ Render HTML templates and compile assets. """ from flask import g require('slug', provided_by=['post']) less() app_config_js() copytext_js(env.slug) compiled_includes = {} app_config.configure_targets(env.get('settings', None)) with app.app.test_request_context(): path = 'posts/%s/www/index.html' % env.slug with app.app.test_request_context(path=env.static_path): print 'Rendering %s' % path g.compile_includes = True g.compiled_includes = compiled_includes view = app.__dict__['_post'] content = view(env.slug).data with open(path, 'w') as f: f.write(content)
def host_prompting_wrapper(*args, **kwargs): while not env.get('host_string', False): handle_prompt_abort() host_string = raw_input("No hosts found. Please specify (single)" " host string for connection: ") interpret_host_string(host_string) return func(*args, **kwargs)
def render(): """ Render the Tumblr theme. """ from flask import g less() app_config_js() compiled_includes = {} app_config.configure_targets(env.get('settings', None)) path = 'theme/www/index.html' with app.app.test_request_context(path='/theme'): print 'Rendering %s' % path if env.settings not in ['staging', 'production']: g.compile_includes = False else: g.compile_includes = True g.compiled_includes = compiled_includes view = static_theme.__dict__['_theme'] content = view() with open(path, 'w') as f: f.write(content.encode('utf-8')) local('pbcopy < theme/www/index.html') print 'The Tumblr theme HTML has been copied to your clipboard.' local('open https://www.tumblr.com/customize/%s' % app_config.TUMBLR_NAME)
def deploy(revision="master"): require("django_settings", "base_dir", "virtualenv_settings", provided_by=("env_test", "env_live")) env.remote_pwd = run("pwd") env.django_settings.validate_or_abort() env.virtualenv_settings.validate_or_abort() env.remote_revision = coat_utils.remote_resolve_current_revision() env.deploy_revision = coat_utils.local_resolve_revision(revision) env.deploy_workdir = coat_utils.workdir_prepare_checkout( revision, folders=(env.get('project_name', 'django'), )) workdir_django_prepare(env.deploy_workdir) copy_revision_to_remote(env.deploy_workdir, env.remote_revision, env.deploy_revision) remote_activate_revision(env.deploy_workdir, env.remote_revision, env.deploy_revision) remote_reload()
def init(): """ Setup the server for the first time :return: """ banner("init") with show("output"): if not env.get('no_apt_update'): sudo('apt-get update') require.directory(env.path, mode="777", use_sudo=True) require.directory('/var/run/jianguo/', owner='www-data', group='www-data', mode='770', use_sudo=True) require.directory('/var/log/jianguo/', owner='www-data', group='www-data', mode='770', use_sudo=True) require.directory('/var/log/supervisord/', owner='www-data', group='www-data', mode='770', use_sudo=True) require.directory('/var/run/supervisord/', owner='www-data', group='www-data', mode='770', use_sudo=True) require.directory('~/.ssh', mode='700') put('deployment', '~/.ssh/id_rsa') run('chmod 600 ~/.ssh/id_rsa') require.deb.packages([ 'gcc', 'python-all-dev', 'libpq-dev', 'libjpeg-dev', 'libxml2-dev', 'libxslt1-dev', 'libfreetype6-dev', 'libevent-dev', 'supervisor' ]) require.python.pip(version="1.0") new_virtualenv() me = run('whoami') sudo('adduser %s www-data' % me) install_nginx() install_postgres()
def host_prompting_wrapper(*args, **kwargs): handle_prompt_abort() while not env.get('host_string', False): host_string = raw_input("No hosts found. Please specify (single)" " host string for connection: ") interpret_host_string(host_string) return func(*args, **kwargs)
def run(self, origin=None, version=None, build_dir=None, dest_dir=None, config=None): if config is not None: self.config = config elif self.config is None: self.config = env.get('%s_config' % self.prefix) super(BuildBootstrapTask, self).run(origin, version, build_dir, dest_dir)
def notify_event(commits=None): """ Send a message to slack about a successful deployment :return str: formatted message """ from .project import project_name, git_repository_path, github_link msg = u'*{project}* (*{state}*)' if commits: msg += u' deployed `<{base_url}/compare/{old}...{new}|{old} → {new}>`' else: msg += u' reset to `<{base_url}/commit/{commit}|{commit}>`' msg += u' on `{host}`' old_commit, new_commit = commits or (None, None) commit = commits or git.get_commit(repository_path=git_repository_path(), short=True) msg = msg.format( project=project_name(), base_url=github_link(), state=env.get('state', 'unknown'), old=old_commit, new=new_commit, commit=commit, host=env['host_string'] ) slack.notify(msg) return msg
def _deploy_summary(title, revision): from hashlib import md5 from time import time from .project import project_name deployer = git.get_local_commiter() email = git.get_local_email() project = project_name() state = env.get('state', 'Unknown') avatar_hash = md5(email.strip().lower()).hexdigest() avatar_url = 'https://www.gravatar.com/avatar/{}?s=16'.format(avatar_hash) fallback = "Deploy: {} ({}) by {}".format(project, state, deployer) summary = { 'fallback': fallback, 'color': '#439FE0', 'author_name': deployer, 'author_icon': avatar_url, # 'ts': int(time()), 'title': u'{} ({})'.format(project, state), 'fields': [ {'title': 'Label', 'value': title, 'short': True} ], 'mrkdwn_in': ['text', 'fields'] } if revision: summary['fields'].append({ 'title': 'revision', 'value': '_{}_'.format(revision), 'short': True }) return summary
def fabcast(command): """ Actually run specified commands on the server specified by staging() or production(). """ require('settings', provided_by=['production', 'staging']) if not app_config.DEPLOY_TO_SERVERS: print 'You must set DEPLOY_TO_SERVERS = True in your app_config.py and setup a server before fabcasting.' if env.get('branch'): branch = 'branch:%s' % env.get('branch') else: branch = '' run('cd %s && bash run_on_server.sh fab %s $DEPLOYMENT_TARGET %s' % (app_config.SERVER_REPOSITORY_PATH, branch, command))
def app(port='8000'): """ Serve app.py. """ if env.get('settings'): local("DEPLOYMENT_TARGET=%s bash -c 'gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload --log-file=logs/app.log app:wsgi_app'" % (env.settings, port)) else: local('gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload --log-file=logs/app.log app:wsgi_app' % port)
def run(self, job_name, project_slug, template_job=None): super(CreateJob, self).run() template_job = template_job or env.get('JENKINS_TEMPLATE_JOB', 'example-tests') job = self.api.copy_job(template_job, job_name) job.enable() self.setup_job_config(job, context={'project': project_slug}) job.invoke() puts("Created job %s/job/%s/" % (self.JENKINS_URL, job_name))
def create_db(): with settings(warn_only=True), hide('output', 'running'): if env.get('settings'): execute('servers.stop_service', 'uwsgi') execute('servers.stop_service', 'fetch_and_publish_results') with shell_env(**app_config.database): local( 'dropdb --host={PGHOST} --port={PGPORT} --username={PGUSER} --if-exists {PGDATABASE}' .format(**app_config.database)) local( 'createdb --host={PGHOST} --port={PGPORT} --username={PGUSER} {PGDATABASE}' .format(**app_config.database)) if env.get('settings'): execute('servers.start_service', 'uwsgi') execute('servers.start_service', 'fetch_and_publish_results')
def create_db(): with settings(warn_only=True), hide('output', 'running'): if env.get('settings'): execute('servers.stop_service', 'uwsgi') with shell_env(**app_config.database): local('dropdb --if-exists %s' % app_config.database['PGDATABASE']) if not env.get('settings'): local('psql -c "DROP USER IF EXISTS %s;"' % app_config.database['PGUSER']) local('psql -c "CREATE USER %s WITH SUPERUSER PASSWORD \'%s\';"' % (app_config.database['PGUSER'], app_config.database['PGPASSWORD'])) with shell_env(**app_config.database): local('createdb %s' % app_config.database['PGDATABASE']) if env.get('settings'): execute('servers.start_service', 'uwsgi')
def jasmine(): """Run the jasmine test server. Stops the web process first to open the port.""" stop('web') env = _read_env() with lcd(root_dir): local('jasmine -p %s' % env.get('PORT', ''))
def host_prompting_wrapper(*args, **kwargs): env.prompt or abort( "Needed to prompt for host, but env.prompt is False. " "(You specified --abort-on-prompt)") while not env.get('host_string', False): host_string = raw_input("No hosts found. Please specify (single) host string for connection: ") interpret_host_string(host_string) return func(*args, **kwargs)
def run(self, project_slug, developer): config_name = self.get_server_name(project_slug, developer) config_available_path = '/etc/uwsgi/apps-available/%s.ini' % config_name config_enabled_path = '/etc/uwsgi/apps-enabled-%s/%s.ini' % (env.get('PYTHON_VERSION', '2.7'), config_name) upload_template( template_dir=self.get_template_path(), filename=env.get('UWSGI_CONFIG_TEMPLATE', 'uwsgi.config.tmpl'), destination=config_available_path, context=self.get_context(project_slug, developer), use_sudo=True, use_jinja=True, backup=False ) sudo('ln -fs %(available)s %(enabled)s' % { 'available': config_available_path, 'enabled': config_enabled_path })
def cd(path): """ Context manager that keeps directory state when calling `run`/`sudo`. Any calls to `run` or `sudo` within the wrapped block will implicitly have a string similar to ``"cd <path> && "`` prefixed in order to give the sense that there is actually statefulness involved. Because use of `cd` affects all `run` and `sudo` invocations, any code making use of `run` and/or `sudo`, such as much of the ``contrib`` section, will also be affected by use of `cd`. However, at this time, `get` and `put` do not honor `cd`; we expect this to be fixed in future releases. Like the actual 'cd' shell builtin, `cd` may be called with relative paths (keep in mind that your default starting directory is your remote user's ``$HOME``) and may be nested as well. Below is a "normal" attempt at using the shell 'cd', which doesn't work due to how shell-less SSH connections are implemented -- state is **not** kept between invocations of `run` or `sudo`:: run('cd /var/www') run('ls') The above snippet will list the contents of the remote user's ``$HOME`` instead of ``/var/www``. With `cd`, however, it will work as expected:: with cd('/var/www'): run('ls') # Turns into "cd /var/www && ls" Finally, a demonstration (see inline comments) of nesting:: with cd('/var/www'): run('ls') # cd /var/www && ls with cd('website1'): run('ls') # cd /var/www/website1 && ls .. note:: This context manager is currently implemented by appending to (and, as always, restoring afterwards) the current value of an environment variable, ``env.cwd``. However, this implementation may change in the future, so we do not recommend manually altering ``env.cwd`` -- only the *behavior* of `cd` will have any guarantee of backwards compatibility. .. note:: Space characters will be escaped automatically to make dealing with such directory names easier. """ path = path.replace(' ', '\ ') if env.get('cwd'): new_cwd = env.cwd + '/' + path else: new_cwd = path return _setenv(cwd=new_cwd)
def install_minimal(): """Installs minimal dependencies""" sudo("apt-get update") sudo("apt-get install --yes --force-yes {0}".format(MINIMAL_PACKAGES)) if env.get("extra_packages"): sudo("apt-get install --yes --force-yes {0}".format(env.extra_packages))
def cd(path): """ Context manager that keeps directory state when calling `run`/`sudo`. Any calls to `run` or `sudo` within the wrapped block will implicitly have a string similar to ``"cd <path> && "`` prefixed in order to give the sense that there is actually statefulness involved. Because use of `cd` affects all `run` and `sudo` invocations, any code making use of `run` and/or `sudo`, such as much of the ``contrib`` section, will also be affected by use of `cd`. However, at this time, `get` and `put` do not honor `cd`; we expect this to be fixed in future releases. Like the actual 'cd' shell builtin, `cd` may be called with relative paths (keep in mind that your default starting directory is your remote user's ``$HOME``) and may be nested as well. Below is a "normal" attempt at using the shell 'cd', which doesn't work due to how shell-less SSH connections are implemented -- state is **not** kept between invocations of `run` or `sudo`:: run('cd /var/www') run('ls') The above snippet will list the contents of the remote user's ``$HOME`` instead of ``/var/www``. With `cd`, however, it will work as expected:: with cd('/var/www'): run('ls') # Turns into "cd /var/www && ls" Finally, a demonstration (see inline comments) of nesting:: with cd('/var/www'): run('ls') # cd /var/www && ls with cd('website1'): run('ls') # cd /var/www/website1 && ls .. note:: This context manager is currently implemented by appending to (and, as always, restoring afterwards) the current value of an environment variable, ``env.cwd``. However, this implementation may change in the future, so we do not recommend manually altering ``env.cwd`` -- only the *behavior* of `cd` will have any guarantee of backwards compatibility. .. note:: Space characters will be escaped automatically to make dealing with such directory names easier. """ path = path.replace(' ', '\ ') if env.get('cwd') and not path.startswith('/'): new_cwd = env.cwd + '/' + path else: new_cwd = path return _setenv(cwd=new_cwd)
def dev_web(): """Runs the Django development webserver""" # Stop the gunicorn process stop('web') env = _read_env() manage('runserver', env.get('PORT', ''))
def install_minimal(): """Installs minimal dependencies""" sudo("apt-get update") sudo("apt-get install --yes --force-yes {0}".format(MINIMAL_PACKAGES)) if env.get("extra_packages"): sudo("apt-get install --yes --force-yes {0}".format( env.extra_packages))
def create_databases(): databases = env.get("databases", None) if not databases: raise ValueError("We have no databases configuration.") database_creator = DatabaseCreator() database_creator.create_databases(databases)
def deploy(): """Deploy the complete application.""" try: check() except SystemExit: if not env.get("force"): raise print(red("Check status failed, continuing anyways")) insecure_deploy()
def public_app(port='8001'): """ Serve public_app.py. """ if env.get('settings'): local( "DEPLOYMENT_TARGET=%s bash -c 'gunicorn -b 0.0.0.0:%s --timeout 3600 --reload --log-file=logs/public_app.log public_app:wsgi_app'" % (env.settings, port)) else: local('concurrently "%s" "npm start"' % gunicorn)
def get_stage_var(name, default=None): if "stage" not in env: raise Exception("env.stage cannot be empty") key = "%s_%s" % (env.stage.upper(), name) if default is None: return env["%s_%s" % (env.stage.upper(), name)] return env.get(key, default)
def stringify_env_var(var): key = result = '$%s' % var for value, behaviour, sep in env.get(key, []): if behaviour == 'append': result = result + sep + '"' + value + '"' elif behaviour == 'prepend': result = '"' + value + '"' + sep + result else: result = '"' + value + '"' return "%s=%s" % (var, result)
def verify_prerequisites(): """ Checks to make sure you have curl (with ssh) and git-ftp installed, attempts installation via brew if you do not. """ if env.get('sftp_deploy', False): print( colors.cyan( "Verifying your installation of curl supports sftp...")) ret = capture('curl -V | grep sftp') if ret.return_code == 1: if sys.platform.startswith('darwin'): print( colors.red( "Your version of curl does not support sftp...\n" + "You can attempt installing curl+sftp by running:\n")) print( colors.cyan(" brew update\n" + " brew install curl --with-ssh\n" + " brew link --force curl")) else: print( colors.red( "Your version of curl does not support sftp.\n" + "You may have to recompile it with sftp support.\n" + "See the deploy-tools README for more information.")) sys.exit(1) else: print(colors.green('Your installation of curl supports sftp!')) print(colors.cyan('Ensuring you have git-ftp installed...')) ret = capture('git ftp --version') if ret.return_code == 1: print(colors.red('You do not have git-ftp installed!')) print( colors.yellow( "Install git-ftp version 0.9.0 using the instructions found here:\n" + "https://github.com/git-ftp/git-ftp/blob/develop/INSTALL.md" )) sys.exit(1) else: print(colors.green('You have git-ftp installed!')) else: print(colors.cyan("Verifying you have git installed...")) with settings(warn_only=True): ret = capture('git --version') if ret.return_code is not 0: print( colors.red( "You do not have git installed or it is not properly configured." )) sys.exit(1) print(colors.green('Your system is ready to deploy code!'))
def app(port='8000'): """ Serve app.py. """ gunicorn = 'gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload --log-file=- app:wsgi_app' % port if env.get('settings'): local( "DEPLOYMENT_TARGET=%s bash -c 'gunicorn -b 0.0.0.0:%s --timeout 3600 --debug --reload --log-file=-'" % (env.settings, port)) else: local('concurrently "%s" "npm start"' % gunicorn)
def load_fabfile(path): """ Import given fabfile path and return (docstring, callables). Specifically, the fabfile's ``__doc__`` attribute (a string) and a dictionary of ``{'name': callable}`` containing all callables which pass the "is a Fabric task" test. """ # Get directory and fabfile name directory, fabfile = os.path.split(path) # If the directory isn't in the PYTHONPATH, add it so our import will work added_to_path = False index = None if directory not in sys.path: sys.path.insert(0, directory) added_to_path = True # If the directory IS in the PYTHONPATH, move it to the front temporarily, # otherwise other fabfiles -- like Fabric's own -- may scoop the intended # one. else: i = sys.path.index(directory) if i != 0: # Store index for later restoration index = i # Add to front, then remove from original position sys.path.insert(0, directory) del sys.path[i + 1] # Perform the import (trimming off the .py) imported = __import__(os.path.splitext(fabfile)[0]) # Remove directory from path if we added it ourselves (just to be neat) if added_to_path: del sys.path[0] # Put back in original index if we moved it if index is not None: sys.path.insert(index + 1, directory) del sys.path[0] # Filter down to our two-tuple if not api.task.used: tasks = dict(filter(is_task, vars(imported).items())) else: tasks = dict( (var, obj) for var, obj in vars(imported).items() if hasattr(obj, '__fabtask__') ) # Support for stages stages = os.environ.get('FAB_STAGES', env.get('stages')) if stages: if isinstance(stages, basestring): stages = [stage.strip() for stage in stages.split(',')] env.stages = stages for stage in stages: set_env_stage_command(tasks, stage) return imported.__doc__, tasks