Esempio n. 1
0
def deploy(branch=None):
    ''' Deploy to remote source. '''
    stage = shell.get_stage()
    deployer_user = shell.get_user()
    branch = branch or get_stage_config(stage)['branch']
    commit = git.last_commit(short=True)
    notif.send(notification_types.DEPLOYMENT_STARTED, {
        'user': deployer_user,
        'branch': branch,
        'commit': commit,
        'stage': stage
    })

    # Get the latest code from the repository
    sync(branch)
    install_dependencies()

    # Building the app
    build(stage)
    reload_service()

    notif.send(notification_types.DEPLOYMENT_FINISHED, {
        'user': deployer_user,
        'branch': branch,
        'commit': commit,
        'stage': stage
    })

    remote_info('Deployment Completed')
Esempio n. 2
0
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)
Esempio n. 3
0
def deploy(branch=None):
    ''' Deploy to remote source. '''
    stage = shell.get_stage()
    deployer_user = shell.get_user()
    branch = branch or resolve_deployment_branch(stage)
    commit = git.last_commit(remote=False, short=True)
    notif.send(notification_types.DEPLOYMENT_STARTED, {
        'user': deployer_user,
        'branch': branch,
        'commit': commit,
        'stage': stage
    })

    with cd(get_repo_path()):
        runner.run_script_safely(known_scripts.PRE_DEPLOY)

        # Get the latest code from the repository
        sync(branch)
        install_dependencies()

        # Building the app
        build(stage)
        reload_service()
        runner.run_script_safely(known_scripts.POST_DEPLOY)

    notif.send(notification_types.DEPLOYMENT_FINISHED, {
        'user': deployer_user,
        'branch': branch,
        'commit': commit,
        'stage': stage
    })

    remote_info('Deployment Completed')
Esempio n. 4
0
def get_build_info(history, id):
    ''' Get the build information by build id. '''

    if not history['builds']:
        remote_info('No build history recorded yet.')
        return None

    return get_build_by_id(history, id)
Esempio n. 5
0
def deploy(branch=None):
    ''' Deploy to remote source. '''
    stage = shell.get_stage()
    branch = branch or resolve_deployment_branch(stage)
    params = dict(user=shell.get_user(), stage=stage, branch=branch)
    notif.send(notification_types.DEPLOYMENT_STARTED, params)
    run_deploy_script(stage, branch)
    notif.send(notification_types.DEPLOYMENT_FINISHED, params)
    remote_info('Deployment Completed')
Esempio n. 6
0
def get_current_build_index(history):
    ''' Get the current build index. '''
    if not history['current']:
        remote_info('No current build found.')
        return None

    # Return the build index for the current build.
    current = history['current']
    for i, build in enumerate(history['builds']):
        if build['id'] == current:
            return i

    return None
Esempio n. 7
0
def rollback(id=None):
    '''
    Deployment rollback to the previous build, or
    the build identified by the given id.
    '''
    # TODO: Send rollback started notification
    (_, current_path) = setup_remote()
    history = load_history()

    # If the current build in the history is not set yet or
    # there aren't any previous builds on the history
    # rollback is not possible.
    if not history['current'] or not history['builds']:
        remote_info('Could not get the previous build to rollback.')
        return

    # If the rollback build id is not explicitly provided,
    # rollback to the previous build.
    if not id:
        current_index = get_current_build_index(history)

        # If current_index is None, or there are no builds before the current build
        # print the error since there are no previous builds to rollback.
        build_count = len(history['builds'])
        has_prev_build = 0 < current_index + 1 < build_count

        if current_index is None or not has_prev_build:
            remote_info('No previous builds found to rollback.')
            return

        # Get the previous build information.
        prev_build = history['builds'][current_index + 1]
    else:
        # Otherwise, if the id is provided then, get the build with that id
        prev_build = get_build_by_id(history, id)

        if not prev_build:
            remote_info('Build with id "{}" not found.'.format(id))
            return

    remote_info('Rolling back to build {}'.format(prev_build['id']))
    fs.update_symlink(prev_build['path'], current_path)

    # Save history and display it.
    history['current'] = prev_build['id']
    save_history(history)
    display_list(history)

    # TODO: Send rollback completed notification.
    remote_info('Rollback successful')
