deploy.php
<?php
/**
 * simple php git deploy script
 *
 * automatically deploy the code using php and git.
 *
 * @version 1.3.1
 * @link  https://github.com/476552238li/deploy
 */
 
// =========================================[ configuration start ]===
 
/**
 * it's preferable to configure the script using `deploy-config.php` file.
 *
 * rename `deploy-config.example.php` to `deploy-config.php` and edit the
 * configuration options there instead of here. that way, you won't have to edit
 * the configuration again if you download the new version of `deploy.php`.
 */
if (file_exists(basename(__file__, '.php').'-config.php')) require_once basename(__file__, '.php').'-config.php';
 
/**
 * protect the script from unauthorized access by using a secret access token.
 * if it's not present in the access url as a get variable named `sat`
 * e.g. deploy.php?sat=bett...s the script is not going to deploy.
 *
 * @var string
 */
if (!defined('secret_access_token')) define('secret_access_token', '6a604a35d5a7c3fd8786f5ee94991a8c');
 
/**
 * the address of the remote git repository that contains the code that's being
 * deployed.
 * if the repository is private, you'll need to use the ssh address.
 *
 * @var string
 */
if (!defined('remote_repository')) define('remote_repository', 'https://github.com/476552238li/php-sql-parser.git');
 
/**
 * the branch that's being deployed.
 * must be present in the remote repository.
 *
 * @var string
 */
if (!defined('branch')) define('branch', 'master');
 
/**
 * the location that the code is going to be deployed to.
 * don't forget the trailing slash!
 *
 * @var string full path including the trailing slash
 */
if (!defined('target_dir')) define('target_dir', '/var/www/php-sql-parser');
 
/**
 * whether to delete the files that are not in the repository but are on the
 * local (server) machine.
 *
 * !!! warning !!! this can lead to a serious loss of data if you're not
 * careful. all files that are not in the repository are going to be deleted,
 * except the ones defined in exclude section.
 * be careful!
 *
 * @var boolean
 */
if (!defined('delete_files')) define('delete_files', false);
 
/**
 * the directories and files that are to be excluded when updating the code.
 * normally, these are the directories containing files that are not part of
 * code base, for example user uploads or server-specific configuration files.
 * use rsync exclude pattern syntax for each element.
 *
 * @var serialized array of strings
 */
if (!defined('exclude')) define('exclude', serialize(array(
  '.git',
  )));
 
/**
 * temporary directory we'll use to stage the code before the update. if it
 * already exists, script assumes that it contains an already cloned copy of the
 * repository with the correct remote origin and only fetches changes instead of
 * cloning the entire thing.
 *
 * @var string full path including the trailing slash
 */
if (!defined('tmp_dir')) define('tmp_dir', '/tmp/spgd-'.md5(remote_repository).'/');
 
/**
 * whether to remove the tmp_dir after the deployment.
 * it's useful not to clean up in order to only fetch changes on the next
 * deployment.
 */
if (!defined('clean_up')) define('clean_up', true);
 
/**
 * output the version of the deployed code.
 *
 * @var string full path to the file name
 */
if (!defined('version_file')) define('version_file', tmp_dir.'version');
 
/**
 * time limit for each command.
 *
 * @var int time in seconds
 */
if (!defined('time_limit')) define('time_limit', 30);
 
/**
 * optional
 * backup the target_dir into backup_dir before deployment.
 *
 * @var string full backup directory path e.g. `/tmp/`
 */
if (!defined('backup_dir')) define('backup_dir', false);
 
/**
 * optional
 * whether to invoke composer after the repository is cloned or changes are
 * fetched. composer needs to be available on the server machine, installed
 * globaly (as `composer`). see http://getcomposer.org/doc/00-intro.md#globally
 *
 * @var boolean whether to use composer or not
 * @link http://getcomposer.org/
 */
if (!defined('use_composer')) define('use_composer', false);
 
/**
 * optional
 * the options that the composer is going to use.
 *
 * @var string composer options
 * @link http://getcomposer.org/doc/03-cli.md#install
 */
if (!defined('composer_options')) define('composer_options', '--no-dev');
 
/**
 * optional
 * the composer_home environment variable is needed only if the script is
 * executed by a system user that has no home defined, e.g. `www-data`.
 *
 * @var string path to the composer_home e.g. `/tmp/composer`
 * @link https://getcomposer.org/doc/03-cli.md#composer-home
 */
if (!defined('composer_home')) define('composer_home', false);
 
/**
 * optional
 * email address to be notified on deployment failure.
 *
 * @var string email address
 */
if (!defined('email_on_error')) define('email_on_error', false);
 
// ===========================================[ configuration end ]===
 
