From 5e859c3dd0f153c0b4684e4702ba151bdcc21d20 Mon Sep 17 00:00:00 2001 From: Manuel Ruiz Date: Thu, 14 Oct 2021 17:30:09 +0000 Subject: [PATCH] Add jenkins-cli support for current and next version. Draft for feedback --- src/JenkinsCli.php | 122 ++++++++++++++++++++++++++++++++++++ src/JenkinsRunner.php | 19 +++--- tests/JenkinsRunnerTest.php | 2 +- try-next | 116 ++++++++++++++++++++++++++++++++++ 4 files changed, 251 insertions(+), 8 deletions(-) create mode 100644 src/JenkinsCli.php create mode 100755 try-next diff --git a/src/JenkinsCli.php b/src/JenkinsCli.php new file mode 100644 index 0000000..9900245 --- /dev/null +++ b/src/JenkinsCli.php @@ -0,0 +1,122 @@ + "/usr/etsy/jenkins-cli.jar", + "2.303.1" => "/usr/etsy/jenkins-cli-2.303.1.jar" + ]; + + /** + * Function to obtain Jenkins server info from given server url. + * Jenkins servers return info that can be used by clients to connect properly. + * This info is returned in http headers whose names start with 'x-' + * Example response: + * Array + * ( + * [x-content-type-options] => nosniff + * [x-hudson-theme] => default + * [x-hudson] => 1.395 + * [x-jenkins] => 2.19.3 + * [x-jenkins-session] => 091ef581 + * [x-hudson-cli-port] => 58999 + * [x-jenkins-cli-port] => 58999 + * [x-jenkins-cli2-port] => 58999 + * [x-frame-options] => sameorigin + * [x-instance-identity] => MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0vIS8uMTaf6Hep/4SdDkjz2719m+RlzCbDlpYNUvXzT6GNgE3/lnLmXic1wIn2Ym7B+aHkQjm/5bP33VXuob/x+R6dX3iUe93zi7YRG0KUeHXCgglm+y1BymxldqNzFyFVJ22D24Qnt7qoATEaoXLa4VkQ1ZuIaBzaVq0qNZYR7zShQvS7TRD+itqxFGFEKEWGwkh0sFgenSIFpwy9MmOhY11i/+A2VdA5K/KcPnmnW3AMByNUVoaigRkHOCAU2mnBsfFngTpdxd2SEGCxosewsfi/aCLRFU1INCFXgngt/V7sba4U2ADV8E/kS1rTdHDkWVQQMbiSCwKDPXQ30uQIDAQAB + * [x-ssh-endpoint] => try.etsycloud.com:41108 + * ) + */ + + private static function get_jenkins_server_info($server_url) + { + + $headers = []; + $curl = curl_init($server_url); + curl_setopt($curl, CURLOPT_URL, $server_url); + curl_setopt($curl, CURLOPT_HEADER, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + // see https://stackoverflow.com/a/41135574/16345588 + curl_setopt( + $curl, + CURLOPT_HEADERFUNCTION, + function ($curl, $header) use (&$headers) { + $len = strlen($header); + $header = explode(':', $header, 2); + if (count($header) < 2) // ignore invalid headers + return $len; + $header_key = strtolower(trim($header[0])); + if (substr($header_key, 0, 2) == 'x-') { + $headers[$header_key] = trim($header[1]); + } + + + return $len; + } + ); + echo "Getting server info for ". $server_url . PHP_EOL ; + $response = curl_exec($curl); + return $headers; + } + public static function get_jenkins_cli_info($server_url) + { + try { + $server_info = self::get_jenkins_server_info($server_url); + if (array_key_exists(SERVER_VERSION, $server_info)) { + $server_version = $server_info[SERVER_VERSION]; + $jenkins_cli_jar = self::JENKINS_CLI_JAR_VERSION_TABLE[$server_version]; + + }; + } catch (Exception $e) { + echo "Cannot get server info, falling back to NULL.". PHP_EOL; + $jenkins_cli_jar = NULL; + $server_version = NULL; + }; + echo "jenkins_cli_jar = ". $jenkins_cli_jar . PHP_EOL; + echo "server_version = ". $server_version . PHP_EOL; + return [$jenkins_cli_jar, $server_version ]; + } + public static function get_jenkins_cli_command($jenkins_cli_jar, $server_url, $user, $command) + { + // Get server info from jar name + $server_version = NULL; + foreach(self::JENKINS_CLI_JAR_VERSION_TABLE as $version => $jar){ + if ($jenkins_cli_jar == $jar){ + $server_version = $version; + break; + } + } + // Usage: java -jar jenkins-cli.jar [-s URL] command [opts...] args... + switch ($server_version) { + case "2.19.3": + $cli_options = ""; + break; + case "2.303.1": + $cli_options = sprintf("-ssh -user %s -logger OFF ", $user); + break; + default: + $cli_options = ""; + }; + $cmd_format = "java -jar %s %s -s %s %s"; + $cmd = sprintf( + $cmd_format, + $jenkins_cli_jar, + $cli_options, + $server_url, + $command + ); + return $cmd; + } +} diff --git a/src/JenkinsRunner.php b/src/JenkinsRunner.php index c4bd17b..1dcd8eb 100644 --- a/src/JenkinsRunner.php +++ b/src/JenkinsRunner.php @@ -15,6 +15,7 @@ abstract class JenkinsRunner { protected $jenkins_url; protected $jenkins_cli; + protected $jenkins_version; protected $try_job_name; protected $cmd_runner; @@ -25,6 +26,7 @@ abstract class JenkinsRunner { private $options; private $callbacks; private $ssh_key_path; + private $user; public function __construct( $jenkins_url, @@ -32,21 +34,28 @@ public function __construct( $try_job_name, $cmd_runner ) { + $debug_info = sprintf( "__construct(%s, %s, %s \n", + $jenkins_url, + $jenkins_cli, + $try_job_name, + ); + echo $debug_info; if (filter_var($jenkins_url, FILTER_VALIDATE_URL) !== false) { $this->jenkins_url = $jenkins_url; } else { throw new Exception("jenkins url must include protocol, ie http://, and trailing /, $jenkins_url given"); } - $this->jenkins_cli = $jenkins_cli; + $this->try_job_name = $try_job_name; $this->cmd_runner = $cmd_runner; - + $this->jenkins_cli = $jenkins_cli; $this->options = array(); $this->callbacks = array(); $this->ssh_key_path = null; $this->branch = null; $this->try_status = ''; $this->try_base_url = ''; + $this->user = getenv("USER"); } abstract protected function pollForCompletion($pretty); @@ -56,11 +65,7 @@ abstract protected function getBuildCommand(); abstract protected function getBuildExtraArguments($show_results, $show_progress); public function runJenkinsCommand($command) { - $cmd = sprintf( "java -jar %s -s %s %s", - $this->jenkins_cli, - $this->jenkins_url, - $command - ); + $cmd = JenkinsCli::get_jenkins_cli_command($this->jenkins_cli, $this->jenkins_url, $this->user , $command); $this->cmd_runner->run($cmd, false, false); } diff --git a/tests/JenkinsRunnerTest.php b/tests/JenkinsRunnerTest.php index b02aac9..0f39a1b 100644 --- a/tests/JenkinsRunnerTest.php +++ b/tests/JenkinsRunnerTest.php @@ -60,7 +60,7 @@ function testInvalidUrl() { function testRunJenkinsCommand() { - $expected_cmd = 'java -jar ' . self::JENKINS_CLI . ' -s ' . self::JENKINS_URL . ' dummy-cmd'; + $expected_cmd = 'java -jar ' . self::JENKINS_CLI . ' -s ' . self::JENKINS_URL . ' dummy-cmd'; $this->mock_cmd_runner->expects($this->once()) ->method('run') diff --git a/try-next b/try-next new file mode 100755 index 0000000..46dbfc6 --- /dev/null +++ b/try-next @@ -0,0 +1,116 @@ +#!/usr/bin/env php + 0) { + $default_wc_path = "/home/$user/development/".$matches[1]; + } else { + $default_wc_path = "/home/$user/development/Etsyweb"; // Backwards compatibility + } +} + +$default_branch = 'main'; + +// In the following, in_array matches _only_ -b and preg_grep matches _both_ +// --branch and --branch= +if (in_array("-b", $argv) || preg_grep("/--branch.*/", $argv)) { + // Branch was manually passed in, we can continue as-is + echo "The branch has been set manually, continuing with specified branch...\n"; +} else { + // Manually set the 'default' branch + array_push($argv, "-b", $default_branch); +} + +$options_tuple = TryLib\TryRunner\Options::parse( + $argv, + "https://try.etsycloud.com/", + "try", + "try", + $default_wc_path +); + +$blocklist = array( + 'phplib/EtsyConfig/production.php', + 'phplib/EtsyConfig/giftcards_production.php' +); + +$safelist = $options_tuple[0]->safelist; +if (is_string($safelist)) { + $safelist = array($safelist); +} + +$branch = $options_tuple[0]->branch; + +$jira_ticket_specified = false; +$get_rodeo_tickets_in_commit_script = $options_tuple[0]->wcpath . '/bin/rodeo/reportRodeoTicketInLocalCommits.php'; + +if (file_exists($get_rodeo_tickets_in_commit_script)) { + # Let's add a ROD-XX ticket to the extra-param if not specified + # Extra param is an option of the TryLib_Util_PHPOptions_OptDict of $options_tuple[0] + $extra_param = $options_tuple[0]->offsetGet('extra_param'); + + # Start by transforming the extra_param argument into an array + #(can be null, a string if --extra_param is passed once or an array if --extra_param is passed multiple times) + if (is_null($extra_param) || is_string($extra_param)) { + $extra_param = array($extra_param); + } + + # Function to check if extra_param is a jira param + $is_jira_param = function($k) {return strpos($k, 'jira=') === 0;}; + if (empty (array_filter($extra_param, $is_jira_param))) { + # No JIRA param specified - let's try to add one + $branch = $options_tuple[0]->branch; + exec($get_rodeo_tickets_in_commit_script . ' ' . $branch, $jira_ticket, $ret); + if ($ret === 0) { + echo "Try Rodeo Sniffer using Rodeo ticket from commit message: " . reset($jira_ticket) . "\n"; + $extra_param[] = "jira=" . reset($jira_ticket); + $jira_ticket_specified = true; + } + } else { + $jira_ticket_specified = true; + } + // Filter out potential emtpy values in extra-param and set them back + $options_tuple[0]->offsetSet('extra_param', array_filter($extra_param)); +} + +$jenkinsserver = $options_tuple[0]->jenkinsserver; +$jenkins_cli_info = JenkinsCli::get_jenkins_cli_info($jenkinsserver); +$jenkins_cli_jar = $jenkins_cli_info[0]; +$jenkins_cli_version = $jenkins_cli_info[1]; +echo "try-next " . $jenkins_cli_jar . "," . PHP_EOL; +$try_runner = TryLib\TryRunner\Builder::masterProject() + ->optionsTuple($options_tuple) + ->jenkinsCliJarPath($jenkins_cli_jar) + ->prechecks(array( + new TryLib\Precheck\ScriptRunner($options_tuple[0]->wcpath . '/bin/asset-validator.php'), + new TryLib\Precheck\GitWarnOnBlocklisted($blocklist, $safelist, $options_tuple[0]->staged), + new TryLib\Precheck\GitCopyBehind(array($branch)), + new TryLib\Precheck\GitReportUntracked() + )) + ->sshKeyPath(getenv('HOME') . '/.ssh/try_id_rsa') + ->overrideUser($user) + ->build(); + + +if (!$jira_ticket_specified) { + $report_scoped_files_in_diff_script = $options_tuple[0]->wcpath . '/bin/rodeo/reportScopedFilesInDiff.php'; + if (file_exists($report_scoped_files_in_diff_script)) { + system($report_scoped_files_in_diff_script . ' ' . $try_runner->getPatchLocation(), $ret); + if ($ret !== 0) { + // The rodeo sniffer is gonna fail so prompt if they want to continue or not. + $contin = readline("Would you like to continue (y/N)? "); + if (strlen($contin) === 0 || strtolower(trim($contin))[0] !== 'y') { + exit($ret); + } + } + } +} + +exit($try_runner->run());