#!/bin/bash
#
# push-to-pantheon.sh
#
# This script will push all of the generated build results created on a
# CI server such as Travis or Circle to a Drupal site hosted on Pantheon.
#
# This script is designed only to be called by the CI server.  It relies
# on special encrypted environment variables that must be set up in advance
# to provide the credentials used to log in to the Pantheon dashboard.
#
# The specific environment variables we need are:
#
#   SITE_NAME: The human-readable name of the site to give to site-install
#   PSITE: The name of the Pantheon site to push to
#   PENV:  The environment of the site to push to (e.g. "dev")
#   PEMAIL: The email address to log on to Pantheon with
#   PPASS: The password to log on to Pantheon with
#   CI_BOT_EMAIL: The email address to use in the git commit attribution
#   CI_BOT_NAME: The name to use in the git commit attribution
#
# It is also expected that there should be an SSH private key in
# $HOME/.ssh that can be used to push code to the Pantheon git repository.
#
# For specific configuration instructions, see:
#
#   https://github.com/pantheon-systems/travis-scripts
#   https://github.com/pantheon-systems/circle-scripts
#

SELF_DIRNAME="`dirname -- "$0"`"
PROJECT_BASE_DIR="`cd -P -- "$SELF_DIRNAME/.." && pwd -P`"

#
# Exit with a message if the previous function returned an error.
#
# Usage:
#
#   aborterr "Description of what went wrong"
#
function aborterr() {
  if [ $? != 0 ]
  then
    echo "$1" >&2
    exit 1
  fi
}

#
# Check the result of the last operation, and either print progress or call aborterr
#
# Usage:
#
#   check "Something the script did" "Message to display if it did not work"
#
function check() {
  aborterr "$2"
  echo "$1"
}


# Provide default values for commit attribution if not
# provided in enviornment variables.
if [ -z "$CI_BOT_DOMAIN" ]
then
  CI_BOT_DOMAIN="${PEMAIL/*@/}"
fi
if [ -z "$CI_BOT_EMAIL" ]
then
  CI_BOT_EMAIL="ci-bot@${CI_BOT_DOMAIN}"
fi
if [ -z "$CI_BOT_NAME" ]
then
  CI_BOT_NAME="CI Bot"
fi

# If the CI provides us with an explicit build location (e.g. on Travis),
# then we'll always believe that.  Otherwise, we'll stick with our base
# assumption, which is that the project base dir is in the directory
# above where this script is located (i.e. $PROJECT_BASE_DIR/bin/push-to-pantheon)
if [ -n "$TRAVIS_BUILD_DIR" ]
then
  PROJECT_BASE_DIR="$TRAVIS_BUILD_DIR"
fi

# Add vendor/bin and $HOME/bin to our $PATH
export PATH="$PROJECT_BASE_DIR/bin:$HOME/bin:$PATH"

# If Drush version is not specified, then default to Drush 6
if [ -z "$DRUSH_VERSION" ]
then
  DRUSH_VERSION=6
fi

# Do not attempt to push the site up to Pantheon unless the PPASS
# environment variable is set.  If we receive a PR from an external
# repository, then the secure environment variables will not be set;
# if we test PRs, we cannot send them to Pantheon in this instance.
if [ -n "$PPASS" ]
then
  # Dynamic hosts through Pantheon mean constantly checking interactively
  # that we mean to connect to an unknown host. We ignore those here.
  echo "StrictHostKeyChecking no" > $HOME/.ssh/config

  # Create a local policy file that specifies the version of Drush that
  # the CI server should use when running Drush commands remotely on Pantheon.
  # n.b. we still want to use drush8 for Drupal 8 sites
  mkdir -p "$HOME/.drush"
  cat << __EOF__ > "$HOME/.drush/policy.drush.inc"
<?php