// if there's authorization error, set the correct http header.
if (!isset($_get['sat']) || $_get['sat'] !== secret_access_token || secret_access_token === '6a604a35d5a7c3fd8786f5ee94991a8c') {
  header('http/1.0 403 forbidden');
}
ob_start();
?>
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="robots" content="noindex">
  <title>simple php git deploy script</title>
  <style>
    body { padding: 0 1em; background: #222; color: #fff; }
    h2, .error { color: #c33; }
    .prompt { color: #6be234; }
    .command { color: #729fcf; }
    .output { color: #999; }
  </style>
</head>
<body>
  <?php
  if (!isset($_get['sat']) || $_get['sat'] !== secret_access_token) {
    die('<h2>access denied!</h2>');
  }
  if (secret_access_token === 'betterchangemenoworsuffertheconsequences') {
    die("<h2>you're suffering the consequences!<br>change the secret_access_token from it's default value!</h2>");
  }
  ?>
  <pre>
 
    checking the environment ...
 
    running as <b><?php echo trim(shell_exec('whoami')); ?></b>.
 
    <?php
    // check if the required programs are available
    $requiredbinaries = array('git', 'rsync');
    if (defined('backup_dir') && backup_dir !== false) {
      $requiredbinaries[] = 'tar';
      if (!is_dir(backup_dir) || !is_writable(backup_dir)) {
        die(sprintf('<div>backup_dir `%s` does not exists or is not writeable.</div>', backup_dir));
      }
    }
    if (defined('use_composer') && use_composer === true) {
      $requiredbinaries[] = 'composer --no-ansi';
    }
    foreach ($requiredbinaries as $command) {
      $path = trim(shell_exec('which '.$command));
      if ($path == '') {
        die(sprintf('<div><b>%s</b> not available. it needs to be installed on the server for this script to work.</div>', $command));
      } else {
        $version = explode("\n", shell_exec($command.' --version'));
        printf('<b>%s</b> : %s'."\n"
          , $path
          , $version[0]
          );
      }
    }
    ?>
 
    environment ok.
 
    deploying <?php echo remote_repository; ?> <?php echo branch."\n"; ?>
    to        <?php echo target_dir; ?> ...
 
    <?php
    // the commands
    $commands = array();
 
    // ========================================[ pre-deployment steps ]===
 
    if (!is_dir(tmp_dir)) {
      // clone the repository into the tmp_dir
      $commands[] = sprintf(
        'git clone --depth=1 --branch %s %s %s'
        , branch
        , remote_repository
        , tmp_dir
        );
    } else {
      // tmp_dir exists and hopefully already contains the correct remote origin
      // so we'll fetch the changes and reset the contents.
      $commands[] = sprintf(
        'git --git-dir="%s.git" --work-tree="%s" fetch origin %s'
        , tmp_dir
        , tmp_dir
        , branch
        );
      $commands[] = sprintf(
        'git --git-dir="%s.git" --work-tree="%s" reset --hard fetch_head'
        , tmp_dir
        , tmp_dir
        );
    }
 
    // update the submodules
    $commands[] = sprintf(
      'git submodule update --init --recursive'
      );
 
    // describe the deployed version
    if (defined('version_file') && version_file !== '') {
      $commands[] = sprintf(
        'git --git-dir="%s.git" --work-tree="%s" describe --always > %s'
        , tmp_dir
        , tmp_dir
        , version_file
        );
    }
 
    // backup the target_dir
    // without the backup_dir for the case when it's inside the target_dir
    if (defined('backup_dir') && backup_dir !== false) {
      $commands[] = sprintf(
        "tar --exclude='%s*' -czf %s/%s-%s-%s.tar.gz %s*"
        , backup_dir
        , backup_dir
        , basename(target_dir)
        , md5(target_dir)
        , date('ymdhis')
        , target_dir // we're backing up this directory into backup_dir
      );
    }
 
    // invoke composer
    if (defined('use_composer') && use_composer === true) {
      $commands[] = sprintf(
        'composer --no-ansi --no-interaction --no-progress --working-dir=%s install %s'
        , tmp_dir
        , (defined('composer_options')) ? composer_options : ''
        );
      if (defined('composer_home') && is_dir(composer_home)) {
        putenv('composer_home='.composer_home);
      }
    }
 
    // ==================================================[ deployment ]===
 
    // compile exclude parameters
    $exclude = '';
    foreach (unserialize(exclude) as $exc) {
      $exclude .= ' --exclude='.$exc;
    }
 
    // deployment command
    $commands[] = sprintf(
      'rsync -rltgodzvo %s %s %s %s'
      , tmp_dir
      , target_dir
      , (delete_files) ? '--delete-after' : ''
      , $exclude
    );
 
    // =======================================[ post-deployment steps ]===
 
    // remove the tmp_dir (depends on clean_up)
    if (clean_up) {
      $commands['cleanup'] = sprintf(
        'rm -rf %s'
        , tmp_dir
        );
    }
 
    // =======================================[ run the command steps ]===
    $output = '';
    foreach ($commands as $command) {
      set_time_limit(time_limit); // reset the time limit for each command
      if (file_exists(tmp_dir) && is_dir(tmp_dir)) {
        chdir(tmp_dir); // ensure that we're in the right directory
      }
      $tmp = array();
      exec($command.' 2>&1', $tmp, $return_code); // execute the command
      // output the result
      printf('
        <span>$</span> <span>%s</span>
        <div>%s</div>
        '
        , htmlentities(trim($command))
        , htmlentities(trim(implode("\n", $tmp)))
        );
      $output .= ob_get_contents();
      ob_flush(); // try to output everything as it happens
 
      // error handling and cleanup
      if ($return_code !== 0) {
        printf('
          <div>
            error encountered!
            stopping the script to prevent possible data loss.
            check the data in your target dir!
          </div>
          '
          );
        if (clean_up) {
          $tmp = shell_exec($commands['cleanup']);
          printf('
            cleaning up temporary files ...
 
            <span>$</span> <span>%s</span>
            <div>%s</div>
            '
            , htmlentities(trim($commands['cleanup']))
            , htmlentities(trim($tmp))
            );
        }
        $error = sprintf(
          'deployment error on %s using %s!'
          , $_server['http_host']
          , __file__
          );
        error_log($error);
        if (email_on_error) {
          $output .= ob_get_contents();
          $headers = array();
          $headers[] = sprintf('from: simple php git deploy script <simple-php-git-deploy@%s>', $_server['http_host']);
          $headers[] = sprintf('x-mailer: php/%s', phpversion());
          mail(email_on_error, $error, strip_tags(trim($output)), implode("\r\n", $headers));
        }
        break;
      }
    }
    ?>
  done.
  </pre>
</body>
</html>
   
 
   