/** * @param array $conf Configurations for reaching Gerrit server */ public function __construct() { /** @var \Bart\Configuration\GerritConfig $config */ $config = Diesel::create('Bart\\Configuration\\GerritConfig'); /** @var \Bart\SshWrapper $ssh */ $ssh = Diesel::create('Bart\\SshWrapper', $config->host(), $config->sshPort()); $ssh->setCredentials($config->sshUser(), $config->sshKeyFile()); $this->ssh = $ssh; $this->config = $config; $this->logger = Log4PHP::getLogger(__CLASS__); $this->logger->trace("Configured Gerrit API using ssh {$ssh}"); }
/** * Register a mock shell instance with Diesel */ protected function registerMockShell($mockShell = null) { if (!$mockShell) { $mockShell = $this->getMock('Bart\\Shell'); } Diesel::registerInstantiator('Bart\\Shell', $mockShell, true); }
/** * @param string $dir The git directory of interest * @param string $origin Upstream origin name * @maintenance Migrate over to GitCommit and Bart\Git namespace */ public function __construct($dir = '.git', $origin = 'origin') { $this->dir = $dir; $this->git = "git --git-dir={$dir}"; $this->origin = $origin; $this->shell = Diesel::singleton('Bart\\Shell'); }
/** * @param array $hookConf Configuration for this hook type * @param string $repo Name of the repository */ public function __construct(array $hookConf, $gitDir, $repo) { $this->hookConf = $hookConf; $this->repo = $repo; $this->logger = Log4PHP::getLogger(get_called_class()); /** @var \Bart\Git git handle to current project */ $this->git = Diesel::create('Bart\\Git', $gitDir); }
/** * Register a git stub with Diesel * @return \Bart\Git git stub that was registered */ public function getGitStub() { // mock git and method get change id to return $repo $gitStub = $this->getMock('\\Bart\\Git', array(), array(), '', false); Diesel::registerInstantiator('Bart\\Git', function () use($gitStub) { return $gitStub; }); return $gitStub; }
/** * Register our mock soap instance with local Diesel */ private function register_soap_with_diesel() { $phpu = $this; Diesel::registerInstantiator('\\SoapClient', function ($wsdl, $options) use($phpu) { $phpu->assertEquals($phpu->opts['wsdl'], $wsdl, 'wsdl'); $phpu->assertEquals($phpu->opts['key'], $options['key'], 'opts'); return $phpu->soap; }); }
public function __construct(array $conf, $gitDir, $repo) { $stl_conf = $conf['jenkins']; if (!array_key_exists('job_name', $stl_conf)) { // Default to the repo for convenience $stl_conf['job_name'] = $repo; } parent::__construct($stl_conf, $gitDir, $repo); $this->job = Diesel::create('Bart\\Jenkins\\Job', $stl_conf['host'], $stl_conf['job_name']); }
public function __construct($server) { if (!is_string($server)) { throw new \Exception("Invalid server {$server}"); } $this->server = $server; $this->ssh_user = get_current_user(); $this->shell = Diesel::create('Bart\\Shell'); $this->conf = Diesel::create('Bart\\Config_Parser'); }
/** * Configure Job for injection of stub Curl with $json * @param string $url Expected Jenkins URL * @param JSON $json */ private function configure_diesel($url, $json) { $mock_curl = $this->getMock('\\Bart\\Curl', array(), array(), '', false); $mock_curl->expects($this->once())->method('get')->with($this->equalTo(''), $this->equalTo(array()))->will($this->returnValue(array('content' => $json))); $phpu = $this; Diesel::registerInstantiator('Bart\\Curl', function ($urlParam, $portParam) use($phpu, $url, $mock_curl) { $phpu->assertEquals($url, $urlParam, 'url'); $phpu->assertEquals(8080, $portParam, 'port'); return $mock_curl; }); }
/** * * @param string $usename Jira username * @param string $password Jira password * @param array $options Generic soap options hash. Must, at the least, specify the WSDL to * your Jira SOAP, which can be a local or remote resource. It typically is available at * http://$your-jira-server/rpc/soap/jirasoapservice-v2?wsdl' */ public function __construct($username, $password, $options) { $wsdl = $options['wsdl']; unset($options['wsdl']); $this->soap = Diesel::create('\\SoapClient', $wsdl, $options); try { $this->token = $this->soap->login($username, $password); } catch (\SoapFault $f) { throw new Soap_Exception($f, 'Authentication failed'); } }
/** * @param string $revision * @return bool */ private function isChangeFeatureFlip($revision) { /** @var Git $git */ $git = Diesel::create('\\Bart\\Git'); $fileList = $git->get_file_list($revision); foreach ($fileList as $file) { if ($file == 'conf_override/features.conf') { return true; } } return false; }
private function configure_for($conf, $is_healthy, $job_name) { $mock_job = $this->getMock('\\Bart\\Jenkins\\Job', array(), array(), '', false); $mock_job->expects($this->once())->method('is_healthy')->will($this->returnValue($is_healthy)); $gitStub = $this->getGitStub(); $phpu = $this; Diesel::registerInstantiator('Bart\\Jenkins\\Job', function ($host, $jobNameParam) use($phpu, $conf, $job_name, $mock_job) { $phpu->assertEquals($job_name, $jobNameParam, 'Jenkins job name'); $phpu->assertEquals($conf['jenkins']['host'], $host, 'Jenkins host'); return $mock_job; }); return array('stl' => new StopTheLineJenkins($conf, '', 'Gorg'), 'git' => $gitStub); }
public function testConstructor() { $conf = array(); // mock git and method get_change_id to return $repo $mock_git = $this->getMock('\\Bart\\Git', array(), array(), '', false); $mock_git->expects($this->once())->method('get_change_id')->will($this->returnValue('grinder')); $phpu = $this; Diesel::registerInstantiator('Bart\\Git', function ($gitDir) use($mock_git, $phpu) { $phpu->assertEquals('.git', $gitDir, 'Expected constructor to get git dir'); return $mock_git; }); $hook = new TestGitHookAction($conf, '.git', 'grinder'); $hook->run($this); }
/** * @integrationTest */ public function testRawFileContentsReal() { // Let's make sure the command we're running is legit $expectedContents = trim(shell_exec('git show HEAD:composer.json')); // Replicate the actual shell invocation that would take place Diesel::registerInstantiator('Bart\\Shell\\Command', function () { $shell = new Shell(); $args = func_get_args(); return call_user_func_array([$shell, 'command'], $args); }); $commit = new Commit(new GitRoot(BART_DIR . '/.git'), 'HEAD'); $actualContents = trim($commit->rawFileContents('composer.json')); $this->assertEquals($expectedContents, $actualContents, 'Raw file contents'); }
/** * Basic, shared setup for the Build_In_Jenkins hook * * @param array $conf Configurations for the hook * @param string $commit_msg The commit message for the hook * @param string $job_name The name of the job to be built * @return array The git hook and the git stub */ private function configure_for(array $conf, $commit_msg, $job_name) { $hash = 'HEAD'; $info = array('author' => self::$author, 'subject' => '', 'message' => $commit_msg); $mock_git = $this->getGitStub(); $mock_git->expects($this->once())->method('get_pretty_email')->with($this->equalTo($hash))->will($this->returnValue($info)); $phpu = $this; $mock_job = $this->getMock('\\Bart\\Jenkins\\Job', array(), array(), '', false); Diesel::registerInstantiator('Bart\\Jenkins\\Job', function ($host, $name) use($phpu, $conf, $job_name, $mock_job) { $phpu->assertEquals($job_name, $name, 'Jenkins job name did not match'); $phpu->assertEquals($conf['jenkins']['host'], $host, 'Expected host to match conf'); return $mock_job; }); return array('j' => new BuildInJenkins($conf, '', self::$repo), 'git' => $mock_git, 'job' => $mock_job); }
public function testUnderscoresSupported() { Diesel::registerInstantiator('\\Bart\\Shell', function () { // Actually want to see how this works with a real Shell return new Shell(); }); $this->doStuffWithTempDir(function (BaseTestCase $phpu, $dirName) { Configuration::configure($dirName); // Copy sample INI file to path Configuration will look for TestConfig INI copy(BART_DIR . '/test/etc/conf-parser.conf', $dirName . '/test_underscore.conf'); $configs = new Test_Underscore_Config(false); $phpu->assertEquals(42, $configs->number(), 'Underscore Configs number'); $phpu->assertEquals('Quail', $configs->wildGame(), 'Underscore Configs wildGame'); }); }
public function __construct() { parent::__construct(); /** @var JenkinsConfig $jenkinsConfig */ $jenkinsConfig = Diesel::create('\\Bart\\Jenkins\\JenkinsConfig'); /** @var Connection $connection */ $connection = Diesel::create('\\Bart\\Jenkins\\Connection', $jenkinsConfig->domain(), $jenkinsConfig->protocol(), $jenkinsConfig->port()); $user = $jenkinsConfig->user(); $token = $jenkinsConfig->token(); if ($user !== null && $token !== null) { $connection->setAuth($user, $token); } /** @var Job job */ $this->job = Diesel::create('\\Bart\\Jenkins\\Job', $connection, $jenkinsConfig->jobLocation()); }
public function testBasicCommandExec() { $root = new GitRoot(); $command = CommandTest::withStubbedResult($this, ['a57a266'], 0); Diesel::registerInstantiator('Bart\\Shell\\Command', function ($fmt, $dir, $limit, $author, $pretty) use($command) { // Assert that GitRoot sends expected parameters to create the Command $this->assertEquals('git --git-dir=%s log %s --author=%s --format:%s', $fmt, 'command arg 0'); $this->assertEquals('.git', $dir); $this->assertEquals('-1', $limit, 'Limit of commits'); $this->assertEquals('jbraynard', $author); $this->assertEquals('%h', $pretty, 'pretty format'); return $command; }); // Throw something together with a few args interspersed $result = $root->getCommandResult('log %s --author=%s --format:%s', '-1', 'jbraynard', '%h'); $this->assertEquals('a57a266', $result->getOutput(true), 'git log'); }
/** * Fails if review is not approved & verified in Gerrit * @param Commit $commit A git commit with a Change-Id * @throws GitHookException If Change-Id not found or the review is not approved or verified */ public function run(Commit $commit) { try { $changeId = $commit->gerritChangeId(); } catch (GitException $e) { $this->logger->warn("{$e->getMessage()}. Skipping commit."); throw new GitHookException("Couldn't get Change-Id for {$commit}", $e->getCode(), $e); } /** @var \Bart\Gerrit\Change $change */ $change = Diesel::create('\\Bart\\Gerrit\\Change', $changeId); if (!$change->isReviewedAndVerified()) { $msg = "Could not find an approved & verified change in Gerrit for change {$changeId} in commit {$commit}"; $this->logger->info($msg); throw new GitHookException($msg); } $this->logger->info('Gerrit approved.'); }
/** * Add a comment in JIRA with the commit hash * @param Commit $commit The commit for which we're running the Git Hook * @throws GitHookException if requirement fails */ public function run(Commit $commit) { /** @var \Bart\GitHook\GitHookConfig $hConfigs */ $hConfigs = Diesel::create('\\Bart\\GitHook\\GitHookConfig', $commit); // Apply template to produce desired comment for JIRA issue $template = $hConfigs->jiraCommentTemplate(); $count = preg_match_all('/\\%s/', $template); $this->logger->debug("Loaded jira template --{$template}-- and found {$count} token(s)"); $vsprintf_args = []; if ($count !== false) { $vsprintf_args = array_fill(0, $count, $commit->revision()); } $comment = vsprintf($template, $vsprintf_args); $jiraIssues = $commit->jiras(); $this->logger->debug('Found ' . count($jiraIssues) . " jira issue(s) in {$commit}"); foreach ($jiraIssues as $jira) { $this->logger->debug("Adding comment to jira {$jira}"); $this->jiraClient->addComment($jira->id(), $comment); } }
/** * Curl Jenkins JSON API * * NOTE: This method is not meant to be used on its own. It is used by other classes, * e.g. \Bart\Jenkins\Job, to make API calls against Jenkins. * * @param string $apiPath The full API path to curl against. For example, to do a * simple GET against the Jenkins Job 'Example', the full path, 'job/Example/api/json' * must be passed in. * @param array $postData if null, then curl uses GET, otherwise POSTs data * @return array JSON data decoded as PHP array * @throws JenkinsApiException */ public function curlJenkinsApi($apiPath, array $postData = null) { if (!Strings::startsWith($apiPath, '/')) { $apiPath = "/{$apiPath}"; } $fullUrl = "{$this->baseUrl}{$apiPath}"; $isPost = $postData !== null; $this->logger->debug('Curling ' . ($isPost ? 'POST ' : 'GET ') . $fullUrl); /** @var \Bart\Curl $curl */ $curl = Diesel::create('\\Bart\\Curl', $fullUrl, $this->port); if ($this->curlOptions !== []) { $curl->setPhpCurlOpts($this->curlOptions); } $response = $isPost ? $curl->post('', [], $postData) : $curl->get('', []); $httpCode = $response['info']['http_code']; $content = $response['content']; if ($httpCode !== 200 && $httpCode !== 201 && $httpCode !== 202) { throw new JenkinsApiException("The Jenkins API call returned a {$httpCode}, " . "with the following content: {$content}"); } return JSON::decode($content); }
/** * Stub the expected configuration * @param bool $buildHealth */ private function mockJenkinsJobWithDependencies($buildHealth = true) { $this->shmockAndDieselify('\\Bart\\Jenkins\\JenkinsConfig', function ($jConfigs) { $jConfigs->domain()->once()->return_value('jenkins.example.com'); $jConfigs->port()->once()->return_value('8080'); $jConfigs->protocol()->once()->return_value('http'); $jConfigs->user()->once()->return_value('user'); $jConfigs->token()->once()->return_value('token'); $jConfigs->jobLocation()->once()->return_value('job/Base/job/Build'); }, true); $mockConnection = $this->shmockAndDieselify('\\Bart\\Jenkins\\Connection', function ($connection) { $connection->setAuth()->once(); }, true); $mockJob = $this->shmock('\\Bart\\Jenkins\\Job', function ($jobStub) use($buildHealth) { $jobStub->isHealthy()->once()->return_value($buildHealth); }, true); Diesel::registerInstantiator('\\Bart\\Jenkins\\Job', function ($connection) use($mockJob, $mockConnection) { $this->assertEquals($mockConnection, $connection, '\\Bart\\Jenkins\\Connection object'); return $mockJob; }); }
public function run($commitHash) { $info = $this->git->get_pretty_email($commitHash); $msg = $info['subject'] . PHP_EOL . $info['message']; if (preg_match('/\\{nobuild\\:\\s(\\".+?\\")\\}/', $msg, $matches) > 0) { $reason = $matches[1]; $this->logger->debug('Skipping build with message: ' . $reason); return; } $jobName = $this->hookConf['job_name']; // Default parameters that all jobs may use, but may otherwise ignore $params = array('GIT_HASH' => $commitHash, 'Project_Name' => $this->repo, 'Requested_By' => $info['author']); if (preg_match('/\\{deploy\\}/', $msg, $matches) > 0) { // Submit a deploy job for repo $jobName = $this->hookConf['deploy-job']; // For repos whose deploy job is one and the same as the integration job $params['DEPLOY'] = 'true'; } /** @var \Bart\Jenkins\Job $job */ $job = Diesel::create('Bart\\Jenkins\\Job', $this->hookConf['host'], $jobName); $job->start($params); }
/** * Run each revision against current hook */ private function processRevisions() { /** @var \Bart\Git $git */ $git = Diesel::create('\\Bart\\Git', $this->gitDir); /** @var \Bart\Shell $shell */ $shell = Diesel::create('\\Bart\\Shell'); // TODO This will need to change to support the 'update' hook $stdin = $shell->std_in(); foreach ($stdin as $rangeAndRef) { list($startHash, $endHash, $ref) = explode(" ", $rangeAndRef); $endCommit = Diesel::create('\\Bart\\Git\\Commit', $this->gitRoot, $endHash); /** @var \Bart\GitHook\GitHookConfig $configs */ $configs = Diesel::create('\\Bart\\GitHook\\GitHookConfig', $endCommit); $validRefs = $configs->getValidRefs(); // Check whether current ref should have git hooks run or not if (!in_array($ref, $validRefs)) { $this->logger->info('Skipping hooks on ref ' . $ref); continue; } // TODO should this be reversed? // TODO This list could be massive for branches, should we have some config for how deep to go? $revisions = $git->getRevList($startHash, $endHash); $this->logger->debug('Found ' . count($revisions) . ' revision(s)'); foreach ($revisions as $revision) { $commit = Diesel::create('\\Bart\\Git\\Commit', $this->gitRoot, $revision); // Allow a backdoor in case of emergency or broken hook configuration if ($this->shouldSkip($commit, $configs)) { continue; } $hookRunner = $this->createHookRunner($commit); $this->logger->debug("Created {$hookRunner}"); $this->logger->debug("Verifying all configured hook actions against {$revision}"); // Let any failures bubble up to caller $hookRunner->runAllActions(); } } }
/** * Mocks out the Diesel Curl class * @param callable $configure * @param string $expectedProtocol * @param string $expectedPort * @throws \Bart\DieselException */ private function mockCurl($configure, $expectedProtocol = 'http', $expectedPort = '8080') { $fullUrl = "{$expectedProtocol}://" . self::$fakeDomain . ":{$expectedPort}/" . self::$fakeApiPath; $mockCurl = $this->shmock('\\Bart\\Curl', function ($curlStub) use($configure) { $configure($curlStub); }, true); Diesel::registerInstantiator('\\Bart\\Curl', function ($url, $port) use($mockCurl, $fullUrl, $expectedPort) { $this->assertEquals($fullUrl, $url, 'Full Jenkins REST API URL'); $this->assertEquals($expectedPort, $port, 'Port'); return $mockCurl; }); }
/** * @param String $remoteGerritCmd * @param \PHPUnit_Framework_MockObject_Stub $will * @return Api */ private function configureApiForCmd($remoteGerritCmd, $will) { $ssh = $this->getMock('\\Bart\\SshWrapper', array(), array(), '', false); $ssh->expects($this->once())->method('exec')->with($this->equalTo($remoteGerritCmd))->will($will); $gerritConfigs = $this->gerritConfigs; Diesel::registerInstantiator('Bart\\Configuration\\GerritConfig', function () use($gerritConfigs) { return $gerritConfigs; }); $phpu = $this; Diesel::registerInstantiator('Bart\\SshWrapper', function ($server, $port) use($ssh, $phpu) { $phpu->assertEquals('gerrit.example.com', $server, 'gerrit server'); $phpu->assertEquals(29418, $port, 'gerrit ssh port'); return $ssh; }); return new Api(); }
/** * @param string $changeId Gerrit Change-Id key */ public function __construct($changeId) { $this->api = Diesel::create('\\Bart\\Gerrit\\Api'); $this->changeId = $changeId; $this->logger = Log4PHP::getLogger(__CLASS__); }
/** * @param $pidFileLocation string Full path to where the pid file should be located, e.g. '/var/run/my-process.pid' */ public function __construct($pidFileLocation) { $this->pidFileLocation = $pidFileLocation; $this->shell = Diesel::create('\\Bart\\Shell'); $this->logger = Log4PHP::getLogger(__CLASS__); }
/** * @abstract * @param string $filePath Absolute path to file containing configurations * @param string $subclass Name of the configuration class * @return array Contents of configuration parsed as INI with sections * @throws ConfigurationException */ protected function loadParsedIni($filePath, $subclass) { /** @var \Bart\Shell $shell */ $shell = Diesel::create('\\Bart\\Shell'); if (!$shell->file_exists($filePath)) { throw new ConfigurationException("No configuration file found for {$subclass} at {$filePath}"); } // @NOTE we're not using the ConfigResolver to resolve environment // ...distinctions by default. To add this ability, a new method should // ...be added to this base to resolve and then reset @configurations return $shell->parse_ini_file($filePath, true); }
/** * Instantiate a new hook action * @param string $fqcn Name of GitHookAction class * @return \Bart\GitHook\GitHookAction * @throws GitHookException If class DNE */ private function createHookActionFor($fqcn) { if ($fqcn === '') { throw new GitHookException('Got empty string for GitHookAction FQCN'); } if (!class_exists($fqcn)) { throw new GitHookException("No such hook action ({$fqcn})"); } return Diesel::create($fqcn); }