diff --git a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php --- a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php +++ b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php @@ -36,18 +36,44 @@ $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); - } + if ($this->getConfig('platform') === 'windows') { + // Handle Windows by executing the command under PowerShell. + $command = id(new PhutilCommandString($argv)) + ->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); - $full_command = call_user_func_array('csprintf', $argv); + $change_directory = ''; + if ($this->getWorkingDirectory() !== null) { + $change_directory .= 'cd '.$this->getWorkingDirectory(); + } - 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; + $script = <<applyWorkingDirectoryToArgv($argv); + + $full_command = call_user_func_array('csprintf', $argv); } $command_timeout = ''; diff --git a/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php --- a/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php @@ -3,6 +3,8 @@ final class HarbormasterCommandBuildStepImplementation extends HarbormasterBuildStepImplementation { + private $platform; + public function getName() { return pht('Run Command'); } @@ -18,6 +20,18 @@ $this->formatSettingForDescription('hostartifact')); } + public function escapeCommand($pattern, $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,14 +39,18 @@ $settings = $this->getSettings(); $variables = $build_target->getVariables(); + $artifact = $build->loadArtifact($settings['hostartifact']); + + $lease = $artifact->loadDrydockLease(); + + $this->platform = $lease->getAttribute('platform'); + $command = $this->mergeVariables( - 'vcsprintf', + array($this, 'escapeCommand'), $settings['command'], $variables); - $artifact = $build->loadArtifact($settings['hostartifact']); - - $lease = $artifact->loadDrydockLease(); + $this->platform = null; $interface = $lease->getInterface('command'); @@ -88,6 +106,9 @@ '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'),