@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement "Run Remote Command" build step implementation.

Summary: This adds a build step implementation for running a command on a remote machine over SSH. It supports merging in various variables about the build (such as the commit hash / revision ID, repository call sign, version control type and clone URI).

Test Plan: Configured a build plan to run `/bin/true` on localhost and the build passed. Configured a build plan to run `/bin/false` on localhost and the build failed.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran

Maniphest Tasks: T1049

Differential Revision: https://secure.phabricator.com/D7519

authored by

James Rhodes and committed by
epriestley
e64efa05 0ac1be70

+139
+139
src/applications/harbormaster/step/RemoteCommandBuildStepImplementation.php
··· 1 + <?php 2 + 3 + final class RemoteCommandBuildStepImplementation 4 + extends VariableBuildStepImplementation { 5 + 6 + public function getName() { 7 + return pht('Run Remote Command'); 8 + } 9 + 10 + public function getGenericDescription() { 11 + return pht('Run a command on another machine.'); 12 + } 13 + 14 + public function getDescription() { 15 + $settings = $this->getSettings(); 16 + 17 + return pht( 18 + 'Run \'%s\' on \'%s\'.', 19 + $settings['command'], 20 + $settings['sshhost']); 21 + } 22 + 23 + public function execute( 24 + HarbormasterBuild $build, 25 + HarbormasterBuildStep $build_step) { 26 + 27 + $settings = $this->getSettings(); 28 + 29 + $future = null; 30 + if (empty($settings['sshkey'])) { 31 + $future = new ExecFuture( 32 + 'ssh -o "StrictHostKeyChecking no" -p %s %s %s', 33 + $settings['sshport'], 34 + $settings['sshuser'].'@'.$settings['sshhost'], 35 + $this->mergeVariables($build, $settings['command'])); 36 + } else { 37 + $future = new ExecFuture( 38 + 'ssh -o "StrictHostKeyChecking no" -p %s -i %s %s %s', 39 + $settings['sshport'], 40 + $settings['sshkey'], 41 + $settings['sshuser'].'@'.$settings['sshhost'], 42 + $this->mergeVariables($build, $settings['command'])); 43 + } 44 + 45 + $log_stdout = $build->createLog($build_step, "remote", "stdout"); 46 + $log_stderr = $build->createLog($build_step, "remote", "stderr"); 47 + 48 + $start_stdout = $log_stdout->start(); 49 + $start_stderr = $log_stderr->start(); 50 + 51 + // Read the next amount of available output every second. 52 + while (!$future->isReady()) { 53 + list($stdout, $stderr) = $future->read(); 54 + $log_stdout->append($stdout); 55 + $log_stderr->append($stderr); 56 + $future->discardBuffers(); 57 + 58 + // Check to see if we have moved from a "Building" status. This 59 + // can occur if the user cancels the build, in which case we want 60 + // to terminate whatever we're doing and return as quickly as possible. 61 + if ($build->checkForCancellation()) { 62 + $log_stdout->finalize($start_stdout); 63 + $log_stderr->finalize($start_stderr); 64 + $future->resolveKill(); 65 + return; 66 + } 67 + 68 + // Wait one second before querying for more data. 69 + sleep(1); 70 + } 71 + 72 + // Get the return value so we can log that as well. 73 + list($err) = $future->resolve(); 74 + 75 + $log_stdout->finalize($start_stdout); 76 + $log_stderr->finalize($start_stderr); 77 + 78 + if ($err) { 79 + $build->setBuildStatus(HarbormasterBuild::STATUS_FAILED); 80 + } 81 + } 82 + 83 + public function validateSettings() { 84 + $settings = $this->getSettings(); 85 + 86 + if ($settings['command'] === null || !is_string($settings['command'])) { 87 + return false; 88 + } 89 + if ($settings['sshhost'] === null || !is_string($settings['sshhost'])) { 90 + return false; 91 + } 92 + if ($settings['sshuser'] === null || !is_string($settings['sshuser'])) { 93 + return false; 94 + } 95 + if ($settings['sshkey'] === null || !is_string($settings['sshkey'])) { 96 + return false; 97 + } 98 + if ($settings['sshport'] === null || !is_int($settings['sshport']) || 99 + $settings['sshport'] <= 0 || $settings['sshport'] >= 65536) { 100 + return false; 101 + } 102 + 103 + $whitelist = PhabricatorEnv::getEnvConfig( 104 + 'harbormaster.temporary.hosts.whitelist'); 105 + if (!in_array($settings['sshhost'], $whitelist)) { 106 + return false; 107 + } 108 + 109 + return true; 110 + } 111 + 112 + public function getSettingDefinitions() { 113 + return array( 114 + 'command' => array( 115 + 'name' => 'Command', 116 + 'description' => 'The command to execute on the remote machine.', 117 + 'type' => BuildStepImplementation::SETTING_TYPE_STRING), 118 + 'sshhost' => array( 119 + 'name' => 'SSH Host', 120 + 'description' => 'The SSH host that the command will be run on.', 121 + 'type' => BuildStepImplementation::SETTING_TYPE_STRING), 122 + 'sshport' => array( 123 + 'name' => 'SSH Port', 124 + 'description' => 'The SSH port to connect to.', 125 + 'type' => BuildStepImplementation::SETTING_TYPE_INTEGER, 126 + 'default' => 22), // TODO: 'default' doesn't do anything yet.. 127 + 'sshuser' => array( 128 + 'name' => 'SSH Username', 129 + 'description' => 'The SSH username to use.', 130 + 'type' => BuildStepImplementation::SETTING_TYPE_STRING), 131 + 'sshkey' => array( 132 + 'name' => 'SSH Identity File', 133 + 'description' => 134 + 'The path to the SSH identity file (private key) '. 135 + 'on the local web server.', 136 + 'type' => BuildStepImplementation::SETTING_TYPE_STRING)); 137 + } 138 + 139 + }