mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 22:10:55 +01:00
Execute commands under Powershell on Windows for Harbormaster
Summary: Resolves T5831. This modifies the Drydock SSH interface to execute commands under Powershell when the target host platform is Windows. Powershell is far more featured than cmd.exe, and more closely resembles a UNIX shell. Currently Powershell outputs stderr as an XML blob on a line, and while this code currently doesn't use that, it will allow us in the future (planned next week) to redirect that output to the stderr log instead of having it all merged in with stdout under cmd (where there is no way to distinguish it). Test Plan: Ran various native commands and PowerShell commands from a Harbormaster build, including things like: ``` Write-Host ("my test" + ${build.id}) ``` and saw: ``` my test679 ``` in the output. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: epriestley, Korvin Maniphest Tasks: T5831 Differential Revision: https://secure.phabricator.com/D10248
This commit is contained in:
parent
8ef1ea63dd
commit
ca8f7cdaa5
2 changed files with 63 additions and 16 deletions
|
@ -36,18 +36,44 @@ final class DrydockSSHCommandInterface extends DrydockCommandInterface {
|
|||
|
||||
$argv = func_get_args();
|
||||
|
||||
// This assumes there's a UNIX shell living at the other
|
||||
// end of the connection, which isn't the case for Windows machines.
|
||||
if ($this->getConfig('platform') !== 'windows') {
|
||||
$argv = $this->applyWorkingDirectoryToArgv($argv);
|
||||
}
|
||||
|
||||
$full_command = call_user_func_array('csprintf', $argv);
|
||||
|
||||
if ($this->getConfig('platform') === 'windows') {
|
||||
// On Windows platforms we need to execute cmd.exe explicitly since
|
||||
// most commands are not really executables.
|
||||
$full_command = 'C:\\Windows\\system32\\cmd.exe /C '.$full_command;
|
||||
// Handle Windows by executing the command under PowerShell.
|
||||
$command = id(new PhutilCommandString($argv))
|
||||
->setEscapingMode(PhutilCommandString::MODE_POWERSHELL);
|
||||
|
||||
$change_directory = '';
|
||||
if ($this->getWorkingDirectory() !== null) {
|
||||
$change_directory .= 'cd '.$this->getWorkingDirectory();
|
||||
}
|
||||
|
||||
$script = <<<EOF
|
||||
$change_directory
|
||||
$command
|
||||
if (\$LastExitCode -ne 0) {
|
||||
exit \$LastExitCode
|
||||
}
|
||||
EOF;
|
||||
|
||||
// When Microsoft says "Unicode" they don't mean UTF-8.
|
||||
$script = mb_convert_encoding($script, 'UTF-16LE');
|
||||
|
||||
$script = base64_encode($script);
|
||||
|
||||
$powershell =
|
||||
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
||||
$powershell .=
|
||||
' -ExecutionPolicy Bypass'.
|
||||
' -NonInteractive'.
|
||||
' -InputFormat Text'.
|
||||
' -OutputFormat Text'.
|
||||
' -EncodedCommand '.$script;
|
||||
|
||||
$full_command = $powershell;
|
||||
} else {
|
||||
// Handle UNIX by executing under the native shell.
|
||||
$argv = $this->applyWorkingDirectoryToArgv($argv);
|
||||
|
||||
$full_command = call_user_func_array('csprintf', $argv);
|
||||
}
|
||||
|
||||
$command_timeout = '';
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
final class HarbormasterCommandBuildStepImplementation
|
||||
extends HarbormasterBuildStepImplementation {
|
||||
|
||||
private $platform;
|
||||
|
||||
public function getName() {
|
||||
return pht('Run Command');
|
||||
}
|
||||
|
@ -18,6 +20,18 @@ final class HarbormasterCommandBuildStepImplementation
|
|||
$this->formatSettingForDescription('hostartifact'));
|
||||
}
|
||||
|
||||
public function escapeCommand($pattern, array $args) {
|
||||
array_unshift($args, $pattern);
|
||||
|
||||
$mode = PhutilCommandString::MODE_DEFAULT;
|
||||
if ($this->platform == 'windows') {
|
||||
$mode = PhutilCommandString::MODE_POWERSHELL;
|
||||
}
|
||||
|
||||
return id(new PhutilCommandString($args))
|
||||
->setEscapingMode($mode);
|
||||
}
|
||||
|
||||
public function execute(
|
||||
HarbormasterBuild $build,
|
||||
HarbormasterBuildTarget $build_target) {
|
||||
|
@ -25,15 +39,19 @@ final class HarbormasterCommandBuildStepImplementation
|
|||
$settings = $this->getSettings();
|
||||
$variables = $build_target->getVariables();
|
||||
|
||||
$command = $this->mergeVariables(
|
||||
'vcsprintf',
|
||||
$settings['command'],
|
||||
$variables);
|
||||
|
||||
$artifact = $build->loadArtifact($settings['hostartifact']);
|
||||
|
||||
$lease = $artifact->loadDrydockLease();
|
||||
|
||||
$this->platform = $lease->getAttribute('platform');
|
||||
|
||||
$command = $this->mergeVariables(
|
||||
array($this, 'escapeCommand'),
|
||||
$settings['command'],
|
||||
$variables);
|
||||
|
||||
$this->platform = null;
|
||||
|
||||
$interface = $lease->getInterface('command');
|
||||
|
||||
$future = $interface->getExecFuture('%C', $command);
|
||||
|
@ -88,6 +106,9 @@ final class HarbormasterCommandBuildStepImplementation
|
|||
'name' => pht('Command'),
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'caption' => pht(
|
||||
'Under Windows, this is executed under PowerShell.'.
|
||||
'Under UNIX, this is executed using the user\'s shell.'),
|
||||
),
|
||||
'hostartifact' => array(
|
||||
'name' => pht('Host'),
|
||||
|
|
Loading…
Reference in a new issue