def rollback(): """Swaps the deployed version of the app to the previous version. Requires the env keys: path - root deploy target for this app releases_root - subdirectory that stores the releases current_release_symlink - name of the symlink pointing to the currently deployed version Optional: crontab - relative path from the project root to a crontab to install deploy_user - user that should run the crontab """ require('path') require('releases_root') require('current_release_symlink') require('crontab') require('deploy_user') with cd(os.path.join(env.path, env.releases_root)): previous_link = deploy.release.alternative_release_path() conditional_rm(env.current_release_symlink) run('ln -fs %s %s' % (previous_link, env.current_release_symlink)) deploy.cron.conditional_install_crontab(utils.absolute_release_path(), env.crontab, env.deploy_user) restart_webserver()
def _package_installed(package): with settings(warn_only=True): virtualenv_exists = exists('%(virtualenv)s' % env) if virtualenv_exists: installed = run('%s/bin/python -c "import %s"' % (env.virtualenv, package)) else: installed = run('python -c "import %s"' % package) return installed.return_code == 0
def _install_manual_packages(path=None): require('virtualenv') if not env.package_installation_scripts: return if not path: require('release_path') path = env.release_path with cd(path): for script in env.package_installation_scripts: run('./%s %s' % (script, local("echo $VIRTUAL_ENV") or env.virtualenv))
def _install_pip_requirements(path=None): require('virtualenv') require('pip_requirements') if not path: require('release_path') path = env.release_path if not env.pip_requirements: warn("No pip requirements files found -- %(pip_requirements)s" % env) return with cd(path): for requirements_file in env.pip_requirements: run('%s -E %s -s -r %s' % (env.pip_install_command, env.virtualenv, requirements_file))
def _git_deploy(release, skip_tests): starting_branch = utils.branch() print(green("Deploying from git branch '%s'" % starting_branch)) # Ideally, tests would run on the version you are deploying exactly. # There is no easy way to require that without allowing users to go # through the entire tagging process before failing tests. if not skip_tests and testing.test(): abort(red("Unit tests did not pass -- must fix before deploying")) #local('git push %(master_remote)s' % env, capture=True) #buedafab.deploy.release.make_release(release) make_release(release) require('pretty_release') require('path') require('hosts') print(green("Deploying version %s" % env.pretty_release)) put(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'files', 'ssh_config'), '.ssh/config') deployed = False hard_reset = False deployed_versions = {} bootstrap_release_folders() for release_path in env.release_paths: with cd(os.path.join(env.path, env.releases_root, release_path)): deployed_versions[run('git describe')] = release_path print(green("The host '%s' currently has the revisions: %s" % (env.host, deployed_versions))) if env.pretty_release not in deployed_versions: env.release_path = os.path.join(env.path, env.releases_root, alternative_release_path()) with cd(env.release_path): run('git fetch %(master_remote)s --tag' % env, forward_agent=False) run('git fetch %(master_remote)s' % env, forward_agent=False) run("git reset --hard %(release)s" % env) cron.conditional_install_crontab(env.release_path, env.crontab, env.deploy_user) deployed = True else: warn(red("%(pretty_release)s is already deployed" % env)) env.release_path = os.path.join(env.path, env.releases_root, deployed_versions[env.pretty_release]) with cd(env.release_path): run('git submodule update --init --recursive', forward_agent=False) hard_reset = packages.install_requirements(deployed) run_extra_deploy_tasks(deployed) local('git checkout %s' % starting_branch, capture=True) chmod(os.path.join(env.path, env.releases_root), 'g+w', use_sudo=True) return deployed, hard_reset
def bootstrap_release_folders(): """Create the target deploy directories if they don't exist and clone a fresh copy of the project's repository into each of the release directories. """ require('path') require('deploy_group') conditional_mkdir(os.path.join(env.path, env.releases_root), env.deploy_group, 'g+w', use_sudo=True) with cd(os.path.join(env.path, env.releases_root)): first_exists = exists(env.release_paths[0]) if not first_exists: run('git clone %s %s' % (env.scm, env.release_paths[0]), forward_agent=True) with cd(os.path.join(env.path, env.releases_root)): if not exists(env.release_paths[1]): run('cp -R %s %s' % (env.release_paths[0], env.release_paths[1])) chmod(os.path.join(env.path, env.releases_root), 'g+w', use_sudo=True)
def _install_private_package(package, scm=None, release=None): env.scratch_path = os.path.join('/tmp', '%s-%s' % (package, env.time_now)) archive_path = '%s.tar.gz' % env.scratch_path if not scm: require('s3_key') env.s3_key.key = '%s.tar.gz' % package env.s3_key.get_contents_to_filename(archive_path) else: if 'release' not in env: env.release = release release = release or 'HEAD' if 'pretty_release' in env: original_pretty_release = env.pretty_release else: original_pretty_release = None if 'archive' in env: original_archive = env.archive else: original_archive = None with settings(unit=package, scm=scm, release=release): if not os.path.exists(env.scratch_path): local('git clone %(scm)s %(scratch_path)s' % env) deploy.utils.make_archive() local('mv %s %s' % (os.path.join(env.scratch_path, env.archive), archive_path)) if original_pretty_release: env.pretty_release = original_pretty_release if original_archive: env.archive = original_archive put(archive_path, '/tmp') if env.virtualenv is not None: require('release_path') require('path') with cd(env.release_path): run('%s -E %s -s %s' % (env.pip_install_command, env.virtualenv, archive_path)) else: run('%s -s %s' % (env.pip_install_command, archive_path))
def _git_deploy(release, skip_tests): starting_branch = utils.branch() print(green("Deploying from git branch '%s'" % starting_branch)) # Ideally, tests would run on the version you are deploying exactly. # There is no easy way to require that without allowing users to go # through the entire tagging process before failing tests. if not skip_tests and testing.test(): abort(red("Unit tests did not pass -- must fix before deploying")) local('git push %(master_remote)s' % env, capture=True) deploy.release.make_release(release) require('pretty_release') require('path') require('hosts') print(green("Deploying version %s" % env.pretty_release)) put( os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'files', 'ssh_config'), '.ssh/config') deployed = False hard_reset = False deployed_versions = {} deploy.release.bootstrap_release_folders() for release_path in env.release_paths: with cd(os.path.join(env.path, env.releases_root, release_path)): deployed_versions[run('git describe')] = release_path print( green("The host '%s' currently has the revisions: %s" % (env.host, deployed_versions))) if env.pretty_release not in deployed_versions: env.release_path = os.path.join( env.path, env.releases_root, deploy.release.alternative_release_path()) with cd(env.release_path): run('git fetch %(master_remote)s' % env, forward_agent=True) run('git reset --hard %(release)s' % env) deploy.cron.conditional_install_crontab(env.release_path, env.crontab, env.deploy_user) deployed = True else: warn(red("%(pretty_release)s is already deployed" % env)) env.release_path = os.path.join(env.path, env.releases_root, deployed_versions[env.pretty_release]) with cd(env.release_path): run('git submodule update --init --recursive', forward_agent=True) hard_reset = deploy.packages.install_requirements(deployed) deploy.utils.run_extra_deploy_tasks(deployed) local('git checkout %s' % starting_branch, capture=True) chmod(os.path.join(env.path, env.releases_root), 'g+w', use_sudo=True) return deployed, hard_reset
def conditional_symlink_current_release(deployed=False): """Swap the 'current' symlink to point to the new release if it doesn't point there already. Requires the env keys: pretty_release - set by make_pretty_release(), a commit identifier release_path - root target directory on the remote server """ current_version = None if exists(utils.absolute_release_path()): with settings(cd(utils.absolute_release_path()), warn_only=True): current_version = run('git describe') if (not exists(utils.absolute_release_path()) or deployed or current_version != env.pretty_release): _symlink_current_release(env.release_path)
def alternative_release_path(): """Determine the release directory that is not currently in use. For example if the 'current' symlink points to the 'a' release directory, this method returns 'b'. Requires the env keys: release_paths - a tuple of length 2 with the release directory names (defaults to 'a' and 'b') """ if exists(utils.absolute_release_path()): current_release_path = run('readlink %s' % utils.absolute_release_path()) if os.path.basename(current_release_path) == env.release_paths[0]: alternative = env.release_paths[1] else: alternative = env.release_paths[0] return alternative else: return env.release_paths[0]
def install_jcc(**kwargs): if not _package_installed('jcc'): run('git clone git://gist.github.com/729451.git build-jcc') run('VIRTUAL_ENV=%s build-jcc/install_jcc.sh' % env.virtualenv) run('rm -rf build-jcc')
def install_pylucene(**kwargs): if not _package_installed('lucene'): run('git clone git://gist.github.com/728598.git build-pylucene') run('VIRTUAL_ENV=%s build-pylucene/install_pylucene.sh' % env.virtualenv) run('rm -rf build-pylucene')
def _symlink_current_release(next_release_path): with cd(os.path.join(env.path, env.releases_root)): conditional_rm(env.current_release_symlink) run('ln -fs %s %s' % (next_release_path, env.current_release_symlink))