Refactoring

This commit is contained in:
gamonoid
2017-09-03 20:39:22 +02:00
parent af40881847
commit a7274d3cfd
5075 changed files with 238202 additions and 16291 deletions
@@ -0,0 +1,491 @@
<?php
namespace Robo\Task\Remote;
use Robo\Contract\CommandInterface;
use Robo\Task\BaseTask;
use Robo\Task\Remote;
use Robo\Exception\TaskException;
/**
* Executes rsync in a flexible manner.
*
* ``` php
* $this->taskRsync()
* ->fromPath('src/')
* ->toHost('localhost')
* ->toUser('dev')
* ->toPath('/var/www/html/app/')
* ->remoteShell('ssh -i public_key')
* ->recursive()
* ->excludeVcs()
* ->checksum()
* ->wholeFile()
* ->verbose()
* ->progress()
* ->humanReadable()
* ->stats()
* ->run();
* ```
*
* You could also clone the task and do a dry-run first:
*
* ``` php
* $rsync = $this->taskRsync()
* ->fromPath('src/')
* ->toPath('example.com:/var/www/html/app/')
* ->archive()
* ->excludeVcs()
* ->progress()
* ->stats();
*
* $dryRun = clone $rsync;
* $dryRun->dryRun()->run();
* if ('y' === $this->ask('Do you want to run (y/n)')) {
* $rsync->run();
* }
* ```
*
* @method \Robo\Task\Remote\Rsync fromUser(string $user)
* @method \Robo\Task\Remote\Rsync fromHost(string $hostname)
* @method \Robo\Task\Remote\Rsync toUser(string $user)
* @method \Robo\Task\Remote\Rsync toHost(string $hostname)
*/
class Rsync extends BaseTask implements CommandInterface
{
use \Robo\Common\ExecOneCommand;
/**
* @var string
*/
protected $command;
/**
* @var string
*/
protected $fromUser;
/**
* @var string
*/
protected $fromHost;
/**
* @var string
*/
protected $fromPath;
/**
* @var string
*/
protected $toUser;
/**
* @var string
*/
protected $toHost;
/**
* @var string
*/
protected $toPath;
/**
* @return static
*/
public static function init()
{
return new static();
}
public function __construct()
{
$this->command = 'rsync';
}
/**
* This can either be a full rsync path spec (user@host:path) or just a path.
* In case of the former do not specify host and user.
*
* @param string|array $path
*
* @return $this
*/
public function fromPath($path)
{
$this->fromPath = $path;
return $this;
}
/**
* This can either be a full rsync path spec (user@host:path) or just a path.
* In case of the former do not specify host and user.
*
* @param string $path
*
* @return $this
*/
public function toPath($path)
{
$this->toPath = $path;
return $this;
}
/**
* @param string $fromUser
*
* @return $this
*/
public function fromUser($fromUser)
{
$this->fromUser = $fromUser;
return $this;
}
/**
* @param string $fromHost
*
* @return $this
*/
public function fromHost($fromHost)
{
$this->fromHost = $fromHost;
return $this;
}
/**
* @param string $toUser
*
* @return $this
*/
public function toUser($toUser)
{
$this->toUser = $toUser;
return $this;
}
/**
* @param string $toHost
*
* @return $this
*/
public function toHost($toHost)
{
$this->toHost = $toHost;
return $this;
}
/**
* @return $this
*/
public function progress()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function stats()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function recursive()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function verbose()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function checksum()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function archive()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function compress()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function owner()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function group()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function times()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @return $this
*/
public function delete()
{
$this->option(__FUNCTION__);
return $this;
}
/**
* @param int $seconds
*
* @return $this
*/
public function timeout($seconds)
{
$this->option(__FUNCTION__, $seconds);
return $this;
}
/**
* @return $this
*/
public function humanReadable()
{
$this->option('human-readable');
return $this;
}
/**
* @return $this
*/
public function wholeFile()
{
$this->option('whole-file');
return $this;
}
/**
* @return $this
*/
public function dryRun()
{
$this->option('dry-run');
return $this;
}
/**
* @return $this
*/
public function itemizeChanges()
{
$this->option('itemize-changes');
return $this;
}
/**
* Excludes .git, .svn and .hg items at any depth.
*
* @return $this
*/
public function excludeVcs()
{
return $this->exclude([
'.git',
'.svn',
'.hg',
]);
}
/**
* @param array|string $pattern
*
* @return $this
*/
public function exclude($pattern)
{
return $this->optionList(__FUNCTION__, $pattern);
}
/**
* @param string $file
*
* @return $this
*
* @throws \Robo\Exception\TaskException
*/
public function excludeFrom($file)
{
if (!is_readable($file)) {
throw new TaskException($this, "Exclude file $file is not readable");
}
return $this->option('exclude-from', $file);
}
/**
* @param array|string $pattern
*
* @return $this
*/
public function includeFilter($pattern)
{
return $this->optionList('include', $pattern);
}
/**
* @param array|string $pattern
*
* @return $this
*/
public function filter($pattern)
{
return $this->optionList(__FUNCTION__, $pattern);
}
/**
* @param string $file
*
* @return $this
*
* @throws \Robo\Exception\TaskException
*/
public function filesFrom($file)
{
if (!is_readable($file)) {
throw new TaskException($this, "Files-from file $file is not readable");
}
return $this->option('files-from', $file);
}
/**
* @param string $command
*
* @return $this
*/
public function remoteShell($command)
{
$this->option('rsh', "'$command'");
return $this;
}
/**
* {@inheritdoc}
*/
public function run()
{
$command = $this->getCommand();
$this->printTaskInfo("Running {command}", ['command' => $command]);
return $this->executeCommand($command);
}
/**
* Returns command that can be executed.
* This method is used to pass generated command from one task to another.
*
* @return string
*/
public function getCommand()
{
foreach ((array)$this->fromPath as $from) {
$this->option(null, $this->getFromPathSpec($from));
}
$this->option(null, $this->getToPathSpec());
return $this->command . $this->arguments;
}
/**
* @return string
*/
protected function getFromPathSpec($from)
{
return $this->getPathSpec($this->fromHost, $this->fromUser, $from);
}
/**
* @return string
*/
protected function getToPathSpec()
{
return $this->getPathSpec($this->toHost, $this->toUser, $this->toPath);
}
/**
* @param string $host
* @param string $user
* @param string $path
*
* @return string
*/
protected function getPathSpec($host, $user, $path)
{
$spec = isset($path) ? $path : '';
if (!empty($host)) {
$spec = "{$host}:{$spec}";
}
if (!empty($user)) {
$spec = "{$user}@{$spec}";
}
return $spec;
}
}
@@ -0,0 +1,274 @@
<?php
namespace Robo\Task\Remote;
use Robo\Result;
use Robo\Contract\CommandInterface;
use Robo\Exception\TaskException;
use Robo\Task\BaseTask;
use Robo\Contract\SimulatedInterface;
/**
* Runs multiple commands on a remote server.
* Per default, commands are combined with &&, unless stopOnFail is false.
*
* ```php
* <?php
*
* $this->taskSshExec('remote.example.com', 'user')
* ->remoteDir('/var/www/html')
* ->exec('ls -la')
* ->exec('chmod g+x logs')
* ->run();
*
* ```
*
* You can even exec other tasks (which implement CommandInterface):
*
* ```php
* $gitTask = $this->taskGitStack()
* ->checkout('master')
* ->pull();
*
* $this->taskSshExec('remote.example.com')
* ->remoteDir('/var/www/html/site')
* ->exec($gitTask)
* ->run();
* ```
*
* You can configure the remote directory for all future calls:
*
* ```php
* \Robo\Task\Remote\Ssh::configure('remoteDir', '/some-dir');
* ```
*
* @method $this stopOnFail(bool $stopOnFail) Whether or not to chain commands together with &&
* and stop the chain if one command fails
* @method $this remoteDir(string $remoteWorkingDirectory) Changes to the given directory before running commands
*/
class Ssh extends BaseTask implements CommandInterface, SimulatedInterface
{
use \Robo\Common\CommandReceiver;
use \Robo\Common\ExecOneCommand;
/**
* @var null|string
*/
protected $hostname;
/**
* @var null|string
*/
protected $user;
/**
* @var bool
*/
protected $stopOnFail = true;
/**
* @var array
*/
protected $exec = [];
/**
* Changes to the given directory before running commands.
*
* @var string
*/
protected $remoteDir;
/**
* @param null|string $hostname
* @param null|string $user
*/
public function __construct($hostname = null, $user = null)
{
$this->hostname = $hostname;
$this->user = $user;
}
/**
* @param string $hostname
*
* @return $this
*/
public function hostname($hostname)
{
$this->hostname = $hostname;
return $this;
}
/**
* @param string $user
*
* @return $this
*/
public function user($user)
{
$this->user = $user;
return $this;
}
/**
* @param bool $stopOnFail
*
* @return $this
*/
public function stopOnFail($stopOnFail = true)
{
$this->stopOnFail = $stopOnFail;
return $this;
}
/**
* @param string $remoteDir
*
* @return $this
*/
public function remoteDir($remoteDir)
{
$this->remoteDir = $remoteDir;
return $this;
}
/**
* @param string $filename
*
* @return $this
*/
public function identityFile($filename)
{
$this->option('-i', $filename);
return $this;
}
/**
* @param int $port
*
* @return $this
*/
public function port($port)
{
$this->option('-p', $port);
return $this;
}
/**
* @return $this
*/
public function forcePseudoTty()
{
$this->option('-t');
return $this;
}
/**
* @return $this
*/
public function quiet()
{
$this->option('-q');
return $this;
}
/**
* @return $this
*/
public function verbose()
{
$this->option('-v');
return $this;
}
/**
* @param string|string[]|CommandInterface $command
*
* @return $this
*/
public function exec($command)
{
if (is_array($command)) {
$command = implode(' ', array_filter($command));
}
$this->exec[] = $command;
return $this;
}
/**
* Returns command that can be executed.
* This method is used to pass generated command from one task to another.
*
* @return string
*/
public function getCommand()
{
$commands = [];
foreach ($this->exec as $command) {
$commands[] = $this->receiveCommand($command);
}
$remoteDir = $this->remoteDir ? $this->remoteDir : $this->getConfigValue('remoteDir');
if (!empty($remoteDir)) {
array_unshift($commands, sprintf('cd "%s"', $remoteDir));
}
$command = implode($this->stopOnFail ? ' && ' : ' ; ', $commands);
return $this->sshCommand($command);
}
/**
* {@inheritdoc}
*/
public function run()
{
$this->validateParameters();
$command = $this->getCommand();
return $this->executeCommand($command);
}
/**
* {@inheritdoc}
*/
public function simulate($context)
{
$command = $this->getCommand();
$this->printTaskInfo("Running {command}", ['command' => $command] + $context);
}
protected function validateParameters()
{
if (empty($this->hostname)) {
throw new TaskException($this, 'Please set a hostname');
}
if (empty($this->exec)) {
throw new TaskException($this, 'Please add at least one command');
}
}
/**
* Returns an ssh command string running $command on the remote.
*
* @param string|CommandInterface $command
*
* @return string
*/
protected function sshCommand($command)
{
$command = $this->receiveCommand($command);
$sshOptions = $this->arguments;
$hostSpec = $this->hostname;
if ($this->user) {
$hostSpec = $this->user . '@' . $hostSpec;
}
return sprintf("ssh{$sshOptions} {$hostSpec} '{$command}'");
}
}
@@ -0,0 +1,24 @@
<?php
namespace Robo\Task\Remote;
trait loadTasks
{
/**
* @return \Robo\Task\Remote\Rsync
*/
protected function taskRsync()
{
return $this->task(Rsync::class);
}
/**
* @param null|string $hostname
* @param null|string $user
*
* @return \Robo\Task\Remote\Ssh
*/
protected function taskSshExec($hostname = null, $user = null)
{
return $this->task(Ssh::class, $hostname, $user);
}
}