function policy_drush_sitealias_alter(&\$alias_record) {
  // Fix pantheon aliases!
  if (isset(\$alias_record['remote-host']) && (substr(\$alias_record['remote-host'], 0, 10) == 'appserver.')) {
    \$alias_record['path-aliases']['%drush-script'] = 'drush$DRUSH_VERSION';
  }
}
__EOF__

  # Capture the commit message
  export CI_COMMIT_MSG="$(git log --format=%B --no-merges -n 1)"

  # Install Terminus
  mkdir -p $HOME/bin
  curl https://github.com/pantheon-systems/cli/releases/download/0.5.5/terminus.phar -L -o $HOME/bin/terminus
  chmod +x $HOME/bin/terminus

  # Set up Terminus and aliases, and wake up the site
  echo "Log in to Pantheon via Terminus"
  # Redirect output when logging in.  Terminus prints the email address
  # used during login to stdout, which will end up in the log.  It's better
  # to conceal the email address used, to make it harder for a third party
  # to try to log in via a brute-force password-guessing attack.
  terminus auth login "$PEMAIL" --password="$PPASS" >/dev/null 2>&1
  check "Logged in to Pantheon via Terminus" "Could not log in to Pantheon with Terminus"
  echo "Fetch aliases via Terminus"
  mkdir -p $HOME/.drush
  terminus sites aliases
  aborterr "Could not fetch aliases via Terminus"
  echo "Wake up the site $PSITE"
  terminus site wake --site="$PSITE" --env="$PENV"
  check "Site wakeup successful" "Could not wake up site"
  PUUID=$(terminus site info --site="$PSITE" --field=id)
  aborterr "Could not get UUID for $PSITE"

  # Make sure we are in git mode
  terminus site connection-mode --site="$PSITE" --env="$PENV" --set=git
  check "Changed connection mode to 'git' for $PSITE $PENV environment" "Could not change connection mode to 'git' for $PSITE $PENV environment"

  # Identify the automation user
  git config --global user.email "$CI_BOT_EMAIL"
  git config --global user.name "$CI_BOT_NAME"

  # Clone the Pantheon repository into a separate directory, then
  # move the .git file to the location where we placed our composer targets
  cd $PROJECT_BASE_DIR
  REPO="ssh://codeserver.dev.$PUUID@codeserver.dev.$PUUID.drush.in:2222/~/repository.git"
  git clone --depth 1 "$REPO" $HOME/pantheon
  check "git clone successful" "Could not clone repository from $REPO"
  # Get rid of any .git directories created by composer
  rm -rf $(find $PROJECT_BASE_DIR/drupal -name ".git")
  # Move the .git directory we checked out on top of the files we built.
  # This is functionally equivalent to copying the built files over the
  # checked-out repository, except more efficient.
  mv $HOME/pantheon/.git $PROJECT_BASE_DIR/drupal

  # Use our .gitignore file from our source repository to build
  # a .gitattributes file in our Pantheon repository.  Anything
  # inside of 'drupal' that is ignored in the source repository is
  # ignored because it is a built target.  We want to also ignore
  # the same set of files when making diffs on the Pantheon repository,
  # to avoid large diffs full of uninteresting changes.
  grep drupal $PROJECT_BASE_DIR/.gitignore | sed -e 's#drupal/\(.*\)#\1 -diff#' -e 's#/ -diff#/** -diff#' > $PROJECT_BASE_DIR/drupal/.gitattributes

  # Output of the diff vs upstream.
  cd $PROJECT_BASE_DIR/drupal
  echo "Here's the status change!"
  git status

  # If there is no settings.php file on Pantheon, then we will create one.
  # n.b. there is always a settings.php file in $TAVIS_BUILD_DIR; it was
  # created when we did the local `drush site-install` prior to testing on
  # behat.  We'll remove it now, and replace it with some other settings.php
  # file.
  if [ -z "$INSTALL_SITE" ]
  then
    INSTALL_SITE=false
  fi
  chmod +w sites/default
  rm -f sites/default/settings.php
  aborterr "Could not remote built settings.php file."
  if [ ! -f $HOME/pantheon/sites/default/settings.php ]
  then
    cp -f $HOME/pantheon/sites/default/default.settings.php sites/default/settings.php
    aborterr "Could not create settings.php with default.settings.php."
    # settings.php is excluded by our .gitignore, but
    # we need one on Pantheon in order to use drush si,
    # so we will add it with `git add -f`.
    git add -f sites/default/settings.php
    aborterr "Could not add settings.php"
    INSTALL_SITE=true
  else
    # Maintain existing Pantheon settings.php file.
    cp $HOME/pantheon/sites/default/settings.php sites/default/settings.php
    aborterr "Could not preserve existing settings.php file."

    # Check to see if a pre-installed site is running okay.  If we
    # can't bootstrap Drupal, then we'll re-install
    echo "Drush status on the remote site:"
    drush --strict=0 @pantheon.$PSITE.$PENV status
    # Check to see if we are having any bootstrap problems.
    if [ $? == 0 ]
    then
      BOOTSTRAPPED=$(drush  --strict=0 @pantheon.$PSITE.$PENV status "Drupal bootstrap")
    fi
    if [ $? != 0 ] || [ -z "$BOOTSTRAPPED" ]
    then
      # Wipe the site and start over
      terminus site wipe --site="$PSITE" --env="$PENV" --yes
      INSTALL_SITE=true
    fi
  fi

  # Push our built files up to Pantheon
  git add --all
  aborterr "'git add --all' failed"
  git commit -a -m "Built by CI: '$CI_COMMIT_MSG'"
  aborterr "'git commit' failed"
  git push origin master
  aborterr "'git push' failed"

  # If we created a settings.php file, then run 'site install'
  if $INSTALL_SITE
  then
    # We need to go back to sftp mode to run site-install
    terminus site connection-mode --site="$PSITE" --env="$PENV" --set=sftp
    check "Changed connection mode to 'sftp' for $PSITE $PENV environment" "Could not change connection mode to 'sftp' for $PSITE $PENV environment"
    # Create a new site with site-install.
    drush --strict=0 @pantheon.$PSITE.$PENV -y site-install standard --site-name="$SITE_NAME Pantheon Test Site" --account-name=admin --account-pass="[REDACTED]"
    aborterr "Could not install Drupal on Pantheon test site"
    # Commit the modification made to settings.php and switch back to git mode
    terminus site code commit --site="$PSITE" --env="$PENV" --message="Settings.php modifications made by site-install" --yes
    aborterr "Could not commit settings.php changes"
    terminus site connection-mode --site="$PSITE" --env="$PENV" --set=git
    aborterr "Could not switch back to git mode"
    # Because the site password is in the log, generate a new random
    # password for the site, and change it.  The site owner can use
    # `drush uli` to log in.
    RANDPASS=$(cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1 )
    drush --strict=0 @pantheon.$PSITE.$PENV user-password admin --password="$RANDPASS"
    aborterr "Could not reset password on Pantheon test site"
  else
    # If the database already exists, just run updatedb to bring it up-to-date
    # with the code we just pushed.
    # n.b. If we wanted to re-run our behat tests on the pantheon site, then
    # we would probably want to install a fresh site every time.
    drush --strict=0 @pantheon.$PSITE.$PENV updatedb
    aborterr "updatedb failed on Pantheon site"
  fi
fi
