Refactoring
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user