Skip to content

Commit 0666c4f

Browse files
committed
Refactor connection options management to simplify API and improve consistency across SSH and rsync operations
1 parent fd08d3f commit 0666c4f

6 files changed

Lines changed: 52 additions & 19 deletions

File tree

contrib/rsync.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115

116116
use Deployer\Host\Localhost;
117117
use Deployer\Task\Context;
118+
use function Deployer\Support\rsync_rsh;
118119

119120
set('rsync', [
120121
'exclude' => [
@@ -245,6 +246,6 @@
245246
return;
246247
}
247248

248-
$sshArguments = $host->connectionOptionsString();
249-
runLocally("rsync {$rsyncFlags} -e 'ssh $sshArguments' {{rsync_options}}{{rsync_includes}}{{rsync_excludes}}{{rsync_filter}} '$src/' '{$host->connectionString()}:$dst/'", timeout: $config['timeout']);
249+
$rsh = quote(rsync_rsh($host->connectionOptions()));
250+
runLocally("rsync {$rsyncFlags} -e $rsh {{rsync_options}}{{rsync_includes}}{{rsync_excludes}}{{rsync_filter}} '$src/' '{$host->connectionString()}:$dst/'", timeout: $config['timeout']);
250251
});

src/Command/SshCommand.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\Console\Input\InputInterface;
2222
use Symfony\Component\Console\Output\OutputInterface;
2323
use Symfony\Component\Console\Question\ChoiceQuestion;
24+
use function Deployer\quote;
2425

2526
/**
2627
* @codeCoverageIgnore
@@ -93,13 +94,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int
9394

9495
Context::push(new Context($host));
9596
$host->setSshMultiplexing(false);
96-
$options = $host->connectionOptionsString();
97+
$connOptions = '';
98+
foreach ($host->connectionOptions() as $option) {
99+
$connOptions .= quote($option);
100+
}
97101
$deployPath = $host->get('deploy_path', '~');
98102

99103
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
100-
passthru("ssh -t $options {$host->connectionString()} \"cd $deployPath/current 2>/dev/null || cd $deployPath; $shell_path\"");
104+
passthru("ssh -t $connOptions {$host->connectionString()} \"cd $deployPath/current 2>/dev/null || cd $deployPath; $shell_path\"");
101105
} else {
102-
passthru("ssh -t $options {$host->connectionString()} 'cd $deployPath/current 2>/dev/null || cd $deployPath; $shell_path'");
106+
passthru("ssh -t $connOptions {$host->connectionString()} 'cd $deployPath/current 2>/dev/null || cd $deployPath; $shell_path'");
103107
}
104108
return 0;
105109
}

src/Host/Host.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,10 @@ public function connectionString(): string
266266
return $this->get('hostname');
267267
}
268268

269-
public function connectionOptionsString(): string
270-
{
271-
return implode(' ', array_map('Deployer\quote', $this->connectionOptionsArray()));
272-
}
273-
274269
/**
275270
* @return string[]
276271
*/
277-
public function connectionOptionsArray(): array
272+
public function connectionOptions(): array
278273
{
279274
$options = [];
280275
if ($this->has('ssh_arguments')) {

src/Ssh/SshClient.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function run(Host $host, string $command, RunParams $params): string
4040
$shellCommand = "sudo -H -u {$host->get('become')} " . $shellCommand;
4141
}
4242

43-
$ssh = array_merge(['ssh'], $host->connectionOptionsArray(), [$host->connectionString(), ": $shellId; $shellCommand"]);
43+
$ssh = array_merge(['ssh'], $host->connectionOptions(), [$host->connectionString(), ": $shellId; $shellCommand"]);
4444

4545
// -vvv for ssh command
4646
if ($this->output->isDebug()) {

src/Support/helpers.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,34 @@ function deployer_root(): string
233233
}
234234
}
235235
}
236+
237+
/**
238+
* Constructs an rsync-compatible remote shell command string using SSH and the provided options.
239+
*
240+
* @see https://github.com/RsyncProject/rsync/blob/4f6e4ea64ac3e2ac50f48103a22601f4a40ee8be/rsync.1.md?plain=1#L2152-L2162
241+
*
242+
* Command-line arguments are permitted in COMMAND provided that COMMAND is
243+
* presented to rsync as a single argument. You must use spaces (not tabs or
244+
* other whitespace) to separate the command and args from each other, and you
245+
* can use single- and/or double-quotes to preserve spaces in an argument (but
246+
* not backslashes). Note that doubling a single-quote inside a single-quoted
247+
* string gives you a single-quote; likewise for double-quotes (though you
248+
* need to pay attention to which quotes your shell is parsing and which
249+
* quotes rsync is parsing). Some examples:
250+
*
251+
* -e 'ssh -p 2234'
252+
* -e 'ssh -o "ProxyCommand nohup ssh firewall nc -w1 %h %p"'
253+
*
254+
*/
255+
function rsync_rsh(array $args): string
256+
{
257+
$rsh = 'ssh ';
258+
foreach ($args as $option) {
259+
if (preg_match('/^[a-zA-Z0-9_\-=]+$/', $option)) {
260+
$rsh .= ' ' . $option;
261+
} else {
262+
$rsh .= '"' . addslashes($option) . '" ';
263+
}
264+
}
265+
return $rsh;
266+
}

src/Utility/Rsync.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Process\Process;
2020

2121
use function Deployer\quote;
22+
use function Deployer\Support\rsync_rsh;
2223
use function Deployer\writeln;
2324

2425
class Rsync
@@ -58,9 +59,10 @@ public function call(Host $host, $source, string $destination, array $config = [
5859
$options[] = '--stats';
5960
}
6061

61-
$connectionOptions = $host->connectionOptionsString();
62-
if ($connectionOptions !== '') {
63-
$options = array_merge($options, ['-e', "ssh $connectionOptions"]);
62+
$connectionOptions = $host->connectionOptions();
63+
if (!empty($connectionOptions)) {
64+
$rsh = rsync_rsh($connectionOptions);
65+
$options = array_merge($options, ['-e', $rsh]);
6466
}
6567
if ($host->has('become') && !empty($host->get('become'))) {
6668
$options = array_merge($options, ['--rsync-path', "sudo -H -u {$host->get('become')} rsync"]);
@@ -75,12 +77,12 @@ function (string $value) {
7577
},
7678
));
7779

78-
$commandString = $command[0];
80+
$printCommandForDebug = $command[0];
7981
for ($i = 1; $i < count($command); $i++) {
80-
$commandString .= ' ' . quote($command[$i]);
82+
$printCommandForDebug .= ' ' . quote($command[$i]);
8183
}
8284
if ($this->output->isVerbose()) {
83-
$this->output->writeln("[$host] $commandString");
85+
$this->output->writeln("[$host] $printCommandForDebug");
8486
}
8587

8688
$progressBar = null;
@@ -138,7 +140,7 @@ function (string $value) {
138140
} catch (ProcessFailedException $exception) {
139141
throw new RunException(
140142
$host,
141-
$commandString,
143+
$printCommandForDebug,
142144
$process->getExitCode(),
143145
$process->getOutput(),
144146
$process->getErrorOutput(),

0 commit comments

Comments
 (0)