def install_remote_dependencies(): ''' Install dependencies on the remote host. ''' remote_info('Installing dependencies on the remote') if runner.is_script_defined(known_scripts.INSTALL_REMOTE): runner.run_script(known_scripts.INSTALL_REMOTE) else: runner.run_script(known_scripts.INSTALL)
def test_run_script_send_script_running_notifications(gs_m, gu_m, send_m, gc_m, hi_m, h_m, r_m): gs_m.return_value = 'prod' gu_m.return_value = 'kabir' gc_m.return_value = { 'scripts': { 'foo': 'just foo' }, 'notified_hooks': { 'scripts': ['foo'] } } run_script('foo') send_m.assert_called() (call1, call2) = send_m.call_args_list assert call1[0][0] == RUNNING_SCRIPT_STARTED assert call1[0][1]['script'] == 'foo' assert call1[0][1]['user'] == 'kabir' assert call1[0][1]['stage'] == 'prod' assert call2[0][0] == RUNNING_SCRIPT_FINISHED assert call2[0][1]['script'] == 'foo' assert call2[0][1]['user'] == 'kabir' assert call2[0][1]['stage'] == 'prod'
def run(script): ''' Run a custom script. ''' # Run a custom script defined in the config. # Change the current working directory to the node application # before running the script. with cd(buildman.get_current_path()): try: runner.run_script(script) except RuntimeError as e: halt(str(e))
def build(stage, config): ''' Trigger build script to prepare a build for the given stage. ''' info('Getting the build ready for deployment') # Trigger the install script runner.run_script(known_scripts.INSTALL, remote=False) env_vars = get_build_env_vars(stage, config) with shell_env(**env_vars): runner.run_script(known_scripts.BUILD, remote=False)
def start_or_reload_service(has_started=False): ''' Start or reload the application service. ''' with cd(buildman.get_deploy_dir()): if runner.is_script_defined(known_scripts.START_OR_RELOAD): remote_info('Starting/Reloading the service.') runner.run_script(known_scripts.START_OR_RELOAD) elif has_started and runner.is_script_defined(known_scripts.RELOAD): remote_info('Reloading the service.') runner.run_script_safely(known_scripts.RELOAD) elif runner.is_script_defined(known_scripts.START): remote_info('Starting the service.') runner.run_script(known_scripts.START)
def install_remote_dependencies(commit, current_path, smart_install): ''' Install dependencies on the remote host. ''' history = buildman.load_history() prev_build = buildman.get_prev_build_info(history) # Check if the installation could be skipped (smart_install). can_skip_installation = ( smart_install and prev_build and not has_updated_dependencies(prev_build['commit'], commit) ) # Smart install - copy the node_modules directory from the previous deployment # if it's usable (no dependencies or package manager files have changed). if can_skip_installation: runner.run( 'cp -R {src} {dest}'.format( src=os.path.join(prev_build['path'], 'node_modules'), dest=os.path.join(current_path, 'node_modules') ) ) remote_info('Skipping installation - No change in dependencies.') return # Install dependencies on the remote. with cd(current_path): remote_info('Installing dependencies on the remote') runner.run_script_safely(known_scripts.PRE_INSTALL) if runner.is_script_defined(known_scripts.INSTALL_REMOTE): runner.run_script_safely(known_scripts.PRE_INSTALL_REMOTE) runner.run_script(known_scripts.INSTALL_REMOTE) runner.run_script_safely(known_scripts.POST_INSTALL_REMOTE) else: runner.run_script_safely(known_scripts.INSTALL) runner.run_script_safely(known_scripts.POST_INSTALL)
def services(): ''' List the services running for the application. ''' with cd(buildman.get_current_path()): runner.run_script(known_scripts.LIST_SERVICES)
def status(): ''' Get the status of the service. ''' with cd(buildman.get_current_path()): runner.run_script(known_scripts.STATUS_CHECK)
def deploy(): ''' Zero-Downtime deployment for the backend. ''' config = get_config() stage = shell.get_stage() is_first_deployment = not buildman.is_remote_setup() branch = git.current_branch(remote=False) commit = git.last_commit(remote=False, short=True) info('Deploying <{branch}:{commit}> to the {stage} server'.format( branch=branch, commit=commit, stage=stage)) tmp_path = fs.get_temp_filename() build_dir = buildman.resolve_local_build_dir() included_files = config['deployment']['include_files'] deployer_user = shell.get_user() notif.send(notif.DEPLOYMENT_STARTED, { 'user': deployer_user, 'branch': branch, 'stage': stage }) (release_dir, current_path) = buildman.setup_remote() timestamp = datetime.utcnow() build_id = timestamp.strftime('%Y%m%d%H%M%S') build_name = buildman.get_build_name(build_id) build_compressed = build_name + '.tar.gz' release_path = release_dir + '/' + build_name dist_path = build_name + '/dist' info('Getting the build ready for deployment') # Trigger the install script runner.run_script(constants.SCRIPT_INSTALL, remote=False) # Trigger the build script. # # The stage for which the build script is being run is passed # via an environment variable STAGE. # This could be useful for creating specific builds for # different environments. with shell_env(STAGE=stage): runner.run_script(constants.SCRIPT_BUILD, remote=False) info('Compressing the build') fs.tar_archive(build_compressed, build_dir, remote=False) info('Uploading the build {} to {}'.format(build_compressed, tmp_path)) fs.upload(build_compressed, tmp_path) # Remove the compressed build from the local directory. fs.rm(build_compressed, remote=False) # Once, the build is uploaded to the remote, # set things up in the remote server. with cd(release_dir): remote_info('Extracting the build {}'.format(build_compressed)) # Create a new directory for the build in the remote. fs.mkdir(dist_path, nested=True) # Extract the build. fs.tar_extract(tmp_path, dist_path) # Remove the uploaded archived from the temp path. fs.rm_rf(tmp_path) # Upload the files to be included eg: package.json file # to the remote build location. upload_included_files(included_files, release_path) remote_info('Pointing the current symlink to the latest build') fs.update_symlink(release_path, current_path) # Change directory to the release path. with cd(current_path): install_remote_dependencies() # Start or restart the application service. start_or_reload_service(is_first_deployment) # Save build history buildman.record_history({ 'id': build_id, 'path': release_path, 'branch': branch, 'commit': commit, 'stage': stage, 'createdBy': deployer_user, 'timestamp': timestamp.strftime(buildman.TS_FORMAT) }) # Send deployment finished notification. notif.send(notif.DEPLOYMENT_FINISHED, {'branch': branch, 'stage': stage}) remote_info('Deployment Completed')