#!/usr/bin/php -q

<?php
/**
 * Deployer Tool
 *
 * Rsync a project source to remote servers, supporting git and excluding files.
 *
 * @version     v1.5.0
 * @author      Nick Tsai <myintaer@gmail.com>
 * @filesource  PHP >= 5.4 (Support 5.0 if removing Short-Array-Syntax)
 *
 * @param string $argv[1] Target servers group key of remoteServers
 * @example
 *  $ ./deployer            // Rsync to servers in default group
 *  $ ./deployer stage      // Rsync to servers in stage group
 *  $ ./deployer prod       // Rsync to servers in prod group
 */


/* Configuration */

/**
 * @var array Distant server host list
 */
$config['remoteServers'] = [
    'default' => [
        '110.1.1.1',
        '110.1.2.1',
    ],
    'stage' => [
        '110.1.1.1',
    ],
    'prod' => [
        '110.1.2.1',
    ],
];

/**
 * @var string Remote server user
 */
$config['remoteUser'] = 'www-data';

/**
 * @var string Local directory for deploy 
 */
$config['sourceFile'] = '/home/www/www.project.com/webroot';

/**
 * @var string Remote path for synchronism
 */
$config['remotePath'] = '/home/www/www.project.com/';

/**
 * @var string Addition params of rsync command
 */
$config['rsyncParams'] = '-av --delete';

/**
 * @var array Excluded files based on sourceFile path
 */
$config['excludeFiles'] = [
    'web/upload',
    'runtime/log',
];

/**
 * @var int Seconds waiting of each rsync connections
 */
$config['sleepSeconds'] = 0;


/**
 * @var bool Enabled git or not
 */
$config['gitEnabled'] = false;

/**
 * @var string Execute git checkout -- . before git pull  
 */
$config['gitCheckoutEnabled'] = false;

/**
 * @var string Branch name for git pull, pull default branch if empty  
 */
$config['gitBranch'] = '';

/**
 * @var bool Enabled Composer or not
 */
$config['composerEnabled'] = false;

/**
 * @var string Composer command line for update or install
 */
$config['composerCommand'] = 'composer update';

/**
 * @var string Array of commands executing before deployment
 */
$config['commandsBeforeDeploy'] = [
    // 'cd /var/www/html/your-project',
    // 'gulp minify-all',
    // 'Minify' => 'cd /var/www/html/your-project; gulp minify-all',
];

/* /Configuration */


ob_implicit_flush();

// Target server group list for rsync
$serverEnv = (isset($argv[1])) ? $argv[1] : 'default';

try {

    // Check for server list
    if (!isset($config['remoteServers'][$serverEnv]) 
        || !$config['remoteServers'][$serverEnv]) {
        
        throw new Exception("No server host in group: {$serverEnv}");
    }

    $sourceFile = $config['sourceFile'];
    $remotePath = $config['remotePath'];

    // File existence check
    if (strlen(trim($sourceFile))==0) {

        throw new Exception('None of file input');
    }

    // Check for type of file / directory
    if (!is_file($sourceFile) && !is_dir($sourceFile) ) {

        throw new Exception('Source file is not a file or directory');
    }

    // Check for type of link
    if (is_link($sourceFile)) {

        throw new Exception('File input is symblic link');
    }

    // Directory locate
    $result = shell_exec("cd {$config['sourceFile']};");
        
    // Git process
    if ($config['gitEnabled']) {
        
        echo "Processing Git...\n";
        $cmd = ($config['gitCheckoutEnabled'])
            ? "git checkout - .;"
            : "";
        $cmd .= ($config['gitBranch']) 
            ? "git pull origin {$config['gitBranch']}"
            : "git pull";

        // Shell execution
        $result = shell_exec($cmd);

        echo "/* --- Git Process Result --- */\n";
        echo $result;
        echo "/* -------------------------- */\n";
        echo "\r\n";
    }

    // Composer process
    if ($config['composerEnabled']) {
        
        echo "/* --- Composer Process Start --- */\n";
        $cmd = $config['composerCommand'];

        // Shell execution
        $result = shell_exec($cmd);
        echo $result;

        echo "/* --- Composer Process End --- */\n";
        echo "\r\n";
    }

    // Commands process
    if ($config['commandsBeforeDeploy']) {

        foreach ((array)$config['commandsBeforeDeploy'] as $key => $cmd) {
            
            echo "/* --- Command:{$key} Process Start --- */\n";

            // Format command
            $cmd = "{$cmd};";
            // Shell execution
            $result = shell_exec($cmd);
            echo $result;

            echo "/* --- Command:{$key} Process End --- */\n";
            echo "\r\n";
        }
    }

    // Rsync each servers
    foreach ($config['remoteServers'][$serverEnv] as $key => $server) {

        // Info display
        echo "/* --- Rsync Process Info --- */\n";
        echo '[Process]: '.($key+1)."\n";
        echo '[Group  ]: '.$serverEnv."\n";
        echo '[Server ]: '.$server."\n";
        echo '[User   ]: '.$config['remoteUser']."\n";
        echo '[Source ]: '.$sourceFile."\n";
        echo '[Remote ]: '.$remotePath."\n";
        echo "/* -------------------------- */\n";
        echo "Processing Rsync...\n";


        /* Command builder */
        
        $cmd = 'rsync ' . $config['rsyncParams'];

        // Add exclude
        $excludeFiles = $config['excludeFiles'];
        foreach ((array)$excludeFiles as $key => $file) {
            $cmd .= " --exclude \"{$file}\"";
        }

        // Rsync shell command
        $cmd = sprintf("%s %s %s@%s:%s",
            $cmd,
            $sourceFile,
            $config['remoteUser'],
            $server,
            $remotePath 
        );
        
        echo '[Command]: '.$cmd."\n";
        
        // Shell execution
        $result = shell_exec($cmd);

        echo "/* --- Rsync Process Result --- */\n";
        echo $result;
        echo "/* ---------------------------- */\n";
        echo "\r\n";

        sleep($config['sleepSeconds']);
    }

    echo "\r\n";
    
} catch (Exception $e) {

    die('ERROR:'.$e->getMessage()."\n");
}