Esempio n. 8
0
def setup_remote(quiet=True):
    ''' Setup remote environment before we can proceed with the deployment process. '''
    base_dir = get_deploy_dir()
    release_dir = get_release_dir()
    current_path = get_current_path()
    build_history_path = get_builds_file()
    preset = get_config()['deployment']['preset']
    did_setup = False
    stage = shell.get_stage()

    # If the release directory does not exist, create it.
    if not fs.exists(release_dir):
        remote_info('Setting up {} server for {} deployment'.format(
            stage, preset))
        remote_info('Creating the releases directory {}'.format(
            cyan(release_dir)))
        fs.mkdir(release_dir, nested=True)

        # Add build history file.
        remote_info('Creating new build meta file {}'.format(
            cyan(build_history_path)))
        save_history(merge(INITIAL_BUILD_HISTORY, {'preset': preset}))

        # Setup a default web page for web deployment.
        if preset == presets.WEB:
            setup_default_html(base_dir)

        did_setup = True

    if not did_setup and not quiet:
        remote_info('Remote already setup for deployment')

    return (release_dir, current_path)
Esempio n. 9
0
def record_history(build_info):
    ''' Record a new build in the history. '''
    config = get_config()
    keep_builds = int(config['deployment']['keep_builds'])
    build_history = load_history()

    build_history['current'] = build_info['id']
    build_history['builds'].insert(0, build_info)
    build_history['builds'] = build_history['builds'][0:keep_builds]

    remote_info('Saving the build history')

    # Update build history json file
    save_history(build_history)

    # Delete the previous builds more than the value of `keep_builds`.
    delete_old_builds(build_history)
Esempio n. 10
0
def delete_old_builds(history):
    ''' Auto delete unnecessary build directories from the filesystem. '''
    build_path = get_release_dir()
    kept_builds = map(lambda x: get_build_name(x['id']), history['builds'])
    found_builds = fs.glob(build_path)
    to_be_deleted_builds = [x for x in found_builds if x not in kept_builds]
    deletion_count = len(to_be_deleted_builds)

    # Skip, if there are no builds to be deleted.
    if deletion_count == 0:
        return

    # Remove directories to be deleted.
    with cd(build_path):
        fs.rm_rf(to_be_deleted_builds)
        remote_info(
            'Deleted {} old build(s) from the remote'.format(deletion_count))
Esempio n. 11
0
def setup_default_html(base_dir):
    ''' Setup default html web page on the remote host. '''
    current_path = base_dir + CURRENT_BUILD_LINK
    html_path = BASE_PATH + '/misc/default_html'
    remote_html_path = base_dir + DEFAULT_HTML_PATH

    remote_info('Setting up the default web page')
    fs.upload_dir(html_path, base_dir)

    # Point the current sym link to the default web page.
    fs.update_symlink(remote_html_path, current_path)

    remote_info('Remote is setup and is ready for deployment.')
    remote_print(('Deployed build will point to {0}.\n' +
                  'For serving the latest build, ' +
                  'please set your web server document root to {0}.').format(
                      cyan(current_path)))
Esempio n. 12
0
def display_list(history):
    ''' Display build history. '''
    if not history['builds']:
        remote_info('No builds have been deployed yet.')
        return

    remote_info('Showing recent builds')

    # Map build data into tabular format
    data = map(row_mapper_wrt(history['current']), history['builds'])

    # Prepend heading rows
    data.insert(0, [' ', 'ID', 'Commit', 'Branch', 'Created By', 'Timestamp'])

    table = SingleTable(data)
    print('')
    print(table.table)
Esempio n. 13
0
def deploy(branch=None):
    ''' Deploy to remote source. '''
    stage = shell.get_stage()
    branch = branch or fallback_branch(stage)
    notif.send(notif.DEPLOYMENT_STARTED, {
        'user': shell.get_user(),
        'branch': branch,
        'stage': stage
    })

    # Get the latest code from the repository
    sync(branch)
    install_dependencies()

    # Building the app
    build(stage)
    reload_service()

    notif.send(notif.DEPLOYMENT_FINISHED, {'branch': branch, 'stage': stage})

    remote_info('Deployment Completed')
Esempio n. 14
0
def sync(branch=None):
    ''' Sync the changes on the branch with the remote (origin). '''
    remote_info('Fetching the latest changes.')
    git.fetch()
    branch = branch or git.current_branch()
    remote_info('Checking out to {}.'.format(cyan(branch)))
    git.checkout(branch, True)
    remote_info('Synchronizing with the latest changes.')
    git.sync(branch)
Esempio n. 15
0
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)
Esempio n. 16
0
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)
Esempio n. 17
0
def deploy():
    ''' Zero-Downtime deployment for the backend. '''
    config = get_config()
    stage = shell.get_stage()
    is_remote_setup = buildman.is_remote_setup()
    is_first_deployment = not is_remote_setup

    if is_remote_setup and buildman.is_remote_up_to_date():
        echo('Remote build is already up to date.')
        return

    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
    ))

    build_dir = os.path.abspath(buildman.resolve_local_build_dir())
    included_files = config['deployment']['include_files']
    deployer_user = shell.get_user()
    notif_params = dict(
        user=deployer_user,
        commit=commit,
        branch=branch,
        stage=stage
    )
    notif.send(notification_types.DEPLOYMENT_STARTED, notif_params)
    runner.run_script_safely(known_scripts.PRE_DEPLOY)

    (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)
    release_path = os.path.join(release_dir + '/' + build_name)
    dist_path = os.path.join(release_dir, build_name + '/dist')

    buildman.build(stage, config)

    uploader = BulkUploader()
    uploader.add(build_dir, dist_path)

    # Upload the files to be included eg: package.json file
    # to the remote build location.
    for filename in included_files:
        path = os.path.abspath(filename)
        # Add for upload if the file exist.
        if exists_local(path):
            uploader.add(path, release_path)

    uploader.upload()
    remote_info('Updating the current symlink')
    fs.update_symlink(release_path, current_path)

    # Once, the build is uploaded to the remote,
    # set things up in the remote server.
    # Change directory to the release path.
    install_remote_dependencies(
        commit=commit,
        current_path=current_path,
        smart_install=get_stage_config(stage)['deployment']['smart_install']
    )

    # 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)
    })

    runner.run_script_safely(known_scripts.POST_DEPLOY)

    # Send deployment finished notification.
    notif.send(notification_types.DEPLOYMENT_FINISHED, notif_params)

    info('Deployment Completed')
Esempio n. 18
0
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(notification_types.DEPLOYMENT_STARTED, {
        'user': deployer_user,
        'commit': commit,
        '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'

    buildman.build(stage, config)

    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(notification_types.DEPLOYMENT_FINISHED, {
        'user': deployer_user,
        'branch': branch,
        'commit': commit,
        'stage': stage
    })

    remote_info('Deployment Completed')
Esempio n. 19
0
def stop_service():
    ''' Stop the application service. '''
    with cd(buildman.get_deploy_dir()):
        remote_info('Stopping the service.')
        runner.run_script_safely(known_scripts.STOP)
Esempio n. 20
0
def reload_service():
    ''' Restart the application service. '''
    with cd(buildman.get_deploy_dir()):
        remote_info('Reloading the service.')
        runner.run_script_safely(known_scripts.RELOAD)
Esempio n. 21
0
def deploy():
    ''' Zero-Downtime deployment for the web. '''
    config = get_config()
    stage = shell.get_stage()
    user = get_stage_config(stage)['user']

    # Get the current branch and commit (locally).
    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()

    deploy_dir = buildman.get_deploy_dir()
    deployer_user = shell.get_user()

    notif.send(notification_types.DEPLOYMENT_STARTED, {
        'user': deployer_user,
        'branch': branch,
        'commit': commit,
        '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

    buildman.build(stage, config)

    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(build_name)

        # Extract the build.
        fs.tar_extract(tmp_path, build_name)

        # Remove the uploaded archived from the temp path.
        fs.rm_rf(tmp_path)

        remote_info('Changing ownership of {} to user {}'.format(
            deploy_dir, user))
        fs.chown(release_path, user, user)

        remote_info('Pointing the current symlink to the latest build')
        fs.update_symlink(release_path, current_path)

    # 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(notification_types.DEPLOYMENT_FINISHED, {
        'user': deployer_user,
        'branch': branch,
        'commit': commit,
        'stage': stage
    })

    remote_info('Deployment Completed